根据class名 赋值_匿名内部类 类名规则
今天發現友盟錯誤反饋一條信息如下:
從信息中可以確定是TencentUtil類中某個匿名內部類出了問題,但是因為此類中匿名內部類過多,具體定位是哪一個倒是有點不確定了,所以抽時間研究了下匿名內部類相關知識。
匿名內部類屬于內部類的其中一種,從內部類講起,內部類類型共有如下:
1.成員內部類
public class A {
String s;
class B{
}
}
B就是成員內部類,實例化B需要先實例化A對象(B b = new A().new B();),B會持有A對象的引用,所以鑒于這點,引出java中的內存泄漏問題。
2.局部內部類
public class A {
public C getB(){
class B extends C{
String s = "B";
}
return new B();
}
}
class C{
String s;
}
局部內部類是定義在一個方法或者一個作用域里面的類,它和成員內部類的區別在于局部內部類的訪問僅限于方法內或者該作用域內。
3.匿名內部類
匿名內部類應該是平時我們編寫代碼時用得最多的,在編寫事件監聽的代碼時使用匿名內部類不但方便,而且使代碼更加容易維護。
例如點擊事件
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
}
});
再例如子線程的runnable對象
4.靜態內部類
這個把前面的成員內部類拿過來,前面加個static就行了
public class A {
String s;
static class B{
}
}
然后實例化的時候就不需要先實例化A,(B b = new A.B();),B也不會再持有A的對象引用,所以將內部類改為static能解決內存泄漏這個說法原因是在這。
看完上面 回到本文的出發點上來,內部類命名規則。
這么記吧,所有內部類會在編譯的時候產生相對應的class文件,非匿名內部類類名規則為 OutClass$InnerClass (外部類類名與內部類類名中間用$連接) 匿名內部類類名則為OutClass$數字(OutClass$1,OutClass$2,OutClass$3)
public class A {
C c = new C(){
String s="i am c";
@Override
public void demo() {
}
};
C c2 = new C(){
String s="i am c2";
@Override
public void demo() {
}
};
}
interface C{
public void demo();
}
Thread對象是本類中第六個匿名內部類,runnable對象是這個thread對象中的第一個匿名內部類,OK TencentUtil$6$1.run代表的意思就是這個runnalbe中的run方法了。
===============================分割線===========================
最后說一個相關的問題。我們在用局部內部類和匿名內部類時,都要求局部變量為final,這是為啥呢?
想必這個問題也曾經困擾過很多人,在討論這個問題之前,先看下面這段代碼:
public class Test {
public static void main(String[] args) {
}
public void test(final int b) {
final int a = 10;
new Thread(){
public void run() {
System.out.println(a);
System.out.println(b);
};
}.start();
}
}
這段代碼會被編譯成兩個class文件:Test.class和Testx.class(x為正整數)。
根據上圖可知,test方法中的匿名內部類的名字被起為 Test$1。
上段代碼中,如果把變量a和b前面的任一個final去掉,這段代碼都編譯不過。我們先考慮這樣一個問題:
當test方法執行完畢之后,變量a的生命周期就結束了,而此時Thread對象的生命周期很可能還沒有結束,那么在Thread的run方法中繼續訪問變量a就變成不可能了,但是又要實現這樣的效果,怎么辦呢?Java采用了 復制 的手段來解決這個問題。將這段代碼的字節碼反編譯可以得到下面的內容:
我們看到在run方法中有一條指令:
bipush 10
這條指令表示將操作數10壓棧,表示使用的是一個本地局部變量。這個過程是在編譯期間由編譯器默認進行,如果這個變量的值在編譯期間可以確定,則編譯器默認會在匿名內部類(局部內部類)的常量池中添加一個內容相等的字面量或直接將相應的字節碼嵌入到執行字節碼中。這樣一來,匿名內部類使用的變量是另一個局部變量,只不過值和方法中局部變量的值相等,因此和方法中的局部變量完全獨立開。
下面再看一個例子:
public class Test {
public static void main(String[] args) {
}
public void test(final int a) {
new Thread(){
public void run() {
System.out.println(a);
};
}.start();
}
}
反編譯得到:
我們看到匿名內部類Test$1的構造器含有兩個參數,一個是指向外部類對象的引用,一個是int型變量,很顯然,這里是將變量test方法中的形參a以參數的形式傳進來對匿名內部類中的拷貝(變量a的拷貝)進行賦值初始化。
也就說如果局部變量的值在編譯期間就可以確定,則直接在匿名內部里面創建一個拷貝。如果局部變量的值無法在編譯期間確定,則通過構造器傳參的方式來對拷貝進行初始化賦值。
從上面可以看出,在run方法中訪問的變量a根本就不是test方法中的局部變量a。這樣一來就解決了前面所說的 生命周期不一致的問題。但是新的問題又來了,既然在run方法中訪問的變量a和test方法中的變量a不是同一個變量,當在run方法中改變變量a的值的話,會出現什么情況?
對,會造成數據不一致性,這樣就達不到原本的意圖和要求。為了解決這個問題,java編譯器就限定必須將變量a限制為final變量,不允許對變量a進行更改(對于引用類型的變量,是不允許指向新的對象),這樣數據不一致性的問題就得以解決了。
到這里,想必大家應該清楚為何 方法中的局部變量和形參都必須用final進行限定了。
總結
以上是生活随笔為你收集整理的根据class名 赋值_匿名内部类 类名规则的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python flask与django的
- 下一篇: tomcat中间件的默认端口号_等保2.