开发环境搭建 安装Go 打开Go的官网进行下载安装。
在终端输入命令go version
可查看Go的版本,输入go env
可查看Go的环境配置。
可以通过go build
命令编译代码并生成可执行文件。
可以通过go run
命令在终端运行Go文件。
开发工具 下载安装GoLand开发工具
HelloWorld 1 2 3 4 5 6 7 8 9 10 package mainimport "fmt" func main () { fmt.Println("Hello World!" ) }
基础语法 注释 Go语言的注释和C和C++一样,单行注释是//
和多行注释/* */
程序入口 可执行程序需要声明main
包。
main
函数是程序的入口函数。
变量 Go语言通过var关键字来声明变量。
Go语言声明变量的方式
匿名变量 在Go语言中,匿名变量(也称为”空白标识符”)使用_
来表示,用来占位但忽略其值。
匿名变量不会占用内存,也不会引发编译错误,因此在不需要某个返回值或变量时,可以使用匿名变量代替。
短变量声明 在Go语言中可以通过:=
操作符在函数体内声明和初始化变量,它是Go中常用的一种语法糖(语法糖是一种语法优化,程序的功能和逻辑不会因为语法糖而发生改变)。(不能用于全局变量)
:=
可以自动推断变量的类型并简化语法,提高代码简洁性。
1 2 num := 10 name := "Bileton"
类型推导 在生命变量的时候,省略数据类型,编译器可以根据上下文或赋值语句,自动推导出变量或常量的类型而不需要显式的指定类型。
未初始化变量 没有明确初始化的变量声明会被赋予对应类型的 零值 。
数值类型为 0
,
布尔类型为 false
,
字符串为 ""
(空字符串)。
常量 在Go语言中通过const
关键字来声明常量。
1 const NAME string = "Bileton"
iota iota
是go
语言的常量计数器,只能在常量的表达式中使用。
iota
在const
关键字出现时将被重置为0
。
const
中每新增一行常量声明将使iota
计数一次(iota
可理解为const
语句块中的行索引)。
1 2 3 4 5 6 7 8 9 10 11 12 package mainimport "fmt" func main () { const ( A = iota B C ) fmt.Println(A, B, C) }
关键字 type
自定义类型
1 2 3 4 5 6 type Age int func main () { var myAge Age = 25 fmt.Println(myAge) }
类型别名
1 2 3 4 5 6 type AliasInt = int func main () { var x AliasInt = 10 fmt.Println(x) }
定义结构体
定义接口
数据类型 整型 整型分为以下两个大类: 按长度分为:int8
、int16
、int32
、int64
对应的无符号整型:uint8
、uint16
、uint32
、uint64
。
uint8
也就是byte
类型。
rune
是int32
类型
浮点型 Go语言支持两种浮点型数:float32
和float64
。
布尔型 布尔型数据只有true(真)
和false(假)
两个值。
字符串 Go语言中字符串是string
类型,以原生数据类型出现。
Go 语言里的字符串的内部实现使用UTF-8编码。
nil 空
转义字符
转义字符
含义
\r
回车符(返回行首)
\n
换行符(跳转到下一行同列位置)
\t
制表符
\‘
单引号
\“
双引号
\
反斜杠
数值类型之间的转换 形式为
1 2 3 T(value) 其中 T 是目标类型,value 是要转换的值。
运算符 算数运算符
注意: ++
(自增)和--
(自减)在Go语言中是单独的语句,并不是运算符。
逻辑运算符
位运算符
流程控制 if 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 if 表达式{ ... } if 表达式{ ... }else { ... } if 表达式{ ... }else if { ... }else { ... }
switch 1 2 3 4 5 6 7 8 switch var1 { case val1: ... case val2: ... default : ... }
for 基本语法
1 2 3 4 5 6 7 for 初始化语句; 条件表达式; 循环后语句 { } for i:=0 ;i<10 ;i++{ ... }
条件判断的for循环,相当于while
无限循环
for range循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package mainimport "fmt" func main () { nums := []int {10 , 20 , 30 , 40 , 50 } for index, value := range nums { fmt.Printf("Index: %d, Value: %d\n" , index, value) } } >>> Index: 0 , Value: 10 Index: 1 , Value: 20 Index: 2 , Value: 30 Index: 3 , Value: 40 Index: 4 , Value: 50
range range
是一个关键字,常用来迭代数组、切片、映射(map)、字符串和通道(channel)。
1 2 3 4 5 6 7 for index, value := range collection { }
函数 基本语法 1 2 3 4 func 函数名(参数列表) 返回值类型{ 函数体 return 返回值 }
1 2 3 4 func 函数名(参数列表) 返回值列表{ 函数体 return }
可变参数 可以传递可变参数,用...
表示
1 2 3 func myfunc (args ...int ) { ... }
args
是一个[]int
切片,可以遍历和操作。
递归函数 在函数的定义中调用自身的函数称为递归函数。
递归通常有两个主要部分:
定义递归的停止条件,避免无限递归。
在函数内部调用自身,并逐步接近基准情况。
延迟调用 通过defer
关键字实现延迟调用。
延迟调用的特点是:无论函数正常执行还是因为 return
或异常退出,defer
注册的语句都会在函数返回之前执行。
defer
通常用于资源清理、文件关闭、解锁互斥锁等场景。
简单示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport "fmt" func main () { defer fmt.Println("defer 1" ) fmt.Println("2" ) fmt.Println("3" ) } >>> 2 3 defer 1
如果有多个defer
语句,会按照后进先出的顺序执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package mainimport "fmt" func main () { defer fmt.Println("defer 1" ) fmt.Println("2" ) defer fmt.Println("defer 3" ) fmt.Println("4" ) defer fmt.Println("defer 5" ) fmt.Println("6" ) } >>> 2 4 6 defer 5 defer 3 defer 1
函数的数据类型 函数的数据类型描述了它的参数列表和返回值列表,包括参数的数量、顺序以及类型,以及返回值的数量和类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package mainimport "fmt" func func1 () { fmt.Println("This is func1" ) } func func2 (num int ) int { return num } func main () { fmt.Printf("%T\n" , func1) fmt.Printf("%T\n" , func2) } >>> func () func (int ) int
函数可以赋值给变量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package mainimport "fmt" func func1 (num int ) int { return num } func main () { var func2 func (int ) int func2 = func1 fmt.Println(func2(100 )) } >>> 100
匿名函数 基本用法
1 2 3 4 5 6 7 8 9 10 11 12 package mainimport "fmt" func main () { func () { fmt.Println("This is a func" ) }() } >>> This is a func
匿名函数赋值给变量,通过变量调用函数
1 2 3 4 5 6 7 8 9 10 11 12 13 package mainimport "fmt" func main () { func1 := func () { fmt.Println("This is a func" ) } func1() } >>> This is a func
匿名函数的返回值赋值给变量
1 2 3 4 5 6 7 8 9 10 11 12 13 package mainimport "fmt" func main () { ret := func (a, b int ) int { return a * b }(3 , 5 ) fmt.Println(ret) } >>> 15
回调函数 回调函数可以在一个函数中动态调用另一个函数。函数作为参数传递给另一个函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import "fmt" func add (a, b int ) int { return a + b } func operate (a, b int , fun func (int , int ) int ) int { ret := fun(a, b) return ret } func main () { fmt.Println(operate(1 , 2 , add)) } >>> 3
回调函数调用匿名函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package mainimport "fmt" func operate (a, b int , fun func (int , int ) int ) int { ret := fun(a, b) return ret } func main () { add := func (a, b int ) int { return a + b } fmt.Println(operate(1 , 2 , add)) } >>> 3
在回调函数中直接传递匿名函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package mainimport "fmt" func operate (a, b int , fun func (int , int ) int ) int { ret := fun(a, b) return ret } func main () { fmt.Println(operate(1 , 2 , func (a int , b int ) int { return a + b })) } >>> 3
闭包 闭包(Closure)是指一个函数包含了它外部作用域中的变量,即使在外部作用域结束后,这些变量依然可以被内部函数访问和修改。闭包使得函数可以“记住”外部作用域的状态,这种状态在函数调用之间是保持的。
闭包是一个函数与其外部环境变量的组合。
闭包的核心概念是函数内部可以引用外部作用域的变量 ,即使在函数内部外部作用域已经结束。
简单的闭包示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package mainimport "fmt" func test () func () int { counter := 0 return func () int { counter++ return counter } } func main () { func1 := test() fmt.Println("func1:" , func1()) fmt.Println("func1:" , func1()) fmt.Println("func1:" , func1()) func2 := test() fmt.Println("func2:" , func2()) fmt.Println("func2:" , func2()) fmt.Println("func2:" , func2()) fmt.Println("func1:" , func1()) } >>> func1: 1 func1: 2 func1: 3 func2: 1 func2: 2 func2: 3 func1: 4
Go语言内置函数 len 用于返回特定数据类型的长度或大小。
cap 用于获取切片、数组或通道(channel)的容量。
make 为slice、map或chan初始化并返回引用。make仅仅用于创建slice、map和channel,并返回它们的实例。
函数签名
1 2 3 4 func make (t Type, size ...IntegerType) Type
创建slice
append append
用于向切片中添加元素。
append
可以将一个或多个元素添加到现有的切片中,并返回一个新的切片。
如果切片的容量不足以容纳新元素,append
会自动分配一个新的、更大的底层数组,并返回一个新的切片。
1 slice = append (slice, elements...)
容量每次成倍增加。
每次扩容后,切片的地址就会发生改变。
copy 用于从一个切片复制元素到另一个切片中。
1 2 3 4 func copy (dst, src []Type) int
append扩容的底层是通过copy实现的。
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package mainimport "fmt" func main () { var s = []int {1 , 2 , 3 , 4 , 5 } fmt.Println(s) fmt.Printf("%p\n" , s) fmt.Println(len (s)) fmt.Println(cap (s)) s = append (s, 1 , 2 ) fmt.Println(s) fmt.Printf("%p\n" , s) fmt.Println(len (s)) fmt.Println(cap (s)) } >>> [1 2 3 4 5 ] 0xc0000081b0 5 5 [1 2 3 4 5 1 2 ] 0xc0000081b0 7 10
…
...
在 Go 中用于扩展切片,将其元素展开为单独的参数传递给函数。
1 2 3 4 5 6 7 8 9 10 11 12 package mainimport "fmt" func main () { var s = []int {1 , 2 , 3 , 4 , 5 } s = append (s, s...) fmt.Println(s) } >>> [1 2 3 4 5 1 2 3 4 5 ]
delete 用于从 map
中删除指定的键值对。
new
new用于为指定的类型分配内存,并返回指向该类型的指针。
数组 在Go语言中数组是值类型,而不是引用类型。
基本使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package mainimport "fmt" func main () { var arry [5 ]int for i := 0 ; i < 5 ; i++ { arry[i] = i * 100 } fmt.Println(arry) fmt.Printf("%T" , array) fmt.Printf("%p\n" , &arry[0 ]) fmt.Printf("%p\n" , &arry[1 ]) fmt.Printf("%p\n" , &arry[2 ]) fmt.Printf("%p\n" , &arry[3 ]) fmt.Printf("%p\n" , &arry[4 ]) } >>> [0 100 200 300 400 ] [5 ]int 0xc000010330 0xc000010338 0xc000010340 0xc000010348 0xc000010350
数组的初始化
1 2 3 4 5 6 7 8 9 10 11 12 var array [5 ]int array = [5 ]int {1 , 2 , 3 , 4 , 5 } var array = [5 ]int {1 , 2 , 3 , 4 , 5 }array := [5 ]int {1 , 2 , 3 , 4 , 5 } array := [...]int {1 , 2 , 3 , 4 , 5 }
数组的值传递 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package mainimport "fmt" func main () { array1 := [...]int {1 , 2 , 3 , 4 , 5 } fmt.Println("array1:" , array1) fmt.Printf("array1:%p\n" , &array1) array2 := array1 array2[0 ] = 100 fmt.Println("array2:" , array2) fmt.Printf("array2:%p\n" , &array2) fmt.Println("array1:" , array1) fmt.Printf("array1:%p\n" , &array1) } >>> array1: [1 2 3 4 5 ] array1:0xc00012e000 array2: [100 2 3 4 5 ] array2:0xc00012e060 array1: [1 2 3 4 5 ] array1:0xc00012e000
利用函数传递数组参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package mainimport "fmt" func update (array2 [5]int ) { array2[0 ] = 100 fmt.Println("array2:" , array2) fmt.Printf("array2:%p\n" , &array2) } func main () { array1 := [...]int {1 , 2 , 3 , 4 , 5 } fmt.Println("array1:" , array1) fmt.Printf("array1:%p\n" , &array1) update(array1) fmt.Println("array1:" , array1) fmt.Printf("array1:%p\n" , &array1) } >>> array1: [1 2 3 4 5 ] array1:0xc000010330 array2: [100 2 3 4 5 ] array2:0xc000010390 array1: [1 2 3 4 5 ] array1:0xc000010330
多维数组 1 2 3 4 5 6 7 8 9 10 11 12 13 package mainimport "fmt" func main () { var array = [4 ][3 ]int {{1 , 2 , 3 }, {4 , 5 , 6 }, {7 , 8 , 9 }, {10 , 11 , 12 }} fmt.Println(array) fmt.Println(array[0 ][1 ]) } >>> [[1 2 3 ] [4 5 6 ] [7 8 9 ] [10 11 12 ]] 2
切片Slice 在 Go 语言中,切片(slice )是一个动态数组,是对数组的抽象。它比数组更加灵活,因为切片的长度可以动态调整,而数组的长度是固定的。
引用类型 :切片本质是一个对底层数组的引用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport "fmt" func main () { var s []int var a [5 ]int fmt.Printf("Slice:%T\n" , s) fmt.Printf("Slice:%T\n" , a) } >>> Slice:[]int (切片) Slice:[5 ]int (数组)
1 2 3 4 5 6 7 8 9 10 11 package mainimport "fmt" func main () { var s []int = []int {1 , 2 , 3 , 4 , 5 } fmt.Printf("%T" , s) } >>> []int
通过数组创建切片 1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport "fmt" func main () { array := []int {1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 } s1 := array[0 :9 ] fmt.Printf("%T\n" , s1) fmt.Println(s1) } >>> []int [1 2 3 4 5 6 7 8 9 ]
这种方式创建的切片的长度和容量的关系
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package mainimport "fmt" func main () { array := []int {1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 } s1 := array[0 :5 ] fmt.Println(s1) fmt.Println("s1--len:" , len (s1), "cap:" , cap (s1)) s2 := array[1 :5 ] fmt.Println(s2) fmt.Println("s2--len:" , len (s2), "cap:" , cap (s2)) s3 := array[3 :10 ] fmt.Println(s3) fmt.Println("s3--len:" , len (s3), "cap:" , cap (s3)) } >>> [1 2 3 4 5 ] s1--len : 5 cap : 10 [2 3 4 5 ] s2--len : 4 cap : 9 [4 5 6 7 8 9 10 ] s3--len : 7 cap : 7
切片的容量是从切片起始位置到底层数组末尾 的元素个数。
使用make创建切片 1 2 3 4 5 6 7 8 9 10 11 12 13 package mainimport "fmt" func main () { map1 := make ([]map [int ]string , 5 ) fmt.Println(map1) fmt.Printf("%T" , map1) } >>> [map [] map [] map [] map [] map []] []map [int ]string
map 在Go语言中Map是一种无序的键值对结构。
map的声明 1 2 3 4 5 6 7 8 9 10 11 12 package mainimport "fmt" func main () { var map1 map [string ]int fmt.Println(map1) } >>> map []
使用make创建map
1 2 3 4 5 6 7 8 9 10 11 12 13 package mainimport "fmt" func main () { map1 := make (map [int ]string ) fmt.Println(map1) fmt.Printf("%T" , map1) } >>> map []map [int ]string
向map中添加元素
1 2 3 4 5 6 7 8 9 10 11 12 package mainimport "fmt" func main () { var map1 map [string ]int map1 = map [string ]int {"one" : 1 , "two" : 2 , "three" : 3 } fmt.Println(map1) } >>> map [one:1 three:3 two:2 ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package mainimport "fmt" func main () { var map1 map [string ]int map1 = make (map [string ]int ) map1["one" ] = 1 map1["two" ] = 2 map1["three" ] = 3 fmt.Println(map1) } >>> map [one:1 three:3 two:2 ]
遍历map 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package mainimport "fmt" func main () { var map1 map [string ]int map1 = make (map [string ]int ) map1["one" ] = 1 map1["two" ] = 2 map1["three" ] = 3 for key, value := range map1 { fmt.Println(key, value) } } >>> three 3 one 1 two 2
map是无序的,遍历时结果的顺序不一样
map的长度不固定,是引用类型
可以用len查看map的长度,但不能使用cap查看map的容量
map的key可以是所以可以比较的类型。
map结合切片进行使用 1.使用map存储学生信息
2.每个map保存一个学生的信息
3.将这些map存储到切片里
4.打印这些学生的信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package mainimport "fmt" func main () { stu1 := map [string ]string {"name" : "stu1" , "age" : "17" , "gender" : "male" } stu2 := make (map [string ]string ) stu2["name" ] = "stu2" stu2["gender" ] = "female" stu2["age" ] = "18" var stu3 = map [string ]string {"name" : "stu3" , "age" : "17" , "gender" : "male" } s := append (make ([]map [string ]string , 0 ), stu1, stu2, stu3) fmt.Println(s) for _, value := range s { fmt.Println(value) } for _, value := range s { fmt.Print("name\t" , value["name" ], "\t" ) fmt.Print("gender\t" , value["gender" ], "\t" ) fmt.Println("age" , value["age" ]) } } >>> [map [age:17 gender:male name:stu1] map [age:18 gender:female name:stu2] map [age:17 gender:male name:stu3]] map [age:17 gender:male name:stu1]map [age:18 gender:female name:stu2]map [age:17 gender:male name:stu3]name stu1 gender male age 17 name stu2 gender female age 18 name stu3 gender male age 17
指针 指针是存储变量地址的变量。
指针的声明 1 2 3 4 5 6 7 8 9 10 11 12 13 package mainimport "fmt" func main () { var ptr *int fmt.Println(ptr) fmt.Printf("%T" , ptr) } >>> <nil > *int
数组指针 存储数组地址的指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport "fmt" func main () { var arr = [4 ]int {} var ptr = &arr fmt.Printf("%p\n" , &arr) fmt.Printf("%p\n" , ptr) } >>> 0xc0000161c0 0xc0000161c0
通过数组指针来修改数组
1 2 3 4 5 6 7 8 9 10 11 12 13 package mainimport "fmt" func main () { var arr = [4 ]int {1 , 2 , 3 , 4 } var ptr = &arr (*ptr)[0 ] = 5 fmt.Println(*ptr) } >>> [5 2 3 4 ]
数组指针语法糖
1 2 3 4 5 6 7 8 9 10 11 12 13 package mainimport "fmt" func main () { var arr = [4 ]int {1 , 2 , 3 , 4 } var ptr = &arr ptr[0 ] = 5 fmt.Println(*ptr) } >>> [5 2 3 4 ]
指针数组 一个数组,数组里面的元素是指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package mainimport "fmt" func main () { a := 1 b := 2 c := 3 d := 4 arr := [4 ]*int {&a, &b, &c, &d} fmt.Println(arr) *arr[0 ] = 100 fmt.Println(a) } [0xc00010c098 0xc00010c0b0 0xc00010c0b8 0xc00010c0c0 ] 100
指针函数 指针函数是一个函数,返回值是指针。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package mainimport "fmt" func main () { ptr := pointer() fmt.Printf("%p\n" , ptr) fmt.Println(*ptr) fmt.Println(ptr[0 ]) } func pointer () *[4 ]int { arr := [4 ]int {1 , 2 , 3 , 4 } return &arr } >>> 0xc000122040 [1 2 3 4 ] 1
指针作为函数的参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package mainimport "fmt" func add (x *int , y *int , z *int ) { *z = *x + *y } func main () { x := 100 y := 200 var z int add(&x, &y, &z) fmt.Println(z) } >>> 300
type type 关键字用于自定义新的类型或为现有类型创建别名。
自定义新的类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package mainimport "fmt" type Myint int func main () { var myint Myint fmt.Printf("%T\n" , myint) myint = 10 fmt.Printf("%d" , myint) } >>> main.Myint 10
为现有类型创建别名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package mainimport "fmt" type Myint = int func main () { var myint Myint fmt.Printf("%T\n" , myint) myint = 10 fmt.Printf("%d" , myint) } >>> int 10
结构体 结构体定义 结构体是一种自定义数据类型,可以封装多个基本数据类型。
可以通过struct
来定义自己的类型。
1 2 3 4 5 type 类型名 struct { 字段名 字段类型 字段名 字段类型 ... }
示例:Person类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package mainimport "fmt" type Person struct { name string age int gender string } func main () { var person Person person.name = "Bileton" person.age = 21 person.gender = "male" fmt.Println(person) } >>> {Bileton 21 male}
创建对象的方式 这里以Person结构体为例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var person Person person.name = "Bileton" person.age = 21 person.gender = "male" person := Person{} person.name = "Bileton" person.age = 21 person.gender = "male" person :=Person{name: "Bileton" , age: 30 , gender: "male" } person := Person{"Bileton" , 30 , "male" }
结构体指针 结构体是值类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package mainimport "fmt" type Person struct { name string age int gender string } func main () { person := Person{"Bileton" , 30 , "male" } personc := person personc.gender = "female" fmt.Println(person) } >>> {Bileton 30 male}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package mainimport "fmt" type Person struct { name string age int gender string } func main () { person := Person{"Bileton" , 30 , "male" } personc := &person (*personc).gender = "female" fmt.Println(person) } >>> {Bileton 30 female}
结构体指针语法糖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package mainimport "fmt" type Person struct { name string age int gender string } func main () { person := Person{"Bileton" , 30 , "male" } personc := &person personc.gender = "female" fmt.Println(person) } >>> {Bileton 30 female}
匿名结构体 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package mainimport "fmt" func main () { per := struct { name string age int }{ name: "Bileton" , age: 30 , } fmt.Println(per) } >>> {Bileton 30 }
结构体匿名字段 结构体的字段没有名字,但是不能有重复的数据类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package mainimport "fmt" type Person struct { string int } func main () { per := Person{"Bileton" ,21 } fmt.Println(per) } >>> {Bileton 21 }
结构体嵌套 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package mainimport ( "fmt" ) type Person struct { name string age int gender string } type Student struct { Person school string grade string } func main () { stu1 := new (Student) stu1.Person = Person{"JetBrains" , 20 , "M" } stu1.school = "yangguangxiaoyuan" stu1.grade = "2" fmt.Println(*stu1) } >>> {{JetBrains 20 M} yangguangxiaoyuan 2 }
结构体导出 在Go语言中,结构体的导出指的是如何让结构体及其字段在包(package)外部访问。Go 使用 大小写 来决定标识符的可见性:
面向对象 Go语言不是面向对象的语言,它采用了一种“简化版”的面向对象模型,强调组合而非继承,接口而非类型层次。
继承 在Go语言中通过结构体嵌套 以及匿名字段,字段提升来实现继承的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package mainimport ( "fmt" ) type Person struct { name string age int gender string } type Student struct { Person school string grade string } func main () { stu1 := new (Student) stu1.name= "JetBrains" stu1.age = 20 stu1.gender = "M" stu1.school = "yangguangxiaoyuan" stu1.grade = "2" fmt.Println(*stu1) } >>> {{JetBrains 20 M} yangguangxiaoyuan 2 }
方法 在Go语言中定义方法需要有一个额外的接受者,用于指定方法属于哪个类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package mainimport "fmt" type Person struct { Name string Age int } func (p Person) SayHello() { fmt.Printf("Hello, my name is %s and I am %d years old.\n" , p.Name, p.Age) } func main () { p := Person{Name: "Alice" , Age: 25 } p.SayHello() }
方法与函数的区别 函数 :独立存在,不依附于任何类型。
方法 :绑定到某个具体类型,通过类型的实例调用。
方法的继承 通过嵌套结构体,可以复用嵌套结构体的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package mainimport "fmt" type Animal struct {}func (a Animal) Speak() { fmt.Println("I am an animal." ) } type Dog struct { Animal } func main () { d := Dog{} d.Speak() }
方法的重写 结构体嵌套和同名方法覆盖来实现方法的重写。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package mainimport "fmt" type Animal struct {}func (a Animal) Speak() { fmt.Println("I am an animal." ) } type Dog struct { Animal } func (d Dog) Speak() { fmt.Println("I am a dog." ) } func main () { d := Dog{} d.Speak() d.Animal.Speak() }
接口 接口是方法的集合。
接口只定义方法的签名,不实现具体的逻辑。
只要一个类型实现了接口中的所有方法,该类型就自动实现了接口。
在Go语言中,接口是引用类型。
接口示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 package mainimport "fmt" type USB interface { input() string output(data string ) } type Mouse struct { data string } type Keyboard struct { data string } func (m Mouse) input() string { fmt.Print("Mouse input:" ) var data string _, err := fmt.Scanln(&data) if err != nil { return "" } return data } func (m Mouse) output(data string ) { fmt.Println("this is a Mouse" ) fmt.Println(data) } func (k Keyboard) input() string { fmt.Print("Keyboard input:" ) var data string _, err := fmt.Scanln(&data) if err != nil { return "" } return data } func (k Keyboard) output(data string ) { fmt.Println("this is a Keyboard" ) fmt.Println(data) } func main () { mouse := new (Mouse) mouse.data = mouse.input() mouse.output(mouse.data) keyboard := new (Keyboard) keyboard.data = keyboard.input() keyboard.output(keyboard.data) }
多态 在Go语言中,多态是通过接口实现的。
接口可以让不同类型以统一的方式进行操作,从而实现了行为上的多态。
任何实现了接口的类型都可以作为接口类型的值,具体的实现行为由接口变量中存储的具体类型决定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 package mainimport "fmt" type Animal interface { eat() sleep() } type Cat struct { name string } type Dog struct { name string age int } func (cat Cat) eat() { fmt.Println(cat.name, "is eating" ) } func (cat Cat) sleep() { fmt.Println(cat.name, "is sleeping" ) } func (dog Dog) eat() { fmt.Println(dog.name, "is eating" ) } func (dog Dog) sleep() { fmt.Println(dog.name, "is sleeping" ) } func main () { cat := new (Cat) cat.name = "Tom" test(*cat) dog := new (Dog) dog.name = "Jack" test(*dog) } func test (a Animal) { a.eat() a.sleep() } >>> Tom is eating Tom is sleeping Jack is eating Jack is sleeping
空接口 在Go语言中,空接口是一个非常特殊的接口类型,表示可以存储任何类型的值。
空接口没有任何方法,因此所有类型都实现了空接口。
换句话说,任何类型的值都可以赋值给空接口。
any any是空接口的别名,可以用来替代interface{}
。
any
的本质是一个简单的语法糖,表示空接口interface{}
接口类型断言 在 Go 语言中,类型断言(Type Assertion)用于将接口类型的变量转换为具体的类型,从而访问存储在接口中的值。
x
是一个接口类型的变量。
T
是要断言的具体类型。
value
是转换后的具体类型值。
ok
是一个布尔值,表示断言是否成功。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package mainimport "fmt" func main () { var i interface {} = "Hello, Go!" if str, ok := i.(string ); ok { fmt.Printf("The value is a string: %s\n" , str) } else { fmt.Println("The value is not a string" ) } } >>> The value is a string : Hello, Go!
类型断言与switch搭配使用
.(type)
只能出现在switch
语句中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package mainimport "fmt" func checkType (i interface {}) { switch v := i.(type ) { case string : fmt.Printf("The value is a string: %s\n" , v) case int : fmt.Printf("The value is an integer: %d\n" , v) case bool : fmt.Printf("The value is a boolean: %t\n" , v) default : fmt.Printf("Unknown type: %T\n" , v) } } func main () { checkType("Hello, Go!" ) checkType(123 ) checkType(true ) checkType(3.14 ) } >>> The value is a string : Hello, Go! The value is an integer: 123 The value is a boolean: true Unknown type : float64
接口嵌套 在 Go 语言中,接口嵌套是一种将多个接口组合为一个更大的接口的方式。
如果一个类型要实现嵌套接口,必须显式实现嵌套接口中包含的所有方法。
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 package mainimport "fmt" type AA interface { testAA() } type BB interface { testBB() } type CC interface { AA BB testCC() } type Test struct { CC } func (test Test) testAA() { fmt.Println("This is testAA" ) } func (test Test) testBB() { fmt.Println("This is testBB" ) } func (test Test) testCC() { fmt.Println("This is testCC" ) } func main () { test := new (Test) test.testAA() test.testBB() test.testCC() var ttt AA = test ttt.testAA() } >>> This is testAA This is testBB This is testCC This is testAA
嵌套接口中出现同名方法 在 Go 语言中,如果嵌套的多个接口中定义了同名的方法,这种情况下,嵌套接口本身不会报错,但实现嵌套接口的类型需要对该同名方法提供唯一的实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package mainimport "fmt" type Engine interface { Start() } type Transmission interface { Start() } type Vehicle interface { Engine Transmission } type Car struct {}func (c Car) Start() { fmt.Println("Car is starting..." ) } func main () { var v Vehicle = Car{} v.Start() } >>> Car is starting...
error错误 在 Go 语言中,error
是内置接口,专门用于表示和处理错误。
Go 语言提倡将错误作为普通值处理,通过显式返回错误对象来提高代码的安全性和健壮性。
error的定义
1 2 3 type error interface { Error() string }
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package mainimport ( "fmt" "os" ) func main () { file, err := os.Open("test.txt" ) if err != nil { fmt.Println(err) return } fmt.Println(file.Name()) } >>> open test.txt: The system cannot find the file specified.
error示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package mainimport ( "errors" "fmt" ) func main () { age_err := printAge(-1 ) if age_err != nil { fmt.Println(age_err) } } func printAge (age int ) error { if age < 0 { return errors.New("age must be greater than zero" ) } fmt.Println("age:" , age) return nil } >>> age must be greater than zero
自实现error接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 package main import "fmt" type myError struct { code int } func (e *myError) Error() string { return fmt.Sprintf("myError:%d", e.code) } func main() { i, err := test(1) if err != nil { fmt.Println(err) return } fmt.Println(i) } func test(i int) (int, error) { switch i { case 1: return 1, &myError{code: 1} case 2: return 2, &myError{code: 2} case 3: return 3, &myError{code: 3} case 4: return 4, &myError{code: 4} default: return 777, nil } } >>> myError:1
Go语言小练习 简单的+-*/计算器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package mainimport "fmt" func main () { fmt.Println("This is a Calc Application" ) for { var num1, num2 int fmt.Print("Please input your first num:" ) fmt.Scan(&num1) fmt.Print("Please input your second num:" ) fmt.Scan(&num2) fmt.Print("Please input your operation (+, -, *, /, exit):" ) var operation string fmt.Scan(&operation) switch operation { case "+" : fmt.Printf("Result: %d\n" , num1+num2) case "-" : fmt.Printf("Result: %d\n" , num1-num2) case "*" : fmt.Printf("Result: %d\n" , num1*num2) case "/" : if num2 == 0 { fmt.Println("Error: Division by zero is not allowed. Please try again." ) continue } result := float64 (num1) / float64 (num2) fmt.Printf("Result: %.2f\n" , result) default : fmt.Println("Invalid operation. Please enter +, -, *, /, or exit." ) } } }
9*9乘法表 1 2 3 4 5 6 7 8 9 10 11 12 package mainimport "fmt" func main () { for i := 1 ; i < 10 ; i++ { for j := 1 ; j <= i; j++ { fmt.Printf("%d * %d = %d\t" , j, i, i*j) } fmt.Println() } }
打印菱形 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package mainimport "fmt" const CIR = 7 func Prt (xing, blank int ) { for i := 0 ; i < blank; i++ { fmt.Print(" " ) } for j := 0 ; j < xing; j++ { fmt.Print("*" ) } for i := 0 ; i < blank; i++ { fmt.Print(" " ) } fmt.Println() } func main () { var xing = 1 var blank = (CIR - xing) / 2 for i := 1 ; i <= CIR; i++ { Prt(xing, blank) if i < int (CIR/2 )+1 { xing = xing + 2 blank = blank - 1 } else { xing = xing - 2 blank = blank + 1 } } } >>> * *** ***** ******* ***** *** *
递归求和 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package mainimport "fmt" func getSum (n int ) int { if n == 1 { return 1 } return getSum(n-1 ) + n } func main () { fmt.Println(getSum(3 )) }
递归实现斐波那契数列 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package mainimport "fmt" func FBI (n int ) int { if n == 0 { return 0 } else if n == 1 { return 1 } return FBI(n-1 ) + FBI(n-2 ) } func main () { fmt.Println(FBI(4 )) }
冒泡排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package mainimport "fmt" func main () { var array = [10 ]int {23 , 45 , 67 , 12 , 89 , 34 , 56 , 78 , 90 , 11 } for i := 0 ; i < 9 ; i++ { for j := 0 ; j < 9 -i; j++ { if array[j] > array[j+1 ] { array[j], array[j+1 ] = array[j+1 ], array[j] } } } fmt.Println(array) } >>> [11 12 23 34 45 56 67 78 89 90 ]
通过map和slice写一个简单的图书数据库,并可以通过书名或书号来查询书的位置 一个map存放一本书的信息和书的位置
一个切片存放一组书
一个切片存放所有的书
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 package mainimport "fmt" func main () { book1 := map [string ]string {"name" : "wangluo" , "id" : "001" , "position" : "floor1" } book2 := map [string ]string {"name" : "shujuku" , "id" : "002" , "position" : "floor1" } book3 := map [string ]string {"name" : "mimaxue" , "id" : "003" , "position" : "floor2" } book4 := map [string ]string {"name" : "C" , "id" : "004" , "position" : "floor2" } book5 := map [string ]string {"name" : "C++" , "id" : "005" , "position" : "floor3" } book6 := map [string ]string {"name" : "Python" , "id" : "006" , "position" : "floor3" } floor1 := append (make ([]map [string ]string , 0 ), book1, book2) floor2 := append (make ([]map [string ]string , 0 ), book3, book4) floor3 := append (make ([]map [string ]string , 0 ), book5, book6) database := append (make ([][]map [string ]string , 0 ), floor1, floor2, floor3) fmt.Println("Please select your query_style: (name or id)" ) var queryStyle string find_it := 0 fmt.Scan(&queryStyle) if queryStyle == "name" { fmt.Print("input book name:" ) var name string fmt.Scanln(&name) for _, floor := range database { for _, book := range floor { if name == book["name" ] { find_it = 1 fmt.Println("You have the name of book [" + book["name" ] + "]" ) fmt.Print("name\t" , book["name" ], "\t" ) fmt.Print("id\t" , book["id" ], "\t" ) fmt.Print("position\t" , book["position" ], "\n" ) break } } if find_it == 1 { break } } if find_it == 0 { fmt.Println("no this book!" ) } } else if queryStyle == "id" { fmt.Print("input book id:" ) var id string fmt.Scanln(&id) for _, floor := range database { for _, book := range floor { if id == book["id" ] { find_it = 1 fmt.Println("You have the name of book [" + book["name" ] + "]" ) fmt.Print("name\t" , book["name" ], "\t" ) fmt.Print("id\t" , book["id" ], "\t" ) fmt.Print("position\t" , book["position" ], "\n" ) break } } if find_it == 1 { break } } if find_it == 0 { fmt.Println("no this book!" ) } } else { fmt.Println("Yout select is invalid!" ) } }
标准库 fmt 标准库包,用于格式化输入和输出。
输出
输入 获取用户的输入。
Scan
从标准输入扫描文本,读取由空白符分隔的值保存到传递给本函数的参数中,换行符视为空白符。
Scanf
根据format参数指定的格式去读取由空白符分隔的值保存到传递给本函数的参数中。
Scanln
用于按行读取用户输入,并且按空白字符分隔输入数据,在读取到换行符(回车)时停止。
格式化占位符 1 2 3 4 5 6 7 8 %T //数据类型 %t //true或false %b //二进制 %d //十进制 %x //十六进制 %f //浮点数 %s //字符串或[]byte %p //指针