golang学习
第一天上班,因为公司马上要搬家,新电脑的话就安置在新场地了,原本golang在linux(Ubuntu 14.04 据说16容易崩溃)上用比较好。但是由于条件艰苦,还是先简单看语法内容吧。
首先我为什么要学习go语言,实习内容是做区块链的开发,Go 语言被设计成一门应用于搭载 Web 服务器,存储集群或类似用途的巨型*服务器的系统编程语言。
Go 语言在用于高性能分布式系统开发中,无疑比大多数其它语言有着更高的开发效率。此外,它提供了海量并行的支持,这对于游戏服务端的开发而言也是不错的选择。同时go最终生成的是一个 可执行文件,不管你的程序依赖多少库,都会被打包进行,生成一个可执行文件,所以相比java庞大的jar库来说,他的部署非常方便,执行运行这个可执行文件就好了。
Go开发环境配置:
访问 Golang官网,下载指定平台的安装包,目前支持Windows
、MacOS X
、Linux
和 FreeBSD
四个平台,这里我们以Windows为例:
1.Windows下的安装:
安装包 go1.10.3.windows-amd64.msi
资源地址:
-
检查版本:
直接打开cmd命令行,输入一下指令查询安装Go的版本:C:\Users\Administrator>go version go version go1.10.3 windows/amd6
2.Linux下的安装:
- 下载源码包:go1.10.3.linux-amd64.tar.gz;
- 解压到
/usr/local
目录:tar -C /usr/local -xzf go1.10.3.linux-amd64.tar.gz
; - 将
/usr/local/go/bin
目录添加到Path
环境变量中:export PATH=$PATH:/usr/local/go/bin
。
第一行代码:
-
Tour编辑器:
假如只是前期的语法学习,可以考虑先不安装开发环境,可以直接在 Golang官网 上的网页内嵌 Tour编辑器 中编写代码和运行:
在编辑器中编写如下代码:package main import "fmt" func main() { fmt.Println("Hello world!") }
运行结果:
-
本地运行:
也可以在本地创建一个hello.go
文件,内容与上述一致,然后在命令行定位到创建.go
文件的目录下,执行go run (.go文件名)
:E:\Go\Projects>go run hello.go Hello world!
此过程会将
.go
源码编译成二进制文件,但是编译结果不会保存到本地,可以go build
实现此过程,编译结果hello.exe
会保存到本地,可以直接在命令行运行:E:\Go\Projects>hello.exe Hello world!
-
源码解析:
-
package
:是一个关键字,定义一个包,和Java中的Package
一样,用于模块化编程; -
import
:也是一个关键字,用于引入包,与Java的import
一样,引入包之后可以使用其中的功能; -
fmt
:Go语言的一个自带功能包; -
main()
:主函数,也是程序执行的入口; -
fmt.Println
:用于输出一段字符串。
不难发现Go语言与Java的关键字、函数和包等很相似,但是,Go语言不需要以
;
(分号)符号结尾,一行代表一个语句结束。 -
Go基本语法:
1.基本数据类型:
数值类型 | 说明 |
---|---|
bool | 布尔 |
string | 字符 |
int,int8,int16,int32,int64 | int长度与平台相关 |
uint,uint8,uint16,uint32,uint64 | uint长度与平台相关 |
uintptr | 同指针,32位平台为4字节,64位八字节 |
byte | 等价于uint8 |
rune | 等价于uint32,单个unicode字符 |
float32,float64 | |
complex64,complex128 | 复数类型, value = 3.2+12i |
2.数值定义:
Go语言最独特的就是:声明变量或者常量时,类型表示写在变量或者常量名称之后。
-
常量:
- 显式定义
const a int = 1
- 隐式定义
const a = 1
可以进行多个相同或不同类型的声明:
const a, b, c = 1, false, "str" //多重赋值
-
变量:
var s string = "hehe"
声明后若不赋值,使用默认值:
int a
假如输出
a
结果,则为int默认值0。还可以同时对多个相同或不同类型的变量进行声明:var e, f = 123, "hello"
也有一种简化的声明方式,不需显示声明变量类型也省去了
var
,变量的类型由go根据值推导出来:s := "hehe"
有兴趣的还可以了解一下
值类型
和引用类型
的区别。
3.操作符:
-
赋值:
=
,:=
-
数值运算:
+
(相加),-
(相减),*
(相乘),/
(相除),%
(求余),++
(自增),--
(自减) -
比较运算:
>
,<
,==
,>=
,<=
,!=
-
逻辑运算:
&&
,||
,!
-
位运行:
>>
,<<
,^
,&
(与),|
(或),^x
(取反) -
其他:
&
(变量存储地址),*
(指针变量)
其中有两个比较特殊的操作符:
特殊操作符 | 说明 |
---|---|
:= | 无需指定类型即可赋值:i, j := true, "hello"
|
_ | 空白标识符,可以赋任意值的空对象:_ = "string"
|
空白标识符
_
也被用于抛弃值,如值 5 在:_, b = 5, 7
中被抛弃。_
实际上是一个只写变量,你不能得到它的值。这样做是因为 Go 语言中你必须使用所有被声明的变量,但有时你并不需要使用从一个函数得到的所有返回值。
4.大小写标记访问权限:
在go中不能随便使用大小写的问题,是因为大小写具有特殊意义,在go中,大写字母开头的变量或者函数等是public
的,可以被其他包访问;小写的则是private
的,不能被其他包访问到。这样就省去了public和private声明的烦恼,使代码变的更简洁。
5.关键字:
关键字 | 作用 |
---|---|
package | 代码所属包 |
import | 导入依赖包,不能导入不使用的包 |
main | 主函数入口,无参数无返回值,命令行参数保存在os.Args中 |
func | 函数声明 |
go | 开启协程(并发核心) |
map | 字典类型, map[string]bool |
delete | 专用来删除字典中的元素 |
chan | 通道,协程通信核心 |
select | 同时读取多个chan的信息 |
close | 用来关闭通道 |
make | 用来创建chan或map |
type | 类型定义,定义任意需要的类型 |
struct | C中的结构体,但可以定义方法以实现类功能 |
interface | 接口类型,用来定义接口 |
new | 新建对象, 并获得它的指针 |
range | 类似python的range,配合for遍历列表、数组或map的元素 |
defer | 自动关闭资源,退出代码体(一般是函数)时执行 |
error | error接口,只要实现Error()string方法就是一个error接口 |
panic | 抛出异常,如果不用recover捕获则会终止程序 |
recover | 捕获panic异常,防止程序终止,与recover结合 |
6.注释:
Go
程序的代码注释与 C++
保持一致,主持:块注释 与 行注释
-
块注释:
/* 块注释 */
-
行注释:
// 行注释
7.流程控制:
-
条件:
条件语句 说明 if if 语句 由一个布尔表达式后紧跟一个或多个语句组成。 if…else if 语句 后可以使用可选的 else 语句, else 语句中的表达式在布尔表达式为 false 时执行。 if 嵌套 你可以在 if 或 else if 语句中嵌入一个或多个 if 或 else if 语句。 switch switch 语句用于基于不同条件执行不同动作。 select select 语句类似于 switch 语句,但是select会随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。 -
if
if i>0 { //条件执行内容 }
相比于其他语言,Go语言的条件判断语句不需要小括号。
// 特殊用法, 判断语句中可以初始化变量 if i:=4;i>0{ fmt.Println("i = ",i) }
-
switch,case
switch i { case 0: // ... case 1: // ... default: // ... }
Go语言的
switch
不需要break
来跳出一个case
。 -
select
可以看做是专用于通信的switch
语句,每一个case
必须是一个通信操作,要么是发送要么是接收。select
执行逻辑:随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。一个默认的子句应该总是可运行的。select { case communication clause : statement(s); case communication clause : statement(s); /* 你可以定义任意数量的 case */ default : /* 可选 */ statement(s); }
执行
select
的语法大致如下:- 每个
case
都必须是一个通信 -
channel
表达式都会被求值 - 所有被发送的表达式都会被求值
- 如果任意某个通信可以进行,它就执行;其他被忽略。
- 如果有多个
case
都可以运行,select
会随机公平地选出一个执行。其他不会执行。 - 否则:
1.如果有default
子句,则执行该语句。
2.如果没有default
字句,select
将阻塞,直到某个通信可以运行;Go不会重新对channel或值进行求值。
- 每个
-
-
循环:
循环语句 说明 for 重复执行语句块 for嵌套 在 for 循环中嵌套一个或多个 for 循环 Go语言中的for循环与Java、C#等的区别就是三个由
;
(分号)分开的组成部分不需要小括号括起来:for i := 0; i < 10; i++ { sum += i }
Go语言中没有其他语言中
while
的用法,for
在Go语言中可以当做while
来用:sum := 1 //循环条件 for sum < 1000 { sum += sum } //死循环 for {// 无退出条件,变成死循环 }
-
其他:
在Go语言中,同样可以使用break
、continue
和goto
来控制流程:控制语句 说明 break 经常用于中断当前 for
循环或跳出switch
语句continue 跳过当前循环的剩余语句,然后继续进行下一轮循环。 goto 将控制转移到被标记的语句。 例如
goto
,需要用一个文本来标志某个语句(写在该语句前面):goto label .. . label: statement
label
就是用来标志的文本,statement
是一个普通的Go语句,goto label
执行之后,程序将调到statement
然后顺序往下执行。
8.函数:
Go语言中定义一个函数的格式如下:
func 方法名称([传入参数表]) [返回结果数据类型]{
//函数体
}
上面使用[]
符号括起来的部分表示 非必须 的,可根据使用情况决定是否可以不写,例如无需传入参数时,传入参数列表可以省略。注意:传入的参数列表中形式参数定义的格式也是:变量名在前,变量类型在后。
-
普通用法:
package main import "fmt" func main(){ PrintSth(1,2) sum := 0 sum = mSum(10,20) fmt.Println("sum = ",sum) } func PrintSth(x,y int){ fmt.Println("x = ",x,";y = ",y) } func mSum(x,y int) int{ return x+y }
-
返回多个值:
例如定义一个值互换的方法://定义函数 func swap(x, y string) (string, string) { return y, x } //调用函数 a,b := swap("a","b")
-
函数作为值:
定义函数的时候,也可以将函数声明为一个函数变量,调用的时候用函数变量替换函数真是名称来调用即可:/* 声明函数变量 */ getSquare := func(x float64) float64 { return x*x } /* 使用函数 */ fmt.Println(getSquare(3))
-
闭包:
闭包,是匿名函数(一个“内联”语句或表达式)的一种声明方式,好处就是可以直接使用函数内的变量不必声明://没调用一次getSequence,i就自增1 func getSequence() func() int { i:=0 return func() int { i+=1 return i } }
函数的返回结果是另一个函数。
-
方法:
与函数略有不同,方法声明时会指定其所附的是命名类型或结构类型的一个值或者是一个指针,所有给定类型的方法属于该类型的方法集。例如://定义结构体 type Circle struct { radius float64 } //此属于 Circle 类型对象中的方法 func (c Circle) getArea() float64 { //c.radius 即为 Circle 类型对象中的属性 return 3.14 * c.radius * c.radius } //调用 main(){ var c Circle c.radius = 1 fmt.Println("Area of c = ", c.getArea()) }
9.指针:
-
指针变量:
一个 指针变量 可以指向任何一个值的内存地址,使用指针前需要声明,声明格式如下:var var_name *var-type
var_name
是变量名,var-type
是指针类型,例如:var ip *int // 此指针只能指向整型
-
使用步骤:
创建指针变量,为指针赋值(如:指向变量),输出结果:var a int = 1 //创建指针变量 var ptr *int //指针赋值(指向变量a的地址) ptr = &a /* 指针变量的存储地址 */ fmt.Printf("ptr 变量储存的指针地址: %x\n", ptr ) /* 使用指针访问值 */ fmt.Printf("*ptr 变量的值: %d\n", *ptr )
-
取址符:
Go语言中,使用&
作 取址符,假如放在变量前可以返回相应变量的内存地址:var a int = 1 fmt.Printf("变量的地址: %x\n", &a )
输出结果:
变量的地址: c0420382a0
-
空指针:
Go语言中的空指针是nil
,表示没有分配到任何变量,判断指针是否为空指针://声明指针,不赋值 var ptr *int if ptr == nil{ //条件执行块 }
10.结构体:
以关键字 type
和 struct
来一起声明一个结构体,格式如下:
type structure_variable_type struct{
member menberType
member menberType
...
}
其中 structure_variable_type
是当前定义结构体的名称,创建结构体操作:
variable_name := structure_variable_type(value1,value2,...)
例如:
//定义
type Info struct{
id int
name string
}
//声明
var mInfo Info
//赋值
mInfo.id = 1
mInfo.name = "linsh"
11.包的使用:
每个Go程序都是由包组成的,其中程序运行入口包为 main
,通常包名与导入目录路径最后一级目录名称一致。例如:导入“math、rand”包,这个包必然是以 package rand
语句开始的。
-
导入包:
以import
关键字来实现包的导入,有两种格式:-
多个导入语句:
import "fmt" import "math"
-
“打包”导入:
import ( "fmt" "math" )
使用第二种方式导入包更为方便。
-
-
导出命名:
在Go编程中,某个包中 使用首字母大写的名称定义的属性或者方法可以被导入了此包的脚本调用,相当于是开放了访问权限,例如:这里我们定义了一个 ··Test·· 包,然后再main
包中引入此包并调用其导出方法:package Test func SetName(_name string){ } func setName(_name string){ }
在
main
中先import Test
,然后只能调用SetName
函数而不能调用setname
。