Java的反射机制

Posted 小潘同学

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java的反射机制相关的知识,希望对你有一定的参考价值。

一、反射机制

1、反射的定义:

  在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制。通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以。

 

2、关于Class(类)

(1)  Class是一个类,一个描述类的类(也就是描述类本身),封装了描述方法(Method),描述字段的(Filed),描述构造器的(Constructor)等属性。

(2)  对象反射可以得到的信息:某个类的数据成员名、方法和构造器、某个类到底实现了哪些接口。

(3)  对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。 一个 Class 对象包含了特定某个类的有关信息。

(4)Class 对象只能由系统建立对象。

(5)  一个类在 JVM 中只会有一个Class实例。

栗子:创建一个类。

public class Person {  
    String name;  
    private int age;  
  
    public Person() {  
        System.out.println("无参构造器");  
    }  
  
    public Person(String name, int age) {  
        System.out.println("有参构造器");  
        this.name = name;  
        this.age = age;  
    }  
  
    public String getName() {  
        return name;  
    }  
  
    public void setName(String name) {  
        this.name = name;  
    }  
  
    public int getAge() {  
        return age;  
    }  
  
    public void setAge(int age) {  
        this.age = age;  
    }  
  
    @Override  
    public String toString() {  
        return "Person{" +  
                "name=\'" + name + \'\\\'\' +  
                ", age=" + age +  
                \'}\';  
    }  
}  

 

3、获取反射机制类的三种方法:

  想要使用反射机制,就必须要先获取到该类的字节码文件对象(.class),通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(方法,属性,类名,父类名,实现的所有接口等等),每一个类对应着一个字节码文件也就对应着一个Class类型的对象,也就是字节码文件对象。获取字节码文件对象[反射对象]的三种方式:


 

1、通过Class类中的静态方法forName,直接获取到一个类的字节码文件对象,此时该类还是源文件阶段,并没有变为字节码文件。

Class clazz1 = Class.forName("全限定类名");

2、当类被加载成.class文件时,此时Person类变成了.class,在获取该字节码文件对象,也就是获取自己, 该类处于字节码阶段。

Class clazz2  = Person.class;

3、通过类的实例获取该类的字节码文件对象,该类处于创建对象阶段

Class clazz3 = p.getClass();
@Test  
public void testGetClass() throws ClassNotFoundException {  
    Class clazz = null;  
  
     //1 通过全类名获取,用的比较多,但可能抛出ClassNotFoundException异常  
    clazz = Class.forName("com.java.reflection.Person");  
    System.out.println("通过全类名获取: " + clazz);  
    
    //1 直接通过类名.Class的方式得到  
    clazz = Person.class;  
    System.out.println("通过类名: " + clazz);  
  
    //2 通过对象的getClass()方法获取,这个使用的少(一般是传的是Object,不知道是什么类型的时候才用)  
    Object p = new Person();  
    clazz = p.getClass();  
    System.out.println("通过getClass(): " + clazz);    
   
}  

 

4、利用newInstance创建对象:调用的类必须有无参的构造器。

@Test  
public void testNewInstance()  
        throws ClassNotFoundException, IllegalAccessException, InstantiationException {  
  
    Class clazz = Class.forName("com.java.reflection.Person");  
  
    //使用Class类的newInstance()方法创建类的一个对象  
    //实际调用的类的那个 无参数的构造器(这就是为什么写的类的时候,要写一个无参数的构造器,就是给反射用的)  
    //一般的,一个类若声明了带参数的构造器,也要声明一个无参数的构造器  
    Object obj = clazz.newInstance();  
    System.out.println(obj);  
}  

 

5、ClassLoader类加载器

 

// ClassLoader类装载器 
 
@Test  
public void testClassLoader1() throws ClassNotFoundException, IOException {  
    
    //1、获取一个系统的类加载器  
    ClassLoader classLoader = ClassLoader.getSystemClassLoader();  
    System.out.println("系统的类加载器-->" + classLoader);  
  
    
    //2、获取系统类加载器的父类加载器(扩展类加载器(extensions classLoader))  
    classLoader = classLoader.getParent();  
    System.out.println("扩展类加载器-->" + classLoader);  
  
    
    //3、获取扩展类加载器的父类加载器  
    //输出为Null,无法被Java程序直接引用  
    classLoader = classLoader.getParent();  
    System.out.println("启动类加载器-->" + classLoader);  
  
  
    //4、测试当前类由哪个类加载器进行加载 ,结果就是系统的类加载器  
    classLoader = Class.forName("com.java.reflection.Person").getClassLoader();  
    System.out.println("当前类由哪个类加载器进行加载-->"+classLoader);  
  
    //5、测试JDK提供的Object类由哪个类加载器负责加载的  
    classLoader = Class.forName("java.lang.Object").getClassLoader();  
    System.out.println("JDK提供的Object类由哪个类加载器加载-->" + classLoader);  
}  

  5.1 getResourceAsStream方法

@Test  
    public void testGetResourceAsStream() throws ClassNotFoundException, IOException {  
  
        //5、关于类加载器的一个主要方法  
        //调用getResourceAsStream 获取类路径下的文件对应的输入流  
        InputStream in = this.getClass().getClassLoader()  
                .getResourceAsStream("com/java/reflection/test.properties");  
        System.out.println("in: " +in);  
  
        Properties properties = new Properties();  
        properties.load(in);  
  
        String driverClass = properties.getProperty("dirver");  
        String jdbcUrl = properties.getProperty("jdbcUrl"); 
    
        //中文可能会出现乱码,需要转换一下  
        String user = new String(properties.getProperty("user").getBytes("ISO-8859-1"), "UTF-8");  
        String password = properties.getProperty("password");  
  
        System.out.println("diverClass: "+driverClass);  
        System.out.println("user: " + user);  
    }  

 

6、invoke方法

1 public class PersonInvoke {  
2     public PersonInvoke() {  
3     }  
4   
5     private String method2() {  
6         return "Person private String method2";  
7     }  
8 } 
1 public class StudentInvoke extends PersonInvoke{  
2     private void method1(Integer age) {  
3         System.out.println("Student private void method1, age=:" +age);  
4     }  
5 } 
 1 /** 
 2  * 获取当前类的父类中定义的私有方法 
 3  * 直接调用getSuperclass() 
 4  */  
 5 @Test  
 6 public void testGetSuperClass() throws Exception {  
 7     
 8     String className = "com.java.reflection.StudentInvoke";  
 9   
10     Class clazz = Class.forName(className);  
11     Class superClazz = clazz.getSuperclass();  
12   
13     System.out.println(superClazz);  
14     //输出结果:class com.java.reflection.PersonInvoke  
15 }  

 

7、构造器(Constructor)

/** 
 * 构造器:开发用的比较少 
 */  
@Test  
public void testConstructor() throws ClassNotFoundException, NoSuchMethodException,  
        IllegalAccessException, InvocationTargetException, InstantiationException {  
    String className = "com.java.reflection.Person";  
    Class<Person> clazz = (Class<Person>) Class.forName(className);  
  
    //1.获取Constructor对象  
    Constructor<Person>[] constructors =  
            (Constructor<Person>[]) Class.forName(className).getConstructors();  
  
    // 遍历
    for (Constructor<Person> constructor: constructors) {  
        System.out.println(constructor);  
    }  
  
    Constructor<Person> constructor = clazz.getConstructor(String.class, Integer.class);  
    System.out.println("指定的-->" + constructor);  
  
    //2.调用构造器的newInstance()方法创建对象  
    Object obj= constructor.newInstance("changwen", 11);  
}  

 

8、注解(Annotation)

基本的 Annotation

• 使用 Annotation时要在其前面增加@符号,并把该Annotation 当成一个修饰符使用.用于修饰它支持的程序元素
• 三个基本的Annotation:
    –@Override:限定重写父类方法,该注释只能用于方法
    –@Deprecated:用于表示某个程序元素(类,方法等)已过时
    –@SuppressWarnings:抑制编译器警告.
 
@Retention(RetentionPolicy.RUNTIME) //运行时检验  
@Target(value = {ElementType.METHOD})  //作用在方法上  
public @interface AgeValidator {  
  
    int min();  
    int max();  
}  
 1 /** 
 2  * 通过反射才能获取注解 
 3  */  
 4 @Test  
 5 public void testAnnotation() throws Exception {  
 6     
 7     //这样的方式不能使用注解  
 8    /** Person3 person3 = new Person3();  
 9     person3.setAge(10);*/  
10   
11     String className = "com.java.reflection.Person3";  
12     Class clazz = Class.forName(className);  
13     Object obj = clazz.newInstance();  
14   
15     Method method = clazz.getDeclaredMethod("setAge",Integer.class);  
16     int val =40;  
17   
18     //获取注解  
19     Annotation annotation = method.getAnnotation(AgeValidator.class);  
20     if (annotation != null){  
21         if (annotation instanceof AgeValidator){  
22             AgeValidator ageValidator = (AgeValidator) annotation;  
23   
24             if (val< ageValidator.min() || val>ageValidator.max()){  
25                 throw new RuntimeException("数值超出范围");  
26             }  
27         }  
28     }  
29   
30     method.invoke(obj, val);  
31     System.out.println(obj);  
32 }  

 

原文转载于:https://www.cnblogs.com/bojuetech/p/5896551.html

以上是关于Java的反射机制的主要内容,如果未能解决你的问题,请参考以下文章

反射机制入门

反射机制入门

java 反射代码片段

深入理解java的反射机制

Java反射机制

Java核心技术梳理-类加载机制与反射