Java中的反射学习

1. Class 类的使用
 1)在面向对象的世界里,万事万物皆对象(在Java中,静态的成员和普通数据类型类不是对象)
  类是不是对象呢? 是; 
  那类是谁的对象呢? 类是 java.lang.Class类的实例对象。
 2)这个类怎么表示呢?
 class Foo{}
 Foo这个类是Class类的一个实例对象;如何表示呢?
 任何一个类都是Class的实例对象,这个实例对象有3种表示方式:
 ① Class c1 = Foo.class;
  实际上告诉我们任何一个类都有一个隐含的静态成员class.
 ② 第2种表达方式,已知该类的对象通过getClass方法
  Foo foo1=new Foo();
  Class c2=foo1.getClass();
  c1、c2表示Foo类的类类型(class type);
  万事万物皆对象,类也是对象,是Class类的实例对象,这个对象我们称为该类的类类型。  
 ③ 第3种表示方式 
 Class c3=null;
 try{
  c3=Class.forName("packageName.Foo");
 }catch{}
 我们完全可以通过类的类类型创建该类的对象实例。
  Foo foo=(Foo)c3.newInstance();

2. 动态加载类
  1)Class.forName("类的全名")
   不仅表示了类的类类型,还代表了动态加载类;
   编译时记得加载的类是静态加载,运行时加载的类是动态加载。
  ① new 创建对象是静态加载类,在编译时刻就要加载所有的可能使用到的类
    World w=new World();
    通过动态加载类可以解决这个问题。
  ② 动态加载类,在运行时刻加载
      Class c=Class.forName(str);  
    然后通过定义一个标准(接口),用到哪个类就让那个类遵守这个标准(实现接口)就可以了。
    OfficeAble oa=(OfficeAble)c.newInstance(); //Word ,Execll都实现这个接口就可以动态加载了

3. 获取方法信息
  ① 字节码
  Class c1=int.class; // int的类类型 ,
  Class c2=String.class; //String类的类类型,俗称String 类字节码
  Class c3=void.class;  
  System.out.print(c1.getName());  //输出:int ,c2 -->java.lang.String
  System.out.print(c2.getSimpleName()); //不包含包名的
  基本数据类型  ,void都有字节码
  ② 获取方法信息
public class ClassUtils{
  //打印类的信息,包括类的成员函数、成员变量
	public static void printClassMessage(Object obj){
		//要获取类的信息 ,首先要获取类的类类型
		Class c=obj.getClass(); //传递的是哪个子类的对象
		//获取类的名称
		System.out.print("类的名称是:"+c.getName());
		/**
		* 万事万物皆对象,方法也是对象,是Method类的对象
		* 一个成员方法就是一个Method对象,getMethods()方法获取的是所有public的函数
		* getDeclaredMethods()获取的是所有该类自己声明的方法,不问访问权限
		**/
		Method[] ms=c.getMethods();
		for(int i=0;i<ms.length;i++){
			//得到方法的返回值类型的类类型
			Class returnType=ms[i].getReturnType();
			System.out.println(returnType.getName+" ");
			//得到方法的名称
			System.out.print(ms[i].getName()+"(");
			//获取参数类型 --->得到的是参数列表的类型的类类型
				Class[] paramTypes=ms[i].getParameterTypes();
				for(Class class1:paramTypes ){
					System.out.println(class1.getName+" ,");
				}
				System.out.println(")");
		}
	}
}
4. 获取成员变量构造函数信息
  ① 成员变量也是对象,是 java.lang.reflect.Field 类的对象,Field类封装了关于成员变量的操作,
  getFields()方法获的是所有的public 的成员变量(包含继承父类的)的信息;
  getDeclaredFilds() 获取的是该类自己声明的成员变量的信息,
Field[] fs=c.getDeclaredFields();
	for(Field field:fs){
		//得到成员变量的类型的类类型
		Class fieldType=field.getType();
		String typeName=fieldType.getName();
		//得到成员变量的名称
		String fieldName=field.getName();
	}
 ② 获取对象的构造函数信息 
public static void printConMessage(Object obj){
 	Class c=obj.getClass();
 	/***
 	* 构造函数也是对象
 	* java.lang.Constructor 中封装了构造函数的信息
 	* getConstructors获取所有的public的构造函数
 	* getDeclaredConstructors获取所有的构造函数
 	**/
 	Constructor[] cs=c.getDeclaredConstructors();
 	for(Constructor constructor:cs){
 		System.out.println(constructor.getName+"(");
 		Class[] paramTypes=constructor.getParameterTypes();
 		for(Class class1:paramTypes){
			System.out.println(class1.getName+",");
 		}
 		System.out.println(")");
 	}
 }
5. 方法的反射
  1)如何获取某个方法
     方法的名称和方法的参数列表才能唯一决定某个方法
  2)方法反射的操作
     method.invoke(对象,参数列表)  
public class A{
	public void print(int a,int b){
		System.out.println(a+b);
	}
	public void print(String a,String b){
		System.out.println(a+b);
	}
}
//① 要获取print(int,int)方法 -->1.要获取一个方法就是获取类信息,那首先要获取类的类类型
  A a1=new A();
  Class c=a1.getClass();
  //2.获取方法  名称和参数列表来决定,getMethod获取的是public的方法
  Method m=c.getMethod("print",new Class[]{int.class,int.class});
也可以这样写:
  Method m=c.getMethod("print",int.class,int.class);
 //方法的反射操作
 //a1.print(10,20);  方法的反射操作是用Method对象来进行方法调用,和用a1.print调用的 效果完全相同
 //方法如果没有返回值 返回null,有返回值就返回具体的返回值
 Object o= m.invoke(a1,new Object[]{10,20});  //以前是用对象来操作方法,反射就是用方法来操作对象
 也可以这样写:
  Object o= m.invoke(a1,10,20); 

6. 通过反射(Class,Method)了解集合泛型的本质  
ArrayList list=new ArrayList();  //能放任何类型
ArrayList<String> list1=new ArrayList<String>(); //只能放String类型
// list1.add(10); 是错误的
Class c1=list.getClass();
Class c2=list1.getClass();
System.out.println(c1==c2);  //结果为true
// 反射的操作都是编译之后的操作(运行时的操作)
c1==c2结果返回true,说明编译之后集合的泛型是去泛型化的
java中集合的泛型,是防止错误输入的,只在编译阶段有效,绕过编译就无效了
验证:可以通过方法的反射来操作,绕过编译
Method m=c2.getMethod("add",Object.class);
m.invoke(list1,100);  //绕过编译操作 就绕过了泛型
System.out.println(list1); 结果显示 int值100加入到list1中了

7. 为什么要用反射?
  ①基类对任何派生类一无所知,要想使用基类的方法获取派生类的类型等信息,就需要用到反射了。 
  ②很多场合(也许是为了通用性),要在运行期才能够决定需要调用的类型,那样就有必要用反射来取得类型的相关信息了。




猜你喜欢

转载自blog.csdn.net/ymangu/article/details/45066535