多态的两种表现形式
- 同一类中方法多态(方法重载):同一类中允许多个同名方法,通过参数的数量、类型的差异进行区分。编译时确定调用哪个方法,是编译时多态。
- 子类对父类方法的重新定义(方法覆盖):方法名、返回值和参数形态完全一样。在运行时确定调用哪个方法,是运行时多态。
```java
public class A {
void test(int x) {
} void test(long x) {System.out.println("test(int):" + x );
} void test(double x) {System.out.println("test(long):" + x );
} public static void main (String[ ] args) {System.out.println("test(double):" + x);
} } 运行结果: test(double):5.0 test(int):5A a1 = new A();
a1.test(5.0);
a1.test(5);
【思考】 1、如果将test(int x)方法注释掉,则调用test(5)如何? 运行结果: test(double):5.0 test(long):5 2、3个方法中,只将test(double x)方法注释掉,程序能编译通过吗? 运行结果: java: 对于test(double), 找不到合适的方法 方法 test.test(int)不适用 (参数不匹配; 从double转换到int可能会有损失) 方法 test.test(long)不适用 (参数不匹配; 从double转换到long可能会有损失)
<a name="c5XWB"></a>
# 方法调用的匹配处理原则
**首先按“精确匹配”原则去查找匹配方法,如果找不到,则按“自动类型转换匹配”原则去查找能匹配的方法。**
- 所谓“**精确匹配**”就是实参和形参类型完全一致。
- 所谓“**自动转换匹配**”是指虽然实参和形参类型不同,**但能将实参的数据按自动转换原则赋值给形参。**
```java
public class Complex {
private double x, y; //x,y分别代表复数的实部和虚部
public Complex(double real, double imaginary) {
x = real;
y = imaginary;
}
public String toString() {
return "("+ x+","+y+"i"+")";
}
/* 方法1: 将复数与另一复数a相加 */
public Complex add(Complex a) { //实例方法
return new Complex(x+a.x ,y+a.y);
}
/* 方法2: 将复数与另一个由两实数a,b构成的复数相加 */
public Complex add(double a,double b) { //实例方法
return new Complex(x+a , y+b);
}
/* 方法3:将两复数a和b相加 */
public static Complex add(Complex a, Complex b) { //静态方法
return new Complex(a.x+b.x , a.y+b.y); 思考实例方法和静态方法的差异性!(是否用到成员变量)
}
public static void main(String args[]) {
Complex x,y,z;
x=new Complex(4,5);
y=new Complex(3.4,2.8);
z= add(x,y); //调用方法3进行两复数相加
System.out.println("result1="+z);
z= x.add(y); //调用方法1进行两复数相加
System.out.println("result2="+z);
z= y.add(4,5); //调用方法2进行两复数相加
System.out.println("result3="+z);
}
}
public class A {
void test(int x) …
void test(long x) …
void test(double x) …
}
class B extends A {
void test(int x) {
System.out.println("in B.test(int):" + x);
}
void test(String x , int y) {
System.out.println("in B.test(String,int):" + x+","+y);
}
}
【思考】通过子类B的对象可调用多少test方法?
关于方法覆盖有以下问题值得注意
- 方法名、参数列表、完全相同才会产生方法覆盖;返回类型通常也要一致,只有返回类型为引用类型时,允许子类方法的返回类型是父类方法返回类型的子类型。其他情形导致类型不一致时编译将指示错误。
- 覆盖不能改变方法的静态与非静态属性。子类中不能将父类非静态方法定义为静态方法,反之也一样。
- 不允许子类方法的访问修饰符比父类有更多的限制。
- 例如:子类不能将父类的public方法定义为protected方法。但可以将父类的private方法在子类中重新定义为public方法.
- final方法不能被覆盖。
方法重载与覆盖的区别
- 重载是同一个类中定义了多个同名的方法,表示方法间的关系,是水平关系;覆盖是子类和父类之间的关系,是垂直关系。
- 重载是多个方法之间的关系;覆盖是由一个方法或只能由一对方法产生关系。
- 重载要求参数列表不同;覆盖要求参数列表相同。
- 重载是根据调用时的实参表与形参表选择方法体;覆盖是根据对象类型来调用方法体。
6.2.3 访问继承的成员
(1)如果子类中定义了与父类同名的属性,在子类中将隐藏来自父类的同名属性变量。
(2)通过子类的引用变量访问自己的对象时,无论属性和方法优先考虑子类新定义的。自己类中没有的再到父类找。(优先考虑子类有的)
(3) 可以将子类的对象引用赋值给父类的引用变量;但将父类引用变量的值赋给子类引用变量必须强制转换。
(4)父类引用变量引用子类对象时的成员访问(非常重要)
- 实例方法根据变量所对应对象的实际类型进行访问;———————>称为“动态多态性”
- 属性和静态方法根据引用变量的类型进行访问.
```java
(1)继承关系中方法的继承与父类属性隐藏
class parent{
int x=100;
void m() {
} } public class child extends parent{ int x=200; public static void main(String[] args) {System.out.println(x);
} } 运行结果: 100 200child a=new child();
a.m();
System.out.println(a.x);
(2)继承关系中方法的覆盖 class parent{ int x=100; void m() { System.out.println(x); } } public class child extends parent{ int x=200; void m() { System.out.println(“x=”+x); } public static void main(String[] args) { child a=new child(); a.m(); System.out.println(a.x); } } 运行结果: x=200 200
(3)父类引用子类对象 class parent{ int x=100; void m() { System.out.println(x); } public class child extends parent{ int x=200; void m() { System.out.println(“x=”+x); } public static void main(String[] args) { parent a=new child(); a.m(); System.out.println(a.x); } } 运行结果: x=200 100
```java
class SuperShow {
int y = 8; // 父类SuperShow的y属性
int m = 2;
void show() { //父类SuperShow的show方法
System.out.println("sup.show,y="+y);
}
}
public class ExtendShow extends SuperShow { 每个子类对象有4个属性:y、z、super.y、m
int y = 20; // 子类ExtendShow的y属性
int z = 1;
void show() { // 子类ExtendShow的show方法
System.out.println("ext. show,y="+y);
}
public static void main(String args[]) {
ExtendShow b = new ExtendShow();
SuperShow a = b;
System.out.println("ext.y=" + b.y); 20
System.out.println("sup.y=" + a.y); 8
b.show(); y=20
a.show(); y=20
System.out.println("z="+b.z+",m="+b.m); z=1,m=2
}
}
运行结果:
ext.y=20
sup.y=8
ext. show,y=20
ext. show,y=20
z=1,m=2
class Test5 {
int k = 8;
public void doSome() {
System.out.println("k1="+k);
}
}
class Sub extends Test5 {
int k = 66;
public void doSome() {
k=k-2;
System.out.println("k2="+k);
}
public static void main(String args []) {
Test5 obj = new Sub();
obj.doSome();
System.out.println("k3="+obj.k);
}
}
运行结果:
k2=64
k3=8
思考:
(1)将obj定义为子类引用变量如何?(Test5->Sub)
运行结果:
k2=64
k3=64
(2) 将子类的doSome()方法删除又如何?
k1=8
k3=8
例2:
class exSuper {
public void func(String p, String s) {
System.out.println(p);
}
}
public class test extends exSuper {
public void func(String p, String s) {
System.out.println(p + " : " + s);
}
static public void main(String arg[ ]) {
exSuper e1 = new exSuper( );
e1.func("hello1", "hi1");
exSuper e2 = new test();
e2.func("hello2", "hi2");
}
}
运行结果:
hello1
hello2 : hi2