引言:
Spark 就是使用 Scala 编写的。
Scala 是 Scalable Language 的简写,是一门多范式(编程的方式[面向对象编程,函数式编程])的编程语言
参考:
[toc]
Scala概述 介绍 Spark源码
.scala > .class > JVM虚拟机
环境安装-windows 1 2 3 4 5 6 7 8 9 下载:http://www.scala-lang.org/ 压缩包 配置JDK的环境变量 JAVA_HOME 配置Scala的环境变量SCALA_HOME 将Scala安装目录下的bin目录加入到PATH环境变量。在PATH变量中添加:%SCALA_HOME%\bin 在命令行窗口中输入“scala”命令打开scala解释器(REPL),安装完成
hello.scala
1 2 3 4 5 object Hello { def main (args: Array [String ]): Unit = { println(“Hello World ”) } }
编译
运行
直接运行
没有在磁盘生成.class ,直接在内存里操作的。
环境安装-IDEA 插件安装
idea下的hello world
第一个Scala程序 不用再写分号了(太优秀了…java啥时候学学呀)
输出的三种方式
1 2 3 4 5 6 7 8 9 println("name=" + name + " age=" + age + " url=" + url) printf("name=%s, age=%d, url=%s \n" , name, age, url) println(s"name=$name , age=$age , url=$url " )
反编译
scala字节码 > 反编译> java代码
java反编译工具:jd-gui.exe
注释
变量 变量说明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 object C01 { var num1: Int = 10 var num2 = 10 num2 = 20 val num3 = 10 val dog = new Dog () dog.name = "mimi" }
数据类型
Unit、Null、Nothing
Unit
表示无值,和其他语言中void等同。用作不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()。
Null
null , Null 类型只有一个实例值 null
Nothing
Nothing类型在Scala的类层级的最低端;它是任何其他类型的子类型。 当一个函数,我们确定没有正常的返回值,可以用Nothing 来指定返回类型,这样有一个好处,就是我们可以把返回的值(异常)赋给其它的函数或者变量(兼容性) def f1():Nothing = { throw new Exception() }
1 2 3 4 5 6 var num : Int = 2.7 .toInt
运算符 Scala不支持三目运算符 , 在Scala中使用 if – else 的方式实现。
和java差不多
程序流程控制 顺序控制 1 2 3 4 def main(args : Array[String]) : Unit = { var num1 = 12 var num2 = num1 + 2 }
分支控制 1 2 3 4 5 6 7 if(){ }else if(){ }else{ }
循环控制:for 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 println("案例1:to" ) for (i <- 1 to 3 ){ print(i + " " ) } println() println("案例2:until" ) for (i <- 1 until 3 ) { print(i + " " ) } println() println("案例3: 循环守卫" ) for (i <- 1 to 3 if i != 2 ) { print(i + " " ) } println() println("案例4:引入变量" ) for (i <- 1 to 3 ; j = 4 - i) { print(j + " " ) } println() println("案例5:嵌套循环" ) for (i <- 1 to 3 ; j <- 1 to 3 ) { println(" i =" + i + " j = " + j) } println() println("案例6:循环返回值" ) val res = for (i <- 1 to 10 ) yield i * 2 println(res) println() println("案例6:使用花括号{}代替小括号()" ) for {i <- 1 to 3 ; j = i * 2 } { println(" i= " + i + " j= " + j) } println()
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 案例1:to 1 2 3 案例2:until 1 2 案例3: 循环守卫 1 3 案例4:引入变量 3 2 1 案例5:嵌套循环 i =1 j = 1 i =1 j = 2 i =1 j = 3 i =2 j = 1 i =2 j = 2 i =2 j = 3 i =3 j = 1 i =3 j = 2 i =3 j = 3 案例6:循环返回值 Vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20) 案例6:使用花括号{}代替小括号() i= 1 j= 2 i= 2 j= 4 i= 3 j= 6
循环控制:while 1 2 3 4 5 6 7 8 9 10 11 循环变量初始化 while (循环条件) { 循环体(语句) 循环变量迭代 } 循环变量初始化; do{ 循环体(语句) 循环变量迭代 } while (循环条件)
推荐使用for
循环控制:循环中断 去掉了break和continue。
1 2 3 4 5 6 7 println("案例: 循环中断" ) for (i <- 1 to 10 ){ print(i + " " ) if (i==5 ){ break () } }
函数式编程-基础 在scala中函数式编程和面向对象编程融合在一起了
函数/方法 函数/方法定义
1 2 3 4 5 6 7 8 def 函数名 ([参数名: 参数类型], ...)[[: 返回值类型] =] { 语句... //完成某个功能 return 返回值 } 返回值形式1: // def 函数名(参数列表) : 数据类型 = {函数体} // 返回值确定,清晰 返回值形式2: // def 函数名(参数列表) = {函数体} // 有返回值, 类型是推断出来的 返回值形式3: // def 函数名(参数列表) {函数体} // 无返回值 Unit 如果没有return ,默认以执行到最后一行的结果作为返回值
demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 def sum (n1: Int , n2: Int ): Int = { return n1 + n2 } def f1 (): Int = {100 } println(f1) println(f1()) var f2 = f1var f3 = f1 _ println(f2) println(f3) println(f3()) val f1 = () => "abc" println(f1()) val f2: (String , Double ) => Int = (a: String , b : Double ) => a.toInt + b.toIntprintln(f2("2" ,1.5 )) val f3: (String , Double ) => Int = (a, b) => a.toInt + b.toIntprintln(f3("2" ,1.5 )) var f4 = (a: String , b:Double ) => a.toInt + b.toIntprintln(f4("2" ,1.5 )) var money = 100 def buy (): Int = { money -= 10 money } def test1 (a: Int ) = { println(a) println(a) } def test2 (a: => Int ) = { println(a) println(a) } test1(buy) test2(buy)
高阶函数 1 2 3 4 5 6 7 8 9 10 11 12 def apply (f: Int => String , v: Int ) = f(v)def fmtInt (n: Int ) : String = "[整数值{" + n + "}]" println(apply(fmtInt, 1200 )) def addBy (n: Int ) = { (d : Double ) => n + d } println(addBy(50 )(80.223 ))
函数柯里化 1 2 3 4 5 6 7 8 9 10 def addMulti (a: Int ) = { (b: Int ) => (c: Int ) => (a + b) * c } println(addMulti(50 )(80 )(20 ))
参数(类型)判断 1 2 3 1、参数类型是可以推断时,可以省略参数类型 2、当传入的函数,只有单个参数时,可以省去括号 3、如果变量只在=>右边只出现一次,可以用_来代替
demo
1 2 3 4 5 val list = List (1 , 2 , 3 , 4 )println(list.map((x:Int )=>x + 1 )) println(list.map((x)=>x + 1 )) println(list.map(x=>x + 1 )) println(list.map(_ + 1 ))
惰性函数 类似懒加载,将耗时的计算推迟到绝对需要的时候。
比如java使用单例模式懒汉式 实现该思路
定义:当函数返回值 被声明为lazy 时,函数的执行将被推迟,直到我们首次对此取值,该函数才会执行。这种函数我们称之为惰性函数
1 2 3 4 5 6 7 def sum (n1 : Int , n2 : Int ): Int = { println("sum() 执行了.." ) return n1 + n2 } lazy val res = sum(10 , 20 ) println("-----------------" ) println("res=" + res)
异常 类似java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 try { val r = 10 / 0 } catch { case ex: ArithmeticException => println("捕获了除数为零的算术异常" ) case ex: Exception => println("捕获了异常" ) } finally { println("scala finally..." ) } def test (): Nothing = { throw new Exception ("不对" ) } val res = test()@throws (classOf[NumberFormatException ])def f11 () = { "abc" .toInt } f11() println("---" )
面向对象编程 类与对象 与java对比
scala语法中,类并不声明为public,所有这些类都具有公有可见性(即默认就是public)
一个Scala源文件可以包含多个类.
定义类
1 2 3 4 5 6 7 8 9 10 class Person { var age : Int = 10 var sal = 8090.9 var name = null var address : String = null var a : Double = _ }
创建对象
1 2 3 4 val | var 对象名 [:类型] = new 类型()1 、如果我们不希望改变对象的引用(即:内存地址), 应该声明为val 性质的,否则声明为var , scala设计者推荐使用val ,因为一般来说,在程序中,我们只是改变对象属性的值,而不是改变对象的引用。2 、类型声明可以省略,但当类型和后面new 对象类型有继承关系即多态时,就必须写了
访问属性
类和对象的内存分配机制
1 2 3 4 5 6 7 val p1 = new Person p1.name = "jack" p1.age = 30 val p2 = p1 p2.age = 18 println("p2.age=" + p2.age) println("p1.age=" + p1.age)
构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class Person ( ) { var name: String = _ var age: Int = _ def this (name : String ) { this () this .name = name } def this (name : String , age : Int ) { this () this .name = name this .age = age } def this (age : Int ) { this ("匿名" ) this .age = age } def showInfo (): Unit = { println("person信息如下:" ) println("name=" + this .name) println("age=" + this .age) } }
构造器参数
1 2 3 1、Scala类的主构造器的形参未用任何修饰符修饰,那么这个参数是局部变量。 2、如果参数使用val关键字声明,那么Scala会将参数作为类的私有的只读属性使用 3、如果参数使用var关键字声明,那么Scala会将参数作为类的成员属性使用,并会提供属性对应的xxx()[类似getter]/xxx_$eq()[类似setter]方法,即这时的成员属性是私有的,但是可读写。
Bean属性
1 2 3 4 5 6 import scala.beans.BeanProperty class Car { @BeanProperty var name: String = null }
Scala对象创建对象流程分析
加载类的信息(属性信息和方法信息), 如果父类也没有加载, 则由父到子加载父类
在内存中(堆)给对象开辟空间
使用父类的构造器(主构造器/辅助构造器)完成父类的初始化 (多个父类)
使用本类的主构造器完成初始化
使用本类的辅助构造器继续初始化
将对象在内存中的地址赋给 p 这个引用
包 基本
1、Scala中包名和源码所在的系统文件目录结构要可以不一致,但是编译后的字节码文件路径和包名会保持一致(这个工作由编译器完成)。 2、Scala会自动引入的常用包
1 2 3 java.lang.* scala包 Predef包
3、使用嵌套形式打包:好处是:程序员可以在同一个文件中,将类(class / object)、trait 创建在不同的包中 4、作用域原则:可以直接向上访问。即: Scala中子包中直接访问父包中的内容, 大括号体现作用域。 5、父包要访问子包的内容时,需要import对应的类等 6、可以在同一个.scala文件中,声明多个并列的package(建议嵌套的pakage不要超过3层) 7、包名可以相对路径也可以绝对路径,比如,访问BeanProperty的绝对路径是:root . scala.beans.BeanProperty ,在一般情况下:我们使用相对路径来引入包,只有当包名冲突时,使用绝对路径来处理。
1 2 3 4 5 6 @_root_. scala.beans.BeanProperty var age: Int = _
demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package com.mxx{ class User { } object Monster { } class Dog { } package scala { class User { } object Test { def main (args: Array [String ]): Unit = { var dog = new Dog () println("dog=" + dog) var u = new User () println("u=" + u) var u2 = new com.mxx.User () println("u2=" + u2) } } } }
包对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package com.mxx { package object scala { var name = "jack" def sayOk (): Unit = { println("package object sayOk!" ) } } package scala { class Test { def test () : Unit ={ println(name) sayOk() } } object TestObj { def main (args: Array [String ]): Unit = { val t = new Test () t.test() println("name=" + name) } } } }
包的引入
1、在Scala中,import语句可以出现在任何地方,并不仅限于文件顶部,import语句的作用一直延伸到包含该语句的块末尾。这种语法的好处是:在需要时在引入包,缩小import 包的作用范围,提高效率。 2、Java中如果想要导入包中所有的类,可以通过通配符*,Scala中采用下划线_ 3、如果不想要某个包中全部的类,而是其中的几个类,可以采用选取器(大括号)
1 import scala.collection.mutable.{HashMap , HashSet }
4、如果引入的多个包中含有相同的类,那么可以将类进行重命名进行区分,这个就是重命名。
1 2 3 4 import java.util.{ HashMap =>JavaHashMap , List }import scala.collection.mutable._var map = new HashMap ()var map1 = new JavaHashMap ();
5、如果某个冲突的类根本就不会用到,那么这个类可以直接隐藏掉。
1 2 import java.util.{ HashMap =>_, _}var map = new HashMap ()
封装 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Cat (inAge:Int ) { private var age: Int = 0 var name: String = "" private val age2: Int = 0 val name2: String = "" } object C08 { def main (args: Array [String ]): Unit = { val cat = new Cat (10 ) cat.name = "喵" println(cat.name) } }
继承 1、基本语法
1 2 3 4 5 class Student extends Person { def studying (): Unit = { println(this .name + "学习 scala中...." ) } }
2、子类继承了所有的属性,只是私有的属性不能直接访问,需要通过从父类继承的公共的方法去访问
3、scala明确规定,重写一个非抽象方法需要用override关键字修饰,调用超类的方法使用super关键字
1 2 3 4 5 6 class Emp extends Person { override def printName () { println("Emp printName() " + name) super .printName() } }
4、类型检查和转换
1 2 3 4 5 6 7 8 9 10 object C10 { def main (args: Array [String ]): Unit = { println(classOf[String ]) val s = "zhangsan" println(s.getClass.getName) println(s.isInstanceOf[String ]) println(s.asInstanceOf[String ]) } }
5、超类的构造
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Person (name: String ) {} class Emp (name: String ) extends Person (name ) { def this () { } }
6、覆写字段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class A { val age : Int = 10 } class B extends A { override val age : Int = 20 } object C10 { def main (args: Array [String ]): Unit = { val obj1 : A = new B () val obj2 : B = new B () println(obj1.age) println(obj2.age) } }
7、抽象类
1 2 3 4 5 6 7 8 9 10 11 abstract class Animal { var name : String var age : Int var color : String = "black" def cry () }
8、匿名子类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 abstract class Monster { var name : String def cry () } object C10 { def main (args: Array [String ]): Unit = { var monster = new Monster { override var name: String = "牛魔王" override def cry (): Unit = { println("牛魔王哼哼叫唤.." ) } } } }
9、继承层级
1、在scala中,所有其他类都是AnyRef的子类,类似Java的Object。 2、AnyVal和AnyRef都扩展自Any类。Any类是根节点/根类型 3、Any中定义了isInstanceOf、asInstanceOf方法,以及哈希方法等。 4、Null类型的唯一实例就是null对象。可以将null赋值给任何引用,但不能赋值给值类型的变量[案例演示]。 5、Nothing类型没有实例。它对于泛型结构是有用处的,举例:空列表Nil的类型是List[Nothing],它是List[T]的子类型,T可以是任何类。
静态属性和静态方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class ScalaPerson { var name : String = _ } object ScalaPerson { var sex : Boolean = true def main (args: Array [String ]): Unit = { println(ScalaPerson .sex) } }
单例模式 1 2 3 采用类对象(即伴生对象)方式构建单例对象 - 懒汉式 - 饿汉式
TODO
接口 & 特质(trait) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 trait Trait1 { def getConnect (user: String , pwd: String ): Unit def test (n1:Int ) } class A {}class B extends A {}class C extends A with Trait1 { override def getConnect (user: String , pwd: String ): Unit = { println("c连接mysql" ) } } class D {}class E extends D with Trait1 { def getConnect (user: String , pwd: String ): Unit = { println("e连接oracle" ) } }
TODO
type关键字 使用type关键字可以定义新的数据类型名称。本质上就是类型的一个别名
1 2 3 type S = String var v : S = "abc" def test () : S = "xyz"
嵌套类 在Scala中,你几乎可以在任何语法结构中内嵌任何语法结构。如在类中可以再定义一个类,这样的类是嵌套类,其他语法结构也是一样。
嵌套类类似于Java中的内部类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class ScalaOuterClass { class ScalaInnerClass { } } object ScalaOuterClass { class ScalaStaticInnerClass { } } val outer1 : ScalaOuterClass = new ScalaOuterClass ();val outer2 : ScalaOuterClass = new ScalaOuterClass ();val inner1 = new outer1.ScalaInnerClass ()val inner2 = new outer2.ScalaInnerClass ()val staticInner = new ScalaOuterClass .ScalaStaticInnerClass ()println(staticInner)
TODO
隐式转换和隐式参数 todo
集合 可变与不可变 可变集合
不可变集合
String是一种集合类型
数组 …
元组 Tuple 可以存放各种相同或不同类型的数据 。
注意:元组中最大只能有 22 个元素
1 2 3 4 5 6 7 8 9 10 11 12 val tuple = (1 , 2 , "hello" )println(tuple._3) println(tuple.productElement(2 )) for (i <- tuple.productIterator) { println("i=" + i) }
列表 List 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 val list01 = List (1 , 2 , 3 ) println(list01) val list02 = Nil println(list02) val value1 = list1(1 ) println(value1) var list1 = List (1 , 2 , 3 , "abc" )val list2 = list1 :+ 4 println(list1) println(list2) val list3 = 100 +: list1println("list3=" + list3) val list4 = List (1 , 2 , 3 , "abc" )val list5 = 4 :: 5 :: 6 :: list4 :: Nil println("list5=" + list5) var list6 = List (1 , 2 , 3 , "abc" )val list7 = 4 :: 5 :: 6 :: list6 ::: Nil println("list7=" + list7)
列表 ListBuffer 队列 Queue 映射 Map Scala 中不可变的 Map 是有序的,可变的 Map 是无序的
创建
1 2 3 4 5 6 7 8 9 10 11 12 val map1 = Map ("Alice" -> 10 , "Bob" -> 20 , "Kotlin" -> "北京" )val map2 = scala.collection.mutable.Map ("Alice" -> 10 , "Bob" -> 20 , "Kotlin" -> 30 )val map3 = new scala.collection.mutable.HashMap [String , Int ]val map4 = mutable.Map ( ("A" , 1 ), ("B" , 2 ), ("C" , 3 ),("D" , 30 ) )
取值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 val value1 = map2("Alice" )map4.contains("B" ) var map4 = mutable.Map ( ("A" , 1 ), ("B" , "北京" ), ("C" , 3 ) )println(map4.get("A" )) println(map4.get("A" ).get) val map4 = mutable.Map ( ("A" , 1 ), ("B" , "北京" ), ("C" , 3 ) )println(map4.getOrElse("A" ,"默认" ))
对 map 修改、添加和删除
1 2 3 4 5 6 7 8 9 10 11 12 val map4 = mutable.Map ( ("A" , 1 ), ("B" , "北京" ), ("C" , 3 ) )map4("A" ) = 20 map+=("A" ->1 ,"B" ->2 ) map-=("A" ,"B" ) map4.remove(key)
对 map 遍历
1 2 3 4 5 val map1 = mutable.Map ( ("A" , 1 ), ("B" , "北京" ), ("C" , 3 ) )for ((k, v) <- map1) println(k + " is mapped to " + v)for (v <- map1.keys) println(v)for (v <- map1.values) println(v) for (v <- map1) println(v)
集 Set …
集合应用操作 map 映射操作 集合(List,Array..) [数据] ==> 过滤(函数) ==> 操作(函数计算任务)[map] ==> 得到新的集合
案例:请将 List(3,5,7) 中的所有元素都 * 2 ,将其结果放到一个新的集合中返回
在 Scala 中可以通过 map 映射操作来解决:将集合中的每一个元素通过指定功能(函数)映射(转换)成新的
结果集合这里其实就是所谓的将函数作为参数传递给另外一个函数,这是函数式编程的特点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 val list = List (3 , 5 , 7 )val list2 = list.map(f1)def f1 (n:Int ): Int = { println("hello..." ) n * 2 } val f3 = (n1: Int ,n2: Int ) => {n1 + n2 } println(f3(1 ,2 ))
flatmap 映射 flatmap: flat 即压扁,压平,扁平化,效果就是将集合中的每个元素的子元素映射到某个函数并返回新的集合。
1 2 3 4 5 6 7 8 9 val names = List ("Alice" , "Bob" , "Nick" )def upper ( s : String ) : String = {s. toUpperCase } println(names.flatMap(upper)) val res1 = list.flatMap(_.split(" " ))
过滤-filter 模式匹配 函数式编程-高阶 泛型 上下界 视图界定 上下文界定 协变逆变不变 AKKA 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 object C10 { def main (args: Array [String ]): Unit = { val sub = new Sub () sub.sayOk() } } class Base { var n1: Int = 1 protected var n2: Int = 2 private var n3: Int = 3 def test100 (): Unit = { println("base 100" ) } protected def test200 (): Unit = { println("base 200" ) } private def test300 (): Unit = { println("base 300" ) } } class Sub extends Base { def sayOk (): Unit = { this .n1 = 20 this .n2 = 40 println("范围" + this .n1 + this .n2) } }