Scala基础【面向对象编程(续)】

面向对象编程

1 抽象

抽象类抽象方法

抽象就是不完整,有抽象类和抽象方法(只有声明没有实现)

  abstract class User{
    def test() : Unit
  }

抽象类无法直接实例化,需要由子类继承后完成实例化,子类继承抽象类后可以声明为抽象类,也可以将父类的抽象方法补充完整

  def main(args: Array[String]): Unit = {
    var user = new child()
  }

  abstract class User{
    def test() : Unit
  }
  class Child extends User{
    def test(): Unit ={
      
    }

scala中不完整的方法就是抽象的,所以不需要添加abstract关键字。

抽象属性

scala中属性也可以是抽象的,只有声明,没有初始化,底层实现就是抽象方法

abstract class User{
    var name:String
  }
  class Child extends User{
    var name:String = "zhangsan"
  }

java中没有抽象属性,那么底层如何实现,反编译后的代码如下

编译时不会在类中声明属性,而是会声明属性的get,set方法,并且是抽象的

  public abstract static class User {
      public abstract String name();

      public abstract void name_$eq(final String x$1);
   }

编译时,子类实现抽象属性会在类中声明为私有属性,同时提供属性的get,set方法,并且是公共的

public static class child extends Scala09_Object_Abstract_1.User {
      private String name = "zhangsan";

      public String name() {
         return this.name;
      }

      public void name_$eq(final String x$1) {
         this.name = x$1;
      }
   }

子类重写父类方法

子类重写父类的抽象方法,直接补充完整即可,重写父类完整方法,需要添加override关键字,只是为了明确,并没有其他含义,一般推荐只要重写方法都添加abstract关键字

abstract class User{
    def test(): Unit ={

    }
    def test1(): Unit
  }
  class Child extends User{
    override def test(): Unit = {
      
    }
    override def test1(): Unit ={

    }
  }

子类重写父类属性

子类可以重写父类的抽象属性,补充完整即可,子类可以重写父类的完整属性,必须添加override关键字

 abstract class User{
    var name : String
    var age : Int = 10
  }
  class Child extends User{
    var name : String = "zhangsan"
    override var age : Int = 20
  }

注意var修饰的属性不可以被重写,需要修改为val

如果var能够重写,以下代码本意为将User中的age从10变为20,但是由于对属性的赋值age = 30等同于调用属性的set方法,又因为有动态绑定机制,set方法为成员方法,会查找其实际内存(Child中的set方法),所最终实现的效果为将20变为了30,此为其一,对属性的访问println(age)其实等同于调用属性的get方法,get方法又是成员方法,同理会取到Child的age,此为其二

所以想重写完整方法,为避免歧义,必须使用val修饰

  def main(args: Array[String]): Unit = {
    new Child().test()
  }
  abstract class User{
    var name : String
    var age : Int = 10
    def test(): Unit ={
      age = 30
      println(age)
    }
  }
  class Child extends User{
    var name : String = "zhangsan"
    override var age : Int = 20
  }

2 特质

在java中

接口和当前类有关,和父子类没有任何的关系

public class TestInterface {
    public static void main(String[] args) {
        I i = new B();
        System.out.println(i);
        System.out.println(B.class.getInterfaces().length); //从B类中查看接口的数量 0
        System.out.println(A.class.getInterfaces().length); // 1
    }
}
interface I{

}
class A implements I{

}
class B extends A{

}

以下代码称为多态的传递

I i = new B();
I i = new A()	//实现类代替接口出现
A a = new B()	//子类代替父类对象出现

在scala中,将多个对象中相同的特征从对象中剥离出来,形成一个新的特征,称之为trait(特征)

如果一个对象符合这个特征,那么可以将这个特征加入到这个对象,加入的过程称为混入(extends)

如果一个类只有一个特征时,采用extends关键字进行混入

如果一个类有多个特征,第一个特征采用extends,其他的使用with

如果类存在父类,并同时具备某些特征需要使用extends关键字继承父类,使用with关键字混入特征

  trait Eat{
    def eat(): Unit
  }
  trait Runnable{
    def run(): Unit
  }
  class Person extends Eat with Runnable {
    override def eat(): Unit = {
      println("Peat...")
    }

    override def run(): Unit = {
      println("Prun...")
    }
  }
  class Child extends Person with Eat with Runnable {
    override def eat(): Unit = {
      println("Ceat...")
    }
  }

动态混入

解耦合,增加扩展性,所谓耦合就是你的变化影响到了我

OCP原则(Open Close Principe)可以让程序进行扩展,但是不能修改以前的代码,scala的动态混入机制就提供了很大的帮助

  def main(args: Array[String]): Unit = {
    val user = new User() with UpdateUser
    user.insertUser()
    user.updateUser()
  }
  class User{
    def insertUser(): Unit ={
      println("insert user...")
    }
  }
  trait UpdateUser{
    def updateUser(): Unit ={
      println("update user...")
    }
  }

trait原理

不能将trait简单的理解为接口,可以将trait理解为接口和抽象类的结合体

可以将下列代码重的trait理解为接口,一个类实现了一个接口

  trait Test{
    def test(): Unit
  }
  class User extends Test {
    override def test(): Unit = {

    }

以上代码反编译结果如下

  public interface Test {
      void test();
   }

   public static class User implements Scala09_Object_trait_2.Test {
      public void test() {
      }
   }

java中的接口和类没有任何关系,更不能继承类,但是scala中的特征可以继承类

可以将以下代码中的trait理解为抽象类,一个类继承了抽象类,抽象类又继承了其他类

  trait Test extends Exception{
    def test(): Unit
  }
  class User extends Test {
    override def test(): Unit = {

    }

反编译结果

   public interface Test {
      void test();
   }

   public static class User extends Exception implements Scala09_Object_trait_2.Test {
      public void test() {
      }
   }

那么,trait理解为抽象类,是一个类,那么它也可以有伴生对象

trait MyTrait {

}
object MyTrait{
  
}

初始化顺序

父类的特质 > 父类 > 特质1,特质2 > 当前类

  trait Test extends Test2 {
    println("a")
  }
  trait Test1{
    println("d")
  }
  trait Test2{
      
    println("e")
  }
  class Person{
    println("b")
  }
  class User extends Person with Test with Test1 with Test2{
    println("c")
  }

分别从抽象类和接口的角度去理解,有多个特质,从左到右依次初始化,将Test2看做抽象类,将Test,Test1看做接口,接口先于子类初始化,输出beadc,初始化只会做一次

功能的叠加

java中不能多继承是因为存在钻石问题

钻石问题:当多个类实现了父类的方法,同名且功能不同,子类同时继承同一级别的多个父类时,调用哪个方法

scala使用了一种功能叠加的方式在当前环境下解决钻石问题

super不是父特质的意思,是上一级(个)的意思,只在编译时起作用,在运行时没有super

  def main(args: Array[String]): Unit = {
   new MySQL().OperData()
  }
  trait Oper{
    def OperData(): Unit ={
      println("操作数据")
    }
  }
  trait DB extends Oper {
    override def OperData(): Unit = {
      print("向数据库中")
      super.OperData()
    }
  }
  trait Log extends Oper{
    override def OperData(): Unit = {
      print("向日志文件中")
      super.OperData()
    }
  }
  class MySQL extends DB with Log {

  }

上述代码输出向日志文件中向数据库中操作数据,如果不想让DB的OperData执行,可以在Log的super后面添加一些内容

  trait Log extends Oper{
    override def OperData(): Unit = {
      print("向日志文件中")
      super[Oper].OperData()
    }
  }

删除Log中的[Oper],添加

  trait Other extends Oper{
    override def OperData(): Unit = {
      print("为了测试")
      super[Oper].OperData()
    }
  }

会发现Log和DB都没有执行,可以观察到直接从当前(Other)直接调到Oper执行OperData()

3 扩展

通过反射修改字符串

不可变字符串中不可变指的是底层的char数组内存地址不可变

  def main(args: Array[String]): Unit = {
    val s = " a b "

    //获取类信息
    val unit = classOf[String]
    //获取field域
    val field = unit.getDeclaredField("value")
    //设置访问权限
    field.setAccessible(true)
    //获取字符串
    val obj = field.get(s)
    //转换类型
    val chars = obj.asInstanceOf[Array[Char]]
    //修改
    chars(2) = 'C'

    print(s)
  }

枚举类和应用类

// 枚举类
object Color extends Enumeration {
    val RED = Value(1, "red")
    val YELLOW = Value(2, "yellow")
    val BLUE = Value(3, "blue")
}

以下代码输出bca,详情见类的初始化

object Scala11_Object_App {
  println("bbb")
  def main(args: Array[String]): Unit = {
    println("aaa")
  }
  println("ccc")
}

为避免歧义,见以下代码

// 应用类
object AppTest extends App {
    println("aaa")
    println("bbb")
    println("ccc")
}

顺序执行

Type定义新类型

使用type关键字可以定义新的数据数据类型名称,本质上就是类型的一个别名

  type JavaKVMap = java.util.HashMap[String,String]
  private val map: JavaKVMap = new JavaKVMap()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

OneTenTwo76

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值