查看手写JAVA虚拟机系列可以进我的博客园主页查看。
前面我们介绍了准备工作以及命令行的编写。既然我们的任务实现命令行中的java命令,同时我们知道java命令是将class文件(字节码)转换成机器码,那么我们现在的任务就是读出这个class文件里面的内容。
正文:
java虚拟机规范中是没有规定虚拟机该从哪里找类,也就是找class文件的,而oracle的是根据类路径,也就是classpath来搜索类的。搜索的优先级:启动类路径(bootstrap classpath)>扩展类路径(extension classpath)>用户类路径(user classpath)。
启动类路径(bootstrap classpath):默认为指定的jre\lib目录。
扩展类路径(extension classpath):默认为指定的jre\lib\ext目录。
看一下我们现在的工作目录结构(具体工作目录看我博客首页前面的文章)。
与前一章看起来还是有一些差别的,后面会一一介绍。
类路径的设计我们我们采用组合模式。类路径由启动类路径、扩展类路径和用户类路径组成,这三个路径又由更小的路径构成。
首先定义一个接口来表示类路径。在ch02\classpath目录下创建entry.go文件,在其中定义Entry接口:
package classpath
import"os"import"strings"
//存放路径分隔符
const pathListSeparator = string(os.PathListSeparator)//定义Entry接口
type Entry interface{
readClass(classNamestring)([]byte,Entry,error)//查找和加载class文件
String() string//类似于java中toString()函数
}//类似于java的构造函数,根据参数创建不同类型的Entry
func newEntry(path string)Entry{ifstrings.Contains(path, pathListSeparator) {returnnewCompositeEntry(path)
}if strings.HasSuffix(path, "*") {returnnewWildcardEntry(path)
}if strings.HasSuffix(path, ".jar") || strings.HasSuffix(path, ".JAR") ||strings.HasSuffix(path,".zip") || strings.HasSuffix(path, ".ZIP") {returnnewZipEntry(path)
}returnnewDirEntry(path)
}
由newEntry()方法可能会猜到我们对Entry接口有4个实现,分别是DirEntry、ZipEntry、CompositeEntry和WildcardEntry,因此我们在classpath文件夹下面分别建立四个go文件,如图:
其实这4种实现的基本逻辑都是类似的,我们以DirEntry为例详细说明(也就是entry_dir.go文件):
package classpath
import"io/ioutil"import"path/filepath"type DirEntrystruct{
absDirstring //存放绝对路径
}//用path创建一个DirEntry实例并返回
func newDirEntry(path string) *DirEntry{//将path转换为绝对路径,如果出错则panic,无错则创建DirEntry实例并返回
absDir,err:=filepath.Abs(path)if err!=nil{
panic(err)
}return &DirEntry{absDir}
}//将指定class的内容读出
func (self *DirEntry) readClass(className string) ([]byte,Entry,error){//讲绝对路径和文件名拼接在一起,并使用ioutil包读取该指定文件内容,返回结果
fileName :=filepath.Join(self.absDir,className)
data,err:=ioutil.ReadFile(fileName)returndata,self,err
}
func (self*DirEntry) String() string{returnself.absDir
}
首先引入了两个包,io/ioutil之前介绍过,类似于C的输入输出流,path/filepath用于对路径进行处理。然后定义了DirEntry这个结构体,里面只有一个absDir字段,类型为string,这个字段是用来存储绝对路径的。
再往下是三个函数。第一个newDirEntry(path string) *DirEntry,由于go语言中没有像java那样自带构造函数