Go基础学习笔记(一):基础语法、内建容器、面向对象


3)变量定义要点回顾


* 变量类型写在变量名之后
* 编译器可推测变量类型
* 没有char,只有rune
* 原生支持复数类型


##### 4)、条件语句


1)if


if的条件里不需要括号



const filename = "abc.txt"
contents, err := ioutil.ReadFile(filename)
if err != nil {
	fmt.Println(err)
} else {
	fmt.Printf("%s\n", contents)
}


const filename = "abc.txt"
if contents, err := ioutil.ReadFile(filename); err != nil {
	fmt.Println(err)
} else {
	fmt.Printf("%s\n", contents)
}

* if的条件里可以赋值
* if的条件里赋值的变量作用域就在这个if语句里


2)switch



func grade(score int) string {
g := “”
switch {
case score < 0 || score > 100:
panic(fmt.Sprintf(“Wrong score:%d”, score))
case score < 60:
g = “D”
case score < 80:
g = “C”
case score < 90:
g = “B”
case score <= 100:
g = “A”
}
return g
}


* switch会自动break,除非使用fallthrough
* switch后可以没有表达式


##### 5)、循环


1)for



func convertToBinary(n int) string {
result := “”
for ; n > 0; n /= 2 {
lsb := n % 2
result = strconv.Itoa(lsb) + result
}
return result
}



func printFile(filename string) {
file, err := os.Open(filename)
if err != nil {
panic(err)
}
scanner := bufio.NewScanner(file)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}


* for的条件里不需要括号
* for的条件里可以省略初始条件,结束条件,递增表达式


无限循环:



func forever() {
for {
fmt.Println(“abc”)
}
}


2)基本语法要点回顾


* for,if后面的条件没有括号
* if条件里也可定义变量
* 没有while
* switch不需要break,也可以直接switch多个条件


##### 6)、函数


1)函数



func eval(a, b int, op string) int {
switch op {
case “+”:
return a + b
case “-”:
return a - b
case “*”:
return a * b
case “/”:
return a / b
default:
panic(“unsupported operation:” + op)
}
}


函数可返回多个值



// 13/3=4…1
func div(a, b int) (int, int) {
return a / b, a % b
}



func div(a, b int) (q, r int) {
q = a / b
r = a % b
return
}


* 函数返回多个值时可以起名字
* 仅用于非常简单的函数
* 对于调用者而言没有区别


函数作为参数



func apply(op func(int, int) int, a, b int) int {
p := reflect.ValueOf(op).Pointer()
opName := runtime.FuncForPC§.Name()
fmt.Printf(“Calling function %s with args (%d,%d)\n”, opName, a, b)
return op(a, b)
}

func main() {
fmt.Println(apply(func(a, b int) int {
return int(math.Pow(float64(a), float64(b)))
}, 3, 4)) // Calling function main.main.func1 with args (3,4)
}


可变参数列表



func sum(numbers …int) int {
s := 0
for i := range numbers {
s += numbers[i]
}
return s
}


2)函数语法要点回顾


* 返回值类型写在最后面
* 可返回多个值
* 函数作为参数
* 没有默认参数,可选参数


##### 7)、指针


1)指针


指针不能运算


2)参数传递


Go语言只有值传递一种方式


值传递:


![](https://img-blog.csdnimg.cn/2eb39b44121d42989f5be57ce2746687.png#pic_center)
指针传递:


![](https://img-blog.csdnimg.cn/bfe7afc7b582410693b80531793e2fd4.png#pic_center)
obj:


![](https://img-blog.csdnimg.cn/a3fb00536b4f4c689bb51ddf59e65d30.png#pic_center)
cache中pData是指向data的指针,值传递时只是cache里的pData指针被拷贝了一份,但还是指向同一个data



func swap(a, b int) {
b, a = a, b
}

func main() {
a, b := 3, 4
swap(a, b)
fmt.Println(a, b) // 3 4
}



func swap(a, b *int) {
*b, *a = *a, *b
}

func main() {
a, b := 3, 4
swap(&a, &b)
fmt.Println(a, b) // 4 3
}


#### 2、内建容器


##### 1)、数组


1)数组的定义



var arr1 [5]int
arr2 := [3]int{1, 3, 5}
arr3 := [...]int{2, 4, 6, 8, 10}
fmt.Println(arr1, arr2, arr3)

var grid [4][5]int
fmt.Println(grid)

数量写在类型前面


2)数组的遍历



numbers := [6]int{1, 2, 3, 4, 5, 6}
for i := 0; i < len(numbers); i++ {
	fmt.Println(numbers[i])
}


numbers := [6]int{1, 2, 3, 4, 5, 6}
for i, v := range numbers {
	fmt.Println(i, v)
}


numbers := [6]int{1, 2, 3, 4, 5, 6}
for \_, v := range numbers {
	fmt.Println(v)
}

* 可通过\_省略变量
* 不仅range,任何地方都可通过\_省略变量
* 如果只要i,可写成`for i := range numbers`


3)数组是值类型



func printArray(arr [5]int) {
arr[0] = 100
for i, v := range arr {
fmt.Println(i, v)
}
}

func main() {
var arr [5]int
printArray(arr)
fmt.Println(arr) // [0 0 0 0 0]
}


* [10]int和[20]int是不同类型
* 调用`func f(arr [10]int)`会拷贝数组
* 在Go语言中一般不直接使用数组



func printArray(arr *[5]int) {
arr[0] = 100
for i, v := range arr {
fmt.Println(i, v)
}
}

func main() {
var arr [5]int
printArray(&arr)
fmt.Println(arr) // [100 0 0 0 0]
}


##### 2)、切片的概念


1)Slice(切片)



func updateSlice(s []int) {
s[0] = 100
}

func main() {
arr := […]int{0, 1, 2, 3, 4, 5, 6, 7}
fmt.Println(arr[2:6]) // [2 3 4 5]
fmt.Println(arr[:6]) // [0 1 2 3 4 5]
fmt.Println(arr[2:]) // [2 3 4 5 6 7]
s := arr[:]
fmt.Println(s) // [0 1 2 3 4 5 6 7]
updateSlice(s)
fmt.Println(s) // [100 1 2 3 4 5 6 7]
}


slice本身没有数据,是对底层array的一个view


2)Slice的扩展



arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
s1 := arr[2:6]
s2 := s1[3:5]
fmt.Println(s1) // [2 3 4 5]
fmt.Println(s2) // [5 6]

![](https://img-blog.csdnimg.cn/d3c88fa049bd47bb8d9ab0ea77d3c7ec.png#pic_center)
Slice的实现:


![](https://img-blog.csdnimg.cn/771d475b761843b49c9c865d6923e0d0.png#pic_center)
* slice可以向后扩展,不可以向前扩展
* s[i]不可以超越len(s),向后扩展不可以超越底层数组cap(s)



arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
s1 := arr[2:6]
s2 := s1[3:5]
fmt.Printf("s1=%v, len(s1)=%d, cap(s1)=%d\n", s1, len(s1), cap(s1)) // s1=[2 3 4 5], len(s1)=4, cap(s1)=6
fmt.Printf("s2=%v, len(s2)=%d, cap(s2)=%d\n", s2, len(s2), cap(s2)) // s2=[5 6], len(s2)=2, cap(s2)=3

##### 3)、切片的操作


1)向Slice添加元素



arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
s1 := arr[2:6]
s2 := s1[3:5]
fmt.Printf("s1=%v, len(s1)=%d, cap(s1)=%d\n", s1, len(s1), cap(s1)) // s1=[2 3 4 5], len(s1)=4, cap(s1)=6
fmt.Printf("s2=%v, len(s2)=%d, cap(s2)=%d\n", s2, len(s2), cap(s2)) // s2=[5 6], len(s2)=2, cap(s2)=3
s3 := append(s2, 10)
s4 := append(s3, 11)
s5 := append(s4, 12)
fmt.Println(s3) // [5 6 10]
fmt.Println(s4) // [5 6 10 11]
fmt.Println(s5) // [5 6 10 11 12] s4 and s5 no longer view arr

* 添加元素时如果超过cap,系统会重新分配更大的底层数组
* 由于值传递的关系,必须接收append的返回值


2)Slice其他操作



func printSlice(s []int) {
fmt.Printf(“len=%d, cap=%d\n”, len(s), cap(s))
}

func main() {
// creating slice
var s []int // zero value for slice is nil
for i := 0; i < 100; i++ {
printSlice(s)
s = append(s, 2*i+1)
}
fmt.Println(s)

s1 := []int{2, 4, 6, 8}
printSlice(s1)

s2 := make([]int, 16)
printSlice(s2)

s3 := make([]int, 16, 32)
printSlice(s3) // len=16, cap=32

// copying slice
copy(s2, s1)
fmt.Println(s2) // [2 4 6 8 0 0 0 0 0 0 0 0 0 0 0 0]

// deleting elements from slice
s2 = append(s2[:3], s2[4:]...)
fmt.Println(s2) // [2 4 6 0 0 0 0 0 0 0 0 0 0 0 0]

// popping from front
front := s2[0]
s2 = s2[1:]
// popping from back
tail := s2[len(s2)-1]
s2 = s2[:len(s2)-1]
fmt.Println(front) // 2
fmt.Println(tail)  // 0
fmt.Println(s2)    // [4 6 0 0 0 0 0 0 0 0 0 0 0]

}


##### 4)、 Map



m := map[string]string{
	"one":   "java",
	"two":   "go",
	"three": "python",
}
m2 := make(map[string]int)
var m3 map[string]int
fmt.Println(m, m2, m3)

// traversing map
for k, v := range m {
	fmt.Println(k, v)
}

// getting values
one := m["one"]
fmt.Println(one)
if one2, ok := m["one"]; ok {
	fmt.Println(one2)
}

// deleting values
delete(m, "three")
fmt.Println(m)

1)Map的操作


* 创建:`make(map[string]int)`
* 获取元素:`m[key]`
* key不存在时,获得value类型的初始值
* 用`value, ok := m[key]`来判断是否存在key
* 用delete删除一个key


2)Map的遍历


* 使用range遍历key,或者遍历key value对
* 不保证遍历顺序,如需顺序,需手动对key排序
* 使用len获得元素个数


3)map的key


* map使用哈希表,必须可以比较相等
* 除了slice、map、function的内建类型都可以作为key
* struct类型不包含上述字段,也可以作为key


##### 5)、Map例题


例:[最长不含重复字符的子字符串](https://bbs.csdn.net/topics/618545628)


对于每一个字母x


* lastOccurred[x]不存在,或者<start -> 无需操作
* lastOccurred[x]>=start -> 更新start
* 更新lastOccurred[x],更新maxLength



func lengthOfLongestSubstring(s string) int {
lastOccurred := make(map[byte]int)
start, maxLength := 0, 0
for i, ch := range []byte(s) {
lastI, ok := lastOccurred[ch]
if ok && lastI >= start {
start = lastOccurred[ch] + 1
}
if i-start+1 > maxLength {
maxLength = i - start + 1
}
lastOccurred[ch] = i
}
return maxLength
}


##### 6)、字符和字符串处理


1)rune相当于go的char



s := "你好世界!"
fmt.Println(len(s))    // 15
for i, ch := range s { // ch is a rune
	fmt.Printf("(%d %X)", i, ch) // (0 4F60)(3 597D)(6 4E16)(9 754C)(12 FF01)
}
fmt.Println()

fmt.Println(utf8.RuneCountInString(s)) // 5

bytes := []byte(s)
for len(bytes) > 0 {
	ch, size := utf8.DecodeRune(bytes)
	bytes = bytes[size:]
	fmt.Printf("%c", ch)
} // 你好世界!
fmt.Println()

for i, ch := range []rune(s) {
	fmt.Printf("(%d %c)", i, ch)
} // (0 你)(1 好)(2 世)(3 界)(4 !)
fmt.Println()

* 使用range遍历pos rune对
* 使用`utf8.RuneCountInString`获得字符数量
* 使用len获得字节长度
* 使用[]byte获得字节


[最长不含重复字符的子字符串](https://bbs.csdn.net/topics/618545628)支持中文



func lengthOfLongestSubstring(s string) int {
lastOccurred := make(map[rune]int)
start, maxLength := 0, 0
for i, ch := range []rune(s) {
lastI, ok := lastOccurred[ch]
if ok && lastI >= start {
start = lastOccurred[ch] + 1
}
if i-start+1 > maxLength {
maxLength = i - start + 1
}
lastOccurred[ch] = i
}
return maxLength
}


2)其他字符串操作


* Fields,Split,Join
* Contains,Index
* ToLower,ToUpper
* Trim,TrimRight,TrimLeft


#### 3、面向对象


##### 1)、结构体和方法


1)面向对象


* Go语言仅支持封装,不支持继承和多态
* Go语言没有class,只有struct


2)结构的创建



type treeNode struct {
value int
left, right *treeNode
}

func createNode(value int) *treeNode {
return &treeNode{value: value}
}

func main() {
root := treeNode{value: 3}
root.left = &treeNode{}
root.right = &treeNode{5, nil, nil}
root.right.left = new(treeNode)
root.left.right = createNode(2)
}


* 使用自定义工厂函数
* 注意返回了局部变量的地址


3)为结构定义方法



package main

import “fmt”

type treeNode struct {
value int
left, right *treeNode
}

func (node treeNode) print() {
fmt.Print(node.value, " ")
}

func (node treeNode) setValue(value int) {
node.value = value
}

func (node *treeNode) setValue2(value int) {
node.value = value
}

// 中序遍历
func (node *treeNode) traverse() {
if node == nil {
return
}
node.left.traverse()
node.print()
node.right.traverse()
}

func createNode(value int) *treeNode {
return &treeNode{value: value}
}

func main() {
root := treeNode{value: 3}
root.left = &treeNode{}
root.right = &treeNode{5, nil, nil}
root.right.left = new(treeNode)
root.left.right = createNode(2)
root.print() // 3
fmt.Println()

root.right.left.setValue(4)
root.right.left.print() // 0
root.right.left.setValue2(4)
root.right.left.print() // 4
fmt.Println()

root.traverse() // 0 2 3 4 5

}


* 显示定义和命名方法接收者
* 只有使用指针才可以改变结构内容
* nil指针也可以调用方法


4)值接收者 vs 指针接收者


* 要改变内容必须使用指针接收者
* 结构过大也考虑使用指针接收者
* 一致性:如有指针接收者,最好都是指针接收者
* 值接收者是Go语言特有
* 值/指针接收者均可接收值/指针


##### 2)、包和封装


1)封装


* 名字一般使用CamelCase
* 首字母大写:public
* 首字母小写:private


2)包


* 每个目录一个包
* main包包含可执行入口
* 为结构定义的方法必须放在同一个包内,可以是不同文件



go mod init tree



package node

import “fmt”

type TreeNode struct {
Value int
Left, Right *TreeNode
}

func (node TreeNode) Print() {
fmt.Print(node.Value, " ")
}

func (node TreeNode) SetValue(value int) {
node.Value = value
}

func (node *TreeNode) SetValue2(value int) {
node.Value = value
}

func CreateNode(value int) *TreeNode {
return &TreeNode{Value: value}
}



package node

// 中序遍历
func (node *TreeNode) Traverse() {
if node == nil {
return
}
node.Left.Traverse()
node.Print()
node.Right.Traverse()
}



package main

import (
“fmt”
“tree/node”
)

func main() {
root := node.TreeNode{Value: 3}
root.Left = &node.TreeNode{}
root.Right = &node.TreeNode{5, nil, nil}
root.Right.Left = new(node.TreeNode)
root.Left.Right = node.CreateNode(2)
root.Print() // 3
fmt.Println()

root.Right.Left.SetValue(4)
root.Right.Left.Print() // 0
root.Right.Left.SetValue2(4)
root.Right.Left.Print() // 4
fmt.Println()

root.Traverse() // 0 2 3 4 5

}


##### 3)、扩展已有类型


如何扩充系统类型或者别人的类型


* 定义别名
* 使用组合


1)使用组合



package main

import (
“fmt”
“tree/node”
)

type myTreeNode struct {
node *node.TreeNode
}

// 后序遍历
func (myNode *myTreeNode) postOrder() {
if myNode == nil || myNode.node == nil {
return
}
left := myTreeNode{myNode.node.Left}
left.postOrder()
right := myTreeNode{myNode.node.Right}
right.postOrder()
myNode.node.Print()
}

func main() {
root := node.TreeNode{Value: 3}
root.Left = &node.TreeNode{}
root.Right = &node.TreeNode{5, nil, nil}
root.Right.Left = new(node.TreeNode)
root.Left.Right = node.CreateNode(2)
root.Print() // 3
fmt.Println()

root.Right.Left.SetValue(4)
root.Right.Left.Print() // 0
root.Right.Left.SetValue2(4)
root.Right.Left.Print() // 4
fmt.Println()

root.Traverse() // 0 2 3 4 5
fmt.Println()

myRoot := myTreeNode{&root}
myRoot.postOrder() // 2 0 4 5 3

}


2)定义别名



go mod init queue



package queue

type Queue []int

func (q *Queue) Push(v int) {
*q = append(*q, v)
}

func (q *Queue) Pop() int {
head := (*q)[0]
*q = (*q)[1:]
return head
}

func (q *Queue) IsEmpty() bool {
return len(*q) == 0
}



package main

import (
“fmt”
“queue/queue”
)

func main() {
q := queue.Queue{1}
q.Push(2)
q.Push(3)
fmt.Println(q.Pop()) // 1
fmt.Println(q.Pop()) // 2
fmt.Println(q.IsEmpty()) // false
fmt.Println(q.Pop()) // 3
fmt.Println(q.IsEmpty()) // true
}


##### 4)、使用内嵌来扩展已有类型



package main

import (
“fmt”
“tree/node”
)

type myTreeNode struct {
*node.TreeNode // Embedding 内嵌
}

// 后序遍历
func (myNode *myTreeNode) postOrder() {
if myNode == nil || myNode.TreeNode == nil {
return
}
left := myTreeNode{myNode.Left}
left.postOrder()
right := myTreeNode{myNode.Right}
right.postOrder()
myNode.Print()
}

func (myNode *myTreeNode) Traverse() {
fmt.Println(“This method is shadowed.”)
}

func main() {
root := myTreeNode{&node.TreeNode{Value: 3}}
root.Left = &node.TreeNode{}
root.Right = &node.TreeNode{5, nil, nil}
root.Right.Left = new(node.TreeNode)
root.Left.Right = node.CreateNode(2)
root.Print() // 3
fmt.Println()

root.Right.Left.SetValue(4)
root.Right.Left.Print() // 0
root.Right.Left.SetValue2(4)
root.Right.Left.Print() // 4
fmt.Println()

root.TreeNode.Traverse() // 0 2 3 4 5
fmt.Println()

root.Traverse() // This method is shadowed.

root.postOrder() // 2 0 4 5 3

}


#### 4、Go语言的依赖管理


##### 1)、依赖管理


依赖管理的三个阶段:GOPATH,GOVENDOR,go mod


##### 2)、go mod的使用



go mod init gomodtest



go get -u go.uber.org/zap


![](https://img-blog.csdnimg.cn/d07267a939364f70bac6aa488d1c4b56.png#pic_center)
go.mod文件中指定了依赖库的版本,同时新增了一个go.sum的文件



package main

import “go.uber.org/zap”

func main() {
logger, _ := zap.NewProduction()
logger.Warn(“warning test”)
}


指定版本:



go get -u go.uber.org/zap@v1.11


go mod:


* 由go命令统一的管理,用户不必关心目录结构
* 初始化:go mod init
* 增加依赖:go get
* 更新依赖:go get [@v…],go mod tidy


#### 5、面向接口


##### 1)、接口的概念



package infra

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

reeNode.Traverse() // 0 2 3 4 5
fmt.Println()

root.Traverse() // This method is shadowed.

root.postOrder() // 2 0 4 5 3

}


#### 4、Go语言的依赖管理


##### 1)、依赖管理


依赖管理的三个阶段:GOPATH,GOVENDOR,go mod


##### 2)、go mod的使用



go mod init gomodtest



go get -u go.uber.org/zap


![](https://img-blog.csdnimg.cn/d07267a939364f70bac6aa488d1c4b56.png#pic_center)
go.mod文件中指定了依赖库的版本,同时新增了一个go.sum的文件



package main

import “go.uber.org/zap”

func main() {
logger, _ := zap.NewProduction()
logger.Warn(“warning test”)
}


指定版本:



go get -u go.uber.org/zap@v1.11


go mod:


* 由go命令统一的管理,用户不必关心目录结构
* 初始化:go mod init
* 增加依赖:go get
* 更新依赖:go get [@v…],go mod tidy


#### 5、面向接口


##### 1)、接口的概念



package infra

[外链图片转存中…(img-H4YMqf02-1714405827797)]
[外链图片转存中…(img-nGfMrMuG-1714405827797)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 19
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值