第七章 面向对象编程-中级7.1 包7.1.1 Java 中的包7.1.2 Scala 中的包7.1.3 Scala 包的特点概述7.1.4 Scala 包的命名7.1.5 Scala 会自动引入的常用包7.1.6 Scala 包注意事项和使用细节7.1.7 包对象7.1.8 包对象的底层实现机制分析(重点)7.1.9 包对象的注意事项7.2 包的可见性7.2.1 回顾 Java 包的可见性7.2.2 Scala 中包的可见性介绍7.2.3 Scala 中包的可见性和访问修饰符的使用7.3 包的引入7.3.1 Scala 引入包基本介绍7.3.2 Scala 引入包的细节和注意事项7.4 面向对象编程方法-抽象7.5 面向对象编程三大特征7.5.1 基本介绍7.5.2 封装的实现步骤7.5.3 快速入门案例7.5.4 Scala 封装的注意事项和细节7.6 面向对象编程方法-继承7.6.1 Java 继承的简单回顾7.6.2 Scala 的继承7.6.3 Scala 继承给编程带来的便利7.6.4 重写方法7.6.5 Scala 中类型检查和转换7.6.6 Java 中超类的构造7.6.7 Scala 中超类的构造7.6.8 覆写字段7.6.9 抽象类7.6.10 Scala 抽象类使用的注意事项和细节讨论7.6.11 匿名子类7.6.12 继承层级7.7 作业04


第七章 面向对象编程-中级

7.1 包

7.1.1 Java 中的包

看一个应用场景

回顾-Java 包的三大作用+打包命令+快速入门


示例代码如下:

package com.atguigu.chapter07.javapackage;

public class DogTest {
    public static void main(String[] args) {
        // 使用 xiaoming 的 Dog
        com.atguigu.chapter07.javapackage.xiaoming.Dog dog01 = new com.atguigu.chapter07.javapackage.xiaoming.Dog();
        // 使用 xiaoqiang 的 Dog
        com.atguigu.chapter07.javapackage.xiaoqiang.Dog dog02 = new com.atguigu.chapter07.javapackage.xiaoqiang.Dog();         System.out.println("dog01=" + dog01 + "\ndao02=" + dog02);
    }
}

输出结果如下:

dog01=com.atguigu.chapter07.javapackage.xiaoming.Dog@12a3a380
dao02=com.atguigu.chapter07.javapackage.xiaoqiang.Dog@29453f44

回顾-Java 如何引入包+Java 包的特点

java 包 和 类源码文件路径、包文件路径、.class 文件路径 的关系图

7.1.2 Scala 中的包

Scala 包的基本介绍+快速入门


示例代码如下:

package com.atguigu.chapter07.scalapackage

object CatTest {
  def main(args: Array[String]): Unit = {
    // 使用 xiaoming 的 Cat
    var cat01 = new com.atguigu.chapter07.scalapackage.xiaoming.Cat
    println("cat01=" + cat01)     // 使用 xiaoming 的 Cat
    var cat02 = new com.atguigu.chapter07.scalapackage.xiaoqiang.Cat
    println("cat02=" + cat02)
  }
}

输出结果如下:

cat01=com.atguigu.chapter07.scalapackage.xiaoming.Cat@1fbc7afb
cat02=com.atguigu.chapter07.scalapackage.xiaoqiang.Cat@7dc36524

7.1.3 Scala 包的特点概述

scala 包 和 类源码文件路径、包文件路径、.class 文件路径 的关系图

7.1.4 Scala 包的命名

7.1.5 Scala 会自动引入的常用包

7.1.6 Scala 包注意事项和使用细节

1、scala 进行 package 打包时,可以有如下形式。【案例演示+反编译查看】


第三种打包方式示例代码如下:

// 代码说明:
// 1. package com.atguigu{}  表示我们创建了包 com.atguigu,在{}中我们可以继续写它的子包 scala(即com.atguigu.scala),还可以写类、特质trait、还可以写object。
// 2. 即 sacla 支持,在一个文件中,可以同时创建多个包,以及给各个包创建类、trait和object。【超级灵活】
package com.atguigu { // 创建包 com.atguigu
  package scala { // 表示在 com.atguigu 下创建包 scala     class Person { // 表示在 com.atguigu.scala 下创建类 Person
      val name = "Nick"
      def play(message: String): Unit = {
        println(this.name + " " + message)
      }
    }     object Test { // 表示在 com.atguigu.scala 下创建类 object Test
      def main(args: Array[String]): Unit = {
        println("object Test")
      }
    }   } }

2、包也可以像嵌套类那样嵌套使用(包中有包),这个在前面的第三种打包方式已经讲过了,在使用第三种方式时的好处是:程序员可以在同一个文件中,将类(class/object)、trait 创建在不同的包中,这样就非常灵活了。【案例演示+反编译查看】
示例代码如下:

// 代码说明:
// 1. package com.atguigu{}  表示我们创建了包 com.atguigu,在{}中我们可以继续写它的子包 scala(即com.atguigu.scala),还可以写类、特质trait、还可以写object。
// 2. 即 sacla 支持,在一个文件中,可以同时创建多个包,以及给各个包创建类、trait和object。【超级灵活】
package com.atguigu { // 创建包 com.atguigu   class User { // 表示在 com.atguigu 下创建类 User   }   package scala2 { // 表示在 com.atguigu 下创建包 scala2
    class User {   // 表示在 com.atguigu.scala2 下创建类 User     }
  }   package scala {  // 表示在 com.atguigu 下创建包 scala     class Person { // 表示在 com.atguigu.scala 下创建类 Person
      val name = "Nick"
      def play(message: String): Unit = {
        println(this.name + " " + message)
      }
    }     object Test {  // 表示在 com.atguigu.scala 下创建类 object Test
      def main(args: Array[String]): Unit = {
        println("object Test")
      }
    }   } }

3、作用域原则:可以直接向上访问。即: Scala 中子包中直接访问父包中的内容,大括号体现作用域。(提示:Java 中子包使用父包的类,需要 import)。在子包和父包 类重名时,默认采用就近原则,如果希望指定使用某个类,则带上包名即可。【案例演示+反编译查看】
示例代码如下:

// 代码说明:
// 1. package com.atguigu{}  表示我们创建了包 com.atguigu,在{}中我们可以继续写它的子包 scala(即com.atguigu.scala),还可以写类、特质trait、还可以写object。
// 2. 即 sacla 支持,在一个文件中,可以同时创建多个包,以及给各个包创建类、trait和object。【超级灵活】
package com.atguigu { // 创建包 com.atguigu   // com.atguigu 下的类 User
  class User { // 表示在 com.atguigu 下创建类 User   }   package scala2 { // 表示在 com.atguigu 下创建包 scala2
    // com.atguigu.scala2 下的类 User
    class User {   // 表示在 com.atguigu.scala2 下创建类 User     }
  }   package scala {  // 表示在 com.atguigu 下创建包 scala     class Person { // 表示在 com.atguigu.scala 下创建类 Person
      val name = "Nick"
      def play(message: String): Unit = {
        println(this.name + " " + message)
      }
    }     // com.atguigu.scala 下的类 User
    class User {     }     object Test {  // 表示在 com.atguigu.scala 下创建类 object Test
      def main(args: Array[String]): Unit = {
        println("object Test")         // 我们可以直接使用父包的内容
        // 1.如果有同名的类,则采用【就近原则】来使用内容(比如包)
        val user1 = new User
        println("user1=" + user1)
        // 2.如果就是要使用父包的类,则指定路径即可
        val user2 = new com.atguigu.User
        println("user2=" + user2)
      }
    }   } }

输出结果如下:

object Test
user1=com.atguigu.scala.User@7d417077
user2=com.atguigu.User@7dc36524

4、父包要访问子包的内容时,需要import 对应的子包中的类。
5、可以在同一个 xxx.scala 文件中,可以声明多个并列的 package (建议嵌套的 pakage 不要超过3层)。 【案例演示+反编译】
6、包名可以相对也可以绝对,比如,访问 BeanProperty 的绝对路径是:_root_. scala.beans.BeanProperty,在一般情况下:我们使用相对路径来引入包,只有当包名冲突时,使用绝对路径来处理。
示例代码如下:

object TestBean {
  def main(args: Array[String]): Unit = {
    val m = new Manager("jack")
    println("m=" + m)
  }
} class Manager(var name: String) {
  // 第一种形式:相对路径引入
  // import scala.beans.BeanProperty
  // @BeanProperty var age: Int = _
  // 第二种形式:和第一种一样,都是相对路径引入
  // @scala.beans.BeanProperty var age2: Int = _
  // 第三种形式:是绝对路径引入,可以解决包名冲突
  @_root_.scala.beans.BeanProperty var age3: Int = _
}

7.1.7 包对象

基本介绍
  包可以包含类、对象和特质 trait,但不能包含函数/方法或变量的定义。这是 Java 虚拟机的局限。为了弥补这一点不足,scala 提供了包对象的概念来解决这个问题。

包对象的应用案例
示例代码如下:

package com.atguigu {

  // 在包中直接写方法或者定义变量吧,就会报错
  // var name = "jack"   // 1. 在包中直接写方法,或者定义变量,就错误==>使用包对象的技术来解决
  // 2. package object scala 表示创建一个包对象 scala, 他是 com.atguigu.scala 这个包对应的包对象
  // 3. 每一个包都可以有一个包对象
  // 4. 包对象的名字需要和子包一样
  // 5. 在包对象中可以定义变量,方法
  // 6. 在包对象中定义的变量和方法,就可以在对应的包中使用
  // 7. 在底层这个包对象会生成两个.class文件 package.class 和 package$.class   // 每个包都可以有一个【包对象】。你需要在父包(com.atguigu)中定义它,且名称与子包一样。
  package object scala {
    var name = "jack"     def sayOk(): Unit = {
      println("package object sayOk")
    }
  }
  package scala { // 表示在 com.atguigu 下创建子包 scala     class Test04 {
      def test(): Unit = {
        // 这里的 name 就是包对象 scala 中声明的 name
        println(name)
        sayOk() // 这个 sayOk()函数 就是【包对象】 scala 中声明的函数 sayOk
      }
    }     object TestObj {
      def main(args: Array[String]): Unit = {
        val t = new Test04()
        t.test()
        // 因为 TestObje 和 scala 这个【包对象】在同一包,因此也可以使用
        println("name=" + name)
        sayOk()
      }
    }   } }

输出结果如下:

jack
package object sayOk
name=jack
package object sayOk

7.1.8 包对象的底层实现机制分析(重点)


在底层这个包对象会生成两个.class文件 package.classpackage$.class

7.1.9 包对象的注意事项

  1、每个包都可以有一个包对象。你需要在父包中定义它。
  2、包对象名称需要和包名一致,一般用来对包的功能补充。

7.2 包的可见性

7.2.1 回顾 Java 包的可见性

回顾-Java 访问修饰符基本介绍
java 提供四种访问控制修饰符号控制方法和变量的访问权限(范围):
  1、公开级别:用 public 修饰,对外公开。
  2、受保护级别:用 protected。修饰,对子类和同一个包中的类公开。
  3、默认级别:没有修饰符号,向同一个包的类公开。
  4、私有级别:用 private 修饰,只有类本身可以访问,不对外公开。

回顾-Java 中4种访问修饰符的访问范围

回顾-Java 访问修饰符使用注意事项
  1、修饰符可以用来修饰类中的属性,成员方法以及类。
  2、只有默认的和 public 才能修饰类,并且遵循上述访问权限的特点。

7.2.2 Scala 中包的可见性介绍

  在 Java 中,访问权限分为: public,private,protected 和默认。在 Scala 中,你可以通过类似的修饰符达到同样的效果。但是使用上有区别。
示例代码如下:

package com.atguigu.chapter07.visit

object VisitDemo01 {
  def main(args: Array[String]): Unit = {
    val c = new Clerk()
    c.showInfo()
    Clerk.test(c)
  }
} class Clerk { // 伴生类
  var name: String = "jack"         // 在底层是私有,可读可写(隐式私有)
  private var sal: Double = 9999.9  // 在底层是私有,只可读(显式私有)   def showInfo(): Unit = {
    // 在本类中可以使用私有的属性
    println("name=" + name + " sal=" + sal)
  }
} // 当一个文件中同时出现了 class Clerk 和 object Clerk
// 1、class Clerk  称为伴生类
// 2、object Clerk 称为伴生对象
// 因为 scala 的设计者将 static 拿掉了,他就设计了 伴生类 和 伴生对象 的概念
// 将非静态的内容放在 伴生类 中
// 将静态的内容放在 伴生对象 中
object Clerk { // 伴生对象
  def test(c: Clerk): Unit = {
    // 这里体现出:在伴生对象中,可以访问伴生类的私有属性 c.sal
    println("test() name=" + c.name + " sal=" + c.sal)
  }
}

输出结果如下:

name=jack sal=9999.9
test() name=jack sal=9999.9

7.2.3 Scala 中包的可见性和访问修饰符的使用


所有的示例代码如下:

package com.atguigu.chapter07.visit

object VisitDemo01 {
  def main(args: Array[String]): Unit = {
    val c = new Clerk()
    c.showInfo()
    Clerk.test(c)     val p = new Person
    println(p.pname)
    println(p.page)
  }
} class Clerk { // 伴生类
  var name: String = "jack"         // 在底层是私有,可读可写(隐式私有)
  private var sal: Double = 9999.9  // 在底层是私有,只可读(显式私有),private 为私有权限,只在 类的内部 和 伴生对象 中可用。   def showInfo(): Unit = { // 当方法访问权限为默认时,默认为 public 访问权限。
    // 在本类中可以使用私有的属性
    println("name=" + name + " sal=" + sal)
  }
} // 当一个文件中同时出现了 class Clerk 和 object Clerk
// 1、class Clerk  称为伴生类
// 2、object Clerk 称为伴生对象
// 因为 scala 的设计者将 static 拿掉了,他就设计了 伴生类 和 伴生对象 的概念
// 将非静态的内容放在 伴生类 中
// 将静态的内容放在 伴生对象 中
object Clerk { // 伴生对象
  def test(c: Clerk): Unit = {
    // 这里体现出:在伴生对象中,可以访问伴生类的私有属性 c.sal
    println("test() name=" + c.name + " sal=" + c.sal)
  }
} class Person {
  // 这里我们增加一个包访问权限
  // 下面 private[visit] 说明
  // 1、仍然是 private
  // 2、在 visit 包(包括子包)下也可以使用 name,相当于扩大访问范围
  private[visit] val pname = "tom" // 增加包访问权限后,1. private同时起作用。不仅同类可以使用 2. 同时com.atguigu.scala中包下其他类也可以使用
  // 当然,也可以将可见度延展到上层包
  private[chapter07] val page= "25"
}

补充:
// 在 Scala 中,只有两种访问控制符:public 和 private
// 在 Scala 中,属性只有三种修饰符:默认(可读可写) 和 private(只可读) 和 protected(可读可写,只能子类访问),但是底层都是 private
示例代码如下:

package com.atguigu.chapter07.homework

object Exercise04 {
  def main(args: Array[String]): Unit = {
    println("xxx")
  }
} // 在 Scala 中,只有两种访问控制符:public 和 private
// 在 Scala 中,属性只有三种修饰符:默认(可读可写) 和 private(只可读) 和 protected(可读可写,只能子类访问),但是底层都是 private class Monster {
  var age: Int = 1
  private var name: String = ""
  protected var sal: Double = 0.01
}

7.3 包的引入

7.3.1 Scala 引入包基本介绍

7.3.2 Scala 引入包的细节和注意事项

1、在 Scala 中,import 语句可以出现在任何地方,并不仅限于文件顶部,import 语句的作用一直延伸到包含该语句的块末尾。这种语法的好处是:在需要时在引入包,缩小 import 包的作用范围,提高效率。
2、Java 中如果想要导入包中所有的类,可以通过通配符*,Scala 中采用下划线。


3、如果不想要某个包中全部的类,而是其中的几个类,可以采用选取器(花括号)。

4、如果引入的多个包中含有相同的类,那么可以将不需要的类进行重命名进行区分,这个就是重命名。
5、如果某个冲突的类根本就不会用到,那么这个类可以直接隐藏掉。

7.4 面向对象编程方法-抽象


示例代码如下:

package com.atguigu.chapter07.abstractdemo

object BankDemo {
  def main(args: Array[String]): Unit = {
    // 开卡
    val account = new Account("gh001", 999.9, "123456")
    account.query("123456")
    account.withDraw("123456", 500)
    account.query("123456")
    account.deposit(200)
    account.query("123456")
  }
} /**
  * 属性:
  *   账号,余额,密码
  * 方法:
  *   查询
  *   取款
  *   存款
  */
class Account(InAccountNo: String, InBalance: Double, InPwd: String) {
  val accountNo = InAccountNo
  var balance = InBalance
  var pwd = InPwd   // 查询方法
  def query(InPwd: String): Unit = {
    if (!InPwd.equals(this.pwd)) {
      println("密码输入错误!")
      return
    }     printf("账号为:%s,当前余额是:%.2f", this.accountNo, this.balance)
    println()
  }   // 取款方法
  def withDraw(InPwd: String, money: Double): Any = {
    if (!InPwd.equals(this.pwd)) {
      println("密码输入错误!")
      return
    }
    if (money > this.balance) {
      println("余额不足,请充值!")
      return
    }
    this.balance -= money
    money
  }   // 存款方法
  def deposit(money: Double): Unit = {
    this.balance += money
    println("存款成功!")
  }
}

输出结果如下:

账号为:gh001,当前余额是:999.90
账号为:gh001,当前余额是:499.90
存款成功!
账号为:gh001,当前余额是:699.90

7.5 面向对象编程三大特征

7.5.1 基本介绍

7.5.2 封装的实现步骤

7.5.3 快速入门案例

7.5.4 Scala 封装的注意事项和细节

7.6 面向对象编程方法-继承

7.6.1 Java 继承的简单回顾

Java 继承的简单回顾

Java 中继承的示意图

7.6.2 Scala 的继承


示例代码如下:

package com.atguigu.chapter07.myextends

object Extends01 {
  def main(args: Array[String]): Unit = {
    val stu = new Student
    stu.name = "tom" // 调用了父类 Person 的 stu.name_$eq() 方法相当于 setter 方法
    stu.studying()
    stu.showInfo()
  }
} class Person {
  var name: String = _
  var age: Int = _   def showInfo(): Unit = {
    println("学生信息如下:")
    println("名字:" + this.name)
  }
} class Student extends Person {
  def studying(): Unit = {
    println(this.name + " 在学习 scala 中....")
  }
}

输出结果如下:

tom 在学习 scala 中....
学生信息如下:
名字:tom

7.6.3 Scala 继承给编程带来的便利


示例代码如下:

package com.atguigu.chapter07.myextends

// 说明:
// 1、在 Scala 中,子类继承了父类的所有属性,但是父类的 private 的属性和方法无法访问。
object Extends02 {
  def main(args: Array[String]): Unit = {
    val sub = new Sub()
    sub.sayOk()     // sub.test200() // 错误,protected 为受保护权限,scala 中受保护权限比 Java 中更严格,只能子类访问,同包无法访问 (编译器)。
  }
} class Base {
  var n1: Int = 1
  protected var n2: Int = 2
  private var n3: Int = 3   def test100(): Unit = { // 底层是 public
    println("base 100")
  }   protected def test200(): Unit = { // 底层是 public
    println("base 200")
  }   private def test300(): Unit = { // 底层是 private
    println("base 300")
  }
} class Sub extends Base {
  def sayOk(): Unit = {
    this.n1 = 20
    this.n2 = 40
    println("范围:" + this.n1 + " " + this.n2)
    test100()
    test200()
  }
}

输出结果如下:

范围:20 40
base 100
base 200

7.6.4 重写方法

  说明:scala 明确规定,重写一个非抽象方法需要用 override 修饰符,调用超类的方法使用 super 关键字。
示例代码如下:

package com.atguigu.chapter07.myextends

object MethodOverride01 {
  def main(args: Array[String]): Unit = {
    val emp = new Emp100
    emp.printName()
  }
} class Person100 {
  var name: String = "tom"
  def printName() {
    println("Person printName() " + name)
  }
} // scala 明确规定,重写一个非抽象方法需要用 override 修饰符,调用超类的方法使用 super 关键字。
class Emp100 extends Person100 {
  // 这里需要显式的使用 override
  override def printName() {
    println("Emp printName() " + name)
    super.printName()
  }
}

输出结果如下:

Emp printName() tom
Person printName() tom

7.6.5 Scala 中类型检查和转换


示例代码如下:

package com.atguigu.chapter07.myextends

object TypeConvert {
  def main(args: Array[String]): Unit = {
    // 获取对象类型
    println(classOf[String]) // class java.lang.String     // 这种是 Java 中反射方式得到对象类型
    val s = "zhangsan"
    println(s.getClass.getName) // java.lang.String     println(s.isInstanceOf[String]) // true
    println(s.asInstanceOf[String]) // 将 s 显示转换成 String     var p2 = new Person200
    var emp2 = new Emp200
    // 将子类引用给父类(向上转型,自动)
    p2 = emp2
    // 将父类引用转成子类引用(向下转型,需要手动)
    var emp3 = p2.asInstanceOf[Emp200]
    emp3.sayHello()
  }
} class Person200 {
  var name: String = "tom"   def printName() {
    println("Person printName() " + name)
  }
} // scala 明确规定,重写一个非抽象方法需要用 override 修饰符,调用超类的方法使用 super 关键字。
class Emp200 extends Person200 {
  // 这里需要显式的使用 override
  override def printName() {
    println("Emp printName() " + name)
    super.printName()
  }   def sayHello(): Unit = {
    println("hello")
  }
}

输出结果如下:

class java.lang.String
java.lang.String
true
zhangsan
hello

Scala 中类型检查和转换的最佳实践
  类型检查和转换的最大价值在于:可以判断传入对象的类型,然后转成对应的子类对象,进行相关操作,这里也体现出多态的特点。
示例代码如下:

package com.atguigu.chapter07.myextends

object TypeConvertExer {
  def main(args: Array[String]): Unit = {
    // 创建子类对象
    val stu = new Stu
    val worker = new Worker
    test(stu)     // stuId=100
    test(worker)  // workerId=200
  }   def test(p: Person300): Unit = {
    // 如果传入是 Student 实例,则调用 sayOk
    // 如果传入是 Worker 实例,则调用 sayHello
    // 进行 p.asInstanceOf[T] 转换时,要求 p 的引用本身就是指向 T 类型的引用
    if (p.isInstanceOf[Stu]) {
      // 向下转型
      p.asInstanceOf[Stu].sayOk() // 注意:p.asInstanceOf[Stu] 对 p 的类型没有任何变化,而是返回的是 Stu 类型
    } else if (p.isInstanceOf[Worker]) {
      p.asInstanceOf[Worker].sayHello()
    } else {
      println("转换错误")
    }
  }
} class Person300 {
  var name: String = "tom"   def printName() {
    println("name=" + name)
  }
} class Stu extends Person300 {
  var stuId: Int = 100   def sayOk(): Unit = {
    println("stuId=" + this.stuId)
  }
} class Worker extends Person300 {
  var workerId: Int = 200   def sayHello(): Unit = {
    println("workerId=" + this.workerId)
  }
}

输出结果如下:

stuId=100
workerId=200

7.6.6 Java 中超类的构造

回顾-Java 中超类的构造
示例代码如下:

package com.atguigu.chapter07.myextends;

public class JavaBaseConstractor {
    public static void main(String[] args) {
        B b1 = new B();
        B b2 = new B("tom");
    }
} class A {
    public A() {
        System.out.println("A()");
    }
    public A(String name) {
        System.out.println("A(String name) " + name);
    }
}
class B extends A {
    public B() {
        // 这里会隐式调用super(); 就是无参的父类构造器A()
        System.out.println("B()");
    }
    public B(String name) {
        super(name);
        System.out.println("B(String name) " + name);
    }
}

输出结果如下:

A()
B()
A(String name) tom
B(String name) tom

说明:
  从代码可以看出:在 Java 中,创建子类对象时,子类的构造器总是去调用一个父类的构造器(显式或者隐式调用)。

7.6.7 Scala 中超类的构造

Scala 超类的构造说明
  1、类有一个主构器和任意数量的辅助构造器,而每个辅助构造器都必须先调用主构造器(也可以是间接调用),这点在前面我们说过了。
  2、只有子类的主构造器可以调用父类的构造器(主和辅均可)。子类的辅助构造器不能直接调用父类的构造器。在 Scala 的子类的构造器中,你不能调用 super(params)。
完整示例代码如下:

package com.atguigu.chapter07.myextends

object ScalaBaseConstractor {
  def main(args: Array[String]): Unit = {
    // 分析一下它的执行流程
    // 1、因为 scala 遵守先构建父类部分 extends Person500()
    // 2、Person...
    // 3、Emp... (Emp500的主构造器)
    val emp1 = new Emp500()     println("----------")
    // 分析一下它的执行流程
    // 1、因为 scala 遵守先构建父类部分 extends Person500()
    // 2、Person...
    // 3、Emp.... (Emp500的主构造器)
    // 4、Emp辅助构造器
    val emp2 = new Emp500("lucy")     println("----------")
    // 分析一下它的执行流程
    // 1、因为 scala 遵守先构建父类部分 extends Person600(),由父类的辅助构造器调用该父类的带参主构造器
    // 2、Person...
    // 3、Emp.... (Emp600的主构造器)
    // 4、Emp辅助构造器
    val emp3 = new Emp600("lucy")     println("----------")
    // 分析一下它的执行流程
    // 1、因为 scala 遵守先构建父类部分 extends Person700(eName),调用该父类的带参主构造器
    // 2、Person...
    // 3、Emp.... (Emp700的主构造器)
    val emp4 = new Emp700("lucy", 20)
  }
} class Person500 {
  var name = "zhangsan"
  println("Person...")
} class Emp500 extends Person500 {
  println("Emp...")   def this(name: String) {
    this // 该子类的辅助构造器必须调用该子类的主构造器
    this.name = name
    println("Emp辅助构造器")
  }
} class Person600(pName: String) {
  var name = pName
  println("Person...")   def this() {
    this("默认的名字") // 该父类的辅助构造器必须调用该父类的带参主构造器
    println("默认的名字")
  }
} class Emp600 extends Person600 {
  println("Emp...")   def this(name: String) {
    this // 该子类的辅助构造器必须调用该子类的主构造器
    this.name = name
    println("Emp辅助构造器")
  }
} class Person700(pName: String) {
  var name = pName
  println("Person...")   def this() {
    this("默认的名字") // 该父类的辅助构造器必须调用该父类的带参主构造器
    println("默认的名字")
  }
} class Emp700(eName:String, eAge: Int) extends Person700(eName) {
  println("Emp...")   def this(name: String) {
    this("mary", 10) // 该子类的辅助构造器必须调用该子类的主构造器
    this.name = name
    println("Emp辅助构造器")
  }
}

输出结果如下:

Person...
Emp...
----------
Person...
Emp...
Emp辅助构造器
----------
Person...
默认的名字
Emp...
Emp辅助构造器
----------
Person...
Emp...

7.6.8 覆写字段

Java 另一重要特性: 动态绑定 机制
示例代码如下:

package com.atguigu.chapter07.myextends;

public class JavaDaynamicBind {
    public static void main(String[] args) {
        // 提出 java 的动态绑定机制
        // 1. 当调用对象方法的时候,该方法会和该对象的内存地址绑定。
        // 2. 当调用对象属性时,没有动态绑定机制,哪里声明,那里使用。
        AA a = new BB();                // BB均不注销   BB只注销sum()  BB注销sum()和注销sum1()
        System.out.println(a.sum());    // 40           30              30
        System.out.println(a.sum1());   // 30           30              20         //
        /*
        java中多态中对成员的访问的特点 = 动态绑定
        成员变量:
            编译看左边,运行看左边。
        成员方法:
        编译看左边,运行看右边。
        静态方法:
        编译看左边,运行看左边。
        */
    }
} class AA {
    public int i = 10;
    public int sum() {
        return getI() + 10;
    }
    public int sum1() {
        return i + 10;
    }
    public int getI() {
        return i;
    }
} class BB extends AA {
    public int i = 20;
/*    public int sum() {
        return i + 20;
    }*/
/*    public int sum1() {
        return i + 10;
    }*/
    public int getI() {
        return i;
    }
}

Scala 覆写字段快速入门
示例代码如下:

package com.atguigu.chapter07.myextends

object ScalaFiledOverride {
  def main(args: Array[String]): Unit = {
    val obj1: AAA = new BBB
    val obj2: BBB = new BBB
    println("obj1.age=" + obj1.age) // 动态绑定机制生效
    println("obj2.age=" + obj2.age)
  }
} class AAA {
  val age: Int = 10 // 底层是 public int age() 方法
} class BBB extends AAA {
  override val age: Int = 20 // 底层是 public int age() 方法
}

输出结果如下:

obj1.age=20
obj2.age=20

反编译后的代码:

public class AAA {
  private final int age = 10;
  public int age() { 
    return this.age;
  }
} public class BBB extends AAA {
  private final int age = 20;
  public int age() { 
    return this.age;
  }
}

覆写字段的注意事项和细节


1、def 只能重写另一个 def (即:方法只能重写另一个方法)。
2、val 只能重写另一个 val 属性 或 重写不带参数的 def。
示例代码如下:

package com.atguigu.chapter07.myextends

object ScalaFiledOverrideDetail01 {
  def main(args: Array[String]): Unit = {   }
} // 2、val 只能重写另一个 val 属性。
class AAAA {
  // var name: String = "" // 底层会生成 public String name() 和 public String name_$seq()
  val name: String = "" // 底层只会生成 public String name()
} class BBBB extends AAAA {
  override val name: String = "tom" // 底层只会生成 public String name()
}

示例代码如下:

package com.atguigu.chapter07.myextends

object ScalaFiledOverrideDetail02 {
  def main(args: Array[String]): Unit = {
    val b1 = new BBBBB
    println("b1.sal=" + b1.sal) // b1.sal=20     val b2:AAAAA = new BBBBB // 动态绑定机制生效
    println("b2.sal=" + b2.sal()) // b2.sal=20
  }
} // 2、val 只能重写重写不带参数的 def。
class AAAAA {
  def sal(): Int = { // 底层只会生成 public int sal()
    return 10
  }
}
class BBBBB extends AAAAA {
  override val sal: Int = 20 // 底层只会生成 public int sal()
}

3、var 只能重写另一个抽象的 var 属性。


示例代码如下:

package com.atguigu.chapter07.myextends

object ScalaFiledOverrideDetail03 {
  def main(args: Array[String]): Unit = {
    println()
  }
} // 1、抽象属性:声明未初始化的变量(字段/属性)就是抽象的属性,抽象属性在抽象类中。
// 2、对于抽象的属性,在底层不会生成对应的属性声明,而是生成两个对应的抽象方法。
// public abstract String name();
// public abstract void name_$eq(String paramString);
abstract class A03 {
  var name: String
  val age: Int = 10 // val修饰,底层只生成 public int age()
} // 1、我们在子类中重写父类的抽象属性,本质上是实现了抽象方法,生成了两个 public 方法
// public String name()
// public void name_$eq(String x$1)
// 2、如果是覆写一个父类的抽象属性,那么 override 关键字可省略。
// 3、如果是覆写一个父类的非抽象属性,那么 override 关键字不可省略。
class B03 extends A03 {
  // var name: String = _ // 如果是覆写一个父类的抽象属性,那么 override 关键字可省略。
  override var name: String = _
  override val age: Int = 20 // val修饰,底层只生成 public int age()
}

7.6.9 抽象类


示例代码如下:

package com.atguigu.chapter07.myextends

object AbstractDemo01 {
  def main(args: Array[String]): Unit = {
    println()
  }
} abstract class Animal {
  var name: String // 抽象的字段
  var age: Int // 抽象的字段
  var color: String = "black" // 普通字段   // def cry // 抽象的方法的小括号可以省略
  def cry() // 抽象的方法,不需要标记 abstract,否则报错
}

抽象类基本语法

7.6.10 Scala 抽象类使用的注意事项和细节讨论


示例代码如下:

package com.atguigu.chapter07.myextends

object AbstractClassDetail01 {
  def main(args: Array[String]): Unit = {
    // 1、抽象类不能被实例。但是有一种情况:当动态实现抽象类的所有抽象方法时,抽象类也就被实例化了。本质是该抽象类的匿名子类实现了该抽象类。
    val a1 = new Animal01 {
      override def eat(): Unit = {
        println("eat...")
      }
    }
    a1.eat()     val a5 = new Cat
    a5.name = "小呆萌"
    a5.eat()
  }
} abstract class Animal01 {
  def eat()
} // 2、抽象类不一定要包含 abstract 方法。也就是说,抽象类可以没有 abstract 方法。
abstract class Animal02 {
  // 7、抽象类中可以有实现的方法。
  def eat(): Unit = {
    println("eat...")
  }
} // 5、如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法和抽象属性,除非它自己也声明为 abstract 类。
abstract class Animal05 {
  var name:String
  def eat()
} class Cat extends Animal05 {
  // 9、如果是覆写一个父类的抽象属性,那么 override 关键字可省略。
  override var name: String = _
  // 8、子类重写抽象方法不需要 override,写上也不会错。
  override def eat(): Unit = {
    println(this.name + " eat fash")
  }
}

输出结果如下:

eat...
小呆萌 eat fash

7.6.11 匿名子类


示例代码如下:

package com.atguigu.chapter07.myextends;

public class NoNameDemo01 {
    public static void main(String[] args) {
        A2 a = new A2() {
            @Override
            public void cry() {
                System.out.println("呜呜");
            }
        };
        a.cry();     }
} abstract class A2 {
    abstract public void cry();
}

输出结果如下:

呜呜

示例代码如下:

package com.atguigu.chapter07.myextends

object NoNameDemo02 {
  def main(args: Array[String]): Unit = {
    var monster = new Monster {
      override var name: String = "牛魔王"       override def cry(): Unit = {
        println(this.name + " 哞哞")
      }
    }
    monster.cry()
  }
} abstract class Monster {
  var name: String   def cry()
}

输出结果如下:

牛魔王 哞哞

7.6.12 继承层级

Scala 继承层级一览图


继承层级图小结

7.7 作业04

1、编写一个 Time 类,加入只读属性 hours 和 minutes,和一个检查某一时刻是否早于另一时刻的方法 before(other: Time): Boolean。Time 对象应该以 new Time(hrs, min) 方式构建。
示例代码如下:

package com.atguigu.chapter07.homework

/**
  * 1、编写一个 Time 类,加入只读属性 hours 和 minutes,和一个检查某一时刻是否早于另一时刻的方法 before(other: Time): Boolean。
  * Time 对象应该以 new Time(hrs, min) 方式构建。
  */
object Exercise01 {
  def main(args: Array[String]): Unit = {
    // 测试
    val cur = new Time(10, 20)
    val other = new Time(10, 20)
    println(cur.before(other)) // 结果根据输入的对象不同而定
  }
} class Time(hrs: Int, min: Int) { // 主构造器
  private val hours: Int = hrs // 只读属性
  private val minutes: Int = min // 只读属性   def before(other: Time): Boolean = { // 方法
    if (hours < other.hours) { // 如果当前的小时数小于other
      true
    } else if (hours > other.hours) { // 如果当前的小时数大于other
      false
    } else { // 小时数相等,判断分钟
      if (minutes < other.minutes) true else false
    }
  } }

2、创建一个 Student 类,加入可读写的 JavaBeans 属性 name (类型为 String)和 id (类型为 Long)。
(1)有哪些方法被生产?(用 javap 查看,该指令可以查看 .class 文件的反编译的方法声明,还可以看到反汇编代码)
(2)你可以在 Scala 中调用 JavaBeans 的 getter 和 setter 方法吗?

// 先进行编译的命令
scalac Student.scala
// 查看反编译的方法声明的命令
javap Student
// 查看反汇编代码的命令
javap -c Student

示例代码如下:

import scala.beans.BeanProperty

class Student {
  // 读写属性
  @BeanProperty var name: String = _
  @BeanProperty var id: Long = _
}

演示截图如下:

3、练习使用包的各种声明方式,并查看他们的不同。
答:一共有三种导入方式。在一般情况下:我们使用相对路径来引入包,只有当包名冲突时,使用绝对路径来处理。
示例代码如下:

object TestBean {
  def main(args: Array[String]): Unit = {
    val m = new Manager("jack")
    println("m=" + m)
  }
} class Manager(var name: String) {
  // 第一种形式:相对路径引入
  // import scala.beans.BeanProperty
  // @BeanProperty var age: Int = _
  // 第二种形式:和第一种一样,都是相对路径引入
  // @scala.beans.BeanProperty var age2: Int = _
  // 第三种形式:是绝对路径引入,可以解决包名冲突
  @_root_.scala.beans.BeanProperty var age3: Int = _
}

4、编写一段程序,将 Java 哈希映射(Java 中的 HashMap)中的所有元素拷贝到 Scala 哈希映射(Scala 中的 HashMap)。用引入语句重命名这两个类。
示例代码如下:

package com.atguigu.chapter07.homework

// 说明:
// 1、当我们继承了App后,就可以直接在这个类中执行代码,不需要再写 main 入口了。
object Exercise03 extends App {   import java.util.{HashMap => JavaHashMap} // 重命名 Java 中的 HashMap
  import collection.mutable.{HashMap => ScalaHashMap, _} // 重命名 Scala 中的 HashMap   val javaMap = new JavaHashMap[Int, String] // 创建 Java 的 HashMap,其中 [Int, String] 是泛型
  javaMap.put(1, "One");  // 加入了四对 key-val
  javaMap.put(2, "Two");
  javaMap.put(3, "Three");
  javaMap.put(4, "Four");   val scalaMap = new ScalaHashMap[Int, String] // 创建 Scala 的 HashMap,其中 [Int, String] 是泛型   // 说明
  // 1、javaMap.keySet().toArray,这里是将 javaMap 的 key 转成数组
  // 2、key.asInstanceOf[Int] 将 key 强转成 Int 类型
  // 3、javaMap.get(key),得到这个 key 对应 value
  // 4、(key.asInstanceOf[Int] -> javaMap.get(key))  是 key -> value
  // 5、+= 将 key -> value 加入(拷贝)到 scalaMap
  for (key <- javaMap.keySet().toArray) {
    scalaMap += (key.asInstanceOf[Int] -> javaMap.get(key))
  }
  println(scalaMap) // Map(2 -> Two, 4 -> Four, 1 -> One, 3 -> Three)
  println(scalaMap.mkString(" ")) // 2 -> Two 4 -> Four 1 -> One 3 -> Three
}

输出结果如下:

Map(2 -> Two, 4 -> Four, 1 -> One, 3 -> Three)
2 -> Two 4 -> Four 1 -> One 3 -> Three

大数据技术之_16_Scala学习_05_面向对象编程-中级的更多相关文章

  1. 大数据技术之_16_Scala学习_06_面向对象编程-高级+隐式转换和隐式值

    第八章 面向对象编程-高级8.1 静态属性和静态方法8.1.1 静态属性-提出问题8.1.2 基本介绍8.1.3 伴生对象的快速入门8.1.4 伴生对象的小结8.1.5 最佳实践-使用伴生对象解决小孩 ...

  2. 大数据技术之_16_Scala学习_04_函数式编程-基础+面向对象编程-基础

    第五章 函数式编程-基础5.1 函数式编程内容说明5.1.1 函数式编程内容5.1.2 函数式编程授课顺序5.2 函数式编程介绍5.2.1 几个概念的说明5.2.2 方法.函数.函数式编程和面向对象编 ...

  3. 大数据技术之_16_Scala学习_09_函数式编程-高级

    第十三章 函数式编程-高级13.1 偏函数(partial function)13.1.1 提出一个需求,引出思考13.1.2 解决方式-filter + map 返回新的集合13.1.3 解决方式- ...

  4. 大数据技术之_16_Scala学习_01_Scala 语言概述

    第一章 Scala 语言概述1.1 why is Scala 语言?1.2 Scala 语言诞生小故事1.3 Scala 和 Java 以及 jvm 的关系分析图1.4 Scala 语言的特点1.5 ...

  5. 大数据技术之_16_Scala学习_02_变量

    第二章 变量2.1 变量是程序的基本组成单位2.2 Scala 变量的介绍2.2.1 概念2.2.2 Scala 变量使用的基本步骤2.3 Scala 变量的基本使用2.4 Scala 变量使用说明2 ...

  6. 大数据技术之_16_Scala学习_08_数据结构(下)-集合操作+模式匹配

    第十一章 数据结构(下)-集合操作11.1 集合元素的映射-map11.1.1 map 映射函数的操作11.1.2 高阶函数基本使用案例1+案例211.1.3 使用 map 映射函数来解决11.1.4 ...

  7. 大数据技术之_16_Scala学习_07_数据结构(上)-集合

    第十章 数据结构(上)-集合10.1 数据结构特点10.1.1 Scala 集合基本介绍10.1.2 可变集合和不可变集合举例10.2 Scala 不可变集合继承层次一览图10.2.1 图10.2.2 ...

  8. 大数据技术之_16_Scala学习_13_Scala语言的数据结构和算法_Scala学习之旅收官之作

    第十九章 Scala语言的数据结构和算法19.1 数据结构(算法)的介绍19.2 看几个实际编程中遇到的问题19.2.1 一个五子棋程序19.2.2 约瑟夫问题(丢手帕问题)19.2.3 其它常见算法 ...

  9. 大数据技术之_09_Flume学习_Flume概述+Flume快速入门+Flume企业开发案例+Flume监控之Ganglia+Flume高级之自定义MySQLSource+Flume企业真实面试题(重点)

    第1章 Flume概述1.1 Flume定义1.2 Flume组成架构1.2.1 Agent1.2.2 Source1.2.3 Channel1.2.4 Sink1.2.5 Event1.3 Flum ...

随机推荐

  1. 【Android】完善Android学习(六:API 4.0)

    备注:之前Android入门学习的书籍使用的是杨丰盛的<Android应用开发揭秘>,这本书是基于Android 2.2API的,目前Android已经到4.4了,更新了很多的API,也增 ...

  2. 牛客多校第五场-D-inv

    链接:https://www.nowcoder.com/acm/contest/143/D来源:牛客网 题目描述 Kanade has an even number n and a permutati ...

  3. asyncio 实现 aiohttp

    #asyncio 没有提供http协议的接口 aiohttp import asyncio import socket from urllib.parse import urlparse async ...

  4. jQuery拖拽 & 弹出层

    了解更多请查看 官网 和 API iDrag & iDialog 介绍 特点: iDialog.js依赖于jquery编写的简单易用的对话框,同时还可以通过添加css3,改变对话框的展现动画. ...

  5. android设计准则

    ------------缘由-------------------------------------------------------------------------------------- ...

  6. RabbitMq related

    # RabbitMq related Integration of message queuing tools with systems is the usual solution to handle ...

  7. [Unity]插件Node Editor介绍 实现类似状态机画布的扩展

    Unity自带的动画状态机有一套对策划非常友好的UI.但是Unity官方没有公开这些控件的api.除了Asset Store里一些已有的方案,我在这里介绍一个在github上的开源项目,封装了底层,但 ...

  8. MSSQL备份脚本

    ) ) ) ),),':',''),' ',''),'-',''),'.','') set @name=N'DEMO'+@temp+'-完整 数据库 备份' set @disk=N'F:\Backup ...

  9. js_时间戳和时间格式之间的转换。

    关于我的理解,简单明了点: 时间戳:把一个日期使用一个数字表示出来,这个数字就是这个日期的秒数. 日期:就是我们常见的时间表现形式. 时间戳对于一般看时间不够直观明了,可是在程序的世界里作用可大了. ...

  10. linux系统环境 ——(四)

    默认有6个命令交互通道和一个图形界面交互通道,默认进入到的是图形界面通道 命令交互模式切换:ctrl+alt+f1---f6 图形交互界面 ctrl+alt+f7 1.图形界面交互模式 - termi ...