Class类
- Class类用于描述Java应用程序中的类和接口,也就是一种数据类型
- Class类的实例由Java虚拟机和类加载器自动构造完成,本质上就是加载到内存中的运行时类
- Class类实际上是一个泛型类。例如”String.class”的类型是”Class
“
1.获取Class类
- 通过Object类中的
getClass()
方法来获取Class类型的实例 - 使用静态方法
Class.forName("java.lang.String")
来获取类名对应的Class对象 - 使用T.class的方式来获取对于类型的Class对象
- 使用包装类.TYPE的方式可以获取对应基本数据类型的Class对象
- 使用类加载器ClassLoader的方式获取指定类型的Class对象
2.常用方法
方法声明 | 功能介绍 |
---|---|
Constructor getConstructor(Class<?>… parameterTypes) | 用于获取此Class对象所表示类型中参数指定的公共构造方法 |
Constructor<?>[] getConstructors() | 用于获取此Class对象所表示类型中所有的公共构造方法 |
Field getDeclaredField(String name) | 用于获取此Class对象所表示类中参数指定的单个成员变量信息 |
Field[] getDeclaredFields() | 用于获取此Class对象所表示类中所有成员变量信息 |
Method getMethod(String name, Class<?>… parameterTypes) | 用于获取该Class对象表示类中名字为name参数为parameterTypes的指定公共成员方法 |
Method[] getMethods() | 用于获取该Class对象表示类中所有公共成员方法 |
Package getPackage() | 获取所在的包信息 |
Class<? super T> getSuperclass() | 获取继承的父类信息 |
Class<?>[] getInterfaces() | 获取实现的所有接口 |
Annotation[] getAnnotations() | 获取注解信息 |
Type[] getGenericInterfaces() | 获取泛型信息 |
Constructor类
- 通过
getConstructor().newInstance()
来构造对象。
Object o = Class.forName("cn.hzlim.mydemo.basis.Test")
.getConstructor(String.class, int.class)
.newInstance("小明", 18);
- 通过
getConstructors()
来获取所有的构造方法
Class<?> aClass = Class.forName("cn.hzlim.mydemo.basis.Test");
Constructor<?>[] constructors = aClass.getConstructors();
for (Constructor<?> constructor : constructors) { }
- 常用方法 | 方法声明 | 功能介绍 | | —- | —- | | T newInstance(Object… initargs) | 使用此Constructor对象描述的构造方法来构造Class对象代表类型的新实例 | | int getModifiers() | 获取方法的访问修饰符 | | String getName() | 获取方法的名称 | | Class<?>[] getParameterTypes() | 获取方法所有参数的类型 |
Field类
- 通过
getDeclaredField()
来获取类的字段
Field name = aClass.getDeclaredField("name");
- 通过
getDeclaredFields()
来获取类的全部字段
Field[] declaredFields = aClass.getDeclaredFields();
- 常用方法 | 方法声明 | 功能介绍 | | —- | —- | | Object get(Object obj) | 获取参数对象obj中此Field对象所表示成员变量的数值 | | void set(Object obj, Object value) | 将参数对象obj中此Field对象表示成员变量的数值修改为参数value | | void setAccessible(boolean flag) | 当实参传递true时,则反射对象在使用时应该取消 Java 语言访问检查 | | int getModifiers() | 获取成员变量的访问修饰符 | | Class<?> getType() | 获取成员变量的数据类型 | | String getName() | 获取成员变量的名称 |
Method类
- 通过
getMethod()
来获取类的公共成员方法,并设置参数
Object o = aClass.getConstructor().newInstance();
Method setName = aClass.getMethod("setName", String.class);
setName.invoke(o,"小明");
System.out.println(o);
- 通过
getMethods()
来获取类的全部公共成员方法
Method[] methods = aClass.getMethods();
- 常用方法 | 方法声明 | 功能介绍 | | —- | —- | | Object invoke(Object obj, Object… args) | 使用对象obj来调用此Method对象所表示的成员方法,实参传递args | | int getModifiers() | 获取方法的访问修饰符 | | Class<?> getReturnType() | 获取方法的返回值类型 | | String getName() | 获取方法的名称 | | Class<?>[] getParameterTypes() | 获取方法所有参数的类型 | | Class<?>[] getExceptionTypes() | 获取方法的异常信息 |
反射:
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制
为什么Java反射性能差?
java反射是要解析字节码,将内存中的对象进行解析,包括了一些动态类型,所以JVM无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多!
提高反射性能的方式有哪些?
setAccessible(true),可以防止安全性检查(做这个很费时)
做缓存,把要经常访问的元数据信息放入内存中,class.forName 太耗时
3 getMethods() 等方法尽量少用,尽量调用getMethod(name)指定方法的名称,减少遍历次数
反射
反射就是把Java类中的各个成分映射成一个个的Java对象。即在运行状态中,对于任意一个类,都能够知道这个类的所以属性和方法;对于任意一个对象,都能调用它的任意一个方法和属性。这种动态获取信息及动态调用对象方法的功能叫Java的反射机制。
反射为什么慢:
- Class.forName,Class.getMethod 涉及native调用;符号引用转为直接引用;getMethod涉及遍历查找
- Method.invoke 变长参数方法传的 Object 数组 ->基本类型的自动装箱、拆箱 -> 堆分配空间;涉及native调用;
- 安全检查
- jvm没办法jit优化
性能优化:
- 缓存访问的元素
- 接口优先于反射
- 字节码增强(第一次使用会很慢,后面就相当于转成直接调用了)
- 魔法类:sun.misc.Unsafe
public class InflactTestV2 {
public static void targetMethod(int i){
//打印堆栈信息
new Exception("index : "+i).printStackTrace();
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<?> inflactTest = Class.forName("com.qiaofeng.InflactTestV2");
Method method = inflactTest.getMethod("targetMethod",int.class);
for (int i= 0 ; i < 20 ; i++){
method.invoke(null,i);
}
}
}
-Dsun.reflect.inflationThreshold,默认为16。当反射native调用超过15次就会触发jvm的动态生成字节码,之后的操作,全部都会调用该动态实现。动态实现与native实现相比,动态实现的效率要快的多,这是因为native的实现要在java语言层面切换到c语言,然后再次切换到java语言。
-Dsun.reflect.noInflation=true 关闭反射的多重实现:可以关闭反射的多重实现,使得在第一次调用的时候就生成字节码,在之后的调用中都是Java执行栈自己的调用。可以在jvm中设置-Dsun.reflect.noInflation=true