前言
前一课我们学习了如何通过Git代码管理仓库开发并部署Go程序(https://www.liujason.com/article/426.html),熟悉掌握了云筏容器云平台拉取Git代码仓库中的程序并部署的过程。接下来我们继续学习Go语言基础,通过在git中进行代码修改来熟悉Go语言,并重新在容器环境中部署最新的Go程序。
Go语言的运行
二进制代码的编译
大家会想一下在上次作业中,我们虽然只编写了go-study-2-2.go的代码并部署到云筏容器云中,但是在部署完成后会发现ROOT目录下有一个“go-study”文件,这个文件被称为二进制文件:
简单来说这个文件是将我们的go-study-2-2.go转换成了计算机可以运行的程序文件。一般来说在拉取go代码后,需要在运行的计算机中进行编译工作(扩展阅读:二进制代码),但是云筏容器云在部署时默认会将代码编译,而编译过程中若出现错误就没有办法完成部署,这也是之前大家部署出错的原因。
那么这次我们手动来编译一下自己的代码:
1. 打开容器的SSH终端(相当于连接上了这台服务器,扩展阅读:SSH);
2. 在终端中输入:cd /home/jelastic/webapp/ROOT后回车,其中cd为change directory的缩写,也就是打开目录/home/jelastic/webapp/ROOT,而这个目录是我们容器网页端的根目录;
3. 在终端中输入:ls后回车,查看当前目录下的文件(ls是list的缩写),可以看到go-study go-study-2-2.go README.md三个文件,其中的go-study是已经编译好的文件,此时打开http://域名/go-study-2-2.go,显示Hello World from LiuJason!;
4. 在终端中输入:rm go-study后回车,删除go-study文件(rm是remove的缩写),然后再次使用ls确定是否删除成功,此时目录下只有go-study-2-2.go README.md两个文件;
5. 在终端中输入:go build后回车,编译当前目录下的所有go文件,之后然后再次使用ls确定是否编译成功,可以看到go-study go-study-2-2.go README.md三个文件。此时打开http://域名/go-study-2-2.go,显示Hello World from LiuJason!
二进制代码的执行
Go语言结构
Go 语言的基础组成有:包声明、引入包、函数、变量、语句 & 表达式、注释。我们在上一课中的示例代码中其实已经见过了,接下来我们来一个个的详解上述组分。首先还是拿上次的代码来做案例。
package main import ( "fmt" "net/http" ) func sayhelloName(w http.ResponseWriter, r *http.Request){ fmt.Fprintf(w, "Hello World from LiuJason!") } /*主函数开始*/ func main() { http.HandleFunc("/go-study-2-2.go", sayhelloName) http.ListenAndServe(":8080", nil) }
包声明
示例代码中第1行 package main即为包声明。
包是结构化代码的一种方式:每个程序都由包(通常简称为 pkg)的概念组成,可以使用自身的包或者从其它包中导入内容。
如同其它一些编程语言中的类库或命名空间的概念,每个 Go 文件都属于且仅属于一个包。一个包可以由许多以 .go 为扩展名的源文件组成,因此文件名和包名一般来说都是不相同的。你必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package main。package main表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包。一个应用程序可以包含不同的包,而且即使你只使用 main 包也不必把所有的代码都写在一个巨大的文件里:你可以用一些较小的文件,并且在每个文件非注释的第一行都使用 package main 来指明这些文件都属于 main 包。如果你打算编译包名不是为 main 的源文件,如 pack1,编译后产生的对象文件将会是 pack1.a 而不是可执行程序。另外要注意的是,所有的包名都应该使用小写字母。
引入包
示例代码中第3~6行import ....即为引入包。
一个 Go 程序是通过 import 关键字将一组包链接在一起。import "fmt" 告诉 Go 编译器这个程序需要使用 fmt 包(的函数,或其他元素),fmt 包实现了格式化 IO(输入/输出)的函数。包名被封闭在半角双引号 "" 中。如果你打算从已编译的包中导入并加载公开声明的方法,不需要插入已编译包的源代码。
如果需要多个包,它们可以被分别导入:
import "fmt" import "net/http" /*或*/ import "fmt"; import "net/http" /*但是还有更短且更优雅的方法(被称为因式分解关键字,该方法同样适用于 const、var 和 type 的声明或定义)*/ import ( "fmt" "net/http" ) /*它甚至还可以更短的形式,但使用 gofmt 后将会被强制换行*/ import ("fmt"; "net/http")
当你导入多个包时,最好按照字母顺序排列包名,这样做更加清晰易读。如果包名不是以 . 或 / 开头,如 "fmt" 或者 "container/list",则 Go 会在全局文件进行查找;如果包名以 ./ 开头,则 Go 会在相对目录中查找;如果包名以 / 开头(在 Windows 下也可以这样使用),则会在系统的绝对路径中查找。
函数
函数的定义
示例代码中第8~10&13~16行func ...即为函数的定义。
这是定义一个函数最简单的格式:func functionName()。你可以在括号 () 中写入 0 个或多个函数的参数(使用逗号 , 分隔),每个参数的名称后面必须紧跟着该参数的类型。
main 函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数(如果有 init() 函数则会先执行该函数)。如果你的 main 包的源代码没有包含 main 函数,则会引发构建错误。在程序开始执行并完成初始化后,第一个调用(程序的入口点)的函数是 main.main()(如:C 语言),该函数一旦返回就表示程序已成功执行并立即退出。
函数里的代码(函数体)使用大括号 {} 括起来。左大括号 { 必须与方法的声明放在同一行,这是编译器的强制规定,否则就会出现错误。右大括号 } 需要被放在紧接着函数体的下一行。如果你的函数非常简短,你也可以将它们放在同一行:func Sum(a, b int) int { return a + b }。对于大括号 {} 的使用规则在任何时候都是相同的(如:if 语句等)。因此符合规范的函数一般写成如下的形式:
func functionName(parameter_list) (return_value_list) { … }
其中:
只有当某个函数需要被外部包调用的时候才使用大写字母开头,并遵循 Pascal 命名法;否则就遵循骆驼命名法,即第一个单词的首字母小写,其余单词的首字母大写。
函数的调用
示例代码中第9行fmt.Fprintf()即为函数的调用。这里调用了 fmt 包中的 Fprintf 函数,可以将字符串输出到控制台。
当被调用函数的代码执行到结束符 } 或返回语句时就会返回,然后程序继续执行调用该函数之后的代码。程序正常退出的代码为 0 即 Program exited with code 0;如果程序因为异常而被终止,则会返回非零值,如:1。这个数值可以用来测试是否成功执行一个程序。
变量/常量
一定要看的扩展阅读:常量概述 | 变量概述
变量(或常量)包含数据,这些数据可以有不同的数据类型,简称类型。使用 var 声明的变量的值会自动初始化为该类型的零值。类型定义了某个变量的值的集合与可对其进行操作的集合。
变量(或常量)类型可以是基本类型,如:int、float、bool、string;结构化的(复合的),如:struct、array、slice、map、channel;只描述类型的行为的,如:interface。结构化的类型没有真正的值,它使用 nil 作为默认值(在 Objective-C 中是 nil,在 Java 中是 null,在 C 和 C++ 中是NULL或 0)。值得注意的是,Go 语言中不存在类型继承。
函数也可以是一个确定的类型,就是以函数作为返回类型。这种类型的声明要写在函数名和可选的参数列表之后,例如:func FunctionName (a typea, b typeb) typeFunc 。
使用 type 关键字可以定义你自己的类型,你可能想要定义一个结构体(第 10 章),但是也可以定义一个已经存在的类型的别名,如:type IZ int ;我们可以使用下面的方式声明变量:var a IZ = 5。这里我们可以看到 int 是变量 a 的底层类型。
如果你有多个类型需要定义,可以使用因式分解关键字的方式,例如:
type ( IZ int FZ float64 STR string )
注意!每个值都必须在经过编译后属于某个类型(编译器必须能够推断出所有值的类型)。
注释
在我们写代码的时候,有很多地方需要添加一些文本注释来帮助我们日后读懂代码,这些注释在协作开发的时候非常重要。
注释不会被编译,但可以通过 godoc 来使用(扩展阅读(非重点):godoc)。
单行注释是最常见的注释形式,你可以在任何地方使用以 // 开头的单行注释。多行注释也叫块注释,均已以 /* 开头,并以 */ 结尾,且不可以嵌套使用,多行注释一般用于包的文档描述或注释成块的代码片段。
//这样可以注释单行 但是换行就不行了 /*这样可以注释单行或多行*/ /*多行同样 不会被 GO编译*/
每一个包应该有相关注释,在 package 语句之前的块注释将被默认认为是这个包的文档说明,其中应该提供一些相关信息并对整体功能做简要的介绍。一个包可以分散在多个文件中,但是只需要在其中一个进行注释说明即可。当开发人员需要了解包的一些情况时,自然会用 godoc 来显示包的文档说明,在首行的简要注释之后可以用成段的注释来进行更详细的说明,而不必拥挤在一起。另外,在多段注释之间应以空行分隔加以区分。