上一篇通过flag包实现了命令行参数的解析,其实就是将输入的参数保存到一个结构体中,上一篇说过的例如java -classpath hello.jar HelloWorld这种命令,那么HelloWorld这个类是怎么找出来的呢?是直接在hello.jar中去找吗?
还记得java的类加载机制吗?有个叫做双亲委托机制,就比如我们自己定义一个String类为什么没用呢?虽然说编译时可以通过的,但是在运行的时候却会报错,如下所示,为什么提示String类中没有main方法呢,明明我就写了呀!其实是在类加载的时候,首先会把String类交给启动类加载器加载,也就是在jdk中jre/lib目录下去找;没有的话就使用扩展类加载器去加载,也就是在jdk中jre/lib/ext中去加载,最后才是我们用户的类路径下找,默认是当前路径,也可以通过-classpath命令指定用户类路径;
而String类很明显在启动类路径下rt.jar包中,所以加载当的是官方的String类,当然没有main方法啦!
再回到最开始的问题,例如java -classpath hello.jar HelloWorld这种命令,HelloWorld这个类在哪里找,现在就很清楚了,现在jdk下jre/lib中找,找不到就到jre/lib/ext中找,还找不到就在-classpath指定的路径中找,下面就用go代码实现一下,文件目录如下,这次的目录是ch02,基于上一篇的ch01实现,classpath是一个目录,cmd.go和main.go是文件
一.命令行添加jre路径
为了可以更好的指定jre路径,我们命令行中添加一个参数-Xjre,例如ch2 -Xjre “D:\java\jdk8” java.lang.String,如果命令行中没有指定-Xjre参数,那么就去你计算机环境变量中获取JAVA_HOME了,这就不多说了,所以我们要把cmd.go这里结构体做一个修改,以及对应的解析也添加一个,不多说;
cmd.go
package main import ( "flag" "fmt" "os" ) //这个结构体用保存命令行输入的参数,例如:.\ch02.exe -Xjre "D:\java\jdk8\jre" java.lang.Object type Cmd struct { helpFlag bool versionFlag bool cpOption string XjreOption string class string args []string } //命令行输入 .\ch02.exe -Xjre "D:\java\jdk8\jre" java.lang.Object func parseCmd() *Cmd { cmd := &Cmd{} //这里的意思就是如果解析失败的话,就调用printUsage函数 flag.Usage = printUsage //下面这些在控制台中表示的是-xxx,要匹配上,没有匹配上就报错 //匹配上了的话,在-xxx后面的都是【表情】属于参数,其中第一个表示的是类名,后面的都是其他的参数 flag.BoolVar(&cmd.helpFlag, "help", false, "print help message") flag.BoolVar(&cmd.versionFlag, "version", false, "print version and exit") flag.StringVar(&cmd.cpOption, "classpath", "", "classpath") //解析jre类路径 flag.StringVar(&cmd.XjreOption, "Xjre", "", "path to jre") flag.Parse() args := flag.Args() //解析成功的话,那就继续获取后面的参数 if len(args) > 0 { cmd.class = args[0] cmd.args = args[1:] } return cmd } //这里传进去的参数,解析错误的话就显示第一个参数的提示信息 func printUsage() { fmt.Printf("Usage:%s [-options] class [args]\n", os.Args[0]) }