
结构体
对象创建
go
package main
import "fmt"
type Student struct {
id int
name string
sex byte
age int
addr string
}
func main() {
// 每个成员都必须按顺序初始化
var s1 Student = Student{1, "Angle", 'f', 18, "河北省石家庄市"}
// 可指定成员初始化
var s2 Student = Student{
id: 2,
name: "Bob",
age: 19}
fmt.Println("s1:", s1)
fmt.Println("s2:", s2)
echo(s1)
}
func echo(s Student) {
fmt.Println("id:", s.id)
fmt.Println("name:", s.name)
fmt.Println("sex:", s.sex)
fmt.Println("age:", s.age)
fmt.Println("addr:", s.addr)
}
结构体指针变量
go
package main
import "fmt"
type Student struct {
id int
name string
sex byte
age int
addr string
}
func main() {
// 每个成员都必须按顺序初始化
var s1 *Student = &Student{1, "Angle", 'f', 18, ""}
fmt.Println("s1:", *s1) //s1: {1 Angle 102 18 }
fmt.Printf("%+v", *s1) //{id:1 name:Angle sex:102 age:18 addr:}
}
值传递&地址传递
go
package main
import "fmt"
type Student struct {
id int
name string
sex byte
age int
addr string
}
func main() {
s1 := Student{1, "mike", 'm', 18, "bj"}
fmt.Printf("%+v\n", s1) //{id:1 name:mike sex:109 age:18 addr:bj}
reset(s1)
fmt.Printf("%+v\n", s1) //{id:1 name:mike sex:109 age:18 addr:bj}
reset1(&s1)
fmt.Printf("%+v\n", s1) //{id:0 name:- sex:45 age:0 addr:-}
}
func reset(s Student) { // 形参无法改变实参,因为这里是值传递
s.id = 0
s.name = "-"
s.sex = '-'
s.age = 0
s.addr = "-"
}
func reset1(s *Student) { // 这里是址传递
s.id = 0
s.name = "-"
s.sex = '-'
s.age = 0
s.addr = "-"
}
匿名字段
go
package main
import "fmt"
type People struct {
name string
sex byte
age int
}
type Student struct {
People // 只有类型,没有名字
id int
addr string
}
func main() {
// 顺序初始化,自动推导类型
n1 := Student{People{"Bob", 'm', 22}, 1, "河北省石家庄市长安区"}
// +v显示更详细的信息
fmt.Printf("%+v\n", n1) //{People:{name:Bob sex:109 age:22} id:1 addr:河北省石家庄市长安区}
// 指定成员初始化,没有初始化的成员自动赋值为0
s1 := Student{People: People{age: 22}, id: 1} // 这时候,得加People:否则编译不过
fmt.Printf("%+v\n", s1) //{People:{name: sex:0 age:22} id:1 addr:}
s1 = n1
fmt.Println(s1.name, s1.sex, s1.age, s1.id, s1.addr) //Bob 109 22 1 河北省石家庄市长安区
fmt.Println(s1.People.name, s1.People.sex, s1.People.age, s1.id, s1.addr)//Bob 109 22 1 河北省石家庄市长安区
}
基本类型绑定函数
注意,基本类型不能直接绑定,必须要先声明一个别名才能绑定,好奇葩啊
go
package main
import "fmt"
type MyInt int //自定义类型,给int改名为MyInt
// 在函数定义时,在其名字之前放上一个变量,即是一个方法
func (a MyInt) Add(b MyInt) MyInt { //面向对象
return a + b
}
// 传统方式的定义
func Add(a, b MyInt) MyInt { //面向过程
return a + b
}
func main() {
var a MyInt = 1
var b MyInt = 1
//调用func (a MyInt) Add(b MyInt)
add := a.Add(b)
fmt.Printf("type = %T , a.Add(b) = %d \n", add, add) //type = main.MyInt , a.Add(b) = 2
//调用func Add(a, b MyInt)
fmt.Println("Add(a, b) = ", Add(a, b)) //Add(a, b) = 2
}
对象绑定函数
写法很奇葩,哎,无语
go
package main
import (
"fmt"
)
type People struct {
name string
sex byte
age int
}
// 带有接收者的函数叫方法,接收者本身不能是指针(这里的接收者是People对象)
// 简单的来说就是给对象增加一个函数
func (p People) output() {
fmt.Println("姓名:", p.name)
fmt.Println("性别:", p.sex)
fmt.Println("年龄:", p.age)
}
func main() {
// 定义同时初始化
p := People{"Bob", 'm', 19}
p.output()
}
绑定对象+地址引用
go
package main
import "fmt"
type People struct {
name string
sex byte
age int
}
func (p People) output() {
fmt.Println("姓名:", p.name)
fmt.Println("性别:", p.sex)
fmt.Println("年龄:", p.age)
fmt.Println("===================")
}
// 接收者为People类对象
func (p People) addAgeV() {
p.age++
}
// 接收者为People类对象的指针
func (p *People) addAgeP() {
p.age++
}
func main() {
p := People{"Bob", 'm', 19}
p.output()
p.addAgeV()
p.output()
p.addAgeP()
p.output()
/**
姓名: Bob
性别: 109
年龄: 19
===================
姓名: Bob
性别: 109
年龄: 19
===================
姓名: Bob
性别: 109
年龄: 20
===================
*/
}
接口
- 接⼝命名习惯以 er 结尾
- 接口只有方法声明,没有实现,没有数据字段
- 接口可以匿名嵌入其它接口,或嵌入到结构中
接口的定义
接口的定义,以及多态的实现
go
package main
import "fmt"
type Humaner interface {
sayHi()
sayYes()
}
type People struct {
name string
sex byte
age int
}
type Student struct {
People
id int
addr string
}
func (p *People) sayHi() {
fmt.Printf("People Hi, %s\n", p.name)
}
func (p *People) sayYes() {
fmt.Printf("People yes, %s\n", p.name)
}
func (p *Student) sayHi() {
fmt.Printf("Student Hi, %d-%s\n", p.id, p.name)
}
func (p *Student) sayYes() {
fmt.Printf("Student yes, %d-%s\n", p.id, p.name)
}
// 多态化调用,变相多态
func whoSayHi(i Humaner) {
i.sayHi()
}
func main() {
// 定义接口类型的变量
var i Humaner
// 只要实现此接口方法的类型,那么这个变量的类型(接收者类型)就可以给i赋值
p := &People{"Bob", 'm', 19}
i = p
i.sayHi() // 依照接收者类型选择不同的方法
i.sayYes()
fmt.Println("===============================================")
s := &Student{People{"Ann", 'f', 17}, 1, "河北省保定市"}
i = s
i.sayHi()
i.sayYes()
fmt.Println("===============================================")
whoSayHi(p)
whoSayHi(s)
fmt.Println("===============================================")
x := make([]Humaner, 2)
x[0] = p
x[1] = s
for _, i := range x {
i.sayHi()
}
/**
People Hi, Bob
People yes, Bob
===============================================
Student Hi, 1-Ann
Student yes, 1-Ann
===============================================
People Hi, Bob
Student Hi, 1-Ann
===============================================
People Hi, Bob
Student Hi, 1-Ann
*/
}
空接口
空接口(interface{})不包含任何的方法,正因为如此,所有的类型都实现了空接口,因此空接口可以存储任意类型的数值。它有点类似于C语言的void *类型。
go
var v1 interface{} = 1 // 将int类型赋值给interface{}
var v2 interface{} = "abc" // 将string类型赋值给interface{}
var v3 interface{} = &v2 // 将*interface{}类型赋值给interface{}
var v4 interface{} = struct{ X int }{1}
var v5 interface{} = &struct{ X int }{1}
当函数可以接受任意的对象实例时,我们会将其声明为interface{},最典型的例子是标准库fmt中PrintXXX系列的函数,例如:
go
func Printf(fmt string, args ...interface{})
func Println(args ...interface{})
类型查询
我们知道interface的变量里面可以存储任意类型的数值(该类型实现了interface)。那么我们怎么反向知道这个变量里面实际保存了的是哪个类型的对象呢?目前常用的有两种方法:
- comma-ok断言
- switch测试
comma-ok断言
Go语言里面有一个语法,可以直接判断是否是该类型的变量: value, ok = element.(T),这里value就是变量的值,ok是一个bool类型,element是interface变量,T是断言的类型。
如果element里面确实存储了T类型的数值,那么ok返回true,否则返回false。
go
package main
import "fmt"
type Element interface{}
type Person struct {
name string
age int
}
func main() {
list := make([]Element, 3)
list[0] = 1 // an int
list[1] = "Hello" // a string
list[2] = Person{"mike", 18}
for index, element := range list {
if value, ok := element.(int); ok {
fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
} else if value, ok := element.(string); ok {
fmt.Printf("list[%d] is a string and its value is %s\n", index, value)
} else if value, ok := element.(Person); ok {
fmt.Printf("list[%d] is a Person and its value is [%s, %d]\n", index, value.name, value.age)
} else {
fmt.Printf("list[%d] is of a different type\n", index)
}
}
/* 打印结果:
list[0] is an int and its value is 1
list[1] is a string and its value is Hello
list[2] is a Person and its value is [mike, 18]
*/
}
switch测试
go
package main
import "fmt"
type Element interface{}
type Person struct {
name string
age int
}
func main() {
list := make([]Element, 3)
list[0] = 1 //an int
list[1] = "Hello" //a string
list[2] = Person{"mike", 18}
for index, element := range list {
switch value := element.(type) {
case int:
fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
case string:
fmt.Printf("list[%d] is a string and its value is %s\n", index, value)
case Person:
fmt.Printf("list[%d] is a Person and its value is [%s, %d]\n", index, value.name, value.age)
default:
fmt.Println("list[%d] is of a different type", index)
}
}
/**
list[0] is an int and its value is 1
list[1] is a string and its value is Hello
list[2] is a Person and its value is [mike, 18]
*/
}
异常处理
error的用法
go
package main
import (
"errors"
"fmt"
)
func main() {
var err1 error = errors.New("a normal err1")
fmt.Println(err1) //a normal err1
var err2 error = fmt.Errorf("%s", "a normal err2")
fmt.Println(err2) //a normal err2
}
函数通常在最后的返回值中返回错误信息:
go
package main
import (
"errors"
"fmt"
)
func Divide(a, b float64) (result float64, err error) {
if b == 0 {
result = 0.0
err = errors.New("runtime error: divide by zero")
return
}
result = a / b
err = nil
return
}
func main() {
r, err := Divide(10.0, 0)
if err != nil {
fmt.Println(err) //错误处理 runtime error: divide by zero
} else {
fmt.Println(r) // 使用返回值
}
}
panic的用法
我们不应通过调用panic函数来报告普通的错误,而应该只把它作为报告致命错误的一种方式。当某些不应该发生的场景发生时,我们就应该调用panic。
go
package main
import (
"fmt"
)
func TestA() {
fmt.Println("func TestA()")
}
func TestB() {
panic("func TestB(): panic")
}
func TestC() {
fmt.Println("func TestC()")
}
func main() {
TestA()
TestB() //TestB()发生异常,中断程序
TestC()
/**
func TestA()
panic: func TestB(): panic
goroutine 1 [running]:
main.TestB(...)
E:/go_ws/demo/test.go:12
main.main()
E:/go_ws/demo/test.go:21 +0x5b
*/
}
recover的用法
go
package main
import (
"fmt"
)
func TestA() {
fmt.Println("func TestA()")
}
func TestB() (err error) {
defer func() { //在发生异常时,设置恢复
if x := recover(); x != nil {
//panic value被附加到错误信息中;
//并用err变量接收错误信息,返回给调用者。
err = fmt.Errorf("internal error: %v", x)
}
}()
panic("func TestB(): panic")
}
func TestC() {
fmt.Println("func TestC()")
}
func main() {
TestA()
err := TestB()
fmt.Println(err)
TestC()
/***
func TestA()
internal error: func TestB(): panic
func TestC()
*/
}