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())
}
数据结构
链表