之前写到了设计模式的代理模式,因为下一篇动态代理等内容需要用到反射的知识,所以在之前Java篇的基础上再写一篇有关反射的内容,还是以实际的程序为主,了解反射是做什么的、应该怎么用。

一、什么是反射

  反射就是把Java类中的各个成分映射成一个个的Java对象。即在运行状态中,对于任意一个类,都能够知道这个类的所以属性和方法;对于任意一个对象,都能调用它的任意一个方法和属性。这种动态获取信息及动态调用对象方法的功能叫Java的反射机制。

  1. 反射机制的功能

  Java反射机制主要提供了以下功能:

  • 在运行时判断任意一个对象所属的类。
  • 在运行时构造任意一个类的对象。
  • 在运行时判断任意一个类所具有的成员变量和方法。
  • 在运行时调用任意一个对象的方法。
  • 生成动态代理。

  2. 实现反射机制的类

  Java中主要由以下的类来实现Java反射机制(这些类都位于java.lang.reflect包中):

  • Class类:代表一个类。 Field类:代表类的成员变量(成员变量也称为类的属性)。

  • Method类:代表类的方法。

  • Constructor类:代表类的构造方法。

  • Array类:提供了动态创建数组,以及访问数组的元素的静态方法。

二、反射的使用

  下面分步说明以下如何通过反射获取我们需要的内容。

  我们先随意写一个Customer类(就是一个PO类),然后看看如何通过反射对这个类进行操作。

  1. Customer类

 public class Customer {

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

  2. ReflectTester类

  这个类用来演示Reflection API的基本使用方法。这里自定义的copy方法是用来创建一个和参数objcet同样类型的对象,然后把object对象中的所有属性拷贝到新建的对象中,并将其返回。

 import java.lang.reflect.Field;
import java.lang.reflect.Method; public class ReflectTester { public Object copy(Object object) throws Exception{
//获得对象的类型
Class classType=object.getClass();
System.out.println("Class:"+classType.getName()); //通过默认构造方法创建一个新的对象
Object objectCopy=classType.getConstructor(new Class[]{}).newInstance(new Object[]{}); //获得对象的所有属性
Field fields[]=classType.getDeclaredFields(); for(int i=0; i<fields.length;i++){
Field field=fields[i]; String fieldName=field.getName();
String firstLetter=fieldName.substring(0,1).toUpperCase();
//获得和属性对应的getXXX()方法的名字
String getMethodName="get"+firstLetter+fieldName.substring(1);
//获得和属性对应的setXXX()方法的名字
String setMethodName="set"+firstLetter+fieldName.substring(1); //获得和属性对应的getXXX()方法
Method getMethod=classType.getMethod(getMethodName,new Class[]{});
//获得和属性对应的setXXX()方法
Method setMethod=classType.getMethod(setMethodName,new Class[]{field.getType()}); //调用原对象的getXXX()方法
Object value=getMethod.invoke(object,new Object[]{});
System.out.println(fieldName+":"+value);
//调用拷贝对象的setXXX()方法
setMethod.invoke(objectCopy,new Object[]{value});
}
return objectCopy;
} }

  下面分析一下上述代码。

  首先,通过Object类中的getClass()方法获取对象的类型。

Class classType=object.getClass();

  而Class类是Reflection API中的核心类,主要方法如下:

  • getName():获得类的完整名字。 getFields():获得类的public类型的属性。

  • getDeclaredFields():获得类的所有属性。

  • getMethods():获得类的public类型的方法。

  • getDeclaredMethods():获得类的所有方法。

  • getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes参数指定方法的参数类型。

  • getConstrutors():获得类的public类型的构造方法。

  • getConstrutor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes参数指定构造方法的参数类型。

  • newInstance():通过类的不带参数的构造方法创建这个类的一个对象。

  第二步,通过默认构造方法创建一个新的对象,即先调用Class类的getConstructor()方法获得一个Constructor对象,它代表默认的构造方法,然后调用Constructor对象的newInstance()方法构造一个实例。

Object objectCopy=classType.getConstructor(new Class[]{}).newInstance(new Object[]{});

  第三步,获得对象的所有属性,即通过Class类的getDeclaredFields()方法返回类的所有属性,包括public、protected、default和private访问级别的属性,

Field fields[]=classType.getDeclaredFields();

  第四步,获得每个属性相应的get/set方法,然后执行这些方法,把原来的对象属性拷贝到新的对象中。

  这里我们可以写一个InvokeTester的类,然后运用反射机制调用一个InvokeTester对象的add()方法(自定义方法),如add()方法的两个参数为int类型,那么获取表示add()方法的Method对象代码如下:

Method addMethod=classType.getMethod("add",new Class[]{int.class,int.class});

  上述代码中也有用到Method的invoke方法,其接收参数必须为对象,如果参数为基本数据类型,必须转换为相应的包装类型的对象,如int要转换为Integer。而invoke方法的返回值总是对象,如果实际被调用的方法的返回类型是基本数据类型,那么invoke方法会将其转换为相应的包装类型的对象,再将其返回。

  下面简单测试一下,具体的方法调用如上面提到的add方法,可自行编写(具体实例见下篇):

 public static void main(String[] args) throws Exception {
  Customer customer = new Customer();
  customer.setId(10L);
  customer.setName("adam");
  customer.setAge(3);   new ReflectTester().copy(customer);
}

  运行结果如下:

  

三、具体实例

  下面我们尝试着通过反射机制对一个jar包中的类进行分析,把类中所有的属性和方法提取出来,并写入到一个文件里中。

  目录结构如下:

  

  1. ReflexDemo类

  主要代码部分,通过反射获取类、属性及方法。

 import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile; /**
* @ClassName: ReflexDemo
* @Description: 通过反射获取类、属性及方法
* @author adamjwh
* @date 2018年5月28日
*
*/
public class ReflexDemo { private static StringBuffer sBuffer; public static void getJar(String jar) throws Exception {
try {
File file = new File(jar);
URL url = file.toURI().toURL();
URLClassLoader classLoader = new URLClassLoader(new URL[] { url },
Thread.currentThread().getContextClassLoader()); JarFile jarFile = new JarFile(jar);
Enumeration<JarEntry> enumeration = jarFile.entries();
JarEntry jarEntry; sBuffer = new StringBuffer(); //存数据 while (enumeration.hasMoreElements()) {
jarEntry = enumeration.nextElement(); if (jarEntry.getName().indexOf("META-INF") < 0) {
String classFullName = jarEntry.getName();
if (classFullName.indexOf(".class") < 0) {
classFullName = classFullName.substring(0, classFullName.length() - 1);
} else {
// 去除后缀.class,获得类名
String className = classFullName.substring(0, classFullName.length() - 6).replace("/", ".");
Class<?> myClass = classLoader.loadClass(className);
sBuffer.append("类名\t:" + className);
System.out.println("类名\t:" + className); // 获得属性名
Class<?> clazz = Class.forName(className);
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
sBuffer.append("属性名\t:" + field.getName() + "\n");
System.out.println("属性名\t:" + field.getName());
sBuffer.append("-属性类型\t:" + field.getType() + "\n");
System.out.println("-属性类型\t:" + field.getType());
} // 获得方法名
Method[] methods = myClass.getMethods();
for (Method method : methods) {
if (method.toString().indexOf(className) > 0) {
sBuffer.append("方法名\t:" + method.toString().substring(method.toString().indexOf(className)) + "\n");
System.out.println("方法名\t:" + method.toString().substring(method.toString().indexOf(className)));
}
}
sBuffer.append("--------------------------------------------------------------------------------" + "\n");
System.out.println("--------------------------------------------------------------------------------");
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
sBuffer.append("End");
System.out.println("End"); WriteFile.write(sBuffer); //写文件
}
} }

  2. WriteFile类

  进行写文件操作。

 import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter; /**
* @ClassName: WriteFile
* @Description: 写文件操作
* @author adamjwh
* @date 2018年5月28日
*
*/
public class WriteFile { private static String pathname = "src/com/adamjwh/jnp/ex14/out.txt"; public static void write(StringBuffer sBuffer) throws Exception {
File file = new File(pathname);
BufferedWriter bw = new BufferedWriter(new FileWriter(file)); bw.write(sBuffer.toString());
bw.close();
} }

  3. Main类

  这里我们需要在项目下新建一个lib文件夹,然后将要解析的jar包放入其中,比如这里我们放入jdk的dt.jar。目录结构如下:

  

  执行程序:

 /**
* @ClassName: Main
* @Description:
* @author adamjwh
* @date 2018年5月28日
*
*/
public class Main { private static String jar = "lib/dt.jar"; public static void main(String[] args) throws Exception {
ReflexDemo.getJar(jar);
} }

  运行结果如下:

  

最新文章

  1. Docker dockerfile创建Eclipse镜像初试
  2. NSMutableURLRequest和NSURLConnection用Post方式上传照
  3. 【JAVA基本数据类型包装类】
  4. ExtJS 刷新后,默认选中刷新前最后一次选中的节点
  5. C语言错误 BUG报错整理
  6. leetcode 21
  7. Django部署问题
  8. http中错误代码的含义整理
  9. aspnetcore.webapi实战k8s健康探测机制 - kubernetes
  10. CAN总线报文浅析
  11. jstat查看JVM GC情况
  12. Oracle数据库版本10.2.0.1升级到10.2.0.3(转)
  13. linux-ubuntu 下R无法安装rjava模块的原因及解决方案
  14. ZH奶酪:【数据结构与算法】基础排序算法总结与Python实现
  15. [CTSC 2018]假面
  16. 1260. [CQOI2007]涂色【区间DP】
  17. Metasploit+python生成免杀exe过360杀毒
  18. 使用cronolog工具给tomcat进行日志切割
  19. fzu 2136 取糖果 好几种方法解决。
  20. Unity Shader入门精要读书笔记(三)Shader必须的数学基础

热门文章

  1. 在centos7中python3的安装注意
  2. Android视图篇之一:Android常见基本布局
  3. Spring源码情操陶冶-tx:advice解析器
  4. 一文读懂Https的安全性原理、数字证书、单项认证、双项认证等
  5. spring学习(五) ———— 整合web项目(SSM)
  6. 【.Net Core】处理静态文件
  7. bitset用法小结
  8. JS的MD5加密
  9. 领域驱动设计(DDD:Domain-Driven Design)
  10. spring学习总结——高级装配学习二(处理自动装配的歧义性)