Java反射

来源:互联网 发布:tcpip网络层安全协议 编辑:程序博客网 时间:2024/05/16 15:53

今天大概讲了两个问题,其一为,可变参数,其二为反射。
重点 当然是反射,不过在之前需要用到可变参数,所以就先讲了。

可变参数

思考一个函数形式为

void funtion (String[] args){}

这个函数接受了一个数组作为参数。但是使用数组作为变量不方便,所以在jdk1.5之后添加了可变参数的特性。考虑将[]直接替换为...
就成了如下

void funtion (String... args){}

不会出现问题,那么这个就叫做可变参数,args实际上是被当作一个String类型的数组,不过在传参的时候会变得灵活许多,通过反编译class文件之后,你会发现,编译之后就直接成为了一个数组。
可变参数有几个特性
其一,在一个方法的参数中,只能有一个可变参数。
其二,在一个方法的参数中,可变参数必须放在最后。
所以可变参数是一种语法上的,并没有改变java的执行方式,另外编译之后的函数会被加上关键字transient,不被序列化。
这几个特性很好理解,因为编译器并不知道你需要用几个参数,他只能根据顺序将参数一一匹配,然后多的全部给可变参数。
这就是可变参数了。

反射

反射实际上是一种什么东西的,有一个叫做Class的类,可以创建其他的类,我们可以通过这个类,或者方法,来实现对其他类的操作,包括对成员方法,构造方法,成员变量的操作。

反射就是把Java类中的各种成分映射成一个个的java对象。例如,一个类有:成员变量,方法,构造方法,包等等信息,利用反射技术可以对一个类进行解剖,把各个组成部分映射成一个个对象。

下面来一点一点的了解
如果我们想操作一个类,那么首先我们需要获得一个类,我们通过Class这个类可以得到这样的一个实例。
得到Class有三种方法。
1. 通过class直接得到

Class class=Student.class;
  1. 通过类的实例的getClass方法
Student student=new Student();Class class=student.getClass();
  1. 通过Class的forname方法
Class class=Class.forName("com.mengxs.domo.Student");

有几个点需要注意,Class后面跟泛型,不知道的话我们可以填?比如Class<?> 直接写class也可,如果实现的接口或者继承自某类,就写对应的泛型Classs<? entend Student>同样后面的也同样加上泛型即可。
另外第三种方法中,需要写的是类的全名称,可以通过在eclipse中展开该类文件,然后再下面的类中,右键点击Copy Qualified Name来获得,记住如果不展开文件的话你得到得只是类似下面的路径
/hello/src/com/mengxs/demo/Demo.java
另外第一种方法需要知道类名,然后直接赋值得到,而第二种需要一个该类的实例,如果不知道类名的话,这两种方法都无法使用,所以最推荐第三种,即便类不存在,通过String传入也不会编译不通过,同样,这个String可以通过配置文件,流获得,十分灵活。

得到类的实例

在有了这个类之后我们需要得到这个实例,我们实例化一个类,需要使用构造函数,所以我们首先来得到构造函数。

构造函数有无参和有参的,我们先来看无参的。

在Class中,直接使用

Constructor<?> con = class3.getConstructor(null);

我们能得到一个构造方法类的实例,通过这个方法我们可以实例化对象。
同样有参的构造方法只需要在后面的参数中,加上对应类的class即可
如下

Constructor<?> con = class3.getConstructor(String.class,int.class);

如果是数组,直接为String[].class即可。
在class中还有.getConstructors方法,可以直接得到一个构造函数的数组。这里不再介绍。

那么我们有了构造函数之后我们就可以通过构造函数来实例化一个类了
在构造函数类中有叫做newInstance的方法,就是new新实例
如下

Object o = con.newInstance();

那么有参的也是同样

Object object = con.newInstance("qwe",20);

对于无参的构造方法我们可以直接在class中调用newInstance()前提是你已经写了无参的构造方法。这里直接将值赋给一个Object类型。

得到类的属性

对于类的属性,我们还是需要对类进行操作,来得到类,然后可以对属性进行set,get操作。

属性是Field。
所以我们得到属性就是

Field field=class.getField("name");

这样可以得到一个属性的实例,这里需要注意的是使用getField,和getFields方法只能得到公共的属性,这里也包括父类的公共属性。
如果我们需要得类的所有方法,包括私有属性,我们需要使用getDeclaredField,用法相同,也有getDeclaredFields返回的是属性的一个数组。
有了属性的对象之后我们就可以对属性进行操作。
对一个属性设置值,得到值:

field.set(object,"测试");field.get(object);

得到得都是object可以自己转换,同时set,get都有getInt之类的便捷方法用来使用,可以简化代码。
这里需要注意的是,如果你访问的是私有方法会报
java.lang.IllegalAccessException: Class com.mengxs.domo26.Test can not access a member of class com.mengxs.domo26.Student with modifiers "private"
也就是没有权限访问,如果需要访问的话,我们需要设置对应的属性java访问权限无效,即:

field.setAccessible(true);

最后,如果是静态的话,对象直接填null就行。

得到类的方法

得到类的方法的过程和得到属性,几乎一致。
方法:Method
还是对类进行操作。
首先得到方法的实例:

Method m = class.getMethod("show");

这里和属性类似,总共是四个方法,两个得到得是公共的方法,分别是根据名称查找,或者直接返回一个方法数组,也就是getMethodgetMethods,另外两个就是getDeclaredMethodgetDeclaredMethods得到得是本类所有的方法,包括私有的,但是不包括父类的。

我们得到了方法的实例之后,就可以调用方法。通过方法的invoke来调用。如下:

m.invoke(object,"haha");

第一个变量是调用方法的实例,第二个之后的是参数,如果没有的话,就不填即可。同样如果是静态方法的是,对象填null即可。对于私有的,处理方法和上面的变量一样,另外和自己对象调用一样,也会直接返回值,不过值得类型是Object类型的。

这样基本的操作就完成的差不多了。

最后说一下如果使用反射调用main函数。
基本就是和调用方法差不多,不过main函数是有参的,而且是一个String的数组,但是你传给他一个数组发现,同样也不通过。这里需要直接强转成Object类型,老师说这里是java设计的一个bug,也没有深入了解。

m.invoke(null,(Object)new String[]{"123","adf"});