Java反射API常用的类
目录
java.lang.Class
java.lang.reflect.Constructor
java.lang.reflect.Method
java.lang.reflect.Field
java.lang.reflect.Modifier
java.lang.Class
Java是一门面向对象的语言,其中“万物皆对象”这一理念非常的重要,说到面向对象,我们第一个联想到的就是“封装”、“继承”、“多态”,说到封装,那么就是把对象相同的特征,分装成一个类,那么,类他也是一个对象,这个对象“封装”的那个类就是java.lang.Class。
Java.lang.Class一个比较特殊的点就是他的实例化的过程就比较特殊,他没有共有的构造方法,所以我们无法实现如同以下代码的实例化方法获取实例化对象
Class<Dog> c = new Class();
所有的类在实例化的时候(如以下代码:)
Dog dog = new Dog();
Jvm在执行该段代码的时候首先回去寻找Dog.class文件,然后将Dog.class文件读取内容,存储在内存中,在生成dog对象的内存空间的同时,如果是首次实例化,则会自动在内存中新增一个Class对象,这个Class的对象是自动创建的,并且一个类只会产生一个class对象,所以不允许自己手动实例化Class对象。
正常的加载是先通过实例化对象,如上图的new Dog();然后获取到Class对象,而反射的本质则是先获取Class对象,然后通过Class对象,去获取Dog对象的各种信息。
Class类由于不能被直接实例化,所以我们会用以下三种方法获取Class对象
Class<Dog> dogClass;
//第一种:通过类名去获取Class对象
dogClass = Dog.class;
//第二种:通过对象的getClass();方法获取Class对象
Dog dog = new Dog();
dogClass = u.getClass();
//第三种,通过Class.forName("类的路径");获取Class对象
try {
dogClass = Class.forName("com.entity.Dog");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
不得不说这三种方式种常用第三种,因为第一种存在需要导包的问题,如果没有导包会抛出编译错误。第二种你都有对象了,还要反射做什么。第三种,一个字符串就可以,并且这个字符串我们可以采取读配置文件等方法,比较灵活。
获取到Class类之后,可以通过对象.newInstance();方法,获取到对象,如:
//获取Dog这个Class的对象
Class<Dog> dogClass;
try {
dogClass = Class.forName("com.entity.Dog");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//实例化
Dog dog;
try {
dog = dogClass.newInstance();
} catch (InstantiationException e) {
//说明没有无参的构造方法
e.printStackTrace();
} catch (IllegalAccessException e) {
//说明当前没有权限调用无参构造方法
e.printStackTrace();
}
这个方法可以会抛2个异常,具体原因见上图解释。讲完了无参的构造方法,那么有参的构造方法要怎么用呐?我们需要讲下一个API。
java.lang.reflect.Constructor
万物皆对象,对象的类也是一个对象,那么类的构造方法是不是也会有一个对象?是的,这就是我们接下来要说的Constructor类。
Constructor就是构造器的意思。我们可以通过之前获取到的Class方法,调用它的getConstructor(Class<?>... parameterTypes);方法获取它的构造器,如:
try {
Constructor<Dog> c = dogClass.getConstructor();
Constructor<Dog> c1 = dogClass.getConstructor(String.class,Integer.class);
} catch (NoSuchMethodException e) {
//未找到这个方法
e.printStackTrace();
}
这里给大家举了两个例子,第一个没有参数是这个类的无参构造方法,第二个是参数为一个String,一个Integer的有参构造方法,如果找不到对应的构造方法,会出现NoSuchMethodException。
获取到该方法之后可以通过Constructor的newInstance(Object... initargs);方法,获取实例化对象,如
Constructor<Dog> c;
Constructor<Dog> c1;
try {
c = dogClass.getConstructor();
c1 = dogClass.getConstructor(String.class,Integer.class);
} catch (NoSuchMethodException e) {
//未找到这个方法
e.printStackTrace();
}
//获取实例化对象
try {
//等同于Dog dog = new Dog();
Dog dog=c.newInstance();
//等同于Dog dog1= new Dog("旺财",3);
Dog dog=c1.newInstance("旺财",3);
} catch (InstantiationException e) {
//无法实例化指定的类对象时抛出
e.printStackTrace();
} catch (IllegalAccessException e) {
//说明你当前无权限,可以通过
//c.setAccessible(true);
//上面这行代码强行获取权限
e.printStackTrace();
} catch (InvocationTargetException e) {
//如果你的构造方法抛出了某个异常,会在这里被捕获
//说明你调用的构造方法抛了一个异常
e.printStackTrace();
}
会抛出的异常都在上面解释了,请仔细看注释。
Class类返回Constructor所有的方法:
|
返回一个 |
|
返回包含一个数组 |
|
返回一个 |
|
返回一个反映 |
|
如果此 |
java.lang.reflect.Method
说完了构造方法, 那么,类的方法是不是也是一个对象呐?没错,万物皆对象,接下来我们来看看Method对象。
Method对象获取的方式依旧是依靠Class对象,具体方法见以下代码:
//省略获取dogClass和dog对象的代码,具体代码参考以上博客内容
//获取该类的所有方法
Method[] m = dogClass.getMethods();
System.out.println("------------------------");
//遍历所有的方法
for (Method method : m) {
//输出方法的名称
System.out.println(method.getName());
//毕竟方法是否以set开头
if(method.getName().startsWith("set")){
try {
//用第一个参数dog执行该方法,参数是一个字符串“test”
method.invoke(dog,"test");
} catch (IllegalAccessException e) {
//无权限异常
e.printStackTrace();
} catch (InvocationTargetException e) {
//方法抛出异常,并没有被处理
e.printStackTrace();
}
}
}
这种方法可以获取到所有的set开头的方法,如果我们只需要获取一个明确名称、参数的方法,可以使用以下代码实现:
//省略获取dogClass和dog对象的代码,具体代码参考以上博客内容
//获取该类的所有方法
Method m = null;
try {
//获取名为setName,参数为一个String的方法
m = s.getMethod("setName",String.class);
} catch (NoSuchMethodException e) {
//没有找到该方法会抛此异常
e.printStackTrace();
}
try {
//使用dog对象,执行这个setName方法,参数为“旺财”
m.invoke(dog,"旺财");
} catch (IllegalAccessException e) {
//无权限异常
e.printStackTrace();
} catch (InvocationTargetException e) {
//如果setName抛出异常没有被捕获,进入此处
e.printStackTrace();
}
某种角度看和获取构造方法以及构造方法的使用非常相似。
Method |
返回一个 |
|
返回包含一个数组 |
Method |
如果此 |
Method |
返回一个 |
Method |
返回包含一个数组 |
java.lang.reflect.Field
众所周知,类有一个非常重要的元素,就是类的属性,那么属性也是一个对象,这个对象的类名字就是Field,这里也给大家总结一下,我们先从如何获取这个对象说起:
获取Field对象的四种方法都是以Class对象来获取的
|
返回一个 |
|
返回的数组 |
|
返回一个 |
|
返回包含一个数组 |
他们区别应该很容易看到,有s则说明是返回全部的内容,存在Declared说明会获取类中所有的属性(public、protected、default、private),但不包括继承的属性,不存在就会返回获取类中public类型的属性。
比如我们的Dog类只有Name和Age这两个属性,这两个属性都是私有的(private)
//省略获取dogClass和dog对象的代码,具体代码参考以上博客内容
//获取所有共有属性
Field[] fields = dogClass.getFields();
//输出长度
System.out.println("fields.length"+fields.length);
for (Field field : fields) {
//输出所有属性名字
System.out.println(field.getName());
}
这个就会出现
因为你的所有属性都是私有的,共有的属性长度为0。所以我们需要调用它的
getDeclaredField();
方法,这方法会返回所有的属性
//省略获取dogClass和dog对象的代码,具体代码参考以上博客内容
//获取所有共有属性
Field[] fields = dogClass.getDeclaredFields();
//输出长度
System.out.println("fields.length"+fields.length);
for (Field field : fields) {
//输出所有属性名字
System.out.println(field.getName());
}
这时会返回长度为2,分别为name、age
Field的常用方法:
|
将指定对象参数上的此 |
|
设置作为一个字段的值 |
|
设置作为一个字段的值 |
|
设置作为一个字段的值 |
|
设置作为一个字段的值 |
|
设置作为一个字段的值 |
|
设置作为一个字段的值 |
|
设置作为一个字段的值 |
|
设置作为一个字段的值 |
setXxx方法,第一个为被修改的对象,第二个为需要修改的值。
其中要特别说明一下,他们都会抛出两种异常:
IllegalAccessException和
IllegalArgumentException
IllegalAccessException异常是因为:我们修改的那个属性是final的,无法被修改。不过由于 Field 继承AccessibleObject , 我们可以通过 AccessibleObject.setAccessible() 方法去修改变量的访问性,如field.setAccessible(true)。
IllegalArgumentException异常是因为:新value和原value的类型不一致导致。特备说明,在反射种,包装类(如Integer和int等等)他们不会自动装/拆箱,需要手动修改。
除了set方法, 还存在比较常用的方法:
|
如果此字段表示枚举类型的元素,则返回 |
|
返回该所表示的字段的值 |
|
返回由此 |
|
返回由该 |
其中看到一个方法,叫做getModifiers,此方法返回的是一个int类型,这个是修饰符的意思,那么修饰符是否也是一个类呐?我们接下来看下一个常用API。
java.lang.reflect.Modifier
没错,修饰符也是一个对象,它也有自己的类,叫做Modifier。之前我们提到的Constructor,Method,Field均存在getModifiers这个方法,这个方法会返回一个int类型,然后我们可以通过这个Modifier类,通过以下方法,将次int值作为参数可获取到你需要知道的修饰符。
|
如果整数参数包含 |
|
如果整数参数包含 |
|
如果整数参数包含 |
|
如果整数参数包含 |
|
如果整数参数包含 |
|
如果整数参数包含 |
|
如果整数参数包含 |
|
如果整数参数包含 |
|
如果整数参数包含 |
|
如果整数参数包含 |
|
如果整数参数包含 |
|
如果整数参数包含 |
好,我说完了。