scala 笔记
如果操作列表的话, 请使用 ListBuffer 如:
val lstBuf = mutable.ListBuffer[String]()
lstBuf += "111"
lstBuf += "22"
lstBuf += ("abab", "ddd", "ddd")
lstBuf.toList.foreach(println(_))
常用表达式
if表达式
val isAdult = if (age > 18) 1 else 0
循环
while(true) {}
for(i <- 1 to n) {} //[1,n]
for(i <- 1 until n) {} //[1,n)
for(i <- (1 until n).reverse) //反向遍历
for(c <- "Hello World") print(c)
for(i <- 1 to 9; j <- 1 to 9) {}
for(i <- 1 to 100 if i % 2 == 0) println(i) //if守卫
for(i <- 1 to 3) yield i //推导式:构造集合Vector(1, 2, 3)
异常,与java类似
try {
throw new IllegalArgumentException("")
} catch {
case exception: IllegalArgumentException => println(exception)
} finally {
}
·apply函数
·Scala中的apply函数是非常特殊的一种函数,在Scala的object中,可以声明apply函数。而使用“类名()”的形式,其实就是“类名.apply()”的一种缩写。通常使用这种方式来构造类的对象,而不是使用“new 类名()”的方式。
·例如,"Hello World"(6),因为在StringOps类中有def apply(n: Int): Char的函数定义,所以"Hello World"(6),实际上是"Hello World".apply(6)的缩写。
·例如,Array(1, 2, 3, 4),实际上是用Array object的apply()函数来创建Array类的实例,也就是一个数组。
breakable
import scala.util.control.Breaks._
breakable({
for (i <- 0 until(10)){
if(i==7) break()
println(i)
}
})
any 类型 () AnyVal=()
====
val ages = Map("Leo" -> 30, "Jen" -> 25, "Jack" -> 23)
ages("Leo") = 31 错误: map 是不可变的,里面的值也无法修改
Map 元素的获取, 要使用getOrElse, 直接获取不到时会报异常
ages.getOrElse("leo","default")
// 增加多个元素
ages += ("Mike" -> 35, "Tom" -> 40)
// 移除元素
ages -= "Mike"
// 更新不可变的map
val ages2 = ages + ("Mike" -> 36, "Tom" -> 40)
// 移除不可变map的元素
val ages3 = ages - "Tom"
SortedMap, LinkedHashMap
// SortedMap可以自动对Map的key的排序
val ages = scala.collection.immutable.SortedMap("leo" -> 30, "alice" -> 15, "jen" -> 25)
// LinkedHashMap可以记住插入entry的顺序
val ages = new scala.collection.mutable.LinkedHashMap[String, Int]
ages("leo") = 30
Scala的getter和setter方法的命名与java是不同的,是field和field_=的方式
========
传入可变变量
val s = sum(1 to 5: _*)
在Scala中,提供了lazy值的特性,也就是说,如果将一个变量声明为lazy,则只有在第一次使用该变量时,变量对应的表达式才会发生计算。这种特性对于特别耗时的计算操作特别有用,比如打开文件进行IO,进行网络IO等。
import scala.io.Source._
lazy val lines = fromFile("C://Users//Administrator//Desktop//test.txt").mkString
即使文件不存在,也不会报错,只有第一个使用变量时会报错,证明了表达式计算的lazy特性。
函数没有返回值的成为过程,如:
def fnTest(name: String) {
print(name)
}
====
object通常用于作为单例模式的实现,或者放class的静态成员,比如工具方法
==伴生类, 伴生对象
object通常用于作为单例模式的实现,或者放class的静态成员,比如工具方法
object 对象也可以继承抽象方法
// 伴生类和伴生对象必须存放在一个.scala文件之中
// 伴生类和伴生对象,最大的特点就在于,互相可以访问private field
==函数传参
- 传值调用(call-by-value):先计算参数表达式的值,再应用到函数内部;
- 传名调用(call-by-name):将未计算的参数表达式直接应用到函数内部
在进入函数内部前,传值调用方式就已经将参数表达式的值计算完毕,而传名调用是在函数内部进行参数表达式的值计算的。
这就造成了一种现象,每次使用传名调用时,解释器都会计算一次表达式的值。
参数形式: def delayed( t: => Long ) = {}
=>之前为空 ,不指定类型
========类内部定义的是方法
求为field选择合适的修饰符就好:var、val、private、private[this]
====使用类似 java getter, setter
class Person2(@BeanProperty var name:String){}
val p= new Person2("nihao")
p.getName
p.setName("22")
==内部类:
但是与java不同的是,每个外部类的对象的内部类,都是不同的类
demo:
class Class {
val students = new ArrayBuffer[Student]();
class Student(@BeanProperty val name: String) {
}
def newStudent(name: String): Student = {
new Student(name)
}
}
val class1 = new Class;
val class2 = new Class;
val s1 = class1.newStudent("name1")
class1.students += s1;
val s2 = class2.newStudent("name2")
==========isInstanceOf, asInstanceOf , getClass, classOf
需要使用isInstanceOf判断对象是否是指定类的对象,如果是的话,则可以使用asInstanceOf将对象转换为指定类型
isInstanceOf只能判断出对象是否是指定类以及其子类的对象,而不能精确判断出,对象就是指定类的对象
// 如果要求精确地判断对象就是指定类的对象,那么就只能使用getClass和classOf了
==继承
跟java一样,scala中同样可以使用protected关键字来修饰field和method,这样在子类中就不需要super关键字,直接就可以访问field和method
// 还可以使用protected[this],则只能在当前子类对象中访问父类的field和method,无法通过其他子类对象访问父类的field和method
--调用父类的 constructor
class Person(val name: String, val age: Int)
class student(name: String, age: Int, val score: Int) extends Person(name, age) {
def this(name: String) {
this(name, 0, 0)
}
def this(score: Int) {
this("abc", 0, score)
}
}
// Scala中,每个类可以有一个主constr uctor和任意多个辅助constructor,而每个辅助constructor的第一行都必须是调用其他辅助constructor或者是主constructor;因此子类的辅助constructor是一定不可能直接调用父类的constructor的
// 只能在子类的主constructor中调用父类的constructor,以下这种语法,就是通过子类的主构造函数来调用父类的构造函数
// 注意!如果是父类中接收的参数,比如name和age,子类中接收时,就不要用任何val或var来修饰了,否则会认为是子类要覆盖父类的field
==匿名内部类
匿名子类,也就是说,可以定义一个类的没有名称的子类,并直接创建其对象,然后将对象的引用赋予一个变量。之后甚至可以将该匿名子类的对象传递给其他函数。
class Person(protected val name: String) {
def sayHello = "Hello, I'm " + name
}
val p = new Person("leo") {
override def sayHello = "Hi, I'm " + name
}
def greeting(p: Person { def sayHello: String }) {
println(p.sayHello)
}
====trait 特征
使用trait(特征)相当于java中interface+abstract class
scala中可以将trait混入到对象中,就是将trait中定义的方法、字段添加到一个对象中,
作用: 给一个对象添加一些额外的行为, 例子:
trait Logger {
def log(msg:String) = println(msg)
}
class UserService
def main(args: Array[String]): Unit = {
val service = new UserService with Logger
service.log("混入的方法")
}
trait 责任链(调用链)
trait HandlerTrait {
def handle(data: String) = println("处理数据...")
}
trait DataValidHanlderTrait extends HandlerTrait {
override def handle(data: String): Unit = {
println("验证数据...")
super.handle(data)
}
}
trait SignatureValidHandlerTrait extends HandlerTrait {
override def handle(data: String): Unit = {
println("校验签名...")
super.handle(data)
}
}
class PayService extends DataValidHanlderTrait with SignatureValidHandlerTrait {
override def handle(data: String): Unit = {
println("准备支付...")
super.handle(data)
}
}
val service = new PayService
service.handle("支付参数")
结果:
准备支付...
校验签名...
验证数据...
处理数据...
在Scala中,trait也是有构造代码的,也就是trait中的,不包含在任何方法中的代码
// 而继承了trait的类的构造机制如下:
1、父类的构造函数执行;
2、trait的构造代码执行,多个trait从左到右依次执行;
3、构造trait时会先构造父trait,如果多个trait继承同一个父trait,则父trait只会构造一次;
4、所有trait构造完毕之后,子类的构造函数执行
class Person { println("Person's constructor!") }
trait Logger { println("Logger's constructor!") }
trait MyLogger extends Logger { println("MyLogger's constructor!") }
trait TimeLogger extends Logger { println("TimeLogger's constructor!") }
class Student extends Person with TimeLogger with MyLogger {
println("Student's constructor!")
}
// Person's constructor!
// Logger's constructor!
// TimeLogger's constructor!
// MyLogger's constructor!
// Student's constructor!
=======方法, 函数:
方法是一个以def开头的带有参数列表(可以无参数列表)的一个逻辑操作块,这正如object或者class中的成员方法一样。
函数是一个赋值给一个变量(或者常量)的匿名方法(带或者不带参数列表),并且通过=>转换符号跟上逻辑代码块的一个表达式。=>转换符号后面的逻辑代码块的写法与method的body部分相同。
Scala 中的函数则是一个完整的对象,Scala 中的函数其实就是继承了 Trait 的类的对象。
使用 val 语句可以定义函数,
使用def 语句定义方法。
==> SAM 转换, 简单来说就是把只有一个方法的接口转换为匿名方法
==> implicit 隐式转换
implicit conversion function
主要功能: 1. 隐式类型转换, 2,隐式方法(对象)导入
隐式转换的发生时机:
隐式类似转化包括: 函数/方法参数类型不匹配时,自动转化参数
隐式方法导入包括: 使用对象的方法不存在时,转换当前对象类型
隐式作用域/查找范围:
1.从当前作用域中查找val,var定义的对象
2.从当前类的伴生对象中查找隐式转换
3.从导入的包中查找隐式转化.
Scala默认会使用两种隐式转换,一种是源类型,或者目标类型的伴生对象内的隐式转换函数;一种是当前程序作用域内的可以用唯一标识符表示的隐式转换函数
隐式转换
// 1、调用某个函数,但是给函数传入的参数的类型,与函数定义的接收参数类型不匹配(案例:特殊售票窗口)
// 2、使用某个类型的对象,调用某个方法,而这个方法并不存在于该类型时(案例:超人变身)
// 3、使用某个类型的对象,调用某个方法,虽然该类型有这个方法,但是给方法传入的参数类型,与方法定义的接收参数的类型不匹配(案例:特殊售票窗口加强版)
==> List
head::tail 所以"::"的意思是head+tail组成一个新的 List , 注意 tail::head 是错误的, 因为head是一个值
Set 默认是无需的, 子类包括 LinkedHashSet, SortedSet
===>Actor
Scala的Actor类似于Java中的多线程编程。但是不同的是,Scala的Actor提供的模型与多线程有所不同。Scala的Actor尽可能地避免锁和共享状态,从而避免多线程并发时出现资源争用的情况,进而提升多线程编程的性能。此外,Scala Actor的这种模型还可以避免死锁等一系列传统多线程编程的问题。
元组的索引从1开始,是因为对于拥有静态类型元组的其他语言,如Haskell和ML,从1开始是传统的设定。
可变参数: echo(arr: _*)
这个标注告诉编译器把 arr 的每个元素当作参数,而不是当作单一的参数传给 echo 。
SBT:
什么是SBT? SBT = (not so) Simple Build Tool,是scala的构建工具,与java的maven地位相同。其设计宗旨是让简单的项目可以简单的配置,而复杂的项目可以复杂的配置。
?SCALA 标识符
字母数字标识符、操作符标识符、混合标识符、字面量标识符
—可变集合
var mutableArray = new ArrrayBuffer[int]()
—不可变集合
var arr= new Array[t](5);
可变 list
val li = ListBuffer[String]()
set不可改变
var s1 = Setr(1,2,3,4)
hashset可变集合
var s = new Mutable.HashSet[int]()
—
scala 转为 Java
kafkaParams.asJava
—
object 单例对象
val 不可变变量, 使用_占位符
Scala使用Actor作为其并发模型,Actor是类似线程的实体,通过邮箱发收消息。Actor可以复用线程,因此可以在程序中可以使用数百万个Actor,而线程只能创建数千个。在2.10之后的版本中,使用Akka作为其默认Actor实现。
类和对象
Scala中的类不声明为public,一个Scala源文件中可以有多个类。
class :修饰的称为伴生类;定义在class中的属性都是动态的,用于实例化 的;scala中的class类默认可以传参数,默认的传参数就是默认的构造函数。class 类属性自带getter ,setter方法。使用class时要new (必须new,除非在对象伴生用apply方法【在加载类的时候默认自动调用】已实经例化好),并且new的时候,class中除了方法不执行,其他都执行。
object: 修饰的称为伴生对象;定义在object中的属性(字段、方法)都是静 态的,main函数写在里面;scala 中的object是单例对象,相当于java中的工具类,可以看成是定义静态的方法的类.object不可以传参数。使用object时,不用new.
Apply方法
使用此方法时,可以在main函数中不通过new来创建一个对象,即可以不用专门的一次一次地进行实例化,加载创建对象的这个类的时候,会自动调用apply这个方法,类似Java中的static静态块
在 Scala 中,是没有 static 这个东西的,但是它也为我们提供了单例模式的实现方法,那就是使用关键字 object。
Scala 中使用单例模式时,除了定义的类之外,还要定义一个同名的 object 对象,它和类的区别是,object对象不能带参数。
当单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象:companion object。你必须在同一个源文件里定义类和它的伴生对象。类被称为是这个单例对象的伴生类:companion class。类和它的伴生对象可以互相访问其私有成员。
函数如果没有参数可以不传,, 不写括号
在 Scala 中,字符串的类型实际上是 Java String,它本身没有 String 类。
case class
使用了case关键字的类定义就是样例类(case classes),样例类是种特殊的类。实现了类构造参数的getter方法(构造参数默认被声明为val),当构造参数是声明为var类型的,它将帮你实现setter和getter方法。
样例类默认帮你实现了toString,equals,copy和hashCode等方法。
样例类可以new, 也可以不用new
如:
case class Student(id: Int, age: Int)
var s1= Student(1,1)
var s2=Student(2,22)
println(s2)
————
Actor
Actor Model是用来编写并行计算或分布式系统的高层次抽象(类似java中的Thread)让程序员不必为多线程模式下共享锁而烦恼,被用在Erlang 语言上, 高可用性99.9999999 % 一年只有31ms 宕机Actors将状态和行为封装在一个轻量的进程/线程中,但是不和其他Actors分享状态,每个Actors有自己的世界观,当需要和其他Actors交互时,通过发送事件和消息,发送是异步的,非堵塞的(fire-andforget),发送消息后不必等另外Actors回复,也不必暂停,每个Actors有自己的消息队列,进来的消息按先来后到排列,这就有很好的并发策略和可伸缩性,可以建立性能很好的事件驱动系统。
Actor的特征:
1.ActorModel是消息传递模型,基本特征就是消息传递
2.消息发送是异步的,非阻塞的
3.消息一旦发送成功,不能修改,类似于邮件的发送来往
4.Actor之间传递时,自己决定决定去检查消息,而不是一直等待,是异步非阻塞的