
1.5 类和对象
1.5.1 类的定义
我们已经知道,对象是类的具体实例,类是抽象的,不占用内存,而对象是具体的,占用存储空间。
Scala中一个简单的类定义是使用关键字class,类名必须大写。类中的方法用关键字def定义,例如以下代码:

如果一个类不写访问修饰符,那么默认访问级别为Public,这与Java是不一样的。
关键字new用于创建类的实例。例如,调用上述代码中的count()方法,可以使用以下代码:
new User().count()
1.5.2 单例对象
Scala中没有静态方法或静态字段,但是可以使用关键字object定义一个单例对象,单例对象中的方法相当于Java中的静态方法,可以直接使用“单例对象名.方法名”方式进行调用。单例对象除了没有构造器参数外,可以拥有类的所有特性。
例如以下代码定义了一个单例对象Person,该对象中定义了一个方法showInfo():

可以在任何类或对象中使用代码Person.showInfo()对方法showInfo()进行调用。
1.5.3 伴生对象
当单例对象的名称与某个类的名称一样时,该对象被称为这个类的伴生对象。类被称为该对象的伴生类。
类和它的伴生对象必须定义在同一个文件中,且两者可以互相访问其私有成员。例如以下代码:

运行上述伴生对象Person的main方法,输出结果如下:
姓名:zhangsan 年龄:20
1.5.4 get和set方法
Scala默认会根据类的属性的修饰符生成不同的get和set方法,生成原则如下:
• val修饰的属性,系统会自动生成一个私有常量属性和一个公有get方法。
• var修饰的属性,系统会自动生成一个私有变量和一对公有get/set方法。
• private var修饰的属性,系统会自动生成一对私有get/set方法,相当于类的私有属性,只能在类的内部和伴生对象中使用。
• private[this]修饰的属性,系统不会生成get/set方法,即只能在类的内部使用该属性。
例如有一个Person类,代码如下:
class Person { val id:Int=10 var name="zhangsan" private var gender:Int=0 private[this] var age:Int=20 }
将该类编译为class文件后,再使用Java反编译工具将其反编译为Java代码,代码如下:

使用name属性举例,在Scala中,get和set方法并非被命名为getName和setName,而是被命名为name和name_=,由于JVM不允许在方法名中出现=,因此=被翻译成$eq。
从上述代码可以看出,由于属性id使用val修饰,因此不可修改,只生成了与get方法对应的id();属性name使用var修饰,因此生成了与get和set方法对应的name()和name_$eq()方法,且都为public;属性gender由于使用private var修饰,因此生成了private修饰的get和set方法;属性age由于使用private[this]修饰,因此没有生成get和set方法,只能在类的内部使用。
此时可以使用如下代码对Person类中的属性进行访问:

除了系统自动生成get和set方法外,也可以手动进行编写,例如以下代码:

当然也可以使用如下Java风格定义get和set方法:

1.5.5 构造器
Scala中的构造器分为主构造器和辅助构造器。
1.主构造器
主构造器的参数直接放在类名之后,且将被编译为类的成员变量,其值在初始化类时传入。例如以下代码:

可以通过对主构造器的参数添加访问修饰符来控制参数的访问权限。例如以下代码,将参数age设置为私有的,参数name设置为不可修改(val):
class Person(val name:String, private var age:Int) { }
构造参数也可以不带val或var,此时默认为private[this] val,代码如下:
class Person(name:String,age:Int) { }
在主构造器被执行时,类定义中的所有语句同样会被执行。例如以下代码中的println语句是主构造器的一部分,每当主构造器被执行时,该部分代码同样会被执行,可以在这里做一些类的初始化工作:
class Person(var name:String,var age:Int) { println(name) println(age) //初始化语句... }
如果需要将整个主构造器设置为私有的,那么只需要添加private关键字即可,例如以下代码:
class Person private(var name:String,var age:Int) { }
注意,主构造器也可以没有参数,一个类中如果没有显式地定义主构造器,就默认有一个无参构造器。
2.辅助构造器
Scala类除了可以有主构造器外,还可以有任意多个辅助构造器。辅助构造器的定义需要注意以下几点:
• 辅助构造器的方法名称为this。
• 每一个辅助构造器的方法体中必须首先调用其他已定义的构造器。
• 辅助构造器的参数不能使用var或val进行修饰。
例如以下代码定义了两个辅助构造器:

上述构造器可以使用如下3种方式进行调用:
var per1=new Person //调用无参主构造器 var per2=new Person("lisi") //调用辅助构造器一 var per3=new Person("lisi",28) //调用辅助构造器二
除此之外,主构造器还可以与辅助构造器同时使用,在这种情况下,一般辅助构造器的参数要多于主构造器,代码如下:

上述代码运行的输出结果为:
zhangsan 20 male