java javac 浅析与使用
笔者能力有限,暂做不到看源码来分析具体流程,只能说个大概,如有不足之处,请评论纠正。
计算机是个巨大的黑盒,硬件-操作系统-应用程序每一层都有着层层障壁。java在这黑盒套黑盒中又套了层jvm,变成操作系统-jvm-java应用,真是令人困恼。
网上看java教程,都只说明javac编译一下java文件成字节码文件就可以通过java这个可执行文件直接运行。可看着才14kb的java,javac文件总是不明所以,为什么能运行呢?特别是javac还是由java编写的,为什么它能是个可执行文件,背后究竟做了什么?
openjdk17中的相关文件
//javac的源文件
jdk/src/jdk.compiler/share/classes/com/sun/tools/javac/Main.java
//通用启动器文件
jdk/src/java.base/share/native/launcher/main.c
//java源文件
jdk/src/java.base/share/native/libjli/java.c
//javac编译后的class被存放于modules中,可通过以下指令查看
# 解压 lib/modules 文件
jimage extract --dir extracted_modules {openjdk17-target}/lib/modules
# 查找 javac 的 class 文件
find extracted_modules -name "*javac*
#指定java可执行文件生成的 配置文件代码段
#make/CompileJavaModules.gmk
define DeclareCompileJavaRecipe
$1-java:
+($(CD) $(TOPDIR)/make && $(MAKE) $(MAKE_ARGS) \
$(patsubst %,-I%/modules/$1,$(PHASE_MAKEDIRS)) \
-f CompileJavaModules.gmk MODULE=$1)
endef
$(foreach m, $(JAVA_MODULES), $(eval $(call DeclareCompileJavaRecipe,$m)))
ALL_TARGETS += $(JAVA_TARGETS)
以上是我在openjdk17源码与发行包中找到的相关文件,不难看出,java就是个jvm启动器,通过命令行参数提供jvm的启动参数与具体的执行目标。javac就是启动jvm并提供执行具体的javac字节码入口目标的程序。
抛开工具直接使用java javac
工具jar工程
//不写包是可以编译并打包成jar的
//但是在另一个拥有包名的工程中将无法引用
package com.ppp;
public class HelloWorld{
public static void printMessage(){
System.out.println("Hello,World");
}
}
#将当前位置的java文件编译到指定的包名目录下
#会在out下生成 com/ppp/HelloWorld.class
javac -d out HelloWorld.java
#没有main入口 java命令不能启动
#但你可以通过jshell调用
jshell --class-path ./out
jshell> import com.ppp.HelloWorld;
jshell> HelloWorld.printMessage();
#没有main方法 也没有引入外部lib
#所以不加MANIFEST.MF
jar cvf jar/helloworld.jar -C out .
#这个jar无法运行 因为没有指定main入口 jvm不知道怎么启动
Main工程
//package和路径完全可以不相同
//想想maven的工程路径src/main/java/...
package com.formerlunchbox;
//本地工程的类文件
import com.formerlunchbox.tool.PrintTool;
//jar的类文件
import com.ppp.HelloWorld;
public class Example {
public static void main(String[] args) throws Exception {
for(String s:args){
PrintTool.printMessage(s);
}
//假设HelloWorld无包名 则会报错
//无法 import HelloWorld;
HelloWorld.printMessage();
}
}
package com.formerlunchbox.tool;
public class PrintTool {
public static void printMessage(String str) {
System.out.println(str);
}
}
MANIFEST.MF 指定main入口和引用的jar路径
多个jar 空格隔开
Manifest-Version: 1.0
Main-Class: com.formerlunchbox.Example
Class-Path: lib/helloworld.jar
#cp选项指定JAR包路径 多个lib 用:隔开
#-cp lib/helloworld.jar:lib/anotherlib.jar
javac -cp lib/helloworld.jar -d out src/main/java/com/formerlunchbox/*.java src/main/java/com/formerlunchbox/tool/*.java
#在指定out目录下 引用jar 提供main路径
java -cp out:lib/helloworld.jar com.formerlunchbox.Example 1
# 按指定的方式生成jar文件
jar cmvf META-INF/MANIFEST.MF example.jar -C out .
# 运行jar文件
java -jar example.jar 1
手打几遍就知道maven的好了!
题外话-关于jvm
众所周知,绝大多数jvm都是使用c++编写的,但总是有那么几个jvm不同凡响,用到了java来实现。
-
JikesRVM 和 Maxine VM
通过一个小的 C/C++ 引导程序启动自己的 Java 运行时。在启动之后,它们的 JIT 编译器(也是用 Java 写的,由c/c++引导程序来负载)会将 Java 代码即时编译为机器码,让代码直接在硬件上执行。因此,虽然用 Java 写的,但它们通过自举和自编译的方式,不依赖其他 JVM。 -
GraalVM
Graal 编译器作为 HotSpot JVM 的插件工作。它在 HotSpot 中作为 JIT 编译器来优化和运行 Java 字节码,并将代码编译为机器码直接运行。因此 GraalVM 依赖 HotSpot 提供的 JVM 环境。
java还是离不开大肠包小肠!
Comments NOTHING