Scala Programing Language (一)

概述

Scala是一门专门为计算而生的语言,Scala将(Java或者C++ )面向对象和函数式编程结合在一起。其中函数式编程强调的是通过传递算子(代码|函数)实现大规模的数据集的计算。Scala虽然是一门独立的编程语言,但是它可以无缝的和Java语言对接,Scala编译的代码可以直接运行在Java虚拟机(JVM)上。

语法

一、变量\常量

语法 : var|val 变量名[:类型] = 值
注意 :
	1. Scala中的值类型包括Byte、Short、Char、Int、Long、Float、Double、Boolean
	2. Scala是纯面向对象语言,遵守"万物皆对象"的设计原则,因此Scala中值类型也是对象,没有Java中的基本数据类型
	3. Scala中所有的值类型都归属于scala.*包中,无需声明,直接使用
	4. Scala中的Stirng类型使用的是Java中的java.lang.String类型
	5. Scala中值类型的默认类型:整数(Int)、小数(Double)
	6. Scala中的变量类型支持自动推断,因此在定义变量时类型可以不写,不写则使用默认类型
	7. Scala推荐使用常量,即声明时使用val关键字
// 声明变量
var a:Int = 9

// 简写
var a = 9

// 声明常量
val b:String = "DouDou"

// 简写
val b = "DouDou"
类型转换

通俗的讲就是自动类型提升,小值可以赋给大值,但是大值不可以赋给小值

在这里插入图片描述

// 定义Byte类型的变量
scala> var a : Byte = 10
a: Byte = 10

// 将Byte类型的变量赋值给Short类型
// 小类型 给 大类型 所有可以赋值
scala> var b: Short = a
b: Short = 10

// 同样的 Short类型的变量给Int类型 小类型给大类型 赋值成功
scala> var c : Int = b
c: Int = 10

// 将Int类型的变量赋值类Short类型 大类型给小类型赋值
// 编译不通过 ERROR
scala> var d :Short = c
<console>:12: error: type mismatch;
 found   : Int
 required: Short
       var d :Short = c
                      ^
运算符

Scala中支持的运算符包括 :

  1. 赋值运算符 : =
  2. 关系运算符 : >、<、<=、>=、!=、==
  3. 逻辑运算符 : &&、||、!
  4. 位运算符 : &、|、~、^、<<、>>、>>>
  5. 算术运算符 : +、-、*、/、%

注意:

  • Scala中没有 ++-- 的语法,但是有 +=-= 的语法
  • Scala是纯面向对象的 运算符也是值类型中的方法
// 关系运算符
scala> 10 > 8
res3: Boolean = true

// 逻辑运算符
scala>  1==1 && 3>2
res7: Boolean = true

// 位运算符 
// 位运算符采用值的二进制形式进行计算 效率极高
scala> 1&2
res12: Int = 0

// 运算符也是值类型中的方法
scala> 1.+(1)
res1: Int = 2
Scala类型层次结构

在Scala中,所有的值都有类型,包括数值和函数。下图阐述了类型层次结构的一个子集。
在这里插入图片描述

  • Any是所有类型的超类型,也称为顶级类型。它定义了一些通用的方法如equalshashCodetoStringAny有两个直接子类:AnyValAnyRef
  • AnyVal代表值类型。有9个预定义的非空的值类型分别是:DoubleFloatLongIntShortByteCharUnitBooleanUnit(类似于java中的void)是不带任何意义的值类型,它仅有一个实例可以像这样声明:()。所有的函数必须有返回,所以说有时候Unit也是有用的返回类型。
  • AnyRef代表引用类型。所有非值类型都被定义为引用类型。在Scala中,每个用户自定义的类型都是AnyRef的子类型。如果Scala被应用在Java的运行环境中,AnyRef相当于java.lang.Object
  • Nothing是所有类型的子类型,也称为底部类型。没有一个值是Nothing类型的。它的用途之一是给出非正常终止的信号,如抛出异常、程序退出或者一个无限循环(可以理解为它是一个不对值进行定义的表达式的类型,或者是一个不能正常返回的方法)。
  • Null是所有引用类型的子类型(即AnyRef的任意子类型)。它有一个单例值由关键字null所定义。Null主要是使得Scala满足和其他JVM语言的互操作性,但是几乎不应该在Scala代码中使用。我们将在后面的章节中介绍null的替代方案。

二、分支语句

Scala中的分支语句和Java中的分支语句类似,不同的是

  • Scala中所有的表达式都是有返回值的
  • 我们在使用分支语句的时候必须返回一个值,而表达式的最后一行就是该表达式的返回值
  • 如果表达式中没有返回值时,例如打印语句println(),那么表达式的值就是 ()Unit 类型
  • Scala中的表达式可以返回不同类型的值,只要你定义的变量的类型足够大
  • 在Scala中,如果代码块有多行代码时,必须用大括号 {} 包裹起来,如果代码块只有一行内容,则大括号可以省略。
if 语句
// 语法 :
if (布尔表达式){
    // 满足执行
}

//-------------------------------------
scala> val age = 18
age: Int = 18

scala> :paste
// Entering paste mode (ctrl-D to finish)

if(age > 16){
  println("青年")
}

// Exiting paste mode, now interpreting.

青年
if … else … 语句
// 语法 :
if (布尔表达式){
    // 满足执行
}else{
    // 不满足执行
}

//--------------------------------------
scala> :paste
// Entering paste mode (ctrl-D to finish)

if(age>16){
	println("青年")
}else{
	println("未成年")
}
// Exiting paste mode, now interpreting.

未成年
if … else if … else 语句
// 语法 :
if (布尔表达式1){
    // 满足表达式1执行
} else if (布尔表达式2){
    // 满足表达式2执行
} else {
    // 都不满足执行
}

//------------------------------------
// 表达式的最后一行就是返回值
val d = 
if(age>18){
    age + 1
}else if(age>10){
    age + 2
}else{
    age + 3
}
// val d:Int = 16

//------------------------------------
// 当代码块中只有一行代码的时候 可以省略大括号
val f = 
if(age>18)age + 1
else if(age>10) age + 2
else age + 3

// val f:Int = 16

三、循环

while 循环
# 语法:
while(循环条件){
    // 循环体
}
do … while … 循环
# 语法:
do{
    // 循环体
}while(循环条件)
for 循环

Scala 中的For循环相比Java中的For循环更加的简洁

# java 中的 for 循环
for(int i = 0; i<=100;i++){
    // 循环体
}

# scala 中的 for 循环
for(i <- 0 to 100){
    println(i)
}
// 语法1 :
// 1 to 100 得到的是一个数值区间Range [0,100],包含起始和结束数值
for(i <- 0 to 100){
    // 循环体
}

// 语法2 :
// 1 until 100 的到的是一个数值区间Range[0,100),包含起始和不包含结束数值
for(i <- 0 until 100){
    // 循环体
}
嵌套 For 循环

在Scala中多重嵌套的 For 循环可以使用简化写法

在for的 () 中多重循环用 ; 分割即可

for(m <- 1 to 9; n <- 1 to 9){
    // 这里将会执行 81 次
    // m 每执行1次 n 执行9次
}
指定迭代步长

在使用 m to n 的语法时,可以指定中间迭代的步长,默认为1

// 语法 :
// m to n by step

scala> 1 to 10 by 2
res24: scala.collection.immutable.Range = Range(1, 3, 5, 7, 9)

for(n <- 1 to 10 by 2){
    // n : 1, 3, 5, 7, 9
}
if 守卫

在迭代时添加判断条件 决定是否进入代码块

// 通过循环计算 1~10 奇数之和
// 普通写法
var sum = 0
for(n <- 1 to 10){
    if( n % 2 != 0){
        sum += n
    }
}

// if守卫简化实现
var sum = 0
for(n <- 1 to 10 if n % 2 != 0){
    sum += n
}

// sum = 25
break

Java中break表示结束循环

在Scala中没有 breakcontinue 关键字,但是Scala提供了另外一种方式来实现类似于break的功能

// 导入指定的包
import scala.util.control.Breaks._

// 构建breakable代码块
breakable {
  for (n <- 1 to 10) {
    // 这里的break()是一个方法
    // 在scala中如果是一个无参的方法,可以省去(),直接写方法名
    if (n == 5) break()  
    println(n)
  }
}

这种方式底层是通过抛出和捕获异常完成的,运行时效率较低,因此应尽量避免使用这套机制。

yield

for循环中的yield会将表达式的值记录下来,保存在集合中,循环结束后将集合返回

  • 首先会将循环中yield表达式的值记录下来存入缓冲区中
  • 当循环结束时将缓冲区中的所有值存放在一个 Vector 类型的集合中,然后返回集合
  • 新集合中元素的类型和遍历集合的类型是一致的
scala> val newCollection = for(i <- 1 to 3) yield i + 1
newColle: scala.collection.immutable.IndexedSeq[Int] = Vector(2,3,4)

四、函数

函数的声明
// 语法 :
def 函数名(参数列表) : 返回值类型 = {
    // 函数体
}
// 声明普通函数
def sum(x: Int, y: Int): Int = {
    x + y // 自动将代码块的最后一行内容作为返回值 则此时的返回值就是  x + y
}

// 声明普通函数【简化】
// 省略返回值类型 `:Int`  自动推导返回值的类型
def sum2(x: Int, y: Int) = { 
    x + y
}

// 声明普通函数【继续简化】
// 当函数只有一行代码时可以省略括号
def sum2(x: Int, y: Int) = x + y
// 无返回值的函数 返回值声明为Unit类型 类似于Java中void Unit类型的值为()
def sum3(x: Int, y: Int): Unit = {
    println(x + y)
}

// 无返回值的函数【简化】  
// 省略            `: Unit =`
def sum4(x: Int, y: Int) {
    println(x + y)
}

总结:

  • 有返回值的函数,在声明时可以省略 :返回值类型 ,Scala自动根据类型推导返回值类型
  • 无返回值的函数,在声明时可以省略 :Unit= ,Scala会自动认为是无返回值的函数
可变长参数

Java 中声明可变长参数 public void m1(String... args)

Scala中声明可变长参数 def 函数名(args*):返回值类型 = {}

// 与Java中一样,声明的可变长参数必须是参数列表中的最后一个参数
def m2(init: Int, args: Int*) = {
    var default = init
    for (n <- args) {
        default += n
    }
    default
}
参数默认值

Scala中的参数可以有默认值,在使用时:

  • 如果调用的时候有传值,使用传递的值进行计算
  • 如果调用的时候没有传值,使用声明时的默认值进行计算

声明语法 : ( 参数名:参数类型 = 默认值 )

def sum(x: Int, y: Int, z: Int = 10): Int = {
    x+y+z
}
带名参数

带名参数指的是在调用方法时根据参数名传值

语法 : 方法名(参数名称 = 值)

注意 :

  • 带名参数使用时传参的顺序可以随意
  • 带名参数可以和参数默认值一起使用,即有默认值的参数可以不写
// 声明一个函数
def sum2(x: Int, y: Int = 10, z: Int): Int = { 
    x + y + z
}

// 调用函数
println(sum2(x = 1, z = 2))
// 或者这样调用 参数的顺序可以随意 同时变量y使用默认值
println(sum2(z = 2, x = 1))
Lazy值( 懒加载 | 懒值 )

Scala中的常量可以被声明为 懒值 ,即该变量只有在第一次使用时,才会进行初始化

语法 : lazy 常量的声明

package function
import scala.io.Source._
/**
  * 懒加载
  */
object Function04 {
  def main(args: Array[String]): Unit = {
    // 立即进行初始化 程序直接报错
    // val source = fromFile("D://abc.txt")

    // 懒加载的值 如果只是声明而不使用 程序不会报错
    lazy val source2 = fromFile("D://abc.txt")

    // 一旦该值被使用,懒加载的值初始化了 就会报错 因为abc.txt不存在
    source2.mkString
  }
}
递归函数
// 递归函数 和Java的类似
// Scala中代码块的最后一行就是返回值 因此不需要写return关键字
def m1(n:Int):Int = {
    if(n == 1) 1
    else n * m1(n - 1)
}
匿名函数

匿名函数就是没有名字的函数

语法 : 参数列表 => 函数体

  • Scala作为一款函数式编程语言 大量使用了匿名函数

  • Scala中内置了23个匿名函数对象 Function0 ~ Function22 ,Scala严格遵守面向对象设计模式,因此,每一个匿名函数的背后对应一个函数对象

// 示例
(x:Int,y:Int) => x + y
// 这就是一个Function2[Int,Int,Int]对象 该对象表示接受2个Int类型的参数返回Int类型的值的函数
// 以此类推 : 
// Function3 表示接受3个参数的函数对象
// Function4 表示接受4个参数的函数对象
// ...
package function

/**
  * 匿名函数
  */
object Function06 {
  def main(args: Array[String]): Unit = {

    // 匿名函数声明
    // Function2[Int,Int,String] 就是该函数对象的类型
    // 表名此函数接受2个Int类型的参数,返回String类型的值
    val f2: Function2[Int, Int, String] = (x: Int, y: Int) => x + y + " "

    // 匿名函数使用
    println(f2(1, 2))
    println("-------------------------------------")
    // 这里声明了一个Function[Int,Unit]类型的函数对象
    // 表名此函数接受1个Int类型的参数,返回Unit类型的值(没有返回值)
    val f1: Function1[Int, Unit] = (n: Int) => println(n)

    val list = List(1, 2, 3, 4, 5)

    // 将函数对象作为方法的参数
    list.foreach(f1)

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

    list
      .filter((n: Int) => n != 3) // 保留集合中符合条件的数据
      .foreach((n: Int) => println(n)) // 1 2 4 5
  }
}
柯理化(Currying)

柯理化函数是指将接受多个参数到函数,改造为接受一个参数的函数,并且返回一个函数对象的过程。

// 普通函数
def sum(x: Int, y: Int) = {
    x + y
}

// 柯理化函数
// 该函数的返回值是一个函数对象Function1,也就是函数返回函数
def sum2(x: Int) = {
    (y: Int) => x + y
}
// 【简写1】省略括号
def sum2(x:Int) = (y:Int) => x + y

// 【简写2】省略了 `=`    将匿名函数[(y:Int) => x + y] 的连接符 修改为了 = 即可
def sum3(x: Int)(y: Int) = x + y

说明:def sum(x:Int,y:Int) = x + y 接受2个参数 准备完成的计算工作是 x + y

柯理化就是先传入一个参数,使方法中的一个变量的值固定 然后返回一个值固定的函数对象

即 def sum(x:Int) ={ (y:Int) => x + y } 此时调用sum(2) 则返回一个函数对象 (y:Int) => 2 + y

五、数组

数组就是内存中的一段连续的空间,是存储同一类型的数据的集合

数组的声明
// 语法 : val|var arr = Array[泛型](元素列表)
// 示例 :
val arr = Array[Int](1,2,3,4,5)

// Scala中支持类型推断 因此在定义数组时可以不写泛型
val arr = Array(1,2,3,4,5)
数组的使用
  • 数组的长度 (length) : 1 - n
  • 数组的下标(index): 0 ~ length - 1
package array

object ArrayDemo {
  def main(args: Array[String]): Unit = {
    // scala数组的声明
    val arr = Array[Int](1, 2, 3, 4, 5)

    // 遍历方法1
    for(n <- 0 until arr.length){
      println(arr(n))  // 注意: 获取数组中元素使用的是 数组名(下标)
    }

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

    // 遍历方法2
    for(n <- arr){
      println(n)
    }

    // 修改数组中的元素
    arr(3) = 10

    println("-----------------------------------")
    // 遍历方法3
    for(n <- arr.indices){  // indices 获取到的是数组的下标数值区间
      println(arr(n))
    }
  }
}
可边长数组
  • Array是定长数组,一旦声明,长度固定不可改变
  • ArrayBuffer是可变长的数组,数组会随着元素的增多自动扩容
// 声明
val ab = ArrayBuffer(1,2,3,4,5)

// 添加元素
ab += 6

// 遍历
ab.foreach(println)
Array和ArrayBuffer相互转换
// arrayBuffer ---> array  可变到定长的转换
val arr = ab.toArray

// array ---> arrayBuffer 定长到可变长的转换
arr.toBuffer
多维数组
// 定义
val arr = Array.ofDim[Int](10, 4) // 类似于10行4列的二维表

// 操作
arr(1)(2) = 2

// 遍历
for (m <- 0 to arr.length - 1) {
    for (n <- 0 to arr(m).length - 1) {
        print(arr(m)(n) + "\t")
    }
    println()
}

六、类

简单类

语法: class 类名{}

// 声明一个Animals
class Animals{}

// 创建对象
val animals:Animals = new Animals()
// 或
val animals:Animals = new Animals
  • 创键的类中默认提供一个无参的主构造器
  • 创建对象时如果使用的是无参构造器,括号可以省略
成员和方法
  • Scala中定义的成员全部都是 private 修饰的
  • Scala中成员的getter方法名: 成员名()
  • Scala中成员的setter方法名: 成员名_$eq(参数)
class Animals{
	// Scala中声明成员的时候必须指定初始值
    // 一旦声明了成员后 Scala会自动使用 `private` 修饰变量 ,并且为其提供公开的getter/setter方法
    // getter : name()、age()
    // setter : name_$eq(name:String)、age_$eq(age:Int)
    var name: String = ""
    var age: Int = 1
    
    // 声明常量成员时,依然使用 `private` 修饰,并且为其提供公开的getter方法,没有setter方法
    val color:String = "白色"
    
    // Scala中可以使用private关键字修饰变量,Scala会为其提供私有的getter/setter方法
    private var birthday = new java.util.Date()
    
    // Scala中可以使用private关键字修饰常量,Scala会为其提供私有的getter方法,没有setter方法
    private val age = true
    
    // 方法直接定义在类中即可
    def run():Unit = {
        println("动物在跑~~")
    }
}
@BeanProperty

此注解加载成员的前面,Scala编译时,会自动产生Java版本和Scala版本的getter/setter方法

package classes

import scala.beans.BeanProperty

object CourseTest {
  def main(args: Array[String]): Unit = {
    val chinese = new Chinese

    // Java版本的getter/setter方法
    chinese.getCourseName
    chinese.setCourseName("文言文")

    // Scala版本的getter/setter方法
    chinese.courseName = "四书五经"
    val courseName = chinese.courseName
    println(courseName)
  }
}

class Chinese {
  @BeanProperty var courseName: String = "中文"
}
辅助构造器

辅助构造器,类似于java重载的构造方法

  • scala的一个类中,可以有一个主构造器和若干个辅助构造器
  • 辅助构造器,使用的关键字 this
  • 辅助构造器内,第一行必须调用主构造器或者辅助构造器
// scala默认提供一个无参的主构造器
class Fish {

  var name: String = ""
  var color: String = ""

  // 辅助的构造器
  def this(name: String) {
    this() // 辅助构造器必须以调用一个主构造器或者辅助构造器开始
    this.name = name
  }

  def this(name: String, color: String) {
    this(name)
    this.color = color
  }
}

// 调用
val fish1 = new Fish() 					  // 调用无参数的主构造器
val fish2 = new Fish("金鱼") 				 // 调用无参数的主构造器
val fish3 = new Fish("金鱼", "黄色") 		// 调用无参数的主构造器
主构造器
  • 主构造器的参数列表直接写在类名之后:`class Fish(var name:String,var color:String){}
  • 主构造器的方法体,是整个类,也就是类的{}的所有内容,注意主构造器方法体中的成员和方法仅仅是声明,并不会执行
  • 主构造器保证创建对象时主构造器定义的变量一定能够被初始化
// 主构造器
class Panda(var name: String, var color: String) {

    var age: Int = 0

    // 方法体
    println("before")

    def sayHello(str: String): String = {
        str + name
    }
    println("after")
}

七、对象

对象的作用是代替 Javastatic 关键字的用法,因为Scala没有 static 关键字,所以如果需要定义静态成员或者方法,使用 object 类型

单例对象

不管创建多少次,只存在一个对象实例

使用 object 修饰的类即为单例对象,类中的所有成员和方法都是静态的,全类共享,可直接通过类名访问

package objectes

/**
  * scala中一个类,类型为object,此类即为单例对象
  */
object SingletonObject {

  // 静态属性
  var id: Int = 0

  // 静态方法
  def incrementId(): Int = {
    this.id = this.id +1
    this.id
  }

  def main(args: Array[String]): Unit = {
    val s1 = SingletonObject
    val s2 = SingletonObject

    val s3 = new SingletonObject()
    val s4 = new SingletonObject()

    println(s1 == s2) //true【单例】
    println(s3 == s4) //false【多例】

    val id1 = s1.incrementId()
    val id2 = s2.incrementId()
    println(id1+"\t"+id2) // 2 2
  }
}

class SingletonObject {}
伴生对象&伴生类

在同一个源文件中,如果class对象和obejct对象的名字相同,那么class类称为伴生类,object对象称为伴生对象

// 伴生类
class Person {
  def sayHi(): String = {
    "Hi:" + Person.name + "\t" + Person.id
  }
}

// 伴生对象
object Person {

  val id: Int = 0
  private val name: String = "zs"

  def main(args: Array[String]): Unit = {
    val person = new Person
    println(person.sayHi())
  }
  
}
apply & unapply

apply方法是object类型中的一个方法,通常该方法作为该类的工厂方法,用来创建对象

即 : 调用伴生对象中apply方法,用于创建伴生类对象

package objectes

class Cat(var name: String, var color: String) {}

object Cat {

  // apply方法创建伴生类对象 该方法通常定义再object中
  def apply(name: String, color: String): Cat = new Cat(name, color)

  def main(args: Array[String]): Unit = {
    // 普通方法创建Cat对象
    val cat = new Cat("小花", "white")

    // apply 工厂方法,用以创建类的对象,通常定义在伴生对象中
    // 创建伴生类对象时【简化写法】
    val cat2 = Cat("小黑","black")  // 等价于Cat.apply("小黑","black")
    val cat3 = Cat("小黑","black")  // 等价于Cat.apply("小黑","black")
    println(cat2 == cat3)  // false
  }
}

unapply方法也是object类型中的一个方法,作用于apply方法相反,用于解构对象

即将对象的各个属性赋值给新的变量,供其他地方使用

package objectes

class Cat(var name: String, var color: String) {

}

object Cat {

  // apply方法创建伴生类
  def apply(name: String, color: String): Cat = new Cat(name, color)

  // 将伴生类对象 拆箱为成员
  // Option 【避免出现空指针异常,有值返回 Some  无值返回 None】
  //   - None
  //   - Some
  def unapply(arg: Cat): Option[(String, String)] = {
    if (arg == null) None
    else Some((arg.name, arg.color))  //(name,color) scala元组,可以存放任意类型的list集合 类似于java中的ArrayList[Object]
  }

  def main(args: Array[String]): Unit = {
    // 调用apply方法创建Cat对象
    val cat = new Cat("小花", "white")

    // ================================================
    // unapply方法应用
    val Cat((name, color)) = cat
    // 相当于:
    // Cat.unapply(cat3) = Some(cat3.name,cat3.color)
    // val name = cat3.name
    // val color = cat3.color
    println(name + "\t" + color)
  }
}

型中的一个方法,作用于apply方法相反,用于解构对象

即将对象的各个属性赋值给新的变量,供其他地方使用

package objectes

class Cat(var name: String, var color: String) {

}

object Cat {

  // apply方法创建伴生类
  def apply(name: String, color: String): Cat = new Cat(name, color)

  // 将伴生类对象 拆箱为成员
  // Option 【避免出现空指针异常,有值返回 Some  无值返回 None】
  //   - None
  //   - Some
  def unapply(arg: Cat): Option[(String, String)] = {
    if (arg == null) None
    else Some((arg.name, arg.color))  //(name,color) scala元组,可以存放任意类型的list集合 类似于java中的ArrayList[Object]
  }

  def main(args: Array[String]): Unit = {
    // 调用apply方法创建Cat对象
    val cat = new Cat("小花", "white")

    // ================================================
    // unapply方法应用
    val Cat((name, color)) = cat
    // 相当于:
    // Cat.unapply(cat3) = Some(cat3.name,cat3.color)
    // val name = cat3.name
    // val color = cat3.color
    println(name + "\t" + color)
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值