是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。
go1.11.2.darwin-amd64.pkg
检查安装成功:在终端输入:go,出现go命令列表。
(1)打开终端,cd ~
(2)查看是否有.bash_profile文件:
ls -all
(3)有则跳过此步,没有则:
1)创建:touch .bash_profile
2)编辑:open -e .bash_profile
3)自定义GOPATH和GOBIN位置:
export GOPATH=/Users/lwh/program/Go
export GOBIN=$GOPATH/bin
export PATH=$PATH:$GOBIN
(4)编译:source .bash_profile
终端输入以下命令。显示go运行所需的系统环境变量。
go env
Sublime Text3的gosublime这个插件目前不能用,但是可以在github上下载,相比起来vscode有智能提示所以采用vscode。
goland使用:
goland应该是最好用的,很少的配置,收费,不缺钱可以支持下正版。
i> 官网下载goland。
ii> 新建一个project,选择项目location。
如果go环境安装成功goroot可以选择。然后create项目。
在项目根目录创建directory:main。然后在其下方写一个新的程序:test.go。
package main
import (
"fmt"
)
func main(){
fmt.Println("hello world")
}
Control + shift + R运行即可。#
即创建GOPATH目录,并在GOPATH下建立三个目录:
bin:存放编译后生成的可执行文件(.exe)
pkg:存放编译后生成的包文件(.a)
src:存放项目源码(.go)
函数名首字母小写,表示private;
函数名首字母大写,表示public。
运行期间不可改变值。
可以涉及计算,但每一个值必须是编译期就能获得。
true、false、iota。
初值为0,作为常量组中常量个数的计数器。
const PI float32 = 3.1415926
const PI = 3.1415926 //隐式类型常量定义(省略常量类型,根据赋值自动判断类型)
const a,b,c = 1, "Go", 'c' //整型常量、字符串常量、字符常量 多个常量一起定义
枚举指一系列的常量。
const (
Sunday = iota //0 iota生成了从0开始自动增长的枚举值
Monday //1 后续的iota可以省略
Tuesday //2
)
const (
A = iota //iota每遇到一个const关键字,就重置为0
B
)
fmt.Println(Sunday); //0
fmt.Println(Monday); //1
fmt.Println(Tuesday); //2
fmt.Println("-------");
fmt.Println(A); //0
fmt.Println(B); //1
运行期间可改变的值。
变量名是用来引用变量所存储数据的标识符,实际上代表变量在内存中的地址,可以使用&
获取。
var count int = 10 //格式为:var [variableType]
var count = 10 //缺省
count: = 10 //用:代替var的缺省
即初始化值。
类型 | 零值 |
---|---|
bool | false |
int、float、byte\rune | 0 |
complex | 0+0i |
string | “” |
pointer、function、interface、slice、channel、map | nil |
i> 以字母或下划线开始
ii> 由字母、数字、下划线组成
iii> 避免关键字
iv> 大小写区分为不同的变量
取值范围:true | false。
进制(4种):
123 //十进制
0123 //八进制 以0开头
0x123 //十六进制 以0x开头
1e3 //指数形式 由数字和e组成 1*(10^3) = 1000
有无符号整型:
int //有符号整型
uint //无符号整型
精度控制整数:
类型 | 字节长度 | 取值范围 |
---|---|---|
int | 4/8 | 由操作系统决定。32位机:int32,64位机int64 |
int8 | 1 | -128~127 (- 2^ 7 ~ (2^ 7 -1) ) |
int 16 | 2 | - 2^ 15 ~ (2^ 15 -1) |
int 32 | 4 | - 2^ 31 ~ (2^ 31 -1) |
int 64 | 8 | - 2^ 63 ~ (2^ 63 -1) |
- | - | - |
uint | 4/8 | |
uint8 | 1 | -128~127 (- 2^ 8 ~ (2^ 7) ) |
uint 16 | 2 | …… |
uint 32 | 4 | …… |
uint 64 | 8 | …… |
本质是uint8的类型,使用效果完全一样。
使用该别名是为了增强代码的可读性,告知现在是进行字节处理。
主要作用:表示和存储ASCII码,即处理字符。
字符以ASCII的形式存储到内存中。所以一个byte型数据直接输出就是uint8整数形式;也可以将对应的ASCII码转换成相应的字符。
Go语言处理Unicode时专门的数据类型,完全等价于int32。
使用该别名是为了增强代码的可读性,告知现在是进行Unicode字符处理。
表示形式:
1.25
1.23e3
类型 | 字节长度 | 精确位数 |
---|---|---|
float32 32位浮点型 | 4 | 精确到小数点后7位 |
float64 64位浮点型 | 8 | 精确到小数点后15位 |
浮点数存在舍入误差,所以避免极大数和极小数相加减丢失极小数。
var cp complex64 = 1.2 + 3.4i //复数:a+bi形式的数(i = 根号-1)。
real(cp) //即1.2,fmt内置包计算实部的函数
imag(cp) //即3.4,fmt内置包计算虚部的函数
类型 | 字节长度 |
---|---|
complex64 | 8 |
complex128 | 16 |
uintptr是可以保存指针的无符号整数类型,可以保存32位或64位的指针。根据操作系统决定指针位数。
//一般表现形式:var <指针变量名> * <基类型>
var i_pointer * int //指向整型的变量的指针i_pointer
var i int = 100; i_pointer = &i //将i的内存地址存放到i_pointer中
fmt.Println( * i_pointer ) //通过指针i_pointer读取对应地址存放的数据,结果输出为100。
使用注意:
→
运算符,直接用.
选择符操作指针对象成员。包括:bool、int、float、byte、复数型(complex)、字符串(string)、数组、结构体、错误类型(error)。
值拷贝传递。
字符串(string)、数组、结构体又称为构造类型。
包括:指针、切片(slice)、字典(map)、通道(channel)、接口(interface)、函数(function)。
var str string = "hello"; //字符串初始化
fmt.Println(str[2]); //取字符,以0开始。'A' = 65,'a' = 97, 输出为l = 97+12 = 108
fmt.Println(len(str)); //len()函数 输出为5
fmt.Println(str + " world");
支持2种方式。
var str string = "hello";
//字节数组方式遍历:(每一个字符类型为byte)
for i := 0; i < len(str); i++ {
fmt.Printf("str[%d] = %v\n", i, str[i]);
}
fmt.Println("-----------");
//Unicode字符方式遍历:(每一个字符类型为rune)
for i, ch := range str {
fmt.Printf("str[%d] = %v\n", i, ch);
}
Go标准库包。
包含了字符串查找函数、字符串比较函数、字符串位置索引函数、字符串追加和替换函数
Go标准库包。
提供了字符串与基本数据类型相互转化的基本函数。
数组是一组具有相同类型和名称的变量的集合。
var arr1 [5] int
数组的元素类型必须是基本数据类型。
var arr1 = [2] int {1,2}
var arr2 = [2] int {1}
var arr3 = [...] int {2,2} //...就是三个英文句号。这个不能省略,否则就成切片了
var a[3][4] int //二维数组定义
var a = [3][4] int {{1,2,3}, {4,5}, {6}}
是数组的一个引用,它会生成一个指向数组的指针,并通过切片长度关联到底层数组部分或者全部元素。
切片还提供了一系列对数组的管理功能,可以随时动态的扩充存储空间,并且可以被随意传递而不会导致所管理的数组元素被重复赋值。
故切片通常用于实现变长数组。
type slice struct {
array unsafe.Pointer //Pointer 是指向一个数组的指针
len int //len 代表当前切片的长度
cap int //cap 是当前切片的容量。cap 总是大于等于 len 的
}
var slice1 [] int //不要指定数组长度即切片
默认为nil,长度为0.
var slice1 = [] int {1,2,3,4}
var slice1 = make([] int, 4) //通过内置函数make创建有4个元素的整型切片,元素初始值为int的初始值0.
var slice1 = make([]int, 4, 10) //预留10个元素的存储空间。
slice1 = array1 //指定切片引用范围为数组全部
slice1 = array1[ : ] //指定切片引用范围从第一个元素到最后一个元素
slice1 = array1[m:n] //指定切片引用范围从第m个元素到第n个元素
由键值对组成。
未初始化时值为nil。
var map1 map[string] int {} //创建并初始化。键用[]包起来。
map1["key1"] = 1
或者用make函数
var map1 map[string] int //创建
map1 = make( map[string] int) //初始化,给map1分配存储空间
查找:
var map1 = map[string]int{"key1": 100, "key2": 200}
v, OK := map1["key1"] //查找一个特定的键值对。如果key值存在,则将Key对应的Value值赋予v,OK为true。否则v=0,OK为false。
if OK {
fmt.Println(v, OK) //输出:100 true
} else {
fmt.Println(v)
}
删除:
delete(map1,"key1")
使用type关键字为类型定义别名。
type(
word uint //定义word是uint的别名
小数 float32 //Go支持UTF-8编程格式,所以定义时可用非英文字符。
)
var f 小数 = 3.14
var i word = 3
fmt.Println(f)
fmt.Println(i)
格式:<变量A>[:] = <变量A的类型>(<变量B>)
var a int
var f float32 = 3.14
a = int(f) //如果变量A已经定义过了,可以省略`:`
b := int(f)
fmt.Println(a)
fmt.Println(b)
用分号来终止语句,但这个分号可以由词法分析器扫描源代码过程中自动插入(给每一行末尾根据简单的规则进行判断是否需要加分号,所以出现了左大括号约定)。
//
或
/* */
运算符 | 描述 | 说明 |
---|---|---|
^ | 取反 | |
& | ||
| |
||
&^ | 标志位清除运算符 | 从a上清除b上的标志位 |
<< | 左移 | |
>> |
var a, b byte = 10, 4 //1010
fmt.Println(a) //10b
fmt.Println(^a) //11110101b 2^8-1-10 = 256-11 = 245
fmt.Println(a &^ b) //1010b 清除 10b = 1000b = 8 ;1010b 清除 100b = 不变 = 1010b = 10
fmt.Println(a << 1) //10*2 = 20
详细用法可见通道。
原型:func Printf( format string, a...interface{} ) (n int, err error)
会返回输出的字节数和错误类型。
作用对象 | 格式字符 | 说明 |
---|---|---|
- | %v | 以基本格式输出 |
- | %#v | 输出数据,同时也输出Go语法表示 |
- | %T | 输出数据类型 |
- | %% | 输出% |
bool | %t | 输出布尔值true flase |
int | %b、%d、%o、%x、%X | 以二进制、十进制、八进制、十六进制(小写)、十六进制(大写) 输出 |
int | %c | 以Unicode字符格式输出(否则直接输出为ASCII码) |
int | %q | 输出的每个字符自动加单引号 |
int | %U | Unicode格式:U+1234 == U+%04X |
浮点型、复数 | %b | 无小数部分、两位指数的科学记数法 |
浮点型、复数 | %e、%E | 科学计数法(小写e、大写E) |
浮点型、复数 | %f | 有小数部分,但无指数部分 |
浮点型、复数 | %g、%G | 根据实际情况采用%e(%E)或%f |
字符串、切片 | %s | 直接输出字符串或切片 |
字符串、切片 | %q | 输出字符串的同时加双引号 |
字符串、切片 | %x、%X | 每个字节用两字符的十六进制数表示 |
指针 | %p | 以0x开头的十六进制数表示 |
- | + | 输出数值正负号对%q(%+q)按ASCII码输出 |
- | - | 使用空格填充右侧空缺(默认为左侧) |
- | # | |
- | ‘ ’ | |
- | 0 | 用前置0代替空格填补空缺 |
fmt.Printf("str[%d] = %v\n", i, str[i]);
原型:
func Scan(a ...interface{} )( n int, err error )
将使用空格分割的连续数据顺序存入到参数中(换行也被视为空格)。返回参数的数量n。
var a,b,c int
fmt.Scan(&a,&b,&c)
原型:
func Scanln(a ...interface{} ) ( n int, err error)
同Scan,不同的是Scanln读取到换行符才终止录入。
原型:
func Scanf( format string, a ...interface{} )( n int, err error}
按照格式化字符读取数据。
var a,b,c int
fmt.Scanf("%d,%d %d",&a,&b,&c) //严格按照格式输入才可以读取,如‘1,2 3’
fmt.Print(a)
fmt.Print(b)
fmt.Print(c)
用花括号{}
将多条语句组合在一起。
复合语句中的定义的变量是局部变量,作用域为整个复合语句。
if 表达式1 {
} else if 表达式2 {
} else
注意:if语句块中不允许执行return语句。
switch 条件表达式 {
case 常量1:
语句1
……
default:
语句n
}
或者:
switch{
case 条件表达1:
语句1
……
default:
语句n
}
注意:
1>case语句后面不需要break,执行完自动跳出switch语句。
2>如果想执行完当前case后,继续执行下一个case,在case块最后面添加语句:fallthrough
var op byte = '+';
switch op {
case '+':
fmt.Println(1);
fallthrough
case '-':
fmt.Println(2)
}
只有for循环,没有do和while循环。
注意for语句后面没有括号。
好的编程习惯:
var str string = "hello";
for i := 0; i < len(str); i++ {
fmt.Printf("str[%d] = %v\n", i, str[i]);
}
执行顺序:表达式1,表达式2,表达式4,表达式3
for 表达式1; 表达式2; 表达式3 {
表达式4
}
类似while循环。
for 条件表达式{
}
用break跳出循环。
for{
}
for true{}
for ; ; {}
相当于迭代器,可以对数组(Array)、切片(Slice)、字典(Map)、通道(Channel)等进行遍历。在遍历时会返回一个键值对。
var str string = "hello";
for i, ch := range str { //返回的键值对i,ch i为下标,ch为值。
fmt.Printf("str[%d] = %v\n", i, ch);
}
可以使用占位符过滤掉不需要数据。
for _,ch := range str{} //只需要值不需要键
for i, _ := range str {} //只需要键不需要值
for i := range str {} //只需要键不需要值
与LABEL搭配使用。
var a int = 1;
goto LABEL1
a++
LABEL1:
fmt.Println(a);
包是组成的Go程序的基本单位。每个Go源代码都要进行包声明说明当前文件属于哪个包。
package
可执行Go程序有且仅有一个main包,有且仅有一个main函数(在main包中)。
源码编译后都会生成*.a文件。
分为如下三种模式。导入的包没有使用编译会报错(保证代码体积小,加快编译速度)。
import
举例:
import "fmt" //导入包
fmt.Println(v, OK) //输出:100 true //包内函数调用
包名相近或相同用别名区分。
import 别名
import .
func为关键字。允许多返回值。
func funcName(参数列表)(返回值列表){
return result1, result2
}
main函数没有参数也没有返回值,如果要传入参数,可在os.Args变量中保存。
任意数量:
func main() {
a(1,2,3,4,5);
}
func a(args ... int){
fmt.Println(args) //输出[1 2 3 4 5]
fmt.Println(args[2:]) //输出[3 4 5] 从索引2开始的数字
}
任意类型的变参,类型指定为空接口interface{}:
func main() {
a(1,2,"s",4,5);
}
func a(args ... interface{}){
fmt.Println(args) //输出[1 2 s 4 5]
}
内部函数通过某种方式使其可见范围超出了其定义范围。
在go语言中,闭包可以作为函数对象或匿名函数。确保只要闭包还被使用,那么被闭包引用的变量会一直存在。
通过defer向函数注册退出调用,当主调函数退出时,defer后的函数才会被执行(不管是否出现异常都会被执行)。
defer fmt.Println("defer ")
fmt.Println("main ")
输出:
main defer
for i:=0; i<4; i++{
defer fmt.Println(i)
}
输出:3210
func main() {
fmt.Println("f1 = ",f1())
fmt.Println("f2 = ",f2())
}
//一个延迟执行的函数的变量的值在声明时,值不延迟
func f1() int{
var i int
defer fmt.Println(i)
i = 1
return 1
}
//被延迟的匿名函数会读取f2的返回值,或对f2的返回值赋值
func f2()(i int){
defer func(){
i++
}()
return 1
}
defer语句常用来进行函数的清理、释放资源等工作。
srcFile,err := os.Open("myFile")
defer srcFile.Close
Go没有类的概念,通过结构体来实现面向对象编程。
type date struct {//定义
year int
}
type student struct {
id int
name string
birthday date
}
func main() {
stu := new(student)
stu.name = "a" //访问
stu.id = 0
stu.birthday.year = 5
fmt.Println(stu)
}
stu := student{0,"a",date{1}} //对象初始化
在普通的函数名前增加绑定类型参数。
type student struct {
id int
name string
fee int
}
type teacher struct {
id int
name string
fee int
}
func main() {
stu := student{0,"a",10}
t1 := teacher{0,"a",10}
fmt.Println(stu.getFee())
fmt.Println(t1.getFee())
}
func (revc student) getFee() int{ //rev为变量,student为struct类型。
return 10+100
}
func (revc teacher) getFee() int{ //类似java中的多态。接收类型不同,方法名可以一样
return 10+1000
}
如果指针作为一个receiver,那么是一个引用传递。
type people struct {
id int
name string
fee int
}
type student struct {
people
school string
}
func main() {
stu := student{people{0,"a",10},"ut"}
fmt.Println(stu.getFee())
}
func (revc student) getFee() int{ //重写了方法,隐藏了people的getFee方法
return 10+100
}
func (revc people) getFee() int{
return 10+1000
}
是一组Method的组合,通过Interface来定义对象的一组行为。
type Speaker interface { //关键字:type interface
sayHi()
}
func (s student)sayHi(){
}
func (s student)study(){
}
集合其他接口的功能
type SpeakerLearner interface {
Speaker
study()
}
表示任何数据类型。
interface{}
是format缩写。内置基本包。
Package fmt implements formatted I/O with functions analogous to C’s printf and scanf. The format ‘verbs’ are derived from C’s but are simpler.
Max(int8) Min(int8) //数据的最大值最小值
提供了对字节切片进行读写操作的一系列函数和方法。包括:字节切片处理函数、Buffer对象和Reader对象,类似于strings包。
实现了对数据I/O接口的缓冲功能。
封装于接口io.ReadWriter、io.Reader和io.Writer中,在提供缓冲的同时实现了一些文本的基本I/O操作功能。
UTF-8格式。
标记-清除算法。
GO有两种分配内存机制,分别是使用内置函数new()和make()。
用于给值类型的数据分配内存,调用成功后返回一个初始化的内存块指针,同时该类型被初始化为“0”.
用于给引用类型分配内存空间。
make函数创建的是一个引用类型对象,而不是一个内存空间的指针。
进程、
线程、
协程(用户态线程):不需要OS进行抢占式调度,在真正的实现中寄存于线程。
是Go语言运行库的功能(由Go运行时管理(Runtime)),不是OS提供的功能(不是用线程实现的,所以支持跨平台)。
Goroutine就是一段代码,一个函数入口,以及在堆上为其分配的一个堆栈。因为节省了频繁创建和销毁线程的开销,所以对于进程、线程的开销非常小。可以轻松创建上百万个Goroutine,但它们不是被OS所调度执行的。
Go语言标准库提供的所有系统调用操作(包括同步I/O操作),都会让出处理机给其他Goroutine。
通过关键字go来创建并发执行的Goroutine。
基本格式:go func()
import (
"fmt"
"math/rand"
)
func Test(ch chan int){
ch <- rand.Int() //随机数获取并写入
fmt.Println("Test")
}
func main() {
chs := make([]chan int , 10) //定义一个包含10个Channel的数组chs
for i:=0;i<10;i++{
chs[i] = make(chan int)
go Test(chs[i]) //启动10个Goroutine,对应每一个Goroutin分配一个Channel
}
for _,ch := range chs{
value := <-ch //从通道读取数据
fmt.Println(value)
}
fmt.Println("main") //因为通道的写入和读取都是阻塞的,从而保证了即使没有锁,所有的Goroutin执行完后才main才返回。即:main总是在最后输出的。
}
是Go提供的消息通信机制。主要用于并发通信,类似于单双向数据管道(Pipe),用户可以通过Channel在两个或多个Goroutine之间传递消息。
注意:
Channel是引用类型,一个Channel只能传递一种类型的值,这个类型需要在声明Channel时指定。
支持的类型:基本类型、指针、Array、Slice、Map
var chanName chan ElementType
var ch chan int //一个传递int类型的Channel。chan为关键字。
ch := make(chan int) //用make函数直接声明
ch < - value //通道接收数据。ch表示通道,value表示数据。
value = < - ch //通道发送数据
注意:
3. 向Channel写入数据通常会导致程序阻塞,直到有其他Goroutine从这个Channel中读取数据。
4. 如果Channel中之前没有写入数据,那么从Channel中读取数据也会导致阻塞,直到Channel中被写入数据为止。
throw : all goroutines are asleep-deadlock!
声明时可以将Chanel制定为单向通道:只能接收或只能发送。
var chanName chan <- ElementType //只能接收
var chanName <- chan ElementType //只能发送
给Channel设定一个Buffer值,用于持续传输大量数据。
在Buffer未写满之前,不阻塞发送操作(即使没有读取方,发送方也不断写入数据);
在Buffer未读完之前,不阻塞接收操作。
ch := make(chan int, 1024) //创建一个大小为1024的int类型Channel。
主要用于解决通道通信中的多路复用问题。
select语句(类似switch):
select{
case <- chan1: //如果 chan1 成功读取数据,则进行该case处理语句
case <- chan2:
……
default:
}
select直接检测case语句,每个case语句必须是一个面向Channel的操作.
只要其中一个case完成,程序就会继续向下执行。利用这个特性可以可以为Channe实现超时处理功能。
由于通道的接收是阻塞式的,为了将阻塞式的通信转换为非阻塞式,将select机制和超时机制配合使用,以提高系统通信效率。
在Go语言并发编程的通信过程中,所有错误处理都由超时机制来完成。
超时机制一般用来解决通信死锁,通常设置一个超时参数,通信双方如果在设定的时间内仍然没处理完成任务,则该处理过程会立即被终止,并返回对应的超时信息。
Go没有提供直接的超时处理机制。利用select机制可以实现。
传统的Socket网络编程分为:流式套接字(基于TCP)、数据报套接字(基于UDP)、原始式套接字(基于IP)。
Go语言对socket进行了封装,无论期望用什么协议都只需要调用Dial函数即可。
支持网络协议:tcp、tcp4、tcp6、udp、udp4、udp6、ip、ip4、ip6。
创建连接:
func Dial ( net, addr string) (Conn, error) //net是网络协议名,addr是IP地址或域名,后面可跟随端口号。
conn,err := net.Dial ("tcp", "192.168.0.1:5000")
conn,err := net.Dial ("ip4:icmp", "www.baidu.com")
发送数据:使用conn对象的Write()方法
接收数据:使用conn对象的Read()方法