我们已经知道,对象是类的具体实例,类是抽象的,不占用内存;而对象是具体的,占用存储空间。
Scala中一个简单的类定义是使用关键字class,类名必须大写。类中的方法用关键字def定义,例如以下代码:
class User{ private var age=20 def count(){ age+=1 } }
如果一个类不写访问修饰符,那么默认访问级别为Public,这与Java是不同的。
关键字new用于创建类的实例。例如,调用上述代码中的count()方法,可以使用以下代码:
new User().count()
Scala中没有静态方法或静态字段,但是可以使用关键字object定义一个单例对象。单例对象中的方法相当于Java中的静态方法,可以直接使用“单例对象名.方法名”的方式进行调用。单例对象除了没有构造器参数外,可以拥有类的所有特性。
例如以下代码定义了一个单例对象Person,该对象中定义了一个方法showInfo():
object Person{ private var name="zhangsan" private var age=20 def showInfo():Unit={ println("姓名:"+name+",年龄:"+age) } }
可以在任何类或对象中使用代码Person.showInfo()对方法showInfo()进行调用。
当单例对象的名称与某个类的名称一样时,该对象被称为这个类的伴生对象。类被称为该对象的伴生类。
类和它的伴生对象必须定义在同一个文件中,且两者可以互相访问其私有成员。例如以下代码:
class Person() { private var name="zhangsan" def showInfo(){ //访问伴生对象的私有成员 println("年龄:"+Person.age) } } object Person{ private var age=20 def main(args: Array[String]): Unit = { var per=new Person() //访问伴生类的私有成员 println("姓名:"+per.name) per.showInfo() } }
运行上述伴生对象Person的main方法,输出结果如下:
姓名:zhangsan 年龄:20
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代码,代码如下:
public class Person{ private final int id = 10; public int id() { return this.id; } private String name = "zhangsan"; public String name() { return this.name; } public void name_$eq(String x$1) { this.name = x$1; } private int gender = 0; private int gender() { return this.gender; } private void gender_$eq(int x$1) { this.gender = x$1; } private int age = 20; }
使用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类中的属性进行访问:
object test{ def main(args: Array[String]): Unit = { var per:Person=new Person() per.name="lisi" println(per.id) println(per.name) //将调用方法per.name() per.id=20 //错误,不允许修改 } }
除了系统自动生成get和set方法外,也可以手动进行编写,例如以下代码:
class Person { //声明私有变量 private var privateName="zhangsan" //定义get方法 def name=privateName //定义set方法 def name_=(name:String): Unit ={ this.privateName=name } } object test{ def main(args: Array[String]): Unit = { var per:Person=new Person() //访问变量 per.name="lisi" //修改 println(per.name) //读取 } }
当然也可以使用如下Java风格定义get和set方法:
class Person { //声明私有变量 private var name="zhangsan" //定义get方法 def getName(): String ={ this.name } //定义set方法 def setName(name:String): Unit ={ this.name=name } } object test{ def main(args: Array[String]): Unit = { var per:Person=new Person() //访问属性 per.setName("wangwu") println(per.getName()) } }
Scala中的构造器分为两种:主构造器和辅助构造器。
主构造器的参数直接放在类名之后,且将被编译为类的成员变量,其值在初始化类时传入。例如以下代码:
//定义主构造器,age默认为18 class Person(val name:String,var age:Int=18) { } object Person{ def main(args: Array[String]): Unit = { //调用构造器并设置name和age字段 var per=new Person("zhangsan",20) println(per.age) println(per.name) per.name="lisi"//错误,val修饰的变量不可修改 } }
可以通过对主构造器的参数添加访问修饰符来控制参数的访问权限。例如以下代码,将参数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) { }
主构造器也可以没有参数,一个类中如果没有显式地定义主构造器,就默认有一个无参构造器。
Scala类除了可以有主构造器外,还可以有任意多个辅助构造器。辅助构造器的定义需要注意以下几点:
· 辅助构造器的方法名称为this。
· 每一个辅助构造器的方法体中必须首先调用其他已定义的构造器。
· 辅助构造器的参数不能使用var或val进行修饰。
例如以下代码定义了两个辅助构造器:
class Person { private var name="zhangsan" private var age=20 //定义辅助构造器一 def this(name:String){ this() //调用主构造器 this.name=name } //定义辅助构造器二 def this(name:String,age:Int){ this(name) //调用辅助构造器一 this.age=age } }
上述构造器可以使用如下三种方式进行调用:
var per1=new Person //调用无参主构造器 var per2=new Person("lisi") //调用辅助构造器一 var per3=new Person("lisi",28) //调用辅助构造器二
除此之外,主构造器还可以与辅助构造器同时使用,在这种情况下,一般辅助构造器的参数要多于主构造器,代码如下:
//定义主构造器 class Person(var name:String,var age:Int) { private var gender="" //定义辅助构造器 def this(name:String,age:Int,gender:String){ this(name,age) //调用主构造器 this.gender=gender } } object Person{ def main(args: Array[String]): Unit = { //调用辅助构造器 var per=new Person("zhangsan",20,"male") println(per.name) println(per.age) println(per.gender) } }
上述代码运行的输出结果为:
zhangsan 20 male