• Java反射机制


    反射特征:动态性
    反射允许在运行时检查和操作类的信息、方法和字段,而无需在编译时明确知道这些信息。这种动态性使得你可以在程序运行期间根据具体情况进行操作,比如在后台已经运行的情况下,根据前端请求的内容动态地创建和调用对象。

    反射机制与面向对象的矛盾
    反射机制并不与面向对象编程的原则矛盾,而是为了增加程序的灵活性和适应动态性需求而引入的工具。封装是面向对象编程的一个重要原则,它强调将对象的内部状态和实现细节隐藏在类的内部,只提供公共接口供外部访问。虽然反射允许访问私有方法和属性,但它并不违背封装原则。

    反射调用私有方法和属性仍然受到Java的访问控制规则的限制。如果没有适当的权限,仍然无法访问私有成员。因此,反射并没有直接矛盾于封装原则,只是提供了一种机制,以便在需要的情况下能够访问和操作对象的内部细节。

    总之,反射是一个强大而灵活的工具,可以用于解决一些复杂的问题,但在使用时需要谨慎,以确保程序的安全性和可维护性。它与面向对象编程并不矛盾,而是为了增加编程的灵活性和动态性而引入的补充特性。

    除了动态性和与面向对象编程的兼容性之外,反射还具有以下一些特点和应用:

    1. 类加载器和动态类加载:反射允许你在运行时通过类加载器加载新的类,这对于实现插件式架构非常有用。你可以在不停止应用程序的情况下加载和卸载类。

    2. 序列化和反序列化:反射在Java对象的序列化和反序列化中发挥了关键作用。它允许你在对象与字节流之间进行转换,以便对象可以在网络传输或保存到文件时进行持久化。

    3. 框架和库开发:许多框架和库,如Spring框架,利用反射来实现依赖注入、AOP(面向切面编程)等功能。反射允许这些框架在运行时检查和操作类的结构。

    4. 调试和测试工具:一些调试和测试工具使用反射来分析和检查代码,以提供更丰富的调试信息和测试覆盖率报告。

    5. 动态代理:反射可以用于创建动态代理对象,这对于实现代理模式、事件处理等非常有用。

    6. JavaBeans:JavaBeans是一种可重用的软件组件,反射允许运行时检查和操作JavaBean的属性和事件。

    尽管反射提供了这些强大的功能,但它也需要小心使用,因为它可能会导致性能问题和安全隐患。在大多数情况下,最好尽量避免使用反射,除非没有其他合适的替代方案。

    反射来调用私有构造器, 私有属性以及方法

    可以使用 Class 类对象 clazz 结合 ConstructorFieldMethod 类的方法来实现。

    关于Java.lang.Class

    1、javac.exe 生成字节码文件(.class),
    2.接着使用java.exe命令对某个字节码文件进行解释运行,相当于将文件所对应的类加载到内存中,即为类的加载过程加载到内存中的类就称作运行时类,此运行时类本身作为Class类的一个对象。通过类的.class化做一个对象。类本身也是一个对象,是Class类的对象。万物皆对象。
    换句话说,Class类的实例就对应着一个运行时类

    加载到内存中的运行时类,会缓存一定时间,在此时间内,可以通过不同的方式获取,而不是创建一个对象。而且都是同一个对象,可以看作是单例的

    Class类的实例的方式

    1. //方式一:调用运行时类的属性.class,编译时会发现写死了
    2. Class clazz1 = Person.class;
    3. System.out.println(clazz1);
    4. //方式二:调用运行时类的对象的getClass()方法
    5. Person person = new Person();
    6. Class clazz2 = person.getClass();
    7. //返回的是这个对象的运行时类
    8. System.out.println(clazz2);
    9. //方式三:通过Class的静态方法:forName(全类名:String classpath)
    10. //好处:编译时候不会确定,运行时才会确定这个类存不存在
    11. Class clazz3 = Class.forName("com.nuaa.reflect.Person");
    12. System.out.println(clazz3);
    13. System.out.println(clazz1 == clazz2);
    14. System.out.println(clazz3 == clazz2);
    15. //方式四:使用ClassLoader类加载器的方式,通过loadClass函数来加载得到Person类
    16. ClassLoader classLoader = ReflectionTest.class.getClassLoader();
    17. Class clazz4 = classLoader.loadClass("com.nuaa.reflect.Person");
    18. System.out.println(clazz1 == clazz4);
    19. 运行和结果:
    20. class com.nuaa.reflect.Person
    21. class com.nuaa.reflect.Person
    22. class com.nuaa.reflect.Person
    23. true
    24. true

    Class类的实例可以是哪些:

    class
    Class
    接口
    数组(只要数组的类型和维度是一样的,就是同一个Class对象)
    基本数据类型
    注解

    类的加载器

    将.class文件字节码内容加载到内存空间中,并将这些静态数据转换成方法区运行时数据结构,然后在堆中生成一个代表这个类的java,lang.Class对象,作为方法区中类数据的入口。缓存中会维持一段时间,是对象就会被jvm垃圾收集机制处理。

    自顶向下:
    引导类加载器:用来装载Java核心类库,无法直接获取
    扩展类加载器:负责将jar包装入到工作库。
    系统类加载器:用来将指定全类名加载到内存。

    1. //对于自定义的类,使用的是系统类加载器进行加载
    2. ClassLoader classLoader1 = ReflectionTest.class.getClassLoader();
    3. System.out.println(classLoader1);
    4. //调用系统类加载器的getParent()方法可以获取扩展类加载器
    5. ClassLoader classLoader2 = classLoader1.getParent();
    6. System.out.println(classLoader2);
    7. //调用扩展类加载器的getParent()获取不到引导类加载器,获取不到引导类加载器的。
    8. //引导类加载器是主要负责加载Java的核心类库,无法加载自定义的类
    9. ClassLoader classLoader3 = String.class.getClassLoader();
    10. System.out.println(classLoader3);
    11. sun.miscLauncher$AppClassLoader@18b4aac2
    12. sun.miscLauncher$ExtClassLoader@85ede7b
    13. null

    两种方式读取配置文件

    两种方式都离不开对流的操作,但是流之前的操作可以不一样,对配置文件的默认识别位置也不一样,web工程下将配置文件放在src下进行管理。

    1. Properties properties = new Properties();
    2. //方式一:读取配置文件,此时文件默认在当前module下,要是需要寻找src目录下的文件,
    3. // 需要使用地址:src\\jdbc.properties
    4. FileInputStream fileInputStream = new FileInputStream("jdbc.properties");
    5. properties.load(fileInputStream);
    6. String user = properties.getProperty("user");
    7. String password = properties.getProperty("password");
    8. System.out.println("user=" + user + ",password=" + password);
    9. //方式二:使用ClassLoader,配置文件默认识别为当前module的src下
    10. ClassLoader classLoader = ReflectionTest.class.getClassLoader();
    11. //以流的方式获取资源,jdbc.properties本身就是一个资源包,
    12. // 以下只需要填上文件路径即可。
    13. InputStream resourceAsStream = classLoader.getResourceAsStream("jdbc.properties");
    14. properties.load(fileInputStream);
    15. String user = properties.getProperty("user");
    16. String password = properties.getProperty("password");
    17. System.out.println("user=" + user + ",password=" + password);

    通过反射造对象

    1. Class clazz = Class.forName("com.nuaa.reflect.Person");
    2. //newInstance()方法创建运行时类的对象,内部调用运行时类的空参构造器,
    3. //否则就会报错,找不到默认构造器。一般权限为public---》例如javabean
    4. Object o = clazz.newInstance();
    5. System.out.println(o);

    反射的动态性

    1. public void test1(){
    2. for (int i = 0; i < 100; i++) {
    3. int num = new Random().nextInt(3);
    4. String classPath = "";
    5. switch (num){
    6. case 0:
    7. classPath = "java.util.Date";
    8. break;
    9. case 1:
    10. classPath = "java.lang.Object";
    11. break;
    12. case 2:
    13. classPath = "com.nuaa.reflect";
    14. break;
    15. }
    16. Object instance = null;
    17. try {
    18. instance = getInstance(classPath);
    19. System.out.println(instance);
    20. } catch (Exception e) {
    21. e.printStackTrace();
    22. }
    23. }
    24. }
    25. public Object getInstance(String classPath) throws Exception {
    26. Class aClass = Class.forName(classPath);
    27. return aClass.newInstance();
    28. }
    29. 编译时不能确定具体的类,等到运行的时候能够确定

    反射获取类的所有结构

    1. Class clazz = Class.forName("com.nuaa.reflect.Person");
    2. //只能获取所有声明为public权限的属性。包含父类中声明的public属性
    3. Field[] fields = clazz.getFields();
    4. for (Object o: fields
    5. ) {
    6. System.out.println(o);
    7. }
    8. //获取当前运行时类所有声明的属性。(不包含父类中声明的属性)
    9. Field[] declaredFields = clazz.getDeclaredFields();
    10. //只能获取所有声明为public权限的方法。包含父类中声明的public方法
    11. Method[] methods = clazz.getMethods();
    12. for (Method method:methods
    13. ) {
    14. System.out.println(method);
    15. }
    16. //获取当前运行时类所有声明的方法。(不包含父类中声明的方法)
    17. Method[] declaredMethods = clazz.getDeclaredMethods();
    18. //获取方法声声明的注解,了解注解的功能。
    19. for (Method method:declaredMethods
    20. ) {
    21. Annotation annotation = method.getAnnotation();
    22. System.out.println(annotation);
    23. }
    24. //获取接口,异常信息等

    获取指定结构

    通过反射获取到的只能是一个空壳,还需要给它赋值,找对象
    属性:

    1. Class clazz = Class.forName("com.nuaa.reflect.Person");
    2. Person o = (Person) clazz.newInstance();
    3. //获取指定属性,但是只能获取运行时类中的pulic属性
    4. Field name = clazz.getField("age");
    5. name.set(o, 11);
    6. System.out.println(o.toString());
    7. 结果:
    8. java.lang.NoSuchFieldException: age
    9. Class clazz = Class.forName("com.nuaa.reflect.Person");
    10. Person o = (Person) clazz.newInstance();
    11. //获取指定属性,但是只能获取运行时类中的pulic属性
    12. Field name = clazz.getDeclaredField("name");
    13. name.set(o, "张三");
    14. System.out.println(o.toString());
    15. 结果2:java.lang.IllegalAccessException
    16. 拿到了但是毕竟是私有的
    17. 改进:
    18. Class clazz = Class.forName("com.nuaa.reflect.Person");
    19. Person o = (Person) clazz.newInstance();
    20. //获取指定属性,但是只能获取运行时类中的pulic属性
    21. Field name = clazz.getDeclaredField("name");
    22. //保证当前属性是可以访问的,然后才能获取和操作
    23. name.setAccessible(true);
    24. name.set(o, "张三");
    25. System.out.println(o.toString());

    获取特定方法

    1. Class clazz = Class.forName("com.nuaa.reflect.Person");
    2. Person o = (Person) clazz.newInstance();
    3. //函数名以及参数的名
    4. Method show = clazz.getDeclaredMethod("show", String.class);
    5. Method showDesc = clazz.getDeclaredMethod("showDesc");
    6. //1,如果方法非public,要设置访问许可!!
    7. //2.invoke()方法的返回值就是对应类中调用的方法的返回值,没有返回值就是null
    8. show.setAccessible(true);
    9. Object o1 = show.invoke(o, "哈哈哈");
    10. //invoke()要求第一个参数是对象,.class就是一个类又是一个对象,第二个参数可省。
    11. Object invoke = showDesc.invoke(Person.class);
    12. System.out.println(o1);
    13. System.out.println(invoke);

  • 相关阅读:
    【网络文明】关注网络安全
    次氯酸双光子聚集诱导发光型分子探针/聚集诱导发光环状多烯类分子/氮杂环聚集诱导发光分子的研究
    贪心算法(一)
    使用 Spring 实现控制反转和依赖注入
    一个.Net版本的ChatGPT SDK
    VUE cli3.0项目打包部署服务器
    Linux设置ssh免密登录
    【Java基础】List集合概述、特点、特有方法、案例及List集合子类的特点
    源码分析:规则引擎提交策略
    Springboot——如何保证服务启动后不自动停止?
  • 原文地址:https://blog.csdn.net/qq_57747969/article/details/132732903