反射
反射概述:
Java中的反射(Reflection
)机制就是指在运行过程中,对于任意一个类,都能够知道这个类的属性和方法,对于任意一个对象都能够调用它的任意一个属性和方法,这种动态获取信息和动态调用方法的功能被称之为Java语言的反射机制。
反射的使用:
获取Class对象的三种方式
一、使用 Class.forName()
的静态方法
Class.forName(String className)
方法可以通过类或接口的名称(一个字符串或完全限定名)来获取对应的Class对象
Class<?> cls = Class.forName("java.lang.String");
二、直接获取某个类的Class(最安全,性能最好)
Class<String> cls = String.class;
三、调用某个对象的getClass()
方法
Class<String> cls = str.getClass();
判断是否为某个类的实例
instanceof
关键字来判断是否为某个类的实例。同时我们也可以借助反射中Class对象的*isInstance()
*方法来判断是否为某个类的实例,它是一个Native方法:
public native boolean isInstance(Object obj);
创建实例
通过反射来创建对象有两种方式
使用Class对象的newInstance()
方法
Class<?> c = String.class;
Object str = c.newInstance();
通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()
方法
// 获取String所对应的Class对象
Class<?> c = String.class;
// 获取String类带一个String参数的构造器
Constructor constructor = c.getConstructor(String.class);
// 根据构造器创建实例
Object obj = constructor.newInstance("23333");
System.out.println(obj);
获取方法
获取某个Class对象的方法集合,主要有以下几个方法:
getDeclaredMethods()
方法返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
public Method[] getDeclaredMethods() throws SecurityException
getMethods()
方法返回某个类的所有公用(public)方法,包括其继承类的公用方法。
public Method[] getMethods() throws SecurityException
getMethod()
方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象。
public Method getMethod(String name, Class<?>... parameterTypes)
测试:
public class Test {
public static void main(String[] args) throws Exception {
//通过Class.forName来创建反射对象
Class<?> user = Class.forName("com.ysh.fanshe.User");
//通过反射对象来获取User实例,调用了无参构造
Object o = user.newInstance();
//返回这个类所有的public方法,包括继承类的
Method[] methods = user.getMethods();
for (Method method : methods) {
//获取方法名
System.out.println(method.getName());
}
System.out.println("===========================");
//获取User类所有方法,不包括继承的
Method[] declaredMethods = user.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
//遍历所有方法名
System.out.println(declaredMethod.getName());
}
System.out.println("==========================");
//根据方法名(第一个参数)获取,后面的参数为参数列表中类的Class对象,是一个可变参数
Method add = user.getMethod("add", String.class);
System.out.println(add.getName());
//获取私有的
Method update = user.getDeclaredMethod("update");
System.out.println(update.getName());
}
}
class User{
public String add(String name){
System.out.println("添加成功");
return name;
}
public void delete(){
System.out.println("删除方法");
}
private void update(){
System.out.println("我是私有方法");
}
}
获取类变量
获取的方法同Method相似,主要是这几个方法,在此不再赘述:
Field getField(String name)
访问公有的成员变量。Field[] getDeclaredFields()
:所有已声明的成员变量。但不能得到其父类的成员变量。Field[] getFields()
和Field[] getDeclaredFields()
用法同上。
//获取属性,私有的,其他同理
Field name = user.getDeclaredField("name");
//getType获取该属性的类型,返回的是一个Class对象
//SimpleName获取一个类的简单名称,不带包路径的名
System.out.println(name.getType().getSimpleName());
//getName获取该属性的变量名,返回String
System.out.println(name.getName());
//getModifiers获取属性的修饰符,通过Modifier类的静态方法toString来转换为字符串
String s = Modifier.toString(name.getModifiers());
System.out.println(s);
调用方法(重点)
当我们从类中获取了一个方法后,我们就可以用invoke()方法来调用这个方法。invoke方法的原型为:
public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
//通过Class.forName来创建反射对象
Class<?> user = Class.forName("com.ysh.fanshe.User");
//通过反射对象来获取User实例,调用了无参构造
Object o = user.newInstance();
//根据方法名(第一个参数)获取,后面的参数为参数列表中类的Class对象,是一个可变参数
Method add = user.getMethod("add", String.class);
System.out.println(add.getName());
//invoke调用方法,第一个参数为对象,第二个参数为传入的参数,返回值为该方法的返回值
Object a = add.invoke(o, "易水寒");
System.out.println(a);
利用反射创建数组
数组在Java里是比较特殊的一种类型,它可以赋值给一个Object Reference
。下面我们看一看利用反射创建数组的例子:
public static void TestArray() throws ClassNotFoundException {
// 使用`java.lang.reflect.Array`反射创建长度为25的字符串数组.
Class<?> cls = Class.forName("java.lang.String");
Object array = Array.newInstance(cls, 25);
// 往数组里添加内容
Array.set(array,0, "hello");
Array.set(array,1, "Java");
Array.set(array,2, "Go");
Array.set(array,3, "Scala");
Array.set(array,4, "Clojure");
// 获取某一项的内容
System.out.println(Array.get(array, 3));
}
使用反射获取信息
Class类提供了大量的实例方法来获取该Class对象所对应的详细信息,Class类大致包含如下方法,其中每个方法都包含多个重载版本,因此我们只是做简单的介绍,详细请参考JDK文档。
1、获取类内信息
- 构造器:
Constructor<T> 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()
2、判断类本身信息的方法
- 是否注解类型:
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)
3、使用反射获取泛型信息
为了通过反射操作泛型以迎合实际开发的需要, Java新增了java.lang.reflect.ParameterizedType
、java.lang.reflect.GenericArrayType
、java.lang.reflect.TypeVariable
、java.lang.reflect.WildcardType
几种类型来代表不能归一到Class类型但是又和原始类型同样重要的类型。
ParameterizedType
: 一种参数化类型, 比如CollectionGenericArrayType
: 一种元素类型是参数化类型或者类型变量的数组类型TypeVariable
: 各种类型变量的公共接口WildcardType
: 一种通配符类型表达式, 如?
、? extends Number
、? super Integer
代码示例:
public class Client {
private Map<String, Object> objectMap;
public void test(Map<String, User> map, String string) {
}
public Map<User, Bean> 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(" actual 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(" actual type -> " + actualType);
}
}
}
}