java反射簡解

weixin_34037977發表於2016-08-06

1、首先一個問題,什麼是類,類是不是物件?

我們總是說我們new一個物件出來

那麼我們還沒有new的時候,建造的那個java類是不是物件呢?

它是java.lang.Class的物件

 

對於反射我們首先要知道的就是什麼是Class類

java.lang.Class到底是什麼,有啥用。

首先我們要得到Class,我們有三種可以得到的方法,程式碼如下,c1,c2,c3就是我們需要的Class

//任何一個類都有一個隱含的成員變數叫做calss
Class c1 = Man.class;
        
//通過物件的getClass方法獲取class
Class c2 = man1.getClass();
        
//c1和c2表示了Man的類型別,也就是class type
        
Class c3 = null;
c3 = Class.forName("com.clazz.test.Man");

 

需要知道的是類的類型別是一樣的,都指的是一個,所以c1,c2,c3都是一樣的。都是相等的。

那我們現在已經拿到了Class,可以用它來幹嘛呢?

我們可以通過類的類型別直接建立一個物件,也就是說,沒有new了。

需要注意的是下面的程式碼中newInstance方法會呼叫類的無參構造方法

//通過類的類型別建立物件。
Man man2 = (Man) c1.newInstance();

下面是所有的測試程式碼

package com.clazz.test;

/**
 * Class類的描述測試
 * @author XX
 *
 */
public class ClazzDemo1 {

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException
    {
        //Man是java.lang.Class的物件
        //任何一個類都是class類的例項物件
        
        Man man1 = new Man();
        //man1是Man類的例項物件
        
        //任何一個類都有一個隱含的成員變數叫做calss
        Class c1 = Man.class;
        
        //通過物件的getClass方法獲取class
        Class c2 = man1.getClass();
        
        //c1和c2表示了Man的類型別,也就是class type
        
        Class c3 = null;
        c3 = Class.forName("com.clazz.test.Man");
        
        //通過類的類型別建立物件。
        Man man2 = (Man) c1.newInstance();
    }
}


class Man{
    public Man(){
        System.out.println("建立了這個類");
    }
}

 

 

2、靜態載入類和動態載入類

new建立物件是靜態載入,在編譯時刻就需要載入所有可能使用到的類。

通過動態載入類,可以在執行時刻載入使用到的類

也就是我們的c3使用的方法

package com.clazz.test;

public class DynamicLoadClass {

    public static void main(String args[]) throws ClassNotFoundException, InstantiationException, IllegalAccessException
    {
        //靜態載入類,直接new出物件,呼叫方法,一般都是使用這個,但是一旦沒有Student類,編譯就會報錯
        //而且如果需要使用老師物件那麼程式碼需要重新寫過很多,要把new物件重新寫過,
        Student s1 = new Student();
        s1.speck();
        
        //通過動態載入類,編譯時刻,無論學生還是老師類存不存在,都可以編譯通過,只有當執行時刻才進行載入
        //好處是如果你需要新增一個類,直接實現IPeople介面,只要forName的名字正確即可,而且這個名字可以
        //作為一個引數傳進來然後進行修改
        Class c1 = Class.forName("com.clazz.test.Student");
        IPeople p1 = (IPeople) c1.newInstance();
        p1.speck();
    }
}
package com.clazz.test;

public class Student implements IPeople{

    public void speck()
    {
        System.out.println("我是學生");
    }
}
package com.clazz.test;

public interface IPeople {

    public void speck();
}

 

3通過反射拿到一個類的所有的資訊

下面這個就是反射最基本的用法,也是反射最神奇的地方

package com.clazz.util;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

import com.clazz.test.Student;

/**
 * calss類的測試工具類
 * @author XX
 *
 */
public class ClazzUtil {

    public static void main(String args[])
    {
        //通過反射能拿到類的所有方法資訊
        printAll(new Student());
    }
    
    /**
     * 獲取這個物件的資訊並列印出來
     */
    public static void printAll(Object object)
    {
        Class c = object.getClass();
        
        //列印該類的名字
        System.out.println("類的名字" + c.getName());
        
        //列印該類的所有方法(包含從父類繼承來的)(不包含私有的方法)
        Method[] ms = c.getMethods();
        for (Method method : ms) {
            System.out.println("方法:" + method.getName() + "          方法的返回值:" + method.getReturnType().getName());
            
            //獲取方法的引數
            Class<?>[] par = method.getParameterTypes();
            for (Class<?> class1 : par) {
                System.out.println("方法的入參:" + class1.getName());
            }
        }
        
        //列印該類的方法(不包含從父類繼承來的)包含私有方法
        Method[] ms2 = c.getDeclaredMethods();
        for (Method method : ms2) {
            System.out.println("方法1:" + method.getName() + "          方法的返回值:" + method.getReturnType().getName());
        }
        
        //獲取成員變數(不包含私有變數)
        Field[] fs = c.getFields();
        for (Field field : fs) {
            System.out.println("成員變數: " + field.getName());
        }
        
        //獲取成員變數(包含私有變數)
        Field[] fs1 = c.getDeclaredFields();
        for (Field field : fs1) {
            System.out.println("成員變數: " + field.getName());
        }
        
        //獲取構造方法
        Constructor[] con = c.getDeclaredConstructors();
        for (Constructor constructor : con) {
            System.out.println("構造方法:" + constructor.getName());
        }
    }
}
package com.clazz.test;

public class Student implements IPeople{

    private int a1;
    public int a12;
    
    public void speck()
    {
        System.out.println("我是學生");
    }
    
    private void speck2()
    {
        System.out.println("我是學生");
    }
}

 

4通過反射呼叫類的方法

這個是反射的應用,通過反射呼叫一個類的方法

package com.clazz.test;

import java.lang.reflect.Method;

/**
 * @author XX
 *
 */
public class ReflectMethod {

    public static void main(String args[]) throws Exception
    {
        A a = new A();
        
        Class c = a.getClass();
        
        Method m = c.getMethod("add", int.class,int.class);
        
        System.out.println(m.invoke(a, 1,2));
    }
}


class A{
    public int add(int a,int b)
    {
        return a+b;
    }
    private int minus(int a,int b)
    {
        return a-b;
    }
}

 

 

5利用反射解釋ArrayList的泛型

 

package com.clazz.test;

import java.lang.reflect.Method;
import java.util.ArrayList;

public class ArrayListRflect {

    
    public static void main(String args[]) throws Exception
    {
        /**
         * 下面的程式碼用於證明,在ArrayList中,泛型只在編譯之前有效,編譯之後泛型對於list的限制
         * 是無效的。利用反射就能繞過編譯這個階段在ArrayList新增本不屬於該泛型的值
         */
        ArrayList<String> list = new ArrayList<String>();
        
        list.add("哈哈");
        //list.add(1);如果原來這麼寫,編譯會報錯的
        
        //那麼怎麼在list放個int的1呢?
        
        //獲取list的類型別
        Class c = list.getClass();
        //通過類型別拿到方法
        Method m = c.getMethod("add",Object.class);
        //通過反射呼叫方法
        m.invoke(list, 1);
        
        System.out.println(list);
        //結果為:[哈哈, 1]
        
        //所以證明了上面的觀點,也同時很好的證明了反射多神奇
    }
}

相關文章