再论Java的构造方法

在OOP语言中,都会有构造方法这个概念,它的主要作用就是用于创建一个类的实例,并为这个实例的成员变量赋值。虽然它是一个好像很简单的东西,但是好像还有很多人对它的理解还不是很透彻,往往在面试中就会搞一些比较2B的题目来考大家。

写这个主题主要是因为在自己的代码中出现了问题,在XStream中,它在实例化的时候是不会调用类的默认方法的,后面自己去跟了一下XStream的源码才发现,它是用了Unsafe中的allocateInstance方法来实例化的,而这个方法是不会去调用构造方法的,导致了Map cache = new HashMap();这个cache成员变量没有被赋值,最后用的时候就出现了NullPointerExption。

我猜这XStream是肯定没有调用类的默认构造方法,不然不可能会null的,结果就跟其他两个同事讨论起构造方法的事来,他们都说成员变量的赋值都是在构造方法之前执行的,而我说成员变量的初始化是在构造方法里面执行的。在论证这个之前,我们先来看一个比较误导人的命题:“当一个类没有定义任何构造方法时,编译器会生成一个什么都不做的默认构造方法”,我拿这个问题问了以前的同事,他竟然说这是对的,这段话好像在很多教材里面都会出现。这句话里的一半是对的,至于哪里错了,我们就要用事实来说话了。

先上一段简单的代码:

public class Test {

	int a = 100;

}

 

这段代码只有简单的三行代码,但是却已经可以证明上面那句话的问题了。首先这个类里面是没有任何构造方法的,为了证明上面那句话是有一半错误的,我们需要看编译器为我们生成了些什么东西。在这里我们需要用到javap命令,这个命令能为我们的class文件转成对应的字节码指令。

 

在这里我们看到,编译器确实帮我们生成了一个默认的构造方法,但是它是什么都不做的默认构造方法么?明显不是,生成好几行的字节码指令。那么,我们现在来看一下这些生成的字节码指令都做了些什么事,首先会将this压入栈中,接着调用父类的构造方法,最后为成员变量赋值。现在我们就可以得出两个结论了:1、编译器为我们生成的默认构造方法并不是什么都不做的”懒人“;2、类中的成员变量的赋值是放到构造方法中去的,而并不是在构造方法之前执行的。相当于生成了以下的代码:

 

public class Test {

	int a;
	
	Test()
	{
		super(); // 首先去调用父类的构造方法
		a = 100; // 为成员变量赋值
	}

}

    再来看一下构造方法里面的代码和成员变量赋值的顺序,再来一段简单的代码:

public class Test {

	int a = 100;
	
	int b = 200;

	Test() {
		int e = 300;
		int f = 400;
	}
	
	int c = 500;
	
	int d = 600;
}

 

   看了这段简单的代码后,大家先花几秒钟的时间来猜一下它们的执行顺序是怎么样的大笑

   国际惯例,我们用javap命令来看一下它生成的字节码指令(javap真是一个好用的东西啊,哈哈)

    看了这图之后,相信大家都已经知道答案了吧。相当于生成了以下的代码:

public class Test {

	int a;
	
	int b;

	Test() {
		a = 100;
		b = 200;
		c = 500;
		d = 600;
		int e = 300;
		int f = 400;
	}
	
	int c;
	
	int d;
}

 

   哈哈,我就不多费口舌了。希望这篇博客能帮到大家。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值