闲暇时翻阅了近期下载到的电子书《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:

  1. 理解Golang包导入
  2. godep支持Go 1.5 vendor
  3. 理解Go 1.5 vendor
  4. Go 1.4中值得关注的几个变化
  5. Go 1.5中值得关注的几个变化

Go包导入与Java的差别相关推荐

  1. java工程加包_向java工程添加jar包

    当你在向java类中导入某些包的时候(比如:import javax.servet.*)会报错,提示没有这个包.那是因为我们使用的jdk的JRE System Library库中没有包含servet_ ...

  2. cmd库的导入Java,在cmd命令窗口导入第三方jar包来运行java文件

    在cmd命令窗口导入第三方jar包来运行java文件,以下测试都是基于window环境,Linux环境没有测试. 1.编译 使用命令javac -cp或者javac -classpath 本机测试:如 ...

  3. java 导入包报错_Intellij IDEA 导入个推jar包 报错java.lang.NoClassDefFoundError

    Intellij IDEA使用的是Gradle,发现个推服务器SDK没有Gradle的导入方式,不会用Maven,只好选择jar包方式导入 却遇到了一个坑. 想通过导入jar包方式使用个推的SDK,直 ...

  4. JAVA第三方包导入但找不到类,解决:导入第三方包报错java.lang.NoClassDefFoundError:XXX.XXX,XXXXXX...

    报错信息如下,主要是导入androidpn测试client端,结果报错 04-02 16:28:34.325: E/dalvikvm(485): Could not find class 'org.a ...

  5. JAVA导入Oracle包_将java的jar包导入oracle,通过oracle调用java方法

    最近项目上遇到一个问题,因为项目一些数据设计保密的问题,所以数据库的很多数据都是脱敏存储的;但是需求又需要去模糊查询,这就蛋疼了, 由于项目是用base64脱敏的;第一次想到了把条件进行脱敏之后去数据 ...

  6. 【Java】从默认包导入类和对象报错的解决方法

    在Java工程结构里,源码根目录下的Java类是处于默认包下,不显示包名. 潜在的问题是,导入默认包的某个类(主要指静态导入)非常麻烦.例如,导入默认包下的Test类中的TEST常量,应该写成impo ...

  7. 我的世界java材质包转基岩_Minecraft我的世界基岩版材质包导入教程

    Minecraft我的世界基岩版材质包导入教程!大家好这里是千羽,今天为大家带来Minecraft基岩版材质包的导入方法,包括Win10版以及安卓版的材质包导入教程视频,不知道材质包怎么导入的同学可参 ...

  8. java包(翻译自Java Tutorials)

    原文出自 http://www.cnblogs.com/ggjucheng/archive/2012/12/17/2821935.html 英文出自 http://docs.oracle.com/ja ...

  9. 解决 com.sun.*包导入错误

    解决 com.sun.*包导入错误 com.sun.image.codec.jpeg.*导入错误如何解决: com.sun.*是受限制访问的API,Eclipse 默认把受访问限制的API设成了ERR ...

最新文章

  1. 吴恩达的二八定律:80%的数据+20%的模型=更好的机器学习
  2. pythonwith作用_老生常谈Python startswith()函数与endswith函数
  3. 存储过程学习笔记(SQL数据库
  4. php循环语句w,php ftpconnectphp for 循环语句使用方法详细说明
  5. JFreeChart 1.0.6 用户开发指南(中文)
  6. centos安装jenkins
  7. EfficientDet目标检测谷歌官方终于开源了!
  8. LintCode初级题——fizz buzz
  9. 312. Burst Balloons
  10. CCS3.3之DM642开发环境建立
  11. mysql s授权所有用户_查看MYSQL数据库中所有用户及拥有权限
  12. hdu-1695 GCD(莫比乌斯反演)
  13. python_csv文件使用记录
  14. pagefile.sys这个文件怎麽能删除
  15. 使用IronPython集成Python和.NET
  16. ROS安装教程(ubuntu18.04+melodic版本)
  17. sqlserver tvps java_中毒了,请高手看一下诊断报告。
  18. ftp 创建工具下载,3款能够下载的ftp 创建工具
  19. iphonex 序列号_X的序列号什么开头有什么意思吗?
  20. 【一文读懂】python 中的 numpy.reshape(a, newshape, order=‘C‘) 详细说明及实例讲解

热门文章

  1. awk应用小结(所有命令行均经调试)
  2. python开发环境搭建---pyenv安装python3.5.2
  3. Disruptor是一个高性能的异步处理框架
  4. KindEditorckplayer的简单结合
  5. Cassandra HBase和MongoDb性能比较
  6. axis使用wsdl生成客户端
  7. 新版本找不到tf.contrib的解决方案
  8. OpenCV的图像处理——iOS与OpenCV之间图像转换
  9. OpenCV图像处理——对比两张图像差异的位置并标记
  10. 笔记1——C++多态与Java多态的异同