Go入门(三)
@
一.内置函数
内置函数 | 介绍 |
---|---|
close | 关闭channel的 |
len | 求长度,例如 string,array,slice,map,channel |
new | 用来分配内存,主要用来分配值类型,如int、struct。返回指针类型 |
make | 用来分配内存,用来分配引用类型,如channel |
append | 用来追加元素到数组、slice 中 |
panic 和 recover | 用来做错误处理 |
二. panic 和 recover
Go 语言中,没有异常机制,但是使用panic/recover
模式来处理错误,panic
可以在任何地方引发,但是recover
只有在 defer
调用的函数中有效,首先看一个例子。
1.简单例子
package main
import "fmt"
func funcA() {
fmt.Println("func A")
}
func funcB() {
fmt.Println("func B")
}
func funcC() {
fmt.Println("func C")
}
func main() {
funcA()
funcB()
funcC()
}
// 输出
func A
func B
func C
2.添加panic
关键字
使用panic,相当于刨出了一个异常。
package main
import "fmt"
func funcA() {
fmt.Println("func A")
}
func funcB() {
fmt.Println("func B")
panic("出现了严重的错误") // 程序崩溃退出
}
func funcC() {
fmt.Println("func C")
}
func main() {
funcA()
funcB()
funcC()
}
// 运行结果
func A
func B
panic: 出现了严重的错误
3.立即执行函数的应用
panic 的执行顺序是在 defer 后面:
import "fmt"
func funcA() {
fmt.Println("func A")
}
func funcB() {
defer func() {
fmt.Println("释放数据库链接退出")
}() // 立即执行函数
fmt.Println("func B")
panic("出现了严重的错误") // 程序崩溃退出
}
func funcC() {
fmt.Println("func C")
}
func main() {
funcA()
funcB()
funcC()
}
// 输出:
func A
func B
释放数据库链接退出
panic: 出现了严重的错误
4.使用 recover
捕获异常
他能对异常进行捕获,并进行一些处理,他和上边的最大的区别,就是 funcC() 函数执行了,他能保证代码继续往后执行。
func funcA() {
fmt.Println("func A")
}
func funcB() {
defer func() {
err := recover() // 使用recover 捕获错误
fmt.Println(err) // 把异常输出
if err != nil {
fmt.Println("释放数据库链接退出")
}
}() // 立即执行函数
fmt.Println("func B")
panic("出现了严重的错误") // 程序崩溃退出
}
func funcC() {
fmt.Println("func C")
}
func main() {
funcA()
funcB()
funcC()
}
// 输出
func A
func B
出现了严重的错误
释放数据库链接退出
func C
4. 注意:
- recover() 必须搭配 defer 使用;
- defer 一定要在可能引发panic的语句之前定义;
recover(),一定是在异常报错以后使用,所以他一定要用defer搭配。
因为 panic 一旦触发,他后面的代码就不会执行了,所以你如果现在后面,他永远不会执行。
三.fmt标准库
1.输出
占位符 | 说明 |
---|---|
%V | 默认格式打印 |
%T | 打印值类型 |
%s | 打印字符串 |
func main() {
s := "小王子"
fmt.Printf("%s\n", s)
fmt.Printf("%5s\n", s)
fmt.Printf("%-1s\n", s)
}
// 输出如下:
小王子
小王子
小王子
二进制输出
func main() {
fmt.Printf("%b", 100)
}
2.输入:
Go语言fmt
包中有fmt.Scan
、fmt.Scanf
、fmt.Scanln
三个函数,可以在程序运行的过程中,从标准输入获得用户的输入。
- Scan 从标准输入扫描文本,读取由空白符分割的值保存到传递给本函数的参数中,换行视为空白符。
读入一定要使用指针。
var s string
fmt.Scan(&s)
fmt.Println("你好", s)
var (
name string
age int
class string
)
fmt.Scanf("%s %d %s \n", &name, &age, &class)
fmt.Println(name, age, class)
// 输入
aa 18 bb
aa 18 bb
使用fmt.Scanln
比fmt.Scanf
可以少输入一个百分号。
func main() {
var (
name string
age int
class string
)
fmt.Scanln(&name, &age, &class)
fmt.Println(name, age, class)
}
// 输出
aa 18 bb
aa 18 bb
四.使用go解决分金币的问题
你有50枚金币,需要分配给以下几个人,Matthew,Sarah,Augustus,Heidi,Emilie,Peter,Giana,Adriano,Adron,Eliazbeth :
分配规则如下:
a:名字中有 一个‘e’ 或 'E' 的分1枚金币
b:名字中有 一个‘i’ 或 'I' 的分2枚金币
c:名字中有一个‘0’或‘O’ 分三枚金币,
d:名字中有 一个‘u’ 或 'U' 的分4枚金币
写一个程序,计算每个用户分到多少金币,以及最后剩多少金币?
五.递归
简单点来讲,递归就是自己调用自己(如下)
// digui
func f1() {
f1()
}
func main() {
f1()
}
递归的应用场景,例如,删除文件夹里的文件,要先进入文件夹,然后删除文件,如果遇到文件夹,那么就继续进去,然后采用上一步的操作。继续看到文件就删除,看到文件夹就进去。
1. 递归适用于处理那种问题相同,规模越来越小的场景。
2. 递归一定要有一个明确的退出条件。
例如下面的阶层算法:
func f1(n int) int {
if n <= 1 {
return 1
}
return n * f1(n-1)
}
func main() {
rel := f1(8)
fmt.Println(rel)
}
用 Go 实现上台阶的问题。
有n个台阶,一次可以走一步,也可以走两步,有多少种走法。
func f1(n int) int {
if n <= 1 {
return 1 // 如果只有一个台阶,那么就只有一个
}
if n == 2 {
return 2 // 如果最后剩下两个台阶,那么就有两种写法
}
// 最后剩下一个台阶的方法,加上最后一个台阶的方法
return f1(n-1) + f1(n-2)
}
func main() {
rel := f1(4)
fmt.Println(rel)
}
// 输出5
六.自定义类型和类型别名
自定义类型是指的自己定义的类型,类型别名是指的这个类型就是本来应该有的类型,只不过我给他另外起了一个名字。
// type 是用来造类型的 (基于内置的类型,创造新的类型)
type myInt int // 自定义类型
type youInt = int // 类型别名
func main() {
var n myInt
n = 100
fmt.Println(n)
fmt.Printf("%T\n", n)
var c youInt
c = 100
fmt.Printf("%T\n", c)
}
// 输出
100
main.myInt
int
七.结构体:
type person struct {
name string
age int
gender string
hobby []string
}
func main() {
var p person
p.name = "zhao"
p.age = 20
p.gender = "男"
p.hobby = []string{"网球"}
fmt.Println(p)
}
// 输出
{zhao 20 男 [网球]}
匿名结构体
匿名结构体一般就是临时用一次,临时声明一下,所以一般都是写在方法里面。不是正规的使用方法。
type person struct {
name string
age int
gender string
hobby []string
}
func main() {
var p person
p.name = "zhao"
p.age = 20
p.gender = "男"
p.hobby = []string{"网球"}
fmt.Println(p)
var s struct {
name string
age int
}
s.name = "nihao"
s.age = 15
fmt.Println(s)
}
//输出
{zhao 20 男 [网球]}
{nihao 15}
结构体是指针类型
如何理解?看下面,处理完成以后,并不能改变原来结构体的内容。
GO 函数传递的参数,永远是copy
package main
import "fmt"
type person struct {
name string
gender string
}
func f1(x person) {
x.gender = "女"
}
func main() {
var p person
p.name = "zhao"
p.gender = "男"
f1(p)
fmt.Println(p)
}
// 输出
{zhao 男}
type person struct {
name string
gender string
}
func f1(x person) {
x.gender = "女"
}
func f2(x *person) {
//(*x).gender = "女" // 这是原始的写法
x.gender = "女" // 这是语法糖的写法,
// 因为go语言里面,不支持直接操作指针,所以他会自动的变回来
}
func main() {
var p person
p.name = "zhao"
p.gender = "男"
f1(p)
fmt.Println(p)
f2(&p)
fmt.Println(p)
}
// 输出
{zhao 男}
{zhao 女}
复习一直指针的概念
只有指针才能用%p
来进行取值;
func main() {
a := 10
b := &a
fmt.Println(a) // 表示的是a
fmt.Printf("%p\n", &a)
fmt.Println(&a) // 表示的是对a取值
fmt.Println(b) // 表示的是b的地址
}
// 输出
10
0xc000128058
0xc000128058
0xc000128058
结构体指针的写法
type person struct {
name string
age int
}
func main() {
var p person
p.name = "zhao"
p.age = 12
fmt.Println(p)
var p2 = new(person) // 使用new,返回的是变量的指针
p2.name = "zhang"
fmt.Println(p2.name)
var p3 = person{ // 声明的同时,进行初始化
name: "wang", // 后面必须加 ,号
age: 15, //后面必须加,号
}
fmt.Println(p3)
}
// 输出
{zhao 12}
zhang
{wang 15}
结构体的构造函数
在GO 语言中,是默认没有构造函数这个概念的,但是他可以模仿写一个类似于的构造函数。
构造函数的返回,可以有两种,一种是返回类型,一种是返回指针
- 当类型内的变量比较少的时候,可以直接返回值类型
- 当类型内的变量比较多的时候,为了节约空间,可以选择返回指针类型。
type person struct {
name string
age int
}
// 构造函数的一般默认名字,就是用new 开头
func newPerson(name string, age int) person {
return person{name: name, age: age}
}
func newPerson2(name string, age int) *person {
return &person{name: name, age: age}
}
func main() {
var p1 = newPerson("zhao", 12)
fmt.Println(p1)
var p2 = newPerson2("zhang", 18)
fmt.Println(p2)
fmt.Println(*p2)
}
// 输出
{zhao 12}
&{zhang 18}
{zhang 18}
八.方法
在go 语言里,方法和函数不同,方法是指作用与特定类型变量的函数,这种特定类型变量,叫做接受者,接受者的概念。类似于其他语言的this
或者self
。
type dog struct {
name string
}
func newdog(name string) *dog {
return &dog{name: name}
}
// 方法是作用于特定类型的函数
// 接受者表示的是调用该方法的具体类型变量,多用类型首字母小写
func (d dog) wang() {
fmt.Println(d.name)
fmt.Println("旺旺旺")
}
func main() {
d := newdog("zhao")
d.wang()
}
// 输出
zhao
旺旺旺
标识符:变量名,函数名,类型名,方法名。
GO语言如果标识符的首字母大写,就表示对外部可见(暴露的,公开的)
函数的标准形式
func (接收者,接受者类型) 方法名 (参数列表)(返回参数){
函数体
}
接受变量者的命名,接受者在参数进行命名的时候,建议使用接受者类型名的第一个小写字母,而不是self
this
之类的命名。例如Person
类型的接受者变量应该是p
,Connnector
类型的接受者变量应该命名为 c
等。
指针接受者和值接受者
值类型的接受者,没有办法改变他接受的值。
type Person struct {
name string
age int
}
func newPerson(name string, age int) *Person {
return &Person{
name: name,
age: age,
}
}
func (p Person) guonian() { // 值接受者
p.age = p.age + 1
}
func (p *Person) zhenguonian(age int) { // 指针接受者
p.age = age
}
func main() {
p := newPerson("zhao", 20)
fmt.Println(p.age)
p.guonian()
fmt.Println(p.age)
p.zhenguonian(21)
fmt.Println(p.age)
}
// 输出
20
20
21
什么时候使用指针接受者
- 需要修改接受者的值
- 接受者是拷贝代价比较大的对象
- 保证一致性,如果有某个方法使用了指针接受者,那么其他的方法也应该使用指针接受者。
九.函数版学生管理系统
package main
import (
"fmt"
"os"
)
/*
函数版学生管理系统
写一个系统可以查看、新增、删除、学生
*/
var (
allStudent map[int64]*student // 包含的是 student的指针
)
type student struct {
id int64
name string // 名字
}
// new student 是student的一个构造函数
func newStudent(id int64, name string) *student {
return &student{
id: id,
name: name,
}
}
func showAllStrudent() { // 把所有的学生都打印出来
for k, v := range allStudent {
fmt.Println("学生ID:", k, "学生名字:", v)
}
}
func addStudent() { // 向allstudent 中添加一个新学生。
var (
id int64
name string
)
fmt.Println("请输入id:")
fmt.Scanln(&id)
fmt.Println("输入名字")
fmt.Scanln(&name)
// 调用构造函数添加,生成
nestu := newStudent(id, name)
// 追加到map中
allStudent[id] = nestu
}
func deleteStudent() { // 删除某个学生
/*
1.请输入你要删除学生的学号
2.去 allstudent 这个map 中删除这个键值对
*/
var deletid int64
fmt.Println("请输入你要删除学生的学号")
fmt.Scanln(&deletid)
delete(allStudent, deletid) // 要从map中删除一个人的时候,应该先输入他的 map,然后再输入他的 id
}
func main() {
allStudent = make(map[int64]*student, 48) // 初始化(开辟空间)
for i := 0; i < 100; i++ {
// 1. 打印菜单
fmt.Println("欢迎来到学生管理系统")
fmt.Println(`
1. 查看所有的学生
2. 新增学生
3. 删除学生
4. 退出
`)
// 2. 等待用户选择要做什么
var choice int64
fmt.Print("请输入你想干嘛")
fmt.Scanln(&choice)
fmt.Println("你选择了", choice, "这个选项")
switch choice {
case 1:
showAllStrudent()
case 2:
addStudent()
case 3:
deleteStudent()
case 4:
os.Exit(1) // 退出
default:
fmt.Println("滚~~")
}
}
}