1 [code=java]
2 //多态的经典例子
3 //向上转型后,父类只能调用子类和父类的共同方法和的重写方法(方法名相同,参数也相同),不能调用重载方法(方法名相同,但参数不同)
4 class A {
5 public String show(D obj) {
6 return ("A and D");
7 }
8 public String show(A obj) {
9 return ("A and A");
10 }
11 }
12 class B extends A{
13 public String show(B obj){//重载
14 return ("B and B");
15 }
16 public String show(A obj){//重写
17 return ("B and A");
18 }
19 }
20 class C extends B{
21
22 }
23 class D extends B{
24 }
25 public class Test {
26 public static void main(String[] args) {
27 A a1 = new A();
28 A a2 = new B();//向上转型
29 B b = new B();
30 C c = new C();
31 D d = new D();
32
33 System.out.println("1--" + a1.show(b));//A and A this.show((super)O)
34 System.out.println("2--" + a1.show(c));//A and A this.show((super)(super)O)
35 System.out.println("3--" + a1.show(d));//A and D this.show(O)
36 System.out.println("4--" + a2.show(a1));//B and A super.show(O)
37 System.out.println("4--" + a2.show(b));//B and A super.show((super)O)
38 System.out.println("5--" + a2.show(c));//B and A super.show((super)O)
39 System.out.println("6--" + a2.show(d));//A and D super.show((super)O)
40 System.out.println("7--" + b.show(b));//B and B
41 System.out.println("8--" + b.show(c));//B and B
42 System.out.println("9--" + b.show(d));//A and D
43
44 }
45 }
46 /*
47 多态机制遵循的原则概括为:
48 当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,
49 也就是说被子类覆盖的方法,但是它仍然要根据继承链中方法调用的优先级来确认方法,
50 该优先级为:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。
51 */
52
53
65
66 [/code]
1 abstract class Animal
2 {
3 public abstract void eat();
4 }
5 class Cat extends Animal
6 {
7 public void eat()
8 {
9 System.out.println("吃鱼");
10 }
11 public void catchMouse()
12 {
13 System.out.println("抓老鼠");
14 }
15 }
16
17 class Dog extends Animal
18 {
19 public void eat()
20 {
21 System.out.println("吃骨头");
22 }
23 public void kanJia()
24 {
25 System.out.println("看家");
26 }
27 }
28
29 class Pig extends Animal
30 {
31 public void eat()
32 {
33 System.out.println("饲料");
34 }
35 public void gongDi()
36 {
37 System.out.println("拱地");
38 }
39 }
40 class Function
41 {
42 public static void function(Animal a){
43 a.eat();
44 if(a instanceof Cat){
45 Cat c=(Cat)a;
46 c.catchMouse();
47 }
48 if(a instanceof Dog){
49 Dog d=(Dog)a;
50 d.kanJia();
51 }
52 //instanceof :用于判断对象的具体类型,只能用于引用数据类型判断,通常在向下转型前用于健壮性的判断。
53 if(a instanceof Pig){
54 Pig p=(Pig)a;
55 p.gongDi();
56 }
57 }
58 }
59 class DuoTaiDemo
60 {
61 public static void main(String[] args)
62 {
63 Function.function(new Cat());
64 Function.function(new Dog());
65 Function.function(new Pig());
66
67 //Animal a = new Cat();//类型提升。 向上转型。
68 //a.eat();
69
70 //如果想要调用猫的特有方法时,如何操作?
71 //强制将父类的引用。转成子类类型。向下转型。
72 //Cat c = (Cat)a;
73 //c.catchMouse();
74
75
76 Animal a = new Cat();//向上转型
77 a.eat();
78 //a.catchMouse(); 向上转型后不能再用子类的特有方法,想要使用必须再向下转型
79 Cat c = (Cat)a;//子类向上转化为父类后才能向下转型
80 c.catchMouse();
81
82 //Dog d=(Dog)a;
83 //d.kanJia(); 这样做是错误的,没有实现
84
85 //下面这样也是错误的
86 /*
87 Animal e=new Animal() {
88
89 @Override
90 public void eat() {
91 System.out.println("eee");
92
93 }
94 };
95 e.eat();
96
97 Dog f=(Dog)e;
98 f.eat();
99 f.kanJia();
100 */
101
102 //千万不要出现这样的操作:就是将父类对象转成子类类型。
103 //我们能转换的是父类引用指向了自己的子类对象时,该应用可以被提升,也可以被强制转换。
104 //多态自始至终都是子类对象在做着变化。
105 }
106 }
class ExtendsDemo{
public static void main(String[] args){
Zi z = new Zi();//>构造函数最先执行,见下面的说明
z.show();
}
}
class Fu{
Fu(){
show();
}
void show(){//被子类覆盖了,所以运行的是子类的show方法
System.out.println("fu show" );
}
}
class Zi extends Fu{
int num = 8;//>构造函数最先执行
Zi(){
//隐式的super();
//通过super初始化父类内容时,子类的成员变量并未显示初始化,
//等super()父类初始化完毕后,才进行子类的成员变量显示初始化
}
void show(){
System.out.println("zi show..." + num);
}
}
/*
在对子类对象进行初始化时,父类的构造函数也会运行,那是因为子类的所有构造函数默认第一行有一条隐式的语句 super();
super():会访问父类中空参数的构造函数。而且子类中所有的构造函数默认第一行都是super()。
以Person p = new Person();为例:
1. JVM会读取指定的路径下的Person.class文件,并加载进内存,并会先加载Person的父类(如果有直接的父类的情况下)。
2. 在内存中开辟空间,并分配地址。
3. 并在对象空间中,对对象的属性进行默认初始化。
4. 调用对应的构造函数进行初始化。
5. 在构造函数中,第一行会先到调用父类中构造函数进行初始化。
6. 父类初始化完毕后,再对子类的属性进行显示初始化。
7. 再进行子类构造函数的特定初始化。
8. 初始化完毕后,将地址值赋值给引用变量。
覆盖:
1.子类覆盖父类,必须保证子类权限大于等于父类权限,才可以覆盖,否则编译失败。
2.静态只能覆盖静态。
3.父类中的私有方法不可以被覆盖。父类为static的方法无法覆盖。
4.重载:只看同名函数的参数列表。重写:子父类方法要一模一样。
*/
/*
class Fu{
int num ;
Fu(){
num = 10;
System.out.println("A fu run" );
}
Fu(int x){
System.out.println("B fu run..." + x);
}
}
class Zi extends Fu{
Zi(){
//super();//默认调用的就是父类中的空参数的构造函数
System.out.println("C zi run " + num);
}
Zi(int x){
super(4);
System.out.println("D zi run " + x);
}
}
class ExtendDemo{
public static void main(String[] args){
new Zi();
System.out.println("-------------------" );
new Zi(6);
}
}
*/
public class Animal {
public static void main(String[] args) {
//将一个衍生类引用转换为其基类引用,这叫做向上转换
/*
* 向上转型:子类引用的对象转换为父类类型称为向上转型。通俗地说就是是将子类对象转为父类对象。此处父类对象可以是接口
person b;
b= new lgj();//定义了b为父类,又将子类的引用赋给b,下面为简写
*/
person b=new lgj(); //子类对象当成父类对象,只能调用父类定义过的的成员和方法,
//如果子类重写了父类的方法就根据这个引用指向调用子类重写的这个方法(这个方法就是覆盖override)。这个调用过程就称为“动态绑定”。
b.eat();
//b.fly(); //此处提示在Animal中没有定义fly方法。
//向下转型:父类引用的对象转换为子类类型称为向下转型。
//我们可以将一个基类引用向下转型(downcast)成为衍生类的引用,但要求该基类引用所指向的对象,
//已经是所要downcast的衍生类对象。比如可以将上面的lgj向上转型为person类引用后,再向下转型成为lgj类引用。
lgj c =(lgj)b;//b已经是父类的引用,强制转型为子类的引用c
c.eat();
c.fly();
//或者:
//向下转型时,如果父类的引用一开始就指向子类对象,那么转型之后是安全的,若果父类的引用一开始指向的不是子类对象,
//那么转型成子类对象时,编译不会出错,运行时会显示ClassCastException。我们可以通过关键字instanceof来检测安全性
lgj f=new lgj();
person d=f; //向上转型
// person d=new person();
System.out.println("aaaa");
if(d instanceof lgj){
lgj e=(lgj)d;//向下转型,这条语句不会执行,因为d对象指向的不是类lgj实例化的对象
e.eat();
e.fly();
}
}
}
class person {
public void eat(){
System.out.println("person eatting...");
}
}
class lgj extends person{
public void eat(){
System.out.println("lgj eatting...");
}
public void fly(){
System.out.println("lgj flying...");
}
}
/*
* 总结:
1、父类引用可以指向子类对象,子类引用不能指向父类对象。
2、把子类对象直接赋给父类引用叫upcasting向上转型,向上转型不用强制转型。
如Father father = new Son();
3、把指向子类对象的父类引用赋给子类引用叫向下转型(downcasting),要强制转型。
如father就是一个指向子类对象的父类引用,把father赋给子类引用son 即Son son =(Son)father;
其中father前面的(Son)必须添加,进行强制转换。
4、upcasting 会丢失子类特有的方法,但是子类overriding 父类的方法,子类方法有效
5、向上转型的作用,减少重复代码,父类为参数,调有时用子类作为参数,就是利用了向上转型。这样使代码变得简洁。体现了JAVA的抽象编程思想。
#对比基础类型转换
如果我们从一个高精度类型转换到低精度类型,比如从float转换到int,那么我们有可能会损失信息。
这样的转换叫做收缩变换(narrowing conversion)。这种情况下,我们需要显示的声明类型转换
int a;
a = (int) 1.23;
如果我们从低精度类型转换成高精度类型,则不存在信息损失的顾虑。这样的变换叫做宽松变换(widening conversion)。
我们不需要显示的要求类型转换,Java可以自动进行:
int a = 3;
double b;
b = a;
父类 >>高精度
子类 >>低精度
*/