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
*/
}