《JavaEE》第八周day4学习笔记-反射、类加载、内存结构

一、反射

(一)反射概述


反射允许在Java程序运行状态下,获取任意类(源码不透明)的所有属性和方法、调用任意对象的所有属性和方法,体现了Java的灵活性和动态性,具有以下几点意义:

  • 降低模块的耦合性
  • 使程序动态操作类和对象而无需提前编码
  • 渗透未知类的内容
  • 是构建框架技术的基础

(二)创建Class对象


Class对象本质由.class文件加载至内存产生,为类加载的早期阶段(尚未完成);同一个类的Class对象只会被加载一次,再次加载会返回已有引用,因此多种方式创建的同一个类的Class对象是唯一的。
1.Class.forName()方式

Class<?> aClass = Class.forName("java.util.Date");
Date date11 = (Date)aClass.newInstance();

2.类.class方式

Class<Date> dateClass = Date.class;
Date date12 = dateClass.newInstance();

3.对象.getClass方式

Date date = new Date();
Class<? extends Date> aClass1 = date.getClass();
Date date13 = aClass1.newInstance();

(三)Class方法


1.成员变量-Field

  • Field[] getFields() :获取public修饰的成员变量,返回Field数组
  • Field getField(String name):获取指定name的成员变量
  • Field[] getDeclaredFields():获取任意修饰的成员变量,返回Field数组
  • Field getDeclaredField(String name) 获取任意修饰指定name的成员变量
  • void set(Object obj, Object value):设置变量值,注意首个参数为类对象
  • get(Object obj):获取参数值
  • setAccessible(true丨false):设置属性的访问(编辑)能力(可访问、不可访问)

2.构造方法-Constructor

  • Constructor<?>[] getConstructors():获取public修饰的构造方法,返回Constructor数组
  • Constructor getConstructor(Class<?>… parameterTypes):获取public修饰的指定形式参数的构造方法,例如(String.class,int.class)
  • Constructor<?>[] getDeclaredConstructors():获取任意修饰的构造方法,返回Constructor数组
  • Constructor getDeclaredConstructor(Class<?>… parameterTypes):获取任意修饰的指定形式参数的构造方法,例如(String.class,int.class)
  • T newInstance(Object… initargs):通过构造器生成对象

3.成员方法-Method

  • Method[] getMethods():获取public修饰的成员方法,包括父类里面public修饰的方法,返回Method数组
  • Method getMethod(String name, 类<?>… parameterTypes):获取public修饰的指定形式参数的成员方法,例如(name,String.class,int.class)
  • Method[] getDeclaredMethods():获取任意修饰的成员方法,不包括父类的方法,返回Method数组
  • Method getDeclaredMethod(String name, 类<?>… parameterTypes):获取任意修饰的指定形式参数的成员方法,例如(name,- String.class,int.class),不包括父类的方法
  • Object invoke(Object obj, Object… args):调用obj对象的此方法
  • String getName():获取成员方法名

4.获取全类名

  • String getName():获取全类名

二、Java类加载机制

(一)加载(Loading)


Java虚拟机通过一个类的全限定名来获取二进制字节流,然后将字节流所代表的的静态存储结构(硬盘)转化为方法区的运行时数据结构(内存),最后在Java堆中生成一个代表这个类的java.lang.Class对象,最为方法区这些数据的访问入口。

(二)链接-验证(Verification)


验证是Java虚拟机保护自身安全的机制,因为.class不一定由Java源码编译而来(可由十六进制编辑器直接编写…),胡乱导入有可能导致系统崩溃等恶果,大体完成文件格式验证、元数据验证、字节码验证和符号引用验证四项内容。

(三)链接-准备(Preparation)


正式为静态类变量(static)分配内存空间并赋予初始值(0,false,null…)的阶段,其中:final修饰(本质为变量的ConstantValue属性)的变量将被赋予目标值,实例变量将在实例化时与对象合并分配内存空间。

(四)链接-解析(Resolution)


是Java虚拟机将常量池内的符号引用(代号)替换为直接引用(内存指针)的过程,解析可能在初始化之后执行,如Java运行时绑定(动态绑定)
形象地理解:符号引用-广州塔,直接引用-{东经:xx、北纬:xx、海拔:xx}?

(五)初始化(Initialization)


开始执行类中定义的Java程序代码(字节码),根据计划去初始化类变量和其他资源。初始化完成后,将对静态内容赋值;初始化有且只有以下四种方式:
1.遇到new、getstatic、putstatic、invokestatic字节码指令时触发初始化,如new对象、读取-设置-调用类的静态内容(被final修饰的除外)等场景;
2.使用java.lang.reflect包的方法对类进行反射调用的时候,若类未初始化,则触发初始化(Class.forName()方法底层调用了Reflection类);
3.初始化子类时,父类会被联动初始化;
4.Java虚拟机启动时,被设定为执行的主类(例如Test注解、包含main()方法的类)将被初始化。

(六)使用(Using)


完成解析及初始化后,Java虚拟机开始从入口开始执行开发者设计的代码。

(七)卸载(Unloading)


开发者设计的代码执行完成后,Java虚拟机开始销毁各类资源及Class内存对象,最后Java虚拟机也退出内存。

三、类加载器

(一)类与类加载器


JVM类的加载是通过ClassLoader类及其子类完成的,共有4个阶段,自底向上检查,自顶向下加载,所有的ClassLoader仅加载一次。
1.Bootstrap ClassLoader
负责加载$JAVA_HOME-jre/lib/rt.jar中所有得class,由C++实现(非ClassLoader子类)。
2.Extension ClassLoader
负责加载扩展jar包,包含$JAVA_HOME-jre/lib/*.jar-Djava.ext.dirs目录的jar包。
3.App ClassLoader
负责加载classpath中指定的jar包及class文件。
4.Custom ClassLoader
定制化的类加载,如JavaEE应用服务器(Tomcat、JBoss)根据J2EE规范自行实现的ClassLoader。

(二)双亲委派模型


要求除顶层的类加载器外,其余的类加载器都应当有自己的父类(非继承,代码组合复用)加载器,始于JDK1.2,类加载器收到类加载请求后,优先委托父级类加载器执行(这是一个递归过程),最终到达顶层类加载器;如果父类加载器可以完成,则子类加载器不会启动。有以下两点优势:
层级化,避免类的重复加载
安全,防止核心类被子类随意替换

四、Java虚拟机运行时内存区域(JDK1.8)

运行java自带的工具:/bin/jconsole.exe,可以看到六个内存池分配区域:
在这里插入图片描述
下图对各个区域进行说明:

在这里插入图片描述

Tip:关于内存结构的疑惑


以下是在网上很容易找到的一张结构图,其中有如下解释:
在这里插入图片描述
{
程序计数器:每个线程一块,指向当前线程正在执行的字节码代码的行号。如果当前线程执行的是native方法,则其值为null。
Java虚拟机栈:局部变量表(基本数据类型、对象引用、返回地址)、操作数栈、运行时常量池引用、方法返回地址。
本地方法栈:与Java虚拟机栈类似,区别在于此栈为虚拟机使用到的native服务。
:存放对象实例(JDK1.8字符串常量池从永久代剥离至堆),其中老年代占2/3,年轻代占1/3(eden占80%,survivor0占1%,survivor1占1%)
元数据区:元数据区取代了1.7版本及以前的永久代。元数据区和永久代本质上都是方法区的实现。方法区存放虚拟机加载的类信息,静态变量,常量等数据。
直接内存:jdk1.4引入了NIO,它可以使用Native函数库直接分配堆外内存。
}

看完以上内容,本人在理解时有如下困惑:

  • 架构图层级关系很奇怪,JVM内存结构(亦有标记为JVM运行时内存区域概览),不是不应该包括本地内存吗?
  • 元数据区不是应该隶属于直接内存吗?
  • 查找过《深入理解java虚拟机》,程序计数器应该是和线程密切相关的,但却没有相应体现;
  • 和“Java监视和管理控制台”(JDK1.8)显示的内存内容无法对应;
  • 等等…

可能初学Java道行尚浅,从业多年后可能才会有更深刻的理解,且看且行且体会吧。另外在网上也搜到了这样一张图,感觉会更直观一些?
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值