偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Java程序員必備基礎(chǔ):內(nèi)部類解析

開發(fā) 后端
整理了一下內(nèi)部類的相關(guān)知識,算是比較全,比較基礎(chǔ)的,希望大家一起學(xué)習(xí)進(jìn)步。

 [[312577]]

前言

整理了一下內(nèi)部類的相關(guān)知識,算是比較全,比較基礎(chǔ)的,希望大家一起學(xué)習(xí)進(jìn)步。

一、什么是內(nèi)部類?

在Java中,可以將一個類的定義放在另外一個類的定義內(nèi)部,這就是內(nèi)部類。內(nèi)部類本身就是類的一個屬性,與其他屬性 定義方式一致。

一個內(nèi)部類的例子: 

  1. public class Outer 
  2.  
  3.  private int radius  
  4.  public static int count    
  5.      
  6. public Outer ()      
  7.     
  8. class inner 
  9.   public void visitOuter () 
  10.   System.out.println"visit outer private member variable:" radius); 
  11.   System.out.println"visit outer static variable:" count); 
  12.  
  13.       

二、內(nèi)部類的種類

內(nèi)部類可以分為四種:成員內(nèi)部類、局部內(nèi)部類、匿名內(nèi)部類和靜態(tài)內(nèi)部類。 

 

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

定義在類內(nèi)部的靜態(tài)類,就是靜態(tài)內(nèi)部類。 

  1.  public class Outer { 
  2.    
  3.      private static int radius = 1; 
  4.    
  5.       static class StaticInner { 
  6.           public void visit() { 
  7.               System.out.println("visit outer static  variable:" + radius); 
  8.           } 
  9.       } 
  10.  } 

靜態(tài)內(nèi)部類可以訪問外部類所有的靜態(tài)變量,而不可訪問外部類的非靜態(tài)變量;靜態(tài)內(nèi)部類的創(chuàng)建方式, new外部類.靜態(tài)內(nèi)部類(),如下: 

  1.  Outer.StaticInner inner = new Outer.StaticInner(); 
  2.  inner.visit(); 

成員內(nèi)部類

定義在類內(nèi)部,成員位置上的非靜態(tài)類,就是成員內(nèi)部類。 

  1.  public class Outer { 
  2.  
  3.     private static  int radius = 1; 
  4.     private int count =2; 
  5.  
  6.      class Inner { 
  7.         public void visit() { 
  8.             System.out.println("visit outer static  variable:" + radius); 
  9.             System.out.println("visit outer   variable:" + count); 
  10.        } 
  11.    } 
  12.  } 

成員內(nèi)部類可以訪問外部類所有的變量和方法,包括靜態(tài)和非靜態(tài),私有和公有。成員內(nèi)部類依賴于外部類的實例,它的創(chuàng)建方式 外部類實例.new內(nèi)部類(),如下: 

  1. Outer outer = new Outer(); 
  2.  
  3. Outer.Inner inner = outer.new Inner(); 
  4.  
  5. inner.visit(); 

局部內(nèi)部類

定義在方法中的內(nèi)部類,就是局部內(nèi)部類。 

  1. public class Outer { 
  2.  
  3.    private  int out_a = 1; 
  4.    private static int STATIC_b = 2; 
  5.  
  6.    public void testFunctionClass(){ 
  7.        int inner_c =3; 
  8.        class Inner { 
  9.           private void fun(){ 
  10.               System.out.println(out_a); 
  11.               System.out.println(STATIC_b); 
  12.               System.out.println(inner_c); 
  13.           } 
  14.       } 
  15.       Inner  inner = new Inner(); 
  16.       inner.fun(); 
  17.    } 
  18.    public static void testStaticFunctionClass(){ 
  19.       int d =3; 
  20.        class Inner { 
  21.           private void fun(){ 
  22.               // System.out.println(out_a); 編譯錯誤,定義在靜態(tài)方法中的局部類不可以訪問外部類的實例變量 
  23.               System.out.println(STATIC_b); 
  24.               System.out.println(d); 
  25.           } 
  26.        } 
  27.       Inner  inner = new Inner(); 
  28.       inner.fun(); 
  29.    } 
  30.  } 

定義在實例方法中的局部類可以訪問外部類的所有變量和方法,定義在靜態(tài)方法中的局部類只能訪問外部類的靜態(tài)變量和方法。局部內(nèi)部類的創(chuàng)建方式,在對應(yīng)方法內(nèi), new內(nèi)部類(),如下: 

  1. public static void testStaticFunctionClass(){ 
  2. class Inner { 
  3. Inner inner = new Inner(); 

匿名內(nèi)部類

匿名內(nèi)部類就是沒有名字的內(nèi)部類,日常開發(fā)中使用的比較多。 

  1.  public class Outer { 
  2.    
  3.       private void test(final int i) { 
  4.           new Service() { 
  5.               public void method() { 
  6.                   for (int j = 0; j < i; j++) { 
  7.                       System.out.println("匿名內(nèi)部類" ); 
  8.                   } 
  9.               } 
  10.          }.method(); 
  11.      } 
  12.   } 
  13.   //匿名內(nèi)部類必須繼承或?qū)崿F(xiàn)一個已有的接口 
  14.   interface Service{ 
  15.      void method(); 
  16.  } 

除了沒有名字,匿名內(nèi)部類還有以下特點:

  • 匿名內(nèi)部類必須繼承一個抽象類或者實現(xiàn)一個接口。
  • 匿名內(nèi)部類不能定義任何靜態(tài)成員和靜態(tài)方法。
  • 當(dāng)所在的方法的形參需要被匿名內(nèi)部類使用時,必須聲明為 final。
  • 匿名內(nèi)部類不能是抽象的,它必須要實現(xiàn)繼承的類或者實現(xiàn)的接口的所有抽象方法。

匿名內(nèi)部類創(chuàng)建方式: 

  1. new 類/接口{ 
  2.  
  3. //匿名內(nèi)部類實現(xiàn)部分 
  4.  

三、內(nèi)部類的優(yōu)點

我們?yōu)槭裁匆褂脙?nèi)部類呢?因為它有以下優(yōu)點:

  • 一個內(nèi)部類對象可以訪問創(chuàng)建它的外部類對象的內(nèi)容,包括私有數(shù)據(jù)!
  • 內(nèi)部類不為同一包的其他類所見,具有很好的封裝性;
  • 內(nèi)部類有效實現(xiàn)了“多重繼承”,優(yōu)化 java 單繼承的缺陷。
  • 匿名內(nèi)部類可以很方便的定義回調(diào)。

一個內(nèi)部類對象可以訪問創(chuàng)建它的外部類對象的內(nèi)容,包括私有數(shù)據(jù)! 

  1.   public class Outer { 
  2.   
  3.      private  int radius = 1; 
  4.    
  5.      protected void test(){ 
  6.          System.out.println("我是外部類方法"); 
  7.      } 
  8.   
  9.      class Inner { 
  10.         public void visit() { 
  11.             System.out.println("訪問外部類變量" + radius); 
  12.            test(); 
  13.         } 
  14.     } 
  15.  } 

我們可以看到,內(nèi)部類Inner是可以訪問外部類Outer的私有變量radius或者方法test的。

內(nèi)部類不為同一包的其他類所見,具有很好的封裝性

當(dāng)內(nèi)部類使用 private修飾時,這個類就對外隱藏了。當(dāng)內(nèi)部類實現(xiàn)某個接口,并且進(jìn)行向上轉(zhuǎn)型,對外部來說,接口的實現(xiàn)已經(jīng)隱藏起來了,很好體現(xiàn)了封裝性。 

  1.   //提供的接口 
  2.   interface IContent{ 
  3.       String getContents(); 
  4.   } 
  5.   
  6.   public class Outer { 
  7.       //私有內(nèi)部類屏蔽實現(xiàn)細(xì)節(jié) 
  8.        private class PContents implements IContent{ 
  9.            @Override 
  10.          public String getContents() { 
  11.               System.out.println("獲取內(nèi)部類內(nèi)容"); 
  12.               return "內(nèi)部類內(nèi)容"
  13.           } 
  14.       } 
  15.   
  16.      //對外提供方法 
  17.      public IContent getIContent() { 
  18.          return new PContents(); 
  19.      } 
  20.   
  21.      public static void main(String[] args) { 
  22.          Outer outer=new Outer(); 
  23.          IContent a1=outer.getIContent(); 
  24.          a1.getContents(); 
  25.      } 
  26.  } 

我們可以發(fā)現(xiàn),Outer外部類對外提供方法getIContent,用內(nèi)部類實現(xiàn)細(xì)節(jié),再用private修飾內(nèi)部類,屏蔽起來,把Java的封裝性表現(xiàn)的淋漓盡致。

內(nèi)部類有效實現(xiàn)了“多重繼承”,優(yōu)化 java 單繼承的缺陷。

我們知道Java世界中,一個類只能有一個直接父類,即以單繼承方式存在。但是內(nèi)部類讓“多繼承”成為可能:

  • 一般來說,內(nèi)部類繼承某個類或者實現(xiàn)某個接口,內(nèi)部類的代碼操作創(chuàng)建它的外圍類的對象。內(nèi)部類提供了某種進(jìn)入其外圍類的窗口。
  • 每個內(nèi)部類都可以隊里的繼承自一個(接口的)實現(xiàn),所以無論外圍類是否已經(jīng)繼承了某個(接口的)實現(xiàn),對于內(nèi)部類沒有影響
  • 接口解決了部分問題,一個類可以實現(xiàn)多個接口,內(nèi)部類允許繼承多個非接口類型(類或抽象類)。

一份來自Java編程思想,內(nèi)部類實現(xiàn)“多繼承”的溫暖如下: 

  1. class D {} 
  2.  
  3. abstract class E{} 
  4.  
  5. class Z extends D { 
  6.  
  7. E makeE(){ return new E() {}; } 
  8.  
  9.  
  10. public class MultiImplementation { 
  11.  
  12. static void takesD(D d) {} 
  13.  
  14. static void takesE(E e) {} 
  15.  
  16. public static void main(String[] args){ 
  17.  
  18. Z z = new Z(); 
  19.  
  20. takesD(z); 
  21.  
  22. takesE(z.makeE()); 
  23.  
  24.  

代碼中出現(xiàn)了一個類D,一個抽象類E。然后,用類Z繼承D,內(nèi)部類構(gòu)造返回E。因此,當(dāng)你不管要的是D還是E,Z都可以應(yīng)付,“多繼承”的特點完美表現(xiàn)出來。

匿名內(nèi)部類可以很方便的定義回調(diào)。

什么是回調(diào)?假設(shè)有兩個類A和B,在A中調(diào)用B的一個方法b,而b在執(zhí)行又調(diào)用了A的方法c,則c就稱為回調(diào)函數(shù)。 

 

當(dāng)然,回調(diào)函數(shù)也可以是a函數(shù),這就是同步回調(diào),最簡單的回調(diào)方式。回調(diào)應(yīng)用場景挺多的,如android中的事件監(jiān)聽器。匿名內(nèi)部類可以很方便的定義回調(diào),看個例子

  1.   //定義一個CallBack接口 
  2.   public interface CallBack { 
  3.       void execute(); 
  4.   } 
  5.    
  6.   public class TimeTools { 
  7.    
  8.       /** 
  9.        * 測試函數(shù)調(diào)用時長,通過定義CallBack接口的execute方法 
  10.       * @param callBack 
  11.       */ 
  12.      public   void  testTime(CallBack callBack) { 
  13.          long  beginTime = System.currentTimeMillis(); //記錄起始時間 
  14.          callBack.execute(); ///進(jìn)行回調(diào)操作 
  15.          long  endTime = System.currentTimeMillis(); //記錄結(jié)束時間 
  16.          System.out.println("[use time]:"  + (endTime - beginTime)); //打印使用時間 
  17.      } 
  18.   
  19.      public   static   void  main(String[] args) { 
  20.          TimeTools tool = new  TimeTools(); 
  21.          tool.testTime(new  CallBack(){ 
  22.              //匿名內(nèi)部類,定義execute方法 
  23.              public   void  execute(){ 
  24.                  TestTimeObject testTimeObject = new TestTimeObject(); 
  25.                  testTimeObject.testMethod(); 
  26.              } 
  27.          }); 
  28.      } 
  29.  } 

在調(diào)用testTime()測時間的時候,用匿名內(nèi)部類實現(xiàn)一個方法execute(),在該方法內(nèi)搞事情(執(zhí)行目標(biāo)函數(shù)),執(zhí)行完后,又回到testTime方法,很好了實現(xiàn)測試函數(shù)調(diào)用時長的功能。顯然,匿名內(nèi)部類讓回調(diào)實現(xiàn)變得簡單。

四、內(nèi)部類的底層

內(nèi)部類標(biāo)志符

每個內(nèi)部類都會產(chǎn)生一個.class文件,其中包含了如何創(chuàng)建該類型的對象的全部信息。內(nèi)部類也必須生成一個.class文件以包含它們的Class對象信息。內(nèi)部類文件的命名有嚴(yán)格規(guī)則:外圍類的名字+$+內(nèi)部類的名字。

一個簡單例子: 

  1. public class Outer { 
  2.  
  3. class Inner
  4.  
  5.  

javac Outer.java編譯完成后, 生成的class文件如下:  

 

如果內(nèi)部類是匿名的,編譯器會簡單地產(chǎn)生一個數(shù)字作為其標(biāo)識符。如果內(nèi)部類是嵌套在別的內(nèi)部類之中(靜態(tài)內(nèi)部類),只需直接將它們的名字加在其外圍類標(biāo)志符與“$”的后面。

為什么內(nèi)部類可以訪問外部類的成員,包括私有數(shù)據(jù)?

由上一小節(jié),我們知道內(nèi)部類可以訪問外部類的成員,包括私有數(shù)據(jù)。那么它是怎么做到的呢?接下來揭曉答案。

先看這個簡單地例子: 

  1.   public class Outer { 
  2.    
  3.       private int i = 0; 
  4.   
  5.       class Inner
  6.           void method(){ 
  7.               System.out.println(i); 
  8.           } 
  9.       } 
  10.  } 

一個外部類Outer,一個外部類私有屬性i,一個內(nèi)部類Inner,一個內(nèi)部類方法method。內(nèi)部類方法訪問了外部類屬性i。

先編譯,javac Outer.java,生成.class文件,如下: 

 

用命令 javap-classpath.-vOuter$Inner,反編譯Outter$Inner.class文件得到以下信息: 

 

我們可以看到這一行,它是一個指向外部類對象的指針: 

  1. final innerclass.Outer this$0; 

雖然編譯器在創(chuàng)建內(nèi)部類時為它加上了一個指向外部類的引用, 但是這個引用是怎樣賦值的呢?編譯器會為內(nèi)部類的構(gòu)造方法添加一個參數(shù),進(jìn)行初始化, 參數(shù)的類型就是外部類的類型,如下: 

  1. innerclass.Outer$Inner(innerclass.Outer); 

成員內(nèi)部類中的Outter this&0 指針便指向了外部類對象,因此可以在成員內(nèi)部類中隨意訪問外部類的成員。

局部內(nèi)部類和匿名內(nèi)部類訪問局部變量的時候,為什么變量必須要加上final?

局部內(nèi)部類和匿名內(nèi)部類訪問局部變量的時候,為什么變量必須要加上final呢?它內(nèi)部原理是什么呢?

先看這段代碼: 

  1.   public class Outer { 
  2.    
  3.       void outMethod(){ 
  4.           final int a =10; 
  5.           class Inner { 
  6.               void innerMethod(){ 
  7.                   System.out.println(a); 
  8.               } 
  9.    
  10.          } 
  11.      } 
  12.  } 

反編譯(Outer$1Inner)得到以下信息: 

 

我們在內(nèi)部類innerMethod方法中,可以看到以下這條指令: 

  1. 3:bipush   10 
  • 它表示將常量10壓入棧中,表示使用的是一個本地局部變量。
  • 其實,如果一個變量的值在編譯期間可以確定(demo中確定是10了),則編譯器會默認(rèn)在匿名內(nèi)部類(局部內(nèi)部類)的常量池中添加一個內(nèi)容相等的字面量或直接將相應(yīng)的字節(jié)碼嵌入到執(zhí)行字節(jié)碼中。
  • 醬紫可以確保局部內(nèi)部類使用的變量與外層的局部變量區(qū)分開,它們只是值相等而已。

以上例子,為什么要加final呢?是因為生命周期不一致, 局部變量直接存儲在棧中,當(dāng)方法執(zhí)行結(jié)束后,非final的局部變量就被銷毀。而局部內(nèi)部類對局部變量的引用依然存在,如果局部內(nèi)部類要調(diào)用局部變量時,就會出錯。加了final,可以確保局部內(nèi)部類使用的變量與外層的局部變量區(qū)分開,解決了這個問題。

我們再來看一段代碼,其實就是把變量a挪到傳參方式進(jìn)來。 

  1.   public class Outer { 
  2.    
  3.       void outMethod(final int a){ 
  4.           class Inner { 
  5.               void innerMethod(){ 
  6.                   System.out.println(a); 
  7.               } 
  8.           } 
  9.       } 
  10.  } 

反編譯可得: 

 

我們看到匿名內(nèi)部類Outer$1Inner的構(gòu)造器含有兩個參數(shù),一個是指向外部類對象的引用,一個是int型變量,很顯然,這里是將變量innerMethod方法中的形參a以參數(shù)的形式傳進(jìn)來對匿名內(nèi)部類中的拷貝(變量a的拷貝)進(jìn)行賦值初始化。

那么,新的問題又來了,既然在innerMethod方法中訪問的變量a和outMethod方法中的變量a不是同一個變量,當(dāng)在innerMethod方法中修改a會怎樣?那就會造成數(shù)據(jù)不一致的問題了。

怎么解決呢?使用final修飾符,final修飾的引用類型變量,不允許指向新的對象,這就解決數(shù)據(jù)不一致問題。注意: 在Java8 中,被局部內(nèi)部類引用的局部變量,默認(rèn)添加final,所以不需要添加final關(guān)鍵詞。

五、內(nèi)部類的應(yīng)用場景。

一般我們在哪些場景下使用內(nèi)部類呢?

場景之一:一些多算法場合

一些算法多的場合,也可以借助內(nèi)部類,如: 

  1.   Arrays.sort(emps,new Comparator(){ 
  2.     Public int compare(Object o1,Object o2) 
  3.     { 
  4.      return ((Employee)o1).getServedYears()-((Employee)o2).getServedYears(); 
  5.     } 
  6.   }); 

場景二:解決一些非面向?qū)ο蟮恼Z句塊。

如果一些語句塊,包括if…else語句,case語句等等比較多,不好維護(hù)擴(kuò)展,那么就可以借助內(nèi)部類+設(shè)計模式解決。

場景之三:適當(dāng)使用內(nèi)部類,使得代碼更加靈活和富有擴(kuò)展性。

適當(dāng)?shù)氖褂脙?nèi)部類,可以使得你的代碼更加靈活和富有擴(kuò)展性。如JDK的lamda表達(dá)式,用內(nèi)部類非常多,代碼優(yōu)雅很多。如下: 

  1. // JDK8 Lambda表達(dá)式寫法 
  2.  
  3. new Thread(() -> System.out.println("Thread run()")).start(); 

場景四:當(dāng)某個類除了它的外部類,不再被其他的類使用時。

如果一個類,不能為其他的類使用;或者出于某種原因,不能被其他類引用。那我們就可以考慮把它實現(xiàn)為內(nèi)部類。數(shù)據(jù)庫連接池就是這樣一個典型例子。

六、內(nèi)部類常見面試題

最后,我們來看一道經(jīng)典內(nèi)部類面試題吧。 

  1.   public class Outer { 
  2.       private int age = 12; 
  3.    
  4.       class Inner { 
  5.           private int age = 13; 
  6.           public void print() { 
  7.               int age = 14; 
  8.               System.out.println("局部變量:" + age); 
  9.               System.out.println("內(nèi)部類變量:" + this.age); 
  10.              System.out.println("外部類變量:" + Outer.this.age); 
  11.          } 
  12.      } 
  13.  
  14.      public static void main(String[] args) { 
  15.          Outer.Inner in = new Outer().new Inner(); 
  16.          in.print(); 
  17.      } 
  18.   
  19.  } 

運行結(jié)果: 

 

 

責(zé)任編輯:華軒 來源: 撿田螺的小男孩
相關(guān)推薦

2020-05-09 11:20:02

Java結(jié)構(gòu)圖虛擬機(jī)

2020-05-06 15:59:07

JavaScript程序員技術(shù)

2009-06-25 09:33:43

Java API程序員

2020-07-20 07:46:01

程序員加簽驗簽

2020-04-20 11:19:00

Java開發(fā)序列化

2011-03-29 14:11:15

內(nèi)部類

2020-03-16 08:22:11

Java程序員虛擬機(jī)

2020-04-20 19:00:30

程序員分布式事務(wù)架構(gòu)

2022-10-24 09:00:47

畫圖工具程序員XMind

2019-09-25 11:39:07

程序員編程技術(shù)

2020-01-15 11:14:21

Java算法排序

2014-08-15 14:25:48

Android程序員資源

2011-06-11 20:59:12

程序員

2014-08-20 10:28:29

Android

2021-05-06 07:04:35

安全漏洞業(yè)務(wù)

2009-07-02 15:10:17

Java程序員面試

2015-08-20 14:34:25

程序員java基礎(chǔ)網(wǎng)絡(luò)編程

2020-09-21 07:00:42

Java內(nèi)部類接口

2022-03-17 10:24:28

JavaJVM

2019-07-05 10:45:27

Java程序員編程語言
點贊
收藏

51CTO技術(shù)棧公眾號