有关Java反射的使用看这一篇就够了

1. 简介

本篇文章不探讨反射的实现机制或者说实现原理,仅仅从使用的角度去讲解我们常用的一些API接口,方便自己以后需要使用时信手拈来,同时也方便广大博友能够快速了解API的使用。

什么是反射?

反射是java语言的一个特性,它允许一个java的类获取他所有的成员变量和方法并且显示出来,这样说起来有些抽象,例如我们可以通过反射去实例化一个对象,并不非得使用new这个关键字来实例化,同时我们也可以通过反射知道Java类中有哪些变量、哪些方法等等,这些特性在C或者C++语言中是不存在的。

反射中,我们会常用到三个类,分别为Class、Method和Field,通过类名相信大家已经能够知道大概意思,分别代表类、方法和变量,本篇文章将会用大量的Demo来讲解这三个类中的部分常用重要API的作用,Demo中用到的一个实体类如下:

public class Person {

    private String name;

    public Person() {
        System.out.println("Person()...");
    }

    public Person(String name) {
        this.name = name;
        System.out.println("Person(String name)...");
    }

    private void privateMethod(){
        System.out.println("privateMethod-->name="+this.name);
    }

    protected void protectedMethod(){
        System.out.println("protectedMethod-->name="+this.name);
    }

    void defaultMethod(){
        System.out.println("defaultMethod-->name="+this.name);
    }

    public void setName(String name) {
        this.name = name;
        System.out.println("setName()...");
    }

    public String getName() {
        System.out.println("getName()...");
        return name;
    }

    @Override
    public String toString() {
        return "name: "+this.name;
    }
}

2. Class 类

2.1 获取Class实例

// 方法一  forName函数
Class c= Class.forName("Person");

// 方法二  getClass()函数
Person person = new Person();
Class c = person.getClass();

// 方法三  使用类字面常量
Class c=Person.class;

2.2 通过反射来实例化对象

// 方法一
try {
    Class c = Person.class;
    Person person = (Person) c.newInstance();
    System.out.println(person instanceof Person);  // true
} catch (InstantiationException e) {
    e.printStackTrace();
} catch (IllegalAccessException e) {
    e.printStackTrace();
}

// 方法二
try {
    Class c = Class.forName("reflect.demo.Person");
    Person person = (Person) c.newInstance();
    System.out.println(person instanceof Person);  // true
} catch (ClassNotFoundException e) {
    e.printStackTrace();
} catch (IllegalAccessException e) {
    e.printStackTrace();
} catch (InstantiationException e) {
    e.printStackTrace();
}

以上方式都是调用的Person类无参构造,如果需要调用Person(String name),改如何呢?

// 首先获取到Person类中的有参构造,通过构造函数实例化对象
try {
    Class c = Person.class;
    Constructor constructor = c.getConstructor(new Class[]{String.class});
    // 调用的是有参构造
    Person person = (Person) constructor.newInstance("jack");
    System.out.println(person.toString());      // name: jack
} catch (NoSuchMethodException e) {
    e.printStackTrace();
} catch (IllegalAccessException e) {
    e.printStackTrace();
} catch (InstantiationException e) {
    e.printStackTrace();
} catch (InvocationTargetException e) {
    e.printStackTrace();
}

2.3 获取类中所有的方法对象

try {
    Class c = Person.class;
    // 方式一
    Method [] methods = c.getMethods();     // 获取所有公共方法,且包括父类的共有方法
    for (Method method:methods) {
        System.out.print(method.getName()+",  ");
    }
    System.out.println("");
    
    // 方式二
    methods = c.getDeclaredMethods();     // 获取所有方法,包括私有、共有等,但是只定义在该类中
    for (Method method:methods) {
        System.out.print(method.getName()+",  ");
    }
} catch (Exception e) {
    e.printStackTrace();
}

输出:
toString,  getName,  setName,  wait,  wait,  wait,  equals,  hashCode,  getClass,  notify,  notifyAll, 
toString,  getName,  setName,  protectedMethod,  privateMethod,  defaultMethod,

// 其他
Method getMethod(String name, Class<?>... parameterTypes)  // 获取某个方法对象,只能是共有的
Method getDeclaredMethod(String name, Class<?>... parameterTypes) // 获取获取某个方法对象,只能是当前类的,可以是private、public等

2.4 获取类中字段对象

try {
    Class c = Person.class;
    Field[] fields = c.getFields();  //  同 getMethods
    for (Field field : fields) {
        System.out.print(field.getName() + ",  ");
    }
    System.out.println("5***************");
    fields = c.getDeclaredFields();     // 同 getDeclaredMethods
    for (Field field : fields) {
        System.out.print(field.getName() + ",  ");
    }
} catch (Exception e) {
    e.printStackTrace();
}

2.5 其他方法

  • getName : 返回类名,包括包名
  • getSimpleName: 返回类名,不包含包名
  • cast(Object obj):将obj类型对象转化为当前class类型对象
Class cc = Person.class;
// getName: reflect.demo.Person    getSimpleName: Person
System.out.println("getName: "+cc.getName()+"    getSimpleName: "+cc.getSimpleName());

3. Method类

  • getName() 获取方法名
  • invoke(Object obj, Object... args) 执行obj中该方法
  • Parameter[] getParameters() 获取方法中的参数
  • setAccessible(boolean flag) 设置访问权限
Person per = new Person();
Class c = Person.class;
Method method;

try {
    method = c.getDeclaredMethod("setName", String.class);
    System.out.println(method.getName());       // 输出: setName
    method.invoke(per, "hello reflect");        // 调用了 setName方法
    System.out.println(per);        // 输出 name: hello reflect

    Parameter[] parameters = method.getParameters();
    for (Parameter parameter:parameters) {
        System.out.print(parameter.getName()+",  "); // 输出:arg0
    }
} catch (Exception e) {
    e.printStackTrace();
}

// 如何反射调用私有方法
try {
    method = c.getDeclaredMethod("privateMethod");
    method.setAccessible(true);  // 私有方法必须设置访问权限为true
    method.invoke(per);    // 调用privateMethod方法
} catch (Exception e) {
    e.printStackTrace();
}

4. Field API

  • String getName() 获取变量名
  • setAccessible(boolean flag) 设置访问权限
  • Object get(Object obj) 获取obj对象中改变量值
  • set(Object obj, Object value) 将obj对象中的改变量值设置为value
Class c = Person.class;
Person person = new Person("lvjie");
try {
//  Field field = c.getField("name");             // 只能获取到共有变量,包括父类
    Field field = c.getDeclaredField("name");   // 可以获取当前类中定义的任何变量,包括private、public等
    System.out.println(field.getName());        // name
    field.setAccessible(true);      // 私有变量必须设置访问权限为true
    System.out.println(field.get(person));  // 获取该对象中的变量值,私有变量需要添加访问权限
    System.out.println(person);     // name: lvjie
    field.set(person, "jack");      //  对该对象的变量设置值, 私有变量需要添加访问权限
    System.out.println(person);     // name: jack
} catch (Exception e) {
    e.printStackTrace();
}

以上相关API在使用反射过程中是经常遇见的,当然还有其他相关API,例如获取该类、方法和变量上使用到的注解等等,这些在注解的使用上会做讲解。反射配合注解,会产生巨大的功能,目前许多开源库也是基于这两点技术来实现,例如Android中的 EventBus、ButterKnife、DBFlow等等。

如果对Java注解感兴趣,请看下一篇Java注解全面总结

猜你喜欢

转载自blog.csdn.net/u010349644/article/details/82498452