一丶Scala入门
1.1什么是Scala
Scala是Scalable Language两个单词的缩写,表示可伸缩语言的意思。从计算机的角度来讲,Scala是一门完整的软件编程语言,那么连在一起就表示Scala是一门可伸缩的软件编程语言。之所以说它是可伸缩,是因为这门语言体现了面向对象,函数式编程等多种不同的语言范式,且融合了不同语言新的特性。
Scala官网:https://www.scala-lang.org
1.2为什么学习Scala
在学习Scala这门新语言时,我们首先要知道这门语言的作用,学习Scala有以下的原因:
1.大数据主要的批处理计算引擎框架Spark是基于Scala语言开发的
2.大数据主要的流式计算引擎框架Flink野提供了Scala相应的API
3.大数据领域中函数式编程的开发效率更高,更直观,更容易理解
1.3Java and Scala
- JDK1.5的泛型,增强for循环,自动类型转换等都是从Pizza语言引入的新特性
- JDK1.8的类型推断,λ(lambda)表达式是从Scala语言引入的新特性
由上可知,Scala语言是基于Java开发的,所以其编译后的文件也是字节码文件,并可以运行在JVM中。
1.4Scala安装以及环境变量的搭建
- 安装JDK 1.8(略)
- 安装Scala2.12
- 解压文件:scala-2.12.11.zip,解压目录要求无中文无空格
- 配置环境变量
3.环境测试,如果出现以下窗口内容,则说明安装成功
1.5Scala插件安装
默认情况下IDEA不支持Scala的开发,需要安装Scala插件。
如果下载慢的,请访问网址:https://plugins.jetbrains.com/plugin/1347-scala/versions
1.6Hello Scala 案例
1.创建Maven项目
2.增加Scala框架支持
默认情况,IDEA中创建项目时不支持Scala的开发,需要添加Scala框架的支持。
3.创建类
在main文件目录中创建Scala类:com.atguigu.bigdata.scala.HelloScala
package com.atguigu.bigdata.scala
object HelloScala {
def main(args: Array[String]): Unit = {
System.out.println("Hello Scala")
println("Hello Scala")
}
}
1.7源码关联
在使用Scala过程中,为了搞清楚Scala底层的机制,需要查看源码,那么就需要关联和查看Scala的源码包。
二 丶变量和数据类型
2.1注释
Scala注释使用的和Java完全一样。注释是一个程序员必须要具有的良好编程习惯。将自己的思想通过注释先整理出来,再用代码去体现。
2.1.1 单行注释
package com.atguigu.bigdata.scala
object ScalaComment{
def main(args: Array[String]): Unit = {
// 单行注释
}
}
2.1.2 多行注释
package com.atguigu.bigdata.scala
object ScalaComment{
def main(args: Array[String]): Unit = {
/*
多行注释
*/
}
}
2.1.3 文档注释
package com.atguigu.bigdata.scala
/**
* doc注释
*/
object ScalaComment{
def main(args: Array[String]): Unit = {
}
}
2.2变量
变量是一种使用方便的占位符,用于引用计算机内存地址,变量创建后会占用一定的内存空间。基于变量的数据类型,操作系统会进行内存分配并且决定什么将被储存在保留内存中。因此,通过给变量分配不同的数据类型,你可以在这些变量中存储整数,小数或者字母。
2.2.1 语法声明
变量的类型在变量名之后等号之前声明。
object ScalaVariable {
def main(args: Array[String]): Unit = {
// var | val 变量名 :变量类型 = 变量值
// 用户名称
var username : String = "zhangsan"
// 用户密码
val userpswd : String = "000000"
}
}
变量的类型如果能够通过变量值推断出来,那么可以省略类型声明,这里的省略,并不是不声明,而是由Scala编译器在编译时自动声明编译的。
object ScalaVariable {
def main(args: Array[String]): Unit = {
// 因为变量值为字符串,又因为Scala是静态类型语言,所以即使不声明类型
// Scala也能在编译时正确的判断出变量的类型,这体现了Scala语言的简洁特性。
var username = "zhangsan"
val userpswd = "000000"
}
}
2.2.2 变量初始化
Java语法中变量在使用前进行初始化就可以,但是Scala语法中是不允许的,必须显示进行初始化操作。
object ScalaVariable {
def main(args: Array[String]): Unit = {
var username // Error
val username = "zhangsan" // OK
}
}
2.2.3 可变变量
值可以改变的变量,称之为可变变量,但是变量类型无法发生改变, Scala中可变变量使用关键字var进行声明
object ScalaVariable {
def main(args: Array[String]): Unit = {
// 用户名称
var username : String = "zhangsan"
username = "lisi" // OK
username = true // Error
}
}
2.2.4 不可变变量
值一旦初始化后无法改变的变量,称之为不可变变量。Scala中不可变变量使用关键字val进行声明, 类似于Java语言中的final关键字
object ScalaVariable {
def main(args: Array[String]): Unit = {
// 用户名称
val username : String = "zhangsan"
username = "lisi" // Error
username = true // Error
}
}
2.3 标识符
Scala 可以使用两种形式的标志符,字符数字和符号。
- 字符数字使用字母或是下划线开头,后面可以接字母或是数字,符号”$”在 Scala 中也看作为字母。然而以”$”开头的标识符为保留的 Scala 编译器产生的标志符使用,应用程序应该避免使用”$”开始的标识符,以免造成冲突。
- Scala 的命名规范采用和 Java 类似的 camel 命名规范,首字符小写,比如 toString。类名的首字符还是使用大写。此外也应该避免使用以下划线结尾的标志符以避免冲突。
- Scala 内部实现时会使用转义的标志符,比如:-> 使用 $colon$minus$greater 来表示这个符号。
// 和Java一样的标识符命名规则
val name = "zhangsan" // OK
val name1 = "zhangsan0" // OK
//val 1name = "zhangsan0" // Error
val name$ = "zhangsan1" // OK
val $name = "zhangsan2" // OK
val name_ = "zhangsan3" // OK
val _name = "zhangsan4" // OK
val $ = "zhangsan5" // OK
val _ = "zhangsan6" // OK
//val 1 = "zhangsan6" // Error
//val true = "zhangsan6" // Error
// 和Java不一样的标识符命名规则
val + = "lisi" // OK
val - = "lisi" // OK
val * = "lisi" // OK
val / = "lisi" // OK
val ! = "lisi" // OK
//val @ = "lisi" // Error
val @@ = "lisi" // OK
//val # = "lisi" // Error
val ## = "lisi" // OK
val % = "lisi" // OK
val ^ = "lisi" // OK
val & = "lisi" // OK
//val ( = "lisi" // Error
//val ( = "lisi" // Error
//val ) = "lisi" // Error
//val = = "lisi" // Error
val == = "lisi" // OK
//val [ = "lisi" // Error
//val ] = "lisi" // Error
//val : = "lisi" // Error
val :: = "lisi" // OK
//val ; = "lisi" // Error
//val ' = "lisi" // Error
//val " = "lisi" // Error
val "" = "lisi" // OK
val = "lisi" // OK
val ? = "lisi" // OK
val | = "lisi" // OK
val = "lisi" // OK
//val ` = "lisi" // Error
val ~ = "lisi" // OK
val :-> = "wangwu" // OK
val :-
Scala 中的标识符也不能是关键字或保留字,那么Scala中有多少关键字或保留字呢?
2.4 字符串
在 Scala 中,字符串的类型实际上就是 Java中的 String类,它本身是没有 String 类的。
在 Scala 中,String 是一个不可变的字符串对象,所以该对象不可被修改。这就意味着你如果修改字符串就会产生一个新的字符串对象。
object ScalaString {
def main(args: Array[String]): Unit = {
val name : String = "scala"
val subname : String = name.substring(0,2)
}
}
2.4.1 字符串连接
object ScalaString{
def main (args:Array[String]):Unit = {
//字符串连接
println("Hello " + name)
}
}
2.4.2 传值字符串
object ScalaString {
def main(args: Array[String]): Unit = {
//传值字符串(格式化字符串)
printf("name=%sn",name)
}
}
2.4.3 插值字符串
object ScalaString {
def main(args: Array[String]): Unit = {
// 插值字符串
// 将变量值插入到字符串
println(s"name=${name}")
}
}
2.4.4 多行字符串
object ScalaString {
def main(args: Array[String]): Unit = {
// 多行格式化字符串
// 在封装JSON或SQL时比较常用
// | 默认顶格符
println(
s"""
| Hello
| ${name}
""".stripMargin)
}
}
2.4.5 封装JSON示例
object scala03_String {
def main(args: Array[String]): Unit = {
val name ="zhangsan"
val password ="123456"
//官方无法使用插值字符串封装JSON吗,但是可以用多行字符串封装
//val json=s"{"username":"${name}","password":"${password}"}"
//多行字符串,竖线表示顶格符
val json =
s"""
|{"username":"${name},"password":"${password}"}
|""".stripMargin
println(json)
}
}
2.5 输入输出
2.5.1 输入
从屏幕(控制台)中获取输入
object ScalaIn {
def main(args: Array[String]): Unit = {
// 标准化屏幕输入
val age : Int = scala.io.StdIn.readInt()
println(age)
}
}
从文件中获取输入
object ScalaIn {
def main(args: Array[String]): Unit = {
// 请注意文件路径的位置
scala.io.Source.fromFile("input/user.json").foreach(
line => {
print(line)
}
)
scala.io.Source.fromFile("input/user.json").getLines()
}
}
2.5.2 输出
Scala进行文件写操作,用的都是 java中的I/O类
object ScalaOut {
def main(args: Array[String]): Unit = {
val writer = new PrintWriter(new File("output/test.txt" ))
writer.write("Hello Scala")
writer.close()
}
}
2.5.3 网络
Scala进行网络数据交互时,采用的也依然是 java中的I/O类
object TestServer {
def main(args: Array[String]): Unit = {
val server = new ServerSocket(9999)
while ( true ) {
val socket: Socket = server.accept()
val reader = new BufferedReader(
new InputStreamReader(
socket.getInputStream,
"UTF-8"
)
)
var s : String = ""
var flg = true
while ( flg ) {
s = reader.readLine()
if ( s != null ) {
println(s)
} else {
flg = false
}
}
}
}
}
...
object TestClient {
def main(args: Array[String]): Unit = {
val client = new Socket("localhost", 9999)
val out = new PrintWriter(
new OutputStreamWriter(
client.getOutputStream,
"UTF-8"
)
)
out.print("hello Scala")
out.flush()
out.close()
client.close()
}
}
2.6 数据类型
Scala与Java有着相同的数据类型,但是又有不一样的地方
2.6.1 Java数据类型
Java的数据类型包含基本类型和引用类型
- 基本类型:byte,short,char,int,long,float,double,boolean
- 引用类型:Object,数组,字符串,包装类,集合,POJO对象等
2.6.2 Scala数据类型
Scala是完全面向对象的语言,所以不存在基本数据类型的概念,有的只是任意值对象类型(AnyVal)和任意引用对象类型(AnyRef)
2.7 类型转换
2.7.1 自动类型转化(隐式转换)
object ScalaDataType {
def main(args: Array[String]): Unit = {
val b : Byte = 10
val s : Short = b
val i : Int = s
val lon : Long = i
}
}
2.7.2 强制类型转化
- Java语言
int a = 10
byte b = (byte)a
- Scala语言
var a : Int = 10
Var b : Byte = a.toByte
// Scala作为完全面向对象语言,基本上所有的AnyVal类型之间都提供了相应转换的方法。
2.7.3 字符串类型转化
scala是完全面向对象的语言,所有的类型都提供了toString方法,可以直接转换为字符串
lon.toString
任意类型都提供了和字符串进行拼接的方法,直接拼接即可
val i = 10
val s = "hello " + i
三丶运算符
scala运算符的使用和Java运算符的使用基本相同,只有个别细节上不同。
假定变量 A 为 10,B 为 20
3.1 算数运算符
3.2 关系运算符
3.3 赋值运算符
++运算有歧义,容易理解出现错误,所以scala中没有这样的语法,所以采用 +=的方式来代替。
3.4 逻辑运算符
3.5 位运算符
如果指定 A = 60; 及 B = 13; 两个变量对应的二进制为
A = 0011 1100
B = 0000 1101
3.6 运算符本质
在Scala中其实是没有运算符的,所有的运算符都是方法
scala是完全面向对象的语言,所以数字其实也是对象,当调用对象的方法,点.可以省略,如果函数参加只有一个,或者没有参数,()可以省略
object ScalaOper {
def main(args: Array[String]): Unit = {
val i : Int = 10
val j : Int = i.+(10)
val k : Int = j +(20)
val m : Int = k + 30
println(m)
}
}
四丶流程控制
Scala程序代码和所有编程语言代码一样,都会有特定的执行流程顺序,默认情况下是顺序执行,上一条逻辑执行完成后才会执行下一条逻辑,执行期间也可以根据某些条件执行不同的分支逻辑代码。
4.1 分支控制
让程序有选择的的执行,分支控制有三种:单分支、双分支、多分支,和Java一样
4.1.1
IF…ELSE 语句是通过一条或多条语句的执行结果(true或者false)来决定执行的代码块
object ScalaBranch {
def main(args: Array[String]): Unit = {
val b = true
if ( b ) {
println("true")
}
}
}
4.1.2 双分支
object ScalaBranch {
def main(args: Array[String]): Unit = {
val b = true
if ( b ) {
println("true")
} else {
println("false")
}
}
}
4.1.3 多分支
object ScalaBranch {
def main(args: Array[String]): Unit = {
val age = 30
if ( age
实际上,Scala中的表达式都是有返回值的,所以上面的小功能还有其他的实现方式
object ScalaBranch {
def main(args: Array[String]): Unit = {
val age = 30
val result = if ( age
4.2 循环控制
4.2.1 for循环
object Scala09_ScalaLoop {
def main(args: Array[String]): Unit = {
for (i
循环守卫:循环时可以增加条件来决定是否继续循环体执行,这里的判断条件成为循环守卫
object ScalaLoop {
def main(args: Array[String]): Unit = {
for ( i
循环步长:scala的结合也可以设定循环的增长幅度,也就是所谓的步长step
object ScalaLoop {
def main(args: Array[String]): Unit = {
for ( i
循环嵌套
object ScalaLoop {
def main(args: Array[String]): Unit = {
for ( i
循环返回值:Scala所有的表达式都是有返回值的,但是这里的返回值并不一定是都有值的,如果希望for循环表达式的返回值有具体的值,就要使用关键字yield
object ScalaLoop {
def main(args: Array[String]): Unit = {
//杨辉三角
val result = for ( i
4.2.2 while循环
1.基本语法:当循环条件达到返回值true时 ,执行循环体代码
while循环:
object Scala10_ScalaLoop2 {
def main(args: Array[String]): Unit = {
var i=0
while ( i
do…while 循环
var j = 0
do {
println("j ="+j)
j += 1
} while( j
4.2.3 循环中断
scala是完全面向对象的语言,所以无法使用break,continue关键字这样的方式来中断,或继续循环逻辑,而是采用了函数式编程的方式代替了循环语法中的break和continue
continue的平替:
for (i
break的平替:
Breaks.breakable {
for (i
注意Breaks.breakable存在的位置,存在循环外部的是break关键字,存在循环内部的是continu关键字
五丶函数式编程
在之前Java课程的学习中,我们一直学习的就是面向对象编程,所以解决问题都是按照面向对象的方式来处理的。比如用户登陆等业务功能,但是接下来,我们会学习函数式编程,采用函数式编程的思路来解决问题。scala编程语言将函数式编程和面向对象编程完美地融合在一起了。
面向对象编程:
分解对象,行为,属性,然后通过对象的关系以及行为的调用来解决问题
面向函数编程:
将问题分解成一个一个的步骤,将每个步骤进行封装(函数),通过调用这些封装好的功能按照指定的步骤解决问题
5.1 基础函数编程
5.1.1 基本语法
[修饰符] def 函数名 ( 参数列表 ) [:返回值类型] = {
函数体
}
private def test( s : String ) : Unit = {
println(s)
}
5.1.2 函数定义
object Scala11_ScalaFunction {
def main(args: Array[String]): Unit = {
//无参无返回值
def fun1(): Unit = {
println("函数体")
}
fun1()
//无参,有返回值
def fun2(): String = {
"zhangsan"
}
println(fun2())
//有参,无返回值
def fun3(name: String): Unit = {
println(name)
}
fun3("zhangsan")
//有参,有返回值
def fun4(name: String): String = {
name
}
println(fun4("zhangsan"))
//多参,无返回值
def fun5(name: String, age: Int): Unit = {
println("name = " + name, "age = " + age)
}
fun5("zhangsan", 23)
//多参,有返回值
def fun6(name: String, age: Int): String = {
"name = " + name
}
println(fun6("zhangsan", 23))
//可变参数
def fun7(names:String*):Unit = {
println(names)
}
fun7()
fun7( "zhangsan" )
fun7( "zhangsan", "lisi")
//参数默认值
def fun8( name:String, password:String = "000000" ): Unit = {
println( name + "," + password )
}
fun8("zhangsan", "123123")
fun8("zhangsan")
//带名参数,指定传参
def fun9(password: String="000000",name:String):Unit={
println(name+"," + password)
}
fun9("123123","zhangsan")
fun9(name="zhangsan")
}
}
5.1.3函数至简原则
所谓的至简原则,其实就是Scala的作者为了开发人员能够大幅度提高开发效率。通过编译器的动态判定功能,帮助我们将函数声明中能简化的地方全部都进行了简化。也就是说将函数声明中那些能省的地方全部都省掉。所以这里的至简原则,简单来说就是:能省则省。
object Scala12_ScalaFunction2 {
def main(args: Array[String]): Unit = {
//省略return关键字
//如果函数体返回值类型明确为Unit, 那么函数体中即使有return关键字也不起作用
def fun1():String={
"zhangsan"
}
//省略花括号
def fun2():String="zhangsan"
//省略返回值类型
//如果函数体中有明确的return语句,那么返回值类型不能省略
//如果函数体返回值类型声明为Unit, 但是又想省略,那么此时就必须连同等号一起省略
def fun3()="zhangsan"
def fun7 (){
return "zhangsan"
}
//省略参数列表(小括号),省略完之后调用时也不加小括号
def fun4 ="zhangsan"
fun4
//fun4() ERROR
//省略名称和关键字
() =>{
println("zhangsan")
}
}
}
5.2高阶函数编程
所谓的高阶函数变成,其实就是将函数当成一个类型来使用,而不是当成特定的语法结构
object Scala13_ScalaFunction3 {
def main(args: Array[String]): Unit = {
//函数作为值
def fun1():String={
"zhangsan"
}
val a=fun1
val b=fun1 _
println(a)
println(b)
//函数作为参数
def fun2( i:Int ) : Int = {
i * 2
}
def fun22 ( f :Int => Int):Int = {
f(10)
}
println(fun22(fun2))
//函数作为返回值
def fun3 ( i:Int ):Int={
i*2
}
def fun33 ()={
fun3 _
}
println(fun33()(10))
//匿名函数
def fun4(f :Int=>Int):Int={
f(10)
}
println(fun4((x:Int)=>{x*20}))
println(fun4((x)=>{x * 20}))
println(fun4((x)=>x*20))
println(fun4(x=>x*20))
println(fun4(_ *20))
//控制抽象:可以不传参数而改为传代码,用于自定义代码逻辑
def fun5(op : => Unit)={
op
}
fun5{
println("xx")
}
//闭包,一个函数他使用了外部的变量,把这个歌变量包含在内部来使用,改变了变量的生命周期,形成了一个闭合环境
def fun6()={
val i = 20
def fun66()={
i*2
}
fun66 _
}
fun6()()
//函数柯里化,将没有联系的参数分开去传递
def fun7(a:Int)(b:Int)={
println(a+b)
}
fun7(10)(20)
//递归
// Scala要求递归函数必须明确给出返回值
// 函数内部调用自身
//一定要有跳出递归的逻辑
//调用时传递参数之间要有关系
def fun8(j:Int):Int = {
if ( j
六丶面向对象编程
Scala是一门完全面向对象的语言,摒弃了Java中高很多不是面向对象的语法,但其面向对象的思想和Java还是一致的
6.1基础面向对象编程
6.1.1包
基本和Java完全一致
扩展语法:
- Scala中的包和类的物理路径没有关系
- package关键字可以嵌套声明使用
- 同一个源码文件中的子包可以访问父包中的内容,无需导入
package com
package atguigu {
package bigdata {
class Test {
}
package scala {
object ScalaPackage {
def test(): Unit = {
new Test()
}
}
}
}
}
Scala中package也可以看做对象,并声明属性和函数
package com
package object atguigu {
val name : String = "zhangsan"
def test(): Unit = {
println(name)
}
}
package atguigu {
packafe bigdata {
package scala {
object ScalaPackage {
def test(): Unit = {
}
}
}
}
}
6.1.2导入
Scala中基本的导入也和Java完全一致
扩展语法:
- Scala中导入可以再任意位置使用
object ScalaImport{
def main(args: Array[String]): Unit = {
import java.util.ArrayList
new ArrayList()
}
}
- Scala中可以只导包
object ScalaImport{
def main(args: Array[String]): Unit = {
import java.util
new util.ArrayList()
}
}
- Scala可以在同一行导入相同包中的多个类
import java.util.{List, ArrayList} - Scala可以给类起别名简化使用
import java.util.{ArrayList=>AList}
object ScalaImport{
def main(args: Array[String]): Unit = {
new AList()
}
}
- Scala中可以使用类的绝对路径而非相对路径
import _root_.java.util.ArrayList - Scala可以屏蔽包里的某个类
import java.util._
import java.sql.{ Date=>_, Array=>_, _ }
6.1.3 类
//声明类 :访问权限class类名{主题内容}
class User {
//类的主题内容
}
//对象 :new 类名(参数列表)
new User()
扩展语法:Scala 中一个源文件可以声明多个公共类
6.1.4 属性
基本语法:
class User {
var name : String = _ // 类属性其实就是类变量
var age : Int = _ // 下划线表示类的属性默认初始化
}
6.1.5 方法
Scala中类的方法就是函数,所以声明方式完全一样,但是必须通过对象调用
object Scala14_ScalaMethod {
def main(args: Array[String]): Unit = {
val user =new User
user.login("zhangsan","123456")
}
class User{
def login(name :String,password: String)={
println(name,password)
}
}
}
6.1.6 对象
Scala中对象和Java是类似的
val user :User =new User()
6.1.7 构造方法
Scala中构造对象也需要调用类的构造方法来创建,并且一个类中可以有任意多个不相同的构造方法。这些构造方法可以分为2大类:主构造函数和辅构造函数
class User1(){//主构造函数
var username : String = _
def this(name:String){//辅助构造函数,使用this关键字声明
this()//辅助构造函数一个直接或者间接调用主构造函数
username=name
}
def this (name :String,password:String){
this(name) //构造器调用其他另外构造器,要求被调用构造器必须提前声明
}
}
6.2 高阶面向对象编程
6.2.1 继承
和Java一样,Scala中继承也是单继承,使用extends关键字,构造对象时需要考虑构造方法的执行顺序构造对象时需要考虑构造方法的执行顺序构造对象时需要考虑构造方法的执行顺序
6.2.2 封装
封装就是把抽象出的数据和对数据的操作封装到一起,数据被保护在内部,程序的其他部分只有通过被授权的操作(成员方法),才能对数据进行访问
1.将属性进行私有化
2.提供一个公共的set方法,用于对属性赋值
3.提供一个公共的get方法,用于获取属性的值
6.2.3 抽象
Scala将一个不完整的类称为抽象类
//抽象类,抽象类没办法实例化,只有子类继承,重写才能实例化
abstract class Person{
//抽象方法
def test():Unit
//抽象属性
var name : String
}
class Child extends Person {
def test(): Unit = {
}
var name: String = "zs"
}
abstract class Student(){
def test(): Unit ={
}
val age:Int = 10
}
//子类重写父类的完整方法或者完整属性时,必须添加override关键字,开发时,推荐只要重写,都添加override关键字
class ChildStudent extends Student(){
override def test(): Unit = {
}
override val age : Int = 20
}
6.2.4 特征
将多个对象中相同的特征,从对象中剥离出来,形成独立的一个结构,称之为trait(特征)
如果一个对象符合这些特征,那么可以将这个特征加入到这个对象,这个加入的过程成为混入
如果一个类只有一个特征时,采用extends进行混入
如果一个类有多个特征,这个时候,第一个特征采用extends,后续采用with
如果类存在父类的场合,并且同时具备某个特征,需要使用extends继承父类,使用with混入特征
6.2.5 反射
def main(args: Array[String]): Unit = {
val s=" a b "
//不可变字符串真正意义上是不可直接改变,但可使用反射调用自身进行改变
//反射就相当于镜子,调用自己
//todo:1构建反射对象
val stringClass: Class[String] = classOf[String]
val field : Field= stringClass.getDeclaredField("value")
//添加访问权限
field.setAccessible(true)
//todo:2调用反射,传入值
val obj = field.get(s)
val chars = obj.asInstanceOf[Array[Char]]
chars(2)='D'
println(s)
}
6.2.6 枚举类和应用类
枚举类:
def main(args: Array[String]): Unit = {
println(Season.Spring)
println(Season.Summer)
}
//定义枚举类
object Season extends Enumeration{
val Spring =Value(1,"春天")
val Summer =Value(2,"夏天")
val Autumn =Value(3,"秋天")
val Winter =Value(4,"冬天")
}
应用类:目的是为了顺序执行
object AppTest extends App{
println(1)
println(2)
}
七丶集合
7.1简介
Scala的集合有三大类:序列Seq,集Set,映射Map,所有的集合都扩展自Iterable特质。对于几乎所有的集合类,Scala都同时提供了可变和不可变版本,可变集合可以在适当的地方被更新或扩展。这意味着你可以修改,添加,移除一个集合的元素。而不可变集合类,相比之下,永远不会改变。不过,你仍然可以模拟添加,移除或更新操作。但是这些操作将在每一种情况下都返回一个新的集合,同时使原来的集合不发生改变,所以这里的不可变并不是变量本身的值不可变,而是变量指向的那个内存地址不可变
可变集合和不可变集合,在scala中该如何进行区分呢?我们一般可以根据集合所在包名进行区分:
- scala.collection.immutable
- scala.collection.mutable
7.2 数组
7.2.1 不可变数组
基本语法:
def main(args: Array[String]): Unit = {
//todo -集合 -数组
// 数组:严格意义上,数组不是集合,scala中给数组一个特定的类型:Array
//todo 构建
val array =new Array[String](3)
//访问
//array.update(1,"abc")
array(1)="abc"
//遍历
for (i
数组中常用的方法:
def main(args: Array[String]): Unit = {
//多维数组
var myMatrix = Array.ofDim[Int](3,3)
// myMatrix.foreach(list=>println(list.mkString(",")))
val arr1 = Array(1,2,3,4)
val arr2 = Array(5,6,7,8)
//合并数组
val arr3 = Array.concat(arr1,arr2)
//arr3.foreach(println(_))
//创建指定范围数组
val arr4:Array[Int] = Array.range(0,3)
//arr4.foreach(println(_))
//填充
val arr5 = Array.fill(5)(-1)
arr5.foreach(println)
}
7.2.2 可变数组
基本语法:
def main(args: Array[String]): Unit = {
//可变数组
val buffer = new ArrayBuffer[String]()
//添加
buffer.append("a","b","c")
buffer.appendAll(Array("d","e","f"))
buffer.insert(1,"x")
//修改
buffer.update(0,"h")
buffer(2)="p"
//删除
buffer.remove(3)
buffer.remove(3,2)
println(buffer)
buffer.foreach(println(_))
//可变数组和不可变数组相互转换
val array = buffer.toArray
val buffer1 = array.toBuffer
//Java集合和scala集合互相转换
import scala.collection.JavaConverters._
val list=new java.util.ArrayList()
list.asScala.foreach(println(_))
val arr0 = List(1,2,3)
arr0.asJava.forEach(println)
}
7.2.3 常用方法
def main(args: Array[String]): Unit = {
//集合常用的方法
val array = Array(1,2,3)
println(array.size)
println(array.length)
println(array.isEmpty)
println(array.contains(2))//是否包含
println(array.distinct.mkString(","))//去重
println(array.reverse.mkString(","))//反转
println(array.mkString(","))
array.foreach(println)
array.iterator
println(array.toList.takeRight(2))//取后几个元素
println(array.toList.take(2))//取前几个元素
println(array.find(x => x % 2 == 0))//按条件查找
println(array.toList.drop(2))//删除前几个元素
println(array.toList.dropRight(2))//删除前几个元素
}
7.2.4多集合常用方法
def main(args: Array[String]): Unit = {
val list = List(1,2,3,4)
val list1 = List(1,2,3,4)
val list2 = List(3,4,5,6)
// 集合头
println("head => " + list.head)
// 集合尾
println("tail => " + list.tail)
// 集合尾迭代
println("tails => " + list.tails.next())
// 集合初始值
println("init => " + list.init)
// 集合初始值迭代
println("inits => " + list.inits.hasNext)
// 集合最后元素
println("last => " + list.last)
// 集合并集
println("union => " + list.union(list1))
// 集合交集
println("intersect => " + list.intersect(list1))
// 集合差集
println("diff => " + list.diff(list1))
// 切分集合
println("splitAt => " + list.splitAt(2))
// 滑动(窗口)
println("sliding => " + list.sliding(2).next())
// 滚动(没有重复)
println("sliding => " + list.sliding(2,2).next())
// 拉链
println("zip => " + list.zip(list1))
// 数据索引拉链
println("zipWithIndex => " + list.zipWithIndex)
}
7.2.5 计算函数
def main(args: Array[String]): Unit = {
val list = List(1,2,3,4)
val list1 = List(3,4,5,6)
// 集合最小值
println("min => " + list.min)
// 集合最大值
println("max => " + list.max)
// 集合求和
println("sum => " + list.sum)
// 集合乘积
println("product => " + list.product)
// 集合简化规约
println("reduce => " + list.reduce((x:Int,y:Int)=>{x+y}))
println("reduce => " + list.reduce((x,y)=>{x+y}))
println("reduce => " + list.reduce((x,y)=>x+y))
println("reduce => " + list.reduce(_+_))
// 集合简化规约(左)
println("reduceLeft => " + list.reduceLeft(_+_))
// 集合简化规约(右)
println("reduceRight => " + list.reduceRight(_+_))
// 集合折叠
println("fold => " + list.fold(0)(_+_))
// 集合折叠(左)
println("foldLeft => " + list.foldLeft(0)(_+_))
// 集合折叠(右)
println("foldRight => " + list.foldRight(0)(_+_))
// 集合扫描
println("scan => " + list.scan(0)(_+_))
// 集合扫描(左)
println("scanLeft => " + list.scanLeft(0)(_+_))
// 集合扫描(右)
println("scanRight => " + list.scanRight(0)(_+_))
}
7.2.6 功能函数
def main(args: Array[String]): Unit = {
val list = List(1,2,3,4)
// 集合映射
println("map => " + list.map(x=>{x*2}))
println("map => " + list.map(x=>x*2))
println("map => " + list.map(_*2))
// 集合扁平化
val list1 = List(
List(1,2),
List(3,4)
)
println("flatten =>" + list1.flatten)
// 集合扁平映射
println("flatMap =>" + list1.flatMap(list=>list))
// 集合过滤数据
println("filter =>" + list.filter(_%2 == 0))
// 集合分组数据
println("groupBy =>" + list.groupBy(_%2))
// 集合排序
println("sortBy =>" + list.sortBy(num=>num)(Ordering.Int.reverse))
println("sortWith =>" + list.sortWith((left, right) => {left
7.3 Seq集合
7.3.1 不可变List
def main(args: Array[String]): Unit = {
//Seq集合
val list = List(1,2,3,4)
//增加数据
val list1: List[Int]=list :+ 5
println(list1)
list1.foreach(println(_))
println("_____________------------------")
val list2 : List[Int] = 0 +: list1
list2.foreach(println)
//修改数据
val list3 : List[Int] =list.updated(1,5)
println(list3)
//空集合
val list4: List[Nothing] = List()
val nil = Nil
println(list4 eq nil)
//创建集合
val list5: List[Int] = 1::2::3::Nil
println(list5)
val list6 = list1 ::: Nil
//连接集合
val list7 : List[Int] = List.concat(list1,list2)
println(list7)
//创建一个指定重复数量的元素列表
val list8: List[String]= List.fill[String](3)("a")
println(list8)
}
7.3.2 可变List
基本语法和操作
def main(args: Array[String]): Unit = {
//可变集合
val buffer = new ListBuffer[Int]()
//增加数据
buffer.append(1,2,3,4)
//修改数据
buffer.update(1,3)
//删除数据
//buffer.remove(2)
//buffer.remove(2,2)
//获取数据
println(buffer(0))
//遍历集合
buffer.foreach(println(_))
println(buffer)
println("---------------------------")
val buffer1 =ListBuffer(1,2,3,4)
val buffer2 =ListBuffer(5,6,7,8)
//增加数据
val buffer3:ListBuffer[Int] = buffer1 :+ 5
//+= 和 -=会给原来的buffer1也添加上数据
val buffer4:ListBuffer[Int] = buffer1 += 5
val buffer5:ListBuffer[Int] = buffer1 ++ buffer2
val buffer6:ListBuffer[Int] = buffer1 ++= buffer2
val buffer7:ListBuffer[Int] = buffer1 -= 1
val buffer8:ListBuffer[Int] = buffer1 - 1
println(buffer8)
}
7.3.3 可变集合和不可变集合的转换
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
val buffer9 = ListBuffer(1,2,3,4)
val list = List(5,6,7,8)
//可变集合变不可变集合
val list1: List[Int] = buffer9.toList
//不可变变可变
val buffer10: mutable.Buffer[Int] = list.toBuffer
7.4Set集合
7.4.1 不可变集合
def main(args: Array[String]): Unit = {
val set1 = Set(1,2,3,4)
val set2 = Set(5,6,7,8)
// 增加数据
val set3: Set[Int] = set1 + 5 + 6
val set4: Set[Int] = set1.+(6,7,8)
println( set1 eq set3 ) // false
println( set1 eq set4 ) // false
//set4.foreach(println)
// 删除数据
val set5: Set[Int] = set1 - 2 - 3
//set5.foreach(println)
val set6: Set[Int] = set2 ++ set1
println(set6)
set6.foreach(println)
println("********")
val set7: Set[Int] = set2 ++: set1
set7.foreach(println)
println(set6 eq set7)
}
7.4.2 可变集合
def main(args: Array[String]): Unit = {
import scala.collection.mutable
val set1 = mutable.Set(1,2,3,4)
val set2 = mutable.Set(5,6,7,8)
// 增加数据
set1.add(5)
// 添加数据
set1.update(6,true)
println(set1.mkString(","))
// 删除数据
set1.update(3,false)
println(set1.mkString(","))
// 删除数据
set1.remove(2)
println(set1.mkString(","))
// 遍历数据
set1.foreach(println)
// 交集
val set3: mutable.Set[Int] = set1 & set2
println(set3.mkString(","))
// 差集
val set4: mutable.Set[Int] = set1 &~ set2
println(set4.mkString(","))
}
7.5 Map集合
Map(映射)是一种可迭代的键值对(Key/value) 结构,所有的值都可以通过键来获取。Map中的键都是唯一的
7.5.1 不可变Map
def main(args: Array[String]): Unit = {
//不可变Map
val map1 = Map( "a" -> 1, "b" -> 2, "c" -> 3 )
val map2 = Map( "d" -> 4, "e" -> 5, "f" -> 6 )
// 添加数据
val map3 = map1 + ("d" -> 4)
println(map1 eq map3) // false
println(map3)
// 删除数据
val map4 = map3 - "d"
println(map4.mkString(","))
val map5: Map[String, Int] = map1 ++ map2
println(map5 eq map1)
println(map5.mkString(","))
// 修改数据
val map7: Map[String, Int] = map1.updated("b", 5)
println(map7.mkString(","))
// 遍历数据
map1.foreach(println)
//创建空集合
val empty: Map[String,Int] = Map.empty
println(empty)
//获取指定key的值
println(map1.apply("a"))
println(map1("c"))
//获取可能存在的key的值
val maybeInt:Option[Int] = map1.get("c")
//判断key值是否存在
if (!maybeInt.isEmpty){
println(maybeInt.get)
}else{
//如果不存在,则获取默认值
println(maybeInt.getOrElse(0))
}
println(map1.getOrElse("c",0))
}
7.5.2 可变Map
def main(args: Array[String]): Unit = {
//可变Map
val map1 = mutable.Map("a" -> 1, "b" -> 2, "c" -> 3)
val map2 = mutable.Map("d" -> 4, "e" -> 5, "f" -> 6)
// 添加数据
map1.put("d", 4)
val map3: mutable.Map[String, Int] = map1 + ("e" -> 4)
println(map1 eq map3)
val map4: mutable.Map[String, Int] = map1 += ("e" -> 5)
println(map1 eq map4)
// 修改数据
map1.update("e", 8)
map1("e") = 8
// 删除数据
map1.remove("e")
val map5: mutable.Map[String, Int] = map1 - "e"
println(map1 eq map5)
val map6: mutable.Map[String, Int] = map1 -= "e"
println(map1 eq map6)
// 清除集合
// map1.clear()
val set: Set[(String, Int)] = map1.toSet
val list: List[(String, Int)] = map1.toList
val seq: Seq[(String, Int)] = map1.toSeq
val array: Array[(String, Int)] = map1.toArray
println(set.mkString(","))
println(list.mkString(","))
println(seq.mkString(","))
println(array.mkString(","))
println(map1.get("a"))
println(map1.getOrElse("a", 0))
println(map1.keys)
println(map1.keySet)
println(map1.keysIterator)
println(map1.values)
println(map1.valuesIterator)
map1.foreach(println(_))
}
7.6 元组
在Scala语言中,我们可以将多个无关的数据封装为一个整体,这个整体我们称之为:元素组合,简称元组,有时候也可以将元组看成元素收纳的容器
def main(args: Array[String]): Unit = {
//创建元组,使用小括号
val tuple =(1,"zhangsan",30)
//根据顺序号访问元组数据
println(tuple._1)
println(tuple._2)
println(tuple._3)
//迭代器
val iterator:Iterator[Any] = tuple.productIterator
//根据索引访问元素
tuple.productElement(0)
// 如果元组的元素只有两个,那么我们称之为对偶元组,也称之为键值对
val kv: (String, Int) = ("a", 1)
val kv1: (String, Int) = "a" -> 1
println( kv eq kv1 )
}
7.7 队列
先进先出
def main(args: Array[String]): Unit = {
import scala.collection.mutable
val que = new mutable.Queue[String]()
// 添加元素
que.enqueue("a", "b", "c")
val que1: mutable.Queue[String] = que += "d"
println(que eq que1)
// 获取元素
println(que.dequeue())
println(que.dequeue())
println(que.dequeue())
println(que.dequeue())
}
八丶模式匹配
8.1 简介
Scala 中的模式匹配类似于Javg中的switch语法,但是scala从语法中补充了更多的功能,可以按照指定的规则对数据或者对象进行匹配,
8.2 基本语法
模式匹配语法中,采用match关键字声明,每个分支采用case关键字进行声明,当需要匹配时,会从第一个case分支开始,如果匹配成功,那么执行对应的逻辑代码,如果匹配不成功,继续执行下一个分支进行判断。如果所有case都不匹配,那么会执行case _分支,类似于Java中default语句。如果不存在case _分支,那么会发生错误
object ScalaMatch{
def main(args: Array[String]): Unit = {
var a: Int = 10
var b: Int = 20
var operator: Char = 'd'
var result = operator match {
case '+' => a + b
case '-' => a - b
case '*' => a * b
case '/' => a / b
case _ => "illegal"
}
println(result)
}
}
8.3 匹配规则
8.3.1匹配常量
def describe(x:Any) = x match{
case 5 => "Int five"
case "hello" => "String hello"
case true => "Boolean true"
case '+' => "Char +"
}
println(describe("hello"))
8.3.2匹配类型
def describe2(x: Any) = x match {
case i: Int => "Int"
case s: String => "String hello"
case m: List[_] => "List"
case c: Array[Int] => "Array[Int]"
case someThing => "something else " + someThing
}
println(describe2(List(1,2,3)))
8.3.3匹配数组
for (arr "0" //匹配Array(0) 这个数组
case Array(x, y) => x + "," + y //匹配有两个元素的数组,然后将将元素值赋给对应的x,y
case Array(0, _*) => "以0开头的数组" //匹配以0开头和数组
case _ => "something else"
}
println("result = " + result)
}
8.3.4匹配列表
for (list "0" //匹配List(0)
case List(x, y) => x + "," + y //匹配有两个元素的List
case List(0, _*) => "0 ..."
case _ => "something else"
}
println(result)
}
val list: List[Int] = List(1, 2, 5, 6, 7)
list match {
case first :: second :: rest => println(first + "-" + second + "-" + rest)
case _ => println("something else")
}
8.3.5 匹配元组
def main(args: Array[String]): Unit = {
for (tuple "0 ..." //是第一个元素是0的元组
case (y, 0) => "" + y + "0" // 匹配后一个元素是0的对偶元组
case (a, b) => "" + a + " " + b
case _ => "something else" //默认
}
println(result)
}
}
8.3.6 匹配对象
def main(args: Array[String]): Unit = {
class User (val name:String , val age: Int)
object User{
def apply(name: String,age: Int):User =new User(name, age)
def unapply(user: User): Option[(String, Int)]={
if (user == null)
None
else
Some(user.name,user.age)
}
}
val user = User("zhangsan",11)
user match{
case User("zhangsan",11)=> println("yes")
case _ => println("no")
}
}
8.3.6 匹配对象(样例类)
def main(args: Array[String]): Unit = {
//如果在类前面增加case关键字,这个类钻用于模式匹配,被称为样例类
//在编译时,会生成大量的方法
//1.样例类会自动实现可序列化接口
//2.样例类 构造参数直接能够作为属性使用,但是不能修改,如果想修改,需要将参数使用var声明
//3.增加和重写了大量方法
//4.样例类自动生成伴生对象,而且其中自动声明了apply,unapply
case class User (var name:String , var age: Int)
/*object User{
def apply(name: String,age: Int):User =new User(name, age)
def unapply(user: User): Option[(String, Int)]={
if (user == null)
None
else
Some(user.name,user.age)
}
}*/
val user = User("zhangsan",11)
user match{
case User("zhangsan",11)=> println("yes")
case _ => println("no")
}
}
8.3.7 全量函数
def main(args: Array[String]): Unit = {
//todo-01 偏函数:以偏概全,函数处理事只需对满足条件的数据进行处理
//todo-02 全量韩秘书:函数进行处理时必须对所有数据进行处理
//将数字加一,并去掉字符串
val list :List[Any] =List(1,2,3,4,5,6,"test")
val list1=list.map {
case i: Int => i + 1
case str: String => str
}.filter(_.isInstanceOf[Int])
println(list1)
}
8.3.8 偏函数
def main(args: Array[String]): Unit = {
//todo-01 偏函数:以偏概全,函数处理事只需对满足条件的数据进行处理
//todo-02 全量函数:函数进行处理时必须对所有数据进行处理
//将所有的数字加一,并去掉字符串
val list :List[Any] =List(1,2,3,4,5,6,"test")
val list1=list.collect {
case i: Int => i + 1
}
println(list1)
}
九丶异常
Scala异常原理和Java基本相同,但是捕捉异常的方法不同
def main(args: Array[String]): Unit = {
try {
var n= 10 / 0
} catch {
// 发生算术异常
case ex: ArithmeticException=> println("发生算术异常")
// 对异常处理
case ex: Exception=> println("发生了异常1")
} finally {
println("finally")
}
}
十丶隐式转换
10.1简介
在之前的类型学习中,我们已经学习了自动类型转换,精度小的类型可以自动转换为精度大的类型,这个转换过程无需开发人员参与,有编译器自动完成,这个转换我们称之为隐式转换
在其他的场合,隐式转换也起到了非常重要的作用。如Scala在程序编译错误时,可以通过隐式转换中类型转换机制尝试进行二次编译,将本身错误无法编译通过的代码通过类型转换后编译通过。
10.2 隐式函数
def main(args: Array[String]): Unit = {
implicit def transform(d :Double): Int = {
d.toInt
}
var d : Double =2.0
val i : Int = d
println(i)
}
10.3 隐式参数
def main(args: Array[String]): Unit = {
def transform( implicit d : Double)={
d.toInt
}
implicit val d : Double =2.0
println(transform)
}
10.4 隐式类
在Scala2.10后提供了隐式类,可以使用implicit声明类,隐式类非常强大,同样可以扩展类的功能,在集合的数据处理中,隐式类发挥了重要的作用。
- 其所带的构造参数有且只能有一个
- 隐式类必须被定义在“类”或“伴生对象”或“包对象”里,即隐式类不能是顶级的。
def main(args: Array[String]): Unit = {
val emp = new Emp()
emp.insertUser()
}
class Emp {
}
implicit class User( emp : Emp) {
def insertUser(): Unit = {
println("insert user...")
}
}
10.5 隐式机制
所谓的隐式机制,就是一旦出现编译错误时,编译器会从哪些地方查找对应的隐式转换规则,
- 当前代码作用域
- 当前代码上级作用域
- 当前类所在的包对象
- 当前类(对象)的父类(父类)或特质(父特质)
其实最直接的方式就是直接导入。
十一丶泛型
Scala泛型和Java泛型表达的含义都是一样的,对处理的数据类型进行约束但是Scala提供,了更强大的功能
Scala的泛型可以根据功能进行改变
泛型不可变
def main(args: Array[String]): Unit = {
val test : Test[User] = new Test [User]
val test1 : Test[Parent]= new Test[Parent]
//val test1 : Test[User]= new Test[Parent] Error
//val test2 : Test[User]= new Test[SubUser] Error
class Test[T]{
}
class Parent{
}
class User extends Parent{
}
class SubUser extends User{
}
}
泛型协变
val test : Test[User] = new Test[SubUser]
class Test[+T]{
}
class SubUser extends User{
}
泛型逆变
val test : Test[User] = new Test[Parent]
class Test[-T] {
}
class Parent {
}
class User extends Parent{
}
泛型边界
def main(args: Array[String]): Unit = {
val parent : Parent = new Parent()
val user : User = new User()
val subuser : SubUser = new SubUser()
test[User](parent) // Error
test[User](user) // OK
test[User](subuser) // OK
}
def test[A]( a : A ): Unit = {
println(a)
}
class Parent {
}
class User extends Parent{
}
class SubUser extends User {
}
泛型上限(父类被限制)
def main(args: Array[String]): Unit = {
val parent : Parent = new Parent()
val user : User = new User()
val subuser : SubUser = new SubUser()
test[Parent](parent) // Error
test[User](user) // OK
test[SubUser](subuser) // OK
}
def test[A
泛型下限(子类被限制)
def main(args: Array[String]): Unit = {
val parent : Parent = new Parent()
val user : User = new User()
val subuser : SubUser = new SubUser()
test[Parent](parent) // OK
test[User](user) // OK
test[SubUser](subuser) // Error
}
def test[A>:User]( a : A ): Unit = {
println(a)
}
class Parent {
}
class User extends Parent{
}
class SubUser extends User {
}
上下文限定
上下文限定是将泛型和隐式转换结合的产物,以下两者功能相同使用上下文限定[A : Ordering]之后,方法内无法使用隐式参数名调用隐式参数,需要通过implicitly[Ordering[A]]获取隐式变量,如果此时无法查找到对应类型的隐式变量,会发生出错误。
def main(args: Array[String]): Unit = {
def f[A : Test](a: A) = println(a)
implicit val test : Test[User] = new Test[User]
f( new User() )
}
class Test[T] {
}
class Parent {
}
class User extends Parent{
}
class SubUser extends User {
}
十二丶正则表达式
基本语法
def main(args: Array[String]): Unit = {
// 构建正则表达式
val pattern = "Scala".r
val str = "Scala is Scalable Language"
// 匹配字符串 - 第一个
println(pattern findFirstIn str)
// 匹配字符串 - 所有
val iterator: Regex.MatchIterator = pattern findAllIn str
while ( iterator.hasNext ) {
println(iterator.next())
}
println("***************************")
// 匹配规则:大写,小写都可
val pattern1 = new Regex("(S|s)cala")
val str1 = "Scala is scalable Language"
println((pattern1 findAllIn str1).mkString(","))
}
实操案例:
object ScalaRegex {
def main(args: Array[String]): Unit = {
// 构建正则表达式
val r = """([_A-Za-z0-9-]+(?:.[_A-Za-z0-9-+]+)*)(@[A-Za-z0-9-]+(?:.[A-Za-z0-9-]+)*(?:.[A-Za-z]{2,})) ?""".r
println(r.replaceAllIn("abc.edf+jianli@gmail.com hello@gmail.com.cn", (m => "*****" + m.group(2))))
}
}