1. 程式人生 > >02 從JDK原始碼角度看Boolean

02 從JDK原始碼角度看Boolean

Java的Boolean類主要作用就是對基本型別boolean進行封裝,提供了一些處理boolean型別的方法,比如String型別和boolean型別的轉換。

主要實現原始碼如下圖所示,具體實現程式碼可自行檢視對應的程式碼。

既然是對基本型別boolean的封裝,那必然要有一個變數來儲存,即value,而且它被宣告為final,表明它是不可變的。兩種建構函式可分別傳入boolean和String型別,對於String型別會進行”to boolean”解析,即當傳入的字串忽略大小寫等於”true”時判斷為true,否則為false。

但是我們說一般不推薦直接用建構函式來例項化Boolean物件,這是為什麼?接著往下看,對於布林值也就只有兩種狀態

,我們其實可以僅僅用兩個物件就表示所有的布林值,也就是說在Java的世界中只要全域性存在兩個Boolean物件即可,例項化出多餘的Boolean物件仍然能正確表示布林值,只是會浪費一些空間和影響時間效能。僅需要的兩個物件為

  public static final Boolean TRUE = new Boolean(true);
  public static final Boolean FALSE = new Boolean(false);

所以推薦的形式Boolean.TRUEBoolean.valueOf(true)Boolean.valueOf("true"),避免生成不必要的物件。

接著再看看Boolean的TYPE屬性,它toString的值其實是boolean

public static final Class<Boolean> TYPE = (Class<Boolean>) Class.getPrimitiveClass("boolean");

看看怎麼來的。Class的getPrimitiveClass是一個native方法,在Class.c中有個Java_java_lang_Class_getPrimitiveClass方法與之對應,所以JVM層面會通過JVM_FindPrimitiveClass函式會根據”boolean”字串獲得jclass,最終到Java層則為Class<Boolean>

以下是class.c中Java_java_lang_Class_getPrimitiveClass的原始碼

JNIEXPORT jclass JNICALL
Java_java_lang_Class_getPrimitiveClass(JNIEnv *env,
                                       jclass cls,
                                       jstring name)
{
    const char *utfName;
    jclass result;

    if (name == NULL) {
        JNU_ThrowNullPointerException(env, 0);
        return NULL;
    }

    utfName = (*env)->GetStringUTFChars(env, name, 0);
    if (utfName == 0)
        return NULL;

    result = JVM_FindPrimitiveClass(env, utfName);

    (*env)->ReleaseStringUTFChars(env, name, utfName);

    return result;
}

TYPE執行toString時,邏輯如下,則其實是getName函式決定其值,getName通過native方法getName0從JVM層獲取名稱,

public String toString() {
     return (isInterface() ? "interface " : (isPrimitive() ? "" : "class "))+ getName();
}

這裡呢類似於下面的程式碼執行。

package test;

public class Demo {	
	public static void main(String[] args) {
		System.out.println(Boolean.TYPE.toString());//boolean
	}
}

實際上呼叫的是Class.toString()方法。原始碼如上面的toString()原始碼所示。

getName0根據一個陣列獲得對應的名稱,JVM根據Java層的Class可得到對應型別的陣列下標,比如這裡下標為4,則名稱為”boolean”。

const char* type2name_tab[T_CONFLICT+1] = {
  NULL, NULL, NULL, NULL,
  "boolean",
  "char",
  "float",
  "double",
  "byte",
  "short",
  "int",
  "long",
  "object",
  "array",
  "void",
  "*address*",
  "*narrowoop*",
  "*conflict*"
};

往下繼續看HashCode,實現邏輯如下,即true返回1231而false返回1237。

   /**
     * Returns a hash code for a {@code boolean} value; compatible with
     * {@code Boolean.hashCode()}.
     *
     * @param value the value to hash
     * @return a hash code value for a {@code boolean} value.
     * @since 1.8
     */
    public static int hashCode(boolean value) {
        return value ? 1231 : 1237;
    }

原始碼如上所示。

再繼續往下是equals方法,原始碼如下所示:

   /**
     * Returns {@code true} if and only if the argument is not
     * {@code null} and is a {@code Boolean} object that
     * represents the same {@code boolean} value as this object.
     *
     * @param   obj   the object to compare with.
     * @return  {@code true} if the Boolean objects represent the
     *          same value; {@code false} otherwise.
     */
    public boolean equals(Object obj) {
        if (obj instanceof Boolean) {
            return value == ((Boolean)obj).booleanValue();
        }
        return false;
    }

equals方法就是先判斷是不是從Boolean例項化出來的,然後再繼續比較是不是相等。

因為Boolean實現Comparable<Boolean>介面是為了方便在集合中進行比較,它需要實現的方法為compareTo

public int compareTo(Boolean b) {
    return compare(this.value, b.value);
}

具體compare原始碼如下:

public static int compare(boolean x, boolean y) {
    return (x == y) ? 0 : (x ? 1 : -1);
}

此外,還提供了logicalAnd、logicalOr和logicalXor用於實現三種邏輯運算。具體測試程式碼如下:

		System.out.println(Boolean.logicalAnd(true, true));
		System.out.println(Boolean.logicalAnd(true, false));
		System.out.println(Boolean.logicalAnd(false, true));
		System.out.println(Boolean.logicalAnd(false, false));
		
		System.out.println(Boolean.logicalOr(true, true));
		System.out.println(Boolean.logicalOr(true, false));
		System.out.println(Boolean.logicalOr(false, true));
		System.out.println(Boolean.logicalOr(false, false));
		
		System.out.println(Boolean.logicalXor(true, true));
		System.out.println(Boolean.logicalXor(true, false));
		System.out.println(Boolean.logicalXor(false, true));
		System.out.println(Boolean.logicalXor(false, false));

//列印結果如下:		
//		true
//		false
//		false
//		false
		
//		true
//		true
//		true
//		false
		
//		false
//		true
//		true
//		false

至此,還有兩個方法沒有提到,分別是:parseBoolean和getBoolean

parseBoolean原始碼是

   /**
     * Parses the string argument as a boolean.  The {@code boolean}
     * returned represents the value {@code true} if the string argument
     * is not {@code null} and is equal, ignoring case, to the string
     * {@code "true"}. <p>
     * Example: {@code Boolean.parseBoolean("True")} returns {@code true}.<br>
     * Example: {@code Boolean.parseBoolean("yes")} returns {@code false}.
     *
     * @param      s   the {@code String} containing the boolean
     *                 representation to be parsed
     * @return     the boolean represented by the string argument
     * @since 1.5
     */
    public static boolean parseBoolean(String s) {
        return ((s != null) && s.equalsIgnoreCase("true"));
    }

在上面內容中有提到,如果是一個字串型別,比如構造方法中傳入字串型別,那麼判斷是否為空,並且字串是否和true忽略大小寫equals

測試程式碼如下:

//測試parseBoolean方法
Boolean b1 = new Boolean("true");
String str = null;
Boolean b2 = new Boolean(str);
Boolean b3 = new Boolean("TRUE");
Boolean b4= new Boolean("s");
System.out.println("此處只測試構造方法,因為構造方法中用到了具體的parseBoolean方法");
System.out.println(b1);//		true
System.out.println(b2);//		false
System.out.println(b3);//		true
System.out.println(b4);//       false

getBoolean方法原始碼如下:

   /**
     * Returns {@code true} if and only if the system property
     * named by the argument exists and is equal to the string
     * {@code "true"}. (Beginning with version 1.0.2 of the
     * Java<small><sup>TM</sup></small> platform, the test of
     * this string is case insensitive.) A system property is accessible
     * through {@code getProperty}, a method defined by the
     * {@code System} class.
     * <p>
     * If there is no property with the specified name, or if the specified
     * name is empty or null, then {@code false} is returned.
     *
     * @param   name   the system property name.
     * @return  the {@code boolean} value of the system property.
     * @throws  SecurityException for the same reasons as
     *          {@link System#getProperty(String) System.getProperty}
     * @see     java.lang.System#getProperty(java.lang.String)
     * @see     java.lang.System#getProperty(java.lang.String, java.lang.String)
     */
    public static boolean getBoolean(String name) {
        boolean result = false;
        try {
            result = parseBoolean(System.getProperty(name));
        } catch (IllegalArgumentException | NullPointerException e) {
        }
        return result;
    }

當且僅當以引數命名的系統屬性存在,且等於 "true" 字串時,才返回 true。(從 JavaTM 1.0.2 平臺開始,字串的測試不再區分大小寫。)通過 getProperty 方法可訪問系統屬性,此方法由 System 類定義。 如果沒有以指定名稱命名的屬性或者指定名稱為空或 null,則返回 false。

測試程式碼如下:

/*
		*當且僅當以引數命名的系統屬性存在,且等於 "true" 字串時,才返回 true
		*/

		//大寫的true返回為false,必須是小寫的true
		String s1 = "true";
		String s2 = new String("true");

		//這裡將s1存放到Java系統屬性中了.
		System.setProperty(s1,"true");
		System.setProperty(s2,"true");

		//這裡從系統屬性中獲取s1,所以獲取到了。
		System.out.println(Boolean.getBoolean(s1));//true
		System.out.println(Boolean.getBoolean(s2));//true

		String s3 = "true";

		//很明顯s3並沒有存放到系統屬性中所以返回false。
		System.out.println(Boolean.getBoolean(s3));//false
		System.out.println(Boolean.getBoolean("true"));//false

以上是針對Boolean類的所有的測試,如果有不到位的地方,還請各位補充。

下面是完成的測試案例:

package test;
/**
 * Boolean類的測試案例
 * @Package       test
 * @Title:        BooleanDemo.java
 * @Company:      $
 * @author        BurgessLee 
 * @date          2018年9月29日-上午11:36:39
 * @Description:  $
 */
public class BooleanDemo {
	
	public static void main(String[] args) {
		System.out.println(Boolean.TYPE.toString());
		
		//測試parseBoolean方法
		Boolean b1 = new Boolean("true");
		String str = null;
		Boolean b2 = new Boolean(str);
		Boolean b3 = new Boolean("TRUE");
		Boolean b4= new Boolean("s");
		System.out.println("此處只測試構造方法,因為構造方法中用到了具體的parseBoolean方法");
		System.out.println(b1);//		true
		System.out.println(b2);//		false
		System.out.println(b3);//		true
		System.out.println(b4);//       false



		
		//測試boolean其他的方法
		System.out.println("=================其他方法的測試=========================");
		System.out.println(Boolean.logicalAnd(true, true));
		System.out.println(Boolean.logicalAnd(true, false));
		System.out.println(Boolean.logicalAnd(false, true));
		System.out.println(Boolean.logicalAnd(false, false));
		
		System.out.println(Boolean.logicalOr(true, true));
		System.out.println(Boolean.logicalOr(true, false));
		System.out.println(Boolean.logicalOr(false, true));
		System.out.println(Boolean.logicalOr(false, false));
		
		System.out.println(Boolean.logicalXor(true, true));
		System.out.println(Boolean.logicalXor(true, false));
		System.out.println(Boolean.logicalXor(false, true));
		System.out.println(Boolean.logicalXor(false, false));
		
//		true
//		false
//		false
//		false
		
//		true
//		true
//		true
//		false
		
//		false
//		true
//		true
//		false
		
		
		System.out.println("==================getBoolean方法的測試====================");
		/*
		*當且僅當以引數命名的系統屬性存在,且等於 "true" 字串時,才返回 true
		*/

		//大寫的true返回為false,必須是小寫的true
		String s1 = "true";
		String s2 = new String("true");

		//這裡將s1存放到Java系統屬性中了.
		System.setProperty(s1,"true");
		System.setProperty(s2,"true");

		//這裡從系統屬性中獲取s1,所以獲取到了。
		System.out.println(Boolean.getBoolean(s1));//true
		System.out.println(Boolean.getBoolean(s2));//true

		String s3 = "true";

		//很明顯s3並沒有存放到系統屬性中所以返回false。
		System.out.println(Boolean.getBoolean(s3));//false
		System.out.println(Boolean.getBoolean("true"));//false
	}

}