反射在百度的解释为“一种计算机处理方式,是程序可以访问、检测和修改它本身状态或行为的一种能力”,即在运行的时候可以分析代码的内容,包括注解的内容,得到需要的信息。就像在常用的IDE如Eclipse中利用了反射实现了获取类的信息。
1.Class
java.lang.Class没有公共的构造方法,因此单独声明一个Class对象是不允许的;每个类型都有一个Class对象,这个Class对象记录了改类型的interface,field,method,annotation,generics等。任何类型,包括基础数据类型(int, short, long, float, double, char, btye, boolean)以及void类型都有对应的Class对象。
package com.test.ReflectTest;
/**
* @author 王狗蛋
* @date : 2017年8月13日 上午11:42:42
*/
public class Reflect {
public static void main(String...args) throws Exception {
// The first method to get a class object
// This method will throws ClassNotFoundException
Class<String> clz = (Class<String>)Class.forName("java.lang.String");
// The second method to get a class object
clz = String.class;
// Now let check the attributes of clz
System.out.println("The name of clz is " + clz.getName());
System.out.println("Is clz a primitive " + clz.isPrimitive());
// Of course we can check whether an object is specific class
System.out.println("Is string is a String " + clz.isInstance("string"));
// And there is another method to print info
System.out.println(clz.toGenericString());
// Of course we can create an instance
String instance = clz.newInstance();
System.out.println(instance instanceof String);
}
}
输出结果
The name of clz is java.lang.String
Is clz a primitive fase
Is string is a String true
public final class java.lang.String
true
从结果可知,clz的类名为java.lang.String,并且clz不是一个基础类型,“string”字符串是clz的类型,Class这个类是一个public final 类,由于被final修饰,Class在创建之后不会被修改,保证了代理时的安全性
2.interface
接口不能实例化并且包含了所有继承类应包含的所有方法,但接口可以被访问,也可以调用里面的方法。调用方法会在介绍invoke方法的笔记中记录 。
我们都知道String类继承Serializable,Comparable;但String类到底都继承了那些接口呢,我们来下面代码:
/** 这段代码接上述代码 */
Class<?>[] inters = clz.getInterfaces();
for (Class<?> inter: inters) {
System.out.println(inter.toGenericString());
}
结果如下:
public abstract interface java.io.Serializable
public abstract interface java.lang.Comparable
public abstract interface java.lang.CharSequence
这三个接口就是String类继承的接口,其中Serializable就是可序列化接口,使得该类型的对象可以通过IO输出;Comparable用于实现俩个对象之间的比较,并通过方法compareTo(T object)来制定比较规则 ;CharSequence是String和AbstractStringBuilder的接口,具体内容在String笔记中介绍(我也不知道这个笔记什么时候整理,一般String和StringBuilder就够用了)
3.Field
域就是类的属性,我们可以通过一个Class对象来获取field
首先我们先弄一个类用来演示:
package com.test.ReflectTest;
/**
* @author 王狗蛋
* @date : 2017年8月13日 下午12:21:06
*/
public class DemoClass {
public int num1 = 1;
protected int num2 = 2;
int num3 = 3;
private int num4 = 4;
public static int num5 = 5````
}
然后我们继续在main方法中演示:
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
Class<?> clzDemo = Class.forName("com.test.ReflectTest.DemoClass");
Object obj = clzDemo.newInstance();
Field field = clzDemo.getField("num1");
System.out.println(field.toGenericString());
System.out.println(Modifier.toString(field.getModifiers()));
System.out.println(field.getType().toGenericString());
field.set(obj, 10);
System.out.println(field.get(obj));
输出结果:
public int com.test.ReflectTest.DemoClass.num1
public
int
10
首先先用clzDemo.getFIeid(“field name”)获取了name为”num1”的域
然后field.getModifiers()获取field的修饰类型,但获得的是一个int型对象,需要通过Modifier的静态方法toString(int)来输出;field.getType()获取域的数据类型;通过set方法来修改值并通过get方法获取值;
在set(Object obj, Obejct value)方法中修改对象为obj的域,但num5是一个静态属性,即在内存中只存放一个num5对象,即使创建多个对象,每个对象的num5都是引用内存中那唯一的num5域的值。
所以在set()方法时,如果该域是一个静态的域,那么在传入第一个参数时可以传入空指针null,即set(null,20);这时所有对象的num5都被改为20。
可能你会想这样一个一个域取很麻烦,当然有简单的方法:
Field[] fields = cls.getFields();
for (Field field: fields) {
System.out.println(field.toString() + "\t" + field.get(obj));
}
输出结果:
public int com.test.ReflectTest.FieldDemo.num1 1
public static int com.test.ReflectTest.FieldDemo.num5 10
诶,等等,明明有5个域,你怎么就把public的域输出出来了呢?这是因为getFields()方法只会返回public域,对于private protected 和默认类型不会输出,这时我们就需要用到另一个方法getDeclaredFields()
fields = cls.getDeclaredFields();
for (Field field: fields) {
//field.setAccessible(true);
System.out.println(field.toGenericString() + "\t" + field.get(obj));
}
这时候一定会报一个java.lang.IllegalAccessException异常,这个异常就是说没有访问权限造成的异常,这时把注释掉那行代码加上就不会有问题了,哈哈神奇吧。
输出结果:
public int com.test.ReflectTest.FieldDemo.num1 1
0
protected int com.test.ReflectTest.FieldDemo.num2 2
int com.test.ReflectTest.FieldDemo.num3 3
private int com.test.ReflectTest.FieldDemo.num4 4
public static int com.test.ReflectTest.FieldDemo.num5 5
为什么会这样呢,Accessible = true 会忽略访问权限的限制,具体会在invoke方法介绍的笔记中介绍。
4.Method
方法就是定义在类中的所有方法,构造方法需要用构造调用
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Modifier;
Method[] methods = clz.getMethods();
for (Method method: methods) {
System.out.println("-----------------------");
System.out.println("name:" + method.getName());
System.out.print("defaultValue:" + method.getDefaultValue());
System.out.print("generic return type:" + method.getGenericReturnType());
System.out.print("return type:" + method.getReturnType());
System.out.print("modifiers:" + Modifier.toString(method.getModifiers()));
// Parameters
Parameter[] parameters = method.getParameters();
System.out.print(parameters.length + "parameters:");
for (Parameter para: parameters) {
System.out.print("name:" + para.getName());
System.out.print("type:" + para.getType().toString());
}
Class<?>[] parameterTypes = method.getParameterTypes();
System.out.print(parameterTypes.length + "parameters:");
for (Class<?> paraType: parameterTypes){
print("parameter smiple name: " + paraType.getSimpleName());
System.out.print("parameter type name: " + paraType.getName());
System.out.print("parameter modifier:" + Modifier.toString(paraType.getModifiers()));
System.out.print(paraType.toGenericString());
}
Class<?>[] exceptionTypes = method.getExceptionTypes();
System.out.print(exceptionTypes.length + "exception types:");
for (Class<?> exceptionType: exceptionTypes) {
System.out.print("exception name: " + exceptionType.getName());
System.out.print("exception simple name: " + exceptionType.getSimpleName());
System.out.print(exceptionType.toGenericString());
}
System.out.print("is accessible: " + method.isAccessible());
System.out.print("is varArgs: " + method.isVarArgs());
}
输出结果太多了,就是String类的所有可见方法,只弄一部分结果:
name:equals
defaultValue:null
generic return type:boolean
return type:boolean
modifiers:public
1parameters:
name:arg0
type:class java.lang.Object
1parameters:
parameter smiple name: Object
parameter type name: java.lang.Object
parameter modifier:public
public class java.lang.Object
0exception types:
is accessible: false
is varArgs: false
当然如果你想看不可见的方法,可以调用getDeclaredMethods()方法,
当然在获取一个方法对象后可以通过invoke(Object rescourse, Object…argument)方法调用,具体在invoke笔记中介绍。
6.构造器
我们可以通过类对象来获取该类的构造器
import java.lang.reflect.Constructor;
Constructor<?>[] constructors = clz.getConstructors();
for (Constructor<?> constructor: constructors) {
System.out.println("constructor " + constructor.getName());
System.out.println("number of parameters: " + constructor.getParameterCount());
System.out.println("modifiers: " + Modifier.toString(constructor.getModifiers()));
System.out.println(constructor.toGenericString());
Annotation[] annotaitons = constructor.getAnnotations();
for (Annotation annotation: annotaitons) {
System.out.println(annotation.toString());
}
System.out.println();
}
输出结果:
constructor java.lang.String
number of parameters: 3
modifiers: public
public java.lang.String(byte[],int,int)
constructor java.lang.String
number of parameters: 2
modifiers: public
public java.lang.String(byte[],java.nio.charset.Charset)
constructor java.lang.String
number of parameters: 2
modifiers: public
public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException
constructor java.lang.String
number of parameters: 4
modifiers: public
public java.lang.String(byte[],int,int,java.nio.charset.Charset)
constructor java.lang.String
number of parameters: 4
modifiers: public
public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException
constructor java.lang.String
number of parameters: 1
modifiers: public
public java.lang.String(java.lang.StringBuilder)
constructor java.lang.String
number of parameters: 2
modifiers: public
public java.lang.String(byte[],int)
@java.lang.Deprecated()
constructor java.lang.String
number of parameters: 4
modifiers: public
public java.lang.String(byte[],int,int,int)
@java.lang.Deprecated()
我仅仅保留了一部分结果,在代码中我要求输出可见的构造器的名字,参数,修饰类型,构造器的toGenericString(),以及构造器的注解,对于注解的含义在注解介绍笔记详细介绍,从结果看有@Deprecated注解,表示该方法被遗弃,不建议使用。
java反射机制的基础就像介绍在这里,后续将继续通过笔记介绍java反射的invoke,proxy代理等。
第一篇博客如有不足还请见谅,如有原理问题欢迎提出,共同学习。