0%

Scala学习笔记

本文记录了小编在学习Scala的过程中总结的知识点以及常用语法

Scala介绍


官网通过上述6个方面来对Scala进行介绍,我对Scala这6大特性的理解如下

特性 描述
Seamless Java Interop Java和scala可以混编
Type Inference 支持类型自动推断
Concurrency & Distribution 支持并发和分布式,比如Actors的应用
Traits 特质,相当与Java中的接口和抽象类的结合,因为在特质中方法体可实现也可不实现相当于Java的抽象类,可以多继承相当于Java的接口
pattern matching 模式匹配,相当于Java中的switch
Higher-order functions 支持高阶函数,比如隐函数等

Scala基础

数据类型

Scala支持的所有数据类型如下

数据类型 描述
Byte 8 bit 有符号数字
Short 16 bit 有符号数字
Int 32 bit 有符号数字
Long 64 bit 有符号数字
Float 32 bit 单精度浮点数
Double 64 bit 双精度浮点数
Char 16 bit Unicode字符
Boolean 布尔类型
String 字符串
Unit 无返回值的函数类型,相当于Java的void
Null 空值或者空引用
Nothing 所有类型的子类,表示没有值
Any 所有类型的超类,任何实例都属于Any类型
AnyRef 所有饮用类型的超类
AnyVal 所有值类型的超类

其中容易混淆的“空”类型

数据类型 描述
Null Trait,其唯一的实例就是null,是AnyRef的子类,不是AnyVal的子类
Nothing Trait,所有类型(包括AnyRef和AnyVal)的子类,没有实例
None Option的两个子类之一,另一个子类是Some
Unit 无返回值的函数类型,相当于Java的void
Nil 长度为0的List

各数据类型之间的继承关系为

Note

(1) Nothing 会在类型推断的时候用到,当无法自动推断类型的时候会数据类型写成Nothing,比如

scala> val a = List()
a: List[Nothing] = List()

(2) Scala的数据类型是强类型,所以不同数据类型之间判断是否相等的结果一定是false,比如

scala> 'r' == "r"
^
warning: comparing values of types Char and String using `==` will always yield false
res0: Boolean = false

返回结果为false

代码实现

object Lesson_Base {
def main(args: Array[String]): Unit = {

// var是变量,val是常量
var i = 0
val age = 30

// 1. while/do while 循环
do{
println(s" 第 $i 次求婚。。。")
i += 1
}while(i<10)

while(i < 10){
println(s" 第 $i 次求婚。。。")
i += 1
}

// 2. to/until
// to/until叫操作符操作,因为他们可以写成方法的形式, to是左闭右闭,until是左闭右开
val r: Seq[Int] = 1.to(10, 1)
val r1: Seq[Int] = 1.until(10, 3)

for(i <- 1 until 10) print(i + " ")
for(i <- 1 to 10) print(i + " ")


// 3. for循环
for(i <- 1 until 10){
for(j <- 1 until 10){
if (j <= i){
print(s"$i * $j = " + i*j + "\t")
}
}
println()
}

// 将上述两个for循环体合并
for (i <- 1 until 10; j <- 1 until 10) {
if (j <= i) {
print(s"$i * $j = " + i * j + "\t")
}
if (j == i) {
println()
}
}

// for循环中可以写多个判断语句,中间用空格隔开
for (i <- 1 until 10 if i > 5 if i%2==0) println(i)
for (i <- 1 until 10 if i > 5 & i%2==0) println(i)

// for循环有返回值
val result: Seq[Int] = for (i <- 1 until 100 if i > 10 if i%3==0) yield i

// 4. if/else if/else
if(age <= 20)println("age <= 20")
else if(age > 20 && age<=30) println("age > 20 and age <= 30")
else println("age > 30")

// 5. 字符串
// 字符串前面加s, 表示后面可以直接在字符串里面写变量,引用变量的形式就是$+变量
val str1 = "huangning"
val str2 = "HuangNing"
val str3 = "Tony"

// 寻找字符串中某个元素的index
println(s"第一个符合ascill码的字符串元素n的下标:${str1.indexOf(110)}")
println(s"第一个符合字符串元素n的下标:" + str1.indexOf("n"))
println(s"如果找不到对应的元素会返回:" + str1.indexOf("z"))

// 比较两个字符串的大小
println(s"不忽略大小写比较:${str1 == str2}")
println(s"不忽略大小写比较:${str1.equals(str2)}")
println(s"忽略大小写比较:${str1.equalsIgnoreCase(str2)}")

// 返回值是数字且忽略大小写的字符串比较
println(s"前者等于后者的结果:${str1.compareToIgnoreCase(str2)}")
println(s"前者大于后者的结果:${str3.compareToIgnoreCase(str1)}")
println(s"前者小于后者的结果:${str1.compareToIgnoreCase(str3)}")

// 返回值是数字且不忽略大小写的字符串比较
println(s"前者等于后者的结果:${str1.compareTo(str1)}")
println(s"前者大于后者的结果:${str1.compareTo(str2)}")
println(s"前者小于后者的结果:${str2.compareTo(str1)}")

// 字符串的量种遍历方法
println("第一种遍历方法")
for(i <- str1) print(i + ",")
println("")

println("第二种遍历方法")
str1.foreach((elem: Char) => print(elem + ","))
println("")

str1.foreach(print)
println("")

// 创建可变字符串对象
val str = new StringBuilder
str.append("abc")
str.++=("efg")
str ++= "efg"
str.+=('h')
str += 'h'
str.append(1.0)
str.append("===>")
str.append(18f)
println(s"添加元素后的字符串为:$str")
}
}

Scala类和对象

基础知识

  1. Scala 中的 object 是单例对象,单例对象是一种特殊的类,当它定义于顶层时(即没有包含在其他类中),有且只有一个实例。当对象定义在一个类或方法中时和惰性变量一样是延迟创建的,即当它第一次被使用时创建。相当于 Java 中的工具类,object 中定义的全是静态因此一定都会被加载,这也是 main 方法要写在 object 中的原因。
  2. Scala 中定义变量使用 var常量使用val变量可变常量不可变(定义变量的时候尽量使用val,便于回收)
  3. Scala中每行后面都会有分号自动推断机制,所以不用每行后面写分号;因为有类型自动推断机制,所以变量和常量类型不用写
  4. Scala 中命名建议类名首字母大写方法首字母小,都使用驼峰命名法
  5. Scala 中class可以传参数传参一定要指定类型,有了参数就有了默认的构造方法,object 不可以传参数,如果非要传参数,就会调用相应参数长度的 apply 方法(ps:Trait不可以传参数)。类中的属性默认有gettersetter方法
  6. 类中重载构造方法时,构造方法中第一行必须先调用默认的构造方法,即 def this(){this(…)}
  7. Scala中当new class时,类中方法不执行(除了构造方法),其他的都执行
  8. 在同一个scala文件中,class名称和object名称一样时,这个class叫做这个object的伴生类,这个 object 叫做这个 class 的伴生对象,他们的成员之间可以互相访问私有变量

代码实现

// 如果pname前面写val/var,就相当于公有的,在object外部可以用,而不加val/var就相当于私有的
class Person(pname:String, page: Int){

// 私有成员变量在class外部不能访问
private val name = pname
var age: Int = page
var gender = 'M'

// 构造方法的重载
def this(yname: String, yage: Int, ygender: Char){

// 第一行必须先调用默认的构造方法
this(yname, yage)
this.gender = ygender
}

// 类的私有成员变量在外部只能通过方法来调用
def sayName(): String ={
name
}

def sayObjectName(): String = {
Lesson_ObjectAndClass.name
}

println("======= 当Person类的对象创建的时候方法以外(除了构造方法)的语句都执行 =======")

}

object Lesson_ObjectAndClass {

val name = "wangwu"
println("++++ object中的所有语句都会被加载 +++++++")

def apply(i:Int): Unit ={
println("score is " + i)
}

def apply(i:Int, j: Int): Unit ={
println("score is " + i + " the height is " + j)
}

def main(args: Array[String]): Unit = {

val p = new Person("zhangsan", 20)
val p1 = new Person("diaochan", 18, 'F')
println("Person的第一位成员姓名:" + p.sayName() + " 性别:" + p.gender + " 年龄:" + p.age)
println("Person的第二位成员姓名:" + p1.sayName() + "性别:" + p1.gender+ " 年龄:" + p.age )

// 修改类中成员变量的值
p.age = 2
println(p.sayName() + "的年龄被修改成" + p.age)

// 直接调用内部成员变量
println(name)
// Person对象调用object Lesson_Base 中成员变量
println("Lesson_Base 中成员变量的name:" + p.sayObjectName())

// object 不可以传参数,如果非要传参数,就会调用相应参数长度的apply方法
Lesson_ObjectAndClass(100)
Lesson_ObjectAndClass(1000, 200)
}
}

Scala方法与函数

object Lesson_Method {

/**
* 1. 方法定义
* (1) def 来定义方法
* (2) 定义方法传入的参数一定要指定类型
* (3) 如果定义方法时,省略了方法名称和方法体之间的"=",那么无论方法体最后一行计算的结果是什么,都会被丢弃,返回Unit
* (4) 方法的方法体可以如果可以一行搞定,那么方法体的花括号可以省略
* (5) 方法体中最后返回值可以使用return,那么方法体的返回值类型一定要指定
* (6) 方法体中没有return,默认将方法体中最后一行的计算结果当作返回值,那么方法体的返回值类型可以省略,会自动推断返回值的类型
*/
def max(a: Int, b: Int): Int = {
if(a > b){
a
}else{
b
}
}

def max2(a: Int, b: Int): Int = if(a > b) a else b

/**
* 2.递归方法
* (1) 递归方法一定要指定返回值类型
* (2) 递归方法的前面有小圆圈
*/
def recursionFun(num: Int): Int = {
if(num == 1){
1
}else{
num * recursionFun(num-1)
}
}

/**
* 3.参数有默认值的方法
* (1) 当传递参数个数小于指定参数列表个数,那么一定要指定传递的是哪个参数,不然实参就会一个一个传递给形参
*/
def defaultValue(a:Int = 10 , b:Int): Int = {
a + b
}

/**
* 4.可变长参数的方法
*/
def variableLength(s:String*): Unit = {
s.foreach((elem: String) => {
print(" => " + elem)
})
println()
}

/**
* 5. 匿名函数
* (1) "=>"就是匿名函数,
* (2) 当方法的参数是函数时,经常用匿名函数
* (3) 可以将匿名函数赋值给变量,方便在其他地方调用, 语法: def/val/var 变量名:(函数的输入类型) => 函数的输出类型
* (4) 可以将方法赋值给变量,变量类型是函数, 但是一定要手动指定函数类型
*/
def lambadFun: (Int, Int) => Int = (a:Int, b:Int) => {
a+b
}
def lambadMethod(a: Int): Unit = {
println(a)
}
val lambdaTest: Int => Unit = lambadMethod

/**
* 6. 嵌套方法
*/
def nestingFun(num:Int): Int = {
def fun(a:Int): Int = if(a==1) 1 else a * fun(a-1)
fun(num)
}

/**
* 7.偏应用函数
* (1) 当方法中参数非常多,调用这个方法非常频繁并且每次调用只有固定的某个参数变化,其他都不变,可以使用偏应用来实现
*/
def showLog(date:Date, log:String): Unit = {
println(s"时间是 $date , 日志是 $log ")
}
val date = new Date()
def partialAppFun: String => Unit = showLog(date, _:String)

/**
* 8. 高阶函数
* (1) 方法的参数是函数
* (2)方法的返回是函数, <要显式地将返回值写成相应函数类型,才能将相应的方法转成函数并作为返回值, 不然就会报错
* 因为编译器看到方法名不知道接下是返回相应的函数,还是调用该方法。如果加 _ 强制将方法转成函数并作为返回值, 也就是可以不显示的声明方法的返回值类型>
* (3)方法的参数和返回都是函数
*/
def paramFun(f:(Int, Int) => Int, s:String): String = {
val i: Int = f(100, 200)
i + " # " + s
}

def returnFun1(s: String):(String, String) => String = {

def fun(s1:String, s2:String):String={
s1 + "~" + s2 + "#" + s
}
fun
}

def returnFun2(s: String) = {

def fun(s1:String, s2:String):String={
s1 + "~" + s2 + "#" + s
}
fun _
}

def paramAndReturnFun(f:(Int, Int)=>Int):(String, String)=>String = {
val i: Int = f(10, 90)

def fun(s1:String, s2:String): String = {
s1 + "@" + s2 + "*" + i
}
fun
}

/**
* 9. 柯里化函数:就是高阶函数第二种方式的简化写法,
*/
def curryingFun(a: Int, b:Int)(c:Int, d:Int): Int = {
a + b + c + d
}

def main(args: Array[String]): Unit = {
val result10: Int = max(100, 20)
val result11: Int = max2(100, 20)
val result2: Int = recursionFun(5)
val result3: Int = defaultValue(b = 2)
val result5: Int = lambadFun(1, 3)
val result6: Int = nestingFun(5)
val result80: String = paramFun((a, b) => {a * b}, "scala")
val result81: String = returnFun1("a")("b", "c")
val result82: String = returnFun1("a")("b", "c")
val result83: String = paramAndReturnFun((a, b)=>{a + b})("huang","ning")
val result9 = curryingFun(1,2)(3,4)

println("1. max方法的结果:" + result10)
println("1. max方法写成1行的结果:" + result11)
println("2. 递归方法的结果:" + result2)
println("3. 参数有默认值的方法的结果:" + result3)
println("4. 可变长参数方法的结果:")
variableLength("www", "d", "dd", "a")
println("5. 匿名函数的结果:" + result5)
println("6. 嵌套方法的结果:" + result6)
println("7. 偏应用函数的结果")
partialAppFun("aaa")
partialAppFun("bbb")
partialAppFun("ccc")
println("8. 高阶函数--方法的参数是函数的结果:" + result80)
println("8. 高阶函数--方法的返回值是函数的结果:" + result81)
println("8. 高阶函数--方法的返回值是函数的结果:" + result82)
println("8. 高阶函数--方法和返回值都是函数的结果:" + result83)
println("9. 柯里化函数的结果:" + result9)
}
}

容器

Array

import scala.collection.mutable.ArrayBuffer

object ArrayLesson {
def main(args: Array[String]): Unit = {

// 创建数组(Array类)
val arrInt = new Array[Int](3) // 整型数组默认都是0
val arrFloat = new Array[Float](3) // Float数组默认都是0.0
val arrString = new Array[String](3) // String数组默认都是null
val arrBoolean = new Array[Boolean](3) // Boolean数组默认都是false

// 创建数组(Array的Object)
val arrIntObj: Array[Int] = Array[Int](1,2,3,4,5)
val arrStringObj: Array[String] = Array[String]("a", "b", "c", "d")

// 数组的赋值/修改
arrInt(0) = 100
arrInt(1) = 200
arrInt(2) = 300

// 两种遍历方式
arrInt.foreach(println)
for(elem <- arrInt) println(elem)

// 创建可变长数组
val arr1: ArrayBuffer[Int] = ArrayBuffer[Int](1, 2, 3)
arr1.+=(4,3) // 向后追加,可添加多个元素
arr1.+=:(100) // 向前追加
arr1.append(7, 8, 9) // 向后追加,可添加多个元素

// Array的fill:创建长度指定且重复某一元素的数组(和柯里化类似)
val strFill: Array[String] = Array.fill(5)("hello")

// Array的concat:将两个数组合并
val constr: Array[String] = Array.concat(strFill, arrStringObj)
constr.foreach(println)

// 创建二维数组
val array = new Array[Array[Int]](3)
array(0) = Array[Int](1,2,3,4)
array(1) = Array[Int](4,5,6)
array(2) = Array[Int](7,8,9)

// 二维数组的两种遍历方法
for(arr <- array ; elem <- arr) print(elem)
println()
array.foreach(arr => {
arr.foreach(print)
})
}
}

List

import scala.collection.mutable.ListBuffer

object ListLesson {
def main(args: Array[String]): Unit = {

// 创建List, Nil是长度为0的list
val l1: List[Int] = List[Int](1, 2, 3, 4, 5)
val l2: List[String] = List[String]("hello scala", "hello spark", "hello java", "hn", "tony")

// 两种遍历方式
for (elem <- l1) println(elem)
l1.foreach(println)

/**
* List自带的方法:
* 1. filter: 将返回是true的元素保留下来
* 2. count: 计算符合条件的元素个数
* 3. map: 一一映射
* 4. flatMap: 先map,再flat
*/
val res1: List[String] = l2.filter(s => {
"hello scala".equals(s)
})

val res2: Int = l2.count(s => {
s.length >= 4
})

val res3: List[Array[String]] = l2.map(elem => {
elem.split(" ")
})

val res4: List[String] = l2.flatMap(elem => {
elem.split(" ")
})

// 创建可变长的List
val list: ListBuffer[Int] = ListBuffer[Int](1,2,3,4)
list.append(4,5,6) // 后向追加多个元素
list.+=(1000, 2000) // 后向追加多个元素
list.+=:(100) // 前向追加多个元素

}

Set

import scala.collection.mutable

/**
* Set的特点是无序,互异
*/
object SetLesson {
def main(args: Array[String]): Unit = {

// 创建Set(Set会自动去重)
val set1: Set[Int] = Set[Int](1, 2, 3, 4, 1)
val set2: Set[Int] = Set[Int](3, 4, 5, 6, 7)


// 两种遍历方式
set1.foreach(print)
println()
for(elem <- set2) print( elem + ",")
println()

/**
* Set的方法:
* 0. 并集: ++/|/union
* 1. 交集:intersect/&/
* 2. 差集: diff/&~/--
* 3. 子集: subsetOf, 判断当前集合是否是传入参数的子集
* 4. 最大: max
* 5. 最小: min
* 6. 转成List: toList
* 7. 转成Array: toArray
* 8. 转成字符串: mkString(“~”)
* 9. filter
*/
val res00: Set[Int] = set1.union(set2)
val res01: Set[Int] = set1 ++ set2
val res02: Set[Int] = set1 | set2
val res10: Set[Int] = set1.intersect(set2)
val res11: Set[Int] = set1 & set2
val res20: Set[Int] = set1.diff(set2)
val res21: Set[Int] = set1 &~ set2
val res22: Set[Int] = set1 -- set2
val res3: Boolean = set1.subsetOf(set2)
val res4: Int = set1.max
val res5: Int = set1.min
val res6: List[Int] = set1.toList
val res7: Array[Int] = set1.toArray
val res8: String = set1.mkString("~")
val res9: Set[Int] = set1.filter(elem => {
elem > 2
})

// 创建可变长Set
val muSet: mutable.Set[Int] = mutable.Set[Int](10, 20, 30)
muSet.+=(100) // 追加一个元素
muSet.+=(100,200,300) // 追加多个元素
muSet.foreach(println)

}
}

Map

import scala.collection.mutable

/**
* 1. 元组最多支持22各元素,超过22个元素的就不叫Tuple,而是Nothing类型并且报错
* 2. 元组可new,也可以不new,甚至可以直接写()
* 3. 与列表类似,唯一不同的是元组可以包含不同类型的元素。
* 4. 元组的值是通过将单个的值包含在圆括号中构成的
* 5. tuple本身不是iterator,所以遍历的时候应该先通过productIterator获取它的iterator
*/
object MapLesson {
def main(args: Array[String]): Unit = {

// 创建Map
val map: Map[String, Int] = Map[String, Int]("a"->100, "b"->200, ("c",300), ("c",400))
val map1: Map[String, Int] = Map[String, Int](("a", 1), ("b", 2), ("c",3), ("d", 4))
val map2: Map[String, Int] = Map[String, Int](("a", 100), ("b", 2), ("c",300), ("e",500))

// 两种遍历方法
map.foreach(println)
for(elem <- map) println(elem)

/**
* Map的方法
* 1. .++的合并方法是后者覆盖前者
* 2. .++:的合并方法是前者覆盖后者
* 3. filter: 过滤出来满足条件的(k,v)
* 4. .values/.keys: 获取Map中的values/keys
* 5. .get("e"): 获取键为"e"对应的Some类型的值, 如果没有键则返回None
* 6. .getOrElse("e", 10000): 获取键为"e"对应的值, 如果没有返回10000
* 7. count: 统计符合条件的记录数
* 8. contains: 判断map中是否包含某个key
* 9. exists: 判断符合条件的记录是否存在
*/
val combMap1: Map[String, Int] = map1.++(map2)
val combMap2: Map[String, Int] = map1.++:(map2)
val filterMap: Map[String, Int] = map.filter(elem => {
val key: String = elem._1
val value: Int = elem._2
value >= 200
})
val valuesMap: Iterable[Int] = map.values
val keysMap: Iterable[String] = map.keys
val getMap1: Option[Int] = map.get("e")
val getMap2: Int = map.getOrElse("e", 10000)
val count: Int = map.count(elem => {
elem._2 >= 200
})
val containsKey: Boolean = map.contains("b")
val exists: Boolean = map.exists(elem => {
elem._2 >= 200
})

//创建可变长的map
val muMap: mutable.Map[String, Int] = mutable.Map[String, Int]()
muMap.put("a", 100)
muMap.put("b", 200)
muMap.put("c", 300)
}
}

Tuple

object TupleLesson {
def main(args: Array[String]): Unit = {

// 创建Tuple
val tuple1: Tuple1[String] = new Tuple1("hello")
val tuple2: (String, Int) = new Tuple2("a", 100)
val tuple3: (Int, Boolean, Char) = new Tuple3(1, true, 'c')
val tuple4: (Int, Double, String, Boolean) = Tuple4(1, 3.4, "abc", false)
val tuple6: (Int, Int, Int, Int, Int, String) = (1, 2, 3, 4, 5, "abd")
val tuple22: (Int, Int, Int, Int, String, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) = new Tuple22(1, 2, 3, 4, "ss", 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2)

// 获取Tuple中的值
val value1: String = tuple22._5

// 元组遍历的三种方法(iterator就相当于指向某个位置的指针)
val iterator: Iterator[Any] = tuple2.productIterator
while(iterator.hasNext){
println(iterator.next())
}
for(item <- iterator){
println(item)
}
iterator.foreach(println)

// 2元数组的翻转方法
println(tuple2.swap)

// toString方法
println(tuple3.toString())
}
}

继承关系

并行计算

object ParallelLesson {
def main(args: Array[String]): Unit = {
val list1: Seq[Int] = (1 to 10).toList
val list2: Seq[Int] = (11 to 20).toList
println(list1)

/**
* 没有开启并行化时,我们会看到执行map任务时都在主线程执行
*/
list1.foreach(e => {
val name: String = Thread.currentThread().getName
println(s"当前线程名称: $name ,列表对应的值: $e")
println(s"当前元素: $e 乘2后的结果: ${e*2}")
})

/**
* Scala结合Hadoop中的MapReduce思想,可以模拟分布式计算,即使用par方法,其中
* par方法将list对象转成并行化集合,我们会看到执行map任务时开启了多个线程执行
* 缺点是执行顺序不一致啦!!
*/

list2.par.foreach(e => {
val name: String = Thread.currentThread().getName
println(s"当前线程名称: $name ,列表对应的值: $e")
println(s"当前元素: $e 乘2后的结果: ${e*2}")
})
}
}

Trait

/**
* 1. trait不能传参
* 2. trait就是java中抽象类和借口的结合体,因为trait中可以有方法体的实现也可以有不实现的相当于java的抽象类,可以多继承相当于java的接口
* 3. 一个类继承多个trait的时候,第一个关键字使用extends,之后使用with (ps:trait之间可以进行多继承);
* 类继承的多个trait中有同名的方法和属性的时候, 必须要在该类中通过"override"重写同名的方法
* 4. trait中可以有方法体的实现,也可以有方法体的不实现;
* 类继承trait,就要实现trait中没有实现的方法
*/
trait Read{

val readType = "Read"
val state = "active"

def read(): Unit = {
println(s"Read is starting ...")
}

def description(): Unit = {
println(s"该用户的状态:$state, 行为描述:$readType")
}
}

trait Listen{

val listenType = "Listen"
val state = "active"

def listen(): Unit = {
println(s"Listen is starting ...")
}

def description(): Unit = {
println(s"该用户的状态:$state, 行为描述:$listenType")
}
}

// 判断两个学生是否相同
trait IsEqual{

// 未实现的方法
def isEqu(o:Any):Boolean

// 已实现的方法
def isNotEqu(o:Any):Boolean = !isEqu(o)
}

class Student(sname: String, sage: Int) extends Read with Listen with IsEqual{

val name: String = sname
private val age: Int = sage

var gender: String = "F"
var id: String = "0000"

def this(sname: String, sage: Int, sid: String){
this(sname, sage)
this.id = sid
}

def this(sname: String, sgender: String, sage: Int){
this(sname, sage)
this.gender = sgender
}

def this(sname: String, sage: Int, sid: String, sgender: String){
this(sname, sage)
this.gender = sgender
this.id = sid
}

// 年龄是私有成员变量,在外部只能通过方法进行调用
def sayAge(): Int = {
age
}

// override继承的特质中有重复的属性gender
override val state: String = "active"

// override继承的特质中重复的方法description
override def description(): Unit = {
println(s"${name}的状态:$state, 存在的行为:$listenType, $readType")
}

// 需要去实现在trait中没有实现的方法isEqu
def isEqu(o:Any):Boolean = {
o.isInstanceOf[Student] &&
id == o.asInstanceOf[Student].id &&
name == o.asInstanceOf[Student].name &&
gender == o.asInstanceOf[Student].gender
}

def isEquAge(o:Any):Boolean = {
o.isInstanceOf[Student] &&
age == o.asInstanceOf[Student].age
}
}

object Lesson_Trait {
def main(args: Array[String]): Unit = {
val h = new Student("huangning", 25)
val n = new Student("Tony", 25, "0011", "F")
println("Student的第一位成员姓名:" + h.name + " 性别:" + h.gender + " 年龄:" + h.sayAge() + " id:" + h.id)
println("Student的第二位成员姓名:" + n.name + " 性别:" + n.gender + " 年龄:" + n.sayAge() + " id:" + n.id)

h.read()
h.listen()
h.description()

val res: Boolean = h.isEqu(n)
println(s"${h.name}${n.name}相同么:$res")
println(s"${h.name}${n.name}的年龄相同么:${h.isEquAge(n)}")
}
}

Match

/**
* 1. match不仅可以匹配值还可以匹配类型
* 2. 从上往下匹配,匹配上之后自动终止
* 3. 模式匹配外部的{...}可以省略掉
* 4. case _ 可以匹配任何情况(相当于java的default),一般用在都匹配不上的时候,一定放在最后!!!
* 5. 匹配过程中会有数值的转换,比如1.0转换成1来和相应项进行匹配
*/
object Lesson_Match {

def main(args: Array[String]): Unit = {
val tp: (Int, Char, Double, String, Char, Boolean, Null) = (1, 'd', 2.0, "abc", 'c', true, null)
val iterator: Iterator[Any] = tp.productIterator
iterator.foreach(MatchTest)

val v = 1.0
val str: String = v match {
case i1 if v >= 2 && v <= 5 => s"$i1 的范围在 2~5"
case i2 if 3 to 10 contains i2 => s"$i2 的范围在 3~10"
case 1 => "value is 1"
case _ => "default"
}
println(str)

}

// 判断元祖中的每个数据类型
def MatchTest(o:Any): Unit =
o match {
case i: Int =>println(s"type is Int, value = $i")
case 1 => println("value is 1")
case d: Double => println(s"type is Double, value = $d")
case s: String => println(s"type is String, value = $s")
case 'd' => println(s"value is d")
case c: Char => println(s"type is Char, value = $c")
case b: Boolean => println(s"type is Boolean, value = $b")
case _ => println("no match...")
}
}

PartialFunction

如果一个函数中没有match只有case,可以其定义成PartialFunction(偏函数)。偏函数定义时,不能使用括号传参

/**
* 1. 偏函数中case后面的值类型必须和传入的类型一致
* 2. 偏函数只能匹配一个值,匹配上了返回某个值
* 3. PartialFunction[A, B] A是匹配的类型,B是匹配上返回的类型
*/
object Lesson_PartitionFun {

// 定义偏函数
def MyTest : PartialFunction[String, Int] = {
case "abc" => 2
case "a" => 1
case _ => 200
}

def main(args: Array[String]): Unit = {
val result: Int = MyTest("abc")
println(result)
}
}

样例类

用关键字case修饰的类叫样例类,它的构造参数默认被声明为val,并且实现了类构造参数的getter方法(因为是val类型参数,所以不能实现setter方法),当构造参数声明为var类型的时候,它将帮你实现settergetter方法

case class Person1(var name:String, age:Int)

class Person2(var name:String, val age:Int)

object Lesson_CaseClass {
def main(args: Array[String]): Unit = {
val p1: Person1 = Person1("zhangsan", 18)
val p2: Person1 = Person1("lisi", 23)
val p3: Person1 = Person1("wangwu", 17)
println(s"样例类中的构造参数默认是val,通过构造参数实现的setter方法获取age:${p1.age}")

val list: List[Person1] = List[Person1](p1, p2, p3)
println("======================================")
println("将样例类中Person1的指定成员挑选出来")

// foreach中的match用到了equal,而样例类重写了equals(即比较内容而不是地址)
list.foreach { elem => {
elem match {
case Person1("zhangsan", 18) => println("找到了zhangsan的信息")
case Person1("lisi", 23) => println("找到了lisi的信息")
case _ => println("no mathch...")
}
}
}

println("============上述match的简化版本===========")
list.foreach {
case Person1("zhangsan", 18) => println("找到了zhangsan的信息")
case Person1("lisi", 23) => println("找到了lisi的信息")
case _ => println("no mathch...")
}
println("========================================")

val p = new Person2("zhangsan", 18)
println("普通类中调用构造参数的方式只能是将构造参数显式的写成val/var")
println("使用构造参数的getter方法获取name:" + p.name)
p.name = "zhangsanModify"
println("使用var构造参数的setter方法修改name:" + p.name)

println("重写了equals方法的样例类:" + p1.equals(Person1("zhangsan", 18)))
println("未重写equals方法的普通类:" + p.equals(new Person2("zhangsan", 18)))

println("重写了toString方法的样例类:" + p1.toString)
println("未重写toString方法的普通类:" + p.toString)

}
}

隐式转换

隐式转换是在Scala编译器进行类型匹配的时候发生。如果找不到合适的类型,那么隐式转换会让编译器在作用范围内自动推导出合适的类型

隐式值&隐式参数

  • 隐式值:定义参数的时候,用implicit修饰的变量
  • 隐式参数:方法中用implicit修饰的参数(必须使用柯里化的方式将隐式参数写在方法后面的括号中)

  • 隐式转换作用:当调用方法的时候,不必手动传入方法中的隐式参数,Scala会自动在作用域内寻找隐式值自动传入

/**
* 1. 同类型参数的隐式值在作用域内只能出现一次
* 2. implicit关键字必须放在隐式参数定义的开头
* 3. 如果方法只有一个参数且是隐式转换参数时,那么可以通过只写函数名的方式调用该函数
* 4. 如果方法有多个参数,要实现部分参数的隐式转换必须使用柯里化这种方式, 隐式关键字出现在后面且出现一次
*/
object Lesson_ImplicitTrans {

def Student(age:Int)(implicit name:String, weight:Int): Unit = {
println(s"$name is a student... , age = $age, weight = $weight")
}

def Teacher(implicit name:String): Unit ={
println(s"$name is a teacher")
}

def main(args: Array[String]): Unit = {
implicit val n: String = "huangning"
implicit val w: Int = 25

Student(12)
Teacher
}
}

隐式转换函数

隐式转换函数是使用关键字implicit修饰的方法。假如某个A类型变量调用了方法M,但是发现该变量没有方法M,而B类型变量有方法M,那么Scala会在作用域中寻找将A类型转换成B类型的隐式转换函数,如果有这样的隐式转换函数,那么A类型变量就可以调用方法M

/**
* 隐式转换函数只与函数的参数类型和返回类型有关,与函数名称无关,
* 所以作用域内不能有相同的参数类型和返回类型的不同名称隐式转换函数
*/
class Animal(name:String){
def fly(): Unit ={

// 直接调用主构造方法中的name参数
println(s"$name can fly ....")
}
}

class Rabbit(xname:String){
val name: String = xname
}

object Lesson_implicitTrans2 {

implicit def rabbitToAnimal(r:Rabbit):Animal={
new Animal(r.name)
}

def main(args: Array[String]): Unit = {
val rabbit = new Rabbit("rabbit")
rabbit.fly()
}
}

隐式类

使用implicit关键字修饰的类就是隐式类。如果某类型变量A想要调用一些不存在的方法或者变量的时候,可以定义隐式类,然后在隐式类中定义这些方法或者变量,并且在隐式类中传入变量A即可

/**
* 1. implicit类不能定义在外面,只能定义在object和class内部
* 2. 隐式类的构造必须只有一个参数,
* 3. 同一个object或者class中不能出现同类型构造的隐式类。
*/

class Dog(xname:String){
val name: String = xname
}

object Lesson_ImplicitTrans3 {

// 在object中创建隐式类
implicit class Animal1(r:Dog){
def showname(): Unit = {
println(s"${r.name} is ")
}
}

def main(args: Array[String]): Unit = {
val dog = new Dog("puppy")
dog.showname()
}
}

ActorModel

ActorModel是用来编写并行计算分布式系统的,类似于Java的Thread,Actors将状态和行为封装在进程中,不与其他Actors分享状态,当需要和其他Actors交互时通过发送消息,每个Actors都有自己的消息队列

/**
* 1. Actor模型的基本特征就是消息传递
* 2. 消息一旦发送成功,不能修改
* 3. 消息发送是异步非阻塞的
*/
class SingleActor extends Actor {
def receive: Receive = {
case "hello" => println("您好!")
case _ => println("您是?")
}
}

object SingleActor{
def main(args: Array[String]): Unit = {
// 创建通信器并发送消息
val actor: ActorRef = ActorSystem("test").actorOf(Props[SingleActor], name = "helloActor")
actor ! "hello"
actor ! "good morning"
}
}

实现Actor之间的信息传递

object MultiActor {
def main(args: Array[String]): Unit = {

//创建ActorSystem对象
val acf: ActorSystem = ActorSystem("actorfactory")
//先创建JohnActor的引用
val john: ActorRef = acf.actorOf(Props[JohnActor], "JohnActor")
//再创建TonyActor的引用
val tony: ActorRef = acf.actorOf(Props(new TonyActor(john)), "TonyActor")
// 先向TonyActor发送第一个消息
tony ! "Hey Tony"
}
}

class JohnActor extends Actor{
override def receive:Receive = {
case "Hello John" =>
Thread.sleep(100)
val msg = "见到你很高兴"
println(s"John: $msg")
sender() ! msg

case "打篮球" =>
Thread.sleep(100)
val msg = "哇,打篮球可以啊"
println(s"John: $msg")
sender() ! msg

case _ =>
println("什么消息都没有收到")
}
}

class TonyActor(actorRef: ActorRef) extends Actor {

// 首先发送消息的actor需要拿到第二个actor的引用
val johnRef: ActorRef = actorRef

override def receive: Receive = {

case "Hey Tony" =>
println("Tony: 哇! 你竟然知道我的名字")

//发给自己
self ! "我心里好紧张"

case "我心里好紧张" =>
johnRef ! "Hello John"

case "见到你很高兴" =>
println("Tony: 我们下午要不要做点有趣的事情?")
Thread.sleep(1000)
//给JohnActor发出消息
println("请作者提出有趣的事情:")
val msg: String = StdIn.readLine()
println(s"Tony: $msg")
johnRef ! msg

case _ =>
println("对话结束... ...")
}
}

补充

  • 通过打电话的例子说明阻塞,非阻塞,同步以及异步的区别

    1. 阻塞/同步:打一个电话一直到有人接为止
    2. 非阻塞:打一个电话没人接,每隔5分钟再打一次,直到有人接为止
    3. 异步:打一个电话没人接,转到语音邮箱留言,然后等待对方回电
  • Akka 是一个用 Scala 编写的库,是Actor Model的应用,底层实现就是Actor。spark1.6之前,spark分布式节点之间使用Akka传递消息,spark1.6之后使用netty传递消息

文件IO

object FileIOLesson {
def main(args: Array[String]): Unit = {
/**
* 文件写入
*/
val path: String = "/Users/huangning/HNCode/neo_spark/data/envelope.txt"
val printWriter_name: PrintWriter = new PrintWriter(new File(path))
printWriter_name.write("I love you")
printWriter_name.close()

/**
* 文件读取
*/
val source: BufferedSource = Source.fromFile("/Users/huangning/HNCode/neo_spark/data/envelope.txt")
for(line<-source.getLines())
println(s"当前行字符长度:${line.length}, 具体内容:$line")
}
}