面试题答案
一键面试构造函数调用顺序
- 基类主构造函数:如果子类的主构造函数委托给基类的主构造函数,首先会执行基类主构造函数中的代码。
- 基类二级构造函数:若子类委托给基类的二级构造函数,在执行子类构造函数代码前,会先执行基类二级构造函数的代码。
- 子类初始化块:在基类构造函数执行完毕后,会执行子类的初始化块。
- 子类主构造函数:最后执行子类主构造函数的代码。
可能遇到的问题
- 参数校验问题:如果在基类构造函数中进行参数校验,子类传递的参数可能不符合基类的校验规则,导致运行时错误。
- 资源初始化重复:若基类和子类都需要初始化相同类型的资源,可能会出现重复初始化,浪费资源。
- 构造函数混乱:复杂的继承体系中,多个构造函数可能导致代码难以理解和维护。
优化策略
- 集中参数校验:可以在基类中定义一个内部校验函数,在基类和子类的构造函数中都调用这个函数,确保参数校验的一致性。
- 资源初始化管理:将资源初始化的逻辑集中在一个地方,例如在基类中定义一个初始化资源的方法,由子类在合适的时机调用,避免重复初始化。
- 简化构造函数:尽量减少构造函数的数量,通过默认参数和重载方法来简化构造逻辑,提高代码的可读性和可维护性。
代码示例
open class BaseClass {
var value: Int
var resource: String
// 基类主构造函数
constructor(value: Int, resource: String) {
checkValue(value)
this.value = value
this.resource = initResource(resource)
}
// 基类二级构造函数
constructor(value: Int) : this(value, "default resource")
private fun checkValue(value: Int) {
if (value < 0) {
throw IllegalArgumentException("Value must be non - negative")
}
}
private fun initResource(resource: String): String {
// 实际资源初始化逻辑,这里简单返回
return "Initialized: $resource"
}
}
class SubClass : BaseClass {
var subValue: String
// 子类主构造函数,委托给基类主构造函数
constructor(value: Int, resource: String, subValue: String) : super(value, resource) {
this.subValue = subValue
}
// 子类二级构造函数,委托给基类二级构造函数
constructor(value: Int, subValue: String) : super(value) {
this.subValue = subValue
}
}
可以通过以下方式测试:
fun main() {
try {
val sub1 = SubClass(10, "custom resource", "sub value 1")
println("Sub1 value: ${sub1.value}, resource: ${sub1.resource}, subValue: ${sub1.subValue}")
val sub2 = SubClass(20, "sub value 2")
println("Sub2 value: ${sub2.value}, resource: ${sub2.resource}, subValue: ${sub2.subValue}")
// 尝试传递负数会抛出异常
// val sub3 = SubClass(-1, "sub value 3")
} catch (e: IllegalArgumentException) {
println("Caught exception: $e")
}
}