Java内部类(Inner Class)小记
一、引子
看到Trinea的博文Junit單測代碼中java序列化失敗的解決,讓我想到Java內部類的一些小Gocha,初學Java時很迷惑。這里記錄一下。
就以Trinea的博文中的序列化失敗的例子做為引子吧。
方便演示先準備一個工具方法:
package com.oldratlee.io.s; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; public class?Util?{public?static?void?writeObject(T?data)?throws?IOException?{ObjectOutputStream out = null;try {out = new ObjectOutputStream(new ByteArrayOutputStream());out.writeObject(data);} finally {if (null != out) {try {out.close();} catch (Exception e) {}}}} }下面的測試運行失敗:
package com.oldratlee.io.s; import java.io.Serializable; import org.junit.Test; public class?InnnerClassSerialization_FailTest?{interface?GetDataInterface?extends?Serializable?{public?Object?getData();}GetDataInterface attributeData = new GetDataInterface() {private static final long serialVersionUID = 1L;public?Object?getData()?{return null;}};public class?InnerClassSerialize?implements?Serializable?{private static final long serialVersionUID = 1L;}@Testpublic?void?testSerializable_Attribute_AnonymousClass()?throws?Exception?{Util.writeObject(attributeData);}@Testpublic?void?testSerializable_LocalVar_AnonymousClass()?throws?Exception?{GetDataInterface local = new GetDataInterface() {private static final long serialVersionUID = 1L;public?Object?getData()?{return null;}};Util.writeObject(local);}@Testpublic?void?testSerializable_InnerClass()?throws?Exception?{InnerClassSerialize data = new InnerClassSerialize();Util.writeObject(data);} }上面的3個測試用例全部運行失敗。
出錯異常相同:
java.io.NotSerializableException: com.oldratlee.io.s.InnnerClassSerialization_FailTestat java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1164)at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1518)at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1483)at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1400)at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1158)at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:330)at com.oldratlee.io.s.Util.writeObject(Util.java:12)at com.oldratlee.io.s.InnnerClassSerialization_FailTest.testSerializable_Attribute_AnonymousClass(InnnerClassSerialization_FailTest.java:26)......異常信息里,可以看到報的類com.oldratlee.io.s.InnnerClassSerialization_FailTest類不可序列化。
三個Test Case序列化是操作沒有顯式涉及這個類,為什么序列化時要用到這個類呢?后說到內部類的特性會給出原因。在本文的最后一節給出了解決方法。
二、內部類特性
1) 內部類和外部類可以互相訪問所有成員(屬性&方法)
示例如下,只演示了可以互相訪問Private屬性。
package com.oldratlee.io.s; public class?A?{private int attrib;private static int staticAttrib;public class?Inner1?{private int a1;public?int?method1()?{return attrib;}}public static class?StaticInner2?{private int sa1;public?int?method2()?{return staticAttrib;}}public?int?method()?{int a = new Inner1().a1;a += new StaticInner2().sa1;return a;} }2) 非靜態內部類隱含了一個指向外部類引用
內部類實例通過這個指向外部類引用,與外部類實例關聯起來,才能夠訪問外部類實例的成員。
可以通過 外部類名.this 來在內部類中訪問這個引用。當內部類的成員和外部類成員重名時,就通過外部類引用來訪問外部類的成員;不重名的外部類屬性可以省去寫上這個引用。
代碼示例:
package com.oldratlee.io.s; public class?B?{private int foo;private int age;public class?Inner?{private int foo;public?int?method()?{return foo + B.this.foo + age;}} }這一點就是引子里,UT運行失敗的原因。
3) 靜態成員沒有隱含了一個指向外部類引用
4) 匿名內部類如何聲明靜態
三、內部類使用方法
四、內部類的使用場景
五、總結
六、引子問題的解決
1) 修改外部類
外部類(UT類)實現了Serializable接口,這樣外部類可以序列化,下面UT通過。
package com.oldratlee.io.s; import java.io.Serializable; import org.junit.Test; public class?InnnerClassSerializationTest?implements?Serializable?{private static final long serialVersionUID = 1L;interface?GetDataInterface?extends?Serializable?{public?Object?getData();}GetDataInterface attributeData = new GetDataInterface() {private static final long serialVersionUID = 1L;public?Object?getData()?{return null;}};public class?InnerClassSerialize?implements?Serializable?{private static final long serialVersionUID = 88940079192401092L;}@Testpublic?void?testSerializable_Attribute_AnonymousClass()?throws?Exception?{Util.writeObject(attributeData);}@Testpublic?void?testSerializable_LocalVar_AnonymousClass()?throws?Exception?{GetDataInterface local = new GetDataInterface() {private static final long serialVersionUID = 1L;public?Object?getData()?{return null;}};Util.writeObject(local);}@Testpublic?void?testSerializable_InnerClass()?throws?Exception?{InnerClassSerialize data = new InnerClassSerialize();Util.writeObject(data);} }2) 修改內部類成為靜態
這樣就不會引用外部類,沒有報外部類沒有序列化的異常。
演示了聲明靜態內部類的方法:
非匿名內部類,在類聲明上加上static關鍵字即可 匿名內部類,a) 類屬性用到匿名內部類時,把類屬性聲明成靜態 b) 方法里用到匿名內部類時,把方法聲明成靜態
# 如果類屬性、方法不是靜態的,生成匿名內部類則不是靜態類。
【注】這里使用了JUnit 3,因為JUnit 3允許UT方法為靜態;JUnit 4的UT方法,不能為靜態,運行時會拋Exception中止。
from:?http://oldratlee.com/516/tech/java/java-inner-class-tips.html
總結
以上是生活随笔為你收集整理的Java内部类(Inner Class)小记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java对象初始化顺序
- 下一篇: MessagePack:一种高效二进制序