向上转型是用父类的引用指向子类的对象。即方法区中存放的是父类的说明,但堆中实际存储的是子类的对象。而父类创建的对象方法区中是父类的说明,堆中存储的是父类的对象。其实这个上转型我们经常用到的,比如List,我们一般用的是List list=new ArrayList();
当一个儿子长大成人娶妻生子之后,他便“向上转型”为一个父亲了,他除了非常私有的(private)一些情况保持不变外(只要变量名和父亲的不一样),如姓名,老婆的名字等等外,其他的属性(public,protected,friendly)都成为一个标准的父亲应有的属性(被覆盖了),如对家庭的责任感,由做儿子时的“低”变为了父亲应有的“高”;自由的程度,由作儿子时的“高”变为了作父亲后的“低”。而他作儿子时一些特有的(不属于父亲应有的或者说父类没有的)作为(方法)在其成为父亲后便被舍弃了,如恣意妄为,夜不归宿等等。而正常的行为,比如挣钱的方法,每个父亲肯定不尽相同,作son时的方法也就保存了下来(覆盖了父类的方法),他有一套自己的做事方法,但这套方法里应有的属性就如父亲的标准属性一样。子类想用自己的方法,只能创建一个子类,用子类来执行方法。
Java代码
1 class Foo {
1 public int a;
1 public Foo() { a = 3; }
1 public void addFive() { a += 5; }
1 }
1 public class Bar extends Foo{
1 public int a;
1 public Bar() { a = 8; }
1 public void addFive() { this.a +=5; }
1
1 public static void main(String[] args) {
1 Foo foo = new Bar();
1 foo.addFive();
1 System.out.println("Value: "+ foo.a); //3
1 }
1 }
与java得多态有关 方法可以多态重载,但属性不能
1 public int addFive() {
1 super.a += 5;
1 }
Java代码
1 class Foo {
1 public int a;
1 public Foo() {
1 a = 3;
1 }
1 public int addFive() {
1 a += 5;
1 return a;
1 }
1 public int getA() {
1 return a;
1 }
1 }
1 public class Bar extends Foo {
1 public int a;
1
1 public Bar() {
1 a = 8;
1 }
1
1 public int addFive() {
1 this.a += 5;
1 return a;
1 }
1 public int getA() {
1 return a;
1 }
1 public static void main(String[] args) {
1 Foo foo = new Bar();
1 //调用的是子类中的方法
1 System.out.println(foo.getA());
1 //直接调用父类中的a
1 System.out.println("Value: " + foo.a);
1 //调用的是子类中的方法
1 System.out.println(foo.addFive());
1 }
1 }
输出结果:
8
Value: 3
13
class Base{
public int a;
public static int b;
public Base(){
a=1;
b=1;
}
public void addFivetoa(){
a+=5;
}
public static void addSixtob(){
b+=6;
}
}
public class Son extends Base{
public Son(){
a=2;
b=2;
}
public void addFivetoa(){
a+=7;
}
public static void addSixtob(){
b+=8;
}
public static void main(String args[]){
Son son = new Son();
Base base = son;
base.addFivetoa();
base.addSixtob();
System.out.println(base.a);//9
System.out.println(base.b);//8
System.out.println(son.a);//9
System.out.println(son.b);//8
}
}
过程:先初始化Base的b为0,然后调用父类的构造方法初始化a=1,b=1,然后调用Son的构造方法初始化a=2,b=2;//按实际得到
base.addFivetoa();//a=7;调用子类的
base.addSixtob();//b=8;调用父类的静态方法;
son.addSixtob();//b=10;调用子类的静态方法;
属性是不能多态的,静态方法也不能多态。
衍生:
Java代码
public int a;
public static int b;
public Base(){
a=1;
b=1;
}
public void addFivetoa(){
a+=5;
}
public static void addSixtob(){
b+=6;
}
}
public class Son extends Base{
public int a;
public static int b;
public Son(){
a=2;
b=2;
}
public void addFivetoa(){
this.a+=7;
}
public static void addSixtob(){
b+=8;
}
public static void main(String args[]){
Son son = new Son();
Base base = son;
base.addFivetoa();
base.addSixtob();
System.out.println(base.a);//1
System.out.println(base.b);//7
System.out.println(son.a);//9
System.out.println(son.b);//2
}
}
过程:先初始化Base的b为0,然后调用父类的构造方法初始化a=1,b=1,然后调用Son的构造方法初始化子类中的a和b,a=2,b=2;//父类的a和b 与子类的是不同
base.addFivetoa();//调用子类的方法,son.a=7
base.addSixtob();//调用父类的方法,base.b=1+6=7 ,(子类的没有变化)
剩下的base.a和son.b都没有变化所以分别为1,2。
父类的除private方法以外的方法都被子类所继承.并且覆盖但是属性却没有被覆盖,方法是重写,属性不能重写,所以foo.a访问的是父类的属性,而 foo.addFive(); 调用的是子类覆盖父类的 addFive()方法,既然调用的是子类的方法,所以这个方法修改的是子类属性的值。
Foo foo = new Bar(); 此时,new的虽然是Bar()的实例,但是在内存看到的只是父类的属性,与子类的属性无关。所以,a的值为3。
1 Foo foo = new Bar(); //foo.a=3,bar.a=8互不相碍
1 foo.addFive(); //bar.a=8+5,父类的方法被覆盖了,所以foo.a没有变化
1 System.out.println("Value: "+ foo.a); //foo.a=3,bar.a=13
这也是隐藏域,提供setter/getter的一个原因吧。
父类中的a和子类中的a是两个不同的成员变量,同时存在的,不会出现覆盖的现象。
Foo foo = new TestEYE1(); //调用父类构造。
此时,foo虽然是TestEYE1,但是它是被当作Foo来执行的。
所以它的一些TestEYE1属性全部都被禁用。但是堆中存在这些属性。
当调用foo.addFive()方法时,由于子类对父类的方法重写,执行子类的方法,
this.a +=5; 其中this可以不加的,默认的就是调用本类(TestEYE1)的成员变量。
最后打印foo.a,foo虽然有两个a成员变量,但是因为它现在是Foo身份,所以子类的a是禁用的,但是它还是存在的,只要身份变成TestEYE1,就会打印子类的a属性。看第二次打印的结果就知道了。
System.out.println("Value: "+ foo.a);
System.out.println("Value: "+ ((TestEYE1)foo).a);
因为对计算机而言,这两个成员变量就算是名字相同,它也认为不同的。
注意:这句 Foo foo = new Bar();创建了一个Bar对象
一个对象创建的过程分成4步:
1.分配总空间
2.递归创建父类对象:创建了父类的属性和方法,并没有创建父类对象
3.调用本类构造方法
4.初始化本类属性;
有点值得关注的是。如果addFive返回类型改成int 之后
你打印System.out.println(foo.addFive()); 的值是18,而不是13。
在子类中调用父类同名属性可以这样写super.a,super不能在静态的主方法里使用
子类重写父类方法会造成父类方法被覆盖,而属性却不是这样;
Foo foo = new Bar();
程序运行到这一部,是实例化一个子类对象交给父类引用,在这个对象中有两个属性a,父类的属性a=3和子类自己的a=8 ,而addFive方法只有一个,那就是子类重写的addFive方法;foo.addFive();
这一步调用的是子类重写后的方法,所以在这里值发生改变的是子类的a而不是父类的,这时候子类的a=8+5=13,而父类的a=3没有变。
System.out.println("Value: "+ foo.a);
foo是一个父类的引用,foo.a是父类的a当然是等于3了
Foo foo = new Bar();
像这种upcast(向上造型)
1.域:调用父类属性 (子类的属性也存在)
2.方法:调用子类方法
引用
详情见:thinking in java 4th 的例子
中文版156
英文版290
Foo foo = new Bar(); //实际上实例化Foo 类中的a = 3;
foo.addFive(); //实际上调用了Bar类的方法
如果是想得到 5 + 8 = 13的结果,就封装a字段
class Foo {
private int a;
public Foo() { a = 3; }
public void addFive() { a += 5; }
public int getA() {
return a;
}
}
public class Bar extends Foo{
private int a;
public Bar() { a = 8; }
public int getA() {
return a;
}
@Override
public void addFive() { a +=5; }
public static void main(String[] args) {
Foo foo = new Bar();
foo.addFive();
System.out.println("Value: "+ foo.getA()); //getA()也是调用子类的
} }
这个是java类初始化问题,每个子类对象都含有父类对象,所以要先调用父类的构造函数构造一个父类对象。
Java类初始化顺序,构造链表