黑馬程序員 java高新技術--java5的註解、java5的泛型

                                                                   -------android培訓java培訓java學習型技術博客、期待與您交流! ----------

  

                                                                            知識點五   java5的註解

一、概述:

1、註解相當於一種標記,在程序中加了註解就等於爲程序打上了某種標記,沒加,則沒有某種標記。

2、以後,java編譯器、開發工具和其他應用程序就可以用反射來了解自己的類及各種元素上有無何種標記,有什麼標記,就會做出相應的處理。

3、標記可以加在包、類、字段、方法、方法參數,以及局部變量上等等。

4、在java.lang包中提供了最基本的annotation,即註解。

5、格式:@註解類名()。如果有屬性,則在括號中加上屬性名(可省略)和屬性值。

二、java中三種最基本的註解:

1、@SuppressWarning(”deprecation”)--->壓制警告

SupressWarning是告知編譯器或開發工具等提示指定的編譯器警告;

”deprecation”是告知具體的信息即方法已過時。

通過System.runFinalizersOnExit(true);的編譯警告引出@SuppressWarnings("deprecation") 

2、@Deprecated--->提示成員等已經過時,不再推薦使用。

源代碼標記@Deprecated是在JDK1.5中作爲內置的annotation引入的,用於表明類(class)、方法(method)、字段(field)已經不再推薦使用,並且在以後的JDK版本中可能將其刪除,編譯器在默認情況下檢測到有此標記的時候會提示警告信息。

直接在剛纔的類中增加一個方法,並加上@Deprecated標註,在另外一個類中調用這個方法。

例如:假定之前的某個類升級了,其中的某個方法已經過時了,不能夠將過時的方法刪除,因爲可能會影響到調用此類的這個方法的某些程序,這是就可以通過在方法上加這個註解。

3、@Override--->提示覆蓋(父類方法)

加上此註解,,可對自己類中的方法判斷是否是要覆蓋的父類的方法,典型的例子即在集合中覆蓋equals(Object obj)方法,其中的參數類型必須是Object,才能被覆蓋,若不是,加上此註解就會提示警告。

例如:public boolean equals(Reflect other)方法與HashSet結合

三、註釋的應用--->註解類:

1、定義格式:@interface 名稱{statement}

2、元註解(註解的註解)

一個註解有其生命週期(Retetion)和存放的位置(Taget),這就可以通過元註解說明。

1)Retetion:用於說明註解保留在哪個時期,加載定義的註解之上。

①一個註解的聲明週期包含:

java源程序--(javac)-->class文件--(類加載器)-->內存中的字節碼

第一、當再源程序上加了註解,javac將java源程序編譯爲class文件,可能會把源程序中的一些註解去掉,進行相應的處理操作,當我們拿到源程序的時候,就看不到這些註解了。

第二、假設javac把這些註解留在了源程序中(或者說留在了class文件中),當運行此class文件的時候,用類加載器將class文件調入內存中,此時有轉換的過程,即把class文件中的註解是否保留下來也不一定。

注意:class文件中不是字節碼,只有把class文件中的內部加載進內存,用類加載器加載處理後(進行完整的檢查等處理),最終得到的二進制內容纔是字節碼。

②Reteton(枚舉類)取值:

Retetion.Policy.SOURSE:java源文件時期,如@Overried和@SuppressWarning

Retetion.Policy.CLASS: class文件時期(默認階段)

Retetion.Policy.RUNTIME:運行時期,如@Deprecated

2)Taget:用於說明註解存放在哪些成分上,默認值是任何元素

其值可設置爲枚舉類ElementType類中的任何一個,包括:包、字段、方法、方法參數、構造器、類等值。取值爲:

PACKAGE(包聲明)

FIELD(字段聲明)

ANNOTATION_TYPE(註釋類型聲明)

CONSIRUCTOR(構造器聲明)

METHOD(方法聲明)

PARAMETER(參數聲明)

TYPE(類、接口(包含註釋類型)或枚舉聲明)

LOCAL_VARIABLE(局部變量聲明)

注意:其中代表類的值是TYPE。因爲class、enum、interface和@interface等都是屬於Type的。不可用CLASS表示。

3、通過反射查看其它類中的註釋:

過程:

第一、註解類:@interfaceA{}

第二、應用了“註釋類”的類:@Aclass B{}

第三、對“應用註釋類的類”進行反射操作的類:class{...},操作如下:

B.class.isAnnotionPresent(A.class);//判斷是否存在此註解類

A a = B.class.getAnnotation(a.class);//存在的話則得到這個註釋類的對象

註解的應用結構圖:


示例:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface ItcastAnnotation {}

@ItcastAnnotation()
public class AnnotionTest {
	@SuppressWarnings("deprecation")//表示壓制警告的註解
	@ItcastAnnotation()
	public static void main(String[] args) {
		System.runFinalizersOnExit(true);
		//反射方式查看註解
		//檢查類上是否有註解
		if(AnnotionTest.class.isAnnotationPresent(ItcastAnnotation.class)){
			//通過反射獲取到註解
			ItcastAnnotation annotation = AnnotionTest.class.getAnnotation(ItcastAnnotation.class);
			System.out.println(annotation);
		}
	}
	
四、自定義註解及其應用

1)定義一個最簡單的註解:public @interface MyAnnotation {}
2)把它加在某個類上:@MyAnnotation public class AnnotationTest{}
3)用反射進行測試AnnotationTest的定義上是否有@MyAnnotation 
4)根據發射測試的問題,引出@Retention元註解的講解,其三種取值:RetetionPolicy.SOURCE、RetetionPolicy.CLASS、RetetionPolicy.RUNTIME;分別對應:java源文件-->class文件-->內存中的字節碼。
思考:@Override、@SuppressWarnings和@Deprecated這三個註解的屬性值分別是什麼?
5)@Target元註解
Target的默認值爲任何元素,設置Target等於ElementType.METHOD,原來加在類上的註解就報錯了,改爲用數組方式設置{ElementType.METHOD,ElementType.TYPE}就可以了。
元註解以及其枚舉屬性值不用記,只要會看jdk提供那幾個基本註解的API幫助文檔的定義或其源代碼,按圖索驥即可查到,或者直接看java.lang.annotation包下面的類。

五、爲註解增加基本屬性

1、屬性:

一個註解相當於一個胸牌,如果你胸前貼了胸牌,就是傳智播客的學生,否則,就不是。如果還想區分出是傳智播客哪個班的學生,這時候可以爲胸牌在增加一個屬性來進行區分。加了屬性的標記效果爲:@MyAnnotation(color="red")

2、定義格式:同接口中的方法一樣:String color();

定義缺省格式:Stringvalue() default ”ignal”;

定義基本類型的屬性和應用屬性:
在註解類中增加String color();
@MyAnnotation(color="red")

3、應用:直接在註解的括號中添加自身的屬性,如:

@ItcastAnnotation(color=”red”)

這個和上面的@SuppressWarnings("deprecation")是一樣的,其中的"deprecation"就是屬性值

1)當只有一個屬性時,可直接傳入屬性值。如”red”

2)當含有其他屬性值的時候,如果那個屬性值是缺省的(default),也可以直接傳入這個屬性值。

4.用反射方式獲得註解對應的實例對象後,再通過該對象調用屬性對應的方法
MyAnnotation a = (MyAnnotation)AnnotationTest.class.getAnnotation(MyAnnotation.class);
System.out.println(a.color());
可以認爲上面這個@MyAnnotation是MyAnnotaion類的一個實例對象

5.爲屬性指定缺省值:
String color() default "yellow";
value屬性:
String value() default "zxx"; 
如果註解中有一個名稱爲value的屬性,且你只想設置value屬性(即其他屬性都採用默認值或者你只有一個value屬性),那麼可以省略value=部分,例如:@MyAnnotation("lhm")。

六、爲註解增加高級屬性

1、可以爲註解增加的高級屬性的返回值類型有:

1)八種基本數據類型   2)String類型  3)Class類型

4)枚舉類型   5)註解類型   6)前五種類型的數組

2、數組類型的屬性:

定義:int[]arrayArr() default {1,2,3};     -->可不定義默認值

應用:@MyAnnotation(arrayArr={2,3,4})  --> 可重新賦值

注:若數組屬性中只有一個元素(或重新賦值爲一個元素),這時屬性值部分可省略大括號。

3、枚舉類型的屬性:

假設定義了一個枚舉類TraffLamp,它是EnumTest的內部類,其值是交通燈的三色。

定義:EnumTest.TrafficLamplamp();

應用:@MyAnnotation(lamp=EnumTestTrafficLamp.GREEN)

4、註解類型的屬性:

假定有個註解類:MetaAnnotation,其中定義了一個屬性:String value()

定義:MetaAnnotationannotation() default @MetaAnnotation(”xxx”);

應用:@MyAnnotation(annotation=@MetaAnnotation(”yyy”))  --> 可重新賦值

可認爲上面的@MetaAnnotation是MyAnnotation類的一個實例對象,同樣可以認爲上面的@MetaAnnotation是MetaAnnotation類的一個實例對象,調用:

MetaAnnotation ma =MyAnnotation.annotation();

System.out.println(ma.value());

5、Class類型的屬性:

定義:Class cls();

應用:@MyAnnotation(cls=ItcastAnnotion.class)

注:這裏的.class必須是已定義的類,或是已有的字節碼對象

7、基本數據類型的屬性(以int爲例):

定義:int val()default 3;     -->可不定義默認值

應用:@MyAnnotation(val=7)  --> 可重新賦值

8、註解的詳細語法可通過查看java語言規範瞭解即javaLanguage Specification

示例:

//自定義註解類
package cn.itcast.text2;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import cn.itcast.text1.EnumText;
//將定義的註解的生命週期設置在運行時期
@Retention(RetentionPolicy.RUNTIME)
//定義註解的放置位置
@Target({ElementType.TYPE,ElementType.METHOD})
//自定義註解
public @interface ItcastAnnotation {
	//定義屬性
	String str();
	int val() default 1;
	int[] arr() default {2,3,4};
	Class cls() default AnnotionTest.class;
	EnumText.TrafficLamp lamp() default EnumText.TrafficLamp.YELLOW;
	MetaAnnotation annotation() default @MetaAnnotation("sss");
}

//測試註解類,用反射查看其屬性
package cn.itcast.text2;
import cn.itcast.text1.EnumText;
@ItcastAnnotation(annotation=@MetaAnnotation("anntation"),
				Lamp=EnumText.TrafficLamp.RED,
				arr=7,val=5,str="String",
				cls=ItcastAnnotation.class)
public class AnnotionTest {
	@SuppressWarnings("deprecation")//表示壓制警告的註解
	@ItcastAnnotation(str = "yyy")//有缺省值可不用寫缺省部分
	public static void main(String[] args) {
		//反射方式查看註解
		//檢查類上是否有註解
		if(AnnotionTest.class.isAnnotationPresent(ItcastAnnotation.class)){
			//通過反射獲取到註解
			ItcastAnnotation annotation = AnnotionTest.class.getAnnotation(ItcastAnnotation.class);
			//打印查看屬性值
			System.out.println(annotation);
			System.out.println(annotation.str());
			System.out.println(annotation.val());
			System.out.println(annotation.arr().length);
			System.out.println(annotation.cls().getName());
			System.out.println(annotation.lamp().nextLamp());
			System.out.println(annotation.annotation().value());
		}
	}
}

//定義枚舉類,交通燈
package cn.itcast.text1;
public class EnumText {
	public static void main(String[] args) {}
	//定義交通燈
	public enum TrafficLamp{
		//定義3個元素,即此類的子類,覆寫抽象方法
		RED(30){
			@Override
			public TrafficLamp nextLamp() {return GREEN;}},
		GREEN(45){
			@Override
			public TrafficLamp nextLamp() {return YELLOW;}},
		YELLOW(5) {
			@Override
			public TrafficLamp nextLamp() {return RED;}};
		private int time;
		//構造方法
		private TrafficLamp(int time){this.time = time;}
		//抽象方法,轉爲下個燈
		public abstract TrafficLamp nextLamp();
	}
}
補充:

①枚舉和註解都是特殊的類,不能用new 創建它們的實例對象,創建枚舉的實例對象就是在其中增加元素。
在程序中如何創建出一個註解的實例對象啊?直接用@放上一個標記即可
MetaAnnotation註解的定義:
public @interface MetaAnnotation {
   String value();
}
②看java語言規範:
打開j3toc.html頁面後,搜索annotation,進入9.6節,在其中的第5個Discussion部分就有屬性類型的講解說明,可以知道枚舉的屬性類型包括:基本數據類型,String,Class,枚舉,其他註解,以及這些類型的數組。


                                         知識點六   java5的泛型

一 泛型概述

一、泛型的引出

1.Jdk 1.5以前的集合類中存在什麼問題

ArrayList collection = new ArrayList();
collection.add(1);
collection.add(1L);
collection.add("abc");
int i = (Integer) collection.get(1);//編譯要強制類型轉換且運行時出錯!
2.Jdk 1.5的集合類希望你在定義集合時,明確表示你要向集合中裝哪種類型的數據,無法加入指定類型以外的數據
ArrayList<Integer> collection2 = new ArrayList<Integer>();
collection2.add(1);
/*collection2.add(1L);
collection2.add(“abc”);*///這兩行代碼編譯時就報告了語法錯誤
int i2 = collection2.get(0);//不需要再進行類型轉換
3.泛型是提供給javac編譯器使用的,可以限定集合中的輸入類型,讓編譯器擋住源程序中的非法輸入,編譯器編譯帶類型說明的集合時會去除掉“類型”信息,使程序運行效率不受影響,對於參數化的泛型類型,getClass()方法的返回值和原始類型完全一樣。由於編譯生成的字節碼會去掉泛型的類型信息,只要能跳過編譯器,就可以往某個泛型集合中加入其它類型的數據,例如,用反射得到集合,再調用其add方法即可。

二、使用泛型的好處:

1、使用泛型集合,可將一個集合中的元素限定爲一個特定類型,集合中只能存儲同一個類型的對象;這樣就將運行時期出現的問題ClassCastException轉移到了編譯時期,方便與程序員解決問題,讓運行時期問題減少,提高安全性。

2、當從集合中獲取一個對象時,編譯器也可知道這個對象的類型,不需要對對象進行強制轉化,避免了強制轉換的麻煩,這樣更方便。

三、泛型格式:

     通過<>來定義要操作的引用數據類型

     如:ArrayList<String>     -----> 來定義要存入集合中的元素指定爲String類型

四、ArrayList<E>類定義和ArrayList<Integer>類引用中涉及如下術語:
整個稱爲ArrayList<E>泛型類型
ArrayList<E>中的E稱爲類型變量或類型參數
整個ArrayList<Integer>稱爲參數化的類型
ArrayList<Integer>中的Integer稱爲類型參數的實例或實際類型參數
ArrayList<Integer>中的<>念着typeof
ArrayList稱爲原始類型

參數化:parametered,已經將參數變爲實際類型的狀態。
五、參數化類型與原始類型的兼容性:
參數化類型可以引用一個原始類型的對象,編譯報告警告,例如, Collection<String> c = new Vector();//可不可以,不就是編譯器一句話的事嗎?
原始類型可以引用一個參數化類型的對象,編譯報告警告,例如, Collection c = new Vector<String>();//原來的方法接受一個集合參數,新的類型也要能傳進去
六、參數化類型不考慮類型參數的繼承關係:
Vector<String> v = new Vector<Object>(); //錯誤!///不寫<Object>沒錯,寫了就是明知故犯
Vector<Object> v = new Vector<String>(); //也錯誤!

解釋:泛型中的類型參數嚴格說明集合中裝載的數據類型是什麼和可以加入什麼類型的數據,記住:Collection<String>和Collection<Object>是兩個沒有轉換關係的參數化的類型。
  假設Vector<String> v = new Vector<Object>();可以的話,那麼以後從v中取出的對象當作String用,而v實際指向的對象中可以加入任意的類型對象;假設Vector<Object> v = new Vector<String>();可以的話,那麼以後可以向v中加入任意的類型對象,而v實際指向的集合中只能裝String類型的對象。
舉例:咱們90的跟70的談事,可以不按90的規則走,但是跟90談,咱們必須按90的套路來。
七、編譯器不允許創建泛型變量的數組。即在創建數組實例時,數組的元素不能使用參數化的類型,例如,下面語句有錯誤:
  Vector<Integer> vectorList[] = new Vector<Integer>[10];

思考題:下面的代碼會報錯誤嗎?

Vector v1 = new Vector<String>(); 
Vector<Object> v = v1;

答:這兩句代碼不會報錯,因爲參數化類型與原始類型相互兼容,應該按照java編譯方式來推,而不應該以java執行的代碼來推;

示例:

import java.lang.reflect.Constructor;
import java.util.*;

public class Generic {
	public static void main(String[] args) throws Exception {
		ArrayList<String> al = new ArrayList<String>();
		al.add("25");
		al.add("b");
		System.out.println(al.get(1));
		
		ArrayList<Integer> at = new ArrayList<Integer>();
		at.add(23);
		at.add(3);
		System.out.println(at.get(1));
		//編譯器生成的字節碼會去掉泛型的類型信息
		System.out.println((al.getClass() == at.getClass()) + 
							"-->" + at.getClass().getName());
		
		//at.add("ab")-->報錯,存儲的應爲Integer類型 
		//反射方式,由於編譯器生成的字節碼會去掉泛型的類型信息,
		//所以用反射可跳過編譯器,存入任何類型
		at.getClass().getMethod("add",Object.class).invoke(at,"abcd");
		at.getClass().getMethod("add",Object.class).invoke(at,5);
		System.out.println("反射方式:" + at.get(3));
		System.out.println("反射方式:" + at.get(4));
		
		//反射方式獲得new String(new StringBuffer("abc"));
		Constructor<String> cons = String.class.getConstructor(StringBuffer.class);
		String st = cons.newInstance(new StringBuffer("abc"));
		System.out.println(st);
 

二  泛型中的?通配符


一、問題:
定義一個方法,該方法用於打印出任意參數化類型的集合中的所有數據,該方法如何定義呢?
錯誤方式:
public static void printCollection(Collection<Object> cols) {
for(Object obj:cols) {
System.out.println(obj);
}
/* cols.add("string");//沒錯
cols = new HashSet<Date>();//會報告錯誤!*/
}
正確方式:
public static void printCollection(Collection<?> cols) {
for(Object obj:cols) {
System.out.println(obj);
}
//cols.add("string");//錯誤,因爲它不知自己未來匹配就一定是String
cols.size();//沒錯,此方法與類型參數沒有關係
cols = new HashSet<Date>();
}
二、總結:
1.使用?通配符可以引用其他各種參數化的類型,?通配符定義的變量主要用作引用,可以調用與參數化無關的方法,不能調用與參數化有關的方法。

2.當傳入的類型不確定時,可以使用通配符?

3.可對通配符變量賦任意值:

如:Collection<?> colls---> cols = newHashSet<Date>();

Cols<Object> 中的Object只是說明Cols<Object> 實例對象中的方法接受的參數是Object

Cols<Object> 是一種具體類型,new HashSet<Date>也是一種具體類型,兩者沒有兼容性問題。

注意:

ollection<?>  a可以與任意參數化的類型匹配,但到底匹配的是什麼類型,只有以後才知道,所以,
a=new ArrayList<Integer>和a=new ArrayList<String>都可以, 但a.add(new Date())或a.add(“abc”)都不行

示例:

public static void printObj(Collection<?> coll){
	//coll.add(1);是錯誤的,如果傳入的是String類型,就不符合了
	for(Object obj : coll){
		System.out.println(obj);
	}
}

import java.util.*; 
class GenerticDemo  
{  
    public static void main(String[] args)   
    {  
        ArrayList<String> p = new ArrayList<String>();  
        p.add("per20");  
        p.add("per11");  
        p.add("per52");  
        print(p);  
        ArrayList<Integer> s = new ArrayList<Integer>();  
        s.add(new Integer(4));  
        s.add(new Integer(7));  
        s.add(new Integer(1));  
        print(s);  
    }  
  
    public static void print(ArrayList<?> al) {  
        Iterator<?> it = al.listIterator();  
        while (it.hasNext()) {
            System.out.println(it.next());  
        }  
    }  
}  

三、泛型中的?通配符的擴展

1.限定通配符的上邊界:
正確:Vector<? extends Number> x = new Vector<Integer>();
錯誤:Vector<? extends Number> x = new Vector<String>();
2.限定通配符的下邊界:
正確:Vector<? super Integer> x = new Vector<Number>();
錯誤:Vector<? super Integer> x = new Vector<Byte>();
提示:
3.限定通配符總是包括自己。
?只能用作引用,不能用它去給其他變量賦值
Vector<? extends Number> y = new Vector<Integer>();
Vector<Number> x = y;
上面的代碼錯誤,原理與Vector<Object > x11 = new Vector<String>();相似,
只能通過強制類型轉換方式來賦值。

示例:

/* 
泛型的限定: 
 
*/  
import java.util.*;  
class GenerticXian2  
{  
    public static void main(String[] args)   
    {  
          
        TreeSet<Student> s = new TreeSet<Student>(new Comp());  
        s.add(new Student("stu0"));  
        s.add(new Student("stu3"));  
        s.add(new Student("stu1"));  
        print(s);  
        System.out.println("Hello World!");  
        TreeSet<Worker> w = new TreeSet<Worker>(new Comp());  
        w.add(new Worker("Worker0"));  
        w.add(new Worker("Worker3"));  
        w.add(new Worker("Worker1"));  
        print(w);  
    }  
  
    public static void print(TreeSet<? extends Person> ts) {  
        Iterator<? extends Person> it = ts.iterator();  
        while (it.hasNext()){  
            Person p = it.next();  
            System.out.println(p.getName());  
        }  
    }  
}  
  
class Person implements Comparable<Person>  {  
    private String name;  
    Person(String name) {  
        this.name = name;  
    }  
    public String getName() {  
        return name;  
    }  
    public int compareTo(Person p){  
        return this.getName().compareTo(p.getName());  
    }  
}  
class Comp implements Comparator<Person> {  
    public int compare(Person p1,Person p2){  
        return p1.getName().compareTo(p2.getName());  
    }  
}  
class Student extends Person {  
    Student(String name){  
        super(name);  
    }  
}  
  
class Worker extends Person {  
    Worker(String name){  
        super(name);  
    }  
}  

三   泛型方法

一、java中泛型方法的定義:

1、何時定義泛型方法:爲了讓不同方法可以操作不同的類型,而且類型不確定,那麼就可以定義泛型方法

2、特殊之處:靜態方法不可以訪問類上定義的泛型,如果靜態方法操作的引用數據類型不確定,可以將泛型定義在方法上

private static <T> T add(T a, T b){
		......
		return null;
}


                add(3,5);//自動裝箱和拆箱
		Number x1 = add(3.5,5);//取兩個數的交集類型Number
		Object x2 = add(3,"abc");//去最大交集爲Object
二、泛型方法的特點:

1、位置:用於放置泛型的類型參數的<>應出現在方法的其他所有修飾符之後和在方法的返回類型之前,也就是緊鄰返回值之前,按照慣例,類型參數通常用單個大寫字母表示。

2、只有引用類型才能作爲泛型方法的實際參數

3、除了在應用泛型時可以使用extends限定符,在定義泛型時也可以使用extends限定符。

4、普通方法、構造函數和靜態方法中都可以使用泛型。

5、可以用類型變量表示異常,稱之爲參數化的異常,可用於方法的throws列表中,但是不能用於catch子句中。

6、在泛型中可同時有多個類型參數,在定義它們的<>中用逗號分開。

	public static <K,V> V getValue(K key){
		Map<K, V> map = new HashMap<K, V>();
		return map.get(key);
	}
	private static <T extends Exception> void sayHello() throws T{
		try{}
		catch(Exception e){
			throw (T)e;
		}
	}

三、這個T和?有什麼區別呢?

1、T限定了類型,傳入什麼類型即爲什麼類型,可以定義變量,接收賦值的內容。

2、?爲通配符,也可以接收任意類型但是不可以定義變量。

但是這樣定義,雖然提高了擴展性,可還是有一個侷限性,就是不能使用其他類對象的特有方法。

3、總結:

通配符方案要比泛型方法更有效,當一個類型變量用來表達兩個參數之間或參數和返回值之間的關係時,即同一個類型變量在方法簽名的兩處被使用,或者類型變量在方法體代碼中也被使用,而不是僅在簽名的時候使用,才需要使用泛型方法。

四 泛型類

一、概述:

1、若類實例對象中多出要使用到同一泛型參數,即這些地方引用類型要保持同一個實際類型時,這時候就要採用泛型類型的方式進行定義,也就是類級別的泛型。

2、何時定義泛型類:當類中要操作的引用數據類型不確定時,在早期定義Object來完成擴展,而現在定義泛型。

3、泛型類定義的泛型,在整個類中都有效,如果被方法調用,那麼泛型類的對象要明確需要操作的具體類型後,所有要操作的類就已經固定了。

4、類級別的泛型是根據引用該類名時指定的類型信息來參數化類型變量的。

二、語法格式:

1、定義

public class GenerDao1<T>{
	private T field;
	public void save(T obj){}
	public T getByteId(int Id){}
}

2、舉例:

擴展:Dao:Data Access Object--->數據訪問對象。

DAO通常位於數據持久層,對數據庫中的數據進行操作。

對其操作:crud即增上刪改查

c:creat,創建、增加;     r:read,讀取、查詢;

u:update,更新、修改    d:delete,刪除。

對javaEE的理解:13種技術。簡單說就是對數據庫的增刪改查。

寫Dao類有五個基本方法:增刪改查,其中查包含查單個和對同類型集合的查詢,如同性別或同地區的集合獲取。

package cn.itcast.text2;
import java.util.*;
public class GenerticDao<T> {
	public static <E> void staMethod(E e){}
	public void add(T obj){}
	public boolean delete(T obj){
		return true;
	}
	public boolean delete(int id){
		return true;
	}
	public T update(T obj){
		return null;
	}
	public T findByUserName(String name){
		return null;
	}
	public Set<T> findByPlace(String place){
		Set<T> set = new TreeSet<T>();
		//....
		return set;
	}
}

三、注意:

1、在對泛型進行參數化時,類型參數的實例必須是引用類型,不能是基本類型。

2、當一個變量被聲明爲參數時,只能被實例變量和方法調用(還有內嵌類型),而不能被靜態變量和靜態方法調用,因爲靜態成員是被所有參數化的類共享的,所以靜態成員不應該有類級別的類型參數。

 總結:

對泛型的定義:

第一、定義泛型:當又不確定的類型需要傳入到集合中,需要定義泛型

第二、定義泛型類:如果類型確定後,所操作的方法都是屬於此類型,則定義泛型類

第三、定義泛型方法:如果定義的方法確定了,裏面所操作的類型不確定,則定義泛型方法

示例:

class GenerticTest  {  
    public static void main(String[] args) {  
        //創建泛型類對象  
        GenClass<Worker> g = new GenClass<Worker> ();  
        g.setTT(new Worker());  
        Worker w =  g.getTT();  
        g.showC(w);  
        System.out.println("----------------------");  
	        //泛型方法測試  
	        GenMethod<String> g1 = new GenMethod<String>();  
	        GenMethod.showS("SSS");  
	        g1.show("sesf");  
	        g1.print("heheh");  
	        g1.printY(new Integer(5));  
	        System.out.println("------------------------");  
	        //泛型接口測試  
	        GenInter g2 = new GenInter();  
	        g2.show("haha");  
	        System.out.println("Hello World!");  
	        GenImpl<Integer> g3 = new GenImpl<Integer>();  
	        g3.show(new Integer(95));  
	    }  
	}  
	//泛型類  
	class GenClass<TT>  {  
	    //定義私有屬性  
	    private TT t;  
	    //定義公共設置方法,設置屬性  
	    public void setTT(TT t) {  
	        this.t = t;  
	    }  
	    //定義公共訪問方法,訪問屬性  
	    public TT getTT() {  
	        return t;  
	    }  
	    //定義方法  
	    public void showC(TT t) {  
	        System.out.println("GenClass show:" + t);  
	    }  
	}  
	//創建Worker類,作爲類型傳入泛型類中  
	class Worker {}  
	//泛型方法  
	class GenMethod<T> {  
	    //靜態的泛型方法  
	    public static <S> void showS(S s) {
	        System.out.println("static show:" + s);  
	    }  
	    //非靜態泛型方法  
	    public void show(T t) {  
	        System.out.println("未指定T show:" + t);  
	    }  
	    public void print(T t) {  
	        System.out.println("指定T print:" + t);  
	    }  
	    //指定接受其他類型的泛型方法  
	    public <Y> void printY(Y y) {  
	        System.out.println("和類指定的不同,爲Y print:" + y);  
	    }  
	 }  
	//泛型接口  
	  
	interface Inter<T> {  
	    void show(T t);  
	}  
	//一般類實現泛型接口  
	class GenInter implements Inter<String> {  
	    public void show(String s) {  
	        System.out.println("接口 show:" + s);  
	    }  
	}  
	//泛型類實現泛型接口  
	class GenImpl<T> implements Inter<T> {  
	    public void show(T t) {  
	        System.out.println("類接收類型不確定的實現接口 show:" + t);  
	}  
四、泛型集合類

能寫出下面的代碼即代表掌握了Java的泛型集合類:

 HashMap<String,Integer> hm = new HashMap<String,Integer>();
  hm.put("zxx",19);
  hm.put("lis",18);
  
  Set<Map.Entry<String,Integer>> mes= hm.entrySet();
  for(Map.Entry<String,Integer> me : mes) {
   System.out.println(me.getKey() + ":" + me.getValue());
  }
對在jsp頁面中也經常要對Set或Map集合進行迭代:
<c:forEach items=“${map}” var=“entry”>
	${entry.key}:${entry.value}
</c:forEach>   

五  類型參數的類型推斷

一、概述:

1、定義:編譯器判斷泛型方法的實際參數的過程,稱之爲類型推斷。

2、類型推斷是相對於直覺推斷的,其實現方法是一種非常複雜的過程。

二、類型推斷的具體規則:

根據調用泛型方法時,實際傳遞的參數類型或返回值的類型來推斷。

具體規則如下:
1.當某個類型變量只在整個參數列表中的所有參數和返回值中的一處被應用了,那麼根據調用方法時該處的實際應用類型來確定,這很容易憑着感覺推斷出來,即直接根據調用方法時傳遞的參數類型或返回值來決定泛型參數的類型,例如:
swap(new String[3],3,4)  -->    static <E> void swap(E[] a, int i, int j)
2.當某個類型變量在整個參數列表中的所有參數和返回值中的多處被應用了,如果調用方法時這多處的實際應用類型都對應同一種類型來確定,這很容易憑着感覺推斷出來,例如:
add(3,5)   --> static <T> T add(T a, T b) 
3.當某個類型變量在整個參數列表中的所有參數和返回值中的多處被應用了,如果調用方法時這多處的實際應用類型對應到了不同的類型,且沒有使用返回值,這時候取多個參數中的最大交集類型,例如,下面語句實際對應的類型就是Number了,編譯沒問題,只是運行時出問題:
fill(new Integer[3],3.5f)   --> static <T> void fill(T[] a, T v) 
4.當某個類型變量在整個參數列表中的所有參數和返回值中的多處被應用了,如果調用方法時這多處的實際應用類型對應到了不同的類型, 並且使用返回值,這時候優先考慮返回值的類型,例如,下面語句實際對應的類型就是Integer了,編譯將報告錯誤,將變量x的類型改爲float,對比eclipse報告的錯誤提示,接着再將變量x類型改爲Number,則沒有了錯誤:
int x =(3,3.5f) --> static <T> T add(T a, T b) 
5.參數類型的類型推斷具有傳遞性,下面第一種情況推斷實際參數類型爲Object,編譯沒有問題,而第二種情況則根據參數化的Vector類實例將類型變量直接確定爲String類型,編譯將出現問題:
copy(new Integer[5],new String[5])--> static <T> void copy(T[] a,T[]  b);
copy(new Vector<String>(), new Integer[5]) --> static <T> void copy(Collection<T> a , T[] b);
 

六  定義泛型類型

1.如果類的實例對象中的多處都要用到同一個泛型參數,即這些地方引用的泛型類型要保持同一個實際類型時,這時候就要採用泛型類型的方式進行定義,也就是類級別的泛型,語法格式如下:

public class GenericDao<T> {
private T field1;
public void save(T obj){}
public T getById(int id){}
}
2.類級別的泛型是根據引用該類名時指定的類型信息來參數化類型變量的,例如,如下兩種方式都可以:
GenericDao<String> dao = null;
new genericDao<String>();
3.注意:
在對泛型類型進行參數化時,類型參數的實例必須是引用類型,不能是基本類型。
當一個變量被聲明爲泛型時,只能被實例變量、方法和內部類調用,而不能被靜態變量和靜態方法調用。因爲靜態成員是被所有參數化的類所共享的,所以靜態成員不應該有類級別的類型參數。

問題:類中只有一個方法需要使用泛型,是使用類級別的泛型,還是使用方法級別的泛型?

答:方法級別的泛型

4.泛型的繼承

泛型的繼承有兩種方式:

1)繼承時,對泛型父類的類型參數不實例化

2)繼承時,對泛型父類的類型參數實例化


七 擴展--> 通過反射獲得泛型的參數化類型

示例1:

package cn.itcast.text2;  
  import java.lang.reflect.*;  
import java.sql.Date;  
import java.util.*;  
import cn.itcast.text1.ReflectPoint;  
  

public class GenerticTest {  
    public static void main(String[] args) throws Exception {  
        // TODO Auto-generated method stub  
        Object obj = "abc";  
        String str = autoContor(obj);  
          
        GenerticDao<ReflectPoint> gd = new GenerticDao<ReflectPoint>();  
        gd.add(new ReflectPoint(3,5));  
        //通過獲得方法本身的方法  
        Method applyMethod = GenerticTest.class.getMethod("applyVector", Vector.class);  
        //通過方法的獲取泛型參數的方法得到原始參數類型的集合  
        Type[] types = applyMethod.getGenericParameterTypes();  
        //將參數類型轉換爲參數化類型  
        ParameterizedType pType = (ParameterizedType)types[0];  
        //得到原始類型  
        System.out.println(pType.getRawType());  
        //得到實際參數類型  
        System.out.println(pType.getActualTypeArguments()[0]);  
    }  


示例2:

示例代碼:
Class GenericalReflection {
 private Vector<Date> dates = new Vector<Date>();
 public void setDates(Vector<Date> dates) {
   this.dates = dates;
 }
 public static void main(String[] args) {
  Method methodApply = GenericalReflection.class.getDeclaredMethod("applyGeneric", Vector.class);
  ParameterizedType pType = (ParameterizedType)
                   (methodApply .getGenericParameterTypes())[0];
           System.out.println("setDates("
                   + ((Class) pType.getRawType()).getName() + "<"
                   + ((Class) (pType.getActualTypeArguments()[0])).getName()
                   + ">)" );
 }
}
泛型DAO的應用:
public abstract class DaoBaseImpl<T> implements DaoBase<T> {
protected Class<T> clazz;
public DaoBaseImpl() {
Type type = this.getClass().getGenericSuperclass();
ParameterizedType pt = (ParameterizedType) type;
this.clazz = (Class) pt.getActualTypeArguments()[0];
System.out.println("clazz = " + this.clazz);
}
}
public class ArticleDaoImpl extends DaoBaseImpl<Article> implements ArticleDao {
}


最新最全的的java學習視頻教程:http://pro.net.itcast.cn/View-22-1458.aspx

-------android培訓java培訓java學習型技術博客、期待與您交流! ----------

詳細請查看:http://edu.csdn.net/heima







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章