一、关于Scala安装

https://www.scala-lang.org/download/

 

如果你是Java程序员,想学习Scala,请看官网提供的快速入门:

https://docs.scala-lang.org/tutorials/scala-for-java-programmers.html

 

二、Scala语言Hello World的编写

object HelloWorld{
    def main(args : Array[String]){
        println("Hello World")   //Scala每行不强求使用使用;
    }
}

Java程序员应该熟悉这个程序的结构:它包含一个调用main命令行参数的方法,一个字符串数组作为参数; 此方法的主体包含对预定义方法的单个调用println ,其中"Hello World"作为参数。该main方法没有返回值(它是一个过程方法)。因此,没有必要声明返回类型。

Java程序员不太熟悉的是object 包含该main方法的声明。这样的声明引入了通常所说的单例对象即具有单个实例的类。因此,上面的声明声明了一个被调用的类HelloWorld和该类的一个实例,也被称为HelloWorld。该实例是在第一次使用时按需创建的。

精明的读者可能已经注意到该main方法未在static此处声明。这是因为Scala中不存在静态成员(方法或字段)。Scala程序员不是定义静态成员,而是在单例对象中声明这些成员。

 

编译

如果我们将上述程序保存在一个名为的文件中 HelloWorld.scala,我们可以通过发出以下命令来编译它(大于号>表示shell提示符):我们使用scalacScala编译器,它生成的目标文件是标准的Java类文件。

> scalac HelloWorld.scala

运行

使用该scala命令运行Scala程序。它的用法与java用于运行Java程序的命令非常相似,并且接受相同的选项。

> scala HelloWorld

Hello World

 

三、Scala入门

3.1    val 和 var 的区别

val:值          (类似于Java里面的final) 值不可变

标准格式   val 值名称 : 类型 = 值大小

//标准写法 
val age : Int = 20

//写法1 大部分情况Scala可以识别(即省略  :类型)
val age = 20

Scala学习笔记【从0开始深入学习Scala】

 

var:变量     (类似于Java里面的变量) 值可变

标准格式   val 值名称 : 类型 = 值大小

Scala学习笔记【从0开始深入学习Scala】

总结:当工作中用到常量(值不需要改变时)用val , 用到变量(值需要被改变)则用var。

 

 

3.2   Scala八大基本数据类型【类型转换、类型判断】

Scala与Java一样,有八大基本数据类型:

Byte、Char、Short、Int、Long、Float、Double、Boolean 【注意都为大写】

Scala学习笔记【从0开始深入学习Scala】

 

基本使用就是这样子,很简单,但有几个注意点,看下图:

1、数据类型都为大写字母开头,不同于Java的基本数据类型为小写。

Scala学习笔记【从0开始深入学习Scala】

2、如何命名一个Float类型的常量呢?

Scala学习笔记【从0开始深入学习Scala】

信息1:默认为Double    信息2:需要声明为Float类型时,必须在后面加上"f"

当然这样也是可以的:

Scala学习笔记【从0开始深入学习Scala】

3、如何进行基本数据类型的转换呢?————asInstanceOf[转换后的类型]

Scala学习笔记【从0开始深入学习Scala】

4、如何进行基本数据类型的判断呢?————isInstanceOf[是否为该类型]

Scala学习笔记【从0开始深入学习Scala】

 

 

3.3    lazy在Scala中的应用【延迟加载】

如果一个变量或常量声明为lazy,并不会立刻发生计算输出,只有第一次使用到该变量或常量时候,才会计算返回结果

Scala学习笔记【从0开始深入学习Scala】

使用场景:通常使用在耗费资源的计算、占用大量网络/文件IO等情况

Scala学习笔记【从0开始深入学习Scala】

注意当使用lazy时候,如文件路径写错了,并不会立刻报错,而是使用到该变量时候才报错

 

 

3.4    如何使用IDEA整合Maven构建Scala程序

Scala学习笔记【从0开始深入学习Scala】

Scala学习笔记【从0开始深入学习Scala】

下一步---Finish

Plus:为什么在IDEA New中找不到Scala Class呢?

Scala学习笔记【从0开始深入学习Scala】

请在Plugin中点击Install Jetrains plugin... 在里面搜索Scala并下载安装即可,重启IDEA。

Scala学习笔记【从0开始深入学习Scala】

设置Scala-SDK ,添加电脑安装的Scala路径,Scala Class是不是出来拉?

Scala学习笔记【从0开始深入学习Scala】

最后附上scala入门程序的pom.xml配置

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>cn.itcats</groupId>
  <artifactId>myscala</artifactId>
  <version>1.0-SNAPSHOT</version>
  <inceptionYear>2008</inceptionYear>
  <properties>
    <scala.version>2.11.8</scala.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.scala-lang</groupId>
      <artifactId>scala-library</artifactId>
      <version>${scala.version}</version>
    </dependency>
  </dependencies>

  <build>
    <sourceDirectory>src/main/scala</sourceDirectory>
    <testSourceDirectory>src/test/scala</testSourceDirectory>
    <plugins>
      <plugin>
        <groupId>org.scala-tools</groupId>
        <artifactId>maven-scala-plugin</artifactId>
        <version>2.15.2</version>
        <executions>
          <execution>
            <goals>
              <goal>compile</goal>
              <goal>testCompile</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

 

 

四、Scala函数

4.1    方法的定义和使用

Scala学习笔记【从0开始深入学习Scala】

例子说明:

object FunctionTest {
  //main方法 Unit代表无返回值
  def main(args: Array[String]): Unit = {

    println(add1(1, 1))

    println(add2(1, 1))
    //方式3
    println(add3())
    //方式4 (等同于方式3,当无入参时,可以省略括号)
    println(add3)

    sayHello
  }

  //得到 a + b 结果(方式1)
  def add1(a: Int, b: Int): Int = {
    a + b //最后一行就是返回值,不需要手动书写return
  }

  //得到 a + b 结果(方式2)
  //如果只有一行  {} 可以省略
  //另外省略了方法的返回值类型,Scala会猜测返回值类型(Int + Int 那肯定还是Int呀)。实际该方式与方式1相同
  def add2(a: Int, b: Int) = a + b


  //得到 1 + 1 结果(方式3)————无入参
  def add3() = 1 + 1   //省略了方法返回值类型,由于只有一行,省略了{}

  def sayHello: Unit ={
    println("Hello")
  }
  
}

 

 

4.2    默认参数的使用

所谓的默认参数就是:在函数定义时,允许指定参数的缺省值,例子如下:

object FunctionTest1 {
  def main(args: Array[String]): Unit = {
    //不传递name
    showName()      //默认值

    //传递name
    showName("小明") //小明    注意:这个括号不能省略!!!
  }

  //默认参数演示1
  def showName(name : String = "默认值"): Unit ={
    println(name)
  }
}

 

 

4.3    命名参数的使用【不推荐使用】

调用方无序严格按照函数入参的顺序传递参数,只需使用命名参数即可随意调换入参的顺序,例子:

object Function2 {
  def main(args: Array[String]): Unit = {
    //常规调用方式
    speed(100, 10)
    //命名参数调用方式1
    speed(distance = 100, time = 10)
    //命名参数调用方式2 (调换顺序)
    speed(time = 10, distance = 100)
  }

  //演示命名参数
  def speed(distance: Int, time: Int): Unit = {
    println(distance / time)
  }
}

 

 

4.4    可变参数的使用

object Function3 {

  def main(args: Array[String]): Unit = {
    println(sum(1, 2, 3, 4))
  }

  //演示可变参数
  def sum(nums: Int*): Int = {
    var res = 0;
    for (num <- nums) {
      res += num;
    }
    res
  }
  
}

 

 

4.5    条件表达式

object Function4 {
  def main(args: Array[String]): Unit = {
    println(res)   //false
  }

  //演示scala条件表达式
  val x = 0
  val res = if (x > 0) {
    true
  } else {
    false
  }
}

 

 

4.6    循环表达式

scala中的循环表达式分为3类:① to     ②  Range   ③ until  【底层实际上都是scala.collection.immutable.Range】

Scala学习笔记【从0开始深入学习Scala】

可以设置步长【但注意步长不能为0】

Scala学习笔记【从0开始深入学习Scala】

until底层实际上也是调用Range

Scala学习笔记【从0开始深入学习Scala】

在IDEA中如何使用呢?

Scala学习笔记【从0开始深入学习Scala】

输出结果为:   Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

下面将演示for循环、foreach循环、while循环

//循环表达式
object Function5 {
  def main(args: Array[String]): Unit = {}

  //循环演示1 (for)
  for (i <- 1 to 10 if i % 2 == 0) {
    println(i)
  }

  //循环演示2 (for)
  var skills = Array("Hadoop", "Spark", "Storm", "Hive", "HBase", "Kafka")
  for (skill <- skills) {
    println(skill)
  }

  //循环演示3 (foreach)
  //skill实际上是skills里面的每个元素
  // => 实际上是将左边的skill作用到右边的一个函数,输出另外一个结果
  skills.foreach(skill => println(skill))

  //循环演示4 累加100到0的每个数(while)
  var (sum, num) = (0, 100)
  while (num >= 0) {
    sum += num;
    num = num - 1; //scala不能num --   步长
  }
  println(sum)
}

细心观察的朋友可能会发现,scala中的for和while都不需要书写在某个方法体里面即可执行,这不同于Java。

 

 

五、Scala面向对象

5.1    类的定义和使用

plus:占位符_ 的使用【不能使用val

Scala学习笔记【从0开始深入学习Scala】

例子演示:

object Test1 {
  def main(args: Array[String]): Unit = {
      //创建Person对象
    val person = new Person()
    println(person.name + " " + person.age) //null 18
    person.name = "小明"
    //person.age = 19 报错  val age 不可变
    println(person.name + " " + person.age) //小明 18
    println(person.eat)  //调用person的eat()方法   小明is eating
    person.watchMovie("泰坦尼克号")  //小明 is watching 泰坦尼克号
    person.printInfo  //调用person中的printInfo()方法    小明的性别是 male
    // person.gender 报错  private [this] 所修饰的变量只能在其本类访问,故编译不通过s
  }
}

//在scala中定义一个类
class Person{
  //相当于书写了getter/setter(var)
  var name : String = _   //占位符_   必须为var,不能为val,否则报错 String占位符默认为null
  //相当于书写了setter(val)
  val age : Int = 18   //也可以写成 val age = 18

  private [this] val gender = "male"

  def printInfo: Unit ={
    println(name + "的性别是 "+ gender)  //private [this] 可以在本类Person中使用
  }

  //定义方法
  def eat():String = {
    name + "is eating"
  }

  def watchMovie(movieName : String) :Unit = {
    println(name + " is watching " + movieName)
  }

}

 

5.2    构造器【主构造器与附属构造器】

//scala构造器演示
object Constructor {
  def main(args: Array[String]): Unit = {
    val student = new Student("小明", 20)
    println("姓名:" + student.name + " 年龄:" + student.age + " 职业:" + student.occupation) //姓名:小明 年龄:20 职业:学生

    val student2 = new Student("张三", 21, "男")
    println("姓名:" + student2.name + " 年龄:" + student2.age + " 职业:" + student2.occupation) //姓名:小明 年龄:20 职业:学生
  }
}

//主构造器:紧跟在类后面的参数列表  val或var不可省略,否则第行student.属性(省略var或val的报错,无法访问)
class Student(val name: String, val age: Int) {
  println("主构造器执行")
  val occupation: String = "学生"
  var gender: String = _

  //附属构造器
  def this(name: String, age: Int, gender: String) {
    this(name, age) //附属构造器第一行代码必须调用主构造器或其他附属构造器
    this.gender = gender
  }

  println("主构造器结束")
}

运行结果:

主构造器执行
主构造器结束
姓名:小明 年龄:20 职业:学生
主构造器执行
主构造器结束
姓名:张三 年龄:21 职业:学生

 

5.3    继承与重写

继承:

在创建子类对象时候,首先会执行父类的构造方法

object Test2 {
  def main(args: Array[String]): Unit = {
    var an = new Bird("鹦鹉",2,"黑色")
    println(an.name + " "+an.age + " "+ an.color)
  }
}

//父类主构造器
class Animal(val name: String, val age: Int) {
  println("Animal Constructor in")
  println("Animal Constructor out")
}


//子类主构造
//这里需要注意,从父类继承过来的name,age,在子类主构造器中不需要写var/val , 而color没有继承过来,必须要写var/val
class Bird(name: String, age: Int, val color: String) extends Animal(name, age) {
  println("Bird Constructor in")

  println("Bird Constructor out")
}

运行结果:

Animal Constructor in
Animal Constructor out
Bird Constructor in
Bird Constructor out
鹦鹉 2 黑色

 

重写:

对于父类已有的属性和方法,子类重写需要加上override关键字

//重写
object Test3 {
  def main(args: Array[String]): Unit = {
    var apple = new Apple("iPhone Xs",4999.9,"黑色")
    println(apple.toString)
  }
}

//父类主构造器
class Phone(var name: String, val price: Double) {
    val country = "中国"
}


//子类主构造
//这里需要注意,从父类继承过来的name,age,在子类主构造器中不需要写var/val , 而color没有继承过来,必须要写var/val
class Apple(name: String, price: Double, val color: String) extends Phone(name, price) {
  //重写父类的name属性
  override val country = "美国"   //val country = "美国"报错,必须要加override关键字

  //重写toStirng()方法
  override def toString() : String = {
    "name = " + name +" , price = "+ price+" , "+"color = " +color+" , "+"country = " +country
  }
}

输出结果:

name = iPhone Xs , price = 4999.9 , color = 黑色 , country = 美国

 

5.4    抽象类

抽象类使用abstract关键字修饰,且抽象类中[属性、方法]只有定义没有实现,抽象类不能new

object AbstractTest {
  def main(args: Array[String]): Unit = {
    //抽象类不能被new   new Plane() 报错
    var boeing = new Boeing();   //可以new实现类 编译通过
  }
}

/*
 *  类的一个或者多个方法没有完整的实现(只有定义,没有实现)
 */
abstract class Plane {
  def speak

  val name: String
  val age: Int
}

//创建Plane的实现类
class Boeing extends Plane {
  override def speak: Unit = ??? // ???等同于 {}
  override val name: String = "737"  //使用IDEA提示生成的 override val name: String = _ 运行报错 unbound placeholder parameter
  override val age: Int = 2         //override val age: Int = _ 报错unbound placeholder parameter
}

 

 

5.5    伴生类与伴生对象

在 Scala 中,是没有 static 这个东西的,但是它也为我们提供了单例模式的实现方法,那就是使用关键字 object。

Scala 中使用单例模式时,除了定义的类之外,还要定义一个同名的 object 对象,它和类的区别是,object对象不能带参数。

类和它的伴生对象可以互相访问其私有成员。

object ApplyApp {
}

//伴生类与伴生对象
class ApplyTest{ 
}

object ApplyTest{
}

如果有一个class,还有一个与之同名的object,那么就称这个object是是class的伴生对象,class是object的伴生类

其中class ApplyTest是object ApplyTest的伴生类,object ApplyTest是class ApplyTest的伴生对象。

 

 

5.6    apply

object ApplyApp {
  def main(args: Array[String]): Unit = {
    for (i <- 1 to 10) { //循环10次
      ApplyTest.incr
    }
    println(ApplyTest.count) //10 object本身就是一个单例对象
  }
}

//伴生类与伴生对象
class ApplyTest {}

object ApplyTest {
  println("object ApplyTest start")
  var count = 0

  def incr: Unit = {
    count = count + 1
  }

  println("object ApplyTest end")
}

输出结果

object ApplyTest start
object ApplyTest end
10

 

面试高频考点:

object ApplyApp {
  def main(args: Array[String]): Unit = {
    //3.
    val b = ApplyTest()   //==>调用了object.apply()
    //4.
    println("------------分割线------------")
    var c = new ApplyTest
    println(c)
    c()                   //==>调用了class.apply()

    /*总结:
     *        类名()  ==>   object.apply()
     *        对象()  ==>   class.apply()
     *        通常做法是在object中的apply()中最后一行new class
     */
  }
}

//伴生类与伴生对象
class ApplyTest {
  def apply(): Unit ={
    println("class ApplyTest apply")
  }
}

object ApplyTest {
  println("object ApplyTest start")

  //1.在object中书写一个apply()方法 注意此处apply()括号不可省略,否则ApplyTest()报错
  def apply(): ApplyTest ={
    println("object ApplyTest apply")

  //2.在object中的apply()方法内new class
    new ApplyTest
  }

  println("object ApplyTest end")
}

 

 

5.7    case class

//通常用在模式匹配
object CaseClass {
  def main(args: Array[String]): Unit = {
    println(Language("English").name)
  }
}

//特点case class不用new即可使用
case class Language(name : String){    //case class className {}没有参数是不允许的,但可以使用case class className() {}

}

 

 

 

5.8    trait

Scala Trait(特征) 相当于 Java 的接口,实际上它比接口还功能强大。

与接口不同的是,它还可以定义属性和方法的实现。

一般情况下Scala的类只能够继承单一父类,但是如果是 Trait(特征) 的话就可以继承多个,从结果来看就是实现了多重继承。

Trait(特征) 定义的方式与类类似,但它使用的关键字是 trait,如下所示:

trait Equal {
  def isEqual(x: Any): Boolean
  def isNotEqual(x: Any): Boolean = !isEqual(x)
}

以上Trait(特征)由两个方法组成:isEqual 和 isNotEqual。isEqual 方法没有定义方法的实现,isNotEqual定义了方法的实现。子类继承特征可以实现未被实现的方法。所以其实 Scala Trait(特征)更像 Java 的抽象类。

一个类可以扩展多个特质使用with进行连接,如

trait xxx extends ATrait with BbTrait

 

提问:Scala中特质trait与抽象类abstract的区别?

  • 优先使用特质trait。一个类扩展多个特质trait是很方便的,但却只能扩展一个抽象类。
  • 如果你需要构造函数参数,使用抽象类。因为抽象类可以定义带参数的构造函数,而特质不行。例如:
  • trait t(i: Int) {},参数i是非法的。

 

 

六    Scala集合

6.1    数组

不可变长数组[scala.collection.immutable]

Scala学习笔记【从0开始深入学习Scala】

object ArrayApp extends App {

  //创建定长数组
  //方式1 使用new Array[类型](数组长度方式)
  val array1 = new Array[String](3);
  array1.length //长度为3
  array1(1) //获取数组中索引为1的元素
  array1(1) = "haha" //将数组中索引为1的元素改为haha


  //方式2:使用Array object中的apply(),底层帮我们new了Array,返回值类型为Array[T]
  val b = Array("hadoop", "spark", "storm", "flink")

  val c = Array(1,2,3,4,5)
  c.sum
  c.min
  c.max
  //将数组转换为一个字符串
  c.mkString      //12345
  c.mkString(",") // 1,2,3,4,5
  c.mkString("(",",",")")  // (1,2,3,4,5)
}

可变长数组[scala.collection.mutable]

/**
  * 可变长度的数组 ArrayBuffer
  */
object ArrayBufferApp {
  def main(args: Array[String]): Unit = {

    var c = scala.collection.mutable.ArrayBuffer[Int](1,2,3) //创建一个可变长度的数组ArrayBuffer
    c += 4                //ArrayBuffer(1, 2, 3, 4)
    c += (5,6,7)          //ArrayBuffer(1, 2, 3, 4, 5, 6, 7)
    c ++= Array(9,10)     //ArrayBuffer(1, 2, 3, 4, 5, 6, 7, 9, 10)
    c.insert(0,0)   //def insert(index: Int ,elems: A*) 向index位置添加elems(可变)  ArrayBuffer(0, 1, 2, 3, 4, 5, 6, 7, 9, 10)
    c.remove(0,1)  //def remove(n: Int,count: Int) 从索引为n开始,向后删除count个元素  ArrayBuffer(1, 2, 3, 4, 5, 6, 7, 9, 10)
    c.trimEnd(4)   //从尾巴开始删除4个元素    ArrayBuffer(1, 2, 3, 4, 5)
    c.toArray     //将可变数组ArrayBuffer转换为不可变数组Array
    c.remove(1)   //ArrayBuffer(1, 3, 4, 5)
    //遍历方式1
    for(ele <- c){
      println(ele)
    }

    //遍历方式2,使用to Range Until
    for(i <- 0 to c.length - 1) {  //length = 4  c(0)、c(1)、c(2)、c(3)
      print(c(i))
    }


    for(i <- Range(0,c.length)){   //i 0~3
      print(c(i))
    }

    for(i <- 0.until(c.length)){   //i 0~3
      print(c(i))
    }

    //如何将数组倒序输出呢?
    for(i <- (0 until c.length).reverse){  //实际上反转了i的输出顺序
      print(c(i))
    }
  }
}

 

6.2    List【有序可重复有索引的集合】

Scala学习笔记【从0开始深入学习Scala】

Nil就是一个空的不可变的(immutable)的List

Scala学习笔记【从0开始深入学习Scala】

可见:List第一个元素为head,后面的元素都为tail

Scala学习笔记【从0开始深入学习Scala】

头 :: 尾 ,所以最后一条命令 4是head ,List(1,2,3)为tail。

Scala学习笔记【从0开始深入学习Scala】

object ListApp extends App {
  val l = List(1,2,3,4,5) //底层是object List的apply()

  //定义可变的List
  val l1 = scala.collection.mutable.ListBuffer[Int]()   //底层是apply() 所以()括号不能省略
  l1 += 1             //ListBuffer(1)
  l1 += (2,3,4)       //ListBuffer(1, 2, 3, 4)
  l1 ++= List(5,6)    //ListBuffer(1, 2, 3, 4, 5, 6)

  //减少元素
  l1 -= 3             //减去元素3                   ListBuffer(1, 2, 4, 5, 6)
  l1 -= (0,4,6)       //没有0但不报错,没有就不减去0呗  ListBuffer(1, 2, 5)
  l1 --= List(1,5)    //ListBuffer(2)

  l1.toList           //转换为List(不可变长)
  l1.toArray          //转换为Array
  l1.isEmpty
  l1.head
  l1.tail
  println(l1)


  //递归求和算法
  def sum(nums : Int* ):Int = {
    if(nums.length == 0){
      0
    }
    else {
      nums.head + sum(nums.tail:_*)      
    }
  }
}

 

 

6.3    Set【无序不可重复无索引的集合】

操作同List

 

 

6.4    Map

Map(映射)是一种可迭代的键值对(key/value)结构。

所有的值都可以通过键来获取。

Map 中的键都是唯一的。

Map 也叫哈希表(Hash tables)。

Map 有两种类型,可变与不可变,区别在于可变对象可以修改它,而不可变对象不可以。

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

在 Scala 中 你可以同时使用可变与不可变 Map,不可变的直接使用 Map,可变的使用 mutable.Map。以下实例演示了不可变 Map 的应用

// 空哈希表,键为字符串,值为整型
var A:Map[Char,Int] = Map()

// Map 键值对演示
val colors = Map("red" -> "#FF0000", "azure" -> "#F0FFFF")

定义 Map 时,需要为键值对定义类型。如果需要添加 key-value 对,可以使用 + 号,如下所示:

A += ('I' -> 1)
A += ('J' -> 5)
A += ('K' -> 10)
A += ('L' -> 100)

Scala Map 有三个基本操作:

方法 描述
keys 返回 Map 所有的键(key)          Set(key1,key2...)
values 返回 Map 所有的值(value)       MapLike(v1,v2...)
isEmpty 在 Map 为空时返回true            

Map 合并:

object Test {
   def main(args: Array[String]) {
      val colors1 = Map("red" -> "#FF0000",
                        "azure" -> "#F0FFFF",
                        "peru" -> "#CD853F")
      val colors2 = Map("blue" -> "#0033FF",
                        "yellow" -> "#FFFF00",
                        "red" -> "#FF0000")

      //  ++ 作为运算符
      var colors = colors1 ++ colors2
      println( "colors1 ++ colors2 : " + colors )

      //  ++ 作为方法
      colors = colors1.++(colors2)
      println( "colors1.++(colors2)) : " + colors )

   }
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
colors1 ++ colors2 : Map(blue -> #0033FF, azure -> #F0FFFF, peru -> #CD853F, yellow -> #FFFF00, red -> #FF0000)
colors1.++(colors2)) : Map(blue -> #0033FF, azure -> #F0FFFF, peru -> #CD853F, yellow -> #FFFF00, red -> #FF0000)

输出 Map 的 keys 和 values

object Test {
   def main(args: Array[String]) {
      val sites = Map("runoob" -> "http://www.runoob.com",
                       "baidu" -> "http://www.baidu.com",
                       "taobao" -> "http://www.taobao.com")

      sites.keys.foreach{ i =>  
                           print( "Key = " + i )
                           println(" Value = " + sites(i) )}
   }
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
Key = runoob Value = http://www.runoob.com
Key = baidu Value = http://www.baidu.com
Key = taobao Value = http://www.taobao.com

 

 

6.5    Option&Some&None

Scala 程序使用 Option 非常频繁,在 Java 中使用 null 来表示空值,代码中很多地方都要添加 null 关键字检测,不然很容易出现 NullPointException。因此 Java 程序需要关心那些变量可能是 null,而这些变量出现 null 的可能性很低,但一但出现,很难查出为什么出现 NullPointerException。

Scala 的 Option 类型可以避免这种情况,因此 Scala 应用推荐使用 Option 类型来代表一些可选值。使用 Option 类型,读者一眼就可以看出这种类型的值可能为 None。

 

Scala Option(选项)类型用来表示一个值是可选的(有值或无值)。

Option[T] 是一个类型为 T 的可选值的容器: 如果值存在, Option[T] 就是一个 Some[T] ,如果不存在, Option[T] 就是对象 None

// 虽然 Scala 可以不定义变量的类型,不过为了清楚些,我还是
// 把他显示的定义上了
 
val myMap: Map[String, String] = Map("key1" -> "value")
val value1: Option[String] = myMap.get("key1")
val value2: Option[String] = myMap.get("key2")
 
println(value1) // Some("value1")
println(value2) // None

//println(value2.get)  //抛出异常 java.util.NoSuchElementException: None.get
  println(value1.get)    //value

在上面的代码中,myMap 一个是一个 Key 的类型是 String,Value 的类型是 String 的 hash map,但不一样的是他的 get() 返回的是一个叫 Option[String] 的类别。

Scala 使用 Option[String] 来告诉你:「我会想办法回传一个 String,但也可能没有 String 给你」。

myMap 里并没有 key2 这笔数据,get() 方法返回 None。

Option 有两个子类别,一个是 Some,一个是 None,当他回传 Some 的时候,代表这个函式成功地给了你一个 String,而你可以透过 get() 这个函式拿到那个 String,如果他返回的是 None,则代表没有字符串可以给你。

 

getOrElse() 方法【没有值就给默认值,避免抛出异常】

你可以使用 getOrElse() 方法来获取元组中存在的元素或者使用其默认的值,实例如下:

object Test {
   def main(args: Array[String]) {
      val a:Option[Int] = Some(5)
      val b:Option[Int] = None 
      
      println("a.getOrElse(0): " + a.getOrElse(0) )
      println("b.getOrElse(10): " + b.getOrElse(10) )
   }
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
a.getOrElse(0): 5
b.getOrElse(10): 10

isEmpty() 方法

你可以使用 isEmpty() 方法来检测元组中的元素是否为 None,实例如下:

object Test {
   def main(args: Array[String]) {
      val a:Option[Int] = Some(5)
      val b:Option[Int] = None 
      
      println("a.isEmpty: " + a.isEmpty )
      println("b.isEmpty: " + b.isEmpty )
   }
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
a.isEmpty: false
b.isEmpty: true

 

6.6    Tuple(Scala 元组)

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

元组的值是通过将单个的值包含在圆括号中构成的。例如:

val t = (1, 3.14, "Fred")  

以上实例在元组中定义了三个元素,对应的类型分别为[Int, Double, java.lang.String]。

此外我们也可以使用以下方式来定义:

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。对于更大长度你可以使用集合,或者扩展元组。

访问元组的元素可以通过数字索引,如下一个元组:

val t = (4,3,2,1)

我们可以使用 t._1 访问第一个元素, t._2 访问第二个元素,如下所示:

object Test {
   def main(args: Array[String]) {
      val t = (4,3,2,1)

      val sum = t._1 + t._2 + t._3 + t._4

      println( "元素之和为: "  + sum )
   }
}
$ scalac Test.scala 
$ scala Test
元素之和为: 10

迭代元组

你可以使用 Tuple.productIterator() 方法来迭代输出元组的所有元素:

object Test {
   def main(args: Array[String]) {
      val t = (4,3,2,1)
      
      t.productIterator.foreach{ i =>println("Value = " + i )}
   }
}
$ scalac Test.scala 
$ scala Test
Value = 4
Value = 3
Value = 2
Value = 1

 

元组转为字符串

你可以使用 Tuple.toString() 方法将元组的所有元素组合成一个字符串,实例如下:

object Test {
   def main(args: Array[String]) {
      val t = new Tuple3(1, "hello", Console)
      
      println("连接后的字符串为: " + t.toString() )
   }
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
连接后的字符串为: (1,hello,[email protected])

元素交换

你可以使用 Tuple.swap 方法来交换元组的元素。如下实例:

object Test {
   def main(args: Array[String]) {
      val t = new Tuple2("www.google.com", "www.runoob.com")
      
      println("交换后的元组: " + t.swap )
   }
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
交换后的元组: (www.runoob.com,www.google.com)

 

七    Scala模式匹配

7.1    基本数据类型的模式匹配

object BaseMatch extends App {
  val a: Array[String] = Array("a,", "b", "c", "d")
  val name: String = a(Random.nextInt(a.length))
  name match {
    case "a" => println("This is a")
    case "b" => println("This is b")
    case "c" => println("This is c")
    case "d" => println("This is d")
    case _   => println("Can not find it")
  }

  def matchMethod(num : Int) :String = num match {
    case 1 => "This is a"
    case 2 => "This is b"
    case 3 => "This is c"
    case 4 => "This is d"
    case _ => "Can not fiSnd it"
  }
}

7.1.1    条件过滤

object BaseMatch extends App {
  print(matchMethod("haha",4))   //This is d
  def matchMethod(keyword: String, num: Int): String = num match {
    case 1 => "This is a"
    case 2 => "This is b"
    case 3 => "This is c"
    case 4 if (keyword == "haha") => "This is d"
    case _ => "Can not fiSnd it"
  }
}

 

 

7.2    Array模式匹配

def who(arr : Array[String]): Unit = arr match {
    //只匹配数组中含有一个"zhangsan"的case
    case Array("zhangsan") => println("This is zhangsan")
    case Array("zhangsan","lisi") => println("This is zhangsan and lisi")
    //匹配数组中只含三个元素的case(对元素没有要求)
    case Array(x,y,z) => println("This is x,y,z")
    //匹配第一个为wangwu,后可有可无任意多个元素的case
    case Array("wangwu",_*) => println("This is wangwu and others")
    case _ => println("who is it?")
  }

 

 

7.3    List模式匹配

与Array的模式匹配十分相似

def whoList(list : List[String]): Unit = list match {
    //只匹配数组中含有一个"zhangsan"的case
    case "zhangsan"::Nil => println("This is zhangsan")
    case "zhangsan"::"lisi"::Nil => println("This is zhangsan and lisi")
    //匹配数组中只含三个元素的case(对元素没有要求)
    case x::y::z::Nil => println("This is x,y,z")
    //匹配第一个为wangwu,后可有可无任意多个元素的case
    case "wangwu"::tail => println("This is wangwu and others")
    case _ => println("who is it?")
  }

 

 

7.4    Scala异常处理【catch中类型模式匹配】

//Scala中的异常处理
object MyException extends App {
  try{
    val i = 1 / 0; //制造异常
  }catch {
    case e:ArithmeticException => println("除数不能为0")
    case e:Exception => println("出现了未知异常")
  }finally {
    println("关闭资源处理")
  }
}

 

 

7.5    case class模式匹配

object CaseClassMatch extends App {

  def caseMatch(plane : Plane): Unit = plane match {
    case Boeing(name: String, age: Int) => println("This is boeing 787 , age is 2")
    case Airbus(name: String, age: Double) => println("This is airbus 330 , age is 0.8")
    case Other(name: String, age: Int) => println("This is 战斗机 , age is 7")
  }

  caseMatch(Boeing("播音787",2))
  caseMatch(Airbus("空客330",0.8))
  caseMatch(Other("战斗机",7))
}

class Plane

case class Boeing(name: String, age: Int) extends Plane {}

case class Airbus(name: String, age: Double) extends Plane {}

case class Other(name: String, age: Int) extends Plane {}

 

 

7.6    Some&None模式匹配

操作map中的数据,需要频繁地调用get方法

object TestOption {
  def main(args: Array[String]): Unit = {
    val bigDataSkills =
      Map("Java" -> "first",
        "Hadoop" -> "second",
        "Spark" -> "third",
        "storm" -> "forth",
        "hbase" -> "fifth",
        "hive" -> "sixth",
        "photoshop" -> null)
 
    println(bigDataSkills.get("Java") == Some("first"))
    println(bigDataSkills.get("photoshop").get == null)
    println(bigDataSkills.get("Spark").get == "third")
    println(bigDataSkills.get("abc123") == None)
 
  }
}

2、通过模式匹配操作map中的数据,这是一个非常函数化的概念,它允许有效地 “启用” 类型和/或值,更不用说在定义中将值绑定到变量、在 Some() 和 None 之间切换,以及提取 Some 的值(而不需要调用麻烦的 get() 方法)。

object TestOption2 {
 
  def main(args: Array[String]): Unit = {
    val bigDataSkills =
      Map("Java" -> "first",
        "Hadoop" -> "second",
        "Spark" -> "third",
        "storm" -> "forth",
        "hbase" -> "fifth",
        "hive" -> "sixth",
        "photoshop" -> null)
 
    def show(value: Option[String]) =
      {
        value match {
          case Some(a) => a
          case None    => "No this Skill"
        }
      }
 
    println(show(bigDataSkills.get("Java")) == "first")
    println(show(bigDataSkills.get("photoshop")) == null)
    println(show(bigDataSkills.get("Spark")) == "third")
    println(show(bigDataSkills.get("abc123"))== "No this Skill")
 
  }
}

 

 

7.7    类型模式匹配

 //类型模式匹配
  matchType(Map("haha" -> "hehe"))
  def matchType(obj : Any): Unit = {
    obj match{
      case x:Int => println("Int")
      case y:String => println("String")
      case z:Map[_,_] => z.foreach(println)
      case _ => println("unkown")
    }
  }

 

 

八    Scala函数的高级操作

8.1    字符串的高级操作

8.1.1    插值和多行

object StringApp extends App {
  val a: String = "a"
  val b: String = "b"
  //字符串插值操作
  println(s"Hello : $a")  //在字符串前添加s , 在引用到字符串的位置使用$引用即可

  //多行字符串演示(连续输入三个"后按回车)
  val c =
    """
      |Hello
      |World
    """.stripMargin
  println(c)
}

 

 

 

8.2    匿名函数

//匿名函数:函数是可以命名也可以匿名的
object AnonymityApp extends App {
  //命名函数定义
  def hello(name: String): Unit = {
    println(s"Hello : $name")
  }

  //匿名函数可以传递给变量
  val f = (name:String) => println(s"Hello : $name")
  //也可以传递给函数
  def an = (name:String) => println(s"Hello : $name")

  //调用
  hello("a")
  f("b")
  an("c")
}

 

 

8.3    curry函数

object CurryApp extends App {
  //curry函数:将原来接收两个参数的一个函数,转换成2个
  //原函数写法
  def sum(a: Double, b: Double): Double = {
    a + b
  }

  //curry函数
  def currySum(a: Double)(b: Double): Double = {
    a + b
  }

  //调用
  println(   sum(1,1)         )
  println(   currySum(1)(1)   )
}

 

 

8.4    高阶函数【重点掌握】

object HighLevelFunction extends App {
  val l = List(1, 2, 3, 4, 5)

  //1.map 逐个操作集合里面的每个元素
  l.map((x: Int) => x + 1) //将List内的每个元素(x:Int) + 1
  l.map(x => x + 1)        //scala能帮我们推断出List内每个元素是Int,且只有一个元素时候,括号可以省略
  l.map(_ + 1)             //对List中的任意元素都 + 1
  //遍历
  l.map(_ + 1).foreach(print)    //23456 List[Int]

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

  //2.在map取出每个元素 + 1的基础上,使用filter过滤出 < 4的数字
  l.map(_ + 1).filter(_ < 4).foreach(print)   //23  List[Int]

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

  //3.使用take取集合中的前3个元素
  l.take(3).foreach(print)      //123    List[Int]

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

  //4.reduce
  print(  l.reduce(_ + _)  )  //15     Int  将l集合中的前后元素两两相加 1+2 3+3 6+4 10+5

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

  //5.reduceLeft = reduce
  print(  l.reduceLeft(_ - _)  )   //-13  1 - 2 - 3 - 4 - 5 = -13

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

  //6.reduceRight
  print(  l.reduceRight(_ - _)  )  //3   ( 1 - ( 2 - ( 3 - ( 4 - 5 ))))

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

  //7.fold(第一个括号代表初始值)
  print(  l.fold(0)(_ - _)  )    //-15  0-1-2-3-4-5 = -15

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

  //8.foldLeft = fold  (0在最左边)
  print(  l.foldLeft(0)(_ - _))  //-15  0-1-2-3-4-5 = -15

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

  //9.foldRight        (0在最右边)
  print(  l.foldRight(0)(_ - _) ) //3    ( 1 - ( 2 - ( 3 - ( 4 - (5 - 0) ) ) ) )

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

  //10.flatten
  val f = List(List(1,2),List(3,4),List(5,6)) //List内部是一个List类型的元祖
  print(f.flatten)   //flatten可以理解为压扁  List(1, 2, 3, 4, 5, 6)

  //11.flatMap(可以理解为map + flatten)
  println()
  println("-------------")
  //先来看一个例子,将f中List元祖都扩大两倍(使用map) 第一次使用map取出来的是List(x,y) 第二次用map取出来的是(x,y)
  print(f.map(_.map(_ * 2)))   //List(List(2, 4), List(6, 8), List(10, 12))

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

  //flatMap所实现的效果是  List(2, 4, 6, 8, 10, 12)
  print(f.flatMap(_.map(_ * 2)))

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

  //12.flatMap的一些应用
  val t = List("hello,hello,world,hello")
  // t.flatMap(_.split(","))   List(hello, hello, world, hello)
  t.flatMap(_.split(",")).map(x => (x,1)).foreach(println)
  /**
    * 输出:
    *   (hello,1)
        (hello,1)
        (world,1)
        (hello,1)
    */
}

 

 

8.5    偏函数

被包在花括号内没有match的一组case语句

//演示偏函数  被包在花括号内没有match的一组case语句
object PartialFunctionApp extends App {

  //正常的match..case匹配
  def baseMatch(name : String): Unit = name match{
    case "zhangsan" => "This is zhangsan"
    case "lisi" => "This is lisi"
    case _ => "I don't know who is it"
  }

  /*
   * 使用偏函数 def functionName:PartialFunction[入参类型,返回参数类型] = {
   *              case "case1" => "return1"
   *              case "case2" => "return2"
   *              case _       => "return3"
   *          }
   */
  def partialFunction:PartialFunction[String,String] = {
    case "zhangsan" => "This is zhangsan"
    case "lisi" => "This is lisi"
    case _ => "I don't know who is it"
  }

  //调用  传递入参即可
  println(partialFunction("zhangsan"))  //This is zhangsan
}

 

九    Scala隐式转换

为已存在的类添加一个新的方法,在Scala中通常使用隐式转换解决

//隐式转换
object ImplicitApp extends App {
  //普通人只有吃的方法,那么如何将超人飞的方法添加到普通人上呢? ————使用隐式转换
  //套路 implicit def functionName(需要增强的类类型) : 返回含有增强方法的类类型 = new 含有增强方法的类()
  implicit def manTosuperMan(man:Man) : SuperMan = new SuperMan(man.name)

  //创建被增强对象
  var man = new Man("普通人");
  //发现man已包含了fly()方法
  println(  man.fly  )  //superman[普通人] can fly
}

//普通人只会吃
class Man(val name:String){
  def eat(): Unit ={
    println(s"Man[$name] only can eat")
  }
}

//超人会飞
class SuperMan(val name:String){
  def fly(): Unit ={
    println(s"superman[$name] can fly")
  }
}

隐式转换切面封装
创建一个ImplicitAspect类,将隐式转换语句拷贝到该类,最后再import ImplicitAspect._

 

9.1    隐式参数

指的是在函数或者方法中,定义一个用implicit修饰的参数,此时Scala会尝试找到一个指定类型的,用implicit修饰的对象,即隐式值,并注入参数

//隐式参数
object ImplicitParam extends App {
  def implicitTest(implicit name: String): Unit = {
    println(s"Hello : $name")
  }

  implicit val name: String = "zhangsan"
  //implicit val s: String = "s"    输出结果 : Hello : s
  //如果同时书写两个implicit参数,在调用方法时候不传递,则会报错,因为不知道调用哪个参数

  implicitTest   //不传递参数,但结果为   :   Hello : zhangsan
}

 

9.2    隐式类转换

object ImplicitClass extends App{
  implicit class sum(x : Int) {  //被implicit修饰的类,入参是什么类型,就具有该类的方法,如
    def add(a : Int) = a + x
  }
  println(  1.add(3)  )  //4    sum类被implicit修饰,所以sum类构造方法中含有入参Int类型,所以Int类型会存在方法add
}

 

 

十    Scala操作外部数据

包括文件、网络数据、MySQL、XML等外部数据

10.1   操作文件/网络资源

//使用scala操作File文件
object FileTest {
  def main(args: Array[String]): Unit = {
    //读取文件
    var file = scala.io.Source.fromFile("/Users/fatah/Desktop/1.txt")(scala.io.Codec.UTF8);
    //var file = scala.io.Source.fromFile("/Users/fatah/Desktop/1.txt");
    def readLine(): Unit = {
      for (line <- file.getLines()) {
        println(line)
      }
    }
    //调用读取文件资源
    //readLine
    //调用读取网络资源
    readNet
    
    //读取网络数据
    def readNet(): Unit ={
      var file = scala.io.Source.fromURL("https://www.baidu.com");
      for(line <- file.getLines()){
        println(line)
      }
    }
  }
}

 

 

10.2   操作XML

//方式1
    var xml1 = XML.load(this.getClass.getClassLoader().getResource("test.xml"))
    //方式2
    val xml2 = XML.load(new FileInputStream("/Users/fatah/Desktop/Java开发工具/workspace/myscala/src/main/resources/test.xml"))
    println(xml1)

解析XML

Scala XML API提供了类似XPath的语法来解析XML。在NodeSeq这类父类里,定义了两个很重要的操作符("\"和"\\"),用来获得解析XML:

 

在resources下创建test.xml 

  • \ :Projection function, which returns elements of this sequence based on the string that--简单来说,\ 根据条件搜索下一子节点
  • \\:Projection function, which returns elements of this sequence and of all its subsequences, based on the string that--而 \\ 则是根据条件搜索所有的子节点
<fix major="4" minor="2">
  <header>
    <field name="BeginString" required="Y">FIX4.2</field>
    <field name="MsgType" required="Y">Test</field>
  </header>
  <trailer>
    <field name="Signature" required="N"/>
    <field name="CheckSum" required="Y"/>
  </trailer>
  <messages>
    <message name="Logon" msgtype="A" msgcat="admin">
      <field name="ResetSeqNumFlag" required="N"/>
      <field name="MaxMessageSize" required="N"/>
      <group name="NoMsgTypes" required="N">
        <field name="RefMsgType" required="N"/>
        <field name="MsgDirection" required="N"/>
      </group>
    </message>
    <message name="ResendRequest" msgtype="2" msgcat="admin">
      <field name="BeginSeqNo" required="Y"/>
      <field name="EndSeqNo" required="Y"/>
    </message>
  </messages>
  <fields>
    <field number="1" name="TradingEntityId" type="STRING"/>
    <field number="4" name="AdvSide" type="STRING">
      <value enum="X" description="CROSS"/>
      <value enum="T" description="TRADE"/>
    </field>
    <field number="5" name="AdvTransType" type="STRING">
      <value enum="N" description="NEW"/>
    </field>
  </fields>
</fix>

1. 首先来个简单的,如果要找header下的field,那么这样写即可:

val headerField = xml1 \"header"\"field"   //xml1对应的是你之前读xml的变量名

2.找所有的field:

val field = xml1 \\"field"

3. 找特定的属性(attribute),如找header下的所有field的name属性的值:

val fieldAttributes = (xml1 \"header"\"field").map(_\"@name")

val fieldAttributes = xml1 \"header"\"field"\\"@name"

两个都能找到header下面所有field的name属性,但问题是输出的格式不一样。前者会返回一个List-List(BeginString, MsgType),而后者仅仅是BeginStringMsgType。中间连空格也没有。所以建议用前一种方法获得属性。

之前以为,下面的方法,和第二种方法一样能够获得属性的值:

val fieldAttributes = xml1 \"header"\"field"\"@name"

根据\操作符的定义,理论上应该可以输出name属性的。实际上输出的结果是空,什么也没有。

\操作符的源码里有这么一段:

that match {
      case ""                                         => fail
      case "_"                                        => makeSeq(!_.isAtom)
      case _ if (that(0) == '@' && this.length == 1)  => atResult
      case _                                          => makeSeq(_.label == that)
    }

第三个case表面,只有当this.length==1时,才可以这样做。原因其实很简单,somXml\"header"\"field"返回的是一个Seq[Node]的集合,包含多个对象。而\"@"的操作无法确定操作哪一个对象的属性:

val x = <b><h id="bla"/><h id="blub"/></b>
  val y = <b><h id="bla"/></b>
  println(x\\"h"\"@id") //Wrong
  println(y\\"h"\"@id") //Correct with output: bla

4. 查找并输出属性值和节点值的映射:

(xml1 \"header"\"field").map(n=>(n\"@name", n.text, n\"@required"))

这样的输出是List((BeginString,FIX4.2,Y), (MsgType,Test,Y))

5. 有条件地查找节点,例如查找name=Logon的message:

val resultXml1 = (xml1 \\"message").filter(_.attribute("name").exists(_.text.equals("Logon")))

val resultXml2 = (xml1 \\"message").filter(x=>((x\"@name").text)=="Logon")

6. 通过 \\"_" 获得所有的子节点,例如:

println(resultXml1\\"_")
<message msgcat="admin" msgtype="A" name="Logon">
      <field required="N" name="ResetSeqNumFlag"/>
      <field required="N" name="MaxMessageSize"/>
      <group required="N" name="NoMsgTypes">
        <field required="N" name="RefMsgType"/>
        <field required="N" name="MsgDirection"/>
      </group>
</message>
<field required="N" name="ResetSeqNumFlag"/>
<field required="N" name="MaxMessageSize"/>
<group required="N" name="NoMsgTypes">
        <field required="N" name="RefMsgType"/>
        <field required="N" name="MsgDirection"/>
</group>
<field required="N" name="RefMsgType"/>
<field required="N" name="MsgDirection"/>

 

 

10.3    操作MySQL

//使用scala连接数据库,操作MySQL
object MySQLTest {
  def main(args: Array[String]): Unit = {
    //书写连接数据库相关信息
    val url = "jdbc:mysql://localhost:3306/crash_course"
    val username = "root"
    val password = "root"

    //在scala中这一步与Java的class.forName写法有所不同
    try {
      classOf[com.mysql.jdbc.Driver]
      val conn = DriverManager.getConnection(url,username,password)
      val sql = "select * from orders"
      val stmt = conn.prepareStatement(sql)
      val rs = stmt.executeQuery();
      while(rs.next()){
        val order_num = rs.getString("order_num")
        val order_date = rs.getString("order_date")
        val cust_id = rs.getString("cust_id")
        println(s"$order_num , $order_date , $cust_id")
      }
    }catch {
      case e : Exception => e.printStackTrace()
    }finally {
        //关闭资源,懒得关了,懂就行
    }
  }
}

 

相关文章:

  • 2021-12-22
  • 2021-10-17
  • 2020-02-01
  • 2020-02-02
  • 2020-02-10
  • 2020-02-18
  • 2020-05-17
  • 2018-09-28
猜你喜欢
  • 2020-02-15
  • 2021-11-14
  • 2021-09-08
  • 2021-10-16
  • 2021-09-05
  • 2021-12-10
  • 2021-11-22
  • 2018-09-10
相关资源
相似解决方案