java反射基础知识(四)反射应用实践
反射基础
p.s: 本文需要读者对反射机制的API有一定程度的了解,如果之前没有接触过的话,建议先看一下官方文档的Quick Start。
在应用反射机制之前,首先我们先来看一下如何获取一个对象对应的反射类Class,在Java中我们有三种方法可以获取一个对象的反射类。
通过getClass方法
在Java中,每一个Object都有一个getClass()方法,通过getClass方法我们可以获取到这个对象对应的反射类:
|
1
2
|
String s = "ziwenxie";Class<?> c = s.getClass(); |
通过forName方法
我们也可以调用Class类的静态方法forName():
|
1
|
Class<?> c = Class.forName("java.lang.String"); |
使用.class
或者我们也可以直接使用.class:
|
1
|
Class<?> c = String.class; |
获取类型信息
在文章开头我们就提到反射的一大好处就是可以允许我们在运行期间获取对象的类型信息,下面我们通过一个例子来具体看一下。
首先我们在typeinfo.interfacea包下面新建一个接口A:
|
1
2
|
package typeinfo.interfacea;public interface A { void f(); } |
接着我们在typeinfo.packageaccess包下面新建一个接口C,接口C继承自接口A,并且我们还另外创建了几个用于测试的方法,注意下面几个方法的权限都是不同的。
|
1
2
3
4
5
6
7
8
9
10
11
12
|
package typeinfo.packageaccess;import typeinfo.interfacea.A;class C implements A { public void f() { System.out.println("public C.f()"); } public void g() { System.out.println("public C.g()"); } protected void v () { System.out.println("protected C.v()"); } void u() { System.out.println("package C.u()"); } private void w() { System.out.println("private C.w()"); }}public class HiddenC { public static A makeA() { return new C(); }} |
在callHiddenMethod()方法中我们用到了几个新的API,其中getDeclaredMethod()根据方法名用于获取Class类指代对象自己声明的某个方法,然后我们通过调用invoke()方法就可以触发对象的相关方法:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
package typeinfo;import typeinfo.interfacea.A;import typeinfo.packageaccess.HiddenC;import java.lang.reflect.Method;public class HiddenImplementation { public static void main(String[] args) throws Exception { A a = HiddenC.makeA(); a.f(); System.out.println(a.getClass().getName()); // Oops! Reflection still allows us to call g(): callHiddenMethod(a, "g"); // And even methods that are less accessible! callHiddenMethod(a, "u"); callHiddenMethod(a, "v"); callHiddenMethod(a, "w"); } static void callHiddenMethod(Object a, String methodName) throws Exception { Method g = a.getClass().getDeclaredMethod(methodName); g.setAccessible(true); g.invoke(a); }} |
从输出结果我们可以看出来,不管是public,default,protect还是pricate方法,通过反射类我们都可以自由调用。当然这里我们只是为了显示反射的强大威力,在实际开发中这种技巧还是不提倡。
|
1
2
3
4
5
6
|
public C.f()typeinfo.packageaccess.Cpublic C.g()package C.u()protected C.v()private C.w() |
上面我们只是测试了Method对象,感兴趣的读者在熟悉了反射的API之后,不妨测试一下Filed,这里我们就不重复了。
与注解相结合
在单元测试框架比如Junit中反射机制也得到了广泛的应用,即通过注解的方式。下面我们简单地来了解一下如何通过反射机制来获取相关方法的注解信息,比如说我们有下面这样一个业务场景,当用户在修改自己密码的时候,为了保证密码的安全性,我们要求用户的新密码要满足一些条件,比如说至少要包含一个非数字字符,不能与以前的密码相同之类的条件等。
|
1
2
3
4
5
6
7
|
import java.lang.annotation.*@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface UserCase { public int id(); public String description() default "no description";} |
下面是我们检测密码的工具类的实现:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public class PasswordUtils { @UserCase(id=47, description="Password must contain at least one numeric") public boolean validatePassword(String password) { return (password.matches("\\w*\\d\\w*")); } @UserCase(id=48) public String encryptPassword(String password) { return new StringBuilder(password).reverse().toString(); } @UserCase(id=49, description="New passwords can't equal previously used ones") public boolean checkForNewPassword(List<String> prevPasswords, String password) { return !prevPasswords.contains(password); }} |
利用反射我们可以写出更加清晰的测试代码,其中getDeclaredMethods()方法可以获取相关对象自己声明的相关方法,而getAnnotation()则可以获取Method对象的指定注解。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public class UseCaseTracker { public static void trackUseCases(List<Integer> useCases, Class<?> cl) { for(Method m : cl.getDeclaredMethods()) { UseCase uc = m.getAnnotation(UseCase.class); if(uc != null) { System.out.println("Found Use Case: " + uc.id() + " " + uc.description()); useCases.remove(new Integer(uc.id())); } } for(int i : useCases) { System.out.println("Warning: Missing use case-" + i); } } public static void main(String[] args) { List<Integer> useCases = new ArrayList<Integer>(); Collections.addAll(useCases, 47, 48, 49, 50); trackUseCases(userCases, PasswordUtils.class); }} |
解决泛型擦除
现在有下面这样一个业务场景,我们有一个泛型集合类List<Class<? extends Pet>>,我们需要统计出这个集合类中每种具体的Pet有多少个。由于Java的泛型擦除,注意类似List<? extends Pet>的做法肯定是不行的,因为编译器做了静态类型检查之后,到了运行期间JVM会将集合中的对象都视为Pet,但是并不会知道Pet代表的究竟是Cat还是Dog,所以到了运行期间对象的类型信息其实全部丢失了。p.s: 关于泛型擦除:我在上一篇文章里面有详细解释,感兴趣的朋友可以看一看。
为了实现我们上面的例子,我们先来定义几个类:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public class Pet extends Individual { public Pet(String name) { super(name); } public Pet() { super(); }}public class Cat extends Pet { public Cat(String name) { super(name); } public Cat() { super(); }}public class Dog extends Pet { public Dog(String name) { super(name); } public Dog() { super(); }}public class EgyptianMau extends Cat { public EgyptianMau(String name) { super(name); } public EgyptianMau() { super(); }}public class Mutt extends Dog { public Mutt(String name) { super(name); } public Mutt() { super(); }} |
上面的Pet类继承自Individual,Individual类的的实现稍微复杂一点,我们实现了Comparable接口,重新自定义了类的比较规则,如果不是很明白的话,也没有关系,我们已经将它抽象出来了,所以不理解实现原理也没有关系。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
public class Individual implements Comparable<Individual> { private static long counter = 0; private final long id = counter++; private String name; // name is optional public Individual(String name) { this.name = name; } public Individual() {} public String toString() { return getClass().getSimpleName() + (name == null ? "" : " " + name); } public long id() { return id; } public boolean equals(Object o) { return o instanceof Individual && id == ((Individual)o).id; } public int hashCode() { int result = 17; if (name != null) { result = 37 * result + name.hashCode(); } result = 37 * result + (int) id; return result; } public int compareTo(Individual arg) { // Compare by class name first: String first = getClass().getSimpleName(); String argFirst = arg.getClass().getSimpleName(); int firstCompare = first.compareTo(argFirst); if (firstCompare != 0) { return firstCompare; } if (name != null && arg.name != null) { int secendCompare = name.compareTo(arg.name); if (secendCompare != 0) { return secendCompare; } } return (arg.id < id ? -1 : (arg.id == id ? 0 : 1)); }} |
下面创建了一个抽象类PetCreator,以后我们通过调用arrayList()方法便可以直接获取相关Pet类的集合。这里使用到了我们上面没有提及的newInstance()方法,它会返回Class类所真正指代的类的实例,这是什么意思呢?比如说声明new Dog().getClass().newInstance()和直接new Dog()是等价的。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
public abstract class PetCreator { private Random rand = new Random(47); // The List of the different getTypes of Pet to create: public abstract List<Class<? extends Pet>> getTypes(); public Pet randomPet() { // Create one random Pet int n = rand.nextInt(getTypes().size()); try { return getTypes().get(n).newInstance(); } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } public Pet[] createArray(int size) { Pet[] result = new Pet[size]; for (int i = 0; i < size; i++) { result[i] = randomPet(); } return result; } public ArrayList<Pet> arrayList(int size) { ArrayList<Pet> result = new ArrayList<Pet>(); Collections.addAll(result, createArray(size)); return result; }} |
接下来我们来实现上面这一个抽象类,解释一下下面的代码,在下面的代码中,我们声明了两个集合类,allTypes和types,其中allTypes中包含了我们呢上面所声明的所有类,但是我们具体的类型实际上只有两种即Mutt和EgypianMau,所以我们真正需要new出来的宠物只是types中所包含的类型,以后我们通过调用getTypes()便可以得到types中所包含的所有类型。
|
1
2
3
4
5
6
7
8
9
10
|
public class LiteralPetCreator extends PetCreator { @SuppressWarnings("unchecked") public static final List<Class<? extends Pet>> allTypes = Collections.unmodifiableList( Arrays.asList(Pet.class, Dog.class, Cat.class, Mutt.class, EgyptianMau.class)); private static final List<Class<? extends Pet>> types = allTypes.subList( allTypes.indexOf(Mutt.class), allTypes.size()); public List<Class<? extends Pet>> getTypes() { return types; }} |
总体的逻辑已经完成了,最后我们实现用来统计集合中相关Pet类个数的TypeCounter类。解释一下isAssignalbeFrom()方法,它可以判断一个反射类是某个反射类的子类或者间接子类。而getSuperclass()顾名思义就是得到某个反射类的父类了。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
public class TypeCounter extends HashMap<Class<?>, Integer> { private Class<?> baseType; public TypeCounter(Class<?> baseType) { this.baseType = baseType; } public void count(Object obj) { Class<?> type = obj.getClass(); if (!baseType.isAssignableFrom(type)) { throw new RuntimeException( obj + " incorrect type " + type + ", should be type or subtype of " + baseType); } countClass(type); } private void countClass(Class<?> type) { Integer quantity = get(type); put(type, quantity == null ? 1 : quantity + 1); Class<?> superClass = type.getSuperclass(); if (superClass != null && baseType.isAssignableFrom(superClass)) { countClass(superClass); } } @Override public String toString() { StringBuilder result = new StringBuilder("{"); for (Map.Entry<Class<?>, Integer> pair : entrySet()) { result.append(pair.getKey().getSimpleName()); result.append("="); result.append(pair.getValue()); result.append(", "); } result.delete(result.length() - 2, result.length()); result.append("} "); return result.toString(); }} |
References
java反射基础知识(四)反射应用实践的更多相关文章
- Java的基础知识四
一.Java 流(Stream).文件(File)和IO Java.io 包几乎包含了所有操作输入.输出需要的类.所有这些流类代表了输入源和输出目标. Java.io 包中的流支持很多种格式,比如:基 ...
- C#基础知识回顾-- 反射(3)
C#基础知识回顾-- 反射(3) 获取Type对象的构造函数: 前一篇因为篇幅问题因为篇幅太短被移除首页,反射这一块还有一篇“怎样在程序集中使用反射”, 其他没有什么可以写的了,前两篇主要是铺垫, ...
- C#基础知识回顾-- 反射(1)
C#基础知识回顾-- 反射(1) 反射(reflection)是一种允许用户获得类型信息的C#特性.术语“反射”源自于它的工作方式: Type对象映射它所代表的底层对象.对Type对象进行查询可以 ...
- 什么才是java的基础知识?
近日里,很多人邀请我回答各种j2ee开发的初级问题,我无一都强调java初学者要先扎实自己的基础知识,那什么才是java的基础知识?又怎么样才算掌握了java的基础知识呢?这个问题还真值得仔细思考. ...
- java必备基础知识(一)
学习的一点建议: 每一门语言的学习都要从基础知识开始,学习是一个过程,"万丈高楼平地起",没有一个好的地基,想必再豪华的高楼大厦终究有一天会倒塌.因此,我们学习知识也要打牢根基,厚 ...
- JAVA相关基础知识
JAVA相关基础知识 1.面向对象的特征有哪些方面 1.抽象: 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面.抽象并不打算了解全部问题,而只是选择其中的一部分, ...
- Java 多线程——基础知识
java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...
- Java语言基础(四)
Java语言基础(四) 一.基本数据类型(8) byte 8位 使用较少,一般用于网络传输: -128-+127 short 16位 不常用 -32768-+32767 int 32位 常用 ...
- java部分基础知识整理----百度脑图版
近期发现,通过百度脑图可以很好的归纳总结和整理知识点,本着学习和复习的目的,梳理了一下java部分的知识点,不定期更新,若有不恰之处,请指正,谢谢! 脑图链接如下:java部分基础知识整理----百度 ...
- Python基础知识(四)
Python基础知识(四) 一丶列表 定义格式: 是一个容器,由 [ ]表示,元素与元素之间用逗号隔开. 如:name=["张三","李四"] 作用: 存储任意 ...
随机推荐
- C++Primer学习笔记《2》
数组是一种复合类型,由类型名+数组名+维度组成. 数组定义中的类型能够是C++基本内置类型.也能够是类类型的.数组元素的类型能够是除了引用类型以外的其它不论什么类型.没有全部的元素都是引用的数组. 数 ...
- 第二百一十节,jQuery EasyUI,SearchBox(搜索框)组件
jQuery EasyUI,SearchBox(搜索框)组件 学习要点: 1.加载方式 2.属性列表 3.方法列表 本节课重点了解 EasyUI 中 SearchBox(搜索框)组件的使用方法,这个组 ...
- Java实现验证码的制作
验证码概述 为什么使用验证码? 验证码(CAPTCHA)是一种全自动程序.主要是为了区分“进行操作的是不是人”.如果没有验证码机制,将会导致以下的问题: 对特定网站不断进行登录,破解密码: 对某个网站 ...
- RedMine Email notifications configure for MS Exchange
Boss需要用到RedMine(project management) open source. 由于不熟悉MS的SMTP服务,BOSS说他配好了,然后配置到Email notification时候, ...
- Android 热修复 Tinker接入及源代码浅析
本文已在我的公众号hongyangAndroid首发.转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/54882693本文出自张鸿 ...
- Date类、DateFormat类和Calendar类
1.Date类 常用方法:long getTime():返回1970年1月1日00:00:00以来的毫秒值,把日期对象转换成毫秒值 2.DateFormat类 DateFormat类是日期/时间格式化 ...
- Leetcode-Permuation Sequence
The set [1,2,3,…,n] contains a total of n! unique permutations. By listing and labeling all of the p ...
- AsyncTaskLoader设计原理大揭秘
简介 在Android异步处理之AsyncTaskLoader简单使用中我简单的介绍了一下AsyncTaskLoader的基本用法和使用场景,对AsyncTaskLoader还不是很熟悉的小伙伴可以先 ...
- js中的typeof name
js中的name 使用typeof name得到 string.. 因为name是全局变量,可以在任意浏览器中使用 . cosole.dir(window)查看.. console.log(type ...
- 页面的日志服务 web页面渲染 服务 ; 服务耦合带来的问题
情景: 虽然对web服务做了3节点的负载均衡,但是由于埋点数据的落盘的代码也在相同服务器上,导致当flume处理日志的吞吐量达到瓶颈时,3节点的请求积压,挤占服务器资源,导致接口数据处理迟缓,页面加载 ...