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 {
//处理业务
}
}
效果:
实战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())
}()
}
}
效果: