Java反射篇

思维导图(方便阅读)

在这里插入图片描述

反射的基本使用

通过从配置文件中创建对象并调用方法开始,引入反射的使用

Cat.java(注意要有无参构造,反射创建的对象是通过无参构造创建的)

package com.boot.test;

/**
 * @author bbyh
 * @date 2022/11/5 0005 23:55
 * @description
 */
public class Cat {
    private String name;

    public Cat() {
    }

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

    public void hello() {
        System.out.println(name + "向你问好");
    }

    public void setName(String name) {
        this.name = name;
    }
}

application.properties 的内容

catPath=com.boot.test.Cat
method=hello

测试类(此时输出结果中name并没有被赋值)

package com.boot.test;

import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Properties;

/**
 * @author bbyh
 * @date 2022/11/5 0005 22:55
 * @description
 */
public class Test {
    public static void main(String[] args) throws Exception {
        Properties properties = new Properties();
        properties.load(Files.newInputStream(Paths.get("src/application.properties")));
        String catPath = properties.getProperty("catPath");
        String methodName = properties.getProperty("method");

        Class<?> cls = Class.forName(catPath);
        Object newInstance = cls.newInstance();
        Method method = cls.getMethod(methodName);
        method.invoke(newInstance);
    }
}

改进,设置对象的属性
application.properties的内容

catPath=com.boot.test.Cat
catName=Tom
method=hello
public class Test {
    public static void main(String[] args) throws Exception {
        Properties properties = new Properties();
        properties.load(Files.newInputStream(Paths.get("src/application.properties")));
        String catPath = properties.getProperty("catPath");
        String methodName = properties.getProperty("method");
        String catName = properties.getProperty("catName");

        Class<?> cls = Class.forName(catPath);
        Cat cat = (Cat) cls.newInstance();
        cat.setName(catName);
        Method method = cls.getMethod(methodName);
        method.invoke(cat);
    }
}

getField、getConstructor的使用

通过 getField、getConstructor 获取构造器和字段的值,但对于公有字段才可获取

package com.boot.test;

/**
 * @author bbyh
 * @date 2022/11/5 0005 23:55
 * @description
 */
public class Cat {
    private String name;
    public int age = 10;

    public Cat() {
    }

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

    public void hello() {
        System.out.println(name + "向你问好");
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class Test {
    public static void main(String[] args) throws Exception {
        Properties properties = new Properties();
        properties.load(Files.newInputStream(Paths.get("src/application.properties")));
        String catPath = properties.getProperty("catPath");
        String methodName = properties.getProperty("method");
        String catName = properties.getProperty("catName");

        Class<?> cls = Class.forName(catPath);
        Cat cat = (Cat) cls.newInstance();
        cat.setName(catName);
        Method method = cls.getMethod(methodName);
        method.invoke(cat);

//        Field name = cls.getField("name");
//        name.setAccessible(true);
//        System.out.println(name);

        Field age = cls.getField("age");
        System.out.println(age.get(cat));

        Constructor<?> constructor = cls.getConstructor();
        System.out.println(constructor);

        Constructor<?> constructor1 = cls.getConstructor(String.class);
        System.out.println(constructor1);
    }
}

反射与一般调用的性能比较

可以通过 method.setAccessible(true); 来关闭检查从而 提高一些性能

package com.boot.test;

/**
 * @author bbyh
 * @date 2022/11/5 0005 23:55
 * @description
 */
public class Cat {
    protected String name;
    public int age = 10;

    public Cat() {
    }

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

    public void hello() {
//        System.out.println(name + "向你问好");
        int i = 0;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class RuntimeTest {
    private static final int NUMBER = 1000000000;

    public static void main(String[] args) throws Exception {
        normal();
        reflect();
    }

    private static void reflect() throws Exception {
        Class<?> cls = Class.forName("com.boot.test.Cat");
        Method method = cls.getMethod("hello");
        Object cat = cls.newInstance();
        method.setAccessible(true);
        long start = System.currentTimeMillis();
        for (int i = 0; i < NUMBER; i++) {
            method.invoke(cat);
        }
        long end = System.currentTimeMillis();
        System.out.println("reflect 运行时间: " + (end - start));
    }

    private static void normal() {
        Cat cat = new Cat();
        long start = System.currentTimeMillis();
        for (int i = 0; i < NUMBER; i++) {
            cat.hello();
        }
        long end = System.currentTimeMillis();
        System.out.println("Normal 运行时间: " + (end - start));
    }
}

反射创建的对象为同一个

反射创建的对象为同一个,但和直接new出来的不相同

package com.boot.test;

/**
 * @author bbyh
 * @date 2022/11/6 0006 10:49
 * @description
 */
public class ClassConsistent {
    public static void main(String[] args) throws Exception {
        Cat cat = new Cat();
        Class<?> cls1 = Class.forName("com.boot.test.Cat");
        Class<?> cls2 = Class.forName("com.boot.test.Cat");

        System.out.println(cat.hashCode());
        System.out.println(cls1.hashCode());
        System.out.println(cls2.hashCode());

        System.out.println(cls1 == cls2);
    }
}

Class类对象的常用方法

主要是 forName、newInstance、getField、getFields

package com.boot.test;

import java.lang.reflect.Field;

/**
 * @author bbyh
 * @date 2022/11/6 0006 11:12
 * @description
 */
public class CommonMethod {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("com.boot.test.Cat");
        Cat cat = (Cat) cls.newInstance();

        System.out.println(cls.getPackage().getName());
        System.out.println(cls.getName());
        Field age = cls.getField("age");
        System.out.println(age.get(cat));
        age.set(cat, 20);
        System.out.println(cat);

        Field[] fields = cls.getFields();
        for (Field field : fields) {
            System.out.print(field.getName() + " : ");
            System.out.println(field.get(cat));
        }
    }
}

获取Class对象的四种方式

分别是 代码阶段的forName、class类阶段的 类.class 、 运行阶段的 对象.getClass、以及类加载阶段通过类加载器获得

package com.boot.test;

import java.lang.reflect.Constructor;

/**
 * @author bbyh
 * @date 2022/11/6 0006 11:25
 * @description
 */
public class GetClass {
    public static void main(String[] args) throws Exception {
        String classPath = "com.boot.test.Cat";

        // Class.forName() 已知类的全类名,多用于配置文件
        Class<?> cls1 = Class.forName(classPath);
        System.out.println(cls1);

        // 类.class ,多用于参数传递
        Constructor<?> constructor = cls1.getConstructor(String.class);
        Class<Cat> cls2 = Cat.class;
        System.out.println(cls2);

        // 对象.getClass() ,用于已经有对象时
        Cat cat = new Cat();
        Class<? extends Cat> cls3 = cat.getClass();
        System.out.println(cls3);

        // 通过类加载器获得对象
        ClassLoader classLoader = cat.getClass().getClassLoader();
        Class<?> cls4 = classLoader.loadClass(classPath);
        System.out.println(cls4);

        // 这四个对象是同一个
        System.out.println(cls1.hashCode());
        System.out.println(cls2.hashCode());
        System.out.println(cls3.hashCode());
        System.out.println(cls4.hashCode());

        // 基本数据类型,通过 class 属性获得
        Class<Integer> integerClass = int.class;
        Class<Character> characterClass = char.class;
        System.out.println(integerClass);
        System.out.println(characterClass);

        // 包装数据类型,通过 TYPE 属性获得
        Class<Integer> integerClass1 = Integer.TYPE;
        Class<Character> characterClass1 = Character.TYPE;
        System.out.println(integerClass1);

        System.out.println(integerClass.hashCode());
        System.out.println(characterClass.hashCode());
        System.out.println(integerClass1.hashCode());
        System.out.println(characterClass1.hashCode());
    }
}

有class对象的类有哪些

不管是基本数据类型,还是抽象数据类型,枚举或者接口,注解或是void,都有 class 对象

package com.boot.test;

import java.io.Serializable;

/**
 * @author bbyh
 * @date 2022/11/6 0006 15:50
 * @description
 */
public class ClassScoped {
    public static void main(String[] args) {
        Class<String> cls1 = String.class;
        Class<Serializable> cls2 = Serializable.class;
        Class<Integer[]> cls3 = Integer[].class;
        Class<Integer[][]> cls4 = Integer[][].class;
        Class<Thread.State> cls5 = Thread.State.class;
        Class<Deprecated> cls6 = Deprecated.class;
        Class<Long> cls7 = long.class;
        Class<Void> cls8 = void.class;
        Class<?> cls9 = Class.class;

        System.out.println(cls1);
        System.out.println(cls2);
        System.out.println(cls3);
        System.out.println(cls4);
        System.out.println(cls5);
        System.out.println(cls6);
        System.out.println(cls7);
        System.out.println(cls8);
        System.out.println(cls9);
    }
}

动态和静态加载

以简单的代码编写后能否通过编译进行示例引入动态加载与静态加载

编译的时候就会加载这个类,即 静态加载

package com.boot.test;

import java.util.Scanner;

/**
 * @author bbyh
 * @date 2022/11/6 0006 16:02
 * @description
 */
public class ClassLoaderStatic {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String line = scanner.nextLine();
        switch (line) {
            case "1":
                // 编译的时候就会加载这个类,即 静态加载
                Dog dog = new Dog();
                break;
            case "2":
                System.out.println("OK");
                break;
            default:
                System.out.println("ERROR");
                break;
        }
    }
}

动态加载,只有在用到的时候才会加载该类,所以下面代码可以编译通过

package com.boot.test;

import java.lang.reflect.Method;
import java.util.Scanner;

/**
 * @author bbyh
 * @date 2022/11/6 0006 16:02
 * @description
 */
public class ClassLoaderStatic {
    public static void main(String[] args) throws Exception {
        Scanner scanner = new Scanner(System.in);
        String line = scanner.nextLine();
        switch (line) {
            case "1":
                Class<?> cls = Class.forName("Person");
                Object o = cls.newInstance();
                Method method = cls.getMethod("hello");
                method.invoke(o);
                break;
            case "2":
                System.out.println("OK");
                break;
            default:
                System.out.println("ERROR");
                break;
        }
    }
}

类加载流程

类的加载分 三个阶段:加载、连接、初始化;连接又细分为验证、准备、解析
在这里插入图片描述

加载阶段的工作:将类的 class 文件读入内存,并创建Class对象,此过程由类加载器完成

将字节码从不同的数据源(class文件、jar包,甚至网络)转化为二进制字节流加载到内存中,生成对应的class对象

连接阶段:将类的二进制数据合并到JRE中

验证阶段:
	确保当前class文件的字节流包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全
	文件格式验证(以16进制的魔数cafebabe开头)、元数据验证、字节码验证、符号引用验证
	可以考虑使用 -Xverify:none 参数来关闭大部分的类验证措施,来加快类加载

准备阶段:
	对静态变量分配内存并进行默认初始化;这些变量使用的内存将在方法区中进行分配
public class StaticInit {
    /**
     * 实例变量 n1 在准备阶段不会被处理
     * 静态变量 n2 在准备阶段会分配内存,并赋值默认值0
     * 静态常量 n3 在准备阶段会分配内存并赋值30
     */
    private int n1;
    private static int n2 = 10;
    private static final int NUM = 30;
}

解析阶段:
	虚拟机将常量池的符号引用转化为直接引用

初始化阶段:JVM对类进行初始化,这里主要是指静态成员

clinit方法的执行,多线程间会保证锁的正确性,即线程间同步,同一时间只会有一个线程执行这个类的加载clinit方法


package com.boot.test;

/**
 * @author bbyh
 * @date 2022/11/6 0006 18:13
 * @description
 */
public class InitClass {
    static {
        System.out.println("静态代码块执行");
        num = 100;
    }
    private static int num = 10;

    public InitClass() {
        System.out.println("构造方法被执行");
    }

    /**
     * 加载: 加载B,生成 class对象
     * 连接: num = 0
     * 初始化: 自动收集静态代码块的片段和静态变量的赋值语句
     * clinit{
     *     System.out.println("静态代码块执行");
     *     num = 100;
     *     private static int num = 10;
     * }
     * 合并为
     * clinit{
     *     System.out.println("静态代码块执行");
     *     private static int num = 10;
     * }
     */
    public static void main(String[] args) {
    	// 很明显,直接使用类的属性也会导致类的加载
        System.out.println(InitClass.num);
    }
}

class 对象在多线程下也只会被加载一次,且在内存中类的class对象只会有一份,在下面代码中体现了
在这里插入图片描述

反射获取类的结构信息

cls.getName(); // 加了 Declared 就是本类,没加就是父类和本类
cls.getSimpleName();
cls.getFields();
cls.getDeclaredFields();
cls.getMethods();
cls.getDeclaredMethods();
cls.getConstructors();
cls.getDeclaredConstructors();
cls.getPackage();
cls.getSuperclass();
cls.getInterfaces();
cls.getAnnotations();

package com.boot.test;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * @author bbyh
 * @date 2022/11/6 0006 18:51
 * @description
 */
public class GetClassInfo {
    /**
     * cls.getName();		// 加了 Declared 就是本类,没加就是父类和本类
     * cls.getSimpleName();
     * cls.getFields();
     * cls.getDeclaredFields();
     * cls.getMethods();
     * cls.getDeclaredMethods();
     * cls.getConstructors();
     * cls.getDeclaredConstructors();
     * cls.getPackage();
     * cls.getSuperclass();
     * cls.getInterfaces();
     * cls.getAnnotations();
     */
    public static void main(String[] args) throws Exception {
        String classPath = "com.boot.test.Man";
        Class<?> cls = Class.forName(classPath);
        System.out.println(cls.getName());
        System.out.println(cls.getSimpleName());

        Field[] fields = cls.getFields();
        for (Field field : fields) {
            System.out.println("本类及父类的public属性: " + field.getName());
        }

        Field[] declaredFields = cls.getDeclaredFields();
        for (Field field : declaredFields) {
            System.out.println("本类所有属性: " + field.getName());
        }

        Method[] methods = cls.getMethods();
        for (Method method : methods) {
            System.out.println("本类及父类的public方法(也包含Object): " + method.getName());
        }

        Method[] declaredMethods = cls.getDeclaredMethods();
        for (Method method : declaredMethods) {
            System.out.println("本类的所有方法: " + method.getName());
        }

        Constructor<?>[] constructors = cls.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println("本类的public构造器(也包含Object): " + constructor.getName());
        }

        Constructor<?>[] declaredConstructors = cls.getDeclaredConstructors();
        for (Constructor<?> constructor : declaredConstructors) {
            System.out.println("本类所有的构造器: " + constructor.getName());
        }

        System.out.println(cls.getPackage());
        System.out.println(cls.getSuperclass());

        Class<?>[] interfaces = cls.getInterfaces();
        for (Class<?> anInterface : interfaces) {
            System.out.println(anInterface);
        }

        Annotation[] annotations = cls.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
    }
}

class Person {
    public String personName;
    protected int personAge;
    String personJob;
    private int personSal;

    public Person() {
    }

    public Person(String personName) {
        this.personName = personName;
    }

    private Person(int personAge) {
        this.personAge = personAge;
    }

    public void p1() {
    }

    protected void p2() {
    }

    void p3() {
    }

    private void p4() {
    }
}

@Deprecated
class Man extends Person implements Serializable {
    public String name;
    protected int age;
    String job;
    private int sal;

    public Man() {
    }

    public Man(String personName, String name) {
        super(personName);
        this.name = name;
    }

    private Man(int age) {
        this.age = age;
    }

    public void m1() {
    }

    protected void m2() {
    }

    void m3() {
    }

    private void m4() {
    }
}

字段信息

getModifiers(); 以int 类型返回修饰符,默认是0,public是1,private是2,protected是4,static是8,final是16;多个修饰符采用相加,即 public static 是 1+8
getName();
getType();

package com.boot.test;

import java.lang.reflect.Field;

/**
 * @author bbyh
 * @date 2022/11/6 0006 20:06
 * @description
 */
public class FiledModify {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("com.boot.test.Modify");
        Field[] fields = cls.getDeclaredFields();
        for (Field field : fields) {
            System.out.println("属性名: " + field.getName() + " 修饰符: " + field.getModifiers() +
                    " 属性类型: " + field.getType());
        }
    }
}

class Modify {
    String a;
    public int b;
    private String c;
    protected Integer d;
    static String e;
    final String f = "";
    private static final String G = "";
}

方法信息

通过 getDeclaredMethods、getName、getReturnType、getParameterTypes

package com.boot.test;

import java.lang.reflect.Method;

/**
 * @author bbyh
 * @date 2022/11/6 0006 20:06
 * @description
 */
public class FiledModify {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("com.boot.test.Modify");
        Method[] declaredMethods = cls.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.print("方法名: " + declaredMethod.getName() + " 方法修饰符: " + declaredMethod.getModifiers() +
                    " 方法返回类型: " + declaredMethod.getReturnType());
            Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
            for (Class<?> parameterType : parameterTypes) {
                System.out.print(parameterType + " ");
            }
            System.out.println();
        }
    }
}

class Modify {
    void m1(String name) {
    }

    public void m2(String name, Integer age) {
    }

    private void m3(String name) {
    }

    protected void m4(String name) {
    }

    private static void m5(String name) {
    }

    protected final void m6(String name) {
    }
}

获取构造器的信息

getModifiers();、getParameterTypes();

通过反射创建对象

通过public无参构造器
通过public指定构造器
通过非public构造器创建,这里要使用 setAccessible(true); 进行爆破

package com.boot.test;

import java.lang.reflect.Constructor;

/**
 * @author bbyh
 * @date 2022/11/6 0006 20:37
 * @description
 */
public class ReflectionCreate {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("com.boot.test.User");
        Object o = cls.newInstance();
        System.out.println(o);

        Constructor<?> constructor = cls.getConstructor(String.class);
        Object jack = constructor.newInstance("Jack");
        System.out.println(jack);

        Constructor<?> constructor1 = cls.getDeclaredConstructor(String.class, Integer.class);
        constructor1.setAccessible(true);
        Object mary = constructor1.newInstance("Mary", 40);
        System.out.println(mary);
    }
}

class User {
    private String name = "Hello";
    private Integer age = 20;

    public User() {
    }

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

    private User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

通过反射访问类中成员

对于静态成员 age.set(user, 30); 也可以改为 age.set(null, 30);

package com.boot.test;

import java.lang.reflect.Field;

/**
 * @author bbyh
 * @date 2022/11/6 0006 20:37
 * @description
 */
public class ReflectionCreate {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("com.boot.test.User");
        Object user = cls.newInstance();
        System.out.println(user);

        Field name = cls.getField("name");
        name.set(user, "Jack");
        System.out.println(user);

        Field age = cls.getDeclaredField("age");
        age.setAccessible(true);
        age.set(user, 30);
        // age.set(null, 30);
        System.out.println(user);
    }
}

class User {
    public String name;
    private static Integer age;

    public User() {
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

通过反射操作方法

对于静态成员,hello2.invoke(user, “Mary”); 可以改为 hello2.invoke(null, “Mary”);

package com.boot.test;

import java.lang.reflect.Method;

/**
 * @author bbyh
 * @date 2022/11/6 0006 20:37
 * @description
 */
public class ReflectionCreate {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("com.boot.test.User");
        Object user = cls.newInstance();

        Method hello1 = cls.getMethod("hello1", String.class);
        hello1.invoke(user, "Jack");

        Method hello2 = cls.getDeclaredMethod("hello2", String.class);
        hello2.setAccessible(true);
        hello2.invoke(user, "Mary");
    }
}

class User {
    public User() {
    }

    public void hello1(String str) {
        System.out.println("hello1 " + str);
    }

    private static void hello2(String str) {
        System.out.println("hello2 " + str);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值