文章目录 第七章 面向对象编程-中级 7.1 包 7.2 包的可见性 7.3 包的引入 7.4 面向对象编程方法-抽象 7.5 面向对象编程三大特征 7.6 面向对象编程方法-继承 7.7 作业04 第七章 面向对象编程-中级 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.class 和 package$.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.chap