首页 > 编程语言 >深入剖析Java中的反射,由浅入深,层层剥离!

深入剖析Java中的反射,由浅入深,层层剥离!

时间:2024-02-01 20:13:27浏览次数:20  
标签:由浅入深 反射 Java server Class 剖析 Person classa public

写在开头

之前更新了不少Java的基础知识,比如Java的类、对象、基础类型、关键字、序列化、泛型、值传递等等,今天要上点深度了,来聊一聊Java中的 反射 !

所谓反射,就是在运行时分析、检查和操作类、接口、方法、属性的行为!

简单感受一下反射

在开始详解反射之前,我们先通过一段代码,简单的感受一下反射的魅力!首先,我们已经写了一个Person类,类中有age和name属性,并提供了set/get方法。这时候分别通过正常的实例化对象调用,和反射调用去操作对象的属性值!

【代码示例1】

	//Java正射实现
	Person person = new Person();
	person.setName("JavaBuild-正射");
	System.out.println(person.getName());
	
	//Java反射实现
	//获取类的class对象
	Class<?> classa = Class.forName("com.javabuild.server.pojo.Person");
	//通过class获取类的元数据-setName/getName方法
	Method setName = classa.getMethod("setName", String.class);
	Method getName = classa.getMethod("getName");
	//获取默认空构造方法
	Constructor constructor = classa.getConstructor();
	//通过newInstance()进行实例化
	Object o = constructor.newInstance();
	//通过invoke方法进行方法调用
	setName.invoke(o,"JavaBuild-反射");
	System.out.println(getName.invoke(o));

以上为Java正射与反射的对比实现代码。从代码中我们可以看到,正常通过实例化对象后再调用相应方法的正射方式比通过反射的方式代码量要少很多,并且逻辑更加清晰明确。

使用场景

反射被称之为框架的灵魂,在Java中被大量使用在框架、动态代理、注解等场景下;

  • 开发框架:Spring、SpringBoot、Mybatis、Hibernate等框架中使用了反射
  • 动态代理:在面向切面编程中,需要拦截特定的方法,就会选择动态代理的方式,而动态代理的底层技术就是反射。
  • 注解:注解本身只是起到一个标记符的作用,它需要利用反射机制,根据标记符去执行特定的行为。比如@Component注解就声明了一个类为 Spring Bean ,通过@Value注解读取配置文件中的值,这些的底层都是通过反射来实现需求。

实现过程

我们基于上述代码示例1中的反射进行分步分析,彻底搞明白反射的实现与使用!
【步骤一】
通过如下这句代码获取反射类的Class对象

Class<?> classa = Class.forName("com.javabuild.server.pojo.Person");

可别小看这简简单单的一行代码,这里其实涉及到3个知识点,我们来扩展一下。

知识点一:什么是Class对象?
这里提到的Class和之间了解的用来标识类的class关键字并不一样!Class也是一个类,存放在java.lang包中,它的作用是:编译时生成一个类的Class对象,这个对象中包含了类的结构信息,如类名、继承父类、实现的接口、方法、属性等等,Class对象保存在编译后的.class文件中。

在我们new一个对象或者引用静态成员变量时,会将Class对象加载到JVM中,JVM解释器去分析Class对象获取到类的结构信息,创建我们需要的实例对象或者提供静态成员变量的引用值。

因为类的Class对象时存储在class文件中,所以不管实例化多少个对象,都有且仅有一个Class对象!因为Class类对象本身的特性,所以它可以在运行时操作类,这对反射来说无疑是天赐良缘!

知识点二:获取Class对象的几种方式?

1、在已知具体类的情况下

Class person= Person.class;

2、通过Class.forName()传入类的全路径获取,代码示例1中采用该方式

Class classa = Class.forName("com.javabuild.server.pojo.Person");

3、对象实例instance.getClass()获取

Person person = new Person();
Class aClass = person.getClass();

4、通过类加载器xxxClassLoader.loadClass()传入类路径获取

Class aClass = ClassLoader.getSystemClassLoader().loadClass("com.javabuild.server.pojo.Person");

知识点三:java.lang.reflect包中常用的反射类
在java.lang.reflect包中存着几个反射常用的类,大概的罗列如下,注意,Class类其实是放在java.lang中的。

Class:代表一个类或接口,包含了类的结构信息(如名称、构造函数、方法、字段等)。通过 Class 对象,可以获取类的元数据并操作类的实例。
Constructor:代表类的构造方法,用于创建类的实例。
Method:代表类的方法,可以通过它调用类的实例方法。
Field:代表类的字段,可以获取或修改字段的值。
Modifier:包含方法、字段和类的访问修饰符(如 public、private 等)。

【步骤二】
通过Class对象获取构造方法

Constructor constructor = classa.getConstructor();

除了这种获取默认无参构造的方式,获取构造方法还可以通过如下这些方式:

getConstructor():返回反射类的特定 public 构造方法,可以传递参数,参数为构造方法参数对应 Class 对象;缺省的时候返回默认构造方法。
getDeclaredConstructor():返回反射类的特定构造方法,不限定于 public 的。
getConstructors():返回类的所有 public 构造方法。
getDeclaredConstructors():返回类的所有构造方法,不限定于 public 的。

【代码示例2】

Class<?> classa = Class.forName("com.javabuild.server.pojo.Person");
Constructor constructor01 = classa.getConstructor();
System.out.println(constructor);
System.out.println("---------------");
Constructor<?>[] constructors = classa.getConstructors();
for (Constructor<?> constructor02 : constructors) {
    System.out.println(constructor02);
}
System.out.println("----------------");
Constructor declaredConstructor = classa.getDeclaredConstructor(int.class);
System.out.println(declaredConstructor);

【控制台输出】

public com.javabuild.server.pojo.Person()
---------------
public com.javabuild.server.pojo.Person()
public com.javabuild.server.pojo.Person(int)
public com.javabuild.server.pojo.Person(int,java.lang.String)
----------------
public com.javabuild.server.pojo.Person(int)

【步骤三】
通过 Constructor 对象初始化反射类对象

//通过newInstance()进行实例化
Object o = constructor.newInstance();

【步骤四】
获取要调用的方法的 Method 对象

//通过class获取类的元数据-setName/getName方法
Method setName = classa.getMethod("setName", String.class);
Method getName = classa.getMethod("getName");

【步骤五】
通过invoke方法进行方法调用

setName.invoke(o,"JavaBuild-反射");

以上即为反射使用的全过程!下面贴一下全量代码。

Person类

public class Person {
    private int age;
    private String name;

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    //无参构造
    public Person() {
    }
    //一个参数构造
    public Person(int age) {
        this.age = age;
    }
    //2个参数构造
    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }
}

反射测试类

public class Test {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //Java正射实现
        Person person = new Person();
        person.setName("JavaBuild-正射");
        System.out.println(person.getName());

        //Java反射实现
        //获取类的class对象
        Class<?> classa = Class.forName("com.javabuild.server.pojo.Person");
        //通过class获取类的元数据-setName/getName方法
        Method setName = classa.getMethod("setName", String.class);
        Method getName = classa.getMethod("getName");
        //获取默认空构造方法
        Constructor constructor = classa.getConstructor();
        //通过newInstance()进行实例化
        Object o = constructor.newInstance();
        //通过invoke方法进行方法调用
        setName.invoke(o,"JavaBuild-反射");
        System.out.println(getName.invoke(o));

        //获取默认空构造方法
        Constructor constructor01 = classa.getConstructor();
        System.out.println(constructor);
        System.out.println("---------------");
        Constructor<?>[] constructors = classa.getConstructors();
        for (Constructor<?> constructor02 : constructors) {
            System.out.println(constructor02);
        }
        System.out.println("----------------");
        Constructor declaredConstructor = classa.getDeclaredConstructor(int.class);
        System.out.println(declaredConstructor);
    }
}

输出

JavaBuild-正射
JavaBuild-反射
public com.javabuild.server.pojo.Person()
---------------
public com.javabuild.server.pojo.Person()
public com.javabuild.server.pojo.Person(int)
public com.javabuild.server.pojo.Person(int,java.lang.String)
----------------
public com.javabuild.server.pojo.Person(int)

优缺点

基于上面的内容,我们可以对java中的反射做一个总结吧

优点
1、可以让咱们的代码更加灵活、为各种框架提供开箱即用的功能提供了便利。
缺点
1、破坏封装:由于反射允许访问私有字段和私有方法,所以可能会破坏封装而导致安全问题。
2、性能开销:由于反射涉及到动态解析,因此无法执行 Java 虚拟机优化,再加上反射的写法的确要复杂得多,所以性能要比“正射”差很多,在一些性能敏感的程序中应该避免使用反射。

标签:由浅入深,反射,Java,server,Class,剖析,Person,classa,public
From: https://www.cnblogs.com/JavaBuild/p/18002022

相关文章

  • Java之网络编程的详细解析
    1.网络编程入门1.1网络编程概述计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统网络编程在网络通信协议下,不同计算机上运行的程序,可以进行......
  • MongoDB由浅入深(查询)
    //基础查询db.orderSlic.find();//根据ID查询db.orderSlic.find({_id:54708407});//范围查询db.orderSlic.find({"_id":{"$gte":54708407,"$lte":54708409}});//多字段分组统计db.orderSlic.aggregate([{$group:{_id:{shopId:"$......
  • Java-06函数
    tip:[start]理解函数,最重要的是理解代码的执行顺序。——闫学灿tip:[end]函数基础一个典型的函数定义包括以下部分:修饰符、返回类型、函数名字、由0个或多个形参组成的形参列表以及函数体。编写函数我们来编写一个求阶乘的程序。程序如下所示:publicclassMain{ //函......
  • Java实现学生信息管理系统:从Excel中提取数据的实用方法
    在Java中实现学生信息管理系统并从Excel中读取数据,通常适用于以下场景:数据迁移和集成:如果你有一个现存的学生信息管理系统,该系统可能使用数据库或其他存储方式,但你想将这些数据迁移到新的系统。Excel文件可能提供了一种方便的方式来迁移这些数据,因为Excel文件格式易于查看和编辑。......
  • Java 框架
    框架是整个或者部分系统的可重用设计,从应用角度看框架,可以被当作一组抽象构建及构件实例间交互的方法;从应用目的的角度看,SpringSpring定义Spring通常指的是SpringFramework,它是一个开源框架。Spring是包含众多工具方法的IoC容器。什么是容器容器时用来容纳某种物品的基......
  • Java面向对象程序设计——带异常处理的person类
    带异常处理的person类【问题描述】定义一个Person类,属性包含姓名、年龄。方法:无参构造方法、有参构造方法、getter和setter方法、toString方法。其中:setter方法在设置年龄的时候,要求对年龄进行参数的正确性检测,年龄有效范围在1-100之间,否则抛出IllegalArgumentException异常【输......
  • Java并发基础:CyclicBarrier全面解析!
    内容摘要CyclicBarrier的优点在于实现了线程间的相互等待与协同,确保所有线程在达到预定屏障点后才能继续执行,它支持屏障的重复使用,非常适合多轮次的任务同步,此外,CyclicBarrier还允许在屏障点执行特定操作,为复杂的多线程协作提供了便利。核心概念业务场景CyclicBarrier允许一组......
  • java学习02
    可以用_和字母和$作为标识符开头,特殊字符不能出现在名字中、float和long类型要在赋值后面加上大写首字母最好避免使用浮点数进行比较,因为浮点数是离散的数,接近但不等于使用时会出问题char字符本质都是数字,通过int类型转换可以知道/u0000可以输出起点/uffff是char终点使用下......
  • Java和JavaScript区别与联系
    JavaScript和java属于两门语言。参考了java语言,“看上去与Java足够相似,但是比Java简单”,是简化版Java语言。特别是在定义对象变量的时候,省略的太极端了。java里有的很多常用类型的对象在js中是没有的。JavaScript由原网景(Netscape)公司的高级工程师布兰登·艾奇(BrendanEich)(曾经的M......
  • 在Java中重写hashCode()方法
    而在重写equals()方法时,总要重写hashCode()方法,原因总结下有以下两点:1.使用hashcode方法提前校验,可以避免每一次比对都调用equals方法,提高效率2.保证是同一个对象,如果重写了equals方法,而没有重写hashcode方法,会出现equals相等的对象,hashcode不相等的情况,重写hashcode方法就是为了......