Scala基础编程

Scala编程

  • 第1阶段:基础语法

  • 第2阶段:面向对象(掌握书写)

  • 第3阶段:高级特性(重点)

  • 第4阶段:并发编程 (了解)

scala语言的定位:

1、在大数据开发中,使用最多的语言:java 针对一些高端企业,会要求开发人员使用scala

2、做为大数据开发(综合应用)人员,需要掌握多门语言:java、python、scala (任意两种)

3、在大家进入就业阶段时,集团通过调研,企业还是使用java

4、掌握书写、能看懂后续spark底层源码(spark底层源代码:scala语言)

第1阶段:基础语法

1、基本语法

1.1、数据类型

Scala中的数据类型 说明
Byte 类似于java.lang.Byte
Short 类似于java.lang.Short
Int 类似于java.lang.Integer
Long 类似于java.lang.Long
Float 类似于java.lang.Float
Double 类似于java.lang.Double
Char 类似于java.lang.Character
String 类似于java.lang.String
Boolean 类似于java.lang.Boolean

1.2、变量

变量的定义:

//var声明
var 变量名:数据类型 =var 变量名 =//简化写法:可以省略数据类型    

//val声明
val 变量名:数据类型 =val 变量名 =//简化写法

var和val的区别:

使用var关键字声明的变量,可以在后续代码中对该变量的值进行修改

使用val关键字声明的变量,不可以对该变量的值进行修改(相当于常量)

1.3、字符串

在scala语言中提供多种定义字符串的方式:

  1. 双引号
  2. 插值表达式
  3. 三引号

双引号:

//双引号    用于定义普通字符串
val name:String="字符串"
println(name) //打印:字符串

插值表达式

//插值表达式 :  s""    在一个字符串中引用了其他字符串
val name:String="jackie"
val age:Int=20
val info = s"name=${name},age=${age}"  
println(info) //打印: name=jackie,age=30

三引号

//三引号    用于存储多行文本,例:多行sql语句
 val sql =
      """
        |select
        |*
        |from
        |t_user
        |where
        |user_id ="jackie"
      """.stripMargin
      //stripMargin 用来去除多行字符串前的 "|" 符号
 println(sql)

1.4、运算符

scala语言中的运算符:

  • 算术运算符: + - * / %
  • 关系运算符: == != > < >= <=
  • 逻辑运算符:&& || !
  • 赋值运算符:= += -= *= /= %=

1.4、条件表达式

在scala语言中,有类似于java中的if语句:

  • 单支if
  • 双支if
  • 多支if

在scala语言中提供一种比java中三元运算符更强的表达式: 条件表达式

回顾java中的三元运算符:

变量 = 比较运算 ? 值1 :值2

scala中的条件表达式语法:

val result = if (比较运算表达式) 结果1 else  结果2

scala中的条件表达式强大的地方:

val num:Int = 10
//基本使用
val result1 = if(num > 5) 1 else -1
//混合类型:返回结果可以是不同数据类型
val result2 = if(num > 5) 1 else "error"
//多个情况返回结果值
val result3 = if(num == 10) 0 else if(num > 0) else -1

1.5、块表达式

在scala语言中允许在定义变量时,使用{}包含一系列表达式

val num1:Int = 10
val num2:Int = 5
val result = {
    val sum = num1 + num2
    sum //块表达式的结果值(相当于把sum的值返回并赋值给result)
}
println(result) //打印:15

1.6、for循环

在scala语言中也有:for循环、while循环、do…while循环

for循环语法:for(变量 <- 表达式/数组/集合)

在scala中for循环可以分为:

  1. 简单循环
  2. 嵌套循环
  3. 守卫
  4. for推导式
//简单循环
for(i<- 1 to 10){
    //区间:从1到10(包含10)
}
for(i<- 1 until 10){
    //区间:从1到9(不包含10)   适用于数组的遍历
}
//嵌套循环
//java中的循环语法:
for(int i=1;i<5;i++){
    for(int j=1;j<10;j++){
       ..省略
    }
}
//scala代码更简单
for(i <- 1 to 5; j <- 1 to 10){
    ..省略
}
//守卫
//语法: for(变量 <- 表达式/数组/集合 if 条件表达式){ ... }
for(num <- 1 to 10 if num%3==0){
    //循环时增加了守卫(添加了条件过滤,符合条件的才进入循环体执行)
}
//推导式   使用关键字yield的for循环表达式
//语法: for(变量 <- 表达式/数组/集合) yield 变量 运算符 值
var result = for(num <- 1 to 10) yield num + 10
//result中存储的是: 11,12,13,14,..... 19,20

2、方法

2.1、方法的定义

def 方法名 (参数名:数据类型,参数名:数据类型,...) : 返回值类型 = {
    //方法体
}

注意:

1、方法中参数的数据类型不能省略

2、方法中的返回值类型可以省略。 scala会自动推断返回值类型

3、方法体中可以不书写return。 默认就是{}块表达式的值。

2.2、方法的调用

在scala中方法调用有4种方式:

  1. 后缀调用法
  2. 中缀调用法
  3. 花括号调用法
  4. 无括号调用法

后缀调用法: 和java中的方法调用一样

object MethodDemo{
    def main(args:Array[String]):Unit={
        //后缀调用法: 对象名.方法名(参数)
		val max = Math.max(10,20)
        println(s"最大值=${max}")
    } 
}

中缀调用法: 对象名 方法名 参数

object MethodDemo{
    def main(args:Array[String]):Unit={
        //中缀调用法: 对象名 方法名 (参数1,参数2)     当有多个参数时使用()包含起来
		val max = Math max (10,20)
        println(s"最大值=${max}")
    } 
}

花括号调用法: 当方法仅有一个参数时可以使用花括号调用法

object MethodDemo{
    def main(args:Array[String]) : Unit={
        val result = count{10} //仅适用于方法有一个参数时
        println(result) // 打印:11
    } 
    def count(num:Int) : Int={
        num + 1
    }
}

无括号调用法: 适用于无参方法的调用

object MethodDemo{
    def main(args:Array[String]) : Unit={
        val result = hello //调用时可以省略()
        println(result) // 打印:你好
    } 
    def hello() : String = {
        "你好"
    }
}

2.3、方法的参数

  1. 默认参数

    //定义方法
    def 方法名(参数名1:数据类型=默认值,参数名2:数据类型=默认值) : 返回值类型 = { ... }
    
    //调用方法
    方法名()//使用默认值
    方法名("熊大")  //把"熊大"传递给参数1,参数2使用默认值
    
  2. 带名参数

    //方法的参数传递,是按照参数书写的顺序依次传递值。 使用带名参数可以不按照顺序传递参数值
    def 方法名(参数名1:数据类型=默认值1,参数名2:数据类型=默认值2) : 返回值类型 = { ... }
    
    //调用方法:
    方法名(10) //默认传递给参数1  参数2使用默认值2
    方法名(参数名2=10) //指定传递给参数2, 参数1使用默认值1  
    
  3. 变长参数

    //定义变长参数   scala是在参数数据类型的后面放一个"*",来表示变长参数(可以接收0个或多个参数值)
    def 方法名(参数1:数据类型*) : 返回值类型 = { ... }
    
    //调用方法:
    方法名(1,2,3) //传递3个参数值
    方法名()  //传递0个参数值
    

3、数组

3.1、数组的定义

定义数组的语法:

//标准语法
var 数组名:Array[元素类型] = new Array[元素类型](数组大小)
//简化语法
var 数组名 = new Array[元素类型](数组大小)

//根据指定元素定义数组
var 数组名 = Array(元素1,元素2,元素3....)

3.2、数组的分类

在scala中数组可以分为:

  • 不可变数组
    • 数组的长度不能改变,数组中的元素值可以改变
  • 可变数组
    • 数组的长度可以改变,数组中的元素值可以改变

3.3、数组的使用

示例:

package com.itcast.array
object ArrayDemo{
  def main(args: Array[String]): Unit = {
    //定义数组
    var array:Array[Int]=new Array[Int](3)
    //通过"数组名(下标)"方式给数组元素赋值
    array(0)=100
    array(1)=200
    array(2)=300
    //array(3)=400//执行报错。原因:数组下标越界
    println(array)//打印数组的内存地址
    println(array(0))//打印:100

    //根据指定的元素定义数组
    var array2 = Array("洋洋","婷婷")
    println(s"数组的长度:${array2.length}")
    println(array2(1))
  }
}

3.4、数组的遍历

示例:

package com.itcast.array
object ArrayDemo{
  def main(args: Array[String]): Unit = {
    //根据指定的元素定义数组
    var array = Array("黑马","传智")
    println(s"数组的长度:${array.length}")

    //方式1
    for(num <- array){
      println(num)
    }
      
    //方式2
    for(index <- 0 until array.length ){
      print(index)//打印数组的下标
      println(array(index))//打印数组中的元素
    }
  }
}

3.5、数组常用算法

在scala中,提供了一些方法可以针对数组进行操作

package com.itcast.array
object ArrayDemo {
  def main(args: Array[String]): Unit = {
    //定义数组
    val array = Array(10,20,30)
    //数组元素之和
    val sum = array.sum
    println(s"数组元素之和:${sum}")
    //数组中的最大值
    val max = array.max
    println(s"数组中的最大值:${max}")
    //数组中的最小值
    val min = array.min
    println(s"数组中的最小值:${min}")

    //对数组进行排序(默认:升序)
    val sortArray = array.sorted
    sortArray.foreach(x=>print(x+"\t"))//循环打印数组中的元素
    println("\n----------------------")
    //对数组进行降序
    val sortArray2 = array.sortWith(_>_)
    sortArray2.foreach(x=>print(x+"\t"))
  }
}

3.6、可变数组(数组缓冲)

在scala中,Array属于固定长度的数组。可以改变长度的数组是:ArrayBuffer (也称为数组缓冲)

如果想要ArrayBuffer,需要在程序中使用import导入:import scala.collection.mutable.ArrayBuffer

示例:数组缓冲

package com.itcast.array
import scala.collection.mutable.ArrayBuffer
object ArrayDemo {
  def main(args: Array[String]): Unit = {
    //定义可变数组
    val array = ArrayBuffer[Int]()
    println("数组初始化大小:" + array.length)
    //向数组中添加元素
    array.append(10)
    array.append(100)

    array.foreach(x=>print(x+"\t"))//遍历打印元素
    println("\n--------分割线-------")
    //删除数组中的元素
    //array.remove(1)//按照指定下标删除数组中对应的元素
    //删除数组中多个元素: remove(下标,个数)
    array.remove(0,2)//从指定下标开始删除指定个数的数组元素
    array.foreach(x=>print(x+"\t"))//遍历打印元素
    println("\n--------分割线-------")

    //scala特有写法
    //在scala中所有的运算符都可以看成一个方法
    /* var num:Int =10
       var result =num.+(10)  //值1.运算符(值2)
       println(result)
    */
    //给数组添加一个元素
    array += 20   //后缀调用法:array.+=(10)
    //给数组添加多个元素
    array += (30,300)
    array.foreach(x=>print(x+"\t"))
    println("\n--------分割线-------")
    //删除指定的元素
    array -= 20  //删除一个元素
    array -= (30,300) //删除多个元素
    println("\n--------分割线-------")
    array.foreach(x=>print(x+"\t"))
  }
}

4、元组

在scala语言中有一种特殊的容器:元组

元组的作用:

  1. 将固定数量的元素组合在一起(弊端:最多可以存储22个元素)
    • 可以将元组作为一个整体传递
  2. Map集合的key-value可以使用元组构建

定义元组的方式:将多个元素值用小括号括起来,值和值之间使用逗号分隔

val tuple = ("hadoop",13333.33,true) //元素类型可以不同,但最多存储22个元素
val tuple1 = new Tuple1(10)//只能存储1个元素
val tuple2 = new Tuple2(10,20)//必须存储2个元素
val tuple3 = new Tuple3(10,20,30)
val tuple22 = new Tuple22(1,2,3,4,5,6,7,8,9,.......20,21,22) //存储22个元素

获取元组中的值:使用下划线+序号 格式: tuple._1

val tuple = ("hadoop",13333.33,true)
println(tuple._1) //打印:hadoop
println(tupel._3) //打印:true

5、集合

scala语言中的集合类似于Java集合,也分为三大类:List、Set、Map

List:可以有重复元素,有角标(下标)

Set:唯一元素,没有角标

Map:键值对

5.1、List集合

List集合:用来存储元素,可以存储重复元素,带有角标,可以通过角标访问

List集合可以分为:

  • 可变List集合
    • 集合的大小可以改变,集合中的元素值可以改变
  • 不可变List集合
    • 集合的大小不可以改变,且集合中的元素值也不可以改变

List集合的特点:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UW6w8HGg-1625127612697)(assets/1584592026608.png)]

不可变List集合: List

package com.itcast.list

object ListDemo1 {
  def main(args: Array[String]): Unit = {
      //定义不可变的List集合
      val list1 = List[Int](1,2,3)
     // println(s"集合的初始化大小:${list1.length}")
      //说明:当向list集合中添加新元素后,会重新生成新的List集合
       //验证:List集合中的元素是否可以改变?
      //list1(0)=100 //编译报错

//      println(list1.head)//打印头部: 1
//      println(list1.tail)//打印尾部: (2,3)

      //向集合中添加元素
      //val newList1= list1.+:(100) //添加新元素后,会生成新的集合对象
      //注意: +: 是向集合头部添加元素,所以要添加的元素必须书写在+前面
      //val newList1= 100 +: list1 //+:是向List集合的头部添加元素
      val newList1= 100 :: list1 // :: 和  :+  功能相同

      //向List集合的尾部添加元素
      val newList2 = list1 :+ 200 //要添加的元素必须书写在+后面
//      println(list1)
//      println(newList1)
//      println(newList2)

      //集合添加另一个集合
      val list2 = List(10,20)
      val list3 = List(99,88)
      //
//      val newList3 = list3 ++:  list2
        val newList3 = list3 ::: list2
      println(newList3)
  }
}

可变List集合:ListBuffer ,使用时需要导入:import.scala.collection.mutable.ListBuffer

package com.itcast.list

import scala.collection.mutable.ListBuffer

object ListBufferDemo1 {
  def main(args: Array[String]): Unit = {
     //定义可变List集合
      var list1 = ListBuffer[String]()
      println("集合的初始化大小:"+list1.length)

     //添加元素
     list1.append("新元素1")
     list1.append("新元素2")//元素添加到尾部
     list1.append("新元素3")//元素添加到尾部
     println(list1)
     //元素添加到头部
     list1.insert(0,"新元素4")//把元素插入到指定位置
     list1.insert(0,"itcast","hadoop")
     println(list1)

    //删除元素
    list1.remove(0)//删除一个元素
    list1.remove(0,2)//删除多个元素
    println(list1)


    //添加元素
    list1 += "jackie"
    println(list1)
    //删除元素
    list1 -= "jackie"
    println(list1)

    //合并两个集合
    var list2 =ListBuffer[Int](100,200)
    var list3 =ListBuffer[Int](300,400)
    // ++= 把list2集合添加到list3集合中(添加到头部位置)
    var newList = list2 ++=: list3
    println(newList)
    println(list2)
    println(list3)
  }
}

5.2、Map集合

Map集合,用来存储key-value元素

定义Map集合:

//语法1:
val map = Map(key->value , key->value , .....)

//语法2:使用元组
val map = Map((key,value),(key,value),(key,value),....)

针对Map进行操作时,和java语言一样,都是通过key来获取相应的value值

不可变Map集合的使用

package com.itcast.map

object MapDemo1 {
  def main(args: Array[String]): Unit = {
     //定义不可变Map集合
     val map:Map[String,String]=Map("sh"->"ShangHai","bj"->"BeiJing")
     //添加元素 (会生成新的集合)
     val newMap1=map + ("hz"->"HangZhou")
     println(map)
     println(newMap1)
    //修改元素
    val newMap2 = map + ("sh"->"shanghai") //会覆盖相同key下的value元素
    println(newMap2)
    //删除元素
    val newMap3 = map - "bj"
    println(newMap3)
    //获取value
    val v:Option[String] = map.get("sh")
    println(v)
    /*
    * Option选值类型:  只有两种值
    * 1、None    没有值存储
    * 2、Some(值)  有值存储
    * */
  }
}

可变的Map集合

package com.itcast.map

import scala.collection.mutable
import scala.collection.mutable.Map

object MapDemo2 {
  def main(args: Array[String]): Unit = {
    //定义可变的Map集合
    //注意:可变和不可变Map都是使用:Map作为类型
    val map: mutable.Map[String, String] = mutable.Map(("sh", "ShangHai"), ("bj", "BeiJing"))

    //添加元素
    // map.put("hz","HangZhou")
    map += ("hz" -> "HangZhou") //要求使用 key->value 格式
    println(map)
    //修改元素   语法格式: 集合对象(key) = 新value
    map("sh") = "shanghai"
    println(map)

    //删除元素
    map.remove("bj")
    map -= ("hz")
    println(map)
  }
}

小结:容器

  1. 数组

    • 可变数组:ArrayBuffer

      val array:ArrayBuffer[元素类型] = new ArrayBuffer[元素类型]()
      
      array(下标)=元素
      
    • 不变数组:Array

      val array:Array[元素类型]= new Array[元素类型](初始化数组大小)
      
  2. 元组

    • 把一系列不同类型的元素聚集在一起,使用小括号括起来

      val tuple = (元素1,元素2,元素3...) //最多只能存储22个元素
      
    • 访问元组的中元素: 元组名._序号

      tuple._1  //序号从1开始
      tuple._22  //最多22个元素
      
  3. 集合:List、Set、Map

    • 可变集合 :集合的大小可以改变,元素值也可以改变
      • ListBuffer
      • mutalbe.Map
    • 不可变集合: 集合的大小不可以改变,元素值也不可以改变
      • List
        • 当向List集合添加元素时,会生成新的集合
      • Map
        • 当向Map集合添加元素时,会生成新的集合

第2阶段:面向对象

面向对象:编程思想。 思想有一些代码的书写体现

面向对象:

  • 封装
  • 继承
  • 多态
  • 构造
  • 访问权限
  • 接口
  • 抽象类

1、类和对象

在scala也是使用class关键字来定义类

class Person{
    //属性
    val id:String = "9527"
    var age:Int = 24
    var name:String = "jackie"
    //方法
    def sayHello() : Unit ={
        println("你好!")
    }
}
object TestPerson{
    def main(args:Array[String]) : Unit ={
        //实例化Person对象
        val p = new Person()
        println(p)
        //p.id = "9966"  //无法修改val修饰的属性
        p.age = 25 //可以修改var修饰的属性
        println(s"name=${p.name},age=${p.age},id=${p.id}")
        //调用方法
        p.sayHello()
    }
}

2、构造器

当对某个类进行实例化时,会自动调用该类中的匹配构造器(构造方法)

在scala语言中,有两种构造器:

  1. 主构造器
  2. 辅助构造器

在scala中每个类都有一个主构造器,主构造器和书写的类交织在一起

//主构造器:无参
class Person{
  ...省略 
}
//主构造器:有参
class Student(var name:String){
  ...省略 
}

示例:构造器

//在定义类时,主构造器随着类的书写也已经存在
class Person{
  println("Person类中的主构造器...")
  var name:String="jackie"
  println("还在继续执行Person类中的主构造器!")
}
object TestPerson{
  def main(args:Array[String]) : Unit ={
    //实例化Person对象
    val p = new Person() //调用主构造器
  }
}

说明:主构造器会执行类中书写的所有的可执行语句

在scala语言中,每一个类都必须存在一个主构造器(一定存在),同时可以存在任意个辅助构造器(可以存在)

示例:辅助构造器(可以重载)

//在定义类时,主构造器随着类的书写也已经存在
class Person {
  println("Person类中的主构造器...")
  var name: String = _ //占位符。 编译器会根据变量的具体类型赋予相应的初始值

  //辅助构造器
  def this(name: String) {
    this()//this()必须书写在辅助构造器中的第一行
    println("开始执行辅助构造器.....")
    this.name = name
  }
}

object TestPerson {
  def main(args: Array[String]): Unit = {
    //实例化Person对象
    val p = new Person("黑马") //调用辅助构造器
    println(p.name)
  }
}

注意的细节:

  • 类中的主构造器可以是有参的,也可以是无参的
  • 类中辅助构造器可以有任意个,但是每一个辅助构造器中的第1行代码位置必须书写:调用主构造器

3、伴生对象:object

伴生对象

  • 在同一个.scala文件中,存在一个和定义的类同名的object

    //前提:必须是在同一个.scala文件中
    class Person{ .... }
    object Person{ ... }
    
  • object是class的伴生对象

  • class是object的伴生类

伴生类和伴生对象的特点: 可以相互访问,包含访问私有成员

示例:伴生对象

//伴生类
class Person{
    //私有成员变量
    private var name:String = _ 
}
//伴生对象   必须和类名相同
object Person{
    def main(args: Array[String]): Unit = {
    	var p = new Person()
    	p.name="小明"  //可以访问伴生类中的私有成员变量
   	 	println(p.name)
    }
}

伴生对象的使用:apply方法

在学习数组和集合时,我们使用: var array = Array(10,20,30) var list = ListBuffer(100,200)

使用的Array(10,20,30)时,其实使用的并不是数组或集合的本身,而是使用Array、ListBuffer类的object伴生对象

伴生对象的apply方法

//伴生类
class Person {
  var name: String = _
  def this(name:String)={
    this()
    this.name = name
  }
}

//伴生对象
object Person {
  def apply: Person = {
    new Person()
  }

  def apply(name:String): Person = {
    new Person(name)
  }
}

//测试
object TestPerson{
  def main(args: Array[String]): Unit = {
     val p1 = Person
     println(p1)
     val p2 = Person("jackie")
     println(p2)
     println(p2.name)
  }
}

4、继承

继承是面向对象中的概念,用于代码的可重用性。被扩展的类称为:超类或父类,扩展的类称为:子类、派生类

在scala语言中和java一样,使用extends关键字来书写继承关系

示例:Scala中的继承

//父类
class Person {
  var name: String = _
  var age : Int = _
}
//子类
class Student extends Person{
  def study(course:String): Unit ={
    //继承了父类的属性
    println(s"${name}在学习${course}")
  }
}

//测试
object Test{
  def main(args: Array[String]): Unit = {
     val stu = new Student()
     stu.name="小明"
     stu.age=22
     stu.study("大数据")
  }
}

在继承关系中:子类对象可以访问父类中的成员

继承中要注意的细节:

  1. 父类中使用private修饰的成员变量、成员方法,子类不能继承。 私有的成员只能父类自己访问
  2. 当使用final修饰符时:
    • final修饰类: 类不能被继承
    • final修饰成员方法: 方法不能被重写
    • final修饰成员变量:变量不能被重写

5、访问权限

Scala 访问修饰符基本和Java的一样,分别有:private,protected,public

在scala语言中,成员没有使用关键字进行修饰时,默认就是:public

示例:私有成员

package com.itcast.oop.demo4

class Person{
  //私有成员: 允许伴生对象访问的
  private var name:String = "小明"

  //私有成员:不允许伴生对象访问(只能在本类中使用)
  private[this] var userCard:String="9527"

}
//伴生对象
object Person{
  val person:Person = new Person()

  def demo(): Unit ={
    person.name="小王"
    //person.userCard=""//无法访问使用:private[this]修饰的成员
  }
}

示例:受保护成员

package com.itcast.oop.demo5

//父类
class Person{
  //允许子类访问
  protected var name:String="小明"

  //允许子类访问, 但是不允许子类的伴生对象访问
  protected[this] var userCard:String="9527"
}

//子类
class Student extends Person{
  def test(){
      name ="小五" //可以访问父类中使用protected修饰的成员变量
      println(name)

      userCard="998998"
      println(userCard)
  }

}
//子类的伴生对象
object Student{
  def main(args: Array[String]): Unit = {
//      new Student().test()

       val stu = new Student()
       stu.name="jackie" //name使用protected修饰
       println(stu.name)
      //stu.userCard //报错。  userCard使用protected[this]修饰
  }
}

小结:

private修饰符号: 只能在本类中或当前类的伴生对象中访问

private[this]修饰符号:只能在本类中访问

protected修饰符号:可以在本类、子类、子类伴生对象中访问

protected[this]修饰符号: 只能在本类、子类中访问 (子类伴生对象中无法访问)

6、重写

重写的定位:当父类中的成员属性或成员方法,不能满足子类对象的需求时,可以在子类中对父类中的成员进行重写(override)

示例:重写

package com.itcast.oop.demo6

//父类
class Father{
   //定义不可修改的变量
   val core:String="android-1"
   var ram:Int=1024

  //方法
  def call: Unit ={
    println("打电话...")
  }
}
//子类
class Child extends Father{
  //修改core  (重写父类中的val成员变量)
  override val core:String = "android-100"
  ram=2048 //针对于var修饰的父类成员变量,可以直接进行修改

  //重写父类中的方法
  override def call: Unit ={
      println("开启视频中....")
      super.call //为了代码重复性,建议使用super.父类方法
  }

  def testSuperField: Unit ={
     println(super.ram)
  }

}
//测试
object Test {
  def main(args: Array[String]): Unit = {
      var child = new Child()
      child.testSuperField  //执行报错。 原因:scala不支持使用super.父类成员属性
//      child.call
//      println(child.core)
//      println(child.ram)
  }
}

补充:在scala语言中为什么不能使用super.成员属性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hfquyeHM-1625127612699)(assets/1584603842645.png)]

7、抽象类

抽象:在一个类中声明的成员变量或成员方法不完整(只声明方法,没有具体的方法实现)时,该类会定义为抽象类

在scala语言中,也是使用abstract关键字来修饰抽象

抽象类的特点:

  1. 抽象类不能实例化
  2. 抽象类必须有子类继承,并在子类中重写抽象成员
    • 在子类中重写抽象方法、抽象成员变量时,可以省略override
  3. 在抽象类中,允许存在非抽象内容

示例:抽象类的使用

package com.itcast.oop.demo7

//抽象类
abstract class Person{
  //定义抽象属性
  var name:String
  //定义非抽象属性
  var age:Int = _

  //定义抽象方法
  def study(content:String) //没有具体的方法体实现

  //定义非抽象方法
  def sayHello(): Unit ={
    println("Hello")
  }
}
//子类  继承抽象类并重写抽象类中的抽象成员
class Student extends Person{
  //重写抽象属性
  var name: String = "小明"
  //重写时,可以省略override关键字
  override def study(content: String): Unit = {
    println(this.name +"学习"+content)
  }
}
//测试
object Test {
  def main(args: Array[String]): Unit = {
       val stu = new Student()
       println(stu.name)
       stu.study("烹饪")
       stu.age=20
       println(stu.age)
       stu.sayHello()
  }
}

8、isInstanceOf和asInstanceOf

多态:一个事物的多种体现形式。

举例:生活中的狗。 称它为:狗, 称它为:动物

多态的代码形式: var 父对象:父类类型 = new 子类()

示例:父类对象指向子类对象(多态)

package com.itcast.oop.demo8

//父类
class Person {
  var name: String = _

  //吃
  def eat(food: String): Unit = {
    println("吃" + food)
  }
}

//子类
class Student extends Person {
  def study(course: String): Unit = {
    println(s"${name}在学习${course}")
  }

  //子类重写父类的方法
  override def eat(food: String): Unit = {
    println(s"${name}在吃${food}")
  }
}

//测试
object Test {
  def main(args: Array[String]): Unit = {
    //多态的体现: 父类变量引用子类对名
    var person: Person = new Student()
    person.name = "小明"
    person.eat("大闸蟹")
  }
}

在实例化子类对象时,赋值给父类变量后,父引用是无法去调用子类对象中特有的成员方法。

问题:父引用如何去调用子类对象中特有的成员方法?

答案:类型转换

回顾:java中的类型转换

if( object instanceof 类型 ){

子类对象 = **(子类类型)**object //(子类类型)父引用

}

在scala语言中,也有类似java的这种方式:

  • 判断是否为某个类型: object.isInstanceOf [ 类型 ]
  • 类型强制转换: object.asInstanceOf [ 类型 ]

示例:强制类型转换,实现父引用访问子类特有成员

package com.itcast.oop.demo8

//父类
class Person {
  var name: String = _

  //吃
  def eat(food: String): Unit = {
    println("吃" + food)
  }
}

//子类
class Student extends Person {
  def study(course: String): Unit = {
    println(s"${name}在学习${course}")
  }

  //子类重写父类的方法
  override def eat(food: String): Unit = {
    println(s"${name}在吃${food}")
  }
}

//测试
object Test {
  def main(args: Array[String]): Unit = {
    //多态的体现: 父类变量引用子类对名
    var person: Person = new Student()
    person.name = "小明"
    person.eat("大闸蟹")

    //强制类型转换
    //判断person对象是否为Student的实例
    if(person.isInstanceOf[Student]){
      //把person对象转换为Student对象实例
      var stu:Student = person.asInstanceOf[Student]
      stu.study("大数据")
    }
  }
}

9、getClass和classOf

isInstanceOf只能判断出对象是否为指定类以及其子类的对象,并不能精确的判断出:对象就是指定类的对象

如果要精确的判断出对象就是指定类的对象时,需要使用:getClass 、classOf

getClass:可以精确获取对象的类型。 例:var p:Person p.getClass 的类型:Person

classOf:可以精确获取类型。 例: classOf[Person] 获取到的是Person类的类型

示例:

package com.itcast.oop.demo9

//父类
class Person {}
//子类
class Student extends Person {}

object Test {
  def main(args: Array[String]): Unit = {
    //多态
    val person : Person = new Student()

    println(person.isInstanceOf[Person]) //true

    println(person.getClass == classOf[Person]) //false
    println(person.getClass)
    println(classOf[Person])

    println(person.getClass == classOf[Student]) //true
  }
}

10、trait(特质)

在scala语言中,有一个类似java中接口的对象:Trait

Trait可以用来当做接口使用。 scala中的Trait功能更加强大。

在scala语言中,使用关键字extends实现Trait接口

10.1、将Trait当做接口使用

示例:接口的使用

package com.itcast.demo10

//接口
trait TraitDemo1 {
   def sayHello(content:String) //抽象方法
}
//子类继承Trait接口, 且重写抽象方法
class Child extends  TraitDemo1{
  //重写抽象方法,可以省略override关键字
  override def sayHello(content: String): Unit = {
     println(s"你好,${content}")
  }
}
//测试
object Test{
  def main(args: Array[String]): Unit = {
      //实例化Child对象
     val child = new Child()
     child.sayHello("小明")
  }
}

在把Trait当作接口使用时,需要注意:

Scala中不支持对类进行多继承,但是支持多重继承Trait。

多重继承Trait的语法格式: 使用关键字with,关联多个Trait

trait MyTrait{ ... }
trait TraitOne {...}
trait TraitTwo {...}
class TraitChild extends MyTrait with  TraitOne with TraitTwo { ... }

示例:多重继承Trait

package com.itcast.demo10

trait TraitOne{
  def hello()
}
trait  TraitTwo{
  def hello()
  def eat(food:String)
}
//子类继承多个Trait
class TraitChild extends TraitTwo with TraitOne{
  override def hello(): Unit = {
    /*为什么子类可以多重继承Trait?
      原因: 
      多个Trait中,只针对方法进行了声明,并没有任何方法体的实现,
      在子类继承多个Trait时,针对性进行重写(在子类中完成了功能)
    */
    println("你好")
  }

  override def eat(food: String): Unit = {
    println("吃"+food)
  }
}

object TraitDemo2 {
  def main(args: Array[String]): Unit = {
      //实例化子类
      val child = new TraitChild()
      child.eat("肉")
      child.hello()
  }
}

10.2、Trait的应用

在Trait中,除了可以书写抽象方法外,还可以书写:抽象字段、非抽象方法、非抽象字段

10.2.1、Trait的构造器

示例:Trait中的构造器

//在每一个Trait中都只存在一个主构造器,且构造器只能是无参的
package com.itcast.demo10

trait TraitOne{
  println("开始执行TraitOne的主构造器")
}
trait  TraitTwo{
  println("开始执行TraitTwo的主构造器")
  def eat(food:String)
}
//子类继承多个Trait
class TraitChild extends TraitTwo with TraitOne{
  //重写抽象方法
  override def eat(food: String): Unit = {
    println("吃"+food)
  }
}
//测试
object TraitDemo2 {
  def main(args: Array[String]): Unit = {
      //实例化子类
      val child = new TraitChild()
  }
}

运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ufKjDsoK-1625127612700)(assets/1584610985618.png)]

当子类继承多个Trait时,Trait构造器的执行顺序:从extends向后,依次从左到右调用每一个Trait中的主构造器

10.2.2、使用Trait增强功能

示例:在实例对象中混入某个Trait 语法:val 变量 = new 类名() with Trait

//实现的语法: val 变量 = new 类名()  with Trait
package com.itcast.demo10
//父Trait
trait TraitLogger{
  //记录日志
  def log(msg:String)={
    println(msg)
  }
}
//子类
class LoggerChild extends TraitLogger{
   //子类特有的方法
   def hello(msg:String): Unit ={
      log(":: 测试")
   }

  //当不使用混入的Trait时,可以使用方法重写的形式
  //弊端:破坏原有的代码体
  override def log(msg: String): Unit = {
     println("重写:"+msg)
  }
}
//测试
object TraitDemo3 {
  def main(args: Array[String]): Unit = {
      //实例化LoggerChild
       val child = new LoggerChild() with MyLogger
       /*使用混入Trait的好处:不破坏原有的代码体。仅是增强功能*/

       child.log("test") //子类对象使用父类中已有的功能

       //child.hello("你好")
  }
}

//定义一个Trait,继承TraitLogger (增强功能)
trait MyLogger extends TraitLogger{
  override def log(msg: String): Unit = {
    println("logger:"+msg)
  }
}

在实例化对象时,混入Trait的好处:增强功能。 不会对原有的代码进行破坏。

你可能感兴趣的:(大数据,scala)