JAVA 反射機制....


PS:最近數據庫算是學完了,總算是有時間回頭復習一下以前學的東西了...最近這幾天好好復習了一下反射機制,順便也做個總結...JAVA的學習基本都沒有去寫...慢慢補上吧...

學習內容:

1.反射機制的概念...

2.反射機制的作用...

3.如何去使用反射機制...

4.反射機制的優勢...

1.反射機制:

  反射機制,想必大家都不陌生,但是估計大家也就是知道個概念...那么到底什么是反射機制,估計大家對這個概念有點模糊不清...簡單的概念就是:對於我們定義的每一個類,在任何的時刻,我們都能夠知道這個類里面的屬性和方法...對於任何一個對象,都能夠調用這個類中的方法...這就是反射機制的基本概念..

2.反射機制實現的功能:

在運行時判斷任意一個對象所屬的類,在運行時構造任意一個類的對象,在運行時判斷任意類所具有的方法和屬性,在運行時調用任意一個對象的方法

生成動態代理...

3.如何使用反射機制:

我們如何去使用反射機制呢?

反射機制里一個特點就是實例化class對象,因為任意一個類對象都是class的實例...那么如何實例化class對象呢?三種方法:

i.通過forname()方法...

ii.對象.getclass();

iii.類.class;

在程序運行時通過實例化的對象,然后對象使用反射來調用類內的方法或者是屬性...這樣就實現了動態的獲取信息...

package Fanshe;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Scanner;

class person{
    private String name;
    private String sex;
    private int age;
    
    person(){
        
    }
    public person(String name,String sex,int age){
        super();
        this.name=name;
        this.sex=sex;
        this.age=age;
    }
    private void setname(String name){
        this.name=name;
    }
    public String getsex(){
        return sex;
    }
    public void setsex(String sex){
        this.sex=sex;
    }
    public int getage(){
        return age;
    }
    public void setage(){
        this.age=age;
    }
    public String toString(){
        return "姓名 :"+name+"年齡: "+age;
    }
}

class reflectdemo{
     
      reflectdemo(){
         Scanner cin=new Scanner(System.in);
         String classpath=cin.nextLine();//需要輸入類的完整路徑...我的路徑是Fanshe.person
         try {
                        //三種方式實現對象的實例化
            Class cla=Class.forName(classpath);
//            Class cla_1=person.class;
//            person p=new person();
//            Class cla_2=p.getClass();
            Method [] method=cla.getDeclaredMethods();//定義一個數組來保存類中所有的方法...
            System.out.println("=====類中的方法有=====");
            for(Method meth:method){
                System.out.println(meth.toString());
            }
            System.out.println("=====方法獲取結束=====");
            Field [] field=cla.getDeclaredFields(); //保存類中所有屬性
            System.out.println("=====類中內部的屬性=====");
            for(Field fie:field){
                System.out.println(fie.toString());
            }
            System.out.println("=====屬性獲取結束=====");
            Constructor [] con=cla.getDeclaredConstructors();//類中所有的構造函數...
            System.out.println("=====獲取構造函數=====");
            for(Constructor c:con){
                System.out.println(c.toString());
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.out.println("路徑輸入錯誤");
        }
      }
}

public class fanshe_2 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        reflectdemo ref=new reflectdemo();
    }

}

上述代碼通過使用反射機制來獲取類中的方法和屬性...反射這東西我還是用代碼說話更好一些,所以我還是都使用代碼來讓大家理解的更深刻一些...

package Fanshe_cll;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.annotation.Annotation;  

class reflectdemo{
    
    //私有構造函數
    private reflectdemo(String name,int age){
    }
    reflectdemo(){
    }
    //公有構造函數
    public reflectdemo(String name){
    }
    public void info(){
    }
public void info(String str){
    }
private void set(){
}
    class inner{
    }//內部類...
}

import Fanshe_cll.reflectdemo.inner;
public class clear1 {

    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        reflectdemo r=new reflectdemo();
        Class claz_1=r.getClass();
        System.out.println(claz_1.toString());
        Class<reflectdemo> cla=reflectdemo.class;  //使用泛型,用class來實例化對象..
        System.out.println(cla.toString());
        Class claz=Class.forName("Fanshe_cll.reflectdemo");
        System.out.println(claz.toString());
        System.out.println("=====類中的構造函數=====");
        //獲取全部的構造函數....
        Constructor []constructor=cla.getDeclaredConstructors();//返回類中所有的構造函數,無論是公有的還是私有的...並且這個保存是按照倒序保存的..
就是constructor[0]保存的是最后一個構造函數...而constructor[length-1]保存的是第一個構造函數...比如說我們有一個person類,類中有四個構造方法...
public Person(){}
public Person(String name){this.name=name;}
    public Person(int age){this.age=age;}
    public Person(String name,int age){this.age=age;this.name=name;}
我們在主函數來調用這幾種方法...
Class<?> cla=Person.class;
        Constructor []con=cla.getDeclaredConstructors();
Person per_1=(Person) con[0].newInstance("劍神",20);
       Person per_2=(Person) con[1].newInstance(20);
        Person per_3=(Person) con[2].newInstance("劍聖");
       Person per_4=(Person) con[3].newInstance(); 必須這樣傳參才行,如果把他們倒過來,那么必定會出現java.lang.illegalArgumentException非法參數異常..
for(Constructor con:constructor){ System.out.println(con.toString()); } System.out.println("公有構造函數"); //獲取公有的構造函數.... Constructor []constructor_1=cla.getConstructors();//這個就和上面不同了,這個保存着類中所有的公有構造函數 for(Constructor con_1:constructor_1){ System.out.println(con_1.toString()); } System.out.println("獲取全部方法"); //獲取全部的方法.... Method [] method =cla.getDeclaredMethods(); for(Method meth:method){ System.out.println(meth.toString()); } //獲取公有的方法.... System.out.println("獲取公有方法"); Method [] method_1=cla.getMethods(); for(Method meth_1:method_1){ System.out.println(meth_1.toString());//這個輸出異常處理的函數..這說明有些方法是帶有異常的... } //獲取指定的某個方法.... System.out.println("獲取某個指定的方法"); Method method_2=cla.getMethod("info",null);//獲取參數為空的info方法.. System.out.println(method_2.toString()); Method method_3=cla.getMethod("info", String.class);//獲取指定參數為字符串的方法... System.out.println(method_3.toString()); // Method method_4=cla.getMethod("info", reflectdemo.class); System.out.println("-----------------"); // System.out.println(method_4.toString()); System.out.println("-----------------"); //獲取注釋 System.out.println("獲取注釋"); Annotation []annotations =cla.getAnnotations(); for(Annotation ant:annotations){ System.out.println(ant.toString()); } //獲取包信息 System.out.println("獲取包信息"); Package package_1=cla.getPackage(); System.out.println(package_1.toString()); //獲取內部類 System.out.println("獲取內部類"); Class [] cla_1=cla.getDeclaredClasses(); for(Class clazz : cla_1){ System.out.println(clazz.toString()); } //調用父類 System.out.println("調用父類"); Class cla_2=cla.getSuperclass(); System.out.println(cla_2.toString()); //內部類對象... Class cla_3=Class.forName("Fanshe_cll.reflectdemo$inner"); // Class cla_4=inner.class; //獲取內部類的外部類 System.out.println("使用內部類來獲取外部類信息"); System.out.println(cla_3.getDeclaringClass()); System.out.println("--------------"); System.out.println(cla_3.getPackage()); System.out.println(cla_3.getSuperclass()); } }

我們上面只是返回類中的方法...基本沒有進行調用,那么下面就對上面的那些方法進行調用...

package Fanshe_c;
import java.lang.reflect.Method;
class person{         //這個類大家基本不用看,是一個很普通的類,之所以粘出來是因為自己定義了好幾個person類,怕產生混淆....
    private String name;
    private String sex;
    private int age;
    
    person(){
        
    }
    public person(String name,String sex,int age){
        super();
        this.name=name;
        this.sex=sex;
        this.age=age;
    }
    public void setname(String name){
        this.name=name;
    }
    public String getname(){
        return name;
    }
    public String getsex(){
        return sex;
    }
    public void setsex(String sex){
        this.sex=sex;
    }
    public int getage(){
        return age;
    }
    public void setage(int age){
        this.age=age;
    }
    public String toString(){
        return "姓名 :"+name+"年齡: "+age;
    }
}
public class cll_2 {

    public static void main(String[] args) throws Exception{
        // TODO Auto-generated method stub
        Class cla=person.class;
        person p=new person();//定義一個對象..使用new關鍵字..
        Method method=cla.getMethod("setname",String.class);
        method.invoke(p, "劍神");//這句話還可以寫成 method.invoke(cla.newInstance(),"劍神");下面凡是和這句話類似的都可以使用這種方法,使用newInstance()的默認構造函數去實例化一個對象..
        Method method_1=cla.getMethod("setage",int.class);
        method_1.invoke(p, 20);
        Method method_2=cla.getMethod("setsex", String.class);
        method_2.invoke(p, "男");
        //調用輸出方法...
        Method method_3=cla.getMethod("getname", null);
        System.out.println((method_3.invoke(p, null)).toString());
        Method method_4=cla.getMethod("getsex", null);
        System.out.println((method_4.invoke(p,null)).toString());
        Method method_5=cla.getMethod("getage", null);
        System.out.println((method_5.invoke(p, null)).toString());
    }

}

通過上面的代碼,大家難免會產生一個疑問,就是使用new和newInstance去創建對象到底有什么區別呢??這個我也在這里進行下解釋...

new與newInstance()的區別

首先一個是關鍵字,一個是方法...new是一個關鍵字,使用關鍵字來新建一個對象的時候沒有過多的限制...而newInstance()是一個方法,使用newInstance()創建對象的時候,類一定要有一個默認的無參構造方法...並且這個類必須要被加載,否則的話JVM首先會將這個類進行加載后,然后我們才能使用newInstance()來新建一個對象...那么大家就會問了,那給出兩個創建對象的方法,是不是有點多此一舉了呢?這個當然是否定的...使用newInstance()...其實在一定情況下是很有好處的...大家來看:

Class c = Class.forName("darker");
factory = (AInterface)c.newInstance();
其中AInterface是darker的接口,在看下面
String className = "darker";
Class c = Class.forName(className);
factory = (AInterface)c.newInstance();
進一步的進行擴展...
String className = readfromXMlConfig;//從xml 配置文件中獲得字符串
Class c = Class.forName(className);
factory = (AInterface)c.newInstance();
上面代碼把類名darker徹底的去掉了,這個優點估計大家都看出來了,這樣當我們的darker類做怎樣的修該,我們這句話永遠保持不變...如果使用new就沒辦法了吧??
            

這就是newInstance()在一定的地方使用的好處...

簡單的介紹一下setAccessible()函數...

package Fanshe_fuzhi;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.util.Calendar;
class Person {   //這個代碼目的是為了簡單的介紹一下setAccessible()函數。。
    private String name;
    private String gender;
    private int age;
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getGender() {
        return gender;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    
    public String getSay(){
        return"姓名:"+this.name+" \t性別:"+this.gender+"\t年齡:"+this.age;
    }
}
public class Fanshe_quanxian {

    public static void main(String[] args) throws Exception{
        // TODO Auto-generated method stub
        Class<?> cla=Person.class;
        Field name=cla.getDeclaredField("name");
        System.out.println(name);
        name.setAccessible(true);   //這個函數的參數是boolean類型,當值為true的時候,那么JVM將忽略訪問時的權限...如果為false,那么就不進行忽略...
        name.set(cla.newInstance(),"三豐");
        Field gender=cla.getDeclaredField("gender");
        gender.setAccessible(true);
        gender.set(cla.newInstance(),"重陽");
        Field age=cla.getDeclaredField("age");
        age.setAccessible(true);
        age.setInt(cla.newInstance(), 20);
        Method getsay=cla.getMethod("getSay");
        Object obj=getsay.invoke(cla.newInstance());
        System.out.println(obj.toString());
    }

}

Modifyers函數...很簡單,沒什么東西...

package Fanshe_modifier;

import Fanshe_Person.Person;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class Modify {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Class<?> cla = Person.class;
        Field[] field = cla.getDeclaredFields();
        for (int i = 0; i < field.length; i++) {
            int mo = field[i].getModifiers();
            System.out.println(mo);
            String priv = Modifier.toString(mo);
            System.out.println(priv);
            Class<?> type =field[i].getType();
            System.out.println(type.getName());
            System.out.println(field[i].getName());
            /*
             * 直接toString就可以返回方法的名字...
             * field[i].toString
             * */
        }
    }

}

動態創建和訪問數組...

package Array;
import java.lang.reflect.Array;
public class Array_1 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Object obj=Array.newInstance(int.class,3);
        /*
         *一維數組的創建與賦值..
         */
        Array.set(obj, 0, 20);
        Array.set(obj, 1, 30);
        int obj_1=Array.getLength(obj);
        System.out.println(obj_1);
        Object obj_2=Array.get(obj, 0);
        System.out.println(obj_2);
        Array.set(obj, 2, 40);
        int obj_3=Array.getLength(obj);
        System.out.println(obj_3);


        Object two_arr=Array.newInstance(int.class, 10,10);
        /*
         * 二維數組的賦值:先找到第一維的下標值..然后再傳參..
         * */
        Object firstindex=Array.get(two_arr, 0);
        Array.set(firstindex, 0, 20);
        
    }

}

動態修改數組的大小...

package Array;
import java.lang.reflect.Array;
public class Array_xiugailength {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        int temp[]={0,1,2,3,4,5,6,7,8,9};
        int newtemp[]=(int [])arrayinc(temp,15);
        for(int i=0;i<newtemp.length;i++){
            System.out.println(newtemp[i]);
        }
    }
    public static Object arrayinc(Object obj,int len){
        Class<?>arr=obj.getClass().getComponentType();
        Object newArr=Array.newInstance(arr, len);
        return newArr;
    }
}

4.反射機制的應用場合:

大家不禁會這樣問,什么時候能夠使用到反射機制呢,並且它的應用場合是什么...並且它的優勢到底在哪里呢?

package Fanshefactory;
interface factory_1{
    public abstract void show();
}
class A implements factory_1{
    public void show(){
        System.out.println("A");
    }
}
class B implements factory_1{
    public void show(){
        System.out.println("B");
    }
}
/*
 * 當增添類去實現接口的時候,我們就需要改變factory,當我們添加的類過多的時候
 * 我們的修改量就會很大...可以使用反射進行解決...
 * */
class Factory{
    public static factory_1 getInstance(String fname){
        factory_1 f=null;
        if("A".equals(fname)){
            f=new A();
        }
        if("B".equals(fname)){
            f=new B();
        }
        return f;
    }
}
public class Fanshe_factory_1 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        factory_1 f=Factory.getInstance("A");
        f.show();
    }

}


package Fanshe_factory1;
interface factory_2{
    public abstract void show();
}
class AA implements factory_2{
    public void show(){
        System.out.println("A");
    }
}
class BB implements factory_2{
    public void show(){
        System.out.println("B");
    }
}

class Factory_1{
    public static factory_2 getInstance(String fname){
          factory_2 f=null;
          try {
            Class cla=Class.forName(fname);
            f=(factory_2)cla.newInstance();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
          return f;
    }
}
public class Fanshe_factory_2 {

    public static void main(String[] args) throws Exception{
        // TODO Auto-generated method stub
        factory_2 f=Factory_1.getInstance("Fanshe_factory1.AA");
                if(f!=null){
                 f.show();
                }
    }

}

  這兩個代碼形成對比,一個是未使用反射機制的工廠模式,一個是使用了反射機制的工廠模式...估計大家看完沒什么過多的感覺...但是這里比如說我們要添加更多的類去實現我們暴露出的接口,那么我們Factory類里的判斷部分也要進行大量的修改...這么做會代碼冗余...那么用了反射的我們就沒必要進行修改了,因為我們是使用class實例化的對象,class是字節碼文件,無論類如何變,這個字節碼文件只有一個,那么這樣就可以直接實例化對象....我們沒必要進行一個一個判斷了....這就是反射的優勢,在一定程度上解決代碼冗余,並且可以動態的去獲取類的信息...這樣我們就感覺java就像動態語言一樣..因此反射機制是很強大的一個東西...

 


免責聲明!

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



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