目录
题外话:
为什么要学习Scala语言?
Scala语言是Spark语言的基础,不会scala就无法入门spark语言。spark语言是大数据开发必备的语言,通过RDD运算对离线数据或者微批次数据进行处理。学大数据就一定要学Spark!
复制代码
1、scala语言特点
1. 面向对象的语言
2. 面向函数式编程的语言
3. 静态的语言
4. 扩展性良好
5. 支持Actor并发模型(多线程)
复制代码
2、函数式编程
1. 函数式编程,也是一种编程思想
2. 纯粹的函数式编程语言编写的函数没有变量,输入的值确定,那么输出的结果是确定的。没有副作用
3. 可以使用变量的程序设计语言,是有副作用
一个带有副作用的函数不仅有一个返回值,还可能做了:
修改一个变量
直接修改数据结构
设置一个对象的成员
打印到终端或者读取用户输入
读取或写入一个文件
在屏幕上绘画
抛出一个异常或以一个错误终止
4. 函数是一等公民、是有返回值的,和基本类型的值、对象的地位是一样的,可以直接拿来使用。
5. 以表达式为中心
复制代码
函数式编程的优势:
- 代码简洁,开发速度快
- 接近自然语言,易理解
- 易于代码管理
- 适合并发编程
- 适用于热升级
复制代码
3、scala的安装
1)上传、解压、更名
[root@qianfeng01 ~]# tar -xvf scala-2.11.8.tar -C /usr/java
[root@qianfeng01 ~]# cd /usr/local
[root@qianfeng01 local]# mv scala-2.11.8 scala
2)配置系统环境变量
[root@qianfeng01 local]# vi /etc/profile
........省略......
export SCALA_HOME=/usr/local/scala
export PATH=$SCALA_HOME/bin:$PATH
3)保存退出后重新加载
[root@qianfeng01 local]# source /etc/profile
4)验证
[root@qianfeng01 local]# scala -version
复制代码
4、scala语言的简介
Scala的REPL就是scala的命令行交互界面。
R->read 表示可以接受用户的输入数据
E->evaluate 可以计算数据
P->print 可以打印数据
L->loop 可以循环操作
复制代码
演示
scala> 1
res0: Int = 1 # res0 是内置提供的变量,用来存储回车后的值
scala> res0
res1: Int = 1 # 可以打印变量的值 值又存到res1里了
scala> res0+res1 # 表达式
res3: Int = 2
scala> val age:Int = 28 #使用val定义变量age 同时指定类型 并赋值
age: Int = 28
scala> 1+1*10
res4: Int = 11
scala> val num = 34 # scala可以推断没有指定类型的变量的类型,也可以证明数字的默认类型是Int
num :Int = 34
复制代码
上面那个是windows中安装了scala的环境,在cmd中演示的
下面这种是在windows编译。个人喜好在idea中整合scala敲。
1. 在D盘的dir目录下,创建一个hello.scala源文件,内容如下
object HelloWorld{
def main(args:Array[String]):Unit ={
print("you are best")
}
}
2. 编译源文件: 语法格式:scalac 源文件名
D:\dir> scalac hello.scala
注意: 结果会产生两个class文件
3. 运行字节码文件: 语法格式:scala 字节码文件名(不带后缀)
D:\dir> scala HelloWorld
复制代码
scala和idea的整合
1. file-->settings-->plugins-->搜索scala
2. 选择install, 读条
3. apply,如果有重启,那就重启
复制代码
5、scala编程语言的规范
1. 源文件扩展名(后缀)必须是.scala
2. 源文件名和里面的代码的最顶层的类名一致,区分大小写、所有源文件编码必须是 UTF-8
3. scala中的标识符要符合驼峰命名法、尽量做到见名知意(望文知意)
4. scala的语法缩进要使用空格、不要使用tab键(注意:Idea自动将tab键转成了2个空格,可以百度自行调整四个空格)
5. 单行长度不建议超出150字符、也就是不要超出可视化范围
6. 花括号的用法和java一致、该换行就换行。
7. 空行的目的,用于逻辑分组。比如 方法和成员变量之间应该多一个空行
8. 在简单的表达式上,能省略花括号就省略。
9. Scala的每句话说完,不需要使用分号,换行即可。
10. 注释风格,和java一样
// 单行注释
/* 多行注释*/
/** 文档注释*/
复制代码
6、scala的类型体系(重点!)
1. scala的超类(父类,基类)是Any
2. Any下有两个直接子类型,分别是AnyVal 和AnyRef
Byte-->Short-->Int-->Long-->Float-->Double
Char
boolean
Unit
4. AnyRef包括了java的所有引用类型、Scala的所有集合类型、以及Scala的其他引用类型,以及Null类型
5. Nothing是Scala的所有其他类型的子类型,位于类型层次的最底层
6. Null类型只有一个实例值是null
scala> val a = null
a: Null = null
7. Unit和java的void相似,是一种类型,也只有一个实例()
scala> val a = ()
a: Unit = ()
复制代码
7、变量的用法:
1. 需要使用val和var来定义、而且必须同时使用等号,进行初始化
2. 定义变量时、可以指定类型、也可以不指定类型(scala编译器会自动推断类型)
val num1 = 10 scala会自动推断类型
指定类型的写法: val num2:Long = 10
3. val声明变量时、可以使用lazy修饰、还有特点就是变量不能再次赋值,相当于用了final修改该变量
4. var声明变量时,不能使用lazy修饰、变量可以再次赋值
5. 变量一旦初始化后、类型确定,不能再赋值其他类型的值
6. scala推荐使用val来定义变量,因为:
- 更安全
- 可读性更高
- 节省内存资源
val 对应的单词是value,即值,常量的含义,因此可以理解为是final修饰的。实际上在转成.class时,确实是final修饰的变量
var 对应的单词是variable,即可变的,易变的,多变的含义。 因此对应的变量可以多次赋值。
复制代码
8、数值类型
计算所遵循的原则:
1. 不同的数值类型计算时,需要转为同一种类型进行计算
2. 如果是小于Int类型的两种类型计算、都转为Int类型计算
3. 如果是其他情况的两种不同类型,那么就会转为较大类型的数据进行计算
数值类型有八种、可以细分如下:
整型四种:Byte 、Short 、Int、Long、
- 整型的默认类型是Int
- Long的值需要添加L/l
- 也可以赋值16进制的数字 如: val num = [0x|0X]ffff 就是255的16进制
浮点型两种:Float、Double
- 浮点型的默认类型是Double
- Float的数字需要添加F/f
- 赋值时也可以这样:
省略0的写法: .512 .34F
科学计数法: 5.12e2
字符类型: Char
- 赋值时需要使用单引号,单引号里只能有且只有一个字符
- 也可以赋值整数、范围是0~65535
var ch1 : Char = 65
- 也可以赋值16进制形式
如: var ch1 = '\u0041' 是字符A的unicode编码
布尔类型: Boolean
- 只有两个值,true和false,用于判断条件是否成立
- 一般用于分支或者是循环结构中
复制代码
String类型
1. Scala的String的本质就是java的String
“zhangsan”.getClass.toString
2. 赋值使用双引号
3. 也可以使用三引号,进行换行赋值
var name = """
|zhangsan
|lisi
|"""
复制代码
Option类型
用来表示一个值是可选的,实际上就是有值和无值两种。
有值的话,是Some(value)
无值的话,就是None
总之:Option类型就两个值:Some(value)、None
复制代码
9、类型转换
1. 数值类型
-- 隐式转换: 小类型的变量赋值给大类型的变量,会自动在前面添加相应位数的0或1
比如: var a: Byte = 10 二进制: 0000 1010
var b: Short = a 二进制: 0000 0000 0000 1010
b变量是16位, 此时是Byte类型的10在前面自动添加8个0,进行补全
-- 强制转换: 大类型的变量赋值给小类型的变量时,必须强制转换
java中语法: 小类型名 变量名 = (小类型名)大类型变量
scala中,必须调用相关的转换方法,如
val num1 = 10;
val num2:Short = num1.toShort
2. 数值类型与字符串之间的转换
- 数值类型转字符串,直接使用拼接符号+,拼接即可
var age = 28
var str = age+"" // "28"
var str = age+"hello" // "28hello"
- 字符串转数值类型,需要掉相应的转换方法
var str = "28";
str.toInt // 转成了Int类型的28
注意:非纯数字的字符串无法转成数字类型,NumberFormatException
复制代码
10、常用的运算符
1. 算数运算符: + 、- 、* 、/ 、%
计算原则:
--(1) 不同类型的数据做运算时,会转成较大类型的数据,再运算
--(2) 两个类型都小于Int时,转成Int类型,再运算
比如: 18.0/6 运算逻辑:6要先转成double的6.0, 然后和18.0做运算。结果类型是Double,所以结果是3.0
18/7 结果类型是Int,所以结果是2
也可以这样理解:两个整形运算,除法符号是取整操作
只要有一个是浮点型,就是正常除法运算
2. 关系运算符: >,>=,<,<=,==,!=
-- 用来判断两边的数值的关系是否成立,只有两种结果,true、false
-- 通常用于分支结构和循环结构,充当条件
3. 赋值运算符: = 、+=、-=、*=、/=、%=
4. 短路逻辑运算符: 一般用于连接多个关系表达式的,结果也是true、false
&& : 一假则假,全真为真
|| : 一真则真, 全假为假
! : 非真即假,非假即真
短路逻辑: 当前面的条件可以确定结果后,后面的条件不再执行
5. 位运算符 : 1为 真 0为假
& : 与 口诀: 一0则0,全1为1
| : 或 口诀: 有1则1,全0为0
^ : 异或 口诀: 相同为0,不同为1
>> : 有符号右移, 原来是正,结果就是正,原来是负,结果就是负数
<< :有符号左移, 原来是正,结果就是正,原来是负,结果就是负数
有符号移动时,正数高位补0,负数高位补1
>>>:无符号右移: 表示所有二进制位上的数向右移动,高位补0
复制代码
11、流程控制语句
11.1 Scala表达式(重要)
1. 在scala中,你所看到的完整的语句,都是一个表达式
2. 只要是表达式,那么一定有返回值
比如:
a + b 返回的就是两个变量的和
println("aaaa") 打印语句也是一个表达式,返回Unit类型,值为()
throw new Exception(); 也是一个表达式,返回Nothing
3. 常见的表达式:
- 块表达式
- if表达式
- 循环表达式
- 异常表达式
- Actor并发模型表达式
4. 块表达式:
-- 即{}整体,有返回值,返回值为{}中的最后一个表达式的返回值
5. 赋值表达式
-- 有返回值,返回值为Unit类型
scala> var c = (b+=a)
c: Unit = ()
复制代码
11.2 分支结构
if分支的语法与java语法一样,有以下三种写法:
1. 只有一条分支的写法
if(条件表达式){
逻辑体
}
2. 两条分支带else的写法
if(条件表达式){
逻辑体
}else{
逻辑体
}
3. 带else if的写法
if(条件表达式){
逻辑体
}else if(条件表达式){
逻辑体
}else if(条件表达式){
}.....
小贴士:
1. 只要某一个分支的条件表达式成立,那么剩下的分支直接跳过不执行。
2. 如果带有else模块,那么一定会执行其中一条分支,如果不带else,可能一条分支都不执行。
3. 针对于scala来说,if分支结构本身就是表达式,表达式的值是结构中所有的花括号的最后一个表达式的共同的父类
复制代码
11.3循环结构
说明:
1. 循环结构本身也是一个表达式,有值。
2. 循环结构的种类:
-- while : 条件成立时,才会执行循环体
-- do-while :先执行一次循环体,再判断条件是否成立
-- for循环:
复制代码
11.3.1while和do-while
while(条件表达式){
循环体
}
do{
}while(条件表达式)
复制代码
练习
package com.qf.scala.day03
/**
* 计算100以内的奇数之和
*/
object _02WhileDemo {
def main(args: Array[String]): Unit = {
var num = 1;
var sum = 0;
while(num<100){
if(num%2==1){
sum+=num;
}
num+=1;
}
println(sum)
}
}
复制代码
package com.qf.scala.day03
import scala.io.StdIn
object _03DoWhileDemo {
def main(args: Array[String]): Unit = {
var password = "";
do{
println("请输入密码")
password = StdIn.readLine();
}while(password!="123456")
println("密码正确")
}
}
复制代码
11.3.2 for循环
说明:
循环时的范围表示方式,有两种,分别是使用to或者是until
1. to的写法:
1 to 10
1.to(10)
特点: 包含10. 是一个闭区间
2. until的写法
1 until 10
1.until(10)
特点: 不包含10, 是一个左闭右开区间
复制代码
for循环的语法结构
小贴士: java中的for循环结构有两种写法
一种是经典for循环写法 :
for(循环因子的声明和初始化;循环条件;向着循环结束的方向变化的表达式){}
一种是增强for循环写法
for(类型名 变量名:集合|数组){}
注意:scala里的for循环,相当于java中的增强for循环写法
for( 变量名 <- 集合|数组|Range|表达式 ){
循环体
}
复制代码
练习
package com.qf.scala.day03
object _04ForDemo {
def main(args: Array[String]): Unit = {
//练习1: 打印 1 到 10
for(num <- 1 to 10){
print(num+"\t")
}
println
for(num <- 1 until 11){
print(num+"\t")
}
println
//练习2: 遍历数组
val names = Array("zhangsan","lisi","wangwu","zhaoliu")
for(name <- names){
println(name)
}
//练习3: 打印一个字符串中的所有的字符
val str = "helloworld"
for( i <- 0 until str.length){
print(str(i)+" ")
}
println
//练习4: 计算1到10的和
var sum = 0;
for(i<-1 to 10){
sum+=i;
}
println(sum)
//练习5:
// for循环本身就是一个表达式,因此有值,
// 值比较特殊,是一个Vector集合, 比如循环10次,则是10个循环的最后一行的值的集合
// 那么如何获取这个集合的值, 需要使用yield,
// yield的位置位于{}的前面,小括号的后面
// 而下面的循环结构的最后一行,返回的都是(),因此集合里有10个()
var sum1 = 0;
val result = for(i<-1 to 10) yield{
sum1+=i;
}
//遍历for循环的结果
for(i<-result){
println(i)
}
}
}
复制代码
for循环的多层嵌套
package com.qf.scala.day03
/**
* for循环可以进行嵌套
*/
object _05ForDemo {
def main(args: Array[String]): Unit = {
/**
* 练习1: 打印
* *****
* *****
* *****
* *****
*/
for(i<- 1 to 4){
for(j<- 1 to 5){
print("*")
}
println
}
/**
* 练习1: 打印
* *
* **
* ***
* ****
* *****
*/
for(i <- 1 to 5){
for(j <- 1 to i){
print("*")
}
println
}
/**
* 练习3: 打印乘法口诀表
*/
for(i <- 1 to 9){
for(j <- 1 to i){
print(i+"*"+j+"="+i*j+"\t")
}
println
}
}
}
复制代码
多层简写
package com.qf.scala.day03
/**
* for循环可以进行嵌套
*/
object _06ForDemoSimple {
def main(args: Array[String]): Unit = {
/**
* 练习1: 打印
* *****
* *****
* *****
* *****
*/
for(i<- 1 to 4;j<- 1 to 5){
print("*")
if(j==5){
println
}
}
/**
* 练习1: 打印
* *
* **
* ***
* ****
* *****
*/
for(i <- 1 to 5;j <- 1 to i){
print("*")
if(i==j){
println
}
}
/**
* 练习3: 打印乘法口诀表
*/
for(i <- 1 to 9;j <- 1 to i){
print(i+"*"+j+"="+i*j+"\t")
if(i==j){
println
}
}
/**
* 练习4: 打印10次10以内的所有奇数
*/
for( i <- 1 to 10;j <- 1 to 10;if j%2==1){
print(j+" ")
}
}
}
复制代码
循环结构的终止
说明:java中的循环结构可以使用break,表示结束循环结构,continue结束当次循环,
特殊情况:也可以使用return来结束整个方法
那么,scala中也可以使用以下方式来结束循环:
1. 循环条件 , 就是控制循环的条件表达式
2. return关键字: 注意,该方式会结束整个方法
3. break: 但是需要配合breakable语句块来使用
复制代码
练习:
package com.qf.scala.day03
import scala.util.control.Breaks._
object _07ForDemo {
def main(args: Array[String]): Unit = {
println("-------练习1:------------")
// 使用循环条件来结束循环
var i = 0;
while(i<10){
println(i);
i += 1
}
println("-------练习2:------------")
// 使用return 关键字来结束循环,注意,会结束整个方法
// 当num等于5时,结束循环结构
// for( num <- 1 to 10){
// println(num)
// if(num==5){
// return
// }
// }
// println("-------over------------")
println("-------练习3:------------")
/**
* 使用break 来结束循环结构, 需要配合breakable语句块
* 练习: 当num等于5,结束循环
*/
breakable{
for( num <- 1 to 10){
println(num)
if(num==5){
break
}
}
}
println("-------练习4:使用break来表达continue的用法------------")
/**
* 练习: 当num等于5时,跳过,不打印
*/
for( num <- 1 to 10){
breakable{
if(num==5){
break
}
println(num)
}
}
}
}
复制代码
11.3.3字符串插值器
字符串插值器的应用, 功能是让字符串中的变量生效,换成变量的值
s: 就是让字符串中的变量生效
f: 除了有s功能外,还具有格式化的效果
%[Num]s: 对字符串进行右对齐,左补空格,总长度为Num
%[Num]d: 对整形进行右对齐,左补空格,总长度为Num
%[.Num]f:对浮点数进行保留Num个小数位。
raw: 除了具有s功能外,其他任何字符都被当做普通字符输出 \t\n\s 和lua的[[....]] 以及xml里的<![CDATA[ ]]>功能一样
注意: 变量名前需要添加$variableName符号,或者使${variableName}
复制代码
代码
package com.qf.scala.day03
object _08Interpolation {
def main(args: Array[String]): Unit = {
var name = "michael"
var age = 34
var salary = 55555.1234;
//java中一般使用+拼接
println("有一个帅哥叫"+name+",他的年龄是"+age);
//使用scala的插值器 s
println(s"有一个帅哥叫${name},他的年龄是${age}");
//使用scala的插值器 f
println(f"有一个帅哥叫$name%10s,他的年龄是$age%10.2f,他的工资是$salary%.2f");
println(f"有一个帅哥叫$name%10s,他的年龄是$age%10d,她的工资是$salary%.2f");
//使用raw插值器
println("有一个帅哥叫$name,\t他的年龄使$age")
println(raw"有一个帅哥叫$name,\t他的年龄使$age")
}
}
复制代码
11.3.4文件IO
用来读写磁盘或者使网络上的字符
package com.qf.scala.day03
import scala.io.Source
object _09ScalaIO {
def main(args: Array[String]): Unit = {
println("----------练习1: 读取文件,按照字符进行输出----------");
//读取磁盘上的文件,返回的是一个字符迭代器
var file1 = Source.fromFile("D:/input/dept.txt");
for(ch <- file1){
print(ch) //打印的使每一个字符
}
println("----------练习2: 读取文件,按照行进行输出----------");
//读取磁盘上的文件,返回的是一个字符迭代器
var file2 = Source.fromFile("D:/input/dept.txt");
//调用getLines()方法,返回多行的迭代器
var lines = file2.getLines()
for(line <- lines){
println(line)
}
//读取磁盘上的文件,返回的是一个字符迭代器
println("---------练习3:测试mkString方法---------------")
var file3 = Source.fromFile("D:/input/dept.txt");
/**
* mkString: 该方法的作用是将数组或者集合中的元素拼接到一起。
* 重载方法mkString(x:String): 该方法的作用是将数组或者集合中的元素使用x拼接到一起。
*/
println(file3.mkString)
// 也可以使用将非空迭代器变成数组类型
var a = file3.getLines().toArray
for(ele <- a){
println(ele)
}
println("----------读取文件,按照单词进行输出----------");
var file4 = Source.fromFile("D:/input/dept.txt"); // 字符迭代器
var lines1 = file4.getLines();
for(line <- lines1){
var words = line.mkString.split(",") //切出来的是多个单词
for(word <- words){
println(word)
}
}
println("----------读取文件,按照单词进行输出----------");
var file5 = Source.fromFile("D:/input/dept.txt"); // 字符迭代器
val contents = file5.mkString.split(",")
for(word <- contents)
println(word)
println("----------读取网络的网页信息,按照行进行输出----------");
var contentall = Source.fromURL("http://www.baidu.com")
var lines2 = contentall.getLines();
for(line <- lines2){
println(line)
}
}
}
复制代码
11.3.4## 正则表达式
package com.qf.scala.day03
import scala.util.matching.Regex
object _10ScalaReg {
def main(args: Array[String]): Unit = {
/**
* scala中比较正规的写法,是使用Regex的类型来定义,
* var reg = new Regex("([\\d]{18}|[\\d]{17}[X|x]|[\\d]{15})")
*
* 但是scala不建议这样写,而是要使用最简单的写法,如下:
* var reg = "正则表达式".r
*/
var reg = "[sS]cala".r
var words = "scala is a simple language,java php Scala C++";
// 找到第一个符合正则表达式的单词,如果有就返回Some(单词),如果没有返回None
var word = reg.findFirstIn(words)
println(word)
var content = reg.findAllIn(words)
for(w <- content){
println(w)
}
val pattern = "(S|s)cala".r
val str = "Scala is scalable and cool"
println(pat