从0开始学习scala-抽象类、访问修饰符、封装、继承、with、覆写

抽象类

  • 在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")
  }
}

多态

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值