8.1 类和对象
8.1.1 定义类
基本语法:
[修饰符] class 类名 {
类体
}
注意:
scala 语法中,类并不声明为
public
,所有这些类都具有公有可见性(即默认就是public
)一个 Scala 源文件可以包含多个类
8.1.2 定义属性
属性是类的一个组成部分,一般是值数据类型,也可以是引用类型。
基本语法:
[修饰符] var 属性名称 [:类型] = 属性值
例如:
package com.atguigu.day03
object FunDemo4 {
def main(args: Array[String]): Unit = {
val p1 = new Person()
p1.teacher = new Teacher()
println(p1.age)
println(p1.teacher.name)
}
}
class Person {
// 声明属性, 而且必须给该属性赋值
var age = 10
// 属性可以设置默认值:使用 _
// 数值型的默认值: 0 布尔型的默认值是 false, 引用数据类型的默认值是 null
var teacher: Teacher = _
}
class Teacher {
var name = "大海哥"
}
8.1.3 Bean 属性
JavaBeans 规范定义了 Java 的属性是像getXxx()
和setXxx()
的方法。许多Java 工具(框架)都依赖这个命名习惯。
为了与Java的互操作性。将 Scala 字段加@BeanProperty
时,这样会自动生成规范的setXxx/getXxx
方法。
这时可以使用对象.setXxx()
和对象.getXxx()
来调用属性。
package com.atguigu.day03
import scala.beans.BeanProperty
object FunDemo4 {
def main(args: Array[String]): Unit = {
val p1 = new Person()
p1.teacher = new Teacher()
println(p1.getAge())
p1.setAge(100)
println(p1.getAge)
}
}
class Person {
@BeanProperty
var age = 10
var teacher: Teacher = _
}
class Teacher {
var name = "大海哥"
}
注意:
- 访问方法的时候要不要带圆括号? 一般遵循这样的约定: 访问
getXxx
的时候一般不带括号, 访问setXxx(参数)
的时候一般要带圆括号.
8.1.4 创建对象
基本语法:
val | var 对象名 [:类型] = new 类型()
说明:
- 如果我们不希望改变对象的引用(即:内存地址),应该声明为
val
性质的,否则声明为var
, - scala 设计者推荐使用
val
,因为一般来说,在程序中,我们只是改变对象属性的值,而不是改变对象的引用。
8.1.5 方法
Scala 中的方法其实就是函数,只不过一般将对象中的函数称之为方法。声明规则请参考函数式编程中的函数声明。
8.1.6 构造器
和 Java 一样,Scala 构造对象也需要调用构造方法,并且可以有任意多个构造方法。
Scala 类的构造器包括:
基本语法:
class 类名(形参列表) { // 主构造器
// 类体
def this(形参列表) { // 辅助构造器
};
def this(形参列表) { //辅助构造器可以有多个...
}
}
说明:
主构造器和 Java 有很大不同. 主构造器位于类名之后.
辅构造器和 Java 类似. 只是在 Scala 中辅构造器的名字统一用
this
来表示.主构造器只能有一个, 辅构造器可以有多个.
在创建对象的时候, 和 Java 一样也是通过传入的参数来选择构造器.
调用主构造器的时候, 会执行类中的所有语句(当然不包括类中定义的方法.)
案例1: 调用主构造函数
package com.atguigu.day04
object ObjDemo {
def main(args: Array[String]): Unit = {
val p = new Person("李四", 20)
println(p.name + " " + p.age)
}
}
class Person(inName: String, inAge: Int) { // 主构造器
var name = inName
var age = inAge
// 类内也可以直接写代码,在调用主构造函数的时候会执行.
// 有点类似 Java 中的构造代码块
println("ok")
}
案例2: 辅构造函数
package com.atguigu.day04
object ObjDemo {
def main(args: Array[String]): Unit = {
val p = new Person("李四", 30)
println(p.name + " " + p.age)
}
}
class Person { // 主构造器如果没有参数可以省略圆括号
var name: String = _
var age: Int = _
println("ok")
// 辅构造器
def this(name: String) {
// 构造器第一行必须要能调用到主构造器
this()
this.name = name
}
// 辅构造器
def this(name: String, age: Int) {
this(name)
this.age = age
}
def sayHello(): Unit = {
println("hello: " + this.name)
}
}
说明:
如果不想让外界通过主构造器创建对象, 可以把主构造器私有: 在类名和圆括号中间添加一个关键字
private
辅构造器仍然可以调用私有的主构造器. 私有之后只是不能在外界调用而已.
8.1.7 构造器的形参的说明
Scala 中类的构造器的形参相比 Java 的构造器的形参具有更加灵活的功能
1. 普通形参(未添加任何修饰的形参)
未添加任何修饰的构造函数的形参, 在能访问到的范围内就是一个普通的局部变量
- 主构造函数的形参在整个类的内部都是当做局部变量使用
- 辅构造函数的形参只在当前构造函数内有效
package com.atguigu.day04
object ObjDemo1 {
def main(args: Array[String]): Unit = {
val p = new Stu("李四")
p.sayHello()
}
}
// 对主构造器来说, 未添加任何修饰的构造函数的形参, 在能访问到的范围内就是一个普通的局部常量
// 在整个主构造器内部都只能读取不能修改, 而且在内部也可以通过`this`来读取
// 观看反编译后的`class`, 发现会创建私有的属性, 并且没有给外界提供任何访问的方式
class Stu(name : String) {
println(name)
def sayHello(): Unit ={
println("sayHello:" + name)
}
}
2. 给形参添加var
修饰
给形参添加val
修饰, 那么这个时候的形参就是类的属性. 在类的内部可有直接属性名
访问, 也可以this.属性名
访问
在类的外部也可以访问.
package com.atguigu.day04
object ObjDemo1 {
def main(args: Array[String]): Unit = {
val p = new Stu("李四")
p.sayHello()
}
}
class Stu(var name : String) {
println(name)
this.name = "lisi"
println(name)
println(this.name)
def sayHello(): Unit ={
println("sayHello:" + this.name)
}
}
3. 给形参添加val
修饰
给形参添加val
修饰, 这个时候的形参就是一个只读的属性.
package com.atguigu.day04
object ObjDemo1 {
def main(args: Array[String]): Unit = {
val p = new Stu("李四")
println("外部p.name: " + p.name)
p.sayHello();
}
}
class Stu(val name : String) {
// this.name = "lisi" // 会报错
println(name)
println(this.name)
def sayHello(): Unit ={
println("sayHello:" + this.name)
}
}
4. 再添加@BeanProperty
再添加@BeanProperty
, 就会给属性添加相应的setter
和getter
方法.(如果是val
的则只有getter
方法)
package com.atguigu.day04
import scala.beans.BeanProperty
object ObjDemo1 {
def main(args: Array[String]): Unit = {
val p = new Stu("李四")
println(p.getName)
p.setName("zs")
println(p.getName)
}
}
class Stu(@BeanProperty var name : String) {
}
8.1.8 对象创建流程分析
class Person {
var age: Int = 90
var name: String = _
def this(n: String, a: Int) {
this()
this.name = n
this.age = a
}
}
var p: Person = new Person("小倩", 20)
流程分析(面试题)
加载类信息(属性信息,方法信息)
在堆中,给对象开辟空间
调用主构造器对属性进行初始化
使用辅助构造器对属性进行初始化
把对象空间的地址,返回给
p
引用
8.1.9 查看 Scala 生成的字节码
# 编译 scala 文件
scalac Person.scala
# 查看编译后的字节码 -private 表示也查看私有的成员
javap -private Person
8.1.10 给类起别名
在使用一个大型类库写代码的时候你也许会遇到类名不符合自己心意的情况。类名要么 太长要么不灵巧,或者你只是觉得有一个更好的名字能够表达这种抽象。你拥有这种取别名 的自由,可以给一个类取一个赏心悦目的名字。
// 这是一个很长的类名, 将来使用的时候书写很不方便
class Person2Student
object Person {
def main(args: Array[String]): Unit = {
// 起个别名
type p2s = Person2Student
println(new p2s().getClass)
}
}
说明:
虽然起了别名, 但是并没有改变类的真实类型.
别名只对当前作用域有效
- scala 标准库中很多类都起了别名. 例如,
Set
就是一个别名,它指向immutable
包中的Set
版本, 而不是mutable
包中的版本。