反射基础
在应用反射机制之前,首先我们先来看一下如何获取一个对象对应的反射类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
,所以到了运行期间对象的类型信息其实全部丢失了。
为了实现我们上面的例子,我们先来定义几个类:
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();
}
}
|
喜欢的朋友可以点赞关注,一起学习进步
- Java 反射机制应用实践
反射基础 p.s: 本文需要读者对反射机制的API有一定程度的了解,如果之前没有接触过的话,建议先看一下官方文档的Quick Start(https://docs.oracle.com/javase/ ...
- 一个例子让你了解Java反射机制
本文来自:blog.csdn.net/ljphhj JAVA反射机制: 通俗地说,反射机制就是可以把一个类,类的成员(函数,属性),当成一个对象来操作,希望读者能理解,也就是说,类,类的成员,我们在运 ...
- (转)个例子让你了解Java反射机制
个例子让你了解Java反射机制 原文地址:http://blog.csdn.net/ljphhj/article/details/12858767 JAVA反射机制: 通俗地说,反射机制就是可 ...
- (转)JAVA反射机制理解
JAVA反射机制: 通俗地说,反射机制就是可以把一个类,类的成员(函数,属性),当成一个对象来操作,希望读者能理解,也就是说,类,类的成员,我们在运行的时候还可以动态地去操作他们. 理论的东东太多也没 ...
- 浅谈java反射机制
目录 什么是反射 初探 初始化 类 构造函数 属性 方法 总结 思考 什么是反射 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意 ...
- 一文带你了解Java反射机制
想要获取更多文章可以访问我的博客 - 代码无止境. 上周上班的时候解决一个需求,需要将一批数据导出到Excel.本来公司的中间件组已经封装好了使用POI生成Excel的工具方法,但是无奈产品的需求里面 ...
- 第28章 java反射机制
java反射机制 1.类加载机制 1.1.jvm和类 运行Java程序:java 带有main方法的类名 之后java会启动jvm,并加载字节码(字节码就是一个类在内存空间的状态) 当调用java命令 ...
- Java反射机制
Java反射机制 一:什么事反射机制 简单地说,就是程序运行时能够通过反射的到类的所有信息,只需要获得类名,方法名,属性名. 二:为什么要用反射: 静态编译:在编译时确定类型,绑定对象,即通过 ...
- java基础知识(十一)java反射机制(上)
java.lang.Class类详解 java Class类详解 一.class类 Class类是java语言定义的特定类的实现,在java中每个类都有一个相应的Class对象,以便java程序运行时 ...
随机推荐
- js实现点击切换显示隐藏,点击其它位置再隐藏
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- python爬虫(1)——urllib包
人生苦短,我用python! 一.关于爬虫 鉴于我的windos环境使用命令行感觉非常不便,也懒得折腾虚拟机,于是我选择了一个折中的办法--Cmder.它的下载地址是:cmder.net Cmder是 ...
- 【Tools】ubuntu16.04升级Python2.7到3.5
最近开始学Python,但我发现我ubuntu16.04上默认的Python是2.7,并不是3,x 于是准备Python升级,记录安装过程给初学者参考一下. 1.先取得管理员权限, 个人习惯先取得管理 ...
- iOS开发中UIPopoverController的使用详解
这篇文章主要介绍了iOS开发中UIPopoverController的使用,代码基于传统的Objective-C,需要的朋友可以参考下 一.简单介绍 1.什么是UIPopoverController ...
- 如何为MySQL服务器和客户机启用SSL
本文来自我的github pages博客http://galengao.github.io/ 即www.gaohuirong.cn 摘要: mysql5.7后有ssl新特性 自己搭建mysql ent ...
- CENTOS6.6下nmon的监控
本文来自我的github pages博客http://galengao.github.io/ 即www.gaohuirong.cn Installing Nmon By default nmon is ...
- 阿里云Aliyun_server
一.云概念 云主机是基于云计算平台的一种虚拟的主机服务器产品 云服务器(99.999%安全性,扩展性) 特点: 1.资源分配配置灵活,安全性能强. 2.优于VPS和独立服务器产品.通俗的理解: 云主机 ...
- Flex进度条
Flex中,进度条的皮肤,以及使用Timer让它自动增加~ mxml中: <mx:ProgressBar id="proBar" verticalCenter="0 ...
- java1环境与简介
java1环境与简介 Ⅰ 个人简介 陈鹏 联系方式:15828682774 2012 年至今,从事软件开发 5 年. 1 年新加坡海外工作经历. 先后在民企.外企.创业公司做过开发. 熟悉 JAV ...
- angular aot编译报错 ERROR in ./src/main.ts 解决方法
昨天打包项目时遇到下图这样的错误: 开始以为了某些模块存在但未使用,折腾一番无果,后来升级angular-cli就搞定了,方法很简单: 1.删掉node_modules 2.更改package.jso ...