Java动态性之反射机制(Reflection)

认识反射机制,并且会使用反射机制

一、什么是动态性

– 动态语言指的是程序运行时,可以改变程序结构。典型的是Python、javascript等,而Java并不是动态语言,但是Java有一定的动态性,可以利用反射机制,字节码操作获得类似于动态语言的特性

二、反射是什么

反射机制:指的是可以在运行时加载、使用编译期间完全未知的类。反射可以在运行时根据指定的类名获得类的信息

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

关于Class对象,API解释如下:

Class类的实例表示正在运行的Java应用程序中的类和接口。 枚举是一种类,一个注释是一种界面。 每个数组也属于一个类,它被反映为具有相同元素类型和维数的所有数组共享的Class对象。 原始Java类型( boolean , byte , char , short , int , long , float ,和double ),以及关键字void也表示为Class对象。

java.lang.Class类用来表示Java中的类型本身,用来存储运行类。Class类的对象包含了某个被加载的类的结构,一个被加载的类对应一个Class对象。
当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用时,JVM便自动产生一个Class对象。

三、为什么要使用反射

程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个已加载的类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性。
反射机制让程序变得更加灵活

静态编译:在编译时确定类型,绑定对象,即通过。
动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,降低类之间的藕合性。

四、反射在哪里用

反射十分灵活,可以用到的地方很多。常见的用法如下:
动态加载类
动态获取类的结构信息
动态创建对象
动态调用类的任意方法
动态调用和处理属性
利用反射获取泛型信息
利用反射处理注解等

五、反射怎么用

首先在com.xyj.entity包中,我们有Emp实体类如下:

package com.xyj.entity;

public class Emp {
	
	private int id;
	private int age;
	private String name;
	
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
	public Emp(int id, int age, String name) {
		this.id = id;
		this.age = age;
		this.name = name;
	}
	public Emp() {
		
	}
	
}

1. 动态加载类

package com.xyj.reflection.test;

import com.xyj.entity.Emp;

/**
 * 测试各种类型对应的java.lang.Class对象的获取方式
 */
@SuppressWarnings("all")
public class Demo01 {
	public static void main(String[] args) {
		//实体类的位置
		String path="com.xyj.entity.Emp";
		try {
			/*
			 Class类的实例表示正在运行的Java应用程序中的类和接口。 枚举是一种类,
			 一个注释是一种界面。 每个数组也属于一个类,它被反映为具有相同元素类型和维数的所有数组共享的Class对象。
			  原始Java类型( boolean , byte , char , short , int , long , float ,和double ),
			  以及关键字void也表示为Class对象。 
			 */
			 
			/*
			 *对象是表示或封装一些数据 一个类被加载后,JVM会创建一个对应该类的Class对象,
			 * 类的整个结构信息会被放到对应的Class中
			 * 这个Class对象就像一面镜子一样,通过这面镜子我可以拿到对应类的全部信息
			 */
			//1、获取运行类的方式
			//1.1 Class.forName("包名.类名");
			Class class1 = Class.forName(path);
			//1.2  类名.class
			Class class2=Emp.class;
			//1.3 对象名.getClass()
			Class class3 = new Emp().getClass();
			//一个类只对应一个Class对象
			System.out.println(class1==class2 && class2==class3 && class1==class3);
			
			Class strClass1=String.class;
			Class strClass2="str".getClass();
			
			Class intClass=int.class;
			
			//相同维数相同类型的两个数组属于同一个Class对象
			int[] arr01=new int[10];
			int[] arr02=new int[30];
			int[][] arr03=new int[3][6];
			double[] arr04=new double[10];
			
			System.out.println(arr01.getClass().hashCode());//705927765
			System.out.println(arr02.getClass().hashCode());//705927765
			System.out.println(arr03.getClass().hashCode());//366712642
			System.out.println(arr04.getClass().hashCode());//1829164700
			
			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		
	}
	
}

2. 动态获取类的结构信息

package com.xyj.reflection.test;

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

import com.xyj.entity.Emp;

/**
 * 利用反射获取类中的信息(属性 方法  构造器等)
 */
@SuppressWarnings("all")
public class Demo02 {
	public static void main(String[] args) {
		
		try {
			Class<?> class1 = Class.forName("com.xyj.entity.Emp");
			
			//1、获取类的名字
			System.out.println("获取包名+类名 :"+class1.getName());
			System.out.println("仅获取类名 :"+class1.getSimpleName());
			
			
			//2、获取属性的相关信息
			//2.1 获取属性
			//Field field = class1.getField("name");//只能获取public的属性
			Field field2 = class1.getDeclaredField("name");//可以获取任意指定的属性
			Field[] fields = class1.getDeclaredFields();
			for (Field f : fields) {
				System.out.println("属性 :"+f);
			}
			
			//2.2 获取属性的类型
			System.out.println(field2.getType());
			System.out.println(field2.getType().getName());
			//2.3 获取属性的访问修饰符
			int modifiers = field2.getModifiers();
			System.out.println(Modifier.toString(modifiers));
			
			
			//3、获取方法信息
			//获取所有的公有方法(包括父类的)
			Method[] methods2 = class1.getMethods();
			for (Method method : methods2) {
				System.out.println("方法1:"+method);
			}
			//获取所有的自己声明的所有方法(除构造方法)
			Method[] methods = class1.getDeclaredMethods();
			for (Method method : methods) {
				System.out.println("方法 2:"+method);
			}
			
			//获取指定的无参方法
			Method method1 = class1.getDeclaredMethod("getName",null);
			//获取指定的带参方法  必须传递形参类型对应的Class对象
			class1.getDeclaredMethod("setName",String.class);
			
			
			//4、获取构造器信息
			Constructor<?>[] constructors = class1.getDeclaredConstructors();
			for (Constructor<?> constructor : constructors) {
				System.out.println("构造方法 :"+constructor);
			}
			
			//获取指定的无参构造
			Constructor<?> constructor1 = class1.getDeclaredConstructor();//可以写null也可以不写,因为是可变参数
			System.out.println("获取指定的无参构造 :"+constructor1);
			//获取指定的带参构造
			Constructor<?> constructor2 = class1.getDeclaredConstructor(int.class,int.class,String.class);
			System.out.println("获取指定的带参构造 :"+constructor2);
			
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
	
}


3. 通过反射获取到类中的信息后 ,进一步通过反射对其进行动态的操作

package com.xyj.reflection.test;

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

import com.xyj.entity.Emp;

/**
 * 通过反射获取到类中的信息后 ,进一步通过反射对其进行动态的操作 
 */
@SuppressWarnings("all")
public class Demo03 {
	public static void main(String[] args) {
		
		try {
			Class<Emp> class1 = (Class<Emp>) Class.forName("com.xyj.entity.Emp");
			
			//1、通过反射动态调用构造方法来创建对象
			//1.1 调用无参构造
			Emp emp1 = class1.newInstance();//这个其实就是调用了无参构造
			//1.2 调用带参构造
			Constructor<Emp> constructor = class1.getDeclaredConstructor(int.class,int.class,String.class);
			Emp emp2 = constructor.newInstance(1001,19,"张三");
			System.out.println(emp2.getId()+"--"+emp2.getName()+"--"+emp2.getAge());
			
			
			//2、通过反射操作属性
			Field field = class1.getDeclaredField("name");
			//取消安全检查 暴力反射
			field.setAccessible(true);
			field.set(emp1,"李四");//属性是私有的 直接赋值会报错:Class com.xyj.reflection.test.Demo03 can not access a member of class com.xyj.entity.Emp with modifiers "private"
			System.out.println(field.get(emp1));//通过反射直接读属性的值    //emp1.getName()是通过对象来获取
			
			
			//3、通过反射调用方法
			Method method = class1.getDeclaredMethod("setAge",int.class);
			method.invoke(emp1,42);//相当于emp1.setAge(42)的动态实现
			System.out.println(emp1.getAge());
			
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
}

4. 利用反射获取泛型信息
Java采用泛型擦除的机制来引入泛型。Java中的泛型仅仅是给编译器javac使用的,确保数据安全性和免去强制类型转换的麻烦,但是,一旦编译完成,所有和泛型有关的类型全部擦除。
为了通过反射操作这些类型,Java中的ParameterizedType,GenericArrayType,TypeVariable,WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始数据齐名的类型。

ParameterizedType:表示一种参数化类型,比如Collection< String >
GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型
TypeVariable:是各种类型变量的公共父接口
WildcardType:代表一种通配符类型表达式,比如 ?, ? extends Number, ? super Integer

package com.xyj.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.xyj.entity.Emp;

/**
 * 利用反射操作泛型
 */
@SuppressWarnings("all")
public class Demo04 {
	
	public void test01(Map<String,Emp> map,List<Emp> list) {//泛型为方法参数
		System.out.println("Demo04--test01");
	}
	
	public Map<Integer,Emp> test02() {//泛型为方法返回值
		System.out.println("Demo04--test02");
		return null;
	}
	
	public static void main(String[] args) {
		try {
			//利用反射获取指定方法的泛型参数信息
			Method method1 = Demo04.class.getDeclaredMethod("test01",Map.class,List.class);
			Type[] parameterTypes = method1.getGenericParameterTypes();//获得指定方法的所有参数
			
			for (Type type : parameterTypes) {
				System.out.println("--"+type);
				
				if(type instanceof ParameterizedType) {
					//获取每个泛型里面的具体类型
					Type[] types = ((ParameterizedType)type).getActualTypeArguments();
					for (Type type2 : types) {
						System.out.println(type2.getTypeName());
					}
				}
			}
			
			
			//利用反射获取指定方法的泛型返回值信息
			Method method2 = Demo04.class.getDeclaredMethod("test02",null);
			Type returnType = method2.getGenericReturnType();//获取指定方法的所有返回值
			System.out.println("--"+returnType);
			
			if(returnType instanceof ParameterizedType) {
				//获取泛型里的具体类型
				Type[] types = ((ParameterizedType)returnType).getActualTypeArguments();
				for (Type type : types) {
					System.out.println(type);
				}
			}
			
		} catch (NoSuchMethodException | SecurityException e) {
			e.printStackTrace();
		}
		
	}
}

5. 利用反射处理注解等

package com.xyj.reflection.test;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;

import com.xyj.annotation.Table;


/**
 * 利用反射操作注解
 */
public class Demo05 {
	public static void main(String[] args) {
		try {
			Class<?> class1 = Class.forName("com.xyj.annotation.Student");//获取运行时类
			//获取该类上面使用的注解
			Annotation[] annotations = class1.getAnnotations();
			for (Annotation annotation : annotations) {
				System.out.println(annotation);
			}
			//获取类上面的指定的注解
			Table table = class1.getAnnotation(Table.class);//通过注解.class 获取指定注解
			System.out.println(table.value());
			
			//获取类的属性上面的注解
			Field field = class1.getDeclaredField("name");
			Annotation[] annotations2 = field.getAnnotations();
			for (Annotation annotation : annotations2) {
				System.out.println(annotation);
			}
			
			//获取属性上的指定的注解
			com.xyj.annotation.Field field2 = field.getAnnotation(com.xyj.annotation.Field.class);
			System.out.println(field2.columnName()+"--"+field2.type()+"--"+field2.length());
			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		}
		
	}
}

自定义注解和使用注解的类如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

六、反射机制的性能问题

反射为我们带来自由、灵活编程的同时,也使我们的程序执行速度变慢,降低了运行效率。
我们可以通过禁止安全检查的方式来提高反射的运行速度。
setAccessible(true/false) 值为true表示取消安全检查,值为false则指示反射的对象应该实施Java语言的访问检查,并不是为true就能访问 为false就不能访问。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值