Java反射学习笔记(概念描述与例子分享)

Java反射学习笔记(包含详细例子,以及实际用法)

此篇文章比较适合在社会上的Java初学者,非常适合各个高校的学生进行学习,用最简单的例子,用最朴素的代码展现方式来教会大家。

另外,欢迎大家在评论区交流,指正我描述的问题,大家共同进步,共同学习!

一、Java通过反射创建对象的两种方式:

首先需要了解创建Class对象的三种方式:

public class Test{
    String ClassName = "com.example.Test";
    //方法一:
    Class <?> clazz = ClassName.class;
    //方法二:
    Class <?> clazz = Class.forName(ClassName);
    //方法三:
    Test test = new Test();
    Class <?> clazz = test.getClass();
}

1、使用Class对象的newInstance方法创建对象

public class Test{
    private String name;

    public static void main(String[] args){
        String selectHandlerClassName = "com.example.Test";
        Class <?> handlerClass = Class.forName(selectHandlerClassName);
        Object testclass = handlerClass.newInstance();//newInstance无参构造,默认创建的是空对象。
    }
}

2、使用Constructor对象的newInstance方法创建对象

public class Test{
    private String name;
    public Test(String name){
        this.name = name;
    }
    public static void main(String[] args){
        String selectHandlerClassName = "com.example.Test";
        Class <?> handlerClass = Class.forName(selectHandlerClassName);
        //newInstance有参数的情况,getDeclaredConstructor(Stirng.class)在寻找一个含有String类型的构造方法,并调用newInstance方法传入name参数hello。
        Object testclass = handlerClass.getDeclaredConstructor(String.class).newInstance("hello");
        //另外,在获取构造函数时,getDeclaredConstructor()可以获取所有构造方法,无视权限。但getConstructors()方法只能获取到public类型的构造方法。
    }
}

二、反射在Java中的作用:

​ 首先要了解在Java中什么是反射,反射究竟是用来做什么的。

​ 反射在Java中可以说是非常强大,非常厉害。具体体现在,对任意一个类,都能够知道这个类的所有属性和方法。对于任意一个对象,都能够调用它的任意一个方法和属性。

​ 大家可要注意,Java中有4种访问权限(public、protected、private、default)。public类型就不用多说了,可以在任意的类中被调用。protected类型只能在当前类、子类、同一个包下的类中被调用。private类型只允许在声明该类的内部进行访问。default类型只能在同一个包中的类被调用。而这些只需要setAccessible(true)方法用来无视权限。再搭配反射的机制就可以随意的获取被修饰的方法或者属性。

​ 这个时候肯定会有人说,那这样不就会破坏程序的安全性以及封装性了么。这是必然的,所以在使用时请一定要谨慎。在必要的情况在再使用setAccessible(true)。

举个简单的例子(同时这个例子也是利用反射来获取类的属性的例子):

public class Test{
    private String name;
    
    public Test(String name){
        this.name = name;
	}
}
import java.lang.reflect.Field;

public class Main{
    public static void main(String[] args) throws Exception{
        Test test = new Test();
        Class<?> clazz = test.getClass();
        Field field = clazz.getDeclaredField("name");//获取name字段,表示为Field对象
        field.setAccessible(true);//暴力破解
        String name = (String) field.get(test);
        System.out.println("姓名:" + name);
    }
}

那么再说说实际的用处:

1、用来获取和修改类的字段:

import lombok.Getter;

public class Test{
    @Getter
    private String name;
    
    public Test(String name){
        this.name = name;
	}
}
import java.lang.reflect.Field;

public class Main{
    public static void main(String[] args) throws Exception{
        Test test = new Test("zhouchi");
        Class<?> clazz = test.getClass();
        Field field = clazz.getDeclaredField("name");//获取name字段,表示为Field对象
        field.setAccessible(true);//暴力破解
        String name = (String) field.get(test);//获取字段的实际值
        System.out.println("获取姓名:" + name);
        String name = (String) field.set(test,"cuiqiuyue");//修改字段的值
        System.out.println("修改后的姓名:" + name);
    }
}

2、用来获取类的详细信息:

获取类的名称:
public class Test{
    String ClassName = "com.example.Test";
    Class<?> clazz = ClassName.class;
    String classname = clazz.getName();
    System.out.println("获取到的类的名称" + classname);
}
获取父类的名称:
public class Fathertest{
    //....
}

public class Test extends Fathertest{
    String ClassName = "com.example.Test";
    Class<?> clazz = ClassName.getSuperclass();//获取父类的Class对象
    String classname = clazz.getName();
    System.out.println("获取到的父类的名称" + classname);
}
获取接口列表:
interface TestInterface1{
    //....
}

interface TestInterface2{
    //....
}

public class Test implements TestInterface1 implements TestInterface2{
    String ClassName = "com.example.Test";
    Class<?> clazz = ClassName.class;
    Class<?>[] interfaces = clazz.getInterfaces();//获取接口对象
    for(Class<?> inface : interfaces){
        System.out.println("接口名称:" + inface.getName());
    }
}
获取一个类中的所有字段信息:
import lombok.Getter;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

@Getter
public class Test{
    
    private String name;
    private String age;
    
    public Test(String name){
        this.name = name;
	}
    public Test(String age){
        this.age = age;
	}
}

public class Main(){
    public static void main(String[] args){
        String ClassName = "com.example.Test";
        Class<?> clazz = ClassName.class;
        Field[] fields = clazz.getDeclaredFields();
        for(Field field : fields){
            setAccessible(true);//要放在循环内部
            System.out.println("字段名:" + field.getName());
            System.out.println("字段类型:" + field.getType().getName());
            //在Java反射机制中,字段的修饰符是以整数类型来表示的。当调用getModifiers()方法后,会返回一个int类型的值。
            //public(1)、private(2)、protected(4)、static(8)、final(16)、synchronized(32)和volatile(64)
            int modifiers = field.getModifiers();
            System.out.println("字段修饰符:" + Modifier.toString(modifiers));
        }
    }
}
获取方法的信息(包含方法名称、参数类型、修饰符、返回类型)
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class Test {
    private String name;
    private String age;

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

    public Test(String age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public void displayInfo() {
        System.out.println("姓名:" + name);
        System.out.println("年龄:" + age);
    }

    public static void main(String[] args) {
        String className = "com.example.Test";
        Class<?> clazz = Class.forName(className);
        Method[] methods = clazz.getDeclaredMethods();//获取类中方法的对象列表
        for (Method method : methods) {
            System.out.println("方法名称:" + method.getName());
            System.out.println("返回类型:" + method.getReturnType().getName());
            Class<?>[] parameterTypes = method.getParameterTypes();//获取方法中的参数列表对象
            for (Class<?> paramType : parameterTypes) {
                System.out.println("参数类型:" + paramType.getName());
            }
            int modifiers = method.getModifiers();
            System.out.println("修饰符:" + Modifier.toString(modifiers));
        }
    }
}

三、反射在实际场景中的使用(各个高校的学生可以暂时不看这一段)

  • 例如大名鼎鼎的ORM框架:Mybatis-Plus框架就是利用反射来进行对数据库的操作。

  • 还有像Java反序列化也涉及到了反射。

  • JUnit中标注@Test会使用反射机制。先获取测试类的Class对象,然后通过Class对象的getMethods()方法获取到该类中所有的public方法,然后扫描所有获取到的方法上是否标注了@Test注解,如果有的话会将这些有标注的方法放在一个执行集合中,最后按照一定顺序来执行各个方法。

  • Spring中@Autowried注解来进行依赖注入也是反射机制。Spring使用了JAVA中的反射API,来获取被注入的属性或方法的Class对象,然后通过Class对象的getDeclaredFields()或getDeclaredMethods()方法获取到该类中所有的字段或方法。然后步骤就与上面的JUnit一样了,Spring遍历这些字段或方法,并检查每个字段或方法上是否有@Autowired注解。如果有,则将该字段或方法标记为需要注入的依赖项。最后,Spring会根据这些依赖项的类型信息来查找匹配的Bean实例,并将其注入到目标对象中。

  • Spring Boot中@SpringBootApplication注解也使用了反射,

  • @SpringBootApplication注解包含了三个注解,分别是:
    @Configuration:这个注解主要是用于定义配置类。Spring会通过反射机制来扫描项目中所有带着@Configuration注解的类,交给Spring容器管理,并注册为Bean。
    @EnableAutoConfiguration:这个注解用于开启自动配置功能,它会根据类路径下的配置属性和依赖关系来自动配置应用程序。在运行时,Spring Boot会使用反射机制来查找并加载所有带有@Configuration@EnableAutoConfiguration注解的类加粗样式,并根据这些类中定义的自动配置逻辑来创建相应的Bean。

    @ComponentScan:这个注解用于指定组件扫描的基础包或类,它告诉Spring在指定的包或类中查找带有特定注解(如@Component@Service等)的类,并将这些类注册到容器中。在运行时,Spring会使用反射机制来扫描指定的包或类,并根据其中的注解信息来创建相应的Bean。

  • 动态加载类的场景下,如下:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DynamicProxyDemo {
    public static void main(String[] args) {
        // 创建一个目标对象
        ITarget target = new TargetImpl();

        // 创建一个代理工厂
        InvocationHandler handler = new MyInvocationHandler(target);

        // 使用反射和代理工厂创建代理对象
        ITarget proxy = (ITarget) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),//获取目标对象的类加载器
                target.getClass().getInterfaces(),//获取目标对象实现的所有接口
                handler);//一个实现了InvocationHandler接口的对象,用于处理代理对象的方法调用。

        // 调用代理对象的方法
        proxy.doSomething();
    }
}

interface ITarget {
    void doSomething();
}

class TargetImpl implements ITarget {
    @Override
    public void doSomething() {
        System.out.println("目标对象执行操作");
    }
}

class MyInvocationHandler implements InvocationHandler {
    private final ITarget target;

    public MyInvocationHandler(ITarget target) {
        this.target = target;
    }
	//invoke()方法一定会被执行,
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理对象执行前的操作");
        Object result = method.invoke(target, args);
        System.out.println("代理对象执行后的操作");
        return result;
    }
}

  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值