首頁 >Java >java教程 >Java反射的使用方法及範例分析

Java反射的使用方法及範例分析

WBOY
WBOY轉載
2023-05-06 16:31:08639瀏覽

    反射

    反射定義

    物件可以透過反射取得他的類,類別可以透過反射拿到所有⽅法(包括私有) 透過java語言中的反射機制可以操作字節碼文件,可以讀取和修改字節碼文件

    #反射的基本運用

    1. 取得類別物件

    a. forName()方法

    只需要知道類別名,在載入JDBC的時候會採用實例代碼

    public class test1 {
        public static void main(String[] args) throws ClassNotFoundException {
            Class name = Class.forName("java.lang.Runtime");
            System.out.println(name);
        }
    }

    Java反射的使用方法及範例分析

    b. 直接取得

    使用.class去取得對於的對象

    public class test1 {
        public static void main(String[] args) throws ClassNotFoundException {
            Class<?> name = Runtime.class;
            System.out.println(name);
        }
    }
    c. getClass()方法

    getClass來取得字節碼對象,必須要明確具體的類,然後建立物件

    public class test1 {
        public static void main(String[] args) throws ClassNotFoundException {
            Runtime rt = Runtime.getRuntime();
            Class<?> name = rt.getClass();
            System.out.println(name);
        }
    }
    d. getSystemClassLoader().loadClass()方法

    這個方法和forName類似,只要有類別名稱就可以了,但是差別在於,forName的靜態JVM會裝載類,並執行static()中的代碼

    public class getSystemClassLoader {
        public static void main(String[] args) throws ClassNotFoundException {
            Class<?> name = ClassLoader.getSystemClassLoader().loadClass("java.lang.Runtime");
            System.out.println(name);
        }
    }

    2. 取得類別方法

    a. getDeclaredMethods

    傳回類別或介面宣告的所有方法,包括public 、protected、private和預設方法,但不包含繼承的方法

    import java.lang.reflect.Method;
    
    public class getDeclaredMethods {
        public static void main(String[] args) throws ClassNotFoundException {
            Class<?> name = Class.forName("java.lang.Runtime");
            System.out.println(name);
            Method[] m = name.getDeclaredMethods();
            for(Method x:m)
                System.out.println(x);
        }
    }

    Java反射的使用方法及範例分析

    #b.getDeclaredMethod

    取得特定的方法,第一個參數是方法名,第二個參數是該方法的參數對應的class對象,例如這裡Runtime的exec方法參數為一個String,所以這裡的第二個參數是String.class

    import java.lang.reflect.Method;
    
    public class getDeclaredMethod {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
            Class<?> name = Class.forName("java.lang.Runtime");
            Method m = name.getDeclaredMethod("exec",String.class);
            System.out.println(m);
        }
    }
    c. getMethods

    傳回某個類別所有的public方法,包含繼承類別的public方法

    d. getMethod

    參數同理getDeclaredMethod

    #3. 取得成員變數

    同理Method的那幾個方法

    a. getDeclaredFields

    取得類別的成員的所有變數數組,但是不包括父類別的

    b .getDeclaredField(String name)

    取得特定的,參數是想要的方法的名稱

    c. getFields()

    同理,只能獲得public的,但是包括了父類別的

    d. getField(String name)

    同理,參數是想要的方法的名稱

    4. 取得建構子Constructor

    Constructor>[] getConstructors() :只回傳public建構子

    Constructor>[] getDeclaredConstructors() :傳回所有建構子

    # Constructor getConstructor(類別>... parameterTypes) : 匹配和參數配型相符的public建構子

    Constructor getDeclaredConstructor(類別>... parameterType> getDeclaredConstructor(類別>... parameterType> ) : 匹配和參數配型相符的建構子

    後面兩個方法的參數是對於方法的參數的型別的class對象,和Method的那個類似,例如String.class

    5. 反射創建類別物件

    newInstance

    #可以透過反射來產生實例化對象,一般我們使用Class對象的newInstance()方法來進行建立類別物件

    建立的方法是:只需要透過forname方法取得的class物件中進行newInstance方法建立即可

    Class c = Class.forName("com.reflect.MethodTest"); // 创建Class对象
    Object m1 =  c.newInstance(); // 创建类对象
    invoke

    # invoke方法位於java.lang.reflect.Method類別中,用來執行某個的物件的目標方法,一般會和getMethod方法配合進行呼叫。

    使用用法:

    public Object invoke(Object obj, Object... args)

    第一個參數為類別的實例,第二個參數為對應函數中的參數

    obj:從中呼叫底層方法的對象,必須是實例化物件

    args: 用於方法的調用,是一個object的數組,參數有可能是多個

    但需要注意的是,invoke方法第一個參數並不是固定的:

    • 如果呼叫這個方法是普通方法,第一個參數就是類別物件;

    • ##如果呼叫這個方法是靜態方法,第一個參數就是類別;

    透過一個例子去理解

    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class Invoke {
        public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
            Class c = Class.forName("Invoke");
            Object o = c.newInstance();
            Method m = c.getMethod("test");
            m.invoke(o);
        }
        public void test(){
            System.out.println("测试成功");
        }
    }

    Java反射的使用方法及範例分析

    簡單來說就是這樣

    方法.invoke(類別或類別物件)

    先forName拿到Class,再newInstance取得類別對象,再getMethod取得方法,然後呼叫

    Runtime的rce範例(存取限制突破)

    Runtime類別裡面有一個exec方法,可以執行指令

    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class Exec {
        public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
            Class c = Class.forName("java.lang.Runtime");
            Object o = c.newInstance();
            Method m = c.getMethod("exec",String.class);
            m.invoke(o,"/System/Applications/Calculator.app/Contents/MacOS/Calculator");
        }
    }

    但是發現報錯了

    Java反射的使用方法及範例分析

    出現這個問題的原因:

    • 使用的類別沒有無參考建構子

    • 使用的類別建構子是私有的

    那麼解決方案就是

    setAccessible(true);,用這個去突破存取限制

    Java.lang.reflect.AccessibleObject类是Field,Method和Constructor类对象的基类,可以提供将反射对象标记为使用它抑制摸人Java访问控制检查的功能,同时上述的反射类中的Field,Method和Constructor继承自AccessibleObject。所以我们在这些类方法基础上调用setAccessible()方法,既可对这些私有字段进行操作

    简单来说,私有的属性、方法、构造方法,可以通过这个去突破限制,xxx.setAccessible(true) 可以看到Runtime的构造方法是private的

    Java反射的使用方法及範例分析

    那么这里我们就可以这么去突破限制 先获取构造方法,然后setAccessible获取访问权限 然后再最后invoke里面,第一个参数写成con.newInstance()

    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class Exec {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
            Class c = Class.forName("java.lang.Runtime");
            Constructor con = c.getDeclaredConstructor();
            con.setAccessible(true);
            Method m = c.getMethod("exec",String.class);
            m.invoke(con.newInstance(),"/System/Applications/Calculator.app/Contents/MacOS/Calculator");
        }
    }

    Java反射的使用方法及範例分析

    这里有一个疑问,如果把con.newInstance单独提取出来,他打开计算器不会显示出来,但是后台的确是启动了,不知道啥原因

    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class Exec {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
            Class c = Class.forName("java.lang.Runtime");
            Constructor con = c.getDeclaredConstructor();
            con.setAccessible(true);
            Object o = con.newInstance();
            Method m = c.getMethod("exec",String.class);
            m.invoke(o,"/System/Applications/Calculator.app/Contents/MacOS/Calculator");
        }
    }

    后记

    反射中常用的几个重要方法:

    • 获取类的⽅法: forName

    • 实例化类对象的⽅法: newInstance

    • 获取函数的⽅法: getMethod

    • 执⾏函数的⽅法: invoke

    • 限制突破方法:setAccessible

    以上是Java反射的使用方法及範例分析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

    陳述:
    本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除