*scala语言特点
是运行在jvm之上的语言
同时兼具面向对象编程范式和面向函数编程范式的语言
强类型语言
Scala combines object-oriented and functional programming in one concise, high-level language. Scala's static types help avoid bugs in complex applications, and its JVM and JavaScript runtimes let you build high-performance systems with easy access to huge ecosystems of libraries.
akka
*scala的doc目录
C:\Program Files (x86)\scala\api\scala-library
*变量的声明语法
val 声明一个常量 对应java的 fianl 不能够被重新赋值
val p = new Person()
val p1 = new Person()
p对象的name属性和age属性是可以发生变化的,但是不能:
p = p1
var 声明一个变量,并且可对该变量进行重新赋值
def 声明一个变量,val和var在声明变量时,等号右边的内容只被执行一遍,然后值就赋给变量了
如果使用def的话,每次调用变量时,等号右边的表达式在每次变量被调用时都会重新再执行一遍。
def p = new Person()
println(p)
println(p)
scala声明变量的语法
Person p;
val|var|def variableName:VariableClass = new VariableClass()
scala的一句表达式的分号终止符可以省略
scala声明一个变量,变量的类型是可以省略不写的
val|var|def variableName = new VariableClass()
scala可以从初始化的过程中推断出变量的类型,因此,类型可以省略
在scala中,它推荐我们尽可能的使用val 而不是var(原因:之所以在这种函数式编程语言里,要求大家尽量使用val(常量),少使用var(变量),其中一个很重要的原因是多线程中并发访问中的读写一致性原因。对于变量来说,因为值可以被其它逻辑修改,就存在读写一致性问题,如果读写一致性会影响业务逻辑,就需要增加这种额外的代码来维持这种读写一致性。而对于常量来说,由于不能修改,所以就不存在读写一致性的问题。
在编程中,变量的这种可修改性其实是增加了代码逻辑的复杂性,比如一个变量A,如果在其作用域内,有很多逻辑都会读写这个变量,你就很难确定A的值。如果是存在并发访问的情况就更复杂了。所以为了使得代码逻辑简单,应该多使用常量而不是变量来进行编程。)
def是用来声明方法/函数的
def functionName(param:ParamType,param1:ParamType1) : ReturnType = {
函数体
return functionResultType
}
public static void main(String[] args){}
*scala的基础数据类型和java的基础数据类型
基础类型不可以调用点操作
scala的基础数据类型的变量就是对象,可以调用点操作
对象的字面量:从代码表达式上就能推断出其类型和其值的 对象 就叫字面量
String abc = "abc"
int i = 123
int h = 0xaf
boolean t = true
*String类型
String a = "abc"
a = "def"
*运算符
数学运算符:+ - * \
逻辑运算符:&& || | & !
赋值运算符:= += -=
为运算符: & |
比较运算符:> < >= <= != ==
三目运算符 : scala不支持三目运算符
自增自检运算符:++ --,scala不支持++ --
java的运算符就是语法的一部分
scala的运算符不是语法的一部分,scala里的运算符是一种函数
==的用法和java的不一样
String a = "abc"
String b = "abc"
a == b true
String a = new String("abc")
String b = new String("abc")
a == b false
scala的 == 是比较两个对象的值是否相等,等同于java的equals方法
*流程控制语法
条件判断 循环
语句块
大括号括中的叫做语句块
java的代码块就是逻辑执行过程
scala中代码块是逻辑执行过程,同时还有返回值。
如果java的代码块想要有返回值,只有函数代码块,并且需要明文return,才能有返回值。
scala中代码块的返回值,不需要return,代码块的最后一句表达式,默认就是该代码块的返回值
scala中因为代码块有返回值,所以scala中可以把一个代码块赋值给一个变量
scala的Unit等同于java的void,Unit的字面量形式就是:()
scala的条件判断语句中的语句块都是有返回值的
scala中有match case语法 和java的 switch case语法是对应的
但是scala的match case语法功能要强大很多,所以scala中甚至可能用match case来替代if else
和if else一样,match case也有返回值
variable mach{
case 匹配表达式1 => {语句块}
case 匹配表达式2 => {语句块}
case _ => {语句块}
}
*循环
scala的循环和java的循环一样,不具有返回值的能力
但是for循环可以配合yeild关键词来让for循环具有返回值的能力
for(i <- Collector if condition){}
1-10 只打印偶数 并且不大于5
for循环,可以在循环变量定义的后面添加if过滤条件
集合
for(int i=0;i<10;i++){
for(int j=0;j<10;j++){
...
}
}
scala里没有continue和break语法
scala中提供了break的特殊写法:
val myBreak = new Breaks
import myBreak.breakable
import myBreak.break
breakable(
for(x <- 4 to 10;y<- 1 to 10){
if(y == x){
break()
}else{
println(s"y=$y,x=$x")
}
}
)
在scala也可以使用在循环体内使用return通过终结函数来终结循环,副作用是函数也跟着一块终结
对象 类型
zhangsan Person
函数 类型
def sum(x:Int,y:Int) : Int = x+y 从函数的参数类型,个数以及返回值类型
def mult(x:Int,y:Int): Int = x*y (Int,Int) => Int
(x:Int,y:Int)=>x+y
使用scala计算:BMI
个税计算:
输入 3 个正数,判断能否构成一个三角形。
编写程序,对输入的年、月、日,给出该天是该年的第多少天?不使用日期类型
编写程序,对输入一个 0~99999 之间的任意数,判断输入的数是几位数?
用 while 循环,计算 1~200 之间所有 3 的倍数之和。
*集合类型
Array 定长数组
1.定长,长度不能发生变化
2.元素可以被更改
ArrayBuffer 变长数组
1.长度可以随时发生变化
2.元素也可以被更改
Array(1,2,3,4,5)
head--------tail
集合的第一个元素--------------集合的除了第一个元素之外的剩余元素
1------------(2,3,4,5)
init--------last
集合除了最后一个元素之外的所有元素---------------集合的最后一个元素
(1,2,3,4)---------5
尾递归
集合的交集:intersect
集合的并集:union
List
1.长度确定
2.元素是不可变的
ListBuffer
1.长度可变
2.元素可变
Set分为mutable包下的Set和immutable包下的Set
immutable下的Set长度不能变,元素不能变
mutable下的Set长度可变,元素可变
Map也分为immutable包下的和mutable包下的
元组类型
元组是不可变的,长度不可变,元素也不可变
元组长度有限
元组中每个元素的类型不不要求一致
元组没有直接的方法来遍历,使用_+index的方式来取值
*面向对象
scala中class的定义代码和java基本类似
scala的构造方法
1.不写构造方法的时候默认有一个不接收参数的构造方法
2.scala里可以有多个构造方法,但是构造方法有主辅之分
3.scala的主构造方法(只有一个)是加在类名后面的,辅助构造方法是在类体里面的
4.辅助构造方法的名字统一都叫做this
5.辅助构造方法必须直接的或间接的调用主构造方法
6.主构造方法上的参数前面可以添加val或var修饰符,如果使用val或var做修饰符,那么这个参数将会成为类型的属性
scala的访问控制符
scala中没有public,不写就是public
private 只有本类和内部类可以访问,互为伴生的类和对象之间也可以互访私有成员
protected 子类可以访问
访问控制符,可以添加在类上面,可以添加在类成员(属性和方法),也可以添加在构造方法上
主构造方法添加修饰符是添加在类名后面参数的小口号前面
还可以添加在主构造方法参数上
在scala里面访问修饰符可以在修饰符后面用中括号添加更细粒度的权限控制
比方说private[this]就会把成员只限定到本访问
private[]
protected[]
里面还可以添加包对象,如果添加包对象之后就可以让修饰符有更加宽松的权限
public class xxUtils{
public static void method1(){}
public static void method2(){}
}
object
*伴生类和伴生对象
当scala的一个objcet和一个class名称相同,就成为这个object是这个class的伴生对象,这个class就是这个object的伴生类
互为伴生的object和class他们编译后的class合二为一了
因此,互为伴生的object和class之间可以互相访问私有成员,除非是使用private[this]的形式来声明私有成员
在object中
apply方法在被调用时,可以省略点号和方法的名称,如:object Schoole 的apply方法,可以使用Schoole()的形式调用
伴生对象的apply方法经常被用作伴生类的构造方法来使用
unapply方法,伴生对象的unapply方法经常被用于模式匹配(match case),和属性抽取
*抽象类
和java一样scala的抽象类不能被直接实例化
不一样的点在于,scala的抽象类中不仅可以有抽象的方法,还可以有抽象的属性
抽象方法未被实现
抽象属性未被初始化
抽象方法和抽象属性之前不用加abstract,只要属性未被初始化,或方法未被实现,默认就会被认为是抽象属性和抽象方法。
在继承抽象类的时候要实现抽象类未实现的方法,和初始化抽象类未初始化的属性
*继承
在scala语言中子类和父类也是单继承关系
子类继承父类的成员
子类的构造过程,子类被实例化的时候会先执行父类的构造方法。
如果父类拥有主构造方法参数的话,子类在继承时必须满足父类的主构造方法的参数
子类可以对父类的方法进行重写,在方法前面添加override
子类还可以重写父类的常量(val修饰的),重写时,常量的类型要和父类保持一致
*scala中的trait就是java里面的interface
java
接口(interface):未实现的方法、常量
抽象类:属性,已实现的方法,未实现的方法
scala
接口(trait):未实现的属性 和 未实现的方法 已实现的方法和已实现的属性
抽象类:属性,已实现的方法,未实现的属性 和 未实现的方法
类只能单继承,接口能够多实现
当已实现的方法冲突时,scala要求子类必须自己重写冲突的方法
*函数
scala函数在声明的时候等号一般不要省略,如果省略等号将会使得这个函数变成一个过程,不管函数体代码怎么写,该函数的调用都不会有返回值,使用return也不行。
函数的字面量写法:
使用 => 符号来把函数分割成两部分,左边是输入(参数声明),右边是输出(函数体,语句块)
(a:Int,b:Int) => if(a>b) println("true") else println("false")
当输入是一个参数的时候可以省略小括号
s:String => pritnln(s.length)
(a:Int,b:Int) => {
println(a)
println(b)
}
(a:Int,b:Int) => println(a);println(b)
匿名函数在被使用的时候是可以有一些简化的写法的
List(1,2,3,4,5)
"1=>2=>3=>4=>5"
集合类的聚合函数
def reduce[A1 >: A](op: (A1, A1) => A1): A1
reduce只接受一个参数,这个参数就是reduce迭代聚合的函数,它要求整个迭代过程输出的类型必须和输入的类型一致。
def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1
fold接收两个参数,一个是聚合值的初值,一个是迭代聚合函数,它也要求整个过程输出类型要和输入类型一致
def aggregate[B](z: => B)(seqop: (B, A) => B, combop: (B, B) => B): B
aggregate接收三个参数,第一个是聚合的初始值,第二个是聚合函数(每个元素怎么样聚合到结果值上的函数),第三个函数是分区和分区之间的聚合
*函数的声明/定义方式
1.def
def functionname(param:Type) :ReturnType = {}
2.val
val functionname = fuctionliterl
使用def声明函数的时候 在使用上当调用函数的名称 后面不管加不加小括号都是对函数的调用
使用val声明函数的时候 在使用上当调用函数的名称是,添加小括号就是对函数的调用 不添加小括号,就是对函数的引用
*高阶函数
如果一个函数的返回值是另外一个函数,这个函数是高阶函数,通常这种函数被称为“闭包”
如果一个函数的参数是另外一个函数的话,这个函数也是高阶函数
20180402
*模式匹配的形式
1.常量形式
2.变量匹配模式
3.构造器匹配模式,如果使用构造器匹配,被匹配的类型必须在伴生对象中实现apply方法和unapply方法
4.序列匹配,集合类型匹配(其实归根结底还是构造器匹配)
5.变量绑定匹配模式
6.正则匹配
7.类型匹配,变量名称:类型
Child<Parent
List[Child]<List[Parent]
val a = List[Child]
val a = List(c1,c2,c3)
a match{
case List(p1,p2,p3)
}
*Option Some None
Option是Some None的父类
这三个类定义出来是为了封装其他的类型对象的
封装后方便使用match case的
这三个一般会在方法上进行使用,方法的定义返回值类型,用Option封装,
方法的实现返回值用None和Some来进行封装,从而方便我们对方法返回的结果进行模式匹配,而不是只能用if判断
def getSomeData(sql:String) :List[Obj] = {
...
}
val data = getSomeData("select * from ....")
if(data!=null &&data.length>0){
}
def getSomeData(sql:String) :Option[List[Obj]] = {
...
if("没有结果") None()
else Some(List(.....))
}
val data = getSomeData("select * from ....")
data match{
case _:None => {}
case _:Some(xxxx) => 遍历xxxx
}
public void getType(Object object){
//打印出object的类型
}
*scala的case class
1.case class必须在类的名称后面添加小括号
2.case class自动帮我们实现了类的伴生对象,并且在伴生对象里实现了apply方法、unapply方法、tostring方法
3.在case class类体语句块里面我们可以定义自己的属性和方法
4.case class在主构造器上声明的参数形式会被添加到apply方法和unapply方法上
5.case class主构造器上的参数,不需要添加val或var修饰就自动会被作为类的属性
6.没有在主构造器上出现的属性是不能参加模式匹配的
7.case class主构造器的属性如果不添加var的修饰的话会被自动认为是val的,对象被实例化后属性就不能再被改变
8.case class是类似一种final类型或者说seald类型,是不能被继承
case class一般等同于java里面的Entity类,POJO类,VO类
val a = CaseClassTest("123","345")
val b = CaseClassTest("123","345")
a==b a.equals(b) a.hashCode == b.hashCode
*sealed case class
Person
Man
Woman
Unknown
val p:Person = Man()
p match{
case Man => ...
case Woman => ...
}
sealed abstract class Person
case class Man extends Person
case class Woman extens Person
case class Unknown extends Person
p match{
case Man => ...
case Woman => ...
}
*import引入
1.import语句不仅可以出现在代码的头部,还可以出现在代码的任意语句块中
2.import包的时候,如果要引入包下所有类,java里用* ,scala里用的是 _
java里的*代表的是包下所有的类
scala里的_代表的是包下所有的类和对象(函数,隐式参数,包属性。。。)
3.引入时可以对被引入的类型进行重命名
import java.util.{HashMap => JMap}
4.可以用import把一些类型隐藏
import java.util.{HashMap=> _,_}
隐藏HashMap
*内部类
java的内部类:
1.静态内部类 可以被独立的使用
2.非静态的内部类 不能被独立使用,必须先实例化外部类的对象才能使用内部类
scala的内部类:
1.object的内部类
2.class的内部类
*scala的package形式比java的package形式更加灵活
*包对象
package object importtest
1.名称和其包名称一模一样
2.一个package下面只能有一个包对象
3.包对象上可以定义属性,方法,隐式转换,隐式参数
4.这些对象都可以被引入这个包的程序进行 无引用调用
ByteBufferAsFloatBufferL
//隐式转换把Int隐式转换成RichInt类型
//隐式转换是一个方法,方法前面用implicit作为修饰符
//隐式转换函数的重心部分在于参数类型和返回值类型,它的作用是把参数的类型隐式转换成返回值的类型
//从而让参数的类型具有返回值类型的功能和属性
//隐式转换的函数的名称是什么不重要,
//隐式转换一般被用于对已有类型的扩展上
//当隐式转换的目标类型的方法和原类型的方法冲突时以原类型的方法为准
//隐式转换可以把一个类型转换成多个其他类型,原类型转换后就具有其他多个类型的功能和属性
//当多个其他类型之间有某个方法冲突时,原类型就不具有调用该方法的能力
*隐式参数《------》隐式值
在方法的参数前面添加implicit修饰符的时候这个参数就会被编译成隐式参数
隐式参数在函数被调用的时候是可以不用填内容的
1.在一个参数的小括号里,implicit修饰符必须在这个小括号的最前面
def sum(implicit x:Int, y:Int)
2.一个小括号里一旦有implicit 那这个小括号里所有的参数都会被当做隐式参数
比如上面,x和y都是隐式参数
当我们想让某个参数是隐式参数的时候,为了不影响其他参数,可以使用函数的柯里化特点来解决
在柯里化后的函数上如果要修饰隐式参数的话,implicit只能在最后一个小括号里
3.隐式值,在一个作用域内,某一个类型的隐式值只允许出现一个
在包对象里面如果有多个隐式值类型会以最先定义的隐式值为准
如果在作用域里(类或object的代码块)里定义的隐式值如果有冲突的话,程序就会抛异常
*函数调用时不填写参数的写法
1.隐式参数在调用函数时可以不用填写参数,系统会自动填入隐式值
2.带名参数和默认值参数。
当一个函数的参数有默认值时,调用该函数时可以不用填写有默认值的参数
*scala的项目管理工具:sbt和maven
maven构建scala项目
1.使用maven的模板archetype来创建scala项目
new project--->maven--->create from archetype--->add archetype
填入模板的gav
g:net.alchim31.maven
a: scala-archetype-simple
v: 1.5
选择scala-archetype-simple
---》next---》创建项目
项目生成后:
打开pom文件:
修改scala的版本号
比如我的是:2.11 2.11.8
删除测试的依赖:
specs2_${scala.tools.version}
scalatest_${scala.tools.version}
删除行:
<arg>-make:transitive</arg>
删除测试代码
2.不使用模板来创建scala项目
1创建maven,不选中模板
2打开pom,添加:
<dependencies>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>2.11.8</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<!-- see http://davidb.github.com/scala-maven-plugin -->
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.1.3</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
<configuration>
<args>
<arg>-dependencyfile</arg>
<arg>${project.build.directory}/.scala_dependencies</arg>
</args>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.13</version>
<configuration>
<useFile>false</useFile>
<disableXmlReport>true</disableXmlReport>
<!-- If you have classpath issue like NoDefClassError,... -->
<!-- useManifestOnlyJar>false</useManifestOnlyJar -->
<includes>
<include>**/*Test.*</include>
<include>**/*Suite.*</include>
</includes>
</configuration>
</plugin>
</plugins>
</build>
3.在src/main目录下面创建scala目录,然后选择project structure
--->modules---->循环中scala目录----》点Sources目录
4.scala目录下面就可以写scala代码
*scala可以和java进行无缝互相调用