1. 程式人生 > >反射(Class類、Constructor類、Field類、Method類)

反射(Class類、Constructor類、Field類、Method類)

反射:通過類的Class物件來操作。可以得到屬性物件,方法物件,構造器物件。可以通過Class物件和構造器物件來例項化物件。但是,通過Class物件來例項化時不能傳遞引數,通過構造器時可以傳遞引數。
1.Class類例項化的幾種方式:

  • 可以通過類名.class;
  • 可以通過物件名.getClass;
  • 可以通過Class的靜態方法forName方法(傳遞類的路徑名)來獲取Class物件。
  • 對於包裝器型別來說,可以通過類名.TYPE來獲取。
public class Maintest {
    public static void main(String[] args)throws Exception{
        Integer i=new Integer(12);
        Class i1=Integer.class;
        Class i2=i.getClass();
        Class i3=Class.forName("java.lang.Integer");
        Class i4=Integer.TYPE;//基本資料型別的Class類
        Class i5=int.class;
        System.out.println(i1==i2&&i2==i3);
        System.out.println(i4==i5);
    }
}

執行結果都為true
2.Class類的方法:可以通過Class方法獲得各類所實現的介面,該類的全名,該類的直接父類。獲得所有的構造器,獲得所有的方法及成員變數。getDeclaredxxx表示獲得所有的,getxxx表示獲得public修飾的。

public class Maintest {
    public static void main(String[] args)throws Exception{
        Class c=String.class;
        System.out.println(c.getName());//輸出類的全名
        System.out.println(Arrays.toString(c.getInterfaces()));//輸出類所實現的介面
        System.out.println(c.getSuperclass());//輸出類的直接父類
    }
}

執行結果:

java.lang.String
[interface java.io.Serializable, interface java.lang.Comparable, interface java.lang.CharSequence]
class java.lang.Object

自定義一個Person類,只有兩個私有的name和age屬性,get和set方法,有參和無參構造。
獲得構造器的方法:

public class Maintest {
    public static void main(String[] args)throws Exception{
        Class per=Person.class;
        Constructor[] constructors=per.getDeclaredConstructors();//獲取所有構造
        Constructor[] constructors1=per.getConstructors();//獲取public修飾的構造
        for(Constructor con:constructors)//所有
        {
            System.out.println(con);
        }
        System.out.println("-------------------------");
        for(Constructor con:constructors1)//public修飾的
        {
            System.out.println(con);
        }
    }
}

執行結果:

private test.Person()
public test.Person(java.lang.String,int)
-------------------------
public test.Person(java.lang.String,int)

獲得成員變數物件,並通過每個成員變數物件來獲得變數名,變數型別,變數修飾符。也可以通過getDeclaredField方法傳遞變數名來獲得指定的變數。

public class Maintest {
    public static void main(String[] args)throws Exception{
        Class per=Person.class;
        Field[] allF=per.getDeclaredFields();
        System.out.println("屬性名\t\t"+"屬性型別\t\t"+"修飾符");
        for(Field field:allF)
        {
            System.out.println(field.getName()+"\t\t"+field.getType()+"\t\t"+field.getModifiers());
        }
    }
}

結果:

屬性名		屬性型別		修飾符
name		class java.lang.String		2
age		int		2

獲得方法物件與之類似,可以通過方法名、形參來獲得指定的方法。通過invoke方法傳遞物件和實參來呼叫傳過去物件的該方法。方法物件可以通過getParameterTypes方法獲得方法的引數列表。
3.建立物件並例項化的幾種方法:

  • new的時候呼叫有參構造;
  • 通過Class類得到有參構造器的物件,通過這個構造器物件例項化並傳參。
  • 建立好空物件,通過Class物件獲得get和set方法,呼叫方法傳遞引數
  • 建立好空物件,通過Class物件獲得成員變數Field物件,如果為私有,將其設定為可訪問的。然後直接set值
public class Maintest {
    public static void main(String[] args)throws Exception{
        Class p=Person.class;
       //例項化Person物件
        //第一種
        Person person1=new Person("zhangsan",24);
        System.out.println(person1);
        //第二種 通過反射得到特定構造方法。通過構造器例項化物件(傳參)
        Constructor constructor=p.getDeclaredConstructor(String.class,int.class);
        Person person2=(Person) constructor.newInstance(new Object[]{"lisi",23});
        System.out.println(person2);
        //第三種 空物件已經建立好 通過反射獲得get和set方法並呼叫。
        Person person3=(Person) p.newInstance();//空物件,name為null,age為0
        Method setname=p.getDeclaredMethod("setName", String.class);//獲得到方法物件,輸入型別避免過載
        Method setage=p.getDeclaredMethod("setAge", int.class);
        setname.invoke(person3,"luck");
        setage.invoke(person3,22);
        System.out.println(person3);
        //第四種 和第三種類似,只不過是直接操作私有成員
        Person person4=new Person();//空物件
        Field name=p.getDeclaredField("name");
        Field age=p.getDeclaredField("age");
        name.setAccessible(true);//將私有變數設定為可直接操作的
        age.setAccessible(true);
        name.set(person4,"Dimond");
        age.set(person4,23);
        System.out.println(person4);
    }
}

執行結果:

Person{name='zhangsan', age=24}
Person{name='lisi', age=23}
Person{name='luck', age=22}
Person{name='Dimond', age=23}

setAccessible不僅可以對私有屬性用,還可以對私有構造器用,同樣也可以對私有方法用。可以用getDeclaredxxx來獲取物件。

對陣列的操作,可以通過Array類,這個類就相當於陣列類的構造器物件。

public class Maintest {
    public static void main(String[] args)throws Exception{
        int[] i= (int[])Array.newInstance(int.class,10);
        System.out.println(Arrays.toString(i));//陣列中為0
        Array.setInt(i,8,888);//將第八個數設定為888
        System.out.println(Arrays.toString(i));
    }
}

執行結果:

[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 888, 0]

代理模式:代理物件來代理A物件,要用和A相同的方法。客戶端訪問A的方法時,不直接訪問,而是通過代理間接訪問。因此代理中要有A的引用(關聯關係)。又因為代理類中的方法和A中的方法一樣,因此他們實現同一個介面即可。可以通過反射來實現動態代理。