今天在看springboot的batch时, 看到这样一段代码, 直接把我看懵了, 于是找了一下, 发现这 两个大括号 {{  叫实例初始化器

 FlatFileItemReader<Person> reader = new FlatFileItemReader<>();

        // 设置 csv文件的路径
reader.setResource(new ClassPathResource("people.csv"));
// 对cvs文件的数据和领域模型做对应映射
reader.setLineMapper(new DefaultLineMapper<Person>() {{
setLineTokenizer(new DelimitedLineTokenizer() {{
setNames(new String[] {"name", "age", "nation", "address"});
}});
setFieldSetMapper(new BeanWrapperFieldSetMapper<Person>() {{
setTargetType(Person.class);
}});
}});

1, 什么是java的实例初始化器

1, 平时创建map并put值的时候, 是这样操作的

Set<Integer> set = new HashSet<>();
set.add();
set.add();
set.add();

或者, 在静态代码块中进行初始化

private static final Set<Integer> set = new HashSet<>();
static {
set.add();
set.add();
set.add();
}

或者, 简写成这种形式的

Set<Integer> set = new HashSet<Integer>() {{
add();
add();
add();
}};

于是, 可以作为函数参数传入:

someFunction(new HashSet<Integer>() {{
add();
add();
add();
}}
);

这儿的大括号, 其实就是一段 { } 局部代码的写法:

Set<Integer> set = new HashSet<Integer>() {
{
add();
add();
add();
}
};

这样, 不管使用哪一个构造器, 都会执行我们的这段代码

2, 反模式的内存泄漏

原博客地址: http://deepinmind.iteye.com/blog/2165827

说它是一个反模式主要出于三方面的原因:

1. 可读性

可读性是最次要的一个原因。尽管它使得程序编写变得更简单,并且看起来跟JSON中数据结构的初始化差不多:

{
"firstName" : "John"
, "lastName" : "Smith"
, "organizations" :
{
"" : { "id", "" }
, "abc" : { "id", "" }
}
}

2. 一个实例,一种类型

通过一次双括号的初始化我们其实就已经创建了一个新类型了!通过这种方式所生成的每一个新map,都会隐式地创建了一个无法重复使用的新类型。如果仅用一次的话也无可厚非。但如果在一个大型的应用中到处都充斥着这种代码的话,无形中会给你的类加载器增加了许多负担,你的堆会持有着这些类的引用。不信么?编译下上述代码并查看下编译器的输出。大概会是这样的:

Test$$$.class
Test$$$.class
Test$$.class
Test$.class
Test.class

这里只有最外围的Test.class是有意义的。

不过这还不是最重要的问题。

3. 内存泄露!

最重要的问题就是匿名类所造成的。它们持有着外围实例的引用,这简直就是个定时ZHADAN。想像一下,你把这个看似NB的HashMap初始化放到一个EJB或者是一个很重的包含着这样的生命周期的对象里:

public class ReallyHeavyObject {  

    // Just to illustrate...
private int[] tonsOfValues;
private Resource[] tonsOfResources; // This method almost does nothing
public void quickHarmlessMethod() {
Map source = new HashMap(){{
put("firstName", "John");
put("lastName", "Smith");
put("organizations", new HashMap(){{
put("", new HashMap(){{
put("id", "");
}});
put("abc", new HashMap(){{
put("id", "");
}});
}});
}}; // Some more code here
}
}

这个ReallyHeavyObject类中有许多资源,当ReallyHeavyObject对象被垃圾回收掉时这些资源是需要尽快被释放掉的。不过调用quickHarmlessMethod()方法并不会造成什么影响,因为这个map很快就会被回收掉了。

好的。

我们假设另外一个开发人员,他重构了一下这个方法,返回了这个map,或者是map中的某一部分:

public Map quickHarmlessMethod() {
Map source = new HashMap(){{
put("firstName", "John");
put("lastName", "Smith");
put("organizations", new HashMap(){{
put("", new HashMap(){{
put("id", "");
}});
put("abc", new HashMap(){{
put("id", "");
}});
}});
}}; return source;
}

这下问题就严重了!现在你把ReallyHeavyObject中的所有状态都暴露给外部了,因为每个内部类都会持有一个外围实例的引用,也就是ReallyHeavyObject实例。不信么?运行下这段程序看看:

public static void main(String[] args) throws Exception {
Map map = new ReallyHeavyObject().quickHarmlessMethod();
Field field = map.getClass().getDeclaredField("this$0");
field.setAccessible(true);
System.out.println(field.get(map).getClass());
}

确实是这样!。如果你仍不相信的话,还可以使用调试器来查看下这个返回的map的内部状态。

你会发现外围实例的引用就在这个匿名的HashMap子类中安静地躺着。所有的这些匿名子类型都会持有一个这样的引用。

因此,不要使用这个反模式

你可能会说,如果将quickHarmlessMethod()声明成static的不就好了,这不会出现3中的泄露问题了,你说的没错。

不过上述代码中最糟糕的问题就是即便你知道这个静态上下文中的map该如何使用,但下一个开发人员可能会注意不到,他还可能会把这个static重构或者删除掉。他们还可能会把这个map存储在一个单例中,这样你就很难再从代码中看出哪里会有一个无用的ReallyHeavyObject的引用。

内部类是一头野兽。它已经造成过许多的问题以及认知失衡。匿名内部类则更为严重,因为读到这段代码的人可能完全没有意识到自己已经包装了一个外围实例,并且把这个实例传递到了别处。

结论便是:

不要自作聪明了,别使用双括号来进行初始化。

java-双大括号实例初始化的反模式的更多相关文章

  1. Java类、实例初始化的顺序

    求如下 java 代码的输出?? class T implements Cloneable{ public static int k = 0; public static T t1 = new T(& ...

  2. java类及实例初始化顺序

    1.静态变量.静态代码块初始化顺序级别一致,谁在前,就先初始化谁.从上而下初始化(只在类加载时,初始化一次) 2.非静态变量.非静态代码块初始化顺序级别一致,谁在前,就先初始化谁.从上而下初始化(只要 ...

  3. 对Java中使用两个大括号进行初始化的理解

    最近重读Java 编程思想,读到有关实例化代码块儿 的内容,使我对于使用两个大括号进行初始化有了更深的理解. 实例化代码块儿: 和静态代码块儿的概念相对应,静态代码块儿是static 关键字 + 大括 ...

  4. Java 类的实例变量初始化的过程 静态块、非静态块、构造函数的加载顺序

    先看一道Java面试题: public class Baset { private String baseName = "base"; // 构造方法 public Baset() ...

  5. 什么是Java实例初始化块

    在本篇文章,我将会使用一个例子展示什么是实例变量初始化块,实例初始化块和静态初始化块,然后说明在Java中实例初始化块是如何工作的. 执行顺序 查看下面的代码,你知道哪个先执行吗? package s ...

  6. Java静态初始化,实例初始化以及构造方法

    首先有三个概念需要了解: 一.静态初始化:是指执行静态初始化块里面的内容. 二.实例初始化:是指执行实例初始化块里面的内容. 三.构造方法:一个名称跟类的名称一样的方法,特殊在于不带返回值. 我们先来 ...

  7. java实例初始化块

    实例初始化程序块用于初始化实例数据成员. 它在每次创建类的对象时运行.实例变量的初始化可以是直接的,但是可以在初始化实例初始化块中的实例变量时执行额外的操作. 什么是实例初始化块的使用,我们可以直接分 ...

  8. 关于java中构造方法、实例初始化、静态初始化执行顺序

    在Java笔试中,构造方法.实例初始化.静态初始化执行顺序,是一个经常被考察的知识点. 像下面的这道题(刚刚刷题做到,虽然做对了,但是还是想整理一下) 运行下面的代码,输出的结果是... class ...

  9. Java初始化顺序(静态变量、静态初始化块、实例变量、实例初始化块、构造方法)

    1.执行顺序 1.1.一个类中的初始化顺序 类内容(静态变量.静态初始化块) => 实例内容(变量.初始化块.构造器) 1.2.两个具有继承关系类的初始化顺序 父类的(静态变量.静态初始化块)= ...

随机推荐

  1. win7 环境安装Python + IDE(vs2010)开发

    1.下载python安装文件 python-2.7.10.msi 网址:https://www.python.org/downloads/release/python-2710/ ,根据自己环境,选择 ...

  2. C++中的数组问题

    C++中的数组问题 1. 数组赋值与初始化 (1)直接初始化: ]={,,}: (2)遍历访问初始化: ;i< ;i++) //直接读入,或者用别的数组,以及别的(i+1)等. (3)内存操作函 ...

  3. 快速掌握Java中Lambda表达式的用法

    Lambda表达式的作用: Lambda表达式的作用就是简化代码开发,让代码看起来更加简介.它是用来简化匿名内部类的.但是并不是所有的匿名内部类都能用Lambda表达式简化,Lambda表达式是有使用 ...

  4. 深入研究java.lang.Process类

    一.概述 Process类是一个抽象类(所有的方法均是抽象的),封装了一个进程(即一个执行程序).       Process 类提供了执行从进程输入.执行输出到进程.等待进程完成.检查进程的退出状态 ...

  5. 查看sql server数据库连接数的三种方法

    怎样才能查看sql server数据库连接数呢?下面就将为您介绍三种查看的方法,供您参考,希望能够帮助到您. 1.通过系统的“性能”来查看:开始->管理工具->性能(或者是运行里面输入 m ...

  6. EasyUi TreeGrid/DataGrid getChecked BUG

    问题描述 步骤一,选中左边树一个节点,然后出现相应的数据,选中进行保存.如,我选中了前4个节点,上图: 步骤二,我再选tree中第二个节点,进行相应的选中和取消选中treegrid中的节点,并保存. ...

  7. Mac iTerm2登陆CentOS提示warning: setlocale: LC_CTYPE: cannot change locale (UTF-8): No such file or directory

    [报错原因]:没有utf-8这个语系(没添加语言_国名前缀),LC_ALL又没设定值. 服务端解决方法: 在远程系统上, /etc/environment 加入以下两行,重新登陆即可. LANG=en ...

  8. IIS 绑定 HTTPS 域名

    HTTPS为SSL安全通道,虽然并不清楚具体有什么用,但至少网站看上去比HTTP上档次,访问速度也没什么影响,所以有条件的话,还是做下,可以做噱头忽悠人. WIN2008系统 因为端口443冲突,只能 ...

  9. C# winform程序实现开机自启动,并且识别是开机启动还是双击启动

    开机启动程序,在很多场合都会用到,尤其是那种在后台运行的程序. 效果图: 以上两幅图都用到了命令行启动程序,为了模拟开机启动或者其他程序调用此程序. 第一幅图:程序启动可以根据不同参数,执行不同的操作 ...

  10. Win(Phone)10开发第(2)弹,导出APPX包并签名部署

    当我们新建一个win10 uap项目,如果想导出测试包,需要点击项目名称,选择商店-导出应用包,这个时候会生成一个文件夹,包含appx和ps1等文件. powershell运行Add-AppDevPa ...