scala 语法中,类并不声明为 public,所有这些类都具有公有可见性(即默认就是 public)
Scala 中声明一个属性,必须显示的初始化,然后根据初始化数据的类型自动推断,属性类型可 以省略(这点和 Java 不同)。
如果赋值为 null,则一定要加类型,因为不加类型, 那么该属性的类型就是Null 类型
如果在定义属性时,暂时不赋值,也可以使用符号_(下划线),让系统分配默认值
scala 在声明对象变量时,可以根据创建对象的类型自动推断,所以类型声明可以省略,但当类型和后面 new 对象类型有继承关系即多态时,就必须写
scala 类的构造器包括:主构造器,辅助构造器
class Person(inName:String,inAge:Int) {//主构造器
var name:String = inName
var age:Int = inAge
age+=10
override def toString = s"Person($name, $age)"
def this(name: String)= this(name,10)//辅助构造器必须在第一行显示调用主构造器
def this()= this("未命名",10)//辅助构造器必须在第一行显示调用主构造器
}
object Person{
def apply(inName: String, inAge: Int): Person = new Person(inName, inAge)
def apply(inName:String):Person = new Person(inName)
def apply():Person = new Person()
def main(args: Array[String]): Unit = {
println(apply("张三").toString)
println(apply().toString)
}
}
scala构造器没有返回值,作用是完成对象的初始化
主构造器声明时要放在类名之后,会执行类定义中的所有语句
如果主构造器无参数,小括号可省略
若想私有主构造器,可在()前加上private,此时用户只能通过辅助构造器构造对象
class Person private(){}
辅助构造器的声明不能和主构造器的声明一致,会发生错误(即构造器名重复)
class Worker(inName: String) {
var name:String = inName
}
class Worker2(val inName:String){
var name:String = inName
}
class Worker3(var inName:String){
var name:String = inName
}
object conDemo1{
def main(args: Array[String]): Unit = {
val worker = new Worker("hao")
worker.name//不能访问inName
val worker2 = new Worker2("hao")
worker2.inName//可以访问inName
val worker3 = new Worker3("hao")
worker3.inName="jie"//可读可写
}
}
class Worker3(var inName:String){
@BeanProperty
var name:String = inName
}
object conDemo1{
def main(args: Array[String]): Unit = {
val worker3 = new Worker3("hao")
worker3.getName
}
}
在Scala 字段加@BeanProperty 时,这样会自动生成规范 的 setXxx/getXxx 方法。这时可以使用 对象.setXxx() 和 对象.getXxx() 来调用属性。
加载对象的过程
new 以后,加载类的信息,在内存开辟空间,父类主副构造器,主构造器,辅助构造器,附地址值
1)区分相同名字的类
2) 当类很多时,可以很好的管理类
3) 控制访问范围
Scala 中包名和源码所在的系统文件目录结构要可以不一致,但是编译后的字节码文件路径和包名 会保持一致(这个工作由编译器完成)。
Scala 会自动引入的常用包:
java.lang.* scala Rredef
package com.atguigu{}
表示我们创建了包 com.atguigu ,在{}中 // 我们可以继续写它的子包 scala //com.atguigu.scala, 还可以写类,特质 trait,还可以写 object
sacla 支持,在一个文件中,可以同时创建多个包,以及给各个包创建类,trait 和 object(支持嵌套,建议嵌套不超过3层)
package cn.kgc.fu{
package zi{
class packageDemo{
val name="hao"
def play: String => Unit = (message:String)=>println(this.name+" "+message)
}
object packageDemo{
def main(args: Array[String]): Unit = {
println("ok")
}
}
}
package zi2{
}
}
作用域原则:scala中可以直接向上访问:scala中子包可以直接访问父包的内容(java中需要使用import),大括号表示包的作用域,当子包与父包类重名时,采用就近原则,需要指定使用某个类,带上包名
父包访问子包中的内容是,需要import
包名可以相对也可以绝对,一般情况下使用相对,当包名产生冲突时,采用绝对路径
为了弥补“包中不能包含函数/方法或变量的定义”这一局限,scala提高了包对象的概念
1. 在包中直接写方法,或者定义变量,就错误==>使用包对象的技术来解决
2. package object scala 表示创建一个包对象 scala, 他是 com.atguigu.scala这个包对应的包对象
3. 每一个包都可以有一个包对象
4. 包对象的名字需要和子包一样
5. 在包对象中可以定义变量,方法
6. 在包对象中定义的变量和方法,就可以在对应的包中使用
7. 在底层这个包对象会生成两个类 package.class 和 package$.class
package object pac {
var name = "jie"
def say=()=>println("say me")
}
package cn.kgc.hao.pac
class Pac {
class Person{
val name = "Nick"
def play = (message:String)=>println(s"${this.name} $message")
}
class User{
println("name="+name)
say()
}
object Test10{
def main(args: Array[String]): Unit = {
say
}
}
}
每个包都可以有一个包对象,可以在父包中定义它
包对象名称需要和报名一致,一般用来对包的功能补充
当属性访问权限为默认时,从底层看属性是 private 的,但是因为提供了 xxx_$eq()[类似setter]/xxx()[类似 getter] 方法,因此从使用效果看是任何地方都可以访问)
当方法访问权限为默认时,默认为public
private 为私有权限,只在类的内部和伴生对象中可用
protected 为受保护权限,scala 中受保护权限比 Java 中更严格,只能子类访问,同包无法访问
在 scala 中没有 public 关键字,即不能用 public 显式的修饰属性和方法。
包访问权限
private[x] //该成员除了x可见,对其他都为私有
protected[x] //类比上一句,差不多
Scala 会自动引入的包:java.lang.* scala Predef
Scala中,import可以出现在任何地方,不限于文件顶部,作用范围是包含该语句的块末尾
需要引入时再引入,提高效率
java中导入包下所有类采用*
,scala中使用_
import支持选取器
import scala.collection.mutable.{HashMap, HashSet}
import类支持重命名
import scala.collection.mutable.{HashMap=>ScalaHashMap}
导入包时,若存在冲突的类,且该类不会用到,可直接隐藏该类
import java.util.{HashMap=>_,_} //导入java.util中的若有类,忽略HashMap
封装(encapsulation)就是把抽象出的数据和对数据的操作封装在一起,数据被保护在内部,程序的其
它部分只有通过被授权的操作(成员方法),才能对数据进行操作。
scala中,属性声明为var时,本身自带getter/setter方法,属性声明为private,方法也为private,属性访问控制权限为默认,方法也为public
因此我们如果只是对一个属性进行简单的 set 和 get ,只要声明一下该属性(属性使用默认访问
修饰符) 不用写专门的 getset,默认会创建,访问时,直接对象.变量。
dog.food 直接访问属性,其实底层仍然是访问的方法,
由于上面的特性,有些框架进行反射时,也支持对属性的直接访问
class 子类名 extends 父类名 { 类体 }
classOf[String]就如同 Java 的 String.class 。
obj.isInstanceOf[T]就如同 Java 的 obj instanceofT 判断 obj 是不是 T 类型。
obj.asInstanceOf[T]就如同 Java 的(T)obj 将 obj 强转成 T 类型。
只有主构造器可以调用父类的构造器。辅助构造器不能直接调用父类的构造器。在 Scala 的构造
器中,你不能调用 super(params)
class Fu(name:String,age:Int) {
}
class Zi(name:String)extends Fu(name,1){//正确的调用方式
//super(name) 不支持该语法
//def this()=super("abc") 辅助构造器中不能调用父类构造器
}
注意事项:
1)def只能重写另一个 def(即:方法只能重写另一个方法)
2) val 只能重写另一个 val 属性 或 重写不带参数的 def
3) var 只能重写另一个抽象的 var 属性
- 抽象的字段(属性):就是没有初始化的字段(属性)
- 当一个类含有抽象属性时,则该类需要标记为 abstract
- 对于抽象的属性,在底层不会生成对应的属性声明,而是生成两个对应的抽象方法(name name_$eq)
在子类中重写父类的抽象属性,本质上是实现抽象方法(overide可写可不写)
通过 abstract 关键字标记不能被实例化的类。方法不用标记 abstract,只要省掉方法体
即可。抽象类可以拥有抽象字段,抽象字段/属性就是没有初始值的字段
new抽象类时,直接实现/重写抽象方法、属性(类似于Java)
abstract class Fu(name:String,age:Int) {
abstract var birthday:String
abstract def play():Unit
}
import extendtest.Fu
val fu = new Fu("zhazha",3) {
override var birthday: String = "11"
override def play(): Unit = ""
}
scala是完全面向对象的语言,在scala中没有静态的概念
为了能够与Java语言交互,产生了一个特殊的对象来模拟类对象:伴生对象,类的所有静态内容都可以放置在它的伴生对象中声明和调用
伴生对象的实现机制,伴生对象在底层是个final修饰的类,类中有一个静态属性
MODULE$
指向本类对象public static final MODULE$ =new 本类对象
def apply(pName: String): Pig = new Pig(pName)
scala中没有接口(scala是纯面向对象的语言,接口不属于面向对象的范畴),采用trait(特征)来代替接口的概念
trait:多个类具有相同的特征时,就可以将这个特征独立出来,采用关键字 trait声明。
在 scala 中,java 的接口都可以当做 trait 来使用
trait可以理解为抽象类+接口,可以同时拥有抽象方法和具体方法,一个类可以实现/继承多个
特质。
trait采用extends关键字,当存在多个特质或父类时,使用with连接
//没有父类
class 类名 extends 特质 1 with 特质 2 with 特质 3 ..
//有父类
class 类名 extends 父类 with 特质 1 with 特质 2 with 特质 3
除了在类声明时继承特质,还可以在构建对象时混入特质,扩展目标类的功能
我们执行一个动态混入对象的方法,其执行顺序是怎样的?
顺序是,(1)从右到左开始执行 , (2)当执行到 super 时,是指的左边的特质 (3) 如果左边没有特质
了,则 super 就是父特质
如果想要调用具体特质的方法,可以指定:super[特质].xxx(…).其中的泛型必须是该特质的
直接超类类型
调用当前类的超类构造器
第一个特质的父特质构造器
第一个特质构造器
第二个特质构造器的父特质构造器, 如果已经执行过, 就不再执行
第二个特质构造器
…重复 4,5 的步骤(如果有第 3 个,第 4 个特质)
当前类构造器
调用当前类的超类构造器
当前类构造器
第一个特质构造器的父特质构造器
第一个特质构造器.
第二个特质构造器的父特质构造器, 如果已经执行过,就不再执行
第二个特质构造器
…重复 5,6 的步骤(如果有第 3 个,第 4 个特质)
当前类构造器
第 1 种方式实际是构建类对象, 在混入特质时,该对象还没有创建。
第 2 种方式实际是构造匿名子类,可以理解成在混入特质时,对象已经创建了
类似于特性继承类,但限制了特质混入的类的类型
class Eat{
def eat():Unit= println("吃东西")
}
trait EatBone {//特质,吃骨头
this:Eat=> //明确就是Eat,要求混入该特质的类也是Eat
override def eat(): Unit = this.eat()
}
class Dog extends Eat with EatBone
//方式1: 外部类名.this.属性名
class ScalaOuterClass {
class ScalaInnerClass{
def fun(): Unit ={
var inName=ScalaOuterClass.this.name
}
}
var name= "hao"
private var sal = 100.23
}
//方式2:外部类名别名.属性名
class ScalaOuterClass {
outer=> //这里我们可以这里理解 外部类的别名 看做是外部类的一个实例
class ScalaInnerClass{
def fun(): Unit ={
var inName=ScalaOuterClass.this.name
var inName2=outer.name
}
}
var name= "hao"
private var sal = 100.23
}
scala和java不用,scala的内部类是外部类对象的成员,所以若新建两个外部类的对象a1,a2,则两个对象创建的内部类a1.B和a2.B是不同的,此时可以使用类型投影(Outer#Inner)表示任何Outer中的Inner
class ScalaInnerClass {
def fun(): Unit = {
var inName = ScalaOuterClass.this.name
var inName2 = outer.name
}
}
def fun2(ic: ScalaOuterClass#ScalaInnerClass): Unit = {
println(ic)
}
var name = "hao"
private var sal = 100.23
}
object ScalaOuterClass {
def apply(): ScalaOuterClass = new ScalaOuterClass()
def main(args: Array[String]): Unit = {
val a = ScalaOuterClass()
ScalaOuterClass().fun2(new a.ScalaInnerClass)
}
}