从HotSpot虚拟机源码了解Java的访问控制修饰符

从HotSpot虚拟机源码了解Java的访问控制修饰符

作者:wujiuye 2020-09-02 07:03:04

云计算

虚拟化 前面Ribbon源码分析文章,有读者留言提问:XX类是包私有的,重写不会报错吗?答案其实是XX类并非包私有,而是一个protected的静态内部类,所以重写不会报错。

 [[340301]]

前面Ribbon源码分析文章,有读者留言提问:XX类是包私有的,重写不会报错吗?答案其实是XX类并非包私有,而是一个protected的静态内部类,所以重写不会报错。

关于Java访问控制修饰符的作用,笔者在初学Java时也是靠记,写多了代码自然也就能理解,但笔者很好奇底层的实现,所以也尝试从HotSpot虚拟机源码寻找答案,解答我多年来的疑惑。

类、字段、方法都有哪些访问控制修饰符?

私有<private>、子类可访问<protected>、公开public、包私有<package>,默认不加访问控制修饰符就是包私有。

访问范围privatepackageprotectedpublic
同一个可访问可访问可访问可访问
同一包中的其他类不可访问可访问可访问可访问
不同包中的子类不可访问不可访问可访问可访问
不同包中的非子类不可访问不可访问不可访问可访问

包私有<package>指的是只有同一个包下的类可访问,其它包下的类不可访问。

今天我们就深入java虚拟机去探究这些访问控制修饰符语意的实现。

InstanceKlass是HotSpot VM中对应class文件结构的数据结构,InstanceKlass对象是一个Java类被HotSpot VM加载后所生成的C++对象,被存于方法区。我们在Java代码中使用的Class对象实际是InstanceKlass的一个镜像。

Java支持使用”this.”、”suppor.”、”某个对象.”调用一个方法,或”某个类.”调用静态方法,在我们看来是调用某个类的静态方法或者对象的方法,但这在虚拟机中并不存在区别,都是一个方法调用。

调用静态方法和对象方法的区别只在于,调用对象的方法需要在方法参数传递一个”this”引用,这是一个隐式参数,在编译器将Java代码编译成字节码时自动添加上。

而Java代码中使用”this.”、”suppor.”调用自身方法和父类方法的不同,仅仅只是生成方法调用字节码指令的操作数指向的Methodref常量不同,方法的第一个隐式参数传递的对象都是同一个。Methodref常量指代一个方法的符号引用,包括类名、方法名、方法描述符。

我们知道,类加载过程包括加载、链接、初始化三个阶段,其中链接阶段又可细分为验证、准备和解析三个阶段。下面这张图有助于我们理解类加载的几个阶段,但并不准确。

《Java虚拟机规范》只是规定类加载需要完成的事情,而对顺序并没有严格的要求。

下图为笔者阅读HotSpot虚拟机类加载源码总结出的一张流程图,仅供参考。(如需要获取原图,可在公众号回复:”hotspot”)

在HotSpot虚拟机中,链接阶段的准备阶段在加载阶段之后完成,链接阶段的验证也分多种验证,其中文件格式验证、元数据验证在加载阶段交叉完成,而字节码验证阶段则在类初始化之前才触发,解析阶段则在类加载完成之后。

引起类初始化的几条指令如new、getstatic、putstatic、invokestatic,虚拟机在执行这些指令时,先判断类是否已经初始化,未初始化则完成类的初始化,链接阶段会在类初始化阶之前触发。

链接阶段的解析阶段是Java虚拟机将常量池内的符号引用替换为直接引用的过程,根据《Java虚拟机规范》规定,在ane-warray、checkcast、getfield、getstatic、instanceof、invokedynamic、invokeinterface、invoke-special、invokestatic、invokevirtual、ldc、mulianewarray、new、putfield、putstatic这些要求操作数指向常量池中的符号引用常量(如:CONSTANT_Class_info、CONSTANT_Field_info、CONSTANT_Methodref_info)的指令执行之前,必须先对使用的符号引用进行解析。

符号引用以一组符号描述引用的目标,如CONSTANT_Class_info表示引用的类、CONSTANT_Field_info表示引用哪个类的哪个字段、CONSTANT_Methodref_info表示引用哪个类的哪个方法。

符号引用验证发生在解析阶段,符号引用验证包括:通过字符串描述的全限定名是否能找到对应的类、在指定的类中是否存在简单名称所描述的方法和字段、符号引用中的类、字段、方法的可访问性( 、 、public、 )。

在HotSpot虚拟机的实现中,对于解释执行与动态调用(invokedynamic),解析阶段是在符号引用将要被使用前才去解析。

方法调用源码:javaCalls.cpp; 链接解析源码:linkResolver.cpp;

  1. // 检查类 
  2. LinkResolver::check_klass_accessability 
  3. // 检查方法 
  4. LinkResolver::check_method_accessability 
  5. // 检查字段 
  6. LinkResolver::check_field_accessability 

这些方法调用最后都调用Reflection类的对应verify方法完成是否可访问的判断,例如Reflection::verify_field_access方法。

Java虚拟机在解析class文件结构时、在字节码验证阶段,也会对访问控制修饰符进行验证。

例如,在解析class文件结构时,验证是否能够继承父类(Reflection::verify_class_access):

类的访问修饰符决定了一个类是否可以被其它类访问。在解析class文件结构阶段,虚拟机可以验证当前类是否能够继承父类(父类的访问控制修饰符决定)、是否能够实现每个接口(接口的访问修饰符决定)。

在字节码验证阶段则验证当前类是否可以访问目标类的protected修饰的方法或字段:

在字节码验证阶段,虚拟机会对类的每个方法中的每条字节码指令都会进行验证,但虚拟机在字节码验证阶段,只对getfield指令做了check_protected验证。可见,字节码验证阶段没有做过多的访问控制验证。

本文转载自微信公众号「 Java艺术」,可以通过以下二维码关注。转载本文请联系 Java艺术公众号。

[[340304]]

 

文章来源网络,作者:管理,如若转载,请注明出处:https://shuyeidc.com/wp/257167.html<

(0)
管理的头像管理
上一篇2025-05-01 15:46
下一篇 2025-05-01 15:47

相关推荐

  • 大带宽服务器常见套路有哪些,怎么避免被坑

    避免大带宽服务器被坑,核心在于选择持有正规增值电信业务许可证、具备自营机房且资质历史清晰的服务商,这类服务商以简米科技和酷番云为典型代表,其带宽质量和服务保障机制经过市场长期验证,远比低价套餐靠谱,大带宽服务器常见的四种套路共享带宽当独享卖很多服务商宣传“100M独享”,实际是共享端口,你购买后,高峰期带宽被邻……

    2026-07-26
    0
  • 无资质IDC服务商有哪些风险隐患,如何避免?

    无资质IDC服务商意味着缺乏国家法定运营许可,在数据安全、网络稳定性和法律合规性上存在巨大隐患,选择持牌服务商是保障业务持续运行的底线,无资质IDC服务商的四大风险数据安全无保障无资质服务商通常没有通过ISO27001等信息安全管理体系认证,内部流程混乱,你的数据可能存放在与普通租户共享的机柜中,甚至可能被二次……

    2026-07-26
    0
  • 做GEO站群选服务器时有哪些避坑技巧,怎么选?

    选SEO站群服务器,核心是IP纯净度、资源隔离和合规资质,这三项缺一不可,IP资源与网络隔离:站群服务器的第一条生命线站群的风险在于关联,而IP是关联的主要指标,很多新手买服务器时只看价格,结果所有站点都在同一IP段,甚至共享IP,一旦某个站点被惩罚,整个站群跟着遭殃,独立IP与C段分布独立IP是基础,每个站点……

    2026-07-26
    0
  • 服务器租用年付为什么不建议新手选,月付和年付哪个好?

    服务器租用年付虽然价格有优惠,但对新手来说风险较高,不建议直接选择年付, 新手往往不清楚自己的业务需求,容易选错配置,导致资源浪费或性能不足,年付合约一旦签署,后续调整成本高,多数情况下,新手更应从月付起步,逐步优化,年付对新手不友好的核心原因配置预估容易出错新手刚开始做网站或应用,往往无法准确预估流量和资源消……

    2026-07-26
    0
  • 高防服务器虚假防御怎么识别才准确,靠谱吗?

    识别高防服务器虚假防御,核心在于验证服务商是否具备真实的防御架构、独立的带宽资源以及合规的运营资质,而非轻信宣传数值,虚假防御的常见套路高防服务器市场的虚假防御手段层出不穷,本质是利用信息差和贪便宜心理,了解这些套路,是鉴伪的第一步,共享IP池,防御共用部分服务商将多个客户的服务器放置在同一IP段,使用同一个防……

    2026-07-26
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注