“看到一个沙粒中的世界”,我们很可能会看到最简单的“ Hello World”中的世界,所以我们开始吧,再一次向世界问好。

我猜所有的Java课程,教程都是从这个著名的Hello World程序开始的,这是我可以在没有IDE的帮助下编写的非常罕见的程序之一:)


public class HelloWorld {public static void main(String[] args) {System.out.println("Hello World");}
}

1.您知道这些javac选项吗?

编写第一个程序后,您将首先执行以下命令进行编译,否则将无法运行。

javac HelloWorld.java

您可能会发现不必将文件命名为“ HelloWorld.java”,“ Hello.java”也可以使用。 public class HelloWorld也可以降级为class HelloWorld

如果您好奇地按下javac --help ,将会看到很多有关Java编译器的选项,例如,我们要打印中文版“ Hello World”,并希望它完全适用于JDK8语言级别,元数据为包含的参数名称,它看起来像这样:

javac -encoding UTF-8 -source 8 -target 8 -parameters Hello.java

您已经安装了JDK11,但是使用上面的命令仅使用1.8功能发布了类文件。 如果您编写了一些仅可从JDK9获得的内容,则会发现它无法按预期进行编译。

2.类文件的基础

关于Java虚拟机规范中的类文件格式的整章内容,您是否需要对其进行一些探讨?


您会看到字节码(与JDK11一起编译)以一个神奇的,神秘的“ cafe babe”开头,随后为55,很多东西会伤害您的大脑。 其中,“ cafe babe”是魔力,指向次要版本的55分,映射到JDK11。 与读取超赞的类文件格式相比,您还可以使用javap检索该类文件的信息:

# You would use javap -h to see how many options you have
javap -p -l -c -s -constants HelloWorld

您将获得如下内容:

class HelloWorld {HelloWorld();                                                                                        descriptor: ()V                                                                                    Code:                                                                                              0: aload_0                                                                                      1: invokespecial #1                  // Method java/lang/Object."<init>":()V                    4: return                                                                                       LineNumberTable:                                                                                   line 1: 0                                                                                        public static void main(java.lang.String[]);                                                         descriptor: ([Ljava/lang/String;)V                                                                 Code:                                                                                              0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;        3: ldc           #3                  // String Hello World                                      5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V8: return                                                                                       LineNumberTable:                                                                                   line 4: 0                                                                                        line 5: 8
}

您会发现这里的指令与源代码有些相似,带有源代码的行号和指令号的映射,您可能想知道,我可以从这些东西中恢复源代码吗?

3.反编译器

是的你可以。 反编译器有很多,但是其中一些反编译器已经过时,例如JD-GUI ,JAD等,它们在使用最新JDK编译的类文件上不能很好地工作。 您仍然可以使用它们,但CFR更合适。

# java -jar cfr-0.139.jar HelloWorld.class
/*                                               * Decompiled with CFR 0.139.*/
import java.io.PrintStream;                      class HelloWorld {                               HelloWorld() {                               }                                            public static void main(String[] arrstring) {System.out.println("Hello World");       }
}

您可能已经发现源代码和反编译的代码(添加了构造方法)略有不同,实际上,您可能会惊讶地发现有时似乎对源代码进行了修改,从而使您感到惊讶。 但是,其中许多是通过JVM进行的优化,通常可以提高性能,比较它们之间的差异实际上很有趣,并且可以为您提供很多见识。

4.如何再次初始化具有空值的最终变量?

System.out.println("Hello World") ,System是一个类,out是其最终属性的静态属性之一:

public final static PrintStream out = null;

然后问题来了,为什么hack System.out.println("Hello World")不会抛出著名的NullPointerException ,根据语言规范,似乎最终的静态变量out不可能分配给有效值再次吧?

是的,在大多数情况下,如果您不使用肮脏的反射技巧并且不引入native好友,那是对的。

如果您只是想玩转,可以这样做:

Field f = clazz.getDeclaredField("out");
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL);

但是,这对于System无效,实际的秘密隐藏在System.java以下代码行中:

private static native void registerNatives();
static {registerNatives();
}

按照方法上方写的注释,“ VM将调用initializeSystemClass方法来完成此类的初始化”,转到initializeSystemClass方法,您将看到以下行:

FileInputStream fdIn = new FileInputStream(FileDescriptor.in);
FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
FileOutputStream fdErr = new FileOutputStream(FileDescriptor.err);
setIn0(new BufferedInputStream(fdIn));
setOut0(newPrintStream(fdOut, props.getProperty("sun.stdout.encoding")));
setErr0(newPrintStream(fdErr, props.getProperty("sun.stderr.encoding")));

而且你还可以看到这3种本地方法设置inout

private static native void setIn0(InputStream in);
private static native void setOut0(PrintStream out);
private static native void setErr0(PrintStream err);

因此,现在您知道JVM在OS级别上做了这些工作并“绕过”了final限制,您可能会问,JVM将适应的OS级别代码在哪里被破解?

所以这里是System.c (JDK11版本) 。

JNIEXPORT void JNICALL
Java_java_lang_System_registerNatives(JNIEnv *env, jclass cls)
{(*env)->RegisterNatives(env, cls,methods, sizeof(methods)/sizeof(methods[0]));
}
/** The following three functions implement setter methods for* java.lang.System.{in, out, err}. They are natively implemented* because they violate the semantics of the language (i.e. set final* variable).*/
JNIEXPORT void JNICALL
Java_java_lang_System_setIn0(JNIEnv *env, jclass cla, jobject stream)
{jfieldID fid =(*env)->GetStaticFieldID(env,cla,"in","Ljava/io/InputStream;");if (fid == 0)return;(*env)->SetStaticObjectField(env,cla,fid,stream);
}

在这里,您可以在注释中找到后门, “它们是本机实现的,因为它们违反了语言的语义(即,设置最终变量)”

然后,您会发现这是一条漫长的道路。 旅程将永远不会停止。

结束:停一会儿

“用沙粒看世界
还有野花中的天堂
将Infinity握在手中 一小时的永恒”

如果最简单的HelloWorld只是一片沙粒,那么里面肯定有一个世界,也许您对它说过很多次“ Hello”,但这并不意味着您已经探索了一点世界,也许现在时间和探索世界,虽然沙子会使您的手变脏,但花朵却不会。


翻译自: https://www.javacodegeeks.com/2019/02/world-grain-sand-world.html

看到一个沙粒世界:再一次你好世界相关推荐

  1. 世界是沙粒还是宇宙_看到一个沙粒世界:再一次你好世界

    世界是沙粒还是宇宙 "看到一个沙粒中的世界",我们很可能会看到最简单的" Hello World"中的世界,所以我们开始吧,再一次向世界问好. 我猜所有的Jav ...

  2. c语言和Java你好世界,C编程语言之“你好世界”的例子

    在我们研究C编程语言的基本构建块之前,让我们看一下最基本的C程序结构,以便我们在后面得内容将其作为参考. C编程语言之"你好世界"的例子-1.jpg (37.41 KB, 下载次数 ...

  3. wxWidgets:你好世界示例

    wxWidgets:你好世界示例 wxWidgets:你好世界示例 wxWidgets:你好世界示例 这个页面展示了一个非常简单的 wxWidgets 程序,它可以用作您自己代码的骨架. 虽然它没有什 ...

  4. java 写一个quot;HelloJavaWorld你好世界quot;输出到操作系统文件Hello.txt文件中

    package com.beiwo.homework;import java.io.File; import java.io.FileOutputStream; import java.io.IOEx ...

  5. Java IO练习--在程序中写一个“HelloJavaWorld你好世界“输出到操作系统文件Hello.txt文件中

    package com.kj.test;import cn.hutool.core.io.IoUtil;import java.io.File; import java.io.FileOutputSt ...

  6. java 写一个HelloJavaWorld你好世界输出到操作系统文件Hello.txt文件中

    package com.beiwo.homework;import java.io.File; import java.io.FileOutputStream; import java.io.IOEx ...

  7. 第一个C++的程序你好世界

    C++的"开始" Hello World! 你好世界! // 第一个程序 //代表注释这一行 #include <iostream> //c++专属头文件using n ...

  8. 在程序中写一个“HelloJavaWorld你好世界“输出到操作系统文件Hello.txt文件中

    import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOExce ...

  9. 2023ACP世界大赛中国总决赛|让世界再多一个微笑

    5月21日,正值第三十三次全国助残日,作为公益推行的一份子,恒利联创也呈现出了"仁者爱人"的文化内核. 恒利联创携手微笑明天慈善基金会合作同行,旨在推动公益,促进残疾人事业的全面发 ...

最新文章

  1. java类与对象作用_Java类与对象
  2. 简述如何书写工程化的简单代码
  3. halcon 将数据保存到excel_Python保存数据到Excel文件的多个sheet
  4. Redis String 类型操作及常用命令
  5. C++学习之路 | PTA乙级—— 1051 复数乘法 (15 分)(精简)
  6. HDU 3709 Balanced Number(数位DP)题解
  7. 苹果悬赏100万美元找漏洞 辞职的理由找到了!
  8. UCenter创始人、Discuz!创始人、管理员账号的认知(转)
  9. JavaScript案例一:Window弹窗案例
  10. MyBatis源码阅读(五) ---Mapper接口的获取过程
  11. EDEM软件简单介绍
  12. 区块链相关技术学习总结(1)——区块链以及区块链技术入门详解
  13. 中国工程院院士张尧学:透明计算/云计算操作系统
  14. 杂凑算法md5c语言代码,MD5杂凑算法
  15. 谷歌搜索表单参数url参数详解
  16. 节奏大师小游戏制作流程
  17. 参考文献格式详细解释和引用(常见)
  18. xbox控制台小助手服务器连接已阻止,win10系统xbox控制台小帮手无法登录,提示目前您无法登录怎么办...
  19. 飞秒激光脉冲的产生过程
  20. 鲨鱼抓包(Wireshark)简易操作说明

热门文章

  1. 【图论】【高精】产生数(ssl 1021/ luogu 1037)
  2. ACM-ICPC 2018 徐州赛区网络预赛 D. EasyMath
  3. codeforces 884E Binary Matrix 并查集,滚动数组
  4. Nacos(十一)之NacosSync 介绍
  5. 三个好用的并发工具类
  6. TCP 三次握手原理,你真的理解吗
  7. 成为更优秀的程序员:退后一步看问题
  8. jsoup怎么获取两个标签之间的text?
  9. 单例模式懒汉、饿汉和登记
  10. 漫画:什么是MapReduce