面试题答案
一键面试潜在安全风险原因
- 方法覆盖与动态绑定:在向上转型时,子类对象被视为父类对象。由于Java的动态绑定机制,实际调用的方法是子类中重写的方法。然而,如果子类在重写方法时引入了新的逻辑或行为,而父类的引用持有者并不期望这种改变,就可能产生安全风险。例如,父类定义了一个简单的读取数据方法,子类重写该方法并在读取数据前添加了权限验证逻辑。如果使用父类引用调用该方法,调用者可能并不知道权限验证的存在,导致意外的行为。
- 类型信息丢失:向上转型会丢失子类特有的类型信息。这意味着无法直接访问子类新增的方法和属性。如果代码依赖于子类的特定功能,但由于向上转型而无法调用,可能导致功能缺失或逻辑错误。
避免方法
- 使用instanceof关键字:在调用方法前,通过
instanceof
关键字检查对象的实际类型,确保调用的方法是符合预期的。例如:
class Animal {
public void makeSound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Dog barks");
}
public void wagTail() {
System.out.println("Dog wags its tail");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Dog();
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.wagTail();
}
animal.makeSound();
}
}
在上述代码中,通过instanceof
判断animal
是否为Dog
类型,如果是则进行向下转型,这样就可以调用Dog
类特有的wagTail
方法,避免因为向上转型而无法调用子类特有方法的问题。
2. 在父类中定义通用方法:在父类中定义足够通用的抽象方法或接口,让子类按照统一的规范实现,这样调用者通过父类引用调用方法时,行为更具可预测性。例如:
abstract class Shape {
abstract double getArea();
}
class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
double getArea() {
return Math.PI * radius * radius;
}
}
class Rectangle extends Shape {
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
double getArea() {
return width * height;
}
}
public class Main {
public static void main(String[] args) {
Shape shape1 = new Circle(5);
Shape shape2 = new Rectangle(4, 6);
System.out.println("Circle area: " + shape1.getArea());
System.out.println("Rectangle area: " + shape2.getArea());
}
}
在这个例子中,父类Shape
定义了抽象方法getArea
,子类Circle
和Rectangle
按照统一规范实现该方法。通过父类Shape
的引用调用getArea
方法,行为是可预测的,避免了因向上转型导致的方法调用不符合预期的情况。