GO语言笔记

数组

数组定义

  1. 数组定义:var arr [4]int

切片

切片的底层

  • 每一个切片引用了一个底层数组
  • 切片本身不存储任何数据,都是底层数组存储,修改切片数据也是修改底层数组数据
  • 当向切片添加数据时,没超过容量可以直接添加,超过容量会自动扩容(2倍增长)
  • 切片一旦扩容,都要指向一个新的底层数组。
package main
import "fmt"

func main() {

	s1 := []int{1, 2, 3}
	fmt.Println(s1)
	fmt.Printf("len:%d, cap:%d\n", len(s1), cap(s1))
	fmt.Printf("%p\n", s1)

	s1 = append(s1, 4, 5)
	fmt.Println(s1)
	fmt.Printf("len:%d, cap:%d\n", len(s1), cap(s1))
	fmt.Printf("%p\n", s1)

	s1 = append(s1, 6, 7, 8)
	fmt.Println(s1)
	fmt.Printf("len:%d, cap:%d\n", len(s1), cap(s1))
	fmt.Printf("%p\n", s1)
}

结果

直接再数组上创建切片

  1. 从已有的数组上创建切片,该切片的底层数组就是当前数组
  2. slice := arr[start:end) 左闭右开
  3. 长度是从start到end的数据量
  4. 容量是从start到数组的末尾 比如,s2的容量,就是7,3-10
  5. 如果切片的长度超过该数组长度,那么该切片会引用一个新的底层数组
  6. 改变切片的数据也会对应改变数组的数据
a := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

	s1 := a[:5]  //1-5
	s2 := a[3:8] //4-8
	s3 := a[5:]  // 6-10
	s4 := a[:]   // 1-10

	fmt.Println("a:", a)
	fmt.Println("s1:", s1)
	fmt.Println("s2:", s2)
	fmt.Println("s3:", s3)
	fmt.Println("s4:", s4)

在这里插入图片描述

深拷贝和浅拷贝

  1. 深拷贝:拷贝的是数据背身 如:a = b
  2. 浅拷贝 拷贝的是数据地址 如:切片
  3. 拷贝元素函数,copy(s2,s3) s3的元素拷贝到s2,是按照下标一一对应拷贝的

Map

  1. map是一种引用类型
  2. 定义map的方法 三种,其中第一种声明为空 map[key]value
  3. map是无序的
	var map1 map[int]string //只有声明,没有初始化 nil
	var map2 = make(map[int]string) //make方法创建的map是非空
	var map3 = map[string]int{"go": 98, "python": 87, "c": 88}

	fmt.Println(map1)
	fmt.Println(map2)
	fmt.Println(map3)

	fmt.Println(map1 == nil)
	fmt.Println(map2 == nil)
	fmt.Println(map3 == nil)

在这里插入图片描述

map的操作

  1. 获取数据,map[key]
  2. 添加数据, map[key] = value
  3. 如果key存在,获取数组,如果key不存在,获取的是value类型的零值
  4. v1, ok = map[key] 可以返回一个值判断key是否存在
  5. 删除数据,delete(map,key)

map的遍历

  1. for range 遍历: for k,v := range map k,v代表的是键和值,不像数组是下标和值
  2. 切片种存入map, 定义:slice := make([ ][key][value])

函数

可变参数

  1. 一个函数的类型可以确定,但是个数不确定的情况下使用
  2. 对于函数,可变参数相当于一个切片

在这里插入图片描述
在这里插入图片描述

高阶函数

  1. 高阶函数:接受一个函数作为参数的函数 比如oper
  2. 回到函数:作为另一个函数参数的函数 比如add
res3 := oper(3, 4, add)
	fmt.Println(res3)
}
func add(x, y int) int {
	return x + y
}
func oper(x, y int, fun func(int, int) int) int {
	fmt.Println(x, y, fun)
	res := fun(x, y)
	return res
}

3 4 0x71e8c0             
7 

闭包

  1. 一个外层函数中,有内层函数,该内层函数会操作外层函数的局部变量,并且该外层函数的返回值就是内层函数。
  2. 这个内层函数和外层函数的局部变量,被称为闭包结构
# 注意这里的res1和res2
	res1 := increment()
	fmt.Println(res1)
	v1 := res1()
	fmt.Println(v1)
	v2 := res1()
	fmt.Println(v2)

	res2 := increment()
	v3 := res2()
	fmt.Println(v3)
	v4 := res2()
	fmt.Println(v4)

}
func increment() func() int {
	i := 0
	res := func() int {
		i += 1
		return i
	}
	return res
}
0xf8eac0                 
1                        
2                        
1                        
2

指针数组 数组指针

package main

import "fmt"

func main() {

	// 定义数组 要标明长度
	arr1 := [4]int{1, 2, 3, 4}

	// 定义指针 用var
	var p1 *[4]int

	p1 = &arr1
	// p1存的是数组地址  *p1是拿到地址的数据
	fmt.Println(p1)
	fmt.Println(*p1)

	//根据指针 操作数组 两种方法
	(*p1)[0] = 200
	fmt.Println(arr1)

	p1[0] = 300
	fmt.Println(arr1)

	// 指针数组
	a := 10
	b := 20
	c := 30
	arr2 := [3]int{a, b, c}
	//指针数组
	arr3 := [3]*int{&a, &b, &c}

	fmt.Println(arr2)
	fmt.Println(arr3)
	//在指针数组改变数值
	*arr3[0] = 2000
	fmt.Println(a)
	//但是arr2没有改变,因为存取的时候arr2存取的是数值
	fmt.Println(arr2)

}

函数返回指针

  • 函数指针:一个指针 指向函数的指针
  • 指针函数:该函数的返回值是指针
func main() {
	// arr2接受的是arr1数组的指针,当函数结束时这个数组并没有被销毁
	arr2 := fun3()
	fmt.Println(arr2)
	fmt.Println(&arr2)

	arr2[0] = 10
	fmt.Println(arr2)
	arr3 := fun3()
	fmt.Println(arr3)
	fmt.Println(&arr3)
}

// 函数返回指针数组
func fun3() *[3]int {
	arr1 := [3]int{1, 2, 3}
	return &arr1
}

指针作为参数

func main() {
	a := 10
	fmt.Println(a)
	fun3(&a)
	fmt.Println(a)

}

func fun3(a *int) {
	*a = 100
}

结构体

结构体的定义

package main

import "fmt"

type Person struct {
	name string
	sex  string
	age  int
}

func main() {

	//方法一
	var p1 Person
	p1.name = "王二狗"
	p1.sex = "男"
	p1.age = 10
	fmt.Println(p1)

	//方法二
	p2 := Person{}
	p2.name = "李二狗"
	p2.sex = "男"
	p2.age = 10
	fmt.Println(p2)

	//方法三
	p3 := Person{name: "黄", sex: "男", age: 10}
	fmt.Println(p3)

	//方法四
	p4 := Person{"黄", "男", 20}
	fmt.Println(p4)
}

结构体指针

  • 有两种定义方法
type Person struct {
	name string
	sex  string
	age  int
}

func main() {

	// 定义结构体指针

	var p1 *Person

	//使用var定义指针初始为nil
	fmt.Println(p1)
	fmt.Printf("%T", p1)

	//使用new方法定义
	//该方法定义不为空,定义的指针已经指向了新分配的内存空间,里面存储的值为0

	p2 := new(Person)
	fmt.Println(p2)
	fmt.Printf("%T", p2)
}

go语言的面向对象

  • 模拟继承性可以提升关系
  • 也就是当访问B时,可以直接访问A的字段,而不需要B.A.name B.name即可

在这里插入图片描述

结构体的方法

  • 子类继承匿名的"父类" 可以继承父类的方法,也可以重写

在这里插入图片描述

接口

在这里插入图片描述

  • 定义简单接口
func main() {

	m1 := Mouse{"罗技"}
	d1 := FlashDisk{"闪迪"}
	// 传入Mouse类
	test(m1)
	//传入Disk类
	test(d1)

	fmt.Println("接口测试:")
	var u USB = m1
	//传入接口类
	test(u)
	u.start()
	u.end()
}

type USB interface {
	start()
	end()
}
type Mouse struct {
	name string
}
type FlashDisk struct {
	name string
}

func (m Mouse) start() {
	fmt.Println(m.name, "鼠标启动")
}
func (m Mouse) end() {
	fmt.Println(m.name, "鼠标结束")
}
func (d FlashDisk) start() {
	fmt.Println(d.name, "U盘启动")
}
func (d FlashDisk) end() {
	fmt.Println(d.name, "U盘结束")
}

func test(u USB) {
	u.start()
	u.end()
}

罗技 鼠标启动
罗技 鼠标结束
闪迪 U盘启动 
闪迪 U盘结束 
接口测试:    
罗技 鼠标启动
罗技 鼠标结束
罗技 鼠标启动
罗技 鼠标结束

接口类型

  • 接口本质,功能的定义和功能的实现分离开,降低耦合性
  • 接口特性:非侵入式
  • 多态:U盘可以是U盘设备也可以是设备,可以承担2个角色,能实现的功能范围不同
  • go语言通过接口模拟多态
    看成实现本身的类型,能够访问实现中的属性和方法
    看成对应的接口类型,那就只能访问接口中的方法

空接口

  • 学习了interface{}特殊变量,可以存储任意类型的数据
func main() {
	var a1 A = Cat{color: "yellow"}
	var a2 A = Person{name: "黄", age: 10}
	var a3 A = 3
	var a4 A = "11"

	fmt.Println(a1)
	fmt.Println(a2)
	fmt.Println(a3)
	fmt.Println(a4)

	test(a1)
	test(a2)
	test(1)
	test("a")

	test2(412412312)

	// interface{} 可以接受任意类型
	//利用map 再value中存入任意数据

	//但是map类型可以不用2个参数
	map1 := make(map[int]interface{})

	map1[0] = 1
	map1[1] = "字符串"
	map1[2] = Cat{color: "yellow"}
	map1[3] = &Cat{color: "yellow"}

	fmt.Println(map1)
	fmt.Println("打印切片")
	//切片至少要2个参数,这里的1是给他分配了一个空的内存空间 这个是我刚发现的 ,我给0就是初始时不分配存储空间
	slice1 := make([]interface{}, 0)
	slice1 = append(slice1, 1, 2, 3, "fwqf", Cat{color: "blue"})
	fmt.Println(slice1)
}
func test(a A) {
	fmt.Println("测试函数:", a)
}
func test2(a interface{}) {
	fmt.Println("测试函数2", a)
}

type A interface {
}
type Cat struct {
	color string
}
type Person struct {
	name string
	age  int
}



{yellow}
{10}                                    
3                                          
11                                         
测试函数: {yellow}                        
测试函数: {10}                         
测试函数: 1                               
测试函数: a                               
测试函数2 412412312                        
map[0:1 1:字符串 2:{yellow} 3:0xc000050270]
打印切片                                   
[1 2 3 fwqf {blue}]   

接口嵌套

在这里插入图片描述

func main() {
	//不使用接口
	var c Cat = Cat{name: "小花"}
	c.test1()
	c.test2()
	c.test3()

	fmt.Println("接口A调用:")
	var a A = c
	a.test1()
	fmt.Println("接口B调用:")
	var b B = c
	b.test2()
	fmt.Println("接口C调用:")
	var d C = c
	d.test1()
	d.test2()
	d.test3()
}

type A interface {
	test1()
}
type B interface {
	test2()
}
type C interface {
	A
	B
	test3()
}
type Cat struct {
	name string
}

func (c Cat) test1() {
	fmt.Println("这是1方法")
}
func (c Cat) test2() {
	fmt.Println("这是2方法")
}
func (c Cat) test3() {
	fmt.Println("这是3方法")
}


这是1方法
这是2方法  
这是3方法  
接口A调用:
这是1方法  
接口B调用:
这是2方法  
接口C调用:
这是1方法  
这是2方法  
这是3方法 

接口断言

  • 定义断言有两种方法
  • 如果想要接口USB接收Mouse和FlashDisk类,那么接口中的start(), end()方法都要一一单独实现才不会报错,也就是说func (m Mouse) start()不能省略
package main

import "fmt"

func main() {

	m1 := Mouse{"罗技"}
	d1 := FlashDisk{"闪迪"}
	var u USB = m1

	getType(m1)
	getType(d1)
	getType(u)
	//getType(100) 这种类型会报错

	fmt.Println("switch方法测试:")
	//Switch方法测试
	getType2(m1)
	getType2(d1)
	getType2(u)
}

type USB interface {
	start()
	end()
}
type Mouse struct {
	name string
}
type FlashDisk struct {
	name string
}

func (m Mouse) start() {
	fmt.Println(m.name, "鼠标启动")
}
func (m Mouse) end() {
	fmt.Println(m.name, "鼠标结束")
}
func (d FlashDisk) start() {
	fmt.Println(d.name, "U盘启动")
}
func (d FlashDisk) end() {
	fmt.Println(d.name, "U盘结束")
}

func test(u USB) {
	u.start()
	u.end()
}
func getType(u USB) {
	//使用switch 语句进行断言
	if ins, ok := u.(Mouse); ok {
		fmt.Println(ins.name, "鼠标")
	} else if ins, ok := u.(FlashDisk); ok {
		fmt.Println(ins.name, "U盘")
	}
}
func getType2(u USB) {
	switch ins := u.(type) {
	case Mouse:
		fmt.Println(ins.name, "鼠标")
	case FlashDisk:
		fmt.Println(ins.name, "U盘")

	}
}

罗技 鼠标
闪迪 U盘        
罗技 鼠标       
switch方法测试:
罗技 鼠标       
闪迪 U盘        
罗技 鼠标     

type关键字

  • 使用type,定义新类型
    type myint int 定义新类型myint
    虽然存储的都是int类型 但是2个类型不能相互转换
  • 类型别名
    type myint = int
    这样就可以 相互转换,因为myint 和 int 本质上是同一个东西
  • 非本地类型不能定义方法
    意思就是你取函数别名的时候要在该函数的包内定义,但是如果你是定义函数新类型的话就可以

error

  • Go的错误是一种类型,错误用内置的error表示,错误可以存储在变量中
package main

import (
	"fmt"
	"os"
)
//简单例子
func main() {
	f, err := os.Open("test.txt")
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(f.Name(), "打开文件成功")
}

package main

import (
	"fmt"
	"os"
)

func main() {
	f, err := os.Open("test.txt")
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(f.Name(), "打开文件成功")
}
open test.txt: The system cannot find the file specified.

自定义错误

创建错误变量的两种方式

package main

import (
	"errors"
	"fmt"
)

func main() {
	//方法一
	err1 := errors.New("自建错误1")
	fmt.Println(err1)
	fmt.Printf("%T", err1)
	//方法二
	fmt.Println()
	fmt.Println("------------")
	err2 := fmt.Errorf("自建错误2")
	fmt.Println(err2)
	fmt.Printf("%T", err2)
}
  • 用error接口创建错误
package main

import (
	"fmt"
	"math"
)

func main() {
	radius := -3.0
	arr, err := circleArea(radius)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(arr)
}

// 定义一个结构体,表示错误类型
type areaError struct {
	msg    string
	radius float64
}

// 实现error接口,就是实现Error()方法
func (e *areaError) Error() string {
	return fmt.Sprintf("error:半径, %.2f, %s", e.radius, e.msg)
}
func circleArea(radius float64) (float64, error) {
	if radius < 0 {
		return 0, &areaError{"半径是非法的", radius}
	}
	return math.Pi * radius * radius, nil
}

error:半径, -3.00, 半径是非法的

  • 该实例更为完成的阐述自定义error的使用,同时可以断言err得到更多的错误信息
package main

import "fmt"

func main() {

	length, width := -3.7, -3.4
	area, err := rectArea(length, width)
	if err != nil {
		fmt.Println(err)
		//用断言获取更多错误信息
		if err, ok := err.(*areaError); ok {
			if err.lengthNegative() {
				fmt.Println("长度错误", err.length)
			}
			if err.widthNegative(); ok {
				fmt.Println("宽度错误", err.width)
			}
		}
		return
	}
	fmt.Println(area)
}

type areaError struct {
	msg    string
	length float64
	width  float64
}

func (e *areaError) Error() string {
	return e.msg
}
func (e *areaError) lengthNegative() bool {
	return e.length < 0
}
func (e *areaError) widthNegative() bool {
	return e.width < 0
}
func rectArea(length, width float64) (float64, error) {
	msg := ""
	if length < 0 {
		msg = "长度小于0"
	}
	if width < 0 {
		if msg == "" {
			msg = "宽度小于0"
		} else {
			msg = "长和宽都小于0"
		}
	}
	if msg != "" {
		return 0, &areaError{msg, length, width}
	}
	return length * width, nil
}

长和宽都小于0
长度错误 -3.7
宽度错误 -3.4

panic()和recover()

  • panic()终止代码运行,但是运行了defer的语句会继续运行完
  • recover()恢复代码的运行
    go可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理
package main

import "fmt"

func main() {
	funA()
	defer myPrint("defer函数 main 3")
	funB()
	defer myPrint("defer函数 main 4")
	fmt.Println("main over")
}
func myPrint(s string) {
	fmt.Println(s)
}
func funA() {
	fmt.Println("我是函数A")
}
func funB() {
	fmt.Println("我是函数B")
	defer myPrint("defer函数B 1")
	for i := 0; i <= 5; i++ {
		fmt.Println("i:", i)
	}
	defer myPrint("defer函数B 2")
}

正常执行带有defer的代码
我是函数A
我是函数B       
i: 0            
i: 1            
i: 2            
i: 3            
i: 4            
i: 5            
defer函数B 2    
defer函数B 1    
main over       
defer函数 main 4
defer函数 main 3

package main

import "fmt"

func main() {
	funA()
	defer myPrint("defer函数 main 3")
	funB()
	defer myPrint("defer函数 main 4")
	fmt.Println("main over")
}
func myPrint(s string) {
	fmt.Println(s)
}
func funA() {
	fmt.Println("我是函数A")
}
func funB() {
	fmt.Println("我是函数B")
	defer myPrint("defer函数B 1")
	for i := 0; i <= 5; i++ {
		fmt.Println("i:", i)
		if i == 3 {
			//让程序中断
			panic("函数B恐慌了")
		}
	}
	defer myPrint("defer函数B 2")
}

我是函数A
我是函数B
i: 0
i: 1
i: 2
i: 3
defer函数B 1
defer函数 main 3
panic: 函数B恐慌了

  • 加入recover()之后,主函数会恢复执行,但是剩下的for循环和defer函数B 2没有继续执行了。
package main

import (
	"fmt"
)

func main() {
	funA()
	defer myPrint("defer函数 main 3")
	funB()
	defer myPrint("defer函数 main 4")
	fmt.Println("main over")
}
func myPrint(s string) {
	fmt.Println(s)
}
func funA() {
	fmt.Println("我是函数A")
}
func funB() {
	defer func() {
		if msg := recover(); msg != nil {
			fmt.Println("程序恢复啦")
		}
	}()
	defer fmt.Println("我是函数B")
	defer myPrint("defer函数B 1")
	for i := 0; i <= 5; i++ {
		fmt.Println("i:", i)
		if i == 3 {
			//让程序中断
			panic("函数B恐慌了")
		}
	}
	defer myPrint("defer函数B 2")
}

我是函数A
i: 0            
i: 1            
i: 2            
i: 3            
defer函数B 1    
我是函数B       
程序恢复啦      
main over       
defer函数 main 4
defer函数 main 3

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值