清明三天看了两天的java反射的知识,也敲了些代码,总觉得有点乱。还没去深入了解过反射的作用,不过先写个总结。
Java反射机制:
Java反射机制是在运行状态中,对于任何一个类,都能知道这个类的所有属性和方法,包括private的和public的。而对于任何一个对象,都能调用这个对象的任何一个方法和属性。
获取class的实例
首先,要获取class的实例,有4种方法:
先创建一个Person类:
</pre><pre>
public class Person {
public String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return this.age;
}
public String getName() {
return this.name;
}
public void show() {
System.out.println("我是一个人!");
}
private void show2() {
System.out.println("你也是一个人!");
}
}
1.调用运行时类本身的class属性:
Class clazz=Person.class;
2.通过运行时类对象获取:
Person p=new Person();
Class clazz=p.getClass();
3.通过Class的静态方法获取:
String className ="Person";
Class clazz=Class.forName(className);
方法3体现了反射的动态性,要注意的一点是类名要写完整,即加上包名。示例中的Person类在default package下,所以省略了包名。
4.通过类加载器:
ClassLoader classLoader = this.getClass().getClassLoader();
Class clazz=classLoader.loadClass(className);//className见方法3;
通过以上几种方法获取Class实例后,我们就可以进行如下的一系列操作:
1.创建对应的运行时类的对象;
2.获取对应的运行时类的完整结构(属性,方法,构造器,代码块,父类,包名);
3.调用对应的运行时类的指定结构(属性,方法,构造器);
4.反射的应用:动态代理;
创建对象:
//1.创建clazz对应的运行时类person类的对象
Class<Person> clazz=Person.class;
Person p= clazz.newInstance();//上面两行相当与创建一个对象 new Person();
创建运行时类的对象,其实就是调用类的空参的构造器。所以以后咱们在写类的时候,最好都写一个空参的构造器,一个是便于反射的时候调用newInstance()方法,还有一个就是如果没有空参构造器,其子类会报错。而且创建成功还需要构造器的权限足够。获取属性:
Class clazz=Person.class;
Field f1=clazz.getField("show");//得到show方法
Field[] field= clazz.getFields();
//用getField()方法只能得到public属性的参数,也可以得到父类中的public属性值
for(int i=0;i<field.length;i++){
System.out.println(field[i]);
}
//可获取所有的属性,但只能是运行时类本身的所有属性
Field[] fields=clazz.getDeclaredFields();
for(int i=0;i<fields.length;i++){
System.out.println(fields[i]);
}
}
获取 属性的权限修饰符, 变量类型, 变量名:
Class clazz=Person.class;
Field[] fields=clazz.getDeclaredFields();
for(Field f: fields ){
//1.获取权限修饰符,如public,private
System.out.print(Modifier.toString( f.getModifiers() )+" ");
//2.获取变量类型,如string,int
System.out.print(f.getType()+" ");
//3.获取属性名
System.out.print(f.getName()+" ");
System.out.println();
}
f.getModifiers()返回的是一个int值,代表不同的权限修饰符。0代表默认,1代表public,2代表private。Modifier有个静态方法Modifier.toString()将int值转成权限修饰符。获取类的所有构造器:
Class clazz=Person.class;
Constructor[] con= clazz.getDeclaredConstructors();
for(int i=0;i<con.length;i++){
System.out.println("第"+i+"个构造器"+con[i].getName());
//得到构造器的形参
Class[] pa= con[i].getParameterTypes();
for(Class m:pa){
System.out.print(m+" ");
}
System.out.println();
}
获取方法的完整结构:
Class clazz=Person.class;<pre name="code" class="java"><span style="white-space:pre"> </span>//1.获取运行时类及其父类中所有的public方法
<span style="white-space:pre"> </span>Method[] methods= clazz.getMethods();
for(Method me:methods){
System.out.println(me);
}
// 获取方法的 注解,权限修饰符,返回值 方法名 形参列表 异常等等
Method [] methods2= clazz.getDeclaredMethods();//获取类本身的所有方法
for(Method m:methods2){
//1.注解
Annotation[] a=m.getAnnotations();//得到方法的注解
for(Annotation an:a){
System.out.println(an+" ");
}
//2.权限修饰符
System.out.println("权限是"+Modifier.toString(m.getModifiers()));
//3.返回值类型
System.out.println("返回值类型是"+m.getReturnType());
//4.方法名
System.out.println(m.getName());
//5.形参列表
Class[] c=m.getParameterTypes();System.out.print("形参列表:");
for(Class cl:c){
System.out.print(cl.getName()+" ");
}
//6,异常
Class[] ex= m.getExceptionTypes();
if(ex.length>0) System.out.print("throws");
for(int i=0;i<ex.length;i++){
System.out.println(ex[i].getName()+" "+i+" ");}
System.out.println();
}
调用运行时类指定的方法:
Class clazz=Person.class;
Method m1=clazz.getMethod("show");//若方法无形参,就去掉形参名parameterTypes
//方法是一般的,所以需要对象
Person p=(Person) clazz.newInstance();
m1.invoke(p);//<span style="color:#ff0000;">让p调用该方法</span>
<span style="color:#ff0000;">//该方法有返回值,返回值就是前面show方法的返回值</span>
Method m2=clazz.getMethod("toString");
Object obj=m2.invoke(p);
System.out.println(obj);
//static方法调用
Method m3=clazz.getMethod("info");
m3.invoke(Person.class);
调用运行时类指定的属性
Class clazz=Person.class;
//1.获取指定的属性
Field name= clazz.getField("name");
//2.创建运行时类的对象
Person p=(Person) clazz.newInstance();
System.out.println(p);
//3.将运行时类的对象的指定的属性赋值
name.set(p, "Bruce");
System.out.println(p);
Field age=clazz.getDeclaredField("age");
<span style="color:#ff0000;">age.setAccessible(true);//少了这一行不能直接赋值</span>
//默认类型的参数,如id,可以不加这句话,但为了保险起见,可以加上
age.set(p, 20);
System.out.println(p);
这里需要注意的是,如果调用的属性权限修饰符为private,需要加setAccessible(true)方法,否则会报异常。
调用指定的构造器:
Class clazz=Person.class;
Constructor con=clazz.getConstructor(String.class,int.class);
//后面表示形参的类型,根据类型可确定构造器
con.setAccessible(true);
Object obj= con.newInstance("dsad",20);//创建一个对象,name=dsad,age=20
System.out.println(obj);
总结:不管调用方法或者是属性,都是get方法同时也都需要创建一个对象。
如果是属性,则是getField(属性名),然后调用field.set(属性名,值)方法设置值。
如果是方法,则是getMethod(方法名),然后调用method.invoke(对象名)。
但需要注意的是,如果是private属性或者private方法,需要getDeclaredField()或者getDeclaredMethod()方法,而且需要加setAccessible(true)方法。
同时,还有通过反射获取一些其他的东西:
获取运行时类的父类:
Class clazz=Person.class;
Class superClass= clazz.getSuperclass();
获取带有泛型的父类:
Class clazz=Person.class;
Type ty= clazz.getGenericSuperclass();
获取父类的泛型(在JDBC中有用):
Class clazz=Person.class;
//先获取带泛型的父类
Type ty= clazz.getGenericSuperclass();
//强制转型
ParameterizedType pt= (ParameterizedType)ty;
Type[] tp= pt.getActualTypeArguments();
//for(Type t:tp)
System.out.println("泛型是"+tp[0]);
Class clazz=Person.class;
Class[] cl=clazz.getInterfaces();//4.获取实现的接口
for(int i=0;i<cl.length;i++)
System.out.println(cl[i]);
Package pa=clazz.getPackage();//5.获取所在的包
System.out.println(pa);
Annotation[] an= clazz.getAnnotations(); //6.获取注解
for(int i=0;i<an.length;i++){
System.out.println(an[i]);
}
反射的应用:动态代理: