Go 语言入门指南(三) 青训营

90 阅读3分钟

Go 语言入门指南(三) 青训营

这篇博客继续对go的基础语法进行整理。主要包括:接口、文件操作。

一、接口

接口也有点不好理解,首先注意:==接口是一种类型----抽象类型。==有点像C++的模板类型!底层分为动态类型和动态值两部分。

接口类型对行为(方法)做约束。就像是一种协议、一种规则约束。

不同的类有一个相似的方法,就可以屏蔽类的差异,以这个方法为约束构造一个新的类型---interface

同一个类可能属于多个interface。另外interface 也可以嵌套使用。

看下面的例子:

// 在函数传参时就可以使用speaker这个类型来当形参
// 实参可以是实现了speak方法的任意类
type speaker interface{
  // 具体的方法声明,所有有这些方法的类都可以通过speaker这个interface来统一
  speak()
  // ...
}

// cat类有speak这个方法
type cat struct{
  name string
}
func (c cat)speak(){
	// ...
}
// dog类有speak这个方法
type dog struct{
  name string
}
func (d dog)speak(){
	// ...
}
// pig类有speak这个方法
type pig struct{
  name string
}
func (p pig)speak(){
	// ...
}

func amimalSpeak(x speaker){
  x.speak()
}
func main(){
  c := cat{name: "aaa"}
  d := dog{name: "sss"}
  p := pig{name: "ddd"}
  // 实参可以是实现了speak方法的任何类
  amimalSpeak(c)
  amimalSpeak(p)
  amimalSpeak(d)
  
  // 可以直接赋值!speaker就是抽象类型
  var am speaker 
  fmt.Printf("%T\n", am)  // nil
  // am 有动态类型和动态值
  am = c
  fmt.Printf("%T\n", am)  // main.cat
  am = d 
  fmt.Printf("%T\n", am)  // main.dog
  am = p
  fmt.Printf("%T\n", am)  // main.pig
}
1. 使用值接收者和使用指针接收者的区别

实现值接收者的类可以赋值给interface类型,实现值接收者的类的指针也可以赋值给interface类型。

但是实现指针接收者的类想要赋值给interface类型,必须取地址。(常用)

2. 空接口

应用:函数形参(可以接收任意类型的变量)

mapvalue类型

看下面例子:

// 接口里没有任何约束,也就是任何类型都可以传给空接口
// fmt.Println() 的形参就是典型的空接口

interface{}  // 这个就是空接口类型

// 这个map的value可以是任意类型
var m map[string]interface{}
m = make(map[string]interface{}, 10)
m["aaa"] = "111"
m["sss"] = 1
m["ddd"] = []int{1,2,3}

// 可以接收任意类型的变量
func show(a interface{}){
  
}

二、文件操作

所有的文件操作其实都是对底层操作系统的文件操作做了封装,所以只需要会用那些封装的接口就可以了,go的文件操作主要有三个包:osbufioioutil。下面是简单的接口用法。

打开文件:

// *os.File
func Open(name string) (file *File, err error)

// flag标记:O_WRONLY/O_RDONLY...和底层的一样; FileMode:文件权限
func OpenFile(name string, flag int, perm FileMode) (file *File, err error)

读的操作:

func main(){
  // Open:只读的方式打开文件
  file,err := os.Open("./test.txt")
  if err != nil{
    fmt.Printf("open file test.txt error:%v\n", nil)
    return
  }
  // 关闭文件描述符,并设置成defer,这也是defer的应用场景
  // 在判断完err后写这个关闭!!
  defer file.Close()
 
  // 读取文件方法一
  tmp := make([]byte, 128)
  // 读到tmp里,返回值n表示实际读到的字节数
  n,err := file.Read(tmp)
  if err != nil{
    // ...
  } 
  
  // 读取文件方法二  使用bufio包(基于缓冲区)
  reader := bufio.NewReader(file)
  // reader里有很多读取方法
  line,err := reader.ReadString('\n') // 一次读一行,读到line中
  if err == os.EOF{ 
    // ...
    return
  }
  if err != nil{
    // ...
  }  
  
  // 读取文件方法三  使用ioutil包
  // 不用打开文件,直接读取,函数里肯定还是打开和关闭文件了
  // 直接读取到文件末尾!也就是读取所有内容
  ret,err := ioutil.ReadFile("./xxx.txt")
  if err != nil{
    // ...
  }  
}

写的操作:

file,err := os.OpenFile("./xxx.txt",os.O_CREATE|os.O_WRONLY|os.TRUNC, 	0644)
if err != nil{
 // ...
}
// 一定是在判断完nil后defer关闭
defer file.Close()

// 写入方法一
file.Write([]byte("ssss"))
file.WriteString("sdfsasasa")

// 写入方法二  使用bufio包
writer := bufio.NewWriter(file)
writer.WriteString("sasa")  // 写到缓存里
writer.Flush()              // 刷新缓冲区!!

// 写入方法三  使用ioutil包(基于缓冲区)
// 不用打开文件,直接写入,函数里肯定还是打开和关闭文件了
err := ioutil.WriteFile("./xx.txt", []byte("asdsa"), 0666)
if err != nil{
  // ...
}

这篇博客就总结到这里吧。