1. 认识HelloWorld
在前面的《Go语言入门系列(一)之Go的安装和使用》这篇文章中已经写过
HelloWorld.go
了,现在就来逐行认识一下它。
package mainimport "fmt"func main() {fmt.Println("Hello, World!")}
第1行:我们的Go程序是由包——
package
构成的,包的声明形式为:
package <包名>
。该行的意思是:当前
HelloWorld.go
文件属于
main
包。
第2行:如果你使用过Java或Python,那你对import肯定不陌生。该行的意思是:导入一个名为
fmt
的包。如果需要导入多个包,有两种写法:
import "fmt"import "math"
或者使用分组形式同时导入多个包
import ("fmt""math/rand")
显然第二种使用括号,以分组的形式导入更加方便。
第3行:我们使用
func
关键字来声明一个函数,在这个例子中,我们声明的是
main
函数。如果你有过其他语言的编程经验,肯定熟悉
main
函数的作用:程序的入口。
第4行:我们的函数内容放在函数的
{}
中,在该例中,调用了
fmt
包中的打印方法,由此可见,Golang语言调用函数的方法和Java、Python一样使用小数点:
<包名>.<函数名>
。
看完这几行简单代码,我们发现:在Go语言中,并不需要分号
;
来结束语句。
2. 变量
2.1. 命名规范
Go语言要求标识符以一个字母或下划线开头,后面可以跟任意数量的字母、数字、下划线。不能使用关键字作为标识符
区分大小写,变量
num
和变量
Num
是两个变量。
2.2. 声明
Go语言的变量声明方式和其他语言(如C、Java、Python)不太一样。
比如,我们声明一个为int类型的变量a:
var a int
可以看出,Go语言声明变量的方式为:
var <变量名> <变量类型>
我们还可以同时声明多个类型相同的变量:
var <变量名1>, <变量名2>, <变量名3>, ... <变量类型>
使用分组同时声明多个类型不同的变量:
var (<变量名1> <变量类型1><变量名2> <变量类型2><变量名3> <变量类型3>...)
下面是一个具体的例子:
package mainimport "fmt"var i bool//类型在后func main() {var a, b, c int//多个fmt.Println(i, a, b, c)}
2.3. 初始化
当我们声明变量时,可以对其进行初始化。当有初始值时,我们可以省略变量的类型,变量会根据对应的初始值获取正确的类型。见下例:
package mainimport "fmt"var a int = 1var b, c bool = true, falsefunc main() {var i, j, k = 2, false, "行小观" //省略变量类型fmt.Println(a, b, c, i, j, k)k = "行小观2号" //赋值操作fmt.Println(k)}
若我们在声明变量时,不给变量赋初始值,则这些变量会被赋予“零值”。
这些零值为:
- 数值类型为0
- 布尔类型为false
- 字符串为""(即空字符串)
如果上例中的代码中的变量不进行初始化,即:
package mainimport "fmt"var a intvar b, c boolfunc main() {var i intvar j boolvar k stringfmt.Println(a, b, c, i, j, k)}
则打印结果为:0 false false 0 false (变量k由于是空字符串,所以打印出来了但是看不到)
2.4. 短变量声明
前面介绍了当我们声明变量时有以下几种方式:
var i int //声明一个int变量var i, j, k int //同时声明多个int变量var i int = 1 //声明一个int变量并初始化var i, j, k int = 1, 2, 3 //同时声明多个int变量并分别初始化var i = 1 //省略类型,声明一个int变量并初始化var i, j, k = 1, 2, 3 //省略类型,同时声明多个int变量并分别初始化
除此之外还有一种更简洁的声明变量的方式:
i := 1i, j, k := 1, 2, 3
当我们使用
:=
声明变量时,不用写
var
也不用写类型,但是这种方式只能在函数内部使用,不能在函数外部使用。当在函数外部声明变量时,只能使用
var
。
package mainimport "fmt"var a int = 1 //函数外部func main() {var i, j int = 2, 3 //函数内部k := 4 //只能在函数中使用v1, v2, v3 := true, false, "行小观"fmt.Println(a, i, j, k, v1, v2, v3)}
此外,我们声明的变量必须要使用,如果声明了变量,但是没使用,会在编译时报错。比如:
package mainimport "fmt"func main() {var i boolvar j int //声明了,但没使用,会报错fmt.Println(i)}
对于我们导入的包也有此要求,即:导入的包必须使用。
3. 数据类型
3.1. 布尔类型
布尔类型为
bool
,值可取
true
或
false
,默认值为
false
。
3.2. 字符串类型
字符串类型为
string
,默认为空字符串
""
。
3.3. 数值类型
整数类型分为:
-
有符号数:
int
、
int8
、
int16
、
int32 (rune)
、
int64
-
无符号数:
uint
、
uint8 (byte)
、
uint16
、
uint32
、
uint64
、
其中
int
和
uint
的两种类型的长度相同,取决于具体的编译器,比如在32位系统上通常为32位,在64位系统上通常为64位。
像
int8
、
uint8
这些类型则是Go语言直接定义好位数的类型。
rune
、
byte
是
int32
、
uint8
的别名。
当我们需要使用整数时,应当尽量使用
int
类型。当然,如果你有特殊的理由使用其他整数类型,便另当他论。
浮点数类型有两种:
float32
和
float64
,注意没有所谓的
float
类型。
复数类型也有两种:
complex64
和
complex128
注意:不同类型的变量之间不能直接进行赋值或其他运算(比如加减乘除)
package mainimport "fmt"var (a int = 1b int8 = 2c int16)func main() {c = b //不同类型之间进行赋值操作,[报错1]d := a + b //不同类型之间进行相加运算,[报错2]fmt.Printf("c = %v, d = %v", c, d)}
以上代码在编译过程中会报错:
[报错1]:cannot use b (type int8) as type int16 in assignment
[报错2]:invalid operation: a + b (mismatched types int and int8)
3.4. 类型转换
前面一节说过:不同类型的变量之间不能直接进行赋值或其他运算,所以我们可以间接地做。
比如:将
int8
类型转换为
int
类型,这样就可以间接地进行赋值和其他运算。
使用表达式
T(v)
将变量v的值的类型转换为T。注意是转换的是变量的值,变量本身的类型不变。
package mainimport "fmt"var (a int = 1b int8 = 2c uintd int64)func main() {c = uint(b) //将变量b的值2的类型从int8转换为uintd = int64(a) + int64(b)fmt.Printf("c(%T):%v = b(%T):%v\\n", c, c, b, b)fmt.Printf("a(%T):%v + b(%T):%v = d(%T):%v\\n", a, a, b, b, d, d)}
注意:Go语言中的类型转换是显示的,表达式
T()
是必须的,不能省略。
4. 常量
常量是固定的值,值在程序运行期间不会改变。
常量可以定义为数值、字符串、布尔类型
常量的声明方式和变量差不多,区别在于常量需要用
const
关键字修饰,不能使用
:=
进行声明。
package mainimport "fmt"const num int = 555var a int = 1func main() {const world = "世界"const truth = truefmt.Println("Hello,", world)fmt.Println("num = ", num)fmt.Println("a = ", a)fmt.Println("对吗?", truth)}
5. 初识函数
如果你之前学习过C或者Java等语言,肯定已经对函数(方法)有了一定的认识。
简单地来说,函数是对能完成某个功能的部分代码的抽象。当以后再需要该功能,我们只需要调用其对用的函数即可,不必再重复编写代码。
5.1. 函数的声明
我们在前面的内容已经使用到了函数,即
main()
。我们使用
func
关键字声明函数。
func func_name() {}
5.2. 函数的参数
Go语言中,函数可以有0个或多个参数。
package mainimport "fmt"func printName(name string, age int) {fmt.Println("我叫", name, ", 今年", age, "岁了")}func sayHello() {fmt.Println("行小观说:“你好”")}func main() {printName("行小观", 1)sayHello()}
如果你有多个参数的类型相同,你可以进行简写,只需要在这几个相同的参数最后写一遍类型即可。
func foo(x int, y int, z int)可以简写为:func foo(x, y, x int)
5.3. 函数的类型和返回值
函数的类型在函数名之后。(尽快适应Go的这种风格:类型在变量名后)
package mainimport "fmt"func add(x int, y int) int {return x + y}func main() {fmt.Println(add(1, 2))}
当函数没有返回值时,不需要写函数的类型:
func sayHello() {//没有返回值,不写函数类型fmt.Println("行小观说:“你好”")}
函数可以有0个或多个返回值。
多个返回值就意味着该函数有多个返回值类型:
package mainimport "fmt"func sumAndDiff(x, y int) (int, int) { //两个返回值类型sum := x + ydiff := x - yreturn sum, diff //两个返回值}func main() {a, b := sumAndDiff(5, 1)fmt.Println(a, b)}
注意:和参数不同,有几个返回值就写几个返回值类型,不能简写。
Go语言还提供了另一种函数返回的方式:命名返回值。
顾名思义,我们通过给返回值进行命名,使用空
return
语句,这样会直接返回已命名的返回值。如上例的
sumAndDiff
函数可以写为:
func sumAndDiff(x, y int) (sum int, diff int) {//提前命名返回值sum = x + ydiff = x - y //返回值在函数中被初始化return //返回值已经初始化了,不需要再在return语句中写变量了}
下面总结一下函数的使用:
func functionName(input1, input11 type1, input2 type2 ...) (type1, type11, type2 ...){//函数体return value1, value11, value2 ...}
或者命名返回值
func functionName(input1, input11 type1, input2 type2 ...) (output1 type1, output11 type11, output2 type2 ...){//函数体output1 = ...output11 = ...output2 = ......return}
6. 导出名
前面我们已经使用了
import
导入功能,比如
improt "fmt"
,该行代码可以让我们在本包中使用其他包里的函数。
那么我们如何让其他包能够使用到本包的方法或变量呢?答案是:将方法或变量导出。
在Go语言中,如果一个名字以大写字母开头,那么它就是已导出的,这意味着别的包可以使用它。(相当于Java中的
public
的作用)
比如我们常用的打印函数
fmt.Println(...)
,可以看到
Println()
的首字母是大写的,所以我们能够导入
fmt
包后使用该方法。
7. 流程控制语句
7.1. if语句
if语句是条件判断语句,用来判断是否满足某种条件,如果满足,则执行某段代码;如果不满足,则不执行。
if ... {//代码} else if ... {//代码} else {//代码}
注意格式:条件判断语句不需要使用小括号
()
。
下面是几个例子:
if a > 0 {//如果满足a>0,则打印Hello, Worldfmt.Println("Hello, World")}
if a > 0 {//如果满足a>0,则打印 Hello, Worldfmt.Println("Hello, World!")} else {//否则(即不满足a>0),则打印 你好,世界!fmt.Println("你好,世界!")}
if a > 5 {//如果满足a>5,则打印 Hello, Worldfmt.Println("Hello, World!")} else if a <= 5 && a > 0 {//如果满足0<a<=5,则打印 好好学习,天天向上fmt.Println("好好学习,天天向上")} else {//否则(即上面的条件都不满足),则打印 你好,世界!fmt.Println("你好,世界!")}
Go语言的if语句有一个特性:可以在条件表达式前执行一个简单的语句。下面是一个例子:
package mainimport "fmt"func sum(x, y int) int {return x + y}func main () {if i := sum(1, 2); i > 0 {fmt.Println("Hello, World!")//作用域内,能打印i}//fmt.Println(i)//作用域外,不能打印i}
在if语句中,使用
sum(x, y int)
函数计算出i的值,再进行判断。注意:变量i的作用域只在if语句中有效。
7.2. for语句
for语句是Go语言中的循环控制语句。它有几种形式:
(一)基本形式:
for 初始化语句; 条件表达式; 后置语句 {//循环体代码}
- 初始化语句:在第一次循环前之前,且只执行这一次
- 条件表达式:每次循环都会计算该表达式,如果满足(值为true)则继续循环;如果不满足(值为false),则跳出循环
- 后置语句:每次循环执行完都会执行该语句
下面是一个例子,循环打印5次"Hello,World!"
for i := 0; i < 5; i++ {fmt.Println("Hello, World!", i)}
注意该例的初始化语句
i := 0
是一个短变量声明,变量i只在该for循环中生效。
(二)省略形式:
for循环中的初始化语句和后置语句是可以省略的。
i := 0for ; i < 5; i++ {fmt.Println("Hello, World!", i)}
i := 0for ; i < 5; {fmt.Println("Hello, World!", i)i++}
从某种意义上来讲,上面两个例子并没有省略初始化语句或后置语句,只是改变了位置。
(三)while形式
诸如C、Java等语言中都有while循环,但是Go语言中没有while循环,但是我们可以使用for循环来实现“while循环”。
其实
(二)省略形式
中的第二个for循环例子就已经可以看做是while循环了。我们再稍做改进:
i := 0for i < 5 {//去掉两个分号,只写条件表达式fmt.Println("Hello, World!", i)i++}
(四)无限循环形式
//打印无限多个Hello, World!for {fmt.Println("Hello, World!")}
7.3. break和continue
上面提到的循环语句只有当条件表达式的值为false时,才会停止循环。但实际开发中,我们可能在条件表达式的值为true的情况下,需要退出循环。这种时候,就需要使用
break
或
continue
语句。
break
语句用来跳出当前循环,
continue
语句用来跳过本次循环。
下面是两个实例(改进上面循环打印5次"Hello,World!"的例子):
实例1:增加需求,当打印完第2遍
Hello,World!
时,停止打印
for i := 0; i < 5; i++ {if i == 2 {break}fmt.Println("Hello, World!", i)}
实例2:增加需求,不打印第3遍
Hello,World!
for i := 0; i < 5; i++ {if i == 2 {continue}fmt.Println("Hello, World!", i)}
7.4. switch语句
我们可以使用if…else if…else if…进行一连串的条件判断,但是这样过于繁杂。switch语句就是用来简化这个问题的。
switch 变量 {case 选项1 ://操作1代码case 选项2 ://操作2代码case 选项3 ://操作3代码case 选项n://操作n代码default ://默认操作}
switch
语句中有许多
case
和一个
default
,只有当变量和case的选项相匹配时,才会执行对应的操作代码。如果没有case的选项可以匹配,则默认执行
default
的代码。
下面是一个例子:
package mainimport "fmt"func sum(x, y int) int {return x + y}func main () {result := sum(3, 2)switch result {case 1 :fmt.Println("结果为1")case 2, 3, 4: //多种情况聚合在一起fmt.Println("结果为2或3或4")case sum(1, 4): //支持表达式fmt.Println("结果为5")default:fmt.Println("其他结果")}}
从上面的例子可以看出,Go语言中switch的case支持常量(不必为整数)、表达式、多个值聚合。注意:不论是常量、表达式,还是多个值聚合,都要保证常量、表达式的值、多个值的类型和switch的变量相同。
switch语句的匹配顺序是自上到下。Go语言自动为每个case提供了break语句,所以在众多选项中只能执行1个
case
或
default
,然后结束,剩余的不再执行。
但是可以使用
fallthrough
强制执行剩余的case:
result := sum(1, 1)switch result {case 1 :fmt.Println("结果为1")fallthroughcase 2, 3, 4:fmt.Println("结果为2或3或4")fallthroughcase sum(1, 4):fmt.Println("结果为5")fallthroughdefault:fmt.Println("其他结果")}
还是上面的那个例子,但是在每个case中使用
fallthrough
,现在的打印结果为:
结果为2或3或4结果为5其他结果
如有错误,还请指正。
文章首发于公众号『行人观学』