1.从Hello,world开始
package main
import "fmt"
func main() {
/* 简单的程序 万能的hello world */
fmt.Println("Hello Go")
}
-
package关键字声明了是当前 go 文件属于哪一个包,入口文件都必须声明为main包,入口函数是main函数,在自定义包和函数时命名应当尽量避免与之重复。 -
import是导入关键字,后面跟着的是被导入的包名。 -
func是函数声明关键字,用于声明一个函数。 -
fmt.Println("Hello 世界!")是一个语句,调用了fmt包下的Println函数进行输出。
2.import与init函数
golang里面有两个保留的函数:init函数(能够应用于所有的package)和main函数(只能应用于package main)。这两个函数在定义时不能有任何的参数和返回值。
import _ "fmt" //匿名导入包,无法使用包中的方法,但是会执行包中的init()方法
go程序会自动调用init()和main(),所以你不需要在任何地方调用这两个函数。每个package中的init函数都是可选的,但package main就必须包含一个main函数。
包的导入可以看作是一个递归的过程。

3.变量声明
变量主要通过以下两种方式声明
var name string = "Bond"
/*
使用var关键字初始化变量,变量的类型在变量名后声明
变量初始化后会有一个默认初始值,例如:"",0等
也可以在初始化变量时就直接给变量赋值
*/
age := 11
/*
可以省略var,使用:=来声明变量
将会根据值自行判断变量类型
注意 :=左侧的变量不能是已声明过的
*/
在 go 语言中,有一个规则,那就是所有在函数中的变量都必须要被使用,否则编译时会报错。
我们可以用下划线表示不需要某一个变量
比如os.Open函数有两个返回值,我们只想要第一个,不想要第二个,可以按照下面这样写
file, _ := os.Open("readme.txt")
4.条件和循环
条件控制:if else和switch
Go中的if else语句与其他语言用法基本相同。
Go的switch语句不同之处在于:当前的case执行结束后,不会继续执行下一个分支,所以不必在case语句的最后添加break,当然如果想要继续执行下一个分支,可以添加fallthrough关键字来声明。另外Go的case条件更宽泛,不仅局限于单个的数值或字符。还可以在表达式之前编写一些简单语句,例如声明新变量。
func main() {
switch num := f(); { // 等价于 switch num := f(); true {
case num >= 0 && num <= 1:
num++
case num > 1:
num--
fallthrough
case num < 0:
num += num
}
}
func f() int {
return 1
}
循环控制:for
在 Go 中,有且仅有一种循环语句:for,Go 抛弃了while语句,for语句可以被当作while来使用。
语句格式如下
for init statement; expression; post statement {
execute statement
}
//可以同时初始化多个变量,然后将其递增
for i, j := 1, 2; i < 100 && j < 1000; i, j = i+1, j+1 {
fmt.Println(i, j)
}
5.数组、切片与Map
在Go语言中,数组长度在定义后就不可更改,在声明时长度可以为一个常量或者一个常量表达式(常量表达式是指在编译期即可计算结果的表达式)。数组的长度是该数组类型的一个内置常量,可以用Go语言的内置函数len()来获取。
需要特别注意的是,在Go语言中数组是一个值类型(value type)。所有的值类型变量在赋值和作为参数传递时都将产生一次复制动作。如果将数组作为函数的参数类型,则在函数调用时该 参数将发生数据复制。因此,在函数体中无法修改传入的数组的内容,因为函数内操作的只是所传入数组的一个副本。
/*数组的初始化*/
num := [3]int{1,2,3}
bytes := [2][2]byte
切片slice是动态数组,底层共享数组内存,一个切片在未初始化之前默认为 nil,长度为 0
基于数组创建:数组切片可以只使用数组的一部分元素或者整个数组来创建,甚至可以创建一个比所基于的数组还要大的数组切片。
array := [10]int{1,2,3,4,5,6,7,8,9,10}
slice1 := array[:5] //基于array前5个元素创建切片
slice2 := array[5:] //基于从第5个元素开始的所有元素创建数组切片
slice3 := array[:] //基于array的所有元素创建数组切片
slice4 := array[:20] //不足的位置会初始化为0补齐
直接创建:使用make()函数来创建切片,本质上仍是基于数组的,事实上还会有一个匿名数组被创建出来。
var slice1 []type = make([]type, len)
//也可以简写为
slice1 := make([]type, len)
append() 向切片追加新元素和 copy() 函数拷贝切片
func append(slice []Type, elems ...Type) []Type
copy(dest, src)
map和slice类似,只不过是数据结构不同
//第一种声明
var test1 map[string]string
//在使用map前,需要先make,make的作用就是给map分配数据空间
test1 = make(map[string]string, 10)
test1["one"] = "php"
test1["two"] = "golang"
test1["three"] = "java"
fmt.Println(test1) //map[two:golang three:java one:php]
//第二种声明
test2 := make(map[string]string)
test2["one"] = "php"
test2["two"] = "golang"
test2["three"] = "java"
fmt.Println(test2) //map[one:php two:golang three:java]
//第三种声明
test3 := map[string]string{
"one" : "php",
"two" : "golang",
"three" : "java",}
6.指针
与C++不同的是Go 中是不支持指针运算的。因为垃圾回收功能的支持, 开发者无需担心所指向的对象失效的问题,因此Go语言中不需要delete关键字,也不需要free() 方法来明确释放内存。
7.函数
Go的函数使用关键字func声明,且可以有多个返回值,但是不支持函数重载。
func 函数名([参数列表]) [返回值] {
函数体
}
func Sum(a int, b int) (int,int) {
return b,a+b
}
函数参数的传递分为值传递和引用传递
可以使用关键字defer在函数中声明某条语句,该语句不会立即执行,而是在压入栈中,在函数结束后依次出栈执行。
8.面向对象特征
结构体可以存储一组不同类型的数据,是一种复合类型。Go 抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go 并非是一个传统 OOP 的语言,但是 Go 依旧有着 OOP 的影子,通过结构体和方法也可以模拟出一个类。
package main
import "fmt"
//定义一个结构体
type T struct {
name string
}
//为结构体类型定义方法
//方法的接收者是值类型,无法修改接收者的值
func (t T) method1() {
t.name = "new name1"
}
//方法的接收者是指针类型,可以修改接收者的值
func (t *T) method2() {
t.name = "new name2"
}
/*所谓方法的接收者可以理解为this指针
只是在C++中this指针是隐藏起来的
而Go将其显示地表示出来了*/
9.接口
在 Go 语言中,接口是一种抽象类型,用于定义一组方法签名而不提供方法的实现。接口的核心理念是描述行为,而具体的行为实现由实现接口的类型提供。
type Person interface {
Say(string) string
Walk(int)
}
type Number int
func (n Number) Say(s string) string {
return "bibibibibi"
}
func (n Number) Walk(i int) {
fmt.Println("can not walk")
}
这是一个Person接口,有两个对外暴露的方法Walk和Say,在接口里,函数的参数名变得不再重要,当然如果想加上参数名和返回值名也是允许的。
与其他语言不同的是,Go语言的接口是非侵入式的,不必显式地声明继承,一个类只需要实现了接口要求的所有函数,我们就说这个类实现了该接口,整个过程非常自然。
在Go语言中,只要两个接口拥有相同的方法列表(次序不同不要紧),那么它们就是等同的,可以相互赋值。接口赋值并不要求两个接口必须等价。如果接口A的方法列表是接口B的方法列表的子集, 那么接口B可以赋值给接口A。
10.Go特性:goroutine
Go 在语言级别支持协程,叫goroutine。Go 语言标准库提供的所有系统调用操作(包括所有同步IO操作),都会出让CPU给其他goroutine。这让轻量级线程的切换管理不依赖于系统的线程和进程,也不需要依赖于CPU的核心数量。
只需在函数调⽤语句前添加 go 关键字,就可创建并发执⾏单元。开发⼈员无需了解任何执行细节,调度器会自动将其安排到合适的系统线程上执行。
package main
import (
"fmt"
"time"
)
func newTask() {
i := 0
for {
i++
fmt.Printf("new goroutine: i = %d\n", i)
time.Sleep(1*time.Second) //延时1s
}
}
func main() {
//创建一个 goroutine,启动另外一个任务
go newTask()
i := 0
//main goroutine 循环打印
for {
i++
fmt.Printf("main goroutine: i = %d\n", i)
time.Sleep(1 * time.Second) //延时1s
}
}
11.Go特性:channel
channel是Go语言中的一个核心类型,可以把它看成管道。并发核心单元通过它就可以发送或者接收数据进行通讯。
//Type指定这个channel所能传递的元素类型。
make(chan Type) //等价于make(chan Type, 0)
make(chan Type, capacity)
channel <- value //发送value到channel
<-channel //接收并将其丢弃
x := <-channel //从channel中接收数据,并赋值给x
x, ok := <-channel //功能同上,同时检查通道是否已关闭或者是否为空
/*从channel接收数据时,<-和channel间不能有空格*/
默认情况下,channel接收和发送数据都是阻塞的,除非另一端已经准备好,这样就使得goroutine同步变的更加的简单,而不需要显式的lock。
无缓冲的channel
无缓冲的通道(unbuffered channel)是指在接收前没有能力保存任何数据值的通道。
这种类型的通道要求发送goroutine和接收goroutine同时准备好,才能完成发送和接收操作。否则,通道会导致先执行发送或接收操作的 goroutine 阻塞等待。
这种对通道进行发送和接收的交互行为本身就是同步的。其中任意一个操作都无法离开另一个操作单独存在。

有缓冲的channel
有缓冲的通道(buffered channel)是一种在被接收前能存储一个或者多个数据值的通道。
这种类型的通道并不强制要求 goroutine 之间必须同时完成发送和接收。通道会阻塞发送和接收动作的条件也不同。
只有通道中没有要接收的值时,接收动作才会阻塞。
只有通道没有可用缓冲区容纳被发送的值时,发送动作才会阻塞。
这导致有缓冲的通道和无缓冲的通道之间的一个很大的不同:无缓冲的通道保证进行发送和接收的 goroutine 会在同一时间进行数据交换;有缓冲的通道没有这种保证。

select作用
Go里面提供了一个关键字select,通过select可以监听channel上的数据流动,用于处理异步IO问题
select的用法与switch语言非常类似,由select开始一个新的选择块,每个选择条件由case语句来描述。
与switch语句相比,select有比较多的限制,其中最大的一条限制就是每个case语句里必须是一个IO操作。
有时候我们希望能够借助channel发送或接收数据,并避免因为发送或者接收导致的阻塞,尤其是当channel没有准备好写或者读时。select语句就可以实现这样的功能。
select {
case <- chan1:
// 如果chan1成功读到数据,则进行该case处理语句
case chan2 <- 1:
// 如果成功向chan2写入数据,则进行该case处理语句
default:
// 如果上面都没有成功,则进入default处理流程
}
/*可以看出,select不像switch,后面并不带判断条件,而是直接去查看case语句。每个
case语句都必须是一个面向channel的操作。*/
如果其中的任意一语句可以继续执行(即没有被阻塞),那么就从那些可以执行的语句中任意选择一条来使用。
如果没有任意一条语句可以执行(即所有的通道都被阻塞),那么有两种可能的情况:
l 如果给出了default语句,那么就会执行default语句,同时程序的执行会从select语句后的语句中恢复。
l 如果没有default语句,那么select语句将被阻塞,直到至少有一个通信可以进行下去。
最近在转Go语言,这篇文章是一个小小的总结。希望对Go语言感兴趣的人可以通过这篇文章对Go有一个初步的了解。
本文的内容和图片主要来自以上两个文档,侵删。
Go语言从Hello,world开始学习
154






