嘘~ 正在从服务器偷取页面 . . .

goNote


Go安装

  • GOPATH:你写Go代码的工作区,保存你的Go代码的。
  • go env
  • GOPATH/bin添加到环境变量
    • 环境变量:当前目录下找不到相关命令时,到环境变量目录中寻找相关命令
    • go install命令会把生成的二进制可执行文件拷贝到bin

Go命令

  • go build:编译Go程序
  • go build -o "xx.exe":编译成xx.exe文件
  • go run main.go:像执行脚本一样执行mian.go文件
  • go install:先编译后拷贝

Go语言文件基础语法

  • 文件第一行:package关键字声明包名
  • 单行注释://
  • 多行注释:/* */
  • 如果要编译一个可执行文件,必须要有一个main包,和一个main函数(入口函数,无参数和返回值)
  • go语言函数外的语句必须以关键字开头
  • 函数内部定义的变量必须使用

变量

  • 三种声明方式:
    • var name type
    • var name = "zhangsan"
    • name := "zhangsan" 只能在函数内使用
  • 匿名变量(哑元变量)
    • 当有些数据必须用变量接收,但又不使用它时,使用_接收

常量

  • const pi = 3.1415926
  • const UserNotExistErr = 10000
  • iota实现枚举
    • iotaconst关键字出现时将被重置为0
    • const中每新增一行常量声明,iota累加1
  • nil指没有分配内存

流程控制

  • if

    • var age = 19
      if age >18{
          fmt.Println("成年了")
      }else if age>7{
          fmt.Println("上小学")
      }else{
          fmt.Println("最快乐的时光")
      }
      
  • for

    • 各种for循环

      for i:=0;i<10;i++{
          fmt.Println(i)
      }
      
      var i = 0
      for ;i<10;i++{
          fmt.Println(i)
      }
      
      for{
          fmt.Println("无限循环")
      }
      
      //基于范围的for循环
      s := "hello"
      fmt.Println(len(s))
      //i是索引
      for i,v:=range s{
          fmt.Printf("第%d个字母是%c\n",i,v)
      }
      
  • switch

    • switch op{
          case 1,2:
          //...(无break)
          case 2,3//...
          case 4,5:
          //...
          default:
          //...
      }
      

基本数据类型

  • 整型

    • 无符号整型

      • uint8,uint16,uint32,uint64
    • 有符号整型

      • int8,int16,int32,int64
    • int具体是32位或64位取决于操作系统

    • uintptr表示指针

    • 其他进制数

      • 八进制

        var n = 0777

      • 十六进制

        var n = 0xff

  • 浮点型

    • float32
    • float64(默认)
    • 两种类型不能直接比较
  • 布尔值

    • bool

    • truefalse

    • 不能和其他类型转换

  • 复数

    • complex128
    • complex64
  • 字符串

    • string

    • 使用双引号、反引号包裹,其中反引号支持多行字符串,且自动转义字符,不需要使用\

    • 字符串不能修改

      • 如果要修改字符串,需要先将其转换成[]rune[]byte类型,完成后再转换成string类型。无论哪种转换,都会重新分配内存,并复制字节数组。

      • s2:="白萝卜"
        s3:=[]rune(s2)//把字符串强制转换乘一个rune切片
        s3[0]='红'
        fmt.Println(string(s3))//把rune切片强制转换成字符串
        
    • go语言中字符串都是utf-8编码,一个常用汉字一般占3个字节

    • 字符串的常用操作

      • var str = fmt.Sprintf("%s%s",str1,str2)
  • byte(uint8)rune(int32)

    • 均属于类型别名

fmt占位符

  • %T查看类型
  • %v查看变量的值,万能的
  • %d查看十进制
  • %b查看二进制
  • %o查看八进制
  • %x查看十六进制
  • %s查看字符串

运算符

  • 算术运算符
    • +,-,*,/
  • 逻辑运算符
    • &&,||,!
  • 位运算符
    • >>,<<,|,^,&
  • 赋值运算符
    • =,+=,-=
  • 比较运算符
    • <,>,<=,>=,==

复合数据类型

数组(Array)

  • var ages [30]int

  • 数组声明包含元素的类型和元素的个数,元素的个数(数组的长度)属于数据类型的一部分

  • 数组是值类型(与之对应的是引用类型

  • 数组的初始化

    • var ages = [30]int{1,2,3,4,5}
    • var ages = [...]int{1,2,3,4,5,6,7,8}
    • var ages = [...]int{1:1,99:2}指定索引
  • 二维数组

    • var a1 [3][2]int表示3个[2]int类型组成的数组

    • 只有外层[]可以使用...

    • 初始化

      • a1 = [3][2]int{
            [2]int{1,2},
            [2]int{3,4},
            [2]int{5,6},
        }
        

切片(slice)

  • 声明切片var s1 []int,没有分配内存,即==nil

  • 切片是引用类型

  • 初始化

    • s1 = []int{1,2,3}
    • make初始化,分配内存
      • s1:=make([]string,2,4)
      • 上面2是长度,4是容量(有容量即分配了内存)
  • 切片与数组

    • 切片的本质是对底层数组的封装,它包含三个元素:底层数组的指针(指向切片第一个元素),切片的长度和切片的容量。
    • 常用操作
      • slice:=array[x:y]
  • 切片不能直接比较

    • 切片唯一合法的比较操作是和nil比较

    • 一个nil值的切片长度和容量都是0,但是一个长度和容量都是0的切片不一定是nil

    • var s1 []int      //len(s1)=0;cap(s1)=0;s1==nil
      s2:=[]int{}       //len(s2)=0;cap(s2)=0;s2!=nil
      s3:=make([]int,0) //len(s3)=0;cap(s3)=0;s3!=nil
      
  • 切片的赋值拷贝

    • 下面的代码演示了拷贝前后谱两个切片共享底层数组,对一个切片的修改会影响另一个切片的内容

    • func main(){
          s1:=make([]int,3)//[0,0,0]
          s2:=s1  //将s1的值赋值给s2,两者共用一个底层数组
          s2[0]=100
          fmt.Println(s1) //[100,0,0]
          fmt.Println(s2) //[100,0,0]
      }
      
  • copy函数

    • copy函数能迅速的将一个切片的数据复制到另外一个切片空间中

    • copy(des,src)

    • func main(){
          //copy复制切片
          a:=[]int{1,2,3,4,5}
          c:=make([]int,5,5)//长度必须要足够
          copy(c,a)
          fmt.Println(a)//[1,2,3,4,5]
          fmt.Println(c)//[1,2,3,4,5]
          c[0]=1000
          fmt.Println(a)//[1,2,3,4,5]
          fmt.Println(c)//[1000,2,3,4,5]
      }
      
  • append函数

    • slice尾部添加数组并返回新的slice对象

    • func main(){
          s:=[]int{1,2}
          s=append(s,3)//若s未初始化,append可帮助其初始化
          fmt.Println(s)//[1,2,3]
      }
      
  • 切片的扩容策略

    • 首先判断,如果新申请容量(cap)大于2倍的旧容量(old.cap),最终容(newcap)量就是新申请的容量(cap)
    • 否则判断,如果旧切片的长度小于1024,则最终容量(newcap)就是旧容量(old.cap)的两倍
    • 否则判断,如果旧切片长度大于等于1024,则最终容量(newcap)从旧容量(old.cap)开始循环增加原来的1/4,直到最终容量(newcap)大于或等于新申请容量(cap)
    • 如果最终容量(cap)计算值溢出,则最终容量就是新申请容量

指针

  • 两个符号&*
  • Go里面的指针不允许被操作修改(++,–),但可以被重新赋值

map

  • map存储的是键值对的数据,也需要申请内存
  • var m1 map[string]int,未分配内存,不可使用
  • **make**初始化:m1 = make(map[string]int,30)
  • 如果键值(key)不存在,则取回对应类型(value)的零值(与布尔值)
    • 同时取出布尔值:score,ok:=map1[key]
  • delete函数
    • delete(map1,key)
    • 如果key不存在,则无操作

函数

  • Go语言中函数传递的都是值

  • func 函数名(参数)返回值{
        函数体
    }
    
  • 变长函数

    • 变长函数被调用时可以有可变个参数

    • 在参数列表最后的类型名称之前使用省略号“...”表示一个变长函数

    • func sum(vals ...int){
          total:=0
          for _,val := range vals { //vals在函数体内是一个int类型的切片
              total += val
          }
          return total
      }
      
    • 当实参已经存在一个slice之后调用变长函数:在最后一个参数后面放一个省略号

      • total = sum(values...)
  • 函数进阶

    • 高级函数:函数作为函数参数或返回值

      • func main(){
            F(Hello)
        }
        
        func F(f func(string)){ //func(string)是f的类型
            f("张三")
        }
        
        func Hello(name string){
            fmt.Println("hello",name)
        }
        
      • func main(){
            f:=F()
            fmt.Println(f(5,6))
        }
        
        func F() func(int,int)int {
            f1:=func(x,y int) int { //函数内部只能定义匿名函数
                return x+y
            }
            return f1
        }
        
    • 闭包

      • 函数和其外部变量的引用

      • func bibao(f func(string),name string) func(){
            return func() {
                f(name)
            }
        }
        
    • defer延迟调用

      • 多用于处理资源的释放
    • 内置函数

      • panic
      • recover

自定义类型和类型别名

  • 自定义类型

    • 定义了一个全新的类型,我们可以基于内置的基本类型定义,也可以通过struct定义

      type Myint int
      

      通过type关键字,Myint就是一种全新的类型,它具有int的特性

  • 类型别名(同一个类型)

    • type Yourint = int
      

结构体

  • 结构体(值类型)

    • type name struct{
          //content
      }
      
    • 当结构体内部数据首字母大写时,对包外为可见状态

  • 匿名结构体(多用于临时场景)

    • var a = struct{
          //content
          x int
          y int
      }{10,20} //实例化
      
  • 构造(结构体变量)函数

    • func newPerson(n string,i int) person {
          return person{
              name: n,
              age: i,
          }
      }
      
  • 方法和接收者

    • 方法是有接收者的函数,接收者指的是哪个类型的变量可以调用这个函数

    • //接受者使用对应类型首字母小写
      func (p person) dream(s string){
          fmt.Printf("%s的梦想是%s",p.name,s)
      }
      
    • 值接收者和指针接收者

      使用指针接受者的情况

      • 需要更改结构体的值时
      • 结构体本身比较大,拷贝的内存开销比较大时
      • 保持一致性,如果有一个方法使用了指针接收者,其他的方法也要使用指针接收者
  • 结构体的嵌套

    • type addr struct{
          province string
          city string
      }
      type student struct{
          name string
          ad addr
      }
      
  • 结构体的匿名字段

  • type addr struct{
        province string
        city string
    }
    type student struct{
        name string
        addr //匿名嵌套结构体
    }
    
  • json序列化与反序列化

    • go语言中结构体变量转换为json格式的字符串
    • json格式到的字符串转换为go语言中能够识别的结构体变量

包(package)

  • 包的定义:package关键字,包名通常和目录名一致,不能包含“_

    • 一个文件夹就是一个包
    • 文件夹里都是.go文件
  • 包的导入:import

    • 包导入路径:$GOPATH/src

    • 单行导入

    • 多行导入

    • 给导入的包起别名

    • 匿名导入

  • 包中标识符(变量名、函数名、结构体名、接口名、常量)的可见性:标识符首字母大写

  • init()

    • 包导入的时候自动执行
    • 一个包里只有一个init()
    • init()没有参数叶没有返回值叶不能调用它
    • 一般用于做一些初始化操作

接口(interface)

  • 接口是一种类型,将实现同一种方法的所有类型进行汇总

  • type mover interface{
        方法的签名(参数)(返回值)
    }
    
  • type person struct{}
    
    type cat struct{}
    
    type dog struct{}
    
    //接口是一种特殊的类型,它规定了变量有哪些方法
    type Speaker interface {
        speak() //只要实现了speak方法的变量都是speaker类型的变量
    }
    
    func (c cat) speak() {
        fmt.Println("喵喵喵~")
    }
    
    func (d dog) speak() {
        fmt.Println("汪汪汪~")
    }
    
    func (p person) speak() {
        fmt.Println("嘤嘤嘤~")
    }
    
    /*发现问题,传入参数的类型不固定*/
    func da(x Speaker) {
        //传入一个参数,传进来什么就打什么
        x.speak() //挨打了就要叫
    }
    
    /*在编程中,会遇到以下场景:
    我不关心一个变量是什么类型,我只关心能调用它的什么方法*/
    
    func main() {
        var c1 cat
        var d1 dog
        var p1 person
    
        da(c1)
        da(d1)
        da(p1)
    }
    
  • 空接口

    • 任意类型都实现了空接口(“interface{}”)
    • 用法
      • 作为函数参数
      • map[string]interface{}
  • 类型断言

    • 只能是接口类型

    • x.(T),多用switch来做类型断言

    • func main(){
          var a interface{}//定义一个空接口变量
          a=100
      
          //x.(T)
          if v1,ok := a.(int8);ok{
              fmt.Println("猜对了",v1)
              return
          }else{
              fmt.Println("none")
          }
      
          //switch
          switch v2:=a.(type){
          case int8:
              fmt.Println("int8",v2)
          case int16:
              fmt.Println("int16",v2)
          case int:
              fmt.Println("int",v2)
          default:
              fmt.Println("none")
          }
      }
      

文件操作

  • 打开文件和关闭文件

    • os.open打开一个文件(只读文件),返回一个*file和一个error。对得到的文件实例调用close()方法关闭文件

    • func main() {
          fileObj, err := os.Open("./file.go") //os.Open与os.Close用来文件的打开和关闭
      
          if err != nil { //常规语句,用来判断读取文件是否出错
              fmt.Printf("open file failed, err=%v", err)
              return
          }
      
          //记得关闭文件
          defer fileObj.Close()
      
          //读文件
          //var tmp = make([]byte, 128) //指定读的长度
          var tmp [128]byte
      
          for {
              n, err := fileObj.Read(tmp[:]) //fileObj.Read把内容读到参数tmp[:]中,返回读取长度与error
      
              if err != nil {
                  fmt.Printf("read from file failed, err:%v", err)
                  return
              }
      
              fmt.Printf("读了%d个字节\n", n)
              fmt.Println(string(tmp[:n])) //string()函数,使参数化为string类型
      
              if n < 128 {
                  return
              }
          }
      }
      
  • 读文件

    • func (f *os.File)Read(b []byte)(n int,err error)

      • 它接收一个字节切片,返回读取的字节数和可能出现的具体错误,读到文件末尾会返回0io.EOF
    • bufio利用缓冲区

      • func readFromFilebyBufio() {
            fileObj, err := os.Open("./file_2.go")
        
            if err != nil {
                fmt.Printf("open file failed, err=%v", err)
                return
            }
        
            //记得关闭文件
            defer fileObj.Close()
        
            //创建一个用来从文件中读内容的对象
            reader := bufio.NewReader(fileObj)
            for {
                line, err := reader.ReadString('\n') //参数是结束符号,返回截止符号前的string与error
                if err == io.EOF {
                    fmt.Println("文件读完")
                    return
                }
                if err != nil {
                    fmt.Printf("reader line failed,err:%v", err)
                    return
                }
        
                fmt.Println(line)
            }
        }
        
        func main() {
            readFromFilebyBufio()
        }
        
    • ioutil包的ReadFile方法能够读取完整的文件,只需要将文件名作为参数传入

      • func readFromFilebyIoutil() { //读取整个文件,且无需打开与关闭文件
            ret, err := ioutil.ReadFile("./file_3.go") //返回[]byte类型与error
            if err != nil {
                fmt.Printf("read file failed, err:%v", err)
                return
            }
            fmt.Printf(string(ret))
        }
        
        func main() {
            readFromFilebyIoutil()
        }
        
  • 写文件

    • os.OpenFile(name string,flag int,perm FileMode)(*File,error)

    • WriteWriteString

    • func main() {
          fileObj, err := os.OpenFile("./xx.txt", os.O_APPEND|os.O_CREATE|os.O_TRUNC, 0644)
          if err != nil {
              fmt.Printf("open file failed,err:%v", err)
              return
          }
      
          //write
          fileObj.Write([]byte("123456789\n")) //写入字节切片数据
      
          //writestring
          fileObj.WriteString("987654321") //直接写入字符串数据
      
          fileObj.Close()
      }
      
    • 利用缓存bufio

    • func writeDemo2() {
          fileObj, err := os.OpenFile("./xx.txt", os.O_APPEND|os.O_CREATE|os.O_TRUNC, 0644)
          if err != nil {
              fmt.Printf("open file failed,err:%v", err)
              return
          }
      
          defer fileObj.Close()
      
          //创建一个写的对象
          wr := bufio.NewWriter(fileObj)
          wr.WriteString("hello沙河\n") //写到缓存中
          wr.Flush()
      }
      
      func main() {
          writeDemo2()
      }
      
    • 利用ioutil

    • func main() {
          str := "hello 沙河"
          err := ioutil.WriteFile("./xx.txt", []byte(str), 0666)
          if err != nil {
              fmt.Println("write file failed, err:", err)
              return
          }
      }
      

版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 樱桃会长成樱花树吗 !
  目录