走进 GO 语言基础 | 青训营笔记

86 阅读3分钟

走进 GO 语言基础

go语言主要特征

1.自动立即回收。

2.更丰富的内置类型。

3.函数多返回值。

4.错误处理。

5.匿名函数和闭包。

6.类型和接口。

7.并发编程。

8.反射。

9.语言交互性。

Hello World

package main
​
import "fmt"func main(){
   fmt.Println("Hello World!")
}

变量

Go语言在声明变量的时候,会自动对变量对应的内存区域进行初始化操作。每个变量会被初始化成其类 型的默认值,例如: 整型和浮点型变量的默认值为0。 字符串变量的默认值为空字符串。 布尔型变量 默认为 false 。 切片、函数、指针变量的默认为 nil 。

匿名变量

在使用多重赋值时,如果想要忽略某个值,可以使用 匿名变量(anonymous variable) 。 匿名变量 用一个下划线_表示,匿名变量不占用命名空间,不会分配内存,所以匿名变量之间不存在重复声明。

常量

相对于变量,常量是恒定不变的值,多用于定义程序运行期间不会改变的那些值。 常量的声明和变量 声明非常类似,只是把 var 换成了 const ,常量在定义的时候必须赋值。

package main
​
import (
   "fmt"
)
​
func main(){
   var a = "goland"
   var b,c = "hello",123
   var d = true
   var e float64
   e = 1.23
   f := a+"nihao"
   fmt.Println(a,b,c,d,e) //  goland hello 123 true 1.23
   fmt.Println(f) //  golandnihao
​
   const s = 7000
   const h string = "constant"
   fmt.Println(s,h)// 7000 constant 
}

if else

if 在布尔表达式为 true 时,其后紧跟的语句块执行,如果为 false 则执行 else 语句块。

package main
​
import "fmt"func main(){
   if 8%2==0{
      fmt.Println("8 是偶数")
   }
   
   if num := 7;num <0{
      fmt.Println(num+num)
   }else{
      fmt.Println(num)
   }
​
   if i := 0; i<0 {
      fmt.Println(i+10)
   }else if i > 10{
      fmt.Println(i)
   }else {
        fmt.Println(i+3)
   }
​
}

循环

for循环是一个循环控制结构,可以执行指定次数的循环。

package main
​
import "fmt"
​
func main(){
   for i:=0;i<3;i++{
      fmt.Println(i)
   }
    for i:=0;i<20;i++{
      if i%3 == 0{
         continue  // 继续循环
      }
      fmt.Println(i)
    }
   for {          // for后面啥也不加,死循环
         fmt.Println("Hello1")
         break   // 停止循环
      }
   }
}

swich

switch 语句用于基于不同条件执行不同动作,每一个 case 分支都是唯一的,从上直下逐一测试, 直到匹配为止。 Golang switch 分支表达式可以是任意类型,不限于常量。可省略 break,默认 自动终止。

package main
​
import (
   "fmt"
   "time"
)
​
func main(){
   a := 2
   switch a{
   case 1:
      fmt.Println("one")   
      // 不需要加 break
   case 2:
      fmt.Println("two")
   case 3,4:
      fmt.Println("three or four")
   default:
      fmt.Println("other")
   }
​
   t := time.Now()
   switch {
   case t.Hour() < 12:
      fmt.Println("it is before noon")
   case t.Hour() >12:
      fmt.Println("it is after noon")
   }
}

数组

数组是一个具有编号且长度固定的元素序列

  1. 数组:是同一种数据类型的固定长度的序列。
  2. 数组定义: var a [len]int,比如:var a [5]int,
  3. 数组长度必须是常量,且是类型的组成 部分。一旦定义,长度不能变。 5.长度是数组类型的一部分,因此,var a[5] int var a[10]int 是不同的类型。
  4. 数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1
  5. 访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic
  6. 数组是值类型,赋值和传参会复制整个数组,而不是指针。因此改变副本的值,不会改变本身的值。
  7. 支持 "=="、"!=" 操作符,因为内存总是被初始化过的。
  8. 指针数组 [n]*T,数组指针 *[n]T。
package main
​
import "fmt"
​
func main(){
   var a [5]int
   a[4] = 4
   fmt.Println(a[4])   //  4
​
   b := []string{"Hi","Hello","Goland"}
   fmt.Println(b[2],len(b))   // Goland   3
   fmt.Println(b)   //  [Hi Hello Goland]
​
    var c [3][4]int
   num := 1
   for i:=0;i<len(c);i++{
      for j:=0;j<len(c[0]);j++{
         c[i][j] = num
         num++
      }
   }
   fmt.Println(c)   // [  [1 2 3 4] [5 6 7 8] [9 10 11 12]  ]
}

切片(slice)

切片不同于数组可以任意更改长度,通过make来常见一个切片,可以像数组一样取值,使用append来追加元素,使用append时必须把append的结果赋值给原数组。

使用 make 动态创建slice,避免了数组必须用常量做长度的麻烦。还可用指针直接访问底层数组, 退化成普通数组操作。

slice原理实际上是存储了一个长度和一个容量,加一个指向数组的指针,在你执行append操作时如果容量不够,会扩容并且返回新的slice。

package main
​
import "fmt"func main(){
   s := make([]string,3)
   s[0]="a"
   s[1]="b"
   s[2]="c"
   fmt.Println(s[0])    // a
   fmt.Println(s,len(s))    // [a b c] 3
​
   s = append(s, "d")
   s = append(s,"e","f")
   fmt.Println(s)    // [a b c d e f]
​
   s2 := make([]string,len(s))  
   copy(s2,s)     // 复制切片
   fmt.Println(s2,len(s2))    // [a b c d e f] 6
​
   fmt.Println(s[:3])    // 左闭右开  [a b c] 
   fmt.Println(s[2:4])    //   [c d]
   fmt.Println(s[3:])     //   [d e f]
​
   s3 := []int{1,2,3,4,5}
   fmt.Println(s3)     // [1 2 3 4 5]
}

map

map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能使用。

map 实际过程中最常用的数据结构

用make来创建map时需要两个类型,一个key一个value,我们可以从里面取出键值对,也可以用delete删除

map是无序的,遍历时按照随机顺序

package main
​
import "fmt"func main(){
   m := make(map[string]int)
   m["one"] = 1
   m["two"] = 2
   fmt.Println(m)     //  map[one:1 two:2]
   fmt.Println(len(m))   //  2
   fmt.Println(m["one"])    // 1
   fmt.Println(m["three"])   //  0  没有对应的键值对
​
   r,ok := m["three"]    // r接收value,ok 判断是否有该键值对
   fmt.Println(r,ok)    // 0 false
​
   delete(m,"one")
   fmt.Println(m)    //  map[two:2]
​
   m2 := map[int]string{1:"one",2:"two"}
   var m3 = map[int]string{1:"one",2:"two"}
   fmt.Println(m2,m3)   //  map[1:one 2:two] map[1:one 2:two]
}

range

对于一个 slice 和 map 来说,我们可以用range来快速遍历。range 遍历时返回两个值,一个是索引,一个是对应位置的值。如果不需要索引可以用下划线忽略。

package main
​
import "fmt"func main(){
   nums := []int{1,2,3,4,5}
   sum := 0
   for num := range nums{
      sum += num
      if num == 3{
         fmt.Println(num)     // 3
      }
   }
   fmt.Println(sum)   // 10
​
   m := map[string]string{"A":"a","B":"b"}
   for k,v := range m{
      fmt.Println(k,v)   // A a
                         // B b
   }
   for _,v := range m{
      fmt.Println(v)    // a
                        // b
   }
}

函数

package main
​
import "fmt"func add(a,b int)int{
   sum := a+b
   return sum
}
​
func exists(m map[string]int, k string ) (v int, ok bool){
   r,ok := m[k]
   return r,ok
}
​
func main(){
   sum := add(7,9)
   fmt.Println(sum)           //  16
   m := map[string]int{"one": 1, "two": 2}
   value,ok := exists(m,"two")
   fmt.Println(value,ok)      //  2 true
   value,ok = exists(m,"three")
   fmt.Println(value,ok)      //  0 false
}

指针

package main
​
import "fmt"func add1(n int){
   n += 2
   fmt.Println(n)  //  5
}
func add2(n *int){
   *n += 2
   fmt.Println(n)  //  5
}
func main(){
   n := 3
   add1(n)
   fmt.Println(n)   // 3
   add2(&n)   
   fmt.Println(n)   // 5
}

结构体

Go语言提供了一种自定义数据类型,可以 封装多个基本数据类型,这种数据类型叫结构体,英文名称struct。 也就是我们可以通过struct来 定义自己的类型了。

package main
​
import "fmt"type user struct {
   name string
   age  string
}
​
func checkAge(u user,age string)bool{
   return  u.age == age
}
func checkAge2(u *user,age string){
   if u.age != age {
      u.age = age
   }
}
​
func main(){
   pika := user{name:"pika",age:"3"}
   fmt.Println(pika)      //  {pika 3}
   var duola user
   duola.name = "duola"
   duola.age = "2"
   fmt.Println(duola.name)    // duola
   fmt.Println(checkAge(pika,"2"))   // false
    checkAge2(&duola,"1")
   fmt.Println(duola)    // {duola 1}
}

结构体方法

package main
​
import "fmt"type user struct {
   name string
   password  string
}
​
func (u user)checkAge(password string)bool{
   return  u.password == password
}
func (u *user)rePassword(newPassword string){
   u.password = newPassword
}
​
func main(){
   pika := user{name:"pika",password:"123"}
   fmt.Println(pika)   //  {pika 123}
   fmt.Println(pika.checkAge("000"))   // false
   pika.rePassword("666")  
   fmt.Println(pika)   //   {pika 666}
}

错误处理

错误处理在go语言里面符合语言习惯的就是使用一个单独的返回值来传递错误信息

在函数里面,可以在函数的返回值类型里面加一个 error ,就代表这个函数可能会返回错误

package main
​
import (
   "errors"
   "fmt"
)
​
type user struct {
   name string
   password  string
}
​
func findUser(users []user,name string)(u *user,err error){
   for _,user := range users{
      if user.name == name{
         return &user,nil
      }
   }
   return nil,errors.New("not found")
}
​
func main(){
   pika := user{name:"pika",password:"123"}
   duola := user{name:"duola",password:"123"}
   haibao := user{name:"haibao",password:"123"}
    users := []user{pika,duola,haibao}
   u,err := findUser(users,"pika")
   if err != nil {
      fmt.Println(err)
      return
   }
   fmt.Println(u)   // &{pika 123}
   if u,err := findUser(users,"haha");err!=nil{
      fmt.Println(err)    // not found
      return
   }else {
      fmt.Println(u)
   }
}

字符串操作

contains 判断一个字符串里面是否包含另一个字符,count 字符串计数,index 查找某个字符串的位置,replace 替换字符串,repeat重复多个字符串,join 连接多个字符串

package main
​
import (
   "fmt"
   "strings"
)
​
func main (){
   s := "hello"
   fmt.Println(strings.Contains(s,"lo"))      // true
   fmt.Println(strings.Count(s,"o"))          // 1
   fmt.Println(strings.Index(s,"l"))          //  2
   fmt.Println(strings.Replace(s,"e","E",-1))    // hEllo
   fmt.Println(strings.Repeat(s,2))           //  hellohello
   fmt.Println(strings.Join([]string{"he" ,"llo",},"-"))   // he-llo
}

字符串格式化

package main
​
import (
   "fmt"
)
​
func main (){
   s := "hello"
    d := 123
   f := []int{1,2,3}
   g := map[string]int{"one":1,"two":2}
   //  Printf 格式化输出
   //  %v 可以输出任意类型
   fmt.Printf("s=%v\n",s)          // s=hello
   fmt.Printf("d=%v\n",d)          // d=123
   fmt.Printf("f=%v\n",f)          // f=[1 2 3]
   fmt.Printf("g=%v\n",g)          // g=map[one:1 two:2]
   fmt.Printf("g=%+v\n",g)         // g=map[one:1 two:2]
   fmt.Printf("g=%#v\n",g)         // g=map[string]int{"one":1, "two":2}
​
   h := 1.2345679
   fmt.Println(h)                     // 1.2345679
   fmt.Printf("%.2f\n",h)      // 1.23
}

JOSN处理

JSON编码是将Golang中的数据结构转换为JSON格式,而JSON解码则是将JSON格式的数据转换为Golang中的数据结构。

JSON格式具有以下几个基本规则:

  • JSON数据必须是键值对的形式。
  • 数据由逗号分隔,并且必须被大括号{}或中括号[]括起来。
  • 键必须包含在双引号""中,值可以是字符串、数字、布尔值、数组或对象。

对于一个结构体,只要保证每个字段的第一个字母是大写,也就是公开字段,那么这个结构体就能用 json.marshaler 去序列化,变成一个JSON的字符串。

序列化之后的字符也能够用 json.Unmarshal 去反序列化一个空的变量里面。

package main
​
import (
   "encoding/json"
   "fmt"
)
​
type user struct {
   Name string
   Age  string
   Hobby []string
}
func main (){
   pika := user{Name: "pika",Age: "3",Hobby: []string{"sing","dance","pingpong"}}
   pikajson,err := json.Marshal(pika)
    if err != nil{
      fmt.Println(err)
       return
    }
   fmt.Println(pikajson)        //  [123 34 78 97 109 101 34 58 34...]
   fmt.Println(string(pikajson))   // pika 是 []byte 类型,在这里转换为string类型
   // {"Name":"pika","Age":"3","Hobby":["sing","dance","pingpong"]}pika
​
   var result user
   err = json.Unmarshal(pikajson,&result)
   if err != nil{
      fmt.Println(err)
      return
   }
   fmt.Printf("%#v\n",result)
   // main.user{Name:"pika", Age:"3", Hobby:[]string{"sing", "dance", "pingpong"}}
}

时间处理

最常用的就是用 time.now() 来获取当前时间,然后也可以用 time.data 去构造一个带区的时间。也能用点 sub 去对两个时间进行减法,得到一个时间段。时间戳用 .UNIX 来获取。

package main
​
import (
   "fmt"
   "time"
)
​
func main (){
   now := time.Now()
   fmt.Println(now)    //  2023-05-13 10:26:25.5765925 +0800 CST m=+0.002576101
   t1 := time.Date(2023,5,10,13,14,0,0,time.UTC)
   t2 := time.Date(2023,5,10,14,15,0,0,time.UTC)
    fmt.Println(t1)     //  2023-05-10 13:14:00 +0000 UTC
   fmt.Println(t1.Year(),t1.Month(),t1.Day(),t1.Hour(),t1.Minute(),t1.Second())
   //  2023 May 10 13 14 0
   fmt.Println(t1.Format("2006-01-02 15:04:05")) // 2023-05-10 13:14:00
​
   diff := t2.Sub(t1)
   fmt.Println(diff)    //  1h1m0s
   fmt.Println(diff.Minutes(),diff.Seconds())   //  61 3660
   // 时间戳
   fmt.Println(now.Unix())    //  1683944893
}

for 和 for range 区别

for可以 遍历array和slice

遍历key为整型递增的map

遍历string

for range可以完成所有for可以做的事情,却能做到for不能做的,包括

遍历key为string类型的map并同时获取key和value

遍历channel