`

Java反射知识点总结

 
阅读更多
我们知道,Java的类型信息分为编译时类型信息和运行时类型信息,而反射就是Java提供的对运行时类型信息获取和操作的机制。
那么Java的类型信息有什么呢?一个Java的类主要包括两个元素,即是成员变量和成员方法。成员变量包括实例成员变量和静态成员变量,而成员方法也有实例成员方法和静态成员方法,构造方法则是特殊的实例成员方法。而反射的主要作用是能够在运行时获取一个Class的各个元素的结构,但无法更改这些元素的结构。这些元素就是前面说的成员变量和成员方法,并且对于成员变量,反射可以对其进行设值和取值,对于成员方法,反射可以直接对其进行方法调用,而对于特殊的成员方法构造方法而言,反射可以调用构造方法元素的newInstance()方法直接实例化一个该类的对象,而不需要使用new的方式。
在Java的反射API里,Java的类型信息体现为几个元素,这些元素在java.lang.reflect包下体现为以下几个类:
Field 描述字段的结构信息
Constructo描述构造方法的结构信息
Method 描述普通方法的结构信息
在Java8以后,新增了一个Parameter类,用于描述构造方法和普通方法的参数结构信息。
目前还有很多企业还是使用Java6的API,特别是做企业信息化系统的企业,至少前公司用的就是Java6的API。但是Java8的API添加了挺多API。所以下面分别提供了Java6和Java8里关于反射API的类图,也好有个对比,当然重点还是介绍Java8的API。
Java6的反射类图

Java8的反射类图

看到上面两个类图,Type接口及其子接口是与泛型相关的,AnnotatedType接口及其子接口则是与注解相关的,我们主要关注Field、Executable、Constructor、Method和Parameter这几个类,还有Class这个反射的入口类。
假设有这样的接口和类
interface MyInterface{
	void method(String arg0, Integer arg1);
}
public class JavaReflect implements MyInterface{
	public JavaReflect(){}
	public JavaReflect(String name){
		System.out.println("实例化了一个对象,传入参数name为:" + name);
	}
	public String field;
	@Override
	public void method(String arg0, Integer arg1){
		System.out.println("调用了method方法,参数值为:" + arg0 + "和" + arg1);
	}
}

关于Class这个反射的入口类,我们有三种方式可以获得。
1.	Class<?> clazz = Class.foName(“JavaReflect”);
2.	Class<?> clazz = JavaReflect.class;
3.	JavaReflect reflect = new JavaReflect ();
Class<?> clazz = reflect.getClass();


通过这三种方式得到Class对象之后,我们就可以访问这个类型的运行时信息了。
利用这些信息,我们可以做到以下几点。
一. 实例化对象
反射实例化对象的方式有两种,一种是调用Class类的newInstance()方法,该方法会调用无参构造方法实例化一个对象。
Object obj = clazz.newInstance();

另一种则是先调用Class类的getConstructor(Class<?>... parameterTypes)方法获取对应的Constructor对象,然后调用Constructor.newInstance(Object … args)方法实例化一个对象。
Constructor<?> constructor = clazz.getConstructor(String.class);
Object obj = constructor.newInstance("一个参数");

二. 对字段取值和设值
反射对字段取值和设值都需要先获取Field类的实例,然后调用Field类的取值和设值方法进行取值和设值。
Class类有两种获取Field类实例的方法,一个是getField(String name)方法,另一个是Field[] getFields()方法先获取Field[]数组,然后遍历该数组,根据字段名来判断所需的字段。
Field类的设值方法是set(Object obj, Object value),而原始类型则是setXXX(Object obj, Object value)
Field类的取值方法是get(Object obj),而原始类型则是getXXX(Object obj)
代码示例
Field field = clazz.getField("field");
	field.set(obj, "给字段设值");
	Object fieldValue = field.get(obj);

三. 调用方法
反射调用Method getMethod(String name, Class<?>... parameterTypes)方法需要先获取Method类的实例,然后调用Method类的Object invoke(Object obj, Object... args)方法调用对应的方法。(因为方法存在重载,所以不能通过获取所有Method[]数组然后循环比较方法名来取得特定的方法)
Method method = clazz.getMethod("method", String.class, Integer.class);
method.invoke(obj, "字符串", 1);

四. 反射与数组
在Java反射中,数组使用Array描述,Array类主要有三类方法,全部都是静态方法。
1. 实例化数组的方法
newInstance(Class<?> componentType, int... dimensions) 创建一个具有指定的组件类型和维度的新数组。
Object array2 = Array.newInstance(clazz, 2, 3);

newInstance(Class<?> componentType, int length) 创建一个具有指定的组件类型和长度的新数组。
Object array1 = Array.newInstance(clazz, 2);

2. 对数组元素设值的方法
set(Object array, int index, Object value) 将指定数组对象中索引组件的值设置为指定的新值,若是原始类型数组则是setXXX(Object array, int index, Object value)方法。
Array.set(array1, 1, obj);
Object obj2 = clazz.newInstance();
	obj2.getClass().getField("field").set(obj2, "反射处理二维数组");
	Array.set(Array.get(array2, 1), 2, obj2);

3. 对数组元素取值的方法
get(Object array, int index) 返回指定数组对象中索引组件的值,若是原始类型数组则是getXXX(Object array, int index)方法。
Object obj1 = Array.get(array1, 1);
Object obj3 = Array.get(Array.get(array2, 1), 2);

五. 反射与动态代理
Java的动态代理使用起来很简单,使用Proxy类和InvocationHandler接口就可以实现。Java实现的动态代理对象就是实现了指定n个接口的类的一个对象,使用代理对象调用任何方法时,都会替换为InvocationHandler接口实现类重写的invoke方法。
1. 实现InvocationHandler接口,重写invoke(Object proxy, Method method, Object[] args)方法,该方法就是代理方法,将会替换掉代理对象被调用的方法。
public class InvocationHandlerImpl implements InvocationHandler{
	private Object target;
	public InvocationHandlerImpl(Object target){
		this.target = target;
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("方法调用前,相当于Spring AOP的前置通知");
		method.invoke(target, args);
		System.out.println("方法调用后,相当于Spring AOP的后置通知");
		return null;
	}
}

2. 调用Proxy的静态方法创建代理的对象,有以下两种方法
一种方法是使用Proxy的Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法直接创建代理对象,
Object proxyObject = Proxy.newProxyInstance(clazz.getClassLoader(), 
				new Class<?>[]{MyInterface.class}, 
				new InvocationHandlerImpl(obj));

另一种是先调用Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)获取代理类,然后获取代理类的含有一个InvocationHandler类型的参数的构造方法,再使用这个构造方法实例化一个代理对象。
Class<?> proxyClass = Proxy.getProxyClass(clazz.getClassLoader(),
MyInterface.class);
		Object proxyObject = proxyClass.
getConstructor(InvocationHandler.class).
				newInstance(new InvocationHandlerImpl(obj));

3. 使用代理的对象调用目标方法。
((MyInterface) proxyObject).method("使用动态代理调用该方法", 0);

六. 反射与泛型
参考我的博文Java反射获取实际泛型类型参数
七. 反射与注解
参考我的博文Java注解知识点总结

上面的代码完整的示例如下:
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface MyInterface{
	void method(String arg0, Integer arg1);
}
public class JavaReflect implements MyInterface{
	public JavaReflect(){}
	public JavaReflect(String name){
		System.out.println("实例化了一个对象,传入参数name为:" + name);
	}
	public String field;
	@Override
	public void method(String arg0, Integer arg1){
		System.out.println("调用了method方法,参数值为:" + arg0 + "和" + arg1);
	}
	
	public static void main(String[] args) throws Exception {
		// 获取Class对象,这个是Java反射的入口
		Class<?> clazz = JavaReflect.class;
		// 实例化对象
		// Object obj = clazz.newInstance();
		Constructor<?> constructor = clazz.getConstructor(String.class);
		Object obj = constructor.newInstance("一个参数");
		// 字段设值和取值
		Field field = clazz.getField("field");
		field.set(obj, "给字段设值");
		Object fieldValue = field.get(obj);
		System.out.println(fieldValue);
		// 方法调用
		Method method = clazz.getMethod("method", String.class, Integer.class);
		method.invoke(obj, "字符串", 1);
		// 实例化数组
		Object array1 = Array.newInstance(clazz, 2);
		Object array2 = Array.newInstance(clazz, 2, 3);
		// 数组设值和取值
		Array.set(array1, 1, obj);
		Object obj1 = Array.get(array1, 1);
		System.out.println("array1的第一个元素的field字段的值为:" + obj1.getClass().getField("field").get(obj1));
		// 对array2二维数组和多位数组的设值要先取到对应的一维数组
		Object obj2 = clazz.newInstance();
		obj2.getClass().getField("field").set(obj2, "反射处理二维数组");
		Array.set(Array.get(array2, 1), 2, obj2);
		Object obj3 = Array.get(Array.get(array2, 1), 2);
		System.out.println("array1的第一个元素的field字段的值为:" + obj3.getClass().getField("field").get(obj3));
		// Java反射与动态代理
//		Class<?> proxyClass = Proxy.getProxyClass(clazz.getClassLoader(), MyInterface.class);
//		Object proxyObject = proxyClass.getConstructor(InvocationHandler.class).
//				newInstance(new InvocationHandlerImpl(obj));
		Object proxyObject = Proxy.newProxyInstance(clazz.getClassLoader(), 
				new Class<?>[]{MyInterface.class}, 
				new InvocationHandlerImpl(obj));
		((MyInterface) proxyObject).method("使用动态代理调用该方法", 0);
		
	}
}

public class InvocationHandlerImpl implements InvocationHandler{
	private Object target;
	public InvocationHandlerImpl(Object target){
		this.target = target;
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("方法调用前,相当于Spring AOP的前置通知");
		method.invoke(target, args);
		System.out.println("方法调用后,相当于Spring AOP的后置通知");
		return null;
	}
}


代码执行结果为:
  • 大小: 51.8 KB
  • 大小: 64 KB
  • 大小: 34.9 KB
1
1
分享到:
评论

相关推荐

    JAVA反射知识点总结

    本文介绍了JAVA反射类的基本概念,欢迎阅读学习,一起进步。 文章目录一.反射的基本概念二.反射常用类三.使用反射的基本步骤四.Class类讲解(1)Class类是反射机制的起源和入口(2)Class类存放类的结构信息(3)...

    Java基础知识点总结.docx

    Java学习更是如此,知识点总结目录如下: 目录 一、 Java概述 3 二、 Java语法基础 5 数据类型 5 运算符号 14 语句 15 函数 15 方法重载(Overloadjing)与重写(Overriding) 16 数组 17 总结 18 三、 常见关键字 ...

    java反射的作用知识点总结

    在本篇文章里小编给大家整理的是关于java反射的作用知识点总结,需要的朋友们可以学习下。

    java反射机制

    java反射机制知识点总结

    2017Java软件工程师面试知识点总结

    Java基础知识点总结,适应于面试。知识点包括Java基础,什么是面对对象、IO流、反射、线程、数据库、工厂模式、SSH框架的区别和联系、一些常见的面试经典问题

    java各知识点详细总结(毕向东笔记整理)

    java各知识点详细总结(毕向东笔记整理)。第一章:编程基础 3-11 第二章:数组 11 -31 第三章:面向对象程序开发 31 -74 第四章:异常机制 74 -89 第五章:多线程技术 89 -122122122 第六章:常用类 API 122API 122 ...

    java基础的注解和反射的相关知识点总结

    Java的反射机制是指在程序的运行状态中,**可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用,操作任意一个对象的属性和方法。**这种动态获取程序信息以及...

    Java知识点总结.zip

    动态性:Java可以通过反射、注解等机制实现在运行时动态加载类和修改行为,增加了程序的灵活性。 综上所述,Java凭借其强大的特性和广泛的适用范围,在企业级应用、互联网服务、移动开发等领域均扮演着举足轻重的...

    JAVA基础知识总结

    对java的基础知识进行了详细的总结,内容从基础数据类型到单例设计模式、内部类、多线程等容易被忽视的知识点都有介绍。以及集合框架和反射的知识点

    javaOOP文档、知识点总结.CHM

    本文档总结javaOOP所有关键知识,包括java基础知识、面向对象、常用API、多线程、集合、IO、泛型、枚举、反射、注解等概念性知识。

    Java 排序算法知识点总结.zip

    动态性:Java可以通过反射、注解等机制实现在运行时动态加载类和修改行为,增加了程序的灵活性。 综上所述,Java凭借其强大的特性和广泛的适用范围,在企业级应用、互联网服务、移动开发等领域均扮演着举足轻重的...

    java框架知识点汇总(反射,XML,设计模式,Redis缓存数据库)

    Java 反射机制是在运行状态中,对于任意一个类,都能够获得这个类的所有属性和方法,对于任意一个对象都能够调用它的任意一个属性和方法。这种在运行时动态的获取信息以及动态调用对象的方法的功能称为 Java 的反射...

    「Java学习+面试指南」一份涵盖大部分 Java 程序员所需要掌握的核心知识 准备 Java 面试,首选.zip

    Java 基础常见知识点&面试题总结(上) Java 基础常见知识点&面试题总结(中) Java 基础常见知识点&面试题总结(下) 重要知识点详解 : 为什么 Java 中只有值传递? Java 序列化详解 泛型&通配符详解 Java 反射机制详解...

    注解、反射、单元测试、lombok知识点总结笔记

    关于注解、反射、单元测试、lombok等知识点的总结笔记

    「Java学习+面试指南」一份涵盖大部分 Java 程序员所需要掌握的核心知识

    Java 基础常见知识点&面试题总结(上) Java 基础常见知识点&面试题总结(中) Java 基础常见知识点&面试题总结(下) 重要知识点详解: 为什么 Java 中只有值传递? Java 序列化详解 泛型&通配符详解 Java 反射机制详解 ...

    Java反射中java.beans包学习总结

    本篇文章通过学习Java反射中java.beans包,吧知识点做了总结,并把相关内容做了关联,对此有需要的朋友可以学习参考下。

    Java基础 反射篇.md

    反射是一个非常重要的知识点,在学习Spring 框架时,Bean的初始化用到了反射,在破坏单例模式时也用到了反射,在获取标注的注解时也会用到反射······ 当然了,反射在日常开发中,我们没碰到过多少,至少我没...

    J2SE知识点总结(主要帮助初学者)

    包括基础语法、面向对象、异常处理、数组、常用类、容器、IO、反射、枚举类型与泛型、线程、网络、GUI等知识点总结和小例子,另外还附加了J2EE的Servlet、JSP、EL表达式、JSTL标签库、JavaBean的一些知识点,该文档...

    java reflect

    java反射的一些总结知识点,这是java中反射比较好入门的资料

Global site tag (gtag.js) - Google Analytics