Java反射总结

2017-12-25  本文已影响0人  ObadiObada

Java反射总结

概述

通常在Java代码中使用一个类,需要在编译时知道类的位置。但是在某些时候,需要使用的类在编译时是未知的,相关信息需要在运行时确定。此时就可以通过反射获得使用只有在运行时才能确定名称的类,完成创建类的对象,读写/成员,调用函数等操作。本文通过一系列的例子讲述反射常用的方法的使用,用于测试的类如下:


package com.minghui.model;

class Engineer extends Employee implements Comparable<Engineer> {

    private String mType;
    private int mTypeCode;
    String mSkill;

    private Engineer(String name, String tittle) {
        this(name, tittle, 0);

    }

    protected Engineer(String name) {
        this(name, "Junior", 0);
    }

    public Engineer(String name, String tittle, int typeCode) {
        super(name, tittle);
        setType(typeCode);
        setSkill(typeCode);
        mTypeCode = typeCode;
    }

    private String setType(int typeCode) {
        mType = (typeCode == 0) ? "Software Engineer" : " Test Engineer";
        return mType;
    }

    void setSkill(int typeCode) {
        mSkill = (typeCode == 0) ? "Code" : " Test";
    }

    public String getType() {
        return mType;
    }

    public int compareTo(Engineer otherEngineer) {
        return mTypeCode - otherEngineer.mTypeCode;
    }

    @Override
    public String toString() {
        return "Name:" + mName + " Tittle:" + mTittle + " Type: " + mType + " Skill: " + mSkill;
    }

    public static String getSkillByCode(int typeCode) {
        return (typeCode == 0) ? "Code" : " Test";
    }
}

它的父类如下:

package com.minghui.model;

public class Employee {
    public static String descripiton = "EMPLOYEE";

    protected String mName;
    protected String mTittle;

    public Employee(String name, String tittle) {
        mName = name;
        mTittle = tittle;
    }

    public String getName() {
        return mName;
    }

    String getSalry() {
        return "Confidential";
    }

    public String getTittle() {
        return mTittle;
    }
}

获取类对象以及父类

可以通过Class.forName方法获得类对象,相关代码如下:

try {
    Class<?> engineerClass = Class.forName("com.minghui.model.Engineer");
    Class<?> superClass = engineerClass.getSuperclass();
    System.out.println("engineerClass:" + engineerClass.getName() + " superClass:"
            + superClass.getName());
    Class<?>[] interfaces = engineerClass.getInterfaces();
    for (Class<?> cls : interfaces) {
        System.out.println("engineerClass interface:" + cls.getName());
    }
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

上述代码输出如下:

engineerClass:com.minghui.model.Engineer superClass:com.minghui.model.Employee

engineerClass interface:java.lang.Comparable

获得到类的对象后,即可通过该对象完成创建对象,读写成员等各类操作。

创建对象

创建对象首选需要获得构造函数,Class类中有下面几个常用的获得构造函数的方法:

package com.minghui.generic;

Constructor<?>[] engineerConstructors = engineerClass.getConstructors();
for (Constructor<?> con : engineerConstructors) {
    System.out.println("engineerConstructor con:" + con.toGenericString());
}

Constructor<?>[] engineerDeclareConstructors = engineerClass.getDeclaredConstructors();
for (Constructor<?> con : engineerDeclareConstructors) {
    System.out.println("engineerDeclareConstructors con:" + con.toGenericString());
}

Constructor<?> constructor = engineerClass.getConstructor(String.class, String.class,
        int.class);
System.out.println("engineerClass  constructor :" + constructor.toGenericString());

上述代码结果为:

engineerConstructor con:public com.minghui.model.Engineer(java.lang.String,java.lang.String,int)

engineerDeclareConstructors con:public com.minghui.model.Engineer(java.lang.String,java.lang.String,int)

engineerDeclareConstructors con:protected com.minghui.model.Engineer(java.lang.String)

engineerDeclareConstructors con:private com.minghui.model.Engineer(java.lang.String,java.lang.String)

engineerClass constructor :public com.minghui.model.Engineer(java.lang.String,java.lang.String,int)

获得构造函数对象后,可以通过newInstance方法创建对象,在之前代码中增加如下几行:

constructor.setAccessible(true);
Object employ = constructor.newInstance("Bill", "Senior", 0);
System.out.println("employ objct is obj :" + employ);

输出如下:

employ objct is obj :Name:Bill Tittle:Senior Type: Software Engineer Skill: Code

注意在调用newInstance之前先调用了

constructor.setAccessible(true)

原因在于Engineer类的可见性为Package,测试类和其不再同一个包中因此无法访问,如果未增加这行代码则会抛出java.lang.IllegalAccessException.这段代码同时也说明反射的另外一个作用: 访问类的不可访问的成员(private,package,protected).

读/写成员

上一节的例子中我们通过反射获创建了Engineer对象。这一节介绍通过反射读写类的对象的成员变量的方法,参考如下代码:

Field nameField = superClass.getDeclaredField("mName");
nameField.setAccessible(true);
Field tittleField = superClass.getDeclaredField("mTittle");
tittleField.setAccessible(true);
Field skillField = engineerClass.getDeclaredField("mSkill");
skillField.setAccessible(true);
String name = (String) nameField.get(employ);
String tittle = (String) tittleField.get(employ);
String skill = (String) skillField.get(employ);
System.out.println("employ objct name is: " + name + " tittle is: " + tittle
        + " skill is: " + skill);

nameField.set(employ, "Tony");
tittleField.set(employ, "Derictor");
skillField.set(employ, "Wash-Cut-Blow");
System.out.println("employ after modifyed by reflect ois: " + employ);

上述代码输出为:

employ objct name is: Bill tittle is: Senior skill is: Code

employ after modifyed by reflect ois: Name:Tony Tittle:Derictor Type: Software Engineer Skill: Wash-Cut-Blow

上面的代码需要注意下面几点:

  1. mNamemTittle定义在父类中,使用父类的class对象获取
  2. mNamemTittle以及mSkill都不是可见性都不是public,在读写之前需要调用setAccessible(true)

调用方法

上一节介绍了通过通过反射读/写成员变量,这一节介绍通过反射调用成员方法,参考如下代码:

Method getSkillByCodeMethod =  engineerClass.getMethod("getSkillByCode", int.class);
getSkillByCodeMethod.setAccessible(true);
Method setTypeMethod =  engineerClass.getDeclaredMethod("setType", int.class);
setTypeMethod.setAccessible(true);
Method setSkillMethod = engineerClass.getDeclaredMethod("setSkill", int.class);
setSkillMethod.setAccessible(true);

Object skillByCode = getSkillByCodeMethod.invoke(employ, 0);
System.out.println("employ getSkillByCode skillByCode class: " + skillByCode.getClass()
        + " value: " + skillByCode);

Object setTypeRetValue = setTypeMethod.invoke(employ, 0);
System.out.println("employ setType setTypeRetValue class: " + setTypeRetValue.getClass()
        + " value: " + setTypeRetValue);
System.out.println("employ after setType employ : " + employ);

Object setSkillRetValue = setSkillMethod.invoke(employ, 0);
System.out.println("employ setSkill setSkillRetValue class : " + setSkillRetValue);
System.out.println("employ after setSkill employ : " + employ);

输出结果:

employ getSkillByCode skillByCode class: class java.lang.String value: Code

employ setType setTypeRetValue class: class java.lang.String value: Software Engineer

employ after setType employ : Name:Tony Tittle:Derictor Type: Software Engineer Skill: Wash-Cut-Blow

employ setSkill setSkillRetValue class : null

employ after setSkill employ : Name:Tony Tittle:Derictor Type: Software Engineer Skill: Code

上面的代码需要注意的几个点:

  1. getSkillByCodeMethod 方法是public类型需要可以通过getMethod获得,另外2个需要通过getDeclaredMethod获得
  2. 由于Engineer类可见性为package,因此三个方法都需要调用setAccessible(true)后才可以调用invoke

读取修饰符

通过反射获得的Class,Field,Method对象都包含一个getModifiers的方法,这个方法返回一个整形,代表了相关对象的修饰符。可以通过Modifier.toString(int)方法将整形的修饰符变成字符串,下面是例子:

int getSkillByCodeModifier = getSkillByCodeMethod.getModifiers();
int setTypeModifier = setTypeMethod.getModifiers();
int setSkillModifier = setSkillMethod.getModifiers();

System.out.println("employ getSkillByCodeModifier is : " + getSkillByCodeModifier + " "
        + Modifier.toString(getSkillByCodeModifier));
System.out.println("employ setTypeModifier is : " + setTypeModifier
        + Modifier.toString(setTypeModifier));
System.out.println("employ setSkillModifier is : " + setSkillModifier
        + Modifier.toString(setTypeModifier));

输出结果如下:

employ getSkillByCodeModifier is : 9 public static

employ setTypeModifier is : 2 private

employ setSkillModifier is : 0

getModifiers返回的整形,不同的位代表了不同的修饰符,定义在Modifier.java中,相关代码如下:

public static final int PUBLIC           = 0x00000001;
public static final int PRIVATE          = 0x00000002;
public static final int PROTECTED        = 0x00000004;
public static final int STATIC           = 0x00000008;
public static final int FINAL            = 0x00000010;
public static final int SYNCHRONIZED     = 0x00000020;
public static final int VOLATILE         = 0x00000040;
public static final int TRANSIENT        = 0x00000080;
public static final int NATIVE           = 0x00000100;
public static final int INTERFACE        = 0x00000200;
public static final int ABSTRACT         = 0x00000400;
public static final int STRICT           = 0x00000800;

总结

Java反射的机制可以用于在运行时获得 classfieldconstuctormethod 对象。反射式Java中一个重要的辅助机制实际使用的并不多。使用反射回带来性能上的问题并会破坏面向对象编程中的封装性,实际的编码过程中应慎用反射。

上一篇 下一篇

猜你喜欢

热点阅读