继承的概念和基本语法
定义:在原有类的基础上定义一个新类,原有类称为父类,新类称为子类。
class 子类名 extends 父类名 { 类体 }
好处:复用代码和实现多态。复用代码:子类可以继承父类的特性。多态 : 子类可以在自己内部实现父类没有的特性。
语法: 假设定义Parents为父类,C1为子类,通过关键字extends子类便可以继承父类的特性
继承的好处之复用代码
那一个类继承了另一个类之后,有什么好处呢?
一旦我们完成了继承,就可以直接在子类的对象中调用父类的方法。
object Class01 {
class Boss {
def power():Unit = {
println("Boss有权力....")
}
var car ="豪车"
}
class NoBody extends Boss{
}
def main(args: Array[String]): Unit = {
val b = new NoBody()
println(b.car)
b.power()
}
}
继承的方法重写
当子类从父类继承的方法不能满足需要时,子类需要有自己的行为,怎么办?此时使用使用 override 可以重写父类的方法。
object Class01 {
class Boss {
def power():Unit = {
println("Boss有权力....")
}
var car ="豪车"
}
// 子类 继承 父类
class NoBody extends Boss{
//子类自己的方法,与父类方法不同名
def run():Unit = {
}
override def power(): Unit = {
println("老莫,我想吃鱼了")
}
}
def main(args: Array[String]): Unit = {
val b = new NoBody()
println(b.car)
b.power()
b.run()
}
}
继承与多态
面向对象的三个特点:_封装 继承 多态 。同一操作作用于不同的对象, 可以有不同的解释,产生不同的执行结果,这就是多态性。
通过代码来理解多态-写一个函数,它的参数类型是父类。
/*
*1.类和对象
* 2.new 创建对象, new XXXX()
* 3.构造函数,构造器
* 4.辅助构造器 this
* 5.private,override,toString,this
*
*
* 父类和子类的构造器调用顺序:先调用父类的构造器,再调用子类的构造器
*/
object Class02 {
class Father() {
println("Father 构造器被调用")
}
class Son extends Father(){
println("Son 构造器被调用")
}
def main(args: Array[String]): Unit = {
// new Father()
new Son()
}
}
构造器的调用顺序
当我们实例化子类的对象时,是否需要调用父类的构造器? 是否需要调用子类的构造器?
代码验证调用顺序:父类的构造器->子类构造器
object Class03 {
class Dog(var name:String,var age:Int) {
println(s"Dog${name},${age}构造器被调用")
}
//子类 中华田园犬
//子类带参构造器
//1.继承父类的属性。不用写val,var 修饰符
//父类构造器
//直接传入参数,不用写属性的类型
class RuralDog (name:String,age:Int) extends Dog(name,age){
println("RuralDog的构造器...")
}
def main(args: Array[String]): Unit = {
new RuralDog("旺财",3)
}
}
带参构造器的写法
object Class04 {
// 定义 Point 类
class Point(var x: Double, var y: Double) {
def getDist(): Double = {
Math.sqrt(x*x + y*y)
}
// 方法1:判断所在象限
def whereAmI(): String = {
if (x > 0 && y > 0) {
"第1象限"
} else if (x < 0 && y > 0) {
"第2象限"
} else if (x < 0 && y < 0) {
"第3象限"
} else if (x > 0 && y < 0) {
"第4象限"
} else {
"在坐标轴上"
}
}
def fromPoint(other:Point):Double={
val dx = other.x - x
val dy = other.y - y
Math.sqrt(dx*dx+dy*dy)
}
override def toString:String=s"Point(${x},${y})"
override def equals(obj:Any):Boolean={
}
class LabelPoint() extends Point{
}
def main(args: Array[String]): Unit = {
var p1 = new Point(1.0,4.0)
val p2 = new Point(3.0,4.0)
println(p1.whereAmI())
println(p1.getDist())
println(p1.fromPoint(p2))
println(p1)
}
}
你正在设计一个二维游戏的界面,需要用很多的点来表示游戏中的角色。
设计一个Point类,其x和y可以通过构造器提供。
它有几个方法:
1. 方法1: 计算自己在哪个象限。 whereAmI():String
2. 方法2: 计算和坐标原点的距离。getDist():Double
3. 方法3: 计算与另一个点的距离。fromPoint(other:Point):Double
4. 方法4: 重写equals 判断是否是同一个点(x和y都相等就是同一个点)。
5. 方法5: 重写toString,更友好的输出点的信息。
再设计一个子类LabelPoint它来继承Point类,其构造器接收一个标签值和x,y坐标。
例如: New LabelPoint("black",1,2)
object Class05 {
// 定义 Point 类
class Point(var x: Double, var y: Double) {
// 方法2:计算与坐标原点的距离
def getDist(): Double = {
Math.sqrt(x * x + y * y)
}
// 方法1:判断所在象限
def whereAmI(): String = {
if (x > 0 && y > 0) {
"第1象限"
} else if (x < 0 && y > 0) {
"第2象限"
} else if (x < 0 && y < 0) {
"第3象限"
} else if (x > 0 && y < 0) {
"第4象限"
} else {
"在坐标轴上"
}
}
// 方法3:计算与另一个点的距离
def fromPoint(other: Point): Double = {
val dx = other.x - this.x
val dy = other.y - this.y
Math.sqrt(dx * dx + dy * dy)
}
// 方法5:重写toString,友好输出
override def toString: String = s"Point(${x}, ${y})"
// 方法4:重写equals,判断是否为同一个点(修复类型检查漏洞)
override def equals(obj: Any): Boolean = obj match {
case other: Point => this.x == other.x && this.y == other.y
case _ => false // 非Point类型直接返回false
}
}
// 子类LabelPoint:继承Point,增加标签属性
class LabelPoint(val label: String, x: Double, y: Double) extends Point(x, y) {
// 重写toString,包含标签信息
override def toString: String = s"LabelPoint(label='${label}', x=${x}, y=${y})"
// 重写equals:标签和坐标都相等才是同一个点
override def equals(obj: Any): Boolean = obj match {
case other: LabelPoint =>
this.label == other.label && this.x == other.x && this.y == other.y
case _ => false
}
}
// 测试主方法(修复格式化语法)
def main(args: Array[String]): Unit = {
// 测试Point类
val p1 = new Point(1.0, 4.0)
val p2 = new Point(3.0, 4.0)
val p3 = new Point(1.0, 4.0)
println("=== Point类测试 ===")
println(s"p1所在象限:${p1.whereAmI()}") // 第1象限
// 修复方式1:用 f 字符串格式化(支持 .2f 格式符)
println(f"p1到原点距离:${p1.getDist()}%.2f") // 4.12(保留2位小数)
println(f"p1到p2的距离:${p1.fromPoint(p2)}%.2f") // 2.00
println(s"p1 == p2?${p1 == p2}") // false
println(s"p1 == p3?${p1 == p3}") // true
println(s"p1的toString:${p1}") // Point(1.0, 4.0)
println("\n=== LabelPoint类测试 ===")
// 测试LabelPoint类
val lp1 = new LabelPoint("black", 1, 2)
val lp2 = new LabelPoint("white", 1, 2)
val lp3 = new LabelPoint("black", 1, 2)
println(s"lp1所在象限:${lp1.whereAmI()}") // 第1象限(继承自Point的方法)
// 修复方式2:用 formatted 方法格式化(兼容 s 字符串)
println(s"lp1到原点距离:${p1.getDist().formatted("%.2f")}") // 4.12
println(s"lp1到lp2的距离:${lp1.fromPoint(lp2).formatted("%.2f")}") // 0.00
println(s"lp1 == lp2?${lp1 == lp2}") // false(标签不同)
println(s"lp1 == lp3?${lp1 == lp3}") // true(标签和坐标都相同)
println(s"lp1的toString:${lp1}") // LabelPoint(label='black', x=1.0, y=2.0)
// 测试跨类型比较(确保equals逻辑正确)
println(s"\n跨类型比较:p1 == lp1?${p1 == lp1}") // false(类型不同)
}
}