您现在的位置是:网站首页> 编程资料编程资料
Go语言编程入门超级指南_Golang_
2023-05-26
449人已围观
简介 Go语言编程入门超级指南_Golang_
1.序言
Golang作为一门出身名门望族的编程语言新星,像豆瓣的Redis平台Codis、类Evernote的云笔记leanote等。
1.1 为什么要学习
如果有人说X语言比Y语言好,两方的支持者经常会激烈地争吵。如果你是某种语言老手,你就是那门语言的“传道者”,下意识地会保护它。无论承认与否,你都已被困在一个隧道里,你看到的完全是局限的。《肖申克的救赎》对此有很好的注脚:
[Red] These walls are funny. First you hate ‘em, then you get used to ‘em. Enough time passes, you get so you depend on them. That's institutionalized.
这些墙很有趣。起初你恨它们,之后你习惯了它们。随着时间流逝,你开始以来它们。这就是体制。
在你还没有被完全“体制化”时,为何不多学些语言,哪怕只是浅尝辄止,潜移默化中也许你的思维壁垒就松动了。不管是Golang还是Ruby还是其他语言,当看到一些语法习惯与之前熟悉的C和Java不同时,的确潜意识里就会产生抵触情绪,觉得这不好,还是自己习惯的那套好。长此以往,如果不能冲破自己的心理,“坐以待毙”,被时间淘汰恐怕只是早晚的事儿。所以这里的关键也 不是非要学习Golang,而是要不断地学!
1.2 用什么工具来开发
Golang也有专门的IDE,但由于最近迷上了Sublime Text神器,所以这里还是用ST来学习Golang。配置步骤与在ST中使用其他语言开发都类似:
安装智能提示插件GoSublime
创建编译配置脚本
点Preferences -> Package Settings -> GoSublime -> User Settings中写入(感觉保存时自动格式化出来的缩进、空格等风格有些“讨厌”,所以就禁掉了):
{
"fmt_enabled": false,
"env": {
"path":"D:\\Program Files (x86)\\Go\bin"
}
}
点新建Build System产生go.sublime-build中写入:
{
"path": "D:\\Program Files (x86)\\Go\\bin",
"cmd": ["go", "run", "${file}"],
"selector": "source.go"
}
2.你好,世界
Golang版的HelloWorld来了!一眼望去,package和import的声明方式与Java如出一辙,比较明显的区别是:func关键字、每行末尾没有分号、Println()大写的函数名。这个例子虽小,却“五脏俱全”,后面会逐一分析这个小例子中碰到的Golang语法点。
package main
import "fmt"
func main() {
fmt.Println("你好,世界!")
}
2.1 运行方式
Golang提供了go run“解释”执行和go build编译执行两种运行方式,所谓的“解释”执行其实也是编译出了可执行文件后才执行的。
$ go run helloworld.go
你好,世界!
$ go build helloworld.go
$ ls
helloworld helloworld.go
$ ./helloworld
你好,世界!
2.2 Package管理
上面例子中我们使用的就是fmt包下的Println()函数。Golang约定:我们可以用./或../相对路径来引自己的package;如果不是相对路径,那么go会去$GOPATH/src下查找。
2.3 格式化输出
类似C、Java等语言,Golang的fmt包提供了格式化输出功能,而且像%d、%s等占位符和\t、\r、\n转义也几乎完全一致。但Golang的Println不支持格式化,只有Printf支持,所以我们经常会在后面加入\n换行。此外,Golang加入了%T打印值的类型,%v打印数组等集合的所有元素。
package main
import "fmt"
import "math"
/**
* This is Printer!
* 布尔值:false
* 二进制:11111111
* 八进制:377
* 十六进制:FF
* 十进制:255
* 浮点数:3.141593
* 字符串:printer
*
* 对象类型:int,string,bool,float64
* 集合:[1 2 3 4 5]
*/
func main() {
fmt.Println("This is Printer!")
fmt.Printf("布尔值:%t\n", 1 == 2)
fmt.Printf("二进制:%b\n", 255)
fmt.Printf("八进制:%o\n", 255)
fmt.Printf("十六进制:%X\n", 255)
fmt.Printf("十进制:%d\n", 255)
fmt.Printf("浮点数:%f\n", math.Pi)
fmt.Printf("字符串:%s\n", "printer")
fmt.Printf("对象类型:%T,%T,%T,%T\n", 1, "hello", true, math.E)
fmt.Printf("集合:%v\n", [5]int{1, 2, 3, 4, 5})
}
3.语法基础
3.1 变量和常量
虽然Golang是静态类型语言,却用类似JavaScript中的var关键字声明变量。而且像同样是静态语言的Scala一样,支持类型自动推断。有一点很重要的不同是:如果明确指明变量类型的话,类型要放在变量名后面。这有点别扭吧?!后面会看到函数的入参和返回值的类型也要这样声明。
package main
import "fmt"
/**
* 单变量声明:num[100], word[hello]
* 多变量声明:i[1], i[2], k[3]
* 推导类型:b1[true], b2[false]
* 常量:age[20], pi[3.141593]
*/
func main() {
var num int = 100
var word string = "hello"
fmt.Printf("单变量声明:num[%d], word[%s]\n", num, word)
var i, j, k int = 1, 2, 3
fmt.Printf("多变量声明:i[%d], i[%d], k[%d]\n", i, j, k)
var b1 = true
b2 := false
fmt.Printf("推导类型:b1[%t], b2[%t]\n", b1, b2)
const age int = 20
const pi float32 = 3.1415926
fmt.Printf("常量:age[%d], pi[%f]\n", age, pi)
}
3.2 控制语句
作为最基本的语法要素,Golang的各种控制语句也是特点鲜明。在对C继承发扬的同时,也有自己的想法融入其中:
if/switch/for的条件部分都没有圆括号,但必须有花括号。
switch的case中不需要break。《C专家编程》里也“控诉”了C的fall-through问题。既然90%以上的情况都要break,为何不将break作为case的默认行为?而且编程语言后来者也鲜有纠正这一问题的。
switch的case条件可以是多个值。
Golang中没有while。
package main
import "fmt"
/**
* testIf: x[2] is even
* testIf: x[3] is odd
*
* testSwitch: One
* testSwitch: Two
* testSwitch: Three, Four, Five [3]
* testSwitch: Three, Four, Five [4]
* testSwitch: Three, Four, Five [5]
*
* 标准模式:[0] [1] [2] [3] [4] [5] [6]
* While模式:[0] [1] [2] [3] [4] [5] [6]
* 死循环模式:[0] [1] [2] [3] [4] [5] [6]
*/
func main() {
testIf(2)
testIf(3)
testSwitch(1)
testSwitch(2)
testSwitch(3)
testSwitch(4)
testSwitch(5)
testFor(7)
}
func testIf(x int) {
if x % 2 == 0 {
fmt.Printf("testIf: x[%d] is even\n", x)
} else {
fmt.Printf("testIf: x[%d] is odd\n", x)
}
}
func testSwitch(i int) {
switch i {
case 1:
fmt.Println("testSwitch: One")
case 2:
fmt.Println("testSwitch: Two")
case 3, 4, 5:
fmt.Printf("testSwitch: Three, Four, Five [%d]\n", i)
default:
fmt.Printf("testSwitch: Invalid value[%d]\n", i)
}
}
func testFor(upper int) {
fmt.Print("标准模式:")
for i := 0; i < upper; i++ {
fmt.Printf("[%d] ", i)
}
fmt.Println()
fmt.Print("While模式:")
j := 0
for j < upper {
fmt.Printf("[%d] ", j)
j++
}
fmt.Println()
fmt.Print("死循环模式:")
k := 0
for {
if (k >= upper) {
break
}
fmt.Printf("[%d] ", k)
k++
}
fmt.Println()
}
分号和花括号
分号由词法分析器在扫描源代码过程自动插入的,分析器使用简单的规则:如果在一个新行前方的最后一个标记是一个标识符(包括像int和float64这样的单词)、一个基本的如数值这样的文字、或break continue fallthrough return ++ – ) }中的一个时,它就会自动插入分号。
分号的自动插入规则产生了“蝴蝶效应”:所有控制结构的左花括号不都能放在下一行。因为按照上面的规则,这样做会导致分析器在左花括号的前方插入一个分号,从而引起难以预料的结果。所以Golang中是不能随便换行的。
3.3 函数
函数有几点不同:
func关键字。
最大的不同就是“倒序”的类型声明。
不需要函数原型,引用的函数可以后定义。这一点很好,真不喜欢C语言里要么将“最底层抽象”的函数放在最前面定义,要么写一堆函数原型声明在最前面。
3.4 集合
Golang提供了数组和Map作为基本数据结构:
数组中的元素会自动初始化,例如int数组元素初始化为0
切片(借鉴Python)的区间跟主流语言一样,都是 “左闭右开”
用 range()遍历数组和Map
package main
import "fmt"
/**
* Array未初始化: [0 0 0 0 0]
* Array赋值: [0 10 0 20 0]
* Array初始化: [0 1 2 3 4 5]
* Array二维: [[0 1 2] [1 2 3]]
* Array切片: [2 3] [0 1 2 3] [2 3 4 5]
*
* Map哈希表:map[one:1 two:2 three:3],长度[3]
* Map删除元素后:map[one:1 three:3],长度[2]
* Map打印:
* one => 1
* four => 4
* three => 3
* five => 5
*/
func main() {
testArray()
testMap()
}
func testArray() {
var a [5]int
fmt.Println("Array未初始化: ", a)
a[1] = 10
a[3] = 20
fmt.Println("Array赋值: ", a)
b := []int{0, 1, 2, 3, 4, 5}
fmt.Println("Array初始化: ", b)
var c [2][3]int
for i := 0; i < 2; i++ {
for j := 0; j < 3; j++ {
c[i][j] = i + j
}
}
fmt.Println("Array二维: ", c)
d := b[2:4] // b[3,4]
e := b[:4] // b[1,2,3,4]
f := b[2:] // b[3,4,5]
fmt.Println("Array切片:", d, e, f)
}
func testMap() {
m := make(map[string]int)
m["one"] = 1
m["two"] = 2
m["three"] = 3
fmt.Printf("Map哈希表:%v,长度[%d]\n", m, len(m))
delete(m, "two")
fmt.Printf("Map删除元素后:%v,长度[%d]\n", m, len(m))
m["four"] = 4
m["five"] = 5
fmt.Println("Map打印:")
for key, val := range m {
fmt.Printf("\t%s => %d\n", key, val)
}
fmt.Println()
}
3.5 指针和内存分配
Golang中可以使用指针,并提供了两种内存分配机制:
new:分配长度为0的空白内存,返回类型T*。
make:仅用于 切片、map、chan消息管道,返回类型T而不是指针。
package main
import "fmt"
/**
* 整数i=[10],指针pInt=[0x184000c0],指针指向*pInt=[10]
* 整数i=[3],指针pInt=[0x184000c0],指针指向*pInt=[3]
* 整数i=[5],指针pInt=[0x184000c0],指针指向*pInt=[5]
*
* Wild的数组指针:
* Wild的数组指针==nil[true]
*
* New分配的数组指针: &[]
* New分配的数组指针[0x18443010],长度[0]
* New分配的数组指针==nil[false]
* New分配的数组指针Make后: &[0 0 0 0 0 0 0 0 0 0]
* New分配的数组元素[3]: 23
*
* Make分配的数组引用: [0 0 0 0 0 0 0 0 0 0]
*/
func main() {
te
点击排行
本栏推荐
