Scala笔记
2023-06-07 13:43:52 0 举报
AI智能生成
Scala笔记
作者其他创作
大纲/内容
解压文件
配置路径
vim /etc/profile
source /etc/profile
Scala环境的搭建(基于Mac系统)
对应版本下载plugins
install plugins from disk
重启,创建maven项目
add framework support
Idea下Scala插件的加载
Scala入门第一步
Scala是一门以java虚拟机(JVM)为目标运行环境并将面向对象和函数式编程的最佳特性结合在一起的静态类型编程语言
运行时代码可以根据某些条件改变自身结构
动态语言
运行时结构不可变的语言就是静态语言
静态语言
静态语言与动态语言
是指在运行期间才去做数据类型检查的语言,说的是数据类型
动态类型语言
在编译期间(或运行之前)确定的,编写代码的时候要明确确定变量的数据类型
静态类型语言
静态类型语言与动态类型语言
需通过编译器(compiler)将源代码编译成机器码,之后才能执行的语言
编译型语言
解释性语言的程序不需要编译,相比编译型语言省了道工序,解释性语言在运行程序的时候才逐行翻译
解释型语言
解释型语言与编译型语言
补充:
Scala是什么
Scala 是一门多范式 (multi-paradigm) 的编程语言,Scala支持面向对象和函数式编程
Scala源代码(.scala)会被编译成Java字节码(.class),然后运行于JVM之上,并可以调用现有的Java类库,实现两种语言的无缝对接。
简洁高效
基于Java设计思想,融合函数式编程语言,注意Java与Scala的相同与不同
Scala语言特点
object ScalaTest {
def main(args : Array[String]) : Unit = {
println("Hello World")
}
代码示例
Scala的执行代码基本结构
如果使用Object声明类,可以编译出两个类,一个是当前类,另外一个是当前类的辅助类(类名$),执行时,辅助类可以直接构建对象使用(伴生对象)
主要目的是模拟静态语法
使用object关键字声明
使用def关键字(defined)声明
Scala中没有public关键字,默认public访问权限
不需要使用public关键字声明
Scala完全面向对象,所以没有静态语法,因而没有static关键字
静态是基于类的,而非对象,面向对象,因而没有静态语法
不需要使用static关键字声明
Scala无void关键字,因为void也并非面向对象语法
不需要使用void关键字声明返回值类型
java :参数类型 参数名
scala : 参数名 :参数类型
方法都有参数列表,但是参数的声明方式不同
java : 返回值类型 方法名
scala : 方法名 () : 返回值类型
scala中的Unit类型代表无返回值 void => Unit
声明main方法
与Java的对比
Scala执行代码的基本结构与Java的对比
Scala概述
var | val 变量名[: 变量类型]
基本语法
类型推断:声明时可省略类型
强数据类型:类型确定后不可更改
声明变量时必须有初始值
val指向的是引用,只要更改它的引用项就可以更改
但对象的状态(值)却是可以改变的。(比如: 自定义对象、数组、集合等等)
var修饰可更改,val修饰不可更改
注意事项
变量定义
64位,双精度浮点数
Double
32位,单精度浮点数
Float
浮点型
64位
Long
32位,-21E~21E
Int
16位,-3W2~3W2
Short
8位,-128到127
Byte
整型
Boolean
Char
StringOps
表示无值,和其他语言中的void等同,只有一个实例()
Unit
AnyVal
Scala collections
all java classes
Other Scala classes
备注:Null为所以引用对象的子类,只有一个实例null
AnyRef
Nothing是所有类型的子类
备注
Any
分支主题
图示
Scala数据类型
小转大,自动转
大转小,需强转
规则
多种类型混合运算时,自动将所有数据转为最大的
细节
自动类型转换(implicit conversion)
.toXxx
用法
强制类型转换
值类型转换
变量与数据类型
标识符命名规则
标识符命名规范
标识符的命名规范
Scala变量
整数除只保留整数部分
/号整数除与小数除有区别
Scala中没有++、--运算符,通过+=、-=来实现同样效果
算术运算符
==
!=
<
>
<=
>=
符号
True Or False
结果
比较运算符
&&
&
||
|
逻辑运算符
=
+=
-=
*=
/=
%=
<<=
>>=
&=
^=
|=
赋值运算符
^
~
<<
>>
>>>
位运算符
Scala运算符
顺序控制
单分支
双分支
多分支
if-else
val oper = '#'
val n1 = 20
val n2 = 10
var res = 0
oper match {
case '+' => res = n1 + n2
case '-' => res = n1 - n2
case '*' => res = n1 * n2
case '/' => res = n1 / n2
case _ => println("oper error")
println("res=" + res)
代码实例
都不匹配,执行case _,类似于default
都不匹配,有没有case _,抛出MatchError
不用break语句,自动中断case
case _ 在前则先执行case _,然后跳出
基础知识
如果想要表达匹配某个范围的数据,就需要在模式匹配中增加条件守卫
概念介绍
for (ch <- "+-3!") {
var sign = 0
var digit = 0
ch match {
case '+' => sign = 1
case '-' => sign = -1
case _ if ch.toString.equals("3") => digit = 3
case _ => sign = 2
}
println(ch + " " + sign + " " + digit)
守卫
case后接变量,则将表达式值赋给变量
模式中的变量
val result = obj match {
case a : Int => a
case d : Array[String] => "对象是一个字符串数组"
case e : Array[Int] => "对象是一个数字数组"
case f : BigInt => Int.MaxValue
case _ => "啥也不是"
println(result)
类型匹配
对象匹配
具体匹配
样例类仍然是类
样例类用case关键字进行声明。
样例类是为模式匹配(对象)而优化的类
构造器中的每一个参数都成为val——除非它被显式地声明为var
在样例类对应的伴生对象中提供apply方法让你不用new关键字就能构造出相应的对象
提供unapply方法让模式匹配可以工作
将自动生成toString、equals、hashCode和copy方法(有点类似模板类,直接给生成,供程序员使用)
除上述外,样例类和其他类完全一样。你可以添加方法和字段,扩展它们
样例类默认很多方法,供直接使用
abstract class Amount
case class Dollar(value: Double) extends Amount
case object NoAmount extends Amount
list match {
case first :: second :: rest => println(first + “**” + second + “**” + rest)
case _ => println("匹配不到...")
case语句的中置表达式
样例类
如果想让case类的所有子类都必须在申明该类的相同的源文件中定义,可以将样例类的通用超类声明为sealed,这个超类称之为密封类。
abstract sealed class Amount
case class Dollar(value : Double) extends Amount
case object Nothing extends Amount
密封类
match-case
类Switch分支
一般嵌套不超过3层
嵌套分支
类型
Scala中任意表达式都是有返回值的,也就意味着if else表达式其实是有返回结果的,具体返回结果的值取决于满足条件的代码体的最后一行内容.
val result = if (flg) 1 else 0
Scala中没有三元运算符
Scala注意事项
分支控制
for(i <- 1 to 3)
for(i <- 1 until 3)
基本循环
true进入循环内部
false跳过
for(i <- 1 to 3 if i != 2)
循环守卫
val result = for(i <- 1 to 10) yield i * 2
循环返回值
for {
i <- 1 to 5
j = i - 1
} {
}
多行逻辑小括号变大括号
println("i = " + i)
方法1
println(i)
方法2
循环步长
for
var i = 0
breakable{
\twhile(i < 10){
\t\tif(i == 5) {
\t\t\tbreak
\t\t}
\t\tprintln("i=" + i)
\t\ti = i +1
\t}
while循环中的Break操作
while
循环控制
Scala程序流程控制
def 函数名(参数列表) : 函数返回值类型 = {
\t函数体
函数的声明:方法即函数,类中函数称方法,方法有重载和重写,函数没有
Scala语法松散,任何语法结构都可以嵌套,方法中可以声明函数,函数中可以声明函数,但方法中不能声明方法
函数概述
可变参数在类型后面加*,可变参数应该在参数列表的最后声明
当一个函数中有多个传入参数,且有默认值时,建议使用带名参数,带名指定赋值参数
Scala中的可变参数与带名参数
def f2(name : String) : String = {
"Name :" + name
声明函数后,可以将函数体最后一行作为返回值,所以return关键字可以省略
def f3(name : String) = {
"Name :" + name
如果函数可以自动推断返回值类型,那么返回值类型可以省略
def f4(name : String) = "Name" + name
如果函数逻辑只有一行代码,那么花括号可以省略
def f5 = "abc"
如果参数列表没有参数,那么参数列表的括号可以省略
如果函数的参数列表已声明,那么调用时可以使用小括号,也可以不使用
如果函数的参数列表未声明,那么调用时不能使用小括号
参数列表
def f6() : Int = {
return 2
如果函数体中有明确的return操作,必须声明返回值类型
def f7() : Unit = {
如果函数声明返回值类型为Unit,那么函数体中的return不起作用
def f8(){
return 3
如果Unit类型也想省略,可以同时将等号省略
()->{println("xxxxxxxx")}
如果只关心逻辑,而不关心方法名称,那么函数名称可以省略,def关键字也可省略
val f9 = (i : Int)=>{println("xxxxx")}
匿名函数
Scala语法规则
新函数的参数接受原来的第二个参数为唯一参数
函数柯里化
如果函数中使用外部变量,为了防止变量数据丢失,将变量包含到函数的内部,形成闭合的效果,形成闭包,改变变量的生命周期
闭包就是一个函数和与其相关的引用环境(变量/值)组合的一个整体(实体)。
def minusxy(x: Int) = (y: Int) => x – y
//minusxy 他会返回一个匿名函数 (y: Int) => x – y
//匿名函数,他使用了一个外部的变量 x
//f函数就是闭包.
val f = minusxy(20)
println("f(1)=" + f(1)) //
println("f(2)=" + f(2)) //
函数闭包
函数柯里化和函数闭包
函数自己调用自己
函数应该有跳出递归的逻辑
函数调用自身的深度不要太深
递归函数在调用时,需要传递的参数有规律
Scala中,递归函数不能省略返回值类型
递归的注意事项
递归
lazy不可修饰var型变量
惰性函数
try {
val r = 10 / 0
} catch {
case ex: ArithmeticException=> println(“捕获了除数为零的算术异常")
case ex: Exception => println("捕获了异常")
} finally {
// 最终要执行的代码
println("scala finally...")
Scala中的异常处理与Java类似
case类似于switch case的写法
异常
执行isDefinedAt() 为false 就过滤掉这个元素,即不构建新的Int对象.
处理全部数据,选取自己想要的数据,其他数据抛弃
//说明
def isDefinedAt(any: Any) = {
if (any.isInstanceOf[Int]) true
else false
def apply(any: Any) = any.asInstanceOf[Int] + 1
完整版
println(list3)
简化版
偏函数
参数是函数
函数参数没有输入值也没有返回值
控制抽象概念
def myRunInThread(f1: => Unit): Unit = { 11; new Thread {11; override def run(): Unit = {11; f1 11; }11; }.start()11;}11;myRunInThread { //说明11; println("干活咯!5秒完成...") Thread.sleep(5000)11; println("干完咯!")11;}
控制抽象
函数式编程思想
package com.atguigu.xxx
import java.util.List
class Test extends Parent implements Interface {
private String fields;
public boolean login(){
return true
}
Java面向对象编程
package com.atguigu.base.Object
object Scala01_Object {
def main(args: Array[String]): Unit = {
val user = new User()
println(user.name)
}
Scala面向对象编程
Java面向对象编程与Scala面向对象编程对比
课程笔记
分门别类的管理的类
区分相同名称的类
包的访问权限
Java包的作用
package关键字可以多次声明
Scala源码和包名称没有直接的关系
package可以声明作用域
Scala将包当成一个对象
Scala包的作用
域名的反写 + 项目名 + 模块 + 程序类型
包的命名
包对象
包(package)
可以在任意的地方使用
可以导入指定包中的类,java.lang和scala中的类不需要
import java.util._
导入指定包中所有的类,在scala中使用下划线代替java中的星号
导入指定包中的多个类,可以使用大括号,声明在一行
import java.util
scala中的import可以导包
import sacala.collection.mutable._
scala可以在导类时候隐藏指定的类
scala可以给导入的类起别名
new _root_.java.util.HashMap
scala中包的概念是相对路径,这样可能会出现冲突,一般采用绝对路径的方式解决
Import
类
为了和java的开发规范匹配,提供注解(@BeanProperty)来生成2对set/get方法(Scala和Java的)
Scala也可以给属性默认初始化,使用_
private var name = "ZHANGSAN"
私有属性,类内部使用,外部无法使用
protected var age = 20
Scala中受保护的权限只能子类访问,同包无法访问
private[Object] var email = "xxxx@163.com"
private后需要增加中括号,指明能访问包的名称
使用private作为访问修饰符
属性
类中的方法其实就是函数,所以遵循函数的声明和调用
Scala中允许不使用new构建对象,会调用伴生对象的特殊方法来构建对象
方法
对象
类与对象的内存分配机制
类与对象
主构造器(一个) 和 辅助构造器(多个),构造器也是方法,辅助构造函数一定要直接或间接地调用主构造函数
class 类名(形参列表) { // 主构造器
// 类体
def this(形参列表) { // 辅助构造器
}
def this(形参列表) { //辅助构造器可以有多个...
}
构造器
定义
如果属性声明为private的,那么自动生成的setter/getter方法也是private的
如果属性省略访问权限修饰符,那么自动生成的setter/getter方法是public的
声明属性时,本身就自动提供了对应setter/getter方法
封装
isInstanceOf
asInstanceOf
classOf
实例
类有一个主构器和任意数量的辅助构造器,而每个辅助构造器都必须先调用主构造器(也可以是间接调用)
只有主构造器可以调用父类的构造器。辅助构造器不能直接调用父类的构造器。在Scala的构造器中,你不能调用super(params)
子类与父类
当调用对象方法的时候,该方法会和该对象的内存地址绑定
当调用对象属性时,没有动态绑定机制,哪里声明,那里使用
动态绑定机制
类通过abstarct修饰
方法省略掉方法体
abstract class Person() { // 抽象类
var age = 20 // 普通属性
def printName // 抽象方法 没有方法体
def test() = {
println(“test…”)
抽象类不能被实例化
抽象方法不能有主体,不允许使用abstract修饰。
如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法和抽象属性,除非它自己也声明为abstract类。
抽象方法和抽象属性不能使用private、final 来修饰,因为这些关键字都是和重写/实现相违背的。
抽象类中可以有实现的方法.
子类重写抽象方法不需要override,写上也不会错(另一种叫法是实现).
说明
抽象类
通过包含带有定义或重写的代码块的方式创建一个匿名的子类
匿名子类
继承
多态
面向对象三大原则
Java中静态方法并不是通过对象调用的,而是通过类对象调用的,所以静态操作并不是面向对象的。
Scala中静态的概念-伴生对象,模拟类对象,这个类的所有静态内容都可以放置在它的伴生对象中声明和调用
在伴生对象中定义apply方法,可以实现: 类名(参数) 方式来创建对象实例.
apply方法
Scala中伴生对象采用object关键字声明,伴生对象中声明的全是 "静态"内容,可以通过伴生对象名称直接调用。
伴生对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。
伴生对象中的属性和方法都可以通过伴生对象名直接调用访问
从语法角度来讲,所谓的伴生对象其实就是类的静态方法和静态变量的集合
从技术角度来讲,scala还是没有生成静态的内容,只不过是将伴生对象生成了一个新的类,实现属性和方法的调用。
从底层原理看,伴生对象实现静态特性是依赖于 public static final MODULE$ 实现的。
伴生对象的声明应该和伴生类的声明在同一个源码文件中(如果不在同一个文件中会运行错误!),但是如果没有伴生类,也就没有所谓的伴生对象了,所以放在哪里就无所谓了。
总结
静态属性和静态方法
Scala语言中,采用trait(特质,特征)来代替接口的概念
trait 特质名 {
trait体
Trait的声明
如果类存在父类,首先初始化父类
如果类存在特质,那么会首先初始化特质
特质初始化后,才会进行类的初始化
父类初始化完成后,类存在多个特质,从左到右依次初始化
执行特质中的功能时,从右往左
特质只会初始化一次
特质的继承使用
动态混入是Scala特有的方式(java没有动态混入),可在不修改类声明/定义的情况下,扩展类的功能,非常的灵活,耦合性低
在构建对象时混入特质,扩展对象的功能
Trait的动态插入
特质(Trait)
面向对象编程思想
隐式转换函数是以implicit关键字声明的带有单个参数的函数。这种函数将会自动应用,将值从一种类型转换为另一种类型
implicit def f1(d: Double): Int = {11; d.toInt11;}
object Scala01 {
val num1 : Int = 3.5
val num2 : Int = 4.6
println(num1)
println(num2)
基本概念
给MySQL类增加delete方法
思路:给MySQL类增加一个隐式转换,转换为另外的类,并在该类中写明增加的方法
class MySQL{
def insert(): Unit = {
println("insert")
class DB {
def delete(): Unit = {
println("delete")
implicit def addDelete(mysql:MySQL): DB = {
new DB //
val mysql = new MySQL
mysql.insert()
mysql.delete()
隐式转换在丰富功能的应用
作为缺省参数使用
隐式值
扩展功能
作用
其所带的构造参数有且只能有一个
隐式类必须被定义在“类”或“伴生对象”或“包对象”里,即隐式类不能是 顶级的(top-level objects)。
隐式类不能是case class(case class在后续介绍 样例类)
作用域内不能有与之相同名称的标识符
特点
def main(args: Array[String]): Unit = {
//DB1会对应生成隐式类
implicit class DB1(val m: MySQL1) {
def addSuffix(): String = { //方法
m + " scala"
val mysql1 = new MySQL1
mysql1.sayOk()
println(mysql1.addSuffix())
class MySQL1 {
def sayOk(): Unit = {
println("sayOk")
隐式类
隐式转换和隐式参数
不可变集合:scala.collection.immutable
可变集合: scala.collection.mutable
可变不可变
序列Seq
集Set
映射Map
三大类
分类
Scala默认采用不可变集合
基本概述
访问通过_顺序号,顺序号从1开始
productIterator
遍历
元组Tuple
val arr1 = new Array[Int](10)
声明
数组-定长数组(声明泛型)
val arr2 = ArrayBuffer[Int]()
每append一次,arr在底层会重新分配空间,进行扩容,arr2的内存地址会发生变化,也就成为新的ArrayBuffer
数组-变长数组(声明泛型)
arr1.toBuffer //定长数组转可变数组
arr2.toArray //可变数组转定长数组
定长变长数组转换
import scala.collection.JavaConversions.bufferAsJavaList
val javaArr = new ProcessBuilder(arr)
val arrList = javaArr.command()
Scala数组转Java List
import scala.collection.JavaConversions.asScalaBuffer
import scala.collection.mutable
// java.util.List ==> Buffer
val scalaArr: mutable.Buffer[String] = arrList
Java List转Scala数组
Scala数组和Java数组的转换
数组Array
Scala的List可以直接存放数据,就是一个object
val list02 = Nil
空集合的运用
val list2 = list1 :+ 4
在列表的最后增加数据
val list2 = 4 +: list1
在列表的最前面增加数据
val list5 = 4 :: 5 :: 6 :: list1 :: Nil // 从右向左的逻辑
val list7 = 4 :: 5 :: 6 :: list1 ::: Nil
::表示新建集合添加元素,:::表示将集合中每一个元素加入到空集合中
List元素的追加
ListBuffer是可变的list集合,可以添加删除元素,ListBuffer属于序列
ListBuffer
List(属于Seq)
队列是一个有序列表,在底层可以用数组或是链表来实现,遵循FIFO,一般使用可变集合中的数列
val q1 = new mutable.Queue[Int]
创建
队列Queue
Scala中不可变的Map是有序的,可变的Map是无序的
集是不重复元素的结合
不可变集合
import scala.collection.mutable.Set
可变集合
基础集合类型
val strings2 = names.map(_.toUpperCase)
实例应用1
val strings = tuples.map(t => { (t._1 + " ") * t._2})
实例应用2
map映射函数
实例应用
flatmap扁平化映射
Return a new distributed dataset formed by passing each element of the source through a function func.
map
flatmap
定义解释
在使用时map会将一个长度为N的RDD转换为另一个长度为N的RDD;而flatMap会将一个长度为N的RDD转换成一个N个元素的集合,然后再把这N个元素合成到一个单个RDD的结果集。
可以理解为flatmap比map多了一个flatten操作
map与flatmap的区别
val tuples = list.filter(word => {word._1.contains("H")})
filter过滤
println(list.reduceLeft(_ + _))
reduceleft归约
fold函数将上一步返回的值作为函数的第一个参数继续传递参与运算,直到list中的所有元素被遍历。
foldLeft /:
foldRight :\\
fold
对某个集合的所有元素做fold操作,但是会把产生的所有中间结果放置于一个集合中保存
scanLeft
拉链zip
stream是一个集合。这个集合,可以用于存放无穷多个元素,但是这无穷个元素并不会一次性生产出来,而是需要用到多大的区间,就会动态的生产,末尾元素遵循lazy规则(即:要使用结果才进行计算的) 。
流Stream
集合的方法和应用
Scala集合
类似于Java中的switch语法,都不匹配执行case _分支,如果没有case _分支则抛出异常
守卫代码实现
在进行类型匹配时,编译器会预先检测是否有可能的匹配,如果没有则报错.
val result = arr match {
case Array(0) => "0"
case _ => "什么集合都不是"
println("result = " + result)
匹配数组
val result = list match {
case 0 :: Nil => "0" //
case x :: y :: Nil => x + " " + y
case 0 :: tail => "0 ..."
case _ => "something else"
println(result)
匹配列表
val result = pair match {
case _ => "other"
匹配元组
case中对象的unapply方法(对象提取器)返回Some集合则为匹配成功
返回none集合则为匹配失败
条件
namesString match {
println("three people's names")
// 打印字符串
println(s"$first $second $third")
case _ => println("nothing matched")
模式匹配
Scala用[]规定泛型
基础
[T <: A]
上界
[T >: A]
下界
泛型的上下界
<% 的意思是“view bounds”(视界),它比<:适用的范围更广,除了所有的子类型,还允许隐式转换类型。
视图界定
上下文界定
C[+T]:如果A是B的子类,那么C[A]是C[B]的子类,称为协变
协变
C[-T]:如果A是B的子类,那么C[B]是C[A]的子类,称为逆变
逆变
C[T]:无论A和B是什么关系,C[A]和C[B]没有从属关系。称为不变.
不可变
Scala中可通过+ - 号,如tarit List[+T]表协变
泛型
Scala笔记
0 条评论
回复 删除
下一页