Golang学习注记

从main 函数看到规范

package main //package main表示当前文件包含main函数()
//import可以多行包括同时引入
import (
	"fmt"
	"time"
)
// 分号可以加和不加
func main()  //GO会强制要求代码风格 要求左括号必须与函数同行
{
	fmt.Println("hello Go!")
}

声明变量几种方式

package main

import "fmt"

/*
四种变量声明方式
*/

// 全局测试 只能用下面三种:
var gA int
var gB int = 100
var gC = 200

//:= 只能够在函数体内声明
//gD:=400;

// 局部测试
func main() {
	gA = 0
	//方法一 声明一个变量 默认的值是0
	var a int
	fmt.Println("a= ", a)
	fmt.Printf("type of a = %T\n", a)
	//方法二 声明一个变量同时初始化一个值
	var b int = 100
	fmt.Println("b = ", b)
	fmt.Printf("type of b = %T\n", b)
	//方法三 初始化时数据类型,通过赋值自动匹配变量的数据类型
	var c = "abcd"
	fmt.Println("c = ", c)
	fmt.Printf("type of c = %T\n", c)
	//方法四 (常用方法) 省去var 直接匹配
	e := 100
	fmt.Println("e =", e)
	fmt.Printf("type of e = %T \n", e)

	f := "abcd"
	fmt.Println("f =", f)
	fmt.Printf("type of f = %T \n", f)

	g := 3.14
	fmt.Println("g =", g)
	fmt.Printf("type of g = %T \n", g)

	//测试全局
	fmt.Println("gA =", gA, "gB =", gB, "gC =", gC) //,"gD = ",gD)
	//声明多个变量
	var xx, yy int = 100, 200
	fmt.Println("xx = ", xx, "yy = ", yy)
	var kk, ll = 100, "Aceld"
	fmt.Println("kk = ", kk, "ll = ", ll)
	//多行声明
	var (
		vv int  = 100
		jj bool = true
	)
	fmt.Println("vv = ", vv, "jj = ", jj)
}

常量和iota

package main

import (
	"fmt"
)

// 用const 来定义枚举类型
const (
	BEIJING  = 0
	SHANGHAI = 1
	SHENZHEN = 2
)

// 可以在const() 添加一个关键字iota 让每行iota会累加1 第一行的默认值为0
const (
	BEIJINGA  = iota
	SHANGHAIA //-> 1
	SHENZHENA //-> 2
)

// 多应用1
const (
	BEIJINGB  = 10 * iota //-> 0 * 10
	SHANGHAIB             //-> 1 * 10
	SHENZHENB             //-> 2 * 10
)

// 多应用2
const (
	a, b = iota + 1, iota + 2 //iota=0,公式:iota + 1, iota + 2, a = 1, b = 2
	c, d                      //iota=1,公式:iota + 1, iota + 2, c = 2, d = 3
	e, f                      //iota=2,公式:iota + 1, iota + 2, e = 3, f = 4
	g, h = iota * 2, iota * 3 //iota=3,公式:iota * 2, iota * 3, g = 6, h = 9
	i, k                      //iota=4,公式:iota * 2, iota * 3, i = 8, k = 12
)

func main() {
	//常量(只读)
	const length int = 10
	// length = 20  -> cannot assign to length (constant 10 of type int)
	//fmt.Println("length  = ", length)
	fmt.Printf("T =  %T\n", length)
	//测试iota常量
	fmt.Println("BEIJING = ", BEIJINGA)   //-> 0
	fmt.Println("SHANGHAI = ", SHANGHAIA) //-> 1
	fmt.Println("SHENZHEN = ", SHENZHENA) //-> 2
	//测试iota常量2
	fmt.Println("BEIJING = ", BEIJINGB)   //-> 0
	fmt.Println("SHANGHAI = ", SHANGHAIB) //-> 10
	fmt.Println("SHENZHEN = ", SHENZHENB) //-> 20
	//测试iota常量3
	fmt.Println(
		"a = ", a, " b = ", b,
		"\nc = ", c, " d = ", d,
		"\ne = ", e, " f = ", f,
		"\ng = ", g, " h = ", h,
		"\ni = ", i, " k = ", k)

	//iota 只能在const中使用 无法赋值给变量
	//var a int = iota
}

基本函数声明及多返回值

package main

import "fmt"

// 基本函数
func foo1(a string, b int) int {
	fmt.Println("---- foo1 ----")
	fmt.Println("a = ", a, "b = ", b)
	c := 100
	return c

}

// 多返回值[匿名]-基本函数
func foo2(a string, b int) (int, int) {
	fmt.Println("---- foo2 ----")
	fmt.Println("a = ", a, "b = ", b)
	return 666, 777
}

// 多返回值[形参名]-基本函数
func foo3(a string, b int) (r1 int, r2 int) {
	fmt.Println("---- foo3 ----")
	fmt.Println("a = ", a, "b = ", b)
	//此时r1 , r2 也是foo3的形参, 拥有默认值int 0, 作用域是foo3(){}
	fmt.Println("r1 = ", r1, "r2 = ", r2)
	//返回形参
	r1 = 1000
	r2 = 2000
	return
}

// 如果返回形参类型一致,可以省略声明如:
func foo4(a string, b int) (r1, r2 int) {
	fmt.Println("---- foo4 ----")
	fmt.Println("a = ", a, "b = ", b)
	return 3000, 4000
}

func main() {
	//foo1
	c := foo1("foo1", 444)
	fmt.Println("c = ", c)
	//foo2
	ret1, ret2 := foo2("foo2", 222)
	fmt.Println("ret1=", ret1, "ret2=", ret2)
	//foo3
	ret3, ret4 := foo3("foo3", 333)
	fmt.Println("ret3 = ", ret3, "ret4 = ", ret4)
	//foo4
	ret3, ret4 = foo4("foo4", 333)
	fmt.Println("ret3 = ", ret3, "ret4 = ", ret4)
}

slice 切片定义

package main

import "fmt"

func main() {
  //slice1
	slice1 := []int{1, 2, 3}
	fmt.Printf("slice1: len = %d, slice = %v\n", len(slice1), slice1)
  //slice2
	var slice2 []int
	slice2 = make([]int, 3)
	fmt.Printf("slice2: len = %d,slice = %v\n", len(slice2), slice2)
  //slice3
	var slice3 []int = make([]int, 3)
	fmt.Printf("slice3: len = %d,slice = %v\n", len(slice3), slice3)
  //slice4
	slice4 := make([]int, 3)
	fmt.Printf("slice4: len = %d,slice = %v\n", len(slice4), slice4)
}

slice 基本应用

package main

import "fmt"

func main() {
  //len = 6, cap = 6, slice = [1 2 3 4 5 6]
  //caps是每次开拓默认要增加新的空间长度 
	numbers := []int{1, 2, 3, 4, 5, 6}
	fmt.Printf(
		"len = %d, cap = %d, slice = %v\n",
		len(numbers),
		cap(numbers),
		numbers,
	)
	//len = 1, slice = [3]
	numbers2 := numbers[2:3]
	fmt.Printf(
		"len = %d, slice = %v\n",
		len(numbers2),
		numbers2,
	)
  //len = 4, slice = [3 2 3 4]
	numbers2 = append(numbers2, 2, 3, 4)
	fmt.Printf(
		"len = %d, slice = %v\n",
		len(numbers2),
		numbers2,
	)
}

map 基本应用

package main

import "fmt"

func main() {
  //map[one:php three:java two:golang]
	var test1 map[string]string
	test1 = make(map[string]string, 10)
	test1["one"] = "php"
	test1["two"] = "golang"
	test1["three"] = "java"
	fmt.Println(test1)
	fmt.Println("------------------")
  //map[one:php2 three:java2 two:golang2]
	test2 := make(map[string]string)
	test2["one"] = "php2"
	test2["two"] = "golang2"
	test2["three"] = "java2"
	fmt.Println(test2)
	fmt.Println("------------------")
  //map[one:php3 three:java3 two:golang3]
	test3 := map[string]string{
		"one":   "php3",
		"two":   "golang3",
		"three": "java3",
	}
  fmt.Println(test3)
  fmt.Println("------------------")
  //map[golang:map[desc:C++ is very good id:2] php:map[desc:PHP是世界上最难写的语言 id:1]]
  lang := make(map[string]map[string]string)
  lang["php"] = make(map[string]string, 2)
  lang["php"]["id"] = "1"
  lang["php"]["desc"] = "PHP是世界上最难写的语言"
  lang["golang"] = make(map[string]string, 2)
  lang["golang"]["id"] = "2"
  lang["golang"]["desc"] = "C++ is very good"
  fmt.Println(lang)
  fmt.Println("------------------")
  //C++会被删掉
  //map[golang:map[desc:C++ is very good id:2] php:map[desc:PHP是世界上最难写的语言 id:1]]
  delete(lang, "desc")
  fmt.Println(lang)
  fmt.Println("------------------")
 	// ------------------
	// list =  php
	// Set = id
	// Value = 1
	// Set = desc
	// Value = PHP是世界上最难写的语言
	// ---------------
	// list =  golang
	// Set = id
	// Value = 3
	// Set = desc
	// Value = C++ is very good	
	// ---------------
	lang["golang"]["id"] = "3"
	for list, item := range lang {
		fmt.Println("list = ", list)
		for itemA, ValueA := range item {
			fmt.Println("Set =", itemA)
			fmt.Println("Value =", ValueA)
		}
		fmt.Println("---------------")
	}
}

struct 类的初步

package main

import "fmt"

type Hero struct {
	Name  string
	Ad    int
	Level int
}

func (this Hero) GetName() {
	fmt.Println("Name = ", this.Name)
}

func (this Hero) Show() {
	fmt.Println("NAme = ", this.Name)
	fmt.Println("Ad =", this.Ad)
	fmt.Println("Level =", this.Level)
}

func (this *Hero) SetName(newName string) {
	this.Name = newName
}

func (this Hero) test() {
	fmt.Println("test")
}

func (this Hero) Test() {
	fmt.Println("Test")
}

func main() {
	hero := Hero{Name: "zhang3", Ad: 100, Level: 1}
	hero.Show()
	hero.SetName("zhang2")
	hero.Show()

	//当首字母为小写时属于内部方法
	// hero.Test()
	hero.test()
}

struct 类和继承子类

package main

import "fmt"

type Human struct {
	name string
	sex  string
}

func (this *Human) Eat() {
	fmt.Println("Human Eat()....")
}

func (this *Human) Walk() {
	fmt.Println("Human Walk()....")
}

func (this *SuperMan) Print() {
	fmt.Println("name=", this.name)
	fmt.Println("sex=", this.sex)
	fmt.Println("level=", this.level)
}

type SuperMan struct {
	Human //SuperMan 继承了Human类的方法
	level int
}

func (this *SuperMan) Eat() {
	fmt.Println("SuperMan.Eat()...")
}

func (this *SuperMan) Fly() {
	fmt.Println("SuperMan.Fly()...")
}

func main() {
	h := Human{"zhang3", "female"}
	h.Eat()
	h.Walk()
	fmt.Println("-----------")
	s := SuperMan{Human{"li4", "female"}, 99}
	s.Walk()
	s.Eat()
	s.Fly()
	s.Eat()
	fmt.Println("-----------")
	var s2 SuperMan
	s2.level = 100
	s2.name = "w5"
	s2.sex = "male"
	s2.Walk()
	s2.Eat()
	s2.Fly()
	s2.Eat()
}

interface 多态实现

package main

import "fmt"

//1、定义一个父类(有接口)
type aIF interface {
	GetCurrent() string //获取当前状态
}

//2、定义一个子类(实现父类接口定义)
type a_son struct {
	Current string
}

func (this *a_son) GetCurrent() string {
	return this.Current
}

//3、父类指针指向子类
func main() {
	var xiaoming aIF
	xiaoming = &a_son{"Run"}
	fmt.Println(xiaoming.GetCurrent())
}

interface空接口

package main

import "fmt"

//interface空接口可作万能数据类型
func myFunc(arg interface{}) {
	fmt.Println("arg: ", arg)
	//如何区分interface{}的数据类型?
	//"类型断言"
	value, ok := arg.(string)

	if ok {
		fmt.Println("agr is string type,value = ", value)
		fmt.Printf("value type is  %T \n", value)
	} else {
		fmt.Println("arg is not string type")
	}

	fmt.Println("--------------")
}

//Book类
type Book struct {
	auth string
}

func main() {
	book := Book{"Golang"}
	myFunc(book)
	myFunc(100)
	myFunc("100")
	myFunc(3.14)
}

断言结构解析

package main

import "fmt"

type ReaderIf interface {
	ReadBook()
}

type WriterIf interface {
	WriteBook()
}

type Book struct {
	id string
}

func (this *Book) ReadBook() {
	fmt.Println(this.id, ":Read a Book")
}

func (this *Book) WriteBook() {
	fmt.Println(this.id, ":Write a Book")
}

func main() {
	b := &Book{"b"}
	/*
		当b => Book{}
		那么b 就拥有了两个func ReadBook 、 WriteBook 和 属性id
		比如:
	*/
	b.ReadBook()
	b.WriteBook()

	/*
		尝试定义一个interface ReaderIf 的 r
		ReaderIf{
			ReadBook()
		}
		当 r => b 的时候 需要b提供func ReadBook给 r  此时他们之间的关系是这样的
		r{
			b[面向r]{
				private id string
				public ReadBook()
				private WriteBook()
			}
		}
		比如:
	*/
	var r ReaderIf
	r = b
	r.ReadBook() //b :Read a Book

	/*
		当我们尝试再定义一个 interface WriteIf 给 w
		WriteIf{
			WriteBook()
		}
		此时他们之间的关系是这样的
		w{
			b[面向w]{
				private  id string
				private ReadBook()
				public WriteBook()
			}
		}
		//比如:
		var w WriterIf = b
		w.WriteBook() //b :Write a Book
	*/

	/*
		假如不为w提供b,而是用一个断言将 ReadIf的 r 强制转换为  WriteIf 也可行吗,为什么?
	*/

	var w WriterIf
	/*
		因为 定义 w为 interface WriterIf 是一个动态类型
		w 此时的内部结构为<pair type: 空 value: 空/>
	*/
	w = r.(WriterIf)
	/*
		r 此时的内部结构为<pair type:Book value: Book{"b"}>
		r.(WriterIf) 以后 WriterIf实现的type也依旧是Book
		因为
			r = &b,
			b = Book{"b"}  内部实现了Book{"b"}.WriteBook()
			当r(即ReaderIf的实现)强制转换到WriterIf的时候
			w(WriterIf的实现)继承的内部结构需要和r的pair是一致的
			<r.(WriterIf) pair type:Book value: Book{"b"}>

	*/
	w.WriteBook() //b :Write a Book
}

反射

package main

import (
	"fmt"
	"reflect"
)

type User struct {
	Id   int
	Name string
	Age  int
}

func (this *User) Call() {
	fmt.Println("user is called ..")
	fmt.Printf("%v\n", this)
}

func main() {
	user := User{1, "Done", 18}
	DoFiledAndMethod(user)
}

func DoFiledAndMethod(input interface{}) {
	//获取input 的type
	inputType := reflect.TypeOf(input)
	//获取input的value
	inputValue := reflect.ValueOf(input)
	//fmt.Println("inputValue:", inputType, "\ninputValue:", inputValue)
	//获取type 获取里面的字段
		//1 获取interface的reflect.Type 通过Type得到NumField
		//2 得到每个field 数据类型
		//3 通过filed 有一个Interface方法得到对应的value
	for i := 0; i < inputType.NumField(); i++ {
		field := inputType.Field(i)
		value := inputValue.Field(i).Interface()
		fmt.Printf("%s: %v = %v\n", field.Name, field.Type, value)
	}

	for i := 0; i < inputType.NumMethod(); i++ {
		m := inputType.Method(i)
		fmt.Printf("%s: %v\n", m.Name, m.Type)
	}
}

go程(goroutine Golang版本的Coroutine)

package main

import (
	"fmt"
	"time"
)

func main() {
	//用go创建承载一个形参为空 返回值为空的go程
	// go func() {
	// 	defer fmt.Println("A.defer")
	// 	func() {
	// 		defer fmt.Println("B.defer")
	// 		//runtime.Goexit() // 退出当前goroutine
	// 		fmt.Println("B")
	// 	}()
	// 	fmt.Println("A")
	// }()
	
    //用go创建承载一个带形参的go程
	go func(a int, b int) bool {
		fmt.Println("a = ", a, ", b = ", b)
		return true
	}(10, 20)

	for {
        //死循环 
		time.Sleep(1 * time.Second)
	}
}

channel 定义

package main

import "fmt"

//主go程
func main() {
	//channel 定义
	c := make(chan int)
	//从go程
	go func() {
		defer fmt.Println("goroutine exit().")
		fmt.Println("go routine runtime..")
		c <- 666
	}()
	fmt.Println(<-c)
	/*
		channel
			会发送阻塞信号使得从go程和主go程同步,
			若主go程不取得c,那么从go程就会一直在阻塞状态
			所以如果注释了这行,这里的go程匿名函数就会一直阻塞不被执行
	*/
	fmt.Println("main go routine exit().")
}

带缓冲空间的channel

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() {
	//设置缓冲大小
	size := 3
	sendsize := 4
	//定义chanel
	c := make(chan int, size) //当无法存入时阻塞
	fmt.Println("chanel c: 默认缓冲空间=", cap(c))
	//从go程发送元素
	go func() {
		defer fmt.Println("------------从go程 发送完毕------------")
		for i := 0; i < sendsize; i++ {
			randint := rand.Int()
			// //发送随机数
			c <- randint
			//发送延迟
			time.Sleep(1 * time.Second)
			fmt.Printf("从go程 发送1个元素:%d  %d / %d \n", randint, i+1, sendsize)
		}
	}()

	//主go程接受从go程发送过来的c
	for i := 0; i < sendsize; i++ {
		//接收延迟
		time.Sleep(6 * time.Second)
		value := <-c
		fmt.Printf("| 主go程 收到元素:%d  进度: %d / %d \n", value, i+1, sendsize)
		fmt.Printf("| 从go程 剩余空间: %d \n", size-len(c))
	}

	defer fmt.Println("------------主go程 接收完毕------------")
}

channel关闭

package main

import "fmt"

func main() {

	c := make(chan int)
	go func() {
		for i := 0; i < 5; i++ {
			c <- i
			//close(c)
		}
		close(c)

		/*
			tips
				close(channel chan <- type)
				1、channel 不像文件一样需要经常关闭 只有当确实没有任何发送数据以后 / 你想显式的结束range循环之类 才去关闭channel
				2、关闭channel后,无法再向channel发送数据 (引发panic错误后导致接受立即返回零值
				3、关闭channel后 可以继续从channel 接收数据

			输出:
					0
					1
					2
					3
					4
					Finished..
		*/
	}()

	for {

		if data, ok := <-c; ok {
			fmt.Println(data)
		} else {
			break
		}
	}

	fmt.Println("Finished..")
}

channel 用range迭代接收

package main

import "fmt"

func main() {

	c := make(chan int)
	go func() {
		for i := 0; i < 5; i++ {
			//close(c)
			c <- i
		}

		close(c) //关闭channel
	}()

	/*

		for {
			if data, ok := <-c; ok {
				fmt.Println(data)
			} else {
				break
			}
		}
	*/
	//可以使用range迭代数据
	for data := range c {
		fmt.Println(data)
	}

	fmt.Println("Finished..")
}

channel 用select case控制接收

package main

import "fmt"

//输出斐波那契
func fibonacii(c, quit chan int) {
	x, y := 1, 1
	ws := 0
	for {
		select {
		case c <- x: // 如果 c 被写入数据
			x = y
			y = x + y
		case <-quit: //如果 quit 读到数据
			fmt.Printf("quit 经历了%d个default \n", ws)
			return
		default:
			ws += 1
		}

	}
}
func main() {
	c := make(chan int)
	quit := make(chan int)
	// 在channel作用下 主go程main和从匿名go是同步的
	// 所以相当于fibonacii在匿名go程的for下被执行 然后进select case
	go func() {
		for i := 0; i < 6; i++ {
			fmt.Println(<-c)
		}
		quit <- 0
	}()

	/*
		输出:
			1
			1
			2
			4
			8
			16
			quit 经历了1580271个default
	*/

	fibonacii(c, quit)
}

实战1 struct类应用

main.go
package main

func main() {
	Server := Test("localhost", 8889)
	// Server.Start()
	Server.Start()
}
server.go
package main

import (
	"fmt"
	"net"
)

//定义Server 类 class Server
type Server struct {
	IP   string
	PORT int
}

//定义一个类方法
func Test(ip string, port int) *Server {
	//创建一个Server
	var Server = &Server{
		IP:   ip,
		PORT: port,
	}
	//返回出去
	return Server
}

//启动服务
func (Server Server) Start() {
	//启动监听服务
	listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", Server.IP, Server.PORT))
	//当err不为nil -> 也就是发生了错误
	//当err为nil -> 正常运行
	if err == nil {
		fmt.Print(listener.Addr().String() + "\n")
	} else {
		fmt.Print(err)
	}
	defer listener.Close()

	//保持循环防止go程跑完if后就自动结束
	for {
		//处理业务
	}
}
效果:

image-20230201114830215

实战2 检测连接进来的IP

package main

import (
	"fmt"
	"net"
)

//定义Server 类 class Server
type Server struct {
	IP   string
	PORT int
}

//定义一个类方法
func Test(ip string, port int) *Server {
	//创建一个Server
	var Server = &Server{
		IP:   ip,
		PORT: port,
	}
	//返回出去
	return Server
}

//启动服务
func (Server Server) Start() {
	/*
		启动本地监听
		fmt.Sprintf(%s:$d) 会格式化为 -> 127.0.0.1:8080 这样的字符串
	*/
	listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", Server.IP, Server.PORT))
	//当err不为nil -> 也就是发生了错误
	//当err为nil -> 正常运行
	if err != nil {
		fmt.Print(err)
	} else {
		fmt.Println("监听地址:" + listener.Addr().String() + "\n")
	}

	defer listener.Close()

	//保持循环防止go程跑完if后就自动结束
	for {
		/*
			监听外部连接
			Accept 似乎会触发进程等待 直到有连接进来才会往下走
		*/
		tcp_connect, err := listener.Accept()
		//_, err := listener.Accept()

		if err != nil {
			fmt.Println("Accept err:", err)
			continue //不希望连接错误就结束整个进程 contine 让循环继续
			//listener.Close()
		} //else {
		// 	tcp_connect.Write([]byte(tcp_connect.RemoteAddr().String() + "欢迎你连接窒息空间"))
		// 	tcp_connect.Close()
		// }

		/*
			建立go程 异步处理外部连接的事件
				上面提到net.Listen().Accept 会触发等待 直到有连接进来才会往下走
				所以这里 for{ go func(){}() } 并不会直接被死循环 而是在有连接的时候才会走到
		*/

		go func() {
			fmt.Println("有新连接来自:" + tcp_connect.RemoteAddr().String())
		}()
	}
}
效果:

c25503c02eef4621d8183cf6f4cab0b8