第1阶段:基础语法
第2阶段:面向对象(掌握书写)
第3阶段:高级特性(重点)
第4阶段:并发编程 (了解)
scala语言的定位:
1、在大数据开发中,使用最多的语言:java 针对一些高端企业,会要求开发人员使用scala
2、做为大数据开发(综合应用)人员,需要掌握多门语言:java、python、scala (任意两种)
3、在大家进入就业阶段时,集团通过调研,企业还是使用java
4、掌握书写、能看懂后续spark底层源码(spark底层源代码:scala语言)
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 |
变量的定义:
//var声明
var 变量名:数据类型 = 值
var 变量名 = 值 //简化写法:可以省略数据类型
//val声明
val 变量名:数据类型 = 值
val 变量名 = 值 //简化写法
var和val的区别:
使用var关键字声明的变量,可以在后续代码中对该变量的值进行修改
使用val关键字声明的变量,不可以对该变量的值进行修改(相当于常量)
在scala语言中提供多种定义字符串的方式:
双引号:
//双引号 用于定义普通字符串
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)
scala语言中的运算符:
在scala语言中,有类似于java中的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
在scala语言中允许在定义变量时,使用{}包含一系列表达式
val num1:Int = 10
val num2:Int = 5
val result = {
val sum = num1 + num2
sum //块表达式的结果值(相当于把sum的值返回并赋值给result)
}
println(result) //打印:15
在scala语言中也有:for循环、while循环、do…while循环
for循环语法:for(变量 <- 表达式/数组/集合)
在scala中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
def 方法名 (参数名:数据类型,参数名:数据类型,...) : 返回值类型 = {
//方法体
}
注意:
1、方法中参数的数据类型不能省略
2、方法中的返回值类型可以省略。 scala会自动推断返回值类型
3、方法体中可以不书写return。 默认就是{}块表达式的值。
在scala中方法调用有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 = {
"你好"
}
}
默认参数
//定义方法
def 方法名(参数名1:数据类型=默认值,参数名2:数据类型=默认值) : 返回值类型 = { ... }
//调用方法
方法名()//使用默认值
方法名("熊大") //把"熊大"传递给参数1,参数2使用默认值
带名参数
//方法的参数传递,是按照参数书写的顺序依次传递值。 使用带名参数可以不按照顺序传递参数值
def 方法名(参数名1:数据类型=默认值1,参数名2:数据类型=默认值2) : 返回值类型 = { ... }
//调用方法:
方法名(10) //默认传递给参数1 参数2使用默认值2
方法名(参数名2=10) //指定传递给参数2, 参数1使用默认值1
变长参数
//定义变长参数 scala是在参数数据类型的后面放一个"*",来表示变长参数(可以接收0个或多个参数值)
def 方法名(参数1:数据类型*) : 返回值类型 = { ... }
//调用方法:
方法名(1,2,3) //传递3个参数值
方法名() //传递0个参数值
定义数组的语法:
//标准语法
var 数组名:Array[元素类型] = new Array[元素类型](数组大小)
//简化语法
var 数组名 = new Array[元素类型](数组大小)
//根据指定元素定义数组
var 数组名 = Array(元素1,元素2,元素3,....)
在scala中数组可以分为:
示例:
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))
}
}
示例:
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))//打印数组中的元素
}
}
}
在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"))
}
}
在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"))
}
}
在scala语言中有一种特殊的容器:元组
元组的作用:
定义元组的方式:将多个元素值用小括号括起来,值和值之间使用逗号分隔
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
scala语言中的集合类似于Java集合,也分为三大类:List、Set、Map
List:可以有重复元素,有角标(下标)
Set:唯一元素,没有角标
Map:键值对
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)
}
}
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)
}
}
小结:容器
数组
可变数组:ArrayBuffer
val array:ArrayBuffer[元素类型] = new ArrayBuffer[元素类型]()
array(下标)=元素
不变数组:Array
val array:Array[元素类型]= new Array[元素类型](初始化数组大小)
元组
把一系列不同类型的元素聚集在一起,使用小括号括起来
val tuple = (元素1,元素2,元素3,...) //最多只能存储22个元素
访问元组的中元素: 元组名._序号
tuple._1 //序号从1开始
tuple._22 //最多22个元素
集合:List、Set、Map
面向对象:编程思想。 思想有一些代码的书写体现
面向对象:
在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()
}
}
当对某个类进行实例化时,会自动调用该类中的匹配构造器(构造方法)
在scala语言中,有两种构造器:
在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)
}
}
注意的细节:
伴生对象
在同一个.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)
}
}
继承是面向对象中的概念,用于代码的可重用性。被扩展的类称为:超类或父类,扩展的类称为:子类、派生类
在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("大数据")
}
}
在继承关系中:子类对象可以访问父类中的成员
继承中要注意的细节:
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]修饰符号: 只能在本类、子类中访问 (子类伴生对象中无法访问)
重写的定位:当父类中的成员属性或成员方法,不能满足子类对象的需求时,可以在子类中对父类中的成员进行重写(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)]
抽象:在一个类中声明的成员变量或成员方法不完整(只声明方法,没有具体的方法实现)时,该类会定义为抽象类
在scala语言中,也是使用abstract关键字来修饰抽象
抽象类的特点:
示例:抽象类的使用
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()
}
}
多态:一个事物的多种体现形式。
举例:生活中的狗。 称它为:狗, 称它为:动物
多态的代码形式: 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的这种方式:
示例:强制类型转换,实现父引用访问子类特有成员
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("大数据")
}
}
}
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
}
}
在scala语言中,有一个类似java中接口的对象:Trait
Trait可以用来当做接口使用。 scala中的Trait功能更加强大。
在scala语言中,使用关键字extends实现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()
}
}
在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中的主构造器
示例:在实例对象中混入某个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的好处:增强功能。 不会对原有的代码进行破坏。