这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天
说明
go语言给我的感觉是简洁版的c/c++语言。而我本人主语言就是c/c++所以我会通过对比的方式来学习Go语言。
一、基本语法
1.包的概念
可以在一个 Go语言源文件包声明语句之后,其它非导入声明语句之前,包含零到多个导入包声明语句。每个导入声明可以单独指定一个导入路径,也可以通过圆括号同时导入多个导入路径。要引用其他包的标识符,可以使用 import 关键字,导入的包名使用双引号包围,包名是从 GOPATH 开始计算的路径,使用/进行路径分隔。
类比于c/c++中的include不过其更具灵活性,能从开源社区中直接导入包,直接用,总而言之go语言的包管理十分成熟和友善,不像c/c++那般复杂。
2.变量和常量
变量的声明类型后置,也可以按类型推导。 三种方法如下:
//方法一:声明一个变量
var (
b, c int = 1, 2
)
fmt.Println(b, c)
fmt.Printf("type c is %T ", c)
//方法二:自动推到类型
var e = 10
var f = "hello go"
fmt.Println(f)
fmt.Println(e)
fmt.Printf("type e is %T", e)
//方法三:只能用于声明函数内的局部变量 自动推导类型
d := 2
fmt.Println(d)
fmt.Print("hello world")
方法二和方法三即使是自动类型推导,其和py好像有点类似,但是go是强类型语言! 多变量声明:
//多变量声明
var i, g = 1, 2
fmt.Println(i, g)
//多行声明多变量
var (
m = 1
n = 1
)
fmt.Println(m, n)
多变量声明这点和c++有类似但是又不完全相同,多行声明是go的特点。 go语言中的常量关键字是const其一点作用就是可用作枚举:
const l = 10
fmt.Println(l)
const (
//iota会逐渐递增
BEIJING = iota * 3
SHANGHAI
TIANJING
)
3.函数
go语言中的函数,一开始用着我是十分的不习惯,因为是类型后置,但是类型后置确实是有一点逻辑在里面的,go语言的函数是可以返回多个值的。
package main
import "fmt"
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("Mahesh", "Kumar")
fmt.Println(a, b)
}
函数的参数也跟c++一样有值传递,指针传递(引用传递),指针的概念和c/c++如出一辙没什么太大区别。不过这里需要注意一点,当指针作为函数参数的时候,其*是置于前面的:
func swap(x *int, y *int) {
var temp int
temp = *x /* 保存 x 地址上的值 */
*x = *y /* 将 y 值赋给 x */
*y = temp /* 将 temp 值赋给 y */
}
4.defer关键字
defer关键字用于预定对一个函数的调用,被其调用的函数可以称为延迟函数,一般用于释放占用的资源,捕捉处理异常,输出日志。如果一个函数中有多个defer他会类似于入栈的形式,后进先出。还有一个重点:defer的调用晚于函数中的return语句执行。
package main
import "fmt"
// return 先于 defer执行
// defer的调用方式类似与压栈 先进后出
func deferfunc() int {
fmt.Println("this is defer")
return 0
}
func returnfunc() int {
fmt.Println("this is return")
return 0
}
func test() int {
defer deferfunc()
return returnfunc()
}
// defer的执行顺序是一个压栈的顺序
func test2() {
defer fmt.Println("1")
defer fmt.Println("2")
defer fmt.Println("3")
}
func main() {
test()
test2()
}
5.静态数组和动态数组slice
静态数组就是和c数组差不多,就是类型后置,slice是动态数组,可以动态的变换长度,可以类比于std::vector,下面是slice的基本使用(学过cpp应该都懂 切片作为函数传递的时候是引用传递 这句话是不准确的 他只是引用了底层的数组,长度len和容量cap都是拷贝):
```
package main
import "fmt"
func main() {
//slice的声明方式
//注意make返回的是引用类型本身
a := []int{1, 2, 3, 4, 5}
fmt.Printf("%v", a)
var b []int
fmt.Printf("%v", b)
var c []int
//分配三片空间
c = make([]int, 3)
fmt.Printf("%v", c)
var d []int = make([]int, 3)
fmt.Printf("%v", d)
e := make([]int, 3)
fmt.Printf("%v", e)
//切片的追加
//切片有容量和长度之分 长度是有效值的长度 容量是分配内存的大小 通过重复分配一片内存截取的话 使用copy函数
f := make([]int, 3, 5)
fmt.Printf("%d %d\n", len(f), cap(f))
f = append(f, 2)
f = append(f, 2)
f = append(f, 2)
f = append(f, 2)
//此时元素的总量已经超过了长度 所以需要追加申请内存 申请内存的大小取决于设置的容量
fmt.Printf("%d %d\n", len(f), cap(f))
//这样子的话 长度和容量是相等的
g := make([]int, 3)
fmt.Printf("%d %d\n", len(g), cap(g))
//切片的截取 切片的截取是获得其内部的指针 两块指向同一块内存空间 类似于c++中的string_view
h := g[0:2] //左闭右开
for key, value := range h {
print(key, value, "\n")
}
println(cap(h), len(g))
}
```
6.结构体
go语言中的结构体,成员变量和初始化都和c++中的class差不多,其独特的点就是成员方法的声明和定义,还有继承和多态的差别:
package main
import (
"fmt"
)
// go语言利用命名的大小写来指定类、类方法、类成员变量、普通函数的共有私有属性 其私有公有是针对其他的包的
type hero struct {
id int
name string
age int
}
// 值传递
func changehero(h hero) {
h.age = 18
}
// 引用传递
func changehero1(h *hero) {
h.age = 22
}
type man struct {
name string
age int
}
//类的成员方法的定义
//这种定义方式是一个副本
//func (this man) Set(name string, age int) man {
// this.name = name
// this.age = age
// return this
//}
//
//func (this man) Show() {
// fmt.Println("年龄是", this.age, "姓名是", this.name)
//}
// 这是指针传递的类方法
func (this *man) Set(name string, age int) *man {
this.name = name
this.age = age
return this
}
func (this *man) Show() {
fmt.Println("年龄是", this.age, "姓名是", this.name)
}
type Amnimal struct {
len int
name string
}
// 面向对象的继承
func (this *Amnimal) test() {
fmt.Print("hello world \n")
}
type Cat struct {
//表明继承自animal
Amnimal
}
// 可以重写父类的方法
func (this *Cat) test() {
fmt.Print("hello world yes ! \n")
}
// 面向对象的多态
// 定义一个接口 其他类重写接口既可实现多态
type color interface {
showColor()
}
type blue struct {
color string
}
func (this *blue) showColor() {
println(this.color)
}
type red struct {
color string
}
func (this *red) showColor() {
println(this.color)
}
func show(c color) {
c.showColor()
}
func main() {
//结构体的初始化
//h: = hero{id: 1,name:"hero",age: 1}
var h hero = hero{id: 0, name: "hero", age: 0}
fmt.Printf("%v\n", h)
changehero(h)
fmt.Printf("%v\n", h)
changehero1(&h)
fmt.Printf("%v\n", h)
m := man{}
m.age = 10
m1 := m.Set("王仁鑫", 18)
m1.Show()
m.age = 10
m.Show()
var cat Cat
cat.len = 1
cat.name = "hello"
cat.test()
b := blue{"蓝色"}
r := red{color: "红色"}
show(&b)
show(&r)
}
二、常用模块
Json
json的数据格式十分常见,go语言中十分简洁的定义了其序列化和反序列化的方法:
package main
import (
"encoding/json"
"fmt"
)
// 字段名必须设为公有 不然其他包方法运行调用结构体是访问不到的
type userInfo struct {
Name string
Id int `json:"id"` //可以通过这种方式命名json转化后别名
Hobby []string
}
func main() {
a := userInfo{Name: "王仁鑫", Id: 1, Hobby: []string{"篮球", "唱", "跳"}}
fmt.Println(a)
//转成json格式
buf, err := json.Marshal(a)
//panic 和 recover理解为异常的抛出和捕捉
if err != nil {
panic(err)
}
//byte是对应的Ascll编码值
fmt.Println(buf)
//用字符串形式打包
fmt.Println(string(buf))
//每个元素都以新的缩进开始
buf, err = json.MarshalIndent(a, "", "\t")
if err != nil {
panic(err)
}
fmt.Println(string(buf))
//反序列化 将json数据反序列化为一个对象
var b userInfo
//传进去序列化的buf和对象的地址
err = json.Unmarshal(buf, &b)
fmt.Printf("%#v\n", b)
}
Time
一些日常调用时间的方法,获取时间段,时间点,时间戳等。
package main
import (
"fmt"
"time"
)
func main() {
//获取当前的时间
now := time.Now()
fmt.Println(now)
//构造一个时间点
//年月日小时分钟秒钟 加时区
t1 := time.Date(2022, 1, 1, 0, 0, 0, 0, time.Local)
t2 := time.Date(2022, 1, 2, 0, 0, 0, 0, time.Local)
//可以依次获取时间的各个信息
fmt.Println(t1)
fmt.Println(t1.Date())
fmt.Println(t1.Hour())
fmt.Println(t1.Local())
fmt.Println(t1.Second())
//自定义格式化输出
fmt.Println(t1.Format("2002-01-01 15:04:05"))
//输出两个时间点的时间间隔
diff := t2.Sub(t1)
fmt.Println(diff)
//解析字符串 前一个是模板 后一个是实际需要解析的内容
//t3, err := time.Parse("2002-01-01 15:04:05", "2022-01-01 15:04:05")
//if err != nil {
// // panic(err)
// //}
// //fmt.Println(t3 == t1)
//获取时间戳(格林威治时间到当前时间的总秒数)
fmt.Println(now.Unix())
}
Number
对字符串和数字之间的转化。
package main
import (
"fmt"
"strconv"
)
func main() {
//64代表64位精度的
f, _ := strconv.ParseFloat("1.123", 64)
fmt.Println(f)
i, _ := strconv.ParseInt("1", 10, 64)
fmt.Println(i)
//直接转化为十进制的数字
n2, _ := strconv.Atoi("13213")
fmt.Println(n2)
//如果字符串不合法
n3, err := strconv.Atoi("aaa")
if err != nil {
panic("出大问题")
}
fmt.Println(n3)
}
三、项目
项目一猜谜游戏
重点:熟悉go语言的基本语法和一些模块的使用
项目二在线词典
重点:了解了go语言常见的几个工具以及go原生自带的http框架
- Json生成结构体oktools.net/json2go
- 利用curl生成http请求头部等curlconverter.com/go/
项目三socket5代理服务器
重点:深度了解了代理服务器协议socket5! socks协议的设计初衷是在保证网络隔离的情况下,提高部分人员的网络访问权限,但是对应网络安全来讲,更多的是利用socks5这个协议来访问到内部的网络,访问一些访问不到的资源,这也是对于网络攻防层面来讲,但是socks5的用途也是很多的。