Spark大数据分析实战 第1章 Scala基础
所属分类 spark
浏览量 931
Scala 2.12.7 JDK1.8
Scala是一种将面向对象和函数式编程结合在一起的高级语言
旨在以简洁、优雅和类型安全的方式表达通用编程模式
Scala运行于Java平台,编译成class字节码文件后运行
运行时性能与Java不分上下,并且可以调用Java方法、继承Java类、实现Java接口等
Spark主要由Scala语言编写
scala命令 进入 Scala的命令行
变量 val 不可变 var 可变
自动类型推断
val str="hello scala"
声明变量时指定数据类型
val str:String="hello scala"
多个变量一起声明
val x,y="hello scala"
定义变量需要初始化,否则会报错
定义变量时可以不指定数据类型,会根据初始化值推断变量的类型
优先使用val(常量),除非确实需要修改
Scala语句不需要写结束符,除非同一行代码使用多条语句时才需要使用分号隔开
所有的值都有一个类型,包括数值和函数
Any 超类或顶级类 定义了一些通用的方法,例如equals()、hashCode()和toString()
Any有两个直接子类 AnyVal和AnyRef
AnyVal表示值类型
有9种预定义的值类型
Double Float Long Int Short Byte Char Unit Boolean
Unit 不包含任何信息的值类型 类似 void
Unit只有一个实例值 ()
AnyRef 引用类型 所有非值类型被定义为引用类型
AnyRef 对应 Java中的Java.lang.Object
Nothing是所有类型的子类 Nothing没有对象,没有具体值,可以用来定义一个空类型
类似于Java中的标示性接口(如 Serializable,用来标识该类可以进行序列化)
Null是所有引用类型(AnyRef)的子类,可以赋值给所有的引用类型,
Null有一个唯一的单例值null
val list: List[Any] = List(
"a string",
732, //an integer
'c', //a character
true, //a boolean value
() => "an anonymous function returning a string"
)
值类型转换
Byte Short Int Long Float Double
Char Int
表达式 条件表达式 和 块表达式
块表达式
块表达式为包含在符号{}中的语句块:
scala> val result={
| val a=10
| val b=10
| a+b
| }
result: Int = 20
返回值是最后一条语句的执行结果,不需要 return
如果表达式中没有执行结果,则返回一个Unit对象
scala> val result={
| val a=10
| }
result: Unit = ()
for循环
for(变量<-集合或数组){
方法体
}
例如,循环从1到5输出变量i的值:
scala> for(i<- 1 to 5) println(i)
若不想包括5,可使用关键字until:
scala> for(i<- 1 until 5) println(i)
将字符串“hello”中的字符循环输出:
scala> val str="hello"
scala> for(i<-0 until str.length) println(str(i))
将字符串看做一个由多个字符组成的集合,简化写法:
scala> for(i<-str) println(i)
do while 与while 类似,但是do while 会确保至少执行一次循环
do {
循环体
} while(条件)
scala> do{
| i=i+1
| println(i)
| }while(i<5)
方法与函数
方法是类或对象中定义的成员,而函数是一个对象,可以将函数赋值给一个变量
方法的定义使用def关键字
def 方法名 (参数列表):返回类型={
方法体
}
将两个数字求和然后返回,返回类型为Int
def addNum( a:Int, b:Int ) : Int = {
var sum = 0
sum = a + b
return sum
}
去掉返回类型和return关键字
def addNum( a:Int, b:Int ) = {
var sum = 0
sum = a + b
sum
}
如果方法没有返回值,返回类型可设置为Unit
def addNum( a:Int, b:Int ) : Unit = {
var sum = 0
sum = a + b
println(sum)
}
参数默认值
def addNum( a:Int=5, b:Int ) = {
var sum = 0
sum = a + b
sum
}
方法的调用,通过指定参数名称,只传入参数b
addNum(b=2)
a,b两个参数都传入
addNum(1,2)
函数的定义
(参数列表)=>函数体
定义一个匿名函数,参数为a和b,且都是Int类型,函数体为a+b
( a:Int, b:Int ) =>a+b
如果函数体有多行,放入{}中可以通过一个变量来引用函数,变量相当于函数名称
val f1=( a:Int, b:Int ) =>{ a+b }
函数调用
f1(1,2)
可以没有参数
val f2=( ) =>println("hello scala")
f2()
方法与函数的区别
(1)方法是类的一部分,而函数是一个对象并且可以赋值给一个变量
(2)函数可以作为参数传入到方法中
定义一个方法m1,参数f要求是一个函数
该函数有两个Int类型参数,且函数的返回类型为Int
方法体中直接调用该函数
def m1(f: (Int, Int) => Int): Int = {
f(2, 6)
}
定义一个函数f1:
val f1 = (x: Int, y: Int) => x + y
调用方法m1,并传入函数f1
val res = m1(f1)
println(res)
结果为8
(3)方法可以转换为函数
当把一个方法作为参数传递给其它的方法或者函数时,系统将自动将该方法转换为函数
例如,有一个方法m2
def m2(x:Int,y:Int) = x+y
调用(2)中的m1方法,并将m2作为参数传入,此时系统会自动将m2方法转为函数
val res = m1(m2)
println(res)
也可以手动进行转换 , 在方法名称后加一个空格和一个下划线
val f2=m2 _
val res=m1(f2)
println(res)
集合 可变集合 不可变集合
数组 定长数组 变长数组
//自动推断数组类型
val arr=Array(1,2,3)
或者
val arr=Array[Int](1,2,3)
val arr=new Array[Int](3)
arr(0)=1
arr(1)=2
arr(2)=3
使用for循环遍历数组
val arr=Array(1,2,3)
for(i<-arr){
println(i)
}
变长数组 scala.collection.mutable.ArrayBuffer
//定义一个变长Int类型数组
val arr=new ArrayBuffer[Int]()
//添加三个元素
arr+=1
arr+=2
arr+=3
使用-=符号 删除
删除数组中值为3的元素
arr-=3
如有多个值为3的元素,将从前向后删除第一个匹配的值
在数组arr的下标为0的位置插入两个元素1和2
arr.insert(0,1,2)
从数组arr的下标为1的位置开始移除两个元素
arr.remove(1, 2)
List 可变List 不可变List
val nums: List[Int] = List(1, 2, 3, 4)
在该List的头部追加一个元素1,生成一个新的List
val nums2=nums.+:(1)
在该List的尾部追加一个元素5,生成一个新的List
val nums3=nums:+5
支持合并操作,将两个List合并为一个新的List
val nums1: List[Int] = List(1, 2, 3)
val nums2: List[Int] = List(4, 5, 6)
val nums3=nums1++:nums2
println(nums3)
输出结果
List(1, 2, 3, 4, 5, 6)
可变List scala.collection.mutable.ListBuffer类。
val listBuffer= ListBuffer(1, 2, 3)
val listBuffer= new ListBuffer[Int]()
listBuffer+=1
listBuffer+=2
listBuffer+=3
val listBuffer= ListBuffer(1, 2, 3)
val listBuffer3= ListBuffer(4, 5, 6)
println(listBuffer++listBuffer3)
输出结果
ListBuffer(1, 2, 3, 4, 5, 6)
Map 可变Map 不可变Map 默认为不可变Map
创建一个不可变Map:
val mp = Map(
"key1" -> "value1",
"key2" -> "value2",
"key3" -> "value3"
)
val mp = Map(
("key1" , "value1"),
("key2" , "value2"),
("key3" , "value3")
)
for((k,v)<-mp){
println(k+":"+v)
}
可变Map scala.collection.mutable.Map
val mp = Map(
("key1" , "value1"),
("key2" , "value2")
)
println(mp("key1"))
mp("key1")="value2"
添加元素
mp+=("key3" -> "value3")
或
mp+=(("key3","value3"))
删除一个元素
mp-="key3"
元组 可以存放不同类型对象的集合,元组中的元素不可修改
val t=(1,"scala",2.6)
val t2 = new Tuple3(1,"scala",2.6)
println(t._1)
元组下标从1开始
使用 Tuple.productIterator() 迭代输出元组的所有元素
val t = (4,3,2,1)
t.productIterator.foreach{ i =>println("Value = " + i )}
Set 不可重复 可变集合和不可变集合,默认使用不可变集合
使用可变集合 scala.collection.mutable.Set
val set = Set(1,2,3)
与List集合一样,对于不可变Set进行元素的增加和删除,实际上会产生一个新的Set,原来的Set并没有改变
val set = Set(1,2,3)
//增加一个元素
val set1=set+4
//减少一个元素
val set2=set-3
val site = Set("Ali", "Google", "Baidu") //输出第一个元素
println(site.head) //取得除了第一个元素的所有元素的集合
val set2=site.tail
println(set2)
println(site.isEmpty) //查看元素是否为空
使用++运算符可以连接两个集合
val site1 = Set("Ali", "Google", "Baidu")
val site2 = Set("Faceboook", "Taobao")
val site=site1++site2
println(site)
输出结果
Set(Faceboook, Taobao, Google, Ali, Baidu)
val num = Set(5,8,7,20,10,66)
println(num.min) //输出集合中的最小元素
println(num.max) //输出集合中的最大元素
类和对象
类是抽象的,不占用内存
对象是类的具体实例,占用存储空间
类定义 使用关键字class,类名必须大写
类中的方法用关键字def定义
class User{
private var age=20
def count(){
age+=1
}
}
类不写访问修饰符,默认访问级别为Public
关键字new用于创建类的实例
new User().count()
单例对象
Scala 没有静态方法或静态字段
可以使用关键字object定义一个单例对象
单例对象中的方法相当于Java中的静态方法
可以直接使用“单例对象名.方法名”方式进行调用
单例对象除了没有构造器参数外,可以拥有类的所有特性
定义一个单例对象Person,该对象中定义了一个方法showInfo()
object Person{
private var name="zhangsan"
private var age=20
def showInfo():Unit={
println("姓名:"+name+",年龄:"+age)
}
}
伴生对象
当单例对象的名称与某个类的名称一样时
该对象被称为这个类的伴生对象 ,类被称为该对象的伴生类
类和它的伴生对象必须定义在同一个文件中,且两者可以互相访问其私有成员
class Person() {
private var name="zhangsan"
def showInfo(){
println("年龄:"+Person.age) //访问伴生对象的私有成员
}
}
object Person{
private var age=20
def main(args: Array[String]): Unit = {
var per=new Person()
println("姓名:"+per.name) //访问伴生类的私有成员
per.showInfo()
}
}
get和set方法
默认根据类的属性的修饰符生成不同的get和set方法
val修饰的属性,自动生成一个私有常量属性和一个公有get方法
var修饰的属性,自动生成一个私有变量和一对公有get/set方法
private var 修饰的属性,系统会自动生成一对私有get/set方法,相当于类的私有属性,只能在类的内部和伴生对象中使用。
private[this] 修饰的属性,系统不会生成get/set方法,即只能在类的内部使用该属性
get和set方法并非被命名为getName和setName
而是被命名为name和name_=
JVM不允许在方法名中出现=,因此=被替换成$eq
private String name = "zhangsan";
public String name() {
return this.name;
}
public void name_$eq(String x$1) {
this.name = x$1;
}
除了自动生成get和set方法外,也可以手动编写
class Person {
//声明私有变量
private var privateName="zhangsan"
def name=privateName //定义get方法
def name_=(name:String): Unit ={ //定义set方法
this.privateName=name
}
}
object Test{
def main(args: Array[String]): Unit = {
var per:Person=new Person()
per.name="lisi“ //修改
println(per.name) //读取
}
}
构造器分为主构造器和辅助构造器
主构造器的参数直接放在类名之后,且将被编译为类的成员变量,其值由初始化类时传入
//定义主构造器,年龄age默认为18
class Person(val name:String,var age:Int=18) {
}
object Person{
def main(args: Array[String]): Unit = {
var per=new Person("zhangsan",20)
println(per.age)
println(per.name)
per.name="lisi"//错误,val修饰的变量不可修改
}
}
将参数age设置为私有的,参数name设置为不可修改(val)
class Person(val name:String, private var age:Int) {
}
构造参数可以不带val或var,默认为private[this] val
class Person(name:String,age:Int) {
}
将主构造器设置为私有的,添加private关键字即可
class Person private(var name:String,var age:Int) {
}
可以有任意多个辅助构造器
(1)辅助构造器的方法名称为this
(2)每一个辅助构造器的方法体中必须首先调用其它已定义的构造器
(3)辅助构造器的参数不能使用var或val进行修饰
定义两个辅助构造器
class Person {
private var name="zhangsan"
private var age=20
//定义辅助构造器一
def this(name:String){
this()//调用主构造器
this.name=name
}
//定义辅助构造器二
def this(name:String,age:Int){
this(name)//调用辅助构造器一
this.age=age
}
}
三种调用方式
var per1=new Person//调用无参主构造器
var per2=new Person("lisi")//调用辅助构造器一
var per3=new Person("lisi",28)//调用辅助构造器二
主构造器可以与辅助构造器同时使用
一般辅助构造器的参数要多于主构造器
//定义主构造器
class Person(var name:String,var age:Int) {
private var gender=""
//定义辅助构造器
def this(name:String,age:Int,gender:String){
this(name,age)//调用主构造器
this.gender=gender
}
}
object Person{
def main(args: Array[String]): Unit = {
//调用辅助构造器
var per=new Person("zhangsan",20,"male")
println(per.name)
println(per.age)
println(per.gender)
}
}
抽象类使用关键字abstract定义
(1)抽象类不能被实例化
(2)抽象类中可以定义抽象字段(没有初始化的字段)和抽象方法(没有被实现的方法),也可以定义被初始化的字段和被实现的方法。
(3)若某个子类继承了一个抽象类,则必须实现抽象类中的抽象字段和抽象方法。且实现的过程中可以添加override关键字也可以省略。若重写了抽象类中已经实现的方法,则必须添加override关键字。
定义一个抽象类Person
abstract class Person {
var name:String //抽象字段
var age:Int
var address:String="北京" //普通字段
def speak() //抽象方法
def eat():Unit={ //普通方法
println("吃东西")
}
}
定义一个普通类Teacher,并继承抽象类Person
实现Person中的抽象字段和抽象方法,并重写方法eat()
//继承了抽象类Person
class Teacher extends Person{
//实现抽象字段
var name: String = "王丽"
var age: Int = 28
//实现抽象方法
def speak(): Unit = {
println("姓名:"+this.name)
println("年龄:"+this.age)
println("地址:"+this.address)//继承而来
println("擅长讲课")
}
//重写非抽象方法,必须添加override关键字
override def eat():Unit={
println("爱吃中餐")
}
}
object AppTest{
def main(args: Array[String]): Unit = {
val teacher=new Teacher()
//调用方法
teacher.speak()
teacher.eat()
}
}
特质 trait定义
类似Java的接口 interface
//定义特质(宠物)
trait Pet {
var name:String //抽象字段
var age:Int
def run //抽象方法
def eat: Unit ={ //非抽象方法
println("吃东西")
}
}
定义一个普通类Cat,实现了上述特质Pet(必须实现未实现的字段和方法)
class Cat extends Pet
var name:String="john" { //实现抽象字段
var age:Int=3
def run: Unit = {//实现抽象方法
println("会跑")
}
override def eat: Unit ={ //重写非抽象方法
println("吃鱼")
}
}
若需要实现多个特质,可以通过with关键字添加额外特质
但位于最左侧的特质必须使用extends关键字
trait Animal{
}
trait Runable{
}
//类Dog实现了三个特质
class Dog extends Pet with Animal with Runable{
//省略...
}
在类实例化的时候,可以通过with关键字混入多个特质,从而使用特质中的方法
例如,定义两个特质Runable、Flyable和一个类Bird
//定义两个特质
trait Runable{
def run=println("会跑")
}
trait Flyable{
def fly=println("会飞")
}
//定义一个类
class Bird{
}
在类Bird实例化时混入特质Runable和Flyable
val bird=new Bird() with Runable with Flyable
bird.run //输出结果“会跑”
bird.fly //输出结果“会飞”
上一篇
下一篇
中概互联 恒生互联网 恒生科技 指数简介及比较
指数估值方法
权益类 固收类 现金类资产 区别
指数估值工具
编程式绘图工具mermaid
Spark大数据分析实战 第2章 初识Spark