.

.

Go 学习笔记

1 - 系统目录

Go 安装后的一些重要的目录

1.1 - 查看目录

在操作系统中安装Go语言编译环境后,有几个需要注意的重要目录,包括GOROOT,GOPATH和GOBIN等。

可以使用 go env命令查看Go环境变量,包含这几个目录对应的环境变量。

 $ go env
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/huabing/.cache/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/huabing/go"
GOPROXY=""
GORACE=""
GOROOT="/usr/local/go"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build688094926=/tmp/go-build -gno-record-gcc-switches"

1.2 - GOPATH

GOPATH目录是Go的工作目录,其中包含本地的项目文件,项目中引用的的第三方package,以及生成的二进制文件。

目录结构

Go语言工具要求GOPATH的目录结构包含bin,pkg,src三个子目录。

  • bin目录下是项目编译生成的二进制文件。
  • pkg目录下是项目需要依赖的一些第三方package,这些第三方package并不包含在GOROOT中pkg目录下的基础package中。例如mysql数据库驱动,web框架等。
  • src目录中包含项目的源文件,以及通过go get下载的第三方package的源码。

通过go get下载第三方包

可以用go get命令下载第三方包,go get会将源码下载到src目录下,并编译源码生成.a 二进制文件放到pkg目录下。如果源码中包含main文件,则会生成对应的二进制文件放到bin目录中。

使用go get命令安装mysql驱动和代码规范检查工具golint

go get github.com/go-sql-driver/mysql
go get golang.org/x/lint/golint

上面的命令会下载mysql driver和golint的源码,并编译生成二进制package包,由于golint源码中包含了main方法,还会在bin目录生成对应的可执行文件。

目录示例

├── bin                               //项目生成的可执行文件和通过go get安装的可执行文件
│   ├── golint
│   └── myproject
├── pkg                               //通过go get安装的第三方包二进制文件
│   └── linux_amd64
│       ├── github.com
│       │   └── go-sql-driver
│       │       └── mysql.a
│       └── golang.org
│           └── x
│               └── lint.a
└── src                              //项目代码和通过go get安装的第三方包源码
    ├── golang.org
    │   └── x
    │       ├── lint
    │       │   ├── CONTRIBUTING.md
    │       │   ├── golint
    │       │   │   ├── golint.go
    │       │   │   ├── importcomment.go
    │       │   │   └── import.go
    │       ......                
    ├── go-sql-driver
    │   │   └── mysql
    │   │       ├── appengine.go
    │   │       ├── auth.go
	│   ......
    │
    └── myproject                   //本地项目代码
        └── myapp.go

GOPATH中是否可以采用多个目录?

GOPATH可以允许设置为多个目录,多个目录之间用:(Linux)或者;(Windows)隔开。在GOPATH设置为多个目录的情况下,go get下载的第三方包会安装到第一个目录下。可以通过设置两个目录来将本地工作目录和第三方包分开:因为go get只会将通过go get下载的第三方包安装在第一个目录中,因此可以将第二个目录作为本地工作目录,以保持工作目录的整洁。

1.3 - GOROOT

GOROOT是Go的安装目录。Go安装程序会自动设置$GOROOT环境变量,一般不需要手动进行设置。

Linux下GOROOT的缺省目录为 /usr/local/go/,其目录结构如下所示。可以看到,GOROOT目录中包含了go命令行和go语言自带的一些基础包,如io,net,math等。在安装后,GOROOT目录中的内容不会变化。

├── bin                            //go命令行二进制
│   ├── go
│   ├── godoc
│   └── gofmt                            
├── pkg                            //go标准库的package二进制包,如io,net,math等。
│   ├── include
│   ├── linux_amd64
│   │   ├── archive
│   │   │   ├── tar.a
│   │   │   └── zip.a
│   │   ├── bufio.a
│   │   ├── bytes.a
│   │   ├── compress
│   │   │   ├── bzip2.a
│   │   │   └── ......
│   └── tool
├── src                           //go标准库源文件,如io,net,math等。
│   ├── archive
│   │   ├── tar
│   │   │   ├── common.go
│   │   │   ├── example_test.go
│   │   │   └── ......
│   ├── bufio
│   ├── builtin
│   ├── bytes
│   ├── ......
│   └── unsafe
└── ......

1.4 - 其他目录

  • GOBIN: go install 和 go get 命令在编译main package后生成的可执行文件的保存目录。如果没有设置GOBIN环境变量,可执行文件会被安装到$GOPATH/bin目录下。
  • GOCACHE:在该目录中保存了go编译过程中生成的一些文件,这些文件可以被以后的编译过程重用,以加快编译过程,不用每次都从头开始编译。
  • GOTMPDIR:go命令行会在该目录中写入一些临时文件。

2 - 包管理

Go package 介绍

Package 用于组织一组逻辑上紧密相关的 go 文件。是 go 语言中代码重用的基础单元。在文件系统中,一个 Package 对应一个文件夹,文件夹中包含该 Packag 中的多个 go 文件。在 go 语言模型中,一个 Packag 中包含了多个紧密相关的变量,结构体和方法。

Package中包含的内容:

└── package                           
    ├── variable
    ├── function
    ├── interface
    |   └─── method
    └── struct
        ├── variable
        └── method

2.1 - Package 命名

Package声明

在go源文件的开头必须申明文件所属的package,如下所示:

package name
......

命名规范

  • 建议package命名用小写字母
  • 建议package命名必和其路径的最后一段一致(main package除外)。注意,这并不是Golang的强制要求,文件目录只是用于存放同一个package的所有源文件,Golang对目录名并无要求。但一个目录下不允许有多个package的源文件。
  • main package中的main方法是可执行文件的入口,main package名一般和路径名不一致
  • 不同路径下package命名可以重复,但其完整路径名必须唯一

package 名和导入路径

package的导入路径是指从$GOPATH/src/开始的路径名。

例如 package github.com/zhaohuabing/demo 在文件系统中的路径为:$GOPATH/src/github.com/zhaohuabing/demo

2.2 - 对比JAVA和Go的Package

Go语言中的package和java类似,都用于将一组相关代码组织在一起,但两个语言对packag的使用又有不同之处。本文对比go和java package的异同,以帮助java程序员更好理解go的package。

Package声明

Java package声明

Java文件中的package声明和其文件路径是完全一致的。package是以.分割开来的文件名。

java 文件中的package命名:

package com.zhaohuabing.demo
......

其对应的目录结构如下:

├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── zhaohuabing
│   │   │           └── demo
│   │   │               ├── test.java
......
```language

Go package声明

Go 文件中的package声明中并没有层级结构。推荐将package名称和该package所在的源文件的目录名称保持相同。虽然这并不是强制要求,但遵循该建议会让Package的组织和使用容易很多。

package demo
......

其对应的目录结构如下:

├── src
│   ├── github.com
│   │   ├── zhaohuabing
│   │   │   └── demo
│   │   │       └── test.go
....

Package引用

在引用package时,java和go都需要采用完整路径名称。但import的粒度不同,java的最小粒度是class,而go是package。

Java package引用

Java语言模型一切皆为对象,因此在引用时以类为基本单位。在引用时要么引用一个具体的类,要么用*表示引用该package下的所有类。

import com.zhaohuabing.demo.*; //import demo包下面的所有class
import java.util.list;         //import一个class

Go package引用

Go语言有Struct,而且Struct也可以有成员函数,类似于Java的class。但Go并不是严格的面向对象的语言,Go的package中可以直接定义变量和函数。因此Java要求所有文件必须对应到一个类,但Go的文件和Struct之间没有必然的对应关系。

在Go语言中,只能import一个package,不能import一个Struct(类)。

import "github.com\zhaohuabing\demo"

源文件

Java

在Java语言中一切皆是对象,Java中类是一等公民,一个源文件对应于一个class,而package只是class的目录,package自身不能拥有方法和变量。

这样组织的优点是代码结构非常清晰,但约束性很强,无法在package层次上定义公用的方法及变量,一个变通的方法是在一个类中使用static来定义package中的全局变量和方法。该方式又反过来打破了Java中一切皆为对象的约定,是一种为了解决问题的不得已的“丑陋”解决方案。

Go

Go语言中package的使用更为灵活,package是一组相关Variable,Function和Struct的集合。由于可以直接在package上定义变量和方法,Go语言解决了Java中的缺点。

小结

Go语言中package的声明只取路径的最后一段,而引用需要采用全路径。Go语言中package声明和应用的不一致导致从java刚开始转向go的程序员比较难以理解go的package的使用。

除此以外,Go语言还有一些类似的和Java不一致的,但感觉没有必要的设计,例如函数的返回值在后面而不是前面。这些语言设计的细节不知是否为了和Java区分而故意为之?考虑到Java程序员的广大基数,如果换做是我来设计一门新的语言,我会尽量考虑遵从一些Java程序员的习惯,以吸引更多的潜在用户。

Java语言“一切皆为对象”的设计原则太过于理想化,而在解决实际问题时,一个过于理想化的方案往往是行不通的。Go语言通过Struct和Interface支持了面向对象的特性,也可以在package上定义变量和方法,比Java语言更为灵活。