Scala
大数据中两个重要概念
一:分布式,垂直分割保证各司其职(洗菜,切菜,炒菜…)
二:集群,水平扩展解决高并发(多个人洗菜,多个人切菜,多个人炒菜…)
idea出品的Kotlin改变了安卓开发
1、引言
Scala编程语言(大数据领域 Spark Kafka)
特点:
1.面向对象编程语言(优先考虑传递数据)(落脚点:把现实问题抽象为实体类,Service建模)
2.面向函数式编程,专门为数据而生语言(优先考虑传递函数方法)(有多种编程方式)
3.强大的数组集合计算能力(落脚点:数据的分析和计算)
JVM类型的编程语言(不同操作系统的解合,clojure,jpython,jjavaScript都是运行在jvm之上)
为什么需要Scala运行在虚拟机上?
1.天生屏蔽操作系统OS之间的差异
2.无缝对接java(降低程序员的学习成本)
Scala怎么运行在JVM虚拟机上?
1.把Scala源码编译成.class文件(编译器)
(面试坑)Scala慢不慢?
不慢,至少和java代码相比没差,都是.class文件
为什么要学习scala
现在主流大数据计算框架对Scala编程比较友好,例如:Kafka.0.11.0_2.11.tar.gz(后面是scala语言版本)、Spark使用Scala(导致使用Scala语言可以 高效 的实现数据的分析处理)
2、Scala的安装
Scala版本不要过新要配合后面的Spark
运行命令 scala 进入 scala交互页面
- scala(windows的安装)
下一步默认安装(默认安装路径不好使就装到某个盘符的根目录下) - scala(linux的安装)
rpm -ivh sacala–xxx–.rpm
rpm 安装:默认安装在usr/下(自动做了make install放到usr/bin下)
[root@hive1 ~]# rpm -ivh scala-2.11.12.rpm
#查找存放的位置
[root@hive1 ~]# which scala
/usr/bin/scala
3、常见变量
Scala是纯粹的面向对象编程语言(Java做对比,基本类型和对象类型),在Scala没有基本类型,所有值类型都是AnyVal的子类,所有对象类型都是AnyRef的子类。一般所有的AnyVal类型都有字面值,所有AnyRef类型默认值都是Null。所有类型都属于对象类型。
变量声明
Java变量声明
变量类型 变量名 = 变量值;
int a = 1;
Scala变量声明(有强大的类型推断能力)
(var|val)变量名[:变量类型] = 变量值[:变量类型]
变量 | 常量
Scala>var i:Int = 1:Int
i:Int = 1
scala>var i = 1
i:Int = 1
scala>var i:Byte = 1
i:Byte = 1
scala>var i = 1:Byte
i:Byte = 1
scala>i = 127
i:Byte = 127
scala> i=128
<console>:12: error: type mismatch;
found : Int(128)
required: Byte
i=128
^
正常情况下用户可以省略类型,因为Scala可以根据字面量自动推断出变量的类型,所以在定义一个用户只需要指明该变量时是变量还是常量即可。
scala> var j = "hello scala"
j:String = hello scala
scala> var sex = true
sex:Boolean = true
scala>sex = "false"
<console>:12: error: type mismatch;
found : String("false")
required: Boolean
sex="false"
注意var修饰变量值可以改变,val修饰常量 值不可改变等价于Java中final修饰的变量
Scala> var i = 10
i:Int = 10
scala> i = 11
i:Int = 11
scala> val j = 10
j:Int = 10
scala> j = 11
<console>:12: error: reassignment to val
j=11
^
数值转换
-
类型兼容且有小到大,由整数到小数,可以自动类型提升
scala> var i:Byte = 1 i:Byte = 1 scala> var j:Byte = 1 j:Byte = 1 scala> var k = i + j k:Int = 2 scala>var m = k + 0.5 m:Double = 2.5 m: Double = 2.5 scala> var f:Float=m <console>:12: error: type mismatch; found : Double required: Float var f:Float=m ^
-
类型兼容,且由大到小asInstanceOf,例如:Long转换Int,Double转换为Float
scala> var d:Double = 100.0 d: Double = 100.0 scala> var f:Float = d <console>:12: error: type mismatch; found : Double required: Float var f:Float = d ^ scala> var f:Float = d.asInstanceOf[Float] f:Float = 100.0
在Scala中[]表示的是泛型
-
将字符串类型转为数值类型 toXXX
scala> var sex = true sex:Boolean = true scala> sex = "flase" <console>:12: error: type mismatch; found : String("false") required: Boolean sex="false" ^ scala> sex = "flase".toBoolean scala> var i = 11 i:Int = 1 scala> i = "123".toInt i:Int = 123
注意在类型兼容的前提下 大->小 使用asInstanceOf[类型]
如果是字符串类型转为值类型可以用toInt/toDouble/toFloat/toByte/toShort
Unit类型
在Scala中通常会使用Unit关键字表示一种特殊的返回值类型,等价于Java中的Void关键字。但是在Scala中Unit是有字面量的
scala> var u:Unit=() #值是:空元组
u: Unit = ()
Array类型(数组类型)
scala> var a:Array[Int] = new Array[Int](5)#直接指定数组大小,并且赋值默认值0
a: Array[Int] = Array(0.0.0.0.0)
scala> var a:Array[Int] = Array(1,2,3,4,5)#一种快速创建复杂对象方式(伴生对象)【调用了工厂方法,底层还是new对象】
a:Array[Int] = Array(1,2,3,4,5)
scala> a.length #获取数组的长度
scala> a.size #获取数组的长度
scala> a(0) = -1 #修改数据的0下标的内容(-1前面要有空格)
scala> a
res9:Array[Int] = Array(-1,2,3,4,5)
scala> a(5) = -1 #下标越界
java.lang.ArrayIndexOutOfBoundsException: 5
... 32 elided
元组(Tuple)
由若干个元素构成的 复合变量
scala> var user = (1,"韩梅梅",18,true)
user:(Int,String,Int,Boolean) = (1,韩梅梅,18,true)
scala> var a:(String,Int) = ("Hello Scala",1)
a:(String,Int) = (Hello Scala,1)
scala> user._1 #访问元组中第几个元素,下标从1开始,最多到22
res12: Int = 1
scala> user.1 = 2 #元组中的元素都是val,不允许修改
<console>:12: error: reassignment to val
元组中元素都是只读的,一般有值类型和String类型构成。在Scala一个Tuple中最多能够有22个成员。
元组中的元素都是val类型不允许修改(只读),但是元组变量值可以修改
scala> var user = ("zhangsan",18,true,15000)
user: (String,Int,Boolean,Int) = (zhangsan,18,true,15000)
scala> user = ("wangwu",20,false,20000)
user: (String,Int,Boolean,Int) = (wangwu,20,false,20000)
4、分支循环
if条件分支
语法
var a:Int = new Random().nextInt(100);
if(a<10){
println("小可爱");
}else if (a>=10 && a<30){
println("臭弟弟");
}else{
println("老男人")
}
和java不同,if可以将修饰的代码块的返回值给一个变量
var a:Int = new Random().nextInt(100);
var b = if(a<=10){
"小可爱"
}else if(a<=20){
"臭弟弟"
}else{
"老东西"
}
println(s"结果:${b},哈哈哈")
while、do-while
在scala中while和do-while没有continue和break关键字
var b = 5
while (b>0){
println(b)
b-=1
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
do{
println(b)
b -= 1
}while(b>0)
Breaks
Scala 语言中默认没有break语句,但是在Scala2.8版本后可以使用另外一种方式实现break语句。当在循环中使用break语句,在执行到该语句时,就会中断循环并执行循环体之后的代码来块。
var b = 5
var breaks = new Breaks
breaks.breakable({
do{
print(b)
b-=1
if(b<=3){
breaks.break()
}
}while(b>0)
})
for循环(重点)
-
迭代遍历数组
var array = Array(1,2,3,4) for(item<- array){println(item)}
-
下标遍历数据
var array = Array(1,2,3,4) for(i<- 0 to array.length-1){println(array(i))} //to 包含 for(i <- 0 until(array.size)){println(array(i))} //until 不包含
-
for可使用多个循环因子
for(int i <- 1 to 9;j <- 1 to i){ print(s"${j}*${i}="+i*j+" ") if(i==j){println()} }
-
for 和if的使用(控制步数)
for(i <- 0 to 10;if(i%2==0)){ println(i) }
-
for和yield关键字实现元素的提取,创建子集【重点】
var array = Array(1,2,3,4) var res = for(i<-array) yield 2*i for(a<-res){ println(a) } 2 4 6 8
match-case(模式匹配)
在scala中剔除java中switch语句,提供了match-case替代方案,该方案不仅仅可以按照值匹配,还可以按照类型、以及值的结构(数组匹配,元组匹配,case-class匹配等)
-
值匹配
var c = Array(1,2) var d = c(new Random().nextInt(2)) var e = d match{ case 1 =>"one" case 2 =>"two" case _ =>"three" } println(e)
-
类型匹配
var c = Array(1,"suns") var d = c(new Random().nextInt(2)) var e = d match { case h:Int => s"id:${h}" case h:String => s"name:${h}" case default => null } println(e)
注意:_ 表示默认匹配等价 default关键字
5、函数|方法
在scala中函数方法都是对象,一样
def专门修饰函数|方法 变量
√标准函数
scala> def sum(x:Int,y:Int):Int={
| x+y
| }
sum: (x: Int, y: Int)Int
一般情况下,如果函数体最后一行作为函数的返回值,一般可以省略函数的返回值类型,例如
scala> def sum(x:Int,y:Int)={
| x+y
| }
sum: (x: Int, y: Int)Int
scala> def sum(x:Int,y:Int):Int={
| return x+y
| }
sum: (x: Int, y: Int)Int
如果有return语句,函数不能省略返回值类型
scala> def sum(x:Int,y:Int)={
| return x+y
| }
<console>:12: error: method sum has return statement; needs result type
return x+y
^
可变常参数
scala> def sum(values:Int*):Int={
| var total=0
| for( i<- values) total += i
| total
| }
sum: (values: Int*)Int
scala> sum(1,2,3,4,5)
res9: Int = 15
scala> def sayHi(msg:String,names:String*):Unit={
| for(name <- names){
| println(msg+"\t"+name)
| }
| }
sayHi: (msg: String, names: String*)Unit
scala> sayHi("Hello","zs")
Hello zs
可变长参数必须放置在最后一位 一个方法的可变长参数有且仅有一个
scala> def sayHi(names:String*,msg:String):Unit={
| for(name <- names){
| println(msg+"\t"+name)
| }
| }
<console>:11: error: *-parameter must come last
def sayHi(names:String*,msg:String):Unit={
^
#可变长参数用命名参数不好使
scala> sayHi(names="li",msg="Hello",names="hua")
<console>:13: error: parameter 'names' is already specified at parameter position 1
Note that 'msg' is not a parameter name of the invoked method.
sayHi(names="li",msg="Hello",names="hua")
命名参数
scala> def sayHi(name:String,msg:String):Unit={
| println(s"${msg}\t${name}")
| }
scala> sayHi(msg="hello",name="zhangsan")
hello zhangsan
参数默认值
scala> def sayHi(name:String="未知",msg:String="哈喽"):Unit={
| println(s"${msg}\t${name}")
| }
sayHi: (name: String, msg: String)Unit
scala> sayHi("zhangsan")
哈喽 zhangsan
scala> sayHi(msg="hello")
hello 未知
scala> sayHi("zhangsan","nihao")
nihao zhangsan
内嵌函数(没有要求)
求阶乘案例
def jc2(num:Int):Int={
def compute(x:Int):Int={
if(x>1){
x*compute(x-1)
}else{
1
}
}
compute(num)
}
def jc(num:Int):Int={
if(num>1){
num*jc(num-1)
}else{
1
}
}
√柯里化
在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。可以传递部分参数
def sum1(x:Int,y:Int): Int ={
x+y
}
def sum2(x:Int)(y:Int): Int ={
x+y
}
√匿名函数(精华)
将定义函数,以变量形式表达。
scala> def sum(x:Int,y:Int): Int ={
| x+y
| }
sum: (x: Int, y: Int)Int
scala> sum _
res38: (Int, Int) => Int = <function2>
变量名: 变量类型 变量值
//第一种形式
val sum:(Int,Int)=>Int = (x,y)=> x+y
--
val i:Int=1
//第二种形式
val sum = (x:Int,y:Int)=> x+y
--
val i=1
//第三种形式
val sum = ((x,y)=> x+y):((Int,Int)=>Int)
--
val i=1:Byte
def sum:(Int,Int)=>Int = (x,y)=> x+y
def sum=(x:Int,y:Int)=> x+y
通过以上写法得到:
1)任意一个函数,可以转变成函数式变量。
2)在Scala一切皆对象,因此变量也是对象
函数 = = 特殊变量 (函数变量|匿名函数)== 对象
scala> def j=1 //定义的是一个常量函数。而不是变量
j: Int
scala> j=10
<console>:12: error: value j_= is not a member of object $iw
j=10
^
6、面向对象(只是为了看源码)
Object(入口是静态类的静态方法,Scala特有)
因为Scala中没有静态类和静态方法,但是Scala可以使用object声明静态类(单例类,java中bean交给工程或者静态成员变量,虚拟机只加载一次),该静态类的所有方法都是可以直接调用的,不需要new关键字(new关键字虚拟机会创建新的对象)
object JdbcUtil {
def sayHello():Unit={
println("你好!")
}
}
var jdbcUtil1=JdbcUtil
var jdbcUtil2=JdbcUtil
println(jdbcUtil1)
println(jdbcUtil2)
println(jdbcUtil1==jdbcUtil2) //true
jdbcUtil1.sayHello()
JdbcUtil.sayHello()
Class类
- 没有默认构造
class Animal {
def sleep():Unit={
println("sleep...")
}
}
val a1=new Animal
val a2=new Animal
val a3=new Animal()
println(a1==a2) //false
通过Class创建的类在创建对象的时候必须使用new关键字
-
指定默认构造
Scala默认构造不写,不用写括号
Scala的默认构造必须写在类上,写完必须给默认构造提供参数
class User(name:String) {//默认构造 def sayHi():Unit={ println(s"${name}") } }
val user = new User("张三") user.sayHi()
-
扩展默认构造(java是覆盖默认构造,添加有参构造无参构造就没了;Scala中的默认构造无法覆盖,一直存在)
class User(name:String) {//默认构造
var age:Int= _
def this(name:String,age:Int){ //扩展构造
this(name) //代码第一行必须覆盖默认构造器
this.age=age
}
def sayHi():Unit={
println(s"${name},${age}")
}
}
必须在扩展构造的第一行显示的默认构造的方法。扩展默认构造使用this关键字
构造方法 没有返回值
伴生对象
如果类和object在一个scala源文件中,则成为object User是Class User的伴生对象,伴生对象相较于其他对象有优先操作User的权限,比如可以使用伴生对象User快速创建Class User的对象:
没有new关键字时:
1、有()伴生,调用伴生对象的工厂方法(叫工厂方法是因为是静态的)
2、没有()永远是单例【我就纳闷这个单例是咋创建出来的,类加载的时候】
-
apply 工厂方法 创建类的对象
class User(name:String){ var age:Int=_ def this(name:String,age:Int){ this(name) this.age=age } } object User{ def apply(name:String): User = new User(name) def apply(name: String,age:Int): User = new User(name,age) }
var u1=new User("zs") //创建 var u2=new User("zs",18)// 创建 var u3=User("zs") //调用apply创建 var u4=User("zs",18)//调用apply创建 var u5=User //单例对象 var u6=User //单例对象 var u5=User var u6=User
-
unapply解码对象的属性
class User(var name:String){ var age:Int=_ def this(name:String,age:Int){ this(name) this.age=age } } object User{ def unapply(user: User): Option[(String, Int)] = { if(user==null) { Some(("",0)) }else{ Some((user.name,user.age)) } } def apply(name:String): User = new User(name) def apply(name: String,age:Int): User = new User(name,age) }
val u=new User("张三1",18) val User(name:String,age:Int)=u println(s"${name},${age}")
scala中没有提供get set方法,要想取得对象的属性有两种方式
1、(low)普通变量变成成员变量 添加 var
2、(优雅)可以反序列化参数,只需提供unapply方法(反解 为了看懂源码)【提供unapply方法也要var,感觉可没必要哇】
抽象类(和java中一模一样)
abstract class Animal(name:String) {
def eat():Unit={
println(s"${name}会吃...")
}
def sleep():Unit
}
Java语法类型,可以有构造,但是无法创建对象。
特质Trait=java中的抽象类+接口(特有的性质)
trait Flyable {
def fly():Unit//接口
}
trait Speekable {
def speek():Unit={//方法
println("鸟儿会飞")
}
}
java8中接口中有默认方法实现
类似Java的接口,没有构造,可以有默认方法实现。
继承(和java一样类与类之间是单继承,接口之间多实现)
class Dog(name:String) extends Animal(name:String) with Speekable {
override def sleep(): Unit = {
println(s"${name} 睡觉")
}
override def eat(): Unit = {//覆盖父类中的方法
println(s"${name} 啃骨头")
}
override def speek(): Unit = {
println(s"${name}汪汪叫")
}
}
继承抽象类,抽象类的接口必须实现,抽象类的方法可覆盖,可不覆盖
动态混入(scala特有)
class SmallBird(name:String) extends Animal (name:String){
override def sleep(): Unit = {
println(s"${name} 睡觉...")
}
}
var smallBird=new SmallBird("小麻雀") with Flyable{
override def fly(): Unit = {
println("小麻雀会飞了")
}
}
smallBird.sleep()
smallBird.fly()
强制混入(在接口中限定)
打比方 只要会飞的一定是动物(限制类型)
仅仅实现用extends
又继承又实现 extends + with
trait Flyable {
this:Animal=> //必须在第一行 强制认定所有Flayable的子类,一定是Animal
def fly():Unit={
println(s"${name} 会飞")
}
}
class Parrot(name:String) extends Animal(name:String) with Flyable {
override def sleep(): Unit = {
println(s"${name}睡觉")
}
}
val parrot = new Parrot("小鹦鹉")
parrot.fly()
self-type(自类型)
类里面写匿名内部类,匿名内部类为了避免this冲突,给自己起别名
class Pig(name:String) extends Animal(name:String){
self=> //等价 self:Pig=>
override def sleep(): Unit = {
println(self == this)
println(this.name+" 会睡!")
println(self.name+" 会睡!")
}
}
实际上是混入一种变体形式
case-class(加强版的tuple)
在scala中建模用tuple和样例类
样例类一般用作数据建模,因此样例类的属性都是只读
(元组类似),样例类在使用的使用的时候无需使用new关键字,==比较的内容。
case class User(id:Int,name:String,sex:Boolean)
val u1=new User(1,"zs",true)
val u2=new User(1,"zs",true)
val u3=User(1,"zs",true)
println(u1)
println(u2)
注意样例类与类之间不允许出现继承关系。
样例类可以继承普通类
样例类没有子类
样例类之间可以实现快速拷贝
val u1=new User(1,"zs",true)
val u2=u1.copy(id=2)
println(u1)//User(1,zs,true)
println(u2)//User(2,zs,true)
7、函数式变量|对象
函数接口
在Java1.8中提出了一个Functional Interface,如果一个接口里面只能有一个抽象方法。这种类型的接口也称为SAM接口,即Single Abstract Method interfaces。将该接口成为函数式接口。
@FunctionalInterface
public interface IDemoService {
public Integer sum(Integer x,Integer y);
}
@FunctionalInterface
编译检查,确保该接口中只用一个抽象方法。
在1.8提出lambda表达式,用于表示这种函数式接口。
IDemoService demoService1=(Integer x, Integer y) -> x+y;
IDemoService demoService2=(x,y)->x+y;
和Java类似,Scala中也提供了类似于Java当中的Functional Interface
,默认情况下,提供了Function022个函数式接口,所有的Scala的函数或者方法都是Function022的子类。可以使用 _
将任意一个标准函数,转换为函数式变量。
scala> def sum(x:Int,y:Int): Int ={
| x+y
| }
sum: (x: Int, y: Int)Int
scala> val func=sum _
func: (Int, Int) => Int = <function2>
通常将func
称为sum的函数的部分应用函数
,通常使用部分应用函数表示一个函数对象。不难看出func
字面量是一个<function2>
尝试以下测试
scala> func.isInstanceOf[Function2[Int,Int,Int]]
res2: Boolean = true
可以看出 得到的func
确实是Function2的子类。既然func是Function2的子类,一定可以调用Function2的apply方法实现数据计算逻辑
scala> func.apply(1,2)
res3: Int = 3
scala> func(1,2)
res4: Int = 3
在Scala中Function0~Function22存在着变体写法(lambda写法):
scala> func.isInstanceOf[(Int,Int)=>Int]
res5: Boolean = true
也就意味着:(Int,Int)=>Int
等价 Function2[Int,Int,Int]
class SumFunc1 extends Function2[Int,Int,Int] {
override def apply(v1: Int, v2: Int): Int = {
v1+v2
}
}
---
class SumFunc2 extends ((Int,Int) =>Int) {
override def apply(v1: Int, v2: Int): Int = {
v1+v2
}
}
由于标准函数一般是无法作为参数单独传递,因此在Java中如果想要传递一个函数,必须将该函数封装称为一个函数式对象。
public static Integer process(IDemoService demoService,Integer x,Integer y){
return demoService.sum(x,y);
}
Integer process = process((v1, v2) -> v1 + v2, 1, 2);
System.out.println(process);
可以看出使用Java做Lambda非常麻烦,因为所有lambda背后都必须有一个FunctionInterface
object TestScalaLanguage {
def main(args: Array[String]): Unit = {
println(process1((v1,v2)=>v1+v2,1,3))
println(process2((v1,v2)=>v1*v2,1,3))
}
def process1(fun:Function2[Int,Int,Int],x:Int,y:Int):Int={
fun(x,y)
}
def process2(fun:(Int,Int)=>Int,x:Int,y:Int):Int={
fun(x,y)
}
}
scala> Array(1,2,3).filter(i=>i%2!=0).map(i=>i*i)
res12: Array[Int] = Array(1, 9)
偏函数
偏函数主要适用于处理指定类型的参数数据,通常用于集合处理中。
val fun=new PartialFunction[Any,Int] {//只处理Int类型元素
//需要处理的类型
override def isDefinedAt(x: Any): Boolean = {
println("isDefinedAt:"+x)
x.isInstanceOf[Int]
}
//应用 函数处理数据
override def apply(v1: Any): Int = {
println("apply:"+v1)
v1.asInstanceOf[Int]+1
}
}
val array = Array(1,2,"a",true,3,4,5)
array.collect(fun)
.foreach(item=>print(item+"\t"))
正常用法:
val a:Any=11
if(fun.isDefinedAt(a)){
fun(a)
}
通常偏函数,还有另外一种写法,表示该偏函数,只处理Int类型的数据,如果数据不是Int,系统就抛出异常
val fun2:PartialFunction[Any,Int] = {case x:Int => x+1}
val array = Array(1,2,"a",true,3,4,5)
array.collect(fun2) //array.collect({case x:Int => x+1})
---
val a= for(i<- array;if(fun2.isDefinedAt(i))) yield fun2(i)
8、可见性(了解,因为Scala中不强调封装)
Scala 中的默认可见性为 public,所谓默认即你没有在类或者成员前显示加 private 或 protected 可见性关键字,默认都是public,虽然默认是public的但是scala中并没有public关键字,加上public编译报错。
class Student {
var id:Int = _
var name:String = _
def this(id:Int,name:String){
this()
this.id = id
this.name = name
}
}
var stu = new Student(1,"zs")
println(s"${stu.id}")
private限定
-
private修饰的属性|方法
只能被本类以及伴生对象可见,子类不可见
class Student { private var id:Int = _ private var name:String = _ def this(id:Int,name:String){ this() this.id = id this.name = name } } object Student{ def unapply(student: Student): Option[(Int,String)] = { Some(student.id,student.name) } }
var stu = new Student(1,"zs") var Student(id,name) = stu println(s"${id},${name}")
Scala中private可以私有属性;也可以私有方法,私有方法只有伴生对象能拿到这方法,外界通过伴生对象工厂方法创建对象
Scala并不推荐使用私有属性,同时提供公开方法
一般通过伴生对象的unapply解码对象属性
-
private修饰类
该类的所有成员属性对外皆不可见,包括伴生对象。一旦给一个class加上private修饰就直接将该类的默认构造全部隐藏
- private修饰默认构造
class Student private() {//这个()要不要都无所谓 修饰的是默认构造 var id:Int = _ var name:String = _ def this(id:Int,name:String){//扩展构造 this() this.id = id this.name = name } def sayHello():Unit={ println("hello") } } object Student{ def unapply(student: Student): Option[(Int,String)] = { Some(student.id,student.name) } // def apply: Student = new Student() def apply(): Student = new Student() def apply(id:Int,name:String): Student = new Student(id,name) }
//可以通过伴生对象调用各种方法 var stu =Student(1,"zs") var a = Student//单例 仅仅只是伴生对象 var b = Student()//工厂创建 var Student(id,name) = stu println(s"${id},${name}") println("a "+a) println("b "+b) 1,zs a com.baizhi.demo6.Student$@38082d64 b com.baizhi.demo6.Student@dfd3711
以下写法,该Student对外不可见。同Java修饰内部类
private class Student{//修饰的是类,一般用在内部类中 //..... } class Master {//师傅 private class Slave{ //徒弟 } def teach():Unit={ val slave = new Slave() println(slave) } }
protected 限定
-
修饰属性|方法,
可以被子类以及子类的伴生对象可见。
class Student {
protected var id:Int=_
protected var name:String =_
protected def say():Unit={
println(s"${name},${id}")
}
}
class SmallStudent extends Student {
def this(id:Int,name:String){
this()
this.id=id
this.name=name
}
}
- 修饰类
该类只能被本包下的子类所继承,在继承的时候 并不继承父类的可见性
package com.baizhi.demo09
protected class Student { //只可以被本包子类继承,其它包下不可见
protected var id:Int=_
protected var name:String =_
protected def say():Unit={
println(s"${name},${id}")
}
}
class SmallStudent extends Student {
def this(id:Int,name:String){
this()
this.id=id
this.name=name
}
override def say(): Unit = { //默认情况下,子类再继续父类的方法时候,会继承方法的可见性,覆盖父类方法可见性
println(s"${id},${name}")
}
}
如果子类不覆盖父类的方法,在继承父类方法的时候,也会继承父类方法的可见性。
this限定
当被this限定修饰,该方法或者属性只能被本类内部可见,去除伴生对象。
class Animal {
private|protected[this] var name:String=_ //去除伴生对象的可见性
def this(name:String){
this()
this.name=name
}
def say():Unit={
println(s"${name}")
}
}
包限定
package com.baizhi.demo11
class Animal {
private[demo11] def say():Unit={ //所有在demo11包下的类,都可看到 say方法,此时private限定失去了意义
println("animal say")
}
}
9、常见关键字
final 关键字
修饰类别 | Java | Scala |
---|---|---|
类 | 最终类,没有子类 | 最终类,没有子类 |
方法 | 不能被覆盖 | 不能被覆盖 |
属性 | 常量,不允许修改 | 属性不允许被子类遮盖(val属性) |
class Animal {
final val name:String="大黄"
def say():Unit={
println(s"${name}")
}
}
---
class Dog extends Animal {
override val name:String="小黑" //错误
}
√lazy关键字
被lazy修饰的val常量,只用被真正是使用的是时候,才会被初始化。
lazy val num=sum(1,2)
println(s"${num}")
def sum(x:Int,y:Int):Int={
println("===========")
x+y
}
sealed关键字
修饰一个类,被sealed修饰的类所已知子类,必须和父类放置在一个源文件中。
sealed class Message(content:String)
case class SmsMessage(content:String,phone:String) extends Message(content:String)
case class EmailMessage(content:String,email:String) extends Message(content:String)
val messages = Array[Message](SmsMessage("hello","110"),EmailMessage("你好","11@qq.com"))
messages.foreach(message=>{
message match {
case m:SmsMessage =>{
println("收到短信:"+m.phone)
}
case m:EmailMessage=>{
println("收到邮件:"+m.email)
}
}
})
10、隐式转换/注入/增强 - 重点
implicit 变量- 隐式值
一般定义隐式值的目的是为了隐式注入,一般要求同一个程序上下文只能一种类型隐式值
scala> implicit var a:Int=100
a: Int = 100
scala> var m=implicitly[Int] //按照类型注入
m: Int = 100
implicit 修饰参数- 隐式
object MyImplicits {
implicit var i= 100 //确保隐式值类型 只能有一个
}
object TestMyImplicits {
def main(args: Array[String]): Unit = {
import com.baizhi.demo12.MyImplicits._
var num = implicitly[Int] //那边用i了这边就不能用i否则会报错
println(s"${num}")
}
}
Error:(6, 23) could not find implicit value for parameter e: Int
var i = implicitly[Int]
Error:(6, 23) not enough arguments for method implicitly: (implicit e: Int)Int.
Unspecified value parameter e.
var i = implicitly[Int]
object MyImplicits {
implicit val f=(x:Int,y:Int)=>x+y
}
object TestMyImplicits {
def main(args: Array[String]): Unit = {
import com.baizhi.demo13.MyImplicits._
var result2=process(1,2)
println(s"${result2}")
}
}
def process(x:Int,y:Int)(implicit fun:(Int,Int)=>Int):Int={
fun(x,y)
}
implicit 修饰方法- 隐式转换 不兼容变成兼容
def tellTime(date:Date):Unit={
println(s"${date.toLocaleString}")
}
import com.baizhi.demo13.MyImplicits._
tellTime("2019-9-20 16:34:50") // 等价 tellTime(str2Date("2019-9-20 16:34:50"))
//tellTime(new Date())
object MyImplicits {
implicit def str2Date(str:String):Date={//参数表示要转换的类型,返回值表示需要的目标类型
val sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
sdf.parse(str)
}
}
implicit 修饰类- 隐式增强 不可能变可能
class Pig {
def sleep():Unit={
println("sleep")
}
def eat():Unit={
println("eat")
}
}
object MyImplicits {
implicit class PigImplicits(pig:Pig){
def fly():Unit={
println("pig 会飞")
}
}
}
import com.baizhi.demo13.MyImplicits._
val pig = new Pig()
pig.eat()
pig.sleep()
pig.fly()//增强方法
Scala泛型(看懂)
<: 上界限定
就是 <=
要求 T 必须是指定泛型或者指定泛型的子类
def keep[T<:Dog](t:T):Unit={
println(t)
}
keep(new SmallDog(""))//给参数定义个泛型
>: 下界限定
>=(有点bug 把小狗当做狗了 给小狗加上小狗的泛型就可以了)
原意是表示 T 必须是指定泛型或者指定泛型的父类
def keep[T>:Dog](t:T):Unit={
println(t)
}
keep(new Animal)//给参数定义个泛型
<% 视图限定
强制将T看做成SmallDog,但是要求用户在使用时必须提供隐式转换,将T转换为SmallDog
class SmallDog(name:String) extends Dog {
def speek():Unit={
println(s"${name} 汪汪叫~")
}
}
------
implicit def str2SmallDog(str:String):SmallDog={
new SmallDog(str)
}
playWithSmallDog("小狗")
def playWithSmallDog[T<% SmallDog](dog:T):Unit={
dog.speek()
}
A: T上下文限定
和视图限定相似但是不同
表示上下文中一定会有一个T隐式值
先画大饼
def compare[T:MyOrder](t1:T,t2:T):Unit={
val myOrder = implicitly[MyOrder[T]]
myOrder.compareT(t1,t2)
}
implicit val myOrder = new MyOrder[String]
compare("a","b")
------
class MyOrder[T] {
def compareT(t1: T,t2:T):Unit={
println(t1+" "+t2)
}
}
+A 协变
可以把小给大
即:将小泛型的引用赋值给大泛型的引用
class DogKeeper[+T] {}
var dogKeeper1=new DogKeeper[SmallDog]
var dogKeeper2=new DogKeeper[Dog]
var dogKeeper3=new DogKeeper[Animal]
dogKeeper3=dogKeeper2
dogKeeper2=dogKeeper1
-A 逆变
可以把大给小
即:将大泛型的引用赋值给小泛型的引用
A 不变(java 中没有协变和逆变,只有不变;java中泛型不支持多态)
指定将相同泛型的引用赋值给相同泛型的引用
class DogKeeper[-T] {}
var dogKeeper1=new DogKeeper[SmallDog]
var dogKeeper2=new DogKeeper[Dog]
var dogKeeper3=new DogKeeper[Animal]
dogKeeper2=dogKeeper3
dogKeeper1=dogKeeper2
(重点)集合/数组 操作
Array
scala> var array=Array(1,2,3,4,5)
array: Array[Int] = Array(1, 2, 3, 4, 5)
scala> array(0)= -1
scala> array(0)
res2: Int = -1
scala> array.length
res4: Int = 5
scala> array.size
res5: Int = 5
Range
scala> var range =new Range(0,10,2)
range: scala.collection.immutable.Range = Range(0, 2, 4, 6, 8)
scala> var range= 0 to 10 by 2
range: scala.collection.immutable.Range = Range(0, 2, 4, 6, 8, 10)
scala> var range= 0 until 10 by 2
range: scala.collection.immutable.Range = Range(0, 2, 4, 6, 8)
Vector
scala> var vec=Vector(0,10,1)
vec: scala.collection.immutable.Vector[Int] = Vector(0, 10, 1)
scala> vec(1)
res12: Int = 10
scala> vec(2)
res13: Int = 1
scala> for(i<- 0 to 10 by 2) yield i
res15: scala.collection.immutable.IndexedSeq[Int] = Vector(0, 2, 4, 6, 8, 10)
- Iterator
scala> var it=Iterator(1,2,3)
it: Iterator[Int] = non-empty iterator
scala> for(i<- it) println(i)
1
2
3
scala> for(i<- it) println(i)
scala> var it=Iterator(1,2,3)
it: Iterator[Int] = non-empty iterator
scala> it.size
res21: Int = 3
scala> it.size
res22: Int = 0
- List (只读)
scala> val list = List(1,2,3,4,5)
list: List[Int] = List(1, 2, 3, 4, 5)
scala> list.size
res0: Int = 5
scala>
scala> list.+:(-1) //返回新的List,原始数据不变
res1: List[Int] = List(-1, 1, 2, 3, 4, 5)
scala> list.::(-1) //追加元素
res2: List[Int] = List(-1, 1, 2, 3, 4, 5)
scala>
scala> list.++(List(0,0)) //向后追加,元素
res3: List[Int] = List(1, 2, 3, 4, 5, 0, 0)
scala> list.++:(List(0,0))//向前追加,元素
res4: List[Int] = List(0, 0, 1, 2, 3, 4, 5)
scala> list.:::(List(1,3)) //向前追加 元素
res5: List[Int] = List(1, 3, 1, 2, 3, 4, 5)
scala>
ListBuffer(支持修改)
scala> var listBuffer=new ListBuffer[Int]
listBuffer: scala.collection.mutable.ListBuffer[Int] = ListBuffer()
scala> listBuffer.+=(1)//添加元素
res22: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1)
scala> listBuffer.update(0,-1)//修改0位置为-1
scala> listBuffer.-=(-1)//删除元素
res24: scala.collection.mutable.ListBuffer[Int] = ListBuffer()
scala> listBuffer.++=(Array(1,2,3,4))
res25: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3, 4)
scala> listBuffer.remove(1)
res26: Int = 2
scala> listBuffer
res27: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 3, 4)
Set(不可变)
scala> var set=Set(1,2,3,4,5,1)
set: scala.collection.immutable.Set[Int] = Set(5, 1, 2, 3, 4)
scala> set.+(-1)
res35: scala.collection.immutable.Set[Int] = Set(5, 1, 2, 3, -1, 4)
scala> set.size
res36: Int = 5
scala> set.+=(10)
scala> set
res38: scala.collection.immutable.Set[Int] = Set(5, 10, 1, 2, 3, 4)
Set(可变集合)
scala> var set=scala.collection.mutable.Set(1,2,3,4,5,1)
set: scala.collection.mutable.Set[Int] = Set(1, 5, 2, 3, 4)
scala> set.+(-1)
res51: scala.collection.mutable.Set[Int] = Set(1, 5, 2, -1, 3, 4)
scala> set.size
res52: Int = 5
scala> set.+=(10) //添加元素,修改自己
res53: scala.collection.mutable.Set[Int] = Set(1, 5, 2, 3, 10, 4)
scala> println(set.add(1)) //表示添加是否成功
false
scala> set.remove(1)
res55: Boolean = true
- HashMap(不可变)
import scala.collection.mutable.HashMap
var hm=HashMap[String,String](("001","zhangsan"),("002","李四"))
//val hm1=Map[String,String](("001","张三"),("002","lisi"))
hm.-("001") //删除key,不修改原始值
hm.+(("003","王五")) //添加值,不修改原始值
//获取Map的值
val value = hm.get("004").getOrElse("没有值")
//迭代key
for(k<-hm.keys){
println(hm.get(k).getOrElse(""))
}
val size = hm.size
//迭代值
for(i<-hm.values) println(i)
- HashMap(可变)
var hm=HashMap[String,String](("001","zhangsan"),("002","李四"))
//val hm1=Map[String,String](("001","张三"),("002","lisi"))
hm.-("001") //删除key,不修改原始值
hm.+(("003","王五")) //添加值,不修改原始值
hm.put("004","赵六")
hm.remove("004")
//获取Map的值
val value = hm.get("004").getOrElse("没有值")
//迭代key
for(k<-hm.keys){
println(hm.get(k).getOrElse(""))
}
val size = hm.size
//迭代值
for(i<-hm.values) println(i)
集合计算(必须掌握)
为何要学习Scala集合计算?
算子学习
参考:https://blog.csdn.net/weixin_38231448/article/details/89354185
√ filter算子
用于对集合数据List[T]做过滤,需要传递一个 fn: T => Boolean
scala> List(1,2,3,4,5).filter(item => item %2 ==0) //List(1,2,3,4,5).filter(_ % 2 ==0)
res11: List[Int] = List(2, 4)
scala> val list=List("hello ni hao","hello world","hello scala")
list: List[String] = List(hello ni hao, hello world, hello scala)
scala> list.filter(item=> item.contains("scala"))// list.filter(_.contains("scala"))
res13: List[String] = List(hello scala)
还有一个filterNot算子和filter相反,将满足条件的元素去除。
scala> List(1,2,3,4,5).filterNot(_ % 2 ==0)
res19: List[Int] = List(1, 3, 5)
flatten
用作数组数据降维,例如List[Array[T]] 通过flatten转换可以得到List[T]
scala> val list=List(Array(1,2,3),Array(4,5))
list: List[Array[Int]] = List(Array(1, 2, 3), Array(4, 5))
scala> list.flatten
res21: List[Int] = List(1, 2, 3, 4, 5)
√ map算子
用作数组元素的转换List[T]集合需要提供 fun:T=>U将List[T]转换为List[U]
scala> List("a","b","c").map(item=> (item,1))
res22: List[(String, Int)] = List((a,1), (b,1), (c,1))
scala> val words=List("this is a demo","hello scala").map(line=>line.split(" ")).flatten
words: List[String] = List(this, is, a, demo, hello, scala)
scala> words.map(word=>(word,1))
res27: List[(String, Int)] = List((this,1), (is,1), (a,1), (demo,1), (hello,1),
(scala,1))
√flatMap等价 1.map 2. flatten
专门用于将集合中的元素先进行map转换 List[T] ,尝试将元素T转换为List[U],一般需要提供fun: T =>List[U],这样原始的List[T]就变成List[List[U]],然后该算子会执行flatten将List[List[U]]展开成为List[U]
scala> val lines=List("this is a demo","hello scala")
lines: List[String] = List(this is a demo, hello scala)
scala> val words=lines.flatMap(line=>line.split(" "))
words: List[String] = List(this, is, a, demo, hello, scala)
scala> val linesplits=lines.map(line=> line.split(" "))
linesplits: List[Array[String]] = List(Array(this, is, a, demo), Array(hello, sc
ala))
scala> linesplits.flatten
res36: List[String] = List(this, is, a, demo, hello, scala)
√sort算子
sorted
scala> val nums = List(2,3,5,1,4,6)
nums: List[Int] = List(2, 3, 5, 1, 4, 6)
scala> nums.sorted
res37: List[Int] = List(1, 2, 3, 4, 5, 6)
scala> nums.sorted(new Ordering[Int]{
| override def compare(x: Int, y: Int): Int = {
| (x-y) * -1
| }
| })
res39: List[Int] = List(6, 5, 4, 3, 2, 1)
sortBy(根据某个字段排序)
scala> var users=List((1,"zs",18),(2,"lisi",20))
users: List[(Int, String, Int)] = List((1,zs,18), (2,lisi,20))
scala> users.sortBy(user=>user._3)
res41: List[(Int, String, Int)] = List((1,zs,18), (2,lisi,20))
scala> users.sortBy(user=>user._3)(new Ordering[Int]{
| override def compare(x: Int, y: Int): Int = {
| (x-y) * -1
| }
| })
res42: List[(Int, String, Int)] = List((2,lisi,20), (1,zs,18))
sortWith(感觉比sortBy好使)
scala> var users=List((1,"zs",18),(2,"lisi",20))
users: List[(Int, String, Int)] = List((1,zs,18), (2,lisi,20))
scala> users.sortWith((u1,u2)=> u1._3 > u2._3)
res45: List[(Int, String, Int)] = List((2,lisi,20), (1,zs,18))
scala> users.sortWith((u1,u2)=> u1._3 < u2._3)
res46: List[(Int, String, Int)] = List((1,zs,18), (2,lisi,20))
√groupBy
scala> val list=List("a","b","a","b","c")
list: List[String] = List(a, b, a, b, c)
scala> list.groupBy(c=>c)
res47: scala.collection.immutable.Map[String,List[String]] = Map(b -> List(b, b)
, a -> List(a, a), c -> List(c))
有如下数组:List(“this is a demo”,"good good study ",“day day up”,"come on baby "),请输出格式数据
List((this,1),(good,2),…)
scala> val lines=List("this is a demo","good good study ","day day up","come on
baby")
scala> val wordpair=lines.flatMap(line=>line.split(" ")).map(word=>(word,1))
wordpair: List[(String, Int)] = List((this,1), (is,1), (a,1), (demo,1), (good,1)
, (good,1), (study,1), (day,1), (day,1), (up,1), (come,1), (on,1), (baby,1))
scala> val group=wordpair.groupBy(word=>word._1).toList
group: List[(String, List[(String, Int)])] = List((this,List((this,1))), (demo,L
ist((demo,1))), (is,List((is,1))), (good,List((good,1), (good,1))), (up,List((up
,1))), (a,List((a,1))), (come,List((come,1))), (on,List((on,1))), (baby,List((ba
by,1))), (day,List((day,1), (day,1))), (study,List((study,1))))
scala> group.map(t=>(t._1,t._2.size))
res53: List[(String, Int)] = List((this,1), (demo,1), (is,1), (good,2), (up,1),
(a,1), (come,1), (on,1), (baby,1), (day,2), (study,1))
sum
只能针对数值类型的数组做计算
scala> List(1,2,4,5).sum
res57: Int = 12
count
计算数组中数据的个数
scala> List("a","c","c").count(item=>true)
res63: Int = 3
max|min|maxBy|minBy
scala> List(1,3,4,5,6).max
res65: Int = 6
scala> List(1,3,4,5,6).min
res66: Int = 1
scala> List(("a",1),("a",2),("c",3)).maxBy(t=>t._2)
res68: (String, Int) = (c,3)
scala> List(("a",1),("a",2),("c",3)).maxBy(t=>t._1)
res69: (String, Int) = (c,3)
scala> List(("a",1),("a",2),("c",3)).minBy(t=>t._2)
res70: (String, Int) = (a,1)
√reduce
scala> List(1,2,3,4).reduce((v1,v2)=> v1+v2)//scala> List(1,2,3,4).reduce(_+_)
res71: Int = 10
√aggregate
scala> val ints = List(1,2,3,4,5,6)
ints: List[Int] = List(1, 2, 3, 4, 5, 6)
scala> val tuple = ints.aggregate((0.0,0))((z,v)=>(z._1+v,z._2 +1),(b1,b2)=>
(b1._1+b2._1,b1._2+b2._2))
tuple: (Double, Int) = (21.0,6)
scala> var avg=tuple._1/tuple._2
avg: Double = 3.5
√fold
scala> val ints = List[Int]()
ints: List[Int] = List()
scala> ints.fold(0)((v1,v2)=>v1+v2)
res2: Int = 0
scala> val ints = List[Int](1,2,3,4)
ints: List[Int] = List(1, 2, 3, 4)
scala> ints.fold(0)((v1,v2)=>v1+v2)
res4: Int = 10
字符统计
scala> var arrs=Array("this is a demo","good good study","day day up")
arrs: Array[String] = Array(this is a demo, good good study, day day up)
scala> arrs.flatMap(line=>line.split(" "))
.groupBy(word=>word).toList
.map(t=>(t._1,t._2.size))
.sortBy(t=>t._2)
res18: List[(String, Int)] = List((this,1), (demo,1), (is,1), (up,1), (a,1), (st
udy,1), (good,2), (day,2))
scala> arrs.flatMap(line=>line.split(" "))
.map((_,1))
.groupBy(t=>t._1)
.toList.map(t=>(t._1,t._2.map(_._2).sum))
.sortBy(t=>t._2)
res19: List[(String, Int)] = List((this,1), (demo,1), (is,1), (up,1), (a,1), (st
udy,1), (good,2), (day,2))
count
计算数组中数据的个数
scala> List("a","c","c").count(item=>true)
res63: Int = 3
max|min|maxBy|minBy
scala> List(1,3,4,5,6).max
res65: Int = 6
scala> List(1,3,4,5,6).min
res66: Int = 1
scala> List(("a",1),("a",2),("c",3)).maxBy(t=>t._2)
res68: (String, Int) = (c,3)
scala> List(("a",1),("a",2),("c",3)).maxBy(t=>t._1)
res69: (String, Int) = (c,3)
scala> List(("a",1),("a",2),("c",3)).minBy(t=>t._2)
res70: (String, Int) = (a,1)
√reduce
[外链图片转存中…(img-EMPPPNhU-1571672994569)]
scala> List(1,2,3,4).reduce((v1,v2)=> v1+v2)//scala> List(1,2,3,4).reduce(_+_)
res71: Int = 10
√aggregate
[外链图片转存中…(img-ytbD98oo-1571672994570)]
scala> val ints = List(1,2,3,4,5,6)
ints: List[Int] = List(1, 2, 3, 4, 5, 6)
scala> val tuple = ints.aggregate((0.0,0))((z,v)=>(z._1+v,z._2 +1),(b1,b2)=>
(b1._1+b2._1,b1._2+b2._2))
tuple: (Double, Int) = (21.0,6)
scala> var avg=tuple._1/tuple._2
avg: Double = 3.5
√fold
[外链图片转存中…(img-yhWz4yci-1571672994570)]
scala> val ints = List[Int]()
ints: List[Int] = List()
scala> ints.fold(0)((v1,v2)=>v1+v2)
res2: Int = 0
scala> val ints = List[Int](1,2,3,4)
ints: List[Int] = List(1, 2, 3, 4)
scala> ints.fold(0)((v1,v2)=>v1+v2)
res4: Int = 10
字符统计
scala> var arrs=Array("this is a demo","good good study","day day up")
arrs: Array[String] = Array(this is a demo, good good study, day day up)
scala> arrs.flatMap(line=>line.split(" "))
.groupBy(word=>word).toList
.map(t=>(t._1,t._2.size))
.sortBy(t=>t._2)
res18: List[(String, Int)] = List((this,1), (demo,1), (is,1), (up,1), (a,1), (st
udy,1), (good,2), (day,2))
scala> arrs.flatMap(line=>line.split(" "))
.map((_,1))
.groupBy(t=>t._1)
.toList.map(t=>(t._1,t._2.map(_._2).sum))
.sortBy(t=>t._2)
res19: List[(String, Int)] = List((this,1), (demo,1), (is,1), (up,1), (a,1), (st
udy,1), (good,2), (day,2))