• scala(五):面向对象-包、类型、继承和多态、抽象类、单列对象、特质


    目录

    类和对象

    封装

    继承和多态

    抽象类

    单列对象&伴生对象

    特质(Trait)

    扩展


    (1)基本语法

    package 包名

    (2)Scala包的三大作用(和 Java 一样)

    1)区分相同名字的类;

    2)可以很好的管理类;

    3)控制访问范围;

    包命名

    (1)命名规则

    只能包含数字、字母、下划线、小圆点.,不能用数字、关键字开头。

    (2)命名规范

    一般是小写字母+小圆点

    com.公司名.项目名.业务模块名

    包说明

    (1)说明

    Scala有两种包的管理风格,一种方式和Java的包管理风格相同,包名用“.”进行分隔以表示包的层级关系,如com.zj.scala。

    另一种风格,通过嵌套的风格表示层级关系

    package com{
        package zj{
            package scala{
            }   
        }
    }

    第二种风格有以下特点:

    1)一个源文件中可以声明多个package;

    2)子包中的类可以直接访问父包中的内容,而无需导包;

    包对象

    在Scala中可以为每个包定义一个同名的包对象,定义在包对象中的成员,作为其对应包下所有class和object的共享变量,可以被直接访问。

    1. package com {
    2. object Test01 {
    3. val out: String = "out"
    4. def main(args: Array[String]): Unit = {
    5. println(name)
    6. println(out)
    7. }
    8. }
    9. }
    10. package object com {
    11. val name : String = "com"
    12. }

    导包说明

    import.com.zj.test引入com.zj包下的test(class和object)
    import.com.zj._引入com.zj下的所有成员
    import.com.zj.test._引入com.zj下test的object所有成员
    import.com.zj.{test,test2}引入com.zj下的test和test2
    import.com.zj.{test=>TEST}引入com.zj下的test并改名为TEST
    import.com.zj.{test=>TEST,_}引入com.zj下的所有成员,并将test改名为TEST
    import.com.zj.{test=>_,_}引入com.zj下的屏蔽test类
    new_root_.java.util.HashMap引入的java绝对路径

    scala的三个默认包:import java.lang._        import scala._        import scala.Predef._

    访问权限

    在Java中访问权限分为:public,private,protected和默认。Scala中可以通过类似的修饰符达到同样的效果。但是使用上有区别。

    (1)Scala中无public关键字,但Scala中属性和方法的默认访问权限为public。

    (2)private为私有权限,只在类的内部和伴生对象中可用。

    (3)protected为受保护权限,Scala中受保护权限比Java中更严格,同类、子类可以访问,同包无法访问。

    (4)private[包名]增加包访问权限,包名下的其他类也可以使用。

    1. class Person {
    2. private var name: String = "jeffry"
    3. protected var age: Int = 20
    4. private[faceobject] var sex: String = "男"
    5. def say(): Unit = {
    6. println(name)
    7. println(age)
    8. println(sex)
    9. }
    10. }
    11. object Test01 {
    12. def main(args: Array[String]): Unit = {
    13. //
    14. val person = new Person
    15. person.say()
    16. // println(person.name) //不能使用
    17. // println(person.age) //不能使用
    18. println(person.sex)
    19. }
    20. }
    21. class Teacher extends Person {
    22. def test(): Unit = {
    23. // this.name //不能使用
    24. this.age
    25. this.sex
    26. }
    27. }
    28. class Person02 {
    29. def test(): Unit = {
    30. // new Person().name //不能使用
    31. // new Person().age //不能使用
    32. new Person().sex
    33. }
    34. }

    类和对象

    定义类

    基本语法:【修饰符】class 类名 {类体}

    1Scala语法中,类并不声明为public,所有这些类都具有公有可见性(即默认就是public);

    2)一个Scala源文件可以包含多个类;

    属性

    基本语法:【修饰符】var/val 属性名称 【:类型】 = 属性值

    注:Bean属性(@BeanPropetry可以自动生成规范的setXxx/getXxx方法。

    1. import scala.beans.BeanProperty
    2. class Person03 {
    3. var name: String = "jeffry"
    4. var age: Int = _ // _表示给属性一个默认值
    5. //Bean 属性(@BeanProperty)
    6. @BeanProperty var sex: String = "男"
    7. }
    8. object Tets02 {
    9. def main(args: Array[String]): Unit = {
    10. var person = new Person03()
    11. println(person.name)
    12. person.setSex("男")
    13. println(person.getSex)
    14. }
    15. }

    方法

    基本语法:def 方法名(参数列表)【:返回值类型】 = {方法体}

    1. class SUM {
    2. def Sum(a:Int,b:Int): Int = {
    3. a + b
    4. }
    5. }
    6. object Tets03 {
    7. def main(args: Array[String]): Unit = {
    8. val sum = new SUM()
    9. println(sum.Sum(5,10))
    10. }
    11. }

    创建对象

    基本语法:var/val 对象名 【:类型】 = new 类型()

    1val修饰对象,不能改变对象的引用(即:内存地址),可以改变对象属性的值。

    2var修饰对象,可以修改对象的引用和修改对象的属性值。

    3)自动推导变量类型不能多态,所以多态需要显示声明。

    1. class Person03 {
    2. var name: String = "jeffry"
    3. }
    4. object Tets02 {
    5. def main(args: Array[String]): Unit = {
    6. val person = new Person03()
    7. person.name = "zj"
    8. println(person.name)
    9. }
    10. }

    构造器 

     和Java一样,Scala构造对象也需要调用构造方法,并且可以有任意多个构造方法。

    Scala类的构造器包括:主构造器和辅助构造器

    基本语法:

    class 类名(形参列表) { // 主构造器

                                            // 类体

    def this(形参列表) { // 辅助构造器

    }

    def this(形参列表) { //辅助构造器可以有多个...

    }

    }

    说明:

    (1)辅助构造器,函数的名称this可以有多个,编译器通过参数的个数及类型来区分。

    (2)辅助构造方法不能直接构建对象,必须直接或者间接调用主构造方法。

    (3)构造器调用其他另外的构造器,要求被调用构造器必须提前声明。  

    1. class Person04 {
    2. var name : String = _
    3. var age : Int = _
    4. def this(age: Int) {
    5. this()
    6. this.age = age
    7. println("辅助构造器1")
    8. }
    9. def this(age: Int,name: String) {
    10. this()
    11. this.age = age
    12. this.name = name
    13. println("辅助构造器2")
    14. }
    15. println("主构造器")
    16. }
    17. object Test04 {
    18. def main(args: Array[String]): Unit = {
    19. val person = new Person04(18)
    20. }
    21. }

    构造器参数

     (1)未用任何修饰符修饰,这个参数就是一个局部变量;

    (2var修饰参数,作为类的成员属性使用,可以修改;

    (3val修饰参数,作为类只读属性使用,不能修改; 

    封装

    封装就是把抽象出的数据和对数据的操作封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(成员方法),才能对数据进行操作。Java封装操作如下:

    (1)将属性进行私有化;

    (2)提供一个公共的set方法,用于对属性赋值;

    (3)提供一个公共的get方法,用于获取属性的值;

    Scala中的public属性,底层实际为private,并通过get方法(obj.field())和set方法 (obj.field_=(value))对其进行操作。所以Scala并不推荐将属性设为private,再为其设置public的get和set方法的做法。但由于很多Java框架都利用反射调用getXXX和setXXX方法,有时候为了和这些框架兼容,也会为Scala的属性设置getXXX和setXXX方法(通过 @BeanProperty 注解实现)。

    继承和多态

    基本语法:class 子类名· extends 父类名 {类体}

    (1)子类继承父类的属性和方法;

    (2)scala是单继承;

    案例:

    (1)继承的调用顺序:父类构造器->子类构造器

    1. class Person05(names: String) {
    2. var name = names
    3. var age : Int = _
    4. def this(nameP: String,ageP: Int) {
    5. this(nameP)
    6. this.age = ageP
    7. println("父类辅助构造器")
    8. }
    9. println("父类主构造器")
    10. }
    11. class Person06(names : String,ages : Int) extends Person05(names,ages) {
    12. var no : Int = _
    13. def this(names : String,ages : Int,no : Int) {
    14. this(names,ages)
    15. this.no = no
    16. println("子类的辅助构造器")
    17. println(names,ages,no)
    18. }
    19. }
    20. object Test05 {
    21. def main(args: Array[String]): Unit = {
    22. new Person06("jeffry",20,1001)
    23. }
    24. }

     重写

    Scala中属性和方法都是动态绑定,而Java中只有方法为动态绑定。

    1. class Person07 {
    2. val name : String = "person07"
    3. def say() : Unit = {
    4. println("原方法")
    5. }
    6. }
    7. class Person08 extends Person07 {
    8. override val name: String = "person08"
    9. override def say(): Unit = {
    10. println("重写方法!")
    11. }
    12. }
    13. object Test06 {
    14. def main(args: Array[String]): Unit = {
    15. val person01 : Person07 = new Person07()
    16. println(person01.name)
    17. person01.say()
    18. val person02 : Person08 = new Person08()
    19. println(person02.name)
    20. person02.say()
    21. }
    22. }

    抽象类

    抽象属性和抽象方法

    基本语法:

    (1)定义抽象类:abstract class Person{} //通过 abstract 关键字标记抽象类;

    2)定义抽象属性:val|var name:String //一个属性没有初始化,就是抽象属性;

    3)定义抽象方法:def hello():String //只声明而没有实现的方法,就是抽象方法;

    案例:

    1. abstract class Person {
    2. val name : String
    3. def say() : Unit
    4. }
    5. class Teacher extends Person {
    6. val name : String = "teacher"
    7. override def say(): Unit = {
    8. println("hello teacher!!!!!")
    9. }
    10. }

    继承和重写

    1)如果父类为抽象类,那么子类需要将抽象的属性和方法实现,否则子类也需声明为抽象类;

    2)重写非抽象方法需要用override修饰,重写抽象方法则可以不加override;

    3)子类中调用父类的方法使用super关键字;

    4)子类对抽象属性进行实现,父类抽象属性可以用var修饰;

    (5)子类对非抽象属性重写,父类非抽象属性只支持val类型,而不支持var类型,因为var修饰的为可变变量,子类继承之后就可以直接使用,没有必要重写。

    匿名子类

    Java一样,通过包含带有定义或重写的代码块的方式创建一个匿名的子类。

    案例:

    1. abstract class Person {
    2. val name : String
    3. def say() : Unit
    4. }
    5. object Test07 {
    6. def main(args: Array[String]): Unit = {
    7. val person = new Person {
    8. override val name: String = "jeffry"
    9. override def say(): Unit = {
    10. println("hello jeffry!!!!")
    11. }
    12. }
    13. }
    14. }

    单列对象&伴生对象

    Scala语言是完全面向对象的语言,所以并没有静态的操作。但是为了能够和Java语言交互,就产生了一种特殊的对象来模拟类对象,该对象为单例对象。若单例对象名与类名一致,则称该单例对象为这个类的伴生对象,这个类的所有“静态”内容都可以放置在它的伴生对象中声明。

    单列对象语法

    基本语法:

    object person {

        val name:String=“jeffry”

    }

    (1)单例对象采用 object 关键字声明。

    (2)单例对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。

    (3)单例对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问。

    1. object Person {
    2. var names : String = "Tom"
    3. }
    4. class Person {
    5. var name : String = "jeffry"
    6. }
    7. object Test07 {
    8. def main(args: Array[String]): Unit = {
    9. //单例对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问。
    10. println(Person.names)
    11. val person = new Person()
    12. println(person.name)
    13. }
    14. }

    apply方法

     (1)通过伴生对象的apply方法,实现不使用new方法创建对象。

    (2)如果想让主构造器变成私有的,可以在()之前加上private。

    (3)apply方法可以重载。

    (4)Scala中obj(arg)的语句实际是在调用该对象的apply方法,即obj.apply(arg)。用以统一面向对象编程和函数式编程的风格。

    (5)当使用new关键字构建对象时,调用的其实是类的构造方法,当直接使用类名构建对象时,调用的其实时伴生对象的apply方法。 

    案例:

    1. object Test07 {
    2. def main(args: Array[String]): Unit = {
    3. //通过伴生对象的 apply 方法,实现不使用 new 关键字创建对象
    4. val P = Person()
    5. println("P.name=" + P.name)
    6. val P1 = Person("Tom")
    7. println("P1.name=" + P1.name)
    8. }
    9. }
    10. //如果想让主构造器变成私有的,可以在()之前加上 private
    11. class Person private(names : String) {
    12. var name : String = names
    13. }
    14. object Person {
    15. def apply(): Person = {
    16. println("apply空参被调用")
    17. new Person("jeffry")
    18. }
    19. def apply(names: String): Person = {
    20. println("apply有参被调用")
    21. new Person(names)
    22. }
    23. }

    特质(Trait)

    Scala语言中,采用特质trait(特征)来代替接口的概念,也就是说,多个类具有相同的特质时,就可以将这个特质独立出来,采用关键字trait声明。

    Scala中的trait中即可以有抽象属性和方法,也可以有具体的属性和方法,一个类可以混入多个特质。这种感觉类似于Java中的抽象类。

    Scala引入trait特征,第一可以替代Java的接口,第二个也是对单继承机制的一种补充。

    基本语法:

    trait 特质名 {

        trait 主体

    }

    案例:

    1. trait PersonTrait {
    2. //声明属性
    3. var name : String = _
    4. //声明方法
    5. def say() : Unit = {}
    6. //抽象属性
    7. var age : Int
    8. //抽象方法
    9. def sayy() : Unit
    10. }

    特质基本语法

    一个类具有某种特质,就意味着这个类满足了这个特质的所有要素,所以在使用时,也采用了extends关键字,如果有多个特质或存在父类,那么需要采用with关键字连接。

    基本语法:

    没有父类:class 类名 extends 特质 1 with 特质 2 with 特质 3 …

    有父类:class 类名 extends 父类 with 特质 1 with 特质 2 with 特质 3…

    说明:

    (1)类和特质的关系:使用继承的关系。

    (2)当一个类去继承特质时,第一个连接词是extends,后面是with。

    (3)如果一个类在同时继承特质和父类时,应当把父类写在extends后。

    案例:

    1. trait PersonTrait {
    2. //特质可以同时拥有抽象方法和具体方法
    3. //声明属性
    4. var name : String = _
    5. //抽象属性
    6. var age : Int
    7. //声明方法
    8. def say() : Unit = {}
    9. //抽象方法
    10. def sayy() : Unit
    11. }
    12. trait SexTrait {
    13. var sex : String
    14. }
    15. //一个类可以实现/继承多个特质
    16. //所有的java接口都可以当做scala特质使用
    17. class TeacherTest extends PersonTrait with java.io.Serializable {
    18. override def sayy(): Unit = {
    19. println("sayy")
    20. }
    21. override var age: Int = _
    22. }
    23. object Test08 {
    24. def main(args: Array[String]): Unit = {
    25. val teacher = new TeacherTest
    26. teacher.say()
    27. teacher.sayy()
    28. //动态混入:可灵活的扩展类的功能
    29. val teacher2 = new TeacherTest with SexTrait {
    30. override var sex: String = "男"
    31. }
    32. //调用混入trait的属性
    33. println(teacher2.sex)
    34. }
    35. }

    特质叠加 

    由于一个类可以混入(mixin)多个trait,且trait中可以有具体的属性和方法,若混入的特质中具有相同的方法(方法名,参数列表,返回值均相同),必然会出现继承冲突问题。

    冲突分为以下两种:

    第一种,一个类(Sub)混入的两个trait(TraitA,TraitB)中具有相同的具体方法,且两个trait之间没有任何关系,解决这类冲突问题,直接在类(Sub)中重写冲突方法。

    第二种,一个类(Sub)混入的两个trait(TraitA,TraitB)中具有相同的具体方法,且两个trait继承自相同的trait(TraitC),及所谓的“钻石问题”,解决这类冲突问题,Scala采用了特质叠加的策略。所谓的特质叠加,就是将混入的多个trait中的冲突方法叠加起来,案例如下,

    trait Ball {
    def describe(): String = {
    "ball"
            }
    }
    trait Color extends Ball {
    override def describe(): String = {
    "blue-" + super.describe()
            }
    }
    trait Category extends Ball {
    override def describe(): String = {
    "foot-" + super.describe()
            }
    }
    class MyBall extends Category with Color {
    override def describe(): String = {
    "my ball is a " + super.describe()
            }
    }

    1)案例中的super,不是表示其父特质对象,而是表示上述叠加顺序中的下一个特质,即,MyClass中的super指代Color,Color中的super指代Category,Category中的super指代Ball。

    2)如果想要调用某个指定的混入特质中的方法,可以增加约束:super[],例如:super[Category].describe() 

    特质自身类型

    自身类型可以实现依赖注入的功能。

    案例:

    1. class User(val name : String,val age : Int)
    2. trait Dao {
    3. def insert(user: User) = {
    4. println("Dao :"user.name)
    5. }
    6. }
    7. trait APP {
    8. _: Dao =>
    9. def login(user: User) : Unit = {
    10. println("login :" + user.name)
    11. insert(user)
    12. }
    13. }
    14. object Test09 extends APP with Dao {
    15. def main(args: Array[String]): Unit = {
    16. login(new User("jeffry",20))
    17. }
    18. }

    特质和抽象类的区别

    (1)优先使用特质。一个类扩展多个特质是很方便的,但却只能扩展一个抽象类。

    (2)如果你需要构造函数参数,使用抽象类。因为抽象类可以定义带参数的构造函数,而特质不行,特质是有无参构造。

    扩展

    类型检查和转换

    1obj.isInstanceOf[T]:判断obj是不是T类型。

    2obj.asInstanceOf[T]:将obj强转成T类型。

    3classOf获取对象的类名。

    1. class Person11 {
    2. }
    3. object Test10 {
    4. def main(args: Array[String]): Unit = {
    5. val person = new Person11
    6. // 判断是不是person类型
    7. val bool : Boolean = person.isInstanceOf[Person11]
    8. // 强转成person类型。
    9. if (bool) {
    10. val p1 : Person11 = person.asInstanceOf[Person11]
    11. println(p1)
    12. }
    13. // 获取对象的类名
    14. val PClass : Class[Person11] = classOf[Person11]
    15. println(PClass)
    16. }
    17. }

    枚举类型和应用类型

    枚举类:需要继承Enumeration

    应用类:需要继承App

    1. object Test11 {
    2. def main(args: Array[String]): Unit = {
    3. println(Color.BLUE)
    4. println(APPS)
    5. }
    6. }
    7. //枚举类
    8. object Color extends Enumeration {
    9. val RED = Value(1,"red")
    10. val BLUE = Value(2,"blue")
    11. val YELLOW = Value(3,"yellow")
    12. }
    13. //应用类
    14. object APPS extends App {
    15. println("++++++++++++")
    16. }

    type定义新类型

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

    1. def main(args: Array[String]): Unit = {
    2. type S=String
    3. var v:S="abc"
    4. def test():S="jeffry"
    5. }

  • 相关阅读:
    计算机毕业设计之java+ssm交通信息网上查询系统
    ArcGIS在VUE框架中的构建思想
    SVM(下):如何进行乳腺癌检测?
    AE& VAE 代码和结果记录
    seatunnel win idea 本地调试
    WPF绑定单变量Binding和绑定多变量MultiBinding 字符串格式化 UI绑定数据,数据变化自动更新UI,UI变化自动更新数据
    Java面试核心知识点----Java面试题合集最新版
    【面试:并发篇36:多线程:设计模式】享元模式-应用:自定义连接池
    Java SE 10 新增特性
    C++的deque(双端队列),priority_queue(优先级队列)
  • 原文地址:https://blog.csdn.net/qq_55906442/article/details/127438762