首页 > 技术文章 > Scala基础(一)

zyp0519 2021-10-15 22:52 原文

一、什么是Scala:

  直观的来说,加强版Java。一种以Java虚拟机(JVM)为运行环境的静态类型编程语言。可以直接使用Java的类库。(拥有者强大的编译器)

  特点:面向对象,函数式编程

  Scala有三类事务,Class、Object、Trait。在Scala中把静态的和非静态的严格分开

         Class中编写非静态的东西;

      Object中只能编写静态的,我例如Java中的main方法,它是静态的,在Scala中就要写道Object里。

  简单的举个例子:

object Scala {
  def func1 = println("Hello Zyp")

  def main(args: Array[String]): Unit = {
    /* 
       写个main方法,直接调用函数
     */
    func1
  }
}
在上面的代码中:   
  1. object里面都是静态方法(以及变量等)
2. 数据类型跟在变量后面,中间用“ :”隔开
  3. 返回值在参数完成的后面,方法体是以“ = ”来赋值
  4. 语句末有无 ”;“ 都可

  在Scala中我们声明" func1 "这个函数,他其实是个变量,这就是这个语言不同于java的地方,完全面向对象编程,是函数语言。每一个函数在Scala中都是一个值,当我们定义一个函数的时候就是定义了一个变量。

  Scala中的函数:高阶函数、柯里化函数、内嵌函数(函数中定义函数)等。

    高阶函数:       函数的参数可以传递函数

    克里化函数: 函数可以拆分,部分执行。


 二、Scala中的变量和函数:

1. 定义变量:

  修饰符:var  \ val

    var 代表一个变量 ;val 代表一个常量

def main(args: Array[String]): Unit = {
    var n =10
    n=11
 //变量可以再次赋值
  }
 

 def main(args: Array[String]): Unit = {
    val n:Int =10
    n=11 
//会报错 Reassignment to val }

  从上面的声明变量还可以看出:在Scala中,类型可以不给。(Scala中没有基本数据类型,全是引用数据类型

变量的几种输出方式:

object Program1 {
  def main(args: Array[String]): Unit = {
    var name: String = "zhangsan"
    var age = 10
    var tall = 180.65f
    /*
      字符串的三种输出方式:
    */
  // 方式一: println(age+"\t"+name)

  // 方式二: printf(
"数字是%d 字符串是%s 小数是%.4f",age,name,tall)

  // 方式三:
   // 格式化输出,其中 s:String    d:Int    f:float
    println(s"个人信息如下:\n名字:$name 年龄:${age+15} 身高:$tall")
    //  前面的s标记相当于告诉编译器,后面$跟着变量    而且${}中的{}里面是一个表达式

  }
}

 

打印结果如图:

2. 定义函数:

  关键字:def

def func2(a:Int,b:Int):Int = return a+b

  1.   返回值关键字 "return" 可写可不写

  2.   :Int 表示的是返回值类型,没有返回值就是Unit

  3.   这里的参数类型是变量,也可以用函数作为参数(因为scala中一切皆对象)

例如:

def square(x: Int) = {
    println(x)
    x * x
 }

def printByName(x: => Any): Unit = {
    println(x)
 }

def main(args: Array[String]): Unit = {
    printByName(square(5))
 }

       先执行square,把整个函数作为参数传递给X,那么就会先打印5再返回25

3. scala中的循环:

打印1-10:

  for (i <-1 to 10){
    println(i)
  }
-- 从1到10全部都打印;
而 1 until 10 则打印的是1到9,不包括10

隔两个打印一个数隔两个打印一个数:(步长为2)

  for (i <-1 to 10 by 2){  //by 自增的意思 
    print(i+" ")
  }

  // 等同于Range()
 
for (i <-Range(1,10,2)){
    print(i+" ")
  }

打印结果为:
1 3 5 7 9

用ForEash的方式:

  (1 to 10 by 2).foreach(
    println(_)  _的意思是当前,当前这个数
  )
 
但是你如果不想用下划线的话可以如下:
 
  (1 to 10 by 2).foreach(  推出的方式
    x =>println(x)
  )

补充一个yield的用法:

常用场景:将元素自动封装到集合中
val temp = for(i <- 1 to 100; if i%2 == 0) yield i     //此时rest就为选择出的i构成的一个集合
for(elem <- rest) println(elem)

 

4. Scala中的数据类型:

  Scala所有类型都是Object,顶级的类叫做ScalaObject.我们定义的所有的类都叫做ScalaObject。ScalaObject下面呢就是最根基的类叫做Any,就是任意类型,下面两个体系:值类型(AnyVal)和引用类型(AnyRef)

  数值这些类型(Int Boolean char..)归为值类型(AnyVal)

  String和数组,列表这些都归为引用类型(AnyRef)

Null:是一个类,只有一个值:null。是所有AnyRef(引用)类型的子类

Nothing:没有实例对象的一种数据类型,它是所有类的子类。(可以将Nothing类型的值返回给任意类型的变量或者函数)

Unit:代表的是一个值类型,类似于Java中void

 

5. 函数:

  大致简单介绍下,抽空详细的列举下算子。

  5.1 普通函数:

def fun (a: Int , b: Int ) : Unit = {
   println(a+b)
 }
fun(1,1)
    
def fun1 (a : Int , b : Int)= a+b
    println(fun1(1,2))  

  5.2 包含参数默认值的函数:

 /**
   * 包含参数默认值的函数
   *     1.函数的参数有默认值,在调函数的时候可以传参,也可以不传参,
   *     2.若不传参使用的默认值,
   *     3.如果传参,默认值会被覆盖
   */
  def fun2(num1:Int = 10,num2:Int = 20) = {
    num1 + num2
  }
  
  def fun3(num1:Int,num2:Int = 20) = {
    num1 + num2
  }
  
  def fun4(num1:Int=10,num2:Int) = {
    num1 + num2
  }
  调用:
   println(fun2())
   println(fun3(100))
   println(fun4(num2=1000))

  5.3 可变参数个数的函数:(参数个数可以是一个也可以是多个)

 def fun5(args:Double*) = {
    /**
     * 在scala中
     *     +=前后的数值类型必须一致
     *     +前后的数值类型可以不一致
     */
    var sum = 0.0
    for(arg <- args) sum += arg
    sum
  }

  5.4 匿名函数:

/**
 * 匿名函数
 * 1.有参数的匿名函数
 * 2.无参数的匿名函数
 * 3.有返回值的匿名函数
 * 注意:
 * 可以将匿名函数返回给定义的一个变量
 * 匿名函数不能显式声明函数的返回类型

 */
//有参数匿名函数
val value1 = (a : Int) => {
  println(a)
}
value1(1)
//无参数匿名函数
val value2 =()=>{
  println("Hello Scala")
}
value2()
//有返回值的匿名函数
val value3 = (a:Int,b:Int) =>{
  a+b
}
println(value3(4,4)) 

  5.5 偏函数:

  作用于指定类型的参数指定范围值的参数实施计算,超出它的界定范围之外的参数类型和值它会忽略(未必会忽略,这取决于你打算怎样处理)。简单来讲:给出的参数中,只根据你在方法体中规定的条件进行甄别,符合条件的就操做,不行的直接滤掉(取决于你业务上的逻辑处理)。

  举个简单的例子体会下:

 

 

 

 

 

 

 

 

 


三、Tuples元组

1. 什么是元组?

  用来包含一组不同类型的值。例如:名字,年龄,出生日期....元组的元素是不可变的。它的本体可以作为一个参数进行传递。元组是有上限的,最多只能有22个,超出的话建议使用集合。

2. 定义一个元组:

val t1 = (1, "hello", true)  -- 简写形式,常用,这里声明为常量,值不可变
-- 如果只有两个元素,还可以写成:
val t2 = 1 -> "world"

println(tuple1) println(tuple1._1) -- 1 println(tuple2._2) -- hello

上面代码可以看出,我们通过" ._1 "来获取元组中对应下标的元素(下标是从1开始的)

3. 遍历元组:

  上面2中可以直接打印元组,也可以取出元组中某个位置上的值;那么如何遍历所有元素呢?

  这里要用到Tuple.productIterator()方法,获取迭代器以后再循环遍历:

tuple1.productIterator.foreach(i=>println(i))

或者
for (i <- tuple1.productIterator){
println(i)
}
}
}

 


四、数组

  Scala中的数组分为 定长数组Array 和 变长数组ArrayBuffer 。存储的是固定大小的同类型元素。

定长数组:

  • 数组的长度不允许改变
  • 数组中的元素可以改变

  语法:

// 通过指定长度定义数组
val/var 变量名 = new Array[元素类型](数组长度)

// 用元素直接初始化数组
val/var 变量名 = Array(元素1, 元素2, 元素3...)

  注意:

  1. 在Scala中,数组的泛型用 [ ] 来指定;
  2. 用()来获取元素

  示例一:

  1. 定义一个长度为100的整型数组
  2. 设置第1个元素为110
  3. 打印第1个元素
scala> val a = new Array[Int](100)
a: Array[Int] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)

scala> a(0) = 110

scala> println(a(0))
110

  示例二:

  1. 定义一个包含以下元素的数组:"java", "scala", "python"

  2. 获取数组长度

// 定义包含jave、scala、python三个元素的数组
scala> val a = Array("java", "scala", "python")
a: Array[String] = Array(java, scala, python)

scala> a.length
res17: Int = 3

变长数组:

  • 数组的长度是可变的;
  • 可以向数组中添加、删除元素

  语法:

// 创建空的ArrayBuffer变长数组,语法结构:
val/var a = ArrayBuffer[元素类型]()

// 创建带有初始元素的ArrayBuffer
val/var a = ArrayBuffer(元素1,元素2,元素3....)

  示例:

  定义一个包含以下元素的变长数组:"hadoop", "storm", "spark"

scala> val a = ArrayBuffer("hadoop", "storm", "spark")
a: scala.collection.mutable.ArrayBuffer[String] = ArrayBuffer(hadoop, storm, spark)

区间数组:

   使用 range() 方法来生成一个区间范围内的数组。range() 方法最后一个参数为步长,默认为 1:

import Array._

object Test {
   def main(args: Array[String]) {
      var myList1 = range(10, 20, 2)   // 步长为2
      var myList2 = range(10,20)  // 不写默认步长为1

      // 输出所有数组元素
      for ( x <- myList1 ) {
         print( " " + x )
      }
      println()
      for ( x <- myList2 ) {
         print( " " + x )
      }
   }
}


输出结果为:
$ scalac Test.scala
$ scala Test
10 12 14 16 18
10 11 12 13 14 15 16 17 18 19

添加、修改、删除元素:

  • 使用  += 添加元素
  • 使用  -= 删除元素
  • 使用  ++= 追加一个数组到变长数组

 示例:

  1. 定义一个变长数组,包含以下元素: “hadoop”, “spark”, “flink”;
  2. 往该变长数组添加一个"flume"元素;
  3. 从该变长数组删除"hadoop"元素;
  4. 再将一个数组,该数组包含"hive", "sqoop"追加到变长数组中;
// 定义变长数组
scala> val a = ArrayBuffer("hadoop", "spark", "flink")
a: scala.collection.mutable.ArrayBuffer[String] = ArrayBuffer(hadoop, spark, flink)

// 追加一个元素
scala> a += "flume"
res10: a.type = ArrayBuffer(hadoop, spark, flink, flume)

// 删除一个元素
scala> a -= "hadoop"
res11: a.type = ArrayBuffer(spark, flink, flume)

// 追加一个数组
scala> a ++= Array("hive", "sqoop")
res12: a.type = ArrayBuffer(spark, flink, flume, hive, sqoop)

遍历数组:

两种方式:

  • 使用for表达式直接遍历数组中的元素
  • 使用索引遍历数组中的元素
scala> val a = Array(1,2,3,4,5)
a: Array[Int] = Array(1, 2, 3, 4, 5)

scala> for(i<-a) println(i)

scala> a.foreach(x => { println(x) })
1
2
3
4
5
scala> val a = Array(1,2,3,4,5)
a: Array[Int] = Array(1, 2, 3, 4, 5)

scala> for(i <- 0 to a.length - 1) println(a(i))
1
2
3
4
5

scala> for(i <- 0 until a.length) println(a(i))
1
2
3
4
5

注意:

  0 until n : 遍历0到n-1,不包括n

  0 to n ——遍历0到n,包含n

数组的函数:

参见https://blog.csdn.net/zyp13781913772/article/details/81428862


 五、集合

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

(可变集合可以在适当的地方被更新或扩展。这意味着你可以修改,添加,移除一个集合的元素。

而不可变集合类,相比之下,永远不会改变。不过,你仍然可以模拟添加,移除或更新操作。但是这些操作将在每一种情况下都返回一个新的集合,同时使原来的集合不发生改变。)

集合类型:

  1.  List:可重复切有序

  2.  Set:不可重复无序
  3.  元组:存放的是不同类型的值(上面已经介绍过)
  4.  Map:键值对;每个元素都是一组键值对
  5.  Option:可能包含值的容器(也可能不含值)
  6.  Iterator:迭代器不是一个容器,更准确的说是逐一访问容器内元素的方法

 

 


 

六、伴生类与伴生对象

  (区别于Java来看)

  在同一个文件中,Class与Object同名,构成伴生类和伴生对象

  我们已经知道,class里的相当于Java中的成员变量和成员方法,使用时需要通过new一个对象来调用,而object中则全是静态的,不需要对象调用就可以直接使用。而在伴生类和伴生对象中,如果你在伴生对象里,写了apply方法,那么此时可以通过伴生对象直接来调用伴生类里的方法(就不需要new一个对象来调用了,apply方法相当于就走了你的伴生类中的构造器)。

举例:

class ClassAndObject(name: String) {
  private var username = name
  def aaa() = {
    println("123")
  }
}

object ClassAndObject {
  def main(args: Array[String]): Unit = {
    //此时你想调用aaa方法,只能先new一个对象来调用
    var c = new ClassAndObject()
    c.aaa  
  }
}

若想直接调用:

 class ClassAndObject(name: String) {
  private var username = name

  def aaa() = {
    println("123")
  }
}

object ClassAndObject {
  def apply(name: String): ClassAndObject = new ClassAndObject(name)

  def main(args: Array[String]): Unit = {
    //此时就不需要再去new对象
    ClassAndObject("zs").aaa()
  }
}

 

推荐阅读