
new 是怎么调用构造函数的
Java 中 new 不是“直接分配内存就完事”,它会严格按顺序做三件事:分配堆内存 → 调用构造函数 → 返回引用。构造函数没执行完,对象就算没“活过来”,哪怕内存已经分好了。
常见错误现象:NullPointerException 出现在构造函数里调用 this.xxx() 方法时——因为子类重写了该方法,而此时子类字段还没初始化(父类构造器中调用被子类覆盖的方法,子类字段仍是默认值)。
- 永远别在构造函数里调用可被重写的方法(
protected或public实例方法) - 如果必须初始化逻辑复用,改用
private方法或静态工厂方法 -
new后面跟的必须是具体类(不能是接口或抽象类),否则编译报错:Cannot instantiate the type XXX
为什么有时候 new 不走你写的构造函数
你以为写了 public MyClass(String s) 就万事大吉?不一定。如果类里没写任何构造函数,编译器自动加一个无参构造器;但只要你写了任意一个构造器,那个“默认无参构造器”就没了。
使用场景:Spring、Jackson、Hibernate 这些框架大量依赖无参构造器做反射实例化。你加了一个带参构造器又忘了补 public MyClass() {},框架一 new 就抛 InstantiationException 或 IllegalAccessException。
立即学习“Java免费学习笔记(深入)”;
- 只要类要被框架反序列化或注入,就显式声明
public MyClass() {} - IDE 自动生成构造器时,注意检查是否覆盖了无参构造器
- 如果真不想暴露无参构造器,得配好框架的替代方案(比如 Jackson 的
@JsonCreator)
new 和 Class.forName().newInstance() 有啥实质区别
new 是编译期绑定,类型确定、快、安全;Class.forName().newInstance() 是运行时反射创建,要求类必须有可访问的无参构造器,且在 Java 9+ 已被标记为废弃(内部用 Constructor.newInstance() 替代)。
性能影响:反射创建比 new 慢 10–100 倍(取决于 JVM 优化程度),而且绕过编译检查,容易在运行时报 NoSuchMethodException 或 InvocationTargetException。
- 别为了“看起来灵活”滥用反射创建实例,除非真需要动态类型(如插件系统)
- Java 9+ 必须用
clazz.getDeclaredConstructor().newInstance(),并手动处理setAccessible(true)(如果构造器是private) -
new支持泛型推导(如new ArrayList()),反射做不到
对象没 new 成功,但内存可能已经泄漏了
构造函数里开线程、注册监听器、打开文件流,结果中途抛了异常(比如 IOException),对象没建出来,但资源已经分配出去了——这些资源不会自动回收。
典型错误现象:程序跑一阵后 OutOfMemoryError: unable to create new native thread,查下来发现是某个类的构造器里起了线程却没兜住异常。
- 构造函数里尽量只做字段赋值和简单校验,避免副作用操作
- 真要初始化资源,请用“两阶段构造”:先
new,再调用init()方法,并确保init()可重入或幂等 - 或者改用静态工厂方法,在返回前完成清理(try-with-resources + finally)
new 也不是语法糖——它卡在语义、JVM 规范和框架契约的交汇点上,漏掉任何一个细节,问题都会在最意想不到的时候冒出来。









