也说Java的双括符初始化:其实就是令人费解的不规范代码

初看 cgaolei 翻译的 Java技巧之双括弧初始化 一文,走马观花,只知用法,未细看后面的解释。蔚为惊艳,心里想 Java 竟然有这么神奇的语法而一直未得知。因为在初始化集合时确实方便不少。原来做某些测试要初始化集合时会用到 commons-lang 包和 JDK 的 Arrays 工具类,现在知道可以这么用了:

01.Map map = new HashMap() {{
02.        put("Name", "Unmi");
03.        put("QQ", "1125535");
04.}};
05.
06.List stooges = new ArrayList() {{
07.        add("Larry");
08.        add("Moe");
09.        add("Curly");
10.}};

看起来都是在一条语句里完成,而不需要分步骤写成:

1.Map map = new HashMap();
2.map.put("Name","Unmi");
3.map.put("QQ","1125535");

一不小心没好好理解的人可能以为它是什么特别的语法,关键是大括号连一块了,原作者也是在故作姿态,美其名曰:双括弧语法(double-brace syntax)。真是乱花渐欲迷人眼,其实就是匿名类加初始块。该文有解释:第一层括弧 实际是定义了一个内部匿名类 (Anonymous Inner Class),第二层括弧 实际上是一个实例初始化块 (instance initializer block),这个块在内部匿名类构造时被执行。

那怎么去更好理解它呢?如果我们写成如下的方式应该会更好理解吧,提个技巧,在 Eclipse 中对第一段代码按下 Ctrl + Shift + F 就如下了:

1.Map map = new HashMap() {
2.    {
3.        put("Name", "Unmi");
4.        put("QQ", "1125535");
5.    }
6.};

其实就是匿名类啊,会创建出一个 HashMap 的子类来,匿名类中一个 {} 括起来的初始化块,里面自然可放置初始化代码。{} 块中的代码编译后会放到 <init>(),也就是构造方法中去,所以可用来初始化实例。如果是写在 TestDoubleBrace 类中,编译后你会看到会生成 TestDoubleBrace$1.class 文件,反编译该文件内容是:

01.final class com.unmi.TestDoubleBrace$1 extends java.util.HashMap{ //创建了一个 HashMap 的子类 TestDoubleBracke$1
02.com.unmi.TestDoubleBrace$1();
03.  Code:
04.   0:   aload_0
05.   1:   invokespecial   #8; //Method java/util/HashMap."<init>":()V   //{} 中的代码放到了构造方法中去了
06.   4:   aload_0
07.   5:   ldc     #10; //String Name
08.   7:   ldc     #12; //String Unmi
09.   9:   invokevirtual   #14; //Method put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
10.   12:  pop
11.   13:  aload_0
12.   14:  ldc     #18; //String QQ
13.   16:  ldc     #20; //String 1125535
14.   18:  invokevirtual   #14; //Method put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
15.   21:  pop
16.   22:  return
17.
18.}

所以说白了,什么双括弧语法啊,就是代码写得不规范,才使得那么的令人费解。如果还不能理解,再列两个惯用代码来:

01.JFrame frame = new JFrame();
02.frame.addMouseListener(new MouseAdapter() {
03.    public void mouseClicked(MouseEvent e) {
04.        // do womething here.
05.    }
06.});
07.
08.Thread thread = new Thread() {{ // 也学着样把大括号也连一块写了
09.        this.setName("作业处理线程");
10.    }// 如果不重新定义 run() 方法,那么后面那个大括号也能与这个并一块
11.
12.    public void run() {
13.        // do something here.
14.    }
15.};
16.thread.start();

应该没问题了吧,上面是事件监听器和多线程常用的写法,如果他不把大括号连在一起,而是规范的写代码,相信您一开始也不会对所谓的 Double Brace Syntax 有太多的困惑。要说这种初始化方法运用到集合中还挺方便的,只是无端的多了些匿名类。

刚开始我看到这种双括符写法也是把它奉若圣经,对它只一知半解,昨天在用 XStream 把一个对象生成 XML 文件时,其中有一个 List 属性,我就借用了这种双括符法来初始化元素,结果生成的 XML 文件走了样,原因是 XStream 的 Converter 能处理 ArrayList,但无法很好的处理生成的 ArrayList 的匿名子类。因此才回头认真的重新审视了一番这个所谓的双括符初始化语法。

时间: 2024-08-22 14:36:26

也说Java的双括符初始化:其实就是令人费解的不规范代码的相关文章

JAVA中使用双括号来初始化静态常量的小技巧_java

这貌似是个不为人知的语言技巧.我看到一般人写Java里初始化静态常量都是 复制代码 代码如下: public static final Map<String, String> DATA = new TreeMap<String, String>(); static{ DATA.put("a", "A"); //blah blah blah} 使用所在类的static块来初始化DATA,其实还有另外一种写法: 复制代码 代码如下: public

关于Java中各种修饰符与访问修饰符的说明

访问 补充一下JAVA的基础知识(转)关于Java中各种修饰符与访问修饰符的说明 类:访问修饰符  修饰符  class 类名称 extends 父类名称 implement 接口名称 (访问修饰符与修饰符的位置可以互换) 访问修饰符 名称 说明 备注 public 可以被所有类访问(使用) public类必须定义在和类名相同的同名文件中 package 可以被同一个包中的类访问(使用) 默认的访问权限,可以省略此关键字,可以定义在和public类的同一个文件中   修饰符 名称 说明 备注 f

java中final修饰符实例分析_java

final修饰符: final修饰成员变量必须有程序员显示指定初始值. 类的Field:必须在静态初始化块中或声明该Field时指定初始值. 实例Field:必须在非静态初始块中,声明Field或者构造器中指定初始值. final局部变量:必须由程序员显示初始化. final修饰的基本变量和引用类型变量的区别? final修饰的基本变量:不能对基本变量重新赋值. final修饰的引用变量:只保证这个引用类型所引用的地址不会变,即 一直引用同一个对象,但这个对象完全可以发生改变. 复制代码 代码如

Java的访问修饰符与变量的作用域讲解_java

Java访问修饰符(访问控制符)Java 通过修饰符来控制类.属性和方法的访问权限和其他功能,通常放在语句的最前端.例如: public class className { // body of class } private boolean myFlag; static final double weeks = 9.5; protected static final int BOXWIDTH = 42; public static void main(String[] arguments) {

JAVA中对象创建和初始化过程

分析一下JAVA中对象创建和初始化过程中涉及的相关概念问题,java中栈(stack)与堆(heap),对象.引用.句柄的概念. 1.Java中的数据类型 Java中有3个数据类型: 基本数据类型(在Java中,boolean.byte.short.int.long.char.float.double这八种是基本数据类型) 引用类型 null类型 其中,引用类型包括类类型(含数组).接口类型. 下列语句声明了一些变量: 以下是引用片段: int k ; A a; //a是A数据类型的对象变量名.

关于java中加载和初始化的疑问?各位帮忙分析下

问题描述 关于java中加载和初始化的疑问?各位帮忙分析下 public class ExA { private static ExA a = new ExA(); static { System.out.println("父类--静态代码块"); } public ExA() { System.out.println("父类--构造函数"); } { System.out.println("父类--非静态代码块"); } public stat

java 二维字符串数组初始化及赋值问题

问题描述 java 二维字符串数组初始化及赋值问题 错误是"{ expected after token ; "请问为什么会出现这样的错误,该怎么解决? 解决方案 第一句话没错,第二句话,要放到具体的方法内,比如构造函数里,不要放到类一层的地方 解决方案二: 你确定有报错?我像这样写都没有报错! 解决方案三: java字符串数组初始化和赋值

在java中如何获取或初始化scala.collection.immutable.Map?

问题描述 在java中如何获取或初始化scala.collection.immutable.Map? java调用kafka API时需要一个一个scala.collection.immutable.Map参数,但不知道如何初始化或通过其他API获取 解决方案 java.util.Map javaMap = new java.util.HashMap();scala.collection.immutable.Map scalaImmutableMap = scala.collection.Jav

java中如何理解这种初始化类实例的方式,我只懂new的方式

问题描述 java中如何理解这种初始化类实例的方式,我只懂new的方式 java中public boolean setViewValue(Viewarg0,Object arg1){ImageView imageView =(ImageView)arg0 Bitmap bitmap=(Bitmap)arg1}如何理解这种初始化类实例的方式,我只懂new的方式 解决方案 这种构造方法是将 依赖的成员对象作为构造函数的参数传进入来的,而传人时还是需要new的啊. 解决方案二: 这没有什么别的,只是a