封装:
- 封装意指将抽象的的数据和对数据的操作封装在一起,数据被保护在内部,程序其他部分只有通过被授权的操作(成员方法),才能对数据进行操作;
- 封装的优点:①对数据细节隐藏 ② 对数据进行验证,保证操作安全合理
- scala封装的注意事项和细节:
①scala为了简化代码开发,当声明一个属性变量时(var修饰),底层会生成相应的get/set方法;如果属性省略访问修饰符,则get/set方法为public修饰的,若属性有private修饰,则get/set方法为private修饰的;
②对象.属性:底层是调用的是属性的get方法而不是直接调用属性;
继承:
- scala中继承的优点:
①提高了代码的复用性
②提高了代码的可扩展性和可维护性
- scala继承中的细节:
①子类可以继承父类所有的属性,但private修饰的属性能继承不能访问;
②重写:- 重写非抽象方法:必须显式地用override修饰,调用父类的方法用super关 键字;
- 重写(复写)字段:
- 必须用override修饰;
- val修饰的属性只能唯一重写一个val属性或重写不带参数的方法
- var属性只能重写父类抽象的var属性,此时override可以省略(抽象 类中的抽象属性在底层有对应的抽象方法)
③类型检查和转换:
1)isInstanceOf方法可以判断某一对象是否属于某个类;
2)asInstanceOf方法可以将父类的引用转换为子类的引用;
④scala中只有子类的主构造器才能直接调用父类的构造器,而且构造器中不能以 super(参数)形式调用父类构造器;
- 抽象类:
①抽象类不能被实例;
②抽象类可以包含非abstract方法;
③若类包含了抽象方法或抽象属性,则这个类必须声明为abstract,抽象方法和抽象属性不能使用private、final 来修饰,因为这些关键字都是和重写/实现相违背的。
④子类重写抽象方法不需要写override;
静态相关:
- scala中静态的概念:伴生对象
- scala为了和java交互,就产生了一种特殊的对象来模拟类对象,即类的伴生对象。这个类的所有静态内容都可以放置在它的伴生对象中来声明和调用
- 伴生对象:
①Scala中伴生对象采用object关键字声明,伴生对象中声明的全是 "静态"内容, 可以通过伴生对象名称直接调用。
②伴生对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。
③语法角度来看,所谓的伴生对象其实就是类的静态方法和成员的集合。
④技术角度来看,scala并没有生成静态的内容,只不过是将伴生对象生成了一个新的类,实现属性和方法的调用。
⑤从底层原理看,伴生对象实现静态特性是依赖于 public static final MODULE$ 实现的。
⑥在伴生对象中定义apply方法,可以实现: 类名(参数) 方式来创建对象实例。
trait(特质/特征)
-
scala中用trait来代替接口的概念,trait 等价于 interface&abstract classes,java的接口可以在scala中当做trait使用
-
某个类继承某个trait时,使用extends关键字,有多个trait时用with连接;继承class时,class需要放在最前面(class A extends 父类 with trait1 with trait…)
-
trait可以同时存在抽象方法和非抽象方法
-
带有特质的对象,动态混入:
①除了可以在类声明时继承特质以外,还可以在构建对象时混入特质,扩展 目标类的功能
②此种方式也可以应用于对抽象类功能进行扩展
③动态混入可在不修改类声明/定义的情况下,扩展类的功能,非常的灵 活,耦合性低 。
④动态混入可以在不影响原有的继承关系的基础上,给指定的类扩展功能 -
叠加特质:
构建对象的同时如果混入多个特质,称之为叠加特质;
注意细节:
①特质声明顺序从左到右。
②Scala在执行叠加对象的方法时,会首先从后面的特质(从右向左)开始执行
③Scala中特质中如果调用super,并不是表示调用父特质的方法,而是向前 面(左边)继续查找特质,如果找不到,才会去父特质查找
④如果想要调用具体特质的方法,可以指定:super[特质].xxx(…).其中的泛 型必须是该特质的直接超类类型代码示例:
trait Operate {
println("Operate...")
def insert(id: Int)
}
trait Data extends Operate {
println("Data")
override def insert(id: Int): Unit = {
println("插入数据 = " + id)
}
}
trait DB extends Data {
println("DB")
override def insert(id: Int): Unit = {
print("向数据库")
super.insert(id)
}
}
trait File extends Data {
println("File")
override def insert(id: Int): Unit = {
print("向文件")
super.insert(id)
}
}
class Test {}
object TestAddTrait {
def main(args: Array[String]): Unit = {
val test1 = new Test with DB with File
//依次输出:Operate... Data DB File
test1.insert(666)
//依次输出:向文件 向数据库 插入数据 = 666
//super调用的是其左边第一个特质的insert()方法,然后依次调用直到最左边的特质
//最左边的特质的super调用的父类的方法
}
}
-
特质中的具体字段:
①特质中可以定义具体字段,如果初始化了就是具体字段,如果不初始化就是抽象字段。混入该特质的类就具有了该字段,字段不是继承,而是直接加入类,成为自己的字段。
②特质中未被初始化的字段在具体的子类中必须被重写。 -
特质构造顺序
①特质也是有构造器的,构造器中的内容由“字段的初始化”和一些其他语句构成
②如果混入某特质的类,已经继承了另一个类(A类),则要求A类是该特质超类的子类,否则就出现多继承现象
③构造顺序如代码所示:
trait A { println("A...") }
trait B extends A { println("B...") }
trait C extends B { println("C...") }
trait D extends B { println("D...") }
class E { println("E...") }
class F extends E with D with C { println("F...") }
class K extends E { println("K...") }
object TestConTrait {
def main(args: Array[String]): Unit = {
new F()
//依次输出:E... A... B... C... D... F..
new K() with C with D
//依次输出:E... K... A... B... D... C...
}
}