Go

文章目录

变量

变量声明

//1、单变量声明,类型放在变量名之后,可以为任意类型
var 变量名 类型
var v1,v2,v3 string //多变量同类型声明
//2、多变量声明
var {
v1 int
v2 []int
}

变量初始化

//1、使用关键字var,声明变量类型并赋值
var v1 int=10
//2、使用关键字var,直接对变量赋值,go可以自动推导出变量类型
var v2=10
//3、直接使用“:=”对变量赋值,不使用var,两者同时使用会语法冲突,推荐使用
v3:=10

匿名变量

//Go中所有声明后的变量都需要调用到,当出现函数多返回值,并且部分返回值不需要使用时,可以使用匿名变量丢弃该返回值
func GetName()(firstName,lastName,nickName string){
return "May","Chan","Make"
}
_,_,nickName:=GetName() //使用匿名变量丢弃部分返回值

指针

指针(pointer)在Go语言中可以被拆分为两个核心概念:

  • 类型指针,允许对这个指针类型的数据进行修改,传递数据可以直接使用指针,而无须拷贝数据,类型指针不能进行偏移和运算。(类似Java中的引用)
  • 切片,由指向起始元素的原始指针、元素数量和容量组成。

每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。Go语言中使用在变量名前面添加​​&​​操作符(前缀)来获取变量的内存地址(取地址操作)

取地址操作符&和取值操作符​*​是一对互补操作符,​&​取出地址,​*​根据地址取出地址指向的值。

func main() {
str := "test ptr"
fmt.Println(&str) // &T 取变量T的内存地址值赋值到ptr 0xc000010200 返回的是指针类型
fmt.Println(*&str) // *ptr 取指定内存地址对应的变量值 test ptr 返回的是值类型
}

变量、指针地址、指针变量、取地址、取值的相互关系和特性如下:

  • 对变量进行取地址操作使用​​&​​操作符,可以获得这个变量的指针变量。
  • 指针变量的值是指针地址。
  • 对指针变量进行取值操作使用​​*​​操作符,可以获得指针变量指向的原变量的值。

通过指针修改数据

// 交换函数
func swap1(a, b *int) {
// 取a指针的值, 赋给临时变量t
t := *a
// 取b指针的值, 赋给a指针指向的变量
*a = *b
// 将a指针的值赋给b指针指向的变量
*b = t
}
func main() {
// 准备两个变量, 赋值1和2
x, y := 1, 2
// 交换变量值
swap1(&x, &y)
// 输出变量值
fmt.Println(x, y)
}

new() 函数可以创建一个对应类型的指针,创建过程会分配内存,被创建的指针指向默认值。

func new(Type) *Type

// new() 创建指针的另一种手段
str := new(string)
*str = "test new"
fmt.Println(str) // 0xc000010200
fmt.Println(*str)

go和java类似,都是值传递,修改的是传入值的副本

type Person struct {
name string
age int
}

func updateAge(p Person){
p.age = 10
}

func updateAgeByPtr(p *Person) {
p.age = 10
}

func main() {
p := Person{age: 0,name: "kevin"}
fmt.Println(p.age)
updateAge(p) // go中是值传递
fmt.Println(p.age)
updateAgeByPtr(&p)
fmt.Println(p.age)
}

变量声明而没有赋值,默认为零值,不同类型零值不同,例如字符串零值为空字符串;

指针声明而没有赋值,默认为nil,即该指针没有任何指向。当指针没有指向的时候,不能对(*point)进行操作包括读取,否则会报空指针异常。

var aPot *string
fmt.Println(aPot) // <nil>

指针也是一种类型,不同于一般类型,指针的值是地址,这个地址指向其他的内存,通过指针可以读取其所指向的地址所存储的值。
函数方法的接受者,也可以是指针变量。无论普通接受者还是指针接受者都会被拷贝传入方法中,不同在于拷贝的指针,其指向的地方都一样,只是其自身的地址不一样。

Go函数

Go函数中只关注匿名函数和闭包

所谓闭包就是函数的返回值是也是一个函数

// case1
func foo1(x *int) func() {
// 返回值是一个函数
return func() {
*x = *x + 1
fmt.Printf("foo1 val = %d\n", *x)
}
}

// case2
func foo2(x int) func() {
return func() {
x = x + 1
fmt.Printf("foo1 val = %d\n", x)
}
}

func main() {

x := 133
f1 := foo1(&x)
f1()
fmt.Println(x)

y := 100
f2 := foo2(y)
f2()
fmt.Println(y)
}

执行结果:

foo1 val = 134
134
foo1 val = 101
100

case1和case2的区别在于值传递和引用传递的区别,go和Java一样只有值传递,这里所说的引用传递是传递的是地址的值

闭包的延迟绑定

只有在执行闭包函数的时候才会去寻找最新的函数环境

// case7  闭包的延迟绑定,
func foo7(x int) []func() {
var fs []func()
values := []int{1, 2, 3, 4, 5}
for _, val := range values {
fs = append(fs, func() {
fmt.Printf("foo7 val = %d\n", x+val)
})
}
return fs
}

func main() {
fs := foo7(11)
for _, f := range fs{
f()
}
}

答案是:(在用到的时候去寻找函数的最新环境,此时是val 是 5)

foo7 val = 16
foo7 val = 16
foo7 val = 16
foo7 val = 16

Go面向对象

继承

在Go语言中,并没有显式的继承与显式的接口实现(接口实现其实也算是一种继承),Go对于继承,是通过组合来实现的

// 继承
type People struct {
name string
age int
}

type Teacher struct {
People // 以组合的方式引入
teacherSomeThing string
}

接口

go中的接口不能有变量,go中实现接口是基于方法,如果一个类实现了这个接口中所有的方法,那么这个类就实现了这个接口

import "fmt"

// 只有实现了接口中的所有方法才是实现了这个接口
type Usb interface {
start()
stop()
}

type Usb1 interface {
start()
stop()
}

// computer既实现了Usb1也实现了Usb接口
type computer struct {

}

// 实现start
func (c computer)start() {
fmt.Println("调用start")
}

// 实现stop
func (c computer) stop() {
fmt.Println("调用stop")
}

func testUsb(u Usb) {
u.start()
u.stop()
}

func main() {
c := computer{}
testUsb(c)
}

从接口看go中的错误处理

// 内置的error接口,是一个常规的用于处理错误的接口。
// 其中 nil 表示没有错误。
type error interface {
Error() string
}

实现自定义异常只需要实现这个error接口

type MyError struct {
errorMsg string
}

func (error MyError) Error() string{
return error.errorMsg
}

func main() {
myError := MyError{
errorMsg : "catch error",
}
fmt.Println(myError.Error())
}

数据结构

链表