【java解惑】java构造器的那些事儿

如下所示代码:

public class Example040 {
	private Example040 e40 = new Example040();

	public Example040() throws Exception {
		throw new Exception("这里是exception,不是error");
	}
	public void output() {
		System.out.println("a new class");
	}
	public static void main(String[] args) throws Exception {
		Example040 example040 = new Example040();
		example040.output();
	}
}

代码分析:

运行上述代码,抛出了 StackOverflowError 异常。与大多数抛出 StackOverflowError 异常的程序一样, 本程序也包含了一个无限递归。当你调用一个构造器时,实例变量的初始化操作将先于构造器的程序体而运行。在上述代码中, e40变量的初始化操作递归调用了构造器,而该构造器通过再次调用构造器而初始化该变量自己的e40域,如此无限递归下去。这些递归调用在构造器程序体获得执行机会之前就会抛出 StackOverflowError 异常,因为 StackOverflowError是 Error 的子类型而不是 Exception 的子类型,所以 catch 子句无法捕获它。给e40加上static修饰后,栈溢出异常就会消失了。

对上述代码进行如下改造,加入两个新的变量,新的变量初始化时会产生异常:

public class Example040 {
	// private Example040 e40 = new Example040();
	private static Example040 e40 = new Example040();
	private static Class internal = e40.new InternalClazz().getClass();
	private InternalClazz clazz = (InternalClazz) internal.newInstance();

	public Example040() /* throws Exception */{
		// throw new Exception("这里是exception,不是error");
	}

	public void output() {
		System.out.println("a new class");
	}

	class InternalClazz {
		public InternalClazz() {
		}
	}

	public static void main(String[] args) throws Exception {
		Example040 example040 = new Example040();
		example040.output();
	}
}

上述程序将不能正确编译,原因是:构造器必须声明其实例初始化操作会抛出的所有被检查异常。尽管其构造器没有任何程序体,但是它将抛出两个被检查异常,InstantiationException 和IllegalAccessException。它们是 Class.newInstance抛出的,该方法是在初始化 clazz域的时候被调用的。

上述程序的一种改进方案如下所示,创建一个私有的、 静态的助手方法,它负责计算域的初始值,并恰当地处理异常:

private InternalClazz clazz = newClazz();

	private static InternalClazz newClazz() {
		try {
			return (InternalClazz) internal.newInstance();
		} catch (InstantiationException | IllegalAccessException e) {
			e.printStackTrace();
		}
		return null;
	}

总之, 实例初始化操作是先于构造器的程序体而运行的。 实例初始化操作抛出的任何异常都会传播给构造器。如果初始化操作抛出的是被检查异常,那么构造器必须声明也会抛出这些异常,但是应该避免这样做,因为它会造成混乱。最后,对于我们所设计的类,如果其实例包含同样属于这个类的其他实例,那么对这种无限递归要格外当心。




注:本【java解惑】系列均是博主阅读《java解惑》原书后将原书上的讲解和例子部分改编然后写成博文进行发布的。所有例子均亲自测试通过并共享在github上。通过这些例子激励自己惠及他人。同时本系列所有博文会同步发布在博主个人微信公众号搜索“爱题猿”或者“ape_it”方便大家阅读。如果文中有任何侵犯原作者权利的内容请及时告知博主以便及时删除如果读者对文中的内容有异议或者问题欢迎通过博客留言或者微信公众号留言等方式共同探讨。

源代码地址https://github.com/rocwinger/java-disabuse

时间: 2024-08-06 07:51:44

【java解惑】java构造器的那些事儿的相关文章

【java解惑】java字符串比较那些事儿

    如下代码: public class Example013 { public static void main(String[] args) { final String pig = "length:8"; final String pig1 = "length:8"; int length = pig.length(); final String dog = "length:" + pig.length(); System.out.pr

《Java 解惑》笔记(一)

<Java 解惑>里都是一些编程时容易忽略的细节,却也蛮有意思的,所以将里面的内容稍作整理,简略地概括一下: 1.奇数性 在编程的时候经常会遇到要判断传进来的参数是否为奇数,而且容易惯性地认为判断余数是否为1即可,如下代码: public static boolean isOdd ( int i ) { return i % 2 == 1 } 这段程序在四分之一的时间里返回的都是错误的答案 因为在所有的 int 数值中,有一半都是负数,而 isOdd 方法对于对所有负奇数的判断都会失败.在任何

java解惑之常常忘记的事

java解惑之常常忘记的事 2012-10-17 18:38:57|  分类: JAVA |  标签:基础知识  软件开发  |举报|字号 订阅 针对刚接触java的菜鸟来说,java基础知识都是我们必须认真学习的,但是在工作过几年时间的老鸟来说,有时候也会对java的基础知识产生疑问,对于这种不确定,并且很容易混淆的知识点,java解惑已经为大家进行了很好的总结,现在借用一个作者的总结,进行一下罗列,希望能对你有所帮助. 1. 奇偶判断 不要使用 i % 2 == 1 来判断是否是奇数,因为i

《Java解惑》读书笔记

 摘选自<Java解惑>一书,之前整理了部分,一直没看完,最近为了督促自己每天读点这本书,决定一天至少更新一个谜题的内容,欢迎讨论. 欢迎关注技术博客http://blog.sina.com.cn/u/1822488043 Java解惑读书笔记 谜题1:奇数性 取余操作的定义: ( a / b ) * b + ( a % b ) = a 其中(a/b)是java运算的结果,也就是a/b是一个整数,比如3/2=1. 所以当取余操作返回一个非零结果的时候,它与左操作数具有相同符号. 请测试你的

【java解惑】重载构造函数

如下所示代码: public class Example046 { private Example046(Object o) { System.out.println("Object"); } private Example046(double[] dArray) {//2 System.out.println("double array"); } private Example046(String str) {//3 System.out.println(&quo

Java解惑五:类之谜

本文是依据JAVA解惑这本书,做的笔记.电子书见:http://download.csdn.net/detail/u010378705/7527721 谜题46 函数重载的问题. JAVA重载解析过程:1. 选取全部可用的方法或者构造器:2. 从过程1中选取的方法或构造器中选择最精确的. 一般而言:能够强制要求编译器选择一个精确的重载版本号,将实參转型为形參所声明的类型. 谜题47 继承中静态域的问题. 静态域由声明它的类及其全部子类共享. 假设须要让每个子类都具有某个域的单独拷贝,必须在每个子

Java解惑六:库之谜

本文是根据JAVA解惑这本书,做的笔记. 电子书见:http://download.csdn.net/detail/u010378705/7527721 谜题56 BigInteger.BigDecimal以及包装类型的实例是不可改变. BigInteger five = new BigInteger("5"); BigInteger total = BigInteger.ZERO; total.add(five); //这并不会改变total的值,调用该方法的返回值,才是加法得到的结

【java解惑】无处不在的bug

    如下所示代码: public class Example023 { public static void main(String[] args) { errorMethod(); rightMethod(); } private static void errorMethod() { StringBuffer word = null; Random rnd = new Random(); switch (rnd.nextInt(2)) { case 1: word = new Strin

【java解惑】字符串的字节数组构造函数使用

如下代码: public class Example018 { public static void main(String[] args) { byte bs[] = new byte[256]; for (int i = 0; i < 256; i++) { bs[i] = (byte) i; } string(bs);// 调用1 string(bs, "iso-8859-1");// 调用2 string(bs, "gbk");// 调用3 strin