scala基础学习--面向对象

一.面向对象之类

1.构造器
主构造器:Scala中的每个类都有一个主构造器,主构造器的参数直接放置类名后面,与类交织在一起.
注意:主构造器会执行类中定义的所有语句

package com.legendlee.oop.constructor

class Student(var n: String, var a: Int) {
  var name = n
  var age = a
  a = a + 1
  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(this.age+"岁的"+this.name+"在学习"+s)
  }
}
object Demo01 {
  def main(args: Array[String]): Unit = {
    //创建类的一个对象
    //调用主构造器
    var stu=new Student("ss",23)
    stu.study("linux")
    //调用辅助构造器
    var stu2=new Student("legendlee",25,"男")
    stu2.study("scala")

  }
}

运行结果:
在这里插入图片描述
注意:占位符_只能将变量声明为var时才能使用

2.访问权限
构造器的访问权限:

package com.legendlee.oop.privilege

//构造器的访问权限
class Student private (var n: String, var a: Int) {//私有主构造器
  var name = n
  var age = a
  var sex:String=_//_表示占位符
  //私有化辅助构造器
 private def this( name:String,age:Int,sex:String){
    this(name,age)
    this.sex=sex
  }
}

object Demo01 {
  def main(args: Array[String]): Unit = {
    var stu =new Student("ss",23)
    println(stu)
  }
}

在这里插入图片描述
表明在Demo01中无法访问到Student类中的构造器
说明:在主构造器或辅构造器修饰为private后,访问权限仅限:本类和伴生对象中使用

类中变量的访问权限:
在这里插入图片描述

当把类中的成员变量设为私有时,其他类里无法访问
解决方案:
1.在Student类中添加setter getter方法
2.使用伴生对象

package com.legendlee.oop.privilege

//类中变量的访问权限
class Student{
  //将类中的成员变量私有化
  private var name:String =_
  private var age:Int=_
  //定义一个方法
  def study(c:String): Unit ={
    println(this.age+"岁的"+this.name+"在学习"+c)
  }
  //提供set方法
  def setName(name:String): Unit ={
    this.name=name
  }
  //get
  def getName(): String ={
    name
  }

  //提供set方法
  def setAge(age:Int): Unit ={
    this.age=age
  }
  //get
  def getAge(): Int ={
    age
  }
}
object Demo02 {
  def main(args: Array[String]): Unit = {
    var stu =new Student()
    stu.setName("legendlee")
    stu.setAge(25)
    println(stu.getName())
    stu.study("scala")
  }
}

运行结果:
在这里插入图片描述

二.面向对象编程之对象

1.scala中的Object
object相当于class的单个实例(单例模式),通常在里面放一些静态的field或者method
在Scala中没有静态方法和静态字段,但是可以使用object这个语法结构来达到同样的目的.

单例模式的特点就是对象只有一个。放在Object中相当于静态的,不需要去实例化,可以直接访问

package com.legendlee.oop.object1

class Session{}//伴生类
//伴生对象   伴生对象名要和伴生类名保持一致
object Session{//只要是书写在Object里面的就是静态的
  val session=new Session()
  //获得session对象
  def getSession(): Session ={
    session
  }
}
object Demo01 {
  def main(args: Array[String]): Unit = {
    //相当于单例模式
    var s=Session.getSession()//通过类名直接调用方法
    var s2=Session.session//通过类名直接调用对象
    //打印s s2
    println(s)
    println(s2)
  }
}

运行结果:
在这里插入图片描述
2.scala中的伴生对象
首先,需要明确一个问题,什么是伴生对象?
伴生对象的解释:
如果有一个class,还有一个与class同名的object,那么就称这个object是class的伴生对象,class是object的伴生类
要求: 伴生类和伴生对象必须存放在一个.scala文件中
特点: 伴生类和伴生对象的最大特点是,可以相互访问(可以访问私有成员)
可以理解为:
随着类的加载而自动出现的一个对象(单例)
关键字:Object
必须和类名保持一致
好处:可以在伴生对象中直接访问伴生类中的私有成员

package com.legendlee.oop.object1

class Person private(n: String) {
  //全部都定义为私有的
  private var name: String = n
  private var age: Int = _

  private def this(name: String, age: Int) {
    this(name)
    this.age = age
  }
}
//伴生对象
object Person {
  val person=new Person("ss")
  person.name="珊珊"
  person.age=23

  def getName(): String ={
    this.person.name
  }
}

object Demo02 {
  def main(args: Array[String]): Unit = {
    println(Person.getName())
  }
}

运行结果:
在这里插入图片描述
总结:伴生对象里面可以访问到伴生类中的私有的成员信息

3.scala中的apply方法
1.apply方法的引入
var arr = Array(10,20,30)
var list = ListBuffer(10,20,30)
在使用Array(10,20,30)和ListBuffer(10,20,30)时,其实使用的并不是数组或集合类本身,而是使用Array类和ListBuffer类的object伴生对象

package com.legendlee.oop.object1

class Person private(n: String) {
  //全部都定义为私有的
  private var name: String = n
  private var age: Int = _

  private def this(name: String, age: Int) {
    this(name)
    this.age = age
  }
}
//伴生对象
object Person {
  //创建apply方法
  def apply(name:String): Person ={
    new Person(name)
  }
}
object Demo03 {
  def main(args: Array[String]): Unit = {
    val p=Person("ss")
  }
}

当我鼠标点击Person的时候会出现apply方法:
在这里插入图片描述
说明不需要再重新new一个对象,其直接调用了伴生对象中的apply方法.
apply方法也可以重载

package com.legendlee.oop.object1

class Person private(n: String) {
  //全部都定义为私有的
   var name: String = n
   var age: Int = _

  private def this(name: String, age: Int) {
    this(name)
    this.age = age
  }
}
//伴生对象
object Person {
  //创建apply方法
  def apply(name:String): Person ={
    new Person(name)
  }
  def apply(name:String,age:Int): Person ={
    new Person(name,age)
  }
}
object Demo03 {
  def main(args: Array[String]): Unit = {
    val p=Person("ss",23)
    println(p.name,p.age)
  }
}

根据传参的个数和类型系统自动调用对应的apply方法。

三.面向对象之继承

1.继承的概念
继承是面向对象的概念,用于代码的可重用性。被扩展的类称为超类或父类, 扩展的类称为派生类或子类。
Scala 中,让子类继承父类,与 Java 一样,也是使用 extends 关键字;
继承就代表,子类可继承父类的field和method,然后子类还可以在自己的内部实现父类没有的,子类特有的 field 和method,使用继承可以有效复用代码
在Scala中的继承:
1、private修饰的field和method不可以被子类继承,只能在类的内部使用
2、使用final修饰符时,修饰类:类不能被继承、修饰field和method:不能被覆写

package com.legendlee.extendsDemo
//父类
class Person {
  final def say() = {
    println("打招呼")
  }
}
//子类
class Student extends Person {
  def show() = {
    say()
  }
}
object Demo01 {
  def main(args: Array[String]): Unit = {
    //实例化子类对象
    val stu = new Student()
    stu.show()
  }
}

运行结果:
在这里插入图片描述
子类可以直接使用父类中的方法。

2.scala中的override和super关键字
override的使用场景:
1、Scala中,如果子类要覆盖父类中的一个非抽象方法,必须要使用 override 关键字
2、子类可以覆盖父类的val修饰的field,只要在子类中使用 override 关键字即可
(注意:针对父类中的var修饰的field,子类不能覆写)

package com.legendlee.extendsDemo
class Phone{
  val core:String="android0011"
  def call():Unit={
    println("打电话.....")
  }
}
class NewPhone extends Phone{
  //内核升级   针对父类中的val变量,需要使用override覆写
  override val core:String ="android1100"
  //打电话功能升级    针对父类中的非抽象方法,子类在重写时必须添加:override关键字
  override def call()={
    println("视频...")
    super.call()
  }
}
object ExtendsDemo2 {
  def main(args: Array[String]): Unit = {
      val p= new NewPhone()
      println(p.core)
      p.call
  }
}

super关键字的使用场景:
super.父类中的方法
在子类覆盖父类方法后,如果在子类中要调用父类中被覆盖的方法,则必须要使用super关键字(显示的指出要调用的父类方法)

3.scala中的protected
在Scala中同样提供了protected关键字来修饰field和method,方便子类去继承父类中的field和method
private修饰的成员变量:本类可访问 ,伴生对象可以访问
private[this]修饰的成员变量:仅限本类访问
在这里插入图片描述
private修饰的成员变量:本类可访问 ,伴生对象可以访问
private[this]修饰的成员变量:仅限本类访问
protected修饰的成员变量:本类、本类的伴生对象、子类、子类的伴生对象
protected[this]修饰的成员变量:本类、子类
完整代码如下:

package com.legendlee.extendsDemo

class Person{
   private val id1:String ="9527"
   private[this] val id2:String ="8888"
   //private修饰的成员变量:本类可访问 ,伴生对象可以访问
   //private[this]修饰的成员变量:仅限本类访问
   protected[this] var name:String ="张三"
   //protected修饰的成员变量:本类、本类的伴生对象、子类、子类的伴生对象
   //protected[this]修饰的成员变量:本类、子类
   protected var age:Int=_
   protected def sayHello(): Unit ={
     println(name+"打招呼...")
   }
}
object Person{
  var p=new Person
  p.id1
}
class Student extends Person{
   def study()={
     println(""+this.name)
   }
}
object Student extends App{ //学生类的伴生对象
  val stu = new Student()
  stu.age=22
  stu.study()
}

4.调用父类中的constructor

package com.legendlee.extendsDemo
class Person {
  println("Person类主构造器....")
  var name: String = _
  var age: Int = _

  //重载的辅助构造器
  def this(name: String) = {
    this() //调用主构造器
    this.name = name
    println(s"Person($name)类辅助构造器....")
  }

  def this(name: String, age: Int) = {
    this() //调用主构造器
    this.name = name
    this.age = age
    println("Person(String,Int)类辅助构造器....")
  }
}
//创建子类继承父类
class Student extends Person{
  println("Student类的主构造器....")
  //子类的辅助构造器
  def this(name:String)={
    this()
    this.name = name
    println("Student(String)类的辅助构造器....")
  }
    //子类的辅助构造器
  def this(name:String,age:Int)={
    this()
    this.name = name
    println("Student(String,Int)类的辅助构造器....")
  }
}
object ExtendsDemo extends App{
    val stu = new Student("张三",18)
}

分析下代码的执行过程:
1.先调用Person类的主构造器
2.再调用Student类的主构造器
3.再调用Student类的辅助构造器
运行结果如下:
在这里插入图片描述

5.scala中的抽象类
1.抽象类的概念
如果在父类中,有某些方法无法立即实现,而需要依赖不同的子类来覆盖,重写实现不同的方法。此时,可以将父类中的这些方法编写成只含有方法签名,不含方法体的形式,这种形式就叫做抽象方法。
一个类中,如果含有一个抽象方法或抽象field,就必须使用abstract将类声明为抽象类,该类是不可以被实例化的。
在子类中覆盖抽象类的抽象方法时,可以不加override关键字。

package com.legendlee.extendsDemo
abstract class Person {
  //抽象内容
  var name: String //抽象字段
  def hello(): String //抽象方法

  //非抽象内容
  var age: Int = _
  def sayHello(msg: String) = {
    println("你好," + msg)
  }
}

//子类继承抽象类,必须重写所有的抽象内容
class Student extends Person {
  override var name = "老唐"
  override def hello(): String = {
    name
  }
}
object AbstractDemo1 {
  def main(args: Array[String]): Unit = {
    //实例化学生类
    val stu1 = new Student()
    stu1.name = "老李"
    stu1.sayHello(stu1.hello())
  }
}

运行结果:
在这里插入图片描述
6.Scala中的isInstanceOf和asInstanceOf
在多态中,父类型的引用指向子类型的对象。

//判断person对象是不是属于Student类
if(person.isInstanceOf[Student]){
//把person对象转为Student实例
	var stu:Student=person.asInstanceOf[Student]
}

四.面向对象编程之Trait

1.trait是什么?
trait相当于java中的接口,
与java中接口的不同为:它除了定义抽象方法外,还可以定义属性和方法的实现。
scala中没有implement的概念,无论是继承还是trait,都是用extends关键字。然后必须实现所有的抽象方法,实现时可以不用override关键字。
案例一:HelloTrait

//定义一个trait
trait HelloTrait {
   def sayHello(msg:String)
}
//子类继承Trait,并重写抽象方法
class TraitChild extends HelloTrait{  //trait的单一继承
   def sayHello(msg: String): Unit = {
    println("你好,"+msg)
  }
}
object TraitChild{
  def main(args: Array[String]): Unit = {
      var tc = new TraitChild()
      tc.sayHello("ss")
  }
}

输出结果:
在这里插入图片描述
Scala不支持对类进行多继承,但是支持多重继承 trait,使用 with 关键字即可
案例二:Trait多重继承

//定义第一个trait
trait TraitOne{
  def getDate():String
}
//定义第二个trait
trait TraitTwo{
  def printDate(data:String)
}
//当继承多个Trait时,使用关键字:with
class TraitChildClass extends TraitOne with TraitTwo{
  override def getDate(): String = {
    "测试数据"
  }
  override def printDate(data: String): Unit = {
    println(data)
  }
}
object TraitTest1 extends App{
   val tc = new TraitChildClass()
   tc.printDate( tc.getDate()  )
}

执行结果:
在这里插入图片描述
2.在trait中书写的定义
在trait中除了可以写抽象方法外,还可以写抽象字段(变量没有初始化值),具体方法,具体字段。
案例:

package com.legendlee.traitDemo

trait TraitPerson{
  //抽象方法
  def study(): Unit
  //具体方法
  def eat(food:String): Unit ={
    println(food)
  }
  //抽象字段
  var name:String
  //具体字段
  var age:Int=_
}

//创建一个子类继承Trait
class Person extends  TraitPerson{
  override def study(): Unit = {
    println(this.age+"岁的"+this.name+"在学习")
  }
  override var name: String = _
}


object Demo01 {
  def main(args: Array[String]): Unit = {
    var person =new Person()
    person.name="ss"
    person.age=23
    person.study()
  }

}

运行结果:
在这里插入图片描述
3.Trait的高级应用
3.1在实例对象指定混入某个trait
Trait高级知识: 创建对象实例时,trait加入到对象实例中
可在创建类的对象时,为该对象指定混入某个trait,且只有混入了trait的对象才具有trait中的方法,而其他该类的对象则没有
格式: val 引用变量 = new 类名() with Trait
在创建对象时,使用with关键字指定混入某个trait

//定义第一个trait
trait TraitOne{
  def getDate():String
}
//定义第二个trait
trait TraitTwo{
  def printDate(data:String)
}
//当继承多个Trait时,使用关键字:with
class TraitChildClass extends TraitOne with TraitTwo{
  override def getDate(): String = {
    "测试数据"
  }
  override def printDate(data: String): Unit = {
    println(data)
  }
}
object TraitTest1 extends App{
   val tc = new TraitChildClass()
   tc.printDate( tc.getDate()  )
}

运行结果为:
在这里插入图片描述
3.2、Trait调用链
问题:当类继承多个Trait时,而这些Trait中都共有同一个方法,那么子类调用时到底执行哪个Trait中的方法?

//验证数据
trait ValidTrait{
   def handler(data:String): Unit ={
      println("最终的数据验证:"+data)
   }
}
//数据签名验证
trait SignatureValidTrait extends ValidTrait{
   override def handler(data:String):Unit={
      println("数据签名验证:"+data)
     //调用链: 多个Trait具有相同的方法
     super.handler(data) //会执行ValidTrait中的handler方法
   }
}
//数据的合法性验证
trait DataValidTrait extends ValidTrait{
  override def handler(data:String):Unit={
    println("数据合法性验证:"+data)
    super.handler(data)
  }
}

class Request() extends DataValidTrait with SignatureValidTrait{
    def getParamter(data:String): Unit ={
      handler(data)
    }
}
object ValidTest extends App{
    val r = new Request()
    r.getParamter("测试数据")
}

运行的结果为:
在这里插入图片描述
Trait的调用链机制:
当多个Trait中共同都具有一个相同的方法时,子类在继承了这些Trait后,当子类对象调用这个共同的方法时:
先执行extends后面最右边的Trait中的方法,依次向左边Trait中的方法顺序执行下去(使用了:Super.共同方法() )
3.3、混合使用trait的具体方法和抽象方法
在trait中,可以混合使用具体方法和抽象方法
案例:

trait ValidTrait {
  //抽象方法
  def getData: String
  //非抽象方法
  def valid(data: String): Boolean = {
    data.equals(getData) //在非抽象方法中使用抽象方法
  }
}
class ValidClass extends ValidTrait {
  override def getData = {
    "数据"
  }
}
object ValidTest extends App {
  val vc = new ValidClass()
  if (vc.valid("数据")){
    println("合法数据")
  }else{
    println("非法数据")
  }
}

运行结果:
在这里插入图片描述
3.4、Trait的构造机制
3.4.1、构造机制
在Scala中,trait也是有构造的,即在trait中,不包含在任何方法中的代码
说明:Trait中只有无参构造(主构造器)

//父类
class Father{
  println("Father类的主构造器.....")
}
//父Trait
trait TraitParent{
  println("TraitParent构造器执行了一次......")
}
trait TraitOne extends TraitParent{
    println("TraitOne构造器.....")
}
trait TraitTwo extends TraitParent{
  println("TraitTwo构造器.....")
}
//子类继承了父类,同时又继承了两个Trait (Trait也继承父Trait)
class TraitChildClass extends Father with TraitTwo with TraitOne {
  println("TraitChildClass主构造器.......")
}

object ConstructorDemo extends App{
    val tc = new TraitChildClass()
}

运行结果:
在这里插入图片描述
子类继承了父类的同时,也继承了多个Trait(Trait也有继承)时,构造机制:
1、父类的构造器
2、多个Trait从左向右依次执行(构造Trait时,先构造父Trait)
注意:如果多个Trait继承同一个父Trait,则父Trait只会构造一次
3、子类的构造器
说明:trait的构造器的调用顺序和trait调用链的调用顺序相反

3.4.2、Trait中的field初始化
在Scala中,Trait是没有带参数构造器的(也是Trait和class的区别)
如果想要对Trait中的field进行初始化,怎么解决呢?
答案: Trait的高级特性:提前定义
首先,看一段代码

trait MyTrait{
    //抽象字段
    val msg:String
    //主构造器代码
    println(s"消息的长度是:${msg.length}")
}
class MyTraitChild extends  MyTrait{
  override val msg = "测试数据"
}
object TraitFieldDemo1 extends  App{
   val my = new MyTraitChild()
}

其中在trait中定义了一个抽象的字段,在其子trait实例化了这个抽象字段。
运行结果:
在这里插入图片描述
为什么会出现这个问题?
分析错误原因: 结合前面学习的Trait构造机制
先去执行MyTrait中的构造代码,而在MyTrait的构造代码中需要使用“抽象filed”,但该field还未初始值。“null.length”造成空指针异常。
解决方案:
1.修改实例化的代码,使用混入方案

trait MyTrait{
    //抽象字段
    val msg:String
    //主构造器代码
    println(s"消息的长度是:${msg.length}")
}
class MyTraitChild extends  MyTrait{
  override val msg = "测试数据"
}
object TraitFieldDemo1 extends  App{
//修改实例化的代码,使用混入方案
   val my = new {  override val msg = "测试数据"} with MyTraitChild()
}

运行结果:
在这里插入图片描述
2.不会破坏实例化代码,修改extends后满的代码。

trait MyTrait{
    //抽象字段
    val msg:String
    //主构造器代码
    println(s"消息的长度是:${msg.length}")
}
//相当于提前初始化
class MyTraitChild extends {  override val msg = "测试数据"} with  MyTrait{
  
}
object TraitFieldDemo1 extends  App{
   val my = new MyTraitChild()
}

运行结果:
在这里插入图片描述
3.5、Trait继承class
当Trait继承了某个class后,Trait就继承了该class中的方法,当子类在继承Trait后,也同时继承了class中的方法。

class Person {
  var name: String = _
  var age: Int = _

  def sayHello(): Unit = {
    println("打招呼....")
  }
}

//trait继承Person后,就可以直接使用Person中的成员
trait TraitPerson extends Person {
  def eat(food: String): Unit = {
    println(this.name + "吃" + food) //可以直接使用父类中的成员
  }
}
class Student extends TraitPerson {

}

object Test extends App {
  val stu = new Student()
  stu.name = "张三"
  stu.age = 22
  stu.sayHello()
  stu.eat("食物")
}

运行结果:
在这里插入图片描述

四.模式匹配

猜你喜欢

转载自blog.csdn.net/qq_30869661/article/details/84766812
今日推荐