Java-反射(有图有例子,清晰易懂)

简介: 反射:框架设计的灵魂1、概念Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。

反射:框架设计的灵魂

1、概念

Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。

框架:半成品软件,可以在框架的基础上进行软件开发,简化编码

反射:将类的各个组成部分封装成其他对象(Field,Constructor,Method),这就是反射机制

好处(见第四点)


可以再程序运行过程中,操作这些对象

可以解耦,提高程序的可扩展性

看到这里是不是一脸懵逼,放心看到最后就会豁然开朗的!

2、获取Class对象的方式

什么是Class类?

Class类是一个特殊类,它用于表示JVM运行时类或接口的信息。一个类只有被JVM加载后才能使用,当类被虚拟机加载后都会在内存中创建一个该类的Class对象,用于存储该类的各种信息。

什么是Class对象?


在Java里万物皆对象,那么每个类除了有自己的对象,这些类其实也都是Class类的对象


Class对象的由来


磁盘中的字节码文件是无法直接使用的,需要JVM内存中的才可以使用,而加载类时就是在本地磁盘中找到这个类的字节码文件将其加载到JVM内存中并生成对应的Class对象


反射机制允许程序在运行时取得任意一个类的内部信息,那么是怎样获得其信息的呢,就需要我们首先先得到这个类对应的字节码对象,即Class对象。而Class类的构造方法属于私有方法,故无法直接使用new来生成对象,而是使用以下三种方法。

Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象

多用于配置文件,将类名定义在配置文件中,读取文件来加载类

类名.class():通过类名的属性class获取

多用于参数的传递

对象.getClass():getClass()方法在Object类中定义着

多用于对象的获取字节码的方式常用的是第一种方法

以下为各种方式获取Class对象的示例

package com.reflect;
import com.domain.Person;
public class ReflectDemo1 {
    public static void main(String[] args) throws Exception {
        Class cls = Class.forName("com.domain.Person");
        System.out.println(cls);
        Class cls2 = Person.class;
        System.out.println(cls2);
        Person p = new Person();
        Class cls3 = p.getClass();
        System.out.println(cls3);
        System.out.println(cls == cls2); // true
        System.out.println(cls == cls3); // true
    }
}

结论


同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个


3、Class对象功能

Class对象存储着该类的各种信息,我们可以通过Class类的各种get方法来获取原本类的变量,构造方法以及成员方法等信息,获取方式如下


获取成员变量们

Field[ ] getFields( ):获取所有public修饰的成员变量

Field getField(String name):获取指定名称的public修饰的成员变量

Field[ ] getDeclaredFields( ):获取所有的成员变量,不考虑修饰符

Field getDeclaredField(String name)

获取构造方法们

Constructor<?>[ ] getConstructors( ):获取所有public修饰的构造函数

Constructor<?> getConstructor(类<?>… parameterTypes)

Constructor<?>[ ] getDeclaredConstructors( ):获取所有构造函数,不考虑修饰符

Constructor<?> getDeclaredConstructor(类<?>… parameterTypes)

获取成员方法们

Method[ ] getMethods( ):获取所有public方法(包括父类的方法)

Method getMethod(类<?>… parameterTypes)

Method[ ] getDeclaredMethods( ):获取所有方法(不包括父类的方法)

Method getDeclaredMethod(类<?>… parameterTypes)

获取类名

String getName( ):获取完整类名

获取到类的各个组成部分封装出来的对象之后,就可以通过这个对象来操作类了


Field:成员变量

  1. 设置值:void set(Object obj, Object value)
  2. 获取值:Object get(Object obj)
  3. 忽略访问权限修饰符的安全检查:seeAccessible(true)——暴力反射
package com.reflect;
import com.domain.Person;
import java.lang.reflect.Field;
public class ReflectDemo2 {
    public static void main(String[] args) throws Exception {
        // 获取person的class对象
        Class cls = Person.class;
        // 获取所有public修饰的成员变量
        Field[] fields = cls.getFields();
        for(Field field : fields){
            System.out.println(field);
        }
        // 获取指定名的成员变量
        Field field = cls.getField("height");
        Person p = new Person();
        // 获取p对象中名为height的成员变量的值
        Object value = field.get(p);
        System.out.println(value); // 因为是没有设置数据的整形,故默认为0
        // 为p对象的height成员变量设置值
        field.set(p,150);
        System.out.println(p);
        Field field2 = cls.getDeclaredField("name"); // 获取private成员变量
        field2.setAccessible(true); // 获取或设置非public的成员前需要暴力反射,忽略访问权限修饰符的安全检查
        field2.set(p,"cyh"); // 为name成员变量设置值
        Object value2 = field2.get(p);
        System.out.println(value2);
    }
}

Constructor:构造方法

  • 创建对象:T newInstance(Object… initargs)
  • 如果使用空参数构造方法创建对象,操作可以简化:使用Class对象的newInstance()方法
package com.reflect;
import com.domain.Person;
import java.lang.reflect.Constructor;
public class ReflectDemo3 {
    public static void main(String[] args) throws Exception {
        Class personClass = Person.class;
        // 获取含三个参数的构造函数
        Constructor constructor = personClass.getConstructor(String.class,int.class,int.class);
        // 根据这个构造函数创建一个对象
        Object person1 = constructor.newInstance("cyh",19,170);
        System.out.println(person1);
        // 获取无参构造函数
        Constructor constructor1 = personClass.getConstructor();
        // 无参创建一个对象
        Object person2 = constructor1.newInstance();
        System.out.println(person2);
        // 简化无参构造创建对象的方法
        Object person3 = personClass.newInstance();// 此法已被抛弃
    }
}

Method:方法对象

  • 执行方法:Object invoke(Object obj, Object… args)
  • 获取方法名称:String getName:获取方法名
package com.reflect;
import com.domain.Person;
import java.lang.reflect.Method;
public class ReflectDemo4 {
    public static void main(String[] args) throws Exception{
        Class personClass = Person.class;
        // 获取person类中的所有公有方法(包括父类的方法)
        Method[] methods = personClass.getMethods();
        for(Method method : methods){
            System.out.println(method);
            System.out.println(method.getName());
        }
        System.out.println("======================");
        // 获取person类中的所有方法(不包括父类的方法)
        Method[] methods1 = personClass.getDeclaredMethods();
        for(Method method : methods1){
            System.out.println(method);
            System.out.println(method.getName());
        }
        // 获取方法
        Method method = personClass.getMethod("add",int.class,int.class);
        // 执行方法
        Person p = new Person();
        System.out.println(method.invoke(p,2,3));
        // 忽略访问权限修饰符的安全检查
        // method.setAccessible(true);
        // 获取类名
        System.out.println(personClass.getName());
    }
}

4、案例60258a1ada674affb47821a4a0b280b9.png

97122647024142ffae1c0a5d82b64d17.png

经过上述对反射的阐述以及上图的对比我们可能会想,反射到底有什么用?


我们直接new一个对象就可以实现的工作为什么要绕一大圈获取Class对象再来实现呢?


反射的存在自然有其作用,那就是灵活!


下面例子也许帮助我们初窥其妙用


需求:写一个“框架”,在不改变该类的任何代码的前提下,可以帮我们创建任何类的对象,并且执行其中的方法


实现:


配置文件

反射

步骤:


将需要创建的对象的全类名和需要执行的方法定义在配置文件中

再程序中加载读取配置文件

使用反射技术来加载文件进内存

创建对象

  1. 执行方法
package com.reflect;
// 不用导入类包
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
public class ReflectTest {
    public static void main(String[] args) throws Exception {
        // 1.加载配置文件
        // 1.1创建properties对象
        Properties pro = new Properties();
        // 1.2加载配置文件,转换为一个集合
        // 1.2.1加载class目录下的配置文件
        ClassLoader classLoader = ReflectTest.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("pro.properties");
        pro.load(is);
        // 2.加载配置文件中定义的数据
        String className = pro.getProperty("className");
        String methodName = pro.getProperty("methodName");
        // 3.加载该类到内存
        Class cls = Class.forName(className);
        // 4.创建该类的对象
        // Object obj = cls.newInstance();
        Constructor constructor = cls.getConstructor();
        Object obj = constructor.newInstance();
        // 5.获取方法对象
        Method method = cls.getMethod(methodName);
        // 6.执行方法
        method.invoke(obj);
    }
}
properties
className=com.domain.Person
methodName=eat

以上例子实则就是在配置文件中写下类名,通过反射获得到该类的Class对象进而可以得到这个类的信息或调用其方法


这个时候如果我们不用反射,而是直接导入类,通过new来生成实例的话,那么显然我们每次修改类的方法时,都需要再为这个类写一段调用不同方法的代码,或者使用不同类时,都需要重新写一段创建对象并使 用方法的代码,如下

// 使用Person类时
package com.reflect;
// 导入Person的包
import com.domain.Person;
public class ReflectTest {
    public static void main(String[] args) throws Exception {
        Person p = new Person();
        p.eat();
    }
}
// 使用Student类时
package com.reflect;
// 导入Student的包
import com.domain.Student;
public class ReflectTest {
    public static void main(String[] args) throws Exception {
        // 重写代码以创建对象并使用对象的方法
        Student stu = new Student();
        stu.sleep();
    }
}

但是我们并不希望修改代码,因为修改代码的风险更大,且需要重新进行编译,编写测试等工作(虽然这些在以上这个极其简单的例子难以体现),而即使我们不知道代码的具体实现,也能够修改配置来完成我们想实现的,如果存在变动,那么我们在配置里边写,以后无论什么信息修改了,我都能够通过修改配置的方式去实现,这不就可以提高程序的灵活性了吗?如果使用反射,我们只用将修改配置文件为如下便完成工作,是不是非常方便灵活!

className=com.domain.Student
methodName=sleep

相关文章
|
25天前
|
存储 Java
[Java]反射
本文详细介绍了Java反射机制的基本概念、使用方法及其注意事项。首先解释了反射的定义和类加载过程,接着通过具体示例展示了如何使用反射获取和操作类的构造方法、方法和变量。文章还讨论了反射在类加载、内部类、父类成员访问等方面的特殊行为,并提供了通过反射跳过泛型检查的示例。最后,简要介绍了字面量和符号引用的概念。全文旨在帮助读者深入理解反射机制及其应用场景。
16 0
[Java]反射
|
2月前
|
安全 Java 索引
Java——反射&枚举
本文介绍了Java反射机制及其应用,包括获取Class对象、构造方法、成员变量和成员方法。反射允许在运行时动态操作类和对象,例如创建对象、调用方法和访问字段。文章详细解释了不同方法的使用方式及其注意事项,并展示了如何通过反射获取类的各种信息。此外,还介绍了枚举类型的特点和使用方法,包括枚举的构造方法及其在反射中的特殊处理。
63 9
Java——反射&枚举
|
1月前
|
安全 Java 测试技术
🌟Java零基础-反射:从入门到精通
【10月更文挑战第4天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
25 2
|
2月前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
|
1月前
|
IDE Java 编译器
java的反射与注解
java的反射与注解
16 0
|
2月前
|
Java 程序员 编译器
Java的反射技术reflect
Java的反射技术允许程序在运行时动态加载和操作类,基于字节码文件构建中间语言代码,进而生成机器码在JVM上执行,实现了“一次编译,到处运行”。此技术虽需更多运行时间,但广泛应用于Spring框架的持续集成、动态配置及三大特性(IOC、DI、AOP)中,支持企业级应用的迭代升级和灵活配置管理,适用于集群部署与数据同步场景。
|
2月前
|
存储 安全 Java
扫盲java基础-反射(一)
扫盲java基础-反射(一)
|
2月前
|
Java
扫盲java基础-反射(二)
扫盲java基础-反射(二)
|
4月前
|
安全 Java 测试技术
day26:Java零基础 - 反射
【7月更文挑战第26天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
34 5
|
3月前
|
缓存 安全 Java
【Java 第十篇章】反射
Java 反射技术让程序能在运行时动态获取类信息并操作对象,极大提升了灵活性与扩展性。本文将介绍反射的基本概念、原理及应用,包括如何使用 `Class`、`Field`、`Method` 和 `Constructor` 类进行动态操作。此外,还将探讨反射在动态加载、框架开发与代码测试中的应用场景,并提醒开发者注意性能与安全方面的问题,帮助你更合理地运用这一强大工具。
29 0