面向对象
Scala的面向对象思想和Java的面向思想和概念是一致的。
Scala中语法和Java不同,补充了更多的功能。
Scala包
package 包名
三大作用:
- 区分相同名字的类
- 当类很多的时候,可以很好的管理类
- 控制访问范围
包的命名
只能包含数字、字母、下划线、小圆点,但不能用数字开头,也不要使用关键字。
一般是小写字母 + 小圆点
com.公司名.项目名.业务模块名
包说明
包对象
在Scala中可以为每个包定义一个同名的包对象,定义在包对象的中的成员,作为其对应包下所有class 和 object 的共享变量,可以被直接访问。
package object com {
val shareValue= "share"
def shareMethod() = {}
}
导包说明
- 和Java一样,可以在顶部使用import导入,在这个文件中的所有类都可以使用
- 局部导入,什么时候使用,什么时候导入。在其作用范围内都可以使用
- 通配符导入
import java.util._
- 给类起名
import java.util.{ArrayList => JL}
- 导入相同包的多个类
import java.util.{HashSet, ArrayList}
- 屏蔽类
import java.util.{ArrayList => _,_}
- 导入包的绝对路径
import new_root_.java.util.ArrayList
i
类和对象
类:可以看成一个模板
对象:表示具体的事物
定义类
Scala中没有public,一个Scala中可以写多个类
Scala语法中,类并不声明为public,所有这些类都具有公共可见性,即默认就是public
一个Scala源文件可以包含多个类
package chapter06
import scala.beans.BeanProperty
object Test03_Class {
def main(args: Array[String]): Unit = {
// 创建一个对象
val student = new Student()
// 不能通过以下的方式访问name属性,因为它是private
println(student.age)
println(student.sex)
student.setSex("male")
println(student.sex)
student.age = 20
println(student.getAge)
}
}
// 定义一个类
class Student{
// 定义属性
// 属性默认为public
// 增加注解添加getter和setter
private var name: String = "zihoo"
// @BeanProperty
// val age: Int = 18
//
// val sex: String = "male"
// var nullValue: String = _
@BeanProperty
var age: Int = _
@BeanProperty
var sex: String = _
}
封装
封装就是把抽象出的数据和数据的操作封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(成员方法),才能对数据进行操作。
Scala中的public属性,底层实际为private,并通过get方法和set方法对其进行操作。所以Scala并不推荐将其属性设为private,再为其设置public的get和set方法的做法。
但由于很多Java框架都利用反射调用get和set的方法,有时候为了和这些框架兼容,也会通过@BeanProperty注解为类实现get和set的方法。
访问权限
Scala中属性和方法的默认访问权限为public,但是Scala中无public关键字。
private为私有权限,只有类的内部和伴生对象中可用。
protected为受保护权限,Scala中受保护权限比Java中更严格,同类、子类可以访问,同包无法访问。
private[包名] 增加包的访问权限,包名下的其它类也可以使用
package chapter06
object Test04_Access {
def main(args: Array[String]): Unit = {
// 创建对象
val person: Person = new Person()
// protected只能在子类或本类中可以访问
// person.name
println(person.age)
println(person.sex)
person.printInfo()
val worker: Worker = new Worker()
worker.printInfo()
}
}
class Worker extends Person {
override def printInfo(): Unit = {
name = "Worker"
age = 25
sex = "female"
println(s"Worker => {姓名:${name}, 性别:${sex},年龄:${age}}")
}
}
方法
就是定义在类中的函数
def 方法名(参数列表) [: 返回值类型] = {
方法体
}
创建对象
和Java类似
val person: Person = new Person()
当使用修饰符 val
后,表示常量,也就是不可更改的。所有当使用person = new Person()
是不被允许的。但是可以修改person的属性值 person.age = 12
,这样是被允许的。
构造器
和 Java一样,Scala构造对象也需要调用构造方法,并且可以有任意多个构造方法。
Scala的构造器包括:主构造器和辅助构造器
class 类名(形参列表) { // 主构造器
// 类体
def this(形参列表) { // 辅助构造器
}
def this(形参列表) { // 辅助构造器
}
}
辅助构造器,函数的名称this,可以有多个,编译器通过参数的个数及类型来区分
辅助构造方法不能直接构造对象,必须直接或者间接调用主构造方法
构造器调用其他另外的构造器,要求被调用的构造器必须提前声明
package chapter06
object Test05_Constructor {
def main(args: Array[String]): Unit = {
}
}
// 定义一个类
class Animal() {
// 定义属性
var name: String = _
var age: Int = _
println("1. 主构造方法调用")
def this(name: String, age: Int){
// 直接调用著构造器
this()
println("2. 辅助构造方法1被调用")
this.name = name
this.age = age
println(s"name: ${name}, age:${age}")
}
def this(name: String) {
this(name, 20)
println("3. 辅助构造方法2被调用")
}
}
构造器参数
Scala类的主构造器函数的形参包括三种类型:未用任何修饰符、var修饰符、val修饰符
- 未用任何修饰符,这个参数就是一个局部变量
- var修饰符,作为类的成员属性使用,可修改
- val修饰符,作为类的只读属性使用,不可修改
package chapter06
object Test06_ConstructorParams {
def main(args: Array[String]): Unit = {
val model = new ConstructorModel("zihoo", 21, 'M')
model.age = 20
// model.sex = 'W' 常量不可以修改
println(s"model => {age: ${model.age}, sex: ${model.sex}")
}
}
// 定义一个类
class ConstructorModel(name: String, var age: Int, val sex:Char)
继承和多态
class 子类名 extends 父类名 {
类体
}
子类继承父类的属性和方法
scala是单继承
package chapter06
object Test07_Inherit {
def main(args: Array[String]): Unit = {
val student = new Test07Student("zihoo", 21)
val student2 = new Test07Student("gazikel", 21, "20194077")
}
}
// 定义一个父类
class Test07Person {
var name: String = _
var age: Int = _
println("1. 父类的主构造器调用")
def this(name: String, age:Int) {
this()
println("2. 父类的辅助构造器调用")
this.name = name
this.age = age
}
def printInfo( ): Unit = {
println(s"Person => {${name}, ${age}")
}
}
// 定义子类
class Test07Student(name: String, age: Int) extends Test07Person {
var stuNo: String = _
println("3. 子类的主构造器调用")
def this(name: String, age:Int, stuNo:String) {
this(name, age)
println("4. 子类的辅助构造器调用")
this.stuNo = stuNo
}
override def printInfo(): Unit = {
println(s"Student => {${name}, ${age}, ${stuNo}")
}
}
在Java中,属性是静态绑定的。
而在Scala中,属性和方法都是动态绑定的。
package chapter06
object Test08_DynamicBind {
def main(args: Array[String]): Unit = {
val student: Test08Person = new Test08Student()
println(student.name)
student.hello()
}
}
class Test08Person() {
val name:String = "Person"
def hello(): Unit = {
println("Hello Person")
}
}
class Test08Student extends Test08Person {
override val name:String = "Student"
override def hello(): Unit = {
print("Hello Student")
}
}
抽象类
抽象属性和抽象方法
- 定义抽象类
abstract class Person {}
,通过abstract关键字标记抽象类 - 定义抽象属性
val | var name: String
,一个属性没有初始化,就是抽象属性 - 定义抽象方法
def hello(): String
,只声明而没有实现的方法,就是抽象方法
只要存在了抽象属性或抽象方法,那么这个类一定是抽象类。
package chapter06
object Test09_AbstractClass {
def main(args: Array[String]): Unit = {
}
}
// 定义一个抽象类
abstract class Test09Person{
// 非抽象
val name: String = "Person"
def eat(): Unit = {
println("eat")
}
// 抽象
var age: Int
def sleep(): Unit
}
class Test09Student extends Test09Person {
// 实现抽象类和方法
var age: Int = 21
def sleep(): Unit = {
println("Student Sleep")
}
}
匿名子类
和Java一样,可以通过包含带有定义或重写代码块的方式创建一个匿名的子类
package chapter06
// 匿名子类
object Test10_AnnoymousClass {
def main(args: Array[String]): Unit = {
val person: Test10Person = new Test10Person {
override var name: String = "zihoo"
override def eat(): Unit = println("person eat")
}
println(person.name)
person.eat()
}
}
// 定义一个抽象类
abstract class Test10Person{
var name: String
def eat(): Unit
}
单例对象
Scala语言是完全面向对象的语言,所以并没有静态的操作(即在Scala中没有静态的概念)。但是为了能够和Java语言交互(因为Java中有静态概念),就产生了一种特殊的对象来模拟类对象,该对象为单例对象。若单例对象名与类名一致,则称该单例对象这个类的伴生对象,这个类的所有“静态”内容都可以放置在它的伴生对象中声明。
单例对象语法
object Person {
val country: String = "China"
}
单例对象采用object关键字声明
单例对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。
单例对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问。
package chapter06
object Test11_Object {
def main(args: Array[String]): Unit = {
val person: Test11Person = Test11Person.newPerson("zihoo", 21)
// 使用apply
val person2: Test11Person = Test11Person.apply("gazikel", 22)
// 在Scala的底层,如果使用apply方法创建对象的实例,那么我们可以省区aplly调用方法名,如下。
val person3: Test11Person = Test11Person("gazikel_apply", 23)
person3.printInfo()
}
}
// 构造方法私有化
class Test11Person private(var name: String, var age: Int) {
def printInfo(): Unit = {
println(s"Person => {${name}, ${age}, ${Test11Person.school}}")
}
}
object Test11Person {
val school: String = "atguigu"
// 定义一个创建类对象实例的方法
def newPerson(name: String, age: Int): Test11Person = new Test11Person(name, age)
def apply(name: String, age: Int): Test11Person = new Test11Person(name, age)
}
特质
Scala语言中,采用特质trait来代替接口的概念,也就是说,多个类具有相同的特质时,就可以将这个特质独立出来,采用关键字trait声明。
Scala中的trait中 既可以有抽象属性和方法,也可以有具体的属性和方法,一个类可以混入多个特质。这种感觉类似于Java中的抽象类。
Scala引入trait特质,第一可以替代Java的接口,第二个也是对单继承机制的一种补充。
特质声明
trait 特质名 {
trait主体
}
特质基本语法
一个类具有某种性质,就意味着这个类满足了这个特质的所有要素,所以在使用时,也采用了extends关键字,如果有多个特质或存在父类,那么采用with关键字进行连接。
# 没有父类
class 类名 extends 特质1 with 特质2 with 特质3
# 有父类
class 类名 extends 父类 with 特质1 with 特质2 with 特质3
package chapter06
object Test13_Trait {
def main(args: Array[String]): Unit = {
val student: Test13Student = new Test13Student()
student.study()
student.dating()
}
}
class Test13Person{
val name: String = "person"
var age: Int = 21
def sayHello(): Unit = {
println("Hello" + name)
}
}
// 定义一个特质
trait Young {
// 可以声明抽象或非抽象属性
var age: Int
val name: String = "young"
def play(): Unit = {
println("Young people is playing.")
}
def dating(): Unit
}
class Test13Student extends Test13Person with Young {
// 重写冲突的属性
override val name: String = "Student"
override def dating(): Unit = {
println(s"Student ${name} is dating.")
}
def study(): Unit = println(s"Student ${name} is studying")
override def sayHello(): Unit = {
super.sayHello()
println(s"Hello from Student ${name}")
}
}
特质的覆盖
父类和叠加的特征是平等的。当有相同的方法或属性时,后面的特征会覆盖前面的特征。
叠加顺序是从右到左。
如果遇到两个特征继承自同一个特征,则父特征在最右边。
super指的是此特征的叠加顺序的上一级。
如果想让super为父类,则需要写为 super[类名]
特征和抽象类的区别
1.优先使用特质。一个类扩展多个特质是很方便的,但却只能扩展一个抽象类。
2.如果你需要构造函数参数,使用抽象类。因为抽象类可以定义带参数的构造函数,而特质不行。
自身类型
自身类型可实现依赖注入功能
package chapter06
object Test16_TraitSelfType {
def main(args: Array[String]): Unit = {
val register: RegisterUesr = new RegisterUesr("zihoo", "123456")
register.insert()
}
}
// 定义一个用户类
class User(val name: String, val password: String)
trait UserDao {
// 自身属性
_: User =>
def insert(): Unit = {
println(s"insert into db values(${name}, ${password})")
}
}
class RegisterUesr(name: String, password: String) extends User(name, password) with UserDao {
}
扩展
类型检查和转换
obj.isInstanceOf[T]: 判断obj是不是T类型
obj.asInstanceOf[T]: 判断obj强转成T类型
classOf: 获取对象的类名
枚举类和应用类
枚举类:需要继承Enumeration
应用类:需要继承App
Type定义新类型
使用type关键字可以定义新的数据类型名称,本质上就是类型的一个别名。
object Test{
def main(args: Array[String]): Unit = {
type S = String
var v: S = "abc"
def test(): S = "xyz"
}
}