动态语言,是指程序在运行时可以改变其结构:新的函数可以被引进,已有的函数可以被删除等在结构上的变化。比如众所周知的ECMAScript(JavaScript)便是一个动态语言。除此之外如Ruby、Python等也都属于动态语言,而C、C++等语言则不属于动态语言。(引自: 百度百科)
var execString = "alert(Math.floor(Math.random()*10));";
eval(execString);
Class类提供了大量的实例方法来获取该Class对象所对应的详细信息,Class类大致包含如下方法,其中每个方法都包含多个重载版本,因此这里只是做简单的介绍,详细请参考JDK文档
获取内容 | 方法签名 |
---|---|
构造器 | Constructor getConstructor(Class… parameterTypes) |
包含的方法 | Method getMethod(String name, Class… parameterTypes) |
包含的属性 | Field getField(String name) |
包含的Annotation | < A extends Annotation> A getAnnotation(Class< A> annotationClass) |
内部类 | Class[] getDeclaredClasses() |
外部类 | Class getDeclaringClass() |
所实现的接口 | Class[] getInterfaces() |
修饰符 | int getModifiers() |
所在包 | Package getPackage() |
类名 | String getName() |
简称 | String getSimpleName() |
判断内容 | 方法签名 |
---|---|
注解类型? | boolean isAnnotation() |
使用了该Annotation修饰? | boolean isAnnotationPresent(Class< ? extends Annotation> annotationClass) |
匿名类? | boolean isAnonymousClass() |
数组? | boolean isArray() |
枚举? | boolean isEnum() |
原始类型? | boolean isPrimitive() |
接口? | boolean isInterface() |
obj是否是该Class的实例 | boolean isInstance(Object obj) |
通过反射来生成对象的方式有两种:
通过第一种方式来创建对象比较常见, 像Spring这种框架都需要根据配置文件(如applicationContext.xml)信息来创建Java对象,从配置文件中读取的只是某个类的全限定名字符串,程序需要根据该字符串来创建对应的实例,就必须使用默认的构造器来反射对象.
下面我们就模拟Spring实现一个简单的对象池, 该对象池会根据文件读取key-value对, 然后创建这些对象, 并放入Map中.
{
"objects": [
{
"id": "id1",
"class": "com.java.test.User"
},
{
"id": "id2",
"class": "com.java.test.Bean"
}
]
}
public class ObjectPool {
private Map pool;
private ObjectPool(Map pool) {
this.pool = pool;
}
private static Object getInstance(String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
return Class.forName(className).newInstance();
}
private static JSONArray getObjects(String config) throws IOException {
Reader reader = new InputStreamReader(ClassLoader.getSystemResourceAsStream(config));
return JSONObject.parseObject(CharStreams.toString(reader)).getJSONArray("objects");
}
// 根据指定的JSON配置文件来初始化对象池
public static ObjectPool init(String config) {
try {
JSONArray objects = getObjects(config);
ObjectPool pool = new ObjectPool(new HashMap());
if (objects != null && objects.size() != 0) {
for (int i = 0; i < objects.size(); ++i) {
JSONObject object = objects.getJSONObject(i);
if (object == null || object.size() == 0) {
continue;
}
String id = object.getString("id");
String className = object.getString("class");
pool.putObject(id, getInstance(className));
}
}
return pool;
} catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public Object getObject(String id) {
return pool.get(id);
}
public void putObject(String id, Object object) {
pool.put(id, object);
}
public void clear() {
pool.clear();
}
}
public class Client {
@Test
public void client() {
ObjectPool pool = ObjectPool.init("config.json");
User user = (User) pool.getObject("id1");
System.out.println(user);
Bean bean = (Bean) pool.getObject("id2");
System.out.println(bean);
}
}
public class User {
private int id;
private String name;
private String password;
public int getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
public class Bean {
private Boolean usefull;
private BigDecimal rate;
private String name;
public Boolean getUsefull() {
return usefull;
}
public void setUsefull(Boolean usefull) {
this.usefull = usefull;
}
public BigDecimal getRate() {
return rate;
}
public void setRate(BigDecimal rate) {
this.rate = rate;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Bean{" +
"usefull=" + usefull +
", rate=" + rate +
", name='" + name + '\'' +
'}';
}
}
注意: 需要在pom.xml中添加如下依赖:
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.7version>
dependency>
<dependency>
<groupId>com.google.guavagroupId>
<artifactId>guavaartifactId>
<version>18.0version>
dependency>
当获取到某个类对应的Class对象之后, 就可以通过该Class对象的getMethod来获取一个Method数组或Method对象.每个Method对象对应一个方法,在获得Method对象之后,就可以通过调用invoke方法来调用该Method对象对应的方法.
@CallerSensitive
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
{
...
}
下面我们对上面的对象池加强:可以看到Client获取到的对象的成员变量全都是默认值,既然我们已经使用了JSON这么优秀的工具,我们又学习了动态调用对象的方法,那么我们就通过配置文件来给对象设置值(在对象创建时), 新的配置文件形式如下:
{
"objects": [
{
"id": "id1",
"class": "com.java.test.User",
"fields": [
{
"name": "id",
"value": 101
},
{
"name": "name",
"value": "feiqing"
},
{
"name": "password",
"value": "ICy5YqxZB1uWSwcVLSNLcA=="
}
]
},
{
"id": "id2",
"class": "com.java.test.Bean",
"fields": [
{
"name": "usefull",
"value": true
},
{
"name": "rate",
"value": 3.14
},
{
"name": "name",
"value": "bean-name"
}
]
},
{
"id": "id3",
"class": "com.java.test.ComplexBean",
"fields": [
{
"name": "name",
"value": "complex-bean-name"
},
{
"name": "refBean",
"ref": "id2"
}
]
}
]
}
其中fields代表该Bean所包含的属性, name为属性名称, value为属性值(属性类型为JSON支持的类型), ref代表引用一个对象(也就是属性类型为Object,但是一定要引用一个已经存在了的对象)
public class ObjectPool {
private Map pool;
private ObjectPool(Map pool) {
this.pool = pool;
}
private static JSONArray getObjects(String config) throws IOException {
Reader reader = new InputStreamReader(ClassLoader.getSystemResourceAsStream(config));
return JSONObject.parseObject(CharStreams.toString(reader)).getJSONArray("objects");
}
private static Object getInstance(String className, JSONArray fields)
throws ClassNotFoundException, NoSuchMethodException,
IllegalAccessException, InstantiationException, InvocationTargetException {
// 配置的Class
Class> clazz = Class.forName(className);
// 目标Class的实例对象
Object targetObject = clazz.newInstance();
if (fields != null && fields.size() != 0) {
for (int i = 0; i < fields.size(); ++i) {
JSONObject field = fields.getJSONObject(i);
// 需要设置的成员变量名
String fieldName = field.getString("name");
// 需要设置的成员变量的值
Object fieldValue;
if (field.containsKey("value")) {
fieldValue = field.get("value");
} else if (field.containsKey("ref")) {
String refBeanId = field.getString("ref");
fieldValue = OBJECTPOOL.getObject(refBeanId);
} else {
throw new RuntimeException("neither value nor ref");
}
String setterName = "set" +
fieldName.substring(0, 1).toUpperCase() +
fieldName.substring(1);
// 需要设置的成员变量的setter方法
Method setterMethod = clazz.getMethod(setterName, fieldValue.getClass());
// 调用setter方法将值设置进去
setterMethod.invoke(targetObject, fieldValue);
}
}
return targetObject;
}
private static ObjectPool OBJECTPOOL;
// 创建一个对象池的实例(保证是多线程安全的)
private static void initSingletonPool() {
if (OBJECTPOOL == null) {
synchronized (ObjectPool.class) {
if (OBJECTPOOL == null) {
OBJECTPOOL = new ObjectPool(new ConcurrentHashMap());
}
}
}
}
// 根据指定的JSON配置文件来初始化对象池
public static ObjectPool init(String config) {
// 初始化pool
initSingletonPool();
try {
JSONArray objects = getObjects(config);
for (int i = 0; objects != null && i < objects.size(); ++i) {
JSONObject object = objects.getJSONObject(i);
if (object == null || object.size() == 0) {
continue;
}
String id = object.getString("id");
String className = object.getString("class");
// 初始化bean并放入池中
OBJECTPOOL.putObject(id, getInstance(className, object.getJSONArray("fields")));
}
return OBJECTPOOL;
} catch (IOException | ClassNotFoundException |
InstantiationException | IllegalAccessException |
NoSuchMethodException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
public Object getObject(String id) {
return pool.get(id);
}
public void putObject(String id, Object object) {
pool.put(id, object);
}
public void clear() {
pool.clear();
}
}
public class Client {
@Test
public void client() {
ObjectPool pool = ObjectPool.init("config.json");
User user = (User) pool.getObject("id1");
System.out.println(user);
Bean bean = (Bean) pool.getObject("id2");
System.out.println(bean);
ComplexBean complexBean = (ComplexBean) pool.getObject("id3");
System.out.println(complexBean);
}
}
public class ComplexBean {
private String name;
private Bean refBean;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Bean getRefBean() {
return refBean;
}
public void setRefBean(Bean refBean) {
this.refBean = refBean;
}
@Override
public String toString() {
return "ComplexBean{" +
"name='" + name + '\'' +
", refBean=" + refBean +
'}';
}
}
Spring框架就是通过这种方式将成员变量值以及依赖对象等都放在配置文件中进行管理的,从而实现了较好地解耦(不过Spring是通过XML作为配置文件).
通过Class对象的的getField()方法可以获取该类所包含的全部或指定的成员变量Field,Filed提供了如下两组方法来读取和设置成员变量值.
注: getDeclaredXxx方法可以获取所有的成员变量,无论private/public;
public class Client {
@Test
public void client() throws NoSuchFieldException, IllegalAccessException {
User user = new User();
Field idFiled = User.class.getDeclaredField("id");
setAccessible(idFiled);
idFiled.setInt(user, 46);
Field nameFiled = User.class.getDeclaredField("name");
setAccessible(nameFiled);
nameFiled.set(user, "feiqing");
Field passwordField = User.class.getDeclaredField("password");
setAccessible(passwordField);
passwordField.set(user, "ICy5YqxZB1uWSwcVLSNLcA==");
System.out.println(user);
}
private void setAccessible(AccessibleObject object) {
object.setAccessible(true);
}
}
为了通过反射操作泛型以迎合实际开发的需要, Java新增了
java.lang.reflect.ParameterizedType;
java.lang.reflect.GenericArrayTypejava.lang.reflect.TypeVariable; java.lang.reflect.WildcardType;
几种类型来代表不能归一到Class类型但是又和原始类型同样重要的类型.
类型 | 含义 |
---|---|
ParameterizedType | 一种参数化类型, 比如Collection< String> |
GenericArrayType | 一种元素类型是参数化类型或者类型变量的数组类型 |
TypeVariable | 各种类型变量的公共接口 |
WildcardType | 一种通配符类型表达式, 如? ? extends Number ? super Integer |
其中, 我们可以使用ParameterizedType来获取泛型信息.
public class Client {
private Map objectMap;
public void test(Map map, String string) {
}
public Map test() {
return null;
}
/**
* 测试属性类型
*
* @throws NoSuchFieldException
*/
@Test
public void testFieldType() throws NoSuchFieldException {
Field field = Client.class.getDeclaredField("objectMap");
Type gType = field.getGenericType();
// 打印type与generic type的区别
System.out.println(field.getType());
System.out.println(gType);
System.out.println("**************");
if (gType instanceof ParameterizedType) {
ParameterizedType pType = (ParameterizedType) gType;
Type[] types = pType.getActualTypeArguments();
for (Type type : types) {
System.out.println(type.toString());
}
}
}
/**
* 测试参数类型
*
* @throws NoSuchMethodException
*/
@Test
public void testParamType() throws NoSuchMethodException {
Method testMethod = Client.class.getMethod("test", Map.class, String.class);
Type[] parameterTypes = testMethod.getGenericParameterTypes();
for (Type type : parameterTypes) {
System.out.println("type -> " + type);
if (type instanceof ParameterizedType) {
Type[] actualTypes = ((ParameterizedType) type).getActualTypeArguments();
for (Type actualType : actualTypes) {
System.out.println("\tactual type -> " + actualType);
}
}
}
}
/**
* 测试返回值类型
*
* @throws NoSuchMethodException
*/
@Test
public void testReturnType() throws NoSuchMethodException {
Method testMethod = Client.class.getMethod("test");
Type returnType = testMethod.getGenericReturnType();
System.out.println("return type -> " + returnType);
if (returnType instanceof ParameterizedType) {
Type[] actualTypes = ((ParameterizedType) returnType).getActualTypeArguments();
for (Type actualType : actualTypes) {
System.out.println("\tactual type -> " + actualType);
}
}
}
}
Method/Constructor/Field/Element都继承了AccessibleObject,AccessibleObject类中有一个setAccessible方法:
public void setAccessible(boolean flag) throws SecurityException {
...
}
该方法有两个作用:
public class TestReflect {
@Before
public void testNoneReflect() {
User user = new User();
long start = System.currentTimeMillis();
for (long i = 0; i < Integer.MAX_VALUE; ++i) {
user.getName();
}
long count = System.currentTimeMillis() - start;
System.out.println("没有反射, 共消耗 <" + count + "> 毫秒");
}
@Test
public void testNotAccess() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
User user = new User();
Method method = Class.forName("com.java.test.User").getMethod("getName");
long start = System.currentTimeMillis();
for (long i = 0; i < Integer.MAX_VALUE; ++i) {
method.invoke(user, null);
}
long count = System.currentTimeMillis() - start;
System.out.println("没有访问权限, 共消耗 <" + count + "> 毫秒");
}
@After
public void testUseAccess() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
User user = new User();
Method method = Class.forName("com.java.test.User").getMethod("getName");
method.setAccessible(true);
long start = System.currentTimeMillis();
for (long i = 0; i < Integer.MAX_VALUE; ++i) {
method.invoke(user, null);
}
long count = System.currentTimeMillis() - start;
System.out.println("有访问权限, 共消耗 <" + count + "> 毫秒");
}
}
使用反射会比直接调用慢3000毫秒,但是前提是该方法会执行20E+次,因此在我们的实际开发中,其实是不用担心反射机制带来的性能消耗的,而且禁用访问权限检查,也会有性能的提升.