java反射

动态语言:程序运行时,可以改变程序结构或变量类型,典型语言:python、ruby、javascript。

c、c++和java并非动态语言,但是java有一定的动态性,可以通过反射机制、字节码操作获得类似动态语言的特性。

反射机制:

-可以于运行时加载、探知、使用编译期间完全未知的类;

-程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个已加载的类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性;

Class c = Class.forName("com.test.User");

-加载完类之后,在堆内存中,就产生一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构,这个对象就如同一面镜子,可以透过它看到类的结构,故形象的称之为:反射

jdk中关于Class对象的说明:

Instances of the class Class represent classes and interfaces in a running Java application. An enum is a kind of class and an annotation is a kind of interface. Every array also belongs to a class that is reflected as a Class object that is shared by all arrays with the same element type and number of dimensions. The primitive Java types (boolean, byte, char, short, int, long, float, and double), and the keyword void are also represented as Class objects. 

Class has no public constructor. Instead Class objects are constructed automatically by the Java Virtual Machine as classes are loaded and by calls to the defineClass method in the class loader. 

获取Class的三种方式:

1.通过Class类的静态方法Class.forName("路径");获得

2.通过类名.class的方式获取,如获取String的Class对象可以这样:Class c =String.class

3.通过类的实例.getClass()方法来获得,如获取String的Class对象:Class c = "abc".getClass();

另外关于更多的Class知识点如下程序所示

package cn.reflection.test;

/**
 * 测试java.lang.Classs对象的获取方式 
 */
@SuppressWarnings("all")
public class Demo {
	public static void main(String[] args) {
		String path = "com.bean.User";
		try {
			Class clazz = Class.forName(path);
			//Class对象表示一个类被加载后,JVM会创建一个对应该类的Class对象,类的整个结构信息就存放在对应的Class对象中
			//这个Class对象就像一个镜子一样,通过这个镜子我们可以看到这个类的全部信息
			System.out.println(clazz.hashCode());
			
			Class clazz2 = Class.forName(path);
			System.out.println(clazz2.hashCode());
			
			System.out.println("===============");
			Class strClass = String.class;
			System.out.println(strClass.hashCode());
			Class strClass2 = path.getClass();
			System.out.println(strClass2.hashCode());
			
			System.out.println("===============");
			int[] arr = new int[10];
			System.out.println(arr.getClass().hashCode());
			int[] arr2 = new int[27];
			System.out.println(arr2.getClass().hashCode());
			int[][] arr4 = new int[10][10];
			System.out.println(arr4.getClass().hashCode());
			double[] arr8 = new double[27];
			System.out.println(arr8.getClass().hashCode());
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

可以获知每个类只有一个Class对象,对于数组只要维数和类型相同不论大小均为相同的Class。

通过反射获取类的属性、方法和构造方法的方式:

package cn.reflection.test;

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

public class Demo2 {
	public static void main(String[] args) {
		String path = "com.bean.User";
		try {
			Class clazz = Class.forName(path);
			//获取类的名字
			System.out.println(clazz.getName());			//获得包名+类名
			System.out.println(clazz.getSimpleName());		//获得类名
			//获得属性信息
			//Field[] fields = clazz.getFields();			//只获得声明为public的属性
			Field[] fields = clazz.getDeclaredFields(); 	//可以获得声明的所有属性
			System.out.println(fields.length);
			//Field f = clazz.getDeclaredField("uname");
			//System.out.println(f);
			for(Field temp:fields){
				System.out.println("属性: "+temp);
			}
			//获得方法信息
			Method[] methods = clazz.getDeclaredMethods();
			Method m = clazz.getDeclaredMethod("getUname", null);
			Method m2 = clazz.getDeclaredMethod("setUname", String.class);
			for(Method mtemp:methods){
				System.out.println("方法: "+mtemp);
			}
			//获得构造器信息
			Constructor[] constructors = clazz.getDeclaredConstructors();
			Constructor con = clazz.getDeclaredConstructor(null);
			System.out.println("获得无参构造器: "+con);
			Constructor con2 = clazz.getDeclaredConstructor(int.class,int.class,String.class);
			System.out.println("获得有参构造器: "+con2);
			for(Constructor ctemp:constructors){
				System.out.println("构造器: "+ctemp);
			}
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

通过运行可以发现,但凡是调用Class实例的getDeclared...()方法一般可以获得相应的类的所有声明的属性或是方法,而调用get...()则只能获得相应的类的声明为public的属性或是方法,由于方法可以重载,故而获取方法时要传入该方法所需的参数,从而保证获取的方法在该类中是唯一的。

通过反射对于属性和方法进行操作:

package cn.reflection.test;

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

import com.bean.User;

public class Demo4 {
	public static void main(String[] args) {
		String path = "com.bean.User";
		try {
			Class clazz = Class.forName(path);
			
			//通过反射API调用构造方法,构造对象
			User u = (User)clazz.newInstance();			//其实是调用了User的无参构造方法
			System.out.println(u);
			//通过反射API调用含参数的构造器
			Constructor<User> c = clazz.getDeclaredConstructor(int.class,int.class,String.class);
			User u2 = c.newInstance(10,24,"We_lxk");
			System.out.println(u2.getUname()+"--"+u2.getId()+"--"+u2.getAge());
			
			//通过反射API调用普通方法
			User u5 = new User();
			Method m = clazz.getDeclaredMethod("setUname", String.class);
			m.invoke(u5, "We_hx");
			System.out.println(u5.getUname());

			//通过反射API操作属性
			Field f = clazz.getDeclaredField("uname");
			f.setAccessible(true);			//这个属性不用做安全检查,可以直接访问
			f.set(u, "haha");				//通过反射直接写属性的值
			System.out.println(f.get(u));	//通过反射直接读属性的值
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException 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();
		} catch (NoSuchFieldException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

可以通过反射调用相应的类的方法,首先需要获得类的Class对象;然后获取相应的方法,获取方法时一般需要传入方法名和方法参数类型作为参数;最后可以调用获取的方法,调用时需要将类的实例作为参数传递给invoke方法,以确定是调用那个对象的方法。

下面是通过反射调用方法和普通方法调用的执行时间的对比:

package cn.reflection.test;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import com.bean.User;

@SuppressWarnings("all")
public class Demo6 {
	public static void test(){
		User u = new User();
		long startTime = System.currentTimeMillis();
		for(int i = 0;i < 10000000L; i++){
			u.getUname();
		}
		long endTime = System.currentTimeMillis();
		System.out.println("执行时间为:"+(endTime-startTime)+"ms");
	}
	public static void test2() throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
		User u = new User();
		Class clazz = u.getClass();
		Method m = clazz.getDeclaredMethod("getUname", null);
		long startTime = System.currentTimeMillis();
		for(int i = 0;i < 10000000L; i++){
			m.invoke(u, null);
		}
		long endTime = System.currentTimeMillis();
		System.out.println("执行安全检查的反射方法执行时间:"+(endTime-startTime)+"ms");
	}
	public static void test6() throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
		User u = new User();
		Class clazz = u.getClass();
		Method m = clazz.getDeclaredMethod("getUname", null);
		m.setAccessible(true);
		long startTime = System.currentTimeMillis();
		for(int i = 0;i < 10000000L; i++){
			m.invoke(u, null);
		}
		
		long endTime = System.currentTimeMillis();
		System.out.println("不执行安全检查的反射方法执行时间:"+(endTime-startTime)+"ms");
	}
	public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		test();
		test2();
		test6();
	}
}

test()是普通的方法调用,test2()是执行安全检查的反射方法调用,test6()是不执行安全检查的反射方法调用。

通过运行可以得知test2()方法执行最慢,时间约为test()方法的30倍;test()方法的执行时间最快,执行时间为test6()的1/4

反射和泛型

 java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的麻烦,一旦编译完成,所有和泛型相关的类型全部擦除。

为了通过反射操作这些类型,使符合开发的需要,java增加了ParameterizedType、GenericArrayType、TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型,但是又和原始类型齐名的类型。

泛型运用反射的具体例子如下:

package cn.reflection.test;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

import com.bean.User;

@SuppressWarnings("all")
public class Demo8 {
	public void test(Map<String,User> map,List<User> list){
		System.out.println("Demo8.test()");
	}
	
	public Map<Integer,User> test2(){
		System.out.println("Demo8.test2()");
		return null;
	}
	public static void main(String[] args) {
		try {
			//获得指定方法的
			Method m = Demo8.class.getMethod("test", Map.class,List.class);
			Type[] t = m.getGenericParameterTypes();
			for(Type paramType : t){
				System.out.println("#"+paramType);
				if(paramType instanceof ParameterizedType){
					Type[] genericTypes = ((ParameterizedType)paramType).getActualTypeArguments();
					for(Type genericType : genericTypes){
						System.out.println("泛型类型:"+genericType);
					}
				}
			}
			
			
			Method m2 = Demo8.class.getMethod("test2", null);
			Type returnType = m2.getGenericReturnType();
			System.out.println("返回类型:"+returnType);
			if(returnType instanceof ParameterizedType){
				Type[] genericTypes = ((ParameterizedType)returnType).getActualTypeArguments();
				
				for(Type genericType :genericTypes){
					System.out.println("返回值,泛型类型:"+genericType);
				}
			}
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

  

态度决定高度,细节决定成败,
原文地址:https://www.cnblogs.com/lxk2010012997/p/5064713.html