golang学习(1)

数组、字符串和切片

数组

  • Go语言的数组是一种值类型,虽然数组的元素可以被修改,但是数组本身的赋值和函数传参都是以整体复制的方式处理的。
  • Go语言中数组是值语义。一个数组变量即表示整个数组,它并不是隐式的指向第一个元素的指针(比如C语言的数组),而是一个完整的值。
  • 数组的长度是数组类型的组成部分,指向不同长度数组的数组指针类型也是完全不同的

字符串

string是一种数据类型,这个数据类型占用16字节空间,前8字节是一个指针,指向字符串值的地址,后八个字节是一个整数,标识字符串的长度

type StringHeader struct {
	Data uintptr
	Len  int
}
  • Go语言字符串底层数据也是对应的字节数组,但是字符串的只读属性禁止了在程序中对底层字节数组的元素的修改。字符串赋值只是复制了数据地址和对应的长度,而不会导致底层数据的复制。
  • 每个字符串的长度虽然也是固定的,但是字符串的长度并不是字符串类型的一部分。
  • 字符串结构由两个信息组成:第一个是字符串指向的底层字节数组,第二个是字符串的字节的长度。字符串其实是一个结构体,因此字符串的赋值操作也就是reflect.StringHeader结构体的复制过程,并不会涉及底层字节数组的复制。
    在这里插入图片描述

切片

type SliceHeader struct {
    Data uintptr
    Len  int
    Cap  int
}
  • 切片的行为更为灵活,切片的结构和字符串结构类似,但是解除了只读限制。切片的底层数据虽然也是对应数据类型的数组,但是每个切片还有独立的长度和容量信息,切片赋值和函数传参数时也是将切片头信息部分按传值方式处理。因为切片头含有底层数据的指针,所以它的赋值也不会导致底层数据的复制。
  • 只要是切片的底层数据指针、长度和容量没有发生变化的话,对切片的遍历、元素的读取和修改都和数组是一样的。在对切片本身赋值或参数传递时,和数组指针的操作方式类似,只是复制切片头信息,并不会复制底层的数据。对于类型,和数组的最大不同是,切片的类型和长度信息无关,只要是相同类型元素构成的切片均对应相同的切片类型。

添加切片元素

在这里插入图片描述

删除切片元素

在这里插入图片描述

切片内存技巧

切片高效操作的要点是要降低内存分配的次数,尽量保证append操作不会超出cap的容量,降低触发内存分配的次数和每次分配内存大小。

避免切片内存泄漏

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

map

在这里插入图片描述

在这里插入图片描述

切片类型强制转换

当两个切片类型[]T和[]Y的底层原始切片类型不同时,Go语言是无法直接转换类型的

函数、方法和接口

函数

  • 匿名函数引用了外部作用域中的变量时就成了闭包函数,闭包函数是函数式编程语言的核心
  • 方法是绑定到一个具体类型的特殊函数
  • Go语言通过隐式接口机制实现了鸭子面向对象模型
  • 闭包对捕获的外部变量并不是传值方式访问,而是以引用的方式访问。
  • 如果以切片为参数调用函数时,有时候会给人一种参数采用了传引用的方式的假象:因为在被调用函数内部可以修改传入的切片的元素。其实,任何可以通过函数参数修改调用参数的情形,都是因为函数参数中显式或隐式传入了指针参数。
  • Go语言函数的递归调用深度逻辑上没有限制,函数调用的栈是不会出现溢出错误的,因为Go语言运行时会根据需要动态地调整函数栈的大小。
  • 但最重要的一点是要明白Go语言中指针不再是固定不变的了(因此不能随意将指针保持到数值变量中,Go语言的地址也不能随意保存到不在GC控制的环境中,因此使用CGO时不能在C语言中长期持有Go语言对象的地址)。

方法

  • 在C语言中方法对应一个类对象的成员函数,是关联到具体对象上的虚表中的。但是Go语言的方法却是关联到类型的,
  • 每种类型对应的方法必须和类型的定义在同一个包中,因此是无法给int这类内置类型添加方法的(因为方法的定义和类型的定义不在一个包中)
type Cache struct {
	m map[string]string
	sync.Mutex
}

func (p *Cache) Lookup(key string) string {
	p.Lock()
	defer p.Unlock()

	return p.m[key]
}
  • Cache结构体类型通过嵌入一个匿名的sync.Mutex来继承它的Lock和Unlock方法. 但是在调用p.Lock()和p.Unlock()时, p并不是Lock和Unlock方法的真正接收者, 而是会将它们展开为p.Mutex.Lock()和p.Mutex.Unlock()调用. 这种展开是编译期完成的, 并没有运行时代价.
  • 在传统的面向对象语言(eg.C++或Java)的继承中,子类的方法是在运行时动态绑定到对象的,因此基类实现的某些方法看到的this可能不是基类类型对应的对象,这个特性会导致基类方法运行的不确定性。而在Go语言通过嵌入匿名的成员来“继承”的基类方法,this就是实现该方法的类型的对象,Go语言中方法是编译时静态绑定的。如果需要虚函数的多态特性,我们需要借助Go语言接口来实现。

面向并发的内存模型

常量
赋值不算使用
在这里插入图片描述
iota
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
开启go模块
在这里插入图片描述
编写go.mod模块
在这里插入图片描述
使用go module之后在同一个文件夹不能有相同的main函数
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
字符与数字可以相加,字符串和数字不能相加

// 浮点数
a := 5.0

// 转换为int类型
b := int(a)

//Go允许在底层结构相同的两个类型之间互转。例如:
// IT类型的底层是int类型
type IT int

// a的类型为IT,底层是int
var a IT = 5

// 将a(IT)转换为int,b现在是int类型
b := int(5)

// 将b(int)转换为IT,c现在是IT类型
c := IT(b)

var a int32 = 1
var b int64 = 3
b = int64(a)
fmt.Println(a, b)

/*
    不是所有数据类型都能转换的,
    例如字母格式的string类型"abcd"转换为int肯定会失败
    低精度转换为高精度时是安全的,高精度的值转换为低精度时会丢失精度。
    例如int32转换为int16,float32转换为int
    这种简单的转换方式不能对int(float)和string进行互转,要跨大类型转换,
    可以使用strconv包提供的函数
*/

在这里插入图片描述
使用 _ 接受返回值,防止调用报错
在这里插入图片描述在这里插入图片描述使用切片是公用同一内存
在这里插入图片描述切片动态扩容问题
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述 make,new,nil
在这里插入图片描述
在这里插入图片描述使用new和不适用new的差异
在这里插入图片描述
在这里插入图片描述

函数:第一种和第二种定义方式

在这里插入图片描述第三种定义方式:先定义返回值,return省略
在这里插入图片描述

函数:第四种函数定义方式

在这里插入图片描述多参数省略返回
在这里插入图片描述省略号的作用:
在这里插入图片描述
与python相同,go语言可以将函数赋值给变量,也可以在函数中将函数当成变量传给函数,ex:

func filter(score []int, f func(int) bool)  []int{
	reSlice := make([]int, 0)
	for _, v := range score{
		if f(v){
			reSlice = append(reSlice, v)
		}
	}
	return reSlice
}
func main()  {
	score := []int{10, 50, 80, 90, 85}
	
	fmt.Println(filter(score, func(i int) bool {
		if i >= 60{
			return true
		}else {
			return false
		}
	}))
}

关于python的finally
在这里插入图片描述defer语句执行时的拷贝机制,使用栈存放defer

在这里插入图片描述在这里插入图片描述在这里插入图片描述
painc, recover
在这里插入图片描述
错误指的是可能出现问题的地方出现了问题,比如打开一个文件时失败,这种情况在人们的意料之中 ;而异常指的是不应该出现问题的地方出现了问题,比如引用了空指针,这种情况在人们的意料之外。可见,错误是业务过程的一部分,而异常不是 。

Golang中引入error接口类型作为错误处理的标准模式,如果函数要返回错误,则返回值类型列表中肯定包含error。error处理过程类似于C语言中的错误码,可逐层返回,直到被处理。

Golang中引入两个内置函数panic和recover来触发和终止异常处理流程,同时引入关键字defer来延迟执行defer后面的函数。

一直等到包含defer语句的函数执行完毕时,延迟函数(defer后的函数)才会被执行,而不管包含defer语句的函数是通过return的正常结束,还是由于panic导致的异常结束。你可以在一个函数中执行多条defer语句,它们的执行顺序与声明顺序相反。

当程序运行时,如果遇到引用空指针、下标越界或显式调用panic函数等情况,则先触发panic函数的执行,然后调用延迟函数。调用者继续传递panic,因此该过程一直在调用栈中重复发生:函数停止执行,调用延迟执行函数等。如果一路在延迟函数中没有recover函数的调用,则会到达该携程的起点,该携程结束,然后终止其他所有携程,包括主携程(类似于C语言中的主线程,该携程ID为1)。

错误和异常从Golang机制上讲,就是error和panic的区别。很多其他语言也一样,比如C++/Java,没有error但有errno,没有panic但有throw。

Golang错误和异常是可以互相转换的:

  1. 错误转异常,比如程序逻辑上尝试请求某个URL,最多尝试三次,尝试三次的过程中请求失败是错误,尝试完第三次还不成功的话,失败就被提升为异常了。
  2. 异常转错误,比如panic触发的异常被recover恢复后,将返回值中error类型的变量进行赋值,以便上层函数继续走错误处理流程。

在这里插入图片描述    在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述go type
在这里插入图片描述namedtuple
在这里插入图片描述
go语言的结构体:struct:在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述结构体的多种实例化方式
在这里插入图片描述在这里插入图片描述slice的大小,又结构体定义,所以占用24个字节
在这里插入图片描述在这里插入图片描述结构体方法

package main

import "fmt"
import "math"

type Circle struct {
 x int
 y int
 Radius int
}

// 面积
func (c Circle) Area() float64 {
 return math.Pi * float64(c.Radius) * float64(c.Radius)
}

// 周长
func (c Circle) Circumference() float64 {
 return 2 * math.Pi * float64(c.Radius)
}

func main() {
 var c = Circle {Radius: 50}
 fmt.Println(c.Area(), c.Circumference())
 // 指针变量调用方法形式上是一样的
 var pc = &c
 fmt.Println(pc.Area(), pc.Circumference())
}

Go 语言的方法名称也分首字母大小写,它的权限规则和字段一样,首字母大写就是公开方法,首字母小写就是内部方法,只能归属于同一个包的代码才可以访问内部方法。特性:1. 结构体的方法只能和结构体在同一个包中。2.内置的int类型不能加方法

go语言使用组合的形式实现继承

package main

import "fmt"

type Teacher struct {
    name string
    age int
    title string
}


type Course struct {
    teacher Teacher
    price int
    name string
    url string
}

func getInfo(c Course){
    fmt.Println(c.teacher.name, c.teacher.age)
}

func main() {
    var c Course = Course {
        teacher: Teacher{
            name:"bobby",
            age:18,
            title: "架构师",
        },
        price: 100,
        name: "scrapy分布式爬虫",
        url: "",  // 注意这里的逗号不能少
    }
    getInfo(c)
}

在这里插入图片描述

package main

import "fmt"

type Teacher struct {
    name string
    age int
    title string
}


type Course struct {
    Teacher
    price int
    name string
    url string
}

func getInfo(c Course){
    fmt.Println(c.name, c.age)
}

func main() {
    var c Course = Course {
        Teacher: Teacher{ //还可以这样声明一些属性值,因为Teacher是结构体,匿名,所以需要这样声明
            "bobby", 18, "",
        },
        price: 100,
        name: "scrapy分布式爬虫",
        url: "",  // 注意这里的逗号不能少
    }
    getInfo(c)
}

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

package main

import (
    "fmt"
    "reflect"
)

const tagName = "Testing"

type Info struct {
    Name string `Testing:"-"`
    Age  int    `Testing:"age,min=17,max=60"`
    Sex  string `Testing:"sex,required"`
}

func main() {
    info := Info{
        Name: "benben",
        Age:  23,
        Sex:  "male",
    }

    //通过反射,我们获取变量的动态类型
    t := reflect.TypeOf(info)
    fmt.Println("Type:", t.Name())
    fmt.Println("Kind:", t.Kind())

    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i) //获取结构体的每一个字段
        tag := field.Tag.Get(tagName)
        fmt.Printf("%d. %v (%v), tag: '%v'\n", i+1, field.Name, field.Type.Name(), tag)
    }
} 

python的鸭子类型:一只鸟看起来像鸭子,跑起来像鸭子,叫起来像鸭子,那么这只鸟就是鸭子类型。类名不重要,重要的是这个类实现了什么方法。
在这里插入图片描述 在这里插入图片描述

空接口的作用

第一个作用
在这里插入图片描述
第二个作用在这里插入图片描述
第三个作用
在这里插入图片描述

类型的断言

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

用接口协议去理解sort

要实现对切片的排序:编程思想:关心协议,不关心类型

package main

import (
    "fmt"
    "sort"
)

type Course struct {
    Nmae string
    Price int
    Url string
}
type Courses []Course

func (c Courses) Len() int {
    return len(c)
}

func (c Courses) Less(i, j int)  bool{
    return c[i].Price < c[j].Price
}

func (c Courses) Swap(i, j int)  {
    c[i], c[j] = c[j], c[i]
}
func main()  {
    courses := Courses{
        Course{"django", 300, ""},
        Course{"scrapy", 120, ""},
        Course{"tornado", 340, ""},
    }
    sort.Sort(courses)
    for _, v := range courses{
        fmt.Println(v)
    }
}

go的面向对象

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
多态的最好的体现demo

type Log struct {
	name string
	content string
	addTime int64
}
type NetLog struct {
	Log
}
type IOLog struct {
	Log
}
type write interface {
	echo()
	out()
}
func (this *Log)writeLog(_write write)  {
	fmt.Println(this.name+"——————"+this.content)
	_write.echo()
	_write.out()
}

type ioWrite string

func (this *ioWrite)echo() {
	fmt.Println("ioWrite:echo()")
}
func (this *ioWrite)out() {
	fmt.Println("ioWrite:out()")
}
type netWrite string

func (this *netWrite)echo() {
	fmt.Println("netWrite:echo()")
}
func (this *netWrite)out() {
	fmt.Println("netWrite:out()")
}

func main() {
	log := &Log{
		name: "微信小程序支付日志",
		content: "微信小程序支付日志内容",
		addTime: 0,
	}
	var _ioWrite *ioWrite
	var _netWrite *netWrite
	// write -> ioWrite 父类指向子类
	// write -> netWrite 父类指向子类
	log.writeLog(_ioWrite)
	log.writeLog(_netWrite)

	netLog := &NetLog{Log{
		name: "微信小程序网络支付日志",
		content: "微信小程序网络支付日志内容",
		addTime: 0,
	}}
	netLog.writeLog(_netWrite)

	fileLog := &IOLog{Log{
		name: "微信小程序文件支付日志",
		content: "微信小程序文件支付日志内容",
		addTime: 0,
	}}
	fileLog.writeLog(_ioWrite)
}

多态:同样的类型,但是特性不一样
在这里插入图片描述
多态:通过接口来实现,同样是Person,但是Speak不一样
在这里插入图片描述

设计模式

在这里插入图片描述
左边违背了单一原则
在这里插入图片描述
左边存在的问题假如要跟换引擎,就需要重新修改代码,而右边只需要重新实现接口就ok了
在这里插入图片描述

简单工厂模式

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值