




Java中创建对象的三种常见方式是new、反射和反序列化:new调用构造方法并触发类初始化;反射(如getDeclaredConstructor().newInstance())绕过编译检查但可能抛异常;反序列化不调用构造方法,跳过静态块和构造逻辑。
Java里创建对象最常用的是new关键字,但实际开发中还会遇到反序列化、反射等场景,不同方式行为差异明显。
new ClassName():调用类的构造方法,触发类初始化(如果尚未初始化),是最直接、最可控的方式Class.forName("ClassName").newInstance()(已弃用)或clazz.getDeclaredConstructor().newInstance():绕过编译期检查,适用于插件、框架扩展,但可能抛NoSuchMethodException或IllegalAccessException
ObjectInputStream.readObject()):不调用任何构造方法,直接分配内存并填充字段值,因此静态块和构造逻辑不会执行——这点常被忽略,导致单例被破坏或资源未初始化看似简单的new操作背后有明确的类加载阶段顺序:加载 → 验证 → 准备 → 解析 → 初始化。其中“初始化”阶段才执行static代码块和static变量赋值。
static { System.out.println("init"); },首次new该类实例时才会输出,而非编译或第一次引用类名时Class.forName("X")默认会触发初始化;而ClassLoader.loadClass("X")不会——这是动态加载时控制初始化时机的关键区别
javap -c查看字节码可验证:每个new指令后紧跟着dup和invokespecial(即构造方法调用)这不是语法错误,而是运行时NullPointerException的高发源头,尤其在依赖注入失败、条件分支遗漏或集合取值未判空时。
obj.toString(),先做if (obj != null)或用Objects.toString(obj)(后者对null返回字符串"null")@Autowired字段为null,大概率是该类没走Spring容器管理(比如用new手动实例化),而非注解写错了String s;),编译直接报错;但成员变量会被默认初始化为null/0/false,容易掩盖逻辑缺陷Java对象是否被回收,不仅看new了多少次,更取决于可达性分析——而引用类型决定了GC时的处理策略。
StrongReference(普通= new X()):只要强引用存在,GC绝不回收WeakReference:GC时无论内存是否充足都会回收,适合做缓存(如WeakHashMap)SoftReference:内存不足时才回收,比弱引用“软”一点,曾用于图片缓存,但JDK 12+后回收策略更不可控,慎用PhantomReference:无法通过它获取对象实例,仅用于跟踪对象被
ReferenceQueue
WeakReference对象是否真正“可用”,取决于它有没有逃过GC、构造方法是否完整执行、以及所有依赖是否就绪——这些环节任何一个出问题,都可能让wr = new WeakReference<>(new String("hello")); System.gc(); // 不保证立即执行,但wr.get()很可能返回null
new出来的对象处于不可用状态。