Java反射机制

Class类

1)在面向对象的世界里,万事万物皆对象。Java语言中,静态的成员、普通数据类型不是对象,其他的都是对象。而普通数据类型采用了包装类进行了弥补。我们写的每一个类,类实例化后可以得到一个对象。那么,类是不是对象呢?类是谁的对象呢?类是哪个类的对象呢?我们说,万事万物皆对象,我们写的每一个类,我们所提供的每一个类,也是对象,类是java.lang.Class类的实例对象。比如,写一个A类,它是Class类的实例对象。我写一个Student类,它也是Class类的实例对象。表述这个有一句话叫做“There is a class named Class”。现在有一个类,名字就叫做类。现在有一个类,它的名字就是Class。那么它的实例是什么呢?我们的每一个对象所属的类,就是它的实例。

2)这个对象到底如何表示呢?我们现在要写一个案例,就是要表示Class类的实例,

class Foo{}

这个Foo类就是一个对象。万事万物皆对象。谁的对象?java.lang.Class的实例对象。

那么,Foo的实例对象如何表示?

Foo foo1 = new Foo();

那么,我们知道Foo这个类也是一个对象,Class类的实例对象,如何表示呢?

我们可以看到,Class类的构造:

/*Constructor. Only the Java Virtual Machine creates Class objects.  */

private Class(){}

是私有的构造函数,在类的外部是不能访问的。我们没有办法来直接创建这个对象。

任何一个类都是Class的实例,这个实例对象有三种表示方式。

第一种方式:  实际也在告诉我们,任何一个类都有一个隐含的静态成员变量class

Class c1 = Foo.class;

第二种表达方式: 已经知道该类的对象通过getClass方法

Class c2 = foo1.getClass();

注意,这点很容易混淆。foo1代表的是Foo的实例对象,c1,c2代表的是Class的实例对象,但这个对象本身说明Foo这个类是一个实例对象。官网上的说明为c1,c2表示了Foo类的类类型(class type)。Foo类的类类型指的就是,Foo这个类本身它就是一个对象,哪个类的对象?Class类的对象。我们说类类型指的是本身自己就是一个Class类的实例。但是Foo这个类本身也有实例对象,那就是Foo这个类的实例对象。这就和Foo类的类类型进行分开了。类类型指的是万事万物皆对象,类也是一个对象。这个对象,我们把它称之为类类型。

c1 == c2 //true

不管c1  or  c2都代表了Foo类的类类型,一个类只可能是Class类的一个实例对象。

如果知道类名的话,可以采用.class,如果知道对象的话可以采用getClass()

第三种表达方式: 

Class c3 = null;

try{

   c3 = Class.forName("com.reflext.Foo");  //这里是类的全称,包含了package在内

} catch (ClassNotFoundException e) {

   e.printStackTrace();

}

c2 == c3 // true

以上把Class的概念弄清楚了。

我们完全可以通过类的类型创建该类的对象实例。也就是通过c1 or c2 or c3创建Foo的实例

这个c1如果是A类的类类型,那么创建出来的就是A类的实例对象,如果是B类的类类型,那么创建出来的就是B类的实例对象。现在它是Foo类的类类型,那么创建出来的就是Foo类的实例对象。

所以这个地方需要做强制类型转换。

Foo foo = (Foo)c1.newInstance();  //需要有无参数的构造方法

这里提个疑问,为什么必须要进行强制转型?来看看以下测试:

c1.newInstance() instanceof Foo; //false

c1.newInstance() instanceof Object; //true

显然,Class类的newInstance方法通过调用无参构造,最终返回了一个Object类型的对象。这里很容易和多态的概念弄混淆,让人以为是实例化的过程中创造了Foo对象并进行了Object类型的向上转型,实际则不然,至少不是真正意义上的多态,因为第一个测试返回的结果是false,如若真的是在Foo的基础上进行了向上转型,则测试结果应为true才对。但是,第二个表达式的测试结果又为true,可以对一个Object实例对象进行向下转型,从这角度来看,c1.newInstance() 似乎又具备多态的某些特性。因此,为了理解上的方便,这里我自创一个名词——“伪多态”,我把c1.newInstance()所得的结果称为伪多态的Object实例创建。这就好比前面所提到的万物皆对象,Foo类是Class类的实例一样,实际上,在语法的层面看来,Foo并非是Class的实例对象,但是可以从这样一个层面来进行综合的理解与诠释——Class最终可以通过Foo.class这个联姻得到Foo相关的类型的实例。我们可以把Foo.class理解为Foo这个class所编译之后形成的字节码文件Foo.class文件。这样一来,这个实体的字节码文件,既代表着Foo类的存在意义,同时又代表着Class类的实例,与此同时,Foo.class还可以用来创建新的Foo类实例。

3)动态加载类

Class.forName("类的全称")

   |- 不仅表示了类的类型,还代表了动态加载类

   |- 一定要区分编译,运行

   |- 编译时刻加载类是静态加载类,运行时刻加载类是动态加载类

class Office {
    public static void main(String[] args) {
    if("Word".equals(arg[0])){
      Word w = new Word();
      w.strat();
    }
    if("Excel".equals(arg[0])){
      Excel e = new Excel();
      e.strat();
    }
  }
}

  如果编译这个程序,会发现报错,提示两个类和两个方法找不到。如果再写一个Word类并编译,那么回头编译Office,仍然会提示2个错误,Excel类和run方法找不到。这是因为:new创建对象 是静态加载类,在编译时刻就需要加载所有的可能使用到的类。即便不使用Excel类,即便Word类已经存在,仍然无法使用Office程序。那么在实际应用中,我们当然希望Word存在Word就能够使用,而现在Word虽然存在,却因为Excel不存在,造成了Word无法使用。如果将来有100个功能,只要有一个功能有问题,那么就会造成另外的99个功能不能使用。显然,这种情形不是我们所希望看到的。我们所希望看到的功能是,你用哪个就加载哪个,不用不加载。所以我们希望在运行时进行加载,通过动态加载类,可以解决该问题。以后功能性的类,尽量使用动态加载,而不是使用静态加载。

public class OfficeBetter {
    public static void main(String[] args) {
    try {
		//动态加载类,在运行时才会报错,编译时不会去检查类是否存在
		Class c = Class.forName("args[0]");  
		//通过类类型,创建该类对象
		OfficeAble oa = (OfficeAble)c.newInstance();
		oa.start();
            } catch(Exception e){
		e.printStackTrace();
	}
  }
}        

interface OfficeAble{
	public void start();
}

class Word implements OfficeAble{
	public void start(){
		System.out.println("Word....starts...");
	}
}

  如果需要Excel功能,那么找一个新的程序员,写一个Excel程序即可,不需要修改使用的类,只需要动态加载新的功能。类似于杀毒坂本的更新。它不需要重新编译。

 4)基本的数据类型 void关键字都存在类类型

public class ClassDemo02 {
	public static void main(String[] args) {

		Class c1 = int.class;     //int的类类型
		Class c2 = String.class;  //String类的类类型  String类字节码(自己发明的)
		Class c3 = double.class;
		Class c4 = Double.class;
		Class c5 = void.class;
		
		System.out.println(c1.getName());
		System.out.println(c2.getName());
		System.out.println(c2.getSimpleName()); //不包含包名的类的名称
		System.out.println(c3.getName());
		System.out.println(c4.getName());
		System.out.println(c5.getName());
	}
}

  输出结果:

int
java.lang.String
String
double
java.lang.Double
void

5)Class类的基本API操作

public final native Class<?> getClass();

native声明的本地方法,JNI是专门用来做本地方法的。这个方法由Java俩声明,用C语言来实现,然后在Java里面去调用。我们无需关心底层的实现过程,只用知道传入的是Object类型,那么最终得到的就是Object类的类类型。传入的是Object的子类,那么最终得到的就是Object子类的类类型。

  

方法的反射

1)如何获取某个方法

   方法的名称和方法的参数列表才能唯一决定某个方法

2)方法反射的操作

  method.invoke(对象,参数列表)

ClassUtil.java

package com.reflect;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;

public class ClassUtil {
	/**
	 * 打印类的信息,包括类的成员函数、成员变量(只获取成员函数)
	 * @param obj 该对象所属的类信息
	 */
	public static void printClassMessage(Object obj){
		//要获取类的信息 首先要获取类类型
		Class c = obj.getClass();//传递的是哪个子类的对象 c就是该子类的类类型
		//获取类的名称
		System.out.println("类的名称是"+c.getName());
		/*
		 * Method类:方法对象
		 * 一个成员方法就是一个Method对象
		 * getMethod()方法获取的是所有的public的函数,包括父类继承而来的
		 * getDeclaredMethods()获取的是所有该类自己声明的方法,不问访问权限
		 */
		Method[] ms =c.getMethods();//c.getDeclaredMethods();
		for(int i=0;i < ms.length; i++){
			//得到方法的返回值类型的类类型,如果方法返回的是int类型,得到的就是int.class
			Class returnType = ms[i].getReturnType();
			System.out.print(returnType.getName() + " ");
			//得到方法的名称
			System.out.print(ms[i].getName() +"(");
			/*获取参数类型-->得到的是参数列表的类型的类类型。
			 * 如果参数列表中是(int,int)
			 * 得到的就是[int.class,int.class]
			 */
			Class[] paramTypes = ms[i].getParameterTypes();
			for(Class class1:paramTypes){
				System.out.print(class1.getName()+",");
			}
			System.out.println(")");
		}
		
	}

	/**
	 * 获取成员变量的信息
	 * @param obj
	 */
	public static void printFileldMessage(Object obj) {
		Class c = obj.getClass();
		/*
		 * 成员变量也是对象
		 * java.lang.reflect.Field
		 * Field类封装了关于成员变量的操作
		 * getFields()方法获取的是所有public的成员变量的信息
		 * getDeclaredFields()方法获取的是该类自己声明的成员变量的信息
		 */
//		Field[] fs = c.getFields();
		Field[] fs = c.getDeclaredFields();
		System.out.println("成员变量信息:");
		for(Field field:fs){
			//得到成员变量的类型的类类型,如实String,则最终得到的是String.class
			Class fieldType = field.getType();
			String typeName = fieldType.getName();
			//得到成员变量的名称
			String fieldName = field.getName();
			System.out.println(typeName + " " + fieldName);
		}
	}
	
	/**
	 * 打印对象的构造函数的信息
	 * @param obj
	 */
	public static void printConMessage(Object obj){
		Class c = obj.getClass();
		/*
		 * 构造函数也是此昂
		 * java.lang.reflect.Constructor中封装了构造函数的信息
		 * getConstructors获取所有的public的构造函数
		 * getDeclaredConstructors得到所有的额构造函数,自己声明的
		 */ 
//		Constructor[] cs = c.getConstructors();
		Constructor[] cs = c.getDeclaredConstructors();
		for(Constructor constructor:cs){
			System.out.print(constructor.getName()+"(");
			//获取构造函数的参数列表--->得到的是参数列表的类类型
			Class[] paramTypes = constructor.getParameterTypes();
			for(Class class1:paramTypes){
				System.out.print(class1.getName()+",");
			}
			System.out.println(")");
		}
	}
}

  

ClassDemo3.java

package com.reflect;

public class ClassDemo3 {

	public static void main(String[] args) {
		String s = "hello";
		ClassUtil.printClassMessage(s);

		Integer n1 = 1;
		ClassUtil.printClassMessage(n1);
	}
}

  

ClassDemo4.java

package com.reflect;

public class ClassDemo4 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ClassUtil.printFileldMessage("hello");
		System.out.println("=====================");
		ClassUtil.printFileldMessage(new Integer(1));
	}

}

  

ClassDemo5.java

package com.reflect;

public class ClassDemo5 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ClassUtil.printConMessage("hello");
		ClassUtil.printConMessage(new Integer(1));
	}
}

  

 MethodDemo1.java

package com.reflect;
import java.lang.reflect.*;

public class MethodDemo1 {

	public static void main(String[] args) {
		/*
		 * 要获取print(int, int)方法  
		 * 1.要获取一个方法就是获取类的信息,
		 * 获取类的信息首先要获取类的类型
		 */
		A a1 = new A();
		Class c = a1.getClass();
		/*
		 * 2.获取方法  名称和参数列表决定
		 * c.getMethod(name,parameterTypes)获取的是public的方法
		 * c.getDeclaredMethod(name,parameterTypes)自己声明的方法
		 */	
		try {
//			Method m = c.getMethod("print",new Class[]{int.class, int.class});
			Method m = c.getMethod("print",int.class, int.class);//第二个参数是可变参数,写成数组形式或者逐个列出参数都是对的
		
			/*
			 *方法的反射操作
			 *a1.print(10,20);方法的反射操作是用m对象来进行方法调用 和 a1.print调用的效果完全相同
			 */
			//方法如果没有返回值,返回null,否则,返回具体的返回值,需要用Object做强制类型转换
			Object o = m.invoke(a1, new Object[]{10,20});//类似于js的call和apply方法
//			Object o = m.invoke(a1, 10,20);//有几个参数,就传几个参数,这样也可以
			System.out.println("=========================");
			//获取方法print(String,String)
			Method m1 = c.getMethod("print", String.class,String.class);
			//用方法进行反射操作
			//a1.print("hello","WORLD")
			o = m1.invoke(a1, "hello", "WORLD");
			System.out.println("=========================");
//			Method m2 = c.getMethod("print", new Class[]{});
			Method m2 = c.getMethod("print");
//			m2.invoke(a1, new Object[]{});
			m2.invoke(a1);//参数没有就不写,这样也行
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

}
class A{
	public void print(){
		System.out.println("hello,world");
	}
	public void print(int a, int b){
		System.out.println(a+b);
	}
	public void print(String a, String b){
		System.out.println(a.toUpperCase()+","+b.toLowerCase());
	}
}

  

通过反射了解集合泛型的本质

  |- 通过Class,Method来认识泛型的本质

 MethodDemo2.java

import java.lang.reflect.Method;
import java.util.ArrayList;

public class MethodDemo4 {
	public static void main(String[] args) {
		ArrayList list = new ArrayList();
		
		ArrayList<String> list1 = new ArrayList<String>();
		list1.add("hello");
		//list1.add(20);错误的
		Class c1 = list.getClass();
		Class c2 = list1.getClass();
		System.out.println(c1 == c2);
		//反射的操作都是编译之后的操作
		
		/*
		 * c1==c2结果返回true说明编译之后集合的泛型是去泛型化的
		 * Java中集合的泛型,是防止错误输入的,只在编译阶段有效,
		 * 绕过编译就无效了
		 * 验证:我们可以通过方法的反射来操作,绕过编译
		 */
		try {
			Method m = c2.getMethod("add", Object.class);
			m.invoke(list1, 20);//绕过编译操作就绕过了泛型
			System.out.println(list1.size());
			System.out.println(list1);
			/*for (String string : list1) {
				System.out.println(string);
			}*///现在不能这样遍历
		} catch (Exception e) {
		  e.printStackTrace();
		}
	}

}

  

String.class 的类型是 Class<String>

String类的原型是String.class,String.class的原型是Class。定义一个String对象:String str = new String("hello")。四者的关系为:

==================================

         类                 |            对象

-------------------------------------------------------------

       Class               |          String.class

--------------------------------------------------------------

   String.class          ?           String   

--------------------------------------------------------------

     String                |            str

==================================

红色的对应关系值得注意,这里其实只能在理解上可以这么认为:通过String.class可以得到String的实例;在实现上,需要借用Class类的方法得到String对象str,跨越有点大,是一个转折点。

String.class instanceof Class   //true

str instanceof String   //true

String.class.newInstance() instanceof String  //true

String instanceof String.class   //error

String instanceof Class   //error

可以看到,String.class既是Class的一个实例对象,同时也属于String的类类型。正所谓一切皆对象,至于Class,实际上也是Class.class的类。

Class<?>中的?且为通配符,可以为任意类型。

 Type Parameters:<T> the type of the class modeled by this Class object. For example, the type of String.class is Class<String>. Use Class<?> if the class being modeled is unknown.

 ?通配符在泛型的应用中除了可以用来表示上限,下限外,剩下的就只能用在接收的情形了,也就是接收不确定的情形下可以使用。如果接收是确定的那么不能使用。当然,所有的泛型所接收的参数都是对象。

public Constructor<?>[] getConstructors()
                                 throws SecurityException
Returns an array containing Constructor objects reflecting all the public constructors of the class represented by this Class object. An array of length 0 is returned if the class has no public constructors, or if the class is an array class, or if the class reflects a primitive type or void. Note that while this method returns an array of Constructor<T> objects (that is an array of constructors from this class), the return type of this method is Constructor<?>[] and not Constructor<T>[] as might be expected. This less informative return type is necessary since after being returned from this method, the array could be modified to hold Constructor objects for different classes, which would violate the type guarantees ofConstructor<T>[].

ArrayList<?> list1 = new ArrayList<?>();

ArrayList<?> list1 = new ArrayList<String>();

以上两种使用方法都是错误的。

Info<String> i = new Info<String>();

public static void fun(Info<?> temp){...}

以上这种使用方法是正确的。

原文地址:https://www.cnblogs.com/ioveNature/p/6791408.html