Class对象的获取:
有三个方法:
1、对象的getClass()方法;
2、类的.class(最安全/性能最好)属性
3、运用Class.forName(String className)动态加载类,className需要是类的全限定名(最常用)
从Class中获取信息
获取内容 | 方法签名 |
---|---|
构造器 | 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() |
一些判断类本身的消息
判断内容 | 方法签名 |
---|---|
注解类型? | 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) |
使用反射生成并操作对象
Method Constructor Field这些类都实现了java.lang.reflect.Member接口,程序可以通过Method对象来执行相应的方法,通过Constructor对象来调用对应的构造器创建实例,通过Filed对象直接访问和修改对象的成员变量值.
创建对象
1、使用Class对象的newInstance()方法来创建该Class对象对应类的实例(这种方式要求该Class对象的对应类有默认构造器);
2、先使用Class对象获取指定的Constructor对象, 再调用Constructor对象的newInstance()方法来创建该Class对象对应类的实例(通过这种方式可以选择指定的构造器来创建实例);
调用方法
当获取到某个类对应的Class对象之后, 就可以通过该Class对象的getMethod来获取一个Method数组或Method对象.每个Method对象对应一个方法,在获得Method对象之后,就可以通过调用invoke方法来调用该Method对象对应的方法.
私有方法要用getDeclareMethod来获取,还要打破类的封装
访问成员变量
getXXX、setXXX,
1、getXxx(Object obj): 获取obj对象的该成员变量的值, 此处的Xxx对应8中基本类型,如果该成员变量的类型是引用类型, 则取消get后面的Xxx;
2、setXxx(Object obj, Xxx val): 将obj对象的该成员变量值设置成val值.此处的Xxx对应8种基本类型, 如果该成员类型是引用类型, 则取消set后面的Xxx;
3、getDeclaredXxx方法可以获取所有的成员变量,无论private/public;
使用反射获取泛型信息
类型 | 含义 |
---|---|
ParameterizedType | 一种参数化类型, 比如Collection<String> |
GenericArrayType | 一种元素类型是参数化类型或者类型变量的数组类型 |
TypeVariable | 各种类型变量的公共接口 |
WildcardType | 一种通配符类型表达式, 如? ? extends Number ? super Integer |
使用功能”.class”来创建Class对象的引用时,不会自动初始化该Class对象,使用forName()会自动初始化该Class对象
编译器将检查类型向下转型是否合法,如果不合法将抛出异常。向下转换类型前,可以使用instanceof判断。
class Base { }
class Derived extends Base { }
public class Main {
public static void main(String[] args) {
Base base = new Derived();
if (base instanceof Derived) {
// 这里可以向下转换了
System.out.println("ok");
}
else {
System.out.println("not ok");
}
}
}
Class是模板,Object是具体,Type是Class的父类(接口),So Everything is Class
数组:
//创建一个int类型的数组,长度为3
int[] intArray = (int[])Array.newInstance(int.class,3);
//通过反射的形式,给数组赋值
for (int i = 0 ;i < intArray.length;i++){
Array.set(intArray,i,i + 2);
}
//通过反射的形式,得到数组中的值
for (int i = 0 ; i < intArray.length;i++){
System.out.println(Array.get(intArray,i));
}
上述就是创建数组,访问数组中的值利用反射方式。
对于得到一个数组的Class对象,简单的可以用int[].class,或者利用Class.forName的形式得到,写法比较奇怪:
Class clz = Class.forName("[I");
System.out.println(clz.getTypeName());
结果为:int[]
这个forName中的字符串,[表示是数组,I表示是int,float就是F,double就是D等等,如果要得到一个普通对象的数组,则用下面的形式:
Class stringClz = Class.forName("[Ljava.lang.String;");
[表示是数组,L的右边是类名,类型的右边是一个;
这种方式获取数组的Class对象实在是太繁琐了。
在得到数组的Class对象之后,就可以调用他的一些独特的方法,比如调用getComponentType来得到数组成员的类型信息,如int数组就是成员类型就是int。
System.out.println(clz.getComponentType().getTypeName());
结果为int
静态方法属性:
//创建类
Class<?> class1 = Class.forName("com.app.Util");
//获取 nameField 属性
Field nameField = class1.getDeclaredField( "name" ) ;
//获取 nameField 的值
String name_ = (String) nameField.get( nameField ) ;
//没有返回值,没有参数
Method getTipMethod1 = class1.getDeclaredMethod( "getTips" ) ;
getTipMethod1.invoke( null ) ;
//有返回值,没有参数
Method getTipMethod2 = class1.getDeclaredMethod( "getTip" ) ;
String result_2 = (String) getTipMethod2.invoke( null ) ;
//没有返回值,有参数
Method getTipMethod3 = class1.getDeclaredMethod( "getTip" , String.class ) ;
String result_3 = (String) getTipMethod3.invoke( null , "第三个方法" ) ;
//有返回值,有参数
Method getTipMethod4 = class1.getDeclaredMethod( "getTip" , int.class ) ;
String result_4 = (String) getTipMethod4.invoke( null , 1 ) ;
static方法调用时,不必得到对象示例,如下:
Class cls = Class.forName("chb.test.reflect.Student");
Method staticMethod = cls.getDeclaredMethod("hi",int.class,String.class);
staticMethod.invoke(cls,20,"chb");//这里不需要newInstance
//staticMethod.invoke(cls.newInstance(),20,"chb");
从属于类,不从属于对象
当参数是 int 类型 和 Integer 类型,反射获取方法不一样:分别是int.class和Integer.class
一般地,我们用instanceof关键字来判断是否为某个类的实例。同时我们也可以借助反射
public native boolean isInstance(Object obj);
利用反射创建数组
public static void testArray() throws ClassNotFoundException {
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,"fuck");
Array.set(array,3,"Scala");
Array.set(array,4,"Clojure");
//获取某一项的内容
System.out.println(Array.get(array,3));
}
其中的Array类为java.lang.reflect.Array类。我们通过Array.newInstance()创建数组对象,它的原型是:
public static Object newInstance(Class<?> componentType, int length)
throws NegativeArraySizeException {
return newArray(componentType, length);
}
如果是基本类型的包装类,则可以通过调用包装类的Type属性来获得该包装类的Class对象。
例如:
Class<?> clazz = Integer.TYPE;
参考1:JAVA中的反射机制
参考2:深入理解Java反射
参考3:Java 反射
参考4:java反射详解
参考5:java中的反射总结
参考6:详解Java反射各种应用
参考7:Java 反射 使用总结
参考8:通过Java反射调用方法
参考9:java 反射详解通俗易懂
参考10:Java反射机制
参考11:Java Reflection(一):Java反射指南
参考12:学习java应该如何理解反射?