首页 > 编程语言 >java反射

java反射

时间:2023-04-16 16:26:09浏览次数:28  
标签:lang 反射 java class public 方法 Class

java反射

1.基本定义

Java反射机制是在运行状态时,对于任意一个类,都能够获取到这个类的所有属性和方法,

对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制。

可以说为对象可以通过反射获取他的类,类可以通过反射拿到所有⽅法(包括私有) 通过java语言中的反射机制可以操作字节码文件,可以读和修改字节码文件

2.获取类

1.forname()方法

package reflect;

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

运行结果:

image-20230416160634419

其中,className是要加载的类的完整限定名,例如java.lang.Runtime。调用forName()方法后,会返回一个Class对象,该对象包含了被加载的类的所有信息,包括类的构造函数、方法、字段等。

2.class直接获得

package reflect;

//获取类
public class forName{
   public static void main(String[] args) throws ClassNotFoundException {
        Class<?> name = Runtime.class;//用.class获取对象
        System.out.println(name);
        }
}

运行结果:

image-20230416160418109

这就是Runtime.class中直接获取Runtime的实例

3.getClass() 方法

obj.getClass() 如果上下⽂中存在某个类的实例 obj ,那么我们可以直接通过 obj.getClass() 来获取它的类

package reflect;



//获取类
public class forName{
   public static void main(String[] args) throws ClassNotFoundException {
           Runtime rt = Runtime.getRuntime();
           Class<?> name = rt.getClass();
           System.out.println(name);
           }
}

运行结果:

image-20230416160430654

因为上面出现了Runtime的实例我们通过rt.getClass()来获取从而获得了Runtime的类

4.getSystemClassLoader().loadClass()方法

forName类似,只要有类名就可以了,但是区别在于,forName的静态JVM会装载类,并执行static()中的代码

package reflect;



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

运行结果:

image-20230416160440966

3. 获取类方法

1.getDeclaredMethods

返回类或接口声明的所有方法,包括public、protected、private和默认方法,但是不包括继承的方法

package reflect;

import java.lang.reflect.Method;
//返回类或接口声明的所有方法,包括public、protected、private和默认方法,但是不包括继承的方法
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();//返回类或接口声明的所有方法,包括public、protected、private和默认方法,但是不包括继承的方法
        for(Method x:m)
            System.out.println(x);
    }
}

运行结果:

image-20230415221153147

2.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);
    }
}

运行结果

public java.lang.Process java.lang.Runtime.exec(java.lang.String) throws java.io.IOException
package reflect;

import java.lang.reflect.Method;
//获取特定的方法,第一个参数是方法名,第二个参数是该方法的参数对应的class对象,
// 例如这里Runtime的exec方法参数为一个String,所以这里的第二个参数是String.class
public class getDeclaredMethod {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
        Class<?> name = Class.forName("java.lang.Runtime");
        Method m = name.getDeclaredMethod("exec",java.lang.String[].class);
        System.out.println(m);
    }
}

运行结果

public java.lang.Process java.lang.Runtime.exec(java.lang.String[]) throws java.io.IOException

3.getMethods()方法

返回某个类所有的public方法,包括继承类的public方法

package reflect;

import java.lang.reflect.Method;

public class getMethods{
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> name = Class.forName("java.lang.Runtime");
        System.out.println(name);
        Method[] m = name.getMethods();//返回某个类所有的public方法,包括继承类的public方法
        for(Method x:m)
            System.out.println(x);
    }
}

运行结果

image-20230415221110006

4.getMethod

//getMethod第一个参数是方法名,第二个参数是该方法的参类型,
//因为存在同方法名不同参数这种情况,所以只有同时指定方法名和参数类型才能唯一确定一个方法

package reflect;

import java.lang.reflect.Method;

public class getMethod {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
        Class<?> name = Class.forName("java.lang.Runtime");
        Method m = name.getMethod("exec",String.class);//数//getMethod第一个参数是方法名,第二个参数是该方法的参类型,
//因为存在同方法名不同参数这种情况,所以只有同时指定方法名和参数类型才能唯一确定一个方法
        System.out.println(m);
    }
}

运行结果

image-20230416160822310

5.区别

getMethod和getDeclaredMethod

//getMethod():获取自身能用所有的公共方法。1.类本身的public 2.继承父类的public 3.实现接口的public
//getDeclaredMethod():获取类自身声明的所有方法。

getDeclaredMethods和getMethods

//返回类或接口声明的所有方法,包括public、protected、private和默认方法,但是不包括继承的方法
//返回某个类所有的public方法,包括继承类的public方法

4.获取成员变量

1. getDeclaredFields

获取类的成员的所有变量数组,但是不包括父类的

myclass类

public class MyClass {
    private int myInt;
    public String myString;
    protected boolean myBoolean;

    // ...其他代码...
}

main类

package reflect;

import java.lang.reflect.Field;
//使用该方法需要先获取目标类的Class对象,然后调用该对象的getDeclaredFields方法即可。
// 需要注意的是,该方法只能获取该类中声明的字段,而无法获取父类中的字段。
public class Main {
    public static void main(String[] args) {
        Class<MyClass> cls = MyClass.class;//调用了MyClass的对象,其中.class是为了获取对象
        Field[] fields = cls.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field.getName());
        }}}

运行结果

image-20230416160837936

2.getDeclaredField(String name)

//使用该方法需要先获取目标类的Class对象,然后调用该对象的getDeclaredField方法并传入字段名称即可
//获取了MyClass中定义的成员变量

public class Main {
    public static void main(String[] args) throws NoSuchFieldException {
        Class<MyClass> cls = MyClass.class;//获取了Myclass的对象
        Field field = cls.getDeclaredField("myInt")
        //使用该方法需要先获取目标类的Class对象,然后调用该对象的getDeclaredField方法并传入字段名称即可
        //获取了MyClass中定义的成员变量
        System.out.println(field.getName()); // 输出 "myInt"
    }
}

运行结果

image-20230416160851245

3.getFields()

只能获得public的,但是包括了父类的

public class Main {
    public static void main(String[] args) {
        Class<MyClass> cls = MyClass.class;
        Field[] fields = cls.getFields();
        for (Field field : fields) {
            //使用该方法需要先获取目标类的Class对象,然后调用该对象的getFields方法即可。
            // 需要注意的是,该方法只能获取该类及其父类中声明为公共的字段
            System.out.println(field);
        }
    }
}

运行结果

image-20230416160902422

4.getField

用于获取一个类中指定名称的公共字段(即公共成员变量)。

public class Main {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class<MyClass> cls = MyClass.class;
        Field field = cls.getDeclaredField("myInt");
        //使用该方法需要先获取目标类的Class对象,然后调用该对象的getDeclaredField方法并传入字段名称即可
        //获取了MyClass中定义的成员变量
        System.out.println(field.getName()); // 输出 "myInt"
    }
}

运行结果

image-20230412151207647image-20230416160917145

5. 获取构造函数Constructor

  1. Constructor<?>[] getConstructors(): 该方法返回当前类或接口的所有 public 构造函数,包括从超类继承的构造函数,但不包括 private 和受保护的构造函数。这个方法可以用于获取当前类可被外部访问到的、创建对象的构造函数。
  2. Constructor<?>[] getDeclaredConstructors(): 该方法返回当前类或接口声明的所有构造函数,包括 publicprotecteddefault(默认)private 修饰符的构造函数,但不包括从超类继承的构造函数。这个方法可以用于获取所有在当前类中声明的构造函数。
  3. Constructor<T> getConstructor(Class<?>... parameterTypes): 该方法返回一个 public 构造函数,其中参数 parameterTypes 指定了要匹配的构造函数的参数类型。如果没有相应的构造函数,则会抛出 NoSuchMethodException 异常。这个方法可以用于获取指定参数类型的 public 的构造函数,方便我们通过反射机制创建 Object 对象时指定特定参数的构造函数。
  4. Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes): 该方法返回一个由参数 parameterTypes 指定的构造函数对象,包括 publicprotecteddefault(默认)private 修饰符的构造函数。这个方法可以用于获取指定参数类型的构造函数,而不必考虑其访问修饰符

6. 反射创建类对象

1.newInstance

可以通过反射来生成实例化对象,一般我们使用Class对象的newInstance()方法来进行创建类对象

创建的方法就是:只需要通过forname方法获取到的class对象中进行newInstance方法创建即可

Class.newInstance() 是 Class 类的一个方法,它会调用目标类的无参构造方法创建一个新的对象。使用该方法创建对象时,要求目标类必须有一个无参构造方法

package reflect;
import java.lang.reflect.Constructor;

public class MyClass {
    private int value;

    public MyClass() {
        this.value = 0;
    }

    public int getValue() {
        return value;
    }

    public static void main(String[] args) throws Exception {
        Class<?> cls = MyClass.class;
        System.out.println(cls);
        MyClass obj = (MyClass) cls.newInstance();
        System.out.println(obj);
        System.out.println(obj.getValue());
    }
}

我们首先获取了 MyClass 类的 Class 对象,然后使用 newInstance() 方法创建一个 MyClass 对象实例,并调用 getValue() 方法输出对象实例的值。由于 MyClass 类中存在默认构造函数,因此可以直接使用 newInstance() 方法创建对象实例。

运行结果

image-20230416160928737

2.通过 Constructor 的 newInstance() 方法

Constructor.newInstance() 是 Constructor 类的一个方法,它可以调用任意一个构造方法来创建对象。使用该方法创建对象时,需要指定构造方法的参数列表就是42


package reflect;
import java.lang.reflect.Constructor;

public class MyClass {
    private int value;

    public MyClass() {
        this.value = 0;
    }

    public MyClass(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    public static void main(String[] args) throws Exception {
        Class<?> cls = MyClass.class;
        Constructor<?> constructor = cls.getConstructor(int.class);
        MyClass obj = (MyClass) constructor.newInstance(42);
        System.out.println(obj.getValue());
    }
}

我们首先获取了 MyClass 类的 int 参数构造函数,然后使用 newInstance() 方法创建一个 MyClass 对象实例,并将参数 42 传递给构造函数。最后,我们调用 getValue() 方法输出对象实例的值。

3.invoke

invoke方法位于java.lang.reflect.Method类中,用于执行某个的对象的目标方法,一般会和getMethod方法配合进行调用。

使用用法:

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

第一个参数为类的实例,第二个参数为相应函数中的参数

obj:从中调用底层方法的对象,必须是实例化对象 args: 用于方法的调用,是一个object的数组,参数有可能是多个

但需要注意的是,invoke方法第一个参数并不是固定的:

  • 如果调用这个方法是普通方法,第一个参数就是类对象;
  • 如果调用这个方法是静态方法,第一个参数就是类;
package reflect;

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("reflect.Invoke"); // 修改类名为 reflect.Invoke
        Object o = c.newInstance();
        Method m = c.getMethod("test");
        m.invoke(o);
    }

    public void test(){
        System.out.println("测试成功");
    }
}

先forName拿到Class,再newInstance获取类对象,再getMethod获取方法,然后调用

7.Runtime的rce例子(访问限制突破)

Runtime类里面有一个exec方法,可以执行命令

package reflect;

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");//获取类
        Object o = c.newInstance();//创建类的对象
        Method m = c.getMethod("exec",String.class);//获取public的属性,不可以获取私有属性,调用了exec方法
        m.invoke(o, "C:\\Windows\\System32\\calc.exe");//获取从c.newInstance();得到的类的实例,

    }
}

运行结果报错

Exception in thread "main" java.lang.IllegalAccessException: class reflect.Exec cannot access a member of class java.lang.Runtime (in module java.base) with modifiers "private"
        at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:361)
        at java.base/jdk.internal.reflect.Reflection.ensureMemberAccess(Reflection.java:99)
        at java.base/java.lang.Class.newInstance(Class.java:579)
        at reflect.Exec.main(Exec.java:10)

出现这个问题的原因:

  1. 使用的类没有无参构造函数
  2. 使用的类构造函数是私有的

因为获取的类是私有的属性而getMethod只能获取公共属性

那么解决方案就是setAccessible(true);,用这个去突破访问限制

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

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

那么这里我们就可以这么去突破限制 先获取构造方法,然后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);//使用exec中的私有属性,本身getMethod不可以调用
        m.invoke(con.newInstance(),"C:\\Windows\\System32\\calc.exe");//con.newInstance()是调用任意的构造方法
    }
}

在命令行中输入java --add-opens java.base/java.lang=ALL-UNNAMED Exec.java来执行

运行结果

image-20230416155740802

执行成功得到了计算器

标签:lang,反射,java,class,public,方法,Class
From: https://www.cnblogs.com/jxdgx/p/17323452.html

相关文章

  • Java中abstract(抽象类)
    1、概述(1)只给出方法定义而不具体实现的方法被称为抽象方法,抽象方法是没有方法体的,在代码的表达上就是没有“{}”。使用abstract修饰符来表示抽象方法和抽象类。(2)abstract修饰符表示所修饰的类没有完全实现,还不能实例化。如果在类的方法声明中使用abstract修饰符,表明该方法是一个......
  • Java JDBC批处理添加出现问题,求解决方案
    晚辈使用JDBC批处理时出现一个问题,使用addBatch()方法将记录加入批处理中,我想让这五千条记录每达到一千条记录再执行,以此提高效率,可最后执行在数据库查看时仅五条记录,我尝试将 preparedStatement.executeUpdate();提出if语句,虽然是有五千条记录,但效率相当的慢请求前辈们给出解决......
  • java多版本共存
    原理通过脚步改变path环境变量来实现java多版本切换.这里使用的是Win10.一,删除原有的java搜索路径.在安装高版本的java时,会添加一个路径到path环境变量中,如我的C:\ProgramFiles\CommonFiles\Oracle\Java\javapath,在该目录下存有java.exe和javac.exe等.在用cmd执行命令时,......
  • java语句
    语句语句是以;或}或)结尾的一段代码,目的是执行某些操作,并且没有返回值。语句块和方法体也算语句。所以,语句是可以嵌套的。函数体是一个有名称的语句。对于拥有子语句的语句,称为复合语句,否则就是简单语句。 简单语句空语句:;;;方法调用语句:setVal(34);表达式语句:i......
  • javascript 把嵌套的 map 转成 object,再转 json 字符串
    使用JSON.stringify转map时发现并没有转成想要的JSON数据,搜索发现要转成Object才能够转成完整的JSON,用递归转换:constconvertNestedMapToObject=(map)=>{if(mapinstanceofMap){map.forEach((v,k)=>{......
  • javascript常用的循环对比及性能分析
    结论:js中的for循环只有在处理百万条数据以上才会展示出他的强大性能,和看出明显优势,但是在百万条数据往下甚至到个位数的数据量通常都是for和while还有do...while不相上下,反而后两者更加优势明显下面是测试耗时截图(在不同浏览器也会有所不同,我这是Chrome版本111.0.5563.149)......
  • 使用文本编辑器和jdk开发简单JavaSE工程
    一个在eclipse下简单的示例 运行时可以在代码编辑器页面右键run测试发布时可以file->export->runnablejarfile 如果没有eclipse只有jdk和文本编辑器呢? 创建和eclipse工程相似的目录 目录JAVASE01binlibsrc\com\zt\javase01\IODemo.java 下面的命令是在编译class......
  • Java中常用排序算法及示例-冒泡排序、希尔排序、选择排序、插入排序、合并排序、基数
    场景Java中需要对数据进行排序处理,常用的排序算法以及示例进行归纳整理。注:实现1、冒泡排序冒泡排序法又称为交换排序法,原理是从第一个元素开始,比较相邻元素的大小,若大小顺序有误,则对调后再进行下一个元素的比较。如此扫描一次之后就可以确保最后一个元素位于正确的顺序,接着逐步进......
  • Java中常用算法及示例-分治、迭代、递归、递推、动态规划、回溯、穷举、贪心
    场景1、分治算法的基本思想是将一个计算复杂的问题分成规模较小、计算简单的小问题求解,然后综合各个小问题,得到最终答案。2、穷举(又称枚举)算法的基本思想是从所有可能的情况中搜索正确的答案。3、迭代法(IterativeMethod)无法使用公式一次求解,而需要使用重复结构(即循环)重复执......
  • Java中创建线程的方式以及线程池创建的方式、推荐使用ThreadPoolExecutor以及示例
    场景Java中创建线程的方式有三种1、通过继承Thread类来创建线程定义一个线程类使其继承Thread类,并重写其中的run方法,run方法内部就是线程要完成的任务,因此run方法也被称为执行体,使用start方法来启动线程。2、通过实现Runanle接口来创建线程首先定义Runnable接口,并重写Runnable接口......