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

  好了,言归正传。

  反射机制是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. python编码问题和逻辑运算

    1,回顾昨天课程及作业 #1.使用while循环输入 1 2 3 4 5 6 8 9 10 ''' count = 0 while count < 10: count += 1 # count ...

  2. vim中的批量替换

    VI中的批量替换   1) 文件内全部替换:   :%s#abc#123#g (如文件内有#,可用/替换,:%s/abc/123/g)     --注:把abc替换成123   (或者: %s/str ...

  3. [搬运] C# 这些年来受欢迎的特性

    原文地址:http://www.dotnetcurry.com/csharp/1411/csharp-favorite-features 在写这篇文章的时候,C# 已经有了 17 年的历史了,可以肯定 ...

  4. 常见 Java 异常解释(恶搞版)

    常见 Java 异常解释:(译者注:非技术角度分析.阅读有风险,理解需谨慎o(╯□╰)o) java.lang ArithmeticException 你正在试图使用电脑解决一个自己解决不了的数学问题 ...

  5. HDU 2502 月之数(二进制,规律)

    月之数 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submis ...

  6. 51 Nod 1119

    机器人走方格 V2 M * N的方格,一个机器人从左上走到右下,只能向右或向下走.有多少种不同的走法?由于方法数量可能很大,只需要输出Mod 10^9 + 7的结果. Input 第1行,2个数M,N ...

  7. Flexbox 练习和总结

    练习地址: http://flexboxfroggy.com/ Welcome to Flexbox Froggy, a game where you help Froggy and friends ...

  8. Java简历与面试

    尊重原创:http://blog.csdn.net/love_java_cc/article/details/78292347 Java就业指导   想要成为合格的Java程序员或工程师到底需要具备哪 ...

  9. final、finally、finalize

    final是一个修饰词.可以修饰变量.方法.类 final修饰变量时分为两种 )1.修饰成员变量:该成员变量不可以被二次赋值.也就是说成员变量无法改变.且该成员变量要么在定义时初始化,要么在构造器中进 ...

  10. PHP flock() 函数

    定义和用法 flock() 函数锁定或释放文件. 若成功,则返回 true.若失败,则返回 false. 语法 flock(file,lock,block) 参数 描述 file 必需.规定要锁定或释 ...