首页 > 代码库 > 【java解惑】java构造器的那些事儿

【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




本文出自 “winger” 博客,谢绝转载!

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