scala03
1. 样例类
样例类是一种特殊的类,它可以用来快速定义一个保存数据的类,类似于java中的pojo类。
- 如果要实现成员变量可以被修改需要添加var
- 默认是val,可以省略
object Demo01 { //变量的默认修饰符不写就是:val case class Person(name: String, var age: Int) def main(args: Array[String]): Unit = { //样例对象不用new,说明重写了apply val p = Person("刘德华", 54) //重写了toString方法 println(p) //验证样例类的默认修饰符是val //p.age=24 println("_" * 30) //演示样例类额equals方法,因为样例类的底层已经重写了 val p1 = Person("周星驰", 46) val p2 = Person("周星驰", 46) println(p1 == p2) //true比较的是属性值 println(p1.eq(p2)) //false比较的是地址值 println("_" * 30) //演示样例类对象的hash值 //相同对象hash肯定相同,不同对象的hash一般不同,但是可能相同。 println(p1.hashCode()) println(p2.hashCode()) println("_" * 30) //补充内容,内容不同,但是hash值相同的特例 println("重地".hashCode) println("通话".hashCode) println("_" * 30) println("儿女".hashCode) println("农丰".hashCode) println("_" * 30) //测试copy方法 val p3 = Person("陈一发", 27) p3.copy("刘一发", 26) //copy相当重新new了一个对象 } }
2. 样例对象
使用case object可以创建样例对象,样例对象是单例的,而且他没有主构造器
trait Sex /*定义一个性别特质*/ case object Male extends Sex // 定义一个样例对象并实现了Sex特质 case object Female extends Sex case class Person(name:String, sex:Sex) object CaseClassDemo { def main(args: Array[String]): Unit = { val zhangsan = Person("张三", Male) println(zhangsan) } }
3. 模式匹配
A:简单模式匹配
package com.itheima.moshipipei import scala.io.StdIn //模式匹配之简单匹配 object Demo01 { def main(args: Array[String]): Unit = { //录入字符串并接收 print("请输入字符串") var str = StdIn.readLine() //判断字符串是否是指定结果,并接收结果 val result: String = str match { case "hadoop" => "大数据分布式存储和计算框架" case "zookeeper" => "大数据分布式协调服务框架" case "spark" => "大数据分布式计算框架" case _ => "未匹配" } //打印结果 println(result) println("-" * 30) str match { case "hadoop" => println("大数据分布式存储和计算框架") case "zookeeper" => println("大数据分布式协调服务框架") case "spark" => println("大数据分布式计算框架") case _ => println("未匹配") } } }
B:匹配类型
package com.itheima.moshipipei //模式匹配之匹配类型 object Demo02 { def main(args: Array[String]): Unit = { val a:Any="hadoop" val b:Any=1.0 a match { case x:String =>println(s"${x}是String类型数据") case x:Double =>println(s"${x}是Double类型数据") case x:Int =>println(s"${x}是Int类型数据") case _=> println("未匹配") } println("-"*30) //如果case在校验的时候,没有被使用,则可以用_替代 b match { case _:String =>println("String") case _:Double =>println("Double") case _:Int =>println("Int") case _=> println("未匹配") } } }
C:守卫
package com.itheima.moshipipei import scala.io.StdIn object Demo03 { def main(args: Array[String]): Unit = { println("请录入一个整数:") var num=StdIn.readInt() num match { case a if a>0 && a<=3 =>println("a[0-3]") case a if a>=4 && a<=8 =>println("a[4-8]") case _=>println("未匹配") } } }
D:匹配样例类
package com.itheima.moshipipei //匹配样例类 //注意:通过match进行匹配的时候,要匹配的对象必须声明成any类型 object Demo06 { case class Customer(var name: String, var age: Int) case class Order(id: Int) def main(args: Array[String]): Unit = { val c: Any = Customer("刘德华", 73) val o: Any = Order(123) c match { case Customer(x, y) => println(s"Customer:${x},${y}") case Order(x) => println("Order") case _ => println("未匹配") } } }
E:匹配数组
package com.itheima.moshipipei object Demo04 { def main(args: Array[String]): Unit = { //定义三个数组 val arr1 = Array(1, 2, 3) val arr2 = Array(0) val arr3 = Array(0, 1, 2, 3, 4, 5) //通过模式匹配找到合适的数组 arr1 match { //长度为3,首元素为1,后两位无所谓 case Array(1, x, y) => println(s"匹配长度为3,首元素是1,后二${x},${y}") //匹配只有一个0元素的数组 case Array(0) => println("只有一个元素0") //匹配第一个元素是1,后面元素无所谓 case Array(1, _*) => println("第一个元素是1,后面元素无所谓") //其他校验 case _ => println("未匹配") } } }
F:匹配列表
package com.itheima.moshipipei object Demo05 { def main(args: Array[String]): Unit = { val list1 = List(0) val list2 = List(0, 1, 2, 3, 4, 5) val list3 = List(1, 2) //用List匹配 list1 match { case List(0) => println("匹配只有一个元素0") case List(0, _*) => println("0___*") case List(x, y) => println("x,y") case _ => println("未匹配") } //关键字优化 list2 match { case Nil => println("匹配只有一个元素0") case 0 :: tail => println("0___*") case x :: y :: Nil => println("x,y") case _ => println("未匹配") } } }
G:匹配元组
package com.itheima.moshipipei //匹配元组 object Demo07 { def main(args: Array[String]): Unit = { val a = (1, 2, 3) val b = (4, 5, 6) a match { case (1, x, y) => println("长度为3,1开头后两个元素无所谓") case (x, y, 5) => println("匹配长度为3,以5结尾,前两个元素无所谓") case _ => println("未匹配") } } }
H:变量声明中的模式匹配
val array = (1 to 10).toArray val Array(_, x, y, z, _*) = array println(x, y, z) val list = (1 to 10).toList val x :: y :: tail = list println(x, y)
4. Option类型
- Some(x):表示实际的值
- None:表示没有值
- getorElse():当值为None的时候可以指定一个默认值
package com.itheima.option object Demo01 { def div(a: Int, b: Int) = { if (b == 0) { None //除数为0没有结果 } else { Some(a / b) //除数不为0,返回具体结果 } } def main(args: Array[String]): Unit = { var result = div(10, 0) result match { case Some(x) => println(x) case None => println("除数不能为0") } println("-" * 30) println(result.getOrElse(0)) } }
5. 偏函数
package com.itheima.panhuanshu object Demo01 { def main(args: Array[String]): Unit = { val ps: PartialFunction[Int, String] = { case 1 => "一" case 2 => "二" case 3 => "三" case _ => "其他" } println(ps(1)) println(ps(2)) } }
package com.itheima.panhuanshu object Demo02 { def main(args: Array[String]): Unit = { val list1 = (1 to 10).toList //将1-3的数字转换为【1-3】 var list2 = list1.map { case x if x >= 1 && x <= 3 => "[1-3]" case x if x >= 4 && x <= 7 => "[4-8]" case _ => "[8-*]" } println(list2) } }
6. 正则表达式
package com.itheima.zhengzhe //案例 正则表达式入门 object Demo01 { def main(args: Array[String]): Unit = { val mail = "qq123456@163.com" /** * .表示任意字符 * +数量词,前面的字符至少出现一次,至多不上限制 * @ 必须出现的 * \是转义字符 */ var regex = """.+@.+\..+""".r if (regex.findAllMatchIn(mail).size != 0) { println("合法邮箱") print(regex.findAllMatchIn(mail).toList) } else { println("非法邮箱") } } }
package com.itheima.zhengzhe object Demo02 { def main(args: Array[String]): Unit = { val emlList = List("38123845@qq.com", "a1da88123f@gmail.com", "zhansan@163.com", "123afadff.com") val regex=""".+@.+\..+""".r val list=emlList.filter(x=>regex.findAllMatchIn(x).size==0) println(list) } }
package com.itheima.zhengzhe object Demo03 { def main(args: Array[String]): Unit = { val regex = """.+@(.+)\..+""".r val emlList = List("38123845@qq.com", "a1da88123f@gmail.com", "zhansan@163.com", "123afadff.com") //根据模式匹配获取所有合法的邮箱及其对应的运营商 val result = emlList.map { //表示emlList中的元素 //company表示正则表达式括号括起来的内容 case email@regex(company) => s"邮箱${email}对应的公司是:${company}" case email => s"${email}未匹配" } println(result) val re = emlList.map { //表示emlList中的元素 //company表示正则表达式括号括起来的内容 case email@regex(company) => email -> s"${company}" case email => email -> "未匹配" } println(re) } }
7. 异常处理
package com.itheima.yichang //演示try catch object Demo01 { def main(args: Array[String]): Unit = { try { val i = 10 / 0 } catch { case ex:Exception =>ex.printStackTrace() } println("你好啊") } }
8. 提取器
- 样例类中自动实现了apply,unapply方法,支持模式匹配。
- 不是所有类的类可以进行这样的模式匹配
- 要支持模式匹配必须要实现一个提取器
package com.itheima.tiquqi object Demo02 { case class Customer(name: String, age: Int) case class Person(id: Int) def main(args: Array[String]): Unit = { val c: Any = Customer("刘德华", 46) val p: Any = Person(1) p match { case Person(id) => println("这是人") case Customer(name, age) => println("这是顾客") } } }
package com.itheima.tiquqi //演示:scala中的提取器 object Demo01 { class Student(var name:String,var age:Int) object Student{ def apply(name:String,age:Int)=new Student(name,age) def unapply(s: Student): Option[(String, Int)] = { if(s!=null) Some(s.name,s.age) else None } } def main(args: Array[String]): Unit = { val s=new Student("刘德华",12)//普通方式创建对象 val s1=Student("周",3) //通过提取器获取对象中的方法 val result=Student.unapply(s1) println(result) } }
9. 泛型
A:泛型方法
package com.itheima.fanxing //泛指某种数据的类型 //细节:泛型方法在调用方法的时候,明确具体数据类型 object Demo01 { //def getMiddleEle(arr:Array[Any])=arr(arr.length/2) def getMiddleEle[T](arr: Array[T]) = arr(arr.length / 2) def main(args: Array[String]): Unit = { println(getMiddleEle(Array(1, 2, 3, 4, 5))) } }
B:定义泛型类
- 泛型类是在创建对象的时候明确具体的数据类型
package com.itheima.fanxing //泛型类:在创建对象的时候,明确数据类型 object Demo02 { case class Pair[T](name:T,age:T) def main(args: Array[String]): Unit = { val pairList = List( Pair("Hadoop", "Storm"), Pair("Hadoop", 2008), Pair(1.0, 2.0), Pair("Hadoop", Some(1.9)) ) println(pairList) } }
C:上下界
- 使用<:类型名表示给类型添加一个上界,表示泛型参数必须是从该类(或本身)继承
- 使用>:类型名表示给类型添加一个下界,表示泛型参数必须是某个类的父类
- 如果类既有上界又有下界,下界写在前面,上界写在后面
上界
package com.itheima.fanxing //演示泛型上界 object Demo03 { class Person class Student extends Person def demo[T<:Person](arr:Array[T])=println(arr) def main(args: Array[String]): Unit = { //demo(Array(1,2,3))报错 demo(Array(new Person)) //demo(Array(new Student))报错 } }
下界
package com.itheima.fanxing //演示下界 object Demo04 { class Person class Policeman extends Person class Superman extends Policeman def demo[T>:Policeman](array:Array[T])=println(array) def main(args: Array[String]): Unit = { demo(Array(new Person)) demo(Array(new Policeman)) //demo(Array(new Superman))报错 } }
D:
协变
- class Pair[+T]
- 类型B是类型A的子类,Pair[B]可以认为是Pair[A]的子类型
- 参数化类型的方向和类型的方向是一致的
逆变
- class Pair[-T]
- 参数B是A的子类型,Pair[A]反过来可以认为是Pair[B]的子类型
- 参数化类型的方向和类型的方向是相反的
package com.itheima.fanxing //演示:非变 object Demo05 { class Super class Sub extends Super class Temp1[Sub] class Temp2[+Sub] class Temp3[-Sub] def main(args: Array[String]): Unit = { val a: Temp1[Sub] = new Demo05() = new Temp1[Sub] //编译报错 //非变 //val b:Temp1[Super]=a //协变 val c: Temp2[Sub] = new Temp2[Sub] val d: Temp2[Super] = c //逆变 val e: Temp3[Super] = new Temp3[Super] val f: Temp3[Sub] = e } }
10. Actor
A:Java并发编程的问题
在java并发编程中,每个对象都有一个逻辑监视器(monitor),可以用来控制对象的多线程访问。我们添加sychronized关键字来标记,需要进行同步加锁访问,这样通过加锁的机制来确保同一时间只有一个线程访问共享数据,但是这种方式存在资源竞争,以及死锁问题,程序越大越麻烦。
B:Actor并发编程模型
Actor编程是一种基于事件模型的并发机制,一种不共享数据,依赖消息传递的一种并发编程模式,有效避免资源争夺,死锁等情况。
C:创建Actor
- 定义class或者object继承Actor特质
- 重写act方法
- 调用Actor的start方法执行Actor
- 类似于java线程,每个Actor是并发执行的
import scala.actors.Actor //actor 并发编程入门 通过class 创建actor //用class还是object来创建object的依据是,该Actor对象是否创建多个。单个用object,多个用class object Demo01 { //创建actor1打印1-10的数字 class Actor1 extends Actor{ override def act(): Unit = for(i<- 1 to 10) println("actor1----"+i) } class Actor2 extends Actor{ override def act(): Unit = for(i<- 11 to 20) println("actor2----"+i) } def main(args: Array[String]): Unit = { new Actor1().start() new Actor2().start() } }
- 调用start方法启动Actor
- 自动执行act方法
- 向Actor发送消息
- act方法执行完成后,程序会自动调用exit()方法
D:发送消息/接受消息
发送消息
如果要给actor1发送一个异步字符串消息:actor ! “您好”
接收消息
Actor中receive方法来接收消息,需要给receive传入一个偏函数
receive方法直接收一次消息
object Demo04 { //创建Sender object ActorSender extends Actor{ override def act(): Unit = { while (true){ ActorReciever ! "你好啊。我是Sender" TimeUnit.MILLISECONDS.sleep(3000) } } } object ActorReciever extends Actor{ override def act(): Unit = { while(true){ receive{//传入一个偏函数 case x:String=>println(x) } } } } def main(args: Array[String]): Unit = { ActorSender.start() ActorReciever.start() } }
E:使用loop和react接收优化消息
使用while(true)会频繁的创建线程,销毁和切换,会影响运行效率
object Demo05 { //创建Sender object ActorSender extends Actor{ override def act(): Unit = { while (true){ ActorReciever !"你好啊。我是Sender" TimeUnit.MILLISECONDS.sleep(3000) } } } object ActorReciever extends Actor{ override def act(): Unit = { loop{ react{ case x:String=>println(x) } } } } //创建Receiver def main(args: Array[String]): Unit = { ActorSender.start() ActorReciever.start() } }
F:发送和接收自定义消息
package com.itheima.scala.Actor import scala.actors.Actor //案例: Actor发送和接收自定义消息, 采用 同步有返回的形式 object Demo06 { //1. 定义两个样例类Message(表示发送数据), ReplyMessage(表示返回数据.) case class Message(id: Int, message: String) //自定义的发送消息 样例类 case class ReplyMessage(message: String, name: String) //自定义的接收消息 样例类 //2. 创建一个MsgActor,用来接收MainActor发送过来的消息, 并向它回复一条消息. object MsgActor extends Actor { override def act(): Unit = { //2.1 接收 主Actor(MainActor) 发送过来的消息. loop { react { //结合偏函数使用 case Message(id, message) => println(s"我是MsgActor, 我收到的消息是: ${id}, ${message}") //2.2 给MainActor回复一条消息. //sender: 获取消息发送方的Actor对象 sender ! ReplyMessage("我很不好, 熏死了!...", "车磊") } } } } def main(args: Array[String]): Unit = { //3. 开启MsgActor MsgActor.start() //4. 通过MainActor, 给MsgActor发送一个 Message对象. //采用 !? 同步有返回. val reply:Any = MsgActor !? Message(1, "你好啊, 我是MainActor, 我在给你发消息!") //resutl表示最终接收到的 返回消息. val result = reply.asInstanceOf[ReplyMessage] //5. 输出结果. println(result) } } package com.itheima.scala.Actor import scala.actors.Actor //案例: Actor发送和接收自定义消息, 采用 异步 无返回的形式 object Demo07 { //1. 定义两个样例类Message(表示发送数据), ReplyMessage(表示返回数据.) case class Message(id: Int, message: String) //自定义的发送消息 样例类 //2. 创建一个MsgActor,用来接收MainActor发送过来的消息, 并向它回复一条消息. object MsgActor extends Actor { override def act(): Unit = { //2.1 接收 主Actor(MainActor) 发送过来的消息. loop { react { //结合偏函数使用 case Message(id, message) => println(s"我是MsgActor, 我收到的消息是: ${id}, ${message}") } } } } def main(args: Array[String]): Unit = { //3. 开启MsgActor MsgActor.start() //4. 通过MainActor, 给MsgActor发送一个 Message对象. //采用 ! 异步无返回 MsgActor ! Message(1, "我是采用 异步无返回 的形式发送消息!") } } package com.itheima.scala.Actor import scala.actors.{Actor, Future} //案例: Actor发送和接收自定义消息, 采用 异步有返回的形式 object Demo08 { //1. 定义两个样例类Message(表示发送数据), ReplyMessage(表示返回数据.) case class Message(id: Int, message: String) //自定义的发送消息 样例类 case class ReplyMessage(message: String, name: String) //自定义的接收消息 样例类 //2. 创建一个MsgActor,用来接收MainActor发送过来的消息, 并向它回复一条消息. object MsgActor extends Actor { override def act(): Unit = { //2.1 接收 主Actor(MainActor) 发送过来的消息. loop { react { //结合偏函数使用 case Message(id, message) => println(s"我是MsgActor, 我收到的消息是: ${id}, ${message}") //2.2 给MainActor回复一条消息. //sender: 获取消息发送方的Actor对象 sender ! ReplyMessage("我很不好, 熏死了!...", "糖糖") } } } } def main(args: Array[String]): Unit = { //3. 开启MsgActor MsgActor.start() //4. 通过MainActor, 给MsgActor发送一个 Message对象. //采用 !! 异步有返回. val future: Future[Any] = MsgActor !! Message(1, "你好啊, 我是MainActor, 我在给你发消息!") //5. 因为future中不一定会立马有数据, 所以我们要校验. //Future的isSet()可检查是否已经收到返回消息,apply()方法可获取返回数据 //!future.isSet表示: 没有接收到具体的返回消息, 就一直死循环. while(!future.isSet){} //通过Future的apply()方法来获取返回的数据. val result = future.apply().asInstanceOf[ReplyMessage] //5. 输出结果. println(result) } }
11. WorldCount案例
需求:在一个文件夹下有多个文件,每个文件中都有若干个单词,单词之间是通过空格隔开。请统计该文件夹下所有的文件中的单词的分别统计个数。