Reflflection
(反射)是
Java
被视为动态语言的关键,反射机制允许程序在执行期借助于
Reflflection API
取
得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
Class<?> aClass = Class.forName("com.kuang.reflection.User");
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这 个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。
public class Test {
public static void main(String[] args) {
try {
Class<?> aClass = Class.forName("com.kuang.reflection.User");
Class<?> aClass2 = Class.forName("com.kuang.reflection.User");
System.out.println(aClass);
System.out.println(aClass2);
System.out.println(aClass.hashCode());
System.out.println(aClass2.hashCode());
System.out.println(aClass == aClass2);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
输出结果:
测试各种类型获得Class对象的方式
package com.kuang.reflection;
public class Test1 {
//测试各种类型获得Class对象的方式
public static void main(String[] args) throws ClassNotFoundException{
User user = new User();
System.out.println("这个User是"+user+user.hashCode());
//已知某个类的实例,调用该实例的getClass()方法获取Class对象
Class<? extends User> c1 = user.getClass();
System.out.println("获得class办法一:通过对象获得"+c1+c1.hashCode());
//已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出
ClassNotFoundException
Class<?> c2 = Class.forName("com.kuang.reflection.User");
System.out.println("获得class办法二:通过字符串获得(包名+类名)"+c2+c2.hashCode());
//若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高。
Class<User> c3 = User.class;
System.out.println("获得class办法三:通过类的静态成员class获得"+c3+c3.hashCode());
Class c4 = Integer.TYPE;
System.out.println("获得class办法四:只针对内置的基本数据类型"+c4+c4.hashCode());
Class c5 = c2.getSuperclass();
System.out.println("获得class办法五:获得父类类型"+c1+c1.hashCode());
}
}
输出结果:
Class类的常用方法
Java内存分析
有了Class对象,能做什么?
创建类的对象:调用
Class
对象的
newInstance()方法
类必须有一个无参数的构造器。
类的构造器的访问权限需要足够
思考?难道没有无参的构造器就不能创建对象了吗?只要在操作的时候明确的调用类中的构造器,
并将参数传递进去之后,才可以实例化操作。
步骤如下:
通过Class
类的
g
etDeclaredConstructor
(Class … parameterTypes)
取得本类的指定形参类型
的构造器
向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。
通过Constructor
实例化对象
package com.kuang.single;
import java.lang.reflect.Constructor;
//enmu是什么?本身也是一个class类
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws Exception {
EnumSingle instance = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSingle instance2 = declaredConstructor.newInstance();
//java.lang.NoSuchMethodException: com.ph.single.EnumSingle.<init>()
System.out.println(instance);
System.out.println(instance2);
}
}
输出结果:
反射源码:
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor; // read volatile
这就是判断如果是枚举类型那么就只接抛出异常了, 也就不能进行构造了
枚举类防止反射机制的破坏
调用指定的方法
通过反射,调用类中的方法,通过Method
类完成。
通过Class
类的
getMethod(String name,Class…parameterTypes)
方法取得一个
Method
对
象,并设置此方法操作时所需要的参数类型。
之后使用Object invoke(Object obj, Object[] args)
进行调用,并向方法中传递要设置的
obj
对
象的参数信息。
Object 对应原方法的返回值,若原方法无返回值,此时返回
null
若原方法若为静态方法,此时形参Object obj
可为
null
若原方法形参列表为空,则Object[] args
为
null
若原方法声明为
private,则需要在调用此invoke()方法前,显式调用方法对象的
setAccessible(true)方法,将可访问private的方法。
setAccessible
Method和
Field
、
Constructor
对象都有
setAccessible()
方法。
setAccessible作用是启动和禁用访问安全检查的开关。
参数值为true
则指示反射的对象在使用时应该取消
Java
语言访问检查。
提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true
。使得原本无法访问的私有成员也可以访问
参数值为false
则指示反射的对象应该实施
Java
语言访问检查
反射操作泛型
Java
采用泛型擦除的机制来引入泛型
, Java
中的泛型仅仅是给编译器
javac
使用的
,
确保数据的安全性 和免去强制类型转换问题 ,
但是
,
一旦编译完成
,
所有和泛型有关的类型全部擦除
为了通过反射操作这些类型
, Java
新增了
ParameterizedType , GenericArrayType , TypeVariable
和
WildcardType
几种类型来代表不能被归一到
Class
类中的类型但是又和原始类型齐名的类型
.
ParameterizedType :
表示一种参数化类型
,
比如
Collection
GenericArrayType :
表示一种元素类型是参数化类型或者类型变量的数组类型
TypeVariable :
是各种类型变量的公共父接口
WildcardType :
代表一种通配符类型表达式