一. 自学笔记
-
scala-doc下载链接
可以用迅雷打开链接进行下载:
https://www.scala-lang.org/files/archive/scala-docs-2.**.*.zip -
java/scala反编译工具:Java反编译工具-JD-GUI
-
变量引用:name
引号里引用:s"$name"
引号里运算:s"${age+10}" -
语言开发的难点:
业务逻辑
性能优化 -
基本类型,存在栈里,但是长时间存在时,会有逃逸分析,放入堆里了
-
scala时强数据类型语言
var n=10;
n=11.1 <报错> -
声明定义变量
var 修饰的变量的值可变,即变量
val 修饰的变量的值不可变,即常量;推荐使用 -
scala,一切皆对象:
java里的基本类型不属于对象,scala里的基本类型属于对象
数字也是对象,可以调用方法:100.toDouble -
java上的异常只能抛给父类,scala异常为所有类型的子类,抛给谁都可以,抛异常可以代表不同的业务逻辑,进行不同的处理
-
运算符
//除
var n1: Int = 10 / 3 //3
var n2: Double = 10 / 3 //3.0
var n3: Double = 10.0 / 3 //3.3333333333333335
// 例如a%b = a-a/b*b
var n4 = 10 % 3 //1
var n5 = -10 % 3 //-1
var n6 = -10 % -3 //-1
var n7 = 10 % -3 //1
//移位运算
var n = 2
n <<= 2 //8
n >>= 3 //4
- scala无++/–
- scala无三元运算符
scala思想:一种事情最好只有一种方法,这样代码可以统一,所有用if…else…代替 - trait:可以继承,拥有java的接口和抽象对象的作用
- 引入scala.io下所有的包:import scala.io._
- 如何查看一个包下所有的内容:光标放到包名上,ctrl+b
- scala无switch,用模式匹配代替:match-case
- for语法格式
//1.两边闭合
//2.i不必定义
for (i <- 1 to 3) {
print(i + " ") //1 2 3
}
//前闭后和
for (i <- 1 until 3) {
print(i + " ") //1 2
}
//循环集合
//循环守卫;添加了条件,代替continue/break
for (i <- 1 to 3 if i != 2) {
print(i + " ") //1 3
}
for (i <- 1 to 3; j = i + 1) {
print(j + " ") //2 3 4
}
//嵌套循环
for (i <- 1 to 3; j <- 1 to 3) {
print(i + "-" + j + " ") //1-1 1-2 1-3 2-1 2-2 2-3 3-1 3-2 3-3
}
//循环返回值:scala将集合交给函数,函数将每个元素遍历后,返回新的集合
var res = for (i <- 1 to 3) yield {
i + 1
}
print(res) //Vector(2, 3, 4)
- for循环的步长:
1) for( i <- Range(1,3,2)){...}
2) for(i <- 1 to 3 if (i % 2 == 1)){...}
- 学习业务代码的重点:业务逻辑/代码逻辑
- 高阶函数:可以接受函数(即代码块:op: => Unit)的函数
scala用breakable高阶函数代替java的break - 函数式编程的:
递归:同样的处理逻辑,变量不同;循环条件向着递归推出靠经;二叉树/红黑树
高阶函数:将函数作为参数 - 方法转函数(面向对象转为面向函数)
val fun = dog.sum()
dog为对象,调用:fun(n1,n2) - scala函数里可以再定义函数
- 函数细节:
- 惰性函数:lazy val res=…
- var name = null; 值为null时必须写具体的类型,否正类型为Null
- 变量赋默认值:var n:Int = _
- 高阶函数:
1)一般:函数(参数)
2)高阶:集合.函数1(函数2)
函数2对集合中的每一个元素进行处理
3)res.foreach(println) - 字符串:
循环:for(c <- str){…}
str.take(1)
str.drop(1) - 大数据本质就是对集合进行处理,利用高阶函数可以组成函数连
- 构造器细节
1)主构造器只有一个,将类中除了方法之外的所有语句执行一遍
2)辅助构造器必须在第一行显示调用主构造器
3)java构造器分为无参/有参(此时无参构造器无,需要自己定义)
4)scala分为主/辅助构造器(此时主构造器有,不受影响)
5)java/scala子类无参/主构造器隐式调用父类的无参/主构造器
6)scala子类辅助构造器需要显示直接/间接调用自己的主构造器
7)主构造器形成用var/val修饰时,则该变量为类的属性
8)@BeanProperty注解,生成属性的get/set方法 - spark属于内存级别计算
1)需要了解数据类型的范围
2)选择合适的类型进行优化,能选择Short绝不选择Int - 对象创建流程
- 包路径:
1)工程/src/main/com…源文件路径,里面指定.class路径
2)工程/target/main/com…:从com开始为包路径
3)java中的源文件里指定的包路径和真实的包路径必须一致
4)scala中的源文件里指定的包路径是生成.class文件的路径,可以和源文件路径不一致;即:scala中的源文件里指定的包路径是什么,生成的.class文件就放到哪里 - scala的包
1)包里可多次创建包/class类
2)子包可直接用父包里的类,就近原则
3)父包引用子包必须import,可以在任何地方import
4)当包名冲突的时候,用绝对路径引入
5)包(package scala{…})里不能直接定义变量/函数,需要加包对象:package object scala{…} 包里就可以直接用包对象里的变量/函数
6)一个包只能有一个包对象
7)引入的包可以重命名
8)引入的子包可以隐藏(包=>,) - scala中没有static,用伴生代替
1)伴生类:非静态
2)伴生对象:静态
3)属性默认为private;方法默认为public;scala中无pulic关键字 - 修饰符:
1)protected受包括权限,只能子类访问,同包无法访问
2)属性默认为可读写
3)private属性时,同包不能访问属性,可增加一个包访问权限
private[包名] var name="…" - 访问权限修饰符
scala有三种
1)默认不写的:public:任何地方都可用
2)protected:只能子类用,同包不可用
3)private:只能本类/伴生对象里用
访问属性:对象.属性(后台还是属性对于的public方法访问) - 面向对象三大特征
1)封装:封装数据和逻辑
2)继承:解决代码复用性/扩展性/维护性
3)多态:一父多子+类型判断/转换 - 继承重写
1)重写一个非抽象方法须加:override
2)非抽象方法:父类已实现的方法
3)子类重写父类方法,调用父类的方法:super.方法
4)java:重写方法;scala重写属性和方法 - 反射技术:对象.getClass.getName
- 类型转换:
1)子类—>父类:=
2)父类—>子类:asInstanceOf
3)类型判断:isInstanceOf - 有继承的构造器执行流程
1)前提:实例一个有参的子类
2)用参数匹配子类构造器A
A为主构造器:父类主->子类主
A为辅助构造器:父类主->父类辅助->子类主->子类辅助
3)只有子类主构造器才能调用父类构造器;extends后的父类
4)本类辅助构造器调用主构造器:this - JVM动态绑定机制(子父类)
父类 对象1 = new 子类
1)对象1为子类对象
2)对象1调用方法:先子类–>后父类
3)对象1调用属性:谁调用返回谁的属性 - scala属性重写:动态绑定机制
1)关键字:override
2)def方法只能重写另一个def方法
3)val属性能重写另一个val属性或者无参的def方法
4)var属性只能重写另一个抽象的var属性:实际为实现一个抽象类
a)抽象的var只能属于抽象类:abstract class A{…}
b)无初始化的var变量 - 混入:效果和继承一样
区别:A混入B后,A的子类AA不受影响,AA不会继承B
1)静态:声明类时混入
2)动态:实例化对象时混入 - java/scala都是单继承,但是scala有混入的方式以实现自身的“继承”
- 若类A混入特质B,A已继承C,则C必须四B类的超类的子类,否则就是多继承,肯定报错
- 自身类型:混入限制,不能随便混入类;指定自身类型,要求混入该类型的类,必须是类型A的子类;
格式:this:类型A => - 嵌入类:内部类,类型投影
- 内部类:成员内部类/静态内部类(声明在伴生对象里)
- java类的五大成员:属性/方法/内部类/构造器/代码块
- 内部类的作用
1)内部类可访问外部类的私有属性
2)延迟加载 - 内部类访问外部类属性方式
1)外部类名.this.属性
2)外部别名.属性 (常用)
类的别名:外部类里:outClass => - 类型投影:实例内部类时添加:var a:外部类#内部类
可以屏蔽外部对象对内部对象的影响 - java中内部类属于外部类;scala中内部类属于外部类的对象
- 隐士转换
1)例如:大量的Double转为Int 时,可指定某些数据类型相互转换,就不用每个都加toInt
2)implicit 隐士函数
3)软件开发OCP原则:关闭源码,同时增加功能开发;即不改源码类增加新的功能
a:A/B类之间无继承
b:implicit def function(a:A):B={…new B}
c:a=new A
d:调用:a.B类方法;即a就可以调用B类的方法;底层:f(a).B类方法
4)何时用隐士转换
a:等号两边数据类型不一样
b:扩展API里的类,给类增加功能时不影响类本身 - 隐士变量:成员变量和形成都加implicit
1)优先级:实参 > 隐士变量 > 默认值
2)默认值只能用在一个函数的形参上
3)隐士值能用在多个函数的形参上 - 隐士类
1)scala 2.1版本加上的
2)集合中常用
3)只能有一个构造参数
4)作用:给已经写好的类B,不改源码的情况下在隐士类的作用域内增加新的功能,增加方法,不影响其他对象
5)格式:增加类B;implicit class A(val b:B){…} - 集合:可变/不可变集合(本身长度不可变,内容可变)
分为:序列seq/集Set/映射Map
常用ArrayBuffer/ListBuffer
java:不可变-数组;可变-List - mysql默认100个连接,关键就是优化用缓存技术
- 大数据特征:
大数据处理
流量:增加快宽带
并发:前端的负载均衡(集群) - 数据在网上进行传输就没有数据结构的样子,全为字符串
- 定长数组:a = new Array[Int](10)
a(1) = 7 - 变长/定长数组互转,本身没有改变,返回新的数组
arr.toArray/arr.toBuffer - 多维数组:arr.ofDim[类型](3,4)3行4列
arr(1)(1) = 11.11
for(row <- arr){ for(i <- rot){…}} - scala数组与java的List互转,List可继续转java的其他集合(什么时候会用到)
- 当类A继承了traitB,那A的实例a可以传递给B的引用
- 元组 Tuple,可放任何类型数据的容器,最大只有22个元素
val tuple = (1,2,3,“xxx”,5)
访问第一个元素:tuple._1 tuple.productElement(0)
遍历:for(i <- tuple.productIterator){…} - List :不可变,属于序列seq,可放任何类型数据
Nil:空列表
访问:List(1)
追加元素:返回新的List
list后追加:listnew = listold :+ 4
list前追加:listnew = 4 +: listold
拼接list::: 从右往左
val listnew = 1:: 2:: listold :: Nil listold 整体放入
var listnew = 1:: 2:: listold ::: Nil listold每个元素分别加入Nil
:::要求两边都为集合 - ListBuffer:可变,属于seq序列
new listBuffer[类型](…)
追加:list += 4
list.append(…)
list ++= listold
list = list1 ++ list2
list = list1 :+ 5
删除:list.remove(0) - Queue 队列:有序列表,底层以数组+链表实现
先入先出
两种:可变(常用)/不可变队列
val q = new mutable.Queue[类型]
追加:q += 20
q ++= list list每个元素分别加入
q += list list整体放入q,q存储类型为泛型
出队列:q.dequeue() 对头去掉
q.enqueue()队尾加入
返回Queue元素:
q.head 头元素
q.last 尾元素
q.tail 除了头元素的所有 - 我们自己也可以实现操作符(+/+=/:+)的重载:
def :+ (n : Int): Unit = {…} - mysql+Redis配合使用
- map 映射
散列表:数组+链表:key-value
放入:map.put
取出:map.get
四种构造方式:
不可变:map(k -> v,…)
可变:mutable.map(k -> v,…)无序
空map:mutable.HashMap[String,Int]
对偶元组方式创建:mutable.map((k,v),…)
四种取出方式:
map(k)
map.contains(k) 无k则抛异常
map.get(k).get 存在map.get(k)返回some,否则返回none
常用:map.getOrElse(k,默认值)
增:map(k) =v k存在则修改
map += (k -> v, …)
删:map -= (k,k1,…)
map -= map - (k,k1,…)
遍历:for((k,v) <- map)
for(k <- map.keys)
for(v <- map.values)
for(t <- map){t._1/t._2} t为类型Tuple - 集Set:不重复元素的集合,无序, 哈希集实现
创建:set = Set(1, 2, “ab”)
增:set.add(4)
set += 6
set.+=(5)
删除:set.-=2
set.remove(“ab”)
遍历:for(v <-set) - 什么时候该用什么集合
Seq有序:arrayBuffer/ListBuffer/Queue队列
Set集:不重复
Map映射:k-v - 大数据计算:mapreduce/spark/storm
传统语言:变量1 -> 处理 -> 变量2
大数据scala:集合1 -> 处理 -> 集合2 - 集合操作,map方法映射
集合 -> 高阶函数处理集合每一个元素 -> 新集合
将集合中每个元素通过指定功能映射成新的集合–
-函数式编程
listnew = listold.map(f) f为处理每个元素的指定功能 - 把函数赋给变量:val f = function _
- 函数扁平化操作 flatmap
listnew = listold.flatmap(f)
将集合中的所有子集合每个元素合成一个集合通过指定功能映射成新的集合 - 过滤:filter
listnew = listold.filter(f)
f实现过滤条件,返回Boolean - 集合简化:reduceLeft/reduce/reduceRight
reduceLeft运行规则:从左边开始执行将得到的结果返回给第一个参数后继续和下一个元素运算
递归实现
简化/折叠必须时二元函数?
var sum = list.reduceLeft(f) f为处理逻辑;例如:所有元素求和 - 折叠:fold/foldLeft/foldRight
将函数上一步返回的值作为函数的第一个参数继续传递参与运算
var sum = list.foldLeft(5)(f) 将5加入list最左侧,f为处理逻辑;例如:5和list的所有元素求和 - 函数柯里化:将入参分散传入函数
- 集合扩展-拉链(合并)
组成两元元组作为元素的集合
listnew = list1.zip(list2) - 迭代器:list.iterator
循环集合时使用,需从集合中获得
循环:
while(iterator.hasNext){iterator.next()}
for(i <- iterator)
循环一次后,迭代器需要恢复初始位置,iterator.take(0)待确定? - 流:无穷多个元素的集合,末尾元素遵循lazy规则
定义:def numStream(n:BigInt):Stream[BigInt]=n #:: numStream[n+1]
创建:val s=numStream(1)
第一个元素:s.head //1
s.tail //(2,?)
s //(1,2,?) - 任何一种集合都可用map方法(.map(f))按照f的逻辑映射出新集合
- 视图 view:
集合.view可以完成自己的lazy规则,用几个元素就计算几个
view返回一个懒执行的集合
不会缓存,每次都要重新计算 - 并行集合:充分使用多核cpu
list.par.map(f)
主要用到的算法
分治算法:splitters分解器/combiners组合器
work steanlin算法:认为调度负载均衡(常用) - 线程安全的集合:以Synchronised开头的集合
- 操作符重载
var r1 = n1 + n2
var r1 = n1.+ n2 - 模式匹配match:代替了java的switch
只执行一个case
res = oper match{
case 2 => {…} //匹配点
case _ if(…) => {…} //匹配范围
case ch if(…) => {…} //匹配范围,ch拿到了oper的值
case ch => {…} //ch拿到了oper的值
case “+” => {…}
case _ => {…} //类似java的default
}
res得到了 =>之后代码块的返回值 - 类型匹配
val res = obj {
case a: Int =>
case b: String => …
}
a/b拿到了obj;res拿到了=>之后代码块的返回值 - 匹配数组
Array(0) 匹配一个元素且为0
Array(0,_*) 匹配以0开头 - 匹配列表
case 0::Nil => //匹配一个元素且为0
case x::y::Nil => //y有两个元素
case 0::tail => //以0开头后边任意 - 匹配元组
case (0,_) //以0开头的二元组
case (y,0) //二元组,第二个元素为0 - 对象匹配
- 样例类:是为对象匹配而设计的
case class…
obj.copy() //直接拷贝
obj.copy(i=1)//改值拷贝
中置表达式匹配:list(1,2,3,4)
case r1::r2::rest => …//r1的值是1,r2的值是2,rest的值是(3,4) - 匹配嵌套结构:类似正则表达式:案例:商品打折
- 密封类:sealed类,不能在其它文件中定义子类
- 偏函数:
包含多类型的list进行操作时使用,效果=先过滤filter再映射map
继承PartialFunction
将包在大括号里的一组case语句封装成函数,只对指定类型或范围的元素进行操作
定义:val partial = new PartialFunction[Any,Int]//入参Any,返回Int
{ def isDefindAt…//返回ture则调用apply
def apply…//入参操作,返回新集合
使用:listnew = list.collect(partial)
简化格式:def f1= new PartialFunction[Any,Int]{case i:Int => … }
list.collect{ case i:int => …} - 函数作为入参:函数类型:function+入参函数的个数
- 匿名函数:val f = (x:Int) => x*2
调用:f(3) - list可直接打印出来,Array.mkString(",")用,隔开打印出来
- 高阶函数:入参/返回都是函数
def test(f1:Int=>Double,f2:Double=>Int,n:Int)={f1(f2(n)},接收两个函数和一个Int参数
可返回函数:用到了思想:柯里化+闭包
参数类型推断,高阶匿名简写:list.map(+1)/list.reduce(+_) - 闭包:就是一个函数和与其相关的引用环境组合成一个新函数
多次调用函数,多次传入用多个参数,其中有一个参数值不变,用闭包可只传一次这个参数,封闭成一个新函数,入参是其它的变化的参数
本质:函数+属性=新函数
使用:有入参的函数返回有入参的匿名函数
例如:f1=f2(n1)//其中有一个参数值不变,f1接收了f2返回的匿名函数
f1(n2)//其它的变化的参数,f1封闭了n1+f2的逻辑 - 函数柯里化:将多个入参的函数转为只有1个入参的函数,分解参数
def f(x:Int)=(y:Int)=>xy
def f(x:Int)(y:Int)=>xy
传统:def(x:Int,y:Int)=>x*y - 控制抽象:将一段代码传递给高阶函数,高阶函数内部执行这些代码
breakable{…}
满足两个条件:入参是函数;该函数无入参无返回值
案例:while的底层实现,柯里化+控制抽象实现 - 编程思想:递归;反转字符串
- 序列化:样例类实现了序列化
类型(对象)—> 字符串
作用:将对象保存在文件中
进行网络上传输
spark上的mast和work交互信息传递需要序列化 - 用数值分析,可以分析代码的执行效率
- 递归:重复计算时,递归次数成幂次递增,大家需要考虑用迭代优化
- Akka,并发编程模型 scala编写
spark的mast和work底层通信用Akka
JVM平台上构建高并发分布式和容错应用的框架
提供了scala和java的开发接口
我们不用过多考虑线程/锁/资源竞争/网络通信等问题 - Actor模型,处理并发问题
处理并发问题的关键:保证共享数据的一致性和正确性,但当加入同步条件Synchronised后,实际上大并发就阻塞这段代码,效率下降 - Actor系统,ActorSystem单例,包含很多Actor,Actor属于轻量级的,1GB内存可容纳上百万级别个Actor
- Akka调用机制(发出消息不等待,类似前端的ajax一异步处理回调)
- scala里class和object的区别,main在object里
- 传统网络编程:CS:socket(tcp/ip协议)BS:http基于tcp/ip协议
- akka网络编程:大并发后端服务程序