反射

问题的提出

  • 需求

    • 根据配置文件re.properties指定信息,创建Cat对象并调用方法hi
    • classfullpath=com.hspedu.Cat
    • method=hi
  • Cat.java

package com.hspedu;

public class Cat {
    public void hi(){
        System.out.println("喵喵喵~");
    }
}
  • re.properties
classFullPath=com.hspedu.Cat
method=hi
  • ReflectionQuestion.java
public class ReflectionQuestion {
    public static void main(String[] args) {
        //传统的方式创建Cat
        Cat cat = new Cat();
        cat.hi();
        //使用properties配置文件创建类,不适用反射则无法创建类
    }
}

初识反射

public class ReflectionQuestion {
    public static void main(String[] args) throws Exception {
        //传统的方式创建Cat
        Cat cat = new Cat();
        cat.hi();
        //使用properties配置文件创建类,不适用反射则无法创建类

        //---------------使用反射机制解决------------------
        Properties properties = new Properties();
        properties.load(new FileInputStream("src/main/resources/re.properties"));
        //获取配置文件中的类路径和方法
        String classFullPath = properties.get("classFullPath").toString();
        String methodName = properties.get("method").toString();

        //1.加载类
        Class aClass = Class.forName(classFullPath);
        //2.通过aClass得到你加载的类 com.hspedu.Cat
        Object o = aClass.newInstance();
        //3.通过aClass得到加载类com.hspedu.Cat的methodName(hi)方法。在反射中可以把方法当做对象
        Method method = aClass.getMethod(methodName);
        //通过method调用方法,即通过方法对象来实现调用方法
        method.invoke(o);   //输出 喵喵喵~
    }
}
  • 在不修改源码的情况下来控制程序(修改配置),新建cry方法。修改配置文件method=cry则直接调用cry方法不需要修改ReflectionQuestion的源码
public class Cat {
    public void hi(){
        System.out.println("喵喵喵~");
    }
    
    public void cry(){
        System.out.println("wuwuwu~");
    }
}

反射机制

  • Java Reflection
    • 反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息(如成员变量,构造器,成员方法等等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到
    • 加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构,这个对象就向一面镜子,透过这个镜子看到类的结构,所以,形象的称之为反射
  • Java反射机制可以完成
    • 在运行时判断任意一个对象所属的类
    • 在运行时构造任意一个类的对象
    • 在运行时得到任意一个类所具有的成员变量和方法
    • 在运行时调用任意一个对象的成员变量和方法
    • 生成动态代理
  • 反射相关的主要类
    • java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象
    • java.lang.reflect。Method:代表类的方法
    • java.lang.reflect.Field:代表类的成员变量
    • java.lang.reflect.Constructor:代表类的构造方法

  • Cat.java
public class Cat {

    public String name = "小猫";

    public Cat(){

    }

    public Cat(String name){
        this.name = name;
    }

    public void hi(){
        System.out.println("喵喵喵~");
    }

    public void cry(){
        System.out.println("wuwuwu~");
    }
}

public class Reflection01 {
    public static void main(String[] args) throws Exception {
        Properties properties = new Properties();
        properties.load(new FileInputStream("src/main/resources/re.properties"));
        //获取配置文件中的类路径和方法
        String classFullPath = properties.get("classFullPath").toString();
        String methodName = properties.get("method").toString();

        //加载类
        Class aClass = Class.forName(classFullPath);
        //通过aClass得到你加载的类 com.hspedu.Cat
        Object o = aClass.newInstance();
        //通过aClass得到加载类com.hspedu.Cat的methodName(hi)方法。在反射中可以把方法当做对象
        Method method = aClass.getMethod(methodName);
        //通过method调用方法,即通过方法对象来实现调用方法
        method.invoke(o);   //输出 喵喵喵~

        //得到name字段,不能获取私有的属性
        Field nameField = aClass.getField("name");
        // 传统写法:对象.成员变量  反射: 成员变量.get(对象)
        System.out.println(nameField.get(o));   //输出 小猫
    }
}
  • 反射优点和缺点
    • 优点:可以动态的创建爱你和使用对象(也是框架底层核心),使用灵活
    • 缺点:使用反射基本是解释执行,对执行速度有影响

Class类

  • Class类常用方法

- Car.java ```java package com.hspedu.class_;

public class Car {
public String brand = "宝马"; //品牌
public int price = 500000; //
public String color = "白色";

@Override
public String toString() {
    return "Car{" +
            "brand='" + brand + '\'' +
            ", price=" + price +
            ", color='" + color + '\'' +
            '}';
}

}

- Class01.java
```java
public class Class01 {
    public static void main(String[] args) throws Exception {
        String classPath = "com.hspedu.class_.Car";
        //获取到Car类对应的Class对象
        Class aClass = Class.forName(classPath);
        //得到包名
        String packageName = aClass.getPackage().getName();
        //得到全类名
        String className = aClass.getName();
        //创建对象实例
        Car car = (Car) aClass.newInstance();
        //获取属性
        Field field = aClass.getField("brand"); //字段为私有属性的话会报错(NoSuchFieldException)
        String brand = (String)field.get(car);
        //给属性赋值
        field.set(car,"奔驰");
        //遍历获取所有的属性
        Field[] fields = aClass.getFields();
        for (Field f : fields){
            //输出每个字段的值
            System.out.println(f.getName() + ":"+f.get(car));
        }
    }
}
  • 获取Class类对象方式
public class Class02 {
    public static void main(String[] args) throws Exception {
        //1. 通过类的全类名
        Class<?> aClass = Class.forName("com.hspedu.class_.Car");
        //2. 类名.class
        Class<Car> carClass = Car.class;
        //3. 对象.getClass
        Car car = new Car();
        Class carClass1 = car.getClass();
        //4. 通过类加载器获取类的Class对象
        ClassLoader classLoader = car.getClass().getClassLoader();
        Class<?> aClass1 = classLoader.loadClass("com.hspedu.class_.Car");
        //5. 基本数据类型 .class
        Class<Integer> intClass = int.class;
        //6. 基本数据类型对应的包装类,可以通过 .type得到Class类对象
        Class<Integer> IntegerClass = Integer.TYPE;
    }
}
  • 通过反射获取类的结构信息
    • getName:获取全类名
    • getSimpleName:获取简单类名
    • getFields:获取所有public修饰的属性,包含本类以及父类的
    • getMethods:获取所有public修饰的方法,包含本类以及父类
    • getDeclaredMethod:获取本类中所有方法
    • getConstructors:获取所有public修饰的构造器
    • getDeclaredConstructors:获取本类的构造器
    • getPackage:以Package形式返回包信息
    • getSuperClass:以Class形式返回父类信息
    • getInterface:以Class[]形式返回接口信息
    • getAnnotations:以Annotation[]形式返回注解信息

  • java.lang.reflect.Field类
    • getModifiers:以int形式返回修饰符
      • 默认修饰符0,public 1,private 2,protect 4,static 8,final 16
    • getType:以Class形式返回类型
    • getName:返回属性名

  • java.lang.reflect.Method类
    • getModifiers:以int形式返回修饰符
      • 默认修饰符0,public 1,private 2,protect 4,static 8,final 16
    • getReturnType:以Class形式获取返回类型
    • getName:返回方法名
    • getParameterTypes:以Class[]返回参数类型数组

  • java.lang.reflect.Constructor类
    • getModifiers:以int形式返回修饰符
    • getName:返回构造器名(全类名)
    • getParameterType:以Class[]返回参数类型数组

类加载

  • 反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载
    • 静态加载:编译时加载相关的类,如果没有则报错,依赖性太强
    • 动态加载:运行时加载需要的类,如果运行时不用该类,则不报错,降低了依赖性
  • 类加载时机
    • 当创建对象时
    • 当子类被加载时
    • 调用类中的静态成员时
    • 通过反射
posted @ 2022-10-13 22:31  youmo~  阅读(16)  评论(0编辑  收藏  举报