一、类和对象

scala是面向对象的语言,也有类和对象的概念。
1、创建类和对象

  1. /**
  2. * @Author laoyan
  3. * @Description TODO
  4. * @Date 2022/5/4 9:38
  5. * @Version 1.0
  6. */
  7. object _01类和对象 {
  8. // 编写一个类
  9. class Person{}
  10. // 简化的写法
  11. // 如果类是空的,没有任何的成员,可以省略 {}
  12. // 如果构造器的参数为空,可以省略()\
  13. class Person2
  14. def main(args: Array[String]): Unit = {
  15. // 进行对象的实例化
  16. val person = new Person()
  17. println(person)
  18. val p2 = new Person2
  19. println(p2)
  20. }
  21. }

2、类中的成员变量

  1. /**
  2. * @Author laoyan
  3. * @Description TODO
  4. * @Date 2022/5/4 9:38
  5. * @Version 1.0
  6. */
  7. object _02成员变量 {
  8. // 编写一个类
  9. class Person{
  10. // 类中的属性可以进行 var/val 修饰
  11. // 对象可以直接访问属性
  12. /**
  13. * 在 var 类型修饰的成员变量中,有默认值,这个默认值可以使用 _ 表示
  14. * int 0
  15. * double 0.0
  16. * boolean false
  17. * String null
  18. * 如果使用val 修饰 ,必须自己手动的进行初始化
  19. * 总结: class 中的成员变量,一定要给初始值。
  20. * var 修饰 ,默认的初始值可以使用 _ 表示
  21. * val 修饰 ,必须自己手动进行初始化
  22. */
  23. var name = "张三"
  24. var age:Int = _;
  25. val school:String = "某某小学";
  26. }
  27. def main(args: Array[String]): Unit = {
  28. // 进行对象的实例化
  29. val person = new Person()
  30. println(person.name)
  31. }
  32. }

3、类中的方法

  1. /**
  2. * @Author laoyan
  3. * @Description TODO
  4. * @Date 2022/5/4 9:52
  5. * @Version 1.0
  6. */
  7. object _03类中的方法 {
  8. // 对比类中的方法和这个object 中的方法的区别是什么?
  9. // 这个syGoodBye 是静态方法
  10. def sayGoodBye(msg:String)={
  11. println("bye,bye:"+msg)
  12. }
  13. class Person{
  14. var name:String = _ // null
  15. // sayHello 指的就是我们所说的类中的方法
  16. // 这个类中的方法不是静态的,不能直接访问,需要有对象来访问
  17. def sayHello(msg:String): Unit ={
  18. println("Hello,"+msg)
  19. }
  20. }
  21. def main(args: Array[String]): Unit = {
  22. var p =new Person
  23. // 是一个普通方法
  24. p.sayHello("laoyan")
  25. //_03类中的方法.sayGoodBye("laoyan")
  26. // 根本上就是一个静态方法,不需要对象就可以访问
  27. sayGoodBye("laoyan")
  28. }
  29. }

4、类中的访问修饰符

  1. /**
  2. * @Author laoyan
  3. * @Description TODO
  4. * @Date 2022/5/4 9:52
  5. * @Version 1.0
  6. */
  7. object _04类中访问修饰符 {
  8. /**
  9. * 语法:
  10. * 成员变量中可以修饰的符号: private /protected
  11. * scala 中没有public 修饰符,任何没有被private /protected 修饰的成员变量都是公共的。
  12. */
  13. class Person{
  14. private var name:String = _ // null
  15. var age = 10;
  16. private def sayHello(): Unit ={
  17. }
  18. }
  19. def main(args: Array[String]): Unit = {
  20. var p =new Person
  21. // p.name; 私有成员访问不了
  22. p.age;
  23. // p.sayHello() 私有的方法也访问不了
  24. }
  25. }

5、类中的构造器

  1. /**
  2. * @Author laoyan
  3. * @Description TODO
  4. * @Date 2022/5/4 10:07
  5. * @Version 1.0
  6. */
  7. object _05类中的构造器 {
  8. /**
  9. * class 类名(var/val 参数名:参数类型= 默认值 ,参数2,参数3....) {
  10. * }
  11. * scala中的类,主构造器的参数时直接放在类上面的
  12. * 构造器中的参数类型不能省略
  13. * 如果编写了主构造器,默认的无参数的构造器,不会被顶掉,还在,可以继续使用
  14. */
  15. class Person(var name:String = "",var age:Int = 0){
  16. println("主构造器调用成功")
  17. }
  18. def main(args: Array[String]): Unit = {
  19. var p1 = new Person("老闫",19)
  20. var p2 = new Person
  21. var p3 = new Person(age = 30)
  22. println(p1)
  23. println(p1.name)
  24. }
  25. }
  1. /**
  2. * @Author laoyan
  3. * @Description TODO
  4. * @Date 2022/5/4 10:16
  5. * @Version 1.0
  6. */
  7. object _06辅助构造器 {
  8. /**
  9. * 辅助构造器:将主构造器之外的构造器
  10. * 辅助构造器跟定义方法一样,需要 def 关键字
  11. * 这个方法的名字: this
  12. * def this(参数名:参数类型,....){
  13. *
  14. * }
  15. *
  16. */
  17. class Customer(var name:String="",var age:Int = 0){
  18. //在这个类中,定义辅助构造器
  19. // 有点类似于我们之前的 构造方法 : 方法的重载
  20. def this(arr:Array[String]){
  21. // 辅助构造器的第一行,必须调用主构造器或者其他辅助构造器
  22. this(arr(0),arr(1).toInt)
  23. }
  24. def this(age:Int){
  25. // 辅助构造器的第一行,必须调用主构造器或者其他辅助构造器
  26. this("张三",age)
  27. }
  28. }
  29. def main(args: Array[String]): Unit = {
  30. val customer = new Customer(Array("张三", "10"))
  31. val customer2 = new Customer(100)
  32. println(customer.name)
  33. println(customer.age)
  34. }
  35. }

二、单例对象

  1. scala 中没有java中的静态成员的,如果我们想要定义 static 变量, static 方法,可以使用Object 对象,这样的对象就是单例对象,单例对象表示全局只有一个对象
  1. /**
  2. * @Author laoyan
  3. * @Description TODO
  4. * @Date 2022/5/4 10:34
  5. * @Version 1.0
  6. */
  7. object _07单例对象 {
  8. // 定义了一个单例对象
  9. object Dog{
  10. // 静态的属性
  11. val LEG_NUM = 4; // LEG_NUM就是静态的
  12. // 里面的方法都是静态方法
  13. def sayHello(): Unit ={
  14. println("wang~~~ wang ~~~")
  15. }
  16. }
  17. object PrintUtil {
  18. def printLine() = {
  19. println("-" * 10)
  20. }
  21. }
  22. def main(args: Array[String]): Unit = {
  23. println(Dog.LEG_NUM);
  24. Dog.sayHello()
  25. PrintUtil.printLine()
  26. }
  27. }

一般使用Object 编写工具类:

import java.text.SimpleDateFormat
import java.util.Date

/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/4 10:43
 * @Version 1.0
 */
object _08单例对象实战 {

    object DateUtils {

        val simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm")
        def format(date:Date) ={
            simpleDateFormat.format(date)
        }
    }

    def main(args: Array[String]): Unit = {
        println(new Date())
        val str: String = DateUtils.format(new Date())
        println(str)

    }
}

单例对象即 Object ,为什么main方法在Object 里面呢?
java中的main方法,就是一个静态的方法,scala中的静态方法,必须通过Object 才可以实现。

/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/4 10:51
 * @Version 1.0
 */

/**
 *  一般程序的入口都是main方法,但是我们也有第二种方式
 *   object A extends App
 *    App 是一个特质(trait) ,特质就是我们之前java中的接口
 */
object _09Main入口 extends App {

    println("我不是main,但是我却跟main一个效果")

    // 静态方法,是程序的入口。
    /*def main(args: Array[String]): Unit = {
        println("我不是main,但是我却跟main一个效果")
    }*/
}

伴生对象:

java 中一个类里面,可以有静态变量,非静态变量,静态方法,非静态方法,原因是人家有static 关键字

如果scala类中也想达到这样的效果呢,必须借助于伴生对象。

/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/4 10:59
 * @Version 1.0
 */
object _10伴生对象 {

    class Person{
        // 非静态的属性
        var name = "张三"
        private var age = 10;
        // 非静态的方法
        def sayHello() = {
            println("伴生对象的年龄是:"+Person.age);

        }
    }
    // object Person 就是 class Person的伴生对象

    /**
     *  伴生对象必须和伴生类名字一定要一样
     *  伴生对象和伴生类必须在一个scala源文件中
     *  伴生对象和伴生类可以互相访问 private 属性
     *  理论讲:伴生类和伴生对象,不分彼此,好兄弟,所以私有成员可以相互访问
     *   但是总有一些属性,不想让对象访问,既然私有的private 修饰都搞不定,
     *   干脆来一个新的内容   private[this] 这样对方就访问不了了。
     */
    object Person {
        private/*[this]*/ var age = 100;
         val p = new Person()
        def run() ={
            println("每个人都会跑步,年龄是:"+p.age)
        }
    }
    object A {
        // 全部都是静态的属性和方法
        // 如果两者结合起来,完美了
    }

    def main(args: Array[String]): Unit = {
        val person = new Person
        person.sayHello()
        Person.run()

    }
}

一个问题引发的问题:

var arr = Array(1,3,5)
如果它是一个对象,为什么不new 
它其实就是一个对象,省略了new 关键字,这个效果可以通过伴生对象的 apply 方法实现。
/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/4 11:17
 * @Version 1.0
 */
object _11伴生对象之apply {

    /**
     *   object 伴生对象名字 {
     *      def apply(参数1,参数2....) =new 类()
     *   }
     *
     *   创建对象的时候
     *   伴生对象名字(参数1,参数2 ...)
     *   var arr = Array(1,2,3)
     *   var list = List(1,2,3)
     */
    class Person(var name:String ="" ,var age :Int = 0)
    object Person {
        def apply(name:String,age:Int) = {
             new Person(name,age)
        }
    }

    def main(args: Array[String]): Unit = {
        var p = Person("杂散",20) // 此处没有new,使用的是apply方法
        println(p.name)
        println(p.age)
    }
}

三、继承

scala支持继承,语法java一样,都使用extends 关键字

class/object 子类 extends 父类 {

}
/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/4 14:36
 * @Version 1.0
 */
object _12继承 {

    class Person{
        var name = "父类"
        def getName = this.name
    }
    // class 以及单例对象都可以继承 class
    class Student extends Person
    object Teacher extends Person

    def main(args: Array[String]): Unit = {
        val person = new Person
        val student =new Student

        student.name = "张三"
        println(person.name)
        println(student.name)

        println(Teacher.name)

    }
}

override & super

/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/4 14:36
 * @Version 1.0
 */
object _13继承中的关键字 {

    class Person{
        val name = "父类"
        def getName = this.name
    }
    class Student extends Person{
        //子类中可以重写父类中的字段
        override val name:String = "大黄"

        //子类中可以重写父类中的方法
        override def getName: String = "Hello,"+super.getName
    }


    def main(args: Array[String]): Unit = {
       println(new Student().getName)
    }
}

四、类型判断

instanceOf    :  一个对象是否属于某个类
                 一个对象是否属于某个接口
                 一个对象是否属于某个父类的子类对象

scala 中有两种类型判断的方法:
isInstanceOf
getClass/classOf


scala中:
isInstanceOf 判断对象是否为指定类的对象 (boolean)
  var tureOrFalse :Boolean = 对象.isInstanceOf[类型]
asInstanceOf 将对象转换为指定类型 (类型转换)
  val 名字 = 对象.asInstanceOf[类型]
/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/4 14:56
 * @Version 1.0
 */
object _14类型转换 {

    class Person
    class Student extends Person

    def main(args: Array[String]): Unit = {
        val student:Person = new Student
        // isInstanceOf 只能判断对象是否为指定类的对象或者子类对象,无法精确的判断出来具体属于哪个类
        println(student.isInstanceOf[Person]); // true
        println(student.isInstanceOf[Student]); // true

        if(student.isInstanceOf[Student]){
            val s2: Student = student.asInstanceOf[Student]
            println(s2)
        }
        println("-"*30)
        val p:Person = new Student
        // 判断p 对象是否为Person类的实例化对象
        println(p.isInstanceOf[Person]) // true
        println(p.getClass == classOf[Person]) // false
        // 以下这种方式可以非常精确的判断出一个对象是否是被某个类给实例化出来的。
        println(p.getClass == classOf[Student]) // true

    }
}

五、抽象类

抽象类:一个类中某个成员(变量,方法)定义是不完整的。
抽象类中所说的不完整:
1、方法没有方法体(抽象方法)
2、变量没有初始化(抽象字段)
定义:
abstract class 类名 {
    val 抽象字段:类型
    def 方法名(一堆参数)
}
/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/4 15:13
 * @Version 1.0
 */
// 定义一个抽象类,里面有一个求面积的抽象方法
abstract  class Shape {
    // 定义一个抽象的字段,不能有初始化的值,必须写上数据类型
    val NAME:String;
    def area:Double
}
// 正方形 ,继承抽象类,必须实现里面的抽象方法  alt + enter
class Square(var edge:Double) extends Shape {
    override def area: Double = edge * edge

    override val NAME: String = "正方形"
}
// 长方形
class Rect(var length:Double , var width:Double) extends Shape {
    override def area: Double = length * width

    override val NAME: String = "长方形"
}
// 圆
class Cirle(var radius:Double ) extends Shape {
    override def area: Double = radius * radius * Math.PI

    override val NAME: String = "圆"
}
object _15抽象类 {

    def main(args: Array[String]): Unit = {
        val square = new Square(3)
        val rect = new Rect(2, 3)
        val cirle = new Cirle(2)
        println(square.area)
        println(square.NAME)
        println(rect.area)
        println(cirle.area)
    }
}

六、匿名内部类

/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/4 15:27
 * @Version 1.0
 */
abstract class Person{
    def sayHello:Unit
}
object _16匿名内部类 {

    /**
     *
     * 跟java中的匿名内部类一模一样
     *   new 抽象类
     *     此时其实有一个子类继承了抽象类,只是我们看不到,所有说是匿名的
     *     我们只关注具体的抽象类的实现就可以了。
     */
    def main(args: Array[String]): Unit = {
        val person: Person = new Person {
            override def sayHello: Unit = println("我是匿名内部类")
        }
        person.sayHello
    }
}

七、特质(trait)

scala中没有接口的概念,可以使用特质来代替接口的使用。

trait 名字 {
  // 抽象字段
  // 抽象方法
}

class 类  extends 特质1 with 特质2 {

}
实现某个特质,没有实现的单词(implements) 只能使用extends 
如果有多个特质,使用 with 即可
/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/4 15:58
 * @Version 1.0
 */
// 接口中有一个抽象方法
trait Logger {
    def log(msg:String);
}
class ConsoleLogger extends Logger {
    override def log(msg: String): Unit = println("控制台打印日志:"+msg)
}
object _17继承单个特质 extends App{

     val consoleLogger = new ConsoleLogger
     consoleLogger.log("这是一条日志信息")
}
/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/4 16:02
 * @Version 1.0
 */
trait MessageSender{
    def send(msg:String):Unit;
}
trait MessageReceive{
    def receive():String
}
class MessageWorker extends MessageSender with MessageReceive {
    override def send(msg: String): Unit = println(s"发送一个消息:${msg}")

    override def receive(): String = "老闫真帅!"
}
object _18实现多个特质的案例 extends App{

     val worker = new MessageWorker
     worker.send("老闫帅到了天边")
     println(worker.receive())
}
/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/4 16:09
 * @Version 1.0
 */
trait Logger2{
    def log(msg:String)
}
// object 也可以实现特质接口
object ConsoleLogger2 extends Logger2{
    override def log(msg: String): Unit = println("控制台打印信息:"+msg)
}
object _19object继承特质 extends App{

    ConsoleLogger2.log("老闫真帅!")
}

特质中可以编写普通方法,该普通方法在子类中可以直接调用:

/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/4 16:14
 * @Version 1.0
 */
trait LoggerDetail {
    // 可以编写抽象方法,也可以编写具体的方法
    def log(msg:String) = println(msg)
}
// 一个类继承了特质,这个特质中的属性和方法都可以直接调用
class UserService extends LoggerDetail {
    def add() = {
        log("调用特质中的方法")
    }
}
object _20特质中的方法 {

    def main(args: Array[String]): Unit = {
        val service = new UserService
        service.add();
    }
}

特质中可以放置普通字段以及抽象字段

import java.text.SimpleDateFormat
import java.util.Date

/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/4 16:14
 * @Version 1.0
 */
trait LoggerDetail2 {
    var sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
    def log(msg:String)
}
// 一个类继承了特质,这个特质中的属性和方法都可以直接调用
class UserService2 extends LoggerDetail2 {
    override def log(msg: String): Unit = {
        // 此时的sdf 其实是 特质中的一个字段而已,可以直接访问,并使用
        val str: String = sdf.format(new Date())
        println(str+"||" + msg)
    }
}
object _21特质中的字段 {

    def main(args: Array[String]): Unit = {
        val service = new UserService2
        service.log("在此刻,老闫刷呆了");
    }
}

模板模式:

/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/4 16:27
 * @Version 1.0
 */
trait Loggerr {
    // 抽象方法
    def log(msg:String)
    // 以下三个方法是普通方法 ,普通方法调用 抽象方法,就是模板模式
    def info(msg:String) = log("Info:"+msg)
    def warn(msg:String) = log("warn:"+msg)
    def error(msg:String) = log("error:"+msg)

}
class ConsoleLoggerr  extends Loggerr{
    override def log(msg: String): Unit = println(msg)
}
object _22特质实现模板模式 {

    /**
     * 在一个特质中,具体的方法需要依赖于抽象方法,而抽象方法可以放在具体的继承trait 的子类中实现的,这样的设计方式
     *  就称之为 模板模式
     */
    def main(args: Array[String]): Unit = {
        val loggerr = new ConsoleLoggerr
        loggerr.info("基本信息")
        loggerr.warn("警告信息")
        loggerr.error("错误信息")
    }
}

对象混入trait
混入的好处:就是可以给对象添加一些额外的行为(功能增强了)

/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/4 16:37
 * @Version 1.0
 */
object _23对象混入trait {

    trait Logger {
        def log(msg:String) = println(msg)

    }
    // 之前就是什么都没有的类,因为有了特质,功能添加了一个打印日志的功能
    class UserService

    def main(args: Array[String]): Unit = {
        // 对象中混入特质 trait
        val service = new UserService with Logger
        service.log("我拥有了新的技能")
    }

}

trait 实现调用链模式(责任链模式)
image.png

类可以继承多个trait后,可以依次调用trait中的同一个方法,只要让多个trait 的同一个方法在最后依次执行super关键字即可。类中的调用多个trait 中都有这个方法,会从右到左依次执行形成一个链条。
这样设计的好处是: 追加新的功能,非常的简单,不需要动什么就可以。
/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/4 16:42
 * @Version 1.0
 */
trait HandlerTrait {
    def handle(data:String) = println("正在处理数据")
}
trait DataValidateHandlerTrait  extends HandlerTrait {
    override def handle(data: String): Unit = {
        println("验证数据")
        super.handle(data)
    }
}

trait SignValidateHandlerTrait  extends HandlerTrait {
    override def handle(data: String): Unit = {
        println("验证签名")
        super.handle(data)
    }
}
class PayService extends DataValidateHandlerTrait with SignValidateHandlerTrait {
    override def handle(data: String): Unit = {
        println("准备支付")
        super.handle(data)
    }
}
object _24trait实现调用链 {

    def main(args: Array[String]): Unit = {
        val service = new PayService
        service.handle("支付参数已经传递...")
    }
}

trait 的构造机制:
一个类实现了多个trait,这些trait是如何构造的?
trait 中也有构造代码,但是和类不一样,特质不能有构造器参数
每一个特质只有一个无参数的构造器
一个类,继承另一个类,并且还继承了一堆的trait,当创建这个类的时候:构造顺序:

1、先执行父类中的构造器
2、从左到右依次执行trait中的构造器
3、如果trait 中还有父trait ,先构造父trait , 如果多个trait 都有相同的trait,父trait 只需要构造一次即可
4、执行子类构造器
/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/4 17:07
 * @Version 1.0
 */
object _25特质中的构造器 {

    // 父 trait
    trait Logger{
        println("执行logger构造器")
    }
    // 两个子特质
    trait MyLogger extends Logger {
        println("MyLogger");
    }
    trait TimeLogger extends Logger {
        println("TimeLogger");

    }
    // 父类
    class Person{
        println("执行person")
    }
    class Student extends Person with TimeLogger with MyLogger {
        println("Student")
    }

    def main(args: Array[String]): Unit = {
        /**
         *  初始化的顺序:
         *    执行person
         *    执行logger构造器
         *    TimeLogger
         *    MyLogger
         *    Student
         */
        new Student
    }

}

trait 可以继承 class :

/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/4 17:14
 * @Version 1.0
 */
class Tools {
    def printMsg(msg:String) = println(msg)
}
// 一个特质竟然继承自一个类,这个类中的方法就可以随意调用了
trait PrintS extends Tools {
    def dayin(msg:String) = printMsg("logger:"+msg)
}
class PPP extends PrintS {
    def sayGoodBye() = dayin("再见!")
}
object _26trait可以继承class extends App {

     val ppp = new PPP
     ppp.sayGoodBye()
}