/******************************************************************************************************************/
一、泛型
不同的數據結構可以用同樣的操作就是泛型
1.類使用泛型
class Person<T> {//要使用泛型的類加上類似與通配符的<T>,中間字符可以任意
//也可以傳入多個類型<T,N>
private T age;//內部類型,使用T代替
public void setAge(T age) {//內部類型,使用T代替
this.age = age;
}
public T getAge() {//內部類型,使用T代替
return this.age;
}
}
public class Generics {
public static void main(String args[]) {
Person<String> p = new Person<String>();//創建對象的時候傳入類型,如果是兩個則<>里面放兩個類型
p.setAge("3 years old");
//System.out.println(p.getAge());
printInfo(p);
Person<Integer> p2 = new Person<Integer>();//<>內只能用類,所以不能用基礎數據類型int要用Integer類代替
p2.setAge(3);
//System.out.println(p2.getAge());
printInfo(p2);
Person<?> p3;
p3 = p;//通用引用p3賦值
//p3.setAge("4 years");//不能設置(類型沖突)
p3.getAge();//但可以獲取
}
public static void printInfo(Person<?> p) {//Person<?>是通配符,表示傳進來可以是String類也可以是Integer類
System.out.println(p.getAge());
}
}
2.方法使用泛型
1)定義
public static <T> void printInfo2(Person<T> p) {//定義格式返回值前面,參數里都要有相應的<T>
System.out.println(p.getAge());
}
2)調用
printInfo2(p);//p為具體的對象Person<String> p = new Person<String>();
printInfo2(p2);
printInfo2(p3);
3.子類使用泛型(泛型的繼承)
1)定義
class Student<T> extends Person<T> {//子類繼續使用泛型
}
class Student2 extends Person<String> {//子類不再使用泛型
//子類確定類型為String,所以前面也就不需要加<T>,同時對應父類的類型也已被確定
}
2)調用
Student<Integer> s = new Student<Integer>();//創建子類對象,傳入類型
s.setAge(10);//調用父類的方法
printInfo(s);//向上轉換
Student2 s2 = new Student2();//已經確定類型,不需傳入
s2.setAge("11 years");//對應父類的類型已被確定,調用父類方法直接傳入String
printInfo(s2);
4.接口使用泛型(接口:特殊的父類)
interface Person<T> {
public void setAge(T age);
public T getAge();
}
class Student<T> implements Person<T> {//子類繼續使用泛型
T age;
public void setAge(T age){
this.age = age;
}
public T getAge() {
return this.age;
}
}
class Student2 implements Person<String> {//指定對應接口的泛型類型
String age;///已經確定了類型,直接使用
public void setAge(String age){
this.age = age;
}
public String getAge() {
return this.age;
}
}
public static void main(String args[]) {
Student<Integer> s = new Student<Integer>();//創建子類對象,傳入類型
s.setAge(10);
printInfo(s);//向上轉換(接口:特殊的父類),子類里實現了方法,同時父類也就是接口沒有實現對應的方法,所以里面調用的是子類的方法
Student2 s2 = new Student2();//已經確定類型,不需傳入
s2.setAge("11 years");
printInfo(s2);
}
public static void printInfo(Person<?> p) {
System.out.println(p.getAge());
}
5.受限泛型
聲明泛型的時候可以指定泛型的上限和下限
1)泛型的上限:<T extends Number> T只能是Number類或其子類
/*T只能是Number類或其子類Integer, Float 等*/
class Student<T extends Number> implements Person<T> {
T age;
public void setAge(T age)
{
this.age = age;
}
public T getAge() {
return this.age;
}
}
2)泛型的下限:<? super String> T只能是String類或其父類
super只能使用通配符,不能直接在名字里使用(即只能在使用這個泛型的時候再指定),所以還是:
class Student<T> implements Person<T> {
T age;
public void setAge(T age)
{
this.age = age;
}
public T getAge() {
return this.age;
}
}
//使用的時候指定下限(用通配符?指定下限)
public static void printInfo(Person<? super String> p) {//傳進來 只能是String類或其父類
System.out.println(p.getAge());
}
調用:
Student<String> s = new Student<String>();
s.setAge("10");
printInfo(s);
/******************************************************************************************************************/
二、反射
正常步驟我們是import"包.類"然后通過new實例化最后得到實例化對象
那么能否反過來呢
從實例化對象得到getClass方法最后得到完整的"包.類"名稱
這個反過來的操作(根據實例化對象得到完整的"包.類"名稱)就是所謂的反射操作(當然反射的作用不僅僅是得到完整的"包.類"名稱)
注意:在反射操作中,一切的操作都使用Object完成,類,數組的引用都可以使用object進行接收
1.class對象和"類的實例化對象"
Person p=new Person();
System.out.println(p.getClass().getName());//打印"包.類"名稱
JVM會加載*.Class文件(java寫的類)到內存里,也就是會在內存里創建一個class object用來描述這個類,包括類的包,類名稱,構造方法,方法,屬性,這樣就可以使用這個class object來實例化對象(在內存里對於一個class只有一個class object,這個class object是用來描述類本身的,我們可以使用這個class object來創建實例化對象)可以有三種方法獲得一個類的class object:
2.獲得class
1).Class<?> c=Class.forName("包.類");
Class<?> c1 = null;
try {
c1 = Class.forName("a.b.c.d.Person");
} catch (ClassNotFoundException e) {
System.out.println(e);
}
System.out.println(c1.getName());
2).Class<?> c=new X().getClass();
Person p = new Person();
Class<?> c2 = p.getClass();
System.out.println(c2.getName());
3).Class<?> c=X.class
Class<?> c3 = Person.class;
System.out.println(c3.getName());
3.對於數組或者其他數據類型,也有對應的類,對應的class object
int arr[] = {1,2,3};
int arr2[] = {1,2,3,4};
int arr3[][] = {{1,2,3,4},{1}};
Class<?> c4 = arr.getClass();
Class<?> c5 = arr2.getClass();
Class<?> c6 = arr3.getClass();
Class<?> c7 = int.class;
System.out.println(c4.getName());
System.out.println(c5.getName());
System.out.println(c6.getName());
System.out.println(c7.getName());//基本的數據類型也是一個類,里面也有class
System.out.println((c4 == c5));//同樣的數組類型是一樣的class
System.out.println((c4 != c6));//二維數組和一維數組不一樣所以不一樣的class
4.使用反射來獲取類的實例化對象(類的屬性與方法),取代import a.b.c.d.Person;(的形式)
public static void main(String args[]) throws Exception {//Exception 是其他的異常父類,所以可以這樣代替InstantiationException等其他異常
Class<?> c = null;
try {
c = Class.forName("a.b.c.d.Person");//使用名字來獲得這個Person的class object
} catch (ClassNotFoundException e) {
System.out.println(e);
}
Object p = null;//然后創建它的實例化對象(Object是所有類的父類,所以可以這么寫)
try {
p = c.newInstance();//然后創建它的實例化對象(Object是所有類的父類,所以可以這么寫(可以向上轉換)),實際是調用了那個類的無參構造方法
} catch (InstantiationException e) {
System.out.println(e);
}
//調用有參構造方法的實例化
Constructor<?> con = c.getConstructor(String.class);//獲得參數是String的構造方法 的class
Object p2 = con.newInstance("123");//然后創建它的實例化對象,有參數
5.獲得並調用類的方法:
Method set = c.getMethod("setName", String.class);//傳入方法名稱(該方法在定義的地方要有public權限),參數類型
set.invoke(p2, "123");//調用實例化對象中的方法,要設置的實例化對象,然后才是傳入的值
set.invoke(p, "abc");
//對於靜態方法, invoke的第1個參數可以寫為null
Method get = c.getMethod("getName");//沒有傳入參數
System.out.println(get.invoke(p));
System.out.println(get.invoke(p2));
6.讀取或設置類的屬性
1).最好是用上面的方式,通過方法來訪問類的屬性
2).也可以直接獲取屬性:
(1)Field f = c.getField(String name); // 獲得公共屬性, 此方法先搜本類, 再搜它實現的接口,最后在父類中搜索
(2)Field name = c.getDeclaredField("name");//可以獲得類里面所有屬性(包括private,public等)中名為name的屬性
name.setAccessible(true);//設置為可訪問,如果要訪問的屬性為public可以不要這句話,其實設置為可訪問也就破壞了類的封裝性,所以一般不使用這種方法,而是去調用那個類的設置獲取方法
name.set(p, "www");//設置某個實例化對象中的name屬性
name.set(p2, "123");//設置p2這個實例化對象中的name屬性
System.out.println(name.get(p));//獲得某個對象的name屬性
System.out.println(name.get(p2));
7.使用反射的好處
增加程序靈活性,通過類的名稱(放在文件里或者通過參數傳遞,這就不用在代碼中寫死,傳入什么就可以實例化什么),然后就可以實例化出不同的對象