Java反射

本文介绍了Java反射的基本概念,展示了如何使用反射动态创建对象、调用方法和访问属性,同时讨论了反射的优缺点,以及Class类的常用方法和获取Class类对象的不同途径。
摘要由CSDN通过智能技术生成

反射

Java中的反射是指在运行时动态地获取一个类的信息,包括类的属性、方法、构造方法等,并且可以在运行时调用这些方法。通过反射,我们可以在编译时无法确定的情况下,动态地创建对象、调用方法、访问属性等。

基本代码

public class Reflection01 {
    public static void main(String[] args) throws Exception {
        //1. 使用 Properties 类,  可以读写配置文件
        Properties properties = new Properties(); 
        properties.load(new FileInputStream("src\\re.properties"));
        
        String classfullpath =
            properties.get("classfullpath").toString();
        
        methodName = properties.get("method").toString();//"hi"

        //2. 使用反射机制解决
        //(1) 加载类, 返回 Class 类型的对象 cls 
        Class cls = Class.forName(classfullpath);
        
        //(2) 通过 cls  得到你加载的类的对象实例
        Object o = cls.newInstance();
        System.out.println("o 的运行类型=" + o.getClass()); //运行类型
        
        //(3) 通过 cls 得到加载的类的方法对象
        //	即:在反射中,可以把方法视为对象(万物皆对象)
        Method method1 = cls.getMethod(methodName);
        
        //(4) 通过 method1  调用方法: 即通过方法对象来实现调用方法
        System.out.println("=============================");
        method1.invoke(o); //传统方法 对象.方法() ,  反射机制 方法.invoke(对象)
        
        //java.lang.reflect.Field: 代表类的成员变量, Field 对象表示某个类的成员变量
        //得到 name 字段
        //getField 不能得到私有的属性
        Field nameField = cls.getField("age"); //
        System.out.println(nameField.get(o)); // 成员变量对象.get(对象)

        //java.lang.reflect.Constructor: 代表类的构造方法, Constructor 对象表示构造器
        Constructor constructor = cls.getConstructor(); //()中可以指定构造器参数类型, 若未指定则返回无参构造器
        System.out.println(constructor);//Cat()

        Constructor constructor2 = cls.getConstructor(String.class); 
        System.out.println(constructor2);//Cat(String name)
    }
}

基本原理

1)编译阶段:在编译阶段,Java源代码会被编译成字节码文件(.class文件),其中包含了类的结构信息。

2)加载阶段:在加载阶段,类加载器会根据类的全限定名(包括包名和类名)来加载字节码文件,并将其转换为一个Class对象。类加载器可以从本地文件系统、网络等地方加载字节码文件。

3)运行阶段:在运行阶段,通过Class对象可以获取类的构造函数、字段和方法等信息,并且可以动态地创建对象、调用方法、访问和修改字段的值。通过反射,可以实现一些动态的操作,例如动态代理、动态生成代码等。

请添加图片描述

反射的优缺点

优点:可以动态的创建和使用对象,使用灵活。

缺点:使用反射是解释执行,对执行速度有影响。因此,在性能要求较高的场景下,应该谨慎使用反射,尽量避免频繁地使用反射操作。

Class类

1)Class也是类,因此也继承Object类。

2)Class类对象不是new出来的,而是系统创建的。

3)对于某个类的Class类对象,在内存中只要一份,因为类只加载一次。

4)每个类的实例都会记得自己是由哪个Class类实例生成的。

5)通过Class对象调用一系列API可以得到一个类的完整结构。

常用方法

//1 .  获取到 Car 类 对应的 Class 对象
//<?> 表示不确定的 Java 类型
Class<?> cls = Class.forName(classAllPath);

//2. 输出 cls
System.out.println(cls); //显示 cls 对象, 是哪个类的 Class 对象 com.xxx.Car
System.out.println(cls.getClass());//输出 cls 运行类型 java.lang.Class

//3. 得到包名
System.out.println(cls.getPackage().getName());//包名

//4. 得到全类名
System.out.println(cls.getName());

//5. 通过 cls 创建对象实例
Car car = (Car) cls.newInstance(); 
System.out.println(car);//car.toString()

//6. 通过反射获取属性 brand
Field brand = cls.getField("brand");
System.out.println(brand.get(car));//宝马

//7. 通过反射给属性赋值
brand.set(car, "奔驰");
System.out.println(brand.get(car));//奔驰

//8 得到所有的属性(字段)
System.out.println("=======所有的字段属性===="); 
Field[] fields = cls.getFields();
for (Field f : fields) {
	System.out.println(f.getName());//名称
}

6中获取Class类对象的方法

//1. Class.forName
String classAllPath = "com.xxx.Car"; //通过读取配置文件获取
Class<?> cls1 = Class.forName(classAllPath); 
System.out.println(cls1);

//2. 类名.class , 应用场景:  用于参数传递
Class cls2 = Car.class; 
System.out.println(cls2);

//3. 对象.getClass(), 应用场景,有对象实例
Car car = new Car();
Class cls3 = car.getClass(); 
System.out.println(cls3);

//4. 通过类加载器【4 种】来获取到类的 Class 对象
//(1)先得到类加载器 car
ClassLoader classLoader = car.getClass().getClassLoader();
//(2)通过类加载器得到 Class 对象
Class cls4 = classLoader.loadClass(classAllPath); 
System.out.println(cls4);

//以上cls1 , cls2 , cls3 , cls4 其实是同一个对象

//5. 基本数据(int, char,boolean,float,double,byte,long,short) 按如下方式得到 Class 类对象
Class<Integer> integerClass = int.class; 
Class<Character> characterClass = char.class; 
Class<Boolean> booleanClass = boolean.class; 
System.out.println(integerClass);//int

//6. 基本数据类型对应的包装类,可以通过 .TYPE 得到 Class 类对象
Class<Integer> type1 = Integer.TYPE;
Class<Character> type2 = Character.TYPE; //其它包装类 BOOLEAN, DOUBLE, LONG,BYTE 同理
System.out.println(type1);

暴力破解

使用反射可以访问 private 构造器/方法/属性, 反射面前,都是纸老虎。

只需给私有构造器/方法/属性 设置 setAccessible(true)即可 ;

public static void main(String[] args) throws Exception {
    Class<?> po = Class.forName("com.lhs.Po");
    Field name = po.getDeclaredField("name"); // 获取私有属性
    name.setAccessible(true);
    name.set(null,"yyy");

    Constructor<?> constructor = po.getDeclaredConstructor(String.class); // 获取私有构造器
    constructor.setAccessible(true);
    Object o = constructor.newInstance("yyy");

    Method say = po.getDeclaredMethod("say"); // 获取私有方法
    say.setAccessible(true);
    say.invoke(o);
}
}

class Po{
    private static String name = "xxx";

    private Po(String name){
        this.name = name;
    }

    private void say(){
        System.out.println("hello world");
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林小果呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值