scala
scala是多范式编程语言,集成面向对象编程和函数式编程的各种特性
运行在虚拟机,兼容java程序
scala被编译成java字节码,运行于jvm,可以调用java类库
函数式编程更实用mapreduce和大数据模型,摒弃了数据与状态的计算模型,着眼于函数本身,而非执行过程的数据和状态数据的处理,函数时逻辑清晰简单更适合处理基于不变数据的批量处理工作这些工作都是通过mapreduce操作转化数据后生成新的副本,再进行处理,
spark flink kafka都是采用scala开发
在执行时调用不带$的.class字节码文件,带$的.class 是scala编译之后的文件,,不带$的.class文件是jvm和scala文件运行的纽带
scala编码规范
命名规范和java一样
文件名
1首字母大写
2多个单词之间用驼峰命名
3以名词居多
4尽量不要使用名词的复数形式
方法函数命名
首字母小写
多个单词之间的使用驼峰命名
以动词+名词
文件编码统一为uft-8
每行缩进使用4空格代替缩进tab
{}的位置写法和java保持一致
scala基础知识与语法
scala语言的特点
可扩展
面向对象
函数式编程
兼容java
类库调用
互操作
语法简洁
代码行短
类型推断
抽象控制
静态类型化
可检验
安全重构
支持并发控制
强计算能力
自定义其他控制结构
scala与java的关系
1 都是基于jvm虚拟机运行的
scala编译之后的文件也是.class,都要转换为字节码,然后运行在jvm虚拟机上
2 scala和java相互调用
在scala中可以直接调用java代码,同时可以在java中直接调用scala的代码
3 java8 vs scala
1)java8(lambda)没有出来之前java只是面向对象的一门语言,但是java8出来之后,java就是一个面向对象和面向函数的混合语言了
2)首先我们要对scala进行精准定位,,从某种程度上讲,scala并不是一个纯粹的面向函数的编程语言,有人认为scala是一个带有闭包的静态面向对象语言,更准确的说scala是面向函数与面向对象的混合
3)scala设计初中是面向函数fp(函数式编程)而java是面向对象oo,现在两者都是oo和fp的混合语言,
由于面向对象oo和面向函数fp两种范式是类似横坐标和纵坐标两者不同坐标方式的思考方式,类似与数据库和对象之间的不匹配阻抗关系,
面向对象是最接近人类思维的方式,而面向函数是最接近计算机的思维方式,如果你想让计算机为人的业务建模服务,那么以oo为主,如果你希望计算机能自己通过算法从大数据中自动建模,那么以fp为主,所以java可能还占据企业工程主要市场,而scala则会在科学大数据分析等领域抢占java市场,比如scala的spark大有替代javahadoop之趋势
scala解释器
1,scala解释器读到一个表达式,对他进行求值,将他打印出来,接着继续读下一个表达式,这个过程称为读取read--求值eva--打印print--循环loop--即:repl
从技术上将scala程序并不是一个解释器,实际发生的是,你输入的内容被快速的变编译成字节码,然后这段字节码交由java虚拟机执行,正因为如此,大多数scala程序员把他称为repl
scala变量的定义
scala的变量的定义分为两种方式,分别需要使用关键字val和var
val
val a=3
var
var a=3
val和var都可以用来定义变量,唯一的区别就是val定义的变量是不可变,var定义的变量可变,不可变的量确实类似常量,但不叫常量,只能叫做不可变的量。
在开发中,能用val就用val,var尽量不用,因为scala编程的特点,是在创建变量的时候可以省略数据类型,val有助于进行类型推断,var在操作过程中,不利于变量的类型
一个完整变量的定义如下
val a:Int = 3
其中val为变量修饰符,a为变量名,Int为a的数据类型,其中需要将变脸名和数据类型使用:连接=右侧为变量的值
scala的数据类型
byte 1字节
short 2字节
int 4字节
long 8字节
float 4字节
double 8字节
char 2字节
string 字符串
boolean布尔型
unit 表示无值和其他语言void相等
null 空值或者空引用
nothing 所有其他类型,表示没有值
any 所有类型的超类,任何实例都是any类型
anyref 所有引用类型的超类
说明:scala拥有和java一样的数据类型,和java数据类型的内存布局完全一致,精度也完全一样,需要说明的是any和anyref,其中any是对象的超类,(运行时的一个概念,比如val p =new Person(),引用p指向的对象有一个父类any),anyref是类的超类(class Person{},该类Person有一个隐含的超类Anyref),实例队形是类的具体化
scala操作符说明
scala的操作符和java一样
1,在scala调用一些方法或者函数的时候,如果方法或者函数是空参的可以省略掉
2,在scala中一行表示的结尾不像java需要使用“;” ,scala可以省略
scala的流程控制结构
if表达式,接班和java 中的if表达式一模一样,唯一区别是scala中的if,if-else,ifelseifelse有返回值
记住:scala中任何的表达式都有返回值
如果else丢失了
if (x>0) 1
那么有可能if语句没有输出值,但是在scala中每一个表达式都幼稚,这个问题的解决方案是引入一个unit类,不写() ,不带else语句的if语句等同于if(x>0) 1 else()
scala的返回值可以省略return关键字,表达式的最后一句话作为表达式的返回值返回,return关键字通常使用在函数中进行逻辑的终止,比如循环
注意前面提到过,scala中的语句终止就是换行,也就是一行一个语句,,此时便可以省略“;”,但是当一行有多条语句的时候,就应该使用“;”分隔,在表达式块中同样可以使用换行符作为一条语句的分隔
如果if、循环{}中只有一条语句时,可以省略{} ,但是加上{}会显得结构清晰
循环
注意scala中不能对一个变量执行自增自减,++/--是scala集合的一个函数
循环的终止
在java中终止循环有关键字,continue,break,return但是在scala中没有前两者,continue
有三种循环来结束循环
1使用return来控制循环结束
2使用循环的条件来进行控制,如上count>=0
3还可以使用breakable函数体来进行控制
for循环
在java中有两种写法,普通for(变量;条件;自增自减){循环体}
or
for(类型 变量:集合){}
scala中并没有java中普通for循环的语法结构,更像是高级for循环
for(变量<-集合){} val rang=1.to(10) var sum=0 for (n<-rang reverse) { print(n) } print(sum)
val range=1.to(10) 从1到10
添加reverse就是向进行循环的反转
异常控制
scala的异常体系,在很大程度沿用java的异常体系,只不过在书写的结构上有差异
try { //lazy懒加载,或者延迟加载,被lazy所修饰的变量,之后在第一次被调用的时候才会进行初始化 lazy val num=Integer.valueOf("abcd") print(num) }catch { case e: ArithmeticException=>{ print(e.getMessage) } case e1:NumberFormatException=>{ e1.printStackTrace() } case _:Exception=>{ print("文件找不到") } }
在java中学习了两种异常处理方式throws和try-catch,throw和java中一模一样,将异常转交给调用者进行处理,try-catch则意味着自己要进行处理,但是编写的方式和java不一样
case e: ArithmeticException=>{ print(e.getMessage) }
被lazy(懒加载)所修饰的变量,只有当第一次被使用的时候,才会进行初始化,没有使用之前只是记录了存在,检查语法结构是否正常,可以节省一定的资源空间
scala的函数
scala除了方法外还支持函数,方法对对象进行操作,函数不是,要定义函数,你需要给出函数的明恒,参数,函数体,
def functionname(name:string):returntype={method body}
1 你必须给出所有参数的类型,不过只要函数不是递归的,你就不需要指定返回的类型,scala编译器可以通过=符号右侧的表达式的类型推断出返回类型
2 =并不只是用来翻个函数名和函数体的,他的另一个作用是告诉编译器是否对函数的返回值进行类型推断,如果省去等号则认为函数是没有返回值的
3 在scala中的一个函数的最后一句话就是该函数的返回值,不需要使用return当然加上也行
def main(args: Array[String]): Unit = { val ret=sayHi("好好学习") print(ret) } def show()={ print("你还能学") } def sayHi(str: String)={ "你一定要"+str }
,函数可以不用return语句,但是如果加上return那返回值类型就必须加上
def sayHi(str: String):String={ return "你一定要"+str }
特殊函数
单行函数
所谓单行函数,指的是函数体只有一行的函数
def sayHello(str:string)=print("你一定要"+str)
注意,单行函数,必须要使用=来做函数的返回值类型推断
无参函数
参数列表为空
def show()={ print("好好学习") }
注意,定义空参函数的时候,如果函数加了(),在调用的时候,()可以省略,但是如果定义的时候没有加(),在调用的时候也只能省略,不能加()
递归函数
在特定的条件下调用函数本身
递归 def factorial(n:Int):Int={ if(n==1) 1 else n*factorial(n-1) }
函数参数的特点
默认参数和带名参数
def main(args: Array[String]): Unit = { showAddr("菜狗子",1888888888,"北京","cp")//会覆盖默认值 showAddr("二狗",1999999988,"重庆","na")//使用默认值 //传递参数的时候,可以指定具体的参数名称 showAddr(phone = 131234123L,name = "狗蛋",province = "lll") } def showAddr(name:String,phone:Long,province:String,country:String="China"):Unit={ print(s"name=$name,phene=$phone,province=$province,contry=$country") }
scala的函数参数列表,在定义的时候,可以使用有默认值的参数,在做函数调用的时候可以使用带名的参数,所以有时我们是无法通过函数传递的参数来推断具体的参数列表
可变参数
scala版本
def main(args: Array[String]): Unit = { print(add(1,2,3)) print(add(1,2,3,5)) //声明scala中的一个数组 val a=Array(3,4,5) print(add(a:_*)) } def add(arr:Int*)={ var sum=0 for (a<-arr){ sum+=a } sum }
说明
1 scala中的可变参数和java中有一点不同,
java中
add(new int[]{1, 3, 5, 7}
add(1, 3, 5, 7, 9)
这两个实际是同一个函数
而在scala中是两个不同的函数
2 在对函数add(arr:int*)就不能向java一样,直接传递一个数据,否则报错,类型不匹配,想给一个可变参数传递数组就需要将数组中的元素提取出来,在传递,
val arr = Array(3,4,5)
ret = add(arr:_*)
scala数组和集合
scala的集合分为两类,一类是可变集合(集合可以执行增删改查操作),另一类是不可变的集合(集合可以执行增删查改操作),另一类是不可变集合(集合元素在初始化的时候确定,后续只能进行查,有的可以修改,有的不可以),二者的可能名称一样,但是在不同的包下面对应的包为:scla.collection.mutable和scala.collection.immutable
scala默认使用的底盒,或者默认导入的包是immutable
说明:这里提到的可变或者不可变,指的是容器内部的内容,以及容器的长度可变或者不可变
scala数组
Array可以理解为java中的数组
1.数组的定义
java中数组的定义: new int[5] int[] aa={1,2,3} int[] bb=new int[]{1,2,3,4} scala中数组的定义: val array=new Array[类型(比如Int/Long)](5)-->定义了一个长度为5的Int/Long类型的数组,每一个元素都有默认值:0 val array=Array(1,2,3,4,5)--->定义类一个初始化为荣为1,2,3,4,5的Int型的数组 其中你第二种的定义方式,没有new关键,其实class Array的伴生队形的构建方式
获取数组中的元素
数组名(index)
val ele = array(3)
元素的遍历
for(ele<-数组){ ... } for(i<-array){ println(i) }
数组长度
array.length array.size
数组判断
判断元素是否包含 array.contains(ele) println("判断元素-3时候在数组中存在的:"+array.contains(-2))
数组元素的拼接输出
使用数据的方法,mkString //拼接数组元素,没有分隔符 println("数组元素"+ array.mkString) //拼接数组元素,使用分隔符“,” println("数组元素"+array.mkString(",")) //拼接数组元素,使用分隔符“,”其实字符为[,终止字符为] printlb("数组元素,"+array.mkString("[",",",","]"))
可变数组ArrayBuffer
ArrayBuffer可以理解为java中的arraylist
定义
//定义一个int类型的可变数组 val arrayBuffer =new ArrayBuffer[int]() //定义一个String累心的可变数组 val arrayBuffer =ArrayBuffer[String]() //定义一个String=ArrayBuffer[String](16)
crud
//增 ab.append(1) println("ab.append(1)"+ab) ab.append(2,3,4) println(ab) //插 insert(index,ele*)index位置ele*多个元素 ab.insert(2,-2,-4) //查 //获取指定索引的位置的元素 val ele =ab(5) println(ele) //改 ab(5) = -5等号左右分开,不然会报错 ab(5) = -5 println(ab(5)) //删除drop var newAb = ab.drop(2) println(ab) println(newAb) new = ab.dropRight(2) println(ab) println(newAb)
遍历和长度
遍历和array方式一样
长度可以使用length也可以使用size
for(i<-ab){ print(i+"\t") }
拼接数组字符串
ab.mkString("[",",","]")
包含
ab.contains(ele)判断元素是否包含
数组求和
调用数组的函数sum print("数组求和"+ab.sum)
数组的最大值和最小值
调用数组的函数max和min println("最大值"+ab.max) println("最小值"+ab.min)
Array和arrayBuffer之间的转换
类似java中数组和ArrayList之间 的转换
java版本 数组--->List:Arrays.aslist(array) List--->数组:list.toArray(new Xxx[list.size()]) scala版本 Array--->ArrayBuffer:array.toBuffer ArrayBuffer---->Array:ab.toArray
数组内容的显示
mkString(start,sep,end)
scala集合
map映射
map是一个对偶,映射的kv键值对的集合,在一个map中可以包含若干组kv映射关系,前提是k不能重复,同样map也有可变和不可变之分
不可变map
import scala.collection.immutable.Map
定义
因为Map是一个类似java中的接口,无法直接创建对象,所以需要使用它的伴生对象创建
val map = Map[Type1,Type2]() 创建一个key的类型为Type1,value的类型为Type2的map映射
初始化
不可变的map数组,只能在初始化的时候指定 val map= Map[Type1,Type2]( (k1 -> v1), (k2 -> v2), (k3 -> v3), .... ) 或者 val map = Map[Type1,Type2]( k1 -> v1, k2 -> v2, .... )
crud
//国家--->首都 val capital = Map[String,String]( ("china" -> "BJ"), "japan" -> "tokyo", "south korea" -> "汉城" ) //crud //增 不可变的map不能追加 // capital+=("usa" -> "wc") // capital("usa")="wc" //删除 val ret = capital.drop(1)//原来的集合不会发生变化 println("capital.drop(1)返回值"+ret) println("capital.drop(1)之后的map:"+capital) //改 // capital("china")="wc" //查 /* map.get(key) 返回类似some或者None 也就是如果key在map中存在返回some,意思就是存在 反之返回none 总结:value也就是可能有值,也可能没有值 scala中把这种可能有值,也可能没值通过一个类option[T]来表示 Option有两个子类Some和None */ println(capital.get("japan")) println(capital.get("uk")) get的返回值是option也就是无法直接取得value,想要获得value就要调用option。get() java.util.nosuchElementException:None.get 同时需要注意的是,get只能对some操作,对none操作会抛出异常NosuchelementException println(capital.get("japan").get) 获取元素的方式二 java.util.NosuchElementException:key not found:uk key 不存在会报错 println(capital("japan")) 推荐使用 val countryC = capital.getOrElse("china","DBJ") print(countryC) 判断 if (capital.contains("uk")){ println(capital("uk")) } 长度 capital.size
可变map
import scala.collection.mutable.Map
定义并出示化
val map =mutable.Mpa[Type1,Type2]( (k1 -> v1), (k2 -> v2) ) 创建一个mpa,key 的类型Type1,value的类型Type2,并且提供了初始化k-v键值对,当然可以去掉其中的kv,因为是可变的,name便可以进行后续的元素追加
crud
追加 map +=("String" -> "String") map("String")="String" 修改 map("String")="String" 获取 map.getOrElse("String","String") 删除 map.remove("String")
map的通用操作
集合大小
map.size
map的遍历
java for(Map.Entry<K,V)me:map.entryset){ me.getKey me.getValue } for(K key:map.keySet()){ V v = me.get(key) } scala for((k,v)<-map){ pirntln(k+"++"+v) } for(kv <- map){ println(kv) } 函数式编程的foreach遍历 map.foreach(kv => { println(kv) })
tuple元组
定义
元组技术一组对偶,也可以理解成java中的List<Object> ,映射是键值对偶的集合,对偶是元组 的最简单的形式,元组是不同累心的值的聚集,元组的值是通过将单个的值包含在圆括号中构成的
例如
(1,3.14,“leetom")是一个元组,类型为Tuple3[Int,Double,java.lang.String]
创建并初始化
说明:元组Tuple是List<Object> 但是其长度是有范围的,最长可以容纳22个元素,也就是Tuple22其中Tuple1可以简写成Tuple
元组只能在创建的时候进行初始化 val tuple1=new Tuple1[Int](1) val tuple2=new Tuple2[Int,Double](1,3.14)
操作
//获取元组的值 //获取第一个 println("tuple2._1" + tuple2._1) 获取第二个 println("tuple2._2" + tuple2._2) 获取第n(1<=N<=22)个 tuple2._N tuple2._2=4.35不可以修改 //遍历 元组不可以直接进行遍历 for(t<-tuple2.productIterator){ println(t) }
常见的定义元组的方式
val season ("spring","summer","autumn","winter") season._2 val (spring,summer,autumn,winter) = ("spring","summer","autumn","winter") println(spring) println(summer)
zip拉链操作
zip操作,就是将两个单列的集合,组成双列的集合,集合中的每一组元素,就上上面的tuple
val province = Array("山东", "河南", "陕西", "福建") val capital = Array("济南", "郑州", "西安", "福州", "桂林") zip拉链操作 val psc =province.zip(capital) for ((p,c) <-pcs) { println(p+"--->"+c) }
特点,在组合两个集合的时候,集合元素来能量一一对应,,如果两个集合的长度不一致,将超过的部分,或者没有匹配上的部分进行裁剪,丢弃。