Go包导入与Java的差别
闲暇时翻阅了近期下载到的电子书《Go in Practice》 ,看到1.2.4 Package Management一节中的代码Demo,感觉作者对Go package导入的说法似乎不够精确:“Packages are imported by their name”(后续的说明将解释不精确的原因)。联想到前几天遇到的一个Java包导入的问题,让我隐约地感觉Java程序员很容易将两种语言的Package import机制搞混淆,于是打算在这里将Golang和Java的Package import机制做一个对比,对于Java转型到Golang的程序员将大有裨益:)。这里的重点在于与Java的对比,关于Golang的Package Import的细节可以参考我之前写过的一篇文章《理解Golang包导入》。
我们先来看两个功能等价的代码。
//TestDate.java
import java.util.*;
import java.text.DateFormat;public class TestDate {public static void main(String []args){Date d = new Date();String s = DateFormat.getDateInstance().format(d);System.out.println(s);}
}
和
//testdate.go
package mainimport ("fmt""time"
)func main() {t := time.Now()fmt.Println(t.Format("2006-01-02"))
}
两个程序在Run时,都输出下面内容:
2016-9-13
我们看到Golang和Java都是用import关键字来进行包导入的:
import java.util.Date;Date d = new Date();
vs.
import "time"t := time.Now()
咋看起来,Java在package import后似乎使用起来更Easy,使用包内的类和方法时,前面无需再附着Package name,即Date d,而不是java.util.Date d。而Go在导入”time”后,引用包中方法时依然要附着着包名,比如time.Now()。但实质上两种语言在import package的机制上是有很大不同的。
1、机制
虽然都使用import,但import关键字后面的字符串所代表的含义有不同。
Java import导入的是类而不是包,import后面的字符串表示的是按需导入Java Package下面的类,比如import java.util.*; 或导入Package下某个类,比如import java.util.Date。而Go import关键字后面的字符串是包名吗?很多初学者会认为这个就是Go包名,实则不然,Go import后面的字符串实际上是一个包导入路径,这也是Java用”xxx.yyy.zzz”形式而Golang使用”xxx/yyy/zzz”形式的原因。我们用个简单的例子就能证明这一点。我们知道Golang会在\$GOROOT/src + \$GOPATH/src下面导入xxx/yyy/zzz路径下的包,我们在import “fmt”时,实际上导入的是\$GOROOT/src/fmt目录下的包,只是恰好这个下面的包的名字是fmt罢了。如果我们将\$GOROOT/src/fmt目录改名为fmt1,结果会是如何呢?
$go build helloworld.go
helloworld.go:3:8: cannot find package "fmt" in any of:/Users/tony/.bin/go17/src/fmt (from $GOROOT)/Users/tony/Test/GoToolsProjects/src/fmt (from $GOPATH)helloworld.go是一个helloworld go源码。
之所以出错是因为在\$GOROOT/src下已经没有fmt这个目录了,所以下面代码中的两个fmt含义是不同的(这也解释了Go in practice中关于包导入的说法的不精确的原因):
package mainimport "fmt" ---- 这里的fmt指的是$GOROOT/src下的名为"fmt"的目录名func main() {fmt.Println("Hello, World") --- 这里的fmt是真正的包名"fmt"
}
从上面我们可以看出Go的包名和包的源文件所在的路径的名字并没有必须一致的要求,这也是为什么在Go源码使用包时一定是用packagename.XX形式,而不是packagename.subpackagename.XX的形式了。比如导入”net/http”后,我们在源码中使用的是http.xxx,而不是net.http.xxx,因为net/http只是一个路径,并不是一个嵌套的包名。
之所以看起来导入路径的终段目录名与包名一致,只是因为这是Go官方的建议:Go的导入路径的最后一段目录名(xxx/yyy/zzz中的zzz)与该目录(zzz)下面源文件中的Go Package名字相同。
下面是一个非标准库的包名与导入路径终段名完全不一致的例子:
//github.com/pkgtest/pkg1/foo.go
package fooimport "fmt"func Foo() {fmt.Println("Foo in pkg1")
}
//testfoo.go
package mainimport ("github.com/pkgtest/pkg1"
)func main() {foo.Foo() //输出:Foo in pkg1
}
可以看出testfoo.go导入的是”github.com/pkgtest/pkg1″这个路径,但这个路径下的包名却是foo。
Java语言中的包实际以.jar为单位,.jar内部实际上也是以路径组织.class文件的,比如:foo.jar这个jar包中有一个package名为:com.tonybai.foo,foo包中包含类Foo、Bar,那实际上foo.jar内部的目录格式将是:
foo.jar- com/- tonybai/- foo/- Foo.class- Bar.class
但对于Java包的使用者,这些都是透明的。
2、重名
Java中关于包导入(实则是类导入)唯一的约束就是不能有两个类导入后的full name相同,如果存在两个导入类的full name完全相同,Javac在resolve时,要以ClassPath路径的先后顺序为准了,选择最先遇到的那个类。但是在Go中,如果导入的两个路径下的包名相同,那么Go compiler显然是不能允许这种情况的存在的,会给出Error信息。
比如我们在GOPATH下的github.com/pkgtest/pkg1和github.com/pkgtest/pkg2下放置了同名包foo,下面代码将会报错:
package mainimport ("github.com/pkgtest/pkg1""github.com/pkgtest/pkg2"
)func main() {foo.Foo()
}
错误信息如下:
$go run testfoo.go
# command-line-arguments
./testdate.go:8: foo redeclared as imported package nameprevious declaration at ./testfoo.go:7
解决这一问题的方法就是采用package alias:
package mainimport (a "github.com/pkgtest/pkg1"b "github.com/pkgtest/pkg2"
)func main() {a.Foo()b.Foo()
}
编译执行上面程序将得到下面结果,而不是Error:
Foo of foo package in pkg1
Foo in foo package in pkg2
© 2016, bigwhite. 版权所有.
Related posts:
- 理解Golang包导入
- godep支持Go 1.5 vendor
- 理解Go 1.5 vendor
- Go 1.4中值得关注的几个变化
- Go 1.5中值得关注的几个变化
Go包导入与Java的差别相关推荐
- java工程加包_向java工程添加jar包
当你在向java类中导入某些包的时候(比如:import javax.servet.*)会报错,提示没有这个包.那是因为我们使用的jdk的JRE System Library库中没有包含servet_ ...
- cmd库的导入Java,在cmd命令窗口导入第三方jar包来运行java文件
在cmd命令窗口导入第三方jar包来运行java文件,以下测试都是基于window环境,Linux环境没有测试. 1.编译 使用命令javac -cp或者javac -classpath 本机测试:如 ...
- java 导入包报错_Intellij IDEA 导入个推jar包 报错java.lang.NoClassDefFoundError
Intellij IDEA使用的是Gradle,发现个推服务器SDK没有Gradle的导入方式,不会用Maven,只好选择jar包方式导入 却遇到了一个坑. 想通过导入jar包方式使用个推的SDK,直 ...
- JAVA第三方包导入但找不到类,解决:导入第三方包报错java.lang.NoClassDefFoundError:XXX.XXX,XXXXXX...
报错信息如下,主要是导入androidpn测试client端,结果报错 04-02 16:28:34.325: E/dalvikvm(485): Could not find class 'org.a ...
- JAVA导入Oracle包_将java的jar包导入oracle,通过oracle调用java方法
最近项目上遇到一个问题,因为项目一些数据设计保密的问题,所以数据库的很多数据都是脱敏存储的;但是需求又需要去模糊查询,这就蛋疼了, 由于项目是用base64脱敏的;第一次想到了把条件进行脱敏之后去数据 ...
- 【Java】从默认包导入类和对象报错的解决方法
在Java工程结构里,源码根目录下的Java类是处于默认包下,不显示包名. 潜在的问题是,导入默认包的某个类(主要指静态导入)非常麻烦.例如,导入默认包下的Test类中的TEST常量,应该写成impo ...
- 我的世界java材质包转基岩_Minecraft我的世界基岩版材质包导入教程
Minecraft我的世界基岩版材质包导入教程!大家好这里是千羽,今天为大家带来Minecraft基岩版材质包的导入方法,包括Win10版以及安卓版的材质包导入教程视频,不知道材质包怎么导入的同学可参 ...
- java包(翻译自Java Tutorials)
原文出自 http://www.cnblogs.com/ggjucheng/archive/2012/12/17/2821935.html 英文出自 http://docs.oracle.com/ja ...
- 解决 com.sun.*包导入错误
解决 com.sun.*包导入错误 com.sun.image.codec.jpeg.*导入错误如何解决: com.sun.*是受限制访问的API,Eclipse 默认把受访问限制的API设成了ERR ...
最新文章
- 吴恩达的二八定律:80%的数据+20%的模型=更好的机器学习
- pythonwith作用_老生常谈Python startswith()函数与endswith函数
- 存储过程学习笔记(SQL数据库
- php循环语句w,php ftpconnectphp for 循环语句使用方法详细说明
- JFreeChart 1.0.6 用户开发指南(中文)
- centos安装jenkins
- EfficientDet目标检测谷歌官方终于开源了!
- LintCode初级题——fizz buzz
- 312.	Burst Balloons
- CCS3.3之DM642开发环境建立
- mysql s授权所有用户_查看MYSQL数据库中所有用户及拥有权限
- hdu-1695 GCD(莫比乌斯反演)
- python_csv文件使用记录
- pagefile.sys这个文件怎麽能删除
- 使用IronPython集成Python和.NET
- ROS安装教程(ubuntu18.04+melodic版本)
- sqlserver tvps java_中毒了,请高手看一下诊断报告。
- ftp 创建工具下载,3款能够下载的ftp 创建工具
- iphonex 序列号_X的序列号什么开头有什么意思吗?
- 【一文读懂】python 中的 numpy.reshape(a, newshape, order=‘C‘) 详细说明及实例讲解