scala学习笔记9:面向对象(中级)

60 阅读8分钟

scala 学习笔记


前言

一、包

Java 包的三大作用:

  1. 区分相同名字的类
  2. 当类很多时,可以很好的管理类
  3. 控制访问范围 实际上就是创建不同的文件夹来保存类文件,java中 包名和 源码所在的系统 文件目录结构要一致,最常见的操作就是导包,引用包下的某个类。

1.Scala 包的命名

命名规则: 只能包含 数字、字母、下划线、 小圆点.,但不能用数字开头, 也不要使用关键字。 一般是小写字母+小圆点一般是 com. 公司名. 项目名.

2.包对象

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

  • 每个包都可以有一个包对象
  • 包对象名称需要和包名一致
  • 一般用来对包的功能补充
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)
}}}}

3.包的可见性

修饰符 java提供 四种访问控制修饰符号控制方法和变量的访问权限:

  1. 公开级别:用public 修饰,对外公开
  2. 受保护级别:用protected修饰,对子类和同一个包中的类公开
  3. 默认级别:没有修饰符号,向同一个包的类公开.
  4. 私有级别:用private修饰,只有类本身可以访问,不对外公开 在这里插入图片描述
  5. 修饰符可以用来修饰类中的 属性,成员方法以及 类
  6. 只有 默认的和public才能修饰类!,并且遵循上述访问权限的特点。

scala包的可见性和访问修饰符的使用

  1. 当属性访问权限为默认时,从底层看属性是private的,但是因为提供了 xxx_$eq()[类似setter]/xxx()[类似getter] 方法,因此从使用效果看是任何地方都可以访问)
  2. 当方法访问权限为默认时, 默认为public 访问权限
  3. private为私有权限,只在 类的内部和 伴生对象中可用
  4. protected 为受保护权限,scala中受保护权限比Java中更严格,只能子类访问,同包无法访问 (编译器)
  5. 在scala中没有public关键字,即 不能用public显式的修饰属性和方法 显式的修饰属性和方法。
  6. 包访问权限(表示属性有了限制。同时包也有了限制),较java灵活
class Person {
// 增加包访问权限后,1.private同时起作用。不仅同类可以使用 
//2. 同时com.test.scala中包下其他类也可以使用
private [scala] val pname="hello" 
}

4.包的引入

  • 在Scala中,import语句可以出现在任何地方,并不仅限于文件顶部,import语 句的作用一直延伸到包含该语句的块末尾。这种语法的好处是: 在需要时在引 入包,缩小import 包的作用范围,提高效率。
  • Java中如果想要导入包中所有的类,可以通过通配符*,Scala中采用下 _
import java.io._
  • 如果不想要某个包中全部的类,而是其中的几个类,可以采用 选取器(大括号)
import scala.io.{Source, StdIn}
  • 如果引入的多个包中含有相同的类,那么可以将不需要的类进行重命名进行区分,这个就是重命名
//重命名
import java.util.{ HashMap=>JavaHashMap, List}
import scala.collection.mutable._
var map = new HashMap() // 此时的HashMap指向的是scala中的HashMap
var map1 = new JavaHashMap(); // 此时使用的java中hashMap的别名
  • 如果某个冲突的类根本就不会用到,那么这个类可以直接 隐藏掉
// 含义为 引入java.util包的所有类,但是忽略HahsMap类
import java.util.{ HashMap=>_, _} 

二、三大特征

1.封装

封装(encapsulation)就是把抽象出的 数据和对数据的操作封装在一起,数据被保护在内部,程序的其它部分只有通过 被授权的操作(成员方法),才能对数据进行操作。在java中一般通过java bean,把属性设置成private,然后对外提供getXXX/setxxx方法 scala特点:

  1. Scala 中为了简化代码的开发,当声明属性时,本身就自动提供了对应setter/getter方法,如果属性声明为private的,那么自动生成的setter/getter方法也是private的,如果属性省略访问权限修饰符,那么自动生成的setter/getter方法是public的
  2. 因此我们如果只是对一个属性进行简单的 set和 和get ,只要声明一下该属性( 属性使 用默认访问修饰符) 不用写专门的getset ,默认会创建, 访问时,直接对象. 变量。这样也是为了保持访问一致性

2.继承

继承可以 解决代码复用,让我们的编程更加靠近人类思维.当 多个类存在相同的属性(变量)和 方法时,可以从这些类中抽象出父类(比如Student),在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends语句来声明继承父类即可。

class  子类名 extends  父类名 {  类体 }

子类继承父类的属性和方法,只是私有的属性不能直接访问,需要 通过公共的方法去访问。 重写方法:scala明确规定,重写一个非抽象方法需要用override修饰符,调用超类的方法使用super关键字

只有主构造器可以调用父类的构造器。辅助构造器不能直接调用父类的构造器。在Scala的构造器中,你不能调用super(params)

class Person(name: String) { //父类的构造器
}
 // 将子类参数传递给父类构造器, 这种写法√
 class Emp (name: String) extends Person(name) {
// super(name) ( ×)  没有这种语法
  def this() {
     super("abc") // ( ×) 不能在辅助构造器中调用父类的构造器  
    }
}

覆盖字段

在Java中只有方法的重写,没有属性/字段的重写,准确的讲,是隐藏字段代替了重写。 在Scala中,子类改写父类的字段,我们称为 覆写/ 重写字段。覆写字段需使用override修饰。 覆写字段的注意事项和细节

  1. def只能重写另一个def(即:方法只能重写另一个方法)
  2. val只能重写另一个val 属性 或 重写不带参数的def
  3. var只能重写另一个 抽象的var 属性

抽象属性

抽象属性:声明未初始化的变量就是抽象的属性, 抽象属性在抽象类 var重写抽象的var属性小结

  1. 一个属性没有初始化,那么这个属性就是抽象属性
  2. 抽象属性在编译成字节码文件时,属性并不会声明,但是会自动生成抽象方法,所以 类必须声明为抽象类
  3. 如果是覆写一个父类的抽象属性,那么override 关键字可省略 [ 原因:父类的抽象属性, 生成的是抽象方法,因此就不涉及到方法重写的概念,因此override 可省略
  4. 在scala中通过abstract关键字abstract可以用于标记类,不能用于标记方法,只要省掉方法体即可
abstract class Person() { // 抽象类
   var name: String // 抽象字段, 没有初始化
   def printName // 抽象方法, 没有方法体
}

抽象类的价值更多是在于设计 ,是设计者设计好后, 让子类继承并实现抽象类

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

  1. 抽象类不能被实例
  2. 抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法
  3. 一旦类包含了抽象方法或者抽象属性,则这个类必须声明为abstract
  4. 抽象方法不能有主体,不允许使用abstract修饰。
  5. 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法和 抽象属性, 除非它自己也声明为abstract类。【 案例演示+反编译 】 ) 6) 抽象方法和抽象属性 不能使用private、final 来修饰,因为这些关键字都是和 重写/ / 实现相违背的。
  6. 抽象类中可以有实现的方法.
  7. 子类重写抽象方法不需要override,写上也不会错(可写可不写)

3.匿名子类

和JAVA一样可以 通过包含带有定义或重写的代码块的方式创建一个匿名的子类.

abstract class Monster{
  //抽象属性
  var name : String
  //抽象方法
  def cry()
}
//实际定义了一个匿名的Monster子类,重写了属性和方法,但是没具体名字
var monster = new Monster {
  override var name: String = "牛魔王"
  override def cry(): Unit = {
  println("牛魔王哼哼叫唤..")
  }
}

3.多态

object employee {
  def main(args: Array[String]): Unit = {
    val xiaowang = new ptemploy
    xiaowang.name="小王"
    xiaowang.pay=10000
    println(xiaowang.getAnnual)
    xiaowang.work()
    println("------------")
    val wangzong = new mgemployee
    wangzong.name="王总"
    wangzong.pay=50000
    wangzong.bonus=100000
    println(wangzong.getAnnual)
    wangzong.manage()

  }

//员工抽象类
  abstract class employee{

    var name: String
    var pay:Double
    def getAnnual:Double{}
  }
//普通员工
  class ptemploy extends employee{
    override var name: String = _
    override var pay: Double = _
    var bonus:Double= _
    override def getAnnual:Double =
      {
       pay*12
      }
    def work(): Unit ={
      println(name+" is working")
    }
  }
//管理层
  class mgemployee extends employee{

    override var name: String = _
    override var pay: Double = _
    var bonus:Double= _
    override def getAnnual: Double =
      {
     pay*12+bonus
      }

    def manage(): Unit ={
      println(name+" is a manager")
    }
  }
}