多范式的编程语言,设计的初衷是集成面向对象编程和函数式编程的各种特性。运行于现有的Java平台,兼容现有的Java程序。
why should we learn?
优雅,
速度快,一行抵上Java几行,开发速度快,静态编译的。
静态编译就是将你所写的类全都编译成clsaa,方便使用,动态编译是运行时对所需要的类进行编译。
能融合到hadoop的生态圈
(1)方法与函数的区别与练习
函数就是一个对象,函数可以作为方法参数传递进去,
方法要通过def标识,函数是通过=>标识
方法转函数: 方法名 _
如果一个方法用到了递归函数,那么必须要规定返回值的类型
(2)val,var,mutable,immutable的含义与区别
val 是定义的变量不可变
var 定义的变量可以变
mutable,是定义的可变的集合类型
immutable,定义的是不可变的集合类型
两者之间val和var指的是变量名,他指定的是这个变量可以赋予其它的值,或者不能赋予其他的值
mutable和immutable指的是,这个集合的值是可以添加改变的,还是不能改变的,当然即使是不能改变,也是可以进行++=,+=等等一系列的操作的,只是操作后的值代表的是另一个集合,并不是这个集合本身,这个集合本身并没有发生改变。
对于学习一门新的语言,就要从最基础的入手,下面的一些是我自己总结出的一些,建议0基础学习scala的coder可以先从基础敲起来。
package ScalaTest
import scala.collection.mutable.HashMap
import scala.collection.mutable.ArrayBuffer
object Demo01 {
def main(args: Array[String]): Unit = {
//九九乘法表
/*for (i <- 1 to 9;j <- 1 to i ){
print(""+i+"*"+j+"="+i*j)
if (i==j)
println()
}*/
//简单方法定义测试,可以不指定返回值的类型,系统自己判断
//def m1(x:Int,y:Int):Int=x*y
//print(m1(1,2))
//如果用到了递归函数,需要指定返回值的类型,
/*def m2(x:Int):Int={
if (x<=1) 1
else m2(x-1)*x
}
print(m2(8))*/
//定义一个函数,并将函数作为m1方法的参数,进行输出m1(在输出语句中直接执行了)
def m1(x:Int,y:Int):Int=x*y
val f1=(x:Int,y:Int) =>x+y
val q=f1(2,3)
// print(q)
// print(m1(f1(1,1),8))
//将方法转为函数并调用(此时不能直接m1 _(1,2),这样的得到的是,要先进行转化)
val w=m1 _
val e=w(1,2)
//print(e)
//定长数组的定义格式
val arr=new Array[Int](5)//初始化长度为5的值,其所有元素为0
//print(arr)//这样打印的是hashcode值
//print(arr.toBuffer)//这样打印的是数组的值,[I@45c8e616ArrayBuffer(0, 0, 0, 0, 0)
val arr1=Array(1,2,3,4,5)
//print(arr1.toBuffer)
//定义变长数组的格式
val arr2=ArrayBuffer(1)
//print(arr2)
//添加值
arr2 += 2
//print(arr2)
arr2 +=(3,4)
//print(arr2)
//添加一个定长数组
arr2 ++=Array(5,6)
//print(arr2)
arr2++=ArrayBuffer(7,8)
//print(arr2)
arr2.insert(4,9,-3,0)//第一个数字指的是从第几个数后插入,后面的数才是要插入的
//print(arr2)//ArrayBuffer(1, 2, 3, 4, 9, -3, 0, 5, 6, 7, 8)
//修改
//arr2(0)=100
//删除同增加,加号变减号,还多一个
//arr2.remove(1)
//循环遍历数组
//for (i <- arr2)println(i)
//print(arr2)
//until 生成数组的下标
//for (i <- 5 until arr2.length) println(arr2(i))
//for (i <- (5 until arr2.length).reverse) println(arr2(i))
//yield 关键字将原始的数组进行转换会产生一个新的数组,原始的数组不变
//val a=for (i <- arr2) yield i*10
//println(a)//ArrayBuffer(10, 20, 30, 40, 90, -30, 0, 50, 60, 70, 80)
//print(arr2)//ArrayBuffer(1, 2, 3, 4, 9, -3, 0, 5, 6, 7, 8)
//提取数组中的偶数乘以10并生成新的数组,
//val a=for (i <- arr2 if i%2==0) yield i*10
//print(a)
//print(arr2.sum)//求和,min,max,sorted,sorted.reverse
//提取数组中的偶数乘以10并生成新的数组,先创建一个函数,让数组过滤
//val a=(x:Int)=>x%2==0
// val b=arr2.filter(a).map(_*10)//ArrayBuffer(20, 40, 0, 60, 80)
//最高级的方法,直接使用匿名函数
//val a=arr2.filter(_%2==0).map(_*10)
// print(a)
//构建映射
//val map1=Map("weiwei" -> 18,"wangning" -> 19)
//利用元组构建映射
//val map2=Map(("zhouyifan",17),("zhouerfan",19))
//val value=map1("weiwei")//18
//val value=map1.getOrElse("ssss",10)//键并不存在,输出默认值10
//val value=map2("zhouyifan")
//val value=map2.getOrElse("ssss",10)
//我們用val应用变量时,无论怎么操作,原值是不会改变的,会形成新的值,就像是Java中的final
//创建可变得map映射
//val map1=HashMap("weiwei"->18,"wangning"->19)
//val map2=HashMap(("zhouyifan",17),("zhouerfan",19))
//添加,删除也是一样的(可以添加多个也可以删除多个)
//map1 +=("qwe"->11)
//map2 +=("342"->12)
//修改操作
//map2("zhouyifan") = 38
// print(map2)
//(1)元组是不同类型的值的聚集;对偶是最简单的元组。
//(2)元组表示通过将不同的值用小括号括起来,即表示元组
//val a=("weiwei",38,4.38)
//print(a._1)
//将对偶的集合转为映射
//var arr3=Array(("zhouyifan",17),("zhouerfan",19))
//var r=arr3.toMap
//print(arr3.toBuffer)//ArrayBuffer((zhouyifan,17), (zhouerfan,19))
//print(r)//Map(zhouyifan -> 17, zhouerfan -> 19)
//1. 使用 zip 命令可以将多个值绑定在一起
//如果两个数组的元素个数不一致,拉链操作后生成的数组的长度为较小的那个数组的元素个数
//val names=Array("weiwei","zhouyifan")
//val ages=Array(1,2)
//val t=names.zip(ages)
//print(t.toBuffer)//ArrayBuffer((weiwei,1), (zhouyifan,2))
//如果其中一个元素的个数比较少,可以使用 zipAll 用默认的元素填充
//val names=Array("weiwei","zhouyifan","eefsfd")
//val ages=Array(1,2)
//val y=names.zipAll(ages,"yy",19)
//print(y.toBuffer)//ArrayBuffer((weiwei,1), (zhouyifan,2), (eefsfd,19))
//集合
//scala的集合有三种,序列sep,set,映射map,所有的集合扩展自iterable特质,在scala中集合有mutable和 immutable两种
//类型,即可变和不可变,定义了immutable之后初始化之后就不可以改变了
// val a=List(1,2,3)
//val b=List(4,5,6)
//print(a++b)//List(1, 2, 3, 4, 5, 6)
//print(a::b)//List(List(1, 2, 3), 4, 5, 6)
//print(0+:a)//List(0, 1, 2, 3)==a.+:(0)
//print(0::a)//List(0, 1, 2, 3)
//print(a)//List(0, 1, 2, 3)List(1, 2, 3)
//可变的列表
//val a=ListBuffer[Int](1,2,3)
//val b=new ListBuffer[Int]
//print(b)//ListBuffer()
//b+=(4)
//print(b)//ListBuffer(4)
//b.append(5)
//print(b)//ListBuffer(4, 5)
//a ++= b
//print(a)//ListBuffer(1, 2, 3, 4, 5)
//上述的是没有生成新集合的,下面将生成新的集合
//val c= a++b
//print(c)//ListBuffer(1, 2, 3, 4, 5, 4, 5)
//val d=a :+(0)
//print(d)//ListBuffer(1, 2, 3, 4, 5, 0)
//删除是:-和--
//将可变的list转为不可变
//val e=a.toList;
//print(e)//List(1, 2, 3, 4, 5)
//将可变的list转为数组
//val f=a.toArray
//print(f.toBuffer)//ArrayBuffer(1, 2, 3, 4, 5)
//set集合:是指没有重复元素的集合
val a=Set(1,2,3,4,5,6,7,8)
val b=Set(4,5,6)
//print(a)//Set(5, 1, 6, 2, 7, 3, 8, 4)
//print(a.sum)//36
//print(a.size)//8
//print(a.min)
//print(a.max)
//print(a+9)//Set(5, 1, 6, 9, 2, 7, 3, 8, 4)
//两个set的交集
//print(a&b)//Set(5, 6, 4)
//并集
//print(a++b)//Set(5, 1, 6, 2, 7, 3, 8, 4)
//在第一个的基础上去除第二个元素
//print(a--b)//Set(1, 2, 7, 3, 8)
//返回第一个中不同于第二个的元素
//print(a&~b)//Set(1, 2, 7, 3, 8)
//计算符合条件的元素的个数
//print(a.count(_>5))//3
//返回第一个不同于第二个的元素
//print(a.diff(b))//Set(1, 2, 7, 3, 8)
//可变的set,和前面的可变的集合数组类似
//val a=HashSet[Int]()
//map集合和上面的map映射类似,是以键值对显示的,有可变不可变,然后有增删改查的操作
//声明一个可变的map
val a=mutable.HashMap("zhangsan"->18,"lisi"->19)
//print(a.keys)
//遍历map
//通过key值
//for (i<- a.keys)print(i,a(i))//(lisi,19)(zhangsan,18)
//通过模式匹配
//for ((x,y)<-a)print(x,y)//(lisi,19)(zhangsan,18)
//通过foreach
//a.foreach({case (x,y)=>print(x,y)})//(lisi,19)(zhangsan,18)
}
}
类
构造器
Scala 中的每个类都有主构造器,主构造器的参数直接放置类名后面,与类交织在一起。
主构造器会执行类定义的所有语句
每个辅助构造器执行必须以主构造器或者其他辅助构造器的调用开始
对象
object 相当于 class 的单个实例,通常在里面放一些静态的 field 或者 method;在 Scala 中没有静态方法和静态 字段,但是可以使用 object 这个语法结构来达到同样的目的。
object 作用:
1.存放工具方法和常量
2.高效共享单个不可变的实例
3.单例模式
单例对象,不需要 new,用【单例对象名称.方法】调用对象中的方法,调用对象中成员变量
伴生对象
如果有一个 class 文件,还有一个与 class 同名的 object 文件,那么就称这个 objec是 class 的伴生对象,class 是 object 的伴生类;
伴生类和伴生对象必须存放在一个.scala 文件中;
伴生类和伴生对象的最大特点是,可以相互访问
继承
Scala 中,让子类继承父类,与 Java 一样,也是使用 extends 关键字;
继承就代表,子类可继承父类的 field 和 method ,然后子类还可以在自己的内部实现父类没有的,子类特有的 field 和 method,使用继承可以有效复用代码;
子类可以覆盖父类的 field 和 method,但是如果父类用 final 修饰,或者 field 和method 用 final 修饰,则该类是无法被继承的,或者 field 和 method 是无法被覆盖的。
private 修饰的 field 和 method 不可以被子类继承,只能在类的内部使用;field 必须要被定义成 val 的形式才能被继承,并且还要使用 override 关键字。 因为 var 修饰的 field 是可变的,在子类中可直接引用被赋值,不需要被继承;即 val
修饰的才允许被继承,var 修饰的只允许被引用。继承就是改变、覆盖的意思。
Java 中的访问控制权限,同样适用于 Scala
如果实例化了子类的对象,但是将其赋予了父类类型的变量,在后续的过程中,又需要将父类类型的变量转换为子类类型的变量,应该如何做?
首先,需要使用 isInstanceOf 判断对象是否为指定类的对象,如果是的话,则可以使用 asInstanceOf 将对象转换为指定类型;
注意: p.isInstanceOf[XX] 判断 p 是否为 XX 对象的实例;p.asInstanceOf[XX] 把 p转换成 XX 对象的实例
注意:如果没有用 isInstanceOf 先判断对象是否为指定类的实例,就直接用asInstanceOf 转换,则可能会抛出异常;
注意:如果对象是 null,则 isInstanceOf 一定返回 false, asInstanceOf 一定返回null
特质
Scala 中的 trait 是一种特殊的概念;
首先先将 trait 作为接口使用,此时的 trait 就与 Java 中的接口 (interface)非常类似;
在 trait 中可以定义抽象方法,就像抽象类中的抽象方法一样,只要不给出方法的方法体即可;
类可以使用 extends 关键字继承 trait,注意,这里不是 implement,而是 extends ,在Scala 中没有 implement 的概念,无论继承类还是 trait,统一都是 extends;
类继承后,必须实现其中的抽象方法,实现时,不需要使用 override 关键字;
Scala 不支持对类进行多继承,但是支持多重继承 trait,使用 with 关键字即可
模式匹配
Scala 有一个十分强大的模式匹配机制,可以应用到很多场合:如 switch 语句、类型检查等。并且 Scala 还提供了样例类,对模式匹配进行了优化,可以快速进行匹配
样例类
在 Scala 中样例类是一种特殊的类,可用于模式匹配。
定义形式:
case class 类型,是多例的,后面要跟构造参数。
case object 类型,是单例的。
偏函数
1,作为值的函数
将函数作为参数传到方法中,先将函数定义好,直接将函数名放在方法的参数中直接调用。
2,匿名函数
没有函数名,可以直接将函数公式作为参数传递到方法中。
3,柯里化函数
正常方法: def m1(x:Int,y:Int)=x+y
柯里化函数:def m2(x:Int)(y:Int)=x+y
两中方式
(1)m2(1)(2)=3:直接将x,y赋值进去运算
(2)val m3=m2(1)_
m3(2)=3
第一行此时是指x的值为1,后面的 _ 为占位符,后续还有参数传递,这时生成的是一个新的函数
第二行是调用第一行生成的函数,此时赋值2是y的值,因为第一行已经赋予了x的值,所以,此时结果为3,这样可以使函数更灵活,易改变
4,闭包
是一个函数,返回值依赖于声明函数外的一个变量,如上述的m3函数,分开来看,他的返回值依赖于已经定义的x的值,我们也可以这样写
val x=1
val f1(y:Int)=>{x+y}
此时调用f1函数,输入一个y值,输出的是x+y,这就是闭包。
上述1,2,3,4都是高阶函数。
5,隐式转换
它可以允许你手动指定,将某种类型的对象转换成其他类型的对象或者是给一个类增加方法。
它的核心就是定义隐式转换方法,implicit conversion function 在编写的程序中引入就可以使用,就类似于Java中的import,Scala 会根据隐式转换方法的签名,在程序中使用到隐式转换方法接收的参数类型定义的对象时,会自动将其传入隐式转换方法,转换为另外一种类型的对象并返回。这就是“隐式转换”。其中所有的隐式值和隐式方法必须放到 object 中
隐式转换与继承的最大区别就是隐式转换可以解耦合,继承会使两个类产生关系,但是隐式转换不会。
1.同步与异步
同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)
所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了。
换句话说,就是由调用者主动等待这个调用的结果。
而异步则是相反,调用*在发出之后*,这个调用就直接返回了,所以没有返回结果*。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在调用发出后,被调用者*通过状态、通知来通知调用者,或通过回调函数处理这个调用。
阻塞和非阻塞关注的是程序在等待调用结果(*消息,*返回值)时的状态.
阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。