編寫高品質代碼:改善Java程式的151個建議 --[98~105]

來源:互聯網
上載者:User

標籤:pre   declare   reflect   code   產生   情況   tor   row   檔案   

建議的採用順序是List中泛型順序依次為T、?、Object

(1)、List是確定的某一個類型

  List表示的是List集合中的元素都為T類型,具體類型在運行期決定;List<?>表示的是任意類型,與List類似,而List則表示List集合中的所有元素為Object類型,因為Object是所有類的父類,所以List也可以容納所有的類類型,從這一字面意義上分析,List更符合習慣:編碼者知道它是某一個類型,只是在運行期才確定而已。

(2)List可以進行讀寫操作

  List可以進行諸如add,remove等操作,因為它的類型是固定的T類型,在編碼期不需要進行任何的轉型操作。

  List是唯讀類型的,不能進行增加、修改操作,因為編譯器不知道List中容納的是什麼類型的元素,也就無法校正類型是否安全了,而且List<?>讀取出的元素都是Object類型的,需要主動轉型,所以它經常用於泛型方法的傳回值。注意List<?>雖然無法增加,修改元素,但是卻可以刪除元素,比如執行remove、clear等方法,那是因為它的刪除動作與泛型型別無關。

  List 也可以讀寫操作,但是它執行寫入操作時需要向上轉型(Up cast),在讀取資料的時候需要向下轉型,而此時已經失去了泛型存在的意義了。

嚴格限定泛型型別採用多重界限

List介面的toArray方法可以把一個集合轉化為數組,但是使用不方便,toArray()方法返回的是一個Object數組,所以需要自行轉變。 當一個泛型類(特別是泛型集合)轉變為泛型數組時,泛型數組的真實類型不能是泛型的父類型(比如頂層類Object),只能是泛型型別的子類型(當然包括自身類型),否則就會出現類型轉換異常。 通過反射類Array聲明了一個T類型的數組,由於我們無法在運行期獲得泛型型別的參數,因此就需要調用者主動傳入T參數類型。 List轉數組:

public static <T> T[] toArray(List<T> list,Class<T> tClass) {//聲明並初始化一個T類型的數組T[] t = (T[])Array.newInstance(tClass, list.size());for (int i = 0, n = list.size(); i < n; i++) {t[i] = list.get(i);}return t;}

 

注意Class類的特殊性

class類的特殊性:

無建構函式:Java中的類一般都有建構函式,用於建立執行個體對象,但是Class類卻沒有建構函式,不能執行個體化,Class對象是在載入類時由Java虛擬機器通過調用類載入器中的difineClass方法自動構造的。 可以描述基本類型:雖然8個基本類型在JVM中並不是一個對象,它們一般存在於棧記憶體中,但是Class類仍然可以描述它們,例如可以使用int.class表示int類型的類對象。 其對象都是單例模式:一個Class的執行個體對象描述一個類,並且只描述一個類,反過來也成立。一個類只有一個Class執行個體對象

獲得Class對象的三種途徑:

類屬性方式:如String.class 對象的getClass方法,如new String().getClass() forName方法載入:如Class.forName(" java.lang.String") 獲得了Class對象後,就可以通過getAnnotations()獲得註解,通過getMethods()獲得方法,通過getConstructors()獲得建構函式等

適時選擇getDeclaredXXX和getXXX

getXXX法獲得的是所有public存取層級的方法,包括從父類繼承的方法,而getDeclaredXXX獲得的是自身類的方法,包括公用的(public)方法、私人(private)方法,而且不受限於存取權限。 如果需要列出所有繼承自父類的方法,該如何?呢?簡單,先獲得父類,然後使用getDeclaredMethods,之後持續遞迴即可。

反射訪問屬性或方法時將Accessible設定為true

通過反射執行一個方法的過程如下:

擷取一個方法對象;
然後根據isAccessible傳回值確定是否能夠執行,如果傳回值為false則需要調用setAccessible(true);
最後再調用invoke執行方法

Method method= ...;        //檢查是否可以訪問        if(!method.isAccessible()){            method.setAccessible(true);        }        //執行方法        method.invoke(obj, args);

AccessibleObject源碼:

public class AccessibleObject implements AnnotatedElement {      //定義反射的預設操作許可權suppressAccessChecks      static final private java.security.Permission ACCESS_PERMISSION =        new ReflectPermission("suppressAccessChecks");      //是否重設了安全檢查,預設為false      boolean override;      //建構函式      protected AccessibleObject() {}      //是否可以快速擷取,預設是不能      public boolean isAccessible() {        return override;    } }

AccessibleObject是Filed、Method、Constructor的父類,決定其是否可以快速存取而不進行存取控制檢查,在AccessibleObject類中是以override變數儲存該值的,但是具體是否快速執行時在Method的invoke方法中決定的:

public Object invoke(Object obj, Object... args)        throws IllegalAccessException, IllegalArgumentException,           InvocationTargetException    {        //是否可以快速擷取,其值是父類AccessibleObject的override變數        if (!override) {          //不能快速擷取,執行安全檢查               if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {                Class<?> caller = Reflection.getCallerClass(1);                 checkAccess(caller, clazz, obj, modifiers);            }        }        MethodAccessor ma = methodAccessor;             // read volatile        if (ma == null) {            ma = acquireMethodAccessor();        }        //直接執行方法        return ma.invoke(obj, args);    }

Accessible屬性只是用來判斷是否需要進行安全檢查的,如果不需要則直接執行,這就可以大幅度的提升系統效能了(當然了,取消了安全檢查,也可以運行private方法、訪問private屬性的)。經過測試,在大量的反射情況下,設定Accessible為true可以提高效能20倍左右。 Accessible屬性決定Field和Constructor是否受存取控制檢查。我們在設定Field或執行Constructor時,務必要設定Accessible為true。

使用forName動態載入類檔案

動態載入(Dynamic Loading)是指在程式運行時載入需要的類庫檔案,對Java程式來說,一般情況下,一個類檔案在啟動時或首次初始化時會被載入到記憶體中,而反射則可以在運行時再決定是否需要載入一個類。 舉個栗子:

public class Client103 {    public static void main(String[] args) throws ClassNotFoundException {        //動態載入        Class.forName("com.study.advice103.Utils");    }}class Utils{    //靜態代碼塊    static{        System.out.println("Do Something.....");    }}

結果: Do Something.....

forName只是把一個類載入到記憶體中,並不保證由此產生一個執行個體對象,也不會執行任何方法,之所以會初始化static代碼,那是由類載入機制所決定的,而不是forName方法決定的。也就是說,如果沒有static屬性或static代碼塊,forName就是載入類,沒有任何的執行行為。

動態載入不適合數組

數組是一個非常特殊的類,雖然它是一個類,但沒有定義類類路徑。

String [] strs =  new String[10];        Class.forName("java.lang.String[]");

會產生bug:

Exception in thread "main" java.lang.ClassNotFoundException: java/lang/String[]    at java.lang.Class.forName0(Native Method)

動態載入數組:

   // 動態建立數組        String[] strs = (String[]) Array.newInstance(String.class, 8);        // 建立一個多維陣列        int[][] ints = (int[][]) Array.newInstance(int.class, 2, 3);

 

編寫高品質代碼:改善Java程式的151個建議 --[98~105]

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.