目录
一、Scala的异常处理机制
scala中继承了java的异常机制
try catch finally
object ScalaDemo04 {
def main(args: Array[String]): Unit = {
try {
throw new RuntimeException("error");
}catch {
case t: NullPointerException => t.printStackTrace();println("空指针异常");
case t: Exception=>t.printStackTrace();println("其他异常");
}finally {
println("资源释放")
}
}
}
1、scala的异常处理机制同java。只不过形式上有区别。在catch中是通过case来匹配异常然后进行处理
2、scala中没有switch case。取代的是match case
3、scala的match case 有返回值。可以接
val num=6 val result=num match{ case 5=>{ println("555") "555" } case 6=>{ println("666") "666" } case 7=>{ println("777") "777" } }
4、注意:match case 只要发现有一个匹配,则剩下的case不会继续匹配
scala中的match类似于其他语言的switch
5、scala通过import关键字导包。比如 import a._ 表示导入a包下所有类
6、scala可以在任意位置导包。但注意作用域问题
7、scala实现break的continue的方式:
注意:scala中没有break和continue语句,需要通过另外的形式来实现
①、导包:import util.control.Breaks._
②、要实现break: breakable(for(){break跳出循环效果})
③、要实现continue: for(){breakable(break->continue效果)}
import util.control.Breaks._ for(i<-1.to(10)) { breakable( if (i == 8) { break } else { print(i+" ") //结果 1 2 3 4 5 6 7 9 10 } ) }
二、Scala函数
1、函数的声明
scala 函数通过 def 关键字定义,def前面可以具有修饰符,可以通过private、protected来控制其访问权限
注意:没有public,不写默认就是public的。 此外也可跟上override,final等关键字修饰。
2、函数的返回值
1)函数体中return关键字往往可以省略掉,一旦省略掉,函数将会返回整个函数体中最后一行表达式的值,这也要求整个函数体的最后一行必须是正确类型的值的表达式。
2)大部分时候scala都可以通过 =符号 来自动推断出返回值的类型,所以通常返回值类型声明可以省略。
但是注意:如果因为省略了返回值类型造成歧义,则一定要写上返回值声明。
3)如果函数体只有一行内容,则包裹函数体的大括号可以省略
4)如果定义函数时,{}前没有=,则函数的返回值一律为Unit。如果返回值类型是UNIT,则另一种写法是可以去掉返回值类型和等号,把方法体写在花括号内,而这时方法内无论返回什么,返回值都是 UNIT。
格式:[private/protected] def 函数名(参数列表):返回值声明 = {函数体}
5) scala的函数支持默认参数机制。形式:def 函数名(形参名:类型=默认值)={}
6) scala的函数支持变长参数机制。可类比于java的可变参数机制。 def f9(num:Int,a:String*)={
注意:变长参数必须位于参数列表的最后。
object ScalaDemo05 {
def main(args: Array[String]): Unit = {
println(p0("1.txt"))
println(p1(10))
println(p2(3))
p3("hello")
}
// 返回文件名的后缀
def p0(name:String)={
name.split("\\.")(1)
}
def p1(num:Int)={
0 to num
}
//默认参数
def p2(a:Int,b:Int=10)={
a+b
}
//可变参数
def p3(a:String*)={
for(i<-a){
println(i)
}
}
}
三、Scala高阶函数
1、函数的种类
①、成员函数
函数被使用在类的内部,作为类的一份子,称为类的成员函数
object ScalaDemo06 {
def main(args: Array[String]): Unit = {
val p = new Student()
p.eat()
p.study()
}
// eat() 和study()属于 类Student的成员函数
class Student{
def eat(): Unit ={
println("吃饭")
}
def study(): Unit ={
println("学习")
}
}
}
②、本地函数
函数内嵌的函数称为本地函数,这样的函数外界无法访问
/**
* 本地函数
*/
object ScalaDemo07 {
def main(args: Array[String]): Unit = {
val p = new Student()
p.eat("肉")
}
class Student{
def eat(food:String): Unit ={
//cook函数内嵌在eat函数里面,这样的函数称之为本地函数
def cook(food:String)={
"做熟了的"+food
}
println("吃"+cook(food))
}
}
}
③、函数值(匿名函数):最直观的特点就是没有函数名;方法体{}的连接是=>
匿名和函数的作用:把匿名函数作为参数进行赋值;把匿名函数作为参数进行传递
def f1(a: Int, b: Int) = {a + b}
//等价于上式的函数
(a: Int, b: Int) => {a + b}
//如果函数体只有一行代码,可以省去大括号
(a: Int, b: Int) =>a + b
//如果函数的参数类型可以被推测,则可以省略类型值
//(a, b) => a + b
//如果函数参数列表只有一个参数,小括号有可以省略
//a: Int => a + 1;
//可以将f1()函数赋值给f2常量
val f2=f1(_,_);
//可以将f1()函数赋值给f3变量,f3可以更改此函数
var f3=f1(_,_);
f3=(a:Int,b:Int)=>a*b;
//也可以这样写
val f4=(c:Int,d:Int)=>{c+d}
//注意,下面的写法是将f1的函数值复制给f5,而不是函数赋值给f5
val f5=f1(2,3)
④、高阶函数
高阶函数(Higher-Order Function)就是操作其他函数的函数。
object ScalaDemo09 {
def main(args: Array[String]): Unit = {
f1("hello","world",(a:String,b:String)=>{a+b})
}
def f1(a: String,b: String,f:(String,String)=>String){
println(f(a,b))
}
}
2、高阶函数
Scala 中允许使用高阶函数, 高阶函数可以使用其他函数作为参数,或者使用函数作为输出结果。
object ScalaDemo09 {
//定义了compute函数,a,b和f函数 三个参数。其中f函数未做实现。
//我们的目的是对传入的a和b参数利用 f函数做运算,但是具体是什么运算需要由用户自己来指定。
def compute(a:Int,b:Int,f:(Int,Int)=>Int):Int={
f(a,b)
}
def main(args: Array[String]): Unit = {
val f1=(a:Int,b:Int)=>{a+b}
val f2=(a:Int,b:Int)=>{a*b}
val f3=(a:Int,b:Int)=>{a-b}
val f4=(a:Int,b:Int)=>{a/b}
//由下式可以看出,scala中,函数可以当做参数进行传递和调用
val result=compute(2,3,f1)
println(result)
//下式等价于上式
//val result=compute(2,3,(a,b)=>{a+b})
}
}
/**
* 将用户处理后的字符串结果进行打印输出
*/
object ScalaDemo10 {
def handleString(a:String,f:(String)=>String): Unit ={
println("处理完后的字符串:"+f(a))
}
def main(args: Array[String]): Unit = {
val a="hello scala"
handleString(a,a=>a)
handleString(a,(a)=>{a.substring(6)})
handleString(a,(a)=>{a.concat("1706")})
}
}
占位符:占位符指的是scala中的下划线_ ,可以用它当作一个或多个参数来使用。
使用_占位符的前提要求:每个参数在函数仅出现一次。
使用下划线时,如果类型可以自动推断出,则不用声明类型。如果无法自动推断类型,则在下划线后自己来显示声明类型即可。
object ScalaDemo11 {
def compute(a:Int,b:Int,f:(Int,Int)=>Int)={
f(a,b)
}
def handleString(a:String,f:(String)=>String){
println("处理完后的字符串为:"+ f(a))
}
def main(args: Array[String]): Unit = {
val message="hello scala"
//这样用占位符会出错
//handleString(message,(_)=>{_.substring(6)})
//应该为下面的写法
handleString(message,{_.substring(6)})
//如果函数体只有一行代码,则还可以将大括号去掉
handleString(message,_.substring(6))
//compute的代码可简化
compute(2,3,_+_)
compute(2,3,_*_)
compute(2,3,_-_)
//此外
// val f1=(a:Int,b:Int)=>{a+b}
// 等价于下式:
// val f1=(_:Int)+(_:Int)
// compute(2, 3, f1)
//比如
val list=List(1,3,5,7,9)
list.foreach {x => print(x)}
list.foreach {_ => print(_)}
list.foreach{print(_)}
list.foreach{x=>x*2}
list.foreach{_*2}
}
}
3、递归函数
用递归方式实现斐波那契数列:
scala 递归函数的返回值类型必须显示声明,不能使用自动推断
scala 递归函数结束条件的返回值时,必须加return 关键字返回
/**
* 用递归方式实现斐波那契数列
* 给定一个斐波那契数列:
* 1 2 3 5 8 13 21 ...
* 目的:编写一个递归函数f1(n:Int),然后获取第n项的数字
* 写递归函数的技巧,掌握两要素:
* ①找到结束条件:f(0)=1 f(1)=2
* ②找到项与项之间的函数关系:f(n)=f(n-1)+f(n-2)
*/
object ScalaDemo12 {
def f1(n:Int):Int={
if(n==0) return 0
if(n==1) 1
else f1(n-1)+f1(n-2)
}
def main(args: Array[String]): Unit = {
val a =f1(11)
println(a)
}
}
object ScalaDemo04 {
def main(args: Array[String]): Unit = {
//给定一个数列
//2 3 4 9 16 81 ...
//要求编写递归函数f2(n:Int),判断第n项的数字是多少
//结束条件:f(0)=2 f(1)=3
//函数关系:f(n)=f(n-2)*f(n-2)
def f2(n:Int):Int={
if(n==0)return 2
if(n==1) 3
else f2(n-2)*f2(n-2)
}
f2(7)
println(f2(7))
//2 3 4 9 8 27 16 81 ...
//0 1 2 3 4 5 6 7
//要求编写递归函数f3(n:Int),判断第n项的数字是多少
//结束条件: f(0)=2 f(1)=3
//函数关系: 如果n是偶数项, f(n)=2*f(n-2)
//如果n是偶数项,f(n)=3*f(n-2)
def f3(n:Int):Int={
if(n==0)return 2
if(n==1)return 3
if(n%2==0) 2*f3(n-2)
else 3*f3(n-2)
}
println(f3(9))
//编写递归函数计算x的n次方,其中n是整数,要考虑等n是0,正偶数,正奇数,负数这几种情况。
//比如当x=2时,此函数要算出 2^4,2^3,2^0,2^(-1)对应的值
//mi(x:Int,n:Int):Double={ }
//mi(2,10)=1024
//mi(2,-1)=0.5
def mi(x:Double,n:Int):Double={
if(n==0)return 1
else if(n>0&&n%2==0) mi(x,n/2)*mi(x,n/2)
else if(n>0&&n%2==1) x*mi(x,n-1)
else 1/mi(x,-n)
}
println(mi(2,-1))
}
}
四、柯里化Currying
柯里化(Currying)技术 Christopher Strachey 以逻辑学家 Haskell Curry 命名的(尽管它是 Moses Schnfinkel 和 Gottlob Frege 发明的)。它是把接受多个参数的函数变换成接受一个单一参数的函数,并且返回接受余下的参数且返回结果的新函数的技术。
示例一:
object ScalaDemo01 {
def main(args: Array[String]): Unit = {
//首先我们定义一个函数:
def f1(a:Int,b:Int)={a+b}
//现在我们把这个函数变一下形:
def f2(a:Int)(b:Int)={a+b}
//那么我们应用的时候,应该这样使用:f2(2)(3),最后结果一样都是5,这种方式就叫柯里化
val r1 =f2(2)(3)
/**
* 柯里化实质上会演变成这样一个函数:
* 接受一个参数a,返回一个匿名函数,
* 该匿名函数又接收一个参数b,函数体为a+b
*/
// 延迟处理思想
def f3(a:Int)=(b:Int)=>a+b
val f4=f3(2)
f4(3) //答案为5
println(f4(3))
}
}
示例二:
object ScalaDemo02 {
def f1(a:Int,b:Int,c:Int)={a+b+c}
def f2(a:Int)(b:Int)(c:Int)={a+b+c}
def f3(a:Int)(b:Int,c:Int)={a+b+c}
def f4(a:Int,b:Int)(c:Int)={a+b+c}
def main(args: Array[String]): Unit = {
// f1和f2的函数的体现的是传入三个数,马上的到结果
f1(1,2,3)
f2(1)(2)(3)
// f3函数则可以体现:延迟执行的思想以及固定易变因素的思想
val r1=f3(1)(2,3)
println(r1)
}
}
案例三:
object ScalaDemo03 {
def f1(a:Int,b:Int,f:(Int,Int)=>Int)={f(a,b)}
def f2(a:Int)(b:Int)(f:(Int,Int)=>Int)={f(a,b)}
def f3(a:Int,b:Int)(f:(Int,Int)=>Int)={f(a,b)}
def f4(a:Int)(b:Int,f:(Int,Int)=>Int)={f(a,b)}
def main(args: Array[String]): Unit = {
//调用f3
f3(2,3){(a,b)=>a+b}
//可简化,我们发现这和scala中很多函数的形式很相近,比如:for(x<-1 to 10){print(_)}
//所以,柯里化的另外一个作用是让用户灵活的自义定自建控制结构
f3(2,3){_+_}
f3(2,3){_*_}
}
}
五、Scala的类
1、概述
1)scala中的类和java中基本类似。
2)scala中的类同样通过class来进行声明
3)scala中的类同样可以具有成员变量和成员方法
4)scala中的类同样可以通过new关键字来创建出对象
2、创建类
成员属性和成员方法默认都是public的 需要私有可以写private 需要保护可以写protected
class Person {
//创建一个类,并定义类里的两个成员变量name和age。以及一个成员方法 eat()
//需要注意的是:scala中变量(var)声明时需要指定初始值,
var name:String="";
var age:Int=0;
def eat(): Unit ={
println("吃饭")
}
}
当成员变量或成员方法是私有属性时,外部将不能直接访问,这个同java一样
2、类的构造
和java不同,scala中的类不需要明确声明一个构造器,而是直接将构造参数通过构造参数列表声明为类的一部分。
而直接写在类的体中的既不是类的成员变量也不是成员函数的部分,会自动收集为构造函数的体。
object ScalaDemo06 {
//scala中的类不需要明确声明一个构造器,
//而是直接将构造参数通过构造参数列表声明为类的一部分
class Person(var1:String,var2:Int){
var name=var1;
var age=var2;
//而直接写在类的体中的既不是类的成员变量也不是成员函数的部分,
//会自动收集为构造函数的体。
println("……")
println("***")
}
def main(args: Array[String]): Unit = {
//当调用构造方法时,会打印 …… 和***
val p=new Person("tom",23)
}
}
3、辅助构造器
有些时候 一个类里需要多个构造器。scala里主构造器之外的构造器被称为辅助构造器。
1)Scala的辅助构造器定义开始于 def this()
2)Scala里每个辅助构造器的第一个动作都是调用同类的构造器。
object ScalaDemo07 {
//scala支持辅助构造器,
class Person(var1:String,var2:Int){
var name=var1;
var age=var2;
//Scala的辅助构造器定义开始于 def this()
//Scala里每个辅助构造器的第一个动作都是调用同类的构造器。
def this(var1:String){
this(var1:String,0)
}
//Scala的辅助构造器定义开始于 def this()
def this(var2:Int){
this("",var2:Int)
}
}
def main(args: Array[String]): Unit = {
val p1=new Person("tom")
val p2=new Person(23)
}
}
示例:
class Person(v1:String,v2:Int){
private var name=v1
private var age=v2
def this(v1:String){
this(v1:String,0)
}
def this(v2:Int){
this("",v2)
}
def setName(name:String)={this.name=name}
def getName()={name}
def setAge(age:Int)={this.age=age}
def getAge()={age}
def eat()={println("eat")}
def say()={println("say")}
println("&&&&&&")
}
4、单例对象
1、scala中的类(class)不能定义静态成员(或静态方法),取而代之的是定义单例对象
2、单例对象需要通过Object关键字声明
3、一个单例对象可以单独存在,也可以绑定到一个类上,绑定时object和class必须在同一个文件,名字一致
4、单例对象当中的所有方法,都可以不需要创建对象而直接通过object单例对象的名字直接来调用,用起来感觉像一个静态方法一样
5、一个单例对象和某个类写在同一个源文件中且共享同一个名字时,他们就产生了一个绑定的关系
6、此时单例对象称为该类的伴生对象。类称为该对象的伴生类
7、类和它的伴生对象可以互相访问其私有成员
8、伴生的方式可以就可以为类增加静态成员
9、单例对象不能new,因此没有构造参数
10、可以把单例对象当作java中可能会用到的静态方法工具类
11、比如作为程序入口的main方法必须是静态的,所以main方法必须处在一个单例对象中,而不能写在一个类中
12、单例对象在第一次被访问的时候才会初始化
//类Person
class Person{
//此私有变量,伴生类可以访问
private val namespace="1706"
}
//Person的单利对象,也即伴生类Person,
object Person{
def showTime(){
println(System.currentTimeMillis())
}
def shownamespace(){
val p=new Person
//可以访问类的私有变量
println(p.namespace)
}
}
def main(args: Array[String]): Unit = {
Person.showTime();
Person.shownamespace()
}
5、重写与重载
1)重写是指覆盖父类中的方法来在子类中做其他事项,scala支持继承
override def 父类方法名 参数列表 返回值 方法体
2)重载是指在同一个类中提供方法名相同但是参数不同的方法和java中基本一致
3)在重写的时候,如果是一个普通方法,则需要加override关键字,如果是一个抽象方法,则不需要加。
重写eat()方法:
6、final的使用
可以用在成员变量、成员方法、类本身上
作用和java中相同
7、抽象类
scala中同样支持抽象类的使用,抽象类的内部可以包含抽象方法和非抽象方法。
抽象类不允许被实例化,抽象类主要是用来被继承的
抽象方法没有方法体
8、多态
多态:动态绑定,指"在执行期间判断所引用对象的实际类型,根据其实际类型调用其相应的方法"
object Demo13{
def main(args: Array[String]): Unit = {
val t1:Teacher=new TeacherChen("chen",32)
val t2:Teacher=new TeacherLiu("liu",32)
}
}
9、特质trait
1)可以类比java中的接口,但是又和接口非常不一样,特质相当于java中的接口,java中称为类实现了接口,scala中称为混入了特质。
2)和java中的接口不同的是,scala中的特质可以包含具有方法体的方法。
和抽象类不同的地方在于,scala的类只能单继承,但是可以多混入,利用这种方式可以实现类似c语言中多继承的特性。
3)在类中可以通过extends 或 with 关键字来让类混入特质,如果类没有明确继承父类,extends关键字没有被占用就可以使用extends。
4)但是如已经使用了extends显示的继承了父类,再向混入特质就要用with关键字了。
一个类的声明中只能有一个 extends,但是可以有多个with
10、泛型
基本和java中相同,不同的是,泛型是用方括号引起来的,java用<>
val arr = Array[String]();
11、lazy懒值
正常情况下通过val 和 var定义的量都会直接分配空间,即使这个量要在很久以后才使用,这样就会造成内存空间白白被占用。
这种情况下可以加上lazy关键字,延后变量/常量赋值的位置。这样直到后续真正用到这个量时才真正开辟空间赋值,减少了内存的浪费。
object ScalaDemo10 {
def main(args: Array[String]): Unit = {
val name = "zhang"//直接分配空间 即使一时半会用不到
lazy val name1 = "zhang"//并不会立即分配空间 直到后续用到了才会分配
}
}
12、caseclass-样例类
1)只要在声明类时 在class关键字前加上case关键字 这个类就成为了样例类
样例类必须要显式的声明一个主构造器
2)当样例类声明一个主构造器后,会默认隐式的声明一个空构造器
3)样例类默认实现序列化接口,如果是普通类要继承 Serializable
4)样例类默认自动覆盖 toString 方法,便于打印测试
5)样例类不需要new可以直接生成对象
\
13、option类型
在Scala中用来表示一个结果,它可能有值,也可能没有值,它有两个子Option
通过getOrElse(默认值)来操作Option,如果返回None,则返回值为默认值
object ScalaDemo12 {
def f1(a:Int,b:Int):Option[Int]={
if(b!=0){
Some(a/b)
}else{
None
}
}
def main(args: Array[String]): Unit = {
//表示如果有正确结果,返回正确结果,没有则返回指定的默认值
val Result=f1(4,2).getOrElse(0)
println(Result)
}
}
六、scala隐式转换机制
1、概述
scala implicit关键字详解(隐式转换函数、隐式类、隐式参数、隐式值),implicit是scala中的一个关键字,关于它有着丰富的用法,使得scala更灵活和容易扩展
2、隐式转换函数
implicit def int2str(x:Int):String = x.toString
这段代码声明了一个函数int2str,它与正常函数唯一的区别在于前面多出的implicit关键字。这里的implicit就是它字面的含义——隐式,它告诉编译器,这个函数是一个隐式转换函数,能够把Int类型的值转换成String类型的值。
这种隐式转换的意义在于,如果在进行一个对Int类型的操作时不合法,编译器会在当前作用域寻找合适的隐式转换,来尝试使这种操作合法。
需要注意:
1.对于隐式转换函数,编译器最关心的是它的类型签名,即它将哪一种类型转换到另一种类型,也就是说它应该接受只一个参数,对于接受多参数的隐式函数来说就没有隐式转换的功能了。
implicit def int2str(x:Int):String = x.toString // 正确
implicit def int2str(x:Int,y:Int):String = x.toString // 错误
2.不能存在二义性,即同一个作用域不能定义两个相同类型的隐式转换函数,这样编译器将无法决定使用哪个转换
implicit def int2str(x:Int):String = x.toString
implicit def anotherInt2str(x:Int):A = x.toString
object ScalaDemo13 {
val v1=100
//一种方式:做显式的类型转换,形式固定,to指定类型
val v2:String=v1.toString()
//另一种方式:做隐式的类型转换
//可以定义一个隐式的转换函数,完成隐式的类型转换
//implicit 关键字,用于定义隐式方法,以及隐式类和隐式参数
implicit def f1(x:Int)={x.toString()}
val v3:String=v1
}
3、隐式类
scala也支持隐式类的声明,在隐式类中定义的方法都是隐式转换方法。如下:
4、隐式参数
在当前作用域里存在一个Adder[Int]类型的隐式值implicit val a
在调用f2时,编译器可以找到implicit标记过的a,所以我们不必传递隐式参数而是直接调用f2(2,3)。而如果你想要传递隐式参数的话,你也可以自定义一个传给它
trait Adder[T]{
def add(x:T,y:T):T
}
def f1(x:Int,y:Int)(implicit adder: Adder[Int]): Unit ={
adder.add(x,y)
}
implicit val a= new Adder[Int] {
def add(x:Int,y:Int)={x+y}
}
f1(2,3)