抽象类
- 在scala中一个属性如果没有初始化,那么这个属性就是抽象属性,另一方面来说,当类中存在没有初始化的属性那么他就是抽象类,需要使用abstract修饰
- 如果是覆写一个父类的抽象,那么override关键字可以省略
抽象类中可以没有抽象方法或字段,但是有抽象字段或者抽象方法的类一定是抽象类
访问修饰符
私有(Private)成员:
用 private 关键字修饰,带有此标记的成员仅在当前类或对象内部可见[同样的规则还适用内部类]
保护(Protected)成员:
在 scala 中,对保护(Protected)成员的访问比 java 更严格一些。因为它只允许保护成员在定义了该成员的的类的子类中被访问。而在java中,用protected关键字修饰的成员,除了定义了该成员的类的子类可以访问,同一个包里的其他类也可以进行访问。
公共(Public)成员:
Scala中,如果没有指定任何的修饰符,则默认为 public。这样的成员在任何地方都可以被访问。
与java不同的在于protected,注意!注意!
object Extends {
def main(args: Array[String]): Unit = {
var s = new Sub();
s.test()
println(s.a) //都可以反问
// println(s.b) 报错 因为protected修饰 与java不同的是scala中protected修饰成员同一包下不可以访问,只可以在子类或本类内部访问
// println(s.c) 报错 因为private修饰 只可以在父类内部访问
}
}
class Base{
var a = ""
protected var b = ""
private var c = ""
}
class Sub extends Base{
def test(): Unit ={
println(s"a的值${a}")
println(s"b的值${b}") //protected修饰 与java不同的是scala中protected修饰成员同一包下不可以访问,只可以在子类或本类内部访问
// println(s"c的值${c}") //报错 c为private修饰
}
}
我们知道java是一门面向对象的语言,作为一个面向对象的语言那么它的核心就是封装、继承、多态。那么作为同样跑在jvm上的scala是如何实现封装、继承、多态的呢?
封装
封装(encapsulation)就是把抽象出的数据和对数据的操作封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(成员变量),才能对数据进行操作
- Scala中为了简化代码的开发,当声明属性时,会自动对var修饰的属性提供对应的setter/getter方法,会自动对val修饰的属性提供对应的getter方法
- 自动生成的setter/getter方法访问权限修饰符与属性一致,也就是说如果属性声明为private的,那么自动生成的setter/getter方法也是private的。
继承
extend关键字
格式:
class 子类名 extends 父类名 { 类体 }
1.extend
trait PersonTrait {
var name: String;
def eat()
}
class Person(val occupation: String, val age: Int) {
}
class Student(override val occupation: String, override val age: Int)extends Person (occupation, age) with PersonTrait{
override def eat(): Unit = {
println("吃东西")
}
override var name: String = "未知"
}
2.与java一样scala中子类在示例化时,会优先调用父类的构造方法
object Extends {
def main(args: Array[String]): Unit = {
val student = new Student("学生", 20, "男")
println(student)
}
}
class Person {
var gender: String = "未知"
var height: Int = 0
val countLeg: Int = 2
def this(occupation: String, age: Int) {
this() //附属构造器第一行必须是主构造器或者其他附属构造器
println("Persion有参构造器========");
}
println("Persion被调用========");
}
class Student(val occupation: String, val age: Int)
extends Person(occupation, age) {
override val countLeg: Int = 3
def this(occupation: String, age: Int, gender: String) {
this(occupation, age)
this.gender = gender
}
override def toString = s"Student($height, $occupation, $age, $countLeg)"
}
执行结果
Persion被调用========
Persion有参构造器========
Student(0, 学生, 20, 3)
3.复合类型(with)与extend关键字
with 关键字:
class A extends B with C with D with E
解读方式:
class A extends (B with C with D with E)
注意
1.当 B 、 C 、 D 、 E 出现如果存在相同的属性或者方法,需要人为解决冲突,否则时会报错。方式一: 修改方法/属性名称 方式: 在子类中重写方法/属性
object Extends {
def main(args: Array[String]): Unit = {
val d = new Demo()
println(d.name)//demoName
d.func()//Demo
}
}
class A {
val name = "A"
def func(): Unit ={
println("A")
}
}
trait B {
val name = "B"
def func: Unit ={
println("B")
}
}
trait C {
val nameC = "c"
}
class Demo extends A with B with C {
override val name: String = "demoName"
override def func(){
println("Demo")
}
}
4.super关键字
object Extends {
def main(args: Array[String]): Unit = {
val student = new Student("学生", 20, "男")
student.oneDay()
}
}
class Person(val occupation: String, val age: Int) {
def eat(): Unit = {
println("吃东西.....")
}
def sleep(): Unit = {
println("睡觉.....")
}
}
class Student(override val occupation: String, override val age: Int)
extends Person(occupation, age) {
var gender: String = "未知"
def this(occupation: String, age: Int, gender: String) {
this(occupation, age)
this.gender = gender
}
def oneDay() = {
super.eat()
super.sleep()
super.eat()
}
}
注意:
与java不同的是,scala构造器中不可以像java一样显性的使用supper()/supper(XXX)来调用父类的构造期,也就是说scala只有主构造器才可以直接调用父类的构造器,子类的辅助够造器无法直接调用父类的构造器。
with和Scala覆写字段
在scala中,子类不仅可以重写父类的方法还可以重写写父类的字段,若想重写则需使用override修饰。
1.属性在发生负写时,值是在复写的时候被赋值
object Extends {
def main(args: Array[String]): Unit = {
val t = new Tiger()
println(t.height) //200
println(t.weight) //100
println(t.doubleWeight)//200
}
}
class Animal {
val height = 200
val weight = 100
val doubleWeight = 2 * weight
println("父类执行")
}
class Tiger extends Animal {
}
object Extends {
def main(args: Array[String]): Unit = {
val t = new Tiger()
println(t.height)//200
println(t.weight)//150
println(t.doubleWeight)//0
}
}
class Animal {
val height = 200
val weight = 100
val doubleWeight = 2 * weight
println("父类执行")
}
class Tiger extends Animal {
override val weight: Int = 150
println("子类执行")
}
object Extends {
def main(args: Array[String]): Unit = {
val t2 = new {override val weight: Int = 170 } with Tiger()
println(t2.height)//200
println(t2.weight)//170
println(t2.doubleWeight)//340
}
}
class Animal {
val height = 200
val weight = 100
val doubleWeight = 2 * weight
println("父类执行")
}
class Tiger extends Animal {
override val weight: Int = 150
println("子类执行")
}
看完上面的例子你不是是一脸蒙? 我们debug下发现如下
当子类继承父类,调用父类的构造期然后才是子类构造期,首先子类属性全部初始化, 然后执行父类构造期。 未被重写的属性直接赋值, 而该属性被重写了则属性以及该属性相关的属性(weight, doubleWeight)全部继续为默认值,父类执行完成后初始化子类值此时才会将子类重写的属性设置为值。
情况二附图:
情况三附图:
下面看一个列子
发现第一种调用方式会报错,因为Student extends (Person with FileLogger),调用时程序发现FileLogger中的val fileOutput = new PrintWriter(fileName: String)报空指针,依照执行顺序当执行到val fileOutput = new PrintWriter(fileName: String)时fileName还未被赋值。按照上个例子的解决方法我们可以修该如下
object Extends {
def main(args: Array[String]): Unit = {
// new Student().log("trait demo") //这种调用会报错
new { override val fileName: String = "/Library/WebServer/log/file2.log"} with Student().log("这是一个日志")
}
}
trait Logger {
def log(msg: String): Unit
}
trait FileLogger extends Logger {
val fileName: String
val fileOutput = new PrintWriter(fileName: String)
fileOutput.println("#")
def log(msg: String): Unit = {
fileOutput.print(msg)
fileOutput.flush()
}
}
class Person{
}
class Student extends Person with FileLogger {
override val fileName: String = "/Library/WebServer/log/file.log"
}
注意:
- 被val修饰的属性只能被val属性重写
- 被var修饰的属性只有是抽象属性时操可以被var属性重写
- val只能重写另一个val属性或重写不带参数的def
- 子类可以覆盖父类的filed和method,但是要注意的是final关键字,代表field和method无法覆盖
object Extends {
def main(args: Array[String]): Unit = {
val d = new Demo()
println(d.valName)
println(d.defName)
d.func()
}
}
class A {
val valName = "val"
val varName = "var"
def defName= "def"
def func() = {
println("func")
}
}
class Demo extends A{
override val valName = "val1"
// override var varName = "var1" 报错 重写var只能是继承抽象类
override val defName = "def1"
override def func() = {
println("func2")
}
}