为什么建议自定义一个无参构造函数
“这个建议背后其实就一句话:别让框架和工具链找不到你,你手动添加一个有参构造的同时,也别忘了显式补一个无参构造。 Java 有个规则:一旦你定义了任何一个带参数的构造函数,编译器就不会再自动生成默认的无参构造函数了。而这个自动消失的无参构造,恰恰是很多框架和底层机制的生命线。
我分四个场景说清楚为什么它这么重要。
一、框架反射实例化 —— 最常见的翻车现场
Spring、MyBatis、Hibernate、Quartz 等框架,在创建 Bean 或映射对象时,大量使用反射调用 Class.newInstance() 或 Constructor.newInstance() 来实例化对象。它们默认找的就是无参构造函数。
比如你定义了一个 User 类:
Spring 启动时尝试创建 User Bean,反射调用 User.class.getDeclaredConstructor() 直接抛出 NoSuchMethodException,容器启动失败。MyBatis 查询结果映射到 User 时也一样报错。
加上 public User() {} 后,框架就能先“生”出一个空对象,再通过 setter 或字段注入把属性填进去。
二、序列化与反序列化 —— 字节流变对象的通道
Java 原生序列化在反序列化时,并不调用对象的任何构造函数,而是直接从字节流中重构对象。但很多第三方序列化库(如 Jackson、Fastjson、Gson)默认行为是:先找无参构造函数创建空实例,再通过反射填充字段。
如果类里只有带参构造,Jackson 会抛出 JsonMappingException,提示“没有默认构造器”。虽然高版本 Jackson 支持 @JsonCreator 指定带参构造,但不如加一个无参构造来得一劳永逸。
三、继承与子类实例化 —— 父类的门槛
子类构造函数的第一行,如果没有显式调用 super(...),编译器会默认插入 super(),也就是去调用父类的无参构造函数。如果父类没有无参构造,子类就必须在每个构造函数的第一行显式调用父类的有参构造,而且不能省略。
这会导致子类与父类的构造方式强耦合,一旦父类新增或修改了有参构造,所有子类都得跟着改。所以父类保留一个无参构造,就给了子类更多的实现自由。
四、工具与测试的便利性
很多测试框架(如 JUnit、Mockito)在创建测试对象时,习惯用无参构造。工具类如 BeanUtils.copyProperties 也需要目标类有无参构造才能正常工作。没有它,测试代码就得被迫传一堆无关的参数,或者用 mock 绕过去,代码变得别扭。