面向对象
Scala 的面向对象思想和Java的面向对象思想的概念是一致的。相对于Java的语法,Scala 更加丰富。
1、Scala 包
1.1、基本语法
package 包名
1.2、Scala包的三大作用(与Java语言一致)
- 区分相同名字的类
- 当类很多时,可以很好的管理类
- 控制访问范围
1.3、包的命名
1.3.1、命名规则
只能包含数字、字母、下划线,但不能用数字开头,也不要使用关键字
1.3.2、命名规范
同Java语言
com.公司名.项目名.业务模块名
1.4、包说明(包语句)
说明:Scala有两种包的管理风格,一种方式和Java语言的包管理风格相同,每个源文件一个包(包名和源文件所在路径不要求必须一致)com.公司名.项目名
,另一种风格,通过嵌套的风格表示层级关系:
package com {
package 公司名{
package 项目名 {
}
}
嵌套风格特点:
- 一个源文件可以声明多个package
- 子包中的类可以直接访问父包中的内容,而无需导包
1.5、包对象
在Scala中可以为每个包定义个同名的包对象,定义在包对象中成员,作为其对应包下所有的class和object的共享变量,可以直接被访问。
1.5.1、定义
package object com{
val share = "share"
def shareFunction() = {}
}
1.6、导包
- 和Java语言一致,可以在顶部使用
import
导入,在这个文件中的所有类都可以使用 - 局部导入,什么时候使用,什么使用导入。在其作用范围内都可以使用
- 通配符导入:
import java.util._
(导入该包下的所有类) - 给类起名:
import java.util.{ArrayList=JL}
- 导入相同包的多个类:
import.java.uil.{HashSet, ArrayList}
- 屏蔽类:
import java.util.{ArrayList=> _,_}
- 导入包的绝对路径
2、类和对象
类:可以看成一个模板
对象:表示具体的事务
2.1、类的定义
基本语法
[修饰符] class 类名 {}
实例:
def main(args: Array[String]): Unit = {
val student = new Student()
println(student.age)
println(student.gender)
student.gender = "male"
println(student.gender)
}
}
/**
* 类的定义
*/
class Student {
//属性定义
private val name:String = "merlin"
//Scala 中提供了一个注解@BeanProperty, 能够自动生成java的get、set方法
//Scala 中可以使用Java的API,Java 的很多APi都要求有get、set方法,Scala 为了Java提供了@BeanProperty
// @BeanProperty 不能作用于被private修饰的属性上
@BeanProperty
var age: Int = 10
//Scala可以使用 _ 为属性赋默认值,但是属性必须是被var修饰, 如果使用val修饰,该属性将无法修改,大概率赋值默认值就没意义了
var gender: String = _
}
2.2、封装
封装就是把抽象出的数据和对数据的操作封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作(成员方法),才能对数据进行操作。Java封装实现:
- 将属性进行私有化
- 提供公共的get、set方法,用于获取属性值、设置属性值
Scala中不存在public
关键字,缺省即为public
,底层实际为private
,并通过get、set方法对其操作。所以Scala并不推荐将属性设置为private
,再为其设置public的get、set方法的做法。但是由于Java很多框架都利用反射调用get、set方法,为了和这些框架兼容,也需要为Scala中的属性添加get、set方法,Scala提供了@BeanProperty
注解帮助开发者实现
2.2.1、访问权限
- Scala中不存在
public
关键字,缺省即为public
private
为私有权限,只在类的内部和伴生对象中可用protected
为受保护权限,Scala中受保护权限比Java中更严格。同类、子类可以访问,同包无法访问private[包名]
增加包访问权限,包名下的其他类也可以使用
2.2.2、构造器
Scala中,类的构造器包括:主构造器和辅助构造器
基本语法:
class 类名(形参列表) { //主构建器
def this(形参列表) { //辅助构造器
}
def this(形参列表) { //重载辅助构造器
}
}
说明:
- 辅助构造器,函数的名称:
this
,可以重载,编译器通过参数的个数以及类型来区分 - 辅助构造方法不能直接构建对象,必须直接或者间接调用主构造方法
- 构造器调用其他构造器,要求被调用构造器提前声明
2.2.3、构造器参数
Scala类的主构造器函数的形参包括三种类型:未用任何修饰、var修饰、val修饰
- 未用任何修饰符修饰,这个参数就是一个局部变量
- var 修饰参数,作为类的成员属性使用,可以修改
- val 修饰参数,作为类只读属性使用,不能修改
实例:
object Constructor_Demo {
def main(args: Array[String]): Unit = {
// val human = new Human()
val human = new Human("merlin", 23)
//1、调用主构造函数
//2、调用辅助构造参数一
//3、调用辅助构造器二
//student: merlin, 23
println("====================分割线====================")
val programmer = new Programmer
programmer.age = 23
programmer.name = "merlin"
println(s"programmer: name = ${programmer.name}, age = ${programmer.age}")
val programmer2 = new Programmer2("merlin", 23)
println(s"programmer2: name = ${programmer2.name}, age = ${programmer2.age}")
val programmer3 = new Programmer3("merlin", 23)
// println(s"programmer3: name = ${programmer3.name}, age = ${programmer3.age}") //error 无法调用参数
programmer3.printInfo()
val programmer4 = new Programmer4("merlin", 23)
println(s"programmer4: name = ${programmer4.name}, age = ${programmer4.age}")
}
}
//定义一个类
class Human {
var name: String = _
var age: Int = _
println("1、调用主构造函数")
//定义辅助构造器
def this(name: String) {
this()
this.name = name
println("2、调用辅助构造参数一")
}
//重载辅助构造器,且调用提前声明的构造器
def this(name: String, age: Int) {
this(name)
this.age = age
println("3、调用辅助构造器二")
println(s"human: $name, $age")
}
}
class Programmer {
var name: String = _
var age: Int = _
}
//等价与上面定义
class Programmer2(var name: String, var age: Int) {
}
class Programmer3(name: String, age: Int) {
def printInfo(): Unit = {
println(s"programmer3: name = ${name}, age = ${age}")
}
}
/**
* 使用 val修饰参数, 使用这个方式创建对象后,属性值无法再修改
*/
class Programmer4(val name: String, val age: Int) {
}
2.3、继承和多态
基本语法:
class 子类名 extends 父类名 {}
- 子类继承父类的属性和方法
- scala是单继承
实例:
/**
* 定义一个父类
*/
class Person() {
var name: String = _
var age: Int = _
println("1、执行父类主构造器")
def this(name: String, age: Int) {
this()
this.name = name
this.age = age
println("2、执行父类辅助构造器")
}
def printInfo(): Unit = {
println(s"person name=$name, age=${age}")
}
}
/**
* 定义子类
* - 继承时,会调用父类的构造器
*/
class Manager(name: String, age: Int) extends Person {
var company: String = _
println("3、执行子类主构造器")
def this(name: String, age: Int,company: String) {
this(name, age)
this.company = company
println("4、执行子类辅助构造器")
}
//重写父类方法
override def printInfo(): Unit = {
println(s"manager: name=${name}, age=${age}, company=${company}")
}
}
2.4、 抽象类
2.4.1、抽象属性和抽象方法
基本语法:
- 定义抽象类:
abstract class Person{}
,通过abstract关键字标记抽象类 - 定义抽象属性:
val|var name:String
一个属性没有初始化,就是抽象属性 - 定义抽象方法:
def hello(): String
,只声明而没有实现的方法,就是抽象方法
说明:
- 只要类中存在抽象属性或抽象方法,那么这个类必须定义为抽象类
- 子类重写非抽象属性或方法需要加
override
,实现抽象属性或方法不需要加override
- 子类重写非抽象属性只支持
val
修饰的属性,var
修饰的属性可变,直接重写赋值即可
实例:
/**
* 定义抽象类
*
*/
abstract class Person2 {
//普通属性
val name: String = "merlin"
//抽象属性
var age: Int
//普通方法
def eat(): Unit = {
println("恰饭")
}
//抽象方法
def sleep(): Unit
}
/**
* 定义抽象类的实现类
*/
class Student23 extends Person2 {
//重写普通属性
override val name: String = "warning"
//实现抽象属性
var age: Int = 23
//重写普通方法
override def eat(): Unit = {
println("student eat function")
}
//实现抽象方法
def sleep(): Unit = {
println("睡觉")
}
}
2.5、匿名子类
object Anonymous_Demo {
def main(args: Array[String]): Unit = {
//匿名子类
val person = new Person2 {
override var age: Int = 23
override def sleep(): Unit = {
println("匿名子类睡大觉")
}
}
person.sleep()
}
}
/**
* 定义抽象类
*
*/
abstract class Person2 {
//普通属性
val name: String = "merlin"
//抽象属性
var age: Int
//普通方法
def eat(): Unit = {
println("恰饭")
}
//抽象方法
def sleep(): Unit
}
2.6、单例对象(伴生对象)
Scala语言是完全面向对象的语言,所以Scala中不存在静态的概念。为了与Java语言交互,就产生了一种特殊的对象来模拟类对象。该对象为单例对象。若单例对象名与类名一致,则称该单例对象这个类的伴生对象,这个类的所有”静态“内容都可以放置在它的伴生对象中声明
说明:
- 单例对象采用
object
关键字声明 - 单例对象对应的类称之为伴生类,伴生对象的名称和伴生类名一致,且存在于同一个文件中
- 单例对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问
- 伴生对象和伴生类可以相互访问,即使是
private
修饰的方法和属性
object Companion_Demo {
def main(args: Array[String]): Unit = {
val student = Student11.apply("merlin", 23)
student.printInfo()
val student2 = Student11("merlin", 23)
student.printInfo()
}
}
/**
* 伴生类
* - 私有化构造器,在伴生对象中提供对象实例的创建方法
*/
class Student11 private(val name: String, val age:Int) {
def printInfo(): Unit = {
println(s"student11: name=${name}, age=${age}, school=${Student11.school}")
}
}
/**
* 伴生对象
* - 伴生对象提供了一个特殊的apply方法, 通过类名调用时,可以省略这个apply方法名,直接传入参数列表
* -
*/
object Student11 {
val school = "south"
def apply(name: String, age: Int): Student11 = {
new Student11(name, age)
}
}
2.7、特质(trait)
Scala语言中,采用特质(trait)来代替Java语言中的接口(interface
)概念,也就是说,多个类具有相同的特质是,就可以将这些特质独立出来,采用关键字trait
声明。
Scala中的trait
中即可以有抽象属性和方法,也可以有普通属性和方法,一个类可以混入(mixin)多个特质,就像Java可以实现多个接口。
2.7.1、特质声明
一个类具有某种特质,就意味着这个类满足了这个特质的所有要素
说明:
- 类和特质的关系:使用继承的关系
- 当一个类去继承特质是,第一个连接词是
extends
,后面是with
- 如果一个类在同时继承特质和父类时,应当把父类写在
extends
后
基本语法:
trait trait_name {}
class class_name extends 父类 with 特质1 with 特质2 {}
class class_name2 extends 特质1 with 特质2 {}
2.7.2、特质的混入
- 多特质混入
- 动态混入
object Trait_Demo502_Mixin {
def main(args: Array[String]): Unit = {
val student = new Student502
student.increase()
//动态混入
val student2 = new Student502 with Talent502 {
override def dancing(): Unit = println("dancing")
override def singing(): Unit = println("singing")
}
student2.dancing()
student2.singing()
}
}
trait Knowledge502 {
var amount: Int = 0
def increase() : Unit
}
trait Talent502 {
def dancing(): Unit
def singing(): Unit
}
/**
* 多特质混入
*/
class Student502() extends Person501 with Young with Knowledge502 {
//重写冲突属性Person03
override val name = "student merlin"
override def dating(): Unit = println(s"student ${name} dating. ")
def study(): Unit = println(s"student ${name} study. ")
override def sayHello(): Unit = println(s"student ${name} say hello")
//重写特质Knowledge中的抽象方法
override def increase(): Unit = {
amount += 1
println(s"student name=${name} , amount=${amount}")
}
}
2.7.3、特质叠加
- 默认
super
调用的是混入的最后一个特质中的方法
特质叠加中的钻石问题:
当一个类混入多个特质的时候,scala会对所有的特质及其父特质按照一定的顺序进行排序。此处super.describe()
方法调用的实际上是排好序后的下一个特质中的describe()
方法
特质叠加顺序:
- 列出混入的第一个特质的继承关系,作为临时叠加顺序
- 列出混入的第二个特质的继承关系,并将该顺序叠加到临时顺序之前(已出现的不再重复)
- 将子类放在临时叠加顺序第一位,得到最终叠加顺序
super
指定父类:
super[parent_class_name]
特质与抽象类的区别:
- 优先使用特质,特质可以多混入,抽象类仅可以单继承
- 如果需要构造函数参数,使用抽象类。抽象类可以定义带参数的构造器,特质不行
例:
object Trait_Demo504_Superposition {
def main(args: Array[String]): Unit = {
val ball = new MyBall
println(ball.describe()) // red-foot-Ball
}
}
trait Ball {
def describe(): String = "Ball"
}
trait ColorBall extends Ball {
var color = "red"
override def describe(): String = color + "-" + super.describe()
}
trait CategoryBall extends Ball {
val category: String = "foot"
override def describe(): String = category + "-" + super.describe()
}
/**
* 一个实现类,混入两个继承了同一特质的特质
*/
class MyBall extends CategoryBall with ColorBall {
// override def describe(): String = super.describe()
//super指定父类 - super[parentName]
override def describe(): String = super[ColorBall].describe()
}
2.8、特质自身类型
自身类型可实现依赖注入功能
3、拓展
3.1、类型检查与转换
说明:
obj.isInstanceOf[T]
:判断obj是不是T类型obj.asInstanceOf[T]
:将obj强转为T类型classOf
获取对象的类名
3.2、枚举类与应用类
枚举类:继承Enumeration
应用类:需要继承App