golang进修笔记(一)——golang基本和相干数据结构

小白前端一枚,最近在研讨golang,纪录本身进修过程当中的一些笔记,以及本身的明白。

  • go中包的依靠治理
  • go中的切片
  • byte 和 string
  • go中的Map
  • go中的struct构造体
  • go中的要领
  • go中的interface接口
  • interface{}

原文在我的博客中:https://github.com/forthealll…

迎接star~

1、go中包的依靠治理

起首要相识的是GOPATH的寄义,GOPATH是go敕令依靠的重要变量,能够经由过程:

go env

来检察响应的开辟环境中GOPATH的值,也能够经由过程export GOPATH指定:

export GOPATH = /usr/local/go

指定GOPATH目次后, GOPATH目次包括了3个子目次:

  • src寄存源代码(比方.go、.h文件等)
  • pkg编译时天生的中心文件
  • bin编译后天生的可实行文件

另外,go的依靠治理中供应了3个重要的敕令go build、go get和 go install。

  • go build: 会编译目次下或许指定的.go文件,获得一个可实行的文件
  • go install: 只能在GOPATH目次下运用,与go build大抵雷同会编译目次下实行的.go文件,另外go install还会将可实行文件或库文件装置到GOPATH目次下。
  • go install + 长途地点: 会将响应的代码下载到GOPATH同时会编译该长途包
  • go get + 长途地点: 跟go install+长途地点雷同,会下载且编译
  • go get u + 长途地点: 下载并更新响应的长途地点的包,但不会自动编译

典范的例子,比方下载一个dep包:

go get -u github.com/golang/dep/cmd/dep

上述的go get和go install + 长途包的体式格局,不能应用于须要版本治理依靠等场景,能够经由过程装置dep包,来完成依靠治理。dep供应了几个常常使用的敕令,离别用于装置和更新响应的go包。

  • dep init 初始化一个项目依靠
  • dep ensure 装置项目所依靠的一切包
  • dep ensure -update 更新项目中的一切包
  • dep ensure -add github.com/pkg/errors 为项目增加单个依靠包

另外经由过程Gopkg.toml内里能够指定所依靠包的git分支,版本号等等,且在dep ensure -add中也能够指定分支和版本号,比方:

dep ensure -add github.com/pkg/foo@^1.0.1

提到包(package),必需补充一句,在go中假如在其他包中援用变量,是经由过程:

包名.变量名

的情势,在这里变量名必需是大写的,也就是说在go的包中,变量可否导出是依据变量的大小写来肯定的,普遍认为假如变量是大写的就是在包内导出的,假如是变量小写的就是默许是包的私有变量。

2、go中的切片

在go的函数挪用中,假如通报的参数是一个较大的数组,明显假如直接将数组作为实参传入,在实行函数的过程当中,现实上会拷贝一份该数组,会形成内存的糟蹋等。规范的做法,是传入数组的指针,或许关于数组的部份援用。

这里关于数组的部份援用,就是slice切片

(1)、go中的切片简介

数组和切片之间存在着严密的联络,slice供应了接见数组子序列的功用。所谓的切片是关于数组的部份援用,slice由三部份构成指针、长度和容量。

  • 指针: 指向第一个slice元素所对应的数组元素的地点
  • 长度: slice中元素的数量
  • 容量: slice中最多可包容元素的数量

切片的定义体式格局:

var slice1 []type = make([]type, len, cap)

离别指定切片的范例,长度以及容量。

切片的初始化:

s := [] int { 1,2,3 }

或许经由过程已存在的数组来完成切片的初始化,

arr = [10]int {1,2,3,4,5,6,7,8,9,10}
s:=arr[1:5] // arr[startIndex:endIndex]

(2)、go中的切片注重点

go中的slice切片有一个注重点,就是怎样推断切片为空,边境状况大抵以下所示:

var s []int   //len(s)==0,s==nil
s = nil       //len(s)==0,s==nil
s = []int(nil)//len(s)==0,s==nil
s = []int{}   //len(s)==0,s!=nil

明显假如经由过程s==nil来推断,不能区分第四种场景,因而推断切片为空的准确体式格局是len(s)==0.

3、byte 和 string

下述的要领将返回一个byte的切片:

var test:= []byte("hello")

go遍历slice动态删除 map遍历删除平安.

4、go中的Map

map是一个无序的key/value对的鸠合,个中在每个map中key是唯一的。go中的map只需坑在于map是无序的。

(1)、Map简介

声明一个map:

var ages map[string]int  //一样初始的状况下,ages = nil
ages == nil // true

假如声清楚明了然则没有赋值,那末尝试插进去一对key/value会报错,比方上述声明但没有初始化的状况下:

age["jony"] = 25 // 会panic

解决要领,就是给age定义后赋值:

ages = make(map[string]int)

或许定义的时刻同时赋值:

ages := map[string]int{

}

今后插进去不存在的key/value就不会报错。

注重:尝试从map中去一个不存在的key,默许的value值为0

(2)、Map无序性

我们从map的遍历效果,来申明map是无序的。比方我们以这么一个map为例:

var ages = map[string]int{
"a":21,
"b":22,
"c":23,
};
for name,age := range ages {
  fmt.Printf("%s\t%d\n",name,age);
}

经由过程for range能够遍历map对象,离别实行三次遍历后,来看遍历的效果

  • 第一次输出:

    c 23
    a 21
    b 22

  • 第二次输出:
    c 23
    b 22
    a 21
  • 第三次输出:
    a 21
    b 22
    c 23

从上述的效果我们也能够看出map的每次遍历的效果都是不肯定的。

注重:Map的value范例不仅仅能够是基础范例,也能够是聚合范例,比方map或许slice。

5 、go中的struct构造体

跟C++中的构造体相似,go中的构造体是一种聚合数据范例,由0个或许多个恣意值聚合成实体。

(1)、构造体简介

声明一个构造体很简朴,比方我们声清楚明了一个Person构造体:

type Person struct {
   name string
   age int
   salary int
}

然后能够声明一个Person范例的变量:

var person Person

然后能够经由过程点操作符接见和赋值。

person.age = 25

另外,能够经由过程取地点标记加点操作符来接见和赋值,下述取地点的体式格局效果与上述是雷同的。

(&person).age = 25

另外,构造体也支撑嵌套。

6、go中的要领

在go中没有明白的定义类,然则能够将构造体struct来类比其他言语中的class。

go中的要领与构造体相干,为了说名go中的要领,我们先从go中的函数讲起。

(1)、go中的函数简介

在go中函数声明包括函数名、形参列表、返回值列表(可省略 不傲视无返回值)以及函数体。

func name (parameter-list)(result-list){


}

比方我们有一个count函数能够云云简朴的定义:

func count(x,y int) int {
   return x + y
}

(2)、go中要领简介

在函数定义的基础上我们来引见一下,怎样定义要领。在函数声明时,在函数名前放上一个变量,这个变量称为要领的接收器,平常是构造体范例的。

固然也不一定是构造体,基础范例数值、字符串、slice和map上面都能够作为接收器来定义要领。

声明要领的体式格局详细能够以下所示:

func (receive Receive) name(parameter-list)(result-list){


}

从上述的声明中也能够看出来只不过在函数的技术上增加了第一个参数接收器,为响应的接收器增加了该称号的要领。比方我们定一个Person构造体,并为其声明sellHello要领:

type Person struct {
   name string
   age int
   salary int
}

func (person Person) sayHello() string{
  return "Hello "+ person.name
}

p := Person{
   name: "Jony",
   age: 25,
   salary:100
}

fmt.Println(p.sayHello());//输出Hello Jony

上述就是在构造体Person上定义了一个sayHello要领,在构造体被初始化后,能够经由过程p.sayHello()的体式格局直接挪用。

除此之外,我们前面将到定义要领时的接收器不一定是一个构造体,接收器也能够接收基础范例等,比方:

type Mystring string;

func (mystring Mystring)sayHello() string{
  return "Hello"+ string(mystring);
}

var m Mystring
m = "Jony"
fmt.Println(m.sayHello());

上述的例子一样会输出Hello Jony.

以至nil也能够作为要领的接收器,这里就不详细举例。

(3)、基于指针对象的要领

在函数挪用时,是对实参的一个拷贝,假如函数须要更新一个变量,或许通报的参数过大,默许拷贝太为担任,我们常常会运用指针的情势,关于要领而言也一样云云,也就是说要领的接收器能够是指针范例。

对照于上述非指针范例的要领,声明指针范例的要领详细以下所示:

func (receive *Receive) name(parameter-list)(result-list){


}

指针范例的参数作为接收器,能够修正传入参数的现实变量的值。

type Person struct {
  name string
  age int
  salary int
}
func (person *Person) changeAge(newAge int){
  (*person).age = newAge
}
p.changeAge(30);
fmt.Println(p.age); //输出了30,发明age确切发作了转变。

7、go中的interface接口

我们前面也说过go不是一种传统的面向对象的言语,没有类和继续的观点,go内里经由过程interface接口能够完成许多面向对象的特征。

接口的浅显定义:

接口供应了一种体式格局来申明对象的行动,接口定义了一组要领,然则不包括完成。

(1)、interface接口简介

能够经由过程以下花样来定义接口:

type Namer interface {
   Method1(param_list) return_type
   Method2(param_list) return_type
   ...
}

go中的接口都很简短,平常包括了0-3个要领。

同时我们能够经由过程:

var ai Namer 

来定义一个接口范例的变量,初始值为nil.接口范例的变量是一个指针,声明而未赋值的状况下就为nil。

go中的接口有以下须要注重的点:

  • 一个范例能够完成多个接口
  • 接口范例能够包括一个实例的援用,该实例的范例完成了此接口
  • 纵然接口在范例以后定义,两者存在差别的包中,被零丁编译,然则只需范例完成了接口中的要领,它就完成了此接口
  • 完成某个接口的范例,除了完成接口要领外,还能够有其他的要领

上述几点都比较好明白,详细第二点,举例来说:

type Person struct {
 name string
 age int
 salary int
}
type Say interface {
  sayHello() string
}
func (person Person) sayHello() string {
  return "Hello "+person.name
}

func main() {
  p := new(Person)
  p.name = "Jony"

  var s Say;
  s = p;
  fmt.Println(s)
}

上述例子中,我们起首new了一个Person构造体范例的变量,并赋值给p,由于Person接口体中完成了Say接口中的一切要领sayHello等。因而我们就说Person完成了Say接口,因而Person的实例p,能够赋值给一个Say接口范例的变量s。

此时的s是个指针,指向Person构造体实例p。

(2)、interface接口范例断言

任何范例只需完成了接口中的一切要领,我们就说该范例完成了该接口。如许一个接口范例的变量varI能够包括任何范例的值,在go中供应了一种平安的体式格局来检测它的动态范例。

if v,ok := varI.(T);ok {
   Process(v)
   return
}

假如转化正当,那末v是varI转化到范例T的值,ok会是true,不然v是范例T的零值,ok是false。这是一种平安的转化体式格局不会有毛病发作。

我们照样接着上面的代码来说我们的例子:

type Person struct {
 name string
 age int
 salary int
}

type Say interface {
  sayHello() string
}

func (person Person) sayHello() string {
  return "Hello "+person.name
}

func main() {
  p := new(Person)
  p.name = "Jony"

  var s Say;
  s = p;
  if t,ok := s.(*Person);ok {
    fmt.Printf("The type of s is:%T\n",t);
  }
}

输出的效果为The type of s is:*main.Person。也能够运用特别的type-switch来推断。

switch t:= s.(*Person){
   case *Person:
      fmt.Printf("The type of s is:%T\n",t);
   case nil:
      ...
   default:
      ...
}

8、interface{}

interface{}是一个空接口,任何范例的值都能够复制给interface{}范例的变量。

比方,我们起首声明一个范例为interface{}的变量:

var test interface{}

恣意范例的值都能够复制给test,比方以下基础范例的值复制给test是有用的:

var test interface{}
test = 1
test = true
test ="Hello"

另外,庞杂的派生范例也能够赋值给test,我们以指针范例举例:

var test interface{}
var a = 1
test = &a 

interface范例的变量是没有范例的,然则我们能够工资的举行范例转换:

var test interface{}
var a string
test = "hello"
a = test.(string)

上述,能够将test转化成string范例,如许就能够赋值给string范例变量a了。经由过程.(范例名)的要领能够将interface{}范例的变量转化成恣意的范例。

末了举一个简朴的例子:

func main() {
   a := make([]interface{},10)
   b :=1
   a[1]=&b
   fmt.Println(*(a[1].(*int)))
}

上述代码发明,将interface{}范例切片中的某一元素的值复制给了int指针范例,然后举行了范例转化,将interface{}范例的变量转换成了int指针范例。

    原文作者:yuxiaoliang
    原文地址: https://segmentfault.com/a/1190000017520961
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞