1. 程式人生 > >從JDK原始碼看有趣的方法

從JDK原始碼看有趣的方法

在學習JDK的原始碼過程中我遇到了一些有趣有用的方法,在此之前如果要使用這些工具方法,我首先會想到的是commons-langguava這樣的語言擴充套件包,但現在如果是寫一些demo,使用原生即可達到目的。當然我們也不能否認它們的作用,在平時的工作專案中幾乎都會引入這些語言擴充套件包,直接使用他們也使得程式設計風格統一,而且還能夠對低版本的JDK提供支援。

以下收集的程式碼片段可能會逐漸增加,也可能不會。

<textarea readonly="readonly" name="code" class="Java"> 
   public static boolean equals(Object var0, Object var1) {
        return var0 == var1 || var0 != null && var0.equals(var1);
    }
    public static int hashCode(Object var0) {
        return var0 != null ? var0.hashCode() : 0;
    }
    public static <T> T requireNonNull(T var0) {
        if (var0 == null) {
            throw new NullPointerException();
        } else {
            return var0;
        }
    }

    public static <T> T requireNonNull(T var0, String var1) {
        if (var0 == null) {
            throw new NullPointerException(var1);
        } else {
            return var0;
        }
    }   

除此之外還應該從Objects學習到編寫工具類的正確的規範,

  • 定義為final class
  • 只定義一個無參的建構函式且丟擲斷言錯誤,防止被反射呼叫
  • 工具方法都是靜態方法
  • 靜態方法中只丟擲unchecked異常

java.lang.System

這個最早應該是在Hello World程式中見到的,推薦它的一個方法

<textarea readonly="readonly" name="code" class="Java"> 
 /**
     * Returns the same hash code for the given object as
     * would be returned by the default method hashCode(),
     * whether or not the given object's class overrides
     * hashCode().
     * The hash code for the null reference is zero.
     *
     * @param x object for which the hashCode is to be calculated
     * @return  the hashCode
     * @since   JDK1.1
     */
    public static native int identityHashCode(Object x);

註釋寫得很明白了,不管一個物件例項的class有沒有覆蓋Object的hashCode方法,都能使用這個方法獲得hash值。

獲取泛型類的型別引數

我們可以從以下程式碼獲得提示,程式碼來自HashMap

<textarea readonly="readonly" name="code" class="Java">
/**
     * Returns x's Class if it is of the form "class C implements
     * Comparable<C>", else null.
     */
    static Class<?> comparableClassFor(Object x) {
        if (x instanceof Comparable) {
            Class<?> c; Type[] ts, as; Type t; ParameterizedType p;
            if ((c = x.getClass()) == String.class) // bypass checks
                return c;
            if ((ts = c.getGenericInterfaces()) != null) {
                for (int i = 0; i < ts.length; ++i) {
                    if (((t = ts[i]) instanceof ParameterizedType) &&
                        ((p = (ParameterizedType)t).getRawType() ==
                         Comparable.class) &&
                        (as = p.getActualTypeArguments()) != null &&
                        as.length == 1 && as[0] == c) // type arg is c
                        return c;
                }
            }
        }
        return null;
    }

這裡的邏輯是獲得類C,然後獲取它實現的介面Comparable<C>,然後從這個Comparable<C>中獲得型別引數C,然後比較這兩個型別是否相等。雖然我們一直聽說Java的泛型是型別擦除式,但是在這裡我們是可以獲得泛型的引數型別的。照例用一段demo測試一下,

<textarea readonly="readonly" name="code" class="Java">
public class ParameterApp {
    public static void main(String[] args) {
        StringList list = new StringList();
        Class<?> clazz = getTypeArgument(list);
        System.out.println(clazz.getName());
    }

    static Class<?> getTypeArgument(Object x) {
        if (x instanceof Collection) {
            Class<?> c = x.getClass();
            Type[] ts, as; Type t; ParameterizedType p;
            if ((ts = c.getGenericInterfaces()) != null) {
                for (int i = 0; i < ts.length; ++i) {
                    if (((t = ts[i]) instanceof ParameterizedType) &&
                            ((as  = ((ParameterizedType)t).getActualTypeArguments()) != null)
                             &&
                            as.length == 1) // type arg is c
                        return (Class<?>) as[0];
                }
            }
        }
        return null;
    }

    static class StringList extends AbstractList<String> implements List<String> {

        @Override
        public String get(int i) {
            return null;
        }

        @Override
        public int size() {
            return 0;
        }
    }
}

sun.reflect.Reflection

這個工具類是和反射相關的,讓大家知道有這麼一個方法

<textarea readonly="readonly" name="code" class="Java">
 @CallerSensitive
    public static native Class<?> getCallerClass();
我第一次見到這個方法是在java.sql.DriverManager中的getConnection方法中見到的
<textarea readonly="readonly" name="code" class="Java">
@CallerSensitive public static Connection getConnection(String url, String user, String password) throws SQLException { java.util.Properties info = new java.util.Properties(); if (user != null) { info.put("user", user); } if (password != null) { info.put("password", password); } return (getConnection(url, info, Reflection.getCallerClass())); }

Reflection.getCallerClass()是一個native方法,返回的是Class<?>型別,在DriverManager中使用它的目的是為了獲得相應的ClassLoader,上面的程式碼是在Java 8中見到的。其中在Java 7中為獲得ClassLoaderDriverManager就直接提供了native的方法

<textarea readonly="readonly" name="code" class="Java">
/* Returns the caller's class loader, or null if none */
private static native ClassLoader getCallerClassLoader();
<textarea readonly="readonly" name="code" class="Java">
public class CallerApp {

    public static void main(String[] args) {
        CalleeApp app = new CalleeApp();
        Caller1 c1 = new Caller1();
        c1.run(app);
    }

    static class Caller1 {
        void run(CalleeApp calleeApp) {
            if (calleeApp == null) {
                throw new IllegalArgumentException("callee can not be null");
            }
            calleeApp.call();
        }
    }

}

public class CalleeApp { public void call() { Class<?> clazz = Reflection.getCallerClass(); System.out.println("Hello " + clazz); }}

我們用一段程式碼嘗試呼叫這個方法

<textarea readonly="readonly" name="code" class="Java">
public class CalleeApp {

    public void call() {
        Class<?> clazz = Reflection.getCallerClass();
        System.out.println("Hello " + clazz);
    }
}
<textarea readonly="readonly" name="code" class="Java">
public class CallerApp {

    public static void main(String[] args) {
        CalleeApp app = new CalleeApp();
        Caller1 c1 = new Caller1();
        c1.run(app);
    }

    static class Caller1 {
        void run(CalleeApp calleeApp) {
            if (calleeApp == null) {
                throw new IllegalArgumentException("callee can not be null");
            }
            calleeApp.call();
        }
    }

}
執行main方法會丟擲異常
Exception in thread "main" java.lang.InternalError: CallerSensitive annotation expected at frame 1
這個錯誤資訊說的是我們缺少在函式呼叫棧開始位置新增CallerSensitive註解,觀察DriverManagergetConnection方法確實是有這麼個註解的。

那如果給CalleeAppcall加上註解,那結果又會怎樣呢?

Object.wait(long timeout, int nanos)

這個方法是來賣萌,它的本義在註釋是這樣子寫的,

<textarea readonly="readonly" name="code" class="Java">
  /*
     * <p>
     * This method is similar to the {@code wait} method of one
     * argument, but it allows finer control over the amount of time to
     * wait for a notification before giving up. The amount of real time,
     * measured in nanoseconds, is given by:
     * <blockquote>
     * <pre>
     * 1000000*timeout+nanos</pre></blockquote>
     * <p>
     */
意思是提供精細化的時間衡量,nano可是納秒單位啊!!!
而它的實現卻是這樣的,
<textarea readonly="readonly" name="code" class="Java">
public final void wait(long timeout, int nanos) throws InterruptedException {
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos > 0) {
            timeout++;
        }

        wait(timeout);
    }
除了對傳入引數的數值範圍校驗外,對nano的使用緊緊是判斷這個變數是否大於0,是則給timeout加1,這只是增加了1毫秒的時間,並沒有體現出了精細化的地方。