查看手写JAVA虚拟机系列可以进我的博客园主页查看。

  我们知道,我们编译.java并运行.class文件时,需要一些java命令,如最简单的helloworld程序。

  

  这里的程序最好不要加包名,因为加了包名的话编译和运行需要有所改动。

  看这里的命令。javac为编译命令,我们知道java的特点是一次编译,到处运行。这里的编译指的就是javac,对于java程序即.java文件,先要用javac编译成字节码。然后将字节码(.class文件)放到java虚拟机中运行,即上图中的java HelloWorld,java虚拟机把字节码翻译成对应机器上的机器指令,再由机器来执行具体的机器指令。也就是说java程序员是直接与java虚拟机交互,简介与机器交互。所以虚拟机完成的是java命令,也就是我们要完成的是java这个指令的功能

  那么我们把第一个目标定为,实现简单的命令行。即我们通过命令行可以输入一些内容,虚拟机读取之后可以给一定的反馈。

  GO语言中有两个和命令行相关的包,分别是os和flag(java中以类库即jar文件导入,go中直接以包的形式导入)。

  首先在GOPATH目录下的src里面新建一个jvmgo文件夹作为我们的工作空间目录,jvmgo里面再新建一个ch01为我们的第一个目标源码文件夹,添加cmd.go文件。

  

  在cmd.go里面输入如下代码(由于博客园的添加代码方式不支持go语言着色,所以采用C语言着色,高亮可能不太正确)

package main

import "flag"
import "fmt"
import "os" //定义Cmd结构体
type Cmd struct{
helpFlag bool
versionFlag bool
cpOption string
class string
args []string
} //解析命令行参数
func parseCmd() *Cmd {
cmd:=&Cmd{} //将printUsage函数传给flag.Usage
flag.Usage=printUsage
//设置各种解析的选项
flag.BoolVar(&cmd.helpFlag, "help", false, "print help message")
flag.BoolVar(&cmd.helpFlag, "?", false, "print help message")
flag.BoolVar(&cmd.versionFlag, "version", false, "print version and exit")
flag.StringVar(&cmd.cpOption, "classpath", "", "classpath")
flag.StringVar(&cmd.cpOption, "cp", "", "classpath")
//所有选项设置完成后调用flag.Parse解析所有选项,如果Parse失败,则调用flag.Usage打印帮助信息
flag.Parse() //调用flag.Args函数捕获未被解析的参数,第一个参数为主类名,后面的为传递给主类的参数
args:=flag.Args()
if len(args)>{
cmd.class=args[]
cmd.args=args[:]
} return cmd
} func printUsage() {
fmt.Printf("Usage:%s[-options] class [args...]\n",os.Args[])
}

  第一行为包名,main包,接着引入了三个包os,flag,fmt。os和flag都是处理命令行所需的包,fmt类似于C语言的printf和scanf等格式化IO。再往下定义了一个结构体Cmd,用来这个数据结构来格式化存储输入的命令行信息。helpFlag参数为命令行是否请求help,versionFlag参数为命令行是否请求version,cpOption为命令行传入的classpath即目标.class文件所在文件夹,class为命令行传入的.class文件名(不包括.class),args为命令行传入的其他参数。

  紧接着是一个parseCmd函数(go语言有函数和方法之分,方法调用需要receiver,函数调用则不需要 ),返回值为*Cmd,用来解析cmd传过来的参数。该函数里面先声明一个cmd并给这个cmd赋值一个新建的Cmd对象。go语言中的“:=”为声明并赋值,而"="为赋值。先把printUsage的函数赋值给flag.Usage,然后调用flag设置需要解析的选项,全部解析完毕,调用Parse函数解析所有选项。解析成功则结束,解析失败则调用printUsage打印到控制台。

  flag.Args可以捕获其他没有被解析的参数。上面解析成功之后,第一个参数就是主类名,剩下的就是传给主类的参数。

  工具类编写完成,下一个是主函数。先上主函数代码:

package main

import "fmt"

func main() {
//调用parseCmd解析命令行参数
cmd:=parseCmd() if cmd.versionFlag{
//输入了-version选项
fmt.Println("version 0.0.1")
}else if cmd.helpFlag||cmd.class==""{
//输入了-help选项
printUsage()
}else{
//启动jvm
stratJVM(cmd)
}
} func stratJVM(cmd *Cmd){
fmt.Printf("classpath:%s class:%s args:%v\n",
cmd.cpOption,cmd.class,cmd.args)
}

  跟java类似,在go里面main是一个特殊的包,go程序的入口就是main函数,但是不接受任何参数,也不能有返回值。main函数先调用parseCmd解析命令行参数,如果是-version则返回版本号,如果是-help则返回帮助信息,如果是其他则启动jvm,这里用一些输出信息“假装”启动了jvm,真正的jvm代码后面会加上。

  至此,对命令行的解析工作全部完成。先展示一下整个工作目录的结构,不然后面编译运行的时候会出错。

  

  我们的工作目录是D盘下的JVM里的goWorkSpace,再下面src,jvmgo,ch01,ch01里面包含的是我们的go文件。

  来测试一下,打开一命令行,输入go install jvmgo\ch01。这个命令是使用go.exe来install文件,这个文件存在于GOPATH下面的文件夹(jvmgo\ch01中),结果如图:

  

  然后在工作空间(GOPATH)的bin文件夹中就多出了一个ch01.exe。

  

  在此处打开命令行。可以进行一些操作:

  

  到这里,我们的命令行工具就完成了,虽然还没有涉及真正的虚拟机设计,但这也是虚拟机运行的重要一步,后面会逐渐介绍虚拟机的设计。

最新文章

  1. 大熊君学习html5系列之------Online && Offline(在线状态检测)
  2. C++ 中 int 转string, 以及10进制转2进制
  3. Codeforces Round #389 Div.2 E. Santa Claus and Tangerines
  4. 阻塞与非阻塞IO step by step
  5. 新手必看,老鸟绕道–LAMP简易安装
  6. scala学习笔记1
  7. 非堵塞socket实现android手机与PC的文件传输
  8. poj 1654 Area(计算几何--叉积求多边形面积)
  9. USACO Section 1.1-3 Friday the Thirteenth
  10. simpleImageTool又纯java图片水印、缩放工具
  11. bazel 测试过程
  12. Vue解析三之过滤器
  13. codeforces 798c Mike And Gcd Problem
  14. redis的Sorted Set类型!!!!
  15. IIS安装以及发布
  16. 常见的7种XSS
  17. log4j2日志模板
  18. 利用request和re抓取猫眼电影排行
  19. Laravel篇二之本地版本库关联github
  20. ubantu 安装 wget

热门文章

  1. Mego(08) - 高级建模
  2. H5 FormData对象的作用及用法
  3. 第1章 什么是TCP-IP
  4. SpringBoot单元测试中的事务和Session
  5. ELK学习总结(2-3)Mget获取多个文档
  6. 新概念英语(1-69)The car race
  7. python网络爬虫与信息提取 学习笔记day1
  8. PHP环境配置(1)
  9. SpringMVC(六):@RequestMapping下使用@RequestHeader绑定请求报头的属性值、@CookieValue绑定请求中的Cookie值
  10. HDU-1850 Being a Good Boy in Spring Festival---尼姆博奕的运用