java是一门编译型语言;与之对应的称之为解释性(JavaScript,python)。编译型语言在编写完源代码后必须要编译之后才能够运行。解释性语言内置了解释器,程序一边解释,一边执行。 java语言编译之后产生.class字节码文件。字节码文件包含了对类的定义(类的名字,类的属性,类的方法,类实现的接口,继承的父类,使用了什么注解)。
Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法
什么是类的结构信息
.java
文件经过javac
指令编译后生成.class
字节码文件中保存了类的结构信息;一个class文件描述了类的具体信息;(类名,父类,实现的接口,属性,构造方法,实例方法,注解等)
什么是类加载机制
java虚拟机把描述类结构信息的.class
文件加载到方法区,并对数据(.class文件
)进行校验,转换解析和初始化的过程称为类加载。加载到方法区的类已Class<?>
类的对象存在。Class
类的对象我们不能够直接实例化。
什么时候触发类的加载
遇到new
,getStatic
,putStatic
,invokeStatic
指令时,如果该类的类型还没有进行过初始化,则需要触发其初始化阶段。
反射提供的功能
-
在运行时判断任意一个对象所属的类
-
在运行时构造任意一个类的对象
-
在运行时获取任意一个类所具有的成员变量和方法
-
在运行时调用任意一个对象的成员变量和方法
-
生成动态代理
类加载的过程:
加载”只是“类加载”过程的一个阶段,在加载阶段,虚拟机需要完成以下3件事情:
-
通过一个类的全限定名(包名.类名)来获取定义此类的二进制字节流(字节码文件);
-
将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构;
-
在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。该Class类对象没有明确规定是在Java堆中,对于HotSpot虚拟机而言,Class对象比较特殊,它虽然是对象,但是存放在方法区里面。
对于数组类而言,数组类本身不通过类加载器创建,它是由Java虚拟机直接创建的,但是数组类的元素类型(Element Type,是指数组去掉所有维度的类型)最终要靠类加载器去创建,一个数组类(简称为C)创建过程要遵循以下规则:
-
如果数组的组件类型(Component Type,指的是数组去掉一个维度的类型)是引用类型,则递归地加载这个组件类型,数组C将在加载该组件类型的类加载器的类名称空间上被标识。
-
如果数组的组件类型不是引用类型(例如int[]数组),则Java虚拟机将会把数组C标记为与引导类加载器关联。
-
数组类的可见性与它的组件类型的可见性一致,如果组件类型不是引用类型,那数组类的可见性将默认为public
双亲委派模型
双亲委派模型的目的是为了避免重复加载class文件到方法区。
/* 此方法来自于java.lang.ClassLoader的源码,负责从类加载器管理的路径下加载class文件到方法区 */ protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) {//上锁,多线程章节 // 第一步,检查name对应的class是否已经被加载过 Class<?> c = findLoadedClass(name); if (c == null) { //该class尚未被加载到方法区 long t0 = System.nanoTime(); try { //如果存在父类加载器,则从父类加载器中查找class if (parent != null) { c = parent.loadClass(name, false); } else { //parent为null,则说明从启动类加载器(BootstrapClassLoader)查找class //如果启动类加载器中找到了该class,则返回,否则返回null c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } //检测父类加载器是否加载到class if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); //在当前的类加载器中查找class c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } //返回class对象,有可能是一个null return c; } }
Class
JVM通过ClassLoader类加载器将指定路径下的class文件加载到jvm方法区时,通过Class对象来表示具体所加载到的class文件。
举例:现有Student.class,Employee.class, Order.class, Uesr.class
当jvm加载如上这些类时,会为每一个类产生一个Class对象。通过泛型来表示其类型。
//jvm方法区中表示Student类 Class<Student> stuClass; //Class对象保存的是类的结构信息 Class<Employee> empClass; Class<Order> orderClass; Class<User> userClass; //根据类的结构信息实例化Student对象 Student stu = new Student();
-
Class本身也是一个类
-
Class 对象只能由系统建立对象
-
一个类在 JVM 中只会有一个Class实例
-
一个Class对象对应的是一个加载到JVM中的一个.class文件
-
每个类的实例都会记得自己是由哪个 Class 实例所生成
-
通过Class可以完整地得到一个类中的完整结构
一个类的结构:(属性,方法,构造方法,父类,接口,注解)
对照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。
获取Class类对象的几种方式
//Student位于com.hxzy.bean包中,点相当于磁盘目录的分隔符。com/hxyz/bean/Student.java package com.hxzy.bean; class Student{ private String name; //setter and getters public Student(){} public Student(String name){ this.name = name; } public static void main(String[] args){ //通过类名.class得到Student在JVM中的class信息 Class<Student> claz = Student.class; //通过对象.getClass(); Student stu = new Student(); Class<Student> claz2 = stu.getClass(); //通过类的完整路径获取Class对象,很多框架也是利用了这种方式来实现的 //类的完整路径=包名.类名 String path = "com.hxzy.bean.Student"; Class<Student> aClass1 = (Class<Student>) Class.forName(path); } }
Class类的常用方法
方法 | 描述 |
---|---|
forName(String) | 根据类的完整名称获取其Class |
newInstance() | 通过类的无参构造实例化对象 |
getName() | 返回Class对应类的完整类名 |
getConstructor() | 根据参数列表得到具体的构造函数 |
getConstructors() | 得到这个类所有的构造函数 |
getDeclaredFields() | 得到这个类中所有的属性 |
getFields() | 得到这个类中所有的公共的属性 |
getField(String name) | 获取这个类中指定名字的公共的属性 |
getDeclaredField(String name) | 获取这个类中任意访问修饰符的属性 |
getDeclaredMethods() | 得到这个类中所有的方法 |
getMethods() | 得到这个类中所有的公共的方法 |
getDeclaredMethod() | 根据方法名获取任意方法的对象 |
getMethod() | 根据方法名获取公共的方法 |
通过反射实例化对象
Class claz = Student.class; //通过Student类的无参构造方法实例化Student类的对象 Object obj = claz.newInstance();
通过反射获取构造函数
在反射中,一个类中的构造函数可以通过Constructor
的对象表示。 Constructor
类的常用方法:
方法 | 描述 |
---|---|
getModifiers() | 获取访问修饰符 |
getName() | 获取构造方法的名称 |
getParameterCount() | 获取参数列表的个数 |
getParameterTypes() | 获取参数列表的类型 |
newInstance(Object... initargs) | 通过构造方法实例化对象 |
通过反射获取属性Field
通过反射能够得到类中任何属性,其访问修饰符,属性名,属性的类型,为属性赋值,获取属性的值;在反射中,一个属性就是一个Field
的对象
方法 | 描述 |
---|---|
get(Object) | 获取某个对象上该属性的值 |
getModifiers() | 获取改属性的访问修饰符 |
getName() | 获取属性名 |
set(Object obj, Object value) | 为某个对象的该属性赋一个值 |
getType() | 获取该属性的类型 |
setAccessible(boolean) | 设置私有的属性可以被访问 |
通过反射获取方法
通过反射能够获取到类中所有的方法,方法名,方法的返回值类型,方法的参数列表,执行方法等等。在反射中,一个方法就是一个Method
的对象。
方法 | 描述 |
---|---|
getModifiers() | 获取方法的访问修饰符 |
getName() | 获取方法名 |
getParameterCount() | 获取参数的个数 |
getParameterTypes() | 获取参数的类型 |
getReturnType() | 获取返回值类型 |
invoke(Object obj, Object... args) | 执行此方法 |
setAccessible(boolean) | 设置私有的方法可以被访问 |