Go 基础之接口interface、文件读写
Go 6
自定义类型和类型别名
type Myint int // 自定义类型
type newint = int // 类型别名
类型别名只在代码编写过程中有效,编译完之后就不存在,内置的byte
和rune
都属于类型别名。
结构体
结构体是一种数据类型,一种可以保存多个维度数据的类型。
type person struct {
name string
age int
id int64
addr string
}
匿名结构体
多用于临时场景
var a = struct{
x int
y string
}{10,"hina"}
结构体的初始化
var p1 person
p1.name="hina"
p1.age=18
p2:=person{
name:"hina",
age:18;
}
p3 := person{"hina",18}
构造函数
fun newperson(name string, age int) person {
return person{
name:name,
age,age,
}
}
方法和接收者
方法是有接受者的函数,接受者指的是那个类型的变量可以调用这个函数。
func (p person)dream(str string){
fmt.Printf("%s的梦想是%s",p.name,str)
}
指针接收者:
- 需要修改结构体变量的值
- 结构体本身比较大,拷贝内存代价大
- 保持一致性:如果有一个方法使用了指针接收者,其他方法也要统一
func (p person) guonian(){
p.age++
}
结构体的嵌套
type addr struct{
province string
city string
}
type student struct{
name string
address addr
}
结构体的匿名字段
type addr struct{
province string
city string
}
type student struct{
name string
addr // 使用时用类名做名称
}
序列化与反序列化
经常出现的问题:
- 结构体内部的字段首字母要大写!!!不大写在该文件的外部别人访问不到
- 反序列化时要传递指针
type point struct {
X int `json:"hina"`
Y int `json:"rui"`
}
func main() {
p1 := point{100, 200}
b, err := json.Marshal(p1)
if err != nil {
fmt.Printf("marshal failed!err:%v\n", err)
}
fmt.Println(string(b))
// 反序列化:字符串——>结构体变量
str1 := `{"hina":10,"rui":20}`
var p2 point // 造一个结构体变量准备接收反序列化的值
err = json.Unmarshal([]byte(str1), &p2)
if err != nil {
fmt.Printf("unmarshal failed!err:%v\n", err)
}
fmt.Println(p2)
}
homework
main.go
package main
import (
"fmt"
"os"
)
// 学生管理系统
var smr manager // 声明一个全局的学生管理 smr
func showmeau() {
fmt.Println("welcome sms!")
fmt.Println(`
0.退出
1.查看学生
2.添加学生
3.修改学生
4.删除学生
`)
}
func main() {
smr = manager{ // 修改了全局的变量 :=是声明一个新的变量
allstu: make(map[int64]*student),
}
for {
showmeau()
// 等待用户输入
fmt.Print("请输入指令:")
var choice int
fmt.Scanln(&choice)
switch choice {
case 1:
smr.showstu()
case 2:
smr.addstu()
case 3:
smr.editstu()
case 4:
smr.delstu()
case 0:
os.Exit(1)
default:
fmt.Println("指令有误,请重新输入")
}
}
}
manager.go
package main
import "fmt"
// 结构体:1.保存数据——字段。2.功能——方法
type student struct {
id int64
name string
}
type manager struct {
allstu map[int64]*student
}
// 查看、增加、修改、删除
// 查看
func (m *manager) showstu() {
for _, stu := range m.allstu { // stu是每一个具体的学生
fmt.Printf("学号:%d,姓名:%s\n", stu.id, stu.name)
}
}
// 增加
func (m *manager) addstu() {
// 1.根据用户输入的内容创建一个新的学生
var (
sid int64
sname string
)
// 获取用户输入
fmt.Print("请输入学号:")
fmt.Scanln(&sid)
fmt.Print("请输入姓名:")
fmt.Scanln(&sname)
// 根据用户输入创建结构体对象
newstu := student{
id: sid,
name: sname,
}
// 2.把新的学生放到allstu中
m.allstu[sid] = &newstu
fmt.Println("创建成功")
}
// 修改
func (m *manager) editstu() {
// 获取用户输入学号找到该学生信息
var sid int64
fmt.Print("请输入学号:")
fmt.Scanln(&sid)
// 展示该学号对应的学生信息,如果没有提示查无此人
sobj, ok := m.allstu[sid]
if !ok {
fmt.Println("查无此人")
return
}
fmt.Printf("该学生信息如下:学号:%d, 姓名:%s\n", sobj.id, sobj.name)
// 输入修改的学生们
fmt.Println("请输入学生新的名字:")
var newname string
fmt.Scanln(&newname)
// 更新学生姓名
sobj.name = newname
}
// 删除
func (m *manager) delstu() {
// 请用户输入要删除学生的id
var sid int64
fmt.Print("请输入要删除学生的学号:")
fmt.Scanln(&sid)
// 去map中找到该学生,若没有则查无此人
_, ok := m.allstu[sid]
if !ok {
fmt.Println("查无此人")
return
}
delete(m.allstu, sid)
fmt.Println("删除成功")
}
接口 interface
接口是一种类型
在编程中会遇到以下场景:
不关心变量的类型,只关心能调用什么方法
类比python中鸭子模型
package main
import "fmt"
// 定义一个
type speaker interface {
speak() // 只要实现了speak方法的变量都可是speaker类型
}
type cat struct{}
type dog struct{}
func (c cat) speak() {
fmt.Println("miaomiao~")
}
func (d dog) speak() {
fmt.Println("wangwang~")
}
func beat(x speaker) {
// 接受一个参数,传进来什么,就执行什么
x.speak()
}
func main() {
var c1 cat
var d1 dog
beat(c1)
beat(d1)
}
package main
import "fmt"
type car interface {
run()
}
type fala struct {
brand string
}
type baoshi struct {
brand string
}
func (f fala) run() {
fmt.Println("fafafa~")
}
func (b baoshi) run() {
fmt.Println("bababa~")
}
func drive(c car) {
c.run()
}
func main() {
var f1 = fala{
brand: "法拉",
}
var b1 = baoshi{
brand: "baoshi",
}
drive(f1)
drive(b1)
}
接口的定义
type 接口名 interface {
方法名1(参数1,参数2...)(返回值1, 返回值2...)
方法名1(参数1,参数2...)(返回值1, 返回值2...)
...
}
用来给变量\参数\返回值等设置类型。
接口的实现
一个变量如果实现了接口中规定的所有的方法,那么这个变量就实现了这个接口,可以称为这个接口类型的变量。
package main
import "fmt"
// 接口的实现
type animal interface {
move()
eat(string)
}
type cat struct {
name string
feet int8
}
func (c cat) move() {
fmt.Println("maodong")
}
func (c cat) eat(s string) {
fmt.Println(c.name, "正在吃", s)
}
type chicken struct {
feet int8
}
func (c chicken) move() {
fmt.Println("jidong")
}
func (c chicken) eat(s string) {
fmt.Println("xiaoji正在吃", s)
}
func main() {
var a1 animal // 定义一个接口类型的变量
bc := cat{ // 定义一个cat类型的变量bc
name: "taozi",
feet: 4,
}
fmt.Printf("%T,%T,%v\n", a1, a1, a1)
a1 = bc
a1.eat("xiaohuangyu")
fmt.Printf("%T\n", a1)
fmt.Println(a1)
kfc := chicken{
feet: 2,
}
a1 = kfc
a1.eat("xxx")
fmt.Println(a1)
fmt.Printf("%T\n", a1)
}
指针接受者与之接受者实现接口的方法的区别?(通常用指针)
使用值接受者实现接口,结构体类型和结构体指针类型的变量都能存。
指针接受者实现接口只能存结构体指针类型的变量。
package main
// 使用值接受者和指针接受者的区别
import "fmt"
type animal interface {
move()
eat(string)
}
type cat struct {
name string
feet int8
}
// 使用值接受者实现接口的所有方法
// func (c cat) move() {
// fmt.Println("maodong")
// }
// func (c cat) eat(s string) {
// fmt.Println(c.name, "正在吃", s)
// }
// 使用指针接受者实现接口的所有方法
func (c *cat) move() {
fmt.Println("maodong")
}
func (c *cat) eat(s string) {
fmt.Println(c.name, "正在吃", s)
}
func main() {
var a1 animal
c1 := cat{"tom", 4} // cat
c2 := &cat{"假老练", 4} // *cat
a1 = &c1 // 实现animal这个接口的是cat的指针类型
fmt.Println(a1)
a1 = c2
fmt.Println(a1)
a1.eat("ccc")
}
接口与类型的关系
多个类型可以实现同一个接口。
一个类型可以实现多个接口。
package main
import (
"fmt"
)
// 同一个结构体可以实现多个接口
// 接口还可以嵌套
type animal interface {
// move()
// eat(string)
mover
eater
}
type mover interface {
move()
}
type eater interface {
eat(string)
}
type cat struct {
name string
feet int8
}
// cat实现了mover接口
func (c *cat) move() {
fmt.Println("maodong")
}
// cat实现了eater接口
func (c *cat) eat(s string) {
fmt.Println(c.name, "正在吃", s)
}
func main() {
var a1 animal
c1 := cat{"tom", 4}
a1 = &c1
fmt.Println(a1)
a1.move()
a1.eat("xxx")
}
空接口
没有必要起名字,通常定义成下面的格式
interface{} // 空接口
特点:所有的类型都实现了空接口,也就是任意类型都能保存到空接口中,也就是任意类型都能当参数传入例如:fmt.Println()
package main
import "fmt"
// 空接口
// interface:关键字
// interface{}:空接口类型
func main() {
var m1 = make(map[string]interface{}, 16)
m1["name"] = "hina"
m1["age"] = 18
m1["married"] = true
m1["hobby"] = [...]string{"唱", "跳", "rap"}
fmt.Println(m1)
show(false)
show(nil)
show(m1)
}
// 空接口作为函数的参数
func show(a interface{}) {
fmt.Printf("type:%T, value:%v\n", a, a)
}
接口保存分为两部分:值得类型和值本身
![æ¥å£å¼å¾è§£](Go 6.assets/interface.png)
类型断言:想知道空接口接收的值具体是什么类型的。
package main
import "fmt"
// 类型断言
func assign(a interface{}) {
fmt.Printf("%T\n", a)
str, ok := a.(string)
if !ok {
fmt.Println("猜错了")
} else {
fmt.Println("是字符串", str)
}
}
// 类型断言2
func assign2(a interface{}) {
fmt.Printf("%T\n", a)
switch t := a.(type) {
case string:
fmt.Println("是字符串", t)
case int:
fmt.Println("是int", t)
case int64:
fmt.Println("是int64", t)
case bool:
fmt.Println("是bool", t)
}
}
func main() {
// assign(100)
assign2(true)
assign2("hihi")
assign2(int64(22))
}
接口的注意事项:只有当两个或两个以上的具体类型必须以相同的方式进行处理才需要定义接口。
包 package
- 包的路径从
GOPATH/src
后面的路径开始写起,路径分隔符用/
- 向北别的包调用的标识符都要首字符大写!
- 单行导入和多行导入
- 导入包的时候可以指定别名
- 导入包不想使用包内部的标识符,需要使用匿名导入
- 每个包导入的时候会自动执行一个
init()
的函数,他没有参数,也没用返回值,也不能手动调用。 - 多个包中都定义了
init()
函数,则他们的执行顺序见下图:
![å ä¹é´çinit()æ§è¡é¡ºåº](Go 6.assets/init02.png)
package calc
// 包中的标识符(变量名\函数名\结构体\接口等)如果首字母是小写的,表示私有(只能在当前这个包中使用)
// 首字母大写的标识符可以被外部的包调用
func Add(x, y int) int {
return x + y
}
package main
import (
"fmt"
calc "go.study.com/hina/day01/day06"
)
func main() {
res := calc.Add(100, 10)
fmt.Println(res)
}
func init(){
fmt.Println("import 我时自动执行")
}
文件操作
1.字节流[]byte读文件
package main
import (
"fmt"
"os"
)
// 打开文件
func main() {
fileobj, err := os.Open("./main.go")
if err != nil {
fmt.Printf("open file failed, err:%v\n", err)
return
}
// 记得关闭文件
defer fileobj.Close()
// 读文件
// var tmp = make([]byte, 128)
var tmp [128]byte // 读指定长度
for {
n, err := fileobj.Read(tmp[:])
if err != nil {
fmt.Printf("read from file failed, err:%v\n", err)
return
}
fmt.Printf("读了%d个字节\n", n)
fmt.Println(string(tmp[:n]))
if n < 128 {
return
}
}
}
2.bufio一行行读文件
//使用bufio读文件
func bufioreadfile() {
fileobj, err := os.Open("./main.go")
if err != nil {
fmt.Printf("open file failed, err:%v\n", err)
return
}
// 记得关闭文件
defer fileobj.Close()
// 创建一个用来从文件中读内容的对象
reader := bufio.NewReader(fileobj)
for {
line, err := reader.ReadString('\n')
if err == io.EOF {
return
}
if err != nil {
fmt.Printf("read line failed, err:%v\n", err)
return
}
fmt.Print(line)
}
}
3.ioutil读取整个文件
// ioutil读取整个文件
func ioutilreadfile() {
ret, err := ioutil.ReadFile("./main.go")
if err != nil {
fmt.Printf("read file failed, err:%v\n", err)
return
}
fmt.Println(string(ret))
}
文件写入
package main
import (
"bufio"
"fmt"
"io/ioutil"
"os"
)
// 打开文件并写入
func readwrite() {
fileobj, err := os.OpenFile("./xx.txt", os.O_TRUNC|os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
defer fileobj.Close()
if err != nil {
fmt.Printf("open file failed,err:%v\n", err)
return
}
// write
fileobj.Write([]byte("hina xixixi!\n"))
// writeString
fileobj.WriteString("hina 呵呵呵!\n")
}
func bufiowrite() {
fileobj, err := os.OpenFile("./xx.txt", os.O_TRUNC|os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
fmt.Printf("open file failed,err:%v\n", err)
return
}
defer fileobj.Close()
// 创建一个写的对象
wr := bufio.NewWriter(fileobj)
wr.WriteString("hina嘿嘿嘿!!!") // 写到缓存中
wr.Flush() // 将缓存中的内容写入文件
}
func ioutilwrite() {
str := "hina哈哈哈!!"
err := ioutil.WriteFile("./xx.txt", []byte(str), 0000)
if err != nil {
fmt.Println("write file failed, err:", err)
return
}
}
func main() {
// readwrite()
// bufiowrite()
ioutilwrite()
}
终端获取打印内容Scanln升级版
Scanln无法获取带空格的字符串
package main
import (
"bufio"
"fmt"
"os"
)
// 获取用户输入时如果有空格
func usescan() {
var s string
fmt.Print("请输入内容")
fmt.Scanln(&s)
fmt.Printf("输入的内容是:%s\n", s)
}
func usebufio() {
var s string
reader := bufio.NewReader(os.Stdin) // os.Stdin获取终端输入内容
fmt.Print("请输入内容")
s, _ = reader.ReadString('\n')
fmt.Printf("输入的内容是:%s\n", s)
}
func main() {
// usescan()
usebufio()
}
日志操作
package main
import (
"bufio"
"fmt"
"os"
)
// 获取用户输入时如果有空格
func usescan() {
var s string
fmt.Print("请输入内容")
fmt.Scanln(&s)
fmt.Printf("输入的内容是:%s\n", s)
}
func usebufio() {
var s string
reader := bufio.NewReader(os.Stdin) // os.Stdin获取终端输入内容
fmt.Print("请输入内容")
s, _ = reader.ReadString('\n')
fmt.Printf("输入的内容是:%s\n", s)
}
func main() {
// usescan()
// usebufio()
// logger.Trace()
// logger.Dubug()
// logger.Warning()
// logger.Info()
// logger.Error()
// 写日志
fmt.Fprintf(os.Stdout, "这是一条日志记录") // io.Writer 其实就是文件句柄
fileobj, _ := os.OpenFile("./xxx.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
fmt.Fprintf(fileobj, "这是一条日志记录")
}