一、类定义、创建对象
Scala和Java一样,用class关键字来创建类,eg:
class Student{
var score:Int=0
}
利用javap -private Student命令可以看到上述Scala文件对应的字节码文件内容为:
可以看到,编译后成员变量score前面的限定符为private ,Scala默认生成了score()和score_=()方法,JVM由于不允许出现=,所以用$eq代替了。生成的score()对应Java中的setter方法,score_=()对应Java中的getter方法,生成的字节码文件中setter和getter方法是公有的。因此可以直接用new来创建对象。
val stu = new Student()
t.score_=20 //调用setter方法
t.score=20 //直接修改,但其实也是调用setter方法
t.score //调用getter方法
当然也可以定义自己的setter,getter方法,如下
class Student{
private var privateScore:Int=0
def score=privateScore
def score_=(score:Int){
this.privateScore=score
}
}
对应二进制文件如下:
可以看到定义的私有成员其自带的setter和getter方法被private修饰,而我们自己的定义的setter和getter方法可以被访问
val stu=new Student( )
stu.score //访问自定义的getter方法
stu.score = 30 //直接赋值法,其实调用的是setter方法
stu.score_ = 30 //setter方法
通过stu.score=30的这种方法赋值,调用者无需知道是通过字段访问还是方法调用来操作的,这就是著名的统一访问原则
如果类的成员域是val类型的变量,则只会生成getter方法
class Student { val score:Int=0 }
对应二进制文件如下:
可以看出:val变量对应的Java中final类型的变量,只生成了getter方法,如果将成员域定义为private[this],则不会生成getter、setter方法
class Student { private[this] var score:Int=0 }
对应二进制文件如下:
Java中,定义JavaBean时,生成的是setXxx()和getXxx()方法,但scala生成的getter()和setter()方法并不是这样的,如果程序也需要自动生成getter和setter方法,需要引入scala.reflect.BeanProperty,然后采用注解的方式修饰变量
class Student { @BeanProperty var score:Int=0 }
对应二进制文件如下:
下图是setter、getter方法的产生规则
来源:scala for the impatient
二、类的主构造器
Student.scala文件
class Student(var name:String,var age:Int) //括号里的变量为主构造器的参数
编译后的二进制文件
主构造器会执行类中方法外的所有语句
class Student(var name: String, var age: Int) { //println将作为主构建器中的一部分,在创建对象时被执行 println("被主构造器执行的代码 ........") //重写toString()方法 override def toString: String = name + ":" + age def ceshi()={ print("不输出") } }
二进制文件如下:
主构造器还可以使用默认参数
class Student(val name:String="name",val score:Int=60){ println("默认参数主构造器中的代码") val age:Int=10 override def toString: String = name + ":" + age }
二进制文件如下:
主构造器中的参数还可以加访问修饰符
class Student(val name:String="name",private val score:Int=60){ println("主构造器中的参数加访问修饰符") val age:Int=10 override def toString: String = name + ":" + age }
二进制文件如下:可以看出private修饰的score,其getter方法是私有的,外部不能访问
当主构造器的参数不用var或val修饰的时候,参数会生成类的私有val成员,并且不会产生getter和setter方法
class Student(name:String,score:Int){ println("主构造器中的参数不用var和val修饰") val age:Int=10 override def toString: String = name + ":" + age }
二进制文件如下:
主构造器参数不加var和val相当于如下代码:
class Student(private[this] val name:String,private[this] val score:Int){ println("主构造器中的参数不用var和val修饰相当于参数前面加private[this]") val age:Int=10 override def toString: String = name + ":" + age }
二进制文件如下:
下图给出了Scala中主构造器参数生成器类成员和方法时的规则
来源:scala for the impatient
在某些情况下,可能需要禁用主构造器,代码如下:
//类名后面紧跟private关键字可以将主构造器设为私有,不允许外部使用
class Student private(var name:String,var score:Int){ println("禁用主构造器中的参数") val age:Int=10 }
二进制代码如下:
从上可以看出,编译后的文件主构造器被private修饰了,外部不能访问
三、辅助构造函数
当禁用了主构造器,就必须使用辅助构造函数来创建对象。辅助构造函数具有以下两个特点:(1)辅助构造器名称为this(2)调用辅助构造函数时,必须先调用主构造函数或其他已经定义好的构造函数。
1、只有辅助构造函数的Student类,注意:在定义辅助构造函数时,需要注意构造函数的顺序,不要将在构造函数中调用之前未定义的辅助构造函数,否则会发声编译错误
class Student { private var name: String = null private var age: Int = 20 private var sex: String = "man" //辅助构造器 def this(name:String){ this() this.name=name } def this(name:String,age:Int){ this(name) this.age=age } def this(name:String,age:Int,sex:String){ this(name,age) this.sex=sex } }
二进制文件如下:
2、带主构造函数、辅助构造函数的类
class Student(name: String,age: Int) { private var sex:String="man" //辅助构造器 def this(name:String,age:Int,sex:String){ this(name,age) this.sex=sex } }
二进制文件如下:
之前提到过,有时候可能会禁用掉主构造器函数,此时只能通过辅助构造器函数来创建对象
class Student private(name: String,age: Int) { private var sex:String="man" //辅助构造器 def this(name:String,age:Int,sex:String){ this(name,age) this.sex=sex } }
二进制文件如下:
4、单例对象
Scala中,如果我们想像Java中一样直接使用类名来调用方法或成员变量的时候,需要创建单例对象来实现,如下
object Student{ private var sex:String="man" def describle()={ println("我是单例对象中的成员方法") } def main(args: Array[String]): Unit = { Student.describle() } }
object Student编码后将生成两个字节码文件,Student$.class和Student.class
Student$.class编译后的二进制文件如下:
Student.class编译后的二进制文件如下:
可以看出,object Student最终生成了两个类,分别是Student和Student$,它们都是final类型的,而且Student的构造方法是私有的,通过静态成员域public static final test.Student$ MODULES$对Student进行引用,这其实Java语言中单例实现方式
5、伴生类和伴生对象
在同一个Scala文件中,存在相同名称的object和class,此时object 称为class的伴生对象,而class称为object的伴生类,伴生类和伴生对象本质上是不同的两个类,只不过它们两个之间可以互相访问对方的任何成员变量和方法,包括私有的。
6、apply方法
利用apply方法可以直接利用类名创建对象,比如说创建集合,val list=List("A","B","C"),这其实就是调用
val list=List.apply("A","B","C"),只不过第一种方法更简洁,但需要指出的是,这种创建方式仍然免不了new,它后面仍是new的方式,只不过我们在使用的时候可以省去new关键字
7、应用程序对象
我们都知道,main方法是程序的入口,但scala可以通过继承App来省去定义main方法,此时代码更加简洁,App其实是一种trait,它帮助定义了main方法
8、抽象类
抽象类不能被实例化,其中定义了抽象方法,如果要具体实现需要被非抽象子类继承来实现。除抽象方法外,抽象类中还可以有抽象字段。