java反射

jopen 10年前發布 | 31K 次閱讀 反射 Java開發

1.首先理解什么是反射?

反射(Reflection)能夠讓運行于JVM中的程序檢測和修改運行時的行為。

2.為什么需要反射?

反射能夠讓我們:

  • 在運行時檢測對象的類型;
  • 動態構造某個類的對象;
  • 檢測類的屬性和方法;
  • 任意調用對象的方法;
  • 修改構造函數、方法、屬性的可見性等;
  • </ul> --------------------------------------華麗分割線-----------------------------------------------

    1)在面向對象的世界里,萬事萬物皆對象。(java語言中,靜態的成員、普通數據類型等)
       類是不是對象?類是誰的對象?//類是對象,類是java.lang.Class類的實例對象
    2)這個對象到底如何表示?
    3)Class.forName("類的全稱"), 不僅表示了類的類類型,還表示了動態加載類(編譯時加載類是靜態加載類、運行時加載類是動態加載類)

    --------------------------------------華麗分割線-----------------------------------------------

    3.Class類實例對象的方法

    Class實例對象的三種方法:a.通過類的隱含的靜態成員class; b.通過該類的getClass()方法;c.通過類的全稱,用Class.forName("類名")獲取。eg:

        class ClassDemo0{
    public static void main(String[] args) {
    Foo foo1=new Foo();
    //1,任何一個類都有一個隱含的靜態成員class
    Class c1=Foo.class;
    //2,已知該類,通過getClass方法
    Class c2=foo1.getClass();

            //注c1,c2表示Foo類的類類型(class type)  
            System.out.println(c1==c2);  //true, 一個類只可能是Class類的一個實例對象  
    
            //3,通過類的全稱獲取  
            Class c3=null;  
            try{  
                c3=Class.forName("Foo");  
            }catch(ClassNotFoundException e){  
                e.printStackTrace();  
            }  
            System.out.println(c2==c3);//true  
    
            //通過類的類類型創建該類的對象實例,通過c1,c2,c3創建Foo的對象實例  
            try{  
                Foo foo=(Foo)c1.newInstance();//前提:該類要有無參構造函數  
            }catch(InstantiationException e){  
                e.printStackTrace();  
            }catch(IllegalAccessException e){  
                e.printStackTrace();  
            }  
        }  
    }  
    
    class Foo{  
        void print(){  
            System.out.println("Foo");  
        }  
    }  </pre><a href="/misc/goto?guid=4959618301072992214" target="_blank" title="派生到我的代碼片" style="text-indent:0;"></a></div>
    

    </div> </div> 4.動態加載類的實用例子

    假如想要寫一個類調用類word或Excel,如下程序,如果沒有Word和Excel類編譯時肯定會出錯,若希望有其中一個類存在也可以編譯通過,則需要用到動態加載類

        class Office{
    public static void main(String[] args) {

             //new創建對象 是靜態加載類,在編譯時刻就需要加載所有的可能使用到的類,(若沒有建Word類),編譯不通過  
            //若有建Word類,但Excel類沒建,但編譯還是不能通過  
            //希望用到時再加載,可以通過動態加載類實現  
            if("Word".equals(args[0])){  
                Word w=new Word();      
                w.start();  
            }  
    
            if("Excel".equals(args[0])){  
                Excel e=new Excel();  
                e.start();  
            }  
        }  
    }  </pre></div>
    

    </div> </div> 運用動態加載類,使程序在運行時再加載,順利通過編譯,這也有利于后期增加功能,實現在線升級,增強擴展性

        class OfficeBetter{
    public static void main(String[] args) {
    try{
    //動態加載類,在運行時加載
    Class c=Class.forName(args[0]);
    //通過類類型,創建該類對象
    //Word w=(Word)c.newInstance(); //如果加載的是Excel呢?
    //Excel e=(Excel)c.newInstance(); //如果加載的是Excel呢?

                //后期增加功能就不用改動OfficeBetter,實現在線升級,擴展性更強  
                OfficeAble oa=(OfficeAble)c.newInstance();  //所以做一個標準————功能型的類盡量使用動態加載  
                oa.start();  
            }catch(Exception e){  
                e.printStackTrace();  
            }  
        }  
    }  
    
    interface OfficeAble{  
        public void start();  
    }  
    
    class Word implements OfficeAble{  
        public void start(){  
            System.out.println("word...start...");  
        }  
    }  
    
    class Excel implements OfficeAble{  
        public void start(){  
            System.out.println("Excel...start...");  
        }  
    }  </pre><a href="/misc/goto?guid=4959618301072992214" target="_blank" title="派生到我的代碼片" style="text-indent:0;"></a></div>
    

    </div> </div> 注:功能性的類盡量使用動態加載的方法

    5.獲取類的信息

    a.基本數據類型的類類型

     
                 Class c1=int.class;
    Class c2=String.class;//
    Class c3=double.class;
    Class c4=Double.class;
    Class c5=void.class;

    System.out.println(c1.getName());
    System.out.println(c2.getName());
    System.out.println(c2.getSimpleName()); //只獲取類名
    System.out.println(c3.getName());
    System.out.println(c4.getName());
    System.out.println(c5.getName());

    /output:
    int
    java.lang.String
    String
    double
    java.lang.String
    void
    / </pre></div> b.類的成員函數(使用Methods),成員變量(使用Field),構造函數的獲取(Constructor)

        class ClassUtils{

        /* 
         *打印類的信息,包括類的成員函數 
        */  
        public static void printClassMessage(Object obj){  
            //要獲取類的信息 首先要獲取類的類類型  
            Class c=obj.getClass();//傳遞的是哪個子類的對象 c就是該子類的類類型  
            //獲取類的名稱  
            System.out.println("類的名稱是:"+c.getName());  
            /* 
            Method類,方法對象 
            一個成員方法就是一個Method對象 
            getMethods()方法獲取的是所有的public的函數,包括父類繼承而來的 
            getDeclaredMethods()獲取的是所有該類自己聲明的方法,不問訪問父類繼承的 
            */  
            Methods[] ms=c.getMethods();  
            for(int i=0;i<ms.length;i++){  
                //得到方法的返回值類型的類類型  
                Class returnType=ms[i].getReturnType();  
                System.out.print(returnType.getName()+" ");  
                //得到方法的名稱  
                System.out.print(ms[i].getName()+"(");  
                //獲取參數類型-->得到的是參數列表的類型的類類型  
                Class[] paramTypes=ms[i].getParameterTypes();  
                for(Class class1:paramTypes){  
                    System.out.print(class1.getName()+",");  
                }  
                System.out.println(")");  
    
            }  
    
        }  
        /* 
        *打印類的信息,包括類的成員變量 
        */  
        public static void printFieldMessage(Object obj){  
            Class c=obj.getClass();  
            /* 
            成員變量也是對象 
            java.lang.reflect.Field 
            Field類封裝了關于成員變量的操作 
            getFields()方法獲取的是所有的public的成員變量的信息 
            getDeclareFields獲取的是該類自己聲明的成員變量的信息 
            */  
            //Filed[] fs=c.getFileds();  
            Field[] fs=c.getDeclareFields();  
            for(Filed field:fs){  
                //得到成員變量的類型的類類型  
                Class fieldType=field.getType();  
                String typeName=fieldType.getName();  
                //得到成員變量的名稱  
                String fieldName=field.getName();  
                System.out.println(typeName+" "+fieldName);  
            }  
        }  
    
        /* 
        打印對象的構造函數的信息 
        */  
        public static void printConMessage(Object obj){  
            Class c=obj.getClass();  
            /* 
            構造函數也是對象 
            java.lang.Constructor中封裝了構造函數的信息 
            getConstructors獲取所有的public的構造函數 
            getDeclaredConstructors所得的構造函數 
            */  
            //Constructor[] cs=c.getContructors();  
            Constructor[] cs=c.getDeclaredConstructors();  
            for(Constructor constructor:cs){  
                System.out.print(constructor.getName()+"(");  
                //獲取構造函數的參數列表-->得到的是參數列表的類類型  
                Class[] paramTypes=constructor.getParameterTypes();  
                for(Class class1:paramTypes){  
                    System.out.print(class1.getName+",");  
                }  
                System.out.println(")");  
            }  
        }  
    }  </pre><a href="/misc/goto?guid=4959618301072992214" target="_blank" title="派生到我的代碼片" style="text-indent:0;"></a></div>
    

    </div> </div> 6.方法的反射

    1)如何獲取某個方法?
       方法的名稱和方法的參數列表才能唯一決定某個方法
    2)方法的反射的操作?
       method.invoke(對象,參數列表);

        class MethodDemo{
    public static void main(String[] args) {
    //要獲取print(int,int)方法
    //1.要獲取一個方法就是獲取類的信息,獲取類的信息首先要獲取類的類類型
    A a1=new A();
    Class c=a1.getClass();
    //2.獲取方法名稱和參數列表來決定
    try{
    Method m=c.getMethod("print",new Class[]{int.class,int.class});
    //或寫成 c.getMethod("print",int.class,int.class);

                //方法的反射操作   如何達到和a1.print(10,20)的效果?  
                //方法如果沒有返回值返回null,有返回值返回具體的返回值  
                Object o=m.invoke(a1,new Object[]{10,20});  //m.invoke(a1,10,20);  
    
                //獲取方法print(String,String);  
                Method m1=c.getMethod("print",String.class,String.class);  
                m.invoke(a1,"hello","world");  
    
                //獲取無參方法  
                Method m2=c.getMethod("print");// or c.getMethod("print",Object[]{});  
                m.invoke(a1);   //m.invoke(a1,new Object[]{});  
            }catch(Exception e){  
                e.printStackTrace();  
            }  
        }  
    }  
    
    class A{  
        public void print(int a,int b){  
            System.out.println(a+b);  
        }  
        public void print(String a,String b){  
            System.out.println(a.toUpperCase()+","+b.toLowerCase());  
        }  
    
        public void print(){  
            System.out.println("helloworld");  
        }  
    }  </pre><a href="/misc/goto?guid=4959618301072992214" target="_blank" title="派生到我的代碼片" style="text-indent:0;"></a></div>
    

    </div> </div> 最后通過一個栗子來理解集合泛型的本質:集合的泛型是去泛型化,java中集合的泛型,是防止錯誤輸入的,只在編譯階段有效,繞過編譯就無效了,所以可以通過方法的反射來操作,繞過編譯。

        class MethodDemo{
    public static void main(String[] args) {
    ArrayList list=new ArrayList();

            ArrayList<String> list1=new ArrayList<String>();  
            list1.add("hello");  
            //list1.add(20); //error  
            Class c1=list.getClass();  
            Class c2=list2.getClass();  
            System.out.println(c1==c2);  //true;  
    
            //反射的操作都是編譯之后的操作  
    
            /* 
            c1==c2結果返回true說明編譯之后集合的泛型是去泛型化 
            java中集合的泛型,是防止錯誤輸入的,只在編譯階段有效 
            繞過編譯就無效了 
            驗證:可以通過方法的反射來操作,繞過編譯 
            */  
            try{  
                Method m=c2.getMethod("add",Object.class);  
                m.invoke(list1,20);//繞過編譯的操作,就繞過了泛型  
                System.out.println(list1.size());  
                System.out.println(list1);  
            }catch(Exception e){  
                e.printStackTrace();  
            }  
        }  
    }  </pre><a href="/misc/goto?guid=4959618301072992214" target="_blank" title="派生到我的代碼片" style="text-indent:0;"></a></div>
    

    </div> </div>
    ---EOF---

     本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
     轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
     本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!