java代码审计之反射

java反射

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法,这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。

Java反射机制主要提供了以下功能:在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法

反射与正常创建的区别:

  • 正常创建对象的方法(使用 new 关键字)在编译时就已经知道要创建哪个类的实例。
  • 反射创建对象的方法(使用 Class.forName()newInstance())在编译时不知道要创建哪个类的实例,而是在运行时动态指定。

1. 获取包名

方式一:利用Object类的getClass()方法,但是要求必须先产生指定类的对象才可以,几乎不用。

import java.util.Date;

public class ReflectionTest {
    public static void main(String[] args) {
        Date date = new Date();				//实例化一个date对象
        Class<?> cls = date.getClass();	     
        System.out.println(cls);			 //输出类的包名
    }	
}

image-20240408102532462

方式二:利用“类.class”的形式取得Class类的对象

类.class 语法在 Java 中用于获取类的 Class 对象,并且它属于类加载机制的一部分。与 Class.forName 方法类似,类.class 语法也是获取 Class 对象的一种方式,但它的行为有所不同。

类.class 与类加载

  • 编译时常量类.class 语法在编译时解析,不会触发类的初始化。它仅仅是获取 Class 对象的引用,不会导致类加载过程(加载、链接和初始化)中的任何阶段被执行。
  • 直接引用:使用 类.class 语法直接引用类的 Class 对象,这个引用在编译期就确定了。
public class ReflectionTest {
    public static void main(String[] args) {
        Class<?>cls = java.util.Date.class;
        System.out.println(cls);
    }
}

image-20240408103720929

方式三:利用Class类提供的一个方法(forName)完成,在系统架构中使用

Class.forName 方法会根据提供的类名查找并加载类,返回对应的 Class 对象。这个过程会触发类加载过程,包括类的加载、链接和初始化。具体来说,Class.forName 执行的步骤如下:

  1. 类加载:将类的字节码从文件或其他来源加载到 JVM 中。
  2. 类链接:
    • 验证:验证类的字节码是否符合 JVM 的规范。
    • 准备:为类的静态变量分配内存,并初始化为默认值。
    • 解析:将符号引用替换为直接引用。
  3. 类初始化:执行类的静态初始化块和静态变量的赋值操作。
public class ReflectionTest {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?>cls = Class.forName("java.util.Date");
        System.out.println(cls);
    }
}

image-20240408104417025

方式四:利用Class类提供的一个方法(ClassLoader)完成,在系统架构中使用

ClassLoader.getSystemClassLoader().loadClass("java.util.Date") 是使用系统类加载器加载指定类的方法。这种方式与 Class.forName类.class 语法有所不同,它提供了更细粒度的控制,并且允许你使用特定的类加载器来加载类。下面详细描述这种方法的行为及其与其他方法的区别。

使用 ClassLoader.loadClass 方法加载类

ClassLoader.loadClass 方法用于通过指定的类加载器加载类,它会执行类加载和链接,但默认情况下不会初始化类。这与 Class.forName 方法不同,后者默认情况下会触发类的初始化。

public class ReflectionTest02 {
    public static void main(String[] args) throws ClassNotFoundException {
        Class date = ClassLoader.getSystemClassLoader().loadClass("java.util.Date");
        System.out.println(date);
    }
}

2. 对象实例化

实例化对象可以有两种形式,一种是通过关键字new,另外一种是通过反射机制完成

关键词new

import java.util.Date;

public class ReflectionTest {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        Date date = new Date();				//实例化一个date对象
        System.out.println(date);
    }
}

通过反射获取包名后实例化(无参数)

import java.util.Date;

public class ReflectionTest {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        Class<?>cls = Class.forName("java.util.Date");
        Date date = (Date)cls.newInstance();
        System.out.println(date);
    }
}

通过反射获取包名后实例化(有参数)

2.1.获取构造方法

public class Book {
    public Book(String name){
        System.out.println("我的名字是"+"name");
    }
    public Book(String name,double price){
        System.out.println("我的名字是"+name+",我的价格是"+price);
    }

    private Book(String name,double price,String author){
        System.out.println("我的名字是"+name+",我的价格是"+price+",我是被"+author+"创造出来的");
    }
}

获取公有的构造方法

import java.lang.reflect.Constructor;

public class BookTest {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("Book");
        //进行实例化
        Constructor[] conArr = cls.getConstructors();
        for (Constructor c : conArr){
            System.out.println(c);
        }
    }

}

获取公有+私有的构造方法

import java.lang.reflect.Constructor;

public class BookTest {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("Book");
        //进行实例化
        Constructor[] conArr = cls.getDeclaredConstructors();
        for (Constructor c : conArr){
            System.out.println(c);
        }
    }
}

2.2通过构造方法实例化对象

class Book {
    private String title;	//创建title属性,类型为私有
    private double price;	//创建price属性,类型为私有

    public Book(String title,double price){
        this.title = title;
        this.price = price;
    }
    public void setTitle(String title) {
        this.title = title;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    //重写String方法
    @Override
    public String toString() {
        return "图书名称" +this.title + "图书价格"+this.price;
    }
}

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class BookTest {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class<?> cls = Class.forName("Book");
        Constructor<?> cons = cls.getConstructor(String.class,double.class);
        Book book = (Book)cons.newInstance("JAVA反射学习",79.8);
        System.out.println(book);
    }
}

3. 获取public方法与属性

3.1 获取public方法

  1. 新建一个Book类,并且有以下几个方法
public class Book {
    private String title;	//创建title属性,类型为私有
    private double price;	//创建price属性,类型为私有

    public void setTitle(String title) {
        this.title = title;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public void appraise(){
        System.out.println("我的书名是"+this.title+",我十分好看,并且物美价廉,只要"+this.price);
    }
}
  1. 使用反射获取方法
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class BookTest {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class<?> cls = Class.forName("Book");

        //进行实例化
        Book book = (Book)cls.newInstance();

        //获取到setPrice方法
        Method setPriceMesthod = cls.getMethod("setPrice", double.class);
        //将方法与对象绑定,并传入对应的值
        setPriceMesthod.invoke(book,19.56);

        Method setTitleMethod  = cls.getMethod("setTitle", String.class);
        setTitleMethod.invoke(book,"JAVA反射");

        Method appraiseMethod  = cls.getMethod("appraise");
        appraiseMethod.invoke(book);
    }
}

3.2 获取public属性

  1. 新建一个Book类,并且有以下两个属性
public class Book {
    public String title;	//创建title属性,类型为私有
    public double price;	//创建price属性,类型为私有

    public void appraise(){
        System.out.println("我的书名是"+this.title+",我十分好看,并且物美价廉,只要"+this.price);
    }
}
  1. 获取方法并传入值
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class BookTest {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("Book");

        //进行实例化
        Book book = (Book)cls.newInstance();

        //获取到属性并为其赋值,并与实例化出来的对象绑定
        Field title = cls.getDeclaredField("title");
        title.set(book,"《java反射》");

        Field price  =cls.getDeclaredField("price");
        price.set(book,9.9);

        book.appraise();
    }
}

4. 获取private方法与属性

4.1 获取private方法

  1. 新建一个Book类,并且有以下几个方法
public class Book {
    private String title;	//创建title属性,类型为私有
    private double price;	//创建price属性,类型为私有

    private void setTitle(String title) {
        this.title = title;
    }

    private void setPrice(double price) {
        this.price = price;
    }

    private void appraise(){
        System.out.println("我的书名是"+this.title+",我十分好看,并且物美价廉,只要"+this.price);
    }
}
  1. 获取private方法,获取方法以后,需要.setAccessible(true)setAccessible:阻止java对修饰符的检查
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class BookTest {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("Book");

        //进行实例化
        Book book = (Book)cls.newInstance();

        //获取到setPrice方法
        Method setPriceMesthod = cls.getDeclaredMethod("setPrice", double.class);
        //设置setAccessible为true
        setPriceMesthod.setAccessible(true);
        //将方法与对象绑定,并传入对应的值
        setPriceMesthod.invoke(book,19.56);

        Method setTitleMethod  = cls.getDeclaredMethod("setTitle", String.class);
        setTitleMethod.setAccessible(true);
        setTitleMethod.invoke(book,"JAVA反射");

        Method appraiseMethod  = cls.getDeclaredMethod("appraise");
        appraiseMethod.setAccessible(true);
        appraiseMethod.invoke(book);
    }
}

4.2 获取private属性

public class Book {
    private String title;	//创建title属性,类型为私有
    private double price;	//创建price属性,类型为私有
}
import java.lang.reflect.Field;
public class BookTest {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("Book");

        //进行实例化
        Book book = (Book)cls.newInstance();

        //获取到属性并为其赋值,并与实例化出来的对象绑定
        Field title = cls.getDeclaredField("title");
        title.setAccessible(true);
        title.set(book,"《java反射》");


        Field price  =cls.getDeclaredField("price");
        price.setAccessible(true);
        price.set(book,199.9);

        System.out.println("我的名字是"+title.get(book)+",我的价格是"+price.get(book));
    }
}
4.2.2 获取final修饰的private属性,并修改
public class Book {
    private String title;	//创建title属性,类型为私有
    final private double price=9.9;	//创建price属性,类型为私有
}
import java.lang.reflect.Field;
public class BookTest {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("Book");

        //进行实例化
        Book book = (Book)cls.newInstance();

        //获取到属性并为其赋值,并与实例化出来的对象绑定
        Field title = cls.getDeclaredField("title");
        title.setAccessible(true);
        title.set(book,"《java反射》");


        Field price  =cls.getDeclaredField("price");
        price.setAccessible(true);
        price.set(book,199.9);

        System.out.println("我的名字是"+title.get(book)+",我的价格是"+price.get(book));
    }
}

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class BookTest {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("Book");

        //进行实例化
        Book book = (Book)cls.newInstance();

        //获取到属性并为其赋值,并与实例化出来的对象绑定
        Field title = cls.getDeclaredField("title");
        title.setAccessible(true);
        title.set(book,"《java反射》");


        Field price  =cls.getDeclaredField("price");
        
        //删除final
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(price, price.getModifiers() & ~Modifier.FINAL);
        price.setAccessible(true);
        price.set(book,199.9);

        System.out.println("我的名字是"+title.get(book)+",我的价格是"+price.get(book));
    }
}

5. 获取所在包是否执行静态代码块

public class Book {
    // 静态变量
    private static int bookCount;
    // 静态代码块,类加载时执行
    static {
        bookCount = 0;
        System.out.println("执行静态代码块,初始化bookCount为:" + bookCount);
    }

}

执行

public class BookTest {
    public static void main(String[] args) throws Exception {
        Class<?> cls= Class.forName("Book");//执行
    }
}

不执行

public class BookTest {
    public static void main(String[] args) throws Exception {
        Class<?>cls1 = Book.class;         //没执行
    }
}

不执行

public class BookTest {
    public static void main(String[] args) throws Exception {
        Class book = ClassLoader.getSystemClassLoader().loadClass("Book");//没执行
    }
}

6. 获取内部类

public class Book {
    public void outMethod(){
        System.out.println("我是外部类");
    }
    class inClass{
        public void inMethod(){
            System.out.println("我是内部类的方法");
        }
    }
}
public class BookTest {
    public static void main(String[] args) {
        Book book = new Book();
        Book.inClass innerObject = book.new inClass(); 
        innerObject.inMethod(); 
    }
}
  1. import java.lang.reflect.Method; - 导入了 Method 类,以便在后面使用 Java 反射 API。
  2. public class BookTest { - 定义了名为 BookTest 的公共类。
  3. public static void main(String[] args) throws Exception { - main 方法是 Java 程序的入口点,在这里开始执行。方法的签名表明它可以抛出任何异常。
  4. Class<?> out = Class.forName("Book"); - 使用 Class.forName() 方法加载名为 “Book” 的类。这会返回一个 Class 对象,代表 Book 类。
  5. Class<?> in = Class.forName("Book$inClass"); - 使用 Class.forName() 方法加载名为 “Book i n C l a s s " 的内部类。内部类的名称由外部类的名称后面跟上 " inClass" 的内部类。内部类的名称由外部类的名称后面跟上 " inClass"的内部类。内部类的名称由外部类的名称后面跟上"” 符号,再跟上内部类的名称。
  6. inClass in1 = in.getDeclaredConstructors()[0].newInstance(out.newInstance()); - 创建内部类的一个实例。首先,通过 in.getDeclaredConstructors() 获取内部类的所有构造函数,然后选择第一个(通常是默认构造函数)。然后,使用 newInstance() 方法创建一个内部类的新实例,传递外部类的新实例作为参数,以确保内部类实例能够访问外部类的内容。
  7. Method method = in.getDeclaredMethod("inMethod"); - 使用 getDeclaredMethod() 方法获取名为 “inMethod” 的方法的引用。这个方法被定义在内部类 inClass 中。
  8. method.invoke(in1); - 使用 invoke() 方法调用 inMethod() 方法。invoke() 方法接受要调用的对象(这里是内部类的实例)作为第一个参数。这行代码会执行内部类的 inMethod() 方法。
import java.lang.reflect.Method;

public class BookTest {
    public static void main(String[] args) throws Exception {
        Class<?> out = Class.forName("Book");
        Class<?> in = Class.forName("Book$inClass");
        inClass in1 = in.getDeclaredConstructors()[0].newInstance(out.newInstance())
        Method method = in.getDeclaredMethod("inMethod");
        method.invoke(in1);
    }
}
public class BookTest {
    public static void main(String[] args) throws Exception {
        // Load the classes
        Class<?> out = Class.forName("Book");
        Class<?> in = Class.forName("Book$inClass");

		//在Java中,如果要通过反射创建内部类的实例,需要在创建内部类实例时提供外部类的实例作为参数。这是因为内部类实例在创建时需要与外部类实例相关联,以便能够访问外部类的成员变量和方法。
        //通过out.newInstance()创建了一个外部类 Book 的实例。然后利用获取到的内部类 inClass 的构造函数,通过 in.getDeclaredConstructor(out) 获得了 inClass 的构造函数对象。最后,调用 newInstance() 方法,使用外部类 Book 的实例作为参数,创建了 inClass 的一个实例,这个实例被赋值给 inInstance 变量。
        Object inInstance = in.getDeclaredConstructor(out).newInstance(out.newInstance());
        Method method = in.getDeclaredMethod("inMethod");
        method.invoke(inInstance);
    }
}

7. 调用计算器

类方法调用:

import java.io.IOException;

public class calcTest {
    public static void main(String[] args) throws IOException {
        Runtime.getRuntime().exec("calc");
    }
}

反射方法调用:

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

public class calcTest {
    public static void main(String[] args) throws Exception {
       Class<?> calcClass = Class.forName("java.lang.Runtime");
        Constructor cons = calcClass.getDeclaredConstructor();
        cons.setAccessible(true);
        Runtime CalcIn = (Runtime) cons.newInstance();

        Method runtimeMethod = calcClass.getDeclaredMethod("exec",String.class);

        runtimeMethod.invoke(CalcIn,"calc");
    }
}

getRuntime方法是Runtime类的静态方法,它返回一个Runtime对象的实例。因此,我们可以将invoke方法的第一个参数设为null,因为我们不需要一个特定的对象实例来调用这个静态方法。

import java.lang.reflect.Method;

public class calcTest {
    public static void main(String[] args) throws Exception {
        Class<?> runtimeClass = Class.forName("java.lang.Runtime");
        // 获取 getRuntime 方法
        Method getRuntimeMethod = runtimeClass.getMethod("getRuntime");
        // 调用 getRuntime 方法获取 Runtime 实例
        Object runtimeObj = getRuntimeMethod.invoke(null);

        // 获取 exec 方法
        Method execMethod = runtimeClass.getMethod("exec", String.class);
        // 调用 exec 方法执行命令
        execMethod.invoke(runtimeObj, "calc");
    }
}
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class calcTest {
    public static void main(String[] args) throws Exception {
        Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke(Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(null), "calc");
    }
}
  • 11
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值