一、简介
Scala是一门多范式的编程语言,一种类似java的编程语言 ,设计初衷是实现可伸缩的语言 、并集成面向对象编程和函数式编程的各种特性。
1.发展背景
联邦理工学院洛桑(EPFL)的Martin Odersky于2001年基于Funnel的工作开始设计Scala。随着大数据领域的兴起,由于他的函数式编程的特点,广泛被应用在spark等 编程中
2.说明
- 类似于java :Scala源代码(.scala)会被编译成java字节码(.class),然后运行在JVM之上,并可以调用现有的Java类库,实现两种语言的无缝对接 ,大体上和Java的运行模式一样,只不过在编码的时候引进了属于自己 的Scala生态的类库,可以在Java的基础上实现更多的功能
- 面向对象 : Scala是面向对象语言,比Java还面向对象。连他的每一个值都是对象
- 函数式编程: Scala也是一种函数式语言,其函数也能当成值来使用。支持高阶函数,允许嵌套多层函数,并支持柯里化 。
二、安装 Scala
Scala的安装依赖于Java环境,安装前先安装jdk1.8
1.windows下 安装
- 官网下载安装包:scala-2.11.12.msi
- 打开cmd窗口输入Scala测试看是否可以使用
2.Linux下 安装
- 官网下载安装包:scala-2.11.12.rpm
- 将安装包上传到Linux服务器
- 解压安装 rpm -ivh scala-2.11.12.rpm
- 输入Scala测试
3.IDEA下 安装插件
- 进入IDEA—进入设置—进入插件—搜索Scala—点击install----安装后重启
- 使用:创建一个新的maven项目
- 在创建的项目上右键点击Add Framework Support ,往下滑勾选Scala
- 在src/main下创建 Scala文件夹
- 点击创建的这个文件夹,右键mark Directory as —> Sources Root
- 然后右键就可以看到Scala Class选项了,即可以创建Scala项目了
三、Scala的基础
由于和Java类似,就类比Java来学习就行了
1.变量
- 在Scala中声明变量只有两种类型,一种是val 类型,一种是var类型,val类型的变量表示是不可变的,var表示是可变的:赋值以后还可以操作改变他的值。例如:
val a1 = 10
在不指定类型的时候,Scala会自动判断值的类型,并使用
2.数据 类型
- 在Scala中是完全面向对象的语言,所以并不区分基本类型和引用类型,这些类型都是对象,我们称之为常用类型。
- 包括7中数值类型:Byte、Char、Short、Int、Long、Float、Double及Boolean类型,还有String类型。
3.数据类型的继承关系
- 在Scala中是完全面向对象的语言,所以并不区分基本类型和引用类型,这些类型都是对象,称之为常用类型。
- Scala中,所有的值都是类对象,而所有的类,包括值类型,都最终继承自一个统一的根类型Any。
- any下分为两种类型,一种是引用类型,一种是非引用类型
- 三个特殊的 类型1:Null是所有引用类型的子类型。2: Nothing,可以作为没有正常返回值的方法的返回类型。3: Unit类型用来标识过程,也就是没有明确返回值的函数。类似于static
如图所示:
4.值类型转换
- 自动类型转化。当Scala程序在进行赋值或者运算时,精度小的类型自动转换为精度大的数据类型,这个就是自动 类型转换 。自动提升原则:表达式结果的类型自动提升为操作数中最大的类型。(byte,short)和char之间不会相互自动转换,但是byte,short,char可以自动转换为int类型。byte,short,char他们三者可以计算,在计算时先转换为int类型
- 强制类型转换:自动类型转换的逆过程,将容量大的数据类型转换为容量小的数据类型。使用时要加上强制转换函数。例
var num:Int = 2.5.toInt
- 值类型和String类型的转换:
var a2:String = 1 + "" .
4.运算符
- 大体和Java一样,只不过不能使用:
--、++
- 没有三目运算符,使用if实现
5.流程控制
- Scala中任意表达式都是有返回值的,也就意味着if else表达式其实是有返回结果的,具体返回结果的值取决于满足条件的代码体的最后一行内容
- 如果缺少一个判断,什么都没有返回,但是Scala认为任何表达式都会有值,对于空值,使用Unit类
- 三目运算符在Scala中的实现:
val result = if (flg) 1 else 0
- scala中的模式匹配类似于java中的switch语法,但是更加强大。模式匹配语法中,采用match关键字声明,每个分支采用case关键字进行声明,当需要匹配时,会从第一个case分支开始,如果匹配成功,那么执行对应的逻辑代码,如果匹配不成功,继续执行下一个分支进行判断。如果所有case都不匹配,那么会执行case_分支,类似于java中default语句
- for循环:
for (i <- 1 to 10){ //前后闭合
println(i)
}
for (i <- 1 until 10){ //前闭后开
println(i)
}
- 特点:循环守卫,即循环保护式(也称条件判断式,守卫)。保护式为true则进入循环体内部,为false则跳过
for (i <- 1 to 10 ; if i%2 ==0){
println(i)
}
- .嵌套循环
for (i <- 1 to 2;j <- 1 to 3){
println("i="+i+" j="+j)
}
- 循环返回值:将遍历过程中处理的结果返回到一个新Vector集合中,i这里是一个代码块,这就意味我们可以对i进行处理
var res = for(i <- 1 to 10 ) yield i
println(res)
输出结果: Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
- Break & Continue Scala使用函数式风格解决break的功能:1。导入包:import scala.util.control.Breaks
将需要使用break的代码块使用 Breaks.breakable包括起来,当break时就使用 Breaks.break(),break
Breaks.breakable{
for(i<- 1 to 9){
if(i == 5){
Breaks.break()
}
println(i)
}
}
println("结束")
6.函数
- 函数的定义:
def 函数名(参数名1: 参数类型1, 参数名2: 参数类型2) : 返回类型 = {
函数体
}
- 函数的返回值:Scala中的函数可以根据函数体最后一行代码自行推断函数返回值类型。果函数明确声明无返回值(声明Unit)
- 函数的参数可以赋默认值:
def f5(p:String = "f5") {
println(p);
}
- 可变长参数 :
def f7(args: Int*) = {
var result = 0
for(arg <- args)
result += arg
result
}
- 惰性函数:当函数被 调用时候才会加载。也叫做懒加载使用lazy 修饰
- 内嵌函数:函数内部还可以定义函数:
def main(args: Array[String]): Unit = {
def test1(): Unit ={
println("测试")
}
test1()
}
- 匿名函数。可以被作为一个值赋值给一个变量
val f1 = (v1:Int,v2:Int) => v1+v2
- 函数可以作为参数也可以作为返回值
1.作为参数:
def test1(f1:(Int,Int)=>Int,v1:Int,v2:Int)={
f1(v1,v2)
}
2.作为返回值
def main(args: Array[String]): Unit = {
val f1: (Int, Int) => Int = test1()
f1(10,20)
}
7.类和对象
- 定义一个类,类中可以有属性和方法:
class Student {
var name:String = _
var age:Int = 0
def show(): Unit ={
println(s"$name,$age")
}
}
-
根据这个自定义的类,创建一个对象:
val student:Student= new Student()
-
构造器。
class 类名(形参列表){ //主构造器(无参构造器)
def this(形参列表){ //辅助构造器
this()//类似于Java中的super(),但Scala中必须写这个,还得放在第一行
.....
}
def this(形参列表){ //辅助构造器可以有多个
this()
.....
}
}
8.封装继承多态、抽象类、接口
- 封装 :用private 修饰属性后,生成的setter/getter方法
def setAge(age:Int):Unit={
if(age > 0 && age < 100){
this.age = age
}
}
def getAge(): Int ={
return age
}
- 继承
class 子类名 extends 父类名{ 类体 }
// 重写父类方法需要使用override修饰
override def xxx(): Unit ={
}
- 多态
var s:Person = new Student
//要测试某个对象是否属于某个给定的类,可以用isInstanceOf方法。
//用asInstanceOf方法将引用转换为子类的引用
if(s.isInstanceOf[Student]){
val student: Student = s.asInstanceOf[Student]
student.study()
}
- 抽象类:在scala中,通过abstract关键字标记不能被实例化的类。方法不用标记abstract,只要省掉方法体即可
abstract class Person{
var name:String = _
def sayHello()
//def sayHello():Unit = ???
}
- 接口 trait
//声明一个特质 trait
trait 特质名{
trait 体
}
//使用特质
1) 没有父类,可以直接继承特质
class 类名 extends 特质1 with 特质2
2) 有父类
class 类名 extends 父类 with 特质1 with 特质2
9.静态属性和静态方法、伴生对象
- 由于Scala没有静态方法,通过object去定义静态方法或者静态对象
- 果类和object在一个scala文件中,则称为object User 是class User的伴生对象
//Scala中伴生对象采用object关键字声明,伴生对象中的全是”静态“内容,可以通过伴生对象名称直接调用
//伴生对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致
//伴生对象中的属性和方法都可以伴生对象(类名)直接调用访问
//从语法角度来讲,所谓的伴生对象其实就是类的静态方法和成员的集合
class User{
}
object User{ //伴生对象
}
- 伴生对象 apply方法:使用伴生对象可以方便的创建对象,只需要覆盖对应的apply方法,如下:
class User {
def test2(): Unit ={
println("test2方法execute")
}
}
object User{
def apply(): User = {
println("11111111")
new User()
}
}
测试:
val user = User() //或者 val user = User.apply()
user.test2()
10.隐式传值和隐式转换
- 隐式函数:隐式变量的优先级高于默认值
implicit def f(i:Double):Int = {
i.toInt
}
var number:Int = 3.5
println(number)
- 隐式变量:隐式变量的优先级高于默认值
implicit def f(i:Double):Int = {
i.toInt
}
var number:Int = 3.5
println(number)
- 隐式类:(可以给一个已经定义好的方法新增加一些方法或属性)
object Test {
def main(args: Array[String]): Unit = {
val yuan = new Yuan
yuan.one()
//现在写一个类给原类增加一个方法
implicit class Zeng(val yuan: Yuan) {
def two() = {
println("这是给原始类增加的一个方法")
}
}
//再测试一下,就可以调用刚才新增加的方法了
yuan.two()
}
}
class Yuan{
def one() = {
println("这是原来的类自己的方法")
}
}
11.异常
- Scala提供try和catch块来处理异常。try块用于包含可疑代码。catch块用于处理try块中发生的异常。可以根据需要在程序中有任意数量的try…catch块。
try {
val r = 10 / 0
} catch {
case ex: ArithmeticException=> println("捕获了除数为零的算数异常")
case ex: Exception => println("捕获了异常")
} finally {
// 最终要执行的代码
}
11.集合
Scala同时支持不可变集合和可变集合,不可变集合可以安全的并发访问
- Array不可变数组
var arr1 = new Array[String](3)
var arr2 = Array(1,2,3) //省略new,底层通过伴生对象的apply方法构建Array对象
//获取元素
//根据下标获取
arr1(0)
//遍历
for (elem <- arr1) {
println(elem)
}
//求数组长度
arr1.size
- ArrayBuffer(可变数组)
var arrayBuffer = new ArrayBuffer[Int]()
//修改
//添加元素
arrayBuffer += 1
arrayBuffer.append(2)
//修改元素 根据下标修改指定位置元素
arrayBuffer(1) = 5
//删除元素 根据下标删除指定位置元素
arrayBuffer.remove(1)
//获取元素
//根据下标获取
arrayBuffer(0)
//遍历
for (elem <- arrayBuffer) {
println(elem)
}
arr1.toBuffer //定长数组转可变数组
arr2.toArray //可变数组转定长数组
注意: arr1.toBuffer 返回结果才是一个可变数组,arr1本身没有变化
arr2.toArray 返回结果才是一个定长数组,arr2本身没有变化
- Tuple (元组):可以装不同类型的容器
元组是不可变的,最多可以存储22个元素
val t1 = ("zhangsan",12,"lisi",11.2)
println(t1._1) //输出:zhangsan
println(t1._2) //输出:12
for (elem <- t1.productIterator) {
println(elem)
}
- List (不可变列表)
Scala中的List和Java List不一样,在Java中List是一个接口,真正存放数据是ArrayList,而Scala的List可以直接存方数据。默认情况下scala的List是不可变的,List属于序列Seq。
val list1 = List(1,2,3,4)
//根据下标获取
list1(0)
:+ 在list后面追加
+: 在list前面添加
//通过 :+ 和 +: 给list追加元素(本身list集合并没有变化)
val list2: List[Int] = list1 :+ 10
println(list2) //输出结果: List(1, 2, 3, 4, 10)
val list3: List[Int] = 20 +: list1
println(list3) //输出结果: List(20, 1, 2, 3, 4)
//符号::表示向集合(新建)中添加元素
//运算时,集合对象一定要放置在最右边,且运算规则是,从右至左
//:::运算符是将集合中的每一个元素加入到集合中去
val list1 = List(1,2,3,4)
var list5 = 6::7::list1::Nil
//分析过程
//1). List() 2). List(List(1,2,3,4))
//3). List(7,List(1,2,3,4)) 4). List(6,7,List(1,2,3,4))
println(list5) //输出结果: List(6, 7, List(1, 2, 3, 4))
var list6= 6::7::list1:::Nil
println(list6) //输出结果: List(6, 7, 1, 2, 3, 4)
- ListBuffer (可变列表)
ListBuffer是可变的list集合,可以添加,删除元素,ListBuffer属于序列
var listBuffer = ListBuffer[Int](1,2,3)
或者
var listBuffer = ListBuffer[Int]
//向集合中添加一个元素
var listBuffer2 = ListBuffer[Int]()
listBuffer2 += 4
listBuffer2.append(5)
println(listBuffer2) //输出结果: ListBuffer(4, 5)
//向集合中添加另外一个集合
var listBuffer = ListBuffer[Int](1,2,3)
listBuffer ++= listBuffer2
println(listBuffer) // 输出结果:ListBuffer(1, 2, 3, 4, 5)
//根据下标移除数据
var listBuffer = ListBuffer[Int](1,2,3)
listBuffer.remove(0)
println(listBuffer) // 输出结果:ListBuffer(2, 3)
//根据下边修改数据
var listBuffer = ListBuffer[Int](1,2,3)
listBuffer(0) = 10
println(listBuffer) // 输出结果 ListBuffer(10, 2, 3)
//根据下标获取指定位置的元素
listBuffer(0)
//遍历
var listBuffer = ListBuffer[Int](1,2,3)
for (elem <- listBuffer) {
println(elem)
}
- Map
val map1 = Map("a1"->"zhangsan","a2"->"lisi") //不可变
println(map1)
val map2 = mutable.Map("a1"->"zhangsan","a2"->"lisi") //可变
println(map2)
val map3 = new mutable.HashMap[String,String]() //创建空的Map
val map2 = mutable.Map("a1"->"zhangsan","a2"->"lisi")
//key不存在,执行添加。否则是修改
map2("a3") = "wangwu"
map2.put("a4","zhaoliu")
//创建一个map
val map2 = mutable.Map("a1"->"zhangsan","a2"->"lisi")
①. map("key")
作用:如果key存在,则返回对应的值
如果key不存在,则抛出异常[java.util.NoSuchElementException]
注意:在java中,如果key不存在则返回null
例如:map2("a1") 输出:zhangsan
map2("x1") 抛出异常
②. map.get("key")
作用:map集合中的get方法会返回Option对象,如果key存在是Some,否则是None
例如:map2.get("a1") 输出:Some(zhangsan)
map2.get("x1") 输出:None
③. map.get("key").getOrElse([默认值])
作用:如果是Some,getOrElse可以返回key对应的value,否则返回默认值
如果getOrElase没有给定默认值,则返回() (()是Unit的唯一实例)
例如:map2.get("a1").getOrElse() 输出:zhangsan
map2.get("x1").getOrElse() 输出:()
map2.get("x1").getOrElse(null) 输出:null
简写:map2.getOrElse("key",null)
④. map.contains("key")
作用: 判断key是否存在,存在返回true,否则返回false
使用技巧:如果我们确定map有这个key,应当使用map(key)
如果不能确定,则先使用map.contains(key)判断
如果只是期望获取一个值,可以使用map.getOrElse(key,默认值)
//删除
方式1:map -= (key,key) // -= 这种形式可以一次删除多个key-value
方式2:map.remove(key)
注意: 如果key存在则删除,不存在也不会报错
键值对遍历
方式1:k,v是两个变量,接收map遍历获取的key-value
for ((k,v) <- map) {
println(k +"\t" + v)
}
方式2:elem获取map遍历出的元祖,elem可以通过操作元祖的形式获取key-value
for (elem <- map) {
println(elem._1 + "\t" + elem._2)
}
键遍历
for (elem <- map.keys) {
println(elem)
}
值遍历
for (elem <- map.values) {
println(elem)
}
- Set
Set存储的元素不允许重复,且不保留顺序,默认是以哈希Set实现
import scala.collection.mutable
val set1 = Set(1,3,2) //不可变
val set2 = mutable.Set(1,3,2) //可变
//三种向集合添加元素的方式
val set2 = mutable.Set(1,2,3) //可变
set2.add(4)
set2 += 5
//两种从集合中移出元素的方式
set2 -= 2
set2.remove(3)
for (elem <- set2) {
println(elem)
}
- Option
object Test8_Option {
def main(args: Array[String]): Unit = {
//Option 是一个抽象类 它有两个子类 Some (有) 和 None (没有)
/**
* Option
* get 作用:获取值
* getOrElse 作用: 获取值,获取到直接返回,如果获取不到则返回默认值
*
* Some对象 调用get方法可以获取里面存储的值
* 调用getOrElse(默认值)方法,还是返回Some对象本身存储的值
*
* None对象 调用get方法报报错
* 调用getOrElse(默认值)方法,返回默认值
*/
var s = Some(1)
println(s.getOrElse(2)) //输出1
var s2 = None
println(s2.getOrElse(2)) //输出2
val option = test1(2) //如果调用一个方法,返回值是Option类型,你能够保证他的实际类型是Some,那么你就可以调用get方法,否则建议使用getOrElse方法
println(option.getOrElse("匿名"))
}
def test1(i:Int) = {
if(i == 1){
Some("张三")
}else{
None
}
}
}