目录

  • 一、去安装和配置
    • 1.1写我的第一个去程序
  • 二、Go 开发利器:VSCode
    • 2.1为什么选择VSCode?
    • 2.2下载安装
    • 2.3将code命令添加到系统PATH中
    • 2.4安装 Go 插件
    • 2.5去开发相关配置
  • 三、基础知识
    • 3.1命名规范
    • 3.2变量
    • 3.3恒定定义
  • 四、基础数据类型
  • 五、高级数据类型
    • 5.1数组
    • 5.2切片
    • 5.3Map
    • 5.4自定义类型
    • 5.5结构体
    • 5.6函数
    • 5.7方法
    • 5.8接口

一、去安装和配置

下载并安装

  • 下载安装对应版本https://golang.org/dl/
  • 查看 go 安装目录/usr/local/go(Windows 下默认安装到c:\Go)
  • 运行go version命令检查是否安装正确

注意:推荐大家使用默认安装方式

项目环境变量
因为是以importgo,故添加了一个IMPORTGOROOT的环境变量进行了所有的代码开发和演示,具体配置如下:

$ vi ~/.profileexport IMPORTGOROOT=$HOME/importgo
export GOPATH=$IMPORTGOROOT # 覆盖 GOPATH 环境变为 importgo
export PATH=$IMPORTGOROOT/bin:$PATH #

当我们配置完毕后,可以执行source ~/.profile更新系统环境变量。

1.1写我的第一个去程序

首先需要在 GOPATH 文件夹下创建一个src目录用于我们举办的源代码。

$ mkdir -p $GOPATH/src

然后我们在 src 目录下面新建hello/hello.go的文件,内容如下:

包主导入 “fmt”func  main () {fmt . Println ( "你好,世界" )
}

我们使用go run hello.go来运行程序,输出为:

hello, world

二、Go 开发利器:VSCode

2.1为什么选择VSCode?

  • 微软官方出品,更新更新快
  • 一个插件:代码跳转,自动格式化,错误检测等

2.2下载安装

  • 登录 vscode 官网:https ://code.visualstudio.com
  • 视情况选择对应包下载

2.3将code命令添加到系统PATH中

效果:在code <filename/foldername>终端输入用vscode打开文件或文件夹

  • 以 mac 为例:在 vscode 中使用动态更新command + shif + p,
  • 输入shell命令,选择Shell Command:Install ‘code’ command in PATH,

如下图:

2.4安装 Go 插件

  • 登录 vscode 官网Extensions模块:https://marketplace.visualstudio.com/VSCode

  • 搜索go插件

  • 安装推荐lukehoban的 go 插件:https://marketplace.visualstudio.com/items?itemName=lukehoban.Go

  • 点击install后就打开vscode界面,进行安装

2.5去开发相关配置

"files.autoSave": "onFocusChange",
"editor.formatOnSave": true,
"go.gopath":"${workspaceRoot}:/Users/jinxue/golib", // 当前工作空间${wordspaceRoot}加上系统 GOPATH 目录
"go.goroot": "/usr/local/Cellar/go/1.9/libexec", // go 的安装目录
"go.formatOnSave": true, //在保存代码时自动格式化代码
"go.formatTool": "goimports", //使用 goimports 工具进行代码格式化,或者使用 goreturns 和 gofmt
"go.buildOnSave": true, //在保存代码时自动编译代码
"go.lintOnSave": true, //在保存代码时自动检查代码可以优化的地方,并给出建议
"go.vetOnSave": false, //在保存代码时自动检查潜在的错误
"go.coverOnSave": false, //在保存代码时执行测试,并显示测试覆盖率
"go.useCodeSnippetsOnFunctionSuggest": true, //使用代码片段作为提示
"go.gocodeAutoBuild": false //代码自动编译构建

三、基础知识

3.1命名规范

去语言中,任何一些(变量,常量,函数,自定义类型等)都应该满足以下的习惯:

  • 的连续字符(Unicode字母| _)或数字( “0” … “9”)组成。
  • 以字符或下划线开头。
  • 不能和去关键字冲突。

去关键字:

中断        默认      func         接口    选择
case          defer         go            map           struct
chan          else          goto          package       switch
const         fallthrough   if            range         type
continue      for           import        return        var

举例说明:

foo  #合法
foo1 #合法
_foo #合法
变量 #合法
变量1 #合法
_变量 合法1foo #不合法
1 #不合法
type #不合法
go #不合法

3.2变量

类型声明基本语法
在 Go 语言中,采用的是后置类型的声明方式,形如:

<命名> <类型>

例如:

x int // 定义 x 为整数类型

这么定义不是为了弥缝,而是为了让声明变得更加清晰易懂,具体可以参考文章gos-declaration-syntax

变量声明
在 Go 语言中通常我们使用关键字var来声明变量,例如

变种 X  INT  //表示声明一个名为X的整数变量
变种 b  INT  =  1  //表示声明一个名为b的整数变量,并且附上初始值为1个
变种 b  =  1

如果有多个变量同时声明,我们可以采用增加局部声明var的方式:

var (a , b  int   // 同时声明 a, b 的突然c  float64
)

简短声明方式
变量在声明的时候,如果有最终值,我们可以使用:=的简短声明方式:

a  :=  1  // 声明 a 为 1 的紧急情况
b  :=  int64 ( 1 )   // 声明 b 为 1 的 64 位紧急

3.3恒定定义

定义是指值不能改变的,它必须满足以下规则:

  • 定义的时候,必须指定值
  • 指定的值类型主要有三类,布尔值,数字,字符串,各种数字类型包含(符文,整数,浮点数,复数),属于基本数据类型。
  • 不能使用 :=

例子:

const  a  =  64  // 定义常量值为 64常量(b  =  4 c  =  0.1
)

四、基础数据类型

Go语言中的基本数据类型包括:

bool   the set of boolean (true, false)uint8   the set of all unsigned  8-bit integers (0 to 255)
uint16    the set of all unsigned 16-bit integers (0 to 65535)
uint32    the set of all unsigned 32-bit integers (0 to 4294967295)
uint64    the set of all unsigned 64-bit integers (0 to 18446744073709551615)int8     the set of all signed  8-bit integers (-128 to 127)
int16     the set of all signed 16-bit integers (-32768 to 32767)
int32     the set of all signed 32-bit integers (-2147483648 to 2147483647)
int64     the set of all signed 64-bit integers (-9223372036854775808 to 9223372036854775807)float32      the set of all IEEE-754 32-bit floating-point numbers
float64   the set of all IEEE-754 64-bit floating-point numberscomplex64      the set of all complex numbers with float32 real and imaginary parts
complex128    the set of all complex numbers with float64 real and imaginary partsbyte    alias for uint8
rune      alias for int32
uint      either 32 or 64 bits
int   same size as uint
uintptr   an unsigned integer large enough to store the uninterpreted bits of a pointer valuestring   the set of string value (eg: "hi")

我们可以将基本类型分为三大类:

  1. 布尔类型
  2. 数字类型
  3. 字符串类型

1.布尔类型
一般我们用于判断条件,它的取值范围为true,声明false如下:

var  a  bool
var  a  =  true
a  :=  trueconst  a  = 真

2.数字类型:
数字类型主要分为有符号数和无符号数,有符号数可以表示负数,除此之外它们还有其他的区别,不同的单独代表它们的实际存储空间,以及取它们的实际存储值的范围。

例如:

var (a  uint8  =  1 b  int8  =  1
)var (c  int  =  64
)

注意:

  1. 过去数字类型资产取值范围,超过了取值范围,出现overflows的错误。
  2. int,u的长度由他们自己的int决定,在32位系统里面,的长度未32位,64位系统,长度为64位。

3.字符串

var  a  =  "hello"  //单行字符串
var  c  =  " \" "  // 转义符var  b  =  `hello`  //原样输出
var  d  =  `
line3 //多行输出line1
line2
`var  str  =  "你好,世界"b  :=  str [ 0 ]   //b 是 uint8 类型,类似于 byte
fmt。Println ( b )   // 104
fmt . Println ( string ( b )) // h.mt . Println ( len ( str )) //12、查看字符串有多少个字节
fmt . Println ( len ([] rune ( str ))) // 8 查看有多少个字符

4.特殊类型

  1. byte,uint8 别名,用于表示二进制数据的字节
  2. rune,int32 别名,用于表示一个符号
var  str  =  "你好,世界"对于 _ , char  :=  range  str {fmt . printf ( "%T" , char )
}

五、高级数据类型

5.1数组

定义:

  1. 由多个相同类型的元素组成的序列
  2. 一组的长度是固定的,声明后无法改变
  3. 数组的长度是数组类型的,例如:元素类型相同但长度不同的两个数组是不同类型的
  4. 需要严格控制程序所使用的内存时间,各有千秋,因为其固定的长度,有内存二次分配操作

示例

包主导入 “fmt”func  main () {// 定义长度为 5 的数组var  arr1 [ 5 ] int for  i  :=  0 ; 我 <  5 ; i ++ {arr1 [ i ] =  i}打印助手(“arr1”,arr1)//以下赋值会报告类型不匹配错误,因为数组长度是数组类型的部分// arr1 = [3]int{1, 2, 3} arr1  = [ 5 ] int { 2 , 3 , 4 , 5 , 6 } // 长度和元素类型相同,可以正确赋值// 简写在定义的同时给出模式,arr2  := [ 5 ] int { 0 , 1 , 2 , 3 , 4 }printHelper ( "arr2" , arr2 )//数组元素类型相同并且数组长度相等的情况下,数组可以进行比较FMT。Println ( arr1  ==  arr2 )// 也可以不显式定义一大串长度,由编译器完成长度计算var  arr3  = [ ... ] int { 0 , 1 , 2 , 3 , 4 }printHelper ( " arr3 " , arr3 )// 定义前四个元素为默认值 0,最终一个元素为 -1 var  arr4  = [ ... ] int { 4 : - 1 }printHelper ( "arr4" , arr4 )// 多维数组var  twoD [ 2 ][ 3 ] int for  i  :=  0 ; 我 <  2 ; i ++ {对于 j  :=  0 ; j  <  3 ; j ++ {twoD [ i ][ j ] =  i  +  j}}.mt . Println ( "twoD:" , twoD )
}func  printHelper ( name  string , arr [ 5 ] int ) {for  i  :=  0 ; 我 <  5 ; 我++ {fmt。printf ( "%v[%v]: %v \n " , name , i , arr [ i ])}// len 获取长度fmt . printf ( "%v 的 len: %v \n " , name , len ( arr ))// cap 也可以获取数组长度fmt . printf ( "%v 的上限: %v \n " , name , cap ( arr )).mt . Println ()
}

5.2切片

切片组成要素:

  1. 指针:指向底层数组
  2. 长度:切片中元素的长度,不能大于容量
  3. 容量:指针所指向的底层数组的总容量

常见初始化方式

  1. 使用 make 初始化
slice := make([]int, 5)     // 初始化长度和容量都为 5 的切片
slice := make([]int, 5, 10) // 初始化长度为 5, 容量为 10 的切片
  1. 使用简短定义
slice := []int{1, 2, 3, 4, 5}
  1. 使用数组来初始化切片
arr := [5]int{1, 2, 3, 4, 5}
slice := arr[0:3] // 左闭右开区间,最终切片为 [1,2,3]
  1. 使用切片来初始化切片
sliceA := []int{1, 2, 3, 4, 5}
sliceB := sliceA[0:3] // 左闭右开区间,sliceB 最终为 [1,2,3]

长度和容量

package mainimport ("fmt"
)func main() {slice := []int{1, 2, 3, 4, 5}fmt.Println("len: ", len(slice))fmt.Println("cap: ", cap(slice))//改变切片长度slice = append(slice, 6)fmt.Println("after append operation: ")fmt.Println("len: ", len(slice))fmt.Println("cap: ", cap(slice)) //注意,底层数组容量不够时,会重新分配数组空间,通常为两倍
}

以上代码,预期输出如下:

len:  5
cap:  5
after append operation:
len:  6
cap:  12

注意点

  1. 多个切片共享一个底层数组的情况

对底层数组的修改,将影响上层多个切片的值

package mainimport ("fmt"
)func main() {slice := []int{1, 2, 3, 4, 5}newSlice := slice[0:3]fmt.Println("before modifying underlying array:")fmt.Println("slice: ", slice)fmt.Println("newSlice: ", newSlice)fmt.Println()newSlice[0] = 6fmt.Println("after modifying underlying array:")fmt.Println("slice: ", slice)fmt.Println("newSlice: ", newSlice)
}

以上代码预期输出如下:

before modify underlying array:
slice:  [1 2 3 4 5]
newSlice:  [1 2 3]after modify underlying array:
slice:  [6 2 3 4 5]
newSlice:  [6 2 3]
  1. 使用 copy 方法可以避免共享同一个底层数组

示例代码如下:

package mainimport ("fmt"
)func main() {slice := []int{1, 2, 3, 4, 5}newSlice := make([]int, len(slice))copy(newSlice, slice)fmt.Println("before modifying underlying array:")fmt.Println("slice: ", slice)fmt.Println("newSlice: ", newSlice)fmt.Println()newSlice[0] = 6fmt.Println("after modifying underlying array:")fmt.Println("slice: ", slice)fmt.Println("newSlice: ", newSlice)
}

以上代码预期输出如下:

before modifying underlying array:
slice:  [1 2 3 4 5]
newSlice:  [1 2 3 4 5]after modifying underlying array:
slice:  [1 2 3 4 5]
newSlice:  [6 2 3 4 5]

小练习
如何使用 copy 函数进行切片部分拷贝?

// 假设切片 slice 如下:
slice := []int{1, 2, 3, 4, 5}// 如何使用 copy 创建切片 newSlice, 该切片值为 [2, 3, 4]
newSlice = copy(?,?)

5.3Map

在 Go 语言里面,map 一种无序的键值对, 它是数据结构 hash 表的一种实现方式,类似 Python 中的字典。

语法
使用关键字 map 来声明形如:

map[KeyType]ValueType

注意点:

  • 必须指定 key, value 的类型,插入的纪录类型必须匹配。
  • key 具有唯一性,插入纪录的 key 不能重复。
  • KeyType 可以为基础数据类型(例如 bool, 数字类型,字符串), 不能为数组,切片,map,它的取值必须是能够使用 ==进行比较。
  • ValueType 可以为任意类型。
  • 无序性。
  • 线程不安全, 一个 goroutine 在对 map 进行写的时候,另外的 goroutine 不能进行读和写操作,Go 1.6版本以后会抛出 runtime 错误信息。

声明和初始化

  1. 使用 var 声明
var cMap map[string]int  // 只定义, 此时 cMap 为 nil
fmt.Println(cMap == nil)
cMap["北京"] = 1  // 报错,因为 cMap 为 nil
  1. 使用 make
cMap := make(map[string]int)
cMap["北京"] = 1// 指定初始容量
cMap = make(map[string]int, 100)
cMap["北京"] = 1

说明:在使用 make 初始化 map 的时候,可以指定初始容量,这在能预估 map key 数量的情况下,减少动态分配的次数,从而提升性能。

  1. 简短声明方式
cMap := map[string]int{"北京": 1}

map 基本操作

cMap := map[string]int{}cMap["北京"] = 1 //写code := cMap["北京"] // 读
fmt.Println(code)code = cMap["广州"]  // 读不存在 key
fmt.Println(code)code, ok = cMap["广州"]  // 检查 key 是否存在
if ok {fmt.Println(code)
} else {fmt.Println("key not exist")
}delete(cMap, "北京") // 删除 key
fmt.Println("北京")

循环和无序性

cMap := map[string]int{"北京": 1, "上海": 2, "广州": 3, "深圳": 4}for city, code := range cMap {fmt.Printf("%s:%d", city, code)fmt.Println()
}

线程不安全

cMap := make(map[string]int)var wg sync.WaitGroup
wg.Add(2)go func() {cMap["北京"] = 1wg.Done()
}()go func() {cMap["上海"] = 2wg.Done()
}()wg.Wait()

在 Go 1.6 之后的版本,多次运行此段代码,你将遇到这样的错误信息:

fatal error: concurrent map writesgoroutine x [running]:
runtime.throw(0x10c64b6, 0x15)
.....

解决之道:

  • 对读写操作加锁
  • 使用 security map, 例如 sync.map

map 嵌套

provinces := make(map[string]map[string]int)provinces["北京"] = map[string]int{"东城区": 1,"西城区": 2,"朝阳区": 3,"海淀区": 4,
}fmt.Println(provinces["北京"])

5.4自定义类型

前面我们已经学习了不少基础和高级数据类型,在 Go 语言里面,我们还可以通过自定义类型来表示一些特殊的数据结构和业务逻辑。

使用关键字 type 来声明:

 type NAME TYPE

声明语法

  1. 单次声明
type City string
  1. 批量声明
type (B0 = int8B1 = int16B2 = int32B3 = int64
)type (A0 int8A1 int16A2 int32A3 int64
)

简单示例

package mainimport "fmt"type City stringfunc main() {city := City("上海")fmt.Println(city)
}

基本操作

package mainimport "fmt"type City string
type Age intfunc main() {city := City("北京")fmt.Println("I live in", city + " 上海")  //  字符串拼接fmt.Println(len(city))  // len 方法middle := Age(12)if middle >= 12 {fmt.Println("Middle is bigger than 12")}
}

总结: 自定义类型的原始类型的所有操作同样适用。

函数参数

package mainimport "fmt"type Age intfunc main() {middle := Age(12)printAge(middle)
}func printAge(age int) {fmt.Println("Age is", age)
}

当我们运行代码的时候会出现 ./main.go:11:10: cannot use middle (type Age) as type int in argument to printAge 的错误。

因为 printAge 方法期望的是 int 类型,但是我们传入的参数是 Age,他们虽然具有相同的值,但为不同的类型。

我们可以采用显式的类型转换( printAge(int(primary)))来修复。

不同自定义类型间的操作

package mainimport "fmt"type Age int
type Height intfunc main() {age := Age(12)height := Height(175)fmt.Println(height / age)
}

当我们运行代码会出现 ./main.go:12:21: invalid operation: height / age (mismatched types Height and Age) 错误,修复方法使用显式转换:

fmt.Println(int(height) / int(age))

5.5结构体

数组、切片和 Map 可以用来表示同一种数据类型的集合,但是当我们要表示不同数据类型的集合时就需要用到结构体。

结构体是由零个或多个任意类型的值聚合成的实体

关键字 typestruct 用来定义结构体:

type StructName struct{FieldName type
}

简单示例:定义一个学生结构体

package mainimport "fmt"type Student struct {Age     intName    string
}func main() {stu := Student{Age:     18,Name:    "name",}fmt.Println(stu)// 在赋值的时候,字段名可以忽略fmt.Println(Student{20, "new name"})return
}

通常结构体中一个字段占一行,但是类型相同的字段,也可以放在同一行,例如:

type Student struct{Age           intName, Address string
}

一个结构体中的字段名是唯一的,例如一下代码,出现了两个 Name 字段,是错误的:

type Student struct{Name stringName string
}

结构体中的字段如果是小写字母开头,那么其他 package 就无法直接使用该字段,例如:

// 在包 pk1 中定义 Student 结构体
package pk1
type Student struct{Age  intname string
}
// 在另外一个包 pk2 中调用 Student 结构体
package pk2func main(){stu := Student{}stu.Age = 18        //正确stu.name = "name"  // 错误,因为`name` 字段为小写字母开头,不对外暴露
}

结构体中可以内嵌结构体
但是需要注意的是:如果嵌入的结构体是本身,那么只能用指针。请看以下例子。

package mainimport "fmt"type Tree struct {value       intleft, right *Tree
}func main() {tree := Tree{value: 1,left: &Tree{value: 1,left:  nil,right: nil,},right: &Tree{value: 2,left:  nil,right: nil,},}fmt.Printf(">>> %#v\n", tree)
}

结构体是可以比较的
前提是结构体中的字段类型是可以比较的

package mainimport "fmt"type Tree struct {value       intleft, right *Tree
}func main() {tree1 := Tree{value: 2,}tree2 := Tree{value: 1,}fmt.Printf(">>> %#v\n", tree1 == tree2)
}

结构体内嵌匿名成员
声明一个成员对应的数据类型而不指名成员的名字;这类成员就叫匿名成员

package mainimport "fmt"type Person struct {Age  intName string
}type Student struct {Person
}func main() {per := Person{Age:  18,Name: "name",}stu := Student{Person: per}fmt.Println("stu.Age: ", stu.Age)fmt.Println("stu.Name: ", stu.Name)
}

5.6函数

函数是语句序列的集合,能够将一个大的工作分解为小的任务,对外隐藏了实现细节

函数组成:

  • 函数名
  • 参数列表(parameter-list)
  • 返回值(result-list)
  • 函数体(body)
func name(parameter-list) (result-list){body
}
  1. 单返回值函数
func plus(a, b int) (res int){return a + b
}
  1. 多返回值函数
func multi()(string, int){return "name", 18
}
  1. 命名返回值
// 被命名的返回参数的值为该类型的默认零值
// 该例子中 name 默认初始化为空字符串,height 默认初始化为 0
func namedReturnValue()(name string, height int){name = "xiaoming"height = 180return
}
  1. 参数可变函数
func sum(nums ...int)int{fmt.Println("len of nums is : ", len(nums))res := 0for _, v := range nums{res += v}return res
}func main(){fmt.Println(sum(1))fmt.Println(sum(1,2))fmt.Println(sum(1,2,3))
}
  1. 匿名函数
func main(){func(name string){fmt.Println(name)}("禾木课堂")
}
  1. 闭包
func main() {addOne := addInt(1)fmt.Println(addOne())fmt.Println(addOne())fmt.Println(addOne())addTwo := addInt(2)fmt.Println(addTwo())fmt.Println(addTwo())fmt.Println(addTwo())
}func addInt(n int) func() int {i := 0return func() int {i += nreturn i}
}
  1. 函数作为参数
func sayHello(name string) {fmt.Println("Hello ", name)
}func logger(f func(string), name string) {fmt.Println("start calling method sayHello")f(name)fmt.Println("end calling method sayHellog")
}func main() {logger(sayHello, "禾木课堂")
}
  1. 传值和传引用
func sendValue(name string) {name = "hemuketang"
}func sendAddress(name *string) {*name = "hemuketang"
}func main() {// 传值和传引用str := "禾木课堂"fmt.Println("before calling sendValue, str : ", str)sendValue(str)fmt.Println("after calling sendValue, str : ", str)fmt.Println("before calling sendAddress, str : ", str)sendAddress(&str)fmt.Println("after calling sendAddress, str: ", str)
}

5.7方法

方法主要源于 OOP 语言,在传统面向对象语言中 (例如 C++), 我们会用一个“类”来封装属于自己的数据和函数,这些类的函数就叫做方法。

虽然 Go 不是经典意义上的面向对象语言,但是我们可以在一些接收者(自定义类型,结构体)上定义函数,同理这些接收者的函数在 Go 里面也叫做方法。

声明

方法(method)的声明和函数很相似, 只不过它必须指定接收者:

func (t T) F() {}

注意:

  • 接收者的类型只能为用关键字 type 定义的类型,例如自定义类型,结构体。
  • 同一个接收者的方法名不能重复 (没有重载),如果是结构体,方法名还不能和字段名重复。
  • 值作为接收者无法修改其值,如果有更改需求,需要使用指针类型。

简单例子

package maintype T struct{}func (t T) F()  {}func main() {t := T{}t.F()
}

接收者类型不是任意类型
例如:

package mainfunc (t int64) F()  {}func main() {t := int64(10)t.F()
}

当运行以下代码会得到 cannot define new methods on non-local type int64 类似错误信息,我们可以使用自定义类型来解决:

package maintype T int64
func (t T) F()  {}func main() {t := T(10)t.F()
}

小结:接收者不是任意类型,它只能为用关键字 type 定义的类型(例如自定义类型,结构体)。

命名冲突
a. 接收者定义的方法名不能重复, 例如:

package maintype T struct{}func (T) F()         {}
func (T) F(a string) {}func main() {t := T{}t.F()
}

运行代码我们会得到 method redeclared: T.F 类似错误。

b. 结构体方法名不能和字段重复,例如:

package maintype T struct{F string
}func (T) F(){}func main() {t := T{}t.F()
}

运行代码我们会得到 : type T has both field and method named F 类似错误。

小结: 同一个接收者的方法名不能重复 (没有重载);如果是结构体,方法名不能和字段重复。

接收者可以同时为值和指针
在 Go 语言中,方法的接收者可以同时为值或者指针,例如:

package maintype T struct{}func (T) F()  {}
func (*T) N() {}func main() {t := T{}t.F()t.N()t1 := &T{} // 指针类型t1.F()t1.N()
}

可以看到无论值类型 T 还是指针类型 &T 都可以同时访问 FN 方法。

值和指针作为接收者的区别
同样我们先看一段代码:

package mainimport "fmt"type T struct {value int
}func (m T) StayTheSame() {m.value = 3
}func (m *T) Update() {m.value = 3
}func main() {m := T{0}fmt.Println(m) // {0}m.StayTheSame()fmt.Println(m) // {0}m.Update()fmt.Println(m) // {3}
}

运行代码输出结果为:

{0}
{0}
{3}

小结:值作为接收者(T) 不会修改结构体值,而指针 *T 可以修改。

5.8接口

接口类型是一种抽象类型,是方法的集合,其他类型实现了这些方法就是实现了这个接口。

/* 定义接口 */
type interface_name interface {method_name1 [return_type]method_name2 [return_type]method_name3 [return_type]...method_namen [return_type]
}

简单示例:打印不同几何图形的面积和周长

package mainimport ("fmt""math"
)type geometry interface {area() float32perim() float32
}type rect struct {len, wid float32
}func (r rect) area() float32 {return r.len * r.wid
}func (r rect) perim() float32 {return 2 * (r.len + r.wid)
}type circle struct {radius float32
}func (c circle) area() float32 {return math.Pi * c.radius * c.radius
}func (c circle) perim() float32 {return 2 * math.Pi * c.radius
}func show(name string, param interface{}) {switch param.(type) {case geometry:// 类型断言fmt.Printf("area of %v is %v \n", name, param.(geometry).area())fmt.Printf("perim of %v is %v \n", name, param.(geometry).perim())default:fmt.Println("wrong type!")}
}func main() {rec := rect{len: 1,wid: 2,}show("rect", rec)cir := circle{radius: 1,}show("circle", cir)show("test", "test param")
}

接口中可以内嵌接口
对上述例子做以下修改:

  • 首先添加 tmp 接口,该接口定义了 area() 方法
  • tmp 作为 geometry 接口中的匿名成员,并且将 geometry 接口中原本定义的 area() 方法删除

完成以上两步后,geometry 接口将会拥有 tmp 接口所定义的所有方法。运行结果和上述例子相同。

type tmp interface{area() float32
}type geometry interface {// area() float32tmpperim() float32
}

看到这一定是真爱,更多编程精彩文章可以关注我看我主页。

记录零基础GO编程入门笔记之一相关推荐

  1. Java零基础并发编程入门

    Java零基础并发编程入门 并发编程主要包括: 线程,同步,future,锁,fork/join, volatile,信号量,cas(原子性,可见性,顺序一致性),临界性,分布式 了解基础: JMM: ...

  2. coursera python证书_Coursera证书|三天零基础Python编程入门

    推荐推荐推荐‼️‼️密歇根大学在Coursera开设的编程课程. . 哈喽,大家好,这里是卡缪儿.准备秋招时,发现学会编程能够增加自己的asset,而诸如Python.SQL等对于数据分析方面的岗位来 ...

  3. 《零基础D编程入门》

    第一篇      基本概述 ------------------------------------------- "在我看来,大部分'新的'编程语言都可以归结到下面两类:一类来自于有着革新 ...

  4. 《零基础D编程入门三》

    第三章:win系统下安装D 3.1 要求及下载 3.2 文件 3.3 安装 3.4 示列 3.5 连接 3.6 环境变量 3.7 sc.ini 初始化文件 3.8 常见的安装问题 3.9 Window ...

  5. 《零基础掌握 Python 入门到实战》笔记

    Python 零基础掌握 Python 入门到实战笔记 文章目录 Python 内置对象类型 基本交互语句 常用内置函数 整数与浮点数 基本数学运算 高级数学运算 字符串 序列 索引 切片 成员函数 ...

  6. 零基础编程入门python视频-Python编程零基础小白快速入门完整全系列精品课

    1. 课程咨询加老师助理微信:助理1微信: chenjinglei88 ,助理2微信: omf6757 2. 决定购买并想得到陈敬雷老师亲自指导(课程或自己项目难题均可)加老师微信: chenjing ...

  7. 码匠编程:零基础从前端入门到前端开发工程师路线

    一.前端学习路径规划 下面是一个学习线路图,其实还有很多的技术导图中没有涉及到,对于初学者来说,能够掌握上边的知识点已经很不错了. 零基础从前端入门到前端开发工程师路线 二.入坑前的三问 想要入前端的 ...

  8. 零基础学编程,如何快速入门?

    零基础学编程,很多人都会有这样一个误区,结果导致自己很快就放弃了. 这误区,就是在你还没学或者刚学了一点,感觉没看懂,于是判定编程真的太难了. 其实,技术学不会,主要的原因还是没有掌握好学习方法.现在 ...

  9. 电脑编程自学(零基础自学编程怎么入门)

    电脑编程自学入手:确定编程学习的方向.编程语言有多种:php,C++,C,C#,JAVA,Python等,每种语言都有不同的优缺点,可以根据自己的兴趣方向选择一门编程语言作为自己的学习目标. 基础阶段 ...

最新文章

  1. Linux磁盘分区管理(转载)
  2. 关于dotNet加密工具
  3. CTFshow 命令执行 web63
  4. centos6.8 如何编译php,centos 6.8 安装编译php7.1.2
  5. 数据可视化组队学习:《Task04 - 文字图例尽眉目》笔记
  6. UVA 1613 K-Graph Oddity K度图着色 (构造)
  7. CyclicBarrier-同步辅助类
  8. Linux经常使用命令(八) - touch
  9. dom元素滚动条高度 js_js浏览器滚动条卷去的高度scrolltop(实例讲解)
  10. 在线最大公因数计算器
  11. 仅当使用了列的列表,并且 IDENTITY_INSERT 为 ON 时,才能在表中为标识列指定显式值问题...
  12. 客户端(浏览器端)数据存储技术概览
  13. 信息系统运行维护服务方案(IT运维服务方案)
  14. win10 软路由_N合1服务器!NAS、软路由、高清盒子、Web一个都不能少!
  15. searchview怎么改hint大小_老司机偷偷告诉你:改水电一般多少钱?水电改造价格怎么算?...
  16. 使用vim修改只读文件
  17. hdu5820 Lights
  18. openCV实践项目:银行卡卡号识别
  19. 程序员转行可以做什么?
  20. Java判断字符串中是否包含中英文标点符号

热门文章

  1. 图神经网络从入门到入门
  2. GNN综述:从deepwalk到GraphSAGE,GCN,GAT
  3. 开发者必备!Github 上 1.6W 星的「黑魔法」,早知道就不会秃头了
  4. 神了!7行代码建起360亿的支付帝国
  5. 听声辨位过时了!这个AI系统仅凭光回声就能得到3D图像
  6. 不需要借助GPU的力量,用树莓派也能实时训练agent玩Atari
  7. 人工智能顶级会议ICLR取消线下会议:远程出席、视频演讲
  8. 爬一爬 iPhone 11为何嘴上说真丑,销量却真香?
  9. 漫画:五分钟学会贪心算法!
  10. KNN学习之图像分类与KNN原理