草庐IT

java - "final"在运行时是最终的吗?

coder 2023-05-18 原文

我一直在玩ASM ,并且我相信我成功地将 final 修饰符添加到类的实例字段中;但是,然后我继续实例化所述类并在其上调用 setter,这成功地更改了 now-final 字段的值。我的字节码更改是否有问题,还是最终仅由 Java 编译器强制执行?

更新:(7 月 31 日)这里有一些代码供您引用。主要部分是

  1. 一个带有 private int xprivate final int y 的简单 POJO,
  2. MakeFieldsFinalClassAdapter,它使得它访问的每个字段都是最终的,除非它已经是最终的,
  3. 和 AddSetYMethodVisitor,它会导致 POJO 的 setX() 方法也将 y 设置为与设置 x 相同的值。

换句话说,我们从一个具有一个 final (x) 和一个 non-final (y) 字段的类开始。我们使 x 最终。除了设置 x 之外,我们还让 setX() 设置 y。我们跑。 x 和 y 都设置没有错误。 code is on github .你可以克隆它:

git clone git://github.com/zzantozz/testbed.git tmp
cd tmp/asm-playground

需要注意的两点:我首先提出这个问题的原因是:我设置为 final 的字段和已经设置为 final 的字段都可以设置为我认为的是普通的字节码指令。

另一个更新:(8 月 1 日)使用 1.6.0_26-b03 和 1.7.0-b147 进行测试,结果相同。也就是说,JVM 在运行时会愉快地修改 final 字段。

最终(?)更新:(9 月 19 日) 我从这篇文章中删除了完整的源代码,因为它相当冗长,但它仍然可以在 github 上找到(见上文)。

我相信我已经最终证明 JDK7 JVM 违反了规范。 (参见 excerpt in Stephen's answer 。)在使用 ASM 修改字节码后,如前所述,我将其写回到类文件中。使用优秀的JD-GUI ,这个类文件反编译成如下代码:

package rds.asm;

import java.io.PrintStream;

public class TestPojo
{
  private final int x;
  private final int y;

  public TestPojo(int x)
  {
    this.x = x;
    this.y = 1;
  }

  public int getX() {
    return this.x;
  }

  public void setX(int x) {
    System.out.println("Inside setX()");
    this.x = x; this.y = x;
  }

  public String toString()
  {
    return "TestPojo{x=" +
      this.x +
      ", y=" + this.y +
      '}';
  }

  public static void main(String[] args) {
    TestPojo pojo = new TestPojo(10);
    System.out.println(pojo);
    pojo.setX(42);
    System.out.println(pojo);
  }
}

简单的看一下应该会告诉你,由于重新分配了 final 字段,类永远不会编译,但是在普通的 JDK 6 或 7 中运行该类看起来像这样:

$ java rds.asm.TestPojo
TestPojo{x=10, y=1}
Inside setX()
TestPojo{x=42, y=42}
  1. 在我报告这方面的错误之前,其他人是否有意见?
  2. 谁能确认这应该是 JDK 6 中的错误还是 JDK 7 中的错误?

最佳答案

Is “final” final at runtime?

不是你的意思。

AFAIK,final 的语义修饰符仅由字节码编译器强制执行。

没有用于初始化 final 的特殊字节码字段,并且字节码 validator (显然)也不检查“非法”分配。

但是,JIT 编译器可能会处理 final修饰符作为不需要重新获取事物的提示。因此,如果您的字节码修改标记为 final 的变量您有可能导致不可预知的行为。 (如果您使用反射来修改 final 变量,也会发生同样的事情。规范清楚地说明了......)

当然,您可以修改 final使用反射的字段。


更新

我查看了 Java 7 JVM 规范,它与我上面所说的部分矛盾。具体来说,PutField 操作码的描述是:

"Linking Exceptions ... Otherwise, if the field is final, it must be declared in the current class, and the instruction must occur in an instance initialization method (<init>) of the current class. Otherwise, an IllegalAccessError is thrown.".

所以,虽然您可以(理论上)分配给 final字段多次在对象的构造函数中,字节码 validator 应该阻止任何尝试加载包含分配给 final 的字节码的方法。 .哪个...当您想到 Java 安全沙箱时...是一件好事。

关于java - "final"在运行时是最终的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6881288/

有关java - "final"在运行时是最终的吗?的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

    我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

  3. ruby-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

    我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

  4. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

    我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

  5. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  6. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

    为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

  7. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  8. ruby - 无法运行 Rails 2.x 应用程序 - 2

    我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby​​:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r

  9. ruby - Sinatra:运行 rspec 测试时记录噪音 - 2

    Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/

  10. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

随机推荐