JVM 结构
更多关于 JVM 内存结构参考:
1. Java 的内存结构 (Memory Structure) 和垃圾收集 (Garbage Collection) 图解
2. 我对 Java 内存的认识
3. JVM 内存区域划分 Eden Space、Survivor Space、Tenured Gen,Perm Gen 解释
Class File
*.class
文件
Class Loader
类加载器Runtime Data Area
运行时数据区Method Area
方法区 (线程共享)*.class
文件被加载时会在方法区创建该类的描述符- 程序将根据方法区的类描述符创建类的实例
- 类描述符只被创建一次, 当使用完后并不会从方法区移除
Heap
堆区 (线程共享)- 堆内存用来存放由
new
出来的对象, 栈中的变量指向堆内存中的对象地址- 堆中保存着对象的属性、方法的引用 (方法的主体还是在方法区)
- 在堆中分配的内存, 由 JVM 的自动垃圾回收器来管理
- 堆内存用来存放由
Java Stack
栈区- 基本类型的变量和引用变量都在栈内存中分配, 当超过变量的作用域后, Java 会自动释放掉为该变量所分配的内存空间
Native Method Statck
本地方法栈 (=> 操作系统)Program Counter Register
程序计数器
Execution Engine
执行引擎Native Interface
本地接口Native Libraries
本地类库
补充静态代码块:
使用Class.forName(" ")
加载类时, 如果指定第二个参数为false
, 则不执行静态代码块, 等下次加载时会再执行静态代码块 (只执行静态代码块, 并不会重新加载类, 类只会被加载一次)
反射
反射: 动态访问对象的属性和方法 (通过类描述符得到指定的方法描述符, 然后通过 method.invoke(object, param)
调用方法);
java.lang.Class
: 类类, 描述类的类, 类描述符java.lang.reflect.Method
: 方法类, 描述方法的类, 方法描述符java.lang.reflect.Field
: 字段类 (属性类), 描述字段的类, 字段描述符java.lang.reflect.constructors
: 构造器类, 描述构造方法的类, 构造方法描述符java.lang.reflect.modifier
: 修饰符类, 描述修饰符的类, 修饰符描述符
class.getDeclaredMethods
获取类中声明的方法 (不管是 Public 还是 Private, 但不包含继承的父类方法, 因为没有声明), 它的第二个参数是可变参数, 是要获取的方法的参数类型说明, 如 int.class
, String.class
.
class.getMethods
获取类的所有 public 方法 (包含继承的方法)
注意, 反射依然遵守方法修饰符权限的规则 (public, private, protect), 但是通过 method.setAccessible(true)
就能 invoke private or protect methods.
method.getModifiers
获取方法的修饰符, 然后通过 Modifier.isXxxx()
判断是什么类型的修饰符
class.newInstance
要求类有无参构造函数, 如果没有的话就会报异常, 解决方法是通过 class.getDeclaredConstructor
获取构造函数, 然后通过 constructor.newInstance
来实例化对象.
class.getDeclaredConstructor
可以获取到所有的构造函数, 而 class.getConstructor
只能获取到 public 的构造函数.
class.getDeclaredField
获取到类的字段描述后, 可以再通过 field.get(object)
来获取到实例的字段值, 依然遵守方法修饰符权限的规则 (public, private, protect), 但通过 method.setAccessible(true)
可以修改它的修饰符权限.
内省
Reference: 深入理解 Java: 内省 (Introspector)
通过反射的方式访问 JavaBean 的技术, 是 Java 语言对 Bean 类属性、事件的一种缺省处理方法.
例如类 A 中有属性 name, 那我们可以通过 getName
、setName
来得到其值或者设置新的值, 通过 getName/setName
来访问 name 属性, 这就是默认的规则.
Java 中提供了一套 API 用来访问某个属性的 getter/setter
方法, 通过这些 API 可以使你不需要了解这个规则, 这些 API 存放于包 java.beans
中.
一般的做法是通过类 Introspector
来获取某个对象的 BeanInfo 信息, 然后通过 BeanInfo 来获取属性的描述器 (PropertyDescriptor), 通过这个属性描述器就可以获取某个属性对应的 getter/setter
方法, 然后我们就可以通过反射机制来调用这些方法.
下面我们来看一个例子, 这个例子把某个对象的所有属性名称和值都打印出来:
importjava.beans.BeanInfo; importjava.beans.Introspector; importjava.beans.PropertyDescriptor; public class IntrospectorDemo { String name; public static void main(String[]args) throws Exception { IntrospectorDemo demo = new IntrospectorDemo(); demo.setName("WinterLau"); // 如果不想把父类的属性也列出来的话, 那 getBeanInfo 的第二个参数填写父类的信息 BeanInfo bi = Introspector.getBeanInfo(demo.getClass(), Object.class); PropertyDescriptor[] props = bi.getPropertyDescriptors(); /*for (int i = 0; i < props.length; i++) { System.out.println(props[i].getName() + "=" + props[i].getReadMethod().invoke(demo)); }*/ for (PropertyDescriptor prop : props ) { System.out.println(prop.getName() + "=" + prop.getReadMethod().invoke(demo)); } } public String getName(){ retur nname; } public void setName(String name){ this.name=name; } }
另外, 如果已知属性名的话, 是可以直接通过 PropertyDescriptor
类来操作 JavaBean 的.
public class IntrospectorDemo { String name; public static void main(String[]args) throws Exception { IntrospectorDemo demo = new IntrospectorDemo(); demo.setName("WinterLau"); PropertyDescriptor propDesc = new PropertyDescriptor("name", demo); Method methodGetName = proDescriptor.getReadMethod(); Object objName = methodGetName.invoke(demo); System.out.println("get name:" + objName.toString()); } public String getName(){ retur nname; } public void setName(String name){ this.name=name; } }