前言:
类和对象的相关知识比较多,笔者分为两篇来介绍,本篇即第一篇主要介绍类定义及对象创建、getter/setter、类主构造器、辅助构造器。
1.类定义及创建对象
1.1 类
定义类
// 采用关键字class定义
class Person {
// 类成员必须初始化,否则会报错
// 这里定义的是一个公有成员
var name:String=null
}
Person类编译后会生成Person.class文件,使用 javap -p Person 命令反编译代码
D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Person
警告: 二进制文件Person包含com.harvey.classandobject.Person
Compiled from "Person.scala"
public class com.harvey.classandobject.Person {
private java.lang.String name;
public java.lang.String name();
public void name_$eq(java.lang.String);
public com.harvey.classandobject.Person();
}
从字节码文件内容可以看到:虽然我们只在Person类中定义了一个类成员(域)name,类型为String,但Scala会默认帮我们生成name()与name_=()及构造函数Person()。其中name()对应java中的getter方法,name_=()对应java中的setter方法(由于JVM中不允许出现=,所以用$eq代替。值得注意的是定义的是公有成员,但生成的字节码中却是以私有的方式实现的,生成的getter、setter方法是公有的。
Scala中定义的类同Java一样,有一个默认的构造方法。因此,可以直接new操作创建Person对象。
val person = new Person()
person.name_=("tom") // setter 方法
println(person.name) // getter 方法
person.name = "jonh" // 直接修改,其实调用的是person.name_=("jonh")
println(person.name) // getter 方法
1.2 getter setter
Scala 中可以定义自己的getter和setter方法,修改Person类代码如下
class Person {
private var privateName: String = null // 定义私有成员
def name = this.privateName // getter 方法
def name_=(name: String): Unit = { // setter 方法
this.privateName = name
}
}
字节码文件
D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Person
警告: 二进制文件Person包含com.harvey.classandobject.Person
Compiled from "Person.scala"
public class com.harvey.classandobject.Person {
private java.lang.String privateName;
private java.lang.String privateName();
private void privateName_$eq(java.lang.String);
public java.lang.String name();
public void name_$eq(java.lang.String);
public com.harvey.classandobject.Person();
}
上面我们定义了一个私有变量(private修饰),Scala会默认帮我们生成getter和setter方法,但是他们也是私有的,能直接使用的是我们自定义的getter和setter方法,如下
val person = new Person()
println(person.name) // null
person.name = "harvey"
println(person.name) // harvey
我们知道Scala中变量的修饰有两种,上面用的都是var,如果使用val修饰变量,scala则只会生成getter方法,如下
class Person {
val name:String=null
}
字节码文件
D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Person
警告: 二进制文件Person包含com.harvey.classandobject.Person
Compiled from "Person.scala"
public class com.harvey.classandobject.Person {
private final java.lang.String name;
public java.lang.String name();
public com.harvey.classandobject.Person();
}
如果将成员域定义为private[this],那么这个字段是对象私有的,这种情况下,不会生成getter和setter。对象私有字段,只能由当前对象的方法访问,而该类的其他对象的方法是无法访问的
class Person {
private[this] val name:String=null
}
字节码文件:
D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Person
警告: 二进制文件Person包含com.harvey.classandobject.Person
Compiled from "Person.scala"
public class com.harvey.classandobject.Person {
public com.harvey.classandobject.Person();
}
Scala中可以使用private[class-name]来指定可以访问该字段的类,class-name必须是当前定义的类,或者是当前定义的类的外部类。这种情况会生成getter和setter方法。
1.3 Bean 属性
Java中我们定义的JavaBean生成的都是getXXX()、setXXX()方法,Scala中生成的getter()和setter()不是这样的(上述示例中可以看出),如果也想和Java中一样,需要引入BeanProperty,然后使用采用注解的方式修饰变量。使用该注解后,将会生成4个方法:Scala的getter/setter和JavaBeans规范的getter/setter(如果是val声明,就没有setter部分了)
// 在Scala 2.10.0之后已被废弃
// import scala.reflect.BeanProperty
import scala.beans.BeanProperty
class Person {
@BeanProperty var name:String = "john"
}
字节码文件
D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Person
警告: 二进制文件Person包含com.harvey.classandobject.Person
Compiled from "Person.scala"
public class com.harvey.classandobject.Person {
private java.lang.String name;
public java.lang.String name();
public void name_$eq(java.lang.String);
public void setName(java.lang.String);
public java.lang.String getName();
public com.harvey.classandobject.Person();
}
2.类主构造器
2.1 主构造器定义
2.1.1 有参主构造器
主构造器的定义与类的定义交织在一直,将构造器参数直接放在类名称之后,如下代码
// 下列代码定义了类Person,还定义了参数为String、Int类型的主构造器
class Person (val name: String, val age: Int)
字节码文件
D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Person
警告: 二进制文件Person包含com.harvey.classandobject.Person
Compiled from "Person.scala"
public class com.harvey.classandobject.Person {
private final java.lang.String name;
private final int age;
public java.lang.String name();
public int age();
public com.harvey.classandobject.Person(java.lang.String, int);
}
上述代码使用Java语言编写如下
public class Person{
private final String name;
private final int age;
public Person(String name,int age){
this.name=name;
this.age=age;
}
public String getName(){ return name}
public int getAge() {return age}
}
具体操作
object Test {
def main(args: Array[String]): Unit = {
val person = new Person("tom", 18)
println("name=" + person.name + ",age=" + person.age) // 运行结果:name=tom,age=18
}
}
2.1.2 无参主构造器
Scala中主构造器的定义可以有参数,也可以无参数,如下代码
// 下列代码定义了类Person,无参主构造器
class Person
字节码文件
D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Person
警告: 二进制文件Person包含com.harvey.classandobject.Person
Compiled from "Person.scala"
public class com.harvey.classandobject.Person {
public com.harvey.classandobject.Person();
}
具体操作
object Test {
def main(args: Array[String]): Unit = {
val person = new Person() // 创建Person的实例
}
}
2.1.3 使用主构造器进行初始化操作
主构造器会执行类中定义的所有语句,如在创建对象时,需要进行相关初始化操作时,可以将初始化语句放在类体中,同样也可以在类中添加或重写相关方法
class Person(val name: String, val age: Int) {
println("init opertion...")
def show(): Unit = {
println("Hello Scala")
}
// 重写toString()方法
override def toString() = "name = " + name + ",age = " + age
}
具体操作
object Test {
def main(args: Array[String]): Unit = {
val person = new Person("tom", 18)
person.show()
println(person.toString())
}
}
运行结果
init opertion...
Hello Scala
name = tom,age = 18
2.2 主构造器参数
上面我们定义了有参主构造器,主构造器参数可以有默认值,如下代码
class Person(val name: String = "", val age: Int = 18) {
}
具体操作
object Test {
def main(args: Array[String]): Unit = {
// 使用主构造参数的默认值
val person1 = new Person
println("name = " + person1.name + ",age = " + person1.age)
// 指定参数值
val person2 = new Person("john", 20)
println("name = " + person2.name + ",age = " + person2.age)
}
}
2.3 主构造器参数访问控制
主构造器中的参数是可以加访问控制符的,我们先来看下不加参数情况
class Person(name: String, age: Int) {
override def toString()= "name = " + name + ",age = " + age
}
等同于如下代码
class Person(private[this] val name: String,private[this] val age: Int) {
override def toString()= "name = " + name + ",age = " + age
}
字节码文件
D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Person
警告: 二进制文件Person包含com.harvey.classandobject.Person
Compiled from "Person.scala"
public class com.harvey.classandobject.Person {
private final java.lang.String name;
private final int age;
public java.lang.String toString();
public com.harvey.classandobject.Person(java.lang.String, int);
}
默认参数的主构建器,参数带访问控制符号
从字节码文件中可以看到,当主构造器的参数不用var或val修饰的时候,参数会生成类的私有val成员,并且不会产生getter和setter方法
需要注意的是,将上述Person类中的toString()方法去掉,则类中无任何地方使用了主构造器的参数,此时主构造器参数不会生成类成员,修改代码如下
class Person(name: String, age: Int) {
}
字节码文件
D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Person
警告: 二进制文件Person包含com.harvey.classandobject.Person
Compiled from "Person.scala"
public class com.harvey.classandobject.Person {
public com.harvey.classandobject.Person(java.lang.String, int);
}
2.4 禁用主构造器
在某些情况下,可能需要禁用主构建器,代码如下
class Person private(var name: String, var age: Int) {
}
字节码文件
D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Person
警告: 二进制文件Person包含com.harvey.classandobject.Person
Compiled from "Person.scala"
public class com.harvey.classandobject.Person {
private java.lang.String name;
private int age;
public java.lang.String name();
public void name_$eq(java.lang.String);
public int age();
public void age_$eq(int);
private com.harvey.classandobject.Person(java.lang.String, int);
}
3.辅助构造器
前面讲了,如果禁用掉了主构建器,则必须使用辅助构造函数来创建对象。辅助构造函数具有两个特点:
(1). 辅助构建器的名称为this,java中的辅助构造函数与类名相同,这常常会导致修改类名时出现不少问题,scala语言避免了这样的问题
(2). 调用辅助构造函数时,必须先调用主构造函数或其它已经定义好的构造函数。
3.1 类中只有辅助构造函数
如下Person类中只有辅助构造函数
class Person {
// 类成员,私有
private var name: String = null;
private var age: Int = 18;
private var gender: Int = 0;
// 辅助构造器
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, gender: Int) {
this(name, age)
this.gender = gender
}
}
字节码文件
D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Person
警告: 二进制文件Person包含com.harvey.classandobject.Person
Compiled from "Person.scala"
public class com.harvey.classandobject.Person {
private java.lang.String name;
private int age;
private int gender;
private java.lang.String name();
private void name_$eq(java.lang.String);
private int age();
private void age_$eq(int);
private int gender();
private void gender_$eq(int);
public com.harvey.classandobject.Person();
public com.harvey.classandobject.Person(java.lang.String);
public com.harvey.classandobject.Person(java.lang.String, int);
public com.harvey.classandobject.Person(java.lang.String, int, int);
}
在定义辅助构造函数时,需要注意构造函数顺序,如下代码
class Person{
// 类成员,私有
private var name:String=null
private var age:Int=18
private var sex:Int=0
// 辅助构造器
def this(name:String,age:Int,sex:Int){
this(name,age) // 此处会发生编译错误,这是因为def this(name:String,age:Int)没有被定义
this.sex=sex
}
def this(name:String){
this()
this.name=name
}
def this(name:String,age:Int){
this(name)
this.age=age
}
}
3.2 类中既有主构造器也有辅助构造函数
如下定义的Person类中,既有主构造器也有辅助构造函数
// 主构造器
class Person(var name: String, var age: Int) {
// 类成员,私有
private var gender: Int = 0
// 辅助构造器
def this(name: String, age: Int, gender: Int) {
this(name, age)
this.gender = gender
}
}
字节码文件
D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Person
警告: 二进制文件Person包含com.harvey.classandobject.Person
Compiled from "Person.scala"
public class com.harvey.classandobject.Person {
private java.lang.String name;
private int age;
private int gender;
public java.lang.String name();
public void name_$eq(java.lang.String);
public int age();
public void age_$eq(int);
private int gender();
private void gender_$eq(int);
public com.harvey.classandobject.Person(java.lang.String, int);
public com.harvey.classandobject.Person(java.lang.String, int, int);
}
3.3 禁用主构造器,使用辅助构造函数创建对象
上述主构造器中我们有提到,主构造器是有可能被禁用的,此时是能通过辅助构造函数来创建对象
// 禁用主构造器
class Person private(var name: String, var age: Int) {
// 类成员,私有
private var gender: Int = 0
// 辅助构造器
def this(name: String, age: Int, gender: Int) {
this(name, age)
this.gender = gender
}
}
字节码文件
D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Person
警告: 二进制文件Person包含com.harvey.classandobject.Person
Compiled from "Person.scala"
public class com.harvey.classandobject.Person {
private java.lang.String name;
private int age;
private int gender;
public java.lang.String name();
public void name_$eq(java.lang.String);
public int age();
public void age_$eq(int);
private int gender();
private void gender_$eq(int);
private com.harvey.classandobject.Person(java.lang.String, int);
public com.harvey.classandobject.Person(java.lang.String, int, int);
}