Java高級應用之泛型與反射


 

/******************************************************************************************************************/

一、泛型

不同的數據結構可以用同樣的操作就是泛型

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.使用反射的好處

增加程序靈活性,通過類的名稱(放在文件里或者通過參數傳遞,這就不用在代碼中寫死,傳入什么就可以實例化什么),然后就可以實例化出不同的對象

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM