反射

一、什么是反射?

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

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

      
  Class 类的实例表示正在运行的 Java 应用程序中的类和接口。也就是jvm中有N多的实例每个类都有该Class对象。(包括基本数据类型)。Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。
 
 如图是类的正常加载过程:反射的原理在于class对象。

 

 

 

 二、反射的使用(这里使用Person类做演示)

先写一个Person类。
 

1.获取Class对象的三种方式:

(1)Object ——> getClass();
(2)何数据类型(包括基本数据类型)都有一个“静态”的class属性
(3)通过Class类的静态方法:forName(String  className)(常用)

其中(1)是因为Object类中的getClass方法、因为所有类都继承Object类。从而调用Object类来获取
 
public class Person {
    //成员变量
    public String name;
    public int age;
    private String address;

    //构造方法

    //无参构造
    public Person() {
        System.out.println("空参数构造方法");
    }
    //有参构造
    public Person(String name) {
        this.name = name;
        System.out.println("带有String的构造方法");
    }
    //私有的构造方法
    private Person(String name, int age){
        this.name = name;
        this.age = age;
        System.out.println("带有String,int的构造方法");
    }
    //多个参数的构造方法
    public Person(String name, int age, String address){
        this.name = name;
        this.age = age;
        this.address = address;
        System.out.println("带有String, int, String的构造方法");
    }

    //成员方法

    //没有返回值没有参数的方法
    public void method1(){
        System.out.println("没有返回值没有参数的方法");
    }
    //没有返回值,有参数的方法
    public void method2(String name){
        System.out.println("没有返回值,有参数的方法 name= "+ name);
    }
    //有返回值,没有参数
    public int method3(){
        System.out.println("有返回值,没有参数的方法");
        return 123;
    }
    //有返回值,有参数的方法
    public String method4(String name){
        System.out.println("有返回值,有参数的方法");
        return "哈哈" + name;
    }
    //私有方法
    private void method5(){
        System.out.println("私有方法");
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + ", address=" + address+ "]";
    }
}

  

 
注意:在运行期间,一个类,只有一个Class对象产生。
三种方式常用第三种,第一种对象都有了还要反射干什么。第二种需要导入类的包,依赖太强,不导包就抛编译错误。一般都第三种,一个字符串可以传入也可写在配置文件中等多种方法。
 

2.通过反射获取构造方法、成员变量、成员方法并使用

Person类:

package com.pp;
public class Person {
//成员变量
public String name;
public int age;
private String address;

//构造方法

//无参构造
public Person() {
System.out.println("空参数构造方法");
}
//有参构造
public Person(String name) {
this.name = name;
System.out.println("带有String的构造方法");
}
//私有的构造方法
private Person(String name, int age){
this.name = name;
this.age = age;
System.out.println("带有String,int的构造方法");
}
//多个参数的构造方法
public Person(String name, int age, String address){
this.name = name;
this.age = age;
this.address = address;
System.out.println("带有String, int, String的构造方法");
}

//成员方法

//没有返回值没有参数的方法
public void method1(){
System.out.println("没有返回值没有参数的方法");
}
//没有返回值,有参数的方法
public void method2(String name){
System.out.println("没有返回值,有参数的方法 name= "+ name);
}
//有返回值,没有参数
public int method3(){
System.out.println("有返回值,没有参数的方法");
return 123;
}
//有返回值,有参数的方法
public String method4(String name){
System.out.println("有返回值,有参数的方法");
return "哈哈" + name;
}
//私有方法
private void method5(){
System.out.println("私有方法");
}

@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", address=" + address+ "]";
}

}

测试类:

(1)获取构造方法:

1.获取构造方法:
  1).批量的方法:
public Constructor[] getConstructors():所有”公有的”构造方法
            public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
     
  2).获取单个的方法,并调用:
public Constructor getConstructor(Class… parameterTypes):获取单个的”公有的”构造方法:
public Constructor getDeclaredConstructor(Class… parameterTypes):获取”某个构造方法”可以是私有的,或受保护、默认、公有;

  调用构造方法:
Constructor–>newInstance(Object… initargs)
 
2、newInstance是 Constructor类的方法(管理构造函数的类)
api的解释为:
newInstance(Object… initargs)
           使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
它的返回值是T类型,所以newInstance是创建了一个构造方法的声明类的新实例对象。并为之调用。
public class Main {
public static void main(String[] args) throws Exception {
        Class c = Class.forName("com.pp.Person");
        //1.拿所有的构造方法
        Constructor[] cons = c.getDeclaredConstructors();
        for(Constructor con:cons){
            System.out.println(con);
        }
        //2.拿到单个的构造方法
        Constructor constructor = c.getDeclaredConstructor(String.class,int.class,String.class);
        System.out.println(constructor);
        //通过构造方法创建对象
        Object instance = constructor.newInstance("张三", 12, "陕西");
        System.out.println(instance);
        //针对私有方法,通过暴力反射来获取
        Constructor con = c.getDeclaredConstructor(String.class,int.class);
        con.setAccessible(true);//取消java语言检查
        Object instance= con.newInstance("李四", 12);
        System.out.println(instance);
}
}

  (2)获取成员变量:

1.批量的 :

   1).Field[] getFields():获取所有的"公有字段" *

   2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有; 

2.获取单个的: *

   1).public Field getField(String fieldName):获取某个"公有的"字段; 

   2).public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的) 

设置字段的值: *

    Field --> public void set(Object obj,Object value): 

   参数说明:

        1.obj:要设置的字段所在的对象; 

        2.value:要为字段设置的值;

public class Main {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("com.pp.Person");
        //通过反射拿成员变量
        //1.拿到所有的字节码
        Field[] fields = c.getFields();
        for(Field field:fields){
            System.out.println(field);
        }
        //2.获取单个字节码
        Field age = c.getField("age");
        System.out.println(age);
        //通过构造方法创建对象之后,拿到其对应的成员变量字节码
        Constructor constructor = c.getDeclaredConstructor(String.class);
        Object instance = constructor.newInstance("王五");
        //公有的字节码
        Field age = c.getField("age");
        //私有的字节码,通过暴力反射
        Field address = c.getDeclaredField("address");
        address.setAccessible(true);
        //通过方法成员变量获取值初始值
        age.get(instance);
        address.get(instance);
        //赋值
        age.set(instance,13);
        address.set(instance,"北京");
        System.out.println(age.get(instance));
        System.out.println(address.get(instance));
}
}

  

  (3)获取成员方法:

1.批量的: *

    public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类) 

    public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的) 

2.获取单个的: public Method getMethod(String name,Class<?>... parameterTypes):

    参数:

       name : 方法名;

       Class ... : 形参的Class类型对象 

       public Method getDeclaredMethod(String name,Class<?>... parameterTypes)

调用方法:  Method --> public Object invoke(Object obj,Object... args):

      参数说明: 

             obj : 要调用方法的对象; 

             args:调用方式时所传递的实参;

public class Main {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("com.pp.Person");
        //通过反射拿成员方法
        //拿公有的成员方法
        Constructor constructor = c.getDeclaredConstructor(String.class);
        Object instance = constructor.newInstance("娃哈哈");
        Method method = c.getMethod("method4", String.class);
        Object invoke = method.invoke(instance, "任小米");
        System.out.println(invoke);
        //拿私有的成员方法
        Constructor constructor = c.getDeclaredConstructor(String.class);
        Object instance = constructor.newInstance("娃哈哈");
        //暴力反射
        Method method5 = c.getDeclaredMethod("method5");
        method5.setAccessible(true);
        Object invoke = method5.invoke(instance, null);
        System.out.println(invoke);
    }
}

反射小结

 1. Class: 是一个类; 一个描述类的类.

  封装了描述方法的 Method,

       描述字段的 Filed,

              描述构造器的 Constructor 等属性.
 
 2. 如何得到 Class 对象:
    2.1 Person.class
    2.2 person.getClass()
    2.3 Class.forName("com.atguigu.javase.Person")
  
 3. 关于 Method:
    3.1 如何获取 Method:
      1). getDeclaredMethods: 得到 Method 的数组.
      2). getDeclaredMethod(String methondName, Class ... parameterTypes)
  
    3.2 如何调用 Method
      1). 如果方法时 private 修饰的, 需要先调用 Method 的 setAccessible(true), 使其变为可访问
      2). method.invoke(obj, Object ... args);
  
  4. 关于 Field:
    4.1 如何获取 Field: getField(String fieldName)
    4.2 如何获取 Field 的值: 
      1). setAccessible(true)
      2). field.get(Object obj)
    4.3 如何设置 Field 的值:
      field.set(Obejct obj, Object val)
  
  5. 了解 Constructor 和 Annotation 
  
  6. 反射和泛型.
    6.1 getGenericSuperClass: 获取带泛型参数的父类, 返回值为: BaseDao<Employee, String>
    6.2 Type 的子接口: ParameterizedType
    6.3 可以调用 ParameterizedType 的 Type[] getActualTypeArguments() 获取泛型参数的数组.

 

posted @ 2019-06-25 11:10  komorebi-rfj  阅读(119)  评论(0编辑  收藏  举报