够不够你说了算

golang的小基础,闲来翻看一下

Posted by HH on August 3, 2019

简介

修改从微信公众号Go程序员开发搬过来的,一些小基础,闲暇之时可以翻看巩固基础。

前言:为什么一定要学Go

Go语言是谷歌2009发布的第二款开源编程语言。 Go语言专门针对多处理器系统应用程序的编程进行了优化,使用Go编译的程序可以媲美C或C++代码的速度,而且更加安全、支持并行进程。

与其他语言相比的优势

学习曲线

它包含了类C语法、GC内置和工程工具。这一点非常重要,因为Go语言容易学习,所以一个普通的大学生花一个星期就能写出来可以上手的、高性能的应用。在国内大家都追求快,这也是为什么国内Go流行的原因之一。

效率

Go拥有接近C的运行效率和接近PHP的开发效率,这就很有利的支撑了上面大家追求快速的需求。

出身名门、血统纯正

之所以说Go语言出身名门,是因为我们知道Go语言出自Google公司,这个公司在业界的知名度和实力自然不用多说。Google公司聚集了一批牛人,在各种编程语言称雄争霸的局面下推出新的编程语言,自然有它的战略考虑。而且从Go语言的发展态势来看,Google对它这个新的宠儿还是很看重的,Go自然有一个良好的发展前途。我们看看Go语言的主要创造者,血统纯正这点就可见端倪了。

自由高效:组合的思想、无侵入式的接口

Go语言可以说是开发效率和运行效率二者的完美融合,天生的并发编程支持。Go语言支持当前所有的编程范式,包括过程式编程、面向对象编程以及函数式编程。程序员们可以各取所需、自由组合、想怎么玩就怎么玩。

强大的标准库

这包括互联网应用、系统编程和网络编程。Go里面的标准库基本上已经是非常稳定了,特别是我这里提到的三个,网络层、系统层的库非常实用。

部署方便:二进制文件、Copy部署

我相信这一点是很多人选择Go的最大理由,因为部署太方便了,所以现在也有很多人用Go开发运维程序。

简单的并发

它包含了降低心智的并发和简易的数据同步,我觉得这是Go最大的特色。之所以写正确的并发、容错和可扩展的程序如此之难,是因为我们用了错误的工具和错误的抽象,Go可以说这一块做的相当简单。

稳定性

Go拥有强大的编译检查、严格的编码规范和完整的软件生命周期工具,具有很强的稳定性,稳定压倒一切。那么为什么Go相比于其他程序会更稳定呢?这是因为Go提供了软件生命周期(开发、测试、部署、维护等等)的各个环节的工具,如go tool、gofmt、go test。

Go语言适合用来做什么?

  • 服务器编程:以前你如果使用C或者C++做的那些事情,用Go来做很合适,例如处理日志、数据打包、虚拟机处理、文件系统等。
  • 分布式系统:数据库代理器等。
  • 网络编程:这一块目前应用最广,包括Web应用、API应用、下载应用、内存数据库。
  • 云平台:google开发的groupcache,couchbase的部分组建云平台,目前国外很多云平台在采用Go开发,CloudFoundy的部分组建,前VMare的技术总监自己出来搞的apcera云平台。

Go语言成功的项目

nsq:bitly开源的消息队列系统,性能非常高,目前他们每天处理数十亿条的消息 docker:基于lxc的一个虚拟打包工具,能够实现PAAS平台的组建 packer:用来生成不同平台的镜像文件,例如VM、vbox、AWS等,作者是vagrant的作者 skynet:分布式调度框架 Doozer:分布式同步工具,类似ZooKeeper Heka:mazila开源的日志处理系统 cbfs:couchbase开源的分布式文件系统 tsuru:开源的PAAS平台,和SAE实现的功能一模一样 groupcache:memcahe作者写的用于Google下载系统的缓存系统 god:类似redis的缓存系统,但是支持分布式和扩展性 gor:网络流量抓包和重放工具

哪些大公司在用go语言?

  • Google 这个不用多做介绍,作为开发Go语言的公司,当仁不让。Google基于Go有很多优秀的项目,比如:kubernetes ,大家也可以在Github上 查看更多Google的Go开源项目。

  • Facebook Facebook也在用,为此他们还专门在Github上建立了一个开源组织facebookgo,大家可以通过 访问查看facebook开源的项目,比如著名的是平滑升级的grace。

  • 腾讯 腾讯作为国内的大公司,还是敢于尝试的,尤其是Docker容器化这一块,他们在15年已经做了docker万台规模的实践,具体可以参考

  • 百度 目前所知的百度的使用是在运维这边,是百度运维的一个BFE项目,负责前端流量的接入。他们的负责人在2016年有分享,大家可以看下这个

  • 阿里 阿里巴巴具体的项目不太清楚,不过听说其系统部门、CDN(Content Delivery Network)等正在招Go方面的人。

  • 京东 京东云消息推送系统、云存储,以及京东商城等都有使用Go做开发。

  • 小米 小米对Golang的支持,莫过于运维监控系统的开源,此外,小米互娱、小米商城、小米视频、小米生态链等团队都在使用Golang。

  • 360 360对Golang的使用也不少,一个是开源的日志搜索系统Poseidon,托管在Github上。

第一章 环境搭建

linux安装go语言环境

# yum install mercurial
# yum install git 
# yum install gcc 
# cd /usr/local/
# wget https://go.googlecode.com/files/go1.9.1.linux-amd64.tar.gz
# tar -zxvf go1.9.1.linux-amd64.tar.gz

建立Go语言的工作空间(workspace,也就是GOPATH环境变量指向的目录)Go语言代码必须在工作空间内。工作空间是一个目录,其中包含三个子目录: src —- 里面每一个子目录,就是一个包。包内是Go语言的源码文件 pkg —- 编译后生成的,包的目标文件 bin —- 生成的可执行文件 这里,我们在/home目录下,建立一个名为go(可以不是go, 任意名字都可以)的文件夹,然后再建立三个子文件夹(子文件夹名必须为src、pkg、bin)。

# cd /home/
# mkdir go
# cd go/
# mkdir bin
# mkdir src
# mkdir pkg

添加PATH环境变量和设置GOPATH环境变量

# vim /etc/profile 
export GOROOT=/usr/local/go        ##Golang安装目录
export PATH=$GOROOT/bin:$PATH
export GOPATH=/home/go  ##Golang项目目录
# source /etc/profile        ##刷新环境变量
# go version        ##查看go版本
go version go1.9.1 linux/amd64
# go env

进入项目目录

# cd /home/go/src/    
# mkdir project
# cd project/
# mkdir test
# cd test/
# mkdir main
# cd main/
# touch main.go

进入main.go编写第一个程序

# vim main.go 
package main
import "fmt"
func main() {
    fmt.Println("Hello World")
}

使用go install工具编译此程序

# cd /home/go/
# go install project/test/main

进入bin目标查看编译好的二进制文件 ( cd /home/go/bin )

# cd bin/
# ls
main

使用shell命令 ./ 运行编译好的二进制文件

# ./main 
Hello World

Windows搭建Go语言环境

Go程序员开发中有MSI(Microsoft installer)和ZIP两种安装方式。我这里只搬ZIP的方式(看不到底层的安装过程我不放心,所以我通常会选择ZIP,无论哪种语言安装包)。

  • 下载安装包(根据操作系统选择相应Go语言版本) 下载地址:https://golang.google.cn/ 下载最新版本:go1.9.2.windows-386.zip

  • 将下载后的文件解压,放到了C:\根目录下 (可以不是C:\根目录, 任意目录都可以) 注意:目录路径文件命名不能包含中文字符

  • 设置环境变量
    • 设置GOROOT C:\go
    • 将bin路径添加到Path目录中 ;%GOROOT%\bin;
    • 设置GOPATH 在目录中创建一个文件夹src,用来存放我们的源码文件。
    • 设置GOPROXY https://goproxy.io
  • 验证是否安装成功,输出环境信息则安装成功,打开终端运行 go env 命令

Mac搭建Go语言环境

  • 下载安装包 ( 据操作系统选择相应Go语言版本 ) 下载地址:https://golang.google.cn/ 下载最新版本:go1.9.2.darwin-amd64.pkg

  • 打开此pkg文件 并跟随提示来安装Go工具

  • 配置Go环境变量GOPATH和GOBIN

    • 打开终端,输入 cd ~ 命令 回到用户家目录:

    • 查看是否有.bash_profile文件:ls -all

    • 有则跳过创建,没有则:

      创建:touch .bash_profile
      编辑:open -e .bash_profile
      自定义GOPATH和GOBIN位置:
      export GOPATH=/Users/oldboy/Desktop/go        
      export GOBIN=$GOPATH/bin
      export PATH=$PATH:$GOBIN
      
    • 编译:source .bash_profile

  • 查看Go环境信息: go env

终于完成了第一步了,开始语言的正式学习吧。

第二章 Go语言基础

Go语言介绍

主要特性

  1. 可直接编译成机器码,不依赖其他库,glibc的版本有一定要求,部署就是扔一个文件上去就完成了。
  2. 静态类型语言,但是有动态语言的感觉,静态类型的语言就是可以在编译的时候检查出来隐藏的大多数问题,动态语言的感觉就是有很多的包可以使用,写起来的效率很高。ps:静态语言中变量的类型不能轻易改变。
  3. 语言层面支持并发,这个就是Go语言最大的特色,可以充分的利用多核,很容易的使用并发。
  4. 内置runtime,支持垃圾回收,这属于动态语言的特性之一吧,虽然目前来说GC不算完美,但是足以应付我们所能遇到的大多数情况,特别是Go1.1之后的GC。
  5. 简单易学,Go语言的作者都有C的基因,那么Go自然而然就有了C的基因,那么Go关键字是25个,但是表达能力很强大,几乎支持大多数你在其他语言见过的特性:继承、重载、对象等。丰富的标准库,Go目前已经内置了大量的库,特别是网络库非常强大。
  6. 内置强大的工具,Go语言里面内置了很多工具链,最好的应该是gofmt工具,自动化格式化代码,能够让团队review变得如此的简单,代码格式一模一样,想不一样都很困难。跨平台编译,如果你写的Go代码不包含cgo,那么就可以做到window系统编译linux的应用,如何做到的呢?Go引用了plan9的代码,这就是不依赖系统的信息。
  7. 内嵌C语言支持,前面说了作者是C语言的作者,所以Go语言里面也可以直接包含C语言代码,利用现有的丰富的C语言库。

标准命令解释

Go is a tool for managing Go source code.
Usage:
	go <command> [arguments]
The commands are:
	bug         start a bug report
	build       compile packages and dependencies
	clean       remove object files and cached files
	doc         show documentation for package or symbol
	env         print Go environment information
	fix         update packages to use new APIs
	fmt         gofmt (reformat) package sources
	generate    generate Go files by processing source
	get         download and install packages and dependencies
	install     compile and install packages and dependencies
	list        list packages or modules
	mod         module maintenance
	run         compile and run Go program
	test        test packages
	tool        run specified go tool
	version     print Go version
	vet         report likely mistakes in packages
Use "go help <command>" for more information about a command.
Additional help topics:
	buildmode   build modes
	c           calling between Go and C
	cache       build and test caching
	environment environment variables
	filetype    file types
	go.mod      the go.mod file
	gopath      GOPATH environment variable
	gopath-get  legacy GOPATH go get
	goproxy     module proxy protocol
	importpath  import path syntax
	modules     modules, module versions, and more
	module-get  module-aware go get
	packages    package lists and patterns
	testflag    testing flags
	testfunc    testing functions
Use "go help <topic>" for more information about that topic.

简单翻译几个常用的

go env 用于打印Go语言的环境信息。

go run 命令可以编译并运行命令源码文件。

go get 可以根据要求和实际情况从互联网上下载或更新指定的代码包及其依赖包,并对它们进行编译和安装。

go build 命令用于编译我们指定的源码文件或代码包以及它们的依赖包。

go install 用于编译并安装指定的代码包及它们的依赖包。

go clean 命令会删除掉执行其它命令时产生的一些文件和目录。

go doc 命令可以打印附于Go语言程序实体上的文档。我们可以通过把程序实体的标识符作为该命令的参数来达到查看其文档的目的。

go test 命令用于对Go语言编写的程序进行测试。

go list 命令的作用是列出指定的代码包的信息。

go fix 会把指定代码包的所有Go语言源码文件中的旧版本代码修正为新版本的代码。

go vet 是一个用于检查Go语言源码中静态错误的简单工具。

go tool pprof 命令来交互式的访问概要文件的内容。

这里说一下编译和安装的个人理解,编译是从源代码编程可执行的二进制语言。安装会初始化一些环境变量。

Go语言命名

25个关键字

package :用于声明包文件 import :用于导入其它package

interface :用于定义接口 struct :用于定义数据类型

var :用于变量的声明 const :用于常量的声明 type :用于声明类型 func :用于声明函数和方法

chan :用于声明chan类型数据 map :用于声明map类型数据

return :用于从函数返回 defer :延迟调用,在函数退出之前执行 go :创建一个协程 select :用于选择不同类型的通讯

break、case、continue、for、fallthrough、else、if、switch、goto、default :用于流程控制 range :用于遍历array、slice、map、channel数据

37个保留字

在下一小节内置类型详细解说。

语言命名规范

Go语言的包(package)、变量、常量、自定义类型、函数、方法的命名方式遵循以下规则:

  • 首字符可以是任意的Unicode字符或者下划线
  • 剩余字符可以是Unicode字符、下划线、数字
  • 字符长度不限
  • 不要使用Go语言的关键字和保留字命名

内置类型和函数

上一小节的37个保留字分为Constants,Types,Functions三种。

首先来看一下Constants吧,一共有四个,true,false,iota,nil。然后是Types有int,int8,int16,int32,int64,unit,unit8,uint16,uint32,uint64,uintptr,float32,float64,complex64,complex128,bool,byte,rune,string,error。最后是Functions,有make,len,cap,new,append,copy,close,delete,complex,real,imag,panic,recover。

类型 长度(字节) 默认值 说明
bool 1 false bool类型代表逻辑值不能与int转换
byte 1 0 int8的别名
int8,uint8 1 0 int8:-128~127 unit8:0~255
int16,uint16 2 0 int16:-32768~32767 uint16:0~65535
rune 4 0 int32的别名
int32,uint32 4 0 int32:-2147483648~2147483647 uint32:0~4294967295
int64,uint64 8 0 很大
float32 4 0 单精度浮点型
float64 8 0 双精度浮点型
complex64 8 0+0i 复数类型
complex128 16 0+0i 复数类型
uintptr 4或8 0 指针类型,存储指针的uint32或uint64整数
array   默认值根据数据类型来定var array [4] int 默认值为[0 0 0 0] 值类型
struct   默认值要根据其数据类型来确定 值类型
string   “” UTF-8字符串
slice   nil 引用类型
map   nil 引用类型
channel   nil 引用类型
interface   nil 接口类型
function   nil 函数类型
error   nil 错误类型
int,uint 4或8 0 int有符号整数,uint无符号整数

内置函数

Go 语言拥有一些不需要进行导入操作就可以使用的内置函数。它们有时可以针对不同的类型进行操作,例如:len、cap 和 append,或必须用于系统级的操作,例如:panic。因此,它们需要直接获得编译器的支持。

函数 说明
append 用来追加元素到slice中,返回修改后的slice
close 主要用来关闭channel
delete 从map中删除key对应的value
panic 停止常规的goroutine。panic和recover用来做错误处理
recover 允许程序定义goroutine的panic动作
imag 返回虚部
real 返回实部
make 分配内存,返回Type本身,只能应用于slice,map,channel
new 分配内存,主要用来分配值类型,比如int,struct。返回执行type的指针
cap 求最大容量,比如array,slice,channel返回最大容量
copy 复制和连接slice,返回复制的数目
len 求长度,例如string,array,slice,map,channel的长度
print,println 底层打印函数,在部署环境中建议使用fmt包

内置接口error

只要实现了Error()函数,返回值为String的就实现了error接口

type error interface{
    Error() String
}

Go语言特殊函数介绍

main函数

Go语言程序的默认入口函数(主函数):func main(),函数体用{}一对括号包裹。只能应用于package main

package main
func main(){
    //函数体
}

init函数

go语言中init函数用于包package的初始化,该函数是go语言的一个重要特性。

init函数有下面的特征:

1、init函数是用于程序执行前做包的初始化的函数,比如初始化包里的变量等

2、每个包可以拥有多个init函数

3、包的每个源文件也可以拥有多个init函数

4、同一个包中多个init函数的执行顺序go语言没有明确的定义(说明)

5、不同包的init函数按照包导入的依赖关系决定该初始化函数的执行顺序

6、init函数不能被其他函数调用,而是在main函数执行之前,自动被调用

init函数和main函数的异同

相同点: 两个函数在定义时不能有任何的参数和返回值,且Go程序自动调用。

不同点: init函数可以应用于任意包中,且可以重复定义多个。 main函数只能用于main包中,且只能定义一个。

两个函数的执行顺序

对同一个go文件的init()调用顺序是从上到下的。

对同一个package中不同文件是按文件名字符串比较“从小到大”顺序调用各文件中的init()函数。

对于不同的package,如果不相互依赖的话,按照main包中”先import的后调用”的顺序调用其包中的init(),如果package存在依赖,则先调用最早被依赖的package中的init(),最后调用main函数。

如果init函数中使用了println()或者print()你会发现在执行过程中这两个不会按照你想象中的顺序执行。这两个函数官方只推荐在测试环境中使用,对于正式环境不要使用。

Go语言运算符

详细的在Go程序员开发中都有介绍,Go 语言内置的运算符有:算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符、其他运算符。我这里只搬两个我比较容易记混的。

其他运算符

运算符 描述
& 取地址符,返回变量存储地址
* 取值符,返回指针指向的变量

特殊表示符

“_”是特殊标识符,又称占位符(空标识符号),用来忽略结果。

  • 特殊标识符应用在import中

在Go语言里,import的作用是导入其他package。

特殊标识符(如:import _ lemonhh/golang)的作用:当导入一个包时,该包下的文件里所有init()函数都会被执行,然而,有些时候我们并不需要把整个包都导入进来,仅仅是是希望它执行init()函数而已。这个时候就可以使用 import _ 引用该包。即使用(import _ 包路径)只是引用该包,仅仅是为了调用init()函数,所以无法通过包名来调用包中的其他函数。

示例: 代码目录结构

src 
|
+--- main.go            
|
+--- lemonhh
       |
        +--- lemonhh.go

main.go

package main

import _ "./lemonhh"

func main() {
    // lemonhh.Print() 
    //编译报错:./main.go:6:5: undefined: lemonhh
}

lemonhh.go

package lemonhh

import "fmt"

func init() {
    fmt.Println("The lemonhh welcomes you.")
}

func Print() {
    fmt.Println("Hello!")
}

运行结果:

The lemonhh welcomes you.
  • 特殊标识符应用在代码中
package main

func main() {
    str, _ := lemonhh()
    _ = str
}

func lemonhh() (str string, err error) {
    str = "The old boy welcomes you."
    err = nil
    return
}

解释:

占位符,意思是那个位置本应赋给某个值,但是咱们不需要这个值。所以就把该值赋给占位符,意思是丢掉不要。这样编译器可以更好的优化,任何类型的单个值都可以丢给占位符。

第一个占位符:这种情况是忽略函数返回值,函数返回两个结果,而你只想要一个结果。

第二个占位符:这种情况是忽略未使用的局部变量,如果声明的局部变量,不使用,编译器是会报错。

第三章 Go语言程序结构

Go语言变量

变量来源于数学,是计算机语言中能储存计算结果或能表示值抽象概念。变量可以通过变量名访问。

Go语言是静态类型语言,不能在运行期间改变变量类型。使用关键字 var 定义变量,自动初始化为零值。如果提供初始化值,可省略变量类型,由编译器自动推断。

Go语言中变量可以在三个地方声明:

函数外定义的变量称为全局变量

函数内定义的变量称为局部变量

函数定义中的变量称为形式参数

变量声明

  • 第一种,指定变量类型,声明后若不赋值,使用默认值。

    var name string
    
  • 第二种,根据值自行判定变量类型。

    var age = 18
    
  • 第三种,变量声明并赋值

    var class string = "Linux"
    
  • 第四种,一次性声明多个变量

    var Mon, Tues, Wed, Thur, Fri, Sat, Sun int
      
    var (
        Monday    int
        Tuesday   int
        Wednesday int
        Thursday  int
        Friday    int
        Saturday  int
        Sunday    int
    )
    
  • 第五种,短声明变量 在函数内部,可以使用更简略的 “:=” 方式定义变量。”:=” 简洁赋值语句在明确类型的地方,可以用于替代 var 定义。

    package main
      
    func main() {
        name := "lemonhh"
        age, class := 18, "Linux"
        _, _, _ = name, age, class
    }
    

    注意:”:=” 结构不能在函数外使用。

  • 第六种,形式参数

    package main
      
    func main() {
    }
      
    func sum(a, b int) (c int) {
        c = a + b
        return
    }
    

变量优先级

Go语言程序中全局变量与局部变量名称可以相同,但是函数内的局部变量会被优先考虑。实例如下

package main

import "fmt"

// 声明全局变量
var variable string = "全局变量"

func main() {
    // 声明局部变量
    var variable string = "局部变量"
    fmt.Printf("结果:%s\n", variable)
}

运行结果:

结果:局部变量

多变量赋值时,先计算所有相关值,然后再从左到右依次赋值。

package main

import "fmt"

func main() {
    data, i := [4]string{"course", "Python", "Linux", "Golang"}, 0
    i, data[i] = 2, "lemonhh"
    fmt.Printf("i:%v,data:%v\n", i, data)
}

Go语言常量

常量是一个简单值的标识符,在程序运行时,永远是只读的,不能修改。

使用 const 关键字定义常量。常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。

常量定义

  • 第一种,显式类型定义

    const name string = "lemonhh"
    
  • 第二种,隐式类型定义,一个未指定类型的常量由上下文来决定其类型

    const age = 18
    
  • 第三种,一次性定义多个常量

    const Mon, Tues, Wed, Thur, Fri, Sat, Sun = 1, 2, 3, 4, 5, 6, 7
      
    const (
        name = "lemonhh"
        age  = 18
    )
    
  • 第四种,常量可以用len(),cap(),unsafe.Sizeof()函数计算表达式的值。常量表达式中,函数必须是内置函数,否则编译不过:

    const (
        a = "abc"
        b = len(a)
        c = unsafe.Sizeof(b)
    )
    

    len()的返回类型是int,在这里测出来的是int64,即c=8

常量技巧

未使用的局部常量不会引发编译错误。实例如下:

package main

func main() {
    const str = "The old boy welcomes you."
}

在常量组中,如不提供类型和初始化值,那么视作与上一个常量相同。实例如下:

package main

import (
    "fmt"
)

func main() {
    const (
        name = "lemonhh"
        nickname
    )
    fmt.Printf("name : %s, nickname : %s\n", name, nickname)
}

运行结果:

name : lemonhh, nickname : lemonhh

特殊常量

特殊常量,iota,可以认为是一个可以被编译器修改的常量。 在每一个const关键字出现时,被重置为0,然后再下一个const出现之前,每出现一次iota,其所代表的数字会自动增加1。

关键字 iota 定义常量组中从 0 开始按行计数的自增枚举值。实例如下:

package main

import (
    "fmt"
)

const (
    Sunday = iota
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
)

func main() {
    fmt.Println(Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday)
}

运行结果:

0 1 2 3 4 5 6

在同一常量组中,可以提供多个 iota,它们各自增长。实例如下:

package main

import (
    "fmt"
)

const (
    A1, B1 = iota, iota << 10
    A2, B2
)

func main() {
    fmt.Println(A1, B1, A2, B2)
}

运行结果:

0 0 1 1024

如果 iota 自增被打断,须显式恢复。实例如下:

package main

import (
    "fmt"
)

const (
    Sun = iota
    Mon
    Tues
    Wed = "Wednesday"
    Thur
    Fri = iota
    Sat
)

func main() {
    fmt.Println(Sun, Mon, Tues, Wed, Thur, Fri, Sat)
}

运行结果:

0 1 2 Wednesday Wednesday 5 6

Go语言包和文件

工作空间

Go语言工作空间:编译工具对源码目录有严格要求,每个工作空间 (workspace) 必须由bin、pkg、src三个目录组成。

src —- 项目源码目录,里面每一个子目录,就是一个包,包内是Go语言的源码文件。 pkg —- Go语言编译的.a 中间文件存放目录,可自动生成。 bin —- Go语言编译可执行文件存放目录,可自动生成。

包结构

包是结构化代码的一种方式:每个程序都由包的概念组成,可以使用自身的包或者从其它包中导入内容。

如同其它一些编程语言中的类库或命名空间的概念,每个Go文件都属于且仅属于一个包。一个包可以由许多以.go为扩展名的源文件组成,因此文件名和包名一般来说都是不相同的。

包声明 “package + 包名” ,必须在源文件中非注释的第一行指明这个文件属于哪个包。 如:package main。package main表示一个可独立执行的程序,每个Go应用程序都包含一个名为 main 的包。

同一个包(package)下面,可以有非常多的不同文件,只要每个文件的头部都有相同name就可以,如 “package lemonhh” 。文件夹名字可以和这个package名称不一致。

标准库

在Go的安装文件里包含了一些可以直接使用的包,即标准库。Go语言标准库包,覆盖了几乎所有的基础库,提供了丰富广泛的功能特性。标准库的内容还可能继续增加,大家可以通过在线查阅库API或使用godoc(包含在Go发布包中)来获取最新信息以及全面了解每个包所具备的功能。

第三方库

通过”go get + 完整包名”保存下载第三方库。

在执行go get 命令之前,确保你的电脑配置了环境变量GOPATH,并且安装git。

导出包

在Go语言中根据首字母的大小写来确定可以访问的权限。如果首字母大写,则可以被其他的包访问;如果首字母小写,则只能在本包中使用。该规则适用于全局变量、全局常量、类型、结构字段、函数、方法等。

可以简单的理解成,首字母大写是公有的,首字母小写是私有的。

在导入包之后,你只能访问包所导出的名字,任何未导出的名字是不能被包外的代码访问的。

导入包

使用包成员前,必须先用import关键字导入,但不能形成导入循环。

import用法:

第一种,导入系统包。

import "fmt"  

第二种,相对路径导入包,导入同一目录下 test 包中的内容。

import "./test" 

第三种,绝对路径导入包,导入 gopath/src/lemonhh/python 包中的内容。

import "lemonhh/python"    

第四种,导入包并启用别名。

import f "fmt"    

导入fmt,并给他启别名f

import . "fmt"  

将fmt启用别名”.”,这样就可以直接使用其内容,而不用再添加fmt。如fmt.Println可以直接写成Println。

第五种,"import _"

import  _ "fmt" 

表示不使用该包,而是只是使用该包的init函数,并不显示的使用该包的其他内容。这种形式的import,当import时就执行了fmt包中的init函数,而不能够使用该包的其他函数。

注意:未使用的导入包,会被编译器视为错误 (不包括 "import _")。实例如下:

package main

import (
    "fmt"
)

func main() {
}

编译错误:

./main.go:4:2: imported and not used: "fmt"

编写一个Go程序

编码格式

Go语言源码文件编码格式必须是 UTF-8 格式,否则会导致编译器出错。

结束语句

在 Go 程序中,一行代表一个语句结束。每个语句不需要像其它语言一样以分号 ";"结尾,因为这些工作都将由 Go 编译器自动完成。

如果你打算将多个语句写在同一行,它们则必须使用 ";"人为区分。实例如下:

package main

import (
    "fmt"
)

func main() {
    fmt.Println("lemonhh")
    fmt.Println("Linux");
    fmt.Println("Java");fmt.Println("DBA")
    fmt.Println("Python");fmt.Println("Golang");
}

代码注释

注释不会被编译,每一个包应该有相关注释。

Go语言代码注释支持 "//"、"/**/" 两种注释方式,不能嵌套。

单行注释是最常见的注释形式,你可以在任何地方使用以 // 开头的单行注释。多行注释也叫块注释,均已以 /* 开头,并以 */ 结尾。实例如下:

// 单行注释

/*
   多行注释
*/

编写 Hello World

1、创建文件 hello.go,不写入任何内容。运行 go build hello.go 命令进行编译。将会输出如下错误:

can't load package: package main: 
hello.go:1:1: expected 'package', found 'EOF'

因为Go语言中,所有文件必须隶属于某一个包。需要在文件的头部声明一个package name,其中package为关键字,name为包名。

2、在hello.go文件中添加一条语句,之后重新进行编译。

添加内容如下:

package main

执行编译,将会输出如下错误:

runtime.main_main·f: relocation target main.main not defined
runtime.main_main·f: undefined: "main.main"

因为Go程序启动时,需要在文件中有一个可标识入口。Go程序的入口函数是main函数。

3、在hello.go文件中添加另外一行,并且重试。

添加内容如下:

package main

func main(){}

执行运行命令 go run hello.go ,程序正确执行并很快就退出了。到目前为止,我们已经创建了自己的第一个程序。虽然没有功能,但是已经可以正常运行了。

4、我们继续添加功能,添加内容如下:

package main

func main(){
    Println("hello world")
}

执行编译,将会输出如下错误:

./hello.go:4:2: undefined: Println

因为Println函数存放在fmt包中。我们程序中没有主动引入fmt包,所以编译器报未定义错误。

如果我们需要使用这些包中的功能,首先需要import引入相应的包。包中的可导出组件可以通过 包名.组件名的方式进行调用。

函数Println和其它读写文本和字符的函数,都存放在一个叫做fmt的包中——formatting的缩写。

5、再添加几行代码,添加内容如下:

package main

import "fmt"

func main(){
    fmt.Println("hello world")
}

执行 go run hello.go 命令,运行程序。运行结果如下:

hello world

Go 程序启动执行顺序如下:

按顺序导入所有被 main 包引用的其它包,然后在每个包中执行如下流程:

如果该包又导入了其它的包,则从第一步开始递归执行,但是每个包只会被导入一次。

然后以相反的顺序在每个包中初始化常量和变量,如果该包含有 init 函数的话,则调用该函数。

在完成这一切之后,main 也执行同样的过程,最后调用 main 函数开始执行程序。