<!-- GFM-TOC -->php
<!-- GFM-TOC -->sql
八個基本類型:編程
基本類型都有對應的包裝類型,基本類型與其對應的包裝類型之間的賦值使用自動裝箱與拆箱完成。
Integer x = 2; // 裝箱 int y = x; // 拆箱
new Integer(123) 與 Integer.valueOf(123) 的區別在於:
Integer x = new Integer(123); Integer y = new Integer(123); System.out.println(x == y); // false Integer z = Integer.valueOf(123); Integer k = Integer.valueOf(123); System.out.println(z == k); // true
valueOf() 方法的實現比較簡單,就是先判斷值是否在緩存池中,若是在的話就直接返回緩存池的內容。
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
在 Java 8 中,Integer 緩存池的大小默認爲 -128~127。
static final int low = -128; static final int high; static final Integer cache[]; static { // high value may be configured by property int h = 127; String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); if (integerCacheHighPropValue != null) { try { int i = parseInt(integerCacheHighPropValue); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - (-low) -1); } catch( NumberFormatException nfe) { // If the property cannot be parsed into an int, ignore it. } } high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); // range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127; }
編譯器會在自動裝箱過程調用 valueOf() 方法,所以多個 Integer 實例使用自動裝箱來建立而且值相同,那麼就會引用相同的對象。
Integer m = 123; Integer n = 123; System.out.println(m == n); // true
基本類型對應的緩衝池以下:
在使用這些基本類型對應的包裝類型時,就能夠直接使用緩衝池中的對象。
[StackOverflow : Differences between new Integer(123), Integer.valueOf(123) and just 123
](https://stackoverflow.com/que...
String 被聲明爲 final,所以它不可被繼承。
在 Java 8 中,String 內部使用 char 數組存儲數據。
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[]; }
在 Java 9 以後,String 類的實現改用 byte 數組存儲字符串,同時使用 coder
來標識使用了哪一種編碼。
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final byte[] value; /** The identifier of the encoding used to encode the bytes in {@code value}. */ private final byte coder; }
value 數組被聲明爲 final,這意味着 value 數組初始化以後就不能再引用其它數組。而且 String 內部沒有改變 value 數組的方法,所以能夠保證 String 不可變。
1. 能夠緩存 hash 值
由於 String 的 hash 值常常被使用,例如 String 用作 HashMap 的 key。不可變的特性可使得 hash 值也不可變,所以只須要進行一次計算。
2. String Pool 的須要
若是一個 String 對象已經被建立過了,那麼就會從 String Pool 中取得引用。只有 String 是不可變的,纔可能使用 String Pool。
<div align="center"> <img src="pics/f76067a5-7d5f-4135-9549-8199c77d8f1c.jpg" width=""/> </div>
3. 安全性
String 常常做爲參數,String 不可變性能夠保證參數不可變。例如在做爲網絡鏈接參數的狀況下若是 String 是可變的,那麼在網絡鏈接過程當中,String 被改變,改變 String 對象的那一方覺得如今鏈接的是其它主機,而實際狀況卻不必定是。
4. 線程安全
String 不可變性天生具有線程安全,能夠在多個線程中安全地使用。
Program Creek : Why String is immutable in Java?
1. 可變性
2. 線程安全
StackOverflow : String, StringBuffer, and StringBuilder
字符串常量池(String Pool)保存着全部字符串字面量(literal strings),這些字面量在編譯時期就肯定。不只如此,還可使用 String 的 intern() 方法在運行過程當中將字符串添加到 String Pool 中。
當一個字符串調用 intern() 方法時,若是 String Pool 中已經存在一個字符串和該字符串值相等(使用 equals() 方法進行肯定),那麼就會返回 String Pool 中字符串的引用;不然,就會在 String Pool 中添加一個新的字符串,並返回這個新字符串的引用。
下面示例中,s1 和 s2 採用 new String() 的方式新建了兩個不一樣字符串,而 s3 和 s4 是經過 s1.intern() 方法取得一個字符串引用。intern() 首先把 s1 引用的字符串放到 String Pool 中,而後返回這個字符串引用。所以 s3 和 s4 引用的是同一個字符串。
String s1 = new String("aaa"); String s2 = new String("aaa"); System.out.println(s1 == s2); // false String s3 = s1.intern(); String s4 = s1.intern(); System.out.println(s3 == s4); // true
若是是採用 "bbb" 這種字面量的形式建立字符串,會自動地將字符串放入 String Pool 中。
String s5 = "bbb"; String s6 = "bbb"; System.out.println(s5 == s6); // true
在 Java 7 以前,String Pool 被放在運行時常量池中,它屬於永久代。而在 Java 7,String Pool 被移到堆中。這是由於永久代的空間有限,在大量使用字符串的場景下會致使 OutOfMemoryError 錯誤。
使用這種方式一共會建立兩個字符串對象(前提是 String Pool 中尚未 "abc" 字符串對象)。
建立一個測試類,其 main 方法中使用這種方式來建立字符串對象。
public class NewStringTest { public static void main(String[] args) { String s = new String("abc"); } }
使用 javap -verbose 進行反編譯,獲得如下內容:
// ... Constant pool: // ... #2 = Class #18 // java/lang/String #3 = String #19 // abc // ... #18 = Utf8 java/lang/String #19 = Utf8 abc // ... public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=2, args_size=1 0: new #2 // class java/lang/String 3: dup 4: ldc #3 // String abc 6: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V 9: astore_1 // ...
在 Constant Pool 中,#19 存儲這字符串字面量 "abc",#3 是 String Pool 的字符串對象,它指向 #19 這個字符串字面量。在 main 方法中,0: 行使用 new #2 在堆中建立一個字符串對象,而且使用 ldc #3 將 String Pool 中的字符串對象做爲 String 構造函數的參數。
如下是 String 構造函數的源碼,能夠看到,在將一個字符串對象做爲另外一個字符串對象的構造函數參數時,並不會徹底複製 value 數組內容,而是都會指向同一個 value 數組。
public String(String original) { this.value = original.value; this.hash = original.hash; }
Java 的參數是以值傳遞的形式傳入方法中,而不是引用傳遞。
如下代碼中 Dog dog 的 dog 是一個指針,存儲的是對象的地址。在將一個參數傳入一個方法時,本質上是將對象的地址以值的方式傳遞到形參中。所以在方法中使指針引用其它對象,那麼這兩個指針此時指向的是徹底不一樣的對象,在一方改變其所指向對象的內容時對另外一方沒有影響。
public class Dog { String name; Dog(String name) { this.name = name; } String getName() { return this.name; } void setName(String name) { this.name = name; } String getObjectAddress() { return super.toString(); } }
public class PassByValueExample { public static void main(String[] args) { Dog dog = new Dog("A"); System.out.println(dog.getObjectAddress()); // Dog@4554617c func(dog); System.out.println(dog.getObjectAddress()); // Dog@4554617c System.out.println(dog.getName()); // A } private static void func(Dog dog) { System.out.println(dog.getObjectAddress()); // Dog@4554617c dog = new Dog("B"); System.out.println(dog.getObjectAddress()); // Dog@74a14482 System.out.println(dog.getName()); // B } }
若是在方法中改變對象的字段值會改變原對象該字段值,由於改變的是同一個地址指向的內容。
class PassByValueExample { public static void main(String[] args) { Dog dog = new Dog("A"); func(dog); System.out.println(dog.getName()); // B } private static void func(Dog dog) { dog.setName("B"); } }
StackOverflow: Is Java 「pass-by-reference」 or 「pass-by-value」?
Java 不能隱式執行向下轉型,由於這會使得精度下降。
1.1 字面量屬於 double 類型,不能直接將 1.1 直接賦值給 float 變量,由於這是向下轉型。
// float f = 1.1;
1.1f 字面量纔是 float 類型。
float f = 1.1f;
由於字面量 1 是 int 類型,它比 short 類型精度要高,所以不能隱式地將 int 類型下轉型爲 short 類型。
short s1 = 1; // s1 = s1 + 1;
可是使用 += 或者 ++ 運算符能夠執行隱式類型轉換。
s1 += 1; // s1++;
上面的語句至關於將 s1 + 1 的計算結果進行了向下轉型:
s1 = (short) (s1 + 1);
StackOverflow : Why don't Java's +=, -=, *=, /= compound assignment operators require casting?
從 Java 7 開始,能夠在 switch 條件判斷語句中使用 String 對象。
String s = "a"; switch (s) { case "a": System.out.println("aaa"); break; case "b": System.out.println("bbb"); break; }
switch 不支持 long,是由於 switch 的設計初衷是對那些只有少數的幾個值進行等值判斷,若是值過於複雜,那麼仍是用 if 比較合適。
// long x = 111; // switch (x) { // Incompatible types. Found: 'long', required: 'char, byte, short, int, Character, Byte, Short, Integer, String, or an enum' // case 111: // System.out.println(111); // break; // case 222: // System.out.println(222); // break; // }
StackOverflow : Why can't your switch statement data type be long, Java?
Java 中有三個訪問權限修飾符:private、protected 以及 public,若是不加訪問修飾符,表示包級可見。
能夠對類或類中的成員(字段以及方法)加上訪問修飾符。
protected 用於修飾成員,表示在繼承體系中成員對於子類可見,可是這個訪問修飾符對於類沒有意義。
設計良好的模塊會隱藏全部的實現細節,把它的 API 與它的實現清晰地隔離開來。模塊之間只經過它們的 API 進行通訊,一個模塊不須要知道其餘模塊的內部工做狀況,這個概念被稱爲信息隱藏或封裝。所以訪問權限應當儘量地使每一個類或者成員不被外界訪問。
若是子類的方法重寫了父類的方法,那麼子類中該方法的訪問級別不容許低於父類的訪問級別。這是爲了確保可使用父類實例的地方均可以使用子類實例,也就是確保知足里氏替換原則。
字段決不能是公有的,由於這麼作的話就失去了對這個字段修改行爲的控制,客戶端能夠對其隨意修改。例以下面的例子中,AccessExample 擁有 id 公有字段,若是在某個時刻,咱們想要使用 int 存儲 id 字段,那麼就須要修改全部的客戶端代碼。
public class AccessExample { public String id; }
可使用公有的 getter 和 setter 方法來替換公有字段,這樣的話就能夠控制對字段的修改行爲。
public class AccessExample { private int id; public String getId() { return id + ""; } public void setId(String id) { this.id = Integer.valueOf(id); } }
可是也有例外,若是是包級私有的類或者私有的嵌套類,那麼直接暴露成員不會有特別大的影響。
public class AccessWithInnerClassExample { private class InnerClass { int x; } private InnerClass innerClass; public AccessWithInnerClassExample() { innerClass = new InnerClass(); } public int getValue() { return innerClass.x; // 直接訪問 } }
1. 抽象類
抽象類和抽象方法都使用 abstract 關鍵字進行聲明。抽象類通常會包含抽象方法,抽象方法必定位於抽象類中。
抽象類和普通類最大的區別是,抽象類不能被實例化,須要繼承抽象類才能實例化其子類。
public abstract class AbstractClassExample { protected int x; private int y; public abstract void func1(); public void func2() { System.out.println("func2"); } }
public class AbstractExtendClassExample extends AbstractClassExample { @Override public void func1() { System.out.println("func1"); } }
// AbstractClassExample ac1 = new AbstractClassExample(); // 'AbstractClassExample' is abstract; cannot be instantiated AbstractClassExample ac2 = new AbstractExtendClassExample(); ac2.func1();
2. 接口
接口是抽象類的延伸,在 Java 8 以前,它能夠當作是一個徹底抽象的類,也就是說它不能有任何的方法實現。
從 Java 8 開始,接口也能夠擁有默認的方法實現,這是由於不支持默認方法的接口的維護成本過高了。在 Java 8 以前,若是一個接口想要添加新的方法,那麼要修改全部實現了該接口的類。
接口的成員(字段 + 方法)默認都是 public 的,而且不容許定義爲 private 或者 protected。
接口的字段默認都是 static 和 final 的。
public interface InterfaceExample { void func1(); default void func2(){ System.out.println("func2"); } int x = 123; // int y; // Variable 'y' might not have been initialized public int z = 0; // Modifier 'public' is redundant for interface fields // private int k = 0; // Modifier 'private' not allowed here // protected int l = 0; // Modifier 'protected' not allowed here // private void fun3(); // Modifier 'private' not allowed here }
public class InterfaceImplementExample implements InterfaceExample { @Override public void func1() { System.out.println("func1"); } }
// InterfaceExample ie1 = new InterfaceExample(); // 'InterfaceExample' is abstract; cannot be instantiated InterfaceExample ie2 = new InterfaceImplementExample(); ie2.func1(); System.out.println(InterfaceExample.x);
3. 比較
4. 使用選擇
使用接口:
使用抽象類:
在不少狀況下,接口優先於抽象類。由於接口沒有抽象類嚴格的類層次結構要求,能夠靈活地爲一個類添加行爲。而且從 Java 8 開始,接口也能夠有默認的方法實現,使得修改接口的成本也變的很低。
public class SuperExample { protected int x; protected int y; public SuperExample(int x, int y) { this.x = x; this.y = y; } public void func() { System.out.println("SuperExample.func()"); } }
public class SuperExtendExample extends SuperExample { private int z; public SuperExtendExample(int x, int y, int z) { super(x, y); this.z = z; } @Override public void func() { super.func(); System.out.println("SuperExtendExample.func()"); } }
SuperExample e = new SuperExtendExample(1, 2, 3); e.func();
SuperExample.func() SuperExtendExample.func()
1. 重寫(Override)
存在於繼承體系中,指子類實現了一個與父類在方法聲明上徹底相同的一個方法。
爲了知足裏式替換原則,重寫有有如下兩個限制:
使用 @Override 註解,可讓編譯器幫忙檢查是否知足上面的兩個限制條件。
2. 重載(Overload)
存在於同一個類中,指一個方法與已經存在的方法名稱上相同,可是參數類型、個數、順序至少有一個不一樣。
應該注意的是,返回值不一樣,其它都相同不算是重載。
public native int hashCode() public boolean equals(Object obj) protected native Object clone() throws CloneNotSupportedException public String toString() public final native Class<?> getClass() protected void finalize() throws Throwable {} public final native void notify() public final native void notifyAll() public final native void wait(long timeout) throws InterruptedException public final void wait(long timeout, int nanos) throws InterruptedException public final void wait() throws InterruptedException
1. 等價關係
Ⅰ 自反性
x.equals(x); // true
Ⅱ 對稱性
x.equals(y) == y.equals(x); // true
Ⅲ 傳遞性
if (x.equals(y) && y.equals(z)) x.equals(z); // true;
Ⅳ 一致性
屢次調用 equals() 方法結果不變
x.equals(y) == x.equals(y); // true
Ⅴ 與 null 的比較
對任何不是 null 的對象 x 調用 x.equals(null) 結果都爲 false
x.equals(null); // false;
2. 等價與相等
Integer x = new Integer(1); Integer y = new Integer(1); System.out.println(x.equals(y)); // true System.out.println(x == y); // false
3. 實現
public class EqualExample { private int x; private int y; private int z; public EqualExample(int x, int y, int z) { this.x = x; this.y = y; this.z = z; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; EqualExample that = (EqualExample) o; if (x != that.x) return false; if (y != that.y) return false; return z == that.z; } }
hashCode() 返回散列值,而 equals() 是用來判斷兩個對象是否等價。等價的兩個對象散列值必定相同,可是散列值相同的兩個對象不必定等價。
在覆蓋 equals() 方法時應當老是覆蓋 hashCode() 方法,保證等價的兩個對象散列值也相等。
下面的代碼中,新建了兩個等價的對象,並將它們添加到 HashSet 中。咱們但願將這兩個對象當成同樣的,只在集合中添加一個對象,可是由於 EqualExample 沒有實現 hasCode() 方法,所以這兩個對象的散列值是不一樣的,最終致使集合添加了兩個等價的對象。
EqualExample e1 = new EqualExample(1, 1, 1); EqualExample e2 = new EqualExample(1, 1, 1); System.out.println(e1.equals(e2)); // true HashSet<EqualExample> set = new HashSet<>(); set.add(e1); set.add(e2); System.out.println(set.size()); // 2
理想的散列函數應當具備均勻性,即不相等的對象應當均勻分佈到全部可能的散列值上。這就要求了散列函數要把全部域的值都考慮進來。能夠將每一個域都當成 R 進制的某一位,而後組成一個 R 進制的整數。R 通常取 31,由於它是一個奇素數,若是是偶數的話,當出現乘法溢出,信息就會丟失,由於與 2 相乘至關於向左移一位。
一個數與 31 相乘能夠轉換成移位和減法:31*x == (x<<5)-x
,編譯器會自動進行這個優化。
@Override public int hashCode() { int result = 17; result = 31 * result + x; result = 31 * result + y; result = 31 * result + z; return result; }
默認返回 ToStringExample@4554617c 這種形式,其中 @ 後面的數值爲散列碼的無符號十六進制表示。
public class ToStringExample { private int number; public ToStringExample(int number) { this.number = number; } }
ToStringExample example = new ToStringExample(123); System.out.println(example.toString());
ToStringExample@4554617c
1. cloneable
clone() 是 Object 的 protected 方法,它不是 public,一個類不顯式去重寫 clone(),其它類就不能直接去調用該類實例的 clone() 方法。
public class CloneExample { private int a; private int b; }
CloneExample e1 = new CloneExample(); // CloneExample e2 = e1.clone(); // 'clone()' has protected access in 'java.lang.Object'
重寫 clone() 獲得如下實現:
public class CloneExample { private int a; private int b; @Override public CloneExample clone() throws CloneNotSupportedException { return (CloneExample)super.clone(); } }
CloneExample e1 = new CloneExample(); try { CloneExample e2 = e1.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); }
java.lang.CloneNotSupportedException: CloneExample
以上拋出了 CloneNotSupportedException,這是由於 CloneExample 沒有實現 Cloneable 接口。
應該注意的是,clone() 方法並非 Cloneable 接口的方法,而是 Object 的一個 protected 方法。Cloneable 接口只是規定,若是一個類沒有實現 Cloneable 接口又調用了 clone() 方法,就會拋出 CloneNotSupportedException。
public class CloneExample implements Cloneable { private int a; private int b; @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } }
2. 淺拷貝
拷貝對象和原始對象的引用類型引用同一個對象。
public class ShallowCloneExample implements Cloneable { private int[] arr; public ShallowCloneExample() { arr = new int[10]; for (int i = 0; i < arr.length; i++) { arr[i] = i; } } public void set(int index, int value) { arr[index] = value; } public int get(int index) { return arr[index]; } @Override protected ShallowCloneExample clone() throws CloneNotSupportedException { return (ShallowCloneExample) super.clone(); } }
ShallowCloneExample e1 = new ShallowCloneExample(); ShallowCloneExample e2 = null; try { e2 = e1.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } e1.set(2, 222); System.out.println(e2.get(2)); // 222
3. 深拷貝
拷貝對象和原始對象的引用類型引用不一樣對象。
public class DeepCloneExample implements Cloneable { private int[] arr; public DeepCloneExample() { arr = new int[10]; for (int i = 0; i < arr.length; i++) { arr[i] = i; } } public void set(int index, int value) { arr[index] = value; } public int get(int index) { return arr[index]; } @Override protected DeepCloneExample clone() throws CloneNotSupportedException { DeepCloneExample result = (DeepCloneExample) super.clone(); result.arr = new int[arr.length]; for (int i = 0; i < arr.length; i++) { result.arr[i] = arr[i]; } return result; } }
DeepCloneExample e1 = new DeepCloneExample(); DeepCloneExample e2 = null; try { e2 = e1.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } e1.set(2, 222); System.out.println(e2.get(2)); // 2
4. clone() 的替代方案
使用 clone() 方法來拷貝一個對象即複雜又有風險,它會拋出異常,而且還須要類型轉換。Effective Java 書上講到,最好不要去使用 clone(),可使用拷貝構造函數或者拷貝工廠來拷貝一個對象。
public class CloneConstructorExample { private int[] arr; public CloneConstructorExample() { arr = new int[10]; for (int i = 0; i < arr.length; i++) { arr[i] = i; } } public CloneConstructorExample(CloneConstructorExample original) { arr = new int[original.arr.length]; for (int i = 0; i < original.arr.length; i++) { arr[i] = original.arr[i]; } } public void set(int index, int value) { arr[index] = value; } public int get(int index) { return arr[index]; } }
CloneConstructorExample e1 = new CloneConstructorExample(); CloneConstructorExample e2 = new CloneConstructorExample(e1); e1.set(2, 222); System.out.println(e2.get(2)); // 2
1. 數據
聲明數據爲常量,能夠是編譯時常量,也能夠是在運行時被初始化後不能被改變的常量。
final int x = 1; // x = 2; // cannot assign value to final variable 'x' final A y = new A(); y.a = 1;
2. 方法
聲明方法不能被子類重寫。
private 方法隱式地被指定爲 final,若是在子類中定義的方法和基類中的一個 private 方法簽名相同,此時子類的方法不是重寫基類方法,而是在子類中定義了一個新的方法。
3. 類
聲明類不容許被繼承。
1. 靜態變量
public class A { private int x; // 實例變量 private static int y; // 靜態變量 public static void main(String[] args) { // int x = A.x; // Non-static field 'x' cannot be referenced from a static context A a = new A(); int x = a.x; int y = A.y; } }
2. 靜態方法
靜態方法在類加載的時候就存在了,它不依賴於任何實例。因此靜態方法必須有實現,也就是說它不能是抽象方法。
public abstract class A { public static void func1(){ } // public abstract static void func2(); // Illegal combination of modifiers: 'abstract' and 'static' }
只能訪問所屬類的靜態字段和靜態方法,方法中不能有 this 和 super 關鍵字。
public class A { private static int x; private int y; public static void func1(){ int a = x; // int b = y; // Non-static field 'y' cannot be referenced from a static context // int b = this.y; // 'A.this' cannot be referenced from a static context } }
3. 靜態語句塊
靜態語句塊在類初始化時運行一次。
public class A { static { System.out.println("123"); } public static void main(String[] args) { A a1 = new A(); A a2 = new A(); } }
123
4. 靜態內部類
非靜態內部類依賴於外部類的實例,而靜態內部類不須要。
public class OuterClass { class InnerClass { } static class StaticInnerClass { } public static void main(String[] args) { // InnerClass innerClass = new InnerClass(); // 'OuterClass.this' cannot be referenced from a static context OuterClass outerClass = new OuterClass(); InnerClass innerClass = outerClass.new InnerClass(); StaticInnerClass staticInnerClass = new StaticInnerClass(); } }
靜態內部類不能訪問外部類的非靜態的變量和方法。
5. 靜態導包
在使用靜態變量和方法時不用再指明 ClassName,從而簡化代碼,但可讀性大大下降。
import static com.xxx.ClassName.*
6. 初始化順序
靜態變量和靜態語句塊優先於實例變量和普通語句塊,靜態變量和靜態語句塊的初始化順序取決於它們在代碼中的順序。
public static String staticField = "靜態變量";
static { System.out.println("靜態語句塊"); }
public String field = "實例變量";
{ System.out.println("普通語句塊"); }
最後纔是構造函數的初始化。
public InitialOrderTest() { System.out.println("構造函數"); }
存在繼承的狀況下,初始化順序爲:
每一個類都有一個 Class 對象,包含了與類有關的信息。當編譯一個新類時,會產生一個同名的 .class 文件,該文件內容保存着 Class 對象。
類加載至關於 Class 對象的加載,類在第一次使用時才動態加載到 JVM 中。也可使用 Class.forName("com.mysql.jdbc.Driver")
這種方式來控制類的加載,該方法會返回一個 Class 對象。
反射能夠提供運行時的類信息,而且這個類能夠在運行時才加載進來,甚至在編譯時期該類的 .class 不存在也能夠加載進來。
Class 和 java.lang.reflect 一塊兒對反射提供了支持,java.lang.reflect 類庫主要包含了如下三個類:
反射的優勢:
反射的缺點:
儘管反射很是強大,但也不能濫用。若是一個功能能夠不用反射完成,那麼最好就不用。在咱們使用反射技術時,下面幾條內容應該牢記於心。
Throwable 能夠用來表示任何能夠做爲異常拋出的類,分爲兩種: Error 和 Exception。其中 Error 用來表示 JVM 沒法處理的錯誤,Exception 分爲兩種:
<div align="center"> <img src="pics/PPjwP.png" width="600"/> </div>
public class Box<T> { // T stands for "Type" private T t; public void set(T t) { this.t = t; } public T get() { return t; } }
Java 註解是附加在代碼中的一些元信息,用於一些工具在編譯、運行時進行解析和使用,起到說明、配置的功能。註解不會也不能影響代碼的實際邏輯,僅僅起到輔助性的做用。
New highlights in Java SE 8
New highlights in Java SE 7
What are the main differences between Java and C++?
原文: https://github.com/CyC2018/CS...以爲文章不錯的歡迎關注個人WX公衆號:程序員喬戈裏
我是百度後臺開發工程師,哈工大計算機本碩,專一分享技術乾貨/編程資源/求職面試/成長感悟等,關注送3000G編程資源,免費下載CSDN資源。