zoukankan      html  css  js  c++  java
  • 反射


    一.反射的基本概念
    1.类的加载:
         将一个.class文件加载到内存中,形成一个对象,并执行起来;
         Java 虚拟机内使用类加载器将.class文件加载到内存中,形成一个对象;这个对象在内存中只有一份;由虚拟机类加载器负责创建,程序员只能获取使用,程序员不能自己创建;
         进一步细分,可以分为:加载,连接,初始化;
             加载:将.class读取到内存中的过程;
             连接:检查语法格式与关键代码,及常量的赋值与常量的运算;
             初始化:创建出来对象,和以前面向对象的初始化过程一致;
    2.类加载器:用于将所有的.class文件(磁盘上的或者网络上的)加载到内存中,并为之生成对应的java.lang.Class 对象
         Bootstrap ClassLoader:根(原始/引导)类加载器,负责加载JAVA的核心类;不是java.lang.ClassLoader 的子类,而是由JVM自身实现的.
         Extension ClassLoader:扩展类加载器,负责加载jre的扩展目录(%JAVA_HOME%jre/lib/ext)中JAR包的类.
         System ClassLoader:系统(应用)类加载器.
    3.字节码:程序的一种低级表示,可以运行于JAVA虚拟机.
    4.类初始化的时机:
         1:使用关键字new,创建一个类的对象;
         2:使用一个类中的静态的方法或属性;
         3:使用反射强制加载一个类;
         4:初始化一个类(A)的子类(B)的时候;
         5:直接使用java.exe命令运行一个.class文件;
    5.反射概述:
         JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

    二.Class 类介绍及获取字节码对象的三种方式:
         Class:描述类的类;
             所有的数据类型都有对应的.class文件,由.class文件生成的对象,我们称为字节码文件对象!
             所有的字节码文件对象的数据类型都是 Class 类型;
         方式1:
             使用一个数据类型的静态属性,属性名是:class
         方式2:
             使用 Object 类的方法, getClass() 方法;通过实例化对象调用
         方式3:(重点)
             使用 Class 类的静态方法,forName(要加载的类的全路径);全路径就是带包的路径;由 Class 类直接调用.如果不使用eclipse,则类的全路径名为(类所在的文件夹名.类名??(绝对路径?))

    Class 类 (java.lang)
    继承关系:java.lang.Object--java.lang.Class<T>
    定义:public final class Class<T> extends Object implements Serializable, GenericDeclaration, Type, AnnotatedElement
    常用方法:
         public static Class<?> forName(String className) throws ClassNotFoundException{}:返回与带有给定字符串名的类或接口相关联的 Class 对象
         public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException{}:返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。parameterTypes:代表的是参数数据类型的字节码文件对象!
         public Constructor<?>[] getConstructors() throws SecurityException{}:获取到所有的public权限的构造方法,一般不使用该方法
         public Method getMethod(String name,Class<?>... parameterTypes) throws NoSuchMethodException,SecurityException{}: name所赋值(实参)的类型的字节码文件
         public Method[] getMethods() throws SecurityException{}:
         public T newInstance() throws InstantiationException, IllegalAccessException{}:创建此 Class 对象所表示的类的一个新实例。(普通对象)调用该方法的时候,必须保证字节码文件对象中有空参数的public权限的构造方法;否则执行报错
         public Field getDeclaredField(String name) throws NoSuchFieldException,SecurityException{}:
         public Field[] getDeclaredFields() throws SecurityException{}:                                                                                                                           

    代码演示:

      1 import java.lang.reflect.Constructor;
      2  import java.lang.reflect.Method;
      3  import java.lang.reflect.Field;
      4  class Person{
      5      private String name;
      6      private int age;
      7      //无参构造
      8     public Person(){
      9 
     10     }
     11      //全参构造
     12     public Person(String name, int age){
     13          this.name = name;
     14          this.age = age;
     15      }
     16      //getter/setter
     17      public String getName(){
     18          return name;
     19      }
     20      public int getAge(){
     21          return age;
     22      }
     23      public void setName(String name){
     24          this.name = name;
     25      }
     26      public void setAge(int age){
     27          this.age = age;
     28      }
     29      @Override
     30      public String toString(){
     31          return "["+"name="+name+",age="+age+"]";
     32      }
     33  }
     34 
     35 public class Ref{
     36      public static void main(String[] args) throws Exception{
     37          //方式一获取字节码文件:使用数据类型的静态属性
     38         Class c1 = Person.class;
     39          System.out.println(c1);//class Person
     40          //获取String类型的字节码文件
     41         String str = "helloworld";
     42          System.out.println(String.class);//class java.lang.String
     43          System.out.println(str.getClass());//class java.lang.String
     44          //方式二:Object类的方法,getClass方法
     45         Person p = new Person();
     46          System.out.println(p.getClass());//class Person
     47          //方式三:使用Class类的静态方法,forName(要加载的类的全路径)
     48          Class c2 = Class.forName("Person");
     49          System.out.println(c2);
     50 
     51         //获取构造方法一:Class类的getConstructor()方法
     52         Constructor co1 = c1.getConstructor();
     53          System.out.println(co1);//public Person()
     54          //获取构造方法二:Class类的getConstructors()方法
     55         Constructor[] co2 = c1.getConstructors();
     56          for(Constructor c : co2){
     57              System.out.println(c);//public Person()  public Person(java.lang.String,int)
     58          }
     59          //获取构造方法三:利用Constructor类的newInstance()方法初始化Object对象
     60         Class c4 = Class.forName("Person");
     61          Constructor co4 = c4.getConstructor(String.class, int.class);
     62          Object obj = co4.newInstance("Jack",18);
     63          System.out.println(obj);
     64 
     65         //利用newInstance()方法快速获取空对象
     66         Object obj1 = c4.newInstance();
     67          System.out.println(obj1);
     68 
     69         //获取普通方法--setName方法
     70         Class c5 = Class.forName("Person");//获取字节码文件
     71         Object obj5 = c5.newInstance();//获取字节码文件的普通对象
     72         Method m5 = c5.getMethod("setAge",int.class);//面向普通对象获取方法
     73         Object obj6 = m5.invoke(obj5,22);//面向方法,传入对象,及方法需要的实参
     74         System.out.println(obj5);
     75 
     76         //获取属性
     77         Class c6 = Class.forName("Person");//获取字节码文件对象
     78         Field f6 = c6.getDeclaredField("name");//获取name属性
     79 
     80         f6.setAccessible(true);
     81          Object obj7 = c6.newInstance();
     82          f6.set(obj7,"rongrong");
     83 
     84          Field age = c6.getDeclaredField("age");
     85          age.setAccessible(true);
     86          age.set(obj7,24);
     87 
     88         System.out.println(obj7);
     89     }
     90  }
     91 

      

    三.获取构造方法:只有从字节码文件中才能获取构造方法
         1.通过 Class 类的普通方法
             public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException{}:返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。
                                                             parameterTypes:代表的是参数数据类型的字节码文件对象!
             public Constructor<?>[] getConstructors() throws SecurityException{}:获取到所有的public权限的构造方法,一般不使用该方法
         2.通过 Construct 类的 newInstance(),类中必须包含无参构造
             public T newInstance(Object... initargs) throws InstantiationException,IllegalAccessException,IllegalArgumentException,
                          InvocationTargetException{}:

    四.获取普通方法:
         1.通过 Class 类的普通方法
             public Method getMethod(String name,Class<?>... parameterTypes) throws NoSuchMethodException,SecurityException{}: name所赋值(实参)的类型的字节码文件
             public Method[] getMethods() throws SecurityException{}:
             public T newInstance() throws InstantiationException, IllegalAccessException{}:创建此 Class 对象所表示的类的一个新实例。(普通对象)调用该方法的时候,必须保证字节码文件对象中有空参数的public权限的构造方法;否则执行报错
         2.通过 Method 类的方法
             public Object invoke(Object obj,Object... args) throws IllegalAccessException,IllegalArgumentException,InvocationTargetException{}:对带有指定参数的指定对象调用由此 Method 对象表示的底层方法.Obj:代表的是该方法对象所在的字节码文件对象创建出来的实例化对象;Args:方法执行的时候,需要的实际参数;Object:返回的是方法执行的结果,如果方法是void,那么默认该方法的返回值是null
    五.获取属性:
         1.概述:
             由于属性私有,可使用暴力反射(不推荐!!),因此,实际开发中都是通过反射属性的 getXxx 与 setXxx 方法,来替代反射属性;
             通过字节码文件对象,使用反射提取出来的属性或成员变量,称为 Field 类的对象;
             Field 类的对象可以用于保存数据值和获取数据值;
             成员变量随着对象的创建而产生,随着对象的死亡而死亡,因此在使用Field对象的时候,必须有普通对象;
             定义:public final class Field extends AccessibleObject implements Member
         2.通过 Class 类的普通方法
             public Field getDeclaredField(String name) throws NoSuchFieldException,SecurityException{}:
             public Field[] getDeclaredFields() throws SecurityException{}:
         3.AccessibleObject 类的方法:执行之前,忽略权限检查
             public void setAccessible(boolean flag) throws SecurityException{}:权限设为true,通过检查
         4.Field 类的方法
             public void set(Object obj,Object value) throws IllegalArgumentException,IllegalAccessException{}:
             public Object get(Object obj) throws IllegalArgumentException,IllegalAccessException{}:

    六.综合案例:
        

      1 //在eclipse中执行,并在工程中创建相应环境,包括configure.properties文件和Person类(String name, int age);
      2      package case01;
      3      import java.io.FileReader;
      4      import java.lang.reflect.Method;
      5      import java.util.Properties;
      6      import java.util.Set;
      7     /*
      8          使用反射的方式创建一个类的对象,并对对象的属性赋值,打印对象;给对象赋值的时候,属性值从配置文件中读取;
      9      */
     10      public class ReflectCase {
     11          public static void main(String[] args) throws Exception {
     12              // 创建Property集合,用于存储配置文件信息
     13             Properties p = new Properties();
     14              FileReader fr = new FileReader("configure.properties");
     15              p.load(fr);
     16              // 获取反射路径
     17             String path = p.getProperty("classPath");
     18              // 根据路径获取字节码对象
     19             Class c = Class.forName(path);
     20              // 面向字节码对象创建普通对象
     21             Object obj = c.newInstance();
     22              // 获取键集
     23             Set<String> keySet = p.stringPropertyNames();
     24              // 迭代,并拼接成set方法
     25             for (String s : keySet) {
     26                  // 判断如果不是name或age,就跳过
     27                 if (!("name".equals(s) || "age".equals(s))) {
     28                      continue;
     29                  }
     30                  // 拼接成set方法,我们需要反射set方法,需要一个方法名,即setName,setAge
     31                  StringBuilder builder = new StringBuilder();
     32                  builder.append("set").append(s.substring(0, 1).toUpperCase()).append(s.substring(1));
     33                  if (builder.toString().endsWith("ame")) {
     34                      // 反射方法
     35                     Method setNameMethod = c.getMethod(builder.toString(),String.class);
     36                      // 执行方法
     37                     Object obj1 = setNameMethod.invoke(obj, p.getProperty(s));
     38                  } else {
     39                      // 反射方法
     40                     Method setAgeMethod = c.getMethod(builder.toString(), int.class);
     41                      // 执行方法
     42                     Object obj2 = setAgeMethod.invoke(obj,Integer.parseInt(p.getProperty(s)));
     43                  }
     44              }
     45              System.out.println(obj);
     46          }
     47      }

    七.//需求,返回传入数据的类型
        

      1 public class Check{
      2          public static void main(String[] args){
      3              System.out.println(test(-15%4));
      4          }
      5 
      6          public static Object test(Object obj){
      7              return obj.getClass();
      8          }
      9 
     10      }

  • 相关阅读:
    有关WCSF的几点整理
    应用Response.Write实现带有进度条的多文件上传
    使用Response.Write实现在页面的生命周期中前后台的交互
    多线程实现Thread.Start()与ThreadPool.QueueUserWorkItem两种方式对比
    LinQ to SQL 及 non-LinQ方式实现Group的Performance对比
    Object.assign()方法
    继承
    面对对象和原型链
    画布实现验证码
    日期的格式 字符串转日期
  • 原文地址:https://www.cnblogs.com/huguangqin/p/7128830.html
Copyright © 2011-2022 走看看