前一段时间一直忙,所以没什么时间写博客,拖了这么久,也该更新更新了。最近看到各种知识付费的推出,感觉是好事,也是坏事,好事是对知识沉淀的认可与推动,坏事是感觉很多人忙于把自己的知识变现,相对的在沉淀上做的实际还不够,我对此暂时还没有什么想法,总觉得,慢慢来,会更快一点,自己掌握好节奏就好。

  好了,言归正传。

  反射机制是Java中的一个很强大的特性,可以在运行时获取类的信息,比如说类的父类,接口,全部方法名及参数,全部常量和变量,可以说类在反射面前已经衣不遮体了(咳咳,这是正规车)。先举一个小栗子,大家随意感受一下:

public void testA(){
String name = "java.lang.String";
try{
Class cl = Class.forName(name);
Class supercl = cl.getSuperclass();
String modifiers = Modifier.toString(cl.getModifiers());
if (modifiers.length() > 0){
System.out.print(modifiers + " ");
}
System.out.print(name);
if (supercl != null && supercl != Object.class){
System.out.print(" extents " + supercl.getName());
}
System.out.print("{\n");
printFields(cl);
System.out.println();
printConstructors(cl);
System.out.println();
printMethods(cl);
System.out.println("}");
}catch (ClassNotFoundException e){
e.printStackTrace();
}
System.exit(0);
}
    private static void printConstructors(Class cl){
Constructor[] constructors = cl.getDeclaredConstructors(); for (Constructor c : constructors){
String name = c.getName();
System.out.print(" ");
String modifiers = Modifier.toString(c.getModifiers());
if (modifiers.length() > 0){
System.out.print(modifiers + " ");
}
System.out.print(name + "("); Class[] paraTypes = c.getParameterTypes();
for (int j = 0; j < paraTypes.length; j++){
if (j > 0){
System.out.print(", ");
}
System.out.print(paraTypes[j].getSimpleName());
}
System.out.println(");");
}
} private static void printMethods(Class cl){
Method[] methods = cl.getDeclaredMethods();
for (Method m : methods){
Class retType = m.getReturnType();
String name = m.getName(); System.out.print(" ");
String modifiers = Modifier.toString(m.getModifiers());
if (modifiers.length() > 0){
System.out.print(modifiers + " ");
}
System.out.print(retType.getSimpleName() + " " + name +"(");
Class[] paramTypes = m.getParameterTypes();
for(int j = 0; j < paramTypes.length; j++){
if (j > 0){
System.out.print(", ");
}
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
} private static void printFields(Class cl){
Field[] fields = cl.getFields();
for (Field f : fields){
Class type = f.getType();
String name = f.getName();
System.out.print(" ");
String modifiers = Modifier.toString(f.getModifiers());
if (modifiers.length() > 0){
System.out.print(modifiers + " ");
}
System.out.println(type.getSimpleName() + " " + name +";");
}
}

  调用testA方法输出如下:

public final java.lang.String{
public static final Comparator CASE_INSENSITIVE_ORDER; public java.lang.String(byte[], int, int);
public java.lang.String(byte[], Charset);
public java.lang.String(byte[], String);
public java.lang.String(byte[], int, int, Charset);
public java.lang.String(byte[], int, int, String);
java.lang.String(char[], boolean);
public java.lang.String(StringBuilder);
public java.lang.String(StringBuffer);
public java.lang.String(byte[]);
public java.lang.String(int[], int, int);
public java.lang.String();
public java.lang.String(char[]);
public java.lang.String(String);
public java.lang.String(char[], int, int);
public java.lang.String(byte[], int);
public java.lang.String(byte[], int, int, int); public boolean equals(java.lang.Object);
public String toString();
public int hashCode();
public int compareTo(java.lang.String);
public volatile int compareTo(java.lang.Object);
public int indexOf(java.lang.String, int);
public int indexOf(java.lang.String);
public int indexOf(int, int);
public int indexOf(int);
static int indexOf([C, int, int, [C, int, int, int);
static int indexOf([C, int, int, java.lang.String, int);
public static String valueOf(int);
public static String valueOf(long);
public static String valueOf(float);
public static String valueOf(boolean);
public static String valueOf([C);
public static String valueOf([C, int, int);
public static String valueOf(java.lang.Object);
public static String valueOf(char);
public static String valueOf(double);
public char charAt(int);
private static void checkBounds([B, int, int);
public int codePointAt(int);
public int codePointBefore(int);
public int codePointCount(int, int);
public int compareToIgnoreCase(java.lang.String);
public String concat(java.lang.String);
public boolean contains(java.lang.CharSequence);
public boolean contentEquals(java.lang.CharSequence);
public boolean contentEquals(java.lang.StringBuffer);
public static String copyValueOf([C);
public static String copyValueOf([C, int, int);
public boolean endsWith(java.lang.String);
public boolean equalsIgnoreCase(java.lang.String);
public static transient String format(java.util.Locale, java.lang.String, [Ljava.lang.Object;);
public static transient String format(java.lang.String, [Ljava.lang.Object;);
public void getBytes(int, int, [B, int);
public byte[] getBytes(java.nio.charset.Charset);
public byte[] getBytes(java.lang.String);
public byte[] getBytes();
public void getChars(int, int, [C, int);
void getChars([C, int);
private int indexOfSupplementary(int, int);
public native String intern();
public boolean isEmpty();
public static transient String join(java.lang.CharSequence, [Ljava.lang.CharSequence;);
public static String join(java.lang.CharSequence, java.lang.Iterable);
public int lastIndexOf(int);
public int lastIndexOf(java.lang.String);
static int lastIndexOf([C, int, int, java.lang.String, int);
public int lastIndexOf(java.lang.String, int);
public int lastIndexOf(int, int);
static int lastIndexOf([C, int, int, [C, int, int, int);
private int lastIndexOfSupplementary(int, int);
public int length();
public boolean matches(java.lang.String);
private boolean nonSyncContentEquals(java.lang.AbstractStringBuilder);
public int offsetByCodePoints(int, int);
public boolean regionMatches(int, java.lang.String, int, int);
public boolean regionMatches(boolean, int, java.lang.String, int, int);
public String replace(char, char);
public String replace(java.lang.CharSequence, java.lang.CharSequence);
public String replaceAll(java.lang.String, java.lang.String);
public String replaceFirst(java.lang.String, java.lang.String);
public String[] split(java.lang.String);
public String[] split(java.lang.String, int);
public boolean startsWith(java.lang.String, int);
public boolean startsWith(java.lang.String);
public CharSequence subSequence(int, int);
public String substring(int);
public String substring(int, int);
public char[] toCharArray();
public String toLowerCase(java.util.Locale);
public String toLowerCase();
public String toUpperCase();
public String toUpperCase(java.util.Locale);
public String trim();
}

  这里把String类型的所有方法和变量都获取到了,使用的仅仅是String类型的全名。当然,反射的功能不仅仅是获取类的信息,还可以在运行时动态创建对象,回想一下,我们正常的对象使用,都是需要在代码中先声明,然后才能使用它,但是使用反射后,就能在运行期间动态创建对象并调用其中的方法,甚至还能直接查看类的私有成员变量,还能获取类的注解信息,在泛型中类型判断时也经常会用到。反射可以说完全打破了类的封装性,把类的信息全部暴露了出来。

  上面的代码看不太明白也没关系,只要稍微感受一下反射的能力就好了。介绍完了反射能做的事情,本篇教程就不再写一些玩具代码了,这次以一个实用型的代码为媒介来介绍反射。

  在开发中,经常会遇到两个不同类对象之间的复制,把一个类中的字段信息get取出来,然后set到另一个类中,大部分情况下,两个类对应的字段是一样,每次这样使用是很麻烦的,那么利用反射就可以实现一个封装,只需要调用一个方法即可实现简单的类字段复制。

  那么,先来想想,要复制一个类对象的所有字段信息到另一个类对象中,首先,怎么获取一个类的某个字段的值呢?我们先来编写一个方法:

  /**
* 获取对象的指定字段的值
* @param obj 目标对象
* @param fieldName 目标字段
* @return 返回字段值
* @throws Exception 可能抛出异常
*/
private static Object getFieldValue(Object obj, String fieldName) throws Exception{
//获取类型信息
Class clazz = obj.getClass();
//取对应的字段信息
Field field = clazz.getDeclaredField(fieldName);
//设置可访问权限
field.setAccessible(true);
//取字段值
Object value = field.get(obj);
return value;
}

  这里使用了两个之前没有说过的类,一个是Class,是不是很眼熟,想一想,我们每次定义一个类的时候是不是都要用到它,哈哈,那你就想错了,那是class关键词,java是大小写的敏感的,这里的Class是一个类名,那这个类是干嘛用的呢?

  虚拟机在加载每一个类的时候,会自动生成一个对应的Class类来保存该类的信息,可以理解为Class类是那个类的代理类,是连接实际类与类加载器的桥梁,可以通过它来获取虚拟机的类加载器引用,从而实现更多的骚操作。Class类是一个泛型类,每个类都有对应的一个Class类,比如String对应的Class类就是Class<String>。

  Class有很多方法来获取更多关于类的信息,这里使用getDeclaredField方法来获取指定字段信息,返回的是Field类型对象,这个对象里存储着关于字段的一些信息,如字段名称,字段类型,字段修饰符,字段可访问性等,setAccessible方法可以设置字段的可访问性质,这样就能直接访问private修饰的字段了,然后使用get方法来获取指定对象的对应字段的值。

  我们来测试一下:

  public void testB(){
try{
Employee employee = new Employee();
employee.setName("Frank");
employee.setSalary(6666.66);
System.out.println((String)getFieldValue(employee,"name"));
System.out.println((double)getFieldValue(employee,"salary"));
}catch (Exception e){
e.printStackTrace();
}
}

  输出如下:

Frank
6666.66

  接下来,我们需要获取类中所有字段,然后在另一个类中查找是否有对应字段,如果有的话就设置字段的值到相应的字段中。

    /**
* 复制一个类对象属性到另一个类对象中
* @param objA 需要复制的对象
* @param objB 复制到的目标对象类型
* @return 返回复制后的目标对象
*/
private static void parseObj(Object objA,Object objB) throws Exception{
if (objA == null){
return;
}
//获取objA的类信息
Class classA = objA.getClass();
Class classB = objB.getClass();
try {
//获取objA的所有字段
Field[] fieldsA = classA.getDeclaredFields();
//获取objB的所有字段
Field[] fieldsB = classB.getDeclaredFields();
if (fieldsA == null || fieldsA.length <= 0 || fieldsB == null || fieldsB.length <= 0){
return;
} //生成查询map
Map<String,Field> fieldMap = new HashMap<>();
for (Field field:fieldsA){
fieldMap.put(field.getName(),field);
} //开始复制字段信息
for (Field fieldB : fieldsB){
//查找是否在objB的字段中存在该字段
Field fielaA = fieldMap.get(fieldB.getName());
if (fielaA != null){
fieldB.setAccessible(true);
fieldB.set(objB,getFieldValue(objA,fielaA.getName()));
}
}
} catch (IllegalStateException e) {
throw new IllegalStateException("instace fail: " ,e);
}
}

  这里获取到classA和classB的所有字段之后,先生成了一个map用于查找,可以减少遍历次数,然后之后只需要遍历一次就可以判断相应字段是否存在,如果存在则取出对应值设置到相应的字段里去。

  接下来测试一下:

  public void testB(){
try{
//生成Employee对象
Employee employee = new Employee("Frank",6666.66);
//生成一个Manager对象
Manager manager = new Manager();
//复制对象
parseObj(employee,manager);
System.out.println(manager.getName());
System.out.println(manager.getSalary());
}catch (Exception e){
e.printStackTrace();
}
}
public class Employee {
private String name;
private Double salary; public Employee(String name, Double salary) {
this.name = name;
this.salary = salary;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Double getSalary() {
return salary;
} public void setSalary(Double salary) {
this.salary = salary;
}
}
public class Manager {
private String name;
private Double salary;
private Double bonus; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Double getSalary() {
return salary;
} public void setSalary(Double salary) {
this.salary = salary;
} public Double getBonus() {
return bonus;
} public void setBonus(Double bonus) {
this.bonus = bonus;
}
}

  输出如下:

Frank
6666.66

  完美,这样我们就利用了反射机制完美的把相同的字段在不同类的对象之间进行了复制,这里仅仅是两个字段,所以可能好处不明显,但事实上,实际开发中,经常会有将BO转换为VO的操作,这时候,这个操作就很有必要了,简单的一行命令就可以代替一大堆的get和set操作。

  当然,使用反射机制固然高端大气上档次,但是也是一把双刃剑,使用不当很可能会带来严重后果,而且使用反射的话,会占用更多资源,运行效率也会降低,上述工具类是用运行效率换开发效率。开发中不建议大量使用,还是那句话,技术只是手段,需要使用的时候再使用,不要为了使用而使用。

  至于反射中的其他方法和姿势,大家尽可以慢慢去摸索,这里仅仅是抛砖引玉。

  至此,本篇讲解完毕,欢迎大家继续关注。

  

【Java入门提高篇】Day13 Java中的反射机制的更多相关文章

  1. 【Java入门提高篇】Java集合类详解(一)

    今天来看看Java里的一个大家伙,那就是集合. 集合嘛,就跟它的名字那样,是一群人多势众的家伙,如果你学过高数,没错,就跟里面说的集合是一个概念,就是一堆对象的集合体.集合就是用来存放和管理其他类对象 ...

  2. 【Java入门提高篇】Day1 抽象类

    基础部分内容差不多讲解完了,今天开始进入Java提高篇部分,这部分内容会比之前的内容复杂很多,希望大家做好心理准备,看不懂的部分可以多看两遍,仍不理解的部分那一定是我讲的不够生动,记得留言提醒我. 好 ...

  3. 【Java入门提高篇】Day21 Java容器类详解(四)ArrayList源码分析

    今天要介绍的是List接口中最常用的实现类——ArrayList,本篇的源码分析基于JDK8,如果有不一致的地方,可先切换到JDK8后再进行操作. 本篇的内容主要包括这几块: 1.源码结构介绍 2.源 ...

  4. 【Java入门提高篇】Day16 Java异常处理(上)

    当当当当当当,各位看官,好久不见,甚是想念. 今天我们来聊聊Java里的一个小妖精,那就是异常. 什么是异常?什么是异常处理? 异常嘛,顾名思义就是不正常,(逃),是Java程序运行时,发生的预料之外 ...

  5. 【Java入门提高篇】Day16 Java异常处理(下)

    今天继续讲解java中的异常处理机制,主要介绍Exception家族的主要成员,自定义异常,以及异常处理的正确姿势. Exception家族 一图胜千言,先来看一张图. Exception这是一个父类 ...

  6. 【Java入门提高篇】Day31 Java容器类详解(十三)TreeSet详解

    上一篇很水的介绍完了TreeMap,这一篇来看看更水的TreeSet. 本文将从以下几个角度进行展开: 1.TreeSet简介和使用栗子 2.TreeSet源码分析 本篇大约需食用10分钟,各位看官请 ...

  7. 【Java入门提高篇】Day28 Java容器类详解(十)LinkedHashMap详解

    今天来介绍一下容器类中的另一个哈希表———>LinkedHashMap.这是HashMap的关门弟子,直接继承了HashMap的衣钵,所以拥有HashMap的全部特性,并青出于蓝而胜于蓝,有着一 ...

  8. 【Java入门提高篇】Day27 Java容器类详解(九)LinkedList详解

    这次介绍一下List接口的另一个践行者——LinkedList,这是一位集诸多技能于一身的List接口践行者,可谓十八般武艺,样样精通,栈.队列.双端队列.链表.双向链表都可以用它来模拟,话不多说,赶 ...

  9. 【Java入门提高篇】Day26 Java容器类详解(八)HashSet源码分析

    前面花了好几篇的篇幅把HashMap里里外外说了个遍,大家可能对于源码分析篇已经讳莫如深了.别慌别慌,这一篇来说说集合框架里最偷懒的一个家伙——HashSet,为什么说它是最偷懒的呢,先留个悬念,看完 ...

随机推荐

  1. Django 1.10中文文档-第一个应用Part1-请求与响应

    在本教程中,我们将引导您完成一个投票应用程序的创建,它包含下面两部分: 一个可以进行投票和查看结果的公开站点: 一个可以进行增删改查的后台admin管理界面: 我们假设你已经安装了Django.您可以 ...

  2. 开发 | 微信小程序API-wx.setScreenBrightness/wx.getScreenBrightness

    前言 最近接触了微信小程序 API - wx.setScreenBrightness .wx.getScreenBrightness 接口,调用该接口可以调节并显示手机屏幕亮度数据.对于喜欢腾讯新闻. ...

  3. dotnet core cli 命令

    1 dotnet new 2 创建code 程序 dotnet new console using System; namespace cli { class Program { static voi ...

  4. UVA424高精度加法

    One of the first users of BIT's new supercomputer was Chip Diller. He extended his exploration of po ...

  5. 应用中Token的作用

    Token 的作用 Token,就是令牌,最大的特点就是随机性,不可预测.一般黑客或软件无法猜测出来. 那么,Token有什么作用?又是什么原理呢? Token一般用在两个地方: 1)防止表单重复提交 ...

  6. 【LeetCode】476. Number Complement (java实现)

    原题链接 https://leetcode.com/problems/number-complement/ 原题 Given a positive integer, output its comple ...

  7. [国嵌笔记][012][GCC程序编译]

    GCC特点 GCC(GUN C Compiler)是GUN推出的功能强大.性能优越的多平台编译器.其执行效率与一般编译器相比平均效率要高20%~30%. GCC基本用法 gcc [options] f ...

  8. webpack运行常见错误归纳

    今天在运行项目的时候,又遇到坑了,在公司运行的好好的项目,到我自己电脑上就报错,提示跨域,想了好久都不明白为啥,webpack配置文件里的ip地址我也改成与本地ip对应的,百思不得其解,在寻求别人帮助 ...

  9. surging 微服务框架使用系列之surging介绍

    首先,感谢surging的作者fanliang11为.net开源做出的贡献 其次, surging 的git地址:https://github.com/dotnetcore/surging surgi ...

  10. SDK是什么?什么是SDK

    从 SDK导航 看到的 应该比较专业! SDK的英文全名是:software development kit,翻译成中文的意思就是"软件开发工具包" 通俗一点的理解,是指由第三方服 ...