文章目录
前言
你们好我是啊晨
首先呢,内容很多,分为几篇,选择阅读就好,很详细。
请:
一、Scala概述
1、什么是Scala
Scala是一种编程语言,C语言,java语言。
Scala是一种多范式的编程语言,其设计的初衷是要集成面向对象编程和函数式编程的各种特性。Scala运行于Java平台(Java虚拟机),并兼容现有的Java程序。
学习scala的时候,必须安装JDK,同时可以调用JAVA代码(将java代码编译成scala)。
2、为什么要学Scala
优雅:这是框架设计师第一个要考虑的问题,框架的用户是应用开发程序员,API是否优雅直接影响用户体验。
速度快:Scala语言表达能力强,一行代码抵得上Java多行,开发速度快;Scala是静态编译的,所以和JRuby,Groovy比起来速度会快很多。
静态编译:相对于动态编译,编译的时候会将依赖的这一部分都导入进来,在运行的时候可以直接去运行。
能融合到Hadoop生态圈:开发出一个语言就是供大家使用,这个时候需要一个群体去接受。
Hadoop现在是大数据事实标准,Spark并不是要取代Hadoop,而是要完善Hadoop生态。JVM语言大部分可能会想到Java,但Java做出来的API太丑,或者想实现一个优雅的API太费劲。
Scala:效率更高运行速度更快。
二、Scala编译器安装
1、安装JDK
Win+R弹出cmd对话框,因为Scala是运行在JVM平台上的,所以安装Scala之前要安装JDK
2、安装Scala
(1)Windows安装Scala编译器
访问Scala官网http://www.scala-lang.org/下载Scala编译器安装包,目前最新版本是2.13.x,但是目前大多数的框架都是用2.10.x编写开发的,所以这里推荐2.10.x版本,下载scala-2.10.6.msi后点击下一步就可以了
https://www.scala-lang.org/download/
scala-2.11.12.msi,安装版的,就像安装QQ一样点击下一步去安装,会将环境变量自动去配置。
scala-2.11.12.zip解压安装,需要自己去配置环境变量。
安装路径要求:没有空格,英文路径。
安装完成去CMD命令框去验证,scala -version 如果提示“如果提示不是内部或外部命令”重启下电脑。
(2)Linux安装Scala编译器
下载Scala地址http://downloads.typesafe.com/scala/2.10.6/scala-2.10.6.tgz然后解压Scala到指定目录
tar -zxvf scala-2.10.6.tgz -C /usr/java
配置环境变量,将scala加入到PATH中
vi /etc/profile
export JAVA_HOME=/usr/java/jdk1.7.0_45
export PATH=
P
A
T
H
:
PATH:
PATH:JAVA_HOME/bin:/usr/java/scala-2.10.6/bin
(3)Scala开发工具安装
目前Scala的开发工具主要有两种:Eclipse和IDEA,这两个开发工具都有相应的Scala插件,如果使用Eclipse,直接到Scala官网下载即可http://scala-ide.org/download/sdk.html。
由于IDEA的Scala插件更优秀,大多数Scala程序员都选择IDEA,可以到
http://www.jetbrains.com/idea/download/下载社区免费版,点击下一步安装即可,安装时如果有网络可以选择在线安装Scala插件。这里我们使用离线安装Scala插件:
1.安装IDEA,点击下一步即可。由于我们离线安装插件,所以点击Skip All and Set Defaul
2.下载IEDA的scala插件,地址http://plugins.jetbrains.com/?idea_ce
https://plugins.jetbrains.com/plugin/1347-scala/versions
3.安装Scala插件(不同版本的IDEA要安装对应版本的scala插件):Configure -> Plugins -> Install plugin from disk(离线安装) -> 选择Scala插件 -> OK -> 重启IDEA
查找IDEA对应的scala插件版本
没有安装插件,显示 ,点击install就是在线安装,系统会自动匹配版本去安装。
选择对应版本插件下载,将下载的文件拷贝的IDEA安装目录下的plugins目录
桌面图标右键,属性
安装
创建JAVA工程,src->右键new的时候没有新建scala的选项,导入SDK
三、Scala基础
1、声明变量
回顾下JAVA:类中的属性/方法,使用修饰符可以条件static关键字。
satic+属性、static+方法:表示这是一个静态的属性和方法,可以通过类名.属性/方法去调用。
属性、方法:表示这个一个属性和方法,需要通过对象.属性/方法调用。
main方法是程序的入口,它是static的类型。
scala中:class中的属性/方法都是非static的,只能通过new出的对象去访问,对象.属性/方法调用,那么class中的main方法还能执行吗?不能执行。因此class文件中的main方法也就没什么用
scala中object 中所有成员变量和方法默认都是 static 的所以 可以直接访问main方法。
scala 中没有 static 关键字对于一个class来说,class所有的方法和成员变量在实例被 new 出来之前都是无法访问的因此class文件中的main方法也就没什么用了,scala object 中所有成员变量和方法默认都是 static 的所以 可以直接访问main方法。
Scala声明变量有两种方式,一个用val,一个用var。
val / var 变量名 : 变量类型 = 变量值。
val定义的值是不可变的,它不是一个常量,是不可变量,或称之为只读变量。
在scala代码中可以省略分号:省略分号的条件:同一行有多个语句不能省略分号。
如果一行只有一条语句,那么可以省略分号。
package cn.bigdata.scala
object VariableDemo {
//def 定义一个方法
//main 方法名称
//()括号中为参数
//args 形参名称
//:冒号后面是参数类型
//Array[String] Array数组 String数组的元素类型
//Unit 返回值类型,Unit表示无返回值,如果没有返回值:Unit =可以省略
def main(args: Array[String]) {
//使用val定义的变量值是不可变的,相当于java里用final修饰的变量
//语句后面可以省略 分号
val i = 1
i = 2//此处会报错,因为val不允许初始化后再次修改i变量的引用
//使用var定义的变量是可变得,在Scala中鼓励使用val
var s = "hello"
//Scala编译器会自动推断变量的类型,必要的时候可以指定类型
//变量名在前,类型在后
val str: String = "bigdata"
val w1 = "World"
val w2 = "World"
println("Hello World")
println(s"Hello $w1")
println("Hello $w2")
printf("Hello %s", w2)
val w3 = "World"
val w4 ="Hello World"
println("Hello "+w3)
printf("%s",w4)
//1
//print将语句输出到控制台,输出以后不换行
print("hello word")
print("hello tom")
//println将语句输出到控制台,输出以后换行
println("hello jerry")
println("hello lilie")
//定义一个变量
val a = "abc"
val b = 2.13
//现在输出a这个变量
println(a)
println("a = "+a)
//2
//使用 $变量名称 获取变量的值,需要将 $变量名称 包含在双引号中,同时在println的括号里多一个s
println(s"$a")
println(s"a = $a")
//如果 $变量名称 后面还要拼接字符串,需要空格隔开,$变量名称+空格+字符串
//如果 $变量名称 前面拼接字符串,不需要空格
println(s"a = qwe$a bbbb")
//3
//使用 printf 输出到控制台,字符里面使用 %s、%f、%c等,字符串后面跟上对于的变量名称,printf 和语言中的一样
//字符串里面%的类型要和,后面变量的类型一一对应,并且类型要匹配
printf("a = %s, b = %f",a,b)
}
}
尖叫提示:
1、scala默认为匿名变量分配val
2、val定义的变量虽然不能改变其引用的内存地址,但是可以改变其引用的对象的内部的其他属性值。
val 变量名称 = 变量值
变量值的类型:数值类型(AnyVal),这个值就不能改变,因为内存存储就是这个变量的值
变量值的类型:引用类型(AnyRef),内存中存储的就是对象的地址,这个对象的地址不能改变,但是可以改变这个对象中的属性
3、为了减少可变性引起的bug,应该尽可能地使用不可变变量。变量类型可以省略,解析器会根据值进行推断。val和var声明变量时都必须初始化。
2、常用类型
Scala和Java一样,有8种数据类型(AnyVal): 7种数值类型Byte、Char、Short、Int、Long、Float和Double(无包装类型)和一个Boolean类型
Boolean | true 或者 false |
---|---|
Byte | 8位, 有符号 |
Short | 16位, 有符号 |
Int | 32位, 有符号 |
Long | 64位, 有符号 |
Char | 16位, 无符号 |
Float | 32位, 单精度浮点数 |
Double | 64位, 双精度浮点数 |
String | 其实就是由Char数组组成 |
与Java中的数据类型不同,Scala并不区分基本类型和引用类型,所以这些类型都是对象,可以调用相对应的方法。
3、常用类型结构图
Scala中,所有的值都是类对象,而所有的类,包括值类型,都最终继承自一个统一的根类型Any。统一类型,是Scala的又一大特点。更特别的是,Scala中还定义了几个底层类(Bottom Class),比如Null和Nothing。
1)Null是所有引用类型AnyRef的子类型,而Nothing是所有类型的子类型。Null类只有一个实例对象,null,类似于Java中的null引用。null可以赋值给任意引用类型,但是不能赋值给值类型。
Null是所有AnyRef的子类,在scala的类型系统中,AnyRef是Any的子类,同时Any子类的还有AnyVal。对应java值类型的所有类型都是AnyVal的子类。所以Null可以赋值给所有的引用类型(AnyRef),不能赋值给值类型,这个java的语义是相同的。 null是Null的唯一对象。
2)Nothing,可以作为没有正常返回值的方法的返回类型,非常直观的告诉你这个方法不会正常返回,而且由于Nothing是其他任意类型的子类,他还能跟要求返回值的方法兼容。
Nothing是所有类型的子类,也是Null的子类。Nothing没有对象,但是可以用来定义类型。例如,如果一个方法抛出异常,则异常的返回值类型就是Nothing(虽然不会返回)
3)Unit类型用来标识过程,也就是没有明确返回值的函数。 由此可见,Unit类似于Java里的void。Unit只有一个实例,(),这个实例也没有实质的意义。
在JAVA中Object是所有类的超类。
在scala中,Any类是所有类的超类,Any有两个子类:AnyVal和AnyRef。对应Java直接类型的scala封装类,如Int、Double等,AnyVal是它们的基类;对应引用类型,AnyRef是它们的基类。
4、算数操作符重载
±*/%可以完成和Java中相同的工作,但是有一点区别,他们都是方法。你几乎可以用任何符号来为方法命名。
scala> 1 + 2
等同于:
scala> 1.+(2)
尖叫提示: Scala中没有++、–操作符,需要通过+=、-=来实现同样的效果。
5、条件表达式
Scala的的条件表达式比较简洁,但是if…else…运算操作有返回值,例如:
package cn.bigdata.scala
object ConditionDemo {
def main(args: Array[String]) {
val x = 1
//判断x的值,将结果赋给变量y
val y = if (x > 0) 1 else -1
//打印y的值
println(y)
//支持混合类型表达式
val z = if (x > 1) 1 else "error"
//打印z的值
println(z)
//如果缺失else,相当于if (x > 2) 1 else ()
//返回的是小括号()
val m = if (x > 2) 1
println(m)
//在scala中每个表达式都有值,scala中有个Unit类,写做(),相当于Java中的void
val n = if (x > 2) 1 else ()
println(n)
//if和else if
val k = if (x < 0) 0
else if (x >= 1) 1 else -1
println(k)
}
}
scala中没有三目运算符,因为根本不需要。scala中if else表达式是有返回值的,如果if或者else返回的类型不一样,就返回Any类型(所有类型的公共超类型)。
如果缺少一个判断,什么都没有返回,但是Scala认为任何表达式都会有值,对于空值,使用Unit类,写做(),叫做无用占位符,相当于java中的void。
尖叫提示: 行尾的位置不需要分号,只要能够从上下文判断出语句的终止即可。但是如果在单行中写多个语句,则需要分号分割。在Scala中,{}快包含一系列表达式,其结果也是一个表达式。块中最后一个表达式的值就是块的值。
6、块表达式
package cn.bigdata.scala
object BlockExpressionDemo {
def main(args: Array[String]) {
val x = 0
//在scala中{}中包含一系列表达式,块中最后一个表达式的值就是块的值
//下面就是一个块表达式
val result = {
if (x < 0){
-1
} else if(x >= 1) {
1
} else {
"error"
}
}
//result的值就是块表达式的结果
println(result)
val res01 = {
1+2
4+5
4>5
}
println(res01)
}
}
7、循环
在scala中有for循环、while、do~while循环,用for循环比较多
(1)while表达式
Scala提供和Java一样的while和do~while循环,与If语句不同,While语句本身没有值,即整个While语句的结果是Unit类型的()。
while循环
var n = 1;
val while1 = while(n <= 10){
n += 1
}
println(while1)
println(n)
尖叫提示: scala并没有提供break和continue语句来退出循环,如果需要break,可以通过几种方法来做1、使用Boolean型的控制变量 2、使用嵌套函数,从函数中return 3、使用Breaks对象的break方法。
break:退出当前循环,执行循环体后面的语句
continue:不再执行当前循环中continue后面的语句,继续下次循环
(2)for表达式
for循环语法结构:for (i <- 表达式/数组/集合)
to,until都表示范围,二者有何区别?
to为包含上限的闭区间,如:1 to 3,Range为1,2,3;
until不包含上限,如:1 until 3, Range为1,2
在需要使用从0到n-1的区间时,可以使用until方法。
for(i <- 1 to 3){
print(i)
}
println()
for(i <- 1 until 3) {
print(i)
}
println()
语法结构for (i <- 表达式)让变量i遍历<-右边的表达式的所有值。至于这个遍历具体如何执行,则取决于表达式的类型。对于Scala集合比如Range而言,这个循环会让i依次取得区间中的每个值。
说明: 在for循环的变量之前并没有val或var的指定。该变量的类型是集合的元素类型。循环变量的作用域一直持续到循环结束。
在Scala中,对循环的使用并不如其他语言那么频繁。通常我们可以通过对序列中的所有值应用某个函数的方式来处理它们,而完成这项工作只需要一次方法调用即可。
说明:Scala并没有提供break或continue语句来退出循环。那么如果需要break时我们该怎么做呢,有如下几个选择:
1、使用Boolean型的控制变量;
2、使用嵌套函数–你可以从函数当中return;
3、使用Breaks对象中的break方法:
package cn.bigdata.scala
object ForDemo {
def main(args: Array[String]) {
//for(i <- 表达式),表达式1 to 10返回一个Range(区间)
//每次循环将区间中的一个值赋给i
for (i <- 1 to 10)
println(i)
for (i <- 1 until 10)
println(i)
//定义一个Range,步长是-1,前闭后开区间
for (i <- Range(10,1,-1)
println(i)
//使用reverse
for (i <- (1 to 10 ).reverse)
println(i)
//for(i <- 数组)
val arr = Array("a", "b", "c")
//i是数组的元素,不是下标
for (i <- arr)
println(i)
//使用下标遍历数组,j是下标,arry(j) 使用小括号下标获取数组元素
for(j <- 0 until(arry.length))
println(arry(j))
//输出1到10之间的偶数
for(i <- 1 to 10){
if(i%2 ==0)
println(i)
}
}
//引入保护式_守卫(也称条件判断式)保护式满足为true则进入循环内部,满足为false则跳过,类似于continue
//每个生成器都可以带一个条件,注意:if前面没有分号
for(i <- 1 to 10 if i%2 == 0) println(i)
//引入变量 注意: j前面有分号
for(i <- 1 to 3; j = 4 - i) {
print(j + " ")
}
println()
//高级for循环
//每个生成器都可以带一个条件,注意:if前面没有分号 j前面有分号
for(i <- 1 to 3; j <- 1 to 3 if i != j)
print((10 * i + j) + " ")
println()
for(i <- 1 to 9; j <- 1 to i) println(i+"*"+j+"="+i*j)
//for推导式:如果for循环的循环体以yield开始,则该循环会构建出一个集合
//每次迭代生成集合中的一个值
val v = for (i <- 1 to 10) yield i * 10
println(v)
//下面两种有什么区别
val v = for (i <- 1 to 10) yield if(i %2 ==0 ) i
println(v)
val v2 = for (i <- 1 to 10 if i %2 ==0 ) yield i
println(v2)
//可以使用任意多的定义,引入可以在循环中使用的变量
for(i <- 1 to 3; from = 4 - i; j <- from to 3) {
print((10 * i + j) + " ");
}
//输出九九乘法表
for(i <- 1 to 9){
for(j <- 1 to i){
print(i+"*"+j+"="+i*j+"\t")
}
println()
}
}
}
//需要导入包
import scala.util.control.Breaks._
breakable {
for (...) {
if (...) break; //退出breakable块
...
}
}
//continue例子
for (i <- 1 to 10) {
breakable{
if (i == 2) break
println(i)
}
}
//break例子
breakable{
for(i<-1 to 10){
if(i==5){
break
}
println(i)
}
}
//retrun
for(i <- 1 to 10){
if(i==2) return
println(i)
}
尖叫提示: {}和()对于for表达式来说都可以。for 推导式有一个不成文的约定:当for 推导式仅包含单一表达式时使用原括号,当其包含多个表达式时使用大括号。值得注意的是,使用原括号时,早前版本的Scala 要求表达式之间必须使用分号。
倒序输出
//Range(起始,结束,步长)前闭后开
for(i <- Range(10 , 1,-1)){
println("i = "+i)
}
for(i <- (1 to 10).reverse){
println("i = "+i)
}
8、调用方法和函数
Scala中的 + - * / % 等操作符的作用与Java一样,位操作符 & | ^ >> <<也一样。只是有
一点特别的:这些操作符实际上是方法。例如:
a + b
是如下方法调用的简写:
a.+(b)
a 方法 b可以写成 a.方法(b)
9、定义方法和函数
(1)定义方法
方法的返回值类型可以不写,编译器可以自动推断出来,但是对于递归函数,必须指定返回类型
(2)定义函数
(3)方法和函数的区别
方法:形参 返回值
函数:入参 出参
在函数式编程语言中,函数是“头等公民”,它可以像任何其他数据类型一样被传递和操作
案例:首先定义一个方法,再定义一个函数,然后将函数传递到方法里面
package cn.bigdata.scala
object MethodAndFunctionDemo {
//def 定义一个方法
//main 方法名称
//()括号中为参数
//args 形参名称
//:冒号后面是参数类型
//Array[String] Array数组 String数组的类型
//Unit 返回值类型,Unit表示无返回值
def main(args: Array[String]): Unit = {
test1
test1()
test2
//test2()报错不能加()
test3("abc", 12)
val res01 = test4("abc", 12)
println("res01 = "+res01)
val res02 = f1(1,2)
println("res02 = "+res02)
val res03 = f2(1,2)
println("res03 = "+res03)
}
//def 定义一个方法
// test1方法名称 ()可以放参数,里面空的,无参
//:冒号后面是参数类型
//Unit 返回值类型,Unit表示无返回值
def test1():Unit = {
println("这是一个无参的方法")
}
//def 定义一个方法
//省略小括号,省略掉返回值
def test2{
println("这是一个无参的方法")
}
def test3( a:String,b:Int){
println("test3这是一个有参的方法"+a+"~"+b)
}
def test4(a:String,b:Int):String={
def abc{
println("adf")
}
abc
234
123
"abc"
}
//定义一个方法
//方法m1参数要求是一个函数,函数的参数必须是两个Int类型
//返回值类型也是Int类型
def m1(f: (Int, Int) => Int) : Int = {
f(2, 6)
}
def m2(f: (Int, Int) => Int,x:Int,y:Int) : Int = {
f(x, y)
}
//定义一个函数f1,参数是两个Int类型,返回值是一个Int类型
val f1 = (x: Int, y: Int) => x + y
//再定义一个函数f2
val f2 = (m: Int, n: Int) => m * n
//main方法
def main(args: Array[String]) {
//调用m1方法,并传入f1函数
val r1 = m1(f1)
println(r1)
//调用m1方法,并传入f2函数
val r2 = m1(f2)
println(r2)
//调用m2方法,并传入f1函数
val r3 = m2(f1,2,3)
println(r3)
//调用m2方法,并传入f2函数
val r4 = m2(f2,2,3)
println(r4)
}
}
(4)将方法转换成函数(神奇的下划线)
10、懒值
当val被声明为lazy时,他的初始化将被推迟,直到我们首次对此取值,适用于初始化开销较大的场景。
- lazy示例:通过lazy关键字的使用与否,来观察执行过程
object LazyDemo {
def init(): String = {
println(“init方法执行”)
“嘿嘿嘿,我来了~”
}
def main(args: Array[String]): Unit = {
lazy val msg = init()
println(“lazy方法没有执行”)
println(msg)
}
}
未完。。
感谢观看,下篇持续更新