3)实现多态。
什么是多态呢?通俗的理解,就是同一个事件发生在不同的对象上会产生不同的结果,鼠标左键点击窗口上的 X 号可以关闭窗口,点击超链接却可以打开新的网页。
多态可以通过继承(extends)的关系实现,也可以通过接口的形式实现。来看这样一个例子。
Shape 是表示一个形状。
public interface Shape { String name(); }
圆是一个形状。
public class Circle implements Shape { @Override public String name() { return "圆"; } }
正方形也是一个形状。
public class Square implements Shape { @Override public String name() { return "正方形"; } }
然后来看测试类。
List<Shape> shapes = new ArrayList<>(); Shape circleShape = new Circle(); Shape squareShape = new Square(); shapes.add(circleShape); shapes.add(squareShape); for (Shape shape : shapes) { System.out.println(shape.name()); }
多态的存在 3 个前提:
1、要有继承关系,Circle 和 Square 都实现了 Shape 接口
2、子类要重写父类的方法,Circle 和 Square 都重写了 name() 方法
3、父类引用指向子类对象,circleShape 和 squareShape 的类型都为 Shape,但前者指向的是 Circle 对象,后者指向的是 Square 对象。
然后,我们来看一下测试结果:
圆
正方形
也就意味着,尽管在 for 循环中,shape 的类型都为 Shape,但在调用 name() 方法的时候,它知道 Circle 对象应该调用 Circle 类的 name() 方法,Square 对象应该调用 Square 类的 name() 方法。
04、接口与抽象类的区别
好了,关于接口的一切,你应该都搞清楚了。现在回到读者春夏秋冬的那条留言,“兄弟,说说抽象类和接口之间的区别?”
1)语法层面上
接口中不能有 public 和 protected 修饰的方法,抽象类中可以有。
接口中的变量只能是隐式的常量,抽象类中可以有任意类型的变量。
一个类只能继承一个抽象类,但却可以实现多个接口。
2)设计层面上
抽象类是对类的一种抽象,继承抽象类的类和抽象类本身是一种 is-a 的关系。
接口是对类的某种行为的一种抽象,接口和类之间并没有很强的关联关系,所有的类都可以实现 Serializable 接口,从而具有序列化的功能。
就这么多吧,能说道这份上,我相信面试官就不会为难你了。
十一、Java 继承
在 Java 中,一个类可以继承另外一个类或者实现多个接口,我想这一点,大部分的读者应该都知道了。还有一点,我不确定大家是否知道,就是一个接口也可以继承另外一个接口,就像下面这样:
public interface OneInterface extends Cloneable {
}
这样做有什么好处呢?我想有一部分读者应该已经猜出来了,就是实现了 OneInterface 接口的类,也可以使用 Object.clone() 方法了。
public class TestInterface implements OneInterface {
public static void main(String[] args) throws CloneNotSupportedException {
TestInterface c1 = new TestInterface();
TestInterface c2 = (TestInterface) c1.clone();
}
}
除此之外,我们还可以在 OneInterface 接口中定义其他一些抽象方法(比如说深拷贝),使该接口拥有 Cloneable 所不具有的功能。
public interface OneInterface extends Cloneable {
void deepClone();
}
看到了吧?这就是继承的好处:子接口拥有了父接口的方法,使得子接口具有了父接口相同的行为;同时,子接口还可以在此基础上自由发挥,添加属于自己的行为。
以上,把“接口”换成“类”,结论同样成立。让我们来定义一个普通的父类 Wanger:
public class Wanger {
int age;
String name;
void write() {
System.out.println("我写了本《基督山伯爵》");
}
}
然后,我们再来定义一个子类 Wangxiaoer,使用关键字 extends 来继承父类 Wanger:
public class Wangxiaoer extends Wanger{
@Override
void write() {
System.out.println("我写了本《茶花女》");
}
}
我们可以将通用的方法和成员变量放在父类中,达到代码复用的目的;然后将特殊的方法和成员变量放在子类中,除此之外,子类还可以覆盖父类的方法(比如write() 方法)。这样,子类也就焕发出了新的生命力。
Java 只支持单一继承,这一点,我在上一篇接口的文章中已经提到过了。如果一个类在定义的时候没有使用 extends 关键字,那么它隐式地继承了 java.lang.Object 类——在我看来,这恐怕就是 Java 号称万物皆对象的真正原因了。
那究竟子类继承了父类的什么呢?
子类可以继承父类的非 private 成员变量,为了验证这一点,我们来看下面这个示例。
public class Wanger { String defaultName; private String privateName; public String publicName; protected String protectedName; }
父类 Wanger 定义了四种类型的成员变量,缺省的 defaultName、私有的 privateName、共有的 publicName、受保护的 protectedName。
在子类 Wangxiaoer 中定义一个测试方法 testVariable():
可以确认,除了私有的 privateName,其他三种类型的成员变量都可以继承到。