国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語(yǔ)言|JavaScript|易語(yǔ)言|vb.net|

服務(wù)器之家 - 編程語(yǔ)言 - Java教程 - Java內(nèi)部類的實(shí)現(xiàn)原理與可能的內(nèi)存泄漏說(shuō)明

Java內(nèi)部類的實(shí)現(xiàn)原理與可能的內(nèi)存泄漏說(shuō)明

2020-10-08 15:49-SOLO- Java教程

這篇文章主要介紹了Java內(nèi)部類的實(shí)現(xiàn)原理與可能的內(nèi)存泄漏說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧

在使用java內(nèi)部類的時(shí)候要注意可能引起的內(nèi)存泄漏

代碼如下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.example;
public class MyClass {
 
 public static void main(String[] args) throws Throwable {
 
 }
 
 public class A{
  public void methed1(){
 
  }
 }
 
 public static class B{
  public void methed1(){
 
  }
 }

編譯生成了如下文件

Java內(nèi)部類的實(shí)現(xiàn)原理與可能的內(nèi)存泄漏說(shuō)明

反編譯MyClass

Java內(nèi)部類的實(shí)現(xiàn)原理與可能的內(nèi)存泄漏說(shuō)明

反編譯MyClassA

Java內(nèi)部類的實(shí)現(xiàn)原理與可能的內(nèi)存泄漏說(shuō)明

反編譯GlassB

Java內(nèi)部類的實(shí)現(xiàn)原理與可能的內(nèi)存泄漏說(shuō)明

從反編譯的結(jié)果可以知道,內(nèi)部類的實(shí)現(xiàn)其實(shí)是通過(guò)編譯器的語(yǔ)法糖實(shí)現(xiàn)的,通過(guò)生成相應(yīng)的子類即以O(shè)utClassName$InteriorClassName命名的Class文件。

并添加構(gòu)造函數(shù),在構(gòu)造函數(shù)中傳入外部類,這也是為什么內(nèi)部類能使用外部類的方法與字段的原因。

我們明白了這個(gè)也就要小心,當(dāng)外部類與內(nèi)部類生命周期不一致的時(shí)候很有可能發(fā)生內(nèi)存泄漏,例如在一個(gè)Activity啟動(dòng)一個(gè)Thread執(zhí)行一個(gè)任務(wù),因?yàn)門hread是內(nèi)部類持有了Activity的引用,當(dāng)Activity銷毀的時(shí)候如果Thread的任務(wù)沒(méi)有執(zhí)行完成,造成Activity的引用不能釋放,Activity不能被釋放而引起了內(nèi)存泄漏。

這種情況下可以通過(guò)聲明一個(gè)static的內(nèi)部類來(lái)解決問(wèn)題,從反編譯中可以看出,聲明為static的類不會(huì)持有外部類的引用,如果你想使用外部類的話,可以通過(guò)軟引用的方式保存外部類的引用。

具體的代碼就不上了。

補(bǔ)充知識(shí):Java內(nèi)部類的底層實(shí)現(xiàn)原理

摘要:

定義:在一個(gè)類中創(chuàng)建另一個(gè)類,叫做成員內(nèi)部類,這個(gè)內(nèi)部類可以是靜態(tài)的,也可以是非靜態(tài)的。

已知靜態(tài)內(nèi)部類的應(yīng)用(可以解決的問(wèn)題):

通過(guò)內(nèi)部類解決java 的單繼承問(wèn)題,外部類不能同時(shí)繼承的類可以交給內(nèi)部類繼承

設(shè)計(jì)模式中,builder 模式通過(guò)定義一個(gè)靜態(tài)內(nèi)部類實(shí)現(xiàn)

類型匯總:

靜態(tài)內(nèi)部類

成員內(nèi)部類

方法內(nèi)部類

匿名內(nèi)部類

一、靜態(tài)內(nèi)部類

靜態(tài)內(nèi)部類的定義和普通的靜態(tài)變量或者靜態(tài)方法的定義方法是一樣的,使用static關(guān)鍵字,只不過(guò)這次static是修飾在class上的,一般而言,只有靜態(tài)內(nèi)部類才允許使用static關(guān)鍵字修飾,普通類的定義是不能用static關(guān)鍵字修飾的,這一點(diǎn)需要注意一下。下面定義一個(gè)靜態(tài)內(nèi)部類:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Out {
 private static String name;
 private int age;
 
 public static class In{
  private int age;
  public void sayHello(){
   
   System.out.println("my name is : "+name);
   //--編譯報(bào)錯(cuò)---
   //System.out.println("my age is :"+ age);
  }
 }
}

在上述代碼中,In這個(gè)類就是一個(gè)靜態(tài)內(nèi)部類。我們說(shuō)內(nèi)部類是可以訪問(wèn)外部類的私有字段和私有方法的,對(duì)于靜態(tài)內(nèi)部類,它遵循一致的原則,只能訪問(wèn)外部類的靜態(tài)成員。上述代碼中,外部類的非靜態(tài)私有字段age在靜態(tài)內(nèi)部類中使不允許訪問(wèn)的,而靜態(tài)字段name則是可訪問(wèn)的。下面我們看,如何創(chuàng)建一個(gè)靜態(tài)內(nèi)部類的實(shí)例對(duì)象。

?
1
2
3
4
public static void main(String [] args){
 Out.In innerClass = new Out.In();
 innerClass.sayHello();
}

靜態(tài)內(nèi)部類的實(shí)例對(duì)象創(chuàng)建還是比較簡(jiǎn)潔的,不同于成員內(nèi)部類,它不需要關(guān)聯(lián)外部類實(shí)例(具體的下文介紹),下面我們?cè)倏匆欢未a:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Out {
 private static String name;
 
 public static class In{
  public void sayHello(){
   System.out.println(name);
   showName();
  }
 }
 
 private static void showName(){
  System.out.println(name);
 }
}

上述代碼在內(nèi)部類中兩次訪問(wèn)了外部類的靜態(tài)成員,第一次訪問(wèn)了靜態(tài)字段name,第二次訪問(wèn)的靜態(tài)方法showName。在我們反編譯這個(gè)類之前,首先需要知道的是,所謂的內(nèi)部類的概念只是出現(xiàn)在編譯階段,對(duì)于jvm層是沒(méi)有內(nèi)部類這個(gè)概念的。也就是說(shuō),編譯器會(huì)將一個(gè)類編譯成一個(gè)源文件,對(duì)于內(nèi)部類也是一樣,它會(huì)從它的外部類中抽離出來(lái),增加一些與外部類的聯(lián)系,然后被編譯成一個(gè)單獨(dú)的源文件。下面我們先編譯運(yùn)行之后,利用Dj反編譯class文件看看編譯器都做了些什么事情。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//這是我們的Out外部類
public class Out
{
 //省去了一些不重要的部分
 private static void showName()
 {
  System.out.println(name);
 }
 
 private static String name;
 
 static String access$000(){return name;}
 static void access$100(){showName();}
 
}
//這是我們的內(nèi)部類
public static class Out$In
{
 
 public void sayHello()
 {
  System.out.println(Out.access$000());
  Out.access$100();
 }
 
 public Out$In()
 {
 }
}

相信大家也已經(jīng)看出來(lái)這兩者之間的某種聯(lián)系,編譯器將Out這個(gè)類編譯成兩個(gè)獨(dú)立的class源文件。對(duì)于Out中所有的私有成員(也就是內(nèi)部類分離出去之后不能訪問(wèn)的成員),增設(shè)了可供調(diào)用的access$xxx方法,從而實(shí)現(xiàn)內(nèi)部類與外部類之間的聯(lián)系。這就是他們的本質(zhì)。

至于使用場(chǎng)景,一般來(lái)說(shuō),對(duì)于和外部類聯(lián)系緊密但是并不依賴于外部類實(shí)例的情況下,可以考慮定義成靜態(tài)內(nèi)部類。下面我們看稍顯復(fù)雜的成員內(nèi)部類。

二、成員內(nèi)部類

我們說(shuō)了,四種不同類型的內(nèi)部類都各自有各自的使用場(chǎng)景,靜態(tài)內(nèi)部類適合于那種和外部類關(guān)系密切但是并不依賴外部類實(shí)例的情況。但是對(duì)于需要和外部類實(shí)例相關(guān)聯(lián)的情況下,可以選擇將內(nèi)部類定義成成員內(nèi)部類。以下代碼定義了一個(gè)簡(jiǎn)單的成員內(nèi)部類:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Out {
 private String name;
 
 public void showName(){
  System.out.println("my name is : "+name);
 }
 
 public class In{
  public void sayHello(){
   System.out.println(name);
   Out.this.showName();
  }
 }
}

以上定義了一個(gè)簡(jiǎn)單的內(nèi)部類In,我們的成員內(nèi)部類可以直接訪問(wèn)外部類的成員字段和成員方法,因?yàn)樗顷P(guān)聯(lián)著一個(gè)外部類實(shí)例的。下面我們看看在外部是如何創(chuàng)建該內(nèi)部類實(shí)例的。

?
1
2
3
4
5
public static void main(String [] args){
 Out out = new Out();
 Out.In in = out.new In();
 in.sayHello();
}

因?yàn)槌蓡T內(nèi)部類是關(guān)聯(lián)著一個(gè)具體的外部類實(shí)例的,所以它的實(shí)例創(chuàng)建必然是由外部類實(shí)例來(lái)創(chuàng)建的。對(duì)于實(shí)例的創(chuàng)建,我們只需要記住即可,成員內(nèi)部類的實(shí)例創(chuàng)建需要關(guān)聯(lián)外部類實(shí)例對(duì)象,靜態(tài)內(nèi)部類實(shí)例創(chuàng)建相對(duì)簡(jiǎn)單。下面我們主要看看在編譯階段編譯器是如何保持內(nèi)部類對(duì)外部類成員信息可訪問(wèn)的。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//反編譯的Out外部類源碼
public class Out
{
 //省略部分非核心代碼
 public void showName()
 {
  System.out.println((new StringBuilder()).append("my name is : ").append(name).toString());
 }
 
 private String name;
 
 static String access$000(Out o){return o.name;}
}
//反編譯的內(nèi)部類In源碼
public class Out$In
{
 public void sayHello()
 {
  System.out.println(Out.access$000(Out.this));
  showName();
 }
 
 final Out this$0;
 
 public Out$In()
 {
  this.this$0 = Out.this;
  super();
 }
}

由上述代碼其實(shí)我們可以知道,當(dāng)我們利用外部類實(shí)例創(chuàng)建內(nèi)部類實(shí)例的時(shí)候,會(huì)將外部類實(shí)例作為初始資源傳入內(nèi)部類構(gòu)造過(guò)程。這樣我們就可以通過(guò)該實(shí)例訪問(wèn)外部類所有的成員信息,包括私有成員。(顯式增加了暴露方法)

至于使用場(chǎng)景,對(duì)于那種要高度依賴外部類實(shí)例的情況下,定義一個(gè)成員內(nèi)部類則會(huì)顯的更加明智。

三、方法內(nèi)部類

方法內(nèi)部類,顧名思義,定義在一個(gè)方法內(nèi)部的類。方法內(nèi)部類相對(duì)而言要復(fù)雜一些,下面定義一個(gè)方法內(nèi)部類:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Out {
 private String name;
 
 public void sayHello(){
  class In{
   public void showName(){
    System.out.println("my name is : "+name);
   }
  }
 
  In in = new In();
  in.showName();
 }
}

我們定義了一個(gè)類,在該類中又定義了一個(gè)方法sayHello,然而在該方法中我們定義了一個(gè)內(nèi)部類,類In就是一個(gè)方法內(nèi)部類。我們的方法內(nèi)部類的生命周期不超過(guò)包含它的方法的生命周期,也就是說(shuō),方法內(nèi)部類只能在方法中使用。所以在聲明的時(shí)候,任何的訪問(wèn)修飾符都是沒(méi)有意義的,于是Java干脆不允許使用任何的訪問(wèn)修飾符修飾方法內(nèi)部類。其中還需要注意一點(diǎn)的是,定義和使用時(shí)兩回事,別看那一大串定義類的代碼,你實(shí)際想要使用該類,就必須new對(duì)象,而對(duì)于方法內(nèi)部類而言,只能在方法內(nèi)部new對(duì)象。這就是方法內(nèi)部類的簡(jiǎn)單介紹,下面我們看看其實(shí)現(xiàn)原理。

有關(guān)方法內(nèi)部類的實(shí)現(xiàn)原理其實(shí)是和成員內(nèi)部類差不太多的,也是在內(nèi)部類初始化的時(shí)候?yàn)槠鋫魅胍粋€(gè)外部類實(shí)例,區(qū)別在哪呢?就在于方法內(nèi)部類是定義在具體方法的內(nèi)部的,所以該類除了可以通過(guò)傳入的外部實(shí)例訪問(wèn)外部類中的字段和方法,對(duì)于包含它的方法中被傳入的參數(shù)也會(huì)隨著外部類實(shí)例一起初始化給內(nèi)部類。

毋庸置疑的是,方法內(nèi)部類的封裝性比之前介紹的兩種都要完善。所以一般只有在需要高度封裝的時(shí)候才會(huì)將類定義成方法內(nèi)部類。

四、匿名內(nèi)部類

可能內(nèi)部類的所有分類中,匿名內(nèi)部類的名號(hào)是最大的,也是我們最常用到的,多見于函數(shù)式編程,lambda表達(dá)式等。下面我們重點(diǎn)看看這個(gè)匿名內(nèi)部類。

匿名內(nèi)部類就是沒(méi)有名字的內(nèi)部類,在定義完成同時(shí),實(shí)例也創(chuàng)建好了,常常和new關(guān)鍵字緊密結(jié)合。當(dāng)然,它也不局限于類,也可以是接口 ,可以出現(xiàn)在任何位置。下面我們定義一個(gè)匿名內(nèi)部類:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//首先定義一個(gè)普通類
public class Out {
 private String name;
 
 private void sayHello(){
  System.out.println("my name is :" + name);
 }
}
//定義和使用一個(gè)匿名內(nèi)部類
public static void main(String [] args){
 Out out = new Out(){
  @Override
  public void sayHello(){
   System.out.println("my name is cyy");
  }
  public void showName(){
   System.out.println("hello single");
  }
 };
 out.sayHello();
}

從上述代碼中可以很顯然的讓我們看出來(lái),我們的匿名內(nèi)部類必定是要依托一個(gè)父類的,因?yàn)樗菦](méi)有名字的,無(wú)法用一個(gè)具體的類型來(lái)表示。所以匿名內(nèi)部類往往都是通過(guò)繼承一個(gè)父類,重寫或者重新聲明一些成員來(lái)實(shí)現(xiàn)一個(gè)匿名內(nèi)部類的定義。實(shí)際上還是利用了里式轉(zhuǎn)換原理。

從中我們也可以看到,一個(gè)匿名內(nèi)部類定義的完成就意味著該內(nèi)部類實(shí)例創(chuàng)建的完成。下面我們看看其實(shí)現(xiàn)原理:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//反編譯出來(lái)的匿名內(nèi)部類
static class Test$1 extends Out
{
 Out out;
 public void sayHello()
 {
  System.out.println("my name is cyy");
 }
 
 Test$1(Out o)
 {
  this.out = o;
 }
}

其實(shí)在看了上述三種內(nèi)部類的原理之后,反而覺(jué)得匿名內(nèi)部類的實(shí)現(xiàn)較為簡(jiǎn)單了。主要思路還是將內(nèi)部類抽離出來(lái),通過(guò)初始化傳入外部類的實(shí)例以達(dá)到對(duì)外部類所有成員的訪問(wèn)。只是在匿名內(nèi)部類中,被依托的父類不是他的外部類。匿名內(nèi)部類的主要特點(diǎn)在于,沒(méi)有名字,對(duì)象只能被使用一次,可以出現(xiàn)在任意位置。所以它的使用場(chǎng)景也是呼之欲出,對(duì)于一些對(duì)代碼簡(jiǎn)潔度有所要求的情況下,可首選匿名內(nèi)部類。

以上完成了對(duì)四種內(nèi)部類的簡(jiǎn)單介紹,對(duì)于他們各自實(shí)現(xiàn)的原理也都已經(jīng)介紹過(guò)了。其實(shí)大致相同,由于jvm對(duì)每個(gè)類都要求一個(gè)單獨(dú)的源碼文件,所以編譯階段就完成了分離的操作,但是在分離的過(guò)程中又要保持內(nèi)部類和外部類之間的這種聯(lián)系,于是編譯器添加了一些接口保持這種信息共享的結(jié)構(gòu)。使用內(nèi)部類可以大大增加程序的封裝性,使得代碼整體簡(jiǎn)潔度較高。

希望能給大家一個(gè)參考,也希望大家多多支持服務(wù)器之家。

原文鏈接:https://blog.csdn.net/qq_22706515/article/details/51321718

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 91在线免费观看 | 韩国精品一区 | 久久中国 | 久久精品国产99 | 久久亚洲高清 | 伦理午夜电影免费观看 | 亚洲乱码国产乱码精品精的特点 | 国产精品第一 | 啪啪av | www.av在线.com| 亚洲精品视频在线 | 午夜资源| 精品视频久久久 | 国产精品久久久久久久久 | 毛片在线视频 | 欧美视频日韩视频 | 玖玖精品 | 伊人伊人伊人 | 精品国产成人 | av大全在线观看 | 国产影视| 精品国产欧美一区二区三区成人 | 久久久亚洲国产天美传媒修理工 | 一区二区三区免费在线观看 | 久久久久久夜 | 精品一级| 视频一区二区在线观看 | 国产一区二区三区在线 | 午夜精品久久久久久久 | 精品国产乱码久久久久久图片 | 韩日一区| 日韩毛片免费视频一级特黄 | 国产情侣免费视频 | 欧美一级在线观看 | 黄色美女视频网站 | 日韩激情一区二区 | 国产精品久久久久久久久福交 | 欧美日韩高清不卡 | 亚洲天堂av网 | av一区二区三区 | 国产精品国产三级国产aⅴ 亚洲精品免费在线观看 |