您的位置:首页 > 编程语言 > Java开发

深入理解父类变量引用子类对象

2017-03-22 11:52 281 查看
“初学java,只有问题与心得。”

对象内存分析转载自tanqiantot

从对象的内存角度出发

假设现在有一个父类Father,它里面的变量需要占用1M内存.有一个它的子类Son,它里面的变量需要占用0.5M内存.

现在通过代码来看看内存的分配情况:(假设子类继承父类所有属性和方法)

Father f = new Father();//系统将分配1M内存

Son s = new Son();//系统将分配1.5M内存!

因为子类中有一个隐藏的引用super会指向父类实例,所以在实例化子类之前会先实例化一个父类,也就是说会先执行父类的构造函数.由于s中包含了父类的实例,所以s可以调用父类的方法.或者说子类拥有父类的属性和方法。

Son s1 = s;//s1指向那1.5M的内存.

Father f1 = (Father)s;//这时f1会指向那1.5M内存中的1M内存(其实不用强制转换,为了好理解)

即是说,f1只是指向了s中实例的父类实例对象,所以f1只能调用父类的方法(存储在1M内存中),而不能调用子类的方法(存储在0.5M内存中).

Son s2 = (Son)f;//这句代码运行时会报ClassCastException.因为f中只有1M内存,而子类的引用都必须要有1.5M的内存,所以无法转换.

Son s3 = (Son)f1;//这句可以通过运行,这时s3指向那1.5M的内存.由于f1是由s转换过来的,所以它是有1.5M的内存的,只是它指向的只有1M内存.

示例:
class Father{
void print(){};
}
class Son extends Father{
void print(){System.out.println("子类中!");}
void show(){System.out.println("show 中!");}
}
class Demo{
public static void main(String args[]){
Father obj=new Son();
obj.print();
obj.show();  //这个调用会报错!
}
}


1 .如果你想实现多态,那么必须有三个条件,父类引用,子类对象,方法覆盖你这里如果Fathor类有一个show()方法,那么形成方法覆盖,那么此时就可以这么写:obj.show(),此刻形成了多态.

2.没有方法覆盖,那你这里只能解释为父类引用去访问一个子类的方法,当然,父类引用没有这么大范围的权限,当然会报错

PS:多态实际上是一种机制,在编译时刻,会生成一张虚拟表,来记录所有覆盖的方法,没有被覆盖的方法是不会记录到这张表的.若一个父类引用调用了没有覆盖的子类方法,那么是不符合该表的,那么编译时刻就会报错. 在执行程序的时候,虚拟机会去这张虚拟表中找覆盖的方法,比如引用中实际上存的是一个子类对象引用,那么就会去找子类中的相应的覆盖的方法来执行 定义一个父类类型的引用指向一个子类的对象既可以使用子类强大的功能,又可以抽取父类的共性。

所以,父类类型的引用可以调用父类中定义的所有属性和方法,而对于子类中定义而父类中没有的方法,它是无可奈何的;

同时,父类中的一个方法只有在在父类中定义而在子类中没有重写的情况下,才可以被父类类型的引用调用;

看下面这段程序:

class Father{
public void func1(){
func2();
}
//这是父类中的func2()方法,因为下面的子类中重写了该方法
//所以在父类类型的引用中调用时,这个方法将不再有效
//取而代之的是将调用子类中重写的func2()方法
public void func2(){
System.out.println("AAA");
}
}
class Child extends Father{
//func1(int i)是对func1()方法的一个重载
//由于在父类中没有定义这个方法,所以它不能被父类类型的引用调用
//所以在下面的main方法中child.func1(68)是不对的
public void func1(int i){
System.out.println("BBB");
}
//func2()重写了父类Father中的func2()方法
//如果父类类型的引用中调用了func2()方法,那么必然是子类中重写的这个方法
public void func2(){
System.out.println("CCC");
}
}
public class PolymorphismTest {
public static void main(String[] args) {
Father child = new Child();
child.func1();//打印结果将会是什么?
}
}


上面的程序是个很典型的多态的例子。子类Child继承了父类Father,并重载了父类的func1()方法,重写了父类的func2()方法。重载后的func1(int i)和func1()不再是同一个方法,由于父类中没有func1(int i),那么,父类类型的引用child就不能调用func1(int i)方法。而子类重写了func2()方法,那么父类类型的引用child在调用该方法时将会调用子类中重写的func2()。

那么该程序将会打印出什么样的结果呢?

很显然,应该是“CCC”。 变量是不存在重写覆盖的!public class A { int a = 1; } public class B extends A { int a = 2; } 测试类里调用了这个方法void compare(){ if(super.a == this.a) System.out.println(“not overrided”); else System.out.println(“overrided”);} 控制台出来的是overrided 类中的属性是没有多态性的,即你在引用上面使用属性时,系统只会去找引用的静态类型中的那个属性,而与它的实际类型无关。

静态方法也是没有多态性的。
如
class Fruit {
public static void eat() {
System.out.println("吃水果");
}
}
class Apple extends Fruit {
public static  void eat() {
System.out.println("吃苹果");
}
}
class Test2 {
public static void main(String[] args) {
Fruit fruit1 = new Apple();
Apple apple1 = (Apple)fruit1;
fruit1.eat();  //吃水果
apple1.eat(); //吃苹果
}
}


当类中的方法声明为static时,这个方法就和此类创建的对象脱离了关系。

父类方法被static修饰,子类覆盖方法也必须被static修饰,同样,父类方法没有被static修饰,子类覆盖时也不能为static修饰。(其实static修饰的方法不能被覆盖,只能算做被隐藏)。

父类中被static修饰的方法,当父类引用子类对象时,不管子类覆不覆盖(隐不隐藏),父类引用都会调用父类的方法,也就是不存在多态性

总结:当父类变量引用子类对象时,如 Father father = new Son(); 假如子类覆盖了父类的方法,那么

father.method();将调用的是子类中的覆盖的方法,但是当方法是子类独有的时候,father将不能调用此方法。然而,对于属性(变量)来说,不管子类是继承还是覆盖,father.variable都将是调用父类中的变量,当然,如果此变量是子类独有,则不能通过father去调用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  继承 java