admin管理员组文章数量:1794759
七、Scala从入门到精通一一面向对象编程(中级)
目录
- 7、包
- 7.1、看一个应用场景
- 7.2、回顾-Java包的三大作用
- 7.3、回顾-Java打包命令
- 7.4、快速入门
- 7.5、Scala包的基本介绍
- 7.6、Scala包快速入门
- 7.7、Scala包的特点概述
- 7.8、scala包的命名
- 7.9、Scala会自动引入的常用包
- 7.10、Scala包注意事项和使用细节
- 7.11、包对象
- 7.12、包对象的应用案例
- 7.13、包对象的底层实现机制分析(重点)
- 7.14、包对象的注意事项
- 8、包的可见性问题
- 8.1、Scala包的可见性介绍
- 8.2、Scala中包的可见性和访问修饰符的使用
- 9、包的引入
- 9.1、scala引入包的基本介绍
- 9.2、Scala引入包的细节和注意事项
- 10、面向对象编程方法-抽象
- 11、面对对象三大特征
- 11.1、基本介绍
- 11.2、封装介绍
- 11.3、封装的理解和好处
- 11.4、如何体现封装
- 11.5、封装的实现步骤
- 11.6、快速入门案列
- 11.7、Scala封装的注意事项的小结
- 12、面向对象编程-继承
- 12.1、Java继承的简单回顾
- 12.2、继承基本介绍和示意图
- 12.3、Scala继承的基本语法
- 12.4、Scala继承快速入门
- 12.5、Scala继承给编程带来的便利
- 12.6、scala子类继承了什么,怎么继承了?
- 12.7、重写方法
- 12.8、Scala中类型检查和转换
- 12.9、Scala中超类的构造
- 12.10、覆写字段
- 12.11、抽象类
- 12.12、Scala抽象类使用的注意事项和细节讨论:
- 12.13、匿名子类
- 12.14、继承层级
- 13、面向对象编程作业
现在有两个程序员共同开发一个项目,程序员xiaoming希望定义一个类取名Dog,程序员xiaoqiang也想定义一个类也叫Dog。两个程序员为此还吵了起来,怎么办?==》使用包即可以解决这个问题.
7.2、回顾-Java包的三大作用1、区分相同名字的类 2、当类很多时,可以很好的管理类 3、控制访问范围
7.3、回顾-Java打包命令打包基本语法packagecom.包名;
打包的本质分析 实际上就是创建不同的文件夹来保存类文件,画出示意图。
7.4、快速入门使用打包技术来解决上面的问题,不同包下Dog类
package com.test.chapter07.javapackage; public class TestTiger{ public static void main(String[]args){ //使用xm的Tiger com.test.chapter07.javapackage.xm.Tiger tiger01=newcom.test.chapter07.javapackage.xm.Tiger(); //使用xh的Tiger com.test.chapter07.javapackage.xh.Tiger tiger02=newcom.test.chapter07.javapackage.xh.Tiger(); } } 7.5、Scala包的基本介绍和Java一样,Scala中管理项目可以使用包,但Scala中的包的功能更加强大,使用也相对复杂些,下面我们学习Scala包的使用和注意事项。
7.6、Scala包快速入门使用打包技术来解决上面的问题,不同包下Dog类
packagecom.test.chapter07.scalapackage object TestTiger{ def main(args:Array[String]):Unit={ //使用xh的Tiger val tiger1=new com.test.chapter07.scalapackage.xh.Tiger //使用xm的Tiger val tiger2=newcom.test.chapter07.scalapackage.xm.Tigerprintln(tiger1+""+tiger2) } } 7.7、Scala包的特点概述基本语法 package 包名
Scala包的三大作用(和Java一样) 1、区分相同名字的类 2、当类很多时,可以很好的管理类 3、控制访问范围
Scala中包名和源码所在的系统文件目录结构要可以不一致,但是**编译后的字节码文件路径和包名会保持一致(**这个工作由编译器完成)。
packagecom.test.chapter07.scalapackage.hello2 object TestTiger{ defmain(args:Array[String]):Unit={ //使用xh的Tiger val tiger1=newcom.atguigu.chapter07.scalapackage.xh.Tiger //使用xm的Tiger val tiger2=newcom.atguigu.chapter07.scalapackage.xm.Tigerprintln(tiger1+""+tiger2) } } class Employee{ } 7.8、scala包的命名命名规则:
只能包含数字、字母、下划线、小圆点.,但不能用数字开头, 也不要使用关键字。 demo.class.exec1 //错误 , 因为class是关键字 demo.12a // 错误,因为不能以数字开头
命名规范:
一般是小写字母+小圆点一般是 com.公司名.项目名.业务模块名 比如: com.sina.edu.user com.sohu.bank.order
7.9、Scala会自动引入的常用包 7.10、Scala包注意事项和使用细节1、scala进行package 打包时,可以有如下形式。
package com.test.scala class Person{ val name = "Nick" def play(message: String): Unit ={ println(this.name + " " + message) } } //代码说明 传统的方式 package com.test package scala class Person{ val name = "Nick" def play(message: String): Unit ={ println(this.name + " " + message) } } //代码说明 :和第一种方式完全等价 package com.test{ package scala{ class Person{ val name = "Nick" def play(message: String): Unit ={ println(this.name + " " + message) } } } } //代码说明2、包也可以像嵌套类那样嵌套使用(包中有包), 这个在前面的第三种打包方式已经讲过了,在使用第三种方式时的好处是:程序员可以在同一个文件中,将类(class / object)、trait 创建在不同的包中,这样就非常灵活了
代码说明 //1.packagecom.test{}表示我们创建了包com.test,在{}中 //我们可以继续写它的子包scala//com.test.scala,还可以写类,特质trait,还可以写object //2.即sacla支持,在一个文件中,可以同时创建多个包,以及给各个包创建类,trait和object package.test{//包com.test class User{ // 在com.test包下创建一个User类 } package.scala2{//创建包com.test.scala2 class User{//在com.test.scala2包下创建个User类 } } package.scala{//包com.test.scala class Person{//表示在com.test.scala下创建类Person val name="Nick" def play(message:String):Unit{ println(this.name+""+message) } } object Test100{//表示在com.test.scala创建objectTest def main(args:Array[String]):Unit={ println("ok") } } } }3、作用域原则:可以直接向上访问。即:Scala中子包中直接访问父包中的内容,大括号体现作用域。(提示:Java中子包使用父包的类,需要import)。在子包和父包类重名时,默认采用就近原则,如果希望指定使用某个类,则带上包名即可
package com.test{ //这个类就是在com.test包下 class User{ } //这个类对象就是在Monster$ , 也在com.test包下 object Monster { } class Dog { } package scala { //这个类就是在com.test.scala包下 class User{ } //这个Test 类对象 object Test { def main(args: Array[String]): Unit = { //子类可以直接访问父类的内容 var dog = new Dog() println("dog=" + dog) //在子包和父包 类重名时,默认采用就近原则. var u = new User() println("u=" + u) //在子包和父包 类重名时,如果希望指定使用某个类,则带上包路径 var u2 = new com.test.User() println("u2=" + u2) } } } }4、父包要访问子包的内容时,需要import对应的类等
package com.test{ //引入在com.test包中希望使用到子包的类Tiger,因此需要引入. import com.test.scala.Tiger //这个类就是在com.test包下 class User{ } package scala { //Tiger 在 com.test.scala 包中 class Tiger {} } object Test2 { def main(args: Array[String]): Unit = { //如果要在父包使用到子包的类,需要import val tiger = new Tiger() println("tiger=" + tiger) } } }5、可以在同一个.scala文件中,声明多个并列的package(建议嵌套的pakage不要超过3层) 6、包名可以相对也可以绝对,比如,访问BeanProperty的绝对路径是:root. scala.beans.BeanProperty ,在一般情况下:我们使用相对路径来引入包,只有当包名冲突时,使用绝对路径来处理
7.11、包对象基本介绍:包可以包含类、对象和特质trait,但不能包含函数/方法或变量的定义。这是Java虚拟机的局限。为了弥补这一点不足,scala提供了包对象的概念来解决这个问题
7.12、包对象的应用案例 package com.test{ //每个包都可以有一个包对象。你需要在父包(com.test)中定义它,且名称与子包一样。 package object scala { var name = "jack" def sayOk(): Unit = { println("package object sayOk!") } } package scala { class Test { def test() : Unit ={ //这里的name就是包对象scala中声明的name println(name) sayOk()//这个sayOk 就是包对象scala中声明的sayOk } } object TestObj { def main(args: Array[String]): Unit = { val t = new Test() t.test() //因为TestObje和scala这个包对象在同一包,因此也可以使用 println("name=" + name) }}}} 7.13、包对象的底层实现机制分析(重点)1、当创建包对象后,在该包下生成 public final class package 和 public final class package$ 2、通过 package$ 的一个静态实例完成对包对象中的属性和方法的调用。
7.14、包对象的注意事项1、每个包都可以有一个包对象。你需要在父包中定义它。如图 2、包对象名称需要和包名一致,一般用来对包的功能补充
8、包的可见性问题 8.1、Scala包的可见性介绍在Java中,访问权限分为:public,private,protected和默认。在Scala中,你可以通过类似的修饰符达到同样的效果。但是使用上有区别 代码案例:
object Testvisit { 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) }} object Clerk{ def test(c : Clerk): Unit = { //这里体现出在伴生对象中,可以访问c.sal println("test() name=" + c.name + " sal= " + c.sal) }} 8.2、Scala中包的可见性和访问修饰符的使用1、当属性访问权限为默认时,从底层看属性是private的,但是因为提供了xxx_$eq()[类似setter]/xxx()[类似getter] 方法,因此从使用效果看是任何地方都可以访问) 2、当方法访问权限为默认时,默认为public访问权限 3、private为私有权限,只在类的内部和伴生对象中可用 4、protected为受保护权限,scala中受保护权限比Java中更严格,只能子类访问,同包无法访问 5、在scala中没有public关键字,即不能用public显式的修饰属性和方法。 6、包访问权限(表示属性有了限制。同时包也有了限制),这点和Java不一样,体现出Scala包使用的灵活性
package com.test.scala class Person { private[scala] val pname="hello" // 增加包访问权限后,1.private同时起作用。不仅同类可以使用 2. 同时com.test.scala中包下其他类也可以使用 } 当然,也可以将可见度延展到上层包 private[test] val description="zhangsan" 说明:private也可以变化,比如protected[test], 非常的灵活。7、整体的代码演示
package com.test.chapter07.visit object TestVisit { def main(args: Array[String]): Unit = { val c = new Clerk() // println(c.age) 无法访问 c.showInfo() Clerk.test(c) // 创建Person对象 val p1 = new Person println(p1.name) } } class Clerk { var name: String = "jack" //可读 private var sal: Double = 9999.9 //可读可写 protected var age: Int = 10 var job: String = "大数据工程师" def showInfo(): Unit = { // 在奔雷可以使用私有的 println("name=" + name + "\\n" + "sal=" + sal) } } // 当一个文件中出现了 class Clerk 和 object Clerk // 1、class Clerk 称为伴生类 // 2、object Clerk 的伴生对象 // 3、因为scala设计者将static拿掉,他就设计了 伴生类和伴生对象的概念 // 4、伴生类 写非静态的内容 伴生对象 就是静态内容 // 5、在伴生对象里可以访问私有对象 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 name = "jack" } 9、包的引入 9.1、scala引入包的基本介绍Scala引入包也是使用import,基本的原理和机制和Java一样,但是Scala中的import功能更加强大,也更灵活。
因为Scala语言源自于Java,所以java.lang包中的类会自动引入到当前环境中,而Scala中的scala包和Predef包的类也会自动引入到当前环境中,即起其下面的类可以直接使用
如果想要把其他包中的类引入到当前环境中,需要使用import语言
9.2、Scala引入包的细节和注意事项1、在Scala中,import语句可以出现在任何地方,并不仅限于文件顶部,import语句的作用一直延伸到包含该语句的块末尾。这种语法的好处是:在需要时在引入包,缩小import包的作用范围,提高效率。
class User{ import scala.beans.BeanProperty // 在需要时引入,作用域在{} @BeanProperty var name : String = "" class Dog{ // @BeanProperty var name : String = "" //error }2、Java中如果想要导入包中所有的类,可以通过通配符*,Scala中采用下_
import scala.beans._ // _表示将该包的所有内容引入,等价 *3、如果不想要某个包中全部的类,而是其中的几个类,可以采用选取器(大括号)
def test(): Unit ={ // 我们可以使用选择题,选择引入包的内容 ,这里,我们只引入HashMap HashSet import scala.collection.mutable.{HashMap,HashSet} var map = new HashMap() var set = new HashSet() }4、如果引入的多个包中含有相同的类,那么可以将不需要的类进行重命名进行区分,这个就是重命名
def test2(): Unit ={ // 下面的含义是 将java.util.HashMap重命名为JavaHashMap import java.util.{HashMap=>JavaHashMap,List} import scala.collection.mutable._ var map = new HashMap() // Scala var map1 = new JavaHashMap(); // Java }5、如果某个冲突的类根本就不会用到,那么这个类可以直接隐藏掉
importjava.util.{HashMap=>_,_}//含义为引入java.util包的所有类,但是忽略HahsMap类. var map=new HashMap()//此时的HashMap指向的是scala中的HashMap,而且idea工具,的提示也不会显示java.util的HashMaple 10、面向对象编程方法-抽象如何理解抽象
我们在前面去定义一个类时候,实际上就是把一类事物的共有的属性和行为提取出来,形成一个物理模型(模板)。这种研究问题的方法称为抽象。
11、面对对象三大特征 11.1、基本介绍面向对象编程有三大特征:封装、继承和多态。
11.2、封装介绍封装(encapsulation)就是把抽象出的数据和对数据的操作封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(成员方法),才能对数据进行操作。
11.3、封装的理解和好处1、隐藏实现细节 2、提可以对数据进行验证,保证安全合理 3、同时可以加入业务逻辑
11.4、如何体现封装1、对类中的属性进行封装 2、通过成员方法,包实现封装
11.5、封装的实现步骤1、将属性进行私有化 2、提供一个公共的set方法,用于对属性判断并赋值 def setXxx(参数名 : 类型) : Unit = { //加入数据验证的业务逻辑 属性 = 参数名 }
3、提供一个公共的get方法,用于获取属性的值 def getXxx() [: 返回类型] = { return 属性 }
11.6、快速入门案列那么在Scala中如何实现这种类似的控制呢? 1、请大家看一个小程序(TestEncap.scala),不能随便查看人的年龄,工资等隐私,并对输入的年龄进行合理的验证[要求1-120之间]。
object Test extends App { val p = new Person p.setAge(10) } class Person { var name: String = _ //var age ; //当是public时,可以随意的进行修改,不安全 private var age: Int = _ private var salary: Float = _ private var job: String = _ def setAge(age: Int): Unit = { if (age >= 0 && age <= 120) { this.age = age println(this.age) } else { println("输入的数据不合理"); //可考虑给一个默认值 this.age = 20 println(this.age) } } } //案例演示 11.7、Scala封装的注意事项的小结前面讲的Scala的封装特性,大家发现和Java是一样的,下面我们看看Scala封装还有哪些特点。
1、Scala中为了简化代码的开发,当声明属性时,本身就自动提供了对应setter/getter方法,如果属性声明为private的,那么自动生成的setter/getter方法也是private的,如果属性省略访问权限修饰符,那么自动生成的setter/getter方法是public的
2、因此我们如果只是对一个属性进行简单的set和get ,只要声明一下该属性(属性使用默认访问修饰符) 不用写专门的getset,默认会创建,访问时,直接对象.变量。这样也是为了保持访问一致性
3、从形式上看 dog.food 直接访问属性,其实底层仍然是访问的方法, 看一下反编译的代码就明白 4、有了上面的特性,目前很多新的框架,在进行反射时,也支持对属性的直接反射
12、面向对象编程-继承 12.1、Java继承的简单回顾class 子类名 extends 父类名 { 类体 } 子类继承父类的属性和方法
12.2、继承基本介绍和示意图继承可以解决代码复用,让我们的编程更加靠近人类思维.当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类(比如Student),在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends语句来声明继承父类即可。
和Java一样,Scala也支持类的单继承
12.3、Scala继承的基本语法class 子类名 extends 父类名 { 类体 }
12.4、Scala继承快速入门 object Extend01 { def main(args: Array[String]): Unit = { // 使用 val student = new Student student.name = "jack" // 调用了Student.name() student.studying() student.showInfo() } } class Person{ // Person类 var name:String = _ var age:Int = _ def showInfo(): Unit ={ println("学生的信如下") println("名字:"+this.name) } } //Student类继承Person class Student extends Person{ def studying(): Unit ={ // 这里可以使用父类的属性 println(this.name+"学习Scala中····") } } 12.5、Scala继承给编程带来的便利代码的复用性提高了 代码的扩展性和维护性提高了【面试官问:当我们修改父类时,对应的子类就会继承相应的方法和属性】
12.6、scala子类继承了什么,怎么继承了?子类继承了所有的属性,只是私有的属性不能直接访问,需要通过公共的方法去访问【debug代码验证可以看到】
// 说明 // 1.在Scala中,子类继承了父类的所有属性 // 2.但是private的属性和访问无法访问 object Extend02 { def main(args: Array[String]): Unit = { val sub = new Sub sub.sayOk() //sub.test200() 编译器不让过 } } // 父类(基类) class Base { var n1: Int = 1 // public n1() , public n1_$eq() protected var n2: Int = 2 private var n3: Int = 3 //private n1() , private n1_$eq() def test100(): Unit = { // 默认 public test100() println("base 100") } protected def test200(): Unit = { // public println("base 200") } private def test300(): Unit = { // private println("base 300") } } // Sub 继承 Base class Sub extends Base { def sayOk(): Unit = { this.n1 = 20 // 这里访问本质 this.n1_$eq() this.n2 = 40 println("范围=" + this.n1 + this.n2) test100() test200() // 子类中使用protected } } 12.7、重写方法说明: scala明确规定,重写一个非抽象方法需要用override修饰符,调用超类的方法使用super关键字
object MethodOverride01 { def main(args: Array[String]): Unit = { val emp = new Emp100 emp.printName() } } // Person类 class Person100 { var name: String = "tom" def printName() { // 输出名字 println("Person printName() " + name) } def sayHi(): Unit ={ println("SayHI···") } } // 这里我们继承Person class Emp100 extends Person100 { // 这里需要显示的使用override override def printName() { println("Emp printName()" + name) // 在子类中需要去调用父类的方法,使用super super.printName() sayHi() } def sayHello(): Unit ={ } } 12.8、Scala中类型检查和转换要测试某个对象是否属于某个给定的类,可以用isInstanceOf方法。用asInstanceOf方法将引用转换为子类的引用。classOf获取对象的类名。
1、classOf[String]就如同Java的 String.class 。 2、obj.isInstanceOf[T]就如同Java的obj instanceof T 判断obj是不是T类型。 3、obj.asInstanceOf[T]就如同Java的(T)obj 将obj强转成T类型。
object TypeConvert { def main(args: Array[String]): Unit = { // classOf的使用,可以得到类名 println(classOf[String]) //输出 val s = "king" println(s.getClass.getName) // 使用反射机制 // isInstanceOf asInstanceOf var p1 = new Person200 var emp = new Emp200 // 将子类引用给父类(向上转型,自动转换) p1 = emp // 将父类的引用重新转成子类引用(多态),即向下转型 var emp2 = p1.asInstanceOf[Emp200] emp2.sayHello() } } // Person类 class Person200 { var name: String = "tom" def printName() { // 输出名字 println("Person printName() " + name) } def sayHi(): Unit = { println("SayHI···") } } // 这里我们继承Person class Emp200 extends Person200 { // 这里需要显示的使用override override def printName() { println("Emp printName()" + name) // 在子类中需要去调用父类的方法,使用super super.printName() sayHi() } def sayHello(): Unit ={ } }最佳实践 类型检查和转换的最大价值在于:可以判断传入对象的类型,然后转成对应的子类对象,进行相关操作,这里也体现出多态的特点。
object TypeConvertCase { def main(args: Array[String]): Unit = { val stu = new Student400 val emp = new Emp400 test(stu) test(emp) } // 写了一个参数 多态代码 // 因为在OOP中 一个父类的引用可以接收所有子类的引用,多态(多态参数) def test(p: Person400): Unit = { // 使用Scala类型检查和转换 if (p.isInstanceOf[Emp400]) { p.asInstanceOf[Emp400].ShowInfo() // p.isInstanceOf[Emp400]对p的类型没有任何变化,而是返回的Emp400 } else if (p.isInstanceOf[Student400]) { p.asInstanceOf[Student400].cry() }else{ println("转换失败") } } } class Person400 { def printName(): Unit = { println("Person400 printName") } def SayOk(): Unit = { println("Person400 SayOk") } } class Student400 extends Person400 { val stuid = 100 override def printName(): Unit = { println("Student400 printName") } def cry(): Unit = { println("学生的id=" + this.stuid) } } class Emp400 extends Person400 { val Empid = 100 override def printName(): Unit = { println("Emp400 printName") } def ShowInfo(): Unit = { println("雇员的id=" + this.Empid) } } 12.9、Scala中超类的构造1、类有一个主构器和任意数量的辅助构造器,而每个辅助构造器都必须先调用主构造器(也可以是间接调用.),这点在前面我们说过了。 2、只有主构造器可以调用父类的构造器。辅助构造器不能直接调用父类的构造器。在Scala的构造器中,你不能调用super(params)
object ScalaBaseConstrator { def main(args: Array[String]): Unit = { // 分析一下他的执行流程 // 1.因为scala仍然遵守先构建父类的部分 extends Person700() // 2. Person ... // 3. Emp ... (Emp700的主构造器) // val emp = new Emp700 // 分析一下他的执行流程 // 1.因为scala仍然遵守先构建父类的部分 extends Person700() // 2. Person ... // 3. Emp ... (Emp700的主构造器) // 4. Emp 辅助构造器··· println("============") val emp2 = new Emp700("marry") println("************") val emp3 = new Emp700("smith") // Person... name = terry // Emp println("·················") val emp4 = new Emp700("terry", 400) emp4.showinfo() // terry } } // 父类 Person class Person700(pName: String) { var name = pName println("Person...") def this() { this("默认的名字") println("默认的名字") } } // 子类 Emp 继承Person class Emp700(eName: String, eage: Int) extends Person700(eName) { println("Emp .........") // 这是辅助构造器 def this(name: String) { this(name,100) // 必须调用主构造器 this.name = name println("Emp 辅助构造器") } def showinfo(): Unit = { println("雇员的名字是:" + name) } } 12.10、覆写字段在Scala中,子类改写父类的字段,我们称为覆写/重写字段。覆写字段需使用 override修饰
我们看一个关于覆写字段的案例
object ScalaFiledOverrideDemo { def main(args: Array[String]): Unit = { val obj1: AAA = new BBB val obj2: BBB = new BBB //obj1.age => obj1.age() // 动态绑定机制 //obj2.age => obj2.age() println("obj1.age"+obj1.age) println("obj2.age"+obj2.age) } } class AAA { val age: Int = 10 // 会生成 public Age() } class BBB extends AAA { override val age: Int = 20 //会生成 public Age() }覆写字段的注意事项和细节 1、def只能重写另一个def(即:方法只能重写另一个方法) 2、val只能重写另一个val 属性 或 重写不带参数的def 3、var只能重写另一个抽象的var属性 抽象属性:声明未初始化的变量就是抽象的属性,抽象属性在抽象类
var重写抽象的var属性小结 1、一个属性没有初始化,那么这个属性就是抽象属性 2、抽象属性在编译成字节码文件时,属性并不会声明,但是会自动生成 抽象方法,所以类必须声明为抽象类 3、如果是覆写一个父类的抽象属性,那么override 关键字可省略 [原因:父类的抽象属性,生成的是抽象方法,因此就不涉及到方法重写的概念,因此override可省略]
12.11、抽象类在Scala中,通过abstract关键字标记不能被实例化的类。方法不用标记abstract,只要省掉方法体即可。抽象类可以拥有抽象字段,抽象字段/属性就是没有初始值的字段
快速入门案列 我们看看如何把Animal做成抽象类, 包含一个抽象的方法cry()
object AbstractDemo01 { def main(args: Array[String]): Unit = { println("xxx") } } abstract class Animal { var name: String // 抽象的字段 var age: Int // 抽象的字段 var color: String = "balck" def cry() // 抽象方法,不需要标记abstract }抽象类基本语法 abstract class Person() { // 抽象类 var name: String // 抽象字段, 没有初始化 def printName // 抽象方法, 没有方法体
}
说明:抽象类的价值更多是在于设计,是设计者设计好后,让子类继承并实现抽象类(即:实现抽象类的抽象方法)
12.12、Scala抽象类使用的注意事项和细节讨论:1、抽象类不能被实例 2、抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法 3、一旦类包含了抽象方法或者抽象属性,则这个类必须声明为abstract
4、抽象方法不能有主体,不允许使用abstract修饰。 5、如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法和抽象属性,除非它自己也声明为abstract类。 6、抽象方法和抽象属性不能使用private、final 来修饰,因为这些关键字都是和重写/实现相违背的。 7、抽象类中可以有实现的方法. 8、子类重写抽象方法不需要override,写上也不会错.
12.13、匿名子类和Java一样,可以通过包含带有定义或重写的代码块的方式创建一个匿名的子类. scala匿名子类案例
bject ScalaNoNameDemo02 { def main(args: Array[String]): Unit = { val monster = new Monster // 匿名子类 {override def cry(): Unit = { println("ok···") } override var name: String = _ } monster.cry() } } abstract class Monster{ var name:String def cry() } 12.14、继承层级Scala继承层级一览图 对上图的一个小结: 1、在scala中,所有其他类都是AnyRef的子类,类似Java的Object。2、AnyVal和AnyRef都扩展自Any类。Any类是根节点 3、Any中定义了isInstanceOf、asInstanceOf方法,以及哈希方法等。 4、Null类型的唯一实例就是null对象。可以将null赋值给任何引用,但不能赋值给值类型的变量 5、Nothing类型没有实例。它对于泛型结构是有用处的,举例:空列表Nil的类型是List[Nothing],它是List[T]的子类型,T可以是任何类。
13、面向对象编程作业练习1 编写Computer类,包含CPU、内存、硬盘等属性,getDetails方法用于返回Computer的详细信 编写PC子类,继承Computer类,添加特有属性【品牌brand】 编写NotePad子类,继承Computer类,添加特有属性【颜色color】编写Test Object,在main方法中创建PC和NotePad对象,分别对象中特有的属性赋值,以及从Computer类继承的属性赋值,并使用方法并打印输出信
object Exercise01 { def main(args: Array[String]): Unit = { val pc = new PC() val notePad = new NotePad() pc.Cpu = "i7-8700" pc.RAM = "12g" pc.Disk = "ZDisk" pc.Brand = "华硕" notePad.clolr = "黑色" println(pc.Brand) pc.getDetails() println(notePad.clolr) } } class Computer { var Cpu: String = "" var RAM: String ="" var Disk: String ="" def getDetails(): Unit = { println("Cpu=" + Cpu + "\\nRam=" + RAM + "\\nDISk=" + Disk) } } class PC extends Computer{ var Brand :String = "" } class NotePad extends Computer{ var clolr:String = "" }练习2 根据下图实现类。在TestCylinder类中创建Cylinder类的对象,设置圆柱的底面半径和高,并输出圆柱的体积
object Exercise02 { def main(args: Array[String]): Unit = { val area1 = new TestCylinder var area2 = new area1.Cylinder println(area2.findArea(5.0)) println(area2.findVolume(2)) } } class TestCylinder { class Cylinder { @BeanProperty var radius: Double = 1 def findArea(radius: Double): Double = { var area: Double = 0 area = radius * radius * 3.14 area } @BeanProperty var length: Double = 1 def findVolume(length: Double): Double = { var volume: Double = 0 volume = findArea(radius) * length volume } } }练习3(多态应用) 定义员工类Employee,包含姓名和月工资,以及计算年工资getAnnual的方法。普通员工和经理继承了员工,经理类多了奖金bonus属性和管理manage方法,普通员工类多了work方法,普通员工和经理类要求分别重写getAnnual方法
测试类中添加一个方法showEmpAnnal,实现获取任何员工对象的年工资,并在main方法中调用该方法
测试类中添加一个方法,testWork,如果是普通员工,则调用work方法,如果是经理,则调用manage方法
object Exercise03 { def main(args: Array[String]): Unit = { val worker = new Worker2 val manager = new Manager showEmployeeAnnual(worker) showEmployeeAnnual(manager) testEmployee(worker) testEmployee(manager) } def showEmployeeAnnual(e: Employee): Unit = { println(e.getAnnual) } def testEmployee(e: Employee): Unit = { if (e.isInstanceOf[Worker2]) e.asInstanceOf[Worker2].work() else if (e.isInstanceOf[Manager]) e.asInstanceOf[Manager].manage() } } abstract class Employee { // 定义抽象属性 var name: String var salary: Double // 定义抽象方法 def getAnnual: Double } class Worker2 extends Employee { override var name: String = "工人" override var salary: Double = 2000 override def getAnnual: Double = { this.salary * 12 } def work(): Unit = { println("工人工作~") } } class Manager extends Employee { override var name: String = "经理" override var salary: Double = 20000.0 var bonus = 60000 override def getAnnual: Double = { this.salary * 12 + this.bonus } def manage(): Unit = { println("经理在管理~") } }版权声明:本文标题:七、Scala从入门到精通一一面向对象编程(中级) 内容由林淑君副主任自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.xiehuijuan.com/baike/1686514376a76214.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论