zoukankan      html  css  js  c++  java
  • scala之面向对象

    1. 要点

    类:

    • 类中的字段自动带有getter方法和setter方法.
    • @BeanProperty注解来生成JavaBean的getXxx/setXxx方法
    • 主构造器, 主构造器的参数直接构成类的字段
    • 辅构造器, 需要提前调用主构造器this()
    • 权限修饰符
      • protected: 修饰类的成员, 只能在子父类中访问
      • private[package]: 在package包和它的子包中可以访问.
    • 抽象类: 相比java多了抽象字段的概念
    • 类型的判断和转换

    继承:

    • 关键字extends
    • 覆写方法时必须使用override关键字, 并满足两同两小一大
    • 属性的覆写,可以覆写属性, 多态中属性的引用也是使用子类对象的
      • var只能覆写抽象的var, 有定义的var不能覆写
      • val可以覆写val和没有参数def
    • 只有主构造器才可以调用父类构造器

    多态:

    • Scala中, 方法有多态, 属性也有多态!

    特质:

    • 特质叠加冲突2种解决方式
      1. 在子类中覆写该方法
      2. 新增父类, 使用菱形继承关系

    对象:

    • 用对象存作为单例或存放工具方法
    • 类可以拥有一个同名的伴生对象
      • 可以互相访问私有成员
      • 编译后, 伴生对象中的都为静态成员, 伴生类中的成员都为非静态成员.
    • apply: scala中任何对象都可以像函数一样去调用
    • 对象可以扩展或特质
      • 除显式定义main方法外, 可以用扩展App特质的对象
      • 扩展Enumeration对象来实现枚举

    2. 面向对象概念

    面向对象就是通过封装, 继承, 多态(面向对象的三大特征)把程序的耦合性降低, 提高程序的可维护性, 可扩展性, 可复用性.

    3. 类

    定义类

    class Demo

    属性的默认初始化值

    • 数字 0
    • 布尔值 false
    • 引用型 null

    主构造器

    类和java一样, 默认有空构造器, 但是一旦有定义构造器, 则不会再默认提供空构造器.

    主构造器位置紧跟类名

    class User(var name: String, val age: Int)

    可以增加关键字private将构造器私有化

    class private User(var name: String, val age: Int)

    给类定义的所有的属性都是私有. 并且会给这些私有属性自动添加公共的gettersetter, 字节码文件反编译如下:

    public class com.demo.scala.User {
      private java.lang.String name; // 属性私有
      private final int age; // 属性私有
      public java.lang.String name();   // 自动添加name属性的getter
      public void name_$eq(java.lang.String);  // 自动添加name属性的setter
      public int age(); // getter
    }

    @BeanProperty注解可以生成JavaBean的getXxx/setXxx方法

    class User2(@BeanProperty var name: String, @BeanProperty val age: Int)

    主构造器的3种参数:

    1. class demo(var name: String): name自动成为类的私有属性, 并自动生成setter()getter()方法
    2. class demo(val name: String): 生成getter()方法, 但不生成setter()方法
    3. class demo(name: String):不生成getter()方法, 也不生成setter()方法

    构造器重载: 辅助构造器

    辅助构造器与主构造器构成重载关系

    辅助构造器的首行必须调用主构造器

    辅助构造函数的参数, 仅仅是一个普通的参数, 不会成为类的属性

    注意: 辅助构造器只能调用主构造, 父类的构造器由主构造器调用

    class Demo(name: String){
        // 定义一个无参辅助构造器
        def this() = {
            // 首行必须调用主构造器
            this("lisi")
        }
    
        // 定义一个无参辅助构造器
        def this(age: Int) = {
            this("lisi")
            this.age = age
        }   
    }

    scala 的权限修饰符

    1. 用在外部类上:
      • 默认(public, 没有public关键字)

        class A

      • private 只能在当前表使用, 其他地方无法使用
    2. 用在类的内部成员(属性, 方法, 内部类)上:
      • 默认(public, 没有public关键字)
      • protected

        这个限制更加严格. 只能在子父类中访问, 即使在同包中也不能访问

        super.foo();
      • private

        只能在当前类中访问

        scala中做了一些改造, 精细化的控制, 可以指定访问的包和它的子包

        private[mode] def speak() = println("speak...")

        mode包和它的子包中可以访问.

    抽象类

    含有抽象方法或者抽象字段的类为抽象类

    abstract class A{
        def f():Unit = {
            println("f.....")
        }
    
        //抽象方法
        def eat(): Unit
    
        //抽象字段
        var age: Int
        val sex: String
    }

    注意: 相比java多了抽象字段的概念

    类型的判断和转换

    有时候需要在使用多态后, 需要进行类型转换, 需要先判断类型, 然后再向下转型.

    a.isInstanceOf[B]: 判断a是否为B的对象

    a.asInstanceOf[B]: a转换B的类型

    object Extra1 {
        def main(args: Array[String]): Unit = {
            val a:A = new B
            // java中判断类型:   a instanceof B
            if (a.isInstanceOf[B]) {  // 判断a是否为B的对象
                val b = a.asInstanceOf[B] // a转换B的类型
                b.foo()
            }
        }
    }
    
    
    class A
    class B extends A{
        def foo() = println("foo...")
    }

    4. 继承

    继承和java一样, 是扩展类的方式

    面向对象的3大特征: 封装, 继承(单继承), 多态

    继承的基本语法

    class A
    class B extends A

    继承的时候构造的处理

    1. 在子类的辅构造器中, 必须先调用自己的主构造, 不能主动去调用父类的构造器.
    2. 只有主构造器才有权力去调用父类的构造器!!!

    方法的覆写

    java@Overreide注解可以省略. scala中方法覆写需要关键字override, 且不可以省略.

    覆写的原则

    两同, 两小, 一大

    • 两同: 方法名相同, 参数列表相同
    • 两小: 返回值, 异常类型小(相同或为子类)
    • 一大: 权限相同或更大

      java中的权限: public, protected(同包或子类), friendly(同包中访问), private

      scala中的权限: 默认(public, 但没该关键字), protected(子类), private

    属性的覆写

    scala中, 属性也可以覆写, 也具有多态!!!

    属性覆写的规则:

    1. var只能覆写抽象的var, 有定义的var不能覆写
    2. val可以覆写val和没有参数def

    5. 多态

    scala中属性也有多态, 多态中属性的引用也是使用子类对象的属性.

    6. 特质trait

    trait的本质: 字节码之后, 还是接口,

    trait是支持多混入

    class A extends t1 with t2 with t3...

    注意: trait的特性要比接口多, 但最常用的方式还是作为接口来使用.

    叠加冲突

    多个trait有相同实现好的方法的时,会产生冲突

    1. 解决方案1: 在子类中把冲突的方法覆写

    1587905478770

    1. 新增一个父trait, 成菱形关系, 最终使用最后叠加的那个!!!
      1. 初始化的是, 一个trait最多初始化一次
      2. 初始化的时候是从父开始, 然后从左到右
      3. super.foo()不是真正的找父类, 而是按照叠加的顺序向前找
      4. super[T12].foo() 明确指定这个super应该是哪个类

    1587905679513

    特质继承类

    1. 方法1
      class A{
         def foo() = {
             println("A... foo")
         }
      }
      
      
      trait B extends A{
         def eat() = {
             println("B ... eat")
             foo()
         }
      }
      
      // 但继承: extends 要么是A要么A的子类
      class C extends A with B
    2. 使用自身类型(selftype)
      trait B{
         // s就是A类型的对象
         s: A =>
      
         def eat()= {
             println("B ... eat")
             s.foo()
         }
      }

    动态叠加

    java中所有的继承关系都应该在定义类是确定好.

    scala支持特质的动态叠加. 在创建对象的时候, 可以临时只针对整个对象来叠加特质

    ```scala
    object Trait5 {
    def main(args: Array[String]): Unit = {
    val h = new H with F1
    h.foo()
    }
    }

    class H

    trait F1 {
    def foo() = println("f1 foo...")
    }
    ```

    特质的字节码文件

    Scala将特质编译成JVM的类和接口. 如果特质有具体方法, Scala会创建出一个伴生类, 该伴生类用静态方法存放特质的方法.

    例如:

    trait ConsoleLogger extends Logger {
        def log(msg: String) { println(msg)}
    }

    编译后

    public interface ConsoleLogger extends Logger { // 生成java接口
        void log(String msg);
    }
    
    public class ConsoleLogger$class { // 生成java伴生类
        public static void log(ConsoleLogger self, String msg)
            println(msg);
    
    }

    7. 对象

    单例对象

    object 对象名{
    
        def main(args: Array[String]){
    
        }
    }

    java中的单例有两种, 饿汉式, 懒汉式, 涉及到多线程还需要双重判断

    伴生类和伴生对象

    一个类可以有一个和其同名的对象, 该对象就称为该类的伴生对象

    1. class的名字和object的名字相同
    2. 可以互相访问对方的私有成员
    3. 伴生类和伴生对象必须在同一个.scala文件中
    4. 将来编译成字节码之后, 站在java的角度, 伴生对象中的都是为成为静态成员, 伴生类中的成员都会成为非静态成员.

    apply

    函数的特点是可以直接调用, 在scala中, 任何对象也可以像函数一样去调用

    // 函数调用:
    函数名(参数)
    
    // 对象调用
    对象名(参数)

    注意:

    1. 其实函数也可以通过apply进行调用. (方法不行), 如果是方法, 先把方法转成函数在使用.

      在Scala语言中, 函数也是对象, 每一个对象都是scala.FunctionN(1-22)的实例

    2. 伴生对象apply, 通常情况是返回伴生类的对象, 然后在外面创建对象的时候, 可以省略new
    3. 普通类中的apply, 一般根据具体的业务逻辑来实现.
    4. apply 也可以重载

    8. 枚举

    继承Enumeration

    示例:

    object Test {
        def main(args: Array[String]): Unit = {
    
            println(Color.RED)
        }
    }
    
    // 枚举类
    object Color extends Enumeration {
        val RED = Value(1, "red")
        val YELLOW = Value(2, "yellow")
        val BLUE = Value(3, "blue")
    }

    9. 内部类

    类型投影: Outer#Inner

    scala
    def foo(obj: Outer#Inner)

    示例:

    两种方式在内部类中调用外部类成员:

    1. 通过Outer.this
    2. 通过自身类型that =>
    object InnerDemo1 {
        def main(args: Array[String]): Unit = {
            val outer1 = new Outer("outer1")
            val inner1 = new outer1.Inner
    
            val outer2 = new Outer("outer2")
            val inner2 = new outer2.Inner
    
            inner1.foo(inner1)
    
            inner1.foo(inner2)
        }
    }
    class Outer(val outerName: String) {
        // 自身类型
        that =>
        val a = 20
        class Inner {
            val a = 10
    
            def foo(obj: Outer#Inner) = {
                println("Inner ... foo")
                println("inner = " + a)
                println("inner = " + this.a)
                // 外部类的对象
                println("outer = " + Outer.this.a)  //通过Outer.this, 调用外部类对象成员方法一
                println("outerName = " + that.outerName) //通过自身类型that =>, 调用外部类对象成员方法方法二
            }
        }
    
    }

    输出

    Inner ... foo
    inner = 10
    inner = 10
    outer = 20
    outerName = outer1
    Inner ... foo
    inner = 10
    inner = 10
    outer = 20
    outerName = outer1
  • 相关阅读:
    vi编辑器
    数据发送的三种方式
    发送文件的三种方式
    提交表单提示框确定取消 点取消不会刷新页面
    input value="值栈的值"
    值栈
    struts2文件上传突破2M限制
    jsp取不到值栈的值
    站群--插件--点击量
    Java WebService 简单实例
  • 原文地址:https://www.cnblogs.com/bitbitbyte/p/12782541.html
Copyright © 2011-2022 走看看