许世伟的Go语言基础 第二章总结 - Go语言中文社区

许世伟的Go语言基础 第二章总结


第2章 顺序编程

2.1 变量

变量声明

变量声明的类型信息放在变量之后,放在数组的中括号之后,作返回值类型放在方法名之后。

不需要使用分号作为结束符。

//字串和哈希表都是内置类型,都小写

varv1int

varv2string

varv3[10]int

varv4[]int//数组切片

v4=make([]string,10)

varv5stuct{

fint

}

varv6*int

varv7map[string]int//map,key为string类型,value为int类型

varv8func(aint)int

Go语言是个静态类型语言,但以上写法看上去却像动态语言。

var只用来声明,直接不值不用写var。

varv1=1//编译器报错

go语言变量赋值不使用也报错。

变量初始化

注意类型推演符左侧的就是不应该是已被声明过的。

varv1int=10

varv2=10//不指明类型也可以变量推演

varv3:=10//使用类型推演符:=

==总结==:没声明的必须类型推演,只声明没指定类型的可以类型推演,已声明类型的不能类型推演。

不用类型推演:=,用变量必须声明var,可以不写类型。

交换2个变量的值直接用多重赋值

i,j=j,i

匿名变量

可以使用多重返回和匿名变量的写法,使代码更优雅。

funcmain() {

    _,_,nickName:=GetName()

}

//多个同样类型的变量只声明一次就可以

funcGetName() (firstName,lastName,nickNamestring)  {

    return"May","Chan","Maruko"

}

字面常量

Go语言的字面常量是无类型的,只要值在某类型的值域范围内,就可以赋值(不用再写类型转换了,简化代码)。如-12可以赋给int、unit、int32、float64等。

-12

3.14159//浮点型常量

3.2+12i//复数常量

true//布尔型常量

"foo"//字串常量

常量定义

常量也可以限定类型,但不是必须的。

constzerofloat64=3.14159

constzero=3.14159// 无类型常量

const{

zeroint64=0

zero=0//无类型常量

}

==常量赋值是编译期行为==,常量定义的右侧值也可以是一个编译期运行的常量表达式。

constmask=1<<3

//os.GetEnv("HOME")是运行期返回结果,所以下行代码编译错误

constHome=os.GetEnv("HOME")

预定义常量

go语言有3个预定义常量:true、false、iota。

每const关键字出现时,iota被重置为0,每出现一次iota其值增1。

const{

c0=iota

c1=iota

c2=iota

}

两个const赋值表达式是一样的,则可以省略后一个(==方便之处在于枚举型赋值,但可能带来意外赋值==)。这种写法多用于枚举型。

const{

a=1<

b//b == 2

c//c == 4

}

枚举

go语言并不支持enum关键字。枚举使用const后跟一对==圆括号==定义一组常量。

const(

Sunday=iota

Monay

TuesDay

numberOfDays//这个常量没有导出

)

go语言中大写字母的常量在包外可见,相当于访问限制符。

2.3 类型

go语言内置以下基础类型(7个)

bool

byte int unit int8 int16 uint uintptr等。(整形一般用int unit就可以了,以免移植困难)

float32 float64

complex64 complex128

string

字符类型rune

错误类型error

go语言的复合类型(7个)

指针pointer

数组array

切片slice

字典map

通道chan

结构体struct

接口interface

布尔型

varv1bool=true

布尔型不接受其它类型的赋值,也不支持强制类型转换。

整形

int和int32是两种不同的类型,编译器不会自动转换。

value1:=64//自动推导为

varvalue2int32

value2=value1//编译器报错

go语言不会隐式的类型转换,不同类型的整型不能比较和赋值,如int8和int。但可以直接与字面值常量比较(无类型)。(没有隐式类型转换的烦恼)

不同类型赋值要强制类型转换

value2=int32(value1)//强制转换用静态方法

variint32

varjint64

i,j=1,2

ifi==j{}//编译器报错

ifi==1||j==2{}//编译通过

==取反运算符是^x==,这与C语言不同。

浮点型

varfvalue1

fvalue1:=12.0//加小数点,推导为浮点型。

float32等价于C语言中的float型,float64等价于C语言中的double型(浮点型自动推演的的类型)。

用==比较浮点型不稳定,使用以下方案:

import"math"

funcIsEqual(f1,f2,pfloat64){

returnmath.Fdim(f1,f2)

}

复数

复数表示

varvaluecomplex64

value1=3.2+12i

value2:=complex(3.2,12)

real(value1)//取实部

imag(value1)//取虚部

字串

varstrstring

str="hello"

ch:=str[0]

fmt.Printf("the length of "%s" is %d n",str,len(str))

fmt.Printf("the first character of "%s" is %c"str,ch)

字串不能在初始化后修改

str:="hello"

str[0]"X"//编译错误

字串编码转换go只支持utf-8和unicode。其它可用iconv库转换。

字串操作:

x+y//字串连接

len(s)//取字串长度

s[i]//取字符

//两种字串遍历方式

str:="hello"

n:=len(str)

fori,ch:=rangestr{

fmt.Println(i,ch)

}

fori:=0;i

ch:=str[i]

fmt.Println(i,ch)//可以直接打印两个字符

}

字符

两种字符类型,byte表示UTF-8字串的单个字节值,rune代表单个Unicode字符。

数组

数组声明方法:

[32]byte

[2*N]struct{x,yint}//复杂类型数组

[1000]*float指针类型数组

[3][5]int//二维数组

[2][2][2]float64//等同于[2]([2]([2]float64))

varfVar[2][2]float64=[2][2]float64{{1,2},{1,2}}

数组可以是多维的,一旦定义长度后就不能修改。

go语言是值类型,在赋值和参数传递时会进行一次复制动作。因而在函数体中无法修改数组的内容,函数内操作的只是所传入数组的一个副本。

数组切片

数组在长度定义后无法修改,数组是值类型,这些无法满足开发者需求。

数组切片的数据结构可以抽象为以下3个变量:(类似于ArrayList)

指向原生数组的指针

数组切片的元素个数

数组切片已分配的存储空间

vararray1[10]int=[10]int{1,2,3,4,5,6,7,8,9,10}

varslice1[]int=array1[:5]//使用数组的片段创建切片

slice2=make([]int,5)//创建元素个数为5的数组切片

slice3=make([]int,10)//创建元素个数5的数组切片,预留10个存储空间

slice4:=[]int{1,2,3,4,5}//直接创建5个元素的数组切片

len(slice1)

cap(slice1)//返回容量大小

这里书中有误:==make方法返回值==赋变量 ,无需类型推演。

==让编译器自行统计元素个数==:

b := [...]string{"Penn", "Teller"}

切片能够区分元素个数和容量。为避免扩容开销,容量由开发人员指定。

slice1:=append(slice1,8,9,10)//向切片末尾添加元素

slice1:=append(slice1,slice2)//添加切片

使用切片创建切片:

newSlice:=oldSlice[:3]//选择范围超过原切片,则补上0

切片复制:

slice1:=[]int{1,2,3,4,5}

slice2:=[]int(5,4,3)

copy(slice2,slice1)//只复制slice1的前3个元素到slice2中

copy(slice1,slice2)//复制slice2的元素到slice1的前3个位置

切片复制的原则是==不改变原切片长度==。

map

varmap1map[int]string

map1=make(map[int]string)

map1[1]="jack"

map1[2]="bill"

str,ok=map1[1]//查找键为1的数据

map的多返回值判断是否查找成功,使代码更简洁。

map初始化时可以设置容量:

map1=make(map[int]string,100)

添加元素如同元素赋值。

删除元素:

delete(map1,1)

struct(引自博客)

//结构体初始化

b := person{

       name: "wyff",

       age:  300,

   }

2.4 流程控制

选择语句支持switch、case和select。循环语句支持for和rage。跳转语句支持goto。

if语句要求

不需要括号()包条件

无论几条语句,{}是必须的

左花括号必须与if/else在同一行

有返回值函数中,不允许将"最终"return放在if...else中。

if语句也可以在条件表达式前加赋值语句。

switch选择语句

    switchi{

    case0:

        fmt.Print("0")

    case1:

        fmt.Print("1")

    case2:

        fmt.Print("2")

    default:

        fmt.Print("default")

    }

switch后无表达式的写法:

    varNum

    switch{

    case0

        fmt.Print("0-3")

    case4

        fmt.Print("4-6")

    }

//case不再是匹配项,而是表达式。

要求:

左花括号必须与switch处于同一行

条件表达式==不限制不整形或常量==,也可以不写

单个case可以出现多结果选项??

==不需要break来明确退出一个case==,只有加fallthrough关键字才继续执行下一个case。

switch后无表达式,与if...else逻辑等同。

循环语句

无限循还直接写,而不需要for(;;)和do{}while(1)

for{


}

条件表达式支持多重赋值:

foti,j=0,len(arr);i

a[i],a[j]=a[j],a[i]

}

支持continue break循环控制,还可以决定跳出哪个循还。

forj:=0;j<5;j++{

fori:=0;i<5;i++{

ifi>3{

breakJLoop

   }

fmt.

  }

}

JLoop://循环标签

==注意循还标签有冒号==。Java使用bool标志来实现。

跳转语句

HERE:

ifi<10{

gotoHERE

}

2.5 函数

相邻的类型相同的参数,可以只写一个类型声明

函数的返回值的变量需要命名,只有一个可以不命名

funcAdd(a,bint)(resint,errerror){

err=errors.New("have errors")

return

}

forAdd(a,bint)int{}//单个返回值

func(a*Integer)Add(i,jint)int{returnx+y}//类方法的写法,是否加*后面讲

函数和变量的小写仅在包内可见,大写才能被其它包使用。相当于访问限制符。

多返回值

匿名函数和闭包

匿名函数可以直接赋值给一个变量或直接执行。

f:=func(x,yint)int{

returnx+y

}

f:=func(x,yint)int{

returnx+y

}(1,2)//后面带参数表示直接执行

匿名函数是一个闭包。

闭包定义:包含自由变量(未绑定到对象)的代码块。要执行的代码块为自由变量提供绑定的计算环境(作用域)。

闭包的价值:用作函数对象或匿名函数,==支持闭包的语言将函数存储到变量作为参数传递给其它函数==。

闭包是函数式编程中很重要的概念。

funcmain() {

    varjint=5

    a:=func() (func()){

        variint=10

        returnfunc() {fmt.Printf("i,j:%d,%d",i,j)}

    }()

    a()

    j*=2

    a()

}

//闭包内的i值被隔离,不会被修改。

//fmt.Print()用法如java,fmt.Printf()用法如C。

错误处理

typeerrorinterface{

Error()string

}

将error作为多返回值的最后一个,用于函数的返回错误。

错误处理:

iferr!=nil{}

defer关键字

提前写关闭文件、连接的语句。java用finally,C++用包一个类在析构函数中关闭。

funcCopyFile(dst,srcstring) (wint64,errerror) {

    srcFile,err:=os.Open(src)

    iferr!=nil{

        return

    }

    defersrcFile.Close()

    dstFile,err:=os.Create(dst)

    iferr!=nil{

        return

    }

    deferdstFile.Close()

    returnio.Copy(dstFile,srcFile)

}

panic()和recover()方法

错误处理流程:

当一个函数调用panic()函数时,正常的函数执行流程立即终止。

但使用defer关键字推迟执行语句正常展开执行。

函数返回到调用函数,逐层执行panic流程,直至所属的goroutine中所有执行的函数被终止。

报告错误信息,包括panic()函数传入的参数。

panic()的形参接收任意类型的数据。

recover()函数终止错误处理流程:如果没有在没有发生异常的goroutine中明确地调用恢复过程(使用recover()),则goroutine所属的进程==打印异常信息==后直接退出。

版权声明:本文来源简书,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://www.jianshu.com/p/1f95730fa951
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-01-12 12:58:13
  • 阅读 ( 801 )
  • 分类:Go

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢