Java反射总结

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类中有下面几个常用的获得构造函数的方法:

  • getConstructors - 获得所有公有的函数
  • getDeclaredConstructors - 获得所有的构造函数
  • getConstructor - 获得指定签名的构造函数,其参数列表为构造函数参数的类的列表
    注意getConstructors 和 getConstructor 会抛出
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中一个重要的辅助机制实际使用的并不多。使用反射回带来性能上的问题并会破坏面向对象编程中的封装性,实际的编码过程中应慎用反射。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容

  • 概念介绍 Java反射机制JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任...
    niaoge2016阅读 511评论 0 2
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,493评论 18 399
  • 1、Class对象的获取 Java中对象可以分为两种,一种是实例对象,一种是Class对象。Class对象是在类加...
    追逐地平线的甘阅读 276评论 0 1
  • (一)Java部分 1、列举出JAVA中6个比较常用的包【天威诚信面试题】 【参考答案】 java.lang;ja...
    独云阅读 7,026评论 0 62
  • 一道漩涡 旋转 旋转 一道漩涡 美丽 美丽 我投身其中 毫不 犹豫 如果可以 那就让我死在你怀里 然后...
    盛世花酒阅读 349评论 0 0