那些java中的內部類

来源:http://www.cnblogs.com/Wilange/archive/2017/11/13/7732187.html
-Advertisement-
Play Games

java內部類是從JDK1.1開始出現的,因此,很多人都不陌生,但是又會覺得不熟悉。原因是平時編寫代碼時可能用到的場景不多,用得最多的是在有事件監聽的情況下。所以,這裡將從四個方面做一個簡單的總結: 一.內部類基礎 在Java中,可以將一個類定義在另一個類裡面或者一個方法裡面,這樣的類稱為內部類。廣 ...


java內部類是從JDK1.1開始出現的,因此,很多人都不陌生,但是又會覺得不熟悉。原因是平時編寫代碼時可能用到的場景不多,用得最多的是在有事件監聽的情況下。所以,這裡將從四個方面做一個簡單的總結:

一.內部類基礎

在Java中,可以將一個類定義在另一個類裡面或者一個方法裡面,這樣的類稱為內部類。廣泛意義上的內部類一般來說包括這四種:成員內部類、局部內部類、匿名內部類和靜態內部類。下麵就先來簡單瞭解一下這四種內部類的用法。

1.成員內部類

  成員內部類是最普通的內部類,它的定義為位於另一個類的內部,這裡總結它的一些特性:

  • 成員內部類可以無條件訪問外部類的所有成員屬性和成員方法(包括private成員和靜態成員)
  • 當成員內部類擁有和外部類同名的成員變數或者方法時,會發生隱藏現象,即預設情況下訪問的是成員內部類的成員
  • 在外部類中如果要訪問成員內部類的成員,必須先創建一個成員內部類的對象,再通過指向這個對象的引用來訪問
  • 成員內部類是依附外部類而存在的,也就是說,如果要創建成員內部類的對象,前提是必須存在一個外部類的對象
  • 成員內部類中不能定義任何靜態成員變數或靜態成員方法,但可以有靜態常量。
     1 public class InnerClassDemo {
     2     public static void main(String[] args) {
     3         
     4         //必須先創建一個外部類對象,才能創建一個成員內部類的對象
     5         Outer outer = new Outer("Liming", 20);
     6         Outer.Inner inner = outer.new Inner();
     7         inner.show();
     8     }
     9 
    10 }
    11 
    12 class Outer{
    13     
    14     private static String name;
    15     private int age;
    16     private int score = 100;
    17     
    18     public Outer(String name, int age) {
    19         this.name = name;
    20         this.age = age;
    21         
    22         //外部類訪問內部類的成員,必須先創建一個成員內部類的對象
    23         Inner inner = new Inner();
    24         System.out.println(inner.score);//80
    25     }
    26     
    27     class Inner{
    28         
    29         int score = 80;
    30         
    31         void show(){
    32             //內部類能夠訪問外部類的靜態和私有成員
    33             System.out.println(name);//Liming
    34             System.out.println(age);//20
    35             
    36             System.out.println(score);//80
    37             /*
    38              * 發生隱藏現象,訪問的是內部類的score屬性,若要訪問外部類的score屬性,則需要這樣的格式:
    39              *     外部類.this.成員變數
    40              *    外部類.this.成員方法    
    41              */
    42             System.out.println(Outer.this.score);//100
    43             
    44         }
    45     }
    46 }

代碼示例:

 1 public class InnerClassDemo {
 2     public static void main(String[] args) {
 3         
 4         //必須先創建一個外部類對象,才能創建一個成員內部類的對象
 5         Outer outer = new Outer("Liming", 20);
 6         Outer.Inner inner = outer.new Inner();
 7         inner.show();
 8     }
 9 }
10 
11 class Outer{
12     private static String name;
13     private int age;
14     private int score = 100;
15     
16     public Outer(String name, int age) {
17         this.name = name;
18         this.age = age;
19         
20         //外部類訪問內部類的成員,必須先創建一個成員內部類的對象
21         Inner inner = new Inner();
22         System.out.println(inner.score);//80
23     }
24     
25     /*
26      * 內部類也可以有一些訪問控制修飾符,如private,public,protected
27      *     此處採用預設訪問許可權,只能在同一個包中訪問
28      */
29     class Inner{
30         int score = 80; //static int score = 80; 直接報錯,編譯不通過
31         void show(){
32             //內部類能夠訪問外部類的靜態和私有成員
33             System.out.println(name);//Liming
34             System.out.println(age);//20
35             
36             System.out.println(score);//80
37             /*
38              * 此處發生隱藏現象,訪問的是內部類的score屬性,若要訪問外部類的score屬性,則需:
39              *    外部類.this.成員變數
40              *    外部類.this.成員方法    
41              */
42             System.out.println(Outer.this.score);//100
43             
44         }
45     }
46 }

註意:這段代碼的執行順序是:調用Outer構造器創建外部類對象 > 創建內部類對象 > 執行show方法 。

2.靜態內部類

 靜態內部類也是定義在另一個類裡面的類,只不過在類的前面多了一個關鍵字static。

  • 靜態內部類是不需要依賴於外部類的,即是在沒有創建外部類對象的情況下也可以創建靜態內部類的對象
  • 靜態內部類只能訪問外部類的靜態成員,而不能訪問外部類的非靜態成員

代碼示例:

 1 public class InnerClassDemo {
 2     public static void main(String[] args) {
 3         
 4         //直接創建靜態內部類的對象,不依賴於外部類
 5         Inner inner = new Inner();
 6         inner.show();
 7     }
 8 }
 9 
10 class Outer{
11     public static String name = "Liming";
12     public int age = 20;
13     
14     static class Inner{
15         void show(){
16             //靜態內部類只能訪問外部類的靜態成員而不能訪問非靜態成員
17             System.out.println(name);//Liming
18 //            System.out.println(age); 直接報錯,編譯不通過
19         }
20     }
21 
22 }

3.局部內部類

 局部內部類是定義在一個方法或者一個作用域裡面的類,它和成員內部類的區別在於局部內部類的訪問僅限於方法內或者該作用域內。

代碼示例:

 1 class People{
 2     public People() {
 3          
 4     }
 5 }
 6  
 7 class Man{
 8     public Man(){
 9          
10     }
11      
12     public People getWoman(){
13         class Woman extends People{   //局部內部類
14             int age =0;
15         }
16         return new Woman();
17     }
18 }

註意:局部內部類就像是方法裡面的一個局部變數一樣,是不能有public、protected、private以及static修飾符的。

4.匿名內部類

 匿名內部類應該是平時我們編寫代碼時用得最多的,在編寫事件監聽的代碼時使用匿名內部類不但方便,而且使代碼更加容易維護。

  • 匿名內部類不能有訪問修飾符和static修飾符
  • 匿名內部類是唯一一種沒有構造器的類,因為連類名都沒有何來構造器
  • 匿名內部類的使用範圍非常有限,大部分匿名內部類用於介面回調,“new 匿名內部類”,這個類首先是要存在的。

代碼示例:

 1 scan_bt.setOnClickListener(new OnClickListener() {
 2              
 3             @Override
 4             public void onClick(View v) {
 5                 // TODO Auto-generated method stub
 6                  
 7             }
 8         });
 9          
10         history_bt.setOnClickListener(new OnClickListener() {
11              
12             @Override
13             public void onClick(View v) {
14                 // TODO Auto-generated method stub
15                  
16             }
17         });
View Code

二.深入理解Java內部類

1.靜態內部類與非靜態內部類

  • 靜態內部類的創建不依賴於外部類,並且可以定義靜態成員
  • 非靜態內部類依賴於外部類的創建而創建,並且不能定義任何的靜態成員

解析:靜態內部類不依賴於任何外部類,所以可以有自己的靜態成員;而非靜態內部類必須通過外部類創建,它的存在嚴格依賴於外部類,所以不能有自己的靜態成員。

  • 靜態內部類只能訪問外部類的靜態成員,而不能訪問任何非靜態成員

解析:靜態內部類不依賴於外部類,所以可在未創建外部類對象的情況下創建靜態內部類的實例對象,而沒有外部類對象就不能訪問外部類的非靜態成員。

2.成員內部類可以無條件訪問外部類的成員

之前,我們已經討論過了成員內部類可以無條件訪問外部類的成員,那具體究竟是如何實現的呢?下麵通過反編譯位元組碼文件看看究竟。

  • 事實上,編譯器在進行編譯的時候,會將內部類單獨編譯成一個位元組碼文件,命名方式為:外部類$內部類.class外部類$X.class(X為一個整數)。

     

通過反編譯工具,將內部類的位元組碼文件進行反編譯,得到:

 1 E:\Workspace\Test\bin\com\cxh\test2>javap -v Outter$Inner
 2 Compiled from "Outter.java"
 3 public class com.cxh.test2.Outter$Inner extends java.lang.Object
 4   SourceFile: "Outter.java"
 5   InnerClass:
 6    #24= #1 of #22; //Inner=class com/cxh/test2/Outter$Inner of class com/cxh/tes
 7 t2/Outter
 8   minor version: 0
 9   major version: 50
10   Constant pool:
11 const #1 = class        #2;     //  com/cxh/test2/Outter$Inner
12 const #2 = Asciz        com/cxh/test2/Outter$Inner;
13 const #3 = class        #4;     //  java/lang/Object
14 const #4 = Asciz        java/lang/Object;
15 const #5 = Asciz        this$0;
16 const #6 = Asciz        Lcom/cxh/test2/Outter;;
17 const #7 = Asciz        <init>;
18 const #8 = Asciz        (Lcom/cxh/test2/Outter;)V;
19 const #9 = Asciz        Code;
20 const #10 = Field       #1.#11; //  com/cxh/test2/Outter$Inner.this$0:Lcom/cxh/t
21 est2/Outter;
22 const #11 = NameAndType #5:#6;//  this$0:Lcom/cxh/test2/Outter;
23 const #12 = Method      #3.#13; //  java/lang/Object."<init>":()V
24 const #13 = NameAndType #7:#14;//  "<init>":()V
25 const #14 = Asciz       ()V;
26 const #15 = Asciz       LineNumberTable;
27 const #16 = Asciz       LocalVariableTable;
28 const #17 = Asciz       this;
29 const #18 = Asciz       Lcom/cxh/test2/Outter$Inner;;
30 const #19 = Asciz       SourceFile;
31 const #20 = Asciz       Outter.java;
32 const #21 = Asciz       InnerClasses;
33 const #22 = class       #23;    //  com/cxh/test2/Outter
34 const #23 = Asciz       com/cxh/test2/Outter;
35 const #24 = Asciz       Inner;
36  
37 {
38 final com.cxh.test2.Outter this$0;
39  
40 public com.cxh.test2.Outter$Inner(com.cxh.test2.Outter);
41   Code:
42    Stack=2, Locals=2, Args_size=2
43    0:   aload_0
44    1:   aload_1
45    2:   putfield        #10; //Field this$0:Lcom/cxh/test2/Outter;
46    5:   aload_0
47    6:   invokespecial   #12; //Method java/lang/Object."<init>":()V
48    9:   return
49   LineNumberTable:
50    line 16: 0
51    line 18: 9
52  
53   LocalVariableTable:
54    Start  Length  Slot  Name   Signature
55    0      10      0    this       Lcom/cxh/test2/Outter$Inner;
56  
57  
58 }

 第11行~35行是常量池的內容,這裡需要註意的是第38行的內容:

final com.cxh.test2.Outter this$0;

  這行是一個指向外部類對象的指針,看到這裡想必大家豁然開朗了。也就是說編譯器會預設為成員內部類添加了一個指向外部類對象的引用,那麼這個引用是如何賦初值的呢?下麵接著看內部類的構造器:

public com.cxh.test2.Outter$Inner(com.cxh.test2.Outter);

  從這裡可以看出,雖然我們在定義的內部類的構造器是無參構造器,編譯器還是會預設添加一個參數,該參數的類型為指向外部類對象的一個引用,所以成員內部類中的Outter this&0 指針便指向了外部類對象,因此可以在成員內部類中隨意訪問外部類的成員。從這裡也間接說明瞭成員內部類是依賴於外部類的,如果沒有創建外部類的對象,則無法對Outter this&0引用進行初始化賦值,也就無法創建成員內部類的對象了。

3.局部內部類和匿名內部類只能訪問局部final變數

 關於這個問題,我們先來看一段代碼:

 1 /*
 2  * 局部內部類和匿名內部類只能訪問局部final變數 
 3  */
 4 public class InnerClass {
 5 
 6     public static void main(String[] args) {
 7         test(11);
 8     }
 9     
10     public static void test(final int a ){
11         final double b = 11.234;
12         //定義一個匿名內部類
13         new Thread(){
14             public void run() {
15                 System.out.println(a);
16                 System.out.println(b);
17             }
18             
19         }.start();
20     }
21 }

編譯後生成了兩個文件,即匿名內部類單獨生成一個文件:InnerClass$1.class

上段代碼中,如果把變數a和b前面的任一個final去掉,這段代碼都編譯不過。我們可以考慮這樣一個問題: 當test方法執行完畢之後,變數a的生命周期就結束了,而此時Thread對象的生命周期很可能還沒有結束,那麼在Thread的run方法中繼續訪問變數a就變成不可能了,但是又要實現這樣的效果,怎麼辦呢?Java採用“複製”的方法來解決這個問題。

 詳細介紹請參看:http://www.cnblogs.com/dolphin0520/p/3811445.html

4.靜態內部類特殊的地方

  • 靜態內部類是不依賴於外部類的,也就說可以在不創建外部類對象的情況下創建內部類的對象。
  • 靜態內部類是不持有指向外部類對象的引用的,可以反編譯靜態內部類的class文件,發現是沒有Outter this&0引用的。

三.內部類的作用和使用場景

 關於內部類的作用和使用場景,這裡總結了以下兩點:

  • 每個內部類都能獨立的繼承一個(介面的)實現,所以無論外部類是否已經繼承了某個(介面的)實現,對於內部類都沒有影響。所以,內部類使得多重繼承的解決方案變得完整
  • 方便將存在一定邏輯關係的類組織在一起,又可以對外界隱藏
  • 其中,匿名內部類常用於事件監聽和多線程程式

 

全文參考自:http://www.cnblogs.com/dolphin0520/p/3811445.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在採集美女站時,需要對關鍵詞進行分詞,最終採用的是python的結巴分詞方法. 中文分詞是中文文本處理的一個基礎性工作,結巴分詞利用進行中文分詞。其基本實現原理有三點: 安裝(Linux環境) 下載工具包,解壓後進入目錄下,運行:python setup.py install 模式 介面 組件只提供 ...
  • 官方解釋:os: This module provides a portable way of using operating system dependent functionality. 翻譯:提供一種方便的使用操作系統函數的方法。sys:This module provides access ...
  • 7個國家首都與中國的首都連線,線段的交於中國地圖上,記為7個點,這幾個點彼此互不影響,互不重合,按照各自國家所占的貿易比例,同時進行分割地圖。 ...
  • 異常的分類 在使用上 Error不用管他虛擬機錯誤 Exception必須要用catch抓 RuntimeExcption可以處理也可以不用處理 說明 Error:稱為錯誤,由java虛擬機生成並拋出,包括動態連接失敗,虛擬機錯誤,程式對其不做處理。 Exception:所有異常的父類,其子類對應了 ...
  • 初入Python,一開始就被她簡介的語法所吸引,代碼簡潔優雅,之前在C#裡面打開文件寫入文件等操作相比Python複雜多了,而Python打開、修改和保存文件顯得簡單得多。 1、打開文件的例子: 2、利用urllib庫請求頁面進行簡單的翻譯,請求百度翻譯,將要翻譯的內容當做參數傳給百度,然後將結果賦 ...
  • 1、@RequestMapping RequestMapping是一個用來處理請求地址映射的註解,可用於類或方法上。用於類上,表示類中的所有響應請求的方法 都是以該地址作為父路徑,類和方法共同組成的字元串才是一個完整的url. RequestMapping註解有六個屬性,下麵我們把她分成三類進行說明 ...
  • Alt+Enter 自動添加包Ctrl+t SVN更新Ctrl+k SVN提交Ctrl + / 註釋(取消註釋)選擇的行Ctrl+Shift+F 高級查找Ctrl+Enter 補全Shift + Enter 開始新行TAB Shift+TAB 縮進/取消縮進所選擇的行Ctrl + Alt + I 自 ...
  • 程式需求: 流程圖: 好像畫的不咋地 查看代碼: #!/usr/bin/env python # _*_ coding:utf-8 _*_ # File_type:一個登錄介面 # Author:smelond import os username = "smelond"#用戶名 password ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...