要了解如何修复它,您应该首先了解它在做什么。为此,您需要了解两件事:
- 匿名子类
- 实例初始化程序块
如何修复它的 TL;DR 只是将这些put
调用和初始化从 setter 中分离出来:
Map<String, String> valueMap = new LinkedHashMap<String, String>();
valueMap.put("0", "None");
valueMap.put("1", "Subtle");
valueMap.put("2", "Intrusive");
notifyTypeItem.setValueMap(valueMap);
继续阅读以了解正在发生的事情以及为什么它可能是一种不好的方法。
匿名子类
匿名类通常只是一个没有名字的类。例如,您可以创建如下接口的匿名实例Runnable
:
Runnable r = new Runnable() {
@Override
public void run() {
// Do something
}
};
r.run(); // Does that something
同样,您也可以创建抽象类和具体类的匿名实例。例如,创建一个匿名实例是很常见的ThreadLocal
:
private static ThreadLocal<SimpleDateFormat> localIsoDateFormat = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
}
当您不需要一个完整的、专用的类来覆盖一个或两个方法时,这很有用,类似于只用一个方法创建接口的匿名实例。
实例初始化程序块
实例初始化程序块允许您在构造函数之外执行初始化程序。例如:
public class MyClass {
private final String s;
{
s = "My Class String";
}
public String getS() { return s; }
}
它本质上是构造函数的替代品,通常是不必要的,所以你很少看到它。它几乎总是可以移动到构造函数中。
结合它们
您的示例结合了它们。它正在创建一个匿名子类LinkedHashMap
,然后它还使用了一个初始化块。格式更正确,您的代码是:
Map<String, String> map = new LinkedHashMap<>() {
{
put("0", "None");
put("1", "Subtle");
put("2", "Intrusive");
}
};
它是一个匿名实例,LinkedHashMap
带有执行put
调用的实例初始化程序块。
为什么不好?
出于同样的原因,您需要小心创建匿名类:对封闭类实例的引用。
匿名类因成为应用程序内存泄漏的根源而臭名昭著。您的代码似乎在非static
上下文中。这意味着LinkedHashMap
您创建的匿名子类将隐式引用您的方法所在的类。例如,如果您的方法位于MyClass
:
public class MyClass {
private SelectItem notifyTypeItem = new SelectItem();
public void foo() {
notifyTypeItem.setTitle("Default Notification");
notifyTypeItem.setWidth("100%");
notifyTypeItem.setValueMap(new LinkedHashMap<String, String>() {{
put("0", "None");
put("1", "Subtle");
put("2", "Intrusive");
}}
);
}
}
新创建的LinkedHashMap
子类(MyClass$1
将是“类名”,如果你可以这样称呼它)将引用封闭MyClass
实例。在某些情况下,这可能没问题。但是,如果您创建的notifyTypeItem
目的是将其传递给其他东西并丢弃您的MyClass
实例,那么您将在您的应用程序中创建内存泄漏。实例将MyClass
被MyClass$1
实例引用,并且SelectItem
将引用MyClass$1
实例,因此在不再引用该实例MyClass
之前,该实例永远不会被垃圾回收SelectItem
。如果MyClass
除了 之外还有其他几个引用SelectItem
,那么这只会使总内存增加一个MyClass
实例消耗,并导致更多的内存泄漏问题。
参考
以下是一些相关链接: