第6章、面向对象
6.1 Scala包
(1)基本语法
package 包名
(2)包的三大作用(和java一样)
① 区分相同名字的类
② 当类很多,可以很好的管理类
③ 控制访问范围
6.1.1 包的命名
1)命名规则
值包含数字、字母、下划线。不能以数字开头,也不要使用关键字
2)命名规范
小写字母+小圆点
com.公司名.项目名.业务名
6.1.2 包说明
包名和源文件不需要在同一个地址
用.来分割表示包的层级关系
scala的包可以使用Java的风格,也可以使用另一种风格。scala的风格则可以在一个文件中定义多个包,但是非必要不使用
// scala的另一种包表示方式
package com{
package baidu{
package scala{
}
}
}
// 用嵌套风格定义包
package com{
// 在外层包中定义单例对象
import com.baidu.scala.Inner
object Outer{
var out:String = "out"
def main(args:Array[String]):Unit={
print(Inner) // 需要导入内部包 com.baidu.scala.Inner
}
}
package baidu{
package scala{
// 内层包中定义单例对象
object Inner{
def main(args:Array[String]):Unit={
println(Outer.out)
Outer.out = "outer"
println(Outer.out)
}
}
}
}
}
// 在同一文件中定义多个包
package aaa{
package bbb{
def main(args:Array[String]):Unit={
println("hello")
}
}
}
6.1.3 包对象
在Scala中可以给每个包定义一个同名的包对象,定义在包对象中的成员,作为其对应包下所有class和object共享
package object chapter06{
// 定义当前包共享的属性和方法
val commonValue = "张三"
def commonMethod():Unit={
println(s"我们在${commonValue}家学习")
}
}
package chapter06 //同一个包名,chapter06
object Test02{
def main(args:Array[String]):Unit={
commonMethod() // 同一包下都可以访问
println(commonValue)
}
}
package chapter06{// 嵌套定义的包chapter06
object Test02{
def main(args:Array[String]):Unit={
commonMethod() // 同一包下都可以访问
println(commonValue)
}
}
}
6.1.4 导包说明
1)和Java一样,使用import导入
2)局部导入:什么时候使用,什么时候导入
3)通配符导入 import java.util._
4)给类起别名: import java.util.{ArrayList=>JL}
5)导入相同包的多个类: import java.util.{HashSet,ArrayList}
6)屏蔽类: import java.util.{ArrayList=>_,_}
7)导入包的绝对路径: new _root_.java.util.HashMap
6.2 类和对象
类:可以看出一个模板
对象:表示具体的事物
6.2.1 定义类
1)回顾:Java中的类
如果类是public的,则必须和文件名一致
一般,一个 java有一个public类
2)基本语法
[修饰符] class 类名{
类体
}
scala的类,如果不写修饰符,默认是public
object Test03{
def main(args:Array[String]):Unit={
val student = new Student()
// println(student.name) //!!! 不能访问private属性
println(student.age)
println(student.sex)
student.sex = "female"
println(student.sex)
}
}
// 定义一个类
class Student{
//定义属性
private var name2:String = "李四"
@BeanProperty //自动设置get、set方法
var age:Int = 18
var sex:String = _ // 定义sex的初值为空
}
6.3 封装
把抽象的数据和数据的操作封装在一起
(1)将属性的私有化
(2)提供get、set方法 使用@BeanProperty 生成
6.3.1 访问权限
(1)在Java中分为public、private、protected和默认
(2)在Scala中的默认访问权限为public,但Scala中没有public关键字
(3)private为私有权限,只在类的内部和伴生对象中可用
(4)protected为受保护权限,Scala只能在同类和子类中访问,同包不能访问
(5)private[包名] 只针对指定包名下的类可以访问
package chapter06
object Test04{
def main(args:Array[String]):Unit={
// 创建对象
val person:Person = new Person()
//person.idCard //!!! 私有属性无法访问
//println(person.name) //!!! protected无法访问
println(person.age) // 可以访问
println(person.sex)
val worker:Worker = new Worker() // 定义一个worker
worker.printInfo()
}
}
// 定义一个父类
class Person{
private var idCard:String = "12343213"
protected var name:String = "张三"
var sex:String = "男" // 默认为public
private[chapter06] var age:Int = 18 // 只能在chapter06下才能访问
def printInfo():Unit={
println(s"Person:${idCard},${name},${sex},${age}")
}
}
// 定义一个子类
class Worker extends Person{
override def printInfo():Unit={
println("worker")
//println(idCard) //!!! 报错,私有属性子类不能访问
name = "bob"
age = 25
sex = "male"
}
}
6.3.2 构造器(构造方法)
1)基本语法
class 类名(形参列表){//主构造器
def this(形参列表){
// 辅助构造器,需要调用主构造器
}
def this(形参列表){
// 辅助构造器可以有多个,可有可无
}
}
object Test05{
def main(args:Array[String]):Unit={
val student1 =new Student1()
student1.Student() // 调用Student方法
val student2 =new Student1("张三")
val student3 =new Student1("张三",20)
}
}
// 定义一个类
class Student1(){ // 主构造器
// 定义属性
var name:String = _ // 默认值
var age:Int = _
println("1. 主构造方法被调用")
// 辅助构造方法
def this(name:String){
this() // 直接调用主构造器
println("2. 辅助构造方法被调用")
this.name = name
println(s"name $name , age: $age")
}
def this(name:String, age:Int){
// 调用之前的辅助构造器
this(name)
this.age = age
println(s"name $name , age: $age")
}
// !!!这不是构造方法
def Student1():Unit={
println("这不是构造方法,是普通方法")
}
}
6.3.3 构造器参数
1)说明
Scala的主构造器参数包括三种类型:
(1)未用任何修饰符修饰,该参数就是一个局部变量
(2)var修饰参数,作为类的成员属性使用,可以修改
(3)val修饰参数,作为类只读属性使用,不能修改
object Test06{
def main(args:Array[String]):Unit={
val student2 = new Student2()
student2.name = "张三"
student2.age = 18
println(s"student2:name=${student2.name}")
val student3 = new Student3("张三",12)
println(student3.age)
println(student3.name)
val student4 = new Student4("张三",20)
println(student4.age)
student4.printInfo()
val student5 = new Student5("李四",21)
println(student5.age)
val student6 = new Student6("张三",22,"学校")
println(student6.school)
}
}
// 定义类
// 无参构造器
class Student2{
// 单独定义属性
var name:String = _
var age:Int = _
}
// 定义构造器,构造器的参数name、age
class Student3(var name:String, var age:Int){
// 不需要定义name、age了
}
// 主构造器参数无修饰
class Student4(_name:String, _age: Int){
var name:String = _name
var age:Int = _age
def printInfo(){
println(name)
println(age)
}
}
class Student5(val name:String, val age:Int){
// 不需要定义name、age。 但是值不能更改
}
class Student6(var name:String, var age:Int){
var school:String = _
def this(name:String,age:Int, school:String){
this(name,age)
this.school = school
}
}
6.4 继承
scala只能单继承
构造器调用顺序,父类构造器->子类构造器
1)基本语法
class 子类名 extends 父类名{
// 类体
}
object Test07{
def main(args:Array[String]):Unit={
val student7 = new Student7("张三",12)
// 输出结果
// 1. 父类的主构造器调用
// 3 子类主构造器被调用
val student77 = new Student("a",20,"std001")
// 输出结果
// 1. 父类的主构造器调用
// 3 子类主构造器被调用
// 4 子类的辅助构造器调用
}
}
// 定义一个父类
class Person7 (){
var name:String = _
var age:Int = _
println("1. 父类的主构造器调用")
def this(name:String, age:Int){
this()
print("2,父类的辅助构造器被调用")
this.name = name
this.age = age
}
def printlnfo():Unit={
println(s"Person: $name $age ")
}
}
// 定义一个子类
class Student7(name:String,age:Int) extends Person7{
var stdNo:String = _
println("3 子类主构造器被调用")
def this(name:String , age:Int , stdNo:String){
this(name,age)
println("4 子类的辅助构造器调用")
this.stdNo = stdNo
}
override def printlnfo():Unit={
println(s"Person: $name $age $stdNo")
}
}
// 定义一个子类
class Student7(name:String,age:Int) extends Person7(name,age){ //调用了父类的辅助构造器
var stdNo:String = _
println("3 子类主构造器被调用")
def this(name:String , age:Int , stdNo:String){
this(name,age)
println("4 子类的辅助构造器调用")
this.stdNo = stdNo
}
override def printlnfo():Unit={
println(s"Person: $name $age $stdNo")
}
}
// 输出结果
// 1. 父类的主构造器调用
// 2,父类的辅助构造器被调用
// 3 子类主构造器被调用
// 4 子类的辅助构造器调用
6.5 多态
一种接口可以有多种不同的实现方式
object Test07{
def main(args:Array[String]):Unit={
// 定义一个方法
def personInfo(person:Person):Unit={
person.printInfo()
}
personInfo(new Student())
personInfo(new Teacher())
// 输出结果
// Student
// Teacher
}
}
class Person{
def printInfo(){
println("Person")
}
}
class Student extends Person{
override def printInfo(){
println("Student")
}
}
class Teacher extends Person{
override def printInfo(){
println("Teacher")
}
}
Scala的多态和Java的多态的区别
(1)Java的方法是动态绑定,但是属性是静态绑定
(2)Scala的方法是动态般的,属性也是动态绑定
object Test08{
def main(args:Array[String]):Unit={
val student:Person8 = new Student8()
println(student.name) // 属性的动态般的
student.hello() // 方法的动态般的
// 输出结果
// student
// student
}
}
class Person8{
val name:String = "person"
def hello():Unit={
println("person")
}
}
class Student8 extends Person8{
override val name:String = "student"
override def hello():Unit={
println("student")
}
}
6.6 抽象类
抽象类中可以定义抽象属性和抽象方法
1)基本语法
(1) 定义抽象类: abstract class Person{}
(2) 定义抽象数学 val|var name:String //抽象属性没有初始值
(3) 定义抽象方法 def hello():String // 只声明方法,没有实现
2)继承和重写
(1)如果父类为抽象类,则子类需要将属性和方法实现。
(2)重写抽象方法可以不用加override关键字
(3) 子类对抽象属性进行实现,父类可以用var修饰
抽象类中的val类型,可以赋值,并且不需要重写(val 就相当于常量)
class Test09{
def main(args:Array[String]):Unit={
}
}
// 定义抽象类
abstract class Person9{
// 非抽象属性
val name:String = "person" // 常量可以直接赋值
// 抽象属性
var age:Int
// 非抽象方法
def eat():Unit={ println("吃")}
// 抽象方法
def sleep():Unit
}
// 具体的实现子类
class Student9 extends Person9{
// 实现抽象属性和方法
var age:Int = 18 // 抽象类中可以省略override
def sleep():Unit= { println("student sleep")}
// 重写非抽象属性和方法
override val name:String = "student"
override def eat():Unit={
super.eat() // 调用父类的eat方法
println("student eat")
}
}
6.6.1 匿名子类
并不需要直到这个类叫什么
object Test10{
def main(args:Array[String]):Unit={
// 定义一个匿名子类,该类继承Person0
val person:Person10 = new Person10{
override var name:String = "alice"
override def eat():Unit = println("person eat")
}
println(person.name)
person.eat()
}
}
// 定义抽象类
abstract class Person10{
var name:String
def eat():Unit
}
6.7 单例对象(伴生对象)
Scala是完全面向对象的语言,是没有静态属性和方法。
作者认为static不够面向对象,所以将所有的static有关的全部抽取出来,放到object之中
伴生对象和对象同名,且放在同一个文件里
object Test11{
def main(args:Array[String]):Unit={
}
}
// 定义类
class Student11(val name:String , val age:Int){
printInfo(){
print(Student11.school) // 访问静态属性
println(s"$name , $age ")
}
}
// 伴生对象
// 名字必须完全一样
object Student11{
val school:String = "Baidu" // 定义静态属性
}
object Test11{
def main(args:Array[String]):Unit={
//val student11 = new Student11("张三",11)//!!!报错
// 通过静态方法进行new
val student11 = Student11.newStudent("张三",11)
// 以下两行代码完全等价
val student12 = Student11.apply("张三",12)
val student13 = Student11("张三",13) //相当于调用了apply方法。
}
}
// 定义类, 私有化构造方法,不能进行构造
class Student11 private(val name:String , val age:Int){
printInfo(){
print(Student11.school) // 访问静态属性
println(s"$name , $age ")
}
}
// 伴生对象
// 名字必须完全一样
object Student11{
val school:String = "Baidu" // 定义静态属性
// 定义一个类的对象实例的创建方法
def newStudent(name:String, age:Int):Student11 = {
new Student11(name,age)
}
// apply方法,
// 调用时Student11.apply(...) , 可以写成Student11()
def apply(name:String, age:Int):Student11 = {
new Student11(name,age)
}
}
6.7.1 单例设模式
通过object实现伴生对象
实现方法1:
object Test12{
def main(args:Array[String]):Unit={
val student12 = Student12.getInstance()
val student13 = Student12.getInstance()
}
}
class Student12 private(val name:String, val age:Int){}
object Student12{
private val student:Student12 = new Student("alice",18)
def getInstance():Student12 = student
}
实现方法二:
object Test12{
def main(args:Array[String]):Unit={
val student12 = Student12.getInstance()
val student13 = Student12.getInstance()
}
}
class Student12 private(val name:String, val age:Int){}
object Student12{
private var student:Student12 = _ //先给null
def getInstance():Student12 = {
if(student == null){
// 如果没有对象实例的话,就创建一个
student = new Student("alice",18)
}
student
}
}
6.8 特质(Trait)
Scala中用trait来代替接口。
但是trait中可以有非抽象方法
trait替代Java的接口,也是对单继承的补充
6.8.1 特质声明
1)基本语法
trait 特质名{
// 主体
}
6.8.2 特质基本语法
特质的继承也使用extends关键字
如果有多个特质,就采用with关键字
1)基本语法
没有父类 class 类名 extends 特质1 with 特质2 with 特质3
有父类 class 类名 extends 父类 with 特质1 with 特质2
object Test13{
def main(args:Array[String]):Unit={
val student:Student13 = new Student13()
student.sayHello()
student.study()
println(student.name)
}
}
// 定义一个父类
class Person13{
val name:String = "person"
val age: Int = 18
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 Student13 extends Person13 with Young{
// 重写父类冲突的属性,否则会报错
override val name:String = "student"
// 实现抽象方法
def dating():Unit = println("student13")
def study():Unit = println(s"student $name is studying")
// 重写父类方法
override def sayHello():Unit={
super.sayHello()
println("student say hello")
}
}
6.8.3 特质的混入
把特质 和 类进行混入,分别继承或者实现
object Test14{
def main(agrs:Array[String]):Unit={
val student = new Student14()
student.increase()
student.increase()
}
}
// 再定义一个特质
trait Knowledge{
var amount:Int = 0
def increase():Unit
}
// 定义一个父类
class Person14{
val name:String = "person"
val age: Int = 18
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 Student14 extends Person14 with Young with Knowledge{
override def increase():Unit={
amount += 1
println(s"student $name knowledge increase")
}
// 重写父类冲突的属性,否则会报错
override val name:String = "student"
// 实现抽象方法
def dating():Unit = println("student13")
def study():Unit = println(s"student $name is studying")
// 重写父类方法
override def sayHello():Unit={
super.sayHello()
println("student say hello")
}
}
动态混入,多继承的匿名子类
object Test14{
def main(args:Array[String]):Unit={
// 该匿名子类,既继承了Student14,又实现了Talent
val studentWithTalent = new Student14 with Talent{
override def dancing():Unit = println("student is good at singing")
}
}
}
class Student14{}
trait Talent{
def dancing():Unit
}
6.8.4 特质的叠加顺序(多继承实现顺序)
特质的多继承执行顺序,从后往前叠加
class Student15 extends Person15 with Knowledge with Talent
如果有同名方法,先调用Talent,再调用Knowledge,再调用Person15
trait Knowledge{
def increase():Unit={
println("Knowledge")
}
}
trait Talent{
def increase():Unit={
println("talent")
}
}
class Person15{
}
class Student15 extends Person15 with Knowledge with Talent{
// 重写同名方法
override def increase():Unit={
super.increase() // 输出Talent,从后往前进行叠加
}
}
6.8.5 多继承的复杂情况
菱形继承,钻石继承
如Sub是A和B的子类, 然后A和B同时是C的子类
object Test15{
def main(args:Array[String]):Unit={
val ball = new MyBall()
print(ball.d())
}
}
// 定义一个特质Ball
trait Ball{
def d():String = {
println("ball")
"ballxxx"}
}
// 定义颜色特质
trait ColorBall extends Ball{
var color:String = "red"
override def d():String ={
println("color")
color +" " + super.d()
}
}
// 定义种类特质
trait CategoryBall extends Ball{
var category:String = "foot"
override def d():String = {
println("category")
category + " " + super.d()}
}
// 自定义球类
class MyBall extends CategoryBall with ColorBall{
override def d():String = {
"my ball is a "+ super.d()
}
}
class MyBall extends CategoryBall with ColorBall
// 输出结果
// color
// category
// ball
// my ball is a red foot ballxxx
所以是先调用最右边的Color,然后再调用Category,最后调用共同的父类ball
6.8.5 调用指定的父类方法
object Test15{
def main(args:Array[String]):Unit={
val c:CC = new CC()
c.f()
}
}
class AA{
def f(): Unit = println("A")
}
trait BB{
def f():Unit= println("B")
}
class CC extends AA with BB{
override def f():Unit={
super[AA].f() // 指定调用AA父类的f方法
println("C")
}
}
6.8.6 特质和抽象类的区别
(1)优先使用特质trait,因为trait可以实现多继承
(2)如果体现本质,就可以使用抽象类
(3)trait中不可使用带参数的构造方法,抽象类可以使用带参数的构造方法
6.8.7 特质自身类型
自身类型可实现依赖注入的功能
在trait可以使用其他的类,把其他的类融入自身。
一个trait只能 融入一个其他的类
基本语法
别名:类名 =>
例子:
_:User =>
abc:User =>
_表示通配符,
abc并没有本质的意思
object Test15{
def main(args:Array[String]):Unit={
val user:RegisterUser = new RegisterUser("zhangsan","123")
user.insert()
}
}
// 定义一个用户类
class User(val name:String , val password:String){}
class User2(){
val age = 12
}
//
trait UserDao{
// 把User类 融入 UserDao类,
_:User =>
// 向数据库插入数据
def insert():Unit = {
println("插入数据库: " + this.name)
}
}
// 定义注册用户类
class RegisterUser(name:String, password:String) extends User(name,password) with UserDao{}
// 输出结果
// 插入数据库: zhangsan
6.9 扩展
6.9.1 类型检查和转换
1)说明
obj.isInstanceOf[T] 判断obj是不是T类型
obj.asInstanceOf[T] 将obj强制转换为T类型
classOf 获取对象的类型
object Test17{
def main(args:Array[String]):Unit={
val student:Student17 = new Student17("alice",18)
student.study()
student.sayHi()
println(student.isInstanceOf[Student17])// true
println(student.isInstanceOf[Person17])// true
val stu:Person17 = new Student17("alice",20)
stu.sayHi()
println(stu.isInstanceOf[Student17])// true
println(stu.isInstanceOf[Person17])// true
val newStudent = stu.asInstanceOf[Student17] // 强制类型转换
val person:Person17 = new Person17("alice",20)
println(person.isInstanceOf[Student17])// false
println(person.isInstanceOf[Person17])// true
println(classof[Person17])
}
}
class Person17(val name:String , val age:Int){
def sayHi():Unit={
println("hi from person " + name)
}
}
class Student17(name:String , age:Int) extends Person17(name,age){
def sayHi():Unit={
println("hi from student " + name)
}
def study():Unit={
println("student study")
}
}
6.9.2 枚举类和应用类
1)说明
枚举类: 需要继承Enumeration
应用类:需要继承App
// 定义枚举类对象
object Test18{
def main(args:Array[String]):Unit={
println(WorkDay.Monday)
// 输出 Monday
}
}
object WorkDay extends Enumeration{
val MONDAY = Value(1,"Monday")
val TUESDAY = Value(2,"Tuesday")
}
// 定义一个应用类
// 应用类不需要定义main方法
object TestApp extends App{
println("app start")
type MyString = String // 给类别起别名
val a:MyString = "abc"
}