java中对于super和this中同名字段的调用分析

前言

本文用于笔记作用,可能有很多不足的地方,仅作参考作用。当然,如果发现问题的话,希望能帮忙指出,在下感激不尽。
然后,因为代码基本是在记事本打的,所以比较随便,将就看就行了。

正文

在对父类和子类使用同一个名称的字段时,发现了一些神奇的问题,这里就来简单的分析一下。
废话不多说,直接上一段测试代码。

// An highlighted block
public class M extends N{
private int n = 10;

public void setN(int nn){ 
	int nnn = 	super.n ;
	super.n = nn;
	System.out.println("wo is son:"+nnn+"->"+	super.n);
}
public void NN(){//super.setN(23);
	//super.n = 123;
	System.out.println("nn->"+n+","+super.n+","+hashCode()+" "+super.hashCode());
}
	public M(String name){
		System.out.println("M->"+name);
		//n = 55;
}
	public static void main(String []arg){
		System.out.println("--------Father----------");
		N n = new M("sd");	//向上转型
		//System.out.println(n.n);
		n.NN();

		System.out.print(n.n);
		n.n = 2;
		System.out.println("->set:"+n.n);

		n.NN();

		System.out.println("--------Son----------");
		M m = (M) n;//new N("sd");//向下转型
		m.setN(311);
		System.out.print(m.n);
		m.n = 3;
		System.out.println("->set:"+m.n);

		m.NN();

		//System.out.println("Hello World"+" mm-:"+m.m);

		System.out.println("--------Father----------");
		N nn = m; //向上转型
		System.out.print(nn.n);
		nn.n = 30;
		System.out.println("->set:"+nn.n);
		nn.setN(31);
		m.NN();
		n.NN();
	}
}

class N{
static int m = 20;
public void setN(int nn){ 
	int nnn = n ;
	this.n = nn;
	System.out.println("my is father:"+nnn+"->"+n);
}
public int n = 1;
public void NN(){}
public N(){}
		public N(String name){ System.out.println("N->"+name);}
	}

运行结果如下
在这里插入图片描述
这里我们通过对象的哈希码,可以发现this和super是同一个对象,而且无论静态类型如何变,该对象的实例也是不变的。
然后针对这个父类子类都有的n字段,我们发现子类的访问标志是不被父类的同名字段限制的,这也就是说字段是没有重写的。
我们都知道,重写的方法是由动态分派来调用来调用的,而其他类型的方法也有不同的调用方式。但是字段的调用,好像很少被关照过,当然也可能是本人才疏学浅的原因。总之之前确实忽视了字段调用的内容,不过我们在这个例子中,就会发现方法的调用和字段的调用是不一致的。
我们发现在这段代码中,有一个神奇的地方,就是当我们修改字段值的时候,它的字段是根据对象静态类型来修改的,不是根据实际对象类型来修改的。
这也就意味着,如果我们如果要直接访问一个字段的值,那么如果我们在运行的过程中不断对它进行向上转型和向下转型,那么你直接调用的那个变量可能是引用向不同地方的,而我们知道无论你这么转型它的对象始终只有一个,而不同的对象之间的非static字段也是不一样的。那么我们就能判断,该例子里面,我们在这个对象里面存储的多个同名的字段,而这其中存在一个字段的调用机制,他会根据对象的静态类型给我们的调用分配不同版本的字段。
那么我们在这个例子中,就有两个这样的字段this.n和super.n,这两个值是不同的,我们直接调用的时候,对象根据它本身的静态类型调用两者,而在方法中它的调用方式又不同。
我们知道,在方法中我们有this关键字和super关键字,所以我们在方法中能通过这两个关键字直接引用对应的字段,而this一般是默认的关键字,所以我们会发现在方法中直接设置n,修改的是this对应的字段。
最后,可以发现一个有趣的地方,我们总是通过set和get方法来调用变量,而不推荐直接调用变量。之前,个人以为是为了统一管理,而分析了完这些之后,我觉得该设计未尝不是考虑到了同名字段的原因。

下面根据所分析的内容来做一下扩展。
这里添加了C类,同时让N类继承它,C类同样存在n字段。在这里插入图片描述
这里再再加一段测试代码。
在这里插入图片描述
我们发现这里就有3个值了。
在这里插入图片描述
那么当类的接口也有同名的变量时,又会怎么样呢?

public interface M{
int n = 20;
}

我们查看它的字节码会发现该字段被加了static和final修饰符,所以n的值无法更改。
在这里插入图片描述
继续上代码

interface N{
	int n = 20;
}

public  class M implements N{
	public static void main(String[] args){
		M m = new M();
		System.out.println(m.n);
	}
}

在这里插入图片描述
到这里好像没什么问题。
但是我们改下代码

interface N{

	int n = 20;
}

interface C{

	int n = 20;
}

public  class M implements N, C{
	public static void main(String[] args){
		M m = new M();
		System.out.println(m.n);
	}
}

运行时直接报错,因为一个符号有两个引用。
在这里插入图片描述
再改下代码

interface N{

	int n = 20;
}

interface C{

	int n = 20;
}

public  class M implements N, C{
	public static void main(String[] args){
		M m = new M();
		C c = m;
		System.out.println(c.n);
	}
}

这时候就没报错。
说明还是和静态类型有关系,M类型因为没有定义n字段,所以向上查找,发现两个父接口都定义了n字段,所以虚拟机不知道选哪个就直接抛出了异常。
而向上转化为C类型后,它的静态类型编程了C类型,所以直接就找到了C.n。
在这里插入图片描述
接着改

interface N{

	int n = 10;
}

interface C extends N{

	int n = 20;
}

public  class M implements N,C{
	int n = 30;
	public static void main(String[] args){
		M m = new M();
		System.out.println(m.n);
		C c = m;
		System.out.println(c.n);
	}
}

我们看到即便C.n是staitc类型的,M.n的非静态类型的,当是无论是static类型还是非static类型,他都是按我们之前讲的根据静态类型来引用变量的。
在这里插入图片描述
我们注释掉C类的n字段。

interface C extends N{

	//int n = 20;
}

它就会引用C的父接口N的n字段,说明当静态类型没发现对应字段时,它会向上查找。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值