問題描述
先來看一段代碼:
public class DoubleBracesTest { private String key = "key"; private String value="value"; public Map<String, String> test(String[] args){ Map<String, String> map = new HashMap() {{ put("k", "v"); put(key,value); }}; return map; } }
通過javac編譯後,生成文件:DoubleBracesTest.class 和 DoubleBracesTest$1.class,確認上面的代碼中的"{{"的方式寫法,採用了內部類來實現的。
用IDEA查看 DoubleBracesTest$1.class :
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // import java.util.HashMap; class DoubleBracesTest$1 extends HashMap { DoubleBracesTest$1(DoubleBracesTest var1) { this.this$0 = var1; this.put("k", "v"); this.put(this.this$0.key, this.this$0.value); } }
其中的 this.this$0 = var1 代表內部類持有了外部類的引用。
對應的字節碼:
字節碼中的 putfield這一行,這裏表示有一個對DoubleBracesTest的引用被存在了 this$0 中,也就是說它持有了外部類的對象。
test方法返回一個map,如果被其他對象的屬性所引用,GC時便不會回收此對象,從而導致內存泄漏!這個也是非靜態內部類的主要缺點。
非靜態內部類的優點
非靜態匿名內部類持有外部類可以總結爲以下兩個作用 :
1.當匿名內部類只在外部類(主類)中使用時,匿名內部類可以讓外部不知道它的存在,從而減少了代碼的維護工作。
2.當匿名內部類持有外部類時,它就可以直接使用外部類中的變量了,這樣可以很方便的完成調用。
改進方法
1、上述調用方法改成static方法
匿名內部類是靜態的之後,它所引用的對象或屬性也必須是靜態的了,因此就可以直接從 JVM 的 Method Area(方法區)獲取到引用而無需持久外部對象了,也就不會持有外部類的引用了。
但是後期難保不會有人將 static 關鍵字刪掉,那樣問題又會出現了!
2、
使用集合工廠的 of
方法替代
Map map = new HashMap() {{ put("k1", "v1"); put("k2", "v2"); }};
替換成:
Map<String, String> map= Map.of("k1", "v1", "k2", "v2");
List<String> list = new ArrayList() {{
add("aaa");
add("bbb");
}};
替換成:
List<String> list = new ArrayList<String>(Arrays.asList("aaa","bbb"));
Stream API 替代
List<String> list = new ArrayList() {{ add("aaa"); add("bbb"); }};
替換成
List<String> list = Stream.of("aaa", "bbb").collect(Collectors.toList());
參考
https://www.ripjava.com/article/1291630596325408
https://www.cnblogs.com/wenbronk/p/7000643.html
https://cloud.tencent.com/developer/article/1632486
https://cloud.tencent.com/developer/article/1179625
https://hacpai.com/article/1498563483898