手写JVM系列(2)-参数解析
这一章节开始JVM的编写,首先解决第一个问题,就是如何运行java程序,回想最开始学java时,写一个Hello.java的程序,使用javac Hello.java将源代码转换为class文件,然后使用java Hello运行程序。JVM是只认class文件的,如果想让JVM运行该Hello程序,就要通过java命令。本节将介绍java命令的常用方法,并实现java命令的解析。最终通过命令参数,读取对应的class数据。本节的代码均在classpath包下
java 命令简介
Java应用程序需要一个入口点,这个入口点就是我们熟知的main()方法。如果一个类包含public static void main(String[] args)方法,这个类就可以用来启动Java应用程序,我们把这个类叫作主类.这个类是需要我们用命令行手动向JVM提供的;
Java命令一般有以下几种形式:
java [-options] class [args]
java [-options] -jar jarfile [args]
javaw [-options] class [args]
javaw [-options] -jar jarfile [args]
可以向 java 命令传递三组参数:选项、主类名(或者JAR文件名)
和 main() 方法参数。选项由减号-开头。通常,第一个非选项参数给出主类的完全限定名(fully qualified class name)。但是如果用户提供了–jar选项,第一个非选项参数表示JAR文件名,java命令必须从这个JAR文件中寻找主类。javaw命令和java命令几乎一样,唯一的差别在于,javaw命令不显示命令行窗口,因此特别适合用于启动GUI(图形用户界面)应用程序。
可以看到命令中都带有一个 [-options]代表可选项, java命令行常用选项如下:
| 选项 | 用途 |
|---|---|
| -version | 输出版本信息 |
| -?/-help | 输出帮助信息 |
| -cp/-classpath | 指定用户类路径 |
| -Dproperty=value | 设置Java系统属性 |
| -Xms<size> | 设置初始堆空间大小 |
| -Xmx<size> | 设置最大堆空间大小 |
| -Xss<size> | 设置线程栈空间大小 |
当然,java命令行里提供的选项有很多,这里并不打算全部实现,只是挑几个比较重要的去实现.
接下来我打算实现以下几种命令:
java -versionjava -?java -helpjava -cp your/classpath yourClassName arg1 arg2 ...java -classpath your/classpath yourClassName arg1 arg2 ...
命令行解析功能编码
定义Cmd类:
public class Cmd {
boolean isRightFmt = true; //是否是正确的格式;
boolean helpFlag; //是否是help 查看帮助
boolean versionFlag; //是否是查看版本
String cpOption = ""; //classPath 的路径; java -cp(-classpath) xxx
String clazz; //要编译的class 文件;
String[] args; //执行clazz文件需要的参数
public Cmd(String[] strs){
parseCmd(strs);
}
public void parseCmd(String[] args) {
int index = 1;
if (args.length<2){
isRightFmt = false;
return;
}
//首先判断开头是不是 java ,如果连这个都不是,直接退出吧,提示正确的使用方法;
if (!args[0].equals("java")) {
isRightFmt = false;
} else {
if (args[1].equals("-help") || args[1].equals("-?")) {
helpFlag = true;
} else if (args[1].equals("-version")) {
versionFlag = true;
} else if (args[1].equals("-cp") || args[1].equals("classpath")) {
if (args.length < 4) {
//如果走到这一步,那么命令行必定是java -cp aa/bb test 11 22 33 的形式,所以应该至少有4项;
isRightFmt = false;
}
index = 4;
this.cpOption = args[2];
} else if (args[1].equals("-Xjre")) {
if (args.length < 4) {
//如果走到这一步,那么命令行必定是java -Xjre "C:\Program Files\Java\jdk1.8.0_20\jre" java.lang.Object 的形式,所以应该至少有4项;
isRightFmt = false;
}
index = 4;
this.XjreOption = args[2];
}
this.clazz = args[index - 1];
this.args = new String[args.length - index];
for (int i = index; i < args.length; i++) {
this.args[i - index] = args[i];
}
}
}
//命令行格式错误,输出帮助信息
public void printUsage() {
System.out.println("Usage: java [-options] class [args...]\n");
}
}
这里定义了要解析的成员变量,注意这里包含了一个我们自己定义的-Xjre的选项,其具体含义见下一节,其它选项含义参见注释,主要功能在parseCmd()方法中实现解析参数,根据对应的位置的选项,然后在主函数中去使用.
public static void main(String[] args) {
Cmd cmd = new Cmd(args);
if (!cmd.isRightFmt) {
cmd.printUsage();
} else {
if (cmd.versionFlag) {
System.out.println("java version \"1.8.0_20\"\n"
+ "Java(TM) SE Runtime Environment (build 1.8.0_20-b26)\n"
+ "Java HotSpot(TM) 64-Bit Server VM (build 25.20-b23, mixed mode)");
} else if (cmd.helpFlag || cmd.args == null) {
cmd.printUsage();
} else {
startJVM(cmd);
}
}
}
首先检验命令行格式是否正确,如果不正确,输出使用信息,如果是查看版本,输出版本信息,这里暂时提供了一个临时的字符串,此功能待后续实现.如果是查看帮助信息,同样也使出使用信息.其它情况,传递给一个方法startJVM()实现真正的开启虚拟机的逻辑,当然,此方法现在暂时内部是空实现,待后续实现。