scala
2021-06-24 13:17:12 0 举报
AI智能生成
scala基础知识
作者其他创作
大纲/内容
1 Scala入门
概述
Scala和Java关系
Scala是基于Java的
Scala语言特点
Scala是一门以Java虚拟机(JVM)为运行环境并将面向对象和函数式编程的最佳特性结合在一起的静态类型编程语言
注意事项
Scala源文件以“.scala" 为扩展名。
Scala程序的执行入口是object 中的main()函数。
Scala语言严格区分大小写。
Scala方法由一条条语句构成,每个语句后不需要分号(Scala语言会在每行后自动加分号)。(至简原则)
如果在同一行有多条语句,除了最后一条语句不需要分号,其它语句需要分号。
2 变量和数据类型
注释
单行注释://
快捷键 ctrl + /
多行注释:/* */
/* 然后ctrl +enter
文档注释:/**
*
*/
*
*/
/** 然后ctrl +enter
标识符的命名规范
以字母或者下划线开头,后接字母、数字、下划线
以操作符开头,且只包含操作符(+ - * / # !等)
第一种和第二种拼接,第一种在前,二者以下划线分隔
用反引号`....`包括的任意字符串,即使是关键字(39个)也可以
变量
var
声明变量
var a:Int=10
val
声明常量
val a:Int=100
注意
能用常量就不用变量
声明变量时,类型可以省略(编译器自动推导,即类型推导)
类型确定后,就不能修改,说明Scala是强数据类型语言。
变量声明时,需要初始值
在声明/定义一个变量时,可以使用var或者val来修饰,var修饰的变量可改变,val修饰的变量不可改。
val修饰的变量在编译后,等同于加上final通过反编译看下底层代码
var修饰的对象引用可以改变,val修饰的则不可改变,但对象的状态(值)却是可以改变的。(比如:自定义对象、数组、集合等等)
字符串输出
字符串,通过+号连接
println(name + " " + age)
printf用法:字符串,通过%传值
printf("name=%s age=%d\n", name, age)
字符串,通过$引用
println(s"name=$name age=$age")
多行字符串
val s:String =
"""
|nihao
|ceshi
|nishi1
|jichje
""".stripMargin
"""
|nihao
|ceshi
|nishi1
|jichje
""".stripMargin
用3对双引号""" """,然后回车,可以写格式化的内容
数据类型关系
Scala中一切数据都是对象,都是Any的子类。
Scala中数据类型分为两大类:数值类型(AnyVal)、引用类型(AnyRef),不管是值类型还是引用类型都是对象。
Scala数据类型仍然遵守,低精度的值类型向高精度值类型,自动转换(隐式转换)
Scala特殊的类型之Null,它只有一个实例就是Null,它是所有引用类型(AnyRef)的子类。
Scala特殊类型之Nothing,是所有数据类型的子类,主要在一个函数没有正常返回值使用,因为这样我们可以把抛出的返回值,返回给任何的变量或者函数。
Scala特殊类型之Unit,表示无值,和其他语言中void等同。用作不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()
Scala特殊类型之StringOps,看成String的加强版
基本类型
整数类型(Byte 1、Short 2、Int 4、Long 8)
浮点类型(Float 4、Double 8)
字符类型(Char 2)
布尔类型:Boolean 1
数值类型间转换
数值类型自动转换
自动将所有数据转换成精度大的那种数据类型
强制类型转换
自动类型转换的逆过程,将精度大的数值类型转换为精度小的数值类型。使用时要加上强制转函数,但可能造成精度降低或溢出,格外要注意。
java : int num = (int)2.5
scala : var num : Int = 2.7.toInt
scala : var num : Int = 2.7.toInt
数值类型和String类型间转换
基本类型转String类型(语法:将基本类型的值+"" 即可)
String类型转基本数值类型(语法:s1.toInt、s1.toFloat、s1.toDouble、s1.toByte、s1.toLong、s1.toShort)
3 运算符
算术运算符
+ - * / %
关系运算符(比较运算符)
== != < > <= >=
逻辑运算符
&& || !
赋值运算符
= += -= *= /= %= <<= >>= &= ^= |=
位运算符
& | ^ ~ << >> >>>
scala中没有真的运算符,所谓运算符,其实是一个对象的方法
4 流程控制
分支控制if-else
单分支
if (条件表达式) {
执行代码块
}
执行代码块
}
双分支
if (条件表达式) {
执行代码块1
} else {
执行代码块2
}
执行代码块1
} else {
执行代码块2
}
多分支
if (条件表达式1) {
执行代码块1
}
else if (条件表达式2) {
执行代码块2
}
……
else {
执行代码块n
}
执行代码块1
}
else if (条件表达式2) {
执行代码块2
}
……
else {
执行代码块n
}
如果大括号{}内的逻辑代码只有一行,大括号可以省略。
Scala中是没有三元运算符,因为可以这样简写
Scala中是没有三元运算符,因为可以这样简写
var flag:Boolean = true
var result = if(flag) 1 else 0
var result = if(flag) 1 else 0
嵌套分支
if(){
if(){
}else{
}
}
if(){
}else{
}
}
Switch分支结构
在Scala中没有Switch,而是使用模式匹配来处理。
For循环控制
范围数据循环方式1
for(i <- 1 to 3){
print(i + " ")
}
print(i + " ")
}
i 将会从 1-3 循环,前后闭合
范围数据循环方式2
for(i <- 1 until 3) {
print(i + " ")
}
print(i + " ")
}
i是从1到3-1,前闭合后开的范围
循环守卫
for(i <- 1 to 3 if i != 2) {
print(i + " ")
}
print(i + " ")
}
即循环保护式(也称条件判断式,守卫)。保护式为true则进入循环体内部,为false则跳过,类似于continue。
循环步长
for (i <- 1 to 10 by 2) {
println("i=" + i)
}
println("i=" + i)
}
嵌套循环
for(i <- 1 to 3; j <- 1 to 3) {
println(" i =" + i + " j = " + j)
}
println(" i =" + i + " j = " + j)
}
引入变量
for(i <- 1 to 3; j = 4 - i) {
println("i=" + i + " j=" + j)
}
println("i=" + i + " j=" + j)
}
循环返回值
val res = for(i <- 1 to 10) yield i
将遍历过程中处理的结果返回到一个新Vector集合中,使用yield关键字
While循环控制
while (循环条件) {
循环体(语句)
循环变量迭代
}
循环体(语句)
循环变量迭代
}
while (i < 10) {
println("你好" + i)
i += 1
}
println("你好" + i)
i += 1
}
与if语句不同,while语句没有返回值,即整个while语句的结果是Unit类型()
do..while循环控制
do{
循环体(语句)
循环变量迭代
} while(循环条件)
循环体(语句)
循环变量迭代
} while(循环条件)
do {
println("你好" + i)
i += 1
} while (i < 10)
println("你好" + i)
i += 1
} while (i < 10)
多重循环控制
将一个循环放在另一个循环体内,就形成了嵌套循环。其中,for,while,do…while均可以作为外层循环和内层循环。
九九乘法表
var max = 9
for (i <- 1 to max) {
for (j <- 1 to i) {
print(j + "*" + i + "=" + (i * j) + "\t")
}
println()
}
for (i <- 1 to max) {
for (j <- 1 to i) {
print(j + "*" + i + "=" + (i * j) + "\t")
}
println()
}
While循环中断
Scala内置控制结构特地去掉了break和continue,是为了更好的适应函数式编程,推荐使用函数式的风格解决break和continue的功能,而不是一个关键字。scala中使用breakable控制结构来实现break和continue功能。
5 函数式编程
函数基本语法
面向对象和函数式编程的区别
面向对象编程
解决问题
分解对象,行为,属性,然后通过对象的关系以及行为的调用来解决问题。
对象
用户
行为
登录、连接jdbc、读取数据库
属性
用户名、密码
Scala语言是一个完全面向对象编程语言。万物皆对象
函数式编程
解决问题
将问题分解成一个一个的步骤,将每个步骤进行封装(函数),通过调用这些封装好的步骤,解决问题。
例如
请求->用户名、密码->连接jdbc->读取数据库
Scala语言是一个完全函数式编程语言。万物皆函数
方法
def sum(x : Int ,y : Int) : Int={
x+y
}
x+y
}
函数
val f = (a : Int) => a * a
函数和方法的区别
函数
为完成某一功能的程序指令(语句)的集合,称为函数
方法
类中的函数称之方法
函数没有重载和重写的概念;方法可以进行重载和重写
区别
函数可以作为值传递,方法不行
严格来说使用def定义的,不能叫函数
函数也可以直接定义
() = > { }
val f = (a : Int) => a * a
val f = (a : Int) => a * a
方法转为函数 f=sum _
真正使用的时候,不需要区分函数和方法
函数声明
有参/无参
有无返回值
函数参数
可变参数
def test( s : String* ): Unit = {
println(s)
}
println(s)
}
如果参数列表中存在多个参数,那么可变参数一般放置在最后
def test2( name : String, s: String* ): Unit = {
println(name + "," + s)
}
/*
println(name + "," + s)
}
/*
参数默认值
def test3( name : String, age : Int = 30 ): Unit = {
println(s"$name, $age")
}
println(s"$name, $age")
}
如果参数传递了值,那么会覆盖默认值 test3("jinlian", 20)
如果参数有默认值,在调用的时候,可以省略这个参数 test3("dalang")
函数至简原则
函数标准写法
def f1( s : String ): String = {
return s + " jinlian"
}
return s + " jinlian"
}
return可以省略,Scala会使用函数体的最后一行代码作为返回值
def f2( s : String ): String = {
s + " jinlian"
}
s + " jinlian"
}
返回值类型如果能够推断出来,那么可以省略
def f3( s : String ) = {
s + " jinlian"
}
s + " jinlian"
}
如果函数体只有一行代码,可以省略花括号
def f4(s:String) = s + " jinlian"
如果函数无参,则可以省略小括号。若定义函数时省略小括号,则调用该函数时,也需省略小括号;
若定义函数时未省略,则调用时,可省可不省。
若定义函数时未省略,则调用时,可省可不省。
def f5 = "dalang"
println(f5)
如果函数明确声明Unit,那么即使函数体中使用return关键字也不起作用
def f6(): Unit = {
//return "abc"
"dalang"
}
//return "abc"
"dalang"
}
Scala如果想要自动推断无返回值,可以省略等号
def f7() {
"dalang"
}
"dalang"
}
如果不关心名称,只关系逻辑处理,那么函数名(def)可以省略
val f = (x:String)=>{"wusong"}
如果函数明确使用return关键字,那么函数返回就不能使用自行推断了,需要声明返回值类型
def f8() :String = {
return "ximenqing"
}
return "ximenqing"
}
高阶函数
定义
参数为函数的函数称为高阶函数
一个函数可以返回一个函数作为返回值
或者接收一个或者多个函数作为参数
一个函数可以返回一个函数作为返回值
或者接收一个或者多个函数作为参数
def calculator(a: Int, b: Int, operater: (Int, Int) => Int): Int = {
operater(a, b)
}
operater(a, b)
}
匿名函数
没有名字的函数就是匿名函数
子主题
//匿名函数作为参数
println(calculator(2, 3, (x: Int, y: Int) => x + y))
//匿名函数简写形式
println(calculator(2, 3, _ + _))
println(calculator(2, 3, (x: Int, y: Int) => x + y))
//匿名函数简写形式
println(calculator(2, 3, _ + _))
传递匿名函数至简原则
f( (a , b) => a + b)
f(_ + _) 或者 _ + _
要求
匿名只能有两个参数
每个参数只能使用一次
第一个_ 表示第一个参数,第二个_表示第二个参数
闭包
一个函数和与其相关的引用环境(变量)组合的一个整体(实体)
延长局部变量的生命周期
//外部变量
var x: Int = 10
//闭包
def f(x: Int, y: Int): Int = {
x + y
}
var x: Int = 10
//闭包
def f(x: Int, y: Int): Int = {
x + y
}
函数柯里化
将一个接收多个参数的函数转化成一个接受一个参数的函数过程,可以简单的理解为一种特殊的参数列表声明方式
val sum = (x: Int, y: Int, z: Int) => x + y + z
val sum1 = (x: Int) => {
y: Int => {
z: Int => {
x + y + z
}
}
}
val sum2 = (x: Int) => (y: Int) => (z: Int) => x + y + z
def sum3(x: Int)(y: Int)(z: Int) = x + y + z
val sum1 = (x: Int) => {
y: Int => {
z: Int => {
x + y + z
}
}
}
val sum2 = (x: Int) => (y: Int) => (z: Int) => x + y + z
def sum3(x: Int)(y: Int)(z: Int) = x + y + z
递归
一个函数/方法在函数/方法体内又调用了本身,我们称之为递归调用
控制抽象
Scala中可以自己定义类似于if-else,while的流程控制语句,即所谓的控制抽象。
惰性求值
当函数返回值被声明为lazy时,函数的执行将被推迟,直到我们首次对此取值,该函数才会执行。这种函数我们称之为惰性函数。
lazy不能修饰var类型的变量
lazy val res = sum(10, 30)
println("----------------")
println("res=" + res)
}
println("----------------")
println("res=" + res)
}
输出结果:
----------------
sum被执行。。。
res=40
----------------
sum被执行。。。
res=40
6 面向对象
Scala包
三大作用(与java一样)
区分相同名字的类
当类很多时,可以很好的管理类
控制访问范围
命名规则
只能包含数字、字母、下划线、小圆点.,但不能用数字开头,也不要使用关键字。
一般是小写字母+小圆点
com.公司名.项目名.业务模块名
com.公司名.项目名.业务模块名
包对象
定义在包对象中的成员,
作为其对应包下所有class和object的共享变量,
可以被直接访问。
作为其对应包下所有class和object的共享变量,
可以被直接访问。
package object com{
val shareValue="share"
def shareMethod()={}
}
val shareValue="share"
def shareMethod()={}
}
默认的导包
import java.lang._
import scala._
import scala.Predef._
import scala._
import scala.Predef._
访问权限
scala 中属性和方法的默认访问权限为public,但scala中无public关键字。
private为私有权限,只在类的内部和伴生对象中可用。
protected为受保护权限,Scala中受保护权限比Java中更严格,同类、子类可以访问,同包无法访问。
private[包名]增加包访问权限,包名下的其他类也可以
private为私有权限,只在类的内部和伴生对象中可用。
protected为受保护权限,Scala中受保护权限比Java中更严格,同类、子类可以访问,同包无法访问。
private[包名]增加包访问权限,包名下的其他类也可以
类和对象
定义类
[修饰符] class 类名 {
类体
}
类体
}
Scala语法中,类并不声明为public,所有这些类都具有公有可见性(即默认就是public)
一个Scala源文件可以包含多个类
一个Scala源文件可以包含多个类
属性是类的一个组成部分
[修饰符] var 属性名称 [:类型] = 属性值
var name: String = "bobo" //定义属性
Bean属性(@BeanPropetry)
可以自动生成规范的setXxx/getXxx方法
可以自动生成规范的setXxx/getXxx方法
//Bean属性(@BeanProperty)
@BeanProperty var sex: String = "男"
}
@BeanProperty var sex: String = "男"
}
def 方法名(参数列表) [:返回值类型] = {
方法体
}
方法体
}
class Person {
def sum(n1:Int, n2:Int) : Int = {
n1 + n2
}
}
def sum(n1:Int, n2:Int) : Int = {
n1 + n2
}
}
创建对象
val | var 对象名 [:类型] = new 类型()
val修饰对象,不能改变对象的引用(即:内存地址),可以改变对象属性的值。
var修饰对象,可以修改对象的引用和修改对象的属性值
构造器
和Java一样,Scala构造对象也需要调用构造方法,并且可以有任意多个构造方法。
分类
主构造器
主构造器无参数,小括号可省略,构建对象时调用的构造方法的小括号也可以省略。
辅助构造器
辅助构造器,函数的名称this,可以有多个,编译器通过参数的个数来区分。
辅助构造方法不能直接构建对象,必须直接或者间接调用主构造方法。
基本语法
class 类名(形参列表) { // 主构造器
// 类体
def this(形参列表) { // 辅助构造器
}
def this(形参列表) { //辅助构造器可以有多个...
}
}
// 类体
def this(形参列表) { // 辅助构造器
}
def this(形参列表) { //辅助构造器可以有多个...
}
}
构造器参数
Scala类的主构造器函数的形参包括三种类型:未用任何修饰、var修饰、val修饰
未用任何修饰符修饰,这个参数就是一个局部变量
var修饰参数,作为类的成员属性使用,可以修改
val修饰参数,作为类只读属性使用,不能修改
封装
封装就是把抽象出的数据和对数据的操作封装在一起,数据被保护在内部,
程序的其它部分只有通过被授权的操作(成员方法),才能对数据进行操作。
程序的其它部分只有通过被授权的操作(成员方法),才能对数据进行操作。
java封装
将属性进行私有化
提供一个公共的set方法,用于对属性赋值
提供一个公共的get方法,用于获取属性的值
提供一个公共的set方法,用于对属性赋值
提供一个公共的get方法,用于获取属性的值
scala封装
scala中的public属性,底层实际为private,并通过get方法(obj.field())和set方法(obj.field_=(value))对其进行操作。
所以scala并不推荐将属性设为private,再为其设置public的get和set方法的做法。
但由于很多java框架都利用反射调用getXXX和setXXX方法,有时候为了和这些框架兼容,
也会为scala的属性设置getXXX和setXXX方法(通过@BeanProperty注解实现)。
所以scala并不推荐将属性设为private,再为其设置public的get和set方法的做法。
但由于很多java框架都利用反射调用getXXX和setXXX方法,有时候为了和这些框架兼容,
也会为scala的属性设置getXXX和setXXX方法(通过@BeanProperty注解实现)。
继承
语法
class 子类名 extends 父类名 { 类体 }
子类继承父类的属性和方法
scala是单继承
抽象属性和抽象方法
抽象属性和抽象方法
定义
定义抽象类:abstract class Person{} //通过abstract关键字标记抽象类
定义抽象属性:val|var name:String //一个属性没有初始化,就是抽象属性
定义抽象方法:def hello():String //只声明而没有实现的方法,就是抽象方法
继承&重写
如果父类为抽象类,那么子类需要将抽象的属性和方法实现,否则子类也需声明为抽象类
重写非抽象方法需要用override修饰,重写抽象方法则可以不加override。
子类中调用夫类的方法使用super关键字
属性重写只支持val类型,而不支持var。
scala中属性和方法都是动态绑定,而java中只有方法为动态绑定。
匿名子类
Java一样,可以通过包含带有定义或重写的代码块的方式创建一个匿名的子类
当我们对一个实现了抽象类的子类我们只需要调用一次的时候我们可以直接使用匿名类
单例对象(伴生对象)
单例对象语法
Scala语言是完全面向对象的语言,所以并没有静态的操作(即在Scala中没有静态的概念)。
但是为了能够和Java语言交互(因为Java中有静态概念),就产生了一种特殊的对象来模拟类对象,该对象为单例对象。
若单例对象名与类名一致,则称该单例对象这个类的伴生对象,这个类的所有“静态”内容都可以放置在它的伴生对象中声明。
但是为了能够和Java语言交互(因为Java中有静态概念),就产生了一种特殊的对象来模拟类对象,该对象为单例对象。
若单例对象名与类名一致,则称该单例对象这个类的伴生对象,这个类的所有“静态”内容都可以放置在它的伴生对象中声明。
object Person{
val country:String="China"
}
val country:String="China"
}
说明:
单例对象采用object关键字声明
单例对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。
单例对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问。
apply方法
说明
通过伴生对象的apply方法,实现不使用new方法创建对象。
如果想让主构造器变成私有的,可以在()之前加上private。
apply方法可以重载。
Scala中obj(arg)的语句实际是在调用该对象的apply方法,即obj.apply(arg)。用以同一面向对象编程和函数式编程的风格。
特质(Trait)
特质声明
特质可以同时拥有抽象方法和具体方法
一个类可以混入(mixin)多个特质
所有的Java接口都可以当做Scala特质使用
动态混入:可灵活的扩展类的功能
动态混入:创建对象时混入trait,而无需使类混入该trait
如果混入的trait中有未实现的方法,则需要实现
特质基本语法
基本语法
类和特质的关系:使用继承的关系。
没有父类
class 类名 extends 特质1 with 特质2 with 特质3 …
有父类
class 类名 extends 父类 with 特质1 with 特质2 with 特质3…
当一个类去继承特质时,第一个连接词是extends,后面是with。
如果一个类在继承特质和父类时,应当把父类写在extends后。
如果一个类在继承特质和父类时,应当把父类写在extends后。
特质叠加
若混入的特质中具有相同的方法(方法名,参数列表,返回值均相同)
必然会出现继承冲突问题。
必然会出现继承冲突问题。
一个类(Sub)混入的两个trait(TraitA,TraitB)中具有相同的具体方法,
且两个trait之间没有任何关系,解决这类冲突问题,
直接在类(Sub)中重写冲突方法。
且两个trait之间没有任何关系,解决这类冲突问题,
直接在类(Sub)中重写冲突方法。
一个类(Sub)混入的两个trait(TraitA,TraitB)中具有相同的具体方法,
且两个trait继承自相同的trait(TraitC),及所谓的“钻石问题”,
解决这类冲突问题,Scala采用了特质叠加的策略。
且两个trait继承自相同的trait(TraitC),及所谓的“钻石问题”,
解决这类冲突问题,Scala采用了特质叠加的策略。
将混入的多个trait中的冲突方法叠加起来
特质叠加执行顺序
案例中的super,不是表示其父特质对象,而是表示上述叠加顺序中的下一个特质,
即,MyClass中的super指代Color,Color中的super指代Category,Category中的super指代Ball。
即,MyClass中的super指代Color,Color中的super指代Category,Category中的super指代Ball。
如果想要调用某个指定的混入特质中的方法,可以增加约束:super[],例如super[Category].describe()。
特质自身类型
扩展
类型检查和转换
obj.isInstanceOf[T]
判断obj是不是T类型。
obj.asInstanceOf[T]
将obj强转成T类型。
classOf获取对象的类名。
枚举类和应用类
枚举类
需要继承Enumeration
应用类
需要继承App
Type定义新类型
使用type关键字可以定义新的数据数据类型名称,本质上就是类型的一个别名
7 集合
集合简介
说明
cala的集合有三大类:序列Seq、集Set、映射Map,所有的集合都扩展自Iterable特质。
可变和不可变的
不可变集合:scala.collection.immutable
Scala不可变集合,就是指该集合对象不可修改,
每次修改就会返回一个新对象,而不会对原对象进行修改
每次修改就会返回一个新对象,而不会对原对象进行修改
可变集合: scala.collection.mutable
可变集合,就是这个集合可以直接对原对象进行修改,而不会返回新的对象。
不可变集合继承图
1)Set、Map是Java中也有的集合
2)Seq是Java没有的,我们发现List归属到Seq了,因此这里的List就和Java不是同一个概念了
3)我们前面的for循环有一个 1 to 3,就是IndexedSeq下的Vector
4)String也是属于IndexeSeq
5)我们发现经典的数据结构比如Queue和Stack被归属到LinerSeq
6)大家注意Scala中的Map体系有一个SortedMap,说明Scala的Map可以支持排序
7)IndexSeq和LinearSeq的区别:
(1)IndexSeq是通过索引来查找和定位,因此速度快,比如String就是一个索引集合,通过索引即可定位
(2)LineaSeq是线型的,即有头尾的概念,这种数据结构一般是通过遍历来查找
2)Seq是Java没有的,我们发现List归属到Seq了,因此这里的List就和Java不是同一个概念了
3)我们前面的for循环有一个 1 to 3,就是IndexedSeq下的Vector
4)String也是属于IndexeSeq
5)我们发现经典的数据结构比如Queue和Stack被归属到LinerSeq
6)大家注意Scala中的Map体系有一个SortedMap,说明Scala的Map可以支持排序
7)IndexSeq和LinearSeq的区别:
(1)IndexSeq是通过索引来查找和定位,因此速度快,比如String就是一个索引集合,通过索引即可定位
(2)LineaSeq是线型的,即有头尾的概念,这种数据结构一般是通过遍历来查找
可变集合继承图
数组
不可变数组
定义
方法1
val arr1 = new Array[Int](10)
(1)new是关键字
(2)[Int]是指定可以存放的数据类型,如果希望存放任意数据类型,则指定Any
(3)(10),表示数组的大小,确定后就不可以变化
(2)[Int]是指定可以存放的数据类型,如果希望存放任意数据类型,则指定Any
(3)(10),表示数组的大小,确定后就不可以变化
方法2
val arr1 = Array(1, 2)
(1)在定义数组时,直接赋值
(2)使用apply方法创建数组对象
(2)使用apply方法创建数组对象
可变数组
定义
val arr01 = ArrayBuffer[Any](3, 2, 5)
(1)[Any]存放任意数据类型
(2)(3, 2, 5)初始化好的三个元素
(3)ArrayBuffer需要引入scala.collection.mutable.ArrayBuffer
(2)(3, 2, 5)初始化好的三个元素
(3)ArrayBuffer需要引入scala.collection.mutable.ArrayBuffer
(1)ArrayBuffer是有序的集合
(2)增加元素使用的是append方法(),支持可变参数
(2)增加元素使用的是append方法(),支持可变参数
不可变数组与可变数组的转换
不可长数组转可变数组
arr1.toBuffer
可变数组转不可变数组
arr2.toArray
返回结果才是一个不可变数组
arr1以及arr2本身没有变化
arr1以及arr2本身没有变化
多维数组
val arr = Array.ofDim[Double](3,4)
说明:二维数组中有三个一维数组,每个一维数组中有四个元素
说明:二维数组中有三个一维数组,每个一维数组中有四个元素
Seq集合(List)
不可变List
List默认为不可变集合
创建一个List(数据有顺序,可重复)
val list: List[Int] = List(1,2,3,4,3)
遍历List
list.foreach(println)
List增加数据
::的运算规则从右向左
//val list1 = 5::list
//val list1 = 5::list
添加到第一个元素位置
val list2 = list.+:(5)
val list2 = list.+:(5)
集合间合并:将一个整体拆成一个一个的个体,称为扁平化
val list4 = list3:::list1
取指定数据
println(list(0))
空集合Nil
val list5 = 1::2::3::4::Nil
可变ListBuffer
创建一个可变集合ListBuffer
val buffer = ListBuffer(1,2,3,4)
向集合中添加数据
buffer.+=(5)
打印集合数据
buffer.foreach(println)
Set集合
不可变Set
Set默认是不可变集合,数据无序
val set = Set(1,2,3,4,5,6)
数据不可重复
遍历集合
for(x<-set){
println(x)
println(x)
不可变mutable.Set
创建可变集合mutable.Set
val set = mutable.Set(1,2,3,4,5,6)
打印集合
set.foreach(println)
println(set.mkString(","))
println(set.mkString(","))
集合添加元素
set += 8
向集合中添加元素,返回一个新的Set删除数据
val ints = set.+(9)
删除数据
set-=(5)
Map集合
Scala中的Map和Java类似,也是一个散列表,它存储的内容也是键值对(key-value)映射,Scala中不可变的Map是有序的,可变的Map是无序的。
不可变Map
创建不可变集合Map
val map = Map( "a"->1, "b"->2, "c"->3 )
循环打印
map.foreach((kv)=>{println(kv)})
访问数据
for (elem <- map.keys) {
// 使用get访问map集合的数据,会返回特殊类型Option(选项):有值(Some),无值(None)
println(elem + "=" + map.get(elem).get)
}
// 使用get访问map集合的数据,会返回特殊类型Option(选项):有值(Some),无值(None)
println(elem + "=" + map.get(elem).get)
}
如果key不存在,返回0
println(map.get("d").getOrElse(0))
println(map.getOrElse("d", 0))
println(map.getOrElse("d", 0))
可变Map
创建可变集合
val map = mutable.Map( "a"->1, "b"->2, "c"->3 )
打印集合
map.foreach((kv)=>{println(kv)})
向集合增加数据
map.+=("d"->4)
// 将数值4添加到集合,并把集合中原值1返回
val maybeInt: Option[Int] = map.put("a", 4)
println(maybeInt.getOrElse(0))
val maybeInt: Option[Int] = map.put("a", 4)
println(maybeInt.getOrElse(0))
删除数据
map.-=("b", "c")
修改数据
map.update("d",5)
元组
元组也是可以理解为一个容器,可以存放各种相同或不同类型的数据。说的简单点,就是将多个无关的数据封装为一个整体,称为元组。
元组中最大只能有22个元素。
声明元组的方式:(元素,元素2,元素3)
val tuple: (Int, String, Boolean) = (40,"bobo",true)
访问元组
通过元素的顺序进行访问,调用方式:_顺序号
println(tuple._1)
通过索引访问数据
println(tuple.productElement(0))
通过迭代器访问数据
for (elem <- tuple.productIterator) {
println(elem)
}
println(elem)
}
Map中的键值对其实就是元组,只不过元组的元素个数为2,称之为对偶
val map = Map("a"->1, "b"->2, "c"->3)
map.foreach(tuple=>{println(tuple._1 + "=" + tuple._2)})
map.foreach(tuple=>{println(tuple._1 + "=" + tuple._2)})
集合常用函数
基本属性和常用操作
获取集合长度
println(list.length)
获取集合大小
println(list.size)
循环遍历
list.foreach(println)
迭代器
for (elem <- list.iterator) {
println(elem)
}
println(elem)
}
生成字符串
println(list.mkString(","))
是否包含
println(list.contains(3))
衍生集合
获取集合的头head
list1.head
获取集合的尾(不是头就是尾tail
list1.tail
集合最后一个数据 last
list1.last
集合初始数据(不包含最后一个
list1.init
反转
list1.reverse
取前(后) n个元素
list1.take(3)
list1.takeRight(3)
去掉前(后n个元素)
list1.drop(3)
list1.dropRight(3)
并集
list1.union(list2)
交集
list1.intersect(list2)
差集
list1.diff(list2)
拉链
list1.zip(list2)
滑窗
list1.sliding(2, 5).foreach(println)
集合计算初级函数
求和
list.sum
求乘积
list.product
最大值
list.max
最小值
list.min
排序
按照元素大小排序
println(list.sortBy(x => x))
println(list.sortBy(x => x))
按照元素的绝对值大小排序
println(list.sortBy(x => x.abs))
println(list.sortBy(x => x.abs))
按元素大小升序排序
println(list.sortWith((x, y) => x < y))
println(list.sortWith((x, y) => x < y))
按元素大小降序排序
println(list.sortWith((x, y) => x > y))
println(list.sortWith((x, y) => x > y))
集合计算高级函数
过滤
list.filter(x => x % 2 == 0)
转化/映射
list.map(x => x + 1)
扁平化
nestedList.flatten)
扁平化+映射 注:flatMap相当于先进行map操作,在进行flatten操作
wordList.flatMap(x => x.split(" "))
分组
list.groupBy(x => x % 2)
简化(规约)
Reduce简化(规约) :通过指定的逻辑将集合中的数据进行聚合,从而减少数据,最终获取结果。
list.reduce( (x,y) => x-y )
折叠
普通WordCount案例
复杂WordCount案例
队列
scala也提供了队列(Queue)的数据结构,
队列的特点就是先进先出。
进队和出队的方法分别为enqueue和dequeue
队列的特点就是先进先出。
进队和出队的方法分别为enqueue和dequeue
val que = new mutable.Queue[String]()
que.enqueue("a", "b", "c")
que.dequeue()
que.enqueue("a", "b", "c")
que.dequeue()
并行集合
Scala为了充分使用多核CPU,提供了并行集合(有别于前面的串行集合),用于多核环境的并行计算。
val result1 = (0 to 100).map{case _ => Thread.currentThread.getName}
val result2 = (0 to 100).par.map{case _ => Thread.currentThread.getName}
val result2 = (0 to 100).par.map{case _ => Thread.currentThread.getName}
8 模式匹配
基本语法
类似于Java中的switch语法,但是更加强大
模式匹配语法中,采用match关键字声明
每个分支采用case关键字进行声明
每个分支采用case关键字进行声明
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"
}
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"
}
子主题
=> 后面的代码块,是作为一个整体执行,可以使用{}括起来,也可以不括。
match case语句可以匹配任何类型,而不只是字面量。
每个case中,不用break语句,自动中断case。
如果所有case都不匹配,那么会执行case _ 分支,
类似于Java中default语句,若没有case _ 分支,那么会抛出MatchError。
类似于Java中default语句,若没有case _ 分支,那么会抛出MatchError。
模式守卫
如果想要表达匹配某个范围的数据,就需要在模式匹配中增加条件守卫。
def abs(x: Int) = x match {
case i: Int if i >= 0 => i
case j: Int if j < 0 => -j
case _ => "type illegal"
}
case i: Int if i >= 0 => i
case j: Int if j < 0 => -j
case _ => "type illegal"
}
模式匹配类型
匹配常量
scala中,模式匹配可以匹配所有的字面量,包括字符串,字符,数字,布尔值等等。
def describe(x: Any) = x match {
case 5 => "Int five"
case "hello" => "String hello"
case true => "Boolean true"
case '+' => "Char +"
}
case 5 => "Int five"
case "hello" => "String hello"
case true => "Boolean true"
case '+' => "Char +"
}
匹配类型
类型判断时,可以使用isInstanceOf[T]和asInstanceOf[T],
也可使用模式匹配实现同样的功能。
也可使用模式匹配实现同样的功能。
def describe(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
}
case i: Int => "Int"
case s: String => "String hello"
case m: List[_] => "List"
case c: Array[Int] => "Array[Int]"
case someThing => "something else " + someThing
}
匹配数组
scala模式匹配可以对集合进行精确的匹配,例如匹配只有两个元素的、且第一个元素为0的数组。
for (arr <- Array(Array(0), Array(1, 0), Array(0, 1, 0), Array(1, 1, 0), Array(1, 1, 0, 1), Array("hello", 90))) { // 对一个数组集合进行遍历
val result = arr match {
case Array(0) => "0" //匹配Array(0) 这个数组
case Array(x, y) => x + "," + y //匹配有两个元素的数组,然后将将元素值赋给对应的x,y
case Array(0, _*) => "以0开头的数组" //匹配以0开头和数组
case _ => "something else"
}
println("result = " + result)
}
val result = arr match {
case Array(0) => "0" //匹配Array(0) 这个数组
case Array(x, y) => x + "," + y //匹配有两个元素的数组,然后将将元素值赋给对应的x,y
case Array(0, _*) => "以0开头的数组" //匹配以0开头和数组
case _ => "something else"
}
println("result = " + result)
}
匹配列表
方法一
for (list <- Array(List(0), List(1, 0), List(0, 0, 0), List(1, 0, 0), List(88))) {
val result = list match {
case List(0) => "0" //匹配List(0)
case List(x, y) => x + "," + y //匹配有两个元素的List
case List(0, _*) => "0 ..."
case _ => "something else"
}
println(result)
}
val result = list match {
case List(0) => "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")
}
list match {
case first :: second :: rest => println(first + "-" + second + "-" + rest)
case _ => println("something else")
}
匹配元组
for (tuple <- Array((0, 1), (1, 0), (1, 1), (1, 0, 2))) {
val result = tuple match {
case (0, _) => "0 ..." //是第一个元素是0的元组
case (y, 0) => "" + y + "0" // 匹配后一个元素是0的对偶元组
case (a, b) => "" + a + " " + b
case _ => "something else" //默认
}
println(result)
}
val result = tuple match {
case (0, _) => "0 ..." //是第一个元素是0的元组
case (y, 0) => "" + y + "0" // 匹配后一个元素是0的对偶元组
case (a, b) => "" + a + " " + b
case _ => "something else" //默认
}
println(result)
}
匹配对象以及样例类
语法
case class Person (name: String, age: Int)
子主题
val user: User = User("zhangsan", 11)
val result = user match {
case User("zhangsan", 11) => "yes"
case _ => "no"
}
val result = user match {
case User("zhangsan", 11) => "yes"
case _ => "no"
}
变量声明中的模式匹配
case class Person(name: String, age: Int)
object TestMatchVariable {
def main(args: Array[String]): Unit = {
val (x, y) = (1, 2)
println(s"x=$x,y=$y")
val Array(first, second, _*) = Array(1, 7, 2, 9)
println(s"first=$first,second=$second")
val Person(name, age) = Person1("zhangsan", 16)
println(s"name=$name,age=$age")
}
}
object TestMatchVariable {
def main(args: Array[String]): Unit = {
val (x, y) = (1, 2)
println(s"x=$x,y=$y")
val Array(first, second, _*) = Array(1, 7, 2, 9)
println(s"first=$first,second=$second")
val Person(name, age) = Person1("zhangsan", 16)
println(s"name=$name,age=$age")
}
}
for表达式中的模式匹配
偏函数中的模式匹配
偏函数也是函数的一种,通过偏函数我们可以方便的对输入参数做更精确的检查。
例如该偏函数的输入类型为List[Int],而我们需要的是第一个元素是0的集合,这就是通过模式匹配实现的。
例如该偏函数的输入类型为List[Int],而我们需要的是第一个元素是0的集合,这就是通过模式匹配实现的。
定义
val second: PartialFunction[List[Int], Option[Int]] = {
case x :: y :: _ => Some(y)
}
case x :: y :: _ => Some(y)
}
该偏函数的功能是返回输入的List集合的第二个元素
9 异常
java异常处理
Java语言按照try—catch—finally的方式来处理异常
不管有没有异常捕获,都会执行finally,因此通常可以在finally代码块中释放资源。
可以有多个catch,分别捕获对应的异常,这时需要把范围小的异常类写在前面,
把范围大的异常类写在后面,否则编译错误。
把范围大的异常类写在后面,否则编译错误。
Scala异常处理
10 隐式转换
隐式函数
隐式转换可以再不需改任何代码的情况下,扩展某个类的功能。
隐式参数
普通方法或者函数可以通过implicit关键字声明隐式参数,调用该方法时,
就可以传入该参数,编译器会再相应的作用域寻找符合条件的隐式值。
就可以传入该参数,编译器会再相应的作用域寻找符合条件的隐式值。
(1)同一个作用域中,相同类型的隐式值只能有一个
(2)编译器按照隐式参数的类型去寻找对应类型的隐式值,与隐式值的名称无关。
(3)隐式参数优先于默认参数
(2)编译器按照隐式参数的类型去寻找对应类型的隐式值,与隐式值的名称无关。
(3)隐式参数优先于默认参数
隐式类
可以使用implicit声明类,隐式类的非常强大,同样可以扩展类的功能,在集合中隐式类会发挥重要的作用
(1)其所带的构造参数有且只能有一个
(2)隐式类必须被定义在“类”或“伴生对象”或“包对象”里,即隐式类不能是顶级的。
(2)隐式类必须被定义在“类”或“伴生对象”或“包对象”里,即隐式类不能是顶级的。
隐式解析机制
(1)首先会在当前代码作用域下查找隐式实体(隐式方法、隐式类、隐式对象)。(一般是这种情况)
(2)如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找。
类型的作用域是指与该类型相关联的全部伴生对象以及该类型所在包的包对象。
(2)如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找。
类型的作用域是指与该类型相关联的全部伴生对象以及该类型所在包的包对象。
11 泛型
基本概念
泛型用于指定方法或类可以接受任意类型参数,参数在实际使用时才被确定,泛型可以有效地增强程序的适用性,
使用泛型可以使得类或方法具有更强的通用性。泛型的典型应用场景是集合及集合中的方法参数,
可以说同java一样,scala中泛型无处不在,具体可以查看scala的api。
使用泛型可以使得类或方法具有更强的通用性。泛型的典型应用场景是集合及集合中的方法参数,
可以说同java一样,scala中泛型无处不在,具体可以查看scala的api。
class Student[T](val localId: T) {
def getSchoolId(hukouId: T) = "S-" + hukouId + "-" + localId
}
val leo = new Student[Int](111)
def getSchoolId(hukouId: T) = "S-" + hukouId + "-" + localId
}
val leo = new Student[Int](111)
协变和逆变
语法
class MyList[+T]{ //协变
}
class MyList[-T]{ //逆变
}
class MyList[T] //不变
}
class MyList[-T]{ //逆变
}
class MyList[T] //不变
说明
协变:Son是Father的子类,则MyList[Son] 也作为MyList[Father]的“子类”。
逆变:Son是Father的子类,则MyList[Son]作为MyList[Father]的“父类”。
不变:Son是Father的子类,则MyList[Father]与MyList[Son]“无父子关系”。
逆变:Son是Father的子类,则MyList[Son]作为MyList[Father]的“父类”。
不变:Son是Father的子类,则MyList[Father]与MyList[Son]“无父子关系”。
泛型上下限
语法
Class PersonList[T <: Person]{ //泛型上限
}
Class PersonList[T >: Person]{ //泛型下限
}
}
Class PersonList[T >: Person]{ //泛型下限
}
说明
泛型的上下限的作用是对传入的泛型进行限定。
上下文限定
语法
def f[A : B](a: A) = println(a) //等同于def f[A](a:A)(implicit arg:B[A])=println(a)
说明
上下问限定是将泛型和隐式转换的结合产物,以下两者功能相同,
使用上下文限定[A : Ordering]之后,方法内无法使用隐式参数名调用隐式参数,
需要通过implicitly[Ordering[A]]获取隐式变量。
使用上下文限定[A : Ordering]之后,方法内无法使用隐式参数名调用隐式参数,
需要通过implicitly[Ordering[A]]获取隐式变量。
12 复习
1 基础知识
常量 val和变量 val
尽量使用常量
变量的命名
数字字母
下划线
特殊的用法
数据类型
java
基本数据类型
引用数据类型
scala
所有的类型都是对象 Any
AnyVal
Long
Int
...
Unit
类似java的void
AnyRef
java中所有的类,到scala中都属于
scala所有的集合
定义所有的类
Null
只有一个null
Nothing
所有类的子类
抛得异常,则返回类都是Nothing
没有对象也没有子类
数据之间的转换
值类型之间的转换
.toInt
.toLong
...
AnyRef类型之间的转换
.isInstanceof[...]
.asInstanceof[...]
流程控制
顺序
选择
if else if else
模式匹配
循环
while
do while
很少用
for
本质上是一种遍历或迭代
遍历一个序列(集合,字符串)
守卫
嵌套
for推导
var nums=for(i<- 1 to 100) yield i*i
var nums = (1 to 100).map(x=>x*x) 替换上面
循环的退出
抛出异常
使用return
运算符
在scala中没有真正意义的运算符,所有的运算符都是方法名
1 + 2 ==== 1.+(2)
结合性
左结合
数学运算符
右结合
:结尾的运算符
::
在头部添加元素
:::
在头部合并集合
/:
+:
赋值符
a= 3+4
2 函数式编程
基本概念
一等公民
纯函数
没有副作用(对外界产生影响,比如修改外界变量,控制台写东西,数据写入文件等)
引用透明(最后的返回值只依赖参数,不依赖任何外部数据)
基本用法
定义
def sum(a:Int,b:Int)
参数
都是常量
参数的默认值
命名参数
返回值
可以省略,scala会自动推导
内部不能有return
高阶函数
概念
参数接受一个或者多个函数,或者返回值是函数
如何传递
给高阶函数传递函数
匿名函数
( )=>函数的实现
(a:Int,b:Int) => a+b
传给高阶函数的时候
参数的类型可以省略
(a,b) => a+b
简化函数
使用下划线进行化简
参数只用一次,可以简写
arr.reduce(_+_)
闭包
匿名函数和它的环境就是闭包
延长局部变量的生命周期
def foo():Int {
val a=10
()=> a+10
}
val f=foo()
f()
val a=10
()=> a+10
}
val f=foo()
f()
柯里化
理论基础 闭包
把一个参数列表变成多个参数列表
def foo(必传参数)(隐式参数)
部分应用函数
val f = math.pow(_,2)
省略的时候,有时候不能省,可以用部分应用函数来解释
Array(1,2,3).map(x => x)
Array(1,2,3).map(x ) 错误的
抽象控制
名调用和值调用
名调用
控制抽象的理论基础
3 + 4
3+4 这个表达式传递过去,没有先计算
值调用
f(3+4)
等价f(7)
3 面向对象
思想和java一样
类的定义
构造函数
主构造
位置和类名一块
辅助构造
首行必须是主构造
可以有多个
访问权限
默认都是公共
包
导包极其灵活
封装
属性的封装
自己格式的getter和setter
@BeanProperty
可以添加java格式的标准bean
伴生对象和伴生类
object和class的名字相等
特点
必须要一个文件中
可以相互访问对方的私有成员
apply方法
任何对象都可以像调用函数一样去调用对象
继承
extends
覆写
方法的覆写
两同
方法名,参数列表
两小
返回值类型,抛出的异常类型
一大
访问权限
属性的覆写
val可以覆写val和没有参数的def
var只能覆写抽象的var
抽象类
abatract修饰
抽象函数
只有函数的签名,没有函数体
抽象字段
只有声明,没有初始化
trait
当接口用就行
extends,with
动态混入
样例类
使用极其广泛
用来完全替换掉了java的bean
配合模式匹配
4 集合
非常重要
整体架构
不可变集合
Seq
List
空的
List[Int]()
Nil
Set
Map
可变集合
ArrayBuffer
其他
通用的方法和函数
简单
head
拿出第一个
last
拿出最后一个
tail
干掉第一个,干掉前n个,后面的取出来
init
干掉最后一个,干掉前n个,后面的取出来
take(n)
取出前n个
drop(n)
干掉前n个,后面的取出来
takeright(n)
从后面取n个
dropright(n)
干掉后面n个,取余下的
复杂
高阶算子
forearch
map
flatMap
reduce
foldLeft
scanLeft
zip
zip
zipAll
zipWithIndex
偏函数
5 隐式转换
隐式转换函数
implicit def ...
隐式转换函数只看参数类型和返回值的类型,与函数名无关
隐式值类
是隐式转换函数的升级版,语法糖
隐式参数和隐式值
柯里化的时候,这块用的比较多
语法糖的:泛型上下文
查找路径
当前作用域
相关类型的伴生对象中
6 模式匹配
重要和强大
类型匹配
集合内容匹配
对象匹配
把未知的对象匹配到已知的对象中去
偏函数
7 泛型
0 条评论
下一页