在学习多态之前,需要先了解后期绑定(又称为动态绑定),它的含义就是在运行时根据对象的类型进行绑定,从而调用恰当的方法。Java支持动态绑定!
Java中除了static方法和final方法(private方法也属于final方法)之外,其它所有方法都是后期绑定。
这里举个列子:
package s1;
public class B {
public static void main(String[] args) {
Shape shape1 = new Shape();
Shape shape2 = new Circle();
Shape shape3 = new Square();
shape1.draw();
shape2.draw();
shape3.draw();
}
}
class Shape{
void draw() {
System.out.println("Shape draw");
}
}
class Circle extends Shape {
void draw() {
System.out.println("Circle draw");
}
}
class Square extends Shape {
void draw() {
System.out.println("Square draw");
}
}
运行结果:(之所以有下面的运行结果是因为shape1、shape2、shape3虽然都是Shape变量,但是所指向的对象却分别是Shape、Circle、Square,由于运行时绑定,所以才会调用不同对象的方法)
Shape draw
Circle draw
Square draw
这里再举另一个列子:
package s1;
public class B {
public static void main(String[] args) {
Shape shape1 = new Shape();
Shape shape2 = new Circle();
Shape shape3 = new Square();
shape1.test();
shape2.test();
shape3.test();
}
}
class Shape{
void test() {
draw();
}
void draw() {
System.out.println("Shape draw");
}
}
class Circle extends Shape {
void draw() {
System.out.println("Circle draw");
}
}
class Square extends Shape {
void draw() {
System.out.println("Square draw");
}
}
运行时结果:
Shape draw
Circle draw
Square draw
如果是私有方法,则会被自动认为是final方法,并且对子类(导出类)也是不可见的,因而无法实现多态机制。
package s1;
public class B {
public static void main(String[] args) {
Shape shape1 = new Shape();
Shape shape2 = new Circle();
Shape shape3 = new Square();
shape1.test();
shape2.test();
shape3.test();
}
}
class Shape{
void test() {
draw();
}
private void draw() {
System.out.println("Shape draw");
}
}
class Circle extends Shape {
void draw() {
System.out.println("Circle draw");
}
}
class Square extends Shape {
void draw() {
System.out.println("Square draw");
}
}
运行结果:
Shape draw
Shape draw
Shape draw
多态机制对域和静态方法是不支持的。因为任何域访问都是编译器解析,因此不支持多态。
下面举个关于域的例子:
package s1;
public class B {
public static void main(String[] args) {
Shape shape1 = new Shape();
Shape shape2 = new Circle();
Shape shape3 = new Square();
System.out.println("shape1 value:" + shape1.value);
shape1.getValue();
System.out.println("shape2 value:" + shape2.value);
shape2.getValue();
System.out.println("shape3 value:" + shape3.value);
shape3.getValue();
}
}
class Shape{
int value = 0;
void getValue() {
System.out.println("Shape vlaue:" + value);
}
}
class Circle extends Shape {
int value = 1;
void getValue() {
System.out.println("Circle vlaue:" + value);
}
}
class Square extends Shape {
int value = 2;
void getValue() {
System.out.println("Square vlaue:" + value);
}
}
运行结果如下:
shape1 value:0
Shape vlaue:0
shape2 value:0
Circle vlaue:1
shape3 value:0
Square vlaue:2
下面举个关于静态方法的例子:
package s1;
public class B {
public static void main(String[] args) {
Shape shape = new Circle();
shape.staticGet();
shape.dynamicGet();
}
}
class Shape{
public static void staticGet() {
System.out.println("Shape staticGet");
}
public void dynamicGet() {
System.out.println("Shape dynamicGet");
}
}
class Circle extends Shape {
public static void staticGet() {
System.out.println("Circle staticGet");
}
public void dynamicGet() {
System.out.println("Circle dynamicGet");
}
}
运行结果:
Shape staticGet
Circle dynamicGet
构造器和多态,如果在类的构造方法中出现多态,会有怎么样的情况发生,请看下面的例子:
package s1;
public class B {
public static void main(String[] args) {
Shape shape = new Circle();
}
}
class Shape{
public Shape() {
draw();
}
public void draw() {
System.out.println("Shape draw");
}
}
class Circle extends Shape {
int value = 1;
public void draw() {
System.out.println("Circle draw");
System.out.println("value:"+value);
}
}
运行结果:
Circle draw
value:0
从上面的结果,我们可以发现构造器内,依然可以发生多态。但是value的值却是0而不是1。这和初始化的顺序有关(具体可参见https://blog.csdn.net/GracefulGuigui/article/details/103856984)。
在其它任何事物发生之前,将分配给对象的存储空间初始化成二进制的零。由于Shape类的构造器调用的draw()是Circle的draw(),而此时Circle类还没初始化。所以value的值依然是最开始的二进制零。
注意:多态仅发生在非静态方法,并且子类B(B extends A)可以调用父类A中的同名方法。注意类A和B的同名方法的返回类型可以不一致,但是父类的返回类型范围不小于子类的返回类型!
1、public方法完全支持多态机制,包括在同一个包(package)内+不在同一个包内
2、protected方法完全支持多态机制,包括在同一个包(package)内+不在同一个包内
3、默认(包)方法不完全支持多态机制,必须要在同一个包(package)内;不在同一个包内无法发生多态,因为子类B无法调用父类的同名方法
4、private方法完全不支持多态机制
下面举个例子,注意Shape和Circle是在不同的包下的!并且二者的draw()方法的返回类型也不同!
package s1;
import s2.Circle;
public class B {
public static void main(String[] args) {
Shape shape = new Circle();
shape.draw();
}
}
package s1;
public class Shape {
public Shape() {
draw();
}
public Object draw() {
System.out.println("Shape draw");
return "";
}
}
package s2;
import s1.Shape;
public class Circle extends Shape {
int value = 1;
public String draw() {
System.out.println("Circle draw");
System.out.println("value:"+value);
return "";
}
}
运行结果:
Circle draw
value:0
Circle draw
value:1