信息发布→ 登录 注册 退出

Java反射之深入理解

发布时间:2026-01-11

点击量:
目录
  • 一、Java反射机制概述
  • 二、理解Class类并获取Class实例
    • 关于java.lang.Class类的理解
  • 三、通过反射创建对应的运行时类的对象(反射的应用1)
    • 四、获取运行时类的完整结构(反射的应用2)
      • 1.获取当前运行时类的属性结构
      • 2.获取运行时类的方法结构
      • 3.获取运行时类的其他结构
    • 五、调用运行时类的指定结构(反射的应用3)

      一、Java反射机制概述

      //反射之前,对于Person的操作
          @Test
          public void test1(){
              //1.创建Person类的对象
              Person p1 = new Person("Tom",12);
              //2.通过对象,调用其内部的属性、方法
              p1.age = 10;
              System.out.println(p1.toString());//Person{name='Tom', age=10}
              p1.show();//你好,我是一个人
              //在Person类外部,不可以通过Person类的对象调用其内部私有结构。
              //比如:name、showNation()以及私有的构造器
          }
          //反射之后,对于Person的操作
          @Test
          public void test2() throws Exception {
              Class clazz = Person.class;
              //1.通过反射,创建Person类的对象
              Constructor cons = clazz.getConstructor(String.class, int.class);
              Object obj = cons.newInstance("Tom", 12);
              Person p = (Person)obj;
              System.out.println(p.toString());
              //2.通过反射,调用对象指定的属性、方法
              //调用属性
              Field age = clazz.getDeclaredField("age");
              age.set(p,10);
              System.out.println(p.toString());
              //调用方法
              Method show = clazz.getDeclaredMethod("show");
              show.invoke(p);
              System.out.println("*********************************************");
              //通过反射,可以调用Person类的私有结构。比如:私有的构造器、方法、属性
              //调用私有的构造器
              Constructor cons1 = clazz.getDeclaredConstructor(String.class);
              cons1.setAccessible(true);
              Person p1 =(Person) cons1.newInstance("Jerry");
              System.out.println(p1);
              //调用私有的属性
              Field name = clazz.getDeclaredField("name");
              name.setAccessible(true);
              name.set(p1,"HanMeimei");
              System.out.println(p1);
              //调用私有的方法
              Method showNation = clazz.getDeclaredMethod("showNation", String.class);
              showNation.setAccessible(true);
              String nation = (String) showNation.invoke(p1, "中国");//相当于String nation = p1.showNation("中国")
              System.out.println(nation);
          }

      疑问1:通过直接new的方式过反射的方式都可以调用公共的结构,开发中到底用那个?
      建议:直接new的方式
      什么时候后使用:反射的方式?

      (反射的特征:动态性)在编译时不能确定要造那个类的对象时,使用反射。
      疑问2:反射的机制与面向对象中的封装性是不是矛盾的?如何看待两个技术?
      不矛盾。我的理解:开发中,封装性给我们的提示是,私有的属性不建议直接修改,而是通过get、set方法去做。反射给我们的的提示是,不建议直接修该的私有属性,如果非要修改,也是可以通过反射修改的。总结:反射解决的是能不能调用的问题,封装性解决的是建议我们如何掉的问题。

      二、理解Class类并获取Class实例

      关于java.lang.Class类的理解

      //获取Class的实力的方式(前三种方式需要掌握)
          @Test
          public void test3() throws ClassNotFoundException {
              //方式一:调用运行时类的属性:class
              Class clazz1 = Person.class;
              System.out.println(clazz1);//class zx_reflection.Person
              //方式二:通过运行时类的对象,调用getClass()
              Person p1 = new Person();
              Class clazz2 = p1.getClass();
              System.out.println(clazz2);//class zx_reflection.Person
              //方式三:调用Class的静态方法:forName(String classPath)(这种方式常用)
              Class clazz3 = Class.forName("class zx_reflection.Person");
              System.out.println(clazz3);//class zx_reflection.Person
              System.out.println(clazz1 == clazz2);//true
              System.out.println(clazz1 == clazz3);//true
              //方式四:使用类的加载器:ClassLoader(了解)
              ClassLoader classLoader = ReflectionTest.class.getClassLoader();
              Class clazz4 = classLoader.loadClass("class zx_reflection.Person");
              System.out.println(clazz4);
          }
          //Class实例可以时那些结构的说明:
          @Test
          public void test4(){
              Class c1 = Object.class;
              Class c2 = Comparable.class;
              Class c3 = String[].class;
              Class c4 = int[][].class;
              Class c5 = ElementType.class;
              Class c6 = Override.class;
              Class c7 = int.class;
              Class c8 = void.class;
              Class c9 = Class.class;
              int[] a = new int[10];
              int[] b = new int[100];
              Class c10 = a.getClass();
              Class c11 = b.getClass();
              //只要数组的元素类型与维度一样,就是同一个Class
              System.out.println(c10 == c11);//true
          }


      1.类的加载的过程:
      程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。
      接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。
      加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。
      2.换句话说,Class的实例就对应着一个加载到内存中的运行时类。
      3.加载到内存中的一个运行时类,会缓存一段时间;在此时间之内 ,我们可以通过不同的方式来获取此运行时类。

       @Test
          public void test1(){
              //对于自定义类,使用系统类加载器进行加载
              ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
              System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
              //调用系统类加载器的getParent():获取扩展类加载器
              ClassLoader classLoader1 = classLoader.getParent();
              System.out.println(classLoader1);//sun.misc.Launcher$ExtClassLoader@54bedef2
              //调用扩展类加载器的getParent():无法获取引导类加载器
              //引导类加载器主要负责加载java的核心类库,无法加载自定义类的。
              ClassLoader classLoader2 = classLoader1.getParent();
              System.out.println(classLoader2);//null
              ClassLoader classLoader3 = String.class.getClassLoader();
              System.out.println(classLoader3);//null
          }
          Properties:用来读取配置文件
          
          @Test
          public void test2() throws Exception {
              Properties pros = new Properties();
              //此时的文件默认在当前的module下
              //读取配置文件的方式一:
      //        FileInputStream fis = new FileInputStream("jdbc.properties");
      //        pros.load(fis);
              //读取配置文件的方式二:使用ClassLoader
              //配置文件默认识别为:当前module的src下
              ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
              InputStream is = classLoader.getResourceAsStream("jdbc1.properties");
              pros.load(is);
       
              String user = pros.getProperty("user");
              String password = pros.getProperty("password");
              System.out.println("user = " + user + ",password" + password);//user = 吴飞,passwordabc123
          }

      三、通过反射创建对应的运行时类的对象(反射的应用1)

      public class NewInstanceTest {
          @Test
          public void test1() throws IllegalAccessException, InstantiationException {
              Class<Person> clazz = Person.class;
              /*
              newInstance():调用此方法,创建对应的运行时类的对象,内部调用了运行时类的空参构造器
              要想此方法正常的创建运行时类的对象,要求:
              1.运行时类必须提供空参的构造器
              2.空参的构造器的访问权限得够。通常,设置为public
       
              在javabean中要求提供一个public的空参构造器。原因:
              1.便于通过反射,创建运行时类的对象
              2.便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器
               */
              Person obj = clazz.newInstance();
              System.out.println(obj);//Person{name='null', age=0}
          }
          //体会反射的动态性
          @Test
          public void test2() {
              int num = new Random().nextInt(3);//0,1,2
              String classPath = "";
              switch(num){
                  case 0:
                      classPath = "java.util.Date";
                      break;
                  case 1:
                      classPath = "java.lang.Object";
                      break;
                  case 2:
                      classPath = "zx_reflection/Person.java";
                      break;
                  try {
                      Object obj = getInstance(classPath);
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              }
          }
          /*
          创建一个指定类的对象
          classPath:指定类的全类名
           */
          public Object getInstance(String classPath) throws Exception {
              Class clazz = Class.forName(classPath);
              return clazz.newInstance();
          }
      }

      总结:创建类的对象的方式?

      方式一:new + 构造器

      方式二:要创建Xxx类的对象,可以考虑:Xxx、Xxxs、XxxBuilder类中查看是否有静态方法的存在。可以调用其静态方法,创建Xxx对象。

      方式三:通过反射。(使用频率、先后顺序与书写顺序一致)

      四、获取运行时类的完整结构(反射的应用2)

      1.获取当前运行时类的属性结构

      @Test
          public void test1(){
              Class clazz = Person.class;
              //获取属性结构
              //getFields():获取当前运行时及其父类中声明为public访问权限的属性
              Field[] fields = clazz.getFields();
              for(Field f : fields){
                  System.out.println(f);
              }
              System.out.println();
              //getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
              Field[] declaredFields = clazz.getDeclaredFields();
              for(Field f : declaredFields){
                  System.out.println(f);
              }
          }
          @Test
          public void test2(){
              Class clazz = Person.class;
              Field[] declaredFields = clazz.getDeclaredFields();
              for(Field f : declaredFields){
                  //1.权限修饰符
                  int modifiers = f.getModifiers();
                  System.out.print(Modifier.toString(modifiers) + "\t");
                  //2.数据类型
                  Class type = f.getType();
                  System.out.print(type.getName() + "\t");
                  //3.变量名
                  String fName = f.getName();
                  System.out.print(fName);
              }
          }

      2.获取运行时类的方法结构

       @Test
          public void test1(){
              Class clazz = Person.class;
              //getMethods():获取当前运行时类及所有父类中声明为public权限的方法
              Method[] methods = clazz.getMethods();
              for(Method m : methods){
                  System.out.println(m);
              }
              System.out.println();
              //getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
              Method[] declaredMethods = clazz.getDeclaredMethods();
              for(Method m : declaredMethods){
                  System.out.println(m);
              }
          }
          /*
          @Xxxx
          权限修饰符   返回值类型   方法名(参数类型1 形参名1,...) throws XxxException{}
           */
          @Test
          public void test2(){
              Class clazz = Person.class;
              Method[] declaredMethods = clazz.getDeclaredMethods();
              for(Method m : declaredMethods){
                  //1.获取方法声明的注释
                  Annotation[] annos = m.getAnnotations();
                  for(Annotation a : annos){
                      System.out.println(a);
                  }
                  //2.权限修饰符
                  System.out.print(Modifier.toString(m.getModifiers()) + "\t");
                  //3.返回值类型
                  System.out.print(m.getReturnType().getName() + "\t");
                  //4.方法名
                  System.out.print(m.getName());
                  System.out.print("(");
                  //5.形参列表
                  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.println(parameterTypes[i].getName() + "args_" + i);
                              break;
                          }
                          System.out.print(parameterTypes[i].getName() + "args_" + i + ",");
                      }
                  }
                  System.out.print(")");
                  //6.抛出的异常
                  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.println(exceptionTypes[i].getName() + ",");
                      }
                  }
              }
          }

      3.获取运行时类的其他结构

       /*
          获取构造器结构
           */
          @Test
          public void test1(){
              Class clazz = Person.class;
              //getConstructors():获取当前运行时类中声明为public的构造器
              Constructor[] constructors = clazz.getConstructors();
              for(Constructor c : constructors){
                  System.out.println(c);
              }
              System.out.println();
              //getDeclaredConstructors():获取当前运行时类中声明的所有的构造器
              Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
              for(Constructor c : declaredConstructors){
                  System.out.println(c);
              }
          }
          /*
          获取运行时类的父类
           */
          @Test
          public void test2(){
              Class clazz = Person.class;
              Class superclass = clazz.getSuperclass();
              System.out.println(superclass);//class zx_reflection1.Creature
          }
          /*
          获取运行时类的带泛型的父类
           */
          @Test
          public void test3(){
              Class clazz = Person.class;
              Type genericSuperclass = clazz.getGenericSuperclass();
              System.out.println(genericSuperclass);//zx_reflection1.Creature<java.lang.String>
          }
          /*
          获取运行时类的带泛型的父类的泛型
           */
          @Test
          public void test4(){
              Class clazz = Person.class;
              Type genericSuperclass = clazz.getGenericSuperclass();
              ParameterizedType paramType = (ParameterizedType)genericSuperclass;
              //获取泛型类型
              Type[] actualTypeArguments = paramType.getActualTypeArguments();
              System.out.println(actualTypeArguments[0].getTypeName());//java.lang.String
          }
          /*
          获取运行时类实现的接口
           */
          @Test
          public void test5(){
              Class clazz = Person.class;
              Class[] interfaces = clazz.getInterfaces();
              for(Class c : interfaces){
                  System.out.println(c);
              }
              System.out.println();
              //获取运行时类的父类实现的接口
              Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
              for(Class c : interfaces1){
                  System.out.println(c);
              }
          }
          /*
          获取运行时类所在的包
           */
          @Test
          public void test6(){
              Class clazz = Person.class;
              Package pack = clazz.getPackage();
              System.out.println(pack);//package zx_reflection1
          }
          /*
          获取运行时类声明的注解
           */
          @Test
          public void test7(){
              Class clazz = Person.class;
              Annotation[] annotations = clazz.getAnnotations();
              for(Annotation annos : annotations){
                  System.out.println(annos);//@zx_reflection1.MyAnnotation(value=hi)
              }
          }

      五、调用运行时类的指定结构(反射的应用3)

      @Test
          public void testField() throws Exception {
              Class clazz = Person.class;
              //创建运行时类的对象
              Person p = (Person)clazz.newInstance();
              //获取指定的属性:要求运行时类中属性声明为public(通常不采用此方法,因为属性很少声明为public,常用的在下面)
              Field id = clazz.getField("id");
              /*
              设置当前属性的值
              set():参数1:指明设置那个对象的属性  参数2:将此属性值设置为多少
               */
              id.set(p,1001);
              /*
              获取当前属性的值
              get():参数1:获取那个对象的当前属性值
               */
              int pId = (int)id.get(p);
              System.out.println(pId);
          }
          /*
          如何操作运行时类中的指定的属性----需要掌握
           */
          @Test
          public void testField1() throws Exception {
              Class clazz = Person.class;
              //创建运行时类的对象
              Person p = (Person)clazz.newInstance();
              //1.getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
              Field name = clazz.getDeclaredField("name");
              //2.保证当前属性是可访问的
              name.setAccessible(true);
              //3.获取、设置指定对象的此属性值
              name.set(p,"Tom");
              System.out.println(name.get(p));
          }
          /*
          如何操作运行时类中的指定的方法 -- 需要掌握
           */
          @Test
          public void testMethod() throws Exception {
              Class clazz = Person.class;
              //创建运行时类的对象
              Person p  = (Person)clazz.newInstance();
              /*
              1.获取指定的某个方法
              getDeclaredMethod():参数1:指明获取的方法的名称  参数2:指明获取的方法的形参列表
               */
              Method show = clazz.getDeclaredMethod("show", String.class);
              //2.保证当前方法是可访问的
              show.setAccessible(true);
              /*
              2.调用方法的invoke():参数1:方法的调用者  参数2:给方法形参赋值的实参
              invoke()的返回值即为对应类中调用的方法的返回值
               */
              Object returnValue = show.invoke(p, "CHN");//String nation = p.show()
              System.out.println(returnValue);
              System.out.println("*******************如何调用静态方法*********************************");
              Method showDesc = clazz.getDeclaredMethod("showDesc");
              showDesc.setAccessible(true);
              //如果调用的运行时类中的方法没有返回值,则此invoke()返回null
              Object returnVal = showDesc.invoke(Person.class);
              System.out.println(returnVal);//null
          }
          /*
          如何调用运行时类中的指定的构造器
           */
          @Test
          public void testConstructor() throws NoSuchMethodException {
              Class clazz = Person.class;
              /*
              1.获取指定的构造器
              getDeclaredConstructor():参数:指明构造器的参数列表
               */
              Constructor constructor = clazz.getDeclaredConstructor(String.class);
              //2.保证此构造器是可访问的
              constructor.setAccessible(true);
          }
      在线客服
      服务热线

      服务热线

      4008888355

      微信咨询
      二维码
      返回顶部
      ×二维码

      截屏,微信识别二维码

      打开微信

      微信号已复制,请打开微信添加咨询详情!