Java反射解析

样例类

Person类:

public class Person {
    public String name;
    protected int age;
    protected int weight;
    public Person() { }
    public Person(String name) {
        this.name = name;
    }
    public Person(String name, int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    private void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public int getWeight() {
        return weight;
    }
    public void setWeight(int weight) {
        this.weight = weight;
    }
}

Jack类:

public class Jack extends Person {
    public String phone = "13122333333";
    protected String location = "Shanghai";
    private int salary = 10000;
    public Jack() { }
    public Jack(String name) {
        super(name);
    }
    public Jack(String name, int age) {
        super(name, age);
    }
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }
    public String getLocation() {
        return location;
    }
    public void setLocation(String location) {
        this.location = location;
    }
    public int getSalary() {
        return salary;
    }
    public void setSalary(int salary) {
        this.salary = salary;
    }
}

获取Class对象有三种方法

Jack jack = new Jack("jack", 20);
//1 当有对应类对象的时候可以直接调用类对象的getClass方法
Class jackClass1 = jack.getClass();
//2 当知道类的路径,可以使用Class的静态方法forName
//需要注意的是,这种方法存在找不到对用类的可能性,所以Class.forName()会抛出受检异常,业务需要去捕获
Class jackClass2 = Class.forName("Jack");
//3 当知道类名,可以直接调用类名.class
Class jackClass3 = Jack.class;
//一个类只维护一个Class对象,因此不同方式获得的同一个类的Class对象是同意给
System.out.println(jackClass1 == jackClass2);  //true
System.out.println(jackClass2 == jackClass3);  //true

域(Field)、方法名(Method)和构造器(Constructor)

通过Class对象c1、c2和c3,可以获取对应类对象的属性,比如域(Field),方法名(Method)和构造器(Constructor)
获取的方法主要有以下六种:

Field[] fields1 = c1.getFields(); //1
Field[] declaredFields1 = c1.getDeclaredFields(); //2
Method[] methods1 = c1.getMethods(); //3
Method[] declaredMethods = c1.getDeclaredMethods(); //4
Constructor[] constructors = c1.getConstructors(); //5
Constructor[] declaredConstructor = c1.getDeclaredConstructors(); //6

这些方法的返回值都是数组,上面六个方法中1和2,3和4,5和6之间都差了一个Declared的修饰,区别在于普通方法获取的值包括自身的public域/方法名/构造器和父类的public域/方法名/构造器,而有Declared修饰的方法只能获取自身声明的全部域/方法名/构造器
在这里插入图片描述
Class类属于java.lang包,而Field、Method和Constructor都属于反射包java.lang.reflect,四者都属于java基础核心包
通过反射来生成对象,可以使用Class.newInstance和Constructor.newInstance(),前者从安卓9开始就deprecated了,推荐使用Constructor.newInstance()。

Class.newInstance()和Constructor.newInstance()区别:

Class.newInstance()只能反射无参的构造器;
Constructor.newInstance()可以反任何构造器;
Class.newInstance()需要构造器可见(visible);
Constructor.newInstance()可以反私有构造器;
Class.newInstance()对于捕获或者未捕获的异常均由构造器抛出;
Constructor.newInstance()通常会把抛出的异常封装成InvocationTargetException抛出;

//通过Constructor的newInstance来新建对象可以选择所需要的构造器(Class.newInstance只能反射无参的构造器)
Constructor<?> constructor = jackClass1.getConstructor(String.class, int.class);
Object person = constructor.newInstance("jack", 12);
if (person instanceof Person) {
    System.out.println(((Person) person).getAge());  //12
}

域(Field),方法名(Method)和构造器(Constructor)三个类具有以下一些方法

//放回该域/方法/构造器所在类的Class对象,类似于直接打印Jack.class (Field/Method/Constructor)
Class getDeclaringClass()

//返回方法/构造器抛出的异常Class数组对象 (Method/Constructor)
Class[] getExceptionTypes()

//返回域/方法/构造器的修饰符的整数类型,可以使用Modifier类中的方法来解析,具体下面有分析 (Field/Method/Constructor)
int getModifiers()

//返回域/方法/构造器的名字 (Field/Method/Constructor)
String getName()

//返回方法/构造器的参数类型Class数组对象 (Method/Constructor)
Class[] getParameterTypes()

//返回方法的返回值Class对象 (Method)
Class getReturnType()

以下使用修饰符来举例

//从对应的Field反推出所属的Class对象
Field field = jackClass1.getDeclaredField("location");
Class declaringClass = field.getDeclaringClass();
System.out.println(declaringClass);  //class Jack
//获取对应Field的修饰符
System.out.println(Modifier.toString(field.getModifiers()));  //protected

需要注意的是,这里的修饰符Modifier不仅仅可以用来判断public/protected/private,它还可以判断是否abstract/final/interface/native/static/strict/synchronized/volatile
对应的方法如下:

static String toString(int modifiers)
static boolean isAbstract(int modifiers)
static boolean isFinal(int modifiers)
static boolean isInterface(int modifiers)
static boolean isNative(int modifiers)
static boolean isPrivate(int modifiers)
static boolean isProtected(int modifiers)
static boolean isPublic(int modifiers)
static boolean isStatic(int modifiers)
static boolean isStrict(int modifiers)
static boolean isSynchonized(int modifiers)
static boolean isVolatile(int modifiers)

域/方法名/和构造器访问权限

利用发射机制可以获取或者修改对应域的数值

//调用域/方法名/和构造器的get方法需要传入对应的对象,因为成员变量是对象专属的,单凭Field是无法定位的
Object get(Object obj)
//set方法也类似,需要指定需要修改的对象的具体域,因此需要传入对象和新值
void set(Object obj, Object newValue)

这也是很多开发人员调试中经常用到的,但是如果域是由private修饰,则直接调用get或者set方法会报错,需要先调用域/方法名/和构造器的setAccessible方法

void setAccessible(boolean flag)
boolean isAccessible()

//访问private的域,需要调用setAccessible为true
Field fieldAccess = jackClass1.getDeclaredField("salary");
fieldAccess.setAccessible(true);  //删除这行会报错
int location = (int) fieldAccess.get(jack);  //10000
System.out.println(location);

例子汇总

try {
    Jack jack = new Jack("jack", 20);
    //1 当有对应类对象的时候可以直接调用类对象的getClass方法
    Class jackClass1 = jack.getClass();
    //2 当知道类的路径,可以使用Class的静态方法forName
    //需要注意的是,这种方法存在找不到对用类的可能性,所以Class.forName()会抛出受检异常,业务需要去捕获
    Class jackClass2 = Class.forName("Jack");
    //3 当知道类名,可以直接调用类名.class
    Class jackClass3 = Jack.class;
    //一个类只维护一个Class对象,因此不同方式获得的同一个类的Class对象是同意给
    System.out.println(jackClass1 == jackClass2);  //true
    System.out.println(jackClass2 == jackClass3);  //true
    
    //从Class对象可以获取对应的域(Field),方法名(Method)和构造器(Constructor)数组
    Field[] fields1 = jackClass1.getFields();
    Field[] declaredFields1 = jackClass1.getFields();
    Method[] methods1 = jackClass1.getMethods();
    Method[] declaredMethods = jackClass1.getDeclaredMethods();
    Constructor[] constructors = jackClass1.getConstructors();
    Constructor[] declaredConstructor = jackClass1.getDeclaredConstructors();

    //通过Constructor的newInstance来新建对象可以选择所需要的构造器(Class.newInstance只能反射无参的构造器)
    Constructor<?> constructor = jackClass1.getConstructor(String.class, int.class);
    Object person = constructor.newInstance("jack", 12);
    if (person instanceof Person) {
        System.out.println(((Person) person).getAge());  //12
    }

    //从对应的Field反推出所属的Class对象
    Field field = jackClass1.getDeclaredField("location");
    Class declaringClass = field.getDeclaringClass();
    System.out.println(declaringClass);  //class Jack
    //获取对应Field的修饰符
    System.out.println(Modifier.toString(field.getModifiers()));  //protected

    //访问private的域,需要调用setAccessible为true
    Field fieldAccess = jackClass1.getDeclaredField("salary");
    fieldAccess.setAccessible(true);  //删除这行会报错
    int location = (int) fieldAccess.get(jack);  //10000
    System.out.println(location);
} catch (NoSuchMethodException | InstantiationException | InvocationTargetException | ClassNotFoundException
        | NoSuchFieldException | IllegalAccessException e) {
    e.printStackTrace();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值