Java 內部類

轉載自:http://kenby.iteye.com/blog/1603803

兩種內部類

Java的內部類有兩種,一種是靜態內部類,另一種是普通內部類,普通內部類可以獲得外部對象的引用,

所以在普通內部類能夠訪問外部對象的成員變量 ,也就能夠使用外部類的資源,可以說普通內部類依賴

於外部類,普通內部類與外部類是共生共死的,創建普通內部類的對象之前,必須先創建外部類的對象。

創建普通內部類的代碼如下:

Outer o = new Outer();

Outer.Inner inner = o.new Inner();

 

Outer.Inner inner = new o.Inner();         /* 錯誤 */

Outer.Inner inner = new Outer.Inner();  /* 錯誤 */

 

靜態內部類沒有外部對象的引用,所以它無法獲得外部對象的資源,當然好處是,靜態內部類無需依賴於

外部類,它可以獨立於外部對象而存在。創建靜態內部類的代碼如下:

Outer.Inner inner = new Outer.Inner();

 

靜態類的使用場景

在以下場合可以使用靜態內部類:

(1) 外部類需要使用內部類,而內部類無需使用外部類的資源

(2) 內部類可以獨立外部類創建對象

使用靜態內部類的好處是加強了代碼的封裝性以及提高了代碼的可讀性,舉個例子:

 

Java代碼  收藏代碼
  1. public class Person{    
  2.      //姓名    
  3.      private String name;    
  4.      //家庭    
  5.      private Home home;    
  6.      //構造函數設置屬性值    
  7.      public Person(String _name){    
  8.           name = _name;    
  9.      }    
  10.      /* home、name的getter/setter方法省略 */    
  11.    
  12.      public static class Home{    
  13.           //家庭地址    
  14.           private String address;    
  15.           //家庭電話    
  16.           private String tel;    
  17.    
  18.           public Home(String _address,String _tel){    
  19.             address = _address;    
  20.             tel = _tel;    
  21.           }    
  22.           /* address、tel的getter/setter方法省略 */    
  23.      }    
  24. }   

 

把Home放在Person內部,一看便知道Home是Person的一個屬性。使用的時候也很方便,如下代碼:

 

Java代碼  收藏代碼
  1. public static void main(String[] args) {    
  2.     Home home = new Person.Home("上海""021");  
  3.     Person p1 = new Person("張三");    
  4.     Person p2 = new Person("李四");    
  5.     p1.setHome(home);    
  6.     p2.setHome(home);    
  7. }   

 

這裏創建一個home對象,p1和p2都使用這個home對象,p1和p2共享同一個home對象。

如果把Home換成普通內部類,情況就不同了,這時代碼變成:

 

Java代碼  收藏代碼
  1. public static void main(String[] args) {    
  2.     Person p1 = new Person("張三");    
  3.     Home home = p1.new Home("上海""021");  
  4.     p1.setHome(home);    
  5.     Person p2 = new Person("李四");    
  6.     p2.setHome(home);    
  7. }   

 

這裏p1和p2依然共享同一個home對象,但此時home對象和p1是同生共死的,如果p1對象消亡,那麼p2就沒有

家了,這對p2來說實在不公平,而如果爲p1和p2都創建一個Home對象,又浪費資源。所以在這個例子中,

使用靜態內部類比普通內部類要合適。


Java Singleton 寫法:public class Smarty {
    private Smarty() {}
    
    private static class SmartyLoader {
        private static final Smarty instance = new Smarty();
    }

    public static Smarty getInstance() {
        return SmartyLoader.instance;
    }
}

作者:吳強
鏈接:https://www.zhihu.com/question/29971746/answer/46320214
來源:知乎
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

靜態內部類和普通內部類的區別

(1)普通內部類不能聲明static的方法和變量

普通內部類不能聲明static的方法和變量,注意這裏說的是變量,常量(也就是final static修飾的屬性)

還是可以的,而靜態內部類形似外部類,沒有任何限制。

(2)使用靜態內部類,多個外部類的對象可以共享同一個內部類的對象。

使用普通內部類,每個外部類的對象都有自己的內部類對象,外部對象之間不能共享內部類的對象


成員內部類裏面爲什麼不能有靜態成員和方法?

(轉載https://www.cnblogs.com/YLQBL/p/6496511.html)

非靜態內部類不能有靜態成員!

成員內部類必須先實例化外部類對象然後再實例化成員內部類;

非static的內部類,在外部類加載的時候,並不會加載它,所以它裏面不能有靜態變量或者靜態方法。
1、static類型的屬性和方法,在類加載的時候就會存在於內存中。
2、要使用某個類的static屬性或者方法,那麼這個類必須要加載到jvm中。
基於以上兩點,可以看出,如果一個非static的內部類如果具有static的屬性或者方法,那麼就會出現一種情況:內部類未加載,但是卻試圖在內存中創建static的屬性和方法,這當然是錯誤的。原因:類還不存在,但卻希望操作它的屬性和方法。

java很多想這類不能共同存在的 一般都與他們的生命週期有關。。。
比如 靜態成員和靜態方法是隨着類的加載而存在的,也就是說內部類的靜態屬性是隨着類的加載的,但是內部類的實例 是創建後才存在的,也就是說其靜態屬性優先存在於他的類實例的存在 這顯然是矛盾的,所以要把內部類設爲靜態的 這樣他們的生命週期就是相同了;

如果內部類沒有static的話,就需要實例化內部類才能調用,說明非static的內部類不是自動跟隨主類加載的,而是被實例化的時候纔會加載。
而static的語義,就是主類能直接通過內部類名來訪問內部類中的static方法,而非static的內部類又是不會自動加載的,所以這時候內部類也要static,否則會前後衝突。


匿名內部類-回調-閉包

轉載於:https://www.cnblogs.com/highriver/archive/2010/09/09/1822392.html)

詳解內部類請參考:https://blog.csdn.net/chenssy/article/details/13170015

首先明確閉包的概念:一個代碼段被用來做爲方法的參數.
java中沒有直接使用某個方法做爲另一個方法的參數的,java使用匿名內部類來模擬這種情況。

匿名內部類往往是做爲一個內部類(接口)的具體實現。在一些平臺類(platform class)中有一些模板方法。模板方法的包含了固定流程。其中某些步驟是調用了內部類(接口)中的某些方法。但是平臺類將這些方法的具體實現延遲到了業務類中。業務類調用平臺類的模板方法,但是傳入匿名內部類的實現做爲模板方法的參數。

示例:

package callback;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class AnonymousBusinessTemplateExample2 {

// 內部接口也是回調接口,只定義抽象方法。
private interface Callback {
Object doIt(Connection conn)
throws SQLException;
}

// 模板方法(抽象)
private Object execute(Callback callback) throws SQLException {
Connection conn
= openConnection();
try {
return callback.doIt(conn);
}
finally {
closeConnection(conn);
}
}

// 業務方法(具體)
public Object sqlQuery(final String sql) throws SQLException {
//匿名內部類做爲模板方法的參數來模擬閉包
return execute(new Callback() {
public Object doIt(Connection conn) throws SQLException {
return conn.createStatement().executeQuery(sql);
}
});
}

public Connection openConnection() throws SQLException {
return DriverManager.getConnection("", null);
}

public void closeConnection(Connection conn) throws SQLException {
if (conn != null && !conn.isClosed()) {
conn.close();
}
}
}

一般內部接口比內部類用的更多。內部類中的方法可以有默認的實現。匿名內部類做爲業務方法的參數傳入時,會override默認的方法。內部接口的話,沒有默認的實現。完全將具體的實現交給了匿名內部類。

匿名內部類的思想是回調,即好茉塢原則。回調的一個好處是decouple。 客戶端只需要關心業務(比如匿名內部類的具體實現)而不用再關心一些資源的連接釋放什麼的,這些交給平臺類中的模板方法。ruby的閉包還支持對數組中的每個元素,文件中的每行,結果集中的每個記錄的操作。而用java實現這樣的迭代並操作其中元素非常麻煩。感覺java中用的多的偏模板方法,即邏輯中固定一些流程,初始化及釋放某些資源。


爲什麼需要內部類?

java內部類有什麼好處?爲什麼需要內部類?

首先舉一個簡單的例子,如果你想實現一個接口,但是這個接口中的一個方法和你構想的這個類中的一個方法的名稱,參數相同,你應該怎麼辦?這時候,你可以創建一個內部類實現這個接口。由於內部類對於外部類的所有內容都是可以訪問的,所以這樣做可以完成你直接實現這個接口的功能。

不過你可能要質疑,更改一下方法不就可以了嗎?

的確,以此作爲設計內部類的理由,沒有說服力。

真正的原因是這樣的,java中的內部類和接口或抽象類加在一起,可以解決常被C++程序員抱怨java中存在的一個問題 沒有多繼承。實際上,c++的多繼承設計起來很複雜,而java通過內部類+接口,可以很好的實現多繼承的效果。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章