黑馬程序員_Java基礎加強_jdk1.5重要新特性詳解


一,靜態導入:
    import語句是導入包中的某個類,或者包中的所有類,它不占用任何資源,只是讓程序員們少寫一些包名和 類名。     import static 語句是導入一個包中的某個靜態方法或者所有靜態方法。比如說Math類,Arrays類,     Collections類,導入這些類之后,就可以不用寫這些類名了,可以直接寫方法。     import static Math.max;在使用max方法時就可以省略Math類名了。
二,可變參數:     當某個方法要接受若干個參數,就可以使用可變參數。比如計算某幾個數的和,沒有可變參數之前,只能通過多個重載來實現。     override(重寫):當父類的某個方法是私有的,在子類中有一個和父類該私有方法一模一樣的方法,那么子類的這個方法不是重寫的父類的方法,而是一個全新的方法。     overload(重載):兩個方法名字相同,參數列表相同(個數和類型都相同),但是方法返回值類型不同,那么這種情況編譯會出錯。不構成重載。當參數個數相同,但是順序不同,也屬於重載,這種情況屬於參數類型不同。    注意: 可變參數只能在參數列表的最后面。(String name,int...args)。方法體內是以數組的方式訪問這些參數的。
三,增強的for循環:     for(類型 變量名 :集合變量名){...}
四,基本數據類型的自動裝箱與拆箱:     Integer iobj = 15;-->自動裝箱,15是int型的基本數據類型,使用該語句會自動將15裝箱成一個Integer對象。     System.out.println(iobj + 20);-->自動拆箱,將iobj對象拆箱成基本數據類型然后與20相加。 注意:自動裝箱與拆箱只是在基本數據類型與基本數據類型對應的引用類型之間。一共八種基本數據類型,所以自動拆箱與裝箱也就八種情況。
 
示例:     Integer i1= 3;     Integer i2= 3;     System.out.println(i1 == i2);//true     當將同一個int類型的數通過自動裝箱封裝成兩個Integer對象后,如果這個整數小於一個字節(-128-127之間),那么在內存中這兩個Integer對象是同一個對象。原因是當將-128-127之間的整數封裝成對象后,java會把內存中這個對象緩存起來,放到緩存池,當下一次再要使用該整數對象的時候,就不需要重新在新建一個一樣對象了,可以直接拿來用,就會節省內存資源。這種模式也叫享元模式。
享元模式(flywight):當有很多個小的對象,他們有很多相同屬性,那么就把他們變成一對象,緩存起來,當下次再要使用到該對象的時候就不用。在內存中重寫創建新的對象,而直接從緩存中拿出來使用,這樣可以節約資源,這種模式成為享元模式。
五,枚舉:     枚舉就是要讓某個類型變量的取值只能是若干個固定值中的一個,否則編譯器就會報錯。比如,規定將Sunday等於0,為了防止有人寫成7,就可以使用枚舉,如果別人寫成7,那么編譯時就會出錯。作用就是:可以讓編譯器在編譯時就控制遠程序中填寫的非法值,普通變量的方式在開發階段是無法實現這一目標的。
枚舉特點:     (1)枚舉不能使用extends關鍵字,但是可以使用implement關鍵字,這樣我們就可以把不同枚舉類的公有行為提取到接口中規范枚舉的行為。     (2)永遠不能直接調用枚舉的構造函數,通過枚舉元素的變元可以調用相應的構造函數,自定義構造函數不能覆蓋默認執行的構造函數,它會在默認構造函數之后執行。枚舉的構造方法可以重載。     (3)枚舉的構造函數都是私有的。     (4)枚舉中的元素定義必須放在最上面,最后才能是變量和方法的定義。     (5)枚舉中的元素,也就是常量,默認是public static final的,所以要大寫,但是不能顯示的寫出,否則編譯出錯。     (6)創建一個枚舉類:enum{};后面的括號可有可無,不寫編譯器不會報錯。

枚舉是1.5出現的,之前如果要實現枚舉的功能,是通過普通類來實現的。 需求:使用一個普通類模擬枚舉的原理。定義一個WeekDay類,他有一個nextDay()方法,調用該對象的該方法可以獲取下一天的星期數。
abstract class WeekDay1 {
private WeekDay1() {}
//定義一個靜態常量,常量的類型是該常量所在類的類型。常量值是該類的子類實例對象
public final static WeekDay1 SUN = new WeekDay1() {
public WeekDay1 nextDay() {
return MON;
}
//重寫的toString方法
public String toString() {
return "SUN";
}
};

public final static WeekDay1 MON = new WeekDay1() {
public WeekDay1 nextDay() {
return TUE;
}

public String toString() {
return "MON";
}
};


public final static WeekDay1 TUE = new WeekDay1() {
public WeekDay1 nextDay() {
return SUN;
}
public String toString() {
return "TUE";
}
};
//抽象類的抽象方法
public abstract WeekDay1 nextDay();

//這里我們可以采用抽象方法定義nextDay(),就可以將大量的if else語句轉化成一個個獨立的對象
}
public class EnumTest
{
public static void main(String[] args) {
WeekDay1 w = WeekDay1.SUN;
System.out.println(w + "::" + w.nextDay());//結果是:SUN::MON
}
}

枚舉的定義:

public enum WeekDay {
SUN,MON(1),TUE(2,3),WEN(1,2,3),THI(),FRI,SAT;

//構造方法
private WeekDay() {System.out.println("first");}
private WeekDay(int day) {System.out.println("Second");}
private WeekDay(int day,int day2) {System.out.println("third");}
private WeekDay(int day,int day2,int day3) {System.out.println("Four");}
}

定義枚舉時要注意:        (1),枚舉也是一個類,枚舉中的元素(SUN,MON,TUE...)都是該類的實例對象。        (2),枚舉的構造函數,成員變量和成員函數必須放在元素列表(SUN,MON,TUE...)的后面。        (3),在枚舉元素后面添加括號,通過參數列表的不同可以讓不同的元素調用不同的構造函數。        (4),當枚舉的成員沒有添加括號或者只有空括號時,該元素默認調用的是枚舉無參數的構造方法。        (5),如果枚舉中存在了帶參數的構造方法,又有無括號的元素那么枚舉中必須要有無參數的構造方法,因為無括號的元素默認調用無參數的構造方法。
枚舉的實際應用:需求:定義一個交通燈的枚舉,交通燈有三種顏色,每次燈亮有一定的時間。
public enum TrafficLight {
RED(45) {
public TrafficLight nextLight() {
return GREEN;
}
},
GREEN(30) {
public TrafficLight nextLight() {
return YELLOW;
}
},
YELLOW(15) {
public TrafficLight nextLight() {
return RED;
}
};
public abstract TrafficLight nextLight();
private int time;
private TrafficLight(int time) {
this.time = time;
}
}

補充:        (1),當一個枚舉在一個類內部時(也就是內部類,因為枚舉也是一個類),可以使用四個訪問修飾符修飾,因為內部類也是類的成員,和類中的成員是一級的。外部類只有兩種訪問修飾符,public和default(默認的)。        (2),當枚舉只有一個成員的時候,就是一個單例設計模式。所以當定義單例設計模式的時候,可以使用枚舉來定義。 

6,泛型        jdk1.5出現的泛型是新特性當中最難的一個,也是十分重要的一個新特性。        如果不出現泛型,那么任何一個任何類型對象,都可以存儲到集合中,而出現泛型以后就將集合中的元素限定為一種特定類型,這樣更安全,當從集合中取出元素的時候,程序也知道取出的元素是什么類型,這樣就不需要將元素進行強制類型轉換了。如果不使用泛型,那么編譯器就會給出一個警告unchecked。
    泛型是給編譯器使用的,可以限定集合中存儲的元素的類型,擋住用戶向集合中輸入泛型以外的類型,當程序運行時,這些泛型信息就沒有了。也就是使用getClass()得到的字節碼文件里面是沒有泛型信息的。可以使用下面的程序檢測。    ArrayList<String> list1 = new ArrayList<String>();    list1.add("abc");    ArrayList<Integer> list2 = new ArrayList<Integer>();    list2.add(99);    System.out.println(list1.getClass() == list2.getClass());//結果是true,說明這兩個集合指向的是同一份字節碼文件
通過上述原理我們就可以知道,只要跳過編譯器,即使是在編譯期間定義了集合的泛型,在運行期間還是可以加入泛型以外的元素的。方法:    ArrayList<Integer> list2 = new ArrayList<Integer>();    list2.add(99);    //使用反射調用list2的add方法,注意第二個參數是Object.class    list2.getClass().getMethod("add", Object.class).invoke(list2, "defghijkl");    System.out.println(list2);//[99, defghijkl]
使用泛型要注意的幾點:    <1>,ArrayList<E>和ArrayList<Integer>的幾個重要術語:        (1)ArrayList<E>稱為泛型類型        (2)ArrayList<E>中的E稱為類型變量或類型參數        (3)ArrayList<Integer>稱為參數化的類型        (4)ArrayList<Integer>中的Integer稱為類型參數的示例,或參數的實際類型        (5)ArrayList稱為原始類型
    <2>,參數化類型與原始類型的兼容性:        (1)參數化類型可以引用一個原始類型:Collection<String> c = new Vector();        (2)原始類型可以引用一個參數化類型的對象:Collection  c = new Vector<String>();
        這兩中形式都有警告。

    <3>,參數化類型是不考慮類型參數的繼承關系:        Vector<String> v = new Vector<Object>();//錯誤        Vector<Object> v = new Vector<>(String);//錯誤
    <4>,創建數組實例時,數組元素不能使用參數化的類型:Vector<String> v[] = new Vector<String>()[];//錯誤
    <5>,思考:Vector v = new Vector<String>();        Vector<Object> v2 = v;        這個在編譯時是不會報錯的,原因就是因為泛型是給編譯器使用的,編譯器在編譯代碼時是一行一行掃描的,從注意的第二點可以看到這兩種形式都是正確的。運行時,才是執行代碼,不能以執行代碼的方式看着寫代碼。

    泛型通配符的使用:        因為泛型不存在繼承關系,所以當一個方法接收的參數是一個泛型類型的集合應該怎么做呢?這時候就要用到通配符?        public void printCollection(Collection<?> coll);        該方法表示接受的Collection類型的參數是任意類型,不能使用Collection<Object> coll,這是錯誤的。        coll就可以接受Collection<String>,Cololection<Integer>...
        只要是使用了泛型通配符的集合,?就可以引用任意類型的參數化類型,可以調用與參數化無關的方法,而不能調用與參數化有關的方法,比如Collection的add()方法是不能調用的,但是size()方法可以調用,因為與參數類型無關。
public void printCollection(Collection<?> coll) {
coll.add();//錯誤
coll.size();//正確
coll = new HashSet<Date>();//正確
}
public void printCollection(Collection<Object> coll) {
coll.add();//正確
coll = new HashSet<String>();//錯誤,泛型不存在繼承
}

泛型通配符的擴展:
限定通配符的上邊界:
Vector<? extends Number> v = new Vector<Integer>();
限定通配符的下邊界:
Vector<? super Integer> v = new Vector<Number>();

泛型的一個應用:對Map集合中的元素進行迭代:

Map<String,Integer> map = new HashMap<>();
map.put("abc",20);
map.put("def",21);
map.put("ghi",22);
for(Map.Entry<String,Integer> entry : map.entrySet) {
String key = entry.getKey();
int value = entry.getValue();
}

最簡單的自定義泛型的使用:        除了在使用方法時會出現泛型,通配符,限界等,還可以自定義泛型,使用通配符。在自定義方法上定義泛型時,泛型類型T要在返回值類型前面並且是緊挨着返回類型的說明這種類型。比如自定義一個交換數組元素位置的方法:
public static <T> void swap(T[] arr,int i,int j) {
T temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}

這里要注意要說明T類型,在void前,緊挨着void的。T可以使任意類型,arr是任意類型的數組,交換arr數組的i和j位置的元素。    普通方法,構造方法和靜態方法都可以使用泛型。
    泛型中可以同時有多個參數,泛型的參數之間用逗號隔開,比如Map集合,Map<K,V>.也可以使用類型變量表示異常,稱為參數化的異常,可以用於方法的throws列表中,但是不能用於catch子句中。
private <? extends Exception> void sayHello() throws T {
try
{

}
catch (Exception e)
{
throw (T)e;
}
}

泛型的練習:

//將Object類型的對象轉換成其他類型
public static <T> T swapObject(Object obj) {
return (T)obj;
}

//將任意類型的數組中的所有元素填充為相應類型的某個對象
public static <V> void fill(V[] a,V obj) {
for(int i=0;i<a.length;i++) {
a[i] = obj;
}
}

//打印出任意參數化類型的集合中的所有元素
public static <T> void print2(Collection<T> coll) {
for(T a : coll) {
System.out.println(a);
}
}
//把任意參數類型的集合中的數據安全的復制到相應類型的數組中
public static <T> void copy(Collection<T> coll,T[] arr) {
int i = 0;
Iterator<T> it = coll.iterator();
while(it.hasNext()) {
arr[i] = it.next();
i++;
}
}
//把任意參數類型的一個數組中的數據安全的復制到相應類型的另一個數組中
public static <T> void copy2(T[] src,T[] desk) {
for(int i=0;i<src.length();i++) {
deski[i] = src[i];
}
}

定義類級別的泛型:    當類中的多個方法使用到了同一個泛型類型,這時候可以將泛型定義成類類型的泛型。
    定義類類型的泛型還要注意,當類中要定義靜態方法時,靜態方法是不能使用類類型的泛型的,但是可以單獨給靜態方法定義一個泛型,相互之間不影響。如果泛型定義在方法上時,多個方法的泛型之間是相互獨立的。比如上面的示例中定義的泛型,相互之間的T是
各不相同的。
    對數據庫的操作其實就是對數據庫的數據進行增刪改查的,專業術語是:CRUD    當看到要定義一個Dao類時,就說明是要定義一個CRUD的類,DAO--->Data Access Object    定義Dao就是定義一個包含CRUD的類。
一個簡單的Dao,包括CRUD。

public class GenericDao<T> {
public void add(Object obj) {

}
public T findId(int id) {
return null;
}
public void delete(T obj) {

}
public void delete(int id) {

}
public void update(T obj) {

}
//這里的E類型是和類的泛型是沒關系的,和類泛型同名時不沖突的
public static <T> void update2(T obj) {

}
public Set<T> findByCondition(String where) {
return null;
}
public T findByUesrName(String name) {
return null;
}
}

泛型的高級應用:通過反射獲取泛型實際參數類型        比如:public static void void appolyPara(Vector<Date> v):        該怎樣通過反射獲取Vector的泛型類型呢?
public class GenericTest2 {
public static void main(String[] args) throws Exception {
//使用反射獲取方法
Method genMethod = GenericTest2.class.getMethod("applyPara", Vector.class);
//獲取泛型參數的實際類型參數列表
Type[] types = genMethod.getGenericParameterTypes();
//因為參數類型列表只有一個元素,直接獲取
ParameterizedType pType = (ParameterizedType) types[0];
System.out.println(pType.getRawType());//打印方法的參數類型
System.out.println(pType.getActualTypeArguments()[0]);//打印方法參數的泛型類型
}
//要獲取的方法
public static void applyPara(Vector<Date> v) {
}
}

 結果:class java.util.Vectorclass java.util.Date 





注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com