面向对象编程(中)
scala的包
Scala中包名和源码所在的系统文件目录结构要可以不一致,但是编译后的字节码文件路径和包名会保持一致
scala 自动引入的包
Scala中,以下三个包的内容会隐式引用到每个程序上。
import java.lang._ // in JVM projects, or system namespace in .NET
import scala._ // everything in the scala package
import Predef._ // everything in the Predef object
scala 包使用注意事项
- 在scala中一个文件可以同时创建多个包
- 在scala中一个文件可以在不同的包下,创建多个class,object,trait
- 包也可以像嵌套类那样嵌套使用(包中有包)
- 父包要访问子包的内容时,需要import对应的类等
- 可以在同一个.scala文件中,声明多个并列的package(建议嵌套的pakage不要超过3层)
- 包名可以相对路径也可以绝对路径
package p1 {
//表示在xxx.p1包下创建了Car的class
class Car
package p2 {
// 父包要访问子包的内容时,需要import对应的类等
import Test.name
//表示在xxx.p1.p2包下创建了class Test的伴生类和class Test$的伴生对象
object Test {
// BeanProperty 所在包绝对路径
@_root_.scala.beans.BeanProperty
var name: String = _
def main(args: Array[String]): Unit = {
print("路径xxx/p1/p2/下")
}
}
//表示在xxx.p1.p2包下创建了Test2的class
class Test2
//表示在xxx.p1.p2包下创建了Test4的特质
trait Test3
}
}
包对象
包可以包含类、对象和特质trait,但不能包含函数/方法或变量的定义。这是Java虚拟机的局限。为了弥补这一点不足,scala提供了包对象的概念来解决这个问题 package object 1
- 每个包都可以有一个包对象。你需要在父包中定义它
- 包对象名称需要和包名一致,一般用来对包的功能补充
package object p1 {
var name = "jack" //变量
def sayHi(): Unit = { //方法
println("package object p1()")
}
}
package p2 {
package p3 {
//这个包下创建了 class Test[伴生类] 和 class Test [伴生对象]
object Test {
def main(args: Array[String]): Unit = {
//使用对应的包对象的内容
println("name=" + p1.name)
p1.sayHi()
println(p2.p2_name)
}
}
}
}
//p2包功能补充
package object p2{
var p2_name = "nico" //变量
}
name=jack
package object p1()
nico
scala可见性与修饰符
与java类似
- 当属性访问权限为默认时,从底层看属性是private的,但是因为提供了xxx_$eq()[类似setter]/xxx()[类似getter] 方法,因此从使用效果看是任何地方都可以访问)
- 当方法访问权限为默认时,默认为public访问权限
- private为私有权限,只在类的内部和伴生对象中可用
- protected为受保护权限,scala中受保护权限比Java中更严格,只能子类访问,同包无法访问 (编译器从语法层面控制)
- 在scala中没有public关键字,即不能用public显式的修饰属性和方法
object Test {
def main(args: Array[String]): Unit = {
val clerk = new Clerk
clerk.showInfo()
test(clerk)
// clerk.name = "zs" //默认修饰符 但提供了Publicd的方法
// clerk.age = 10 //访问不到
// clerk.job = "hhh" //访问不到
}
}
//1. 如果我们在同一个文件中,写了 class Clerk 和 object Clerk
// 就把 class Clerk 称为 伴生类, object Clerk 称为伴生对象
//2. 如果我们在同一个文件中,只写了 class Clerk ,那么Clerk就是一个普通的类
//3. 如果我们在同一个文件中,只写了 object Clerk, 那么在底层就会自动生成对应的伴生类 class Clerk, 只是这个伴生类是空..
//4. 伴生对象,可以访问到伴生类的任何方法和属性
class Clerk {
//var name , 底层 name是private ,但是会提供两个public方法 name name_$eq
var name: String = "zs"
//protected var age ,底层 age 是private , 会提供两个pulbic方法 age 和 age_$eq
protected var age: Int = 10
private var color: String = "white"
def showInfo(): Unit = {
println("showInfo() name=" + name + " age= " + age + " color= " + color) //c.color()
}
}
object Clerk {
def test(c: Clerk): Unit = {
c.name = "ls"
c.age = 20
c.color = "red"
//这里体现出在伴生对象中,可以访问c.sal[sal是私有]
println("test() name=" + c.name + " age= " + c.age + " color= " + c.color) //c.color()
}
}
showInfo() name=zs age= 10 color= white
test() name=ls age= 20 color= red
面向对象编程方法——抽象
与java类似,实际上就是把一类事物的共有的属性和行为提取出来,形成一个物理模型(模板)。这种研究问题的方法称为抽象。
如 猫 狗 大象 ==》抽象成动物, 提取属性颜色、年龄 ,提取行为 吃饭、睡觉
在Scala中,通过abstract关键字标记不能被实例化的类。方法不用标记abstract,只要省掉方法体即可。抽象类可以拥有抽象字段,抽象字段/属性就是没有初始值的字段
object Test{
def main(args: Array[String]): Unit = {
val mouse: Mouse = new Mouse
mouse.sayOk()
mouse.cry()
println("ok~~")
}
}
//抽象类
abstract class Animal {
var name: String //抽象的字段
var age: Int // 抽象的字段
var color: String = "black" //普通字段
def cry() //抽象方法
def sayOk(): Unit = {
println("ok")
}
}
//快捷键 alt+enter
class Mouse extends Animal {
override var name: String = "Mouse"
override var age: Int = 10
//以后再scala中,override 有两层含义 1. 重写(override 必须保留)继承普通类 2. 实现 (可以省略) 继承抽象类
// override def cry(): Unit = {
//
// }
def cry(): Unit = {
println(name + " " + age)
}
}
ok
Mouse 10
ok~~
面向对象的三大特征——封装
封装(encapsulation)就是把抽象出的数据/属性和对数据的操作/方法封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(成员方法),才能对数据进行操作。
object Test {
def main(args: Array[String]): Unit = {
val p = new Person
p.getInfo()
}
}
class Person{
private var name: String = _
private var age: Int = _
private var salary: Double = _
def getInfo(): Unit = {
println(name + " " + age + " " + salary)
}
}
面向对象的三大特征——继承
写一个非抽象方法需要用override修饰符,调用超类的方法使用super关键字
object Test{
def main(args: Array[String]): Unit = {
val t = new Teacher
t.age = 10
t.name = "zs"
t.salary = 10.55
t.teach
t.getInfo
}
}
class Person1 {
var name: String = _ //name 属性private 底层调用set方法public
var age: Int = _
var salary: Double = _
def getInfo(): Unit = {
println(name + " " + age + " " + salary)
}
}
//继承
class Teacher extends Person1 {
def teach(): Unit = {
//调用父类方法
super.getInfo()
println("teach english")
}
//重写方法
override def getInfo(): Unit = {
println(name + "___" + age + "___" + salary)
}
}
zs
teach english
zs___10___10.55
面向对象的三大特征——多态及类型检查和转换
- isInstanceOf,测试某个对象是否属于某个给定的类。
- asInstanceOf,将引用转换为子类的引用。
- classOf,获取对象的类名。
object TypeConvert {
def main(args: Array[String]): Unit = {
// 获取对象类型
println(classOf[String])
val s = "zhangsan"
println(s.getClass.getName) //这种是Java中反射方式得到类型
println(s.isInstanceOf[String]) //判断类型 true
println(s.asInstanceOf[String]) //将s 显示转换成String
val p1: Father = new Son1 //子类对象给了一个父类的引用
p1.printName()
p1.asInstanceOf[Son1].say()
p1.asInstanceOf[Son1].printName1()
val p2: Father = new Son2 //子类对象给了一个父类的引用
p2.printName()
//如果希望使用的子类的方法say
p2.asInstanceOf[Son2].say()
p2.asInstanceOf[Son2].printName2()
}
}
class Father {
var name = "king"
def printName(): Unit = {
println("FatherName=" + name)
}
}
class Son1 extends Father {
def say(): Unit = {
println("Son1=" + name)
}
//继承父类 同名方法就得重写
override def printName(): Unit = {
println("Son1Name=" + name)
}
def printName1(): Unit = {
println("Son1Name=")
}
}
class Son2 extends Father {
def say(): Unit = {
println("Son2=" + name)
}
//继承父类 同名方法就得重写
override def printName(): Unit = {
println("Son2Name=" + name)
}
def printName2(): Unit = {
println("Son2Name")
}
}
class java.lang.String
java.lang.String
true
zhangsan
Son1Name=king
Son1=king
Son1Name=
Son2Name=king
Son2=king
Son2Name
Scala中超类的构造
- 类有一个主构器和任意数量的辅助构造器,而每个辅助构造器都必须先调用主构造器
- 只有主构造器可以调用父类的构造器。辅助构造器不能直接调用父类的构造器。在Scala的构造器中,你不能调用super(params)
object SuperDemo {
def main(args: Array[String]): Unit = {
new Emp5()
}
}
class Person5(name: String) { //父类的构造器
println("Person5 主构造器" + name)
}
class Emp5(name: String) extends Person5(name) { // 将子类参数传递给父类构造器,这种写法√
println("子类的主构造器=" + name)
//super(name), 错误不能在主构造器中显式的调用super
def this() {
this("xxx")
//super("abc") // (×)不能在辅助构造器中调用显式父类的构造器
}
}
Person5 主构造器xxx
子类的主构造器=xxx
匿名类
类似java,可以通过包含带有定义或重写的代码块的方式创建一个匿名的子类.
//抽象类,他不能实例化,我们可以通过匿名子类的方式创建一个实例
abstract class Monster {
var name: String
def cry()
}
object NoNameClassDemo {
def main(args: Array[String]): Unit = {
//匿名子类创建对象实例比较灵活.
val monster = new Monster {
override def cry(): Unit = {
println("啊啊啊啊啊")
}
override var name: String = _
}
monster.cry()
val monster1 = new Monster {
override def cry(): Unit = {
println("aaaaaaa")
}
override var name: String = _
}
monster1.cry()
}
}
啊啊啊啊啊
aaaaaaa