java基础 —— 1 子父类内存控制

   

    前言
    最近在复习java基础,做基本功,学习过程中整理到的笔记在此用java基础系列来和大家做分享,欢迎大家批评指正。
    



    java的继承是三大特性封装、继承、多态之一,也是在我的是code中经常出现的,但是究竟在内存中子父类是如何存储的? 子父类中一些玄妙的调用、访问为何会有这般运行结果?子父类中super和this到底应该如何运用?它们有哪些注意事项?我们来从头到来。
        
    首先,我们先来搞清楚继承的变量和方法是如何继承的。
    

    [代码清单 1]

class Father{
    public int age;

    public void info(){
        System.out.println("父类的方法");
    }
}

public class Son extends Father{
	public String name;
}
    我们知道子类Son现在应该已经拥有父类的age实例变量和info方法。但是这并不是完全拥有。
    在继承之中,父类的非同名方法方法会直接copy到子类中,也就是说现在实际上Son代码中已经隐式的存在父类的info方法代码。而如果子类中存在同名的方法则编译器不会实现父类方法copy到子类。再说变量,继承中变量是不会被编译器copy的,也就是说子类Son中是没有实
例变量age的。                                                                                                          

    弄清楚上述问题,我们来看解决一个疑惑。
    [代码清单 2]
class Father{
		
	public int count = 10;

	public void show(){
             System.out.println(this.count);
	}
}     

class Son extends Father{
		
	public int count = 100;

	@override    //重写标志 强制程序员重写此方法否则编译时错误
	public void show(){
		System.out.println(this.count);
	}
}          

public class Test{

	public static void main(String[] args){
		Father f = new Father();
		System.out.println("Father类型的Father对象f:" + f.count);    A
		f.show();

		Son s = new Son();
		System.out.pritln("Son类型的Son对象s" + s.count);	B
		s.show();

		Father fs = new Son();
		System.out.println("Father类型的Son对象fs" + fs.count);	C
		fs.show();

		Father f2s = s;
		System.out.println("Father类型的Son对象f2s" + f2s.count);	D
		f2s.show();
	}
}
    看完上述代码我们来讨论一下输出结果。
    A处代码,毋庸置疑,访问父类的实例变量count输出2,调用父类的show方法输出2。
    B出代码,也很明显,访问子类的实例变量count输出20,调用子类的show方法输出20。
    C处代码,输出结果是访问父类的实例变量count输出2,调用子类的show方法输出20。此处,fs是一个Father类型的Son对象,也就是说fs 的声明类型是Father,实际运行对象是一个Son。你可以理解为一个动物类型的猫对象,如果你对多态理解足够的话,这里不难理解。也就是说 java在引用类型变量来访问其指向的对象的变量的时候取决于他的声明时类型,而当在引用类型变量调用其指向的对象的方法时,取决于实际运行的对象,因为在继承之中,父类的非同名方法方法会直接copy到子类中,但是继承中变量是不会被编译器转移的。
    D处代码,输出结果同C处代码。这里需要注意的是Father f2s = s;意味着,s和f2s指向堆内的同一个对象Son s = new Son();

    到这里,java继承里面的访问和调用问题应该解决了。接下来,我们来研究一下在堆内存中,子父类是如何储存的。
    这之前先说一下,类在堆栈里面如何存储。
    1.通常引用类型变量是存储在栈内存中的,特殊情况是当引用类型变量作为数组元素的时候将存储在堆内存中。
    2.局部变量,包括基本数据类型,引用类型变量,都存放在各自的方法栈中。
    3.new 出的对象会存放在堆内存中。
    So 我们回到初衷。当继承发生,子类对父类的变量时如何保存的?我们知道父类的变量不会copy到子类中,但是子类还是可以访问到父类的变量,你可能会说因为继承,或者因为super。但是实际上在内存中,但你new了一个Son的对象,注意我是说在堆内存中实际对象是一个Son,不是Son类型的Father(当然这个不允许)。这时候Son对象是隐式的存储了Father的变量(类变量和实例变量),如果有的话还包括groundfather的变量。

    好吧,搓搓手,现在看一下隐晦的this和super,先看以下代码想想结果。
    [代码清单 3]
class Father{
	String str = "父类变量";

	public Father getThis(){
		return this;
	}
	public void info(){
		System.out.println("父类方法");
	}
}

public class Son{
	String str = "子类变量";

	@override
	public void info(){
		System.out.println("子类方法");
	}
	public void showFatherInfo(){
		super.info();
	}
	public Father getFather(){
		return super.getThis();
	}
	public static void main(String[] args){
		Son s = new Son();
		Father f = s.getFather();

		System.out.println(s==f+","+s.str+","+f.str);

		s.info();
		f.info();
		s.showFatherInfo();
	}
}
    小伙伴们看到结果没?s==f 会输出ture,说明s和f指向同一个对象,那么是哪个对象呢?然后我们看到s.str和f.str
分别输出子类变量和父类变量。为什么?因为子类方法getFather()实际上是返回了一个Father类型的Son对象。所以s.info(); 和f.info();输出都是子类方法。s.showFarherInfo()才真正是父类的行为方法。

    最后,有几点super的注意事项:
    1.super不可以直接用作引用变量,super == s;编译时错误。
    2.不可以return super;
    3.父类中的类变量可以用super.str或者类名.str访问。

转载于:https://my.oschina.net/u/854416/blog/168625

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值