JVM
内存
栈 执行代码是以方法为单位的
遇到方法调用时,会找到该方法,压栈,方法执行完以后弹出,返回值放到上层函数
代码中定义的临时变量/局部变量,方法的参数,都是在栈中定义的
是线程隔离的,一个线程要单独分配一个栈
堆 放置对象,数组对象,类的对象,接口的对象,只有是new创建出来的,都是要放在堆里
常量池所在地(1.8以前实在方法区的)
是线程共享的,多个线程可以同时访问同一块堆资源
方法区 类的信息,保存在Class对象中:类名,父类,有哪些方法/属性,有哪些构造方法
静态属性所在地
是线程共享的,多个线程可以同时访问一个类的信息
本地方法区 存放native修饰的本地方法(由c/c++实现,如System.arrayCopy)
是线程隔离的
程序计数器 记录程序执行到哪里了
是线程隔离的,不同的线程有不同的程序计数器
类的加载
使用的是ClassLoader(类加载器),将类的字节码加载到JVM中
类加载器的分类
启动类加载器BootstrapClassLoader 加载JRE下的类的
扩展加载器 ExtClassLoader jre/lib
系统加载器 SystemClassLoader 当前项目下的
当类加载器加载类时,可以向上委托
类加载的过程
加载 将字节码文件加载到类加载器中
读取字节码文件的内容
链接 将类的二进制数据合并到JRE中
校验 校验字节码文件
准备 将类的信息放在方法区中
静态属性的声明,给它分配内存,同时初始化为类0值(但是此时静态属性不可访问)
解析 解析成JVM认识的东西
初始化 执行静态代码:静态属性的数值,静态块
当只有一个类被用到时,才会被初始化:创建类的对象,访问类的静态属性/调用类的静态方法,使用反射
加载完类以后,在方法区中就产生了一个Class类型的对象(一个类只加载一次,所以只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这恶鬼对象就像一面镜子,透过这个镜子看到类结构,所以我们形象的称之为,反射。
反射
在一个Java程序运行时,通过读取方法区中类的信息,操作类
Class类(final修饰的)
- Class对象只能由系统建立对象
- 一个Class对象对应的时一个加载到JVM中的一个.class文件
- 每个类的实例都会记得自己是由哪个Class实例所生成
- 通过Class可以完整地得到一个类中所有被加载的结构
- Class类时Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象
- Class的实例就对应着一个运行着的类
如何获取一个类的Class对象
通过已有对象获取 Object.getClass()
Student stu = new Student();//已有对象
Class cls1 = stu.getClass();
通过类名 类名.class
Class cls2 = Student.class
Class.forName("类的全限定名")
Class cls3 = Class.forName("package0222.Student");//可能会抛异常,要加try-catch
通过类加载器//不太会
ClassLoader classloader = ReflectionTest.class.getClassLoader();
Class<?> cls4 = classLoader.loadClass("package0222.Student");
Class实例可以代表的结构
-
class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
-
interface:接口
-
[]:数组
-
enum:枚举
-
annotation:注解
-
基本数据类型
-
void
-
Serializable, Cloneable 无方法
通过Class对象可以干什么
类名 获取 System.out.println(cls.getName());
父类 获取
父接口 获取 System.out.println(Arrays.toString(cls.getInterfaces()));
包 获取
属性 获取
获取属性值
设置属性值 Field age = cls.getField("age");
age.setInt(stu1, 18);
修改访问权限 Field name = cls.getDeclaredField("name");
name.setAccessible(true);
name.set(stu1, "tom");
方法 获取 System.out.println(Arrays.toString(cls.getDeclaredMethods()));
调用 Method toString = cls.getMethod("toString");
//调用 str = stu1.toString();
String str = (String) toString.invoke(stu1);
构造方法 获取 System.out.println(Arrays.toString(cls.getConstructors()));
调用
有了反射可以做一些更加通用的东西,比如框架的底层代码就是用反射写的。