• Java笔记--反射机制一


    反射机制的作用

    通过Java语言中的反射机制可以操作字节码文件(读和修改字节码文件),操作代码片段。

    反射机制中重要的类

    java.lang.Class:代表整个字节码,整个类。

    java.lang.reflect.Method:代表字节码中的方法字节码,代表类中的方法。

    java.lang.reflect.Constructor:代表字节码中的构造方法字节码,代表类中的构造方法。

    java.lang.reflect.Field:代码字节码中的属性字节码,代表类中的成员属性。

    示例:

    //java.lang.Class
    public class ReflectTest {
    
        //java.lang.reflect.Field
        public int age;
        
        //java.lang.reflect.Constructor
        public ReflectTest(){
            
        }
        
        //java.lang.reflect.Method
        public void doSome(){
            
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    获取Class的三种方式

    要想要操作一个类的字节码,首先要获取到这个类的字节码。

    方法一:Class.forName()

    1. 这是一个静态方法
    2. 该方法的参数类型时字符串
    3. 字符串需要是一个完整的类名
    4. 该类名必须带有包名
    5. 要处理编译异常

    示例:

    public class ReflectTest {
    
        public static void main(String[] args) {
            try {
                Class c1 = Class.forName("java.lang.String");// c1表示String.class文件,或者说是String类
                Class c2 = Class.forName("java.lang.Runnable");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    方法二:对象.getClass()

    java中任何一个对象都有getClass()方法

    示例:

    public class ReflectTest {
    
        public static void main(String[] args) {
            Class c1 = null;
            try {
                c1 = Class.forName("java.lang.String");// c1表示String.class文件,或者说是String类
                Class c2 = Class.forName("java.lang.Runnable");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
    
            // 通过getClass()方法获取字节码文件
            String x = "test";
            Class c3 = x.getClass();
            System.out.println(c1 == c3); // 输出true
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    方法三:类型.class

    java语言中任何一种类型,包括基本数据类型,都有class属性

    示例:

    public class ReflectTest {
    
        public static void main(String[] args) {
            String x = "test";
            Class c3 = x.getClass();
    
            // java语言中所有类型都有.class属性
            Class c4 = String.class;
            System.out.println(c4 == c3); //输出true
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    通过反射机制实例化对象

    获取到Class文件后能干什么?

    通过Class的newInstacne方法实例化对象,newInstance实际上是调用了无参构造方法,必须保证无参构造方法存在。

    示例:

    User测试类

    public class User {
    
        public User() {
            System.out.println("无参构造方法被调用!!!");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    通过获取User类的字节码文件,实例化User类对象

    public class ReflectTest01 {
        public static void main(String[] args) {
            try {
                // 获取字节码文件
                Class c1 = Class.forName("yxl.more.User");
    
                // 操作字节码文件
                // 创建User对象,newInstance会调用User类中的无参构造方法,完成对象的创建
                // 如果没有无参构造方法,会报错。
                Object user = c1.newInstance();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    使用反射机制创建对象和不使用反射机制的区别

    1. 使用反射机制创建对象更加灵活

      不需要修改源代码,只需要修稿配置文件,就能够实现不同对象的实例化。大部分高级框架底层的实现原理都采用了反射机制。

      classinfo.properties 配置文件内容

      className=yxl.more.User
      
      • 1

      测试类内容

      public class IoProperties {
          public static void main(String[] args) {
              try {
                  // 读取文件
                  FileReader reader = new FileReader("D:\\JAVA笔记\\Test\\AnnotationTest\\src\\classinfo");
                  Properties properties = new Properties();
                  
                  // 加载流
                  properties.load(reader);
                  
                  // 关闭流
                  reader.close();
                  
                  // 获取Map键值对对象,得到key为className的值
                  String name = properties.getProperty("className");
                  
                  // 获取class文件
                  Class c1 = Class.forName(name);
                  
                  // 实例化对象
                  System.out.println(c1.newInstance());
              } catch (FileNotFoundException e) {
                  e.printStackTrace();
              } catch (IOException e) {
                  e.printStackTrace();
              } catch (ClassNotFoundException e) {
                  e.printStackTrace();
              } catch (InstantiationException e) {
                  e.printStackTrace();
              } catch (IllegalAccessException e) {
                  e.printStackTrace();
              }
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34

    Class.forName()发生了什么?

    Class.forName()的执行会导致:类加载。如果只想让类中的静态代码块执行,可以使用Class.forName(“完整类名”),因为静态代码块会在类加载时执行

    示例验证:

    public class ReflectTest02 {
    
        public static void main(String[] args) {
            try {
                Class.forName("yxl.reflect.User");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    
    class User{
        // 静态代码块 会在类加载时执行
        static {
            System.out.println("静态代码块执行!!!");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    获取类路径下文件的绝对路径

    **类路径下:**凡是在src下的都是类路径下。

    src是类的根路径。

    使用此方法的前提是这个文件在类路径下。

    代码解释:

    Thread.currentThread():获取当前线程对象

    getContextClassLoader():获取当前线程的类加载器对象

    getResource():获取资源 (当前线程的类加载器会从类的根路径开始加载资源,所以需要传入文件的从src之后的路径名)

    public class GetPath {
    
        public static void main(String[] args) {
            String path = Thread.currentThread()
                    .getContextClassLoader()
                    .getResource("test")
                    .getPath();
            System.out.println(path);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    以流的形式返回

    不再是返回绝对路径了。

    InputStream reader = Thread.currentThread().getContextClassLoader().getResourceAsStream("classinfo");
    
    • 1
    public class IoProperties {
        public static void main(String[] args) {
            try {
    //            FileReader reader = new FileReader("D:\\JAVA笔记\\Test\\AnnotationTest\\src\\classinfo");
                // 以流的形式返回
                InputStream reader = Thread.currentThread()
                        .getContextClassLoader()
                        .getResourceAsStream("classinfo");
    
                Properties properties = new Properties();
                properties.load(reader);
                reader.close();
                String name = properties.getProperty("className");
                Class c1 = Class.forName(name);
                System.out.println(c1.newInstance());
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    资源绑定器(ResourceBundle)

    便于获取配置文件中的内容,简化了创建流、加载流、关闭流等一系列操作。

    使用条件:

    被绑定的资源必须是properties文件,且这个文件必须在类路径下,同时在写路径时,properties的文件扩展名不能写。

    使用步骤:

    1. 绑定资源
    2. 通过键值对的方式获取值

    示例代码:

    public class ResourceBundleTest {
        public static void main(String[] args) {
            ResourceBundle bundle = ResourceBundle.getBundle("classinfo");
            System.out.println(bundle.getString("className"));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 相关阅读:
    【C++入门指南】类和对象(上)
    std::make_shared和new初始化智能指针的区别
    虚拟桌面和云桌面办公系统
    滑动窗口 ( 单调队列 )
    JavaSE --- 多线程
    Java面向对象(基础)--方法应用
    易点易动固定资产管理系统:定制流程与用量控制的高效管理利器
    快手直播显示请求过快
    GoogleTest学习笔记(一)
    2022-iOS个人开发者账号申请流程
  • 原文地址:https://blog.csdn.net/qq_46653910/article/details/125563668