短变量声明:=
:=
只能用在函数和代码块内部,声明局部变量- 使用短变量的方式声明多个变量时,如果同时存在声明过和没声明过的变量,那么只会会对声明过的变量进行赋值操作,不会重复声明,也不会报错
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
- 只能用于
splice
、map
、channel
- 分配内存、初始化对应的数据类型并返回。
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导入的三种形式
支持绝对路径和相对路径
.
操作,类似于js中的import 模块
import (
. "fmt"
)
//使用时可省略模块名前缀:
Printf("key:%v value=%v;", k, v)
- 别名操作,类似于
import b as c from "b"
import (
f "fmt"
)
f.Printf("key:%v value=%v;", k, v)
_
操作,调用包里面的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")
}