java之反射的应用

Java的反射库是一个被精心设计的工具集,使用它编写和动态的操纵java代码。

那么java的反射能做什么呢?官方的说法是能够在运行中分析类的能力,能够在运行中查看对象。

官方说法很正确,反射确实是在java代码在运行状态中分析类和对象,但一般人看不懂!

所以简单的说,反射就是当你找到一个类,想创建这个类的对象,使用它的方法时,却失望的发现这个类的构造方法和方法不能被直接使用。这时你使用反射便能够构造出这个类,并且使用这个类中不支持直接使用的方法。

java.lang.reflect包的内容

在reflect包下有三个类,
1、Filed类:代表类的成员对象(成员变量也叫类的属性)
2、Method:代表类的方法
3、Contructor:代表类的构造方法
4、Array类:提供动态创建数组和访问数组元素的静态方法

看起来一切似乎都很复杂,那我们直接用代码来代替吧!

首先我们创建一个java项目,然后创建一个包,包名为com.example.reflection,然后创建Employee类,代码如下:

public class Employee {

public Employee(){
    System.out.println("无参数构造方法");
}
private String name;
private int age;

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public int getAge() {
    return age;
}

public void setAge(int age) {
    this.age = age;
}

public Employee(String name, int age) {
    super();
    this.name = name;
    this.age = age;
}

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

private void work(){
    System.out.println("working...");
}

}

一般情况下我们可以直接用new的方式创建这个类的实例,但是现在我们要使用反射来创建这个类,并且使用里面的方法。
现在我们再创建一个ReflectionAPIDemo类,添加Main方法,代码如下:

public class ReflectionAPIDemo {
   public static void main(String[] args)throws Exception {

    }
}

使用反射创建类的实例

使用反射创建类的实例有两种方法,但是它们有个共同的特点,又或者说这是使用反射的基础步骤,就是必须获得想要创建类的Class对象。
创建Class对象的方法很简单,只需知道类的绝对路径就可以了,也就是包名 + 类名,而Employee的绝对路径为:com.example.reflection.Employee。所以使用以下代码获得Employee的Class对象:

Class<?> classType = Class.forName("com.example.reflection.Employee");

当然,上面的写法是最常见的写法,其实要获得Class对象也有其他的写法,如果我们已经拥有了Employee对象的实例employee,那么可以用以下方法:

Employee employee = new Employee();
Class<?> classType = employee.getClass();

又或者直接使用Employee类获得Class对象

Class<?> classType = Employee.class;

拓展一下,获取了Class对象之后也可以获取Employee的父类,也就是Object类,下面代码能输入Object的类名:

System.println.out(classType.getSuperclass().getName());

获得Class对象之后就能开始创建类的实例了,如下

// 第一种创建类实例的方法
Employee employee=(Employee)classType.newInstance();

第一种创建类的实例的方法很简单,只需要使用newInstance()方法便可以了。而第二种获得类的实例的方法则稍显麻烦一点,它首先要获得一个Constructor对象,在Class类中有一个getConstructor()方法能够获取Constructor()对象,然后再通过Constructor的newInstance()方法获取类的实例,代码如下:

Constructor<?> constructor = classType.getConstructor(new Class[]{});
Employee employee=(Employee)constructor.newInstance(new Object[]{});

我们仔细观察使用constructor.newInstance()这种方法获取对象的特点,发现它需要传入一个参数,在获取Constructor对象的时候传入了一个new Class[]{}数组,然后使用constructor.newInstance()要传入一个new Object[]{}数组,它们代表的是什么呢?

其实使用Constructor来创建类的实例优点是它能够创建出带有参数的类的构造方法的实例,也就是能够使用Employee中的带参构造方法,代码如下:

public Employee(String name, int age) {
  super();
  this.name = name;
  this.age = age;
}

但是我们之前的new Class[]{}数组和new Object[]{}数组并没有传入任何参数,所以使用的是不带参数的构造方法。用反射使用带参数的构造方法如下:

Constructor<?> constructor = classType.getConstructor(new Class[]{String.class,int.class});
Employee employee=(Employee)constructor.newInstance(new Object[]{"张三",30});

现在我们发现new Class[]{}数组传入了String.class,int.class两个参数,这是因为Class数组要传入的是Class对象,而它所代表的Class对象则是Employee带参数构造方法中要传入的两个参数String name和int age的Class对象。而constructor.newInstance(new Object[]{"张三",30})中传入的参数就好理解了,它里面的是创建Employee对象时所要传入的实例。

调用类的指定方法

反射中同样有调用类的指定方法的方法,如我们要调用Employee中的toString()方法,代码如下:

Method method = classType.getDeclaredMethod("toString", new Class[]{});
    //方法的调用
String desc=(String)method.invoke(employee, new Object[]{});
System.out.println(desc);

getDeclaredMethod()方法返回的是Employee中的的方法,中第一个参数代表指定调用的方法名,第二个则是要调用方法中需要传入的参数的Class对象,toString()方法不需要传入参数,所以传入一个空的Class数组。
method.invoke()则是代表要调用运行这个方法了,第一个传入的参数表面我们要调用的方法是属于哪一个对象的,在这里我们传入employee。需要注意的是如果我们调用的方法是static静态方法,那么这个参数可以为null。第二个参数new Object[]{}数组则是我们要调用方法所要传入的参数,这里也为空。

这时我们就能够调用Empleyee中的toString()方法了。

现在有一个问题,如果我们想要调用Employee中修饰为private中的work()方法怎么办?标识为private中的方法只能在本类中使用,无法在外部使用,这能调用吗?答案是可以的,只要设置Method类中的setAccessible()为true便可以调用,代码如下:

Method method = classType.getDeclaredMethod("work", new Class[]{});
method.setAccessible(true);
//方法的调用
method.invoke(employee, new Object[]{});

当然了,Java中调用private的方法这是属性是不太建议的,因为这破坏了java的封装性。

当然,上面是调用我们指定的方法,Class类中也提供了获取类中方法的其他方法,如下
获得当前类以及超类的public Method

Method[] arrMethods = classType.getMethods();

获得当前类申明的所有Method

Method[] arrMethods = classType.getDeclaredMethods();

获取当前类以及超类制定的publci Method

Method method = classType.getMethod(String name, Class<?>...parameterTypes);

当然,通过反射动态运行指定Method还是用invoke()方法,如下:

Object obj = method.invoke(Object obj, Object...args);

调用类的属性

Employee中有标注为private的属性name,代码如下:

    private String name;

现在我们用反射调用并且设置它的参数,代码如下:

Field field = classType.getDeclaredField("name");
field.setAccessible(true);
field.set(employee, "李四");
System.out.println(field.get(employee));

在输出结果中发现我们确实调用了Employee中的属性name,并且给它赋值了。

同样reflect中还有很多获取和设置属性的方法,如下:

获得当前类以及超类的public Field

Filed[] arrFields = classType.getFields();

获得当前类申明的所有Field

Field[] arrFields = classType.getDeclaredFields();

获得当前类以及超类制定的public Field

Field field = classType.getField(String name);

获得当前类申明的制定的Field

Field field = classType.getDeclaredField(String name);

通过反射动态设定Field的值

field.set(Object obj, Object value);

通过反射动态获取Field的值

Object obj = field.get(Object obj);

总结

本文只是将Java中常用的反射方法介绍一下,Java的反射库中还有这许多的知识在本文中并没有讲解,如果想要详细了解就只能看java的API文档了。

值得注意的是java中获取Class对象也会有一些特殊的用法,如基本数据类型。

当你使用int、long这些基本数据类型获取他们的Class对象时,你会发现他们居然有两种不同形式的获取类对象的方法,第一种是直接获取,第二种是使用其包装类获取,代码如下:

    Class<?> intClassTYPE = int.class;
    System.out.println(intClassTYPE.getName());
    Class<?> IntegerClassTYPE = Integer.TYPE;
    System.out.println(IntegerClassTYPE.getName());

他们的输入的类名都是int,说明他们能够获取同一个对象。

同理double.class与Double.TYPE也能获取double类名,long.class与Long.TYPE都是获取long类名的。

但是如果你输入Integer.class时获取的Class对象这是Integer了,这点是使用基本数据类型获取Class对象需要注意的。

友情链接:http://www.jianshu.com/p/f2adb1ac520a java中的I/O流

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

推荐阅读更多精彩内容