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()来构造对象。
  1. Object o = Class.forName("cn.hzlim.mydemo.basis.Test")
  2. .getConstructor(String.class, int.class)
  3. .newInstance("小明", 18);
  • 通过getConstructors()来获取所有的构造方法
  1. Class<?> aClass = Class.forName("cn.hzlim.mydemo.basis.Test");
  2. Constructor<?>[] constructors = aClass.getConstructors();
  3. 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无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多!

提高反射性能的方式有哪些?

  1. setAccessible(true),可以防止安全性检查(做这个很费时)

  2. 做缓存,把要经常访问的元数据信息放入内存中,class.forName 太耗时

3 getMethods() 等方法尽量少用,尽量调用getMethod(name)指定方法的名称,减少遍历次数

反射

反射就是把Java类中的各个成分映射成一个个的Java对象。即在运行状态中,对于任意一个类,都能够知道这个类的所以属性和方法;对于任意一个对象,都能调用它的任意一个方法和属性。这种动态获取信息及动态调用对象方法的功能叫Java的反射机制。

反射为什么慢:

  1. Class.forName,Class.getMethod 涉及native调用;符号引用转为直接引用;getMethod涉及遍历查找
  2. Method.invoke 变长参数方法传的 Object 数组 ->基本类型的自动装箱、拆箱 -> 堆分配空间;涉及native调用;
  3. 安全检查
  4. jvm没办法jit优化

性能优化:

  1. 缓存访问的元素
  2. 接口优先于反射
  3. 字节码增强(第一次使用会很慢,后面就相当于转成直接调用了)
  4. 魔法类: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);
        }
    }
}

反射 - 图1

-Dsun.reflect.inflationThreshold,默认为16。当反射native调用超过15次就会触发jvm的动态生成字节码,之后的操作,全部都会调用该动态实现。动态实现与native实现相比,动态实现的效率要快的多,这是因为native的实现要在java语言层面切换到c语言,然后再次切换到java语言。

-Dsun.reflect.noInflation=true 关闭反射的多重实现:可以关闭反射的多重实现,使得在第一次调用的时候就生成字节码,在之后的调用中都是Java执行栈自己的调用。可以在jvm中设置-Dsun.reflect.noInflation=true