9.1 java反射

概念

      Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。(百度百科)
      反射机制指的是可以于运行时加载、探知、使用编译期间完全未知的类。已知一个类的地址及名称,就可以使用此类。
      反射原理:对象用来表示或封装一些数据。一个类被加载后,JVM会创建一个对应该类的Class对象,类的整个结构信息会放到对应的Class对象中。获取到class对应的Class实例(对象)后,就可以获取该class的所有信息。通过Class实例获取class信息的方法称为反射。
      一个类只会被加载一次,因此一个类的Class对象是唯一的,且只能由JVM创建。
      反射把java类中的各种结构(方法、属性、构造器、类名)映射成一个个的Java对象。利用反射技术可以对一个类进行解剖,反射是框架设计的灵魂。
      使用反射后,如果在编译时无法获取某个类,只通过类名就可使编译通过。
      花费我好多心思抄的一张图片。(读书人的事怎么能叫抄呢?)
p1

作用

  1. 动态加载类、动态获取类的信息(属性、方法、构造器等)
  2. 动态构造对象
  3. 动态调用类和对象的任意方法、构造器
  4. 动态调用和处理信息
  5. 获取泛型信息
  6. 处理注解

什么是动态?程序运行时,改变程序结构或变量类型。

获取Class对象的三种方法

  1. 实例对象.getClass();
  2. 类.class;
  3. Class.forName(path);

示例1:

import java.lang.reflect.InvocationTargetException;

/**
 * 反射:java类中的各种结构(方法、属性、构造器、类名)映射成一个个的Java对象
 * 1、获取Class对象
 *   共有三种方式,但最好使用class.forName("包名.类名");
 *   因为此方式不要求 在编译时存在类
 * 2、可以动态创建对象
 * @author dxt
 *
 */
public class Reflection {
	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException{
		//1. 三种方式获取Class对象
		//1.1 对象.getClass()
		Phone p = new Phone();
		Class clz = p.getClass();
		//1.2 类.class
		clz = Phone.class;
		//1.3 class.forName("包名.类名");
		clz = Class.forName("dxt.basic.Phone");
		
		//2. 动态创建对象
		Phone p2 = (Phone)clz.getConstructor(null).newInstance(null);
		System.out.println(p2);
	}
}

class Phone{
	public Phone(){
		
	}
}

示例2:

package dxt.test;
/**
 * 测试各种类型对应的java.lang.Class对象的获取方式
 * @author dxt
 *
 */
@SuppressWarnings("all")	//压制警告
public class Demo01 {
	public static void main(String[] args){
		String path = "dxt.test.bean.User";
		try {
			Class c = Class.forName(path);
			//对象用来表示或封装一些数据。一个类被加载后,JVM会创建一个对应该类的Class对象,类的整个结构信息会放到对应的Class对象中
			//通过这个Class对象,我们可以获取对应类的全部信息。
			System.out.println(c);
			System.out.println(c.hashCode());
			Class c2 = Class.forName(path);
			System.out.println(c2.hashCode());
			
			Class strClazz = String.class;
			Class strClazz2 = path.getClass();
			System.out.println(strClazz == strClazz2);
			
			Class intClass = int.class;
			
			//相同类型和维度的数组对应同一个Class对象
			int[] arr01 = new int[10];
			int[] arr02 = new int[20];
			System.out.println(arr01.getClass().hashCode());
			System.out.println(arr02.getClass().hashCode());
			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
}

反射获取类的信息

  1. 获取类的地址、名字、属性、方法、构造器等信息。

示例:

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

/**
 * 使用反射的API,获取类的信息(类的名字、属性、方法、构造器等)
 * @author dxt
 *
 */
public class Demo02 {
	public static void main(String[] args){
		String path = "dxt.test.bean.User";
		try {
			Class c1 = Class.forName(path);
			//1.1  获取报名+类名
			System.out.println(c1.getName());
			//1.2 获取类名
			System.out.println(c1.getSimpleName());
			
			//2.1 获取类中公开 属性数组
			Field[] fields = c1.getFields();	//只能获取public的属性
			System.out.println(fields.length);
			//2.2 获取所有的属性
			Field[] fields2 = c1.getDeclaredFields();
			System.out.println(fields2.length);
			//2.3 获取对应名字的属性
			Field f = c1.getDeclaredField("name");
			System.out.println(f);
			
			//3.1 获取方法信息
			Method[] methods = c1.getMethods();	//只能获取public方法
			Method[] methods2 = c1.getDeclaredMethods();	//获取所有方法
			Method m = c1.getDeclaredMethod("getName", null);
			//如果方法由参数,则必须传递参数类型对应的class对象
			Method m2 = c1.getDeclaredMethod("setId", int.class);
			System.out.println(m2);
			
			//4 获取构造器信息
			Constructor[] constructors = c1.getConstructors();	//public
			Constructor[] constructors2 = c1.getDeclaredConstructors();
			Constructor cscr = c1.getConstructor(null);	//无参构造器
			System.out.println(cscr);
			Constructor cscr2 = c1.getConstructor(int.class, String.class, int.class);
			System.out.println(cscr2);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

反射操作类的信息

  1. 操作类的属性、方法、构造器等信息。

示例:

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

import dxt.test.bean.User;

/**
 * 通过反射API动态的操作:构造器、方法、属性
 * @author dxt
 *
 */
public class Demo03 {
	public static void main(String[] args){
		String path = "dxt.test.bean.User";
		try {
			Class<User> c = (Class<User>)Class.forName(path);
			
			//通过动态调用 构造方法,构造对象
			//1.1 newInstance()直接构造对象,其实是调用了User的无参构造方法,User必须要有无参构造方法
			User u = c.newInstance();
			System.out.println(u);
			//1.2 先获取对应构造方法,在使用构造器的newInstance()构造对象
			Constructor<User> cscr = c.getDeclaredConstructor(int.class, String.class, int.class);
			User u2 = cscr.newInstance(1001, "dxt", 20);	//有参构造必须要传参数
			System.out.println(u2.getName());
			
			//2. 通过反射API调用普通方法
			User u3 = c.newInstance();
			//u3.setAge(20);	//直接调用
			Method m = c.getDeclaredMethod("setName", String.class);
			m.invoke(u3, "dxt001");	//通过反射调用
			System.out.println(u3.getName());
			
			//3. 通过反射操纵属性
			User u4 = c.newInstance();
			Field f = c.getDeclaredField("id");	//获取对应属性
			//设置可以访问private修饰的属性
			f.setAccessible(true);	//设置这个属性不用做安全检查,可以直接使用。
			f.set(u4, 2001);	//设置属性值
			int id = f.getInt(u4);	//获取属性值
			System.out.println(id);
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

反射机制性能问题

  1. 反射会使性能变差。反射调用耗时有可能是对象直接调用方法耗时的几十倍。
  2. setAccessible()
          启用和禁止安全检查的开关,值为 true 表示反射的对象在使用时应该取消Java语言访问检查;值为 false 表示反射的对象应该实施Java语言访问检查。
          禁止安全检查,会提高反射的运行速度。

反射操作泛型

  1. Java采用泛型擦除的机制来引入泛型。Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的麻烦。但是一旦编译完成,所有和泛型有关的类型全部擦除。即Class类中不包含与泛型有关的类型信息。
  2. 为了通过反射操作这些类型以迎合实际开发的需要,Java就新增了ParameterizedType、GenericArrayType、TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。

示例代码

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

import dxt.test.bean.User;

/**
 * 测试反射操作泛型
 * @author dxt
 *
 */
public class Demo04 {
	public void test01(Map<String, User> map, List<User> list){
		System.out.println("Demo04.test01()");
	}
	public Map<Integer,User> test02(){
		System.out.println("Demo04.test02()");
		return null;
	}
	
	public static void main(String[] args){
		try {
			//1. 获取指定方法参数的泛型
			Method m = Demo04.class.getMethod("test01", 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);
					}
				}
			}
			//2. 获取指定方法返回值泛型信息
			Method m2 = Demo04.class.getMethod("test02", null);
			Type returnType = m2.getGenericReturnType();	//获取带泛型的返回类型
			if(returnType instanceof ParameterizedType){
				Type[] genericTypes = ((ParameterizedType)returnType).getActualTypeArguments();
				for(Type genericType : genericTypes){
					System.out.println("返回值--泛型类型:" + genericType);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} 
	}
}

结果
p1

反射操作注解

内容链接

©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页