IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Java 反射 -> 正文阅读

[Java知识库]Java 反射

反射

一、反射概述

  1. Reflection被视为动态语言的关键,反射机制允许程序在执行期间借助ReflectionAPI取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。在这里插入图片描述

  2. 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。就像一面镜子,透过这个镜子可以看到类的结构,所以我们形象的称之为:反射 在这里插入图片描述

  3. 理解Class类
    • 通过类的加载理解

      程序经过javac.exe命令后,会生成一个或多个字节码文件(.class),接着我们会使用java.exe命令对某个字节码文件进行解释运行,相当于将某个字节码文件加载到内存中。加载到内存中的类,我们称为运行时类,此运行时类,就作为Class的一个实例深度解读万事万物皆对象,类也是一个对象

      	Class c = Person.class;
      
    • 换句话说,Class类的实例对象(不能用new)就对象这一个运行时的类

      	// 该实例就是对应着的Person类   class com.atguigu.java.Person
      	System.out.prinln(c)
      
    • 加载到内存中的运行时类,会缓存一段时间。在此时间之内,我们可通过如下4种方式来获取此运行时类的Class实例。每个运行时类,对应唯一的一个Class实例,不会重复创建

      方式三比较常用,因为其体现了反射的动态性,不会在编译期就写死,可以在运行时根据情况进行改变

      // 方式一:调用运行时类的属性 .class
      Class c1 = Person.class;
      // 方式二:通过运行时类的对象,调用getClass()
      Person p = new Person();
      Class c2 = p.getClass();
      // 方式三:调用Class的静态方法forName(String classPath)
      Class c3 = Class.forName("com.atguigu.java.Person");
      // 方式四:使用类的加载器 ClassLoader:通过测试类(含有main方法调用该类)来获取运行时类的Class实例
      ClassLoader cLoader = ReflectionTest.class.getClassLoader();
      Class c4 = cLoader.loadClass("com.atguigu.java.Person");
      
      
    • 可Class实例的结构
      在这里插入图片描述

  4. 类的加载过程
    • 类加载流图
      在这里插入图片描述

    • 获取类加载器

      getClass Loader() / getParent()

      public class ClassloaderTest {
          @Test
          public void test1() {
              // 系统类加载器 sun.misc.Launcher$AppClassLoader@18b4aac2
              ClassLoader loader = ClassloaderTest.class.getClassLoader();
              System.out.println(loader);
              // 扩展类加载器 sun.misc.Launcher$ExtClassLoader@28a418fc
              ClassLoader loaderParent = loader.getParent();
              System.out.println(loaderParent);
              // 核心类加载器 null,不能被获得
              ClassLoader parent = loaderParent.getParent();
              System.out.println(parent);
              // null,同上
              ClassLoader stringClassloader = String.class.getClassLoader();
              System.out.println(stringClassloader);
          }
      }
      
    • properties配置文件的读取通过Class实例,因为实际项目中配置文件一般放在src下

      public class PropertiesTest {
          @Test
          public void test1() {
              FileInputStream fis = null;
              try {
                  Properties pros = new Properties();
                  
      			// 方式一:通过节点流的方式读取,此配置文件放在module下
                  fis = new FileInputStream("jdbc.properties");
                  pros.load(fis);
      			
                  // 方式二:通过运行时类的Class实例读取,此配置文件放在module的src文件夹下
                  ClassLoader loader = PropertiesTest.class.getClassLoader();
                  InputStream resource = loader.getResourceAsStream("jdbc1.properties");
                  pros.load(resource);
      
                  String user = pros.getProperty("user");
                  String password = pros.getProperty("password");
                  System.out.println("user" + user + "\n" + "password" + password);
              } catch (IOException e) {
                  e.printStackTrace();
              } finally {
                  if (fis != null) {
                      try {
                          fis.close();
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                  }
              }
          }
      }
      

二、获取运行时类的完整结构(参考即可)

  1. 创建运行时类的对象

    newInstance():内部调用了该运行时类的空参构造器,为通用方法

    @Test
        public void test() throws InstantiationException, IllegalAccessException {
            Class<Person> c = Person.class;
            Person p = c.newInstance();
        }
    

    注:要想此方法正常的创建运行时类的对象,需满足

    • 运行时类必须提供空参构造器
    • 空参构造器的访问权限需够用,设置为public
  2. 获取属性及其内部结构
    @Test
        public void testFields() {
            Class<Person> c = Person.class;
            // 1.获取属性
            // 1.1 getFields():获取当前运行时类及其父类的所有public权限的属性
            Field[] fields = c.getFields();
            for (Field f : fields) {
                System.out.println(f);
            }
            // 1.2 getDeclaredFields():获取当前运行时类的所有属性
            Field[] declaredFields = c.getDeclaredFields();
            for (Field f : declaredFields) {
                System.out.println(f);
            }
    
            // 2.获取属性的内部结构
            Field[] declaredFields = c.getDeclaredFields();
            for (Field f : declaredFields) {
                // 2.1 权限修饰符
                int modifiers = f.getModifiers();
                // 在modifier类(修饰符类)中public是1,protected是0,private是1
                System.out.print(modifiers + "\t");
                // 使用Modifier的toString方法可以直接输出修饰符具体名称
                System.out.print(Modifier.toString(modifiers) + "\t");
    
                // 2.2数据类型
                Class<?> type = f.getType();
                System.out.print(type + "\t");
    
                // 2.3 属性名
                String fName = f.getName();
                System.out.println(fName);
            }
        }
    
  3. 获取方法及其内部结构

    经常用的是通过获取运行时类的方法,得到其注解(框架 = 注解 + 反射 + 设计模式

    @Test
        public void testMethods() {
            Class<Person> c = Person.class;
            // 1.获取方法
            // 1.1 getMethods():获取当前运行时类及其父类(包括根类Object)中的所有声明为public权限的方法
            Method[] methods = c.getMethods();
            for (Method m : methods) {
                System.out.println(m);
            }
    
            // 1.2 getDeclaredMethods():获取当前运行时类中声明的所有方法
            Method[] methods2 = c.getDeclaredMethods();
            for (Method m : methods2) {
                System.out.println(m);
            }
            // 2.获取方法内部结构
            Method[] methods1 = c.getDeclaredMethods();
            for (Method m : methods1) {
                System.out.println();
                // 2.1 方法注解
                Annotation[] annotations = m.getAnnotations();
                for (Annotation a : annotations) {
                    System.out.println(a);
                }
                System.out.print(m + "\t");
                // 2.2 权限修饰符
                System.out.print(Modifier.toString(m.getModifiers()) + "\t");
                // 2.3 返回值类型
                System.out.print(m.getReturnType().getName() + "\t");
                // 2.4 方法名
                System.out.print(m.getName());
                // 2.5 形参列表
                System.out.print("(");
                Class[] parameterTypes = m.getParameterTypes();
                if (!(parameterTypes == null && parameterTypes.length == 0)) {
                    for (int i = 0; i < parameterTypes.length; i++) {
                        if (i == parameterTypes.length-1) {
                            System.out.print(parameterTypes[i].getName() + " args_" + i);
                            break;
                        }
                        System.out.print(parameterTypes[i].getName() + " args_" + i + ",");
                    }
                }
                System.out.println(")");
                // 2.6 抛出异常 throws XxxException()
                Class[] exceptionTypes = m.getExceptionTypes();
                if (exceptionTypes.length > 0) {
                    System.out.print("throws  ");
                    for (int i = 0; i < exceptionTypes.length; i++) {
                        if (i == exceptionTypes.length-1) {
                            System.out.println(exceptionTypes[i].getName());
                            break;
                        }
                        System.out.print(exceptionTypes[i].getName() + ",");
                    }
                }
            }
        }
    
  4. 获取构造器及其内部结构
    @Test
        public void testConstructors() {
            Class<Person> c = Person.class;
            // 1. 获取构造器
            // 1.1 getConstructors()获取当前运行时类声明的public权限的构造器
            Constructor[] constructors = c.getConstructors();
            for (Constructor cons : constructors) {
                System.out.println(cons);
            }
            // 1.2 getDeclaredConstructors()获取当前运行时类的所有构造器
            Constructor[] constructors1 = c.getDeclaredConstructors();
            for (Constructor cons : constructors1) {
                System.out.println(cons);
            }        
        }
    
  5. 获取运行时类的父类

    单继承,只有一个父类

    @Test
        public void testSuperClass() {
            Class<Person> c = Person.class;
            // 1 getSuperclass() 不带泛型的父类
            Class superclass = c.getSuperclass();
            System.out.println(superclass);
            // 2 getGenericSuperclass()  带泛型的父类
            Type genericSuperclass = c.getGenericSuperclass();
            System.out.println(genericSuperclass);
            // 3 获取带泛型父类的泛型参数
            ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            for (Type cl : actualTypeArguments ) {
                System.out.println(cl.getTypeName());
            }
        }
    
  6. 获取运行时类实现的接口
    @Test
        public void testInterface() {
            Class c = Person.class;
            // 1 获取运行时类的接口
            Class[] interfaces = c.getInterfaces();
            for (Class in : interfaces) {
                System.out.println(in);
            }
            // 2 获取运行时类的父类的接口
            Class[] interfaces1 = c.getSuperclass().getInterfaces();
            for (Class in1 : interfaces1) {
                System.out.println(in1);
            }
        }
    
  7. 获取运行时类所在的包
     @Test
        public void testPackage() {
            Class c = Person.class;
            Package aPackage = c.getPackage();
            System.out.println(aPackage);
        }
    
  8. 获取运行时类的注解

    作用:通过反射获取注解后来得知该自定义类具体是要做什么

    @Test
        public void testAnnotation() {
            Class c = Person.class;
            Annotation[] annotations = c.getAnnotations();
            for (Annotation a : annotations) {
                System.out.println(a);
            }
        }
    
  9. 重要

    9.1 获取父类的泛型

    9.2 获取运行时类实现的接口(动态代理中会用)

    在这里插入图片描述

    9.3 获取运行时类的注解(在框架中使用较多)

三、调用运行时类的制定结构(方法、属性、构造器)

  1. 操作运行时类的属性
    @Test
        public void testFiled() {
            // 1 创建运行时类的Class实例
            Class<Person> c = Person.class;
            try {
                // 2 创建运行时类的对象
                Person p = (Person) c.newInstance();
                // 3 获取指定的属性
                Field id = c.getDeclaredField("id");
                // 4 保证当前属性是可访问的
                id.setAccessible(true);
                // 5 设置当前属性的值
                // set(): 参数1 指明设置哪个对象的属性   参数2:将此属性值设置为多少
                id.set(p,1001);
                // 6 获取当前属性的值
                // get(): 参数1 获取哪个对象的当前属性值
                int pId = (int)id.get(p);
                System.out.println(pId);
                
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
    
  2. 操作运行时类的方法
    @Test
        public void testMethod() {
            Class<Person> c = Person.class;
            try {
                // 1 创建运行时类的对象
                Person p = c.newInstance();
                // 2 获取指定方法
                // getDeclareMethod():参数1 指明获取的方法名称;参数2 指明获取方法的形参列表
                Method show = c.getDeclaredMethod("show", String.class);
                // 3 保证当前的方法是可访问的
                show.setAccessible(true);
                // 4 给方法传参,执行方法
                // 4.1 非静态方法
                // invoke(): 参数1 方法的调用对象;参数2 给方法传的实参;返回值 与调用方法返回值一致
                Object chn = show.invoke(p, "CHN");
                System.out.println((String)chn);
                // 4.2 静态方法,下面两种都可以
                Object chn1 = show.invoke(Person.class);
                Object chn2 = show.invoke(null)
                
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
    
        }
    
  3. 调用运行时类的构造器

    决定多数的情况,创建运行时类的对象使用的newInstance( ),调用空参构造器

        Class<Person> c = Person.class;
        Person p = c.newInstance();
    

    下面是调用指定构造器

    @Test
        public void testConstructor() {
            Class<Person> c = Person.class;
            try {
                // 1.指明构造器的参数列表(同一类的构造器名都一样)
                Constructor<Person> constructor = c.getDeclaredConstructor(String.class);
                // 2.保证此构造器是可访问的
                constructor.setAccessible(true);
                // 3.调用此构造器创建运行时类的对象
                Person tom = constructor.newInstance("Tom");
                System.out.println(tom);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
    
        }
    
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-12-11 15:35:19  更:2021-12-11 15:36:06 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 6:01:41-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码