类和无参构造器
-
在Scala中,类并不用声明为public;
-
Scala源文件中可以包含多个类,所有这些类都具有公有可见性;
-
val修饰的变量(常量),值不能改变,只提供getter方法,没有setter方法;
-
var修饰的变量,值可以改变,对外提供getter、setter方法;
-
如果没有定义构造器,类会有一个默认的无参构造器;
class Person{ // Scala中声明一个字段,必须显示的初始化,然后根据初始化的数据类型自动推断其类型,字段类型可以省略 var name = "jacky" // _ 表示一个占位符,编译器会根据变量的数据类型赋予相应的初始值 // 使用占位符,变量类型必须指定 // _ 对应的默认值:整型默认值0;浮点型默认值0.0;String与引用类型,默认值null; Boolean默认值false var nickName: String = _ var age = 20 // 如果赋值为null,则一定要加数据类型,因为不加类型, 该字段的数据类型就是Null类型 // var address = null // 改为: var address:String = null; // val修饰的变量不能使用占位符 val num = 30 // 类私有字段,有私有的getter方法和setter方法, // 在类的内部可以访问,其伴生对象也可以访问 private var hobby: String = "旅游" // 对象私有字段,访问权限更加严格,只能在当前类中访问 private[this] val cardInfo = "123456" //自定义方法 def hello(message: String): Unit = { //只能在当前类中访问cardInfo println(s"$message,$cardInfo") } //定义一个方法实现两数相加求和 def addNum(num1: Int, num2: Int): Int = { num1 + num2 } }
-
类的实例化以及使用:
def main(args: Array[String]): Unit = { //创建对象两种方式,这里都是使用的无参构造器来进行创建对象的 val person = new Person() //创建类的对象时,小括号()可以省略 val person1 = new Person //给类的属性赋值 person.age = 50 //注意:如果使用对象的属性加上 _= 给var修饰的属性进行重新赋值,其实就是调用age_=这个 setter方法 person.age_=(20) //直接调用类的属性,其实就是调用getter方法 println(person.age) //调用类中的方法 person.hello("hello") val result = person.addNum(10, 20) println(result) }
自定义getter和setter方法
-
对于 Scala 类中的每一个属性,编译后会有一个私有的字段和相应的getter、setter方法生成。
//getter方法 println(person age) //setter方法 person age_= (18) //getter方法 println(person.age)
-
不使用自动生成的方式,自己定义getter和setter方法,自定义变量的getter和setter方法需要遵循以下原则:
- 字段属性名以“_”作为前缀,如: _leg
- getter方法定义为:def leg = _leg
- setter方法定义为:def leg_=(newLeg: Int)
class Dog{ private var _leg = 0 // 自定义getter方法 def leg = _leg //自定义setter方法 def leg_=(newLeg: Int) { _leg = newLeg } // 使用自定义getter和setter方法 val dog = new Dog dog.leg_=(4) println(dog.leg) }
Bean属性
-
JavaBean规范把Java属性定义为一堆getter和setter方法。
-
当将Scala字段标注为 @BeanProperty时,getFoo和setFoo方法会自动生成。
-
使用@BeanProperty并不会影响Scala自己自动生成的getter和setter方法。
-
在使用时需要导入包scala.beans.BeanProperty
object TeacherScala { class Teacher { @BeanProperty var name:String = _ } def main(args: Array[String]): Unit = { val tea: Teacher = new Teacher tea.name = "zhagnsan" tea.setName("lisi") //BeanProperty生成的setName方法 println(tea.getName) //BeanProperty生成的getName方法 // Teacher类中共生成了四个方法: // 1. name: String // 2. name_= (newValue: String): Unit // 3. getName(): String // 4. setName (newValue: String): Unit }
构造器
-
如果没有定义构造器,Scala类中会有一个默认的无参构造器;
-
Scala当中类的构造器分为两种:主构造器和辅助构造器;
-
主构造器的定义与类的定义交织在一起,将主构造器的参数直接放在类名之后。
-
当主构造器的参数不用var或val修饰时,参数会生成类的私有val成员。
-
Scala中,所有的辅助构造器都必须调用另外一个构造器,另外一个构造器可以是辅助构造器,也可以是主构造器。
object Animal { def main(args: Array[String]): Unit = { val dog1=new Dog("狗蛋",4) val dog2=new Dog("旺才",3,"雄性") val dog3=new Dog("小六",5,"雄性","黑色") } } //主构造器直接定义在类中,其代码不包含在任何方法中 //Scala中的主构造器与类名交织在一起,类名后面的参数即为主构造器的参数 class Dog(name: String, age: Int) { //类中不在任何方法中的代码,都属于主构造器的代码。 //创建类的对象时会去执行主构造器的代码。下面的println代码就是主构造器的一部分 println(name) println(age) var gender: String = "" def this(name: String, age: Int, gender: String) { //每个辅助构造器,都必须以其他辅助构造器,或者主构造器的调用作为第一句代码 this(name, age) this.gender = gender } var color = "" def this(name: String, age: Int, gender: String, color: String) { //调用上面的辅助构造器 this(name, age, gender) this.color = color } }
对象
单例对象
-
Scala并没有提供Java那样的静态方法或静态字段;可以采用object关键字实现单例对象,具备和Java静态方法同样的功能;
-
使用object语法结构【object是Scala中的一个关键字】达到静态方法和静态字段的目的;对象本质上可以拥有类的所有特性,除了不能提供构造器参数;
-
对于任何在Java中用单例对象的地方,在Scala中都可以用object实现:
- 作为存放工具函数或常量的地方
- 高效地共享单个不可变实例
object SessionFactory { val session = new Session def getSession(): Session = { session } def main(args: Array[String]): Unit = { for (x <- 1 to 10) { //通过直接调用,产生的对象都是单例的 val session = SessionFactory.getSession() println(session) } } class Session { def hello(first: Int): Int = { println(first) first } } }
-
Scala中的单例对象具有如下特点:
- 创建单例对象不需要使用new关键字
- object中只有无参构造器
- 主构造代码块只能执行一次,因为它是单例的
object ObjectDemo {
println("这是单例对象的代码!")
def main(args: Array[String]): Unit = {
val object1=ObjectDemo
val object2=ObjectDemo
}
}
伴生类与伴生对象
-
当单例对象与某个类具有相同的名称时,它被称为这个类的“伴生对象”;
-
类和它的伴生对象必须存在于同一个文件中,而且可以相互访问私有成员(字段和方法);
package com.scala.code class ClassObject { val id = 1 private var name = "lagou" def printName(): Unit ={ //在ClassObject类中可以访问伴生对象ClassObject的私有字段 println(ClassObject.CONSTANT + name ) } } object ClassObject{ //伴生对象中的私有字段 private val CONSTANT = "汪汪汪" def main(args: Array[String]) { val p = new ClassObject //访问伴生类的私有字段name p.name = "123" p.printName() } }
应用程序对象
- 每个Scala应用程序都必须从一个对象的main方法开始,这个方法的类型为 Array[String] => Unit;
- main方法写在class中是没有意义的,在IDEA中这样的 class 连run的图标都不能显示,除了main方法以外,也可以扩展App特质(trait)
object Hello extends App {
if (args.length > 0)
println(s"Hello World; args.length = ${args.length}")
else
println("Hello World")
}
apply方法
-
object 中有一个特殊方法 – apply方法;
- apply方法通常定义在伴生对象中,目的是通过伴生类的构造函数功能,来实现伴生对象的构造函数功能;
- 通常我们会在类的伴生对象中定义apply方法,当遇到类名(参数1,…参数n)时apply方法会被调用;
- 在创建伴生对象或伴生类的对象时,通常不会使用new class/class() 的方式,而是直接使用class()隐式的调用伴生对象的 apply 方法
//class Student为伴生类 class Student(name: String, age: Int) { private var gender: String = _ def sayHi(): Unit ={ println(s"大家好,我是$name,$gender 生") } } object Student { //apply方法定义在伴生对象中 def apply(name: String, age: Int): Student = new Student(name, age) def main(args: Array[String]): Unit = { //直接利用类名进行对象的创建,这种方式实际上是调用伴生对象的apply方法实现的 val student=Student("jacky",30) student.gender="男" student.sayHi() } }
-
在Scala中实现工厂方法,让子类声明哪种对象应该被创建,保持对象创建在同一位置。例如,假设要创建Animal工厂,让其返回Cat和Dog类的实例,基于这个需求,通过实现Animal伴生对象的apply方法,工厂的使用者可以像这样创建新的Cat和Dog实例。
package com.scala.code object AnimalObject { def main(args: Array[String]): Unit = { val cat = Animal("cat") cat.speak val dog = Animal("dog") dog.speak } abstract class Animal { def speak } class Dog extends Animal { override def speak: Unit = { println("woof") } } class Cat extends Animal { override def speak: Unit = { println("meow") } } object Animal { def apply(str: String): Animal = { if (str == "dog") new Dog else new Cat } } }