2022-7-2 --> 2022-7-4 Go学习 (学习资源不好,未完成)
2022-7-2 --> 2022-7-4 Go学习
Go的编译
- 编译指令 : 二进制码文件
go build -o 文件名.exe 文件名.go
- 编译的好处:
- 让程序运行更快
- 在没有go的环境里依然可以运行
- 帮助排错
- 编译的坏处:
- 使文件变得更大
- tree查看文件结构
GO常用转意字符
-
\t :一个制表位
-
\n :换行
-
\ " :"
-
\ \ :\
-
\r :回车
package main
import "fmt"
func main(){
fmt.Println("tom\tjack") tom jack
fmt.Println("tom\rjack") jack //tom被覆盖了
fmt.Println("tom\njack") tom
jack
fmt.Println("tom\\jack") tom\jack
fmt.Println("tom\"jack") tom"jack
}
- 示例
package main
import "fmt"
func main(){
fmt.Print("姓名\t年龄\t籍贯\t住址\njack\t12\t河北\t北京")
}
姓名 年龄 籍贯 住址
jack 12 河北 北京
GO的书写规范
- 官方推荐使用行注释
- 有正确的缩进和空白
- 可以使用命令来规范代码
gofmt -w w
GO变量定义的四种方式
//1.无初始值
var a int
//2.有初始值
var a int = 1
//3.无类型自动匹配
var a = 1
//4.局部可用,屈居不可用
a := 1
Go常量定义方式
//1.单个常量定义方式
const A = 1
//2.多个常量定义方式
const(
A = 1
B = 2
)
iota用法
- iota 必须配合常量使用,作用是可以在单个常量或多个常量内定义一个自增长的值,初始为0
//1. 单个
const A = iota //0
const B = iota*2 + 1 //1 单个iota始终为0,故为一
//2.多个
const(
A = iota //0
B //1
C = iota*2 //2*2
D //3
)
Go方法定义 --返回值的个数自选
- 返回值匿名
func foo1 (a string,b int)(int, string){
fmt.Println(a)
fmt.Println(b)
return a,"d"
}
- 返回值有名
func foo1 (a string,b int) (c int,d int) {
fmt.Println(a)
fmt.Println(b)
c = 100
d = 100
return
}
- 返回值同一类型可简写
func foo1 (a string,b int) (c,d int) {
fmt.Println(a)
fmt.Println(b)
c = 100
d = 100
return
}
- 无返回值
func foo1 (a string,b int) {
fmt.Println(a)
fmt.Println(b)
}
- 方法名大写是public
- 方法名小写是private
GO init()
- init()函数运行在main()函数之前
GO 导包
- go导包要注意路径
- go正常情况下导包就必须使用,不然报错
- 导包又不使用,可以如下操作
import _ "包路径/包名"
- 如果包名过长,可起别名
import myLib "包路径/包名"
- 如果方法名不重复的情况下,可如下操作
import . "包路径/包名" //调用方法时可以不写包名,直接调用方法
GO指针
- 未使用指针
package main
import "fmt"
func changeValue(p int){
p = 10
}
func main(){
var a = 1
changeValue(a)
fmt.Println("a=",a) //1
}
- 使用指针
package main
import "fmt"
func changeValue(p *int){
*p = 10
}
func main(){
var a int = 1
changeValue(&a)
fmt.Println("a=",a)
}
-
个人理解:
-
- 未使用指针传的是变量的内存地址投影,是假身,没有在物理上修改那个值,只是镜中花,水中月
- 使用指针传的是真实的内存地址,修改的是真实存在的内存上的值
- *代表存储的是地址 &代表取得是地址
-
一级指针交换值
package main
import "fmt"
func swap(a *int , b *int){
var temp int
temp = *a
*a = *b
*b = temp
}
func main(){
var a int = 10
var b int = 20
swap(&a,&b)
fmt.Println("a=",a," b=",b)
}
defer
- 延期执行 类似压栈的方式(栈就类似一个有底的细管,内容就想一个个小球,最先进去的,就只能最后出来)
package main
import "fmt"
func main(){
defer fmt.Println("main end")
defer fmt.Println("main end1")
fmt.Println("main:hello go1")
fmt.Println("main:hello go2")
}
//main:hello go1
//main:hello go2
//main end1
//main end
- return and defer 测试
package main
import "fmt"
func deferFunc() {
fmt.Println("defer")
}
func returnFunc() int {
fmt.Println("return")
return 1
}
func returnAndDefer() int{
defer deferFunc()
return returnFunc()
}
func main(){
returnAndDefer()
}
//return
//defer
数组
- 数组声明方式
-
package main
import "fmt"
func main(){
var myArray [10]int
for i,j:=0,len(myArray);i<j;i++{
myArray[i]=i
}
fmt.Println(myArray)
}
package main
import "fmt"
func main(){
myArray1 := [10]int{1,2,3,4,}
for index,value := range myArray1{
fmt.Println(index, value)
}
}
slice
- 动态数组里的内容是地址,可以修改值,
- 定义方式一
package main
import "fmt"
func main(){
myArray := []int{1,2,3,4,5}
for index,value := range myArray{
fmt.Println(index , value)
}
fmt.Printf("%T",myArray) //[]int
fmt.Printf("len = %d,slice = %v\n" ,len(myArray),myArray)//5 selice =[1 2 3 4 5]
}
package main
import "fmt"
func main(){
myArray := []int{1,2,3,4,5}
for _,value := range myArray{ //不用可以下划线匿名
fmt.Println(value)
}
fmt.Printf("%T",myArray)
}
- 定义方式二
var selice1 []int //无值无空间
slice1 = make([]int,3) //开辟空间 0 0 0
slice1[0] = 1 // 1 0 0
- 定义方式三
var slice1 []int = make([]int,3) //声明并开辟空间
- 定义方式四
slice1 := make([]int,3)
- 判断是否为空切片
if slice1 == null{
fmt.Println("空")
}else{
fmt.Println("不为空")
}
切片追加
var numbers = make([]int, 3 ,5) //有效元素为 0 0 0 容量为5
fmt.Printf("len = %d, cap = %d,slice = %v\n",len(numbers),cap(numbers),numbers) // 3 5 0 0 0
numbers = append(numbers,1) //在后面追加一个1 ,0 0 0 1
//如果append追加超出容量,会动态的在创建一个cap容量,cap += cap
切片截取
s := []int{1,2,3} // len 3 cap 3
s1 := s[0:2] //s1 = 1,2 左闭右开的区间 [0:2) --> 0,1
copy(s2,s) //拷贝
map
- 定义方式一
var myMap map[string]string //map[key]value
if myMap == null{ //true
fmt.Println("null")
}
//使用前分配空间
make(map[string]string,10) //容量为十,满了在开辟,类似slice
myMap["one"] = "Java"
myMap["two"] = "c"
myMap["three"] = "python"
- 定义方式二
myMap := make(map[int]string) //容量可写可不写,看需要
myMap[1] = "Java"
myMap[2] = "c"
myMap[3] = "python"
- 定义方式三
myMap := map [string]string {
"one" : "php"
"two" : "c++"
"three": "python"
}
map 的使用方式
package main
import "fmt"
func printMap(cityMap map[string]string){
for index,value := range cityMap{
fmt.Println(index,value)
}
}
func changeMap(cityMap map[string]string){
cityMap["England"] = "London"
}
func main(){
cityMap := make(map[string]string)
//添加
cityMap["China"] = "beijing"
cityMap["Japan"] = "Tokyo"
cityMap["USA"] = "NewYork"
//遍历
printMap(cityMap)
//删除
delete(cityMap,"China")
//修改
cityMap["USA"] = "DC"
//遍历
printMap(cityMap)
//添加
changeMap(cityMap)
//遍历
printMap(cityMap)
}
面向对象
- 结构
package main
import "fmt"
//声明一种新的数据类型,myint 是int的一个别名
type myint int
//定义一个结构体
type Book struct{
title string
auth string
}
func changeBook(book Book){
//传递一个book的副本
book.title = "666"
}
func changeBooks(book *Book){
//传递一个book的指针
book.title = "777"
}
func main(){
var a myint = 10
fmt.Println("a=",a) //a= 10
fmt.Printf("a is %T \n",a) //a is main.myint
var book Book
book.title = "Golang"
book.auth = "a3"
fmt.Println(book) //{Golang a3}
changeBook(book)
fmt.Println(book)
changeBooks(&book) //{777 a3}
fmt.Println(book)
}
封装
package main
import "fmt"
//如果类名首字母大写,表示其他包也能够访问
type Hero struct{
//如果说类的属性首字母大写,表示该属性对外能够访问,否则的话只能够类的内部访问
Name string
Ad int
Level int
}
func (this Hero) Show(){
fmt.Println("hero= ",this)
}
func (this Hero) GetName() string{
return this.Name
}
//无指针无法修改
func (this *Hero) SetName(name string) {
this.Name = name
}
func main(){
//创建一个对象
hero := Hero{Name : "a3" ,Ad : 100 , Level : 12 }
hero.Show()
hero.SetName("lisi")
hero.Show()
}
继承
package main
import "fmt"
type Human struct{
name string
sex string
}
func (this Human) Eat() {
fmt.Println("Human.Eat()")
}
func (this Human) Waik(){
fmt.Println("Human.Waik()")
}
type SuperMan struct{
Human //SuperMan类继承了Human
Level int8
}
//重定义父类的方法
func (this SuperMan) Eat(){
fmt.Println("SuperMan.Eat()")
}
//子类的新方法
func (this SuperMan) Fly() {
fmt.Println("SuperMan.Fly")
}
func main(){
h := Human{
"a3",
"female",
}
h.Eat()
h.Waik()
//定义一个子类对象
s := SuperMan{
Human{"leisi","nv"},
12,
}
s.Waik()//父类的方法
s.Eat() //子类的方法
s.Fly() //子类的方法
}
多态
package main
import "fmt"
//本质是一个指针
type AnimalIF interface{
Sleep() //获取动物的
GetColor() string //获取动物的颜色
GetType() string //获取动物的种类
}
//定义一个具体的类
type Cat struct{
color string //猫的颜色
}
func (this Cat)Sleep(){
fmt.Println("Cat is Sleep")
}
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("Dog is Sleep")
}
func (this Dog) GetColor()string{
return this.color
}
func (this Dog) GetType()string{
return "Dog"
}
func showAnimal(animalIF AnimalIF){
animalIF.Sleep() //多态
fmt.Println(animalIF.GetColor())
fmt.Println(animalIF.GetType())
}
func main(){
var animalIF AnimalIF //接口的数据类型,父类指针
animalIF = Cat{"Green"}
animalIF.Sleep() //调用的是Cat的sleep方法 多态的现象
animalIF = Dog{"Yello"}
animalIF.Sleep() //调用Dog的sleep方法 多态的现象
cat := Cat{"Green"}
dog := Dog{"Yello"}
showAnimal(cat)
showAnimal(dog)
}
interface 万能类型
package main
import "fmt"
//interface{}是万能类型
func myFunc(arg interface{}){
fmt.Println("myFunc is called")
fmt.Println(arg)
//interface{} 该如何区分 此时引用的底层数据类型到底是什么?
//interface{} 提供 "断言机制"
value,ok := arg.(string)
if !ok {
fmt.Println("arg is not string type")
}else {
fmt.Println("arg is string type value = ",value)
fmt.Printf("value type is %T \n",value)
}
}
type Book struct{
auth string
}
func main(){
book := Book{"Golang"}
myFunc(book)
myFunc(100)
myFunc("abc")
myFunc(3.14)
}
pari
package main
import "fmt"
func main(){
var a string
//pari<typr : string,value : "aceld">
a = "aceld"
//pari<type : string,value : "aceld">
var allType interface{}
allType = a
value ,ok := allType.(string)
fmt.Println(value,ok) //aceld true
}
package main
import (
"fmt"
"io"
"os"
)
func main() {
//tty : pari<type : *os.File, "dev/tty">
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {
fmt.Println("open file error", err)
return
}
//r:pari<type , value>
var r io.Reader
//r:pari<type : *os.File,value:"/dev/tty"文件描述符>
r = tty
//r:pari<type , value>
var w io.Writer
//r:pari<type : *os.File,value:"/dev/tty"文件描述符>
w = r.(io.Writer)
w.Write([]byte("HELLO THIS IS A TEST!!!\n"))
}
package main
import "fmt"
type Reader interface {
ReadBook()
}
type Writer interface {
WriteBooK()
}
//具体类型
type Book struct {
}
func (this Book) ReadBook() {
fmt.Println("Read A Book")
}
func (this Book) WriteBooK() {
fmt.Println("Writer A Book")
}
func main() {
//b: pari<type : Book ,value : book{}地址>
b := Book{}
//r:pari <type,value>
var r Reader
//b: pari<type : Book ,value : book{}地址>
r = b
r.ReadBook()
var w Writer
//b: pari<type : Book ,value : book{}地址>
w = r.(Writer) //此处的断言为什么会成功?因为w r 具体的type是一致的
w.WriteBooK()
}
反射
- reflect
package main
import (
"fmt"
"reflect"
)
func reflectNum(arg interface{}) {
fmt.Println("type :", reflect.TypeOf(arg)) //float64
fmt.Println("value:", reflect.ValueOf(arg)) //1.2345
}
func main() {
var num float64 = 1.2345
reflectNum(num)
}
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,
"Aceld",
18,
}
DoFiledAndMethod(user)
}
func DoFiledAndMethod(input interface{}) {
//获取input的type
inputType := reflect.TypeOf(input)
fmt.Println("inputType= ", inputType.Name()) //inputType= User
//获取input的value
inputValue := reflect.ValueOf(input)
fmt.Println("inputValue= ", inputValue) //inputValue= {1 Aceld 18}
//分别通过type获取里面的字段
//1.获取interface的reflect.Type,通过Type得到NumField,进行遍历
//2.得到每个filed,数据类型
//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)
}
//通过type获取里面的方法调用
for i := 0 ; i < inputType.NumMethod();i++{
m := inputType.Method(i)
fmt.Printf("%s : %v\n",m.Name,m.Type)
}
}
结构体标签
package main
import (
"fmt"
"reflect"
)
type resume struct {
Name string `info:"name" doc:"我的名字"` //类似comment?
Sex string `info:"sex"`
}
func findTag(str interface{}) {
t := reflect.TypeOf(str).Elem()
for i := 0; i < t.NumField(); i++ {
tagstring := t.Field(i).Tag.Get("info")
tagDoc := t.Field(i).Tag.Get("doc")
fmt.Println("info", tagstring, "doc", tagDoc)
}
}
func main() {
var re resume
findTag(&re)
}
- json 编码解码
package main
import (
"encoding/json"
"fmt"
)
type Movie struct {
Title string `json:"title"`
Year int `json:"Year"`
Price int `json:"rmb"`
Actors []string `json:actors`
}
func main() {
movie := Movie{"喜剧之王", 2000, 10, []string{"星星", "吱吱"}}
//编码的过程 结构体 ---> json
jsonStr, err := json.Marshal(movie)
if err != nil {
fmt.Println("json marshal error", err)
return
}
//jsonStr = {"title":"喜剧之王","Year":2000,"rmb":10,"Actors":["星星","吱吱"]}
fmt.Printf("jsonStr = %s\n", jsonStr)
//解码过程 jsonStr --->结构体
myMovie := Movie{}
err = json.Unmarshal(jsonStr,&myMovie)
if err !=nil{
fmt.Println("json unmarshal error",err)
return
}
fmt.Printf("%v\n",myMovie)
}
goroutine
package main
import (
"fmt"
"time"
)
//子goroutine
func newTask() {
i := 0
for {
i++
fmt.Printf("new Goroutine : i = %d\n", i)
time.Sleep(1 * time.Second)
}
}
//主goroutine
func main() {
//创建一个go程,去执行newTask()进程
go newTask()
i := 0
for {
i++
fmt.Printf("main goroutine: i = %d\n", i)
time.Sleep(1 * time.Second)
}
}
package main
import (
"fmt"
"time"
)
func main() {
//用go创建一个形参为空,返回值为空的一个函数
go func() {
defer fmt.Println("A.defer")
//return
func() {
defer fmt.Println("B defer")
//退出当前goroutine
//runtime.Goexit()
fmt.Println("B")
}()
fmt.Println("A")
}()
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"
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 = ", num)
fmt.Println("main goroutine 结束")
}
//goroutine 正在运行
// goroutine结束
// num = 666
// main goroutine 结束
- 当channel已经满了,在向里面写数据,就会阻塞,当channel为空,从里面取数据也会阻塞
package main
import (
"fmt"
"time"
)
func main() {
c := make(chan int, 3) // 3 代表有缓冲的
fmt.Println("len(c) = ", len(c), "cap(c) = ", cap(c))
go func() {
defer fmt.Println("子go称结束")
for i := 0; i < 3; i++ {
c <- i
fmt.Println("子go程正在运行,发送的元素= ", i, "len(c) = ", len(c), " cap(c)= ", cap(c))
}
}()
time.Sleep(2 * time.Second)
for i := 0; i < 3; i++ {
num := <-c //从c中接收数据,并赋值给num "<-c" 不能有空格
fmt.Println("num = ", num)
}
fmt.Println("main 结束")
}
// len(c) = 0 cap(c) = 3
// 子go程正在运行,发送的元素= 0 len(c) = 0 cap(c)= 3
// 子go程正在运行,发送的元素= 1 len(c) = 1 cap(c)= 3
// 子go程正在运行,发送的元素= 2 len(c) = 2 cap(c)= 3
// 子go称结束
// num = 0
// num = 1
// num = 2
// main 结束
关闭channel
- channel不像文件一样需要经常去关闭,只有当你确实没有任何发送数据了,或者你想显示的结束range循环之类的,才去关闭channel
- 关闭channel后,无法向channel在发送数据(引发 panic 错误后导致接收立即返回零值)
- 关闭channel后,可以继续从channel接收数据
- 对于 nil channel,无论收发都会被阻塞
package main
import "fmt"
func main() {
c := make(chan int)
go func() {
for i := 0; i < 5; i++ {
c <- i
}
//close 可以关闭一个channel
close(c)
}()
for {
//ok如果为true表示channel没有关闭,如果为false表示channel已经关闭
if data, ok := <-c; ok {
fmt.Println(data)
}else{
break
}
}
fmt.Println("main Finished..")
}
// 0
// 1
// 2
// 3
// 4
// main Finished..
channel and range
package main
import "fmt"
func main() {
c := make(chan int)
go func() {
for i := 0; i < 5; i++ {
c <- i
}
//close关闭一个channel
close(c)
}()
//可以使用range来迭代不断操作channel,简写上一个案例
for data := range c {
fmt.Println(data)
}
fmt.Println("main Finished..")
}
channel and select
- select 具备多路channel的监控状态功能
package main
import "fmt"
func fibonacii(c, quit chan int) {
x, y := 1, 1
for {
select {
case c <-x:
//如果c可写,则该case就会进来
x = y
y = x + y
case <-quit:
fmt.Println(quit)
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
//sub go
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
//main go
fibonacii(c, quit)
}
//1
//1
//2
//4
//8
//16
//32
//64
//128
//256
//0xc00001c120
即时通信系统
1.0 基础server构建
package main
//telnet IP 端口 #连接server
//CTRL+] #进入输入界面
//send 内容 #发送字符串
func main() {
server := NewServer("127.0.0.1", 8888)
server.Start()
}
package main
import (
"fmt"
"net"
)
//ip 和 端口
type Server struct {
Ip string
Port int
}
//创建一个server的接口
func NewServer(ip string, port int) *Server {
server := &Server{
Ip: ip,
Port: port,
}
return server
}
func (this Server) Handler(conn net.Conn) {
//...当前连接的业务
fmt.Println("连接建立成功")
}
//启动服务器的接口
func (this *Server) Start() {
//socket Listen
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", this.Ip, this.Port))
if err != nil {
fmt.Println("net.Listen err:", err)
return
}
//close listen socket
defer listener.Close()
for {
//accept
conn, err := listener.Accept()
if err != nil {
fmt.Println("listener accept err :", err)
continue
}
//do handler
go this.Handler(conn)
}
}
2.0 用户上线及广播功能
package main
import "net"
type User struct {
Name string
Addr string
C chan string
conn net.Conn
}
//创建一个用户的API
func NewUser(conn net.Conn) *User {
userAddr := conn.RemoteAddr().String()
user := &User{
Name: userAddr,
Addr: userAddr,
C: make(chan string),
conn: conn,
}
//启动监听当前user channel消息的goroutine
go user.ListenMessage()
return user
}
//监听当前user channel方法,一旦有消息,就直接发送给客户端
func (this User) ListenMessage() {
for {
msg := <-this.C
this.conn.Write([]byte(msg + "\n"))
}
}
package main
import (
"fmt"
"net"
"sync"
)
type Server struct {
Ip string
Port int
//在线用户的列表
OnlineMap map[string]*User
mapLock sync.RWMutex
//消息广播的channel
Message chan string
}
//创建一个server的接口
func NewServer(ip string, port int) *Server {
server := &Server{
Ip: ip,
Port: port,
OnlineMap: make(map[string]*User),
Message: make(chan string),
}
return server
}
//监听Message广播消息channel的goroutine,一旦有消息就发送给全部的在线User
func (this *Server) ListenMessager() {
for {
msg := <-this.Message
//将msg发送给全部的在线User
this.mapLock.Lock()
for _, cli := range this.OnlineMap {
cli.C <- msg
}
this.mapLock.Unlock()
}
}
//广播消息的方法
func (this *Server) BroadCast(user *User, msg string) {
sendMsg := "[" + user.Addr + "]" + user.Name + ":" + msg
this.Message <- sendMsg
}
func (this *Server) Handler(conn net.Conn) {
//...当前链接的业务
//fmt.Println("链接建立成功")
user := NewUser(conn)
//用户上线,将用户加入到onlineMap中
this.mapLock.Lock()
this.OnlineMap[user.Name] = user
this.mapLock.Unlock()
//广播当前用户上线消息
this.BroadCast(user, "已上线")
//当前handler阻塞
select {}
}
//启动服务器的接口
func (this *Server) Start() {
//socket listen
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", this.Ip, this.Port))
if err != nil {
fmt.Println("net.Listen err:", err)
return
}
//close listen socket
defer listener.Close()
//启动监听Message的goroutine
go this.ListenMessager()
for {
//accept
conn, err := listener.Accept()
if err != nil {
fmt.Println("listener accept err:", err)
continue
}
//do handler
go this.Handler(conn)
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
· 零经验选手,Compose 一天开发一款小游戏!