很久之前做的笔记…整理了一下
函数的 { 一定和函数名在同一行,否则编译错误
分号加与不加都可以,一般不加
main函数一定在main包里
导多个包:
import(
"fmt"
"time"
)
// 声明全局变量,方法一、二、三是可以的
var gA int
var gB int = 10
var c = 10
//不能用方法四来声明全局变量
//gD := 100
//:= 只能够用在函数体中来声明
func main() {
//方法一:声明一个变量 默认初始值是0
var a int
//方法二:声明一个变量,初始化一个值
var b int = 10
//方法三:在初始化的时候,可以省去数据类型,通过值自动匹配当前变量的数据类型
var c = 10
//方法四:(常用方法)省去var关键字,直接自动匹配
d := 100
//声明多个变量
var xx, yy int = 100, 200
var kk, ll = 100, "hello"
var (
vv int = 100
jj bool = true
)
}
// const来定义枚举类型
const (
//可以添加关键字 iota 每行的iota都会累加1,第一行的iota默认值是0,这样就不需要一个个赋值了
BEIJING = iota
SHANGHAI
SHENZHEN
)
const (
a, b = iota + 1, iota + 2 // iota = 0, a = iota + 1, b = iota + 2, a = 1, b = 2
c, d // iota = 1, c = iota + 1, d = iota + 2, c = 2, d = 3
e, f // iota = 2, e = iota + 1, f = iota + 2, e = 3, f = 4
g, h = iota * 2, iota * 3 // iota = 3, g = iota * 2, h = iota * 3, g = 6, h = 9
i, k // iota = 4, i = iota * 2, k = iota * 3, i = 8, k = 12
)
func main() {
//常量(只读属性)
const length int = 10
}
// go函数
func fool(s string, b int) int {
res := 100
return res
}
// 返回多个返回值,匿名的
func fool2(s string, b int) (int, int) {
return 666, 777
}
// 返回多个返回值,有形参名称的
func fool3(s string, b int) (r1 int, r2 int) {
r1 = 1000
r2 = 2000
return
}
// 同fool3,若r1、r2返回值类型相同,可以这样写
func fool4(s string, b int) (r1, r2 int) {
r1 = 1000
r2 = 2000
return
}
在 Go 语言中,标识符(函数、变量、结构体、方法等),首字母大写,可以在包外被访问,小写,只能在包内被访问
导入进来的包不使用会报错
import中包名前面加上“_ ” 下划线+空格,表示匿名,这样这个包不被使用也能通过编译
_ “packageName”
myPackgae "packageName"
:给包起别名
. "packageName"
:前面加上点和空格,在使用包内方法的时候,不需要packageName.fun(),只需func()就可以调用 不要轻易使用
在 Go 语言中,如果你给导入的包起了别名或匿名,那么即使没有在代码中显式使用这个包,也能通过编译
defer后接的语句,在当前函数执行结束前执行
多个defer语句的调用顺序:栈的形式,1先入栈后出栈,所以是2先调用1后调用
func main() {
defer fmt.Println("main end 1")
defer fmt.Println("main end 2")
fmt.Println("hello go 1")
fmt.Println("hello go 2")
}
同一个函数,reutrn比defer先被调用
普通数组:
func printArray(array [10]int) {
for i := range array {
fmt.Println(i)
}
}
func main() {
//定义固定长度的数组
var myArray1 [10]int
myArray2 := [10]int{1, 2, 3, 4, 5}
//myArray1和myArray2的数据类型是[10]int
for i := 0; i < len(myArray1); i++ {
fmt.Println(myArray1[i])
}
for i := range myArray2 {
fmt.Println(myArray2[i])
}
for index, value := range myArray2 {
fmt.Println(index, value)
}
printArray(myArray2)
}
传参的时候,必须指定数组长度,且为值传递
slice(动态数组):
func printArray(array []int) {
for _, value := range array {
fmt.Println(value)
}
}
func main() {
//定义slice
myArray := []int{1, 2, 3, 4, 5}
printArray(myArray)
}
引用传递
func main() {
//声明slice1是一个切片,并且初始化,默认值是1,2,3,长度len是3
slice1 := []int{1, 2, 3}
//声明slice1是一个切片,但是并没有分配空间 此时 slice2[0] = 1 这样赋值会报错
var slice2 []int
//开辟空间
slice2 = make([]int, 3)
slice2[0] = 1
//声明slice3是一个切片,同时分配3个空间,初始值是0
var slice3 = make([]int, 3)
if slice2 == nil {
fmt.Println("slice2是一个空切片")
} else {
fmt.Println("slice2是有空间的")
}
}
func main() {
//3:长度(len),5:容量(capacity)
var numbers = make([]int, 3, 5)
//向number是切片追加一个元素1,此时len为4
numbers = append(numbers, 1)
//len变为5
numbers = append(numbers, 2)
//len等于cap时仍然可以追加,此时会自动开辟当前cap的容量,即5。此时cap=10
numbers = append(numbers, 3)
//截取
s := []int{1, 2, 3}
s1 := s[0:2] //{1, 2}
//此时s1和s指向的地址相同
s1[0] = 100 //s1和s的第一个1元素都会被修改
s2 = make([]int, 3)
copy(s2, s) //此时s2不与s指向相同空间
}
func main() {
//第一种声明方式
//key:int value:string 但仅仅这样声明的话,这个map是无法使用的
var myMap1 map[int]string
//在使用map前,需要先用make给map分配数据空间
myMap1 = make(map[int]string, 10)
myMap1[1] = "one"
myMap1[2] = "two"
myMap1[3] = "three"
//第二种声明方式
//容量不写也可以,在赋值的时候会追加
myMap2 := make(map[string]string)
myMap2["one"] = "one"
myMap2["two"] = "two"
myMap2["three"] = "three"
fmt.Println(myMap2)
//第三种声明方式
myMap3 := map[string]string{
"one": "one",
"two": "two",
"three": "three",
}
}
func printMap(cityMap map[string]string) {
//cityMap是一个引用传递
for key, value := range cityMap {
fmt.Println(key, ":", value)
}
}
func main() {
cityMap := make(map[string]string)
//添加
cityMap["China"] = "Beijing"
cityMap["Japan"] = "Tokyo"
//遍历
for key, value := range cityMap {
fmt.Println(key, ":", value)
}
//删除
delete(cityMap, "China")
}
// 声明数据类型myint,是int的一个别名
type myint int
// 定义一个结构体
type Book struct {
name string
author string
}
func changeBook1(book Book) {
//传入book副本,即值传递
fmt.Println(book.author, book.name)
}
func changeBook2(book *Book) {
//引用传递
fmt.Println(book.author, book.name)
}
func main() {
var myint num = 1
var book1 Book
book1.name = "GoLang"
book1.author = "zhangsan"
changeBook1(book1)
changeBook2(&book1) //此时就要传地址了
}
type Hero struct {
Name string
Age int
Level int
}
//func (this Hero) GetName() string {
// return this.Name
//}
//
//func (this Hero) SetName(newName string) {
// //this是调用该方法的对象的一个副本 拷贝
// this.Name = newName
//}
func (this *Hero) GetName() string {
return this.Name
}
func (this *Hero) SetName(newName string) {
this.Name = newName
}
func main() {
//创建一个对象
hero := Hero{Name: "zhangsan", Age: 20, Level: 1}
}
type Human struct {
name string
sex string
}
func (this *Human) Eat() {
fmt.Println("调用human的Eat方法")
}
type SuperMan struct {
Human //表示SuperMan继承了Human类的方法
level int
}
// 重定义父类的方法Eat
func (this *SuperMan) Eat() {
fmt.Println("调用SuperMan的Eat方法 ")
}
func main() {
h := Human{"zhangsan", "man"}
h.Eat()
superMan := SuperMan{Human{"lisi", "female"}, 5}
superMan.Eat()
}
// 本质是一个指针
type AnimalIF interface {
Sleep()
GetColor() string
GetType() string
}
// 具体的类 不需要像继承那样把接口名写在类中,只需实现接口的所有方法
type Cat struct {
color string
}
func (this *Cat) Sleep() {
fmt.Println("猫正在睡觉")
}
func (this *Cat) GetColor() string {
return this.color
}
func (this *Cat) GetType() string {
return "cat"
}
type Dog struct {
color string
}
func (this *Dog) Sleep() {
fmt.Println("狗正在睡觉")
}
func (this *Dog) GetColor() string {
return this.color
}
func (this *Dog) GetType() string {
return "dog"
}
func showAnimal(animal AnimalIF) {
animal.Sleep() //多态
}
func main() {
var animal AnimalIF //接口的数据类型,父类指针
animal = &Cat{color: "red"} //需要加&
animal.Sleep() //调用cat的Sleep()
animal = &Dog{color: "yellow"}
animal.Sleep()
cat := &Cat{color: "red"}
dog := &Dog{color: "yellow"}
showAnimal(cat)
showAnimal(dog)
}
// interface{}是万能数据类型
func MyFunc(arg interface{}) {
fmt.Println(arg)
//interface{} 如何区分此时引用的数据类型是什么? -> 给interface{}提供 类型断言 机制
value, ok := arg.(string) //返回两个值
if !ok {
fmt.Println("arg is not a string")
} else {
fmt.Println("arg is a string")
fmt.Println("value:", value) //arg是string,value是arg的值 否则,value会是string类型的零值,即空字符串""
}
}
type Book struct {
author string
}
func main() {
book := Book{"zhangsan"}
MyFunc(book)
MyFunc(100)
MyFunc("abc")
}
func main() {
var a string
//pair 无论怎样赋值,pair会一直传递下去
a = "abc"
//pair
var allType interface{}
allType = a
value, _ := allType.(string)
fmt.Println(value)
}
在 Go 语言中,反射机制通过 reflect.Value.Interface() 方法获取值时,会严格遵循可见性规则。即使对同一包内的未导出(小写字母开头)结构体字段,该方法的调用仍会触发 panic。这是反射机制对封装性原则的贯彻体现,旨在防止通过非正常途径访问私有数据,即便这种访问发生在同一个包内。
func reflectNum(arg interface{}) {
argType := reflect.TypeOf(arg)
value := reflect.ValueOf(arg)
fmt.Println(argType, value)
}
type User struct {
Name string
Age int
}
func (this *User) Call() {
fmt.Println("user is calling")
fmt.Println("%v\n", this)
}
func DoFiledAndMethod(input interface{}) {
inputType := reflect.TypeOf(input)
inputValue := reflect.ValueOf(input)
//通过type获取里面的字段
//1.通过Type得到NumField
//2.得到每个field,即数据类型
//3.通过field的Interface()方法得到value
for i := 0; i < inputType.NumField(); i++ {
field := inputType.Field(i)
value := inputValue.Field(i).Interface()
//Name string zs
//Age int 18
fmt.Println(field.Name, field.Type, value)
}
}
func main() {
var num float64 = 1.2345
reflectNum(num)
user := User{"zs", 18}
DoFiledAndMethod(user)
}
type resume struct {
Name string `info:"name" doc:"我的名字"` //标签 同一个字段可以有多个
Sex string `info:"sex"`
}
func findTag(arg interface{}) {
t := reflect.TypeOf(arg).Elem()
for i := 0; i < t.NumField(); i++ {
tagInfo := t.Field(i).Tag.Get("info")
tagDoc := t.Field(i).Tag.Get("doc")
fmt.Println(tagInfo)
fmt.Println(tagDoc)
}
}
func main() {
re := resume{"Momentary_SixthSense", "man"}
findTag(&re)
}
import (
"encoding/json"
"fmt"
)
type Movie struct {
Title string `json:"title"`
Year int `json:"year"`
Actors []string
}
func main() {
movie := Movie{"喜剧之王", 2000, []string{"zhouxingchi", "zhangbozhi"}}
//编码的过程:结构体 -> json
jsonStr, err := json.Marshal(movie)
if err != nil {
fmt.Println("json marshal err:", err)
return
}
fmt.Println(string(jsonStr))
//解码的过程:json -> 结构体 jsonStr = {"title":"喜剧之王","year":2000,"Actors":["zhouxingchi","zhangbozhi"]}
my_movie := Movie{}
err = json.Unmarshal(jsonStr, &my_movie)
if err != nil {
fmt.Println("json unmarshal err:", err)
return
}
fmt.Println(my_movie)
}
json中显示的是标签内容title
// 从goroutine
func newTask() {
i := 0
for {
i++
fmt.Println(i)
time.Sleep(1 * time.Second)
}
}
// 主goroutine
func main() {
//创建一个goroutine,去执行newTask流程
go newTask()
i := 0
for {
i++
fmt.Println(i)
time.Sleep(1 * time.Second)
}
}
func main() {
//创建goroutine 调用匿名函数 无参
go func() {
defer fmt.Println("A defer")
func() {
defer fmt.Println("B defer")
fmt.Println("B")
}()
fmt.Println("A")
}() //此处加上括号,表示调用
//有参
go func(a, b int) bool {
fmt.Println(a, b)
return true
}(10, 20)
for {
time.Sleep(1 * time.Second)
}
}
func main() {
//channel定义
c := make(chan int)
go func() {
defer fmt.Println("goroutine 结束")
fmt.Println("goroutine 正在运行")
c <- 666 //将666发送给c
}()
num := <-c //从c中接收数据,赋值给num
fmt.Println(num)
fmt.Println("main goroutine 结束")
}
在Go语言的并发模型中,无缓冲channel的发送和接收操作遵循同步通信机制。发送协程会阻塞直至存在对应的接收操作就绪,接收协程同样会阻塞直到发送操作就绪,这种双向等待机制确保数据传输的原子性和时序一致性。
相比之下,有缓冲channel的发送和接收操作则具有一定的异步性。有缓冲channel在创建时会分配一个固定大小的缓冲区,用于暂存发送的数据。当缓冲区未满时,发送操作可以立即完成,而不需要等待接收方就绪;当缓冲区为空时,接收操作也可以立即完成,而不需要等待发送方就绪。只有当缓冲区满时,发送操作才会阻塞,直到有接收操作从缓冲区中取出数据;同样,当缓冲区为空时,接收操作才会阻塞,直到有发送操作向缓冲区中放入数据。
func main() {
//有缓冲的channel
c := make(chan int, 3)
go func() {
defer fmt.Println("goroutine 结束")
fmt.Println("goroutine 正在运行")
for i := 0; i < 3; i++ {
c <- i
}
}()
for i := 0; i < 3; i++ {
num := <-c
fmt.Println(num)
}
fmt.Println("main goroutine 结束")
}
func main() {
c := make(chan int)
go func() {
for i := 0; i < 5; i++ {
c <- i
}
//关闭一个channel
close(c)
}()
for {
//ok为true表示当前channel没有关闭
if data, ok := <-c; ok {
fmt.Println(data)
} else {
break
}
}
fmt.Println("Main Finished...")
}
for {
//ok为true表示当前channel没有关闭
if data, ok := <-c; ok {
fmt.Println(data)
} else {
break
}
}
//上方代码可以改成:
//可以使用range来不断迭代操作range
for data := range c {
fmt.Println(data)
}
单流程下一个go只能监控一个channel的状态,select可以完成监控多个channel的状态
func fibonacii(c, quit chan int) {
x, y := 1, 1
for {
select {
case c <- x:
x = y
y = x + y
case <-quit:
fmt.Println("quit")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 6; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacii(c, quit)
}
channel
close©
}()
for {
//ok为true表示当前channel没有关闭
if data, ok := <-c; ok {
fmt.Println(data)
} else {
break
}
}
fmt.Println("Main Finished...")
}
# channel与range
```go
for {
//ok为true表示当前channel没有关闭
if data, ok := <-c; ok {
fmt.Println(data)
} else {
break
}
}
//上方代码可以改成:
//可以使用range来不断迭代操作range
for data := range c {
fmt.Println(data)
}
单流程下一个go只能监控一个channel的状态,select可以完成监控多个channel的状态
func fibonacii(c, quit chan int) {
x, y := 1, 1
for {
select {
case c <- x:
x = y
y = x + y
case <-quit:
fmt.Println("quit")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 6; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacii(c, quit)
}