7.1 包
7.1.1 看一个应用场景
现在有两个程序员共同开发一个项目,程序员xiaoming希望定义一个类取名Dog,程序员xiaohong也想定一个类也叫Dog,两个程序员还为此吵了起来,该怎么办?
--->使用包即可解决这个问题
7.1.2 回顾-Java包的三大作用
1) 区分相同名字的类
2) 当类很多时,可以很好的管理类
3) 控制访问范围
7.1.3 回顾-Java打包命令
-打包基本语法
package com.c;
-打包的本质分析
实际上就是创建不同的文件夹来保存类文件
7.1.4 快速入门
使用打包技术来解决上面的问题,不同包下Dog类
public class TestTiger {
public static void main(String[] args) {
//使用xm的Tiger
com.c.scala_exercise.javapackage.xm.Tiger tiger01 = new com.c.scala_exercise.javapackage.xm.Tiger();
//使用xh的Tiger
com.c.scala_exercise.javapackage.xh.Tiger tiger02 = new com.c.scala_exercise.javapackage.xh.Tiger();
System.out.println("tiger01=" + tiger01 + "tiger02=" + tiger02);
}
}
7.1.5 Scala包的基本介绍
和Java一样,Scala中管理项目可以使用包,但Scala中的包的功能更加强大,使用也相对复杂些
7.1.6 Scala包快速入门
使用打包技术来解决上面的问题,不同包下Dog类
object boke_demo01 { def main(args: Array[String]): Unit = { //使用xh的Tiger val tiger1 = new com.c.scala_exercise.scalapackage.xh.Tiger //使用xm的Tiger val tiger2 = new com.c.scala_exercise.scalapackage.xm.Tiger println(tiger1 + " " + tiger2) } }
7.1.7 Scala包的特点概述
-基本语法
package 包名
-Scala包的作用(和Java一样)
1) 区分相同名字的类
2) 当类很多时,可以很好的管理类
3) 控制访问范围
4) 可以对类的功能进行扩展
-Scala中包名和源码所在的系统文件目录结构可以不一致,但是编译后的字节码文件路径和包名会保持一致(这个工作由编译器完成)
object boke_demo01 { def main(args: Array[String]): Unit = { //使用xh的Tiger val tiger1 = new com.c.scala_exercise.scalapackage.xh.Tiger //使用xm的Tiger val tiger2 = new com.c.scala_exercise.scalapackage.xm.Tiger println(tiger1 + " " + tiger2) } } class Employee { }
7.1.8 包的命名
-命名规则:
只能包含数字、字母、下划线、小圆点.,但是不能用梳子开头,也不要使用关键字
demo.class.exercise //错误,因为class是关键字
demo.12a //错误,因为不能以梳子开头
-命名规范:
一般是小写字母+小圆点一般是
com.公司名.项目名.业务模块名 比如:com.baidu.io.model com.baidu.io.controller
7.1.9 Scala包注意事项和使用细节
1) Scala进行package打包时,可以有如下形式
//代码说明 //1. package com.boke{} 表示我们创建了包 com.boke ,在{}中 // 我们可以继续写它的子包 scala //com.boke.scala, 还可以写类,特质trait,还可以写object //2. 即Sacla支持,在一个文件中,可以同时创建多个包,以及给各个包创建类,trait和object package com.boke { //包 com.boke // class User { // 在com.boke包下创建个 User类 // def sayHello(): Unit = { // //想使用 com.boke.scala2包下的 Monster // import com.boke.scala2.Monster // val monster = new Monster() // } // } // // package scala2 { // 创建包 com.boke.scala2 // class User { // 在com.boke.scala2 包下创建个 User类 // } // // class Monster { // // // } // // } // //说明 //1. 在包中直接写方法,或者定义变量,就错误==>使用包对象的技术来解决 //2. package object scala表示创建一个包对象scala, 它是com.boke.scala这个包对应的包对象 //3. 每一个包都可以有一个包对象 //4. 包对象的名字需要和子包一样 //5. 在包对象中可以定义变量,方法 //6. 在包对象中定义的变量和方法,就可以在对应的包中使用 //7. 在底层这个包对象会生成两个类 package.class 和 package$.class package object scala { var name = "king" def sayHiv(): Unit = { println("package object scala sayHI~") } } package scala { //包 com.boke.scala class Person { // 表示在 com.boke.scala下创建类 Person val name = "Nick" def play(message: String): Unit = { println(this.name + " " + message) } } class User { def testUser(): Unit = { println("name = " + name) sayHiv() } } object Test1 { //表示在 com.boke.scala 创建object Test1 def main(args: Array[String]): Unit = { println("name=" + name) name = "yy" sayHiv() // println("ok") // //我们可以直接使用父包的内容 // //1.如果有同名的类,则采用就近原则来使用内容(比如包) // //2.如果就是要使用父包的类,则指定路径即可 // val user = new User // println("user=" + user) // // val user2 = new com.boke.User() // println("user2" + user2) } } } }
2) 包也可以像嵌套类那样嵌套使用(包中有包),好处:可以在同一个文件中,将类(class/object)、trait创建在不同的包中,这样就非常灵活了
3) 作用域原则:可以直接向上访问。即Scala中子包中直接访问父包的内容,大括号体现作用域。(提示:Java中子包使用父包的类,需要import)。在子包和父包类重名时,默认采用就近原则,如果希望指定使用某个类,则带上包名即可
4) 父包要访问子包的内容时,需要import对应的类等
5) 可以在同一个.scala文件中声明多个并列的package(建议嵌套的package不要超过3层)
6) 包名可以相对也可以绝对,比如访问 BeanProperty 的绝对路径是: _root_.scala.beans.BeanProperty,在一般情况下,我们使用相对路径来引入包,只有当包名冲突时,使用绝对路径来处理
import scala.beans.BeanProperty class Manager(var name: String) { //第一种形式 [使用相对路径引入包] @BeanProperty var age: Int = _ //第二种形式, 和第一种一样,都是相对路径引入 @scala.beans.BeanProperty var age2: Int = _ //第三种形式, 是绝对路径引入,可以解决包名冲突 @_root_.scala.beans.BeanProperty var age3: Int = _ } object TestBean { def main(args: Array[String]): Unit = { val m = new Manager("jack") println("m=" + m) } }
7.1.10 包对象
基本介绍:包可以包含类、对象和特质(trait),但不能包含函数/方法或变量的定义。这是Java虚拟机的局限。为了弥补这一点,Scala提供了包对象的概念来解决这个问题
7.1.11 包对象的应用案例
/说明 //1. 在包中直接写方法,或者定义变量,就错误==>使用包对象的技术来解决 //2. package object scala表示创建一个包对象scala, 它是com.boke.scala这个包对应的包对象 //3. 每一个包都可以有一个包对象 //4. 包对象的名字需要和子包一样 //5. 在包对象中可以定义变量,方法 //6. 在包对象中定义的变量和方法,就可以在对应的包中使用 //7. 在底层这个包对象会生成两个类 package.class 和 package$.class package object scala { var name = "king" def sayHiv(): Unit = { println("package object scala sayHI~") } } package scala { //包 com.boke.scala class Person { // 表示在 com.boke.scala下创建类 Person val name = "Nick" def play(message: String): Unit = { println(this.name + " " + message) } } class User { def testUser(): Unit = { println("name = " + name) sayHiv() } } object Test1 { //表示在 com.boke.scala 创建object Test1 def main(args: Array[String]): Unit = { println("name=" + name) name = "yy" sayHiv() } } }
7.1.12 包对象的底层的实现机制
如图所示:一个包对象会生成两个类package和package$
如图所示:说明了包去使用包对象的变量或者方法的原理
7.1.13 包对象的注意事项
1) 每个包都可以有一个包对象,需要在父包中定义它
2) 包对象名称需要和包名一致,一般用来对包的功能补充
7.2 包的可见性问题
7.2.1 回顾-Java访问修饰符基本介绍
Java提供四种访问控制修饰符号来控制方法和变量的访问权限(范围)
1) 公开级别:用public修饰,对外公开
2) 受保护级别:用protected修饰,对于子类和同一包中的类公开
3) 默认级别:没有修饰符,向同一个包中的类公开
4) 私有级别:用private修饰,只有类本身可以访问,不对外公开
7.2.2 回顾-Java中4中访问修饰符的访问范围
7.2.3 回顾-Java访问修饰符使用注意事项
1) 修饰符可以用来修饰类中的属性,成员方法以及类
2) 只有默认的和public才能修饰类!并且遵循上述访问权限的特点
7.2.4 Scala中包的可见性介绍
在Java中,访问权限分为:public,private,protected和默认。在Scala中,可以通过类似的修饰符达到同样的效果,但是使用上有所区别
案例演示
object boke_demo01 { 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 protected var age = 23 var job: String = "大数据工程师" 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) } }
7.2.5 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.scala.exercise class Person { //增加包访问权限后 //1.private同时起作用,不仅同类可以使用 //2.com.scala.exercise中包下的其它类也可以使用 private[exercise] val name = "Jack" //当然,也可以将可见度延展到上层包 private[scala] val age = 23 //说明:private可以变化,比如protected[scala],非常的灵活 }
7) 整体的案例演示
object boke_demo01 { 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 protected var age = 23 var job: String = "大数据工程师" def showInfo(): Unit = { //在本类可以使用私有的 println(" name " + name + " 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[scala] : 1,仍然是private 2. 在scala包(包括子包)下也可以使用name ,相当于扩大访问范围 protected[scala] val name = "Jack" }
7.3 包的引入
7.3.1 Scala引入包基本介绍
Scala引入包也是使用import,基本的原理跟机制和Java一样,但是Scala中的import功能更佳强大,也更灵活。因为Scala语言源自Java,所以java.lang包中的类会自动引入到当前环境中,而Scala中的scala包和predef包的类也会自动引入到当前环境中,即起其下的类可以直接使用。如果想要把其它包中的类引入到当前环境中,需要使用import
7.3.2 Scala引入包的细节和注意事项
1) 在Scala中,import语句可以出现在任何地方,并不仅限于文件顶部,import语句的作用一直延伸到包含该语句的块末尾。这种语法的好处是:在需要时引入包,缩小import包的作用范围,提高效率
class User { import scala.beans.BeanProperty @BeanProperty var name : String = "" } class Dog { @BeanProperty var name : String = "" //可以吗? No }
2) Java中如果想要导入包中所有的类,可以通过通配符*,Scala中采用_(下划线)
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} import scala.collection.mutable._ var map = new HashMap() // 此时的 HashMap 指向的是 scala 中的 HashMap var map1 = new JavaHashMap(); // 此时使用的 java 中 hashMap 的别名 }
5) 如果某个冲突的类根本就不会用到,那么这个类可以直接隐藏掉
import java.util.{ HashMap=>_, _} // 含义为 引入 java.util 包的所有类,但是忽略 HahsMap 类. var map = new HashMap()
7.4 面向对象编程方法-抽象
-如何理解抽象
我们在前面去定义一个类的时候,实际上就是把一类事物的共有的属性和行为提取出来,形成一个物理模型(模版),这种研究问题的方法称为抽象
7.5 面向对象编程三大特征
7.5.1 基本介绍
面向对象有三大特征:封装、继承、多态
7.5.2 封装介绍
封装(encapsulation)就是把抽象出的数据和对数据的操作封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(成员变量),才能对数据进行操作
7.5.3 封装的理解和好处
1) 隐藏实现细节
2) 可以对数据进行验证,保证安全合理
3) 同时可以加入业务逻辑
7.5.4 如何体现封装
1) 对类中的属性进行封装
2) 通过成员方法,包实现封装
7.5.5 封装的实现步骤
1) 将属性进行私有化
2) 提供一个公共的set方法,用于对属性判断并赋值
def setXxx(参数名 : 类型) : Unit = { //加入数据验证的业务逻辑 属性 = 参数名 }
3) 提供一个公共的get方法,用于获取属性的值
def getXxx() [: 返回类型] = { return 属性 }
7.5.6 Scala封装的注意事项的小结
1) Scala中为了简化代码的开发,当声明属性var时,本身就自动提供了对应setter/getter方法,如果属性声明为private的,那么自动生成的setter/getter方法也是private的,如果属性省略访问权限修饰符,那么自动生成的setter/getter方法时public的
2) 因此我们如果只是对一个属性进行简单的set和get,只要声明一下该属性(属性使用默认访问修饰符),不用写专门的set和get,默认会创建,访问时,直接对象.变量。这样也是为了保持访问一致性
3) 从形式上看 dog.food 直接访问属性,其实底层仍然是访问的方法,看一下反编译的代码就会明白
4) 有了上面的特性,目前很多新的框架,在进行反射时,也支持对属性的直接反射
7.6 面向对象编程-继承
7.6.1 Java继承的简单回顾
class 子类名 extends 父类名 { 类体 }
7.6.2 继承基本介绍和示意图
继承可以解决代码复用,让我们的编程更佳靠近人类的思维,当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类(比如Student),在父类中定义这些相同的属性和方法,所有的子类不需要重复定义这些属性和方法,只需要通过extends语句来声明继承父类即可。和Java一样,Scala也支持类的单继承
7.6.3 Scala继承的基本语法
class 子类名 extends 父类名 { 类体 }
7.6.4 Scala继承快速入门
object boke_demo01 { 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中....") } }
7.6.5 Scala继承给编程带来的便利
1) 代码的复用性提高了
2) 代码的扩展性和维护性提高了
7.6.6 Scala子类继承了什么,怎么继承了
子类继承了所有的属性,只是私有的属性不能直接访问,需要通过公共的方法去访问[debug代码验证可以看到]
//说明 //1. 在scala中,子类继承了父类的所有属性 //2. 但是private的属性和方法无法访问 object boke_demo01 { 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 n3() , private n3_$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 } }
7.6.7 Scala重写方法
说明:Scala明确规定,重写一个非抽象方法需要用override修饰符,调用超类的方法使用super关键字
object boke_demo01 { def main(args: Array[String]): Unit = { val emp = new Emp emp.printName() } } //Person类 class Person { var name: String = "tom" def printName() { //输出名字 println("Person printName() " + name) } def sayHi(): Unit = { println("sayHi...") } } //这里我们继承Person class Emp extends Person { //这里需要显式的使用override override def printName() { println("Emp printName() " + name) //在子类中需要去调用父类的方法,使用super super.printName() sayHi() } }
7.6.8 Scala中类型检查和转换
-基本介绍
要测试某个对象是否属于某个给定的类,可以用isInstanceOf方法。用asInstanceOf方法将引用转换为子类的引用。classOf获取对象名
classOf[String]就如同Java的String.class
obj.isInstanceOf[T]就如同Java的 obj instanceofT 判断obj是不是T类型
obj.asInstanceOf[T] 就如同Java的(T)obj 将obj强转成T类型
-案例演示
object boke_demo01 { def main(args: Array[String]): Unit = { //ClassOf的使用,可以得到类名 println(classOf[String]) // 输出 val s = "king" println(s.getClass.getName) //使用反射机制 //isInstanceOf asInstanceOf var p1 = new Person var emp = new Emp //将子类引用给父类(向上转型,自动) p1 = emp //将父类的引用重新转成子类引用(多态),即向下转型 var emp2 = p1.asInstanceOf[Emp] emp2.sayHello() } } //Person类 class Person { var name: String = "tom" def printName() { //输出名字 println("Person printName() " + name) } def sayHi(): Unit = { println("sayHi...") } } //这里我们继承Person class Emp extends Person { //这里需要显式的使用override override def printName() { println("Emp printName() " + name) //在子类中需要去调用父类的方法,使用super super.printName() sayHi() } def sayHello(): Unit = { } }
-最佳实践
类型检查和转换的最大价值在于:可以判断传入对象的类型,然后转成对应的子类对象,进行相关操作,这里也体现出多态的特点
7.6.9 Java中超类的构造
说明:从代码可以看出,在Java中,创建子类对象时,子类的构造器总是去调用一个父类的构造器(显示或者隐式调用)
public class JavaBaseConstractor {
public static void main(String[] args) {
//1.A()
//2.B()
B b = new B();
//1.A(String name) jack
//2.B(String name) jack
B b2 = new B("jack");
}
}
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()
//super();
System.out.println("B()");
}
public B(String name) {
super(name);
System.out.println("B(String name)" + name);
}
}
7.6.10 Scala中超类的构造
1) 类有一个主构造器和任意数量的辅助构造器,而每个辅助构造器都必须先调用主构造器(也可以是间接调用)
2) 只有主构造器可以调用父类的构造器,辅助构造器不能直接调用父类的构造器,在Scala的构造器中,不能调用super(params)
3) 案例演示
object boke_demo01 { def main(args: Array[String]): Unit = { //分析执行的顺序 //1.Person... //2.Emp .... //3.Emp 辅助构造器~ val emp1 = new Emp("smith") println("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") //Person.. //Emp .... val emp2 = new Emp("terry", 10) emp2.showInfo() // 雇员的名字 terry } } //父类Person class Person(pName: String) { var name = pName println("Person...") def this() { this("默认的名字") println("默认的名字") } } //子类Emp继承Person class Emp(eName: String, eAge: Int) extends Person(eName) { println("Emp ....") //辅助构造器 def this(name: String) { this(name, 100) // 必须调用主构造器 this.name = name println("Emp 辅助构造器~") } def showInfo(): Unit = { println("雇员的名字 ", name) } }
7.6.11 覆写字段
-基本介绍
在Scala中,子类改写父类的字段,我们称之为覆写/重写字段。覆写字段需要使用override修饰
-回顾
在Java中只有方法的重写,没有属性/字段的重写,准确的讲,是隐藏字段代替了重写
-回顾-Java另一重要特性:动态绑定机制
动态绑定机制:
1) 如果调用的是方法,则JVM机会将改方法和对象的内存地址绑定
2) 如果调用的是一个属性,则没有动态绑定机制,在哪里调用就返回对应值
public class JavaDaynamicBind {
public static void main(String[] args) {
//将一个子类的对象地址,交给了一个AA(父类的)引用
//java的动态绑定机制的小结
//1.如果调用的是方法,则Jvm机会将该方法和对象的内存地址绑定
//2.如果调用的是一个属性,则没有动态绑定机制,在哪里调用,就返回对应值
AA obj = new BB();
System.out.println(obj.sum()); // 30
System.out.println(obj.sum1()); // 20
}
}
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 getI() {
return i;
}
}
-Scala覆写字段快速入门
object ScalaFiledOverride { def main(args: Array[String]): Unit = { val obj1: AAA = new AAA val obj2: BBB = new BBB //obj1.age => obj1.age() //动态绑定机制 //obj2.age => obj2.age() println("obj1.age=" + obj1.age + "\t 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
-案例演示1(val只能重写另一个val属性)
object ScalaFiledOverride { def main(args: Array[String]): Unit = { val obj1: AAA = new AAA val obj2: BBB = new BBB //obj1.age => obj1.age() //动态绑定机制 //obj2.age => obj2.age() println("obj1.age=" + obj1.age + "\t obj2.age=" + obj2.age) } } //如果 val age 改成 var 报错 class AAA { val age: Int = 10 // 会生成 public age() } class BBB extends AAA { override val age: Int = 20 // 会生成 public age() }
-案例演示2(重写不带参数的def)
object boke_demo01 { def main(args: Array[String]): Unit = { val b1 = new BB() println(b1.sal) // 0 val b2: AA = new BB() println("b2.sal=" + b2.sal()) // 0 } } class AA { def sal(): Int = { return 10 } } class BB extends AA { override val sal: Int = 0 //底层 public sal }
3) var只能重写另一个抽象的var属性
object boke_demo01 { def main(args: Array[String]): Unit = { println("hello~") } } //在AA中,有一个抽象的字段(属性) //1. 抽象的字段(属性):就是没有初始化的字段(属性) //2. 当一个类含有抽象属性时,则该类需要标记为abstract //3. 对于抽象的属性,在底层不会生成对应的属性声明,而是生成两个对应的抽象方法(name name_$eq) abstract class AA { var name: String //抽象 var age: Int = 10 } class Sub_AA extends AA { //说明 //1. 如果我们在子类中去重写父类的抽象属性,本质是实现了抽象方法 //2. 因此这里我们可以写override ,也可以不写 override var name: String = "" }
-抽象属性:声明未初始化的变量就是抽象的属性,抽象属性在抽象类中
-var重写抽象的var属性小结
1) 一个属性没有初始化,那么这个属性就是抽象属性
2) 抽象属性在编译成字节码文件时,属性并不会声明,但是会自动生成抽象方法,所以类必须声明为抽象类
3) 如果是覆写一个父类的抽象,那么override关键字可以省略[原因:父类的抽象属性,生成的是抽象方法,因此不涉及到方法重写的概念,override可以省略]
7.6.12 抽象类
-基本介绍
在Scala中,通过abstract关键字标记不能被实力化的类。方法不用标记abstract,只要省掉方法体即可,抽象类可以拥有抽象字段,抽象字段/属性就是没有初始值的字段
-快速入门案例
将Animal做成抽象类,包含一个抽象方法cry()
object AbstractDemo01 { def main(args: Array[String]): Unit = { } } //抽象类 abstract class Animal { var name: String //抽象的字段 var age: Int // 抽象的字段 var color: String = "black" //普通属性 def cry() //抽象方法,不需要标记 abstract }
7.6.13 Scala抽象类使用的注意事项和细节
1) 抽象类不能被实例
//默认情况下,一个抽象类是不能实例化的,但是你实例化时,动态的实现了抽象类的所有 //抽象方法,也可以,如下 val animal = new Animal { override def sayHello (): Unit = { println ("say hello~~~~") } } animal.sayHello ()
2) 抽象类不一定要包含abstract方法,也就是说,抽象类可以没有abstract方法
abstract class Animal { //在抽象类中可以有实现的方法 def sayHi (): Unit = { println("hello") } }
3) 一旦类包含了抽象方法或者抽象属性,则这个类必须声明为abstract
4) 抽象方法不能有主体,不允许使用abstract修饰
5) 如果一个类继承了抽象类,则它必须实现抽象类中所有的抽象方法和抽象属性,除非它自己也声明为abstract类
abstract class Animal { def sayHello() var food: String } class Dog extends Animal { override def sayHello(): Unit = { println("小狗汪汪叫!") } override var food: String = _ }
6) 抽象方法和抽象属性不能使用private、final来修饰,因为这些关键字都是和重写/实现相违背的
7) 抽象类中可以有实现的方法
8) 子类重写抽象方法不需要override,写上也不会错
7.6.14 匿名子类
-基本介绍
和Java一样,可以通过包含带有定义或重写的代码块的方式创建一个匿名的子类
-回顾-Java中匿名子类的使用
public class NoNameDemo01 {
public static void main(String[] args) {
//在java中去创建一个匿名子类对象
A a = new A() {
@Override
public void cry() {
System.out.println("cry...");
}
};
a.cry();
}
}
abstract class A {
abstract public void cry();
}
-Scala匿名子类的使用
object boke_demo01 { def main(args: Array[String]): Unit = { val monster = new Monster { override def cry(): Unit = { println("...:)") } override var name: String = _ } monster.cry() } } abstract class Monster { var name: String def cry() }
7.6.15 继承层级
-Scala继承层级一览图
-对上图的一个小结
1) 在Scala中,所有其它类都是AnyRef的子类,类似Java的Obiect
2) AnyVal和AnyRef都扩展自Any类,Any类是子节点
3) Any中定义了isInstanceOf、asInstanceOf方法,以及哈希方法等
4) Null类型的唯一实例就是null对象,可以将null赋值给任何引用,但不能赋值给值类型的变量
5) Nothing类型没有实例,它对于泛型结构是有用处的,举例:空列表Nil的类型就是List[Nothing],它是List[T]的子类型,T可以是任何类