1. 程式人生 > >Scala程式設計(二)面向物件程式設計

Scala程式設計(二)面向物件程式設計

Scala程式設計(二)面向物件程式設計

1.類的定義

//scala類
class Person {
  //用val定義的成員變數,只提供了getter方法
  val id = "1234"
  //用var定義的成員變數,提供了setter和getter方法
  var name = "jack"
  var age = 20
  //方法
  def sleep()={
    println("正在休息。。。。。")
  }
}
object Person extends App {
  val p = new Person()
  //獲取和修改成員變數
  //p.id = "23432"  報錯val修飾的成員變數不能修改
  println("id:" + p.id)
  p.age = 22
  println("age:" + p.age)
  println(p.name)
  p.sleep()
}

2.構造器

Scala中的每個類都有一個主構造器,主構造器的引數直接放在類後面,並且主構造器會執行類中定義所有語句

//主構造器放在類名後面
class Student (val n :String,var a :Int){
  val name :String = n
  //var修飾可修改
  a = a + 1
  var age = a
  //使用佔位符時需要指定引數的型別
  //scala類中所使用的佔位符”_”,只有在宣告var變數時才可以使用
  var sex : String= _
  println("主構造器執行了類中定義的所有語句")
  //構造器(構造方法) 輔構造器
  def this(n :String,a :Int,sex :String){
    //呼叫主構造器,不可省略
    this(n,a)
    println("呼叫了輔構造器")
    this.sex = sex
  }
  //方法
  def study(s :String): Unit ={
    println(name + "在學習" + s)
  }

}


object Student extends App{
  //主構造器
  val stu1 = new Student("jack",20)
  println(stu1.age)  //21
  //賦值操作會在其他語句之後執行
  stu1.age = 30
  stu1.study("scala")
  println(stu1.age)  //30
  //輔構造器
  val stu2 = new Student("lisi",20,"男")
  stu2.study("hadoop")
}

執行結果:

主構造器執行了類中定義的所有語句
21
jack在學習scala
30
主構造器執行了類中定義的所有語句
呼叫了輔構造器
lisi在學習hadoop

3.訪問許可權

3.1構造器的訪問許可權

//主構造器放在類名後面
class Student private(val n :String,var a :Int){
  val name :String = n
  //var修飾可修改
  a = a + 1
  var age = a
  //使用佔位符時需要指定引數的型別
  //scala類中所使用的佔位符”_”,只有在宣告var變數時才可以使用
  var sex : String= _
  println("主構造器執行了類中定義的所有語句")
  //構造器(構造方法) 輔構造器
  private def this(n :String,a :Int,sex :String){
    //呼叫主構造器,不可省略
    this(n,a)
    println("呼叫了輔構造器")
    this.sex = sex
  }
  //方法
  def study(s :String): Unit ={
    println(name + "在學習" + s)
  }

}


object Test extends App{
  val stu1 = new Student("張三",20)
  val stu2 = new Student("李四",30,"女")
}

執行結果會出錯:在主構造器或輔構造器修飾為private後,訪問許可權僅限:本類和伴生物件中使用

伴生物件:指的是物件名與類名相同的物件

3.2類中變數的訪問許可權

當類中的成員變數為私有時,有如下兩種解決方法

1、在Student類中新增:getter、setter方法

2、使用伴生物件

//scala類
class Person {
  private var name :String = _
  private var age :Int = _

  //方法
  def sleep()={
    println(s"年齡為:$age 的 $name 正在休息。。。。。")
  }
}

object Test extends App{
  //建立物件
  var p = new Person()
//p.id   報錯並不能訪問私有的成員
  p.sleep()
}

解決方法:

//scala類
class Person { //預設的無參主構造器
  private var name :String = _
  private var age :Int = _
  //get和set方法
  def setName(name: String) ={
    this.name = name
  }
  def getName: String ={
    this.name
  }
  def setAge(age :Int)={
    this.age = age
  }
  def getAge : Int = {
    this.age
  }
  //全參構造器(輔構造器)
  def this(name :String,age : Int){
    this()
    this.name = name
    this.age = age
  }

  //方法
  def sleep()={
    println(s"年齡為:$age 的 $name 正在休息。。。。。")
  }
}

object Test extends App{
  //建立物件  主構造器
  var p = new Person()
  //p.id   報錯並不能訪問私有的成員
  p.setAge(20)
  p.setName("jack")
  p.sleep()

  //輔構造器
  var p1 = new Person("lisi",30)
  p1.sleep()
}

物件

1.Scala中的object

object相當於class的單個例項(單例模式),通常在裡面放一些靜態的field或者method
在Scala中沒有靜態方法和靜態欄位,但是可以使用object這個語法結構來達到同樣的目的

object的單例模式

class Session {}

object Session extends App {
  //相當於靜態塊
  val session = new Session
  //相當於靜態方法
  def getSession() : Session={
    session
  }
}

object Test1 extends App {
  //單例物件
  val session1 = Session.session
  val session2 = Session.getSession()
}

2.scala中的伴生物件

伴生物件的解釋:
如果有一個class,還有一個與class同名的object,那麼就稱這個object是class的伴生物件,class是object的伴生類
要求: 伴生類和伴生物件必須存放在一個.scala檔案中
特點: 伴生類和伴生物件的最大特點是,可以相互訪問(可以訪問私有成員)

//伴生類
class Student private{
    //私有成員
 private var name :String = _
  private var age :Int = _
  //get和set方法
  def setName(name: String) ={
    this.name = name
  }
  def getName: String ={
    this.name
  }
  def setAge(age :Int)={
    this.age = age
  }
  def getAge : Int = {
    this.age
  }
  //私有全參構造器(輔構造器)
  private def this(name :String,age : Int){
    this()
    this.name = name
    this.age = age
  }
  //方法
  def study(s :String): Unit ={
    println(name + "在學習" + s)
  }
}
//伴生物件
object Student{
  val stu1 = new Student()
  stu1.name = "zhangsan"
  stu1.age = 20
  stu1.study("java")
  val stu2 = new Student("lisi",30)
}

3.Scala中的apply方法

//伴生類
class Student private{
  //私有成員
  private var name :String = _
  private var age :Int = _
  //get和set方法
  def setName(name: String) ={
    this.name = name
  }
  def getName: String ={
    this.name
  }
  def setAge(age :Int)={
    this.age = age
  }
  def getAge : Int = {
    this.age
  }
  //私有全參構造器(輔構造器)
  private def this(name :String,age : Int){
    this()
    this.name = name
    this.age = age
  }
  //方法
  def study(s :String): Unit ={
    println(name + "在學習" + s)
  }
}
//伴生物件
object Student{
  //無參
  def apply(): Student = new Student()
  //有參
  def apply(name : String,age : Int): Student = new Student(name,age)
}

//測試類
object Test1 extends App {
  //利用伴生物件建立物件
  var stu = Student()
  //相當於  var stu = Student.apply()
  stu.setName("張三")
  stu.setAge(20)
  stu.study("java")
  //var stu = Student.apply(name,age)
  var stu2 = Student("李四",30)
  stu2.study("scala")
}

繼承

1.繼承的概念

繼承是面向物件的概念,用於程式碼的可重用性。被擴充套件的類稱為超類或父類, 擴充套件的類稱為派生類或子類。
Scala 中,讓子類繼承父類,與 Java 一樣,也是使用 extends 關鍵字;

//父類
class Person{
   var name :String = _
   var age :Int = _
}
//子類
class Student extends Person{
  //子類特有的方法
  def study(s : String): Unit ={
    //name是繼承的person中的變數
    println(name + "在學習" + s)
  }
}
object Test1 extends App {
  var stu = new Student()
  stu.name = "jack"
  stu.age = 30
  stu.study("scala")
}

繼承就代表,子類可繼承父類的field和method,然後子類還可以在自己的內部實現父類沒有的,子類特有的 field 和method,使用繼承可以有效複用程式碼
在Scala中的繼承:
1、private修飾的field和method不可以被子類繼承,只能在類的內部使用
2、使用final修飾符時,修飾類:類不能被繼承、修飾field和method:不能被覆寫

2.Scala中的override和super關鍵字

override

override的使用場景:
1、Scala中,如果子類要覆蓋父類中的一個非抽象方法,必須要使用 override 關鍵字
2、子類可以覆蓋父類的val修飾的field,只要在子類中使用 override 關鍵字即可
(注意:針對父類中的var修飾的field,子類不能覆寫)

super

super的應用場景: super.父類中的方法
在子類覆蓋父類方法後,如果在子類中要呼叫父類中被覆蓋的方法,則必須要使用super關鍵字(顯示的指出要呼叫的父類方法)

override和super的應用案例:

//父類
class Phone {
  //val不可修改的變數
  val id = "12345"
  def call = println("打電話")
}
//子類
class NewPhone extends Phone {
  //覆寫父類中的val變數和非抽象方法必須使用override關鍵字
  override val id: String = "6789"
  override def call: Unit = {
    //呼叫父類的方法使用super關鍵字
    super.call
    println("開啟視訊")
  }
}
object Test1 extends App {
  var p = new NewPhone()
  //呼叫子類覆寫的方法
  p.call
}

3.scala中的protected

在Scala中同樣提供了protected關鍵字來修飾field和method,方便子類去繼承父類中的field和method

//父類
class Person{
  //protected 是為繼承提供的
  //protected [this] 只允許在本類和子類中使用(子類的伴生物件不能訪問)
   protected [this] var name :String = _
   protected var age :Int = _
  protected def say(s :String)= println(s)
}
//子類
class Student extends Person{
  //子類特有的方法
  def study(s : String): Unit ={
    //name是繼承的person中的變數
    println(name + "在學習" + s)
  }
  def setName(n :String)={
    this.name = n
  }
}
object Student extends App {
  var stu = new Student()
  //這裡不能直接訪問父類的name成員
  stu.setName("張三")
  stu.age = 20
  stu.study("scala")
}

4.呼叫父類的constructor

建立子類物件時,會自動呼叫子類相應的構造器:

1、呼叫子類主構造器
2、呼叫子類輔助構造器,輔構造器會再去呼叫主構造器
和java一樣,建立子類物件時,也會先去呼叫父類的構造器:子類主構造器去呼叫父類中的構造器

5.scala中的抽象類

一個類中,如果含有一個抽象方法或抽象field,就必須使用abstract將類宣告為抽象類,該類是不可以被例項化的
在子類中覆蓋抽象類的抽象方法時,可以不加override關鍵字

//抽象類  抽象field和方法都需要放在方法抽象類中
abstract class Person{
  //抽象的field沒有初始值
   var name :String
   var age :Int = _
  //抽象方法沒有方法體實現
  def study(s: String)
  def say:String
}
//子類繼承抽象類
class Student extends Person{
  //覆寫所有的抽象內容  同時可以省略override
  def study(s : String): Unit ={
    //name是繼承的person中的變數
    println(name + "在學習" + s)
  }
  override var name: String = _

  override def say: String = "hello"
}
object Student extends App {
  var stu = new Student()
  stu.name = "jack"
  stu.age = 20
  var hello = stu.say
  stu.study("scala")
}

6.scala中的isInstanceOf和asInstanceOf 多型

在例項化了子類的物件,並將其賦予了父類型別的變數後,父引用無法去呼叫子類中特有的方法,如果想要呼叫子類中特有的方法,應該型別轉換
在java中,進行型別轉換時,為了避免型別轉換異常,會使用instanceof先判斷型別,再進行型別轉換

//父類 
class Person{
  var name :String = _
  var age :Int = _
   def eat(s :String)={
     println("吃" + s)
   }
}
//子類
class Student extends Person{
  //子類特有的方法
  def study(s : String): Unit ={
    //name是繼承的person中的變數
    println(name + "在學習" + s)
  }
  //子類覆寫父類的方法
  override def eat(s: String): Unit = {
    println(this.name + "吃" + s)
  }
}

object Test extends App {
  //宣告一個父型別變數指向子型別
  var person : Person = new Student()
  person.name = "jack"
  person.age = 20
  person.eat("米飯")
  //判斷person物件是否為student的例項
  if (person.isInstanceOf[Student]){
    //將person物件轉成student例項
    var stu : Student = person.asInstanceOf[Student]
    //呼叫子類物件特有的方法
    stu.study("java")
  }
}

Trait

1.將trait作為介面使用

Scala中的Trait(特質)相當於Java的介面,但實際上它比介面要功能強大,與傳統的介面不同之處是:它除了可以定義抽象方法外還可以定義屬性和方法的實現
在Scala中沒有 implement 的概念,無論繼承類還是trait,統一都是 extends
類繼承後,必須實現所有的抽象方法,實現時不需要使用 override 關鍵字

trait Demo1{
  def sayHello(s :String)
}
//子類繼承需要重寫抽象方法
class Person extends Demo1 {
  override def sayHello(s: String): Unit = {
    println("打招呼," + s)
  }
}
object TraitDemo1 extends App {
  val p = new Person()
  p.sayHello("jack")
}

Scala不支援對類進行多繼承,但是支援多重繼承 trait,使用 with 關鍵字即可

trait Demo1{
  def sayHello(s :String)
}
trait Demo2{
  def say():String
}
class Person extends Demo1 with Demo2 {
  override def sayHello(s: String): Unit = {
   println( say() + s)
  }
  override def say(): String = "你好"
}
object TraitDemo1 extends App {
  val p = new Person()
  p.sayHello("scala")
}

在Traint中除了可以書寫抽象方法外,還可以書寫:抽象欄位、具體方法、具體欄位

2.Trait高階應用

2.1在例項物件指定混入某個trait

Trait高階知識: 建立物件例項時,trait加入到物件例項中
可在建立類的物件時,為該物件指定混入某個trait,且只有混入了trait的物件才具有trait中的方法,而其他該類的物件則沒有
格式: val 引用變數 = new 類名() with Trait
在建立物件時,使用with關鍵字指定混入某個trait

trait TraitLogger{
  def log(msg : String)={
    println("日誌內容:------" + msg)
  }
}
trait MyLogger extends TraitLogger{
  override def log(msg: String): Unit = {
    println(System.currentTimeMillis() + msg)
  }
}
class Person extends TraitLogger{
    log("列印日誌。。。")
}
object TraitDemo2 extends App {
  val p = new Person() with MyLogger
}
2.2Trait呼叫鏈

當去呼叫多個trait中的方法時,首先會從最右邊的trait中的方法開始執行,然後依次往左邊的trait執行,形成一個呼叫鏈條(這個特性非常強大:是設計模式中責任鏈模式的一種具體實現)

trait TraitOne{
  def handle(data : String)={
    println("獲取資料--" + data)
  }
}
trait TraitTwo extends TraitOne{
  override def handle(data : String)={
    println("處理資料--"+ data)
    super.handle(data)
  }
}
trait TraitThree extends TraitOne{
  override def handle(data : String)={
    println("分析資料--"+ data)
    super.handle(data)
  }
}
class RequestResult(val data : String) extends TraitThree with TraitTwo {
  def getParam={
    println("引數:" + data)
    //呼叫多個trait中方法名相同的方法
    handle(this.data)
  }
}

object traitdemo3 extends App {
  val p = new RequestResult("大資料")
  p.getParam
}
2.3混合使用trait的具體方法和抽象方法
trait traitOne{
  def getName : String
  def valid(data : String):Boolean={
    data.equals(this.getName)
  }
}
class Person(data :String) extends traitOne{
  override def getName: String = this.data
}
object traitdemo4 extends App {
  val p = new Person("資料")
  if (p.valid("測試")){
    println("資料合法")
  }else{
    println("資料不合法")
  }
}
2.4Trait的構造機制

2.4.1構造機制

在Trait中只有無參構造(主構造器)

trait TraitOne{
  println("TraitOne的構造器。。。。")
}
trait TraitTwo{
  println("TraitTwo的構造器。。。。")
}
class TraitChlid extends TraitOne with TraitTwo {
  println("TraitChlid的構造器。。。。")
}
object traitdemo5 extends App {
  val t = new TraitChlid()
}

子類沒有繼承父類,只繼承多個獨立的Trait(Trait沒有繼承)時,構造機制:
從extends後面第一個Trait構造器開始執行,依次向後面的Trait構造器執行

class Parent{
  println("Parent的構造器。。。。")
}
trait MyTrait{
  println("MyTrait的構造器。。。。")
}
trait TraitOne extends MyTrait{
  println("TraitOne的構造器。。。。")
}
trait TraitTwo extends MyTrait{
  println("TraitTwo的構造器。。。。")
}
class TraitChlid extends Parent with TraitOne with TraitTwo {
  println("TraitChlid的構造器。。。。")
}
object traitdemo5 extends App {
  val t = new TraitChlid()
}

子類繼承了父類的同時,也繼承了多個Trait(Trait也有繼承)時,構造機制:
1、父類的構造器
2、多個Trait從左向右依次執行(構造Trait時,先構造父Trait)
注意:如果多個Trait繼承同一個父Trait,則父Trait只會構造一次
3、子類的構造器

2.4.2Trait中的field初始化

提前定義Trait中的field

trait traitHello{
  //抽象field
  val msg : String
  println("資訊的長度:"+ msg.length)
}
class TraitClass extends {override val msg :String = "初始化值"} with traitHello{
    //方法一在類上覆寫抽象field
}
object traitdemo6 extends App {
  val t = new TraitClass()
  //方法二在例項化時覆寫抽象field
  /**
    * val t = new {
    * override val msg :String = "初始化值"
    * } with TraitClass with traitHello
    */
}