Scala目录
Scala数据类型
java中有的scala数据类型
Byte 8bit的有符号数字,范围在-128 – 127
Short 16 bit有符号数字,范围在-32768 – 32767
Int 32 bit有符号数字,范围-2147483648到2147483647
Long 64 bit有符号数字,范围-9223372036854775808到9223372036854775807
Float 32 bit IEEE 754单精度浮点数
Double 64 bit IEEE 754双精度浮点数
Char 16 bit Unicode字符,范围U+ 0000到∪+FFFFString字符串
Boolean布尔类型
Null空值或者空引用
scala独有的数据类型
- Unit表示无值,和其他语言中void等同(返回是空==返回式Unit)
- Nothing所有其他类型的子类型,表示没有值(为防空指针出现,调用这个对象的某些方法不会报空指针)
- Any所有类型的超类,任何实例都属于Any类型(等于Java里的Object),但是在scala中Object之下有Any(一人之下万人之上)
- AnyRef所有引用类型的超类(引用类型:String)
- AnyVal所有值类型的超类(值类型:Byte,Short,Int,Long)
变量和常量的声明
声明变量:var
变量是可以改变值的(安装scala的方法见前篇博文https://blog.csdn.net/Andrea_null/article/details/83420374)
也可以通过var声明多个对象[例: var heighat,score = 10]
声明常量:val
常量是不可以改变值的(如果改变,会报错如下error: reassignment to val意思是,不能给val重新赋值)
scala里面的入口函数
我们知道,在java中main函数当作入口函数,修饰它的为public static void。在scala中,入口的main函数也是静态的。但是在scala中没有static这一说,它使用的是Object块和class块来修饰。
凡是定义在Object块里面的属性(包括字段,方法)都是静态的
所以,Object块里要写main函数比如下方代码。
Object修饰Scala01就称之为Scala01的伴生对象。
凡是定义在class代码块里的都是非静态(动态)的
Class修饰Scala01就称之为Scala01的伴生类。
Class里面放实例化的属性
object Scala01 {
def main(args: Array[String]): Unit = { }
class Scala01{
}
伴生对象+伴生类==java中的某一个类
在scala中每行后面不需要加分号,直接通过换行来区分
但是一行中写了多个语句,语句与语句之间必须用分号来分割
例:var age = 18 ; var name = “angelababy”
创建类
在java里面,类名后面是不加小括号的,在Scala中,类名后面是可以加小括号的。
class Person(var id:Int, var name:String)代表了Person类的构造函数,这个构造函数有两个参数。
当参数用var修饰,那么可以直接通过对象来修改其值
当参数用val修饰,无法通过对象来修改值
当参数没有修饰,那么外部无法通过对象来调用。(没有修饰的话,这个参数是私有的)没有修饰的参数只能内部调用。
【注:可以理解成,使用var修饰,那么在它内部,给他自动生成来get和set方法。使用val修饰,只生成了set方法。如果没有修饰符,那么get和set都没有。只是举例子,不是真的生成】
代码中方法名是this的函数是这个类的构造器,以上Person类的默认构造器由两个参数xname和xage组成。this函数是重写的构造函数。重写的构造函数里要调用父构造函数。(重写的构造函数参数不能有修饰符,默认的可以加)
apply方法的使用
在加载scalaDemo1的时候,加载p对象调用的是Person类的构造方法,加载person对象的时候调用的是apply方法。apply方法在加载scalaDemo1的时候自动调用。类似于java的static{}。
注意点(类与对象)
- 类和方法命名建议符合驼峰命名法。
- scala 中的object是单例对象,相当于java中的工具类,可以看成是定义静态的方法的类。
- object不可以传参数。另:Trait不可以传参数
- scala中的class类默认可以传参数,默认的传参数就是默认的构造函数。
- 重写构造函数的时候,必须要调用默认的构造函数。
- class 类属性自带getter ,setter方法。
- 使用object时,不用new,使用class时要new ,并且new的时候,class中除了方法不执行,其他都执行。(也可以用apply方法)
- 如果在同一个文件中,object对象和class类的名称相同,则这个对象就是这个类的伴生对象,这个类就是这个对象的伴生类。可以互相访问私有变量。
判断语句
if else
if else if else
循环语句
for
通过索引
until:左闭右开
0.until(10,2)–>0到10,不包含10,步长是2
to:左闭右闭
每一次都把数给index,然后里面取list的值
增强for循环
for(elem <- list){
println(elem)
}
上面elem为什么没有加类型?
因为scala有自动推测功能。由list类型推测出elem类型
循环练习1:打印99乘法表
/**
* 打印九九乘法表
*/
for(i <- 1 to 9){
for(j <- 1 until 10){
if(j <= i){
print(i + "*" + j + "=" + i*j + "\t")
}
if(j == i){
println()
}
}
}
循环练习2:寻找1-100之间的偶数(在for循环中可以加入判断条件)
for(i <- 1 to 100;if i % 2 ==0) println(i)
//也可以使步长是2直接输出
yield关键词案例:1-100集合中的偶数存储到另外一个集合中
Java风格传统写法:
Scala写法:
val rest = for(i <- 1 to 10;if i % 2 ==0) yield i
yied关键词能够将符合要求的元素自动封装到一个集合中
while:前101次求婚
在scala中没有break跳出循环,如果想跳出循环,只能设一个flag作为标识符。
do…while:先执行循环体,再判断。
do…while循环先上船后补票的感觉
函数的定义(可以定义有参的,也可以定义无参的)
参数名在前,参数类型在后,并且中间要使用冒号
scala六种函数
普通函数
简写规则
- 函数的返回值类型可以省略,因为scala具备自动推测功能,他会根据返回值自动决定这个函数的返回值类型。但是如果用return,需要加上返回值类型
- 将要返回的值放在最后一行,那么这个值就会自动返回。
- 如果函数体内只有一个语句,可以将这行语句与函数名在一行。
- 如果匿名函数参数在函数体内只使用了一次,那么可以用下划线_代替
递归函数:函数体内调用自己本身[示例:计算number的阶乘]
5!=54321
递归函数必须加上返回值类型:
自动推测返回值类型的时候,fun1(num-1)的返回值类型暂时未知。
包含参数默认值的函数:函数的参数有默认值
在调函数的时候可以传参,也可以不传参,若不传参,默认值会被覆盖
以上函数参数有两个默认值参数。
如果按照println(fun2(1,2))这样调取,输出的是3。
如果啥都不传println(fun2()),输出的是30 。
如果写成一个参数有默认值,一个参数没有默认值的函数如下:
调取的方法:
调取println(fun3(100))直接传给一个值就好,输出120 。
如果调用fun4并且希望第一个参数使用默认值,第二个参数使用传入的值
如果直接像fun3那样调取,报错not enough arguments
我们需要指定一下参数位置:println(fun4(num2=1000))这样调取就好了。输出1010 。
可变参数个数的函数:参数个数可以是一个,也可以是多个,随机灵活的
+=前后的数值类型必须一致
+前后数值类型可以不一致(出现自动类型转换)
如何调取?println(fun5(1,2))
匿名函数:没有名字的函数–重要
var fun6 = (num1:Int,num2:Int) => num1+ num2
这个函数没名字,咋调用?
可以将这个函数赋给一个变量,在scala中他将函数也看成了一个对象,可以将函数赋给一个变量。
如果不赋给变量该怎么调?没法调。必须赋给变量。
注意点:
- 1、匿名函数不能显示的声明返回值类型
- 2、匿名函数必须赋给一个变量、变量,通过变量来调用这个匿名函数
- 3、匿名函数的参数与函数体必须要用=>来关联
嵌套函数:在函数体内又定义了一个函数
在此处,fun7函数的体内又调用了fun8递归函数。
偏应用函数[示例:打印日志函数]
偏应用函数是一种表达式,不需要提供函数需要的所有参数,只需要提供部分,或不提供所需参数。
以打印日志的函数作为示例
参数前面是日志日期,后面是日志内容。
上图log函数的问题:调用三次log,它的Date都是相同的。所以不需要传。
logBoundDate底层调的也是log函数,把date写死,第二个参数以下划线表示String。
此时调用的时候只需要传字符串改变的值。
如果不提供参数:也行,把第二个参数也写死。但是不适用于生成日志的函数。
高阶函数:函数的参数是函数,或者函数的返回是函数
函数的参数是函数
f1参数是告诉我们要传入函数参数的格式。
此处把函数当成对象传递(scala支持面向函数编程)
如何将tmpFun当作参数调用?
highFun1(tmpFun,1000)
应该如上来调用,不能加小括号传参。
否则传入的参数就不是这个函数类型了,就是这个函数的返回值类型了。
函数的返回是函数
返回(Int,Int)=>Double这样格式的函数
上述两种函数进行了自动类型转换。
怎么调用?
调用方式1:调用这个函数,它给返回一个函数,再给返回的函数传入值。
eg:
val fun = highFun3()
println(fun(1,2))
调用方式2:匿名函数
eg:
highFun1((num:Int)=>num+1,1000)
此处,highFun1的参数需要一个函数,参数是Int类型,返回值也是Int类型,但是匿名函数不能明确给出返回值类型,只能通过最终的结果进行推测。
那么highFun3是highFun2利用匿名函数简洁了一下。
怎么调用?和上面一样,调用这个函数,它给返回一个函数,再给返回的函数传入值。
函数的参数是函数,函数的返回也是函数
此函数的参数是一个函数,返回也是一个函数
调用这个函数:
val restFun = highFun4(_+_,1000)
println(restFun(2000))
println(highFun5(10)(20))
注:highFun5(10)返回一个函数,函数的参数是Int,所以又加了个小括号里面是20。运行结果是30 。
柯里化函数 :可以理解为高阶函数的简化
将两个参数分开来写。
调用方法: println(klhFun(100)(200))
是对klhAllFun这种情况的高阶函数的简化版。
Scala字符串
在java中,字符串是由String来表示的,然而java里面的字符串是不可变的,如果想操作字符串,可以使用StringBuffer和StringBuilder。在scala里面也提供了这个类,同时scala也提供了java给提供的那些方法。
String
equals/equalsIgnoreCase/==
比较两个字符串对象的
值(区分大小写):用equals
值(不区分大小写):用equalsIgnoreCase
引用(地址):用==
split/substring
spilt:分词的方法,按照参数字符串进行分词,返回切割完成的String类型数组
substring:截取方法,给出截取的开始位置,截到最后。
str1.split(" ")
compare/compareToIgnoreCase
如果参数字符串等于此字符串,则返回值 0;
如果此字符串小于字符串参数,则返回一个小于 0 的值;
如果此字符串大于字符串参数,则返回一个大于 0 的值。
compareToIgnoreCase是不区分大小写的比较
println(str.compareToIgnoreCase(str1))
hashcode
获取字符串的哈希值
StringBuilder
SringBuilder是一个可变长度的字符串。可以通过append方法往里面追加数据。如果想把它变成String字符串,要使用toString方法。在StringBuilder里也提供了split,compare等方法。
关于String的操作有更加详细的整理请参照我上传的资源。
数组
普通数组
java里面的数组创建形式:new String[10];
scala里面创建数组:使用Array关键字
如果创建了一个Int类型的数组,那么初始值都是0。
String类型的数组,初始是null
Boolean类型的数组,初始是false
//创建长度为10的Int类型的数组
val nums = new Array[Int](10)
//两种遍历数组的方法
for(index <- 0 until nums.length){
nums(index) = index * index
}
//匿名函数参数类型可以自动推测
nums.foreach {x => println(x)}
//匿名函数的高级用法
nums.foreach {println}
数组的其他方法:
clone克隆出一个新的数组
update更新数组
add往数组里添数据
addString往数组里加字符串
contains返回为真表示该数组里有参数这个值
关于数组中其他方法,参见我上传的资源。
二维数组
在scala里泛型是中括号。
二维数组,泛型里还是个数组。创建二维数组的时候需要一层一层的创建数组。
遍历数组的简便写法:
secArray.foreach { array => array.foreach { println } }
List集合:list集合是一个不可变的集合
contains方法:判断是否包含
drop方法:删除前(参数)个,返回一个新的,原来的不变
reverse方法:反转(生成新的,原来不变)
take方法:取前几个
val list = List(1,2,3,4,5)
list.contains(6)
val dropList = list.drop(2)
dropList.foreach { println }
list.reverse
list.take(3).foreach(println)
val mapList1 = list.map { x=> x +"" }
mapList1.foreach { x=>println(x) }
println(list.exists { x => x > 300 })
println(list.mkString("\t"))
}
map函数:高阶函数,参数是函数,这个函数的参数是int类型返回值是B(用户自定义类型)
List类型调用map,它的返回值是一个新的List
exits:是否存在,返回布尔类型
关于List的方法的总结请看我上传的资源
以下是关于Map函数的分词示例:
//分词
val logList = List("hello bj","hello sh")
val mapList = logList.map { _.split(" ") }
mapList.foreach { x => x.foreach { println } }
以上方法打印出来的每一行都是一个数组,这个集合不是我们想要的。如何获得想要的集合?
flatMap方法 flat扁平 ,将集合压扁压平,数组里封装的元素跑到外面集合里面来了。
在map方法之上,加入扁平操作。
val flatMapList = logList.flatMap{_.split(" ")}
flatMapList.foreach { println }
函数名后面有时加小括号,有时加大括号,都是一样的。
创建不可变的List集合的方式
方式1:List(1,2,3)
方式2:Nil关键词:表示空List
如何往这个空List里添加一条元素?
使用::形式
val list = 2::1::Nil
//表示list这个集合中添加了2和1这两条元素
可变的List集合:ListBuffer
依赖于包:scala.collection.mutable
new这个对象时,在代表泛型的中括号之后可以加小括号,也可以不加(不加代表调用无参的构造函数)
往ListBuffer中添加数据
方法1:使用addString方法
传入参数StringBuilder,返回StringBuilder
方法2:使用+=方法
在上图中,往这个ListBuffer中添加了两个元素。使用这个可变集合的主要目的就是为了方便添加元素。
注意:虽然不可变的List里也提供add方法,也可以加进去,但是加进去之后就不是原来的集合了而是产生一个新的集合,集合里的元素是加进去之后的元素。那么加入n个元素就会产生n多个对象,产生对象过多,容易内存溢出。
拓展知识:研究垃圾回收(GC)机制
Minor GC主要是堆内存的年轻代满了,会清理年轻代的内存。
Full GC主要是堆内存的老年代满了。
GC部分知识后续会持续整理。
往ListBuffer中删除数据:使用-=
删除listBuffer中的hello元素
listBuffer.-=("hello")
关于listBuffer更详细的方法整合,参见我上传的list资源
Set集合:可以自动去重
创建Set的方式
去重测试:比如,创建set如下
val set = Set(1,1,2,3,2,5,6,7)
set.foreach{println}
当遍历的时候,会发现存入的是去重之后的数据(当然,它输出是无序的)
Set的其他方法
交集:intersect,&
val set1 = Set(1,2,3,4)
val set3 = set1.intersect(set2)
set3.foreach{println}
val set4 = set1.&(set2)
set4.foreach{println}
println("*******")
差集:diff,&~
val set1 = Set(1,2,3,4,4)
val set2 = Set(1,2,5)
set1.diff(set2).foreach { println }
set1.&~(set2).foreach { println }
子集:subsetOf
set1.subsetOf(set2)
取最大值:max
println(set1.max)
取最小值:min
println(set1.min)
转成数组:toArray/toList
set1.toArray.foreach{println}
set1.toList.foreach{println}
转成字符串:mkString("~")
集合所有元素作为字符串显示,参数为分隔符。
println(set1.mkString)
println(set1.mkString("\t"))
scala Set的其他方法
其他方法及其作用介绍已整理上传在博客资源。
Map集合
创建Map集合
形式1:Map(1 –>”bjsxt’)
形式2:Map((1,”bjsxt”))
注意:创建map时,相同的key被后面的相同的key顶替掉,只保留一个
val map = Map(
"1" -> "bjsxt",
2 -> "shsxt",
(3,"xasxt")
)
遍历Map集合
方式1:
①获取Map集合的key:返回Interable集合类型
val keys = map.keys
②得到迭代器keyiterator
val keyiterator = keys.iterator
③通过循环来遍历迭代器
while(keyiterator.hasNext){
val key =keyiterator.next()
println(key+"\t"+map.get(key).get)
}
注意:
Ⅰ这个循环里只能出现一次next(),否则会出现指向最后一个位置的时候,游标连跳两次,出现越界。
Ⅱmap.get(key)会返回一个Option对象。
get函数返回的是Option类型,Option类型有两个子类型,分别是Some类型和None类型。如果传入参数的key对应的键值对存在,那么返回的是Some对象。否则,返回None对象。
Ⅲ如果想拿到Some对象里面具体的值怎么拿?需要再.get一下。
Ⅳ如果是None对象,但是使用了.get方法强行取里面的值会发生什么?会报异常。异常是java.util.NoSuchElementException异常,表示没有匹配的元素。
为了避免这个异常,我们可以不使用get换成getOrElse
getOrElse:去集合中取数据,若不存在,返回默认值。
println(key+"\t"+map.get(4).getOrElse("default"))
方式2:for方式
for(k <- map)
println(k._1 + "\t" + k._2)
这里的k不是java中认为的Map中存放的Entry对象
而是二元组类型对象Tuple
一对小括号组成起来的形式就叫二元组
方式3:foreach方式
map本身封装了foreach方法,参数是一个函数
map.foreach(x=>println(x))//这里打印的是一个个二元组
map.foreach(x=>println(x._1 +"\t" +x._2))//分别把值取出来打印
合并Map:使用++
++ 使用方法:map1.++(map2) --map1中加入map2
使用举例:
Map的filter方法
留下符合要求的数据
Map的count方法
Map的count方法参数需要传入一个函数,函数返回值是布尔。意思就是,数一数满足传入函数中条件的元素一共有多少个。
Map的contains方法
contains方法是找map中是否包含某个key
println(map.contains(2))
Map的exist方法
exist方法是找符合条件的记录是否存在。
使用举例如下
println(map.exists(f =>{
f._2.equals("Angelababy")
}))
其他Map的方法总结已整理上传到本博客的资源。
Tuple元组
元组和list区别
List创建的时候指定了泛型,那么集合中必须是这个泛型的对象
元组中可以包含任意类型的元素,这些元素使用一对小括号来封装
创建元组
/*创建二元组*/
//创建方式:
val t2 = Tuple2(1,"hello")
//简写创建方式:
val tt2 = (1,"hello")
/*创建三元组*/
//创建方式:
val t3 = Tuple3(1,true,"hello")
//简写创建方式:
val t3 = (1,true,"hello")
一般我们创建的时候不加Tuple2或者Tuple3等关键词
因为关键词的创建形式最大只可以创建22元组
直接用小括号的话可以创建很大很大的Tuple
取元组中的值
我们使用._的形式来取元组中的值
取x元组的第一个值那么就使用x._1形式获得。
例:如何取三元组t3的第二个值?
val t3value2 = t3._2
遍历元组
遍历元组的方式是使用迭代器遍历
Ⅰ利用.productIterator声明一个相应元组的迭代器
Ⅱ利用while方法遍历迭代器
Ⅲ用.next()取数据
val tupleIterator = tt3.productIterator
while(tupleIterator.hasNext){
println(tupleIterator.next())
}
元组的方法
toString方法
示例:将元组tt3变成String格式
println(tt3.toString())
打印出来的结果是:(1,true,“hello”)
虽然看起来还是很像二元组,实际上它已经是字符串了。
swap交换方法
注意:只有二元组对象才会有这个方法
使用示例:
println(tuple2)
println(tuple2.swap)
输出之后发现后面的输出数据已发生反转。
trait
可以把trait理解为java里面的接口,但是它比java里的接口更加强大。
trait的特质
①可以实现方法
②可以定义任意的属性
举例1:trait中带属性带方法实现
继承的多个trait中如果有同名的方法和属性,必须要在类中使用“override”重新定义。
trait中不可以传参数
trait Read {
val readType = "Read"
val gender = "m"
def read(name:String){
println(name+" is reading")
}
}
trait Listen {
val listenType = "Listen"
val gender = "m"
def listen(name:String){
println(name + " is listenning")
}
}
class Person() extends Read with Listen{
override val gender = "f"
}
object test {
def main(args: Array[String]): Unit = {
val person = new Person()
person.read("zhangsan")
person.listen("lisi")
println(person.listenType)
println(person.readType)
println(person.gender)
}
}
举例2:trait中带方法不实现
object Lesson_Trait2 {
def main(args: Array[String]): Unit = {
val p1 = new Point(1,2)
val p2 = new Point(1,3)
println(p1.isEqule(p2))
println(p1.isNotEqule(p2))
}
}
trait Equle{
def isEqule(x:Any) :Boolean
def isNotEqule(x : Any) = {
!isEqule(x)
}
}
class Point(x:Int, y:Int) extends Equle {
val xx = x
val yy = y
def isEqule(p:Any) = {
p.isInstanceOf[Point] && p.asInstanceOf[Point].xx==xx
}
}
模式匹配match
一个模式匹配包含了一系列备选项,每个都开始于关键字 case。
每个备选项都包含了一个模式及一到多个表达式。箭头符号 => 隔开了模式和表达式。
/**
* 模式匹配 match case
* 1、值
* 2、类型匹配
* 在scala中模式匹配,一旦能匹配上第一个模式,那么接下来的模式都不会进行匹配了
*/
//模式匹配的时候,模式范围小的在最前面
def match_test(m:Any) = {
m match {
case 1 => println("first")
case m:Int => println("Int")
case _ => println("default")
}
}
样例类
样例类:使用case 来修饰的类就叫样例类
样例类和普通类有什么区别?
样例类默认帮你实现了toString,equals,copy和hashCode等方法。
样例类可以new, 也可以不用new
例:
val s1 = Student(1,1)
val s2 = Student(2,2)
val s3 = Student(3,3)
val s4 = Student(4,4)
val s5 = Student(5,5)
val sl = List(s1,s2,s3,s4,s5)
//样例类+match case
sl.foreach { x => x match {
case Student(1,1) => println("1 1")
case s:Student => println("Student" + s)
case _ => println("_______")
} }