史上最全的Scala知识点整理
第一章 变量及基本数据类型
1.1 注释
(1)单行注释: //
(2)多行注释:/* */
(3)文档注释:/** */
(4)格式调整:ctrl+alt+L
1.2 标识符命名规范
(1)以字母或者下划线开头,后接字母、数字、下划线
(2)以操作符开头,且只包含操作符(+ - * / # !等)
(3)第一种和第二种拼接,第一种在前,二者以下划线分隔
(4)用反引号``包括的任意字符串,即使是关键字(39个)也可以
1.3 变量
(1)基本语法:val | var 变量名[:变量类型] = 变量值
(2)声明变量时可不指定类型,自动类型推断;
(3)声明变量时必须指定初始值;
(4)val修饰的变量值/对象值不可以变,var修饰的变量值/对象值可以变;
1.4 字符串
(1)双引号 “ ” | 三引号 “ ” “ “ ” “
(2)插值表达式 s"string ${变量名/表达式}"
(3)占位符 %s 字符串 %f 浮点型 %d 整型
//插值表达式
val hello = "hello"
val world = "world"
val hello_world = s"${hello} ${world}"
println(hello_world)
//占位符 %s 字符串占位符, %f 浮点型占位符, %d 整数占位符
val name = "%s"
println(String.format(name,"hello"))
val str = "hello %s %f %d fighting"
println(str.format("jason",2.34,25))
//printf 格式化输出
printf("hello %s","world")
1.5 数据读取
//键盘输入
val scan = StdIn.readLine("请输入任意值:")
println(scan)
//文件读取
val source: BufferedSource = Source.fromFile("d:/aaa.txt", "utf-8")
println(source.getLines().toBuffer)
1.6 数据类型
1.6.1 概述
(1)Scala是完全面向对象的语言,Any为所有类的父类;
(2)AnyVal是数值类型,包括类似java中的基本数量类型、StringOps、Unit…
AnyRef是引用类型,String、类、集合…
Unit类表示无返回值,是数值类型,等同于void,实例为()
(3)NUll是所有引用类型的子类,其实例为null,当使用null作为变量初始值时不可以使用类型推断!
(4)Nothing是所有类的一个抽象子类,用来抛异常使用;
(5)其它数据类型说明基本与Java中描述一致。
1.6.2 类型转换
1)数值类型间转换
(1)符合自动类型提升原则,即从低精度自动提升为高精度;
(2)Byte、Short、Char互相运算时会先转为Int类型;
(3)类型强转需调用toXxx方法,a:Int=b.toInt
//低精度能够自动转成高精度
var a:Int = 100
var b:Long = a
println(b)
//高精度的不能自动转成低精度的,要想转换需要调用toXXX方法
var c:Double = 10.0
var d:Float = c.toFloat
println(d)
2)字符串与数值类型转换
/**
*数字转字符串:数字拼接空字符串
*字符串转数字:调用toXXX方法
*/
val e = 10
val s:String = e.toString
val a = "10.0"
//val b:Int = a.toInt 报错,因为“.”识别不清代表什么
val b:Double = a.toDouble
val c:Int = a.toDouble.toInt
1.7 运算符
与Java中运算符基本一致,Scala中没有 ++ – 三元运算符
第二章 流程控制
2.1 块表达式
//块表达式有返回值,返回值为{}中最后一个表达式的结果值
val block = {
println("hello ...")
println("霸王别姬")
val result = 1 + 1
result
} //运行结果:2
2.2 If判断
/*
java中if的用法:
1、单if
2、if-else
3、if-else if-..-else
scala中if用法与java完全一样,唯一不同的地方在于scala的if表达式有返回值
*/
val result1 = if( 10%2==0) {
println("hello")
val name = "string"
s"java ${name}"
}
2.3 For循环
2.3.1 Scala中方法调用的两种方式
//1.与java相同 对象名.方法名(形参)
1.to(10)
//2.可以忽略 . 对象名 方法名(形参)
1 to(10)
1 to 10 //只有一个参数可以省略括号
2.3.2 基本语法
//for循环基本语法: for(变量名 <- 数组/集合/表达式)
//1. to代表左闭右闭 until代表左闭右开
for(i <- 1 to 10) { println(i) }
for(i <- 1 until 10) { println(i) }
//2.指定步长
for(i <- 1 to 10 by 2) { println(i) }
//3.守卫 条件判断式 true则进入循环
for(i <- 1 to 10 if i % 2 == 0) { println(i) }
//4.引入变量 用“;”分割
for(i <- 1 to 10;j = i * i) { println(i + j) }
//5.嵌套循环 多个循环语句用“;”分割
for(i <- 1 to 10;j <- 1 until 4;k...) { println(i) }
2.3.3 循环返回值
//需要使用yield关键字
val list = for(i <- 1 to 10 if i%3=0) yield i
println(list) //Vector(3,6,9)
//可以将结果集转换为数据
println(list.toBuffer) //ArrayBuffer(3,6,9)
2.4 While循环
Scala的while循环、do while循环与java的完全一样
2.5 Switch
Scala中没有Switch case
2.6 中断循环
//scala中没有break和continue 因此中断循环需要使用抛异常的方式 通过Breaks封装类
//基本语法
import scala.util.control.Breaks._
breakable{ 循环体 条件判断语句 break()}
//breakale()指定结束循环位置,实际上为try catch
//当breakable()后的逻辑代码不止一行时需要使用{循环内容}
def breakable(op: => Unit) {
try {
op
} catch {
case ex: BreakControl =>
if (ex ne breakException) throw ex
}
}
//break()执行时结束循环
*/
def break(): Nothing = { throw breakException }
//案例1:类似于break的结束当前循环
import scala.util.control.Breaks._
breakable(
for(i <- 1 to 100){
if(i % 10 == 0) break()
println(i)
})
//案例2:类似于continue的结束当次循环
var i = 0
while(i < 100){
breakable({
if(i % 2 == 0) break()
println(i)})
i = i + 1
}
for(i <- 1 to 100){
breakable({
if(i % 10 == 0) break()
println(i)
})
}
第三章 面向函数编程
3.1 概念
面向对象:解决问题时,将问题拆解成为一个个的小问题(对象),分别解决对象关系(封装/继承/多态)
面向函数:编程关心的是问题的具体解决步骤(封装功能):入参和出参
Scala是一门完全面向对象的语言,同时也是一门完全面向函数的语言!!!
3.2 函数的定义
3.2.1 基本语法
//*def 方法名 ( 参数名:参数类型,...):返回值类型 = {方法体}
def add(x:Int,y:Int):Int = {x+y}
//*val 函数名 = (参数名:参数类型,...) => {函数体}
val sum=(x:Int,y:Int) => {x+y}
3.2.2 方法与函数
--方法与函数的区别:
(1) 方法如果定义在class中是可以重载的,函数不可以
(2) 方法保存在方法区,函数保存在堆空间中
(3) 方法是随着class的加载而加载的,函数是创建对象的时候加载的
--方法与函数的转换:
def function(x:Int,y:Int):Int = x+y
val fun = function _
3.2.3 参数与返回值
//1.多种组合类型
//有参 有返回值
def add1(x:Int,y:Int):Int = {x+y}
//无参 有返回值
def add2():String = {"hello jason"}
//有参 无返回值
def add3(x:Int,y:Int) = {println(x+y)}
//无参 无返回值
def add4() = {println("hello jason")}
/*2.可变形参
(1)放在形参列表的最后,因此只能有一个可变形参
(2)可变形参实际上为一个集合,通过遍历的方式传入每一个值
(3)可变形参中可以使用带名参数调用
(4)Java中可变参数可以传入数组,但是scala中不可以 */
def sum(a:String,b:String*):Unit = {println(s"a:${a},b:${b}")}
def fun(a:Int*):Unit = {println(a)}
fun(7,7,7) //WrappedArray(7,7,7)
def foreach(array:Int*):Int={
var sum:Int = 0
for (elem <- array) {sum = sum + elem} sum } //此时的array为一个集合
//scala中的可变形参无法传入数组,但是可以使用 :_* 进行转换
readline(getPath(7):_*)
//3.参数默认值
/**
*(1)当有多个参数时,有默认参数的要放在形参列表最后
*(2)默认参数与可变形参不可以结合使用
*(3)给默认参数赋值会将默认值覆盖掉
*/
def people(name:String,age:Int=18)={println(s"name:${name},age:${age}")}
people("jason") //name:jason,age:18
//4.带名参数 —— 在调用函数时,可指定参数进行赋值,此时有默认值的参数可以不在形参列表最后
def student(age:Int=1001,name:String):Unit={println(s"age:${age},name:${name}")}
student(name="jason") //age:1001,name:jason
3.3 至简原则
3.3.1 函数定义
def desc(name:String):String={
return s"${name} is good!"
}
//1.return: 有返回值类型时,将块中最后一个执行语句返回,因此return可省略
//如果有return,必须指定返回值类型;如果返回值类型为Unit,则return不起作用
def desc(name:String):String={
s"${name} is good!"
}
//2.返回值类型: 可以通过{}最后一个执行语句自动推断判断返回值类型
def desc(name:String)={
s"${name} is good!"
}
//3.{}: 函数体只有一行时,{}可以省略
def desc(name:String)= s"${name} is good!"
//4.(): 无参时,可以省略()
//说明:定义时省略(),调用时必须不加();
// 定义时无参(),调用时可加可不加()
def desc2()= "jason is good!"
desc2()/desc2
//5.=: 如果不需要返回值,=可以省略
def desc(a:String) {println(a)}
3.3.2 函数调用
def addfun(x:Int,y:Int,func:(Int,Int)=>Int) = {func(x,y)}
//调用函数
addfun(100,200,(x:Int,y:Int) => x+y )
//1.传入参数时可以省略参数类型
addfun(100,200,(x,y) => x+y )
//2.参数只调用了一次,可以使用 _ 代替
addfun(100,200,_+_)
/*补充说明:
(1)参数只能调用一次,且必须按照形参列表中的顺序进行调用
(2)参数只有一个,在函数体中直接返回,则不能用_ x=>x
(3)如果函数体中嵌套了表达式,且参数在函数体的表达式中被引用,则不能使用_
printMsg("wangwu",x=>println(x*10))
函数体中有(),而且x在函数体的()中构成了表达式,此时不能用_代替
printMsg("wangwu",x=>println(x))
可以用_代替,代替之后变成了printMsg("wangwu",println(_)) 此时单个_构不成表达式会跳出_
3.4 匿名函数
//匿名函数:没有def和函数名,将匿名函数作为参数传递给另一个函数
val 函数名=(参数名:参数类型...) => { ... }
val function = () => println("jason")
function()
3.5 柯里化&闭包
//1.柯里化: 有多个参数列表的方法称之为柯里化 —— 函数也是对象,因此对象.对象.方法必然需要柯里化
def 函数名(参数名:参数类型...)(参数名:参数类型...)... = { ... }
def add3(x:Int,y:Int) = {
val add2 = (a:Int,b:Int) => {
val add1 = (z:Int) => x+y+a+b+z
add1
}
add2
}
println(add3(1,2)(3,4)(5)) //15
//2.闭包: 函数体中使用了不属于函数本身作用域的参数的函数称之为闭包
val y = 10
val func = (x:Int) => {
val name ="jason"
x+y
}
3.6 递归函数
//Scala中使用递归函数的两个条件:1.有终止条件 2.函数必须明确定义返回值
def sum(n:Int):Int={
if(n==1){
1
}else{
n+sum(n-1)
}
}
3.7 懒值加载
/*懒值加载:
*通过lazy标识的变量不会立即初始化,只有在首次对变量取值的时候才会初始化
*lazy加载能够节省内存空间
*/
val sum=(a:Int,b:Int)=>println(a+b)
lazy result = sum(10,20) //不会初始化,调用时才会初始化
println(result)
3.8 高阶函数 – 套娃
//高阶函数:参数类型或者返回值类型为函数的方法/函数
val fun = (x:Int,y:Int,fun2:(Int,Int) => Int) => {fun2( x+y )}
//调用时简写见 3.3.2
/*案例1. 定义一个高阶函数,根据指定的逻辑对数组中的每个元素进行操作
* val arr =Array("hello","word","python","scala")
* 比如将数组的每个元素转成字符串长度[还可能进行其他操作]
* val result = Array(5,4,6,5)
*/
object MapTest {
def main(args: Array[String]): Unit = {
val arr = Array("hello","word","python","scala")
//todo 2.定义函数2,处理单个元素的逻辑
val fun=(a:String)=>a.length
//todo 3.调用函数,map-fun
println(map(arr, fun).toBuffer)
println(map2(arr, fun).toBuffer)
//直接传入函数
println(map(arr, (a:String)=>a.length).toBuffer)
println(map2(arr, (a:String)=>a.length).toBuffer)
//参数类型、括号、参数均可以省略
println(map(arr,_.length).toBuffer)
println(map2(arr,_.length).toBuffer)
}
//todo 1.定义函数,处理数组逻辑,遍历数组元素
val map=(arr:Array[String],fun:(String)=>Int) => for(a <- arr) yield fun(a)
def map2(arr:Array[String],fun:(String)=>Int)={for(a <- arr) yield fun(a)}
/**
* 案例2. 定义高阶函数,根据指定的逻辑对数组进行统计
* val arr = Array(5,4,6,5)
* 比如统计总和[还可能统计总乘积等等]
* val sum = 20
*
* 思路:
* 遍历数组,第一个元素与第二个元素处理的结果作为下一次的第一个元素传出
*/
object ReduceTest {
def main(args: Array[String]): Unit = {
val arr = Array(5,4,6,5)
val fun=(muti:Int,ele:Int) => muti*ele
//调用函数
println(reduce(arr, fun))
//直接传入函数 简化
println(reduce(arr, (muti:Int,ele:Int) => muti*ele))
println(reduce(arr,_*_))
println(reduce(arr,_+_))
}
//将数组中每个元素取出,传入到fun函数中调用
def reduce(arr:Array[Int],fun:(Int,Int)=>Int):Int = {
var muti:Int = arr(0)
for(index <- 1 until arr.length){
var ele =arr(index)
muti=fun(muti,ele)
}
muti
}
}
/**
* 案例3. 定义一个高阶函数,根据指定的规则对数据进行过滤
* val arr = Array(5,4,6,5)
* 比如只需要偶数数据的结果
* val result = Array(4,6)
*/
object FilterTest {
def main(args: Array[String]): Unit = {
val arr = Array(5,4,6,5)
println(filter(arr))
val fun=(a:Int)=> a % 2 == 0
println(filter2(arr, fun).toBuffer)
}
//如果yield在返回内容前,会将不符合条件时的空值unit也返回
val filter2=(arr:Array[Int],fun:Int=>Boolean)=>{
//for(ele <- arr) yield if(fun(ele)) ele
//将判断语句提为守卫,确定符合条件时才返回值
for(ele <- arr if (fun(ele))) yield ele
}
def filter (arr:Array[Int]):util.ArrayList[Int]={
val list = new util.ArrayList[Int]()
for(index <- 0 until arr.length){
if(arr(index)%2==0){
list.add(arr(index))
}
}
list
}
}
/**
* 案例4. 定义一个高级函数,按照指定的规则对数据进行分组
* val arr = Array("zhangsan 20 shenzhen","lisi 30 shanghai","wangwu 30 shenzhen","zhaoliu 20 shenzhen")
* 比如按照地区进行分区结果
* shenzhen的结果: List["zhangsan 20 shenzhen","wangwu 30 shenzhen","zhaoliu 20 shenzhen"]
* shanghai的结果: List["lisi 30 shanghai"]
*
* 思路:
* 1.取出数组中每个元素并返回
* 2.分组函数传入字符元素,及索引位置,以进行分组
*/
object GroupByTest {
def main(args: Array[String]): Unit = {
val arr = Array("zhangsan 20 shenzhen","lisi 30 shanghai","wangwu 30 shenzhen","zhaoliu 20 shenzhen")
val fun=(ele:String)=>{ele.split(" ")(2)}
println(groupby(arr, fun))
println(groupby(arr, (s:String)=>s.split(" ")(2)))
println(groupby(arr, _.split(" ")(1)))
}
def groupby(arr:Array[String],fun:String =>String)={
//3.定义一个存放key的数组,用以区分不同key
val result=new util.HashMap[String,util.List[String]]()
//1.遍历获取指定元素
for(ele <- arr){
val key = fun(ele)
//2.判定字符是否包含指定key
if(result.containsKey(key)){
val list = result.get(key)
list.add(ele)
}else{
val list = new util.ArrayList[String]()
list.add(ele)
result.put(key,list)
}
}
result
}
}
第四章 面向对象编程
4.1 包-Package
--1.包的作用
(1) 便于管理类
(2) 便于对同名类进行区分
(3) 对类的权限进行限制[private 包名]
--2.包的声明
"域名反写 + 项目名 + 业务线 + 模块"
(1) 在.scala文件第一行中通过package关键字来声明包
(2) 在.scala文件中通过package关键字来声明包,可以在包中定义变量/class文件(在项目结构中不可见,需要在target文件中进行查看)
--3.包的导入
(1) 导入包下的所有类: import 包名._
(2) 导入包下某个类: import 包名.类名
(3) 一次导入包下多个类: import 包名.{类名1,类名2}
(4) 导入包下类的同时给类起别名: import 包名.{类名=> 别名}
(5) 导入包下除开某个类的所有类: import 包名.{类名=>_ ,_}
"Scala是一门完全面向对象的编程语言"
--4.包对象
语法:pachage object 包名
包对象中定义的非private的属性、方法、函数可以在包中任何地方使用[子包中不可以]
--5.Scala中包的声明说明:
(1) 在同一个源码文件中,可以多次声明,声明的类在最后的包中,源码中的类所在的位置不需要和包的路径相同
(2) scala中没有的语法都可以进行嵌套
package可以使用{},在{}里面声明的类在此包中,{}之外声明的类不在此包中
(3) scala中可以声明父包和子包,父包中的类,子类可以直接访问
(4) scala中可以声明package类,但是无法声明变量和函数
(5) scala为了弥补包的不足,使用了包对象的概念,在包对象中可以声明属性和函数
4.2 类-class
--scala中有class和object修饰的类
--编译时区别:
1.object: 在编译时会生成两个类文件,一个是当前类文件,一个是单例类文件
例:Scala01_Class.class, Scala01_Class$.class
2.class: 在编译时只会生成当前类文件
例:Scala01_Class.class
--编译时修饰区别:
1.object: 由于scala中没有static,因此使用object修饰伴随着普通类而生成的单例对象,object中的属性和方法可以通过类名直接调用,类似于java中的静态类
2.class: 修饰普通的类
--object声明的类:伴生对象
--class声明的类:伴生类
--伴生类与伴生对象的声明
--1.声明条件:
1.class与object在同一个.scala文件中
2.class与object的名称要一样
--2.声明方法:
1.object+ 类名 声明伴生对象,直接通过类名调用属性和方法
2.class + 类名 声明伴生类,必须new 对象调用属性和方法
--3.特性:
1.伴生类和伴生对象可以互相访问私有属性
2.伴生对象的apply()方法创建伴生类对象
--4.说明(至简原则):
1.声明类时{}中没有任何东西,{}可以省略
2.new 对象时,不需要传入参数,则()可以省略
//伴生类 @BeanProperty可以提供属性的get/set方法
class People(@BeanProperty var name:String,@BeanProperty var age:Int){
private val address:String="shenzhen"
People.sum(13,34) //可以调用伴生对象的私有属性和方法
}
object People{
private def sum(x:Int,y:Int)=println(x+y)
val people = new People("jason", 25)
people.address //可以调用伴生类的私有属性和方法
//定义apply方法创建伴生类对象
def apply(name:String,age:Int) = new People(name,age)
}
//通过apply方法创建对象
People.apply("jason",25)
//因scala可以自动识别apply,因此apply可以省略
People("jason",25)
4.3 属性-field(封装)
//1.声明语法
[权限修饰符] val/var 属性名:属性类型 = 属性值
private val name:String = "jason"
//2.属性初始值
val:类似于final修饰的属性,不可以修改,因此必须有初始值
var:可以使用默认初始值进行初始化*/
val name:String = "jason"
var age:Int = _
"各类型的初始化值:String_null,Int_0,Double_0.0,Char_'\u0000' 0"
//3.关于封装:
(1) 封装:属性私有,提供公共的set/get方法
(2) scala的属性在定义后之后,会默认的提供set/get方法
--默认的get方法的方法名:属性名
--默认的set方法的方法名:属性名_=
(3) scala为了兼容java,提供了@BeanProperty注解——可以自动为属性生成get/set方法
--@BeanProperty不能标注在私有的private 属性上
4.4 方法/函数
//1.方法声明
[权限修饰符] def 方法名(参数名:参数类型,..):返回值类型 = {方法体}
//2.函数声明
[权限修饰符] val 函数名 = (参数名:参数类型,..) => {函数体}
4.5 对象
--1.对象构建说明:
val修饰的对象不可以改变地址值,可以改变对象属性的值
var修饰的对象可以改变地址值和对象属性的值
--2.创建对象的几种方式:
(1) new Class : val person = new Person
(2) apply: Array.appply()/ Array()
(3) classof
(4) 反序列化
(5) clone
4.6 构造器-construct
//1.scala中构造器有主构造器和辅助构造器两种
构造器用来创建对象,在继承关系中,子类创建对象时会先调用父类构造器
//2.scala主构造器:
(1) 语法:
[private/..] [val/var] 属性名:属性类型[ = 默认值]
public class Person(private val name:String="jason"){}
(2) 说明:
var/val修饰的属性可以在class外部使用[非private修饰]
不带var/val修饰的属性只可以在class内部使用
//3.scala辅助构造器:
(1) 语法:
def this(参数名:参数类型,...) = {}
(2) 说明:
在辅助构造器的"第一行"必须先调用其它构造器,且不能形成死循环
辅助构造器不能直接构建对象,必须直接或者间接地调用"主构造器"
4.7 继承-extends
//1.scala中的继承与java中继承语法一样
语法: 子类 extends 父类
//2.说明:
a."final修饰的class"不能被继承
b.父类"private修饰的属性和方法"不能被继承
c.子类可以通过"override关键字"来重写属性/方法/函数
d.子类重写父类属性时,只能重写"val修饰的属性"
4.8 抽象-abstract
//1.关键字 abstract 可以修饰类/属性/方法
a. abstract class Person{} --抽象类:不能创建对象的类
b. var|val name:String --抽象属性:没有初始值的属性
c. def sum(a:Int,b:Int):Int --抽象方法:没有方法体的方法
//2.重写&继承
a.抽象子类必须重写抽象父类中的所有抽象方法和抽象属性 才能实例化
b.重写非抽象方法必须加"override"关键字,重写抽象方法可以不加
c.子类调用父类的方法需要使用"super"关键字
d.抽象属性重写只支持val 修饰的属性
//3.匿名对象
val obj = new 抽象类{
重写抽象的方法/属性
}
4.9 特质-trait
4.9.1 Trait说明
- Scala语言中,trait实际上用来代替java中接口
- Scala中的类也是 单继承多实现(trait)
- trait中可以定义抽象属性和抽象方法,也可以定义具体的属性和具体的方法
4.9.2 基本语法
//1.trait + 特质名{ 特质体 } 定义,可以定义抽象属性/方法和具体属性/方法
trait Demo{
//具体属性
val name:String="jason"
//抽象属性
val height:Int
//具体方法
def sum(x:Int,y:Int):Int=x+y
//抽象方法
def add(a:Int,b:Int):Unit
}
//2.实现trait需重写抽象属性/方法才可实例化
class Test extends Test1 with Test2{
override val height: Int = 12
override def add(a: Int, b: Int): Unit = println(a+b)
}
//3.一个类可以 "混入" 多个特质 第一个实现/继承用extends,其它的用with
class Sub extends Parent1 with Parent2 with Parent3...
4.9.3 动态混入 mixin
//对象混入,让某个具体的对象实现trait特质的属性和方法
val people = new People with Test1 {
override val height: Int = 180
override def add(a: Int, b: Int): Unit = println(a+b)
}
//对象实现trait后即可使用trait中具体的属性和方法
println("people name:" + people.name)
4.9.4 特质叠加
//特质叠加:将混入的多个trait中的冲突方法叠加起来
//问题:【钻石问题】当一个类实现了多个trait,且这多个trait中都有一个同名同参的方法,则子类直接调用会报错...
//解决方案1:子类重写父trait的同名同参方法
--如果在方法体中super调用父trait的方法,则默认调用"最后一个with的方法体"
trait Test1{
def demo(a:Int,b:Int):Unit=println("我是a+b:"+(a+b))
}
trait Test2{
def demo(a:Int,b:Int):Unit=println("我是a*b:"+(a*b))
}
trait Test3{
def demo(a:Int,b:Int):Unit=println("我是a/b:"+(a/b))
}
//子类重写此同名同参方法,如果在方法体中super调用父trait的方法,则默认调用最后一个with的方法体
class Demo extends Test1 with Test2 with Test3{
override def demo(a:Int,b:Int):Unit={
//println("重写后的:"+(a+b))
super.demo(a,b)} //调用Test3的(a/b)
}
//解决方案2:所有父trait同时继承一个爷trait,在爷trait中声明同名同参的方法[方法提取]
--子类调用该方法时默认调用的最后一个trait
trait Grandpa{
def sum(a:Int,b:Int):Unit=println("a+b:"+(a+b))
}
trait Parent1 extends Grandpa{
override def sum(a:Int, b:Int):Unit=println("a*b:"+(a*b))
}
trait Parent2 extends Grandpa{
override def sum(a:Int, b:Int):Unit=println("a/b:"+(a/b))
}
//子类实现父trait时,默认调用的方法是最后一个trait的
class Son extends Parent1 with Parent2 //Parent2的(a/b)
//*特质叠加的执行顺序【按照继承顺序从右向左依次执行】
举例:声明子父类关系的特质,子类实现特质对象
- trait Grandpa
- trait Parent1 extends Grandpa
- trait Parent2 extends Grandpa
class Son extends Parent1 with Parent2
/*1. 子类Son混入多个特质时,scala会对所有的特质及其父特质按照一定的顺序进行排序并依次叠加
*2. 子类Son的叠加顺序为: Son -> Parent2 -> Parent1 -> Grandpa
*3. 因此,当在子类中使用super直接调用父trait方法时,就近调用Parent2的方法
*4. 如果子类想指定调用某个父trait的方法,可以使用:super[Parent1].sum()*/
class Son extends Parent1 with Parent2{
override def sum(a: Int, b: Int): Unit =
super[Parent1].sum(a, b)
}
//*特质叠加的初始化顺序【按照继承顺序从左向右依次执行】
举例:声明子父类关系的特质,子类实现特质对象
- trait Grandpa
- trait Parent1 extends Grandpa
- trait Parent2 extends Grandpa
class Son extends Parent1 with Parent2
--特质对象初始化顺序为:Grandpa -> Parent1 -> Parent2 -> Son
4.9.5 特质子类型
-- this:Serializable[接口名] => //表示实现该trait必须实现或者继承该指定[接口名]
//需求:编辑一个trait,使其它子类继承该trait后实现自己写入磁盘的功能
object SelfObject {
trait IOObject{
this:Serializable => //提示实现该trait的子类必须实现serializable接口
//定义读方法
def read()={
val ois = new ObjectInputStream(new FileInputStream("d:/abc.txt"))
val obj = ois.readObject()
ois.close()
obj
}
//定义写方法
def write()={
val oos = new ObjectOutputStream(new FileOutputStream("d:/abc.txt"))
oos.writeObject(this)
oos.flush()
oos.close()
}
}
class Person(val name:String,var age:Int) extends IOObject with Serializable
def main(args: Array[String]): Unit = {
val person = new Person("jason",25)
person.write()
}
}
4.10 扩展
4.10.1 类型检查和转换
--1.类型判断
java中判断类型: 对象 instanceof B
scala中判断类型: 对象.isInstanceOf[类名]
--2.类型强转
java中强转: (类型)对象
scala的强转: 对象.asInstanceOf[类名]
--3.获取对象的class
java获取对象的class形式: 对象.getClass
scala获取对象的class形式: 对象.getClass
--4.获取类的class
java中获取类的class形式: 类名.class
scala中获取类的class形式: classOf[类名]
*4.10.2 枚举类与应用类
//1.枚举类 需要继承Enumeration
object Color extends Enumeration {
val RED = Value(1, "red")
val YELLOW = Value(2, "yellow")
val BLUE = Value(3, "blue")
}
//2.应用类 需要继承App 官方提示有BUG不建议使用
object Test20 extends App {
println("App 有Bug喔!");
}
*4.10.3 Type
//type关键字可以给数据类型起别名,在之后的调用中可以直接使用别名
--1.语法: type 别名 = 类名
--2.案例:
type S = String //给String起别名为S
var name : S = "jason" //使用时可以直接使用别名S
def say() : S = "hello jason"
第五章 集合
5.1 概述
--1.Scala中所有的集合扩展自【trait Iterable】特质,分为三大类:序列Seq、集Set、映射Map
--2.大部分集合可以分为可变集合和不可变集合两类
scala.collection.immutable --不可变
scala.collection.mutable --可变
不可变集合:集合的长度不可变,增/删时会创建一个新的集合,原集合不变
可变集合:集合的长度可变,增/删时可以在原集合基础上,也可以创建新的集合
--3.关于集合增/删元素的说明
val arr1 = Array[Int](1,2,3,4,5)
(1) +/++ | -/-- => +/-代表对单个元素的增删,++/--代表对一个集合的增删
arr1.:+(3) => arr2(1,2,3,4,5,3)
(2) 前:/后: +:/:+ => 前:代表新增的元素放在原集合最后,后:代表新增的元素放在原集合的前面
arr1.+:(0) => arr2(0,1,2,3,4,5)
(3) +=/-=/++=... => 在原集合的基础上进行增删操作
arr1.+=(0) => arr1(1,2,3,4,5,0)
5.2 Array(有序,可重复)
5.2.1 不可变Array
//1.创建 import scala.collection.mutable
(1) new Array[元素类型](数组长度)
(2) Array[.apply][元素类型](初始元素,初始元素...)
val arr1 = Array[Int](1,2,3)
val arr2 = new Array[Int](10)
//2.查 array(角标)
println(arr1(2)) //3
println(arr1.toBuffer) //ArrayBuffer(1,2,3)
//3.增 +: | :+ | ++ | ++:
val arr3 = arr1.+:(1) //(1,1,2,3)
val arr4 = arr1.++(Array(0,5,7)) //(1,2,3,0,5,7)
//4.改 array(角标) = 值 原集合更改
arr1(0) = 7 //(7,2,3)
//5.删 没有
//6.遍历
for(i <- arr1) println(i)
//7.可变数组与不可变数组的转换
val arr5 = arr1.toBuffer
val arr6 = arr5.toArray
5.2.2 可变ArrayBuffer
//1.创建 import scala.collection.mutable.ArrayBuffer
(1) new ArrayBuffer[元素类型](数组长度)
(2) ArrayBuffer[.apply][元素类型](初始元素,初始元素...)
val arr1 = ArrayBuffer[Int](1,2,3)
val arr2 = new ArrayBuffer[Int](10)
//2.查 array(角标)
println(arr1(2)) //3
println(arr1) //ArrayBuffer(1,2,3)
//3.新集合增 +: | :+ | ++ | ++: |
// 原集合增 +=: | += | ++= | ++=:
val arr3 = arr1.+:(1) //(1,1,2,3)
val arr4 = arr1.++(Array(0,5,7)) //(1,2,3,0,5,7)
//4.改 array(角标) = 值 原集合更改
arr1(0) = 7 //(7,2,3)
//5.删 - | -- | -= | --=
val arr5 = arr1.--(Array(0,2,4)) //删除集合中没有的元素不会报错
arr1.-=(3)
//6.遍历
for(i <- arr1) println(i)
//7.可变数组与不可变数组的转换
val arr5 = arr1.toBuffer
val arr6 = arr5.toArray
5.3 List(有序,可重复)
5.3.1 不可变 List
//1.创建 import scala.collection.mutable
--1. List[.apply][元素类型](初始元素,初始元素...)
--2. List[元素类型] = Nil --创建空的list(必须指定元素类型)
val list1 = List[Int](2,4,6,8)
val list0:List[Int] = Nil
//2.查 list(角标)
println(list1(2)) //6
println(list1) //List(2,4,6,8)
//3.增 +: | :+ | ++ | ++: | :: | :::
--1.添加单个元素
val list2 = 0 :: list1
--2.连续添加多个元素,"最后必须是一个集合接收"
val list3 = 0 :: (1, 3) :: (4, 6) :: list1 //此时(1,3)是一个元组
--3.可以使用Nil接收元素
val list4 = 0 :: 1 :: 2 :: Nil //(0,1,2)
//4.改 updated(a,n) 将角标为a的元素值改为n,并创建一个新的集合接收
val list5 = list1.updated(1,10) //(2,10,6,8)
//list4(1)=2 不能通过这种方式更改
//5.删 --没有
//6.遍历
for(i <- list1) println(i)
//7.可变集合与不可变集合的转换
val list6 = list1.toBuffer
val list7 = list6.toArray
5.3.2 可变ListBuffer
//1.创建 import scala.collection.mutable.ListBuffer --不能使用Nil
--1. ListBuffer[.apply][元素类型](初始元素,初始元素...)
--2. new ListBuffer[元素类型]()
val list1 = ListBuffer[Int](2,4,6,8)
val list0 = new ListBuffer[Int]()
//2.查 list(角标)
println(list1(2)) //6
println(list1) //List(2,4,6,8)
//3.新集合增 +: | :+ | ++ | ++: |
// 原集合增 +=: | += | ++= | ++=: 没有:: :::方法
val list2 = list1.++(List(2,4,6))
//4.改
--1.list(n) = a 在原集合上修改
list1(0) = 5
--2.updated(n,a) 修改并创建新集合
val list3 = list1.updated(0,3)
--3.update(n,a) 在原集合上修改
list1.update(2,3)
//5.删 - | -- | -= | --=
list1.--=(3)
//6.遍历
for(i <- list1) println(i)
//7.可变集合与不可变集合的转换
val list6 = list1.toBuffer
val list7 = list6.toArray
5.4 Set(无序,不可重复)
5.4.1 不可变Set
//1.创建 Set[.apply][元素类型](初始元素,初始元素...)
val set = Set[Int](1,2,3,4,5,6,7)
//2.增 + | ++ | ++: 因元素为无序的,因此只需使用+|++即可
val set2 = set.+(20)
val set3 = set.++(List(10,20,30)) //添加已有元素时不会报错,也不会添加成功
//3.查
--1.set(n) 查询set中是否有元素n
println(set(0)) //false
--2.println(set) //Set(5, 1, 6, 2, 7, 3, 4)
//4.改 --没有
//5.删 - | --
val set4 = set.-(4)
val set5 = set.--(List(2,3))
//6.遍历
for(i <- set) println(i)
//7.可变集合与不可变集合的转换 --没有
5.4.2 可变Set
//1.创建 import scala.collection.mutable
--mutable.Set[.apply][元素类型](初始元素,初始元素...)
val set = Set[Int](1,2,3,4,5)
//2.增 元素无序,不可重复
--1. + | ++ | ++: | += | ++=
val set2 = set.+(20)
val set3 = set.++(List(10,20,30)) //添加已有元素时不会报错,也不会添加成功
set.+=(7)
set.++=(List(3,5,7))
--2.update(n,Boolean) true增加 false删除
set.update(7,false)
//3.查
--1.set(n) 查询set中是否有元素n
println(set(0)) //false
--2.println(set) //Set(5, 1, 6, 2, 7, 3, 4)
//4.改 --没有
//5.删 - | -- | -= | --=
val set4 = set.-(4)
val set5 = set.--(List(2,3))
set.-=(4)
set.--=(List(2,3))
//6.遍历
for(i <- set) println(i)
//7.可变集合与不可变集合的转换 --没有
5.5 Map(K-V键值对)
5.5.1 不可变Map
//1.不可变Map为有序的(按添加顺序有序)
//2.创建 即多个元组的集合
--1.Map[K的类型,V的类型]((K,V),(K,V),...)
val map1 = Map[String,Int](("zhangsan",20),("lisi",30),("wangwu",40))
--2.Map[K的类型,V的类型](K -> V,K -> V,...)
val map2 = Map[String,Int]("zhangsan"->20,"lisi"->30,"wangwu"->40)
//3.增 + | ++ | ++:
val map3 = map1.+(("zhaoliu",30)) //单个添加按添加顺序排序
val map4 = map1.++(Map("aa"->20,"bb"->30)) //添加map顺序 ?
//4.查
--a.直接使用 "map(key)" 的方式获取,如果key找不到会报错
println(map4("scala")) //10
--b.使用 "map.get(key)" 的方式获取,返回结果为 some(value),如果key找不到返回None
println(map4.get("scala")) //Some(10)
println(map4.get("scala1")) //None
* Option: 【提醒外部值有可能为空,提供外部进行处理】
* Some: 代表非空,数据封装在Some中
* None: 代表空值
--c.使用 "map.getOrElse(key,默认值)" 的方式获取,如果key找不到返回默认值
println(map4.getOrElse("scala1", 100)) //100
//5.改
--updated(key,n) 将key对应的value值改为n
val map5 = map1.updated("zhangsan", 30)
//6.删 - | --
val map6 = map4.-("scala")
val map7 = map4.--(List("scala", "java"))
//7.遍历
--a.全部遍历
for (e <- map4) println(e._1,e._2)
--b.获取所有keys
for(e <- map4.keys) println(e)
--c.获取所有values
for(e <- map4.values) println(e)
5.5.2 可变Map
import scala.collection.mutable
//1.可变Map为无序的
//2.创建 即多个元组的集合
--1. mutable.Map[K的类型,V的类型]((K,V),(K,V))
val map1 = Map[String,Int](("jason",25),("qiang",20))
--2. mutable.Map[K的类型,V的类型](K -> V,K -> V)
val map2 = Map[String,Int]("jason"->25,"qiang"->20)
//3.增 + | += | ++ | ++: | ++= | put
val map3 = map1.+(("yan",18))
val map4 = map1.++(Map("aa"->20,"bb"->30))
map1.++=(Map("aa"->20,"bb"->30))
map1.put("cc",18) //put添加的是两个变量充当k v
//4.查
--a.直接使用 "map(key)" 的方式获取,如果key找不到会报错
println(map("scala")) //10
--b.使用 "map.get(key)" 的方式获取,返回结果为 some(value),如果key找不到返回None
println(map.get("scala")) //Some(10)
println(map.get("scala1")) //None
* Option: 【提醒外部值有可能为空,提供外部进行处理】
* Some: 代表非空,数据封装在Some中
* None: 代表空值
--c.使用 "map.getOrElse(key,默认值)" 的方式获取,如果key找不到返回默认值
println(map4.getOrElse("scala1", 100)) //100
//5.改
--a. map(key) = value
map("key")=100
--b. update(key,n) 在原map中,将key对应的value值改为n
map.update("jason",1000)
--c. updated(key,n) 将key对应的value值改为n,存到新map中
val map5 = map.updated("jason", 30)
//6.删 - | -- | -= | --= | remove
val map6 = map.-("scala")
val map7 = map.--(List("scala", "java"))
map.-=("jason")
map.--=(List("jason","aa")
map.remove("bb")
//7.遍历
--a.全部遍历
for (e <- map) println(e._1,e._2)
--b.获取所有keys
for(e <- map.keys) println(e)
--c.获取所有values
for(e <- map.values) println(e)
5.6 Tuple
//1.说明
(1) 在()内,用","分割,形成一个元组
(2) 一个元组内最多有22个元素
(3) 元组一旦创建,长度和内容都不可以修改
(4) 元组内各元素类型可以不同
//2.创建 (a,b,(c1,c2...)...)
val t1 = ("jason",25,"帅")
val t2 = (1,2,3,(1,2,(3,4,5)))
//3.查 ._n n为角标,从1开始
println(t2._4._3._3) //5
//4.集合嵌套元组
val array: Array[(String, Int, Double)] = Array[(String, Int, Double)](
("jason", 25, 180.0),
("qiang", 26, 178.8),
("yan", 26, 199.9))
for(a <- array) println(a)
//案例:对象封装--样例类
//改善前案例
val data = Array[(String,(String,(String,Int)))](
("深圳",("宝安区",("张三",18))),
("深圳",("宝安区",("李四",18))),
("深圳",("宝安区",("王五",18))),
("深圳",("宝安区",("赵六",18)))
)
for(i<- data) println(i._2._2._1)
//获取数据时很难通过表达式了解真正的含义,因此,可采用样例类的方式
//改善后案例
case class City(name:String,area:Area)
case class Area(name:String,person:Person)
case class Person(name:String,age:Int)
val data2 = Array[School](
City("深圳",Area("宝安区",Person("张三",20))),
City("深圳",Area("宝安区",Person("李四",20))),
City("深圳",Area("宝安区",Person("王五",20))),
City("深圳",Area("宝安区",Person("赵六",20)))
)
for( t <- data2) println(t.area.person.name)
5.7 常用函数
5.7.1 基本属性
//集合的基本属性 集合->基本类型
--length -> Int "获取集合长度"
list.length --3
--size -> Int "获取集合长度"
list.size --3
--isEmpty -> Boolean "判断集合是否为空"
list.isEmpty --true
--contains(n) -> Int "判断集合是否包含元素n"
list.contains("a") --false
--mkString("x") -> String "以x为分割符将集合转为字符串"
list.mkString("-") --1-5-6-8
5.7.2 衍生集合
//集合的衍生集合 集合->集合 --->可变集合在原集合处理,不可变集合需生成新集合
--distinct -> List "集合元素去重"
val list2 = list.distinct
--drop(n) -> List "删除集合前n个元素"
val list2 = list.drop(2)
--dropRight(n) -> List "删除集合后n个元素"
val list2 = list.dropRight(2)
--head -> 元素类型 "返回集合第1个元素"
val head: String = list.head
--last -> 元素类型 "返回集合最后1个元素"
val last: String = list.last
--init -> List "获取除最后1个元素的所有元素集合"
val list2 = list.init
--tail -> List "获取除第1个元素的所有元素集合"
val list2 = list.tail
--reverse -> List "反转"
val list2 = list.reverse
--take(n) -> List "获取前n个元素的所有元素集合"
val list2 = list.take(2)
--takeRight(n) -> List "获取后n个元素的所有元素集合"
val list2 = list.takeRight(2)
--zip(list2) -> List "拉链,将两个集合合并为元组元素存放在新集合中,长度不一以短为准"
val words = List("aa","bb","cc","dd")
val counts = List(10,20,30)
val list = words.zip(counts) //List((aa,10), (bb,20), (cc,30))
--unzip(list2) -> List(List) "反拉链 生成元组,元组中有两个集合"
val list = List((aa,10), (bb,20), (cc,30))
val list2 = list.unzip //(List(aa, bb, cc),List(10, 20, 30))
--intersect -> List "交集"
val list3 = list1.intersect(list2)
--diff -> List "差集 A有B没有的"
val list3 = list1.diff(list2)
--union -> List "并集"
val list3 = list1.union(list2)
--slice(a,b) -> List "获取子集合,对应角标[a,b)"
val list2 = list1.slice(1,3)
--sliding(a,b) -> List "开窗,a为开窗大小,b为滑动长度"
val list = List(10,20,30,40,50,60)
val list2 = list.sliding(3,2)
println(list2.toBuffer)
//ArrayBuffer(List(10, 20, 30), List(30, 40, 50), List(50, 60))
5.7.3 初级函数
//1.基本聚合函数
--max -> Int "获取元素最大值,按字典序排"
val max = list.max
--min -> Int "获取元素最小值,按字典序排"
val min = list.min
--sum -> Int "获取元素平均值"
val sum = list.sum
--product -> Int "获取元素总乘积"
val product = list.product
//2.排序
--sorted -> List "按照元素排序,默认升序排序"
println(list.sorted) //升序
println(list.sorted.reverse) //降序
--sortBy(fun(x:集合类型 => Any)) -> List "指定排序字段,默认升序排序"
println(list.sortBy(_._2))
--sortWith(fun(x,y => Boolean)) -> List "指定排序规则 小于为升序,大于为降序"
println(list.sortWith((a, b) => a > b))
5.7.4 高级函数
//1.map 对集合的每个元素进行操作,返回个数与原集合个数相同
--map(fun(x:元素类型 => Any)) --> 一对一
//2.filter 过滤,针对集合的每个元素,指定判断条件,true的返回
--filter(fun(x:元素类型 => Boolean))
//3.foreach 对集合的每个元素进行操作,无返回值
--foreach(fun(x:元素类型 => Unit)) --> 一对一
//4.flatten 无参,拍平,将集合嵌套集合压平为一个集合
--flatten(fun(集合[集合[元素类型]]) => 集合[元素类型]) --> 一对多
//5.flatMap 相当于map+flatten,先对逐个元素处理,再拍平
--flatMap(fun(集合[集合[元素类型]]) => 集合[元素类型]) --> 一对多
//6.groupBy 分组,指定分组的字段 返回值类型为Map
--groupBy(fun(x:元素类型 => Any)) --> 多对一
//7.reduce 指定聚合规则,将所有元素聚合为一个值,从左到右计算
--reduce(fun(sum:元素类型,a:元素类型 => 元素类型)) --> 多对一
//8.reduceRight 指定聚合规则,将所有元素聚合为一个值,从右到左计算
--reduceRight(fun(a:元素类型,sum:元素类型 => 元素类型)) --> 多对一
//9.fold 指定聚合规则,将所有元素聚合为一个值,从左到右计算,初始的sum为n
--fold(n)(fun(sum:元素类型,a:元素类型 => 元素类型))) --> 多对一
//10.foldRight 指定聚合规则,将所有元素聚合为一个值,从右到左计算,初始的sum为n
--fold(n)(fun(a:元素类型,sum:元素类型 => 元素类型))) --> 多对一
//案例 关于高级函数的计算
val list = List[String]("spark","hello","hadoop","scala","flume","kafka")
//todo 1.map 一对一
println(list.map(_.length)) //List(5, 5, 6, 5, 5, 5)
//todo 2.filter 过滤,true的返回
println(list.filter(_.contains("s"))) //List(spark, scala)
//todo 3.foreach 无返回值
list.foreach(println(_))
//todo 4.flatten 压平 一对多 将集合嵌套集合压平为一个集合
val list1 = List(List(1, 2, 3), List(4, 5, 6), List(7, 8))
println(list1.flatten) List(1, 2, 3, 4, 5, 6, 7, 8)
//todo 5.flatMap map + flatten
val list2 = List(("hello,world"), ("jason,scala,hadoop"))
val list3 = list2.flatMap(x => x.split(","))
println(list3) //List(hello, world, jason, scala, hadoop)
//todo 6.groupBy 多对一
val list4 = List("aa", "bb", "cc", "dd", "bb", "aa")
println(list4.groupBy(x => x))
//Map(bb -> List(bb, bb), cc -> List(cc), dd -> List(dd), aa -> List(aa, aa))
val list5 = List((1,"aa"),(1,"bb"),(3,"dd"),(4,"rr"),(3,"ff"))
println(list5.groupBy(_._1))
//Map(4 -> List((4,rr)), 1 -> List((1,aa), (1,bb)), 3 -> List((3,dd), (3,ff)))
//todo 7.reduce 多对一
println(list1.flatten.reduce(_+_)) //36
//reduceRight
println(list1.flatten.reduceRight(_+_)) //36
//todo 8.fold 多对一 与reduce类似,在第一个参数中指定初始值
println(list1.flatten.fold(100)(_ + _)) //136
//foldRight
println(list1.flatten.foldRight(100)(_ + _)) //136
*5.8 Queue(先进先出)
//1.不可变队列 import scala.collection.immutable.Queue
--1.创建
Queue[.apply][元素类型](初始元素,初始元素...)
val queue = Queue[Int](1,2,3)
--2.增/删 +: | :+ | ++ | ++: | enqueue | dequeue
val queue2 = queue.enqueue(8,9) //只能添加一个元素 (1,2,3,(8,9))
val value = queue.dequeue // 1
//2.可变队列 import scala.collection.mutable
--1.创建
mutable.Queue[.apply][元素类型](初始元素,初始元素...)
val queue0 = mutable.Queue[Int](10,20,30)
--2.增/删 +: | :+ | ++ | ++: | += | +=: | ++= | enqueue | dequeue
queue7.enqueue(80,90)
println(queue0) //(10,20,30,80,90)
println(queue0.dequeue) //10
println(queue0) //(20,30,80,90)
5.9 并行函数
def main(args: Array[String]): Unit = {
val list = List(10,20,30,40)
list.foreach(x=>{
println(Thread.currentThread().getName)
println(x)
})
println("+"*100)
//使用par调用多线程并行运行
list.par.foreach(x=>{
println(Thread.currentThread().getName)
})
}
第六章 模式匹配
6.1 基本语法
/* 1.基本语法
模式匹配 值/函数 match{
case 匹配条件1 => 执行语句1
case 匹配条件2 => 执行语句2
...
case _ => 其它的执行语句 */
val prt: Any = StdIn.readLine("请输入一个任意值:")
prt match{
case "hello" => println("hello...")
case "jason" => println("jason...")
case x:String => println(s"${x} 是一个String类型")
case x:Int => println(s"${x} 是一个Int类型")
case _ => println("啥也不是...")
/* 2.模式守卫 在case语句后使用条件判断,判断为true的执行执行语句
模式匹配 值/函数 match{
case 匹配条件1 条件判断1 => 执行语句1
case 匹配条件2 条件判断2 => 执行语句2
...
case _ => 其它的执行语句 */
val word: String = StdIn.readLine("请输入一个任意值:")
word match {
case x: String if x.contains("j") => println(s"${x}里包含有j")
case x: String if x.contains("s") => println(s"${x}里包含有s")
case x: String if x.startsWith("y") => println(s"${x}以s开头")
6.2 模式匹配类型
6.2.1 常量
"1.对值类型进行匹配"
//如果要匹配类型,则匹配的变量类型必须定义为Any
val list: List[Any] = List("jason", 12, 0.0, true, 'k')
//随机生成list长度以内的整数
val index = Random.nextInt(list.length)
val v: Any = list(index)
v match{
case x:Int if x % 2 == 0 => println(s"${x} 是一个整数,而且是一个偶数")
case x:String if x.startsWith("j") => println(s"${x} 是一个以s开头的字符串")
case x:Double => println(s"${x} 是一个double类型的元素...")
case x:Boolean => println(s"${x} 是一个Boolean类型的元素...")
case x:Char => println(s"${x} 是一个Char类型的元素...")
case _ => println("这不在我考虑的范围内...")
}
"2.对值进行匹配"
val list1: List[Int] = List(5,8,6,13,15)
//随机生成list长度以内的整数
val index1 = Random.nextInt(list1.length)
val v1: Any = list1(index)
v1 match{
case x:Int if(x % 2 == 0) => println(s"${x} 是一个偶数")
case x:Int if(x % 5 == 0) => println(s"${x} 可以被5整除")
case _ => println("这是一个神奇的数字...")
}
6.2.2 数组
//todo 1.可以对数组的 "元素" 个数、类型、值...进行判断
val array: Array[Int] = Array(1, 5, 8, 6, 4, 9)
array match{
case Array(a,b,c) => println(s"数组中有三个元素,分别是:${a} ${b} ${c}")
case Array(a,_*) => println(s"数组中至少有一个元素 ${a}")
case Array(a:Int,b:Int,_*) => println(s"数组中至少有两个Int类型的元素 ${a} ${b}")
case Array(1,_*) => println("数组是以数字1开头的")
}
//todo 2.可以对整个数组的类型进行判断
//如果需要对数组类型进行判断,需要将变量类型改为Any,否则case语句无法匹配会报错
//val array2: Array[Int] = Array(1, 5, 8, 6, 4, 9)
val array2: Any = Array[Int](1, 5, 8, 6, 4, 9)
array2 match{
case array:Array[Any] =>println("Any数组")
case array:Array[Int] =>println("整型数组")
case array:Array[Double] =>println("Double数组")
case array:Array[String] =>println("字符串数组")
}
6.2.3 List (泛型擦除)
val list:Any = List[Int](1, 5, 3, 5, 2)
//todo 1.对集合的元素进行判断
list match{
case List(a,b,c) => println(s"数组中有三个元素,分别是:${a} ${b} ${c}")
case List(a,_*) => println(s"数组中至少有一个元素 ${a}")
case List(a:Int,b:Int,_*) => println(s"数组中至少有两个Int类型的元素 ${a} ${b}")
case List(1,_*) => println("数组是以数字1开头的")
}
//todo 2.对集合类型进行判断
//泛型擦除 集合在编译的时候会去掉泛型 因此匹配类型时只要是List都会匹配成功
// 【类似的存在泛型擦除的有List、queue......】
list match{
case x:Array[Any] => println("我是一个数组")
case x:List[Any] => println("Any集合") //只要是list就会匹配成功
case x:List[Int] =>println("整型集合")
case x:List[Double] =>println("Double集合")
case x:List[String] =>println("字符串集合")
}
//todo 3.List独有的 :: | :::
list match{
case x :: Nil => println(s"集合中只有一个元素${x}")
case a :: b :: Nil => println(s"集合中有两个元素${a} ${b}")
//:: 最后必须以list接收,tail(或其他任意值)此处充当一个类-tail的集合
case (x:Int) :: tail => println(s"集合以int类型的${x}开头,剩下的元素为${tail}")
case (x:Int) :: middle :: tail => println(s"集合以int类型的${x}开头,第二个元素为${middle},剩下的元素为${tail}")
case (x:String) :: tail => println(s"集合以String类型的${x}开头,剩下的元素为${tail}")
case _ => println("这...是我没见过的奇迹")
}
6.2.4 Tuple
val t1: (Any,Any,Any) = ("jason", 25, 176.3)
//todo 1.匹配元组,匹配条件必须与元组长度一致
t1 match{
case (a,b,c) => println(s"元组元素有三个,分别为:${a} ${b} ${c}")
//case (a,_*) => println("") //匹配条件与元组长度必须一致
}
//todo 2.类型判断
//当变量定义为Any类型时,匹配条件中Any和正确的数据类型都可以匹配成功
t1 match{
case (a:Int,b:Int,c:Int) => println(s"${a}:Int,${b}:Int,${c}:Int")
case (a:String,b:Int,c:Double) => println(s"${a}:字符串,${b}:Int,${c}:Double")
case (a:Any,b:Any,c:Any) => println(s"${a}:Any,${b}:Any,${c}:Any")
}
//todo 3.对象元组匹配
val t2: Array[(String, (String, (String, Int)))] = Array[(String, (String, (String, Int)))](
("深圳", ("宝安区", ("张三", 20))),
("深圳", ("宝安区", ("李四", 18))),
("深圳", ("宝安区", ("王五", 38))),
("深圳", ("宝安区", ("赵六", 26)))
)
//如果要遍历某个字段
t2.map(_._2._2._1).foreach(println)
//但是不方便理解取值字段含义,因此可以使用模式匹配增强代码表达性
t2.map(x =>
x match{
case (city,(area,(name,age))) => name
}
).foreach(println)
}
6.3 样例类
--1.样例类
(1) 语法:case class类名([val/var] 属性名:属性类型,...)
(2) 定义样例类时如果不写val/var,则默认是val
(3) 样例类实际上相当于创建了一个伴生类和一个伴生对象
object Person | class Person(...)
--2.样例对象
(1) 语法:case object 名称
(2) 一般用样例对象来做枚举值
--3.普通类实现模式匹配
(1) 创建伴生类和伴生对象
(2) 在伴生对象中定义unapply方法
案例1:样例类匹配模式
//todo 1.1 创建样例类
case class Person(name:String,age:Int)
//1.2 创建样例类对象
val person: Person = Person("jason", 25)
//1.3 一般的属性值输出方式
println(person.age)
println(person.name)
//1.4 样例类的模式匹配
person match{
case Person(name,age) => println(s"name:${name},age:${age}")
}
案例2:样例对象模式匹配
//todo 2 创建样例对象
abstract class Parent
//2.1 样例对象继承Parent,则样例对象即为Parent的子对象
case object Man extends Parent
case object Woman extends Parent
//2.2 自定义方法 需传入Parent的一个对象
def get(parent:Parent)= println(parent)
//2.3样例对象直接当做枚举类来使用
get(Man)
get(Woman)
案例3:普通类实现模式匹配
//3.1 创建伴生类和伴生对象
class People(val name:String,val age:Int)
object People{
//3.2 定义unapply方法
def unapply(arg: People): Option[(String, Int)] = {
if(arg == null)
None
else
Some(arg.name,arg.age)
}
}
//3.3 普通类的对象实现模式匹配
val people = new People("jason", 26)
people match{
case People(name,age) => println(s"name:${name}, age:${age}"
}
6.4 定义变量时模式匹配
object MatchVariable {
def main(args: Array[String]): Unit = {
//定义变量的时候使用模式匹配(其实就是模仿后面类型的结构)
//1.元组
//一般情况
val t1 = ("jason",25,"深圳市")
//模式匹配
val (name,age,address) = ("jason",25,"深圳市")
println(name,age,address)
//如果匹配元素不使用,可以使用_代替
val (name1,_,_) = ("jason",25,"深圳市")
println(name1)
//2.数组
//一般情况
val array: Array[Int] = Array[Int](1, 2, 3, 4)
//模式匹配
val Array(x,_*) = Array[Int](1, 2, 3, 4)
println(x)
val Array(a,b,c,_) = Array[Int](1, 2, 3, 4)
println(a,b,c)
//3.List
//一般情况
val list: List[Any] = List[Any](5, 6, 8, "da", 0.0, true)
//模式匹配
val List(l:Int,_*) = List[Any](5, 6, 8, "da", 0.0, true)
println(l)
val head :: tail = List[Any](5, 6, 8, "da", 0.0, true)
println(head) //5
println(tail) //List(6, 8, da, 0.0, true)
//4.Map for循环的模式匹配
val map = Map("jason"->25,"qiang"->18)
for((k,v)<-map){
println(s"${k},${v}")
}
}
}
6.5 偏函数
//偏函数
(1)语法:IN为参数类型 OUT为返回值类型
val 函数名:PartialFunction[IN,OUT]={
case 匹配条件1:In => OUT
case 匹配条件2:In => OUT
...
}
(2)偏函数,一般在数据结构中比较复杂的元组时,将偏函数作为参数传递给map/flatmap...
object PartialFunction {
def main(args: Array[String]): Unit = {
//1.偏函数的基本使用
val fun:PartialFunction[Any,Int]={
case x:String => 20
case x:Int => 30
case x:Double => 40
case x:Boolean => 10
case _ => 0
}
println(fun(0.0))
println(fun("jason"))
//2.获取指定字段时可以使用偏函数
val t: Array[(String, (String, (String, Int)))] = Array[(String, (String, (String, Int)))](
("深圳", ("宝安区", ("张三", 20))),
("深圳", ("宝安区", ("李四", 18))),
("深圳", ("宝安区", ("王五", 38))),
("深圳", ("宝安区", ("赵六", 26)))
)
//todo 方式1.定义偏函数来获取name字段
val getName:PartialFunction[(String, (String, (String, Int))),String]={
case (city,(area,(name,age))) => name
}
//调用偏函数
t.map(getName(_)).foreach(println)
//todo 方式2.直接在map{}中定义偏函数
t.map{
case (city,(area,(name,age))) => name
}.foreach(println)
}
}
第七章 异常处理
7.1 Java异常处理流程
public class ExceptionDemo {
public static void main(String[] args) {
//使用try来包裹需要抓取异常的代码
try {
int a = 10;
int b = 0;
int c = a / b;
}catch (ArithmeticException e){
// catch时,需要将范围小的写到前面
e.printStackTrace();
}catch (Exception e){
e.printStackTrace();
//最终执行一次,一般用来关闭资源
}finally {
System.out.println("finally");
}
}
}
7.2 Scala异常处理流程
7.2.1 异常处理方式
scala中异常处理的三种方式:
--方式1:
--try{...}catch{case e Exception => ...}finally{...}
类似于java中异常处理流程
--方式2:
--Try(...).getOrElse(默认值)
如果Try包裹的代码报错,则返回默认值,否则正常运行
一般用于获取外部链接的时候使用
--方式3:
--try{Left(...)}catch{case e Exception => Right(...)}
正常时执行Left,异常时执行Right
7.2.2 案例
object Exception {
def main(args: Array[String]): Unit = {
//todo 方式1 try catch finally
try{
println(1/0)
}catch{
case e:Exception=>println(e.getMessage + s"${e}--这样的参数我不能接受呦~")
}finally {
println("Best Wishes...")
}
//todo 方式2 Try().getOrElse()
val result = Try(1/0).getOrElse(0)
println(result)
//案例:数据过滤
val datas = List[String](
"1,zhangsan,20,shanghai",
"2,lisi,20,shanghai",
"3,wangwu,20,shanghai",
"zhaoliu,20,shanghai",
"4,qianqi,shanghai",
"5,wangba,20"
)
datas.map(x=>{
val list = x.split(",")
val id = Try(list(0)).getOrElse(0)
val name = Try(list(1)).getOrElse("")
val age = Try(list(2).toInt).getOrElse(-1)
val area = Try(list(3)).getOrElse("")
(id,name,age,area)
}).filter(_._3 > 0).foreach(println)
//todo 方式3
//当执行语句执行正常时Left,否则Right,他们位置可以互换
def devide(a:Int,b:Int)={
try{
Left(a/b)
}catch{
case e:Exception => Right(b,e)
}
}
}
}
第八章 隐式转换
8.1 隐式方法
--隐式方法[悄悄的将一个类型转成另一个类型]
(1)语法: implicit def 方法名(变量名: 待转换类型):目标类型 = {..}
(2)隐式转换方法的调用时机:
a.当前类型与目标类型不一致的时候,会自动的使用隐式转换
b.对象使用了不属于自己的方法,也会自动的使用隐式转换
"隐式转换时根据类型查找的,并不是根据名称"
//案例1:定义隐式转换方法,
//1.定义两个没1分钱关系的两个类
class Person(val name:String,val age:Int)
class Student(val name:String,val age:Int)
//2.定义隐式转换方法,使入参对象获得出参对象的属性
implicit def studentToPerson(student:Student):Person={
println("------------")
new Person(student.name,student.age)
}
//3.可以直接创建对象使用其属性和方法了
val p:Person = new Student("zhangsan",20)
println(p.name)
println(p.age)
//案例2:为Int类增加方法
//自定义类,定义两个方法
class MyRichInt(val self: Int) {
def myMax(i: Int): Int = {
if (self < i) i else self
}
def myMin(i: Int): Int = {
if (self < i) self else i
}
}
object TestImplicitFunction {
//定义隐式转换函数,使Int类型获得自定义类的方法
implicit def convert(arg: Int): MyRichInt = {
new MyRichInt(arg)
}
def main(args: Array[String]): Unit = {
println(2.myMax(6))
}
}
8.2 隐式参数
--1.隐式参数语法
implicit val 参数名:参数类型 = 值
--2.隐式参数在方法中调用
在定义方法的时候必须指定哪个参数后续会传递隐式参数的值
def 方法名(参数名:参数类型,..)(implicit 参数名:参数类型)
--3.隐式参数说明
(1)同一个作用域中,相同"类型"的隐式值只能有一个
(2)编译器按照"隐式参数的类型去寻找对应类型的隐式值",与隐式值的名称无关。
(3)隐式参数优先于默认参数
//案例1:隐式参数在方法中调用
def add(a:Int)(implicit b:Int) = a+b
def main(args: Array[String]): Unit = {
println(add(10)(5))
}
//案例2:测试编译器调用隐式参数时是按照类型调用的
object TestImplicitParameter {
//1.定义隐式转换参数
implicit val str: String = "hello world!"
//2.隐式转换方法
def hello(implicit arg: String="good bey world!"): Unit = {
println(arg)
}
//当调用hello时,按照类型进行查找隐式转换,因此调用的是隐式转换参数
def main(args: Array[String]): Unit = {
hello //hello world!
}
}
8.3 隐式类
--1.语法
implicit class 类名(待转换类型){...}
--2.说明
(1) 隐式转换类必须要有构造器,"构造器的参数有且仅有一个",参数类型就是待转换类型
(2) 隐式转换类不能处于最顶层,必须放在object/class/package object中
(3) 实质上还是将一个对象转为另一个对象
object $03_ImplicitClass {
//定义隐式转换类 构造器内传入待转换类型[Int -> Person]
implicit class Person(x:Int){
val name:String = "jason"
val age:Int = 20+x
}
def main(args: Array[String]): Unit = {
//直接通过Int类型对象调用隐式类中的属性和方法
val a: Int = 10
println(10.name)
println(10.age)
}
}
8.4 隐式机制解析
--隐式机制
(1)首先会在当前代码作用域下查找隐式实体(隐式方法、隐式类、隐式对象)--一般情况下
(2)如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找。类型的作用域是指与该类型相关联的"全部伴生对象"以及该类型所在包的"包对象"。
//(2)如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找。类型的作用域是指与该类型相关联的全部伴生模块,
object TestTransform extends PersonTrait {
def main(args: Array[String]): Unit = {
//(1)首先会在当前代码作用域下查找隐式实体
val teacher = new Teacher()
teacher.eat()
teacher.say()
}
class Teacher {
def eat(): Unit = {
println("eat...")
}
}
}
trait PersonTrait {}
object PersonTrait {
// 隐式类 : 类型1 => 类型2
implicit class Person5(user:Teacher) {
def say(): Unit = {
println("say...")
}
}
}
第九章 泛型
9.1 泛型方法
//泛型方法 语法:
def 方法名[T,U,..](参数名:T,..) :U = {...}
//案例
object GenericMethod {
def main(args: Array[String]): Unit = {
val arr =Array[Int](10,20,30,5)
println(getMax[Int](10,20))
}
//函数类型、入参出参类型时都可以使用泛型
def getMax[T](x:T,y:T):T = {
x
}
}
9.2 泛型类
//泛型类 语法:
class 类名[泛型1,泛型2,..](val 属性名:泛型,..){
def 方法名(参数:泛型):泛型 = {...}
}
//案例
object GenericClass{
//定义泛型类
class Person[T,U](var name:T,var age:U){
def getName() = this.name
def setName(name:T) = {
this.name=name
}
def getAge() = this.age
def setAge(age:U) = this.age = age
}
def main(args: Array[String]): Unit = {
//调用泛型类
val person = new Person[String,Int]("zhangsan",20)
println(person.getName())
}
}
9.3 上下限
//1.上限: [代表T只能是指定的类型或者指定类型的子类]
Class PersonList[T <: Person]
//2.下限: [代表T只能是指定的类型或者指定类型的父类] --很难做限制,Any是任何类的父类
Class PersonList[T >: Person]
object GenericUpperLower {
class Parent
class Sub1 extends Parent
class Sub2 extends Sub1
class Sub3 extends Sub2
def main(args: Array[String]): Unit = {
//必须是sub1或sub1的子类
// m1(new Parent)
//必须是sub1或sub1的子类,Any也可以
val sub3:Any = "spark"
m2(sub3)
}
//上限
def m1[T<:Sub1](x:T) = {
println(x)
}
//下限
def m2[T>:Sub2](x:T) = {
println(x)
}
}
9.4 非变、协变与逆变
//1.语法:
--非变 Son是Father的子类,则MyList[Father]与MyList[Son]“无父子关系”
class MyList[T]
--协变 Son是Father的子类,则MyList[Son] 也作为MyList[Father]的“子类”
class MyList[+T]
--逆变 Son是Father的子类,则MyList[Son]作为MyList[Father]的“父类”
class MyList[-T]
//2.案例:
object GenericChange {
class Person
class Student extends Person
//非变
class AA[T]
//协变
class BB[+T]
//逆变
class CC[-T]
def main(args: Array[String]): Unit = {
var aa = new AA[Person]
var bb = new AA[Student]
//aa = bb
//协变就是继承泛型的父子关系
var cc = new BB[Person]
var dd = new BB[Student]
cc = dd
//逆变就是颠倒泛型的父子关系
var ee = new CC[Person]
var ff = new CC[Student]
ff = ee
println(ff)
}
}