反射


一.反射的基本概念
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      }

原文地址:https://www.cnblogs.com/huguangqin/p/7128830.html