Java反射机制详解

2023-06-12,,

Java反射机制详解

  Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。

1、关于Class

    1、Class是一个类,一个描述类的类(也就是描述类本身),封装了描述方法的Method,描述字段的Filed,描述构造器的Constructor等属性
    2、对象照镜子后(反射)可以得到的信息:某个类的数据成员名、方法和构造器、某个类到底实现了哪些接口。
    3、对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。
        一个 Class 对象包含了特定某个类的有关信息。
    4、Class 对象只能由系统建立对象
    5、一个类在 JVM 中只会有一个Class实例

 

  1. public class Person {  

  2.     String name;  

  3.     private int age;  

  4.     public Person() {  

  5.         System.out.println("无参构造器");  

  6.     }    

  7.     public Person(String name, int age) {  

  8.         System.out.println("有参构造器");  

  9.         this.name = name;  

  10.         this.age = age;  

  11.     }  

  12.     public String getName() {  

  13.         return name;  

  14.     }  

  15.     public void setName(String name) {  

  16.         this.name = name;  

  17.     }  

  18.     public int getAge() {  

  19.         return age;  

  20.     }  

  21.     public void setAge(int age) {  

  22.         this.age = age;  

  23.     }  

  24.     @Override  

  25.     public String toString() {  

  26.         return "Person{" +  

  27.                 "name='" + name + '\'' +  

  28.                 ", age=" + age +  

  29.                 '}';  

  30.     }  

  31. }  

2、反射机制获取类有三种方法
  1. /** 

  2.  * 反射机制获取类有三种方法 

  3.  */  

  4. @Test  

  5. public void testGetClass() throws ClassNotFoundException {  

  6.     Class clazz = null;  

  7.   

  8.     //1 直接通过类名.Class的方式得到  

  9.     clazz = Person.class;  

  10.     System.out.println("通过类名: " + clazz);  

  11.   

  12.     //2 通过对象的getClass()方法获取,这个使用的少(一般是传的是Object,不知道是什么类型的时候才用)  

  13.     Object obj = new Person();  

  14.     clazz = obj.getClass();  

  15.     System.out.println("通过getClass(): " + clazz);  

  16.   

  17.     //3 通过全类名获取,用的比较多,但可能抛出ClassNotFoundException异常  

  18.     clazz = Class.forName("com.java.reflection.Person");  

  19.     System.out.println("通过全类名获取: " + clazz);  

  20. }  

通过类名: class com.java.reflection.Person
无参构造器
通过getClass(): class com.java.reflection.Person
通过全类名获取: class com.java.reflection.Person
3、利用newInstance创建对象:调用的类必须有无参的构造器
  1. /** 

  2.  * Class类的newInstance()方法,创建类的一个对象。 

  3.  */  

  4. @Test  

  5. public void testNewInstance()  

  6.         throws ClassNotFoundException, IllegalAccessException, InstantiationException {  

  7.   

  8.     Class clazz = Class.forName("com.java.reflection.Person");  

  9.   

  10.     //使用Class类的newInstance()方法创建类的一个对象  

  11.     //实际调用的类的那个 无参数的构造器(这就是为什么写的类的时候,要写一个无参数的构造器,就是给反射用的)  

  12.     //一般的,一个类若声明了带参数的构造器,也要声明一个无参数的构造器  

  13.     Object obj = clazz.newInstance();  

  14.     System.out.println(obj);  

  15. }  

无参构造器
Person{name='null', age=0}
4、ClassLoader类加载器

  1. /** 

  2.  * ClassLoader类装载器 

  3.  */  

  4. @Test  

  5. public void testClassLoader1() throws ClassNotFoundException, IOException {  

  6.     //1、获取一个系统的类加载器  

  7.     ClassLoader classLoader = ClassLoader.getSystemClassLoader();  

  8.     System.out.println("系统的类加载器-->" + classLoader);  

  9.   

  10.     //2、获取系统类加载器的父类加载器(扩展类加载器(extensions classLoader))  

  11.     classLoader = classLoader.getParent();  

  12.     System.out.println("扩展类加载器-->" + classLoader);  

  13.   

  14.     //3、获取扩展类加载器的父类加载器  

  15.     //输出为Null,无法被Java程序直接引用  

  16.     classLoader = classLoader.getParent();  

  17.     System.out.println("启动类加载器-->" + classLoader);  

  18.   

  19.     //  

  20.   

  21.     //4、测试当前类由哪个类加载器进行加载 ,结果就是系统的类加载器  

  22.     classLoader = Class.forName("com.java.reflection.Person").getClassLoader();  

  23.     System.out.println("当前类由哪个类加载器进行加载-->"+classLoader);  

  24.   

  25.     //5、测试JDK提供的Object类由哪个类加载器负责加载的  

  26.     classLoader = Class.forName("java.lang.Object").getClassLoader();  

  27.     System.out.println("JDK提供的Object类由哪个类加载器加载-->" + classLoader);  

  28. }  

系统的类加载器-->sun.misc.Launcher$AppClassLoader@43be2d65
扩展类加载器-->sun.misc.Launcher$ExtClassLoader@7a9664a1
启动类加载器-->null
当前类由哪个类加载器进行加载-->sun.misc.Launcher$AppClassLoader@43be2d65
JDK提供的Object类由哪个类加载器加载-->null

4.1、getResourceAsStream方法

  1.   @Test  

  2.     public void testGetResourceAsStream() throws ClassNotFoundException, IOException {  

  3. //          这么写的话,文件需要放到src目录下  

  4.         //       InputStream in = new FileInputStream("test.properties");  

  5.   

  6.         //5、关于类加载器的一个主要方法  

  7.         //调用getResourceAsStream 获取类路径下的文件对应的输入流  

  8.         InputStream in = this.getClass().getClassLoader()  

  9.                 .getResourceAsStream("com/java/reflection/test.properties");  

  10.         System.out.println("in: " +in);  

  11.   

  12.         Properties properties = new Properties();  

  13.         properties.load(in);  

  14.   

  15.         String driverClass = properties.getProperty("dirver");  

  16.         String jdbcUrl = properties.getProperty("jdbcUrl");  

  17.         //中文可能会出现乱码,需要转换一下  

  18.         String user = new String(properties.getProperty("user").getBytes("ISO-8859-1"), "UTF-8");  

  19.         String password = properties.getProperty("password");  

  20.   

  21.         System.out.println("diverClass: "+driverClass);  

  22.         System.out.println("user: " + user);  

  23.     }  

  24. test.properties内容如下:

  25. dirver=com.mysql.jdbc.Driver;
    jdbcUrl=jdbc:mysql://192.168.42.108:3306/test
    user=123
    password=123

       结果:

in: java.io.BufferedInputStream@2aca0115
diverClass: com.mysql.jdbc.Driver;
user: 123
5、Method: 对应类中的方法
  1. public class Person {  

  2.     private String name;  

  3.     private int age;  

  4.   

  5.     //新增一个私有方法  

  6.     private void privateMthod(){  

  7.     }  

  8.       

  9.     public Person() {  

  10.         System.out.println("无参构造器");  

  11.     }  

  12.   

  13.     public Person(String name, int age) {  

  14.         System.out.println("有参构造器");  

  15.         this.name = name;  

  16.         this.age = age;  

  17.     }  

  18.   

  19.     public String getName() {  

  20.         return name;  

  21.     }  

  22.   

  23.     public void setName(String name) {  

  24.         this.name = name;  

  25.     }  

  26.   

  27.     /** 

  28.      *  

  29.      * @param age  类型用Integer,不用int 

  30.      */  

  31.     public void setName(String name , int age){  

  32.         System.out.println("name: " + name);  

  33.         System.out.println("age:"+ age);  

  34.   

  35.     }  

  36.   

  37.     public int getAge() {  

  38.         return age;  

  39.     }  

  40.   

  41.     public void setAge(int age) {  

  42.         this.age = age;  

  43.     }  

  44.   

  45.     @Override  

  46.     public String toString() {  

  47.         return "Person{" +  

  48.                 "name='" + name + '\'' +  

  49.                 ", age=" + age +  

  50.                 '}';  

  51.     }  

  52. }  

  53. @Test  

  54. public void testMethod() throws ClassNotFoundException, NoSuchMethodException,   

  55.         IllegalAccessException, InstantiationException, InvocationTargetException {  

  56.     Class clazz = Class.forName("com.java.reflection.Person");  

  57.   

  58.     //1、得到clazz 对应的类中有哪些方法,不能获取private方法  

  59.     Method[] methods =clazz.getMethods();  

  60.     System.out.print("        getMethods: ");  

  61.     for (Method method : methods){  

  62.         System.out.print(method.getName() + ", ");  

  63.     }  

  64.   

  65.     //2、获取所有的方法(且只获取当着类声明的方法,包括private方法)  

  66.     Method[] methods2 = clazz.getDeclaredMethods();  

  67.     System.out.print("\ngetDeclaredMethods: ");  

  68.     for (Method method : methods2){  

  69.         System.out.print(method.getName() + ", ");  

  70.     }  

  71.   

  72.     //3、获取指定的方法  

  73.     Method method = clazz.getDeclaredMethod("setName",String.class);//第一个参数是方法名,后面的是方法里的参数  

  74.     System.out.println("\nmethod : " + method);  

  75.   

  76.     Method method2 = clazz.getDeclaredMethod("setName",String.class ,int.class);//第一个参数是方法名,后面的是方法里的参数  

  77.     System.out.println("method2: " + method2);  

  78.   

  79.     //4、执行方法!  

  80.     Object obj = clazz.newInstance();  

  81.     method2.invoke(obj, "changwen", 22);  

  82. }  

    getMethods: toString, getName, setName, setName, setAge, getAge, wait, wait, wait, equals, hashCode, getClass, notify, notifyAll,
getDeclaredMethods: toString, getName, setName, setName, setAge, getAge, privateMthod,
method : public void com.java.reflection.Person.setName(java.lang.String)
method2: public void com.java.reflection.Person.setName(java.lang.String,int)
无参构造器
name: changwen
age:22

6、invoke方法
  1. public class PersonInvoke {  

  2.     public PersonInvoke() {  

  3.     }  

  4.   

  5.     private String method2() {  

  6.         return "Person private String method2";  

  7.     }  

  8. }  

  9. public class StudentInvoke extends PersonInvoke{  

  10.     private void method1(Integer age) {  

  11.         System.out.println("Student private void method1, age=:" +age);  

  12.     }  

  13. }  

获取当前类的父类定义的私有方法

  1. /** 

  2.  * 获取当前类的父类中定义的私有方法 

  3.  * 直接调用getSuperclass() 

  4.  */  

  5. @Test  

  6. public void testGetSuperClass() throws Exception {  

  7.     String className = "com.java.reflection.StudentInvoke";  

  8.   

  9.     Class clazz = Class.forName(className);  

  10.     Class superClazz = clazz.getSuperclass();  

  11.   

  12.     System.out.println(superClazz);  

  13.     //输出结果:class com.java.reflection.PersonInvoke  

  14. }  

另一种写法

  1. /** 

  2.   * @param className  某个类的全类名 

  3.   * @param methodName 类的一个方法的方法名,该方法也可能是私有方法 

  4.   * @param args  调用该方法需要传入的参数 ...可变参数的意思 

  5.   * @return 调用方法后的返回值 

  6.   */  

  7.  public Object invoke(String className, String methodName, Object ... args) {  

  8.      Object obj = null;  

  9.      try {  

  10.          obj = Class.forName(className).newInstance();  

  11.          return invoke(obj, methodName, args);  

  12.   

  13.      } catch (InstantiationException e) {  

  14.          e.printStackTrace();  

  15.      } catch (IllegalAccessException e) {  

  16.          e.printStackTrace();  

  17.      } catch (ClassNotFoundException e) {  

  18.          e.printStackTrace();  

  19.      }  

  20.      return invoke(null, methodName, args);  

  21.  }  

  22.  /** 

  23.   * @param obj  方法执行的那个对象 

  24.   * @param methodName 类的一个方法的方法名,该方法也可能是私有方法,还可能是该方法在父类中定义的私有方法 

  25.   * @param args  调用该方法需要传入的参数 ...可变参数的意思 

  26.   * @return 调用方法后的返回值 

  27.   */  

  28.  public Object invoke(Object obj, String methodName, Object ... args) {  

  29.      //1、获取Method对象  

  30.      Class [] parameterTypes = new Class[args.length];  

  31.      for (int i=0 ; i<args.length; i++){  

  32.          parameterTypes[i] = args[i].getClass();  

  33.      }  

  34.   

  35.      try {  

  36.          //2、执行Method方法  

  37.          Method method = getMethod(obj.getClass(), methodName,parameterTypes);  

  38.   

  39.          //通过反射执行private方法  

  40.          method.setAccessible(true);  

  41.   

  42.          //3、返回方法的返回值  

  43.          return method.invoke(obj,args);  

  44.   

  45.      } catch (Exception e) {  

  46.   

  47.      }  

  48.   

  49.      return null;  

  50.  }  

  51.   

  52.  /** 

  53.   * 获取clazz 的methodName 方法, 该方法可能是私有方法,还可能是父类中的私有方法 

  54.   */  

  55.  public Method getMethod(Class clazz, String methodName, Class ... parameterTypes) {  

  56.      //注意这个循环里的内容!!!  

  57.      for (; clazz != Object.class; clazz = clazz.getSuperclass()){  

  58.          try {  

  59.              return clazz.getDeclaredMethod(methodName, parameterTypes);  

  60.          } catch (Exception e) { //这里要写Exception,不然会出错,应该是有部分异常没有捕获  

  61.   

  62.          }  

  63.      }  

  64.      return null;  

  65.  }  

测试:

  1. @Test  

  2. public void testInvoke2() {  

  3.     Object obj = new StudentInvoke();  

  4.     invoke(obj, "method1", 10);  

  5.   

  6.     Object result = invoke(obj, "method2");  

  7.     System.out.println(result);  

  8. }  

private void method1,age:10
Person private String method2
7、Field字段
  1. public class Person {  

  2.     public String name;  

  3.     private Integer age;  

  4.   

  5.     public Person() {  

  6.     }  

  7.   

  8.     public Person(String name, Integer age) {  

  9.         this.name = name;  

  10.         this.age = age;  

  11.     }  

  12. /** 

  13.  * Field: 封装了字段的信息 

  14.  */  

  15. @Test  

  16. public void testField() throws  

  17.         ClassNotFoundException, NoSuchFieldException, IllegalAccessException {  

  18.   

  19.     Class clazz = Class.forName("com.java.reflection.Person");  

  20.   

  21.     //1、获取字段  

  22.     //1.1 获取Field的数组,私有字段也能获取  

  23.     Field[] fields = clazz.getDeclaredFields();  

  24.     for (Field field: fields) {  

  25.         System.out.print(field.getName() + ", ");  

  26.     }  

  27.   

  28.     //1.2 获取指定名字的Field(如果是私有的,见下面的4)  

  29.     Field field = clazz.getDeclaredField("name");  

  30.     System.out.println("\n获取指定Field名=: " + field.getName());  

  31.   

  32.     Person person = new Person("ABC", 12);  

  33.     //2、获取指定对象的Field的值  

  34.     Object val = field.get(person);  

  35.     System.out.println("获取指定对象字段'name'的Field的值=: " + val);  

  36.   

  37.     //3、设置指定对象的Field的值  

  38.     field.set(person, "changwen2");  

  39.     System.out.println("设置指定对象字段'name'的Field的值=: " + person.name);  

  40.   

  41.     //4、若该字段是私有的,需要调用setAccessible(true)方法  

  42.     Field field2 = clazz.getDeclaredField("age");  

  43.     field2.setAccessible(true);  

  44.     System.out.println("获取指定私有字段名=: " + field2.getName());  

  45. }  

name, age, 
获取指定Field名=: name
获取指定对象字段'name'的Field的值=: ABC
设置指定对象字段'name'的Field的值=: changwen2
获取指定私有字段名=: age

  1. /** 

  2.  * 一个实例: 

  3.  * 反射获取一个继承Person2的Student类 

  4.  * 设置字段"age"=20(该字段可能为私有,可能在其父类中) 

  5.  */  

  6. @Test  

  7. public void testClassField() throws ClassNotFoundException, IllegalAccessException, InstantiationException {  

  8.     String className = "com.java.reflection.Student";  

  9.     String fieldName = "age";  //可能为私有,可能在其父类中  

  10.     Object val = 20;  

  11.   

  12.     //创建className 对应类的对象,并为其fieldName赋值为val  

  13.     Class clazz = Class.forName(className);  

  14.     Field field = null;  

  15.     for (Class clazz2 = clazz; clazz2 != Object.class; clazz2 = clazz2.getSuperclass()){  

  16.         try {  

  17.             field = clazz2.getDeclaredField(fieldName);  

  18.         } catch (Exception e) {  

  19.   

  20.         }  

  21.     }  

  22.   

  23.     Object obj = clazz.newInstance();  

  24.     assert field != null;  

  25.     field.setAccessible(true);  

  26.     field.set(obj, val);  

  27.   

  28.     Student stu = (Student) obj;  

  29.     System.out.println("age = " + stu.getAge());  

  30. }  

8、构造器(Constructor)
  1. /** 

  2.  * 构造器:开发用的比较少 

  3.  */  

  4. @Test  

  5. public void testConstructor() throws ClassNotFoundException, NoSuchMethodException,  

  6.         IllegalAccessException, InvocationTargetException, InstantiationException {  

  7.     String className = "com.java.reflection.Person";  

  8.     Class<Person> clazz = (Class<Person>) Class.forName(className);  

  9.   

  10.     //1.获取Constructor对象  

  11.     Constructor<Person>[] constructors =  

  12.             (Constructor<Person>[]) Class.forName(className).getConstructors();  

  13.   

  14.     for (Constructor<Person> constructor: constructors) {  

  15.         System.out.println(constructor);  

  16.     }  

  17.   

  18.     Constructor<Person> constructor = clazz.getConstructor(String.class, Integer.class);  

  19.     System.out.println("指定的-->" + constructor);  

  20.   

  21.     //2.调用构造器的newInstance()方法创建对象  

  22.     Object obj= constructor.newInstance("changwen", 11);  

  23. }  

public com.java.reflection.Person()
public com.java.reflection.Person(java.lang.String,java.lang.Integer)
指定的-->public com.java.reflection.Person(java.lang.String,java.lang.Integer)
9、注解(Annotation)

  

  从 JDK5.0 开始,Java 增加了对元数据(MetaData)的支持,也就是Annotation(注释)

    Annotation其实就是代码里的特殊标记,这些标记可以在编译,类加载, 运行时被读取,并执行相应的处理.通过使用Annotation,程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息.

    Annotation 可以像修饰符一样被使用,可用于修饰包,类,构造器, 方法,成员变量, 参数,局部变量的声明,这些信息被保存在Annotation的 “name=value”对中.

    Annotation能被用来为程序元素(类,方法,成员变量等)设置元数据

基本的 Annotation

使用 Annotation时要在其前面增加@符号,并把该Annotation 当成一个修饰符使用.用于修饰它支持的程序元素

三个基本的Annotation:

    –@Override:限定重写父类方法,该注释只能用于方法

    –@Deprecated:用于表示某个程序元素(类,方法等)已过时

    –@SuppressWarnings:抑制编译器警告.

自定义 Annotation

    定义新的 Annotation类型使用@interface关键字

    Annotation 的成员变量在Annotation 定义中以无参数方法的形式来声明.其方法名和返回值定义了该成员的名字和类型.

    可以在定义Annotation的成员变量时为其指定初始值,指定成员变量的初始值可使用default关键字

    没有成员定义的Annotation称为标记;包含成员变量的Annotation称为元数据Annotation

《Java反射机制详解.doc》

下载本文的Word格式文档,以方便收藏与打印。