第一次听说java反射机制的时候,觉得很高大上,毕竟从名字上了就可以知道它的专业性,事实上java反射也确实很牛逼,当我开始正真学习它的时候,我的整个学习过程中整个人也是懵逼的,但是呢!这也更激起了我想征服它的欲望,随着我与它的不断“亲近”与“磨合”,慢慢的也有了点收获,虽然平时太忙,但我还是觉得得花点时间记录总结下!
我相信在java的学习过程中,大家肯定都听说过这么一句话“万物皆对象”。首先我先提个问题:既然在java面向对象的世界里,万物皆是对象,那么类是不是一个对象?是谁的对象?当然答案都是肯定的,类是对象,类是java.lang.Class类的实例对象。
那么类的实例对象要怎么表示?我们平常要实例化一个对象,基本都是new出来的,举个列子:A a = new A();那么a是不是就表示出来了,那么A也是一个实例对象,且是Class类的实例对象,那么,这要怎么表示呢?下面我通过小例子进行说明。我个人一直认为通过案例驱动学习是最好的学习方式,先要看到效果,你才好去学习它,了解他。它有三种表现方式:
package reflect;
public class ReflectDemo {
public static void main(String[] args) {
A a = new A();
Class c1 = A.class; //第一种表示方式
Class c2 = a.getClass(); //第二种表示方式
Class c3=null;
try {
c3 = Class.forName("reflect.A"); //第三种表示方式
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println((c1==c2)+","+(c2==c3));
try {
A temp = (A)c1.newInstance(); //A必须要有要有无参的构造方法
temp.print();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
class A{
public void print(){
System.out.println("我是A");
}
}
输出结果为:
首先我们先来学习怎样通过一个类的类类型来获取一个类中的所有成员变量、成员方法以及类的构造函数。
先来获取所有的成员变量:
public class ClassUtils {
public static void main(String[] args) {
String s="我是String";
ClassUtils cu = new ClassUtils();
cu.getClassVariables(s);
}
/**
* 得到类的所有成员变量信息
* @param obj:需要得到的信息类对象
*/
public void getClassVariables(Object obj){
Class c = obj.getClass();
//Field[] fs = c.getFields();获取的是所有的public成员变量的信息
Field[] fs = c.getDeclaredFields(); //获得该类自己声明的所有变量信息,返回的结果是Filed对象的数组
for(Field field:fs){
//得到成员变量的类型的类类型
Class variableType = field.getType(); //得到该字段的声明类型,返回一个Class对象
String typeName = variableType.getName();
//得到成员变量的名称
String variableName = field.getName();
System.out.println(typeName+" "+variableName);
}
}
}
输出结果:
再来获取所有的成员函数:
package reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ClassUtils {
public static void main(String[] args) {
String s="我是String";
ClassUtils cu = new ClassUtils();
cu.getClassMethod(s);
}
/**
* 得到类的所有成员函数
* @param obj:需要得到的信息类对象
*/
public void getClassMethod(Object obj){
//想要要获取类的信息,首先要获取类的类类型
Class c=obj.getClass();
String str=null;
System.out.println("类的名称是:"+c.getName()); //先获取类的名称
//Method[] ms = c.getDeclaredMethods(); //获取的是该类自己声明的方法 注意:不看访问权限
Method[] ms = c.getMethods(); //获取所有的public的函数,也包括父类继承而来的方法 返回的结果是Method对象的数组
for(int i=0;i
输出结果(限于篇幅,仅贴出部分):
最后我们获取类的构造函数:
package reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ClassUtils {
public static void main(String[] args) {
String s="我是String";
ClassUtils cu = new ClassUtils();
cu.getClassConstructor(s);
}
/**
* 得到类的构造函数信息
* @param obj:需要得到的信息类对象
*/
public void getClassConstructor(Object obj){
Class c = obj.getClass();
//eConstructor[] cs = c.getDeclaredConstructors(); //得到所有的构造函数
Constructor[] cs = c.getConstructors(); //获取的是所有的public的构造函数
for(Constructor constructor :cs){
System.out.print(constructor.getName()+"(");
//获取构造函数的参数列表,得到的是参数列表类型的类类型
Class[] paramType = constructor.getParameterTypes();
for(Class c1:paramType){
System.out.print(c1.getName()+",");
}
System.out.println(")");
}
}
}
输出结果:
下面再来了解下发射的基本操作:
public class MethodDemo{
public static void main(String[] args) {
Dw dw = new Dw();
Class c = dw.getClass();
try {
//方法的反射操作
//获取方法print(int,int)
Method m = c.getMethod("print", new Class[]{int.class,int.class});
Object obj = m.invoke(dw,new Object[]{10,20});
System.out.println("----------------");
//获取方法print(String,String)
Method m1 = c.getMethod("print",String.class,String.class);
obj = m1.invoke(dw, "java","反射");
System.out.println("----------------");
//获取方法print();
Method m2 = c.getMethod("print", new Class[]{});
m2.invoke(dw, new Object[]{});
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Dw{
public void print(){
System.out.println("java反射");
}
public void print(String a,String b){
System.out.println(a+","+b);
}
public void print(int a,int b){
System.out.println(a+b);
}
}
输出结果:
最后讲一下通过反射来了解泛型的本质。泛型通俗的讲就是用来固定输入的值为同一类型。输入不同类型的值,在编译的过程中就会报错。但是通过反射我们就可以绕过编译期,直接进入运行期。大家要记住:Class类,Field类,Method类,Constructor类都是在运行期执行的。下面通过一个例子讲下
ArrayList list = new ArrayList();
ArrayList list1 = new ArrayList();
这里我名定义两个ArrayList,分别是list和list1,我们先来添加数据进去,由于list是什么类型都可以装,我们就不赋值了,給list1添加数据
list1.add("你好");
list1.add(20);
我们会发现,后台会报错,因为list1 通过泛型固定了能接受值的类型,而这里输入的值为20是个整型,明显是加不进去的。但是通过泛型我们就可以加进去,首先来看下list和list1是不是相等的
Class c1 = list.getClass();
Class c2 = list1.getClass();
System.out.print(c1 == c2);
这里我们会发现输出的结果是true,说明结果是相等的。这时我们就可以通过反射绕过编译期,在运行期将值加进去。
try { //由于c1和c2是相等的,这里我们用c1还是c2就无所谓了
Method m = c2.getMethod("add", Object.class); //得到add方法,将值注入进去
m.invoke(list1, 20);//绕过编译操作也就绕过了泛型
System.out.println(list1.size());
System.out.println(list1);
} catch (Exception e) {
e.printStackTrace();
}
输出结果:
从输出结果我们可以看出,list1的长度是2,说明值加进去了,结果也能输出。但是在前面list1还是加了泛型的,为什么能将整型值加进去呢?通过c1==c2结果返回true就说明了编译之后集合的泛型是去泛型化的,就是说泛型只在编译期有效,到了运行期,泛型就失效了!所以我们的整型值是能加进去的。但是还要注意一点,这时候你是不能通过循环输出list1里面的值的,会提示类型转换错误,这是需要注意的。