SCALA基础知识学习

注:本文只说和Java不同的地方。
总结自:

  1. Scala详细教程
  2. Scala教程

scala基础语法

  1. Scala 与 Java 的最大区别是:Scala 语句末尾的分号 “;” 是可选的。
  2. def main(args: Array[String])Scala程序从main()方法开始处理,这是每一个Scala程序的强制程序入口部分。
  3. 换行符:Scala是面向行的语言,语句可以用分号(;)结束或换行符。Scala程序里,语句末尾的分号通常是可选的。如果你愿意可以输入一个,但若一行里仅有一个语句也可不写。另一方面,如果一行里写多个语句那么分号是需要的。val s = "菜鸟教程"; println(s)
  4. Scala 也是使用import关键字引用包。
  • 使用方式
 // 1. 第一种方式  和Java一致
 import java.awt.Color  
 // 2. 引入包内所有成员
 import java.awt._  
 // 3. 如果想要引入包中的几个成员,可以使用selector(选取器)
 import java.awt.{Color, Font}
 // 4. 重命名成员
 import java.util.{HashMap => JavaHashMap}
 // 5. 引入了util包的所有成员,但是HashMap被隐藏了
 import java.util.{HashMap => _, _} 
 
  • import语句可以出现在任何地方,而不是只能在文件顶部。import的效果从开始延伸到语句块的结束
  • 默认情况下,Scala 总会引入java.lang._scala._Predef._,这里也能解释,为什么以scala开头的包,在使用时都是省去scala.的。

Scala数据类型:

https://www.runoob.com/scala/scala-data-types.html

  1. Unit 表示无值,和其他语言中void等同。用作不返回任何结果的方法的结果类型。Unit 只有一个实例值,写成()
  2. 上表中列出的数据类型都是对象,也就是说scala没有java中的原生类型。在scala是可以对数字等基础类型调用方法的。

变量定义

在Scala中,使用关键字“var”声明变量,使用关键字“val”声明常量。

声明变量和常量不一定要指明数据类型,在没有指明数据类型的情况下,其数据类型是通过变量或常量的初始值推断出来的。但在没有指明数据类型的情况下声明变量或常量必须要给出其初始值,否则将会报错。

object 变量定义 extends App { 
    /** 
      * 定义变量使用var或者val关 键 字 
      *
      * 语法: 
      *  var | val 变量名称(: 数据类型) =变量值
      */
    // 使用val修饰的变量, 值不能为修改,相当于java中final修饰的变量 
    val name = "tom"
 
    // 使用var修饰的变量,值可以修改 
    var age = 18
 
    // 定义变量时,可以指定数据类型,也可以不指定,不指定时编译器会自动推测变量的数据类型 
    val name2 : String = "jack"
}

Scala 访问修饰符

  • Scala 访问修饰符基本和Java的一样,分别有:private,protected,public。
  • 如果没有指定访问修饰符,默认情况下,Scala 对象的访问级别都是 public。
  • Scala 中的 private限定符,比Java更严格,在嵌套类情况下,外层类甚至不能访问被嵌套类的私有成员。
class Outer{
    class Inner{
    private def f(){println("f")}
    class InnerMost{
        f() // 正确
        }
    }
    (new Inner).f() //错误
}
  • 在 scala 中,对保护(Protected)成员的访问比 java 更严格一些。因为它只允许保护成员在定义了该成员的类的子类中被访问。而在java中,用protected关键字修饰的成员,除了定义了该成员的类的子类可以访问,同一个包里的其他类也可以进行访问。Scala:只允许子类中被访问 Java:子类+同包下的其他类
  • 作用域保护:在Scala中,访问修饰符可以通过使用限定词强调;表示只对谁可见。语法如下:
 /**
  * 这里的x指代某个所属的包、类或单例对象。如果写成private[x],读作"这个成员除了对[…]中的类或[…]中的包中的类及它们的伴生对像可见外,对其它所有类都是private。
  */
private[x] 或 protected[x] 

条件表达式

  • 与Java一致:
object Test {
   def main(args: Array[String]) {
      var x = 30;

      if( x == 10 ){
         println("X 的值为 10");
      }else if( x == 20 ){
         println("X 的值为 20");
      }else if( x == 30 ){
         println("X 的值为 30");
      }else{
         println("无法判断 X 的值");
      }
   }
}
  • 语法糖:
/*
 * Scala if条件表达式
 */
object ScalaIf extends App {
  def main(args: Array[String]): Unit = {
    //if语句的使用
    var faceValue = 98
    var res1 = if (faceValue>90) "帅的一批" else "有点恼火"
    print(res1)

    //3>5 不成立,且代码没有else分支,那么res2应该输出什么呢?
    var i=3
    var res2=if (i>5) i  // 默认 if后面一定有else   如果没写 默认是 else ()
    print(res2)// output  ()代表空

    // 支持嵌套,if...else if ...else代码过多时可以使用{}
    val score=85
    if(score<60)"不及格"
    else if(score>=60&&score<70)"及格"
    else if (score>=80&&score<90)"优秀"
    else "优秀"
  }
}

循环

while循环 和 do while循环 语法与Java一致

  • for循环
object ForTest {
  def main ( args : Array[String]) {
     for( ele <- 1 to 9 ) { // 1.to(9)
       print (ele + " ") ;
     }

     val arry = Array(1,2,3,4,5,6)
     for( ele <- arry ) {
        print(ele + " ")
     }

       // for循环中可以增加过滤条件,下面这段语句是打印arr数组中的偶数
      for(i <- arry if i%2==0){
        print(i)
      }

      printf("\n")
      //双层for循环
      for (i <- 1 to 3;j <- 1 to 3 if i!=j; if i != 2){
        print( i*10 + j + " ")
      }/*output:12,13,21,23,31,32*/

      // for中使用yield 可以将 for 循环的返回值作为一个变量存储
      var retVal = for { a <- arry  if a != 3; if a < 8  } yield a
      for( a <- retVal){
        println( "Value of a: " + a );
      }
  }
}

  • for中使用yield的语法格式:
var retVal = for { var x <- List
     if condition1; if condition2...
} yield x

注意大括号中用于保存变量和条件,retVal是变量,循环中的yield会把当前的元素记下来,保存在集合中,循环结束后将返回该集合。

运算符和运算符重载

Scala 中的+-*/%等操作符的作用与 Java 一样,位操作符 &|^>><<也一样。

只是有一点特别的:这些操作符实际上是方法。例如:

// 下面是等同的
a + b
a.+(b) // a 方法 b 可以写成 a.方法(b)

Int.class的源码

Scala 方法与函数

Scala有方法与函数,二者在语义上的区别很小。Scala方法是类的一部分,而函数是一个对象可以赋值给一个变量。换句话来说在类中定义的函数即是方法。

Scala 中的方法跟 Java 的类似,方法是组成类的一部分。

Scala 中的函数则是一个完整的对象,Scala 中的函数其实就是继承了 Trait 的类的对象。

Scala 中使用 val 语句可以定义函数,def 语句定义方法。

def m(x: Int) = x + 3
val f = (x: Int) => x + 3
def main(args: Array[String]): Unit = {
    printf("方法" + m(2) + "\n")
    printf("函数" + f(2) + "\n")
}

方法声明:
Scala 方法声明格式如下:

def functionName ([参数列表]) : [return type] = {
    // function body
    // return [expr]
}

如果你不写等于号和方法主体,那么方法会被隐式声明为抽象(abstract),包含它的类型也将是一个抽象类型。

return type可以是任意合法的Scala数据类型。如果方法没有返回值,可以返回“Unit”,这个类似于Java的“Void”。另外,方法的返回值类型也可以不写,编译器可以自动推断出来,但是对于递归函数,必须指定返回类型。

object add {
   def addInt( a:Int, b:Int ) : Int = {
      var sum:Int = 0
      sum = a + b

      return sum
   }
}

方法的定义与调用:

object TestMethod {

  def m(x: Int) = x + 3
  val f = (x: Int) => x + 3
  def main(args: Array[String]): Unit = {
    printf("方法" + m(2) + "\n")  // 方法调用
    printf("函数" + f(2) + "\n")  // 函数调用
    val fm = m _  // 方法变函数  方法名 + 空格 + “_”
    printf("方法变函数:" + fm(2))
  }
}

函数的定义与调用:
函数的定义方式一:

调用方式:f1(2),其中f1为函数的引用,也可以叫做函数名。function1表示一个参数的函数。

函数定义方式二:

下面为没有任何参数的函数,函数的返回值为Int类型。

object TestFunction {
  // val 函数名=参数列表 =>{ 函数体 }
  val function2=(x:Int)=> {
    val s = x * 10
    s +1
  } ;

  val function3 = (x:Int , y:Int) => {
    x + y
  }

  // val 函数名:(参数类型列表) => 返回值类型 = (参数名列表) => {函数体}
  val function4:(Int,Int) => Int =(x,y) => x + y

  val function5 =() => {
     printf("8")
  }

  def main(args:Array[String]):Unit = {
    println("function2:" + function2(2))
    println("function3:" + function3(2,3))
    println("function4:" + function4(4,5))
    println("function4:" + function5())
  }
}

传值调用和传名调用

通常,函数的参数是传值参数;也就是说,参数的值在传递给函数之前就已经确定。

但是,在Scala中,我们方法或者函数的参数还可以是一个表达式,也就是将一个代码逻辑传递给了某个方法或者函数。

  • 传值调用(call-by-value):先计算参数表达式的值,再应用到函数内部;
  • 传名调用(call-by-name):将未计算的参数表达式直接应用到函数内部

在进入函数内部前,传值调用方式就已经将参数表达式的值计算完毕,而传名调用是在函数内部进行参数表达式的值计算的。

这就造成了一种现象,每次使用传名调用时,解释器都会计算一次表达式的值。

我的理解:

  • 传值调用:先计算表达式的值,然后表达式的值作为变量 在函数内部使用;和Java类似。
  • 传名调用:直接将函数或者代码片段传入函数内部。在使用时,在执行函数或者代码片段获取具体值,使用继续值做后续流程。

语法格式:

def 函数名 (参数名 : => 参数类型) : Unit = {
 // 方法体
}

使用示例:

object TestCallByName {

  def main(args:Array[String]) {
    // 传名调用
    println("========== 传名调用 =========")
    delayed(getTime())
    println("========== 传值调用 =========")
    // 传值调用
    printTime(getTime())
  }

  def getTime () :Long = {
    println("获取时间")
    System.nanoTime()
  }

  def delayed (t : => Long ): Unit = {
    println("delayed method")
    println("参数值:" +  t)
  }


  def printTime (t:Long) : Unit = {
    println("printTime method")
    println("当前时间是:" + t)
  }
}

下面是运行输出:
========== 传名调用 =========
delayed method
获取时间
参数值:14121545574709
========== 传值调用 =========
获取时间
printTime method
当前时间是:14121545692211

可变参数函数:

Scala 允许你指明函数的最后一个参数可以是重复的,即我们不需要指定函数参数的个数,可以向函数传入可变长度参数列表。

Scala 通过在参数的类型之后放一个星号来设置可变参数(可重复的参数)。例如:

object ScalaVarParams {

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

    printParams("1","2","3","4")
  }

  def printParams (params : String*) : Unit = {
    for (s <- params) {
        println(s)
    }
  }
}

Scala 函数 - 默认参数值

object DefaultParam {

  def main(args:Array[String]) : Unit = {
    println(add())
    println(add(7,8))
  }

  def add ( a:Int = 5 , b:Int = 6 ): Int = {
    a + b
  }
}

Scala函数 - 函数嵌套函数

object Factorial {

  def main (args : Array[String]) : Unit = {
    println(calculate(9))
  }

  def calculate( i: Int ) :Int = {
    def add ( a:Int ) : Int = {
      a + 3
    }

    def subtract (s:Int) : Int = {
      s - 1
    }

    val tmp = add(i)
    subtract(tmp)
  }
}

部分参数应用函数

如果函数传递所有预期的参数,则表示已完全应用它。如果只传递几个参数并不是全部参数,那么将返回部分应用的函数。这样就可以方便地绑定一些参数,其余的参数可稍后填写补上。

object PartsParam {

  def main(args:Array[String]) : Unit = {
    val partAdd = add(1 , _ : Int) ;
    println( partAdd ( 2 ) )
    println( partAdd ( 3 ) )
  }

  def add (a : Int , b : Int) : Int = {
    a + b
  }
}

Scala 指定函数参数名

一般情况下函数调用的参数列表,就按照函数定义时的参数顺序一个个传递。但是我们也可以通过指定函数参数名,并且不需要按照顺序向函数传递参数,实例如下:

object Test {
   def main(args: Array[String]) {
        printInt(b=5, a=7);
   }
   def printInt( a:Int, b:Int ) = {
      println("Value of a : " + a );
      println("Value of b : " + b );
   }
}

高阶函数

高阶函数(Higher-Order Function)就是操作其他函数的函数。
Scala 中允许使用高阶函数,高阶函数可以使用其他函数作为参数,或者使用函数作为输出结果。

def main(args:Array[String]) : Unit = {
    sayHello(getName,100)/*output:100*/
    sayHello(getName,101)/*output:100*/
}

//  高阶函数将其他函数作为参数
def sayHello(f:Int => String,p:Int): Unit ={
    println("hello " + f(p))
}

def getName(id:Int): String ={
    if ( id == 100 ) {
      "张三"
    } else {
      "无名氏"
    }
}

匿名函数

Scala中定义匿名函数的语法很简单,箭头左边是参数列表,右边是函数体。

object AnonymousFunction {
  var add = (x:Int) => x+1
  def main(args:Array[String]) : Unit = {
    println(add(1))
  }
}

函数柯里化(Currying)

柯里化(Currying)指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数为参数的函数。

/*
 *scala的柯里化
 */
object ScalaCurrying  extends App{
    //定义了一个求和函数
    def add(a:Int,b:Int)=a+b
    // 那么我们在调用的时候,应该是add(1,2)
    // 现在我们将这个函数变形
    def add(a:Int)(b:Int)=a+b
    // 那么我们在调用的是后应该是add(1)(2),其结果还是等于3,这种方式(或过程)就叫做柯里化,
    // 经过柯里化之后函数通用性降低,但是适用性有所提高
    //分析下其演变过程
    def add(a:Int): Int => Int={
      (b:Int)=>a+b
    }
    //(b:Int)=>a+b 为一个匿名函数,也就意味着add方法的返回值为一个匿名函数,
    // 那么现在的调用过程为
    var result=add(1)
    var sum1=result(2)
 
}

“闭包”函数

闭包是一个函数,返回值依赖于声明在函数外部的一个或多个变量。
闭包函数通常来说可以简单的认为是可以函数外部局部变量的一个函数。

def main(args: Array[String]) {
    println( "multiplier(1) value = " +  multiplier(1) )
    println( "multiplier(2) value = " +  multiplier(2) )
}
var factor = 3
val multiplier = (i:Int) => i * factor  //闭包函数

Scala数组

数组的定义

  1. 一维数组语法格式如下:

    var 数组变量名:Array[元素类型] = new Array[元素类型](数组大小)
    或
    var 数组变量名 = new Array[元素类型](数组大小)
    或
    var 数组变量名 = Array (数组元素列表)
    

    这里的数组大小是可以省略的,如果不写就表示不定长数组。

  2. 多维数组语法格式如下:
    多维数组 一个数组中的值可以是另一个数组,另一个数组的值也可以是一个数组。

    var 数组变量名 = ofDim[元素类型](数组大小,数组大小,...)
    

数组的使用:

def main (args:Array[String]) : Unit = {
    val array1 = new Array[String](5) ;
    val array2 = Array("1","2","3") ;

    // 输出数组元素
    println(array1.toBuffer) ;
    println(array2.toBuffer) ;

    // 访问指定下标位置的元素
    println(array2(0))
    println(array2(1))
    println(array2(2))

    // 元素赋值
    array1(0) = "0"
    array1(1) = "1"
    array1(2) = "2"
    array1(3) = "3"
    array1(4) = "4"

    println(array1.toBuffer)

    // 创建变长数组
    val array3 = ArrayBuffer[Int]()
    array3 += 1  // 数组元素末尾追加元素1
    array3 += (2,3,4,5)
    array3 ++= Array(6,7)

    array3.foreach(println)

    println("+++++++++++++")
    // 指定位置插入元素
    array3.insert(0,250)
    array3.foreach(println)

    println("-------------")

    // 指定位置移除元素
    array3.remove(0)
    array3.foreach(println)

    println("-------数组遍历------")
    // 数组遍历
    array2.foreach(println)
    for( a <- array2) {
      println(a)
    }

    var total = 0 ;
    for ( i <- array3.indices) {
      total += array3(i)
    }

    println( "total:" + total) ;

    var max = 0 ;
    for ( i <- 1 to (array3.length - 1) ) {
      if (array3(i) > max) max = array3(i)
    }
    println("max:" + max)

    println("+++++++ 数组合并 ++++++")
    // 数组合并
    var array4 = concat(array1 , array2)
    array4.foreach(print)
}

集合

Scala的集合有三大类:序列 Seq、集 Set、映射 Map,所有的集合都扩展自 Iterable特质。在 Scala中集合有可变(mutable)和不可变(immutable)两种类型,immutable类型的集合初始化后就不能改变了。不过,我们仍然可以模拟添加,移除或更新操作。但是这些操作将在每一种情况下都返回一个新的集合,同时使原来的集合不发生改变。

List集合

集合的定义:

// 字符串列表
val site: List[String] = List("Runoob", "Google", "Baidu")

// 整型列表
val nums: List[Int] = List(1, 2, 3, 4)

// 空列表
val empty: List[Nothing] = List()
val list2 = Nil 

// 二维列表
val dim: List[List[Int]] =
   List(
      List(1, 0, 0),
      List(0, 1, 0),
      List(0, 0, 1)
   )

集合的操作:

    // LIST的定义
    val list1 = List (1,2,3) ;

    // Head返回列表第一个元素
    println("list1.head:" + list1.head)
    // tail返回集合中除了第一个元素之外的其他全部元素。
    println("list1.tail:" + list1.tail)
    // 判断集合是否为空
    println("list1.isEmpty:" + list1.isEmpty)

    // 集合连接  可以使用 ::: 运算符或 List.:::() 方法或 List.concat() 方法来连接两个或多个列表
    val list2 = List(5,6,7)

    val list3 = list1 ::: list2 ;
    println("list1 ::: list2 :" + list3)

    val list4 = list2.:::(list1)
    println("list2.:::(list1) :" + list4)

    val list5 = List.concat(list1 , list2)
    println(" List.contract(list1 , list2) :" + list5)

    // :: 操作符是将给定的头和尾创建一个新的列表
    val list6 = list1.:+(9)
    val list7 = 10+:list1

Set集合

Scala Set(集合)是没有重复的对象集合,所有的元素都是唯一的。

Scala集合分为可变的和不可变的集合。

默认情况下,Scala使用的是不可变集合,如果你想使用可变集合,需要引用scala.collection.mutable.Set包。默认引用的是scala.collection.immutable.Set

Set的操作:

// ****** 可变SET ******//
val set: mutable.HashSet[Int] = new mutable.HashSet[Int]()
println(set.getClass.getName)
set+=4
set.add(5) // 追加元素
set++= Set (6,7,8) // 添加另一个set中元素
set-=4

set.remove(6)

set.foreach(print)

println()

// ****** 不可变SET *****//
val set2 = Set(1,2,3)
// 不可变SET 也有添加或删除操元素操作。但是,每次会产生一个新的SET,原来的SET 并没有改变。
val set3 = set2.drop(1)
set2.foreach(print)
println() 
set3.foreach(print)

Map集合

默认情况下 Scala 使用不可变 Map。如果你需要使用可变Map,你需要显式的引入import scala.collection.mutable.Map类;不可变的直接使用 Map,可变的使用mutable.Map

object TestMap {

  def main(args: Array[String]): Unit = {
      // ********** Map定义 ********* //
      val map1:Map[String,Int] = Map()
      val map2 = Map("A" -> 1 , "B" -> 2 , "C" -> 3)
      println(map1)
      println(map2)

      // *********** Map操作 ******** //
      println( "map1 中的键为 : " + map2.keys )
      println( "map1 中的值为 : " + map2.values )
      println( "检测 map1 是否为空 : " + map2.isEmpty )

      // ******** Map合并 *********** //
      val map3 = Map( "C" -> 3 , "D" -> 4)

      val map4 = map2 ++ map3
      val map5 = map2.++(map3)

      println("map4 = map2 ++ map3:" + map4)
      println("map5 = map2.++(map3):" + map5)

     // ******** 删除元素 ******* //
     val map6 = map5 - ("C")
     val map7 = map5.-("A","B")
     println("map5 - (\"C\"):" + map6)
     println("map5.-(\"A\",\"B\"):" + map7)
  }
}

元组

与列表一样,元组也是不可变的,但与列表不同的是元组可以包含不同类型的元素。

元组定义:

val t = (1, 3.14, "Fred");
// 或
val t = new Tuple3(1, 3.14, "Fred");

元组的实际类型取决于它的元素的类型,比如 (99, "runoob") 是 Tuple2[Int, String]。 ('u', 'r', "the", 1, 4, "me") 为 Tuple6[Char, Char, String, Int, Int, String]。

目前 Scala 支持的元组最大长度为22。对于更大长度你可以使用集合,或者扩展元组。

object TestTuple {
  def main(args: Array[String]): Unit ={
    val tuple3 = (1, 3.14, "Fred") ;
    val tuple2 = new Tuple2(1, "Fred")
    val tuple4 = new Tuple4(1, 6.66, "Fred",'C')

    println(tuple3)
    println(tuple2)
    println(tuple4)

    val t = (4,3,2,1)
    val sum = t._1 + t._2 + t._3 + t._4  //使用._n 访问元组中的第N个元素
    println( "元素之和为: "  + sum )
    t.productIterator.foreach{ i =>println("Value = " + i )}  // 元组遍历
    println("连接后的字符串为: " + t.toString())

    println("t.swap" + tuple2.swap) // 二元组中 元素交换

  }
}

集合操作

map, flatten, flatMap, filter, sorted, sortBy, sortWith, grouped, fold(折叠), foldLeft, foldRight, reduce, reduceLeft, aggregate, union, intersect(交集), diff(差集), head, tail, zip, mkString, foreach, length, slice, sum

object TestCollectionOperation {

  def main(args:Array[String]) : Unit = {
    // ******** Map操作 *******//
    val arr = Array(1,5,2,3,4)
    val arrMap1 = arr map((x:Int) => x * 2)
    val arrMap2 = arr.map(x => x * 2)
    val arrMap3 = arr.map(_ * 2)
    arrMap1.foreach(print)
    println()
    arrMap2.foreach(print)
    println()
    arrMap3.foreach(print)

    // ******* flatten操作 *****//
    val words=Array("tom jack oliver jack","hello tom oliver tom ")
    // 将words中元素按照“,”切分
    val splitWords: Array[Array[String]] = words.map(x=>x.split(" "))
    //此时数组中的每个元素进行split操作之后变成了Array,
    // flatten是对splitWords里面的元素进行扁平化操作
    val flatten: Array[String] = splitWords.flatten
    flatten.foreach(print)
    println()

    // ******* flatMap操作 ******* //
     // 上诉两步操作可以等价于flatmap,意味着先map操作之后进行flatten操作
     val result=words.flatMap(_ .split(" "))
     result.foreach(print)
    println()
    // **** filter操作 ****//
    val filterArr = arr.filter( x => x == 2)
    filterArr.foreach(print)

    // **** sorted操作 ****//
    arr.sorted.foreach(print) //升序排序
    println()

    // ***** sortBy操作 ****//
    arr.sortBy( x => -x).foreach(print)

    val wordTuple=List(("a",3),("b",5),("c",2))

    wordTuple.sortBy( t => t._2).foreach(print)
  }
}

Scala 类和对象

类定义

Scala中的类不声明为public,一个Scala源文件中可以有多个类。
Scala中的类定义可以有参数,称为类参数。类参数在整个类中都可以访问。
Scala中也可以使用new关键字来创建类的对象。

实例:

import java.io._

class Point(xc: Int, yc: Int) {
   var x: Int = xc
   var y: Int = yc

   def move(dx: Int, dy: Int) {
      x = x + dx
      y = y + dy
      println ("x 的坐标点: " + x);
      println ("y 的坐标点: " + y);
   }
}

object Test {
   def main(args: Array[String]) {
      val pt = new Point(10, 20);

      // 移到一个新的位置
      pt.move(10, 10);
   }
}

类成员变量 | 构造器

  1. 如果你没有定义构造器,类会有一个默认的空参构造器。
  2. scala中 var类型的类成员变量默认对外提供setter、getter方法。val类型的类成员变量默认对外提供getter方法,不提供setter方法。
   class Student {
     // _表示一个占位符,编译器会根据你变量的类型赋予相应的初始值
     var name:String=_
     // 错误代码,val修饰的变量不能使用占位符
     // val age:Int=_
     val age:Int=10
   }

   object test{
     def main(args: Array[String]): Unit = {
       // 空参构造器可以加()也可以不加
       val student = new Student()
       student.name="JackMa"
       //  错误代码,类中使用val修饰的变量不能更改
       //  student.age=20
       println(s"name=${student.name},age=${student.age}")
     }
   }
  1. scala中构造器分为主构造器和辅助构造器。在类定义后面跟着的参数列表为类主构造器,在类里定义的构造器为辅助构造器,一个类可以有多个辅助构造器。
   /*
    * 定义在类名称后面的构造器为主构造器
    * 类的主构造器中的属性会定义成类的成员变量
    * 类的主构造器中属性没有被var|val修饰的话,该属性不能访问,相当于对外没有提供get方法
    * 如果属性使用var修饰,相当于对外提供set和get方法
    */
   class Student1(var name:String,val age:Int){ // 主构造器
     var gender:String=_
     def this(name:String,age:Int,gender:String){ // 辅助构造器
       this(name,age)
       this.gender=gender
     }
   }

   object test{
     def main(args: Array[String]): Unit = {
       val s1 = new Student1("Tom",18)
       println(s"name=${s1.age},age=${s1.age},gender=${s1.gender}")
       val s2 = new Student1("JackMa",20,"man")
       println(s2.gender)
       // output:
       // name=18,age=18,gender=null
       // man
     }
   }

访问权限

  1. 构造器的访问权限
   /*
    * private 加在构造器之前,这说明该类的构造器是私有的,外部对象或者外部类不能访问。伴生类可以访问
    */
   class Student2 private(var name:String,val age:Int){
     var gender:String=_
     private def this(name:String,age:Int,gender:String){
         this(name,age)
         this.gender=gender
     }
   }
  1. 成员变量的访问权限
   /*
    * age加上private 也就意味着age只能在这个类的内部及其伴生类中可以修改
    */
   class Student3 private(){
      private var name:String=_
      // 伴生类可以访问
      private var age:Int=_
      // private [this]关键字标识给属性只能在类内部访问,伴生类不能访问
      private [this] var gender:String="man"
   }
   // 伴生类
   object Student3 {
      def main(args: Array[String]): Unit = {
        val student3 = new Student3()
        // 伴生对象可以访问
        student3.name = "jack"
        student3.age = 20
        println(s"name=${student3.name},age=${student3.age}")
        // 伴生类不能访问
        //println(student.gender)
      }
   }
  1. 类包的访问权限
   /*
    * private [this] class放在类声明最前面,是修饰类的访问权限,也就是说类在某些包下可见或不能访问
    * private [sheep] class代表该类在sheep包及其子包下可见,同级包不能访问
    */
   private [this] class Student4 (val name:String,private var age:Int){
     var gender :String=_
     // error : age is already defined in the scope
     //  var age:Int = _ ;
   }
   // 伴生类
   object Student4{
     def main(args: Array[String]): Unit = {
       val s = new Student4("JackMa",18)
       print(s.age)
     }
   }

伴生类 | apply方法

在scala 中,是没有static这个东西的,但我们也可以使用关键字object去定义单例对象。

在 Scala 中,当单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象。必须 在同一个源文件里定义类和它的伴生对象。类被称为是这个单例对象的伴生类。类和它的伴生对象可以互相访问其私有成员。

scala中的object相当于java中的public static class,意思是object中成员都是静态的,不需要创建实例对象就可以被虚拟机直接调用。所以scala中的main方法都是写在object类中的。

// 私有构造方法
class Marker private(val color:String) {
  println("创建" + this)
  override def toString(): String = "颜色标记:"+ color
}

// 伴生对象,与类名字相同,可以访问类的私有属性和方法
object Marker{
    private val markers: Map[String, Marker] = Map(
      "red" -> new Marker("red"),
      "blue" -> new Marker("blue"),
      "green" -> new Marker("green")
    )
    def apply(color:String) = {
      if(markers.contains(color)) markers(color) else null
    }
    def getMarker(color:String) = { 
      if(markers.contains(color)) markers(color) else null
    }
    def main(args: Array[String]) { 
        println(Marker("red"))  
        // 单例函数调用,省略了.(点)符号  
        println(Marker getMarker "blue")  
    }
}

Scala 继承

Scala继承跟Java很相似,但我们需要注意以下几点:

  1. 重写一个非抽象方法必须使用override修饰符。
  2. 只有主构造函数才可以往基类的构造函数里写参数。
  3. 在子类中重写超类的抽象方法时,你不需要使用override关键字。
class Person {
  var name = ""
  override def toString = getClass.getName + "[name=" + name + "]"

  def say () :Unit = {
    println("I am person")
  }
}

class Employee extends Person {
  var salary = 0.0
  override def toString = super.toString + "[salary=" + salary + "]"

  override def say(xc: String) :Unit = {
    println("My name is ${name} , i am employer");
  }
}

object TestOverride extends App {
  val fred = new Employee
  fred.name = "Fred"
  fred.salary = 50000
  println(fred)
  fred.say() ;
}

Scala Trait

ScalaTrait 相当于 Java 的接口,实际上它比接口还功能强大。
与接口不同的是,它还可以定义属性和方法的实现,并且还是通过extend关键字实现Trait。
一般情况下Scala的类只能够继承单一父类,但是如果是Trait(特质)的话就可以继承多个,实现了多重继承。
Trait定义的方式与类定义相似,但它使用的关键字是trait。

trait ScalaTrail {
  // 定义了一个属性
  var pro:Int=666

  // 定义一个没有实现的方法
  def sayHello(name:String)

  // 定义了一个带具体实现的方法
  def small(name:String): Unit ={
    println(s"太阳对${name}笑")
  }
}
    
object ScalaTrailImpl extends ScalaTrail {
  // 实现方法时可以有override关键字,也可以没有
  def sayHello(name: String): Unit = {
    println(s"hello $name")
  }
  // 重写方法时必须得有override关键字
  override def small(name: String): Unit = {
    println(s"$name like small")
  }
}

object TestTrait extends App {
  ScalaTrailImpl.sayHello("Oliver")
  // 如果ScalaTrailImpl没有重写small方法,则调用ScalaTrail中已经实现了的方法
  // 如果ScalaTrailImpl重写了small方法,则调用的是ScalaTrailImpl中的方法
  ScalaTrailImpl.small("wang")
  // output:
  // hello Oliver
  // wang like small
}

抽象类

在 Scala 中,使用 abstract 修饰的类称为抽象类. 在抽象类中可以定义属性、未实现的方法和具体实现的方法。

abstract class Animal{
  // 定义了一个属性
  var name:String="animal"
  // 定义一个未实现方法
  def sleep()
  // 定义一个带具体实现方法
  def eat(f:String): Unit ={
    println(s"eating $f")
  }
}

type关键字

Scala里的类型,除了在定义 class,trait,object时会产生类型,还可以通过 type 关键字来声明 类型。
type 相当于声明一个类型别名:

object test extends App {
   // 把String类型用S代替
  type S = String
  var name : S= "Oliver"
  println(name)
}

样例类/样例对象

/*
 * 样例类,使用case关键字修饰的类,其重要的就是支持模式匹配
 * 样例类:case class 类名(属性)
 * 类名定义必须是驼峰式,属性名称第一个字母小写
 */
case class Message(sender:String,massageContent:String)
/*
 * 样例对象不能封装数据
 */
case object CheckHeartBeat

枚举类

Scala中的枚举类继承自Enumeration

object EnumTest {
  /***
    * 定义一个星期的枚举
    */
  object WeekDay extends Enumeration{
    type WeekDay = Value  //声明枚举对外暴露的变量类型
    val Mon = Value("1")
    val Tue = Value("2")
    val Wed = Value("3")
    val Thu = Value("4")
    val Fri = Value("5")
    val Sat = Value("6")
    val Sun = Value("7")
    def checkExists(day:String) = this.values.exists(_.toString==day) //检测是否存在此枚举值
    def isWorkingDay(day:WeekDay) = ! ( day==Sat || day == Sun) //判断是否是工作日
    def showAll = this.values.foreach(println) // 打印所有的枚举值
  }
 
  def main(args: Array[String]): Unit = {
 
    println(WeekDay.checkExists("8"))//检测是否存在
 
    println(WeekDay.Sun==WeekDay.withName("7"))//正确的使用方法
 
    println(WeekDay.Sun=="7")//错误的使用方法
 
    WeekDay.showAll //打印所有的枚举值
 
    println(WeekDay.isWorkingDay(WeekDay.Sun)) //是否是工作日
 
  }
 
}

模式匹配 match case

一个模式匹配包含了一系列备选项,每个都开始于关键字 case。每个备选项都包含了一个模式及一到多个表达式。箭头符号 => 隔开了模式和表达式。match 对应 Java 里的 switch,但是写在选择器表达式之后。即:选择器 match {备选项}

所以,scala中的match case 类似于Java中的swtich case break ; default

match 表达式通过以代码编写的先后次序尝试每个模式来完成计算,只要发现有一个匹配的case,剩下的case不会继续匹配。

值匹配/类型匹配

object TestMatchCase extends App {
   /*
    * 字符串等值匹配
    */
  def contentMatch(str:String)=str match {
    case "dog"=>println("小狗")
    case "cat"=>println("小猫")
    case "1"=>println("数字1")
    case _ => println("匹配失败")
  }
  contentMatch("cat")
  contentMatch("1")
  contentMatch("2")
  // output:
  //  小猫
  //  数字1
  //  匹配失败

  /*
   * 匹配类型
   */
  def typeMatch(ele:Any)=ele match {
    case x:Int=>println(s"Int:${x}")
    case y:Double=>println(s"Double:${y}")
    case z:String=>println(s"String:${z}")
    case _ =>println("match failure")
  }
  typeMatch("hello")
  typeMatch(2)
  typeMatch(2d)
}

数组匹配

object TestMatchCase extends App {
   /*
    * 匹配数组
    */
  def arrayMatch(arr:Any)=arr match {
    case Array(0)=>println("只有一个0元素的数组")
    case Array(0,_)=>println("以0开头,拥有两个元素的数组")
    case Array(1,_,3)=>println("以1开头,3结尾的任意三个元素的数组")
    case Array(_*)=>println("N个元素的数组")
  }
  arrayMatch(Array(0))
  arrayMatch(Array(0,2))
  arrayMatch(Array(1,true,3))
  arrayMatch(Array(1,3,5,7,9))
  // output:
  //  只有一个0元素的数组
  //  以0开头,拥有两个元素的数组
  //  以1开头,3结尾的任意三个元素的数组
  //  N个元素的数组
}

集合匹配

object TestMatchCase extends App {
  /*
   * 匹配集合
   *    Nil表示一个空的list,与list中的元素类型无关,他可以同时表示List[任意类型]的空集合。
   */
  def listMatch(list:Any)=list match {
    case 0::Nil=>println("只有0元素的集合")
    case 7::9::Nil=>println("只有7和9两个元素的集合")
    case x::y::z::Nil=>println(s"只有三个元素集合${x},${y},${z}")
    case m::n=>println(s"拥有head和tail的集合。head:${m},tail:${n}")
  }
  listMatch(List(0))
  listMatch(List(7,9))
  listMatch(List(1,2,3))
  listMatch(List(8,7,6,5,4))
  // output:
  //  只有0元素的集合
  //  只有7和9两个元素的集合
  //  只有三个元素集合1,2,3
  //  拥有head和tail的集合。head:8,tail:List(7, 6, 5, 4)
}

元组匹配

object TestMatchCase extends App {
  /*
   * 匹配元组
   */
  def tupMatch(tup:Any)=tup match {
    case (3,x,y)=>println("第一个元素为3的元组")
    case (_,2)=>println("第二个元素为2,拥有两个元素的数组")
    case (x,y,z)=>println("拥有三个元素的任意元组")
  }
  tupMatch((3,2,1))
  tupMatch((3,2))
  tupMatch((4,2,1))
  // output:
  //   第一个元素为3的元组
  //   第二个元素为2,拥有两个元素的数组
  //   拥有三个元素的任意元组
}

样例类

使用了case关键字的类定义就是样例类(case class),样例类是种特殊的类,经过优化以用于模式匹配。

case object CheckTimeOutTask
case class SubmitTask(id:String,name:String)
case class HeartBeat(time:Long)
object test extends App{
  /*
   * 匹配样例类,样例对象
   */
  def coMatch(ele:Any)=ele match {
    case SubmitTask(id,name)=>println(s"submit task-id:${id},task-name:${name}")
    case CheckTimeOutTask=>println("checking.....")
    case HeartBeat(time)=>println(s"time is ${time}")
  }
  coMatch(SubmitTask("001","node1"))
  coMatch(CheckTimeOutTask)
  coMatch(HeartBeat(8888888L))
  coMatch(HeartBeat(6666))
  // output:
  //  submit task-id:001,task-name:node1
  //  checking.....
  //  time is 8888888
  //  time is 6666
}

正则表达式

import scala.util.matching.Regex

object ScalaTest extends App {
  val pattern = "Scala".r  // String 类的 r() 方法构造了一个Regex对象。
  val str = "Scala is Scalable and cool"

  println(pattern findFirstIn str) // 使用 findFirstIn 方法找到首个匹配项
  println((pattern findAllIn str).mkString(","))   // 使用 findAllIn 方法获得全部的匹配项。 mkString方法来连接正则表达式匹配结果的字符串

  val pattern2 = new Regex("(S|s)cala")  // 首字母可以是大写 S 或小写 s
  val str2 = "Scala is scalable and cool"
  println((pattern2 findAllIn str2).mkString(","))   // 使用 findAllIn 方法获得全部的匹配项。 mkString方法来连接正则表达式匹配结果的字符串

  val pattern3 = "(S|s)cala".r
  val str3 = "Scala is scalable and cool"

  println(pattern3 replaceFirstIn(str3, "Java")) // 使用 replaceFirstIn 方法来替换第一个匹配项
  println(pattern3 replaceAllIn(str3, "Java")) // 使用 replaceAllIn 方法来替换全部匹配项

}

异常处理

Scala中的异常和Java中的异常基本相似;只有捕捉异常的catch子句,语法与其他语言中不太一样。在Scala里,借用了模式匹配的思想来做异常的匹配。因此,在catch的代码里,是一系列case字句。

import java.io.FileReader
import java.io.FileNotFoundException
import java.io.IOException

object Test {
   def main(args: Array[String]) {
      try {
         val f = new FileReader("input.txt")
      } catch {
         case ex: FileNotFoundException => {
            println("Missing file exception")
         }
         case ex: IOException => {
            println("IO Exception")
         }
      } finally {
         println("Exiting finally...")
      }
   }
}

泛型

/*
 * 定义了一个泛型类
 */
class Massage[T]{
  def getMassage(s:T): Unit ={
    println(s)
  }
}
/*
 * 子类继承指明类型
 */
class strMassage[String] extends Massage{

}
object TestT extends App {
  val msg = new Massage[String]
  msg.getMassage("有内鬼,终止交易")
}
  1. [T]定义泛型
  2. 上界:<T extends Test><? extends Test>
  3. 下界:[T <: Test][_ <: Test]
posted @ 2019-12-08 20:31  做个有梦想的咸鱼  阅读(433)  评论(0编辑  收藏  举报