干货:Java反射机制应用实践
2018-03-05 00:00
344 查看
反射基础
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 tocall 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 "nodescription" ; } |
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(Individualarg) { // 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 Pettocreate: public abstract List<Class<? extends Pet>> getTypes(); public PetrandomPet() { // 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(); } } |
相关文章推荐
- (转)Java反射机制应用实践
- [置顶] 机器学习 + 深度学习 + 计算机视觉 + 自然语言处理: 原理, 实践以及应用 --- 干货分享(持续更新…)
- Java反射机制应用实践
- Java反射机制应用实践
- Java反射机制应用实践
- 【反射】Java反射机制应用实践
- Java反射机制应用实践
- 干货!高德、VPGAME(老干爹)等MongoDB应用实践(暨MongoDB杭州用户会成立
- CAT架构的应用与实践 IT大咖说 - 大咖干货,不再错过
- Bluemix云平台实践:构建可穿戴应用(1)
- 【腾讯bugly干货分享】微信Android热补丁实践演进之路
- OpenStack架构企业IT应用的敏捷实践
- H5页面快速搭建之高级字体应用实践
- iOS7 最佳实践:一个天气应用案例(上)
- 【转】移动Web单页应用开发实践——页面结构化
- jquery 应用实践
- struts中的异常应用实践
- 现代质量管理方法的应用思考和实践
- 移动Web单页应用开发实践——页面结构化
- 【转载】干货来袭!Linux小白最佳实践:《超容易的Linux系统管理入门书》(连载七)LAMP集成安装