java new(创建)对象时结尾带上{}和不带的区别

Java 中 new Person () 与 new Person (){} 的区别:匿名类原理与实践

在 Java 中,创建对象看似只有new 类名()一种常规方式,但new 类名(){} 这种带大括号的写法却容易让人困惑。本文通过案例对比两种创建方式的差异,解析匿名类的本质,并介绍其在泛型处理中的实用场景。

一、两种对象创建方式的直观对比

首先定义一个基础的Person类,作为后续对比的载体:

// 基础Person类

public class Person {

     // 普通成员方法

     public void say() {

         System.out.println("hello");

     }

}

接下来看两种创建Person对象的写法:

创建方式 代码示例 直观感受
常规方式 Person p1 = new Person(); 熟悉,直接创建 Person 实例
带大括号的方式 Person p2 = new Person(){}; 陌生,大括号的作用是什么?

二、类信息差异:揭开 Main$1 的神秘面纱

要理解二者的区别,最直接的方式是打印对象的类信息,观察底层类型是否一致。

案例:打印类信息对比

public class Main {

     public static void main(String[] args) {

         // 常规方式创建对象

         Person p1 = new Person();

         // 带大括号方式创建对象

         Person p2 = new Person(){};

          

         // 打印两个对象的类信息

         System.out.println("p1的类信息:" + p1.getClass());

         System.out.println("p2的类信息:" + p2.getClass());

     }

}

运行结果:

p1的类信息:class Person

p2的类信息:class Main$1

关键结论:

  • p1的类型是原始的 Person 类,符合预期;

  • p2的类型是Main$1Main是当前主类名,$1表示这是主类中定义的第一个匿名类),并非原始 Person 类。

这说明:new Person(){} 并没有直接创建 Person 实例,而是创建了一个继承自 Person 的匿名子类的实例!

三、匿名子类的本质:继承与重写

为了进一步验证 “匿名子类” 的结论,我们从两个角度展开:查看父类信息、重写父类方法。

1. 验证父类:匿名子类的父类是 Person

通过getSuperclass()方法获取p2的父类,确认其继承关系:

public class Main {

     public static void main(String[] args) {

         Person p2 = new Person(){};

         System.out.println("p2的类信息:" + p2.getClass());

         System.out.println("p2的父类信息:" + p2.getClass().getSuperclass());

     }

}

运行结果:

p2的类信息:class Main$1

p2的父类信息:class Person

结论:Main$1(匿名类)的父类确实是Person,印证了 “匿名子类” 的本质。

2. 重写父类方法:匿名类的核心能力

既然是子类,就可以重写父类的方法,还能添加静态初始化块实例初始化块(代码块)。案例如下:

public class Main {

     public static void main(String[] args) {

         // 创建Person的匿名子类实例,并重写方法、添加初始化块

         Person p2 = new Person(){

             // 静态初始化块(类加载时执行,仅执行一次)

             static {

                 System.out.println("匿名类静态初始化");

             }

              

             // 实例初始化块(对象创建时执行,每次new都执行)

             {

                 System.out.println("匿名类实例初始化");

             }

              

             // 重写父类Person的say()方法

             @Override

             public void say() {

                 super.say(); // 调用父类的say()方法

                 System.out.println("匿名子类重写:hello + 1");

             }

         };

          

         // 调用重写后的say()方法

         p2.say();

     }

}

运行结果:

匿名类静态初始化

匿名类实例初始化

hello  // 父类say()的输出

匿名子类重写:hello + 1  // 子类重写后的追加输出

关键说明:

  • 静态初始化块:在匿名类加载时执行,仅一次;

  • 实例初始化块:在匿名类实例创建时执行,每次new都会执行;

  • 重写方法:通过@Override标注,可扩展或修改父类的方法逻辑。

四、匿名类的实用场景:泛型类型获取

泛型在 Java 中存在 “类型擦除” 机制 —— 编译后泛型参数会被擦除为Object,无法直接获取原始泛型类型。但通过 “匿名子类继承泛型类” 的方式,可以优雅地解决这个问题。

场景背景:常规方式无法获取泛型类型

定义一个泛型类Person<E>

// 泛型类

public class Person<E> {

     private E e;

}

若直接创建Person<Integer>实例,无法获取泛型参数Integer

public class Main {

     public static void main(String[] args) {

         // 常规方式创建泛型实例

         Person<Integer> p = new Person<>();

         // 编译后泛型被擦除,无法直接获取Integer类型

         System.out.println(p.getClass().getGenericSuperclass());  

         // 输出:java.lang.Object(父类是Object,无泛型信息)

     }

}

解决方案:匿名子类保留泛型信息

通过new Person<Integer>(){} 创建匿名子类实例,子类会保留父类的泛型参数信息,从而可以获取:

import java.lang.reflect.ParameterizedType;

import java.lang.reflect.Type;

public class Main {

     public static void main(String[] args) {

         // 创建Person<Integer>的匿名子类实例

         Person<Integer> p = new Person<>(){};

          

         // 1. 获取匿名子类的父类(即Person<Integer>)

         Type genericSuperclass = p.getClass().getGenericSuperclass();

          

         // 2. 强制转换为ParameterizedType(带泛型参数的类型)

         ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;

          

         // 3. 获取泛型参数数组(此处只有一个参数Integer)

         Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();

          

         // 4. 输出泛型类型

         System.out.println("泛型类型:" + actualTypeArguments[0]);

     }

}

运行结果:

泛型类型:class java.lang.Integer

优势对比:

传统方式需要创建一个显式的子类(如PersonS extends Person<Integer>)才能保留泛型信息,而匿名类无需额外定义子类,代码更简洁优雅:

方式 代码复杂度 适用场景
显式子类 需额外定义子类(如 PersonS) 需多次复用该泛型类型
匿名子类 无需额外定义类,一行搞定 单次使用,追求简洁

五、总结:两种创建方式的核心差异

对比维度 new Person() new Person(){}
创建的对象类型 原始 Person 类的实例 Person 的匿名子类的实例
类名 明确(Person) 隐式(如 Main$1)
能否重写方法 不能(仅使用父类方法) 能(可重写父类方法、添加初始化块)
泛型信息保留 不保留(类型擦除后无法获取) 保留(可获取父类泛型参数)
适用场景 常规对象创建,无需扩展父类逻辑 临时扩展父类逻辑、泛型类型获取等场景

通过本文的案例可以发现:new 类名(){} 的本质是创建 “匿名子类实例”,而非原始类实例。这种写法在需要临时扩展类逻辑、解决泛型类型获取等场景中,能发挥简洁高效的作用。

posted @ 2023-11-01 19:39  雨落寒沙  阅读(251)  评论(0)    收藏  举报