prtln 可以快速的进行输出打印

一、作业代码

  1. /**
  2. * @Author laoyan
  3. * @Description TODO
  4. * @Date 2022/5/5 10:07
  5. * @Version 1.0
  6. */
  7. object _HomeWork01 {
  8. class PC {
  9. var cpu:String = _
  10. var neicun:String = _
  11. var yingpan:String = _
  12. def getDetails():Unit = {
  13. println(s"$cpu,$neicun,$yingpan")
  14. }
  15. }
  16. class XiaoMi extends PC {
  17. var brand: String = _
  18. override def getDetails(): Unit = {
  19. println(s"$cpu,$neicun,$yingpan,$brand")
  20. }
  21. }
  22. //使用Scala OOP实现台式机、笔记本、平台电脑与Computer之间的关系
  23. def main(args: Array[String]): Unit = {
  24. val pc = new PC
  25. /* pc.cpu = "12核"
  26. pc.neicun="32G"
  27. pc.yingpan="1T"*/
  28. val mi = new XiaoMi
  29. mi.cpu = "12核"
  30. mi.neicun="32G"
  31. mi.yingpan="1T"
  32. mi.brand ="小米"
  33. mi.getDetails()
  34. }
  35. }
  1. /**
  2. * @Author laoyan
  3. * @Description TODO
  4. * @Date 2022/5/5 10:16
  5. * @Version 1.0
  6. */
  7. class Point(x:Int,y:Int){
  8. }
  9. object Point{
  10. def apply(x:Int,y:Int) = new Point(x,y)
  11. }
  12. object _HomeWork02 extends App{
  13. // 定义一个Point类和一个伴生对象,使得我们可以不用new而直接用Point(3,4)来构造Point实例
  14. val point: Point = Point(3, 4)
  15. }
  1. /**
  2. * @Author laoyan
  3. * @Description TODO
  4. * @Date 2022/5/5 10:19
  5. * @Version 1.0
  6. */
  7. // App 是可以接收 args 参数的 args: Array[String]
  8. // 使用idea 传递参数有点问题?????
  9. object _HomeWork03 extends App{
  10. // 编写一个Scala应用程序Reverse,使用App特质,以反序打印命令行参数,
  11. // 用空格隔开。举例来说,运行“Hello World”应该打印World Hello`。
  12. for(str <- args.reverse) println(_)
  13. }

二、样例类

  1. case class 类名([var/val]成员变量1:类型1,....) {}
  2. 默认如果不写的话 ,默认就是val ,可以省略
  3. 如果成员变量需要被修改的话,使用var
  1. /**
  2. * @Author laoyan
  3. * @Description TODO
  4. * @Date 2022/5/5 10:33
  5. * @Version 1.0
  6. */
  7. object _01样例类 {
  8. /**
  9. * 样例类有点类似于我们的 pojo (实体)
  10. * 样例类进行实例化的时候,不需要编写 new 关键字
  11. * 以前要达到这个不写new 就可以得到一个对象的方法只有一个:
  12. * 需要编写这个类对应的伴生对象,并且在伴生对象中,还需要编写 apply 方法
  13. *
  14. */
  15. // 样例类中,如果变量前面不加 var val 默认是val的
  16. case class Person(name:String,age:Int)
  17. class Person2(name:String,age:Int)
  18. case class Person3(var name:String,age:Int)
  19. def main(args: Array[String]): Unit = {
  20. val person = Person("张三", 20)
  21. // person.name = "李四" 此处会报错,因为 name使用的是val修饰的
  22. val person2 = new Person2("张三", 20)
  23. println(person)
  24. val person3 = Person3("张三", 20)
  25. person3.name = "李四" // 此处就是正确的,因为使用var 修饰变量
  26. }
  27. }
  1. /**
  2. * @Author laoyan
  3. * @Description TODO
  4. * @Date 2022/5/5 10:43
  5. * @Version 1.0
  6. */
  7. object _02样例类中的方法 {
  8. /**
  9. * 样例类中,有很多默认的方法,不需要我们自己编写就有的
  10. * apply 方法
  11. * toString
  12. * equals
  13. * hashCode
  14. * copy
  15. */
  16. case class CasePerson(name:String,age:Int)
  17. def main(args: Array[String]): Unit = {
  18. val person: CasePerson = CasePerson("老闫", 20)
  19. println(person) // 打印对象,就是打印对象中的toString 方法
  20. println(person.toString) // 这个是样例类中默认的打印方式
  21. // 如何知道使用了apply方法,
  22. // 因为我们一个类,不需要new 就可以得到这个类的实例化对象,这个现象本身就说明了使用apply方法
  23. // equals 等同于 ==
  24. var lisi1 = CasePerson("lisi", 20)
  25. var lisi2 = CasePerson("lisi", 20)
  26. // equals 比较的是 两个对象的值,只要值完全一样,就相等,否则不能
  27. println(lisi1.equals(lisi2)) // 在输出的地方输入prtln true
  28. println(lisi1 == lisi2) // true
  29. // eq 比较的是两个对象的内存地址
  30. println(lisi1 .eq(lisi2) ) // false
  31. // 样例类也自动实现了hashCode 只要两个类中的值完全一样,hashCode 就一样
  32. println(lisi1.hashCode())
  33. println(lisi2.hashCode())
  34. // copy 方法,可以快速的创建相同的实例对象,也可以使用带名的参数重新赋值
  35. var lisi3 = CasePerson("lisi", 20)
  36. // 拷贝了一个值完全相等,但是地址还是不一样的对象
  37. val lisi4: CasePerson = lisi3.copy();
  38. println(lisi3.eq(lisi4))
  39. val lisi5: CasePerson = lisi3.copy(age=25);
  40. println(lisi5)
  41. }
  42. }

三、样例对象

image.png
样例对象,首先没有样例类常用。

1、定义枚举
2、进行消息的传递(Akka编程)
case object 类名
样例对象肯定是单例的了,而且没有主构造器
/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/5 11:26
 * @Version 1.0
 */
// 样例类经常当做枚举使用
trait Sex // 特质
case object Male extends Sex
case object Female extends Sex
case class Student(name:String,sex:Sex)
object _03样例类 {

    def main(args: Array[String]): Unit = {
        val student: Student = Student("老闫", Male)
        println(student)
    }
}

四、模式匹配(重点)

scala有强大的模式匹配机制,可以应用于多个场景:
1、switch 
2、类型查询(类型匹配)
3、可以快速获取数据

简单的模式匹配:

类似于java中的switch 在scala中,使用match 表达式替代

变量 match {
  case "变量1" => 表达式1
  case "变量2" => 表达式2
  case "变量3" => 表达式3
  case _ => 表达式 // 表示什么都没有匹配上的处理
}
import scala.io.StdIn

/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/5 11:36
 * @Version 1.0
 */
object _04简单的模式匹配 {

    def main(args: Array[String]): Unit = {
        val str: String = StdIn.readLine()
        str match {
            case "春" => println("春困")
            case "夏" => println("热")
            case "秋" => println("秋乏")
            case "冬" => println("冷")
            case _  => println("你不适合学习")
        }

        // 模式匹配是有返回值的,默认就是你语句的最后一句话
        val str1: String = str match {
            case "春" => "chun"
            case "夏" => "xia"
            case "秋" => "qiu"
            case "冬" => "dong"
            case _ => "ni zou!"
        }
        println(str1)
    }
}

类型匹配:

变量 match {
  case 类型1变量名:类型1 => 表达式1
  case 类型2变量名:类型2 => 表达式2
  case 类型3变量名:类型3 => 表达式3
  case _ => 表达式 // 表示什么都没有匹配上的处理
}
/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/5 11:45
 * @Version 1.0
 */
object _05类型匹配 {

    def main(args: Array[String]): Unit = {
        // 只看数据类型即可,跟值无关
        val a:Any = 10
        val str: String = a match {
            // 如果case 表达式中无需使用到匹配的变量,可以使用下划线代替
            case _: String => "String类型的数据"
            case _: Int => "Int类型的数据"
            case _: Double => "Double类型的数据"
        }
        println(str)
    }
}

模式匹配中的守卫:

在scala中,可以使用守卫来简化我们的代码(可以在case语句中添加条件的判断)

import scala.io.StdIn

/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/5 11:54
 * @Version 1.0
 */
object _06模式匹配之守卫 {

    def main(args: Array[String]): Unit = {
        val a = StdIn.readInt();
        a match {
            case 1 => println("[0~3]")
            case 2 => println("[0~3]")
            case 3 => println("[0~3]")
            case 4 => println("[4~8]")
            case 5 => println("[4~8]")
            case 6 => println("[4~8]")
            case 7 => println("[4~8]")
            case 8 => println("[4~8]")
        }
        //可以通过守卫的模式进行简化
        a match {
            case _ if a>=0 && a <=3 =>  println("[0~3]")
            case _ if a>=4 && a <=8 =>  println("[4~8]")
        }
    }
}

模式匹配之样例类(case class):

/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/5 12:01
 * @Version 1.0
 */
case class PersonP(name:String,age:Int)
case class Order(id:String)
object _07模式匹配样例类 {



    def main(args: Array[String]): Unit = {

        val person: Any = PersonP("老闫", 30)
        val order: Any = Order("28394829384324")
        order match {
            case PersonP(name,age) => println(s"$name,$age")
            case Order(id) => println(s"订单编号是$id")
            case _ => println("不是以上两种类型")
        }
    }
}

模式匹配之匹配集合

可以匹配数组,list,以及元组

/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/5 12:09
 * @Version 1.0
 */
object _08模式匹配之集合 {

    def main(args: Array[String]): Unit = {
        // 数组案例
        var arr = Array(1,3,4)
        arr match {
            case Array(1,x,y) => println(s"${x},${y}")
            case Array(0) => println("该数组中只有一个0")
            case Array(0,_*) => println("0 ......")
            case _ => println("没有匹配到任何的类型")
        }

        // 匹配list集合
        val list = List(0,2, 3)
        list match {
            case 0::Nil => println("该list中只有一个0")
            case 0::tail => println("该list第一个是0,后面还有很多的值")
            case x:: y :: Nil => println(s"list集合中只有两个值,${x},${y}")
            case _ => println("没有匹配到任何的类型")
        }

        // 匹配元组
        val tuple = (2,3,5)
        tuple match {
            // 匹配模式,如果可以匹配多个值,先匹配哪个就执行哪个,后面不执行
            case (1,x,y) => println(s"元组中有三个值,1开头,另外两个是${x},${y}")
            case (x,y,5) => println(s"元组中有三个值,5结尾,另外两个是${x},${y}")
            case _ => println("没有匹配到任何的类型")

        }
    }
}

变量中的模式匹配:

可以通过模式匹配快速获取数据

/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/5 14:38
 * @Version 1.0
 */
object _09变量模式匹配 {

    def main(args: Array[String]): Unit = {
        // 获取数组中的元素
        var arr = (1 to 10).toArray
        // 通过模式匹配获取 数组中的 第二第三第四个值
        var Array(_,x,y,z,_*) = arr
        println(x,y,z)
        var list = (1 to 10).toList
        val a::b::tail = list
        println(a,b)

    }
}

五、Option 类型

一般是将数据存放到option类型中,需要的时候获取出来。
可以有效的避免null(空指针异常):
有两种数据形式:
Option 是一个总称,里面有两种数据的类型,一个Some 一个None
Some(x) : 表示实际的值
None: 表示没有值

/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/5 14:48
 * @Version 1.0
 */
object _10Option数据类型 {

    // 编写一个两个数相除的方法
    def dvi(a:Double,b:Double) = {
         if(b != 0){
             Some(a / b)
         }else{
             None
         }
    }

    def main(args: Array[String]): Unit = {
        val op: Option[Double] = dvi(10, 5)
        /*op match {
            case Some(x) => println(x)
            case None => println("除0异常")
        }*/
        // 可以使用getOrElse 方法,如果为None,可以给定默认值
        println(op.getOrElse(0))

    }
}

六、偏函数

偏函数是一种简写的方式。
偏函数被包在{} 内,没有 match 的一组 case 语句就是偏函数
语法:
  PartialFuction[A,B] 
 A: 输入的数据类型
 B: 代表输出的数据类型
/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/5 14:59
 * @Version 1.0
 */
object _11偏函数 {



    def main(args: Array[String]): Unit = {
        // 首先是 {} 括起来
        // 没有match  只有case
        // val func1: PartialFunction[Int,String]
        val func1: PartialFunction[Int,String] = {
            case 1 => "一"
            case 2 => "二"
            case 3 => "三"
        }
        println(func1(2))

        val list: List[Int] = (1 to 10).toList
        val list2: List[String] = list.map({
            case x if x >= 1 && x <= 3 => "[1~3]"
            case x if x >= 4 && x <= 8 => "[4~8]"
            case x if x > 8 => "(8~]"
        })
        // 以上方法可以简化为:
        // map 中传递了一个偏函数,此时可以省略()
        val list3: List[String] = list.map{
            case x if x >= 1 && x <= 3 => "[1~3]"
            case x if x >= 4 && x <= 8 => "[4~8]"
            case x if x > 8 => "(8~]"
        }

        println(list3)
    }
}

七、正则表达式

var regex = """正则表达式""".r

findAllMatchIn 方法
可以获取到所有正则匹配到的字符串
/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/5 15:12
 * @Version 1.0
 */
object _12正则表达式 {

    def main(args: Array[String]): Unit = {
        // 邮箱的正则  8383@qq.com
        val regex = """.+@.+\..+""".r
        val email1 = "34343@163.com"
        val email2 = "xxxxxx@com"
        if(regex.findAllMatchIn(email1).size >0 ){
            println("邮箱合法")
        }else{
            println("邮箱不合法")
        }

        if(regex.findAllMatchIn(email2).size >0 ){
            println("邮箱合法")
        }else{
            println("邮箱不合法")
        }

        // 给定一堆的 邮箱,校验哪些是正确的邮箱
        val emailList = List("32343@qq.com","23sfs3re@gmail.com","zhangsanlisi@yeah.net","32333.com");
        val hefaEmailList: List[String] = emailList.filter(x => if (regex.findAllMatchIn(x).size > 0) true else false)
        println(hefaEmailList)

        // 需求升级了,获取合法的邮箱以及运营商名称
        val regex2 = """.+@(.+)\..+""".r  // 跟之前相比,多了一个括号,这个括号叫做匹配分组
        // map 方法,可以将list集合中的数据,一个个的执行函数(此处是偏函数) --> 得到一个新的list集合
        val newList: List[String] = emailList.map ({
            case x @ regex2(yunyingshang) => s"${x}->${yunyingshang}"
            case x => x + "=> 未知运营商"
        })
        println(newList)
    }
}

八、异常处理

异常处理的基本用法:

import scala.util.control.Exception

/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/5 15:38
 * @Version 1.0
 */
object _13异常处理 {

    /**
     *  try{
     *
     *  }catch{
     *     case ex:异常类型1  => 执行某代码
     *     case ex:异常类型2 => 执行某代码
     *  }
     *  finally {
     *
     *  }
     *
     */
    def yichang(){
        /**
         *   遇到某个问题,我们也可以抛异常,这个异常抛出后,不需要在方法上继续throws
         *   java 中的throws 设计被认为是设计上的失败
         */
        throw NullPointerException;
    }
    def main(args: Array[String]): Unit = {
        try{
            var i = 10 / 0;
        }catch{
            case ex:ArithmeticException => println(ex.getMessage)
            case ex:NullPointerException => println("有空指针了,快处理")
        }finally {
            println("不管怎样,我都会执行的")
        }

        println("是否继续运行呢")

        yichang()
    }
}

九、提取器

样例类可以进行模式匹配,但是我们自己写的普通类,是无法进行模式匹配的,为了使我们的普通类也可以进行模式匹配,普通类需要添加提取器。
普通类需要定义提取器才能进行模式匹配:

apply 方法
变量1
变量2
.....
通过apply 方法进行构造  成一个   对象

对象 --> unapply(解构) --> 变量一,变量二.....

如何实现一个类的提取器:
 只需要在这个类的伴生对象中,实现unapply方法即可

def unapply(stu:Student):Option[(类型1,.....)] ={
   Some((变量1,变量2.....))
}
/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/5 16:15
 * @Version 1.0
 */
object _14提取器 {

    class Student(var name:String,var age:Int)
    object Student {
        // 构造
        def apply(name:String, age:Int)={
            new Student(name,age)
        }

        // 伴生对象中的解构方法
        def unapply(student: Student) = {
            if(student != null){
                var tuple = (student.name,student.age)
                Some(tuple)
            }else{
                None
            }

        }
    }

    def main(args: Array[String]): Unit = {
        val zhangsan =  Student("张三", 20)
        zhangsan match {
            case Student(name,age) => println(s"$name,${age}")
        }
    }
}

十、泛型

1、方法上添加泛型

/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/5 16:31
 * @Version 1.0
 */
object _15泛型 {

    /*def getMiddle(arr:Array[Int]) = {
        arr(arr.length /2)
    }*/

    /**
     *   编写方法的时候,我们不指定返回值类型
     *   def 方法名字[泛型] () = {
     *
     *   }
     */
    def getMiddle[T](arr:Array[T]) = {
        arr(arr.length /2)
    }

    // 升级需求:不管这个数组是什么类型,都可以获取中间的值
    def main(args: Array[String]): Unit = {
        val arr: Array[Int] = Array(1, 3, 5, 6, 8)
        println(getMiddle(arr))
        val arr2: Array[String] = Array("1"," 3"," 5", "6", "8")
        println(getMiddle(arr2))
    }
}

2、类上可以添加泛型

class 类名[T](val 变量名:T)
/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/5 16:40
 * @Version 1.0
 */

case class Pair[T](var a:T,var b:T)
object _16类上添加泛型 {

    def main(args: Array[String]): Unit = {
        val list = List(
            Pair("Spark", "Storm"),
            Pair(10, 20)
        )
        println(list)
    }
}

3、泛型的上下界
定义上界:
语法

[T <: 类型]
定义一个泛型的上界,这个泛型的参数必须是这个类本身或者是继承字这个类

定义下界:

[T >: 类型]
该类型必须是某个类的父类(也可以是自己本身)
/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/5 16:47
 * @Version 1.0
 */

class Personn
class Studentt extends Personn
class XiaoXueSheng extends Studentt

object _17泛型的上下界 {

    /*
       如果一个泛型中,既有下边界,又有上边界,先写下界,在写上界

     */
    def fangFaA[T <: Personn](a:Array[T]) = println(a)
    def fangFaB[T >: Studentt](a:Array[T]) = println(a)

    def main(args: Array[String]): Unit = {
        fangFaA(Array(new Personn))
        fangFaA(Array(new Studentt))
        fangFaA(Array(new XiaoXueSheng))
        // d type arguments [String] do not conform to method fangFaA's type parameter bounds [T <: Person]
        //fangFaA(Array("laoyan"))

        fangFaB(Array(new Personn)) // 正确
        fangFaB(Array(new Studentt)) // 正确
        //fangFaB(Array(new XiaoXueSheng)) //错误
    }
}

4、协变、逆变、非变

spark中经常在源代码中出现 协变、逆变、非变
非变:

class pair[T]{}
默认的泛型都是非变的。
类型B是类型A的子类, 但是  pair[A]  pair[B] 没有任何关系 == 非变

pari[Person]   pair[Student] 他们之间只有泛型有继承关系,但是类没有任何的关系。
var a = new Pari[Person];
var b = new Pair[Student];
a b 之间没有任何关系。

协变:

class Pair[+T]
类型B是类型A的子类,Pair[B] 可以认为是 Pair[A] 的子类型

逆变:

class Pair[-T]
类型B是类型A的子类,Pair[A] 反过来可以认为 是Pair[B] 的子类型
/**
 * @Author laoyan 
 * @Description TODO
 * @Date 2022/5/5 17:11
 * @Version 1.0
 */
class Super
class Sub extends Super
// 非变
class Temp1[T]
// 协变
class Temp2[+T]
// 逆变
class Temp3[-T]
object _17协变逆变非变 {


    def main(args: Array[String]): Unit = {
        val a:Temp1[Sub] = new Temp1[Sub]
        //编译报错,两者之间没有关系
        //var b:Temp1[Super] = a;
        val c:Temp2[Sub] = new Temp2[Sub]
        // 协变
        var d:Temp2[Super] = c;

        val e:Temp3[Super] = new Temp3[Super]
        //逆变
        val f:Temp3[Sub] = e;

    }
}