(一)【1】基础语法与概念

1,072 阅读6分钟

短变量声明:=

  • :=只能用在函数和代码块内部,声明局部变量
  • 使用短变量的方式声明多个变量时,如果同时存在声明过和没声明过的变量,那么只会会对声明过的变量进行赋值操作,不会重复声明,也不会报错
func test1(path string) {
	_, err := os.Open(path)
	name, err := filepath.Rel("form", path) // error重复声明
	fmt.Printf( name, err)
}

“如果同时存在声明过和没声明过的变量”中,“声明过的变量”指的是当前作用域中声明过的变量,不是父级

func StartServer() (err error){
	a, err := fmt.Errorf("asdfasf"), 1
	defer func() {
		fmt.Println(err != nil, "_________errr")
	}()

	if true {
		b, err := fmt.Errorf("一个错误"), 1 // 此处err和外部的err是两个err
	}

}

数组(array)和切片(slice)

区别: 数组定长,声明时需要声明长度或者使用...自动计算长度,切片声明时方括号内没有任何字符,类似动态数组,和js中的数组类似,可以动态扩容

共同点: 切片总是指向一个底层的array,所以数组的方法切片都具有

  • 数组截取:开始索引:结束索引
a := [5]int{1, 2, 3, 4, 5}

// 截取前三个
b := a[:3]
// arr[3:]` 表示截取数组 arr 的第四个元素开始的所有元素,即从索引 3 开始到数组的最后一个元素。
b := a[3:] // 等价于 a[3:len(a)]
// 截取数组中所有元素
b := a[:]
  • len: 获取数组、切片的长度
  • cap:数组的最大容量
  • append 向数组里面追加一个或者多个元素,然后返回一个和数组一样类型的数组
  • copy 函数copy从源数组src中复制元素到目标dst,并且返回复制的元素的个数

map

和array类似,但里面的元素是无序的,以key、value的形式存储

m := make(map[string]string)
m["Hello"] = "Bonjour"

结构体:struct

声明新的类型,作为其它类型的属性或字段的容器

  • 声明和初始化

    type person struct {
            name string
            age int
    }
    
    P.name = "Astaxie"  // 赋值"Astaxie"给P的name属性.
    P.age = 25  // 赋值"25"给变量P的age属性
    fmt.Printf("The person's name is %s", P.name)  // 访问P的name属性.
    
    // 也可以使用下面几种方式进行初始化
    // 1.按声明顺序初始化
    P:=person{"jack",26}
    // 2. 通过failed:value初始化
    P := person{age:24, name:"Tom"}
    // 3. 通过new声明一个指针
    P:= new(person)
    
  • 匿名字段,嵌入式字段,即:继承

    package main
    
      import (
              f "fmt"
      )
    
      type commonResponse struct {
              code string
              msg  string
      }
    
      type loginInfoData struct {
              token    string
              userId   string
              userName string
              phone    string
      }
    
      type loginInfo struct {
              commonResponse
              data loginInfoData
      }
    
      func main() {
              loginRes := loginInfo{
                      commonResponse{
                              code: "10000",
                              msg:  "success",
                      },
                      loginInfoData{
                              userId:   "123456",
                              token:    "tttttooooXXXXKKKKK",
                              userName: "皮皮虾",
                              phone:    "16543432076",
                      },
              }
              f.Println(loginRes.code)
              f.Println(loginRes.data)
      }
    
    

make

  • 只能用于splicemapchannel
  • 分配内存、初始化对应的数据类型并返回。

new

  • 只需要传入一个类型

  • 分配内存,存放该类型的零值,并返回指向该内存的指针。

  • new声明的引用类型

面向对象

method

类似js中的class,go中没有this,声明函数时可以定义接收者,这种函数称作method

package main

import (
	f "fmt"
)

const (
	WHITE = iota
	BLACK
	BLUE
	RED
	YELLOW
)

type Color byte

type Box struct {
	width, height int
	color         Color
}

func (r Box) area() int {
	return r.height * r.width
}

func (r *Box) setColor(color Color) {
	r.color = color
	f.Printf("setColor:%v\n", r)
}

func main() {
	box1 := Box{
		10,
		20,
		YELLOW,
	}
	box1.setColor(WHITE)

	box2 := Box{
		6,
		7,
		BLACK,
	}

	f.Println(box1.area())
	f.Println(box2.area())
	f.Println(box1)

}


使用method的时候要注意几点:

  • 虽然method的名字一模一样,但是如果接收者不一样,那么method就不一样
  • method里面可以访问接收者的字段
  • 调用method通过.访问,就像struct里面访问字段一样
  • method 不仅可以用在struct上,还可以定义在任何类型上

下面两个是不同的method

func (r Rectangle) area() float64 {
	return r.width*r.height
}

func (c Circle) area() float64 {
	return c.radius * c.radius * math.Pi
}

指针作为接收者

默认情况下,传递给method的是接收者的一个copy,如果想要修改接收者,需要传递指针过去,例如上面例子中的setColor方法

method继承的

如果匿名字段实现了一个method,那么包含这个匿名字段的struct也能调用该method

接口interface

类似于ts中的interface,但又不全是。go中的interface是一组method的签名组合,定义了对象的一组行为。如果> 一个对象实现了一个接口的所有方法,那么这个对象就实现了此接口。直白点儿说:接口定义了一组方法,但是不需要实现,并且interface不能包含任何变量。

package main

import (
	. "fmt"
)

type Person struct {
	name string
	age  string
	sex  byte
}

type Student struct {
	Person
	school string
	grade  int
}

type Employee struct {
	Person
	salary  string
	company string
}

func (r Person) eat() {
	println("吃八颗大葱")
}

func (r Person) run() {
	Println("跑起来,go go go ......")
}

func (r Employee) work() {
	Println("juanjuanjuan------------")
}

func (r Student) exam() {
	Println("60-60-60-60")
}

type Stu interface {
	eat()
	exam()
}

func main() {
	var stu Stu

	student := Student{
		Person{`小明`, "16", 0},
		"皮皮虾小学",
		65,
	}
        
	employee := Employee{
		Person{"老王", "36", 1},
		"250",
		"热干面",
	}
        

	stu = student
        stu = employee // employee没有实现 Stu interface,所以不能赋值给Stu变量,报错
        
	stu.eat()
	stu.run() // 接口Stu 中不存在run方法,使用会报错 

}


  • 空的interface,可以存储任意类型的值,因为所有类型都实现了空interface,相当于ts的any
  • interface作为参数时,任何实现了该interface变量都可以作为参数
Comma-ok断言,可以判断interface类型中存储的数据类型
package main

import (
	. "fmt"
)

type any interface {
}

func main() {
	var list []any

	list = append(list, 1)
	_, ok := list[0].(string)
	Println(list, ok)
}

携程goroutine

package main

import (
	. "fmt"
	"runtime"
)

func log(s string) {
	for i := 0; i < 5; i++ {
		runtime.Gosched() // 主动让出执行权
		Println(s)
	}
}

func main() {
	go log("hhhhhhhh")
	log("wwwwwwwwwwwww")

}

channels

goroutine运行在相同的地址空间,因此访问共享内存必须做好同步, 无缓冲channels读取,和发送都是阻塞的

package main

import (
	. "fmt"
)

func sum(nums []int, c chan int) {
	total := 0
	for _, v := range nums {
		total += v
	}
	c <- total

}

func main() {
	nums := []int{1, 2, 3, 4, 5, 6}
	ch1 := make(chan int)

	go sum(nums[:len(nums)/2], ch1) // 截取前1/2数组
	go sum(nums[len(nums)/2:], ch1) // 截取后1/2数组

	total1, total2 := <-ch1, <-ch1
	// 多个Goroutine
	Printf(`nums中元素总和:%d`, total1+total2) // 输出: 15 6
}

import导入的三种形式

支持绝对路径和相对路径

  1. .操作,类似于js中的import 模块
import (
   . "fmt"
)
//使用时可省略模块名前缀:
Printf("key:%v value=%v;", k, v)

  1. 别名操作,类似于import b as c from "b"
import (
   f "fmt"
)

f.Printf("key:%v value=%v;", k, v)

  1. _操作,调用包里面的init函数

import (
    "database/sql"
    _ "github.com/ziutek/mymysql/godrv"
)

单元测试

go自带单元测试系统,执行go test,会自动读取目录下的*_test.go文件。并执行内部的前缀为Test的方法。

单元测试文件格式

  • 文件命名:以_test.go结尾
  • 测试方法名:以Test为前缀
package goTest

import (
	"testing"
)

func TestGo(t *testing.T) {
	t.Log("goTest")
}