第七章 面向对象编程-中级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. Java中xml2json,json2xml

    在JAVA中xml与json数据互相转换 package com.sgcc.platform.common.utils; import static java.lang.String.format; ...

  2. UVA 10766 Organising the Organisation

    https://vjudge.net/problem/UVA-10766 题意: n个员工,除总经理外每个人只能有一个直接上级 有m对人不能成为直接的上下级关系 规定k为总经理 问员工分级方案 无向图 ...

  3. 【转载】Lua脚本语法说明(修订)

    原文:http://www.cnblogs.com/ly4cn/archive/2006/08/04/467550.html 挑出来几个 .逻辑运算 and, or, not 其中,and 和 or ...

  4. HDU 1026 Ignatius and the Princess I (广搜)

    题目链接 Problem Description The Princess has been abducted by the BEelzebub feng5166, our hero Ignatius ...

  5. Centos7的iso everything与DVD以及Live的区别

    DVD.ISO 可以用安装程序安装的所有安装包,推荐镜像 Netinstall.iso 从网络安装或者救援系统 Everything.iso 包含centos7的一套完整的软件包,可以用来安装系统或者 ...

  6. Pythone3 sys模块

    1.sys.argv 可以实现从程序外部向程序传递参数2.sys.exit() 程序中间退出,exit(0)正常退出,其他为异常退出3.sys.getdefaultencoding() 获取系统编码方 ...

  7. 使用 ftrace 调试 Linux 内核,第 1 部分【转】

    转自:http://www.ibm.com/developerworks/cn/linux/l-cn-ftrace1/index.html ftrace 是 Linux 内核中提供的一种调试工具.使用 ...

  8. BZOJ 2460: [BeiJing2011]元素 贪心,线性基

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2460 解法:从大到小排序,依次贪心的添加到当前集合就可以了,需要动态维护线性基.用拟阵证明 ...

  9. 零基础学php的自学

    我们都知道,php语言作为一种专业建站的语言,没有华而不实,而是经受住了时间考验,成为一种值得学习的语言.现在国内众多的php学校也说明,php语言在当今有着广泛的市场需求. 那么零基础的同学如何学习 ...

  10. 启动另一个activity

    1. 只负责启动 Intent intent = new Intent(mContext, BookOrderActivity.class); Bundle mEmployeeBundle = new ...