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.C public 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=["张三","李四"] 作用: 存储任意 ...
随机推荐
- 免安装Oracleclient和PL/SQL
写在前面: Oracle是典型的C/S结构,服务端提供oracle服务的实例,主要用于数据库的管理,对象的管理与存储.数据的 存储.查询.数据库资源的监控.监听等一些服务. 而client仅仅是一个与 ...
- 今天升级netbean出错
出现:无法初始化 UI netbean 由于ubuntun装的是open jdk 直接删除open jdk就可以 sudo apt-get autoremove open-jdk*
- Taxi Cab Scheme UVALive - 3126 最小路径覆盖解法(必须是DAG,有向无环图) = 结点数-最大匹配
/** 题目:Taxi Cab Scheme UVALive - 3126 最小路径覆盖解法(必须是DAG,有向无环图) = 结点数-最大匹配 链接:https://vjudge.net/proble ...
- android定义dialog
对于一些特殊的dialog,我们想自定义一些特殊的样式.这时候假设使用dialog指定的一些功能.是无法实现我们的需求的,这时候就要自己去定制实现dialog了,如今这个样例是我从stackoverf ...
- 动态添加js的方法
var Skip={};//获取XMLHttpRequest对象(提供客户端同http服务器通讯的协议)Skip.getXmlHttpRequest=function (){ if ( window. ...
- WCF系列 Restful WCF
由于项目需要,需要完成移动端与服务端以json格式的数据交互,所以研究了Restful WCF相关内容,以实现ios端,android端与浏览器端能够与后台服务交互. 那么首先我们来了解下什么是Res ...
- greenplum全量恢复gprecoverseg -F出现Unable to connect to database时的相关分析及解决方法
之前有两位朋友碰到过在对greenplum的系统构架更改后,出现全量恢复gprecoverseg -F也无法正常执行的情况. 报错信息为Unable to connect to database. R ...
- 从零开始开发一个vue组件打包并发布到npm (把vue组件打包成一个可以直接引用的js文件)
自己写的组件 有的也挺好的,为了方便以后用自己再用或者给别人用,把组件打包发布到npm是最好不过了,本次打包支持 支持正常的组件调用方式,也支持Vue.use, 也可以直接引用打包好的js文件, 配合 ...
- Android 实现个性的ViewPager切换动画 实战PageTransformer(兼容Android3.0下面)
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/40411921.本文出自:[张鸿洋的博客] 1.概述 之前写过一篇博文:Andro ...
- Tomcat unable to start within 45 seconds.
解决的方法当然是设定这个时间,让其大于45秒,修改在当前项目所在的workspace\.metadata\.plugins\org.eclipse.wst.server.core\servers.xm ...