Reflection
1、反射被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection中的API 取得任何类的内部信息(成员变量,构造方法,成员方法等),并能直接操作任意对象的内部属性及方法。
2、加载完类后,在堆内存的方法区中 就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。
3、这个对象就像一面镜子,透过这个镜子看到类的结构,所以我们形象的称之为:反射。
作用:可以调用私有的属性。
如何看待反射机制与封装性?
封装性:表示封装的内容用不到,或在别的地方可以调用反射:可以调但不建议。
开发中用new的方式还是反射的方式?
建议用new的方式,只有在不知道要造那个对象时才用反射。
Class类
java文件在各时期的对应关系:
Class类的加载
类加载过程:
概述:
程序经过javac.exe编译命令以后,会生成一个或多个字节码文件(.class结尾),接着我们使用java.exe运行命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到堆内存中 。此过程被称为类的加载。加载到内存中的类,我们就称为运行时类,即Class的对象(Class的实例就对应着一个运行时类)。
详细解析(五个阶段):
加载阶段:JVM在该阶段的主要目的是将字节码从不同的数据源转化为二进制字节流加载到内存中,并生成一个代表该类的java.lang.Class对象。
连接阶段:
**验证阶段:**目的是为了确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。包含文件格式验证(是否以魔数 oxcafebabe开头)、元数据验证、字节码验证和符号引用验证。可以使用-Xverify:none 参数来关闭大部分的类验证措施,缩短虚拟机类加载的时间。
**准备阶段:**JVM会在该阶段对静态变量,常量等,分配内存并默认初始化((对应数据类型的默认初始值,如0、OL、null、false 等)。这些变量所使用的内存都将在方法区中进行分配。
**解析阶段:**虚拟机将常量池内的符号引用替换为直接引用的过程。
初始化阶段: 到初始化阶段,才真正开始执行类中定义的Java程序代码,此阶段是执行<clinit>0)方法的过程。<clinit>()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量 的赋值动作和静态代码块中的语句,并进行合并。在此阶段虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>)方法完毕。
类加载方式:
静态加载:编译时加载相关的类,如果没有则报错,依赖性太强。(new的方式)
动态加载:运行时加载需要的类,如果运行时不用该类,则不报错,降低了依赖性。(反射)
类加载时时机:
①当创建对象时(new) ②当子类被加载时 ③调用类中的静态成员时 ④通过反射
Class类的其他注意事项:
Class类对象不是new出来的,而是系统创建的。
类的字节码二进制数据,是放在方法区的,称为类的元数据。
对于某个类的Class类对象,在内存中只有一份,因为类只加载一次(加载时会有一个线程锁)。
每个真实类的实例都会记得自己是由哪个 Class实例所生成。通过这个Class对象可以完整地得到一个类的完整结构,并通过Class类的相关API对这个真实类反射出的对象进行操作。
加载到内存中的运行时类,会缓存一段时间。在此时间内,我们可以通过不同的方式来获取运行时类。
常用方法
获取运行时类的属性结构:
getFields():获取当前运行时类及其父类声明为public的属性。
getDecareFields():获取当前运行时类中声明的所有属性(不含父类中声明的属性)。
获取运行时类的方法结构:
getMethods():获取当前运行时类及其父类中声明为public的方法。
getDeclareMethods():获取当前运行时类中声明的所有方法(不含父类中声明的方法)。
获取方法的内部结构:
get Anno tations():获取注解。
getModifiers ():获取权限修饰符。
getReturnType ().getName:获取返回值类型。
getName():获取方法名。
get ParameterTypes():获取形参列表。
getExceptionTypes():获取异常。
获取运行时类的构造器:
getConstructors():获取当前运行时类及其父类声明为public的构造器。
getDeclareConstructors ():获取当前运行时类中声明的所有构造器(不含父类中声明的构造器)。
获取运行时类的父类:
getSuperclass():获取运行时类的父类。
getGenericsuperclass():获取运行时类的带泛型的父类。
getActualTypeguments():获取运行时类的父类的泛型类型。
获取运行时类的接口:
get Interface():获取运行时类的接口。
获取运行时类的包:
get Package():获取运行时类所在的包。
获取运行时类的注解:
get Annotations ():获取运行时类的注解。
获取Class类对象的的六种方式
方式一:调用运行时类的class属性:类.class
Class clazz = 类名.class;
方式二:调用运行时类的**.getClass()方法:**类.getClass()
A a = new A();
Class clazz = a.getClass();
方式三:调用Class的静态方法..forName("类路径"):Class.forName("类路径")
Class clazz = Class.forName("类路径");
方法四:使用类的加载器(了解):
ClassLoader classloder = 类名.class. classLoader();
Class clazz = classloder.lodclass("类路径");
方法五:基本数据类型..class(了解):
Class clazz = int.class;
方法六:包装类.TYPE(了解):
Class clazz = 包装类.TYPE;
哪些类型可以有Class对象
① class:外部类、内部类。
② interface:接口。
③ 数组。
④ enum:枚举 (只要数组的类型和维度一样,就是同一个class)。
⑤ annotation:注解(@interface)。
⑥ 基本数据类型。
① void(返回类型)。