单行注释
//单行注释
多行注释
/*
这是多行注释
这是一个main函数,这个是go语言启动的入口
*/
声明变量使用var关键字:
var name type
var 变量名 变量类型
第一个var是声明变量的关键字
第二个name,就是我们的变量名字
第三个type,就是用来代表变量的类型
命名规则驼峰命名,ex:userInfo
var name string
var age int
var {//定义多个变量
name string //默认值 空
age int //默认值 0
addres string
}
短变量声明并初始化
// := 自动推导
name := "Jay"
age := 18
fmt.Printf("%T,%T", name, age) //查看变量类型
打印变量内存地址
var num int
num = 100
fmt.Printf("num:%d,内存地址:%p", num, &num) //取地址符, &变量名
变量的交换
var a int = 100
var b int = 200
b, a = a, b
fmt.Println(a, b)
匿名变量
func main() {
a, _ := test() //匿名变量_
_, b := test() //匿名变量_
fmt.Println(a)
fmt.Println(b)
}
func test() (int, int) {
return 100, 200
}
变量的作用域
局部变量
func main() {
var a int = 100 //局部变量
var b int = 200 //局部变量
}
函数体内定义,只能在该函数内使用
全局变量
var a int = 100 //全局变量
var b int = 200 //全局变量
func main() {
}
函数体外定义,在整个go文件都可使用
外面定义了全局变量,函数内部还可以定义局部变量,使用就近原则
常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。
const identifer[type] = value
const URL string = "www.baidu.com" //显示定义
const URL2 = "www.google.cn" //隐式定义
const a, b, c = 3.14, "Jay", false
特殊常量-iota
package main
import "fmt"
func main() {
iota可以用作枚举值
const (
a = iota //a=0
b //b=1
c //c=2
d = "haha" //haha
e //haha
f = 100 //100
g //100
h = iota //iota7
i //iota8
)
const (
j = iota //0
k //1
)
fmt.Println(a, b, c, d, e, f, g, h, i, j, k)
}
布尔型的值只可以是常量true或false;var flag bool = true
package main
import "fmt"
func main() {
var isFlag bool
fmt.Println(isFlag) //默认值false
fmt.Printf("%T,%t", isFlag, isFlag) //bool, false
}
序号 | 类型和描述 |
---|---|
1 | uint8无符号8位整型(0到255) |
2 | uint16无符号16位整型(0到65535) |
3 | uint32无符号32位整型(0到4294967295) |
4 | uint64无符号64位整型(0到18446744073709551615) |
5 | int8有符号8位整型(-128到127) |
6 | int16有符号16位整型(-32768到32767) |
7 | int32有符号32位整型(-2147483648到2147483647) |
8 | int64有符号64位整型(-9223372036854775808到9223372036854775807) |
package main
import "fmt"
func main() {
var age int = 18
fmt.Println(age)
fmt.Printf("%T,%d\n", age, age)
}
序号 | 类型和描述 |
---|---|
1 | float32 IEEE-754 32位浮点型数 |
2 | float64 IEEE-754 64位浮点型数(默认) |
3 | complex64 32位实数和虚数 |
4 | complex128 64位实数和虚数 |
package main
import "fmt"
func main() {
var money float64 = 3.145
fmt.Println(money)
fmt.Printf("%T,%d\n", age, age)
//%f默认保留小数点6位数
fmt.Printf("%T,%.2f\n", money, money) //%.2f保留2位;丢失精度,四舍五入
}
package main
import "fmt"
func main() {
var str string
str = "hello,Golang"
fmt.Println(str)
fmt.Println(str + ",study") //字符串拼接 +
//转义字符 \" \n \t
fmt.Println(str + "\"study")
fmt.Printf("%T,%s\n", str, str)
//编码表ASCII字符码
//所有中国汉字GBK表
//全世界的编码表 Unicode编码表
v1 := 'A'
v2 := "A"
v3 := '中'
fmt.Printf("%T,%d\n", v1, v1) //int32,65
fmt.Printf("%T,%s\n", v2, v2) //string,A
fmt.Printf("%T,%d\n", v3, v3) //int32,20013
}
遍历字符串string
package main
import "fmt"
func main() {
str := "hello,jay"
fmt.Println(str)
//获取字符串的长度 len
fmt.Println("字符串的长度为:", len(str))
//获取指定的字节,下标从 0 开始
fmt.Println("字符打印:", str[0]) //输出的是ASCII码表对应的值
//for循环遍历 string
for i := 0; i < len(str); i++ {
fmt.Printf("%c", str[i])
//fmt.Println(str[i])
}
//for range 循环,遍历数组、切片...
for i, v := range str {
fmt.Println()
fmt.Print(i)
fmt.Printf("%c", v)
fmt.Print("\t")
}
}
Go语言不存在隐式类型转换,因此所有的类型转换都必须显示的声明
valueOfTypeB = typeB(valueOfTypeA)
package main
import "fmt"
func main() {
a := 3
b := 5.0
var num byte = 11
//类型转换
c := float64(a)
d := int(b)
e := int(num)
f := byte(a)
fmt.Printf("%T\n", a) //int
fmt.Printf("%T\n", b) //float64
fmt.Printf("%T\n", c) //float64
fmt.Printf("%T\n", d) //int
fmt.Printf("%T\n", e) //int
fmt.Printf("%T\n", f) //uint8
}
算术运算符
假定A的值10,B的值20
运算符 | 描述 | 实例 |
---|---|---|
+ | 相加 | A + B = 30 |
- | 相减 | A - B = -10 |
* | 相乘 | A * B = 200 |
/ | 相除 | B / A = 2 |
% | 求余 | B % A = 0 |
++ | 自增 | A++ = 11 |
– | 自减 | A-- = 9 |
package main
import "fmt"
func main() {
var a int = 10
var b int = 3
fmt.Println(a + b) //13
fmt.Println(a - b) //7
fmt.Println(a * b) //30
fmt.Println(a / b) //3
fmt.Println(a % b) //1
a++
fmt.Println(a) //11
a = 100
a--
fmt.Println(a) //99
}
关系运算符
假定A的值10,B的值20
运算符 | 描述 | 实例 |
---|---|---|
== | 检查两个值是否相等,如果相等返回true否则返回false | (A == B)为false |
!= | 检查两个值是否不相等,如果不相等返回true否则返回false | (A != B)为true |
> | 检查左边值是否大于右边值,如果大于返回true否则返回false | (A > B)为false |
< | 检查左边值是否小于右边值,如果小于返回true否则返回false | (A < B)为true |
>= | 检查左边值是否大于等于右边值,如果大于等于返回true否则返回false | (A >= B)为false |
<= | 检查左边值是否小于等于右边值,如果小于等于返回true否则返回false | (A <= B)为true |
package main
import "fmt"
func main() {
var a int = 11
var b int = 10
//关系运算符返回布尔值
fmt.Println(a == b) //false
fmt.Println(a != b) //true
fmt.Println(a > b) //true
fmt.Println(a < b) //false
fmt.Println(a >= b) //true
fmt.Println(a <= b) //false
}
逻辑运算符
假定A的值true,B的值false
运算符 | 描述 | 实例 |
---|---|---|
&& | 逻辑AND运算符,如果两边的操作数都是true,则为true否则为false | (A && B) 为false |
|| | 逻辑OR运算符,如果两边的操作数有一个true,则为true否则为false | (A || B) 为true |
! | 逻辑NOT运算符,如果条件为true,则为false否则为true | (! A) 为false |
package main
import "fmt"
func main() {
var a bool = true
var b bool = false
fmt.Println(a && b) //false
fmt.Println(a || b) //true
fmt.Println(!b) //true
}
位运算符
假定A为60,B为13
运算符 | 描述 | 实例 |
---|---|---|
& | 按位与运算符&是双目运算符,都是1结果位1,否则为0 | (A&B)=12 |
| | 按位或运算符|是双目运算符,都是0结果为0,否则为1 | (A|B)=61 |
^ | 按位异或运算符^是双目运算符,不同则为1,相同则为0 | (A^B)=49 |
&^ | 位清空A &^ B对于每个数值,如果为0则取A对应位上的值,如果为1,则取0 | (A&^B)=48 |
<< | 左移运算符,高位丢弃,低位补0;移一位等于 *2 | A<<2=240 |
>> | 右移运算法,移一位等于 /2 | A>>2=15 |
package main
import "fmt"
func main() {
var a uint = 60
var b uint = 13
var c uint = 0
// 60 0011 1100
// 13 0000 1101
c = a & b
fmt.Printf("%T,二进制%b\n", c, c) //& 0000 1100
c = a | b
fmt.Printf("%T,二进制%b\n", c, c) // | 0011 1101
c = a ^ b
fmt.Printf("%T,二进制%b\n", c, c) // ^ 0011 0001
c = a &^ b
fmt.Printf("%T,二进制%b\n", c, c) // &^ 0011 0000
c = a << 2
fmt.Printf("%T,二进制%b\n", c, c) // a<<2 1111 0000
c = a >> 2
fmt.Printf("%T,二进制%b\n", c, c) // a>>2 0000 1111
}
赋值运算符
运算符 | 描述 |
---|---|
= | 简单的赋值运算符,将一个表达式的值赋给一个左值 |
+= | 相加后再赋值 a += b //a = a + b |
-= | 相减后再赋值 a -= b //a = a - b |
*= | 相乘后再赋值a *= b //a = a * b |
/= | 相除后再赋值a /= b //a = a / b |
%= | 求余后再赋值a %= b // a = a % b |
<<= | 左移后再赋值 |
>>= | 右移后再赋值 |
&= | 按位与后再赋值 |
^= | 按位异或后再赋值 |
|= | 按位或后再赋值 |
其他运算符
运算符 | 描述 | 实例 |
---|---|---|
& | 返回变量存储地址 | &a;将给出变量的实际地址 |
* | 指针变量 | *a;是一个指针变量想 |
package main
import "fmt"
func main() {
var a int = 4
var b int32
var c float32
var ptr *int
fmt.Printf("%T\n", a) //int
fmt.Printf("%T\n", b) //int32
fmt.Printf("%T\n", c) //float32
ptr = &a
fmt.Printf("%d\n", a) //4
fmt.Printf("%p\n", ptr) //0xc00001c0a8
fmt.Printf("%d\n", ptr) //824633835688
}
package main
import "fmt"
func main() {
/** 输入
fmt.Scanf()
fmt.Scanln()
fmt.Scan()
*/
var x int
var y float64
fmt.Println("请输入两个数:1.整数 2.浮点数")
fmt.Scanln(&x, &y) //指针地址来修改操作变量
fmt.Println("x:", x)
fmt.Println("y:", y)
}
命名规范
包名:package
保持package的名字和目录名字保持一致,尽量采取简短、有意义,尽量和标准库不要冲突。包名应该为小写单词,不要使用下划线或者混合大小写。
package model
package main
文件名
import规范
错误处理
顺序结构:从上到下,逐行执行。默认的逻辑
条件满足某些代码才会执行
if
package main
import "fmt"
func main() {
var score int = 90
if score >= 90 && score <= 100 {
fmt.Println("A")
} else if score >= 80 && score < 90 {
fmt.Println("B")
} else if score >= 70 && score < 80 {
fmt.Println("C")
} else if score >= 60 && score < 70 {
fmt.Println("D")
} else if score >= 0 && score < 60 {
fmt.Println("E")
} else {
fmt.Println("不合格")
}
}
if 布尔表达式1 {
if 布尔表达式2 {
//在布尔表达式2为true时执行
}
}
package main
import "fmt"
func main() {
var a, b int
var pwd int = 20230303
//用户输入
fmt.Println("请输入密码:")
fmt.Scan(&a)
if a == pwd {
fmt.Println("请再次输入密码")
fmt.Scan(&b)
if b == pwd {
fmt.Println("恭喜,登录成功")
} else {
fmt.Println("登录失败了,第二次密码错误")
}
} else {
fmt.Println("登录失败,密码错误")
}
}
package main
import "fmt"
func main() {
var score int = 90
//匹配规则
switch score {
case 90:
fmt.Println("A")
fallthrough //case穿透
case 80:
fmt.Println("B")
case 60, 70:
fmt.Println("C")
default:
fmt.Println("D")
}
} //输出A和B
select
循环结构:条件满足某些代码会被反复执行0-N次
for
package main
import "fmt"
func main() {
//for一个参数都没有,无限循环
//计算1-10的和
sum := 0
for i := 1; i <= 10; i++ {
sum += i
}
fmt.Println(sum)
}
练习题
//习题一:打印一个方阵 5*5
package main
import "fmt
func main() {
for i := 0; i < 5; i++ {
for j := 0; j < 5; j++ {
fmt.Print("*") //打印一排星号
}
fmt.Println() //每打印一排就换行
}
}
//习题二:打印九九乘法表
package main
import "fmt"
func main() {
for i := 1; i <= 9; i++ {
for j := 1; j <= i; j++ {
//fmt.Print(j)
//fmt.Print("*")
//fmt.Print(i)
//fmt.Print("=")
//fmt.Print(i * j)
//fmt.Print("\t")
//Printf 格式化输出,也可以进行格式编排
//fmt.Printf("%d*%d=%d \t", j, i, j*i)
fmt.Print(j, "*", i, "=", j*i, "\t")
}
fmt.Println()
}
}
break 结束当前整个循环
package main
import "fmt"
func main() {
for i := 1; i < 5; i++ {
if i == 3 {
break
}
fmt.Print(i) //输出1,2
}
}
continue 结束当次循环
package main
import "fmt"
func main() {
for i := 1; i < 5; i++ {
if i == 3 {
continue
}
fmt.Print(i) //输出1,2,4
}
}
goto 可以跳出双重循环
package main
import "fmt"
func main() {
test()
}
func test() {
for i := 0; i < 10; i++ {
for j := 0; j < 10; j++ {
if i >= 2 && j >= 2 {
goto END
}
fmt.Println(i, j)
}
}
END:
fmt.Println("END...")
}
for range 循环
package main
import "fmt"
func main() {
str := "hello,jay"
fmt.Println(str)
//获取字符串的长度 len
fmt.Println("字符串的长度为:", len(str))
//获取指定的字节,下标从 0 开始
fmt.Println("字符打印:", str[0]) //输出的是ASCII码表对应的值
//for循环遍历 string
for i := 0; i < len(str); i++ {
fmt.Printf("%c", str[i])
//fmt.Println(str[i])
}
//for range 循环,遍历数组、切片...
for i, v := range str {
fmt.Println()
fmt.Print(i)
fmt.Printf("%c", v)
fmt.Print("\t")
}
}
数组是相同类型的一组数据的集合,数组一旦定义长度不能修改,数组可以通过下标(索引)来访问元素。
数组定义的语法:
var variable_name [size] variable_type
variable_name:数组名称
size:数组长度,必须是常量
variable_type:数组保存元素的类型
省略长度定义数组,会自动推断
var variable_name = [...] variable_type{value1,value2}
通过索引来定义数组
var variable_name = [...] variable_type{index0: value1, index2: value2}
索引(下标)可随意写,没有定义索引的值就是默认值
数组长度len(variable_name)
最大下标len(variable_name) - 1
实例
package main
import "fmt"
func main() {
test1()
test2()
test3()
}
func test1() {
var a1 [2]int
var a2 [3]string
fmt.Printf("a1:%T\n", a1) //a1:[2]int
fmt.Printf("a2:%T\n", a2) //a2:[3]string
fmt.Printf("a1:%v\n", a1) //a1:[0 0] 默认值0
fmt.Printf("a2:%v\n", a2) //a2:[ ] 默认值null
}
func test2() {
var a1 = [2]int{1, 2}
fmt.Printf("a1:%v", a1) //[1,2]
//数组的初始化,默认长度或省略长度 ... 会自动推断长度
var a2 = [...]int{3, 4, 5}
fmt.Printf("\na2:%v", a2) //[3,4,5]
}
func test3() {
var a1 = [...]int{0: 1, 2: 3, 4: 5}
fmt.Printf("a1:%v", a1) //[1,0,3,0,5]
}
遍历数组
package main
import "fmt"
func main() {
testPrint()
testPrint1()
testPrint2()
}
func testPrint() {
var a1 = [3]int{1, 2, 3}
for i := 0; i < len(a1); i++ {
fmt.Println(a1[i]) //1 2 3
}
}
func testPrint1() {
var a1 = [4]int{1, 2, 3, 4}
for i, v := range a1 {
fmt.Printf("a1[%v]: %v \n", i, v) //a1[0]: 1,a1[1]: 2,a1[2]: 3,a1[3]: 4
}
}
func testPrint2() {
//匿名变量可以去掉下标
var a1 = [4]int{1, 2, 3, 4}
for _, v := range a1 {
fmt.Println(v) //1 2 3 4
}
}
Go语言中的切片理解为,可变长度大的数组,其实底层还是数组实现,增加了自动扩容功能,切片(slice)是一个拥有相同类型元素的可变长度的序列。
切片的语法
var identifier []type
切片是引用类型,可以使用make函数类创建切片
var slice []type = make([]type, len)
简写
slice := make([]type, len)
也可以给切片指定容量,其中capacity为可选参数
make([]T, length, capacity)
length是数组的长度,也是切片的初始长度
切片实例
package main
import "fmt"
func main() {
testFunc()
testFunc1()
testFunc2()
}
func testFunc() {
var s1 []int
var s2 []string
fmt.Printf("s1:%v\n", s1) //s1:[]
fmt.Printf("s2:%v\n", s2) //s2:[]
}
func testFunc1() {
var s2 = make([]int, 2)
fmt.Printf("s2:%v\n", s2) //s2:[0 0]
}
func testFunc2() {
var s1 = []int{1, 2, 3}
fmt.Printf("len(s1):%v\n", len(s1)) //len(s1):3
fmt.Printf("cap(s1):%v\n", cap(s1)) //cap(s1):3
}
切片初始化
package main
import "fmt"
func main() {
sliceFunc()
}
func sliceFunc() {
var s1 = []int{1, 2, 3, 4, 5, 6}
s2 := s1[0:3] //左闭右开区间
fmt.Printf("s2:%v\n", s2) //1,2,3
s3 := s1[:]
fmt.Printf("s3:%v\n", s3) //全部取出
}
切片的遍历和数组一样
切片元素的添加、删除、复制
package main
import "fmt"
func main() {
addSlice()
deleteSlice()
updateSlice()
querySlice()
copySlice()
}
func addSlice() {
var s1 = []int{}
s1 = append(s1, 100)
s1 = append(s1, 200)
s1 = append(s1, 300)
fmt.Printf("s1:%v\n", s1) //s1:[100 200 300]
}
func deleteSlice() {
var s2 = []string{"a", "b", "c", "d", "e"}
s2 = append(s2[:2], s2[3:]...) //删除下标2这个元素
//公式:a = append(a[:index],a[index+1:]...)
fmt.Printf("s2:%v\n", s2) //s2:[a b d e]
}
func updateSlice() {
var s3 = []float64{1.0, 2.0, 3.0, 4.0}
s3[1] = 1.1
fmt.Printf("s3:%v\n", s3) //s3:[1 1.1 3 4]
}
func querySlice() {
var s4 = []int{1, 2, 3, 4}
var key = 2
for i, v := range s4 {
if v == key {
fmt.Printf("i:%v\n", i) //i:1
fmt.Printf("v:%v\n", v) //v:2
}
}
}
func copySlice() {
var s5 = []int{1, 2, 3, 4, 5}
var s6 = make([]int, 5)
copy(s6, s5)
s5[0] = 100
fmt.Printf("s5:%v\n", s5) //s5:[100 2 3 4 5]
fmt.Printf("s6:%v\n", s6) //s6:[1 2 3 4 5]
}
map是一种key:value键值对的数据结构容器。map内部实现是哈希表(hash
)。
map最重要的一点是通过key来快速检索数据,key类似于索引,指向数据的值。
map是引用类型的。
map的语法格式
var map_variable map [key_data_type] value_data_type
map_variable = make(map [key_data_type] value_data_type)
map_variable:变量名称
key_data_type:key的数据类型
value_data_type:值得数据类型
实例 map是无序的
package main
import "fmt"
func main() {
mapFunc()
mapFunc1()
}
func mapFunc() {
//map类型的声明
var m1 map[string]string
m1 = make(map[string]string)
fmt.Printf("m1:%v\n", m1) //m1:map[]
fmt.Printf("m1:%T\n", m1) //m1:map[string]string
}
func mapFunc1() {
var m2 = map[string]string{"name": "tom", "age": "20", "email": "[email protected]"}
fmt.Printf("m2:%v\n", m2) //m2:map[age:20 email:[email protected] name:tom]
m3 := make(map[string]string)
m3["name"] = "jack"
m3["age"] = "21"
m3["email"] = "[email protected]"
fmt.Printf("m3:%v\n", m3) //m3:map[age:21 email:[email protected] name:jack]
}
key来取值
package main
import "fmt"
func main() {
mapFunc2()
}
func mapFunc2() {
var m4 = map[string]string{"name": "tom", "age": "20", "email": "[email protected]"}
var k1 = "name"
var k2 = "age1"
v, ok := m4[k1]
fmt.Printf("v:%v\n", v)
fmt.Printf("ok:%v\n", ok)
v1, ok1 := m4[k2]
fmt.Printf("v:%v\n", v1)
fmt.Printf("ok:%v\n", ok1)
}
遍历map
package main
import "fmt"
func main() {
mapIterate()
}
func mapIterate() {
var m1 = map[string]string{"name": "tom", "age": "20", "email": "[email protected]"}
for k, v := range m1 {
fmt.Printf("%v:%v\n", k, v)
}
}
Go语言函数定义格式如下:
func function_name([parameter list]) [return_types] {
//函数体
}
无参无返回值函数
有一个参数的函数
有两个参数的函数
有一个返回值的函数
有多个返回值的函数
package main
import (
"fmt"
)
func main() {
printInfo()
myPrint("有一个参数的函数")
//有返回值的函数,需要接收返回值
myPrintNum(add1(1, 2))
x, y := swap("Jay", "Mikro")
fmt.Println(x, y)
}
// 无参无返回值的函数
func printInfo() {
fmt.Println("无参无返回值函数")
}
// 有一个参数的函数
func myPrint(msg string) {
fmt.Println(msg)
}
func myPrintNum(num int) {
fmt.Println(num)
}
// 有两个参数的函数
func add1(a, b int) int {
c := a + b
return c
}
// 有多个返回值的函数
func swap(x, y string) (string, string) {
return y, x
}
函数形式参数和实际参数
package main
import "fmt"
func main() {
//形参和实参要一一对应,顺序,个数,类型
maxNum := max(10, 15)
fmt.Println(maxNum)
}
// max 两个整数比大小
// 形式参数:定义函数时,用来接收外部传入数据的参数,就是形式参数
// 实际参数:实际调用函数时,传给形参的实际数据叫做实际参数
func max(num1, num2 int) int {
var result int
if num1 > num2 {
result = num1
} else {
result = num2
}
//一个函数定义上有返回值,那么函数中必须使用 return 语句
//返回值,调用处需要使用变量接收该结果
return result
}
概念:一个函数的参数类型确定,但个数不确认,就可以使用可变参数
package main
import "fmt"
func main() {
getSum(1, 2, 3, 4, 10)
}
// ...可变参数,可以传入多个参数
func getSum(nums ...int) {
sum := 0
for i := 0; i < len(nums); i++ {
sum += nums[i]
}
fmt.Println("sum:", sum)
}
注意事项
按照数据的存储特点来分:
值传递
package main
import "fmt"
func main() {
/**
值传递
1.arr2 的数据是从 arr1 复制来的,所以是不同的空间
2.修改 arr2 并不会影响 arr1
3.值传递:传递的是数据的副本,修改数据,对于原始的数据没有影响
4.值类型的数据,默认都是值传递,基础类型、array、struct
*/
//定义一个数组
arr := [4]int{1, 2, 3, 4}
fmt.Println(arr) //输出[1,2,3,4]
//传递,拷贝arr
update(arr)
fmt.Println("调用修改后的数据:", arr) //输出[1,2,3,4]
}
func update(arr2 [4]int) {
fmt.Println("arr2接收的数据:", arr2) //输出[1,2,3,4]
arr2[0] = 100
fmt.Println("arr2修改后的数据:", arr2) //输出[100,2,3,4]
}
引用传递
变量在内存中是存放在一定的地址上的,修改变量实际是修改变量地址的内存。
package main
import "fmt"
func main() {
//切片,可扩容的数组
s1 := []int{1, 2, 3, 4}
fmt.Println("默认的数据:", s1) //输出[1,2,3,4]
//传入的是引用类型的数据,地址;
update2(s1)
fmt.Println("调用方法后的数据:", s1) //输出[100,2,3,4]
}
func update2(s2 []int) {
fmt.Println("传递的数据:", s2) //输出[1,2,3,4]
s2[0] = 100
fmt.Println("修改后的数据:", s2) //输出[100,2,3,4]
}
类型指针不能进行偏移和运算。
Go语言的指针操作非常简单,&(取地址) 和 *(根据地址取值)。
指针地址和指针类型
指针语法
var var_name *var-type
var-type为指针类型
var_name为指针变量名
*用于指定变量作为一个指针
局部变量:函数内定义的变量,叫做局部变量
全局变量:函数外部定义的变量,叫做全局变量
变量遵循就近原则
package main
import "fmt"
// 全局变量
var num int = 100
func main() {
temp := 100
//在 if、for 语句定义的变量,只能在该代码块里面使用
if b := 1; b <= 10 {
temp := 50
fmt.Println(temp) //变量遵循就近原则
fmt.Println(b)
}
fmt.Println(temp)
f1()
f2()
}
func f1() {
a := 1
fmt.Println(a)
fmt.Println(num)
}
func f2() {
//不能使用其他函数定义的变量
//fmt.Println(a)
fmt.Println(num)
}
定义:一个函数自己调用自己,就叫做递归函数。递归十分耗内存
注意:递归函数需要有一个出口,逐渐向出口靠近,没有出口就会形成死循环。
package main
import "fmt"
func main() {
sum := getSum1(5)
fmt.Println(sum)
}
func getSum1(n int) int {
if n == 1 {
return 1
}
return getSum1(n-1) + n
}
defer语义:推迟、延迟
在go语言中,使用defer关键字来延迟一个函数或者方法执行。
defer函数或者方法:一个函数或方法的执行被延迟了
package main
import "fmt"
func main() {
f("1")
fmt.Println("2")
defer f("3") //会被延迟到最后执行
fmt.Println("4")
defer f("5")
fmt.Println("6")
defer f("7")
fmt.Println("8")
}
func f(s string) {
fmt.Println(s)
}
defer函数传递
package main
import "fmt"
func main() {
a := 10
fmt.Println("a=", a)
defer f3(a) //参数就已经传递进去了,在最后执行
a++
fmt.Println("end a=", a)
}
func f3(b int) {
fmt.Println("函数里面的a:", b)
}
Go语言有个特殊的函数init函数,先于main函数执行,实现包级别的一些初始化操作。
init函数特点
Go初始化顺序
初始化顺序:变量初始化–>init–>main()
package main
import "fmt"
var num int = initVar()
func init() {
fmt.Println("init函数") //2.第二输出
}
func init() {
fmt.Println("init函数2") //3.第三输出
}
func initVar() int {
fmt.Println("initVar") //1.最先输出
return 100
}
func main() {
fmt.Println("main函数") //4.最后输出
}
函数的类型:func(参数类型)(返回值类型)
函数也是一种数据类型
函数类型的变量
package main
import "fmt"
// func() 本身就是一个数据类型
func main() {
//函数不加括号,函数就是一个变量
//f1() 如果加了括号那就成了函数的调用
fmt.Printf("%T \n", f1) //输出func() | func(int, int) | func(int, int) int
//定义函数类型的变量
var f5 func(int, int)
f5 = f1
f5(1, 2)
}
// func f1() {
//
// }
func f1(a, b int) {
fmt.Println(a, b)
}
//func f1(a,b int) int {
// return 0
//}
函数在Go语言中是复合类型,可以看做是一种特殊的变量。
函数名():调用返回结果。
函数名:指向函数体的内存地址,一种特殊类型的指针变量。
package main
import "fmt"
func main() {
func1()
func2 := func1 //函数本身也是变量
func2()
//匿名函数
func3 := func() {
fmt.Println("我是匿名func3函数")
}
func3()
func(a, b int) {
fmt.Println(a, b)
fmt.Println("我是匿名func4函数")
}(1, 2)
num2 := func(a, b int) int {
return a + b
}(1, 2)
fmt.Println(num2)
}
func func1() {
fmt.Println("我是func1函数")
}
Go语言是支持函数式编程:
高阶函数:根据Go语言的数据类型的特点,可以将一个函数作为另外一个函数的参数。
package main
import "fmt"
func main() {
sum := add(1, 2)
fmt.Println(sum)
sum2 := operate(3, 4, add) // add() 就作为一个回调函数
fmt.Println(sum2)
sum3 := operate(5, 6, sub)
fmt.Println(sum3)
sum4 := operate(8, 4, func(a, b int) int {
if b == 0 {
fmt.Println("被除数不能为0")
return 0
}
return a / b
})
fmt.Println(sum4)
}
// 高阶函数,可以接受一个函数作为参数
func operate(a, b int, fun func(int, int) int) int {
sum := fun(a, b)
return sum
}
func add(a, b int) int {
return a + b
}
func sub(a, b int) int {
return a - b
}
package main
import "fmt"
func main() {
/*
一个外层函数中,有内层函数,该内层函数中,会操作外层函数的局部变量
并且该外层函数的返回值就是这个内层函数。
这个内层函数和外层函数的局部变量,统称为闭包结构
局部变量的生命周期就会发生改变,正常的局部变量会随着函数的调用二创建,
随着函数的结束而销毁,但是闭包结构中的外层函数的局部变量并不会随着外层函数
的结束而销毁,因为内层函数还在继续使用
*/
r1 := increment()
fmt.Println(r1)
v1 := r1()
fmt.Println(v1) //输出1
v2 := r1()
fmt.Println(v2) //输出2
fmt.Println(r1()) //输出3
fmt.Println(r1()) //输出4
fmt.Println(r1()) //输出5
//再次调用
r2 := increment()
v3 := r2()
fmt.Println(v3) //输出1
fmt.Println(r2()) //输出2
}
func increment() func() int {
i := 0
//定义一个匿名函数,给变量自增并返回
fun := func() int {
i++
return i
}
return fun
}
我们不限定类型,让调用者自己去定义类型。
泛型减少重复代码并提高类型安全性。
泛型 T占位符;形式类型,内置的泛型类型 any
和 comparable
。
any
表示 Go
里面所有的内置基本类型,等价于interface{}
。
comparable
表示Go
里面所有内置的可比较类型: int、uint、float、bool、struct、指针
等一切可比较的类型。
实例
package main
import "fmt"
// 内部业务代码一样
func printStringArray(arr []string) {
for _, v := range arr {
fmt.Println(v)
}
}
// 内部业务代码一样
func printIntArray(arr []int) {
for _, v := range arr {
fmt.Println(v)
}
}
/*func printArray[T string | int | float64](arr []T) {
for _, v := range arr {
fmt.Println(v)
}
}*/
func printArray[T any](arr []T) {
for _, v := range arr {
fmt.Println(v)
}
}
func main() {
strs := []string{"jay", "peng"}
//printStringArray(strs)
is := []int{1, 2, 3}
fs := []float64{1.1, 2.2, 3.3}
//printIntArray(is)
printArray(strs)
printArray(is)
printArray(fs)
}
针对不同的数据类型我们要定义不同的切片或数组,我们使用泛型就能代表这些类型
type Slice[T int | float64 | float32] []T
type MyMap[KEY int|string,VALUE float32|float64] map[KEY]VALUE
例子:
package main
import "fmt"
func main() {
type Slice[T int | float64 | float32] []T
var a Slice[int] = []int{1, 2, 3, 4}
fmt.Println(a)
fmt.Printf("a:%T", a)
var b Slice[float64] = []float64{1.1, 2.2, 3.3, 4.4}
fmt.Println(b)
fmt.Printf("b:%T", b)
var c Slice[float32] = []float32{1, 2, 3, 4}
fmt.Println(c)
fmt.Printf("c:%T", c)
fmt.Println("\n=========")
type MyMap[KEY int | string, VALUE float32 | float64] map[KEY]VALUE
var m1 MyMap[string, float64] = map[string]float64{"java": 9.0, "go": 9.6}
fmt.Println(m1)
}
所有类型定义都可使用类型形参,所以结构体以及接口的定义也可以使用类型形参:
//一个泛型类型的结构体,可以用 int 或 string 类型实例化
type MyStruct [T int | string] struct {
Id T
Name string
}
//一个泛型接口
type MyInterface [T float32 | string] interface {
Print(data T)
}
//一个泛型通道,可用类型实参 int 或 string 实例化
type MyChan[T int | string] chan T
特殊的泛型类型(理解机制)
type Wow[T int | string] int
var a Wow[int] = 123
var b Wow[string] = 123
var c Wow[string] = "hello" //编译错误,因为 "hello" 不能赋值给底层类型 int
例子
package main
import "fmt"
type MySlice[T int | float64] []T
// 泛型方法
func (s MySlice[T]) Sum() T {
var sum T
for _, v := range s {
sum += v
}
return sum
}
// 泛型函数
func Add[T int | float64 | string](a T, b T) T {
return a + b
}
func main() {
fmt.Println(Add[int](1, 2))
fmt.Println(Add(1, 2)) //自动推导类型
fmt.Println(Add[string]("hello", "world"))
fmt.Println(Add("hello", "world"))
fmt.Println(Add[float64](1.1, 2.2))
fmt.Println(Add(1.1, 2.2))
var i MySlice[int] = []int{1, 2, 3, 4}
fmt.Println(i.Sum())
var f MySlice[float64] = []float64{1.1, 2.2, 3.3, 4.4}
fmt.Println(f.Sum())
}
如果类型太多了怎么办?这时候我们就可以自定义泛型类型
//像声明接口一样声明,叫做泛型类型的约束,自定义约束
type MyInt interface {
int | int8 | int32 |int64
}
//T的类型为声明的MyInt
func GetMaxNum[T MyInt](a, b T) T{
if a > b {
return a
}
return b
}
Go预言中的函数传参都是值拷贝,当我们想要修改某个变量的时候,我们可以创建一个指向该变量地址的指针变量。传递数据使用指针,而无须拷贝数据。
类型指针不能进行偏移和运算。
一个指针变量指向了一个值的内存地址。
Go语言中的指针操作非常简单,只需记住两个符号:&(取地址)和*(根据地址取值)。
指针语法
var var_name *var-type
var-type:为指针类型
var_name:为指针变量名
*:用于指定变量是作为一个指针
package main
import "fmt"
func main() {
var ip *int
fmt.Printf("ip:%v\n", ip) //ip:
fmt.Printf("ip:%T\n", ip) //ip:*int
var i int = 100
ip = &i
fmt.Printf("ip:%v\n", ip) //ip:0xc00001c0f0
fmt.Printf("ip:%v\n", *ip) //ip:100
var sp *string
var s string = "hello"
sp = &s
fmt.Printf("sp:%T\n", sp) //sp:*string
fmt.Printf("sp:%v\n", *sp) //sp:hello
var bp *bool
var b bool = true
bp = &b
fmt.Printf("bp:%T\n", bp) //bp:*bool
fmt.Printf("bp:%v\n", *bp) //bp:true
}
指向数组指针
定义语法
var ptr [MAX]*int //表示数组里面的元素类型是指针类型
实例演示
package main
import "fmt"
func main() {
a := [3]int{1, 2, 3}
var ap [3]*int
fmt.Printf("ap:%v\n", ap) //ap:[ ]
for i := 0; i < len(a); i++ {
ap[i] = &a[i] //赋值
}
fmt.Printf("ap:%v\n", ap) //ap:[0xc000010120 0xc000010128 0xc000010130]
for i := 0; i < len(ap); i++ {
fmt.Printf("%v \t", *ap[i]) //1 2 3
}
}
类型定义
type newType Type
类型别名
type newType = Type
区别:
实例
package main
import "fmt"
func main() {
type MyInt int
var i MyInt
i = 100
fmt.Printf("i:%T,%v\n", i, i) //i:main.MyInt,100
type OurInt = int
var a OurInt = 100
fmt.Printf("a:%T,%v\n", a, a) //a:int,100
}
Go语言没有面向对象的概念了,但是可以使用结构体来实现,面向对象编程的一些特性,例如:继承、多态、组合等特性。
结构体的定义
type struct_variable_type struct {
member definition;
member definition;
...
member definition;
}
type: 结构体定义关键字
struct_variable_type: 结构体类型名称
struct: 结构体定义关键字
==member definition;:==成员定义
实例
package main
import "fmt"
func main() {
var tom Person
tom.id = 101
tom.name = "tom"
tom.age = 20
tom.email = "[email protected]"
fmt.Printf("tom:%v\n", tom) //tom:{101 tom 20 [email protected]}
var jack Customer
fmt.Printf("jack:%v\n", jack) //jack:{0 0 }
}
type Person struct { //自定义了一个类型
id int
name string
age int
email string
}
type Customer struct {
id, age int
name, email string
}
匿名结构体
package main
import "fmt"
func main() {
var tom struct { //匿名结构体
id int
name string
age int
}
tom.id = 102
tom.age = 20
tom.name = "name"
fmt.Printf("tom:%v\n", tom)
}
结构体初始化
package main
import "fmt"
func main() {
type Student struct {
id int
name string
age int
email string
}
var tom Student
tom = Student{ //必须和定义的顺序一致
102,
"tom",
23,
"[email protected]",
}
//tom := Student{ //对个别键初始化
//id: 103,
//name: "tom",
//}
//tom = Student{ //键值对初始化
// id: 101,
// name: "tom",
// age: 22,
// email: "[email protected]",
//}
fmt.Printf("tom:%v\n", tom)
}
Animal
shout string
}
Go语言可以使用函数来模拟构造函数的功能。
package main
import (
"fmt"
)
func main() {
per, err := NewPerson("name", 20)
if err == nil {
fmt.Printf("per:%v\n", *per)
} else {
fmt.Printf("err:%v\n", err)
}
}
type Person4 struct {
name string
age int
}
func NewPerson(name string, age int) (*Person4, error) {
if name == "" {
return nil, fmt.Errorf("name不能为空")
}
if age < 0 {
return nil, fmt.Errorf("age不能小于0")
}
return &Person4{name: name, age: age}, nil
}
Go mod使用方法
初始化模块
go mod init <项目块名称>
依赖关系处理,根据go.mod文件
go mod tidy
将依赖包复制到项目下的vendor目录
go mod vendor
如果包被屏蔽(墙),可以使用这个命令,随后使用go build-mod=vendor编译
显示依赖关系
go list -m all
显示详细依赖关系
go list -m -json all
下载依赖
go mod download [path@version]
[path@version]是必须写的
Go语言中的并发是函数相互独立运行的能力。Goroutines是并发运行的函数。Go语言提供了Goroutines作为并发处理操作的一种方式。
实例
package main
import (
"fmt"
"time"
)
func showMessage(msg string) {
for i := 0; i < 5; i++ {
fmt.Println(msg)
time.Sleep(time.Millisecond * 100)
}
}
func main() {
go showMessage("Java")
go showMessage("Golang")
time.Sleep(time.Millisecond * 2000)
fmt.Println("main") //主函数退出,程序就结束了
}
Go提供了一种通道机制,用于在goroutine之间共享数据。当您作为goroutine执行并发活动时,需要在goroutine之间共享资源或数据,通道充当goroutine之间的管道并提供一种机制来保证同步交换。
通道由make函数创建,该函数指定chan关键字和通道元素类型。
Unbuffered := make(chan int) //整型无缓冲通道
buffered := make(chan int, 10) //整型有缓冲通道
将值发送到通道的代码块需要使用 <- 运算符。
goroutine1 := make(chan string, 5) //字符串缓冲通道
goroutine1 <- "hello" //通过通道发送字符串
data := <- goroutine1 //从通道接收字符串
通道的发送和接收特性
package main
import "fmt"
import "sync"
var wp sync.WaitGroup
func showMsg(i int) {
defer wp.Done() // goroutine 结束登记-1
fmt.Printf("i:%v\n", i)
}
func main() {
for i := 0; i < 10; i++ {
go showMsg(i) 启动一个 goroutine 就+1
wp.Add(1)
}
//主协程
fmt.Println("end...")
}
runtime.Gosched() 让出CPU时间片,重新等待安排任务
package main
import (
"fmt"
"runtime"
)
func showInfo(msg string) {
for i := 0; i < 2; i++ {
fmt.Printf("msg:%v\n", msg)
}
}
func main() {
go showInfo("java") //子协程来运行
for i := 0; i < 2; i++ {
runtime.Gosched() //我有权利执行任务了,让给其他子协层来执行
fmt.Println("golang")
}
fmt.Println("end...")
}
runtime.Goexit() 退出当前协程
package main
import (
"fmt"
"runtime"
"time"
)
func show() {
for i := 0; i < 10; i++ {
fmt.Printf("i:%v\n", i)
if i >= 5 {
runtime.Goexit()
}
}
}
func main() {
go show()
time.Sleep(time.Second)
}
runtime.GOMAXPROCS()
package main
import (
"fmt"
"runtime"
"time"
)
func a() {
for i := 0; i < 10; i++ {
fmt.Println("A:", i)
}
}
func b() {
for i := 0; i < 10; i++ {
fmt.Println("B:", i)
}
}
func main() {
fmt.Printf("runtime.NumCPU():%v\n", runtime.NumCPU())
runtime.GOMAXPROCS(2) //设置cpu个数查看交替过程
go a()
go b()
time.Sleep(time.Second)
}
package main
import (
"fmt"
"sync"
"time"
)
var i int = 100
var wg sync.WaitGroup
var lock sync.Mutex
func add() {
defer wg.Done()
lock.Lock()
i += 1
fmt.Println("addI:", i)
time.Sleep(time.Millisecond * 10)
lock.Unlock()
}
func sub() {
lock.Lock()
defer wg.Done()
i -= 1
fmt.Println("subI:", i)
time.Sleep(time.Millisecond * 2)
lock.Unlock()
}
func main() {
for i := 0; i < 100; i++ {
wg.Add(1)
add()
wg.Add(1)
sub()
}
wg.Wait()
fmt.Println("endI:", i)
}
for循环+if判断
for range
实例
package main
import "fmt"
var c = make(chan int)
func main() {
go func() {
for i := 0; i < 3; i++ {
c <- i
}
}()
for { //繁杂
v, ok := <-c
if ok {
fmt.Println("v:", v)
} else {
break
}
}
//for v := range c { //简洁
// fmt.Println("v:", v)
//}
//for i := 0; i < 2; i++ {
// r := <-c
// fmt.Println("r:", r)
//}
}
select是Go中的一个控制结构,类似于switch语句,用于处理异步IO操作。select会监听case语句中channel的读写操作,当case中channel读写操作为非阻塞状态(即能读写)时,将会触发相应的动作。
如果有多个case都可以运行,select会随机公平地选出一个执行,其它不会执行。
如果没有可运行的case语句,且有default语句,那么会执行default的动作。
如果没有可运行的case语句,且没有default语句,select将阻塞,直到某个case通信可运行。
package main
import (
"fmt"
"time"
)
var chanInt = make(chan int, 0)
var chanStr = make(chan string)
func main() {
go func() {
chanInt <- 100
chanStr <- "hello"
defer close(chanInt)
defer close(chanStr)
}()
for {
select {
case r := <-chanInt:
fmt.Println("chanInt:", r)
case r := <-chanStr:
fmt.Println("chanStr:", r)
default:
fmt.Println("default...")
}
time.Sleep(time.Second)
}
}
timer就是定时器的意思,可以实现一些定时操作,内部也是通过channel来实现的。
package main
import (
"fmt"
"time"
)
func main() {
//timer := time.NewTimer(time.Second * 2)
//fmt.Println(time.Now())
//t1 := <-timer.C //阻塞,直到指定时间到达
//fmt.Println(t1)
//
//fmt.Println(time.Now())
//timer1 := time.NewTimer(time.Second * 2)
//<-timer1.C
//fmt.Println(time.Now())
//
//<- time.After(time.Second*2)
//fmt.Println("2s后")
timer2 := time.NewTimer(time.Second * 2)
go func() {
<-timer2.C
fmt.Println("timer2的定时")
}()
stop := timer2.Stop()
if stop {
fmt.Println("timer2 is stopped")
}
time.Sleep(time.Second * 3)
fmt.Println("main end...")
}
ticker可以周期的执行。
package main
import (
"fmt"
"time"
)
func main() {
ticker := time.NewTicker(time.Second)
//counter := 1
//for _ = range ticker.C {
// fmt.Println("ticker...")
// counter++
// if counter > 5 {
// ticker.Stop()
// break
// }
//}
chanInt := make(chan int)
go func() {
for _ = range ticker.C {
select {
case chanInt <- 1:
case chanInt <- 2:
case chanInt <- 3:
}
}
}()
sum := 0
for v := range chanInt {
fmt.Println("v:", v)
sum += v
if sum >= 10 {
fmt.Println("sum:", sum)
break
}
}
}
增减操作
package main
import (
"fmt"
"sync/atomic"
"time"
)
var num int64 = 100
func add2() {
atomic.AddInt64(&num, 1)
}
func sub2() {
atomic.AddInt64(&num, -1)
}
func main() {
for i := 0; i < 100; i++ {
go add2()
go sub2()
}
time.Sleep(time.Second * 2)
fmt.Println("num:", num)
}
载入操作(read)
package main
import (
"fmt"
"sync/atomic"
)
func main() {
var i int32 = 100
atomic.LoadInt32(&i) //read
fmt.Println("i:", i) //i: 100
}
比较并交换
package main
import (
"fmt"
"sync/atomic"
)
func main() {
var i int32 = 100
b := atomic.CompareAndSwapInt32(&i, 100, 200)
fmt.Println("b:", b) //b:true
fmt.Println("i:", i) //i:200
}
存储
package main
import (
"fmt"
"sync/atomic"
)
func main() {
var i int32 = 100
atomic.StoreInt32(&i, 200) //write
fmt.Println("i:", i) //i: 200
}
标准库包名 | 功能简介 |
---|---|
bufio | 带缓冲的 I/O 操作 |
bytes | 实现字节操作 |
container | 封装堆、列表和环形列表等容器 |
crypto | 加密算法 |
database | 数据库驱动和接口 |
debug | 各种调试文件格式访问及调试功能 |
encoding | 常见算法如 JSON、XML、Base64 等 |
flag | 命令行解析 |
fmt | 格式化操作 |
go | Go 语言的词法、语法树、类型等。可通过这个包进行代码信息提取和修改 |
html | HTML 转义及模板系统 |
image | 常见图形格式的访问及生成 |
io | 实现 I/O 原始访问接口及访问封装 |
math | 数学库 |
net | 网络库,支持 Socket、HTTP、邮件、RPC、SMTP 等 |
os | 操作系统平台不依赖平台操作封装 |
path | 兼容各操作系统的路径操作实用函数 |
plugin | Go 1.7 加入的插件系统。支持将代码编译为插件,按需加载 |
reflect | 语言反射支持。可以动态获得代码中的类型信息,获取和修改变量的值 |
regexp | 正则表达式封装 |
runtime | 运行时接口 |
sort | 排序接口 |
strings | 字符串转换、解析及实用函数 |
time | 时间接口 |
text | 文本模板及 Token 词法器 |
创建文件
package main
import (
"fmt"
"os"
)
// 创建文件
func createFile() {
f, err := os.Create("a.txt")
if err != nil {
fmt.Println("err:", err)
} else {
fmt.Println("f.Name()", f.Name())
}
}
func main() {
createFile()
}
创建文件
package main
import (
"fmt"
"os"
)
// 创建目录
func createDir() {
//创建目录
/*err := os.Mkdir("test", os.ModePerm)
if err != nil {
fmt.Println("err", err)
}*/
//创建根目录和子目录
err1 := os.MkdirAll("a/b/c", os.ModePerm)
if err1 != nil {
fmt.Println("err1:", err1)
}
}
func main() {
createDir()
}
删除目录/文件
package main
import (
"fmt"
"os"
)
// 删除目录或者文件
func remove() {
/*err := os.Remove("a.txt")
if err != nil {
fmt.Println("err:", err)
}*/
err := os.RemoveAll("test")
if err != nil {
fmt.Println("err:", err)
}
}
func main() {
remove()
}
读写文件
package main
import (
"fmt"
"os"
)
// 读文件
func readFile() {
file, _ := os.ReadFile("test2.txt")
fmt.Println("file:", string(file[:])) //切片
}
// 写文件
func writeFile() {
os.WriteFile("test2.txt", []byte("hello,world"), os.ModePerm)
}
func main() {
readFile()
writeFile()
}
文件的打开和关闭
package main
import (
"fmt"
"os"
)
// 打开关闭文件
func openClose() {
/*open, err := os.Open("test2.txt")
if err != nil {
fmt.Println("err:", err)
} else {
fmt.Println("open.Name()",open.Name())
}
open.Close()*/
file, err := os.OpenFile("test1.txt", os.O_RDWR|os.O_CREATE, 755)
if err != nil {
fmt.Println("err:", err)
} else {
fmt.Println("file.name():", file.Name())
file.Close()
}
}
func main() {
openClose()
}
读文件
package main
import (
"fmt"
"io"
"os"
)
func readOps() {
open, _ := os.Open("test1.txt")
for {
buf := make([]byte, 3) //可调节读取长度
n, err := open.Read(buf)
//n, _ := open.ReadAt(buf, 3)
if err == io.EOF {
break
}
fmt.Println("n:", n)
fmt.Println("string(buf):", string(buf))
}
open.Close()
}
func main() {
readOps()
}
写文件
package main
import "os"
func write() {
file, _ := os.OpenFile("test1.txt", os.O_RDWR|os.O_APPEND, 0755)
file.Write([]byte("hello,world"))
file.Close()
}
func writeString() {
file, _ := os.OpenFile("test1.txt", os.O_RDWR|os.O_APPEND, 0755)
file.WriteString("hello Java")
file.Close()
}
func writeAt() {
file, _ := os.OpenFile("test1.txt", os.O_RDWR, 0755)
file.WriteAt([]byte("aaa"), 3)
file.Close()
}
func main() {
//write()
//writeString()
writeAt()
}
进程操作
package main
import (
"fmt"
"os"
"time"
)
func main() {
//获得当前正在运行的进程id
fmt.Println("os.Getpid():", os.Getpid())
//父id
fmt.Println("os.Getppid():", os.Getppid())
//设置新进程的属性
attr := &os.ProcAttr{
//files指定新进程继承的活动文件对象
//前三个分别为,标准输入,标准输出,标准错误输出
Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
//新进程的环境变量
Env: os.Environ(),
}
//开始一个新进程
p, err := os.StartProcess("C:\\Windows\\System32\\notepad.exe", []string{
"C:\\Windows\\System32\\notepad.exe", "D:\\a.txt"}, attr)
if err != nil {
fmt.Println("err:", err)
}
fmt.Println(p)
fmt.Println("进程ID:", p.Pid)
//通过进程ID查找进程
p2, _ := os.FindProcess(p.Pid)
fmt.Println(p2)
//等待10秒,执行函数
time.AfterFunc(time.Second*10, func() {
//向p进程发送退出信号
p.Signal(os.Kill)
})
//等待进程p的退出,返回进程状态
ps, _ := p.Wait()
fmt.Println(ps.String())
}
环境相关操作
package main
import (
"fmt"
"os"
)
func main() {
//打印所有的环境变量
fmt.Println(os.Environ())
//打印Go的环境变量
goPath := os.Getenv("GOPATH")
fmt.Println("goPath:", goPath)
//打印Java的环境变量
javaHome := os.Getenv("JAVA_HOME")
fmt.Println("javaHome:", javaHome)
//查找环境变量
env, b := os.LookupEnv("GOPATH")
if b {
fmt.Println("env:", env)
}
//替换环境变量
os.Setenv("NAME","gopher")
os.Setenv("BURROW","/user/gopher")
fmt.Println(os.ExpandEnv("$NAME lives in ${BURROW}."))
//清除所有环境变量
//os.Clearenv()
}
基本的IO接口
Reader接口
type Reader interface {
Read(p []byte) (n int, err error)
}
Writer接口
type Writer interface {
Write(p []byte) (n int, err error)
}
Go语言内置了log包,实现简单的日志服务,通过调用log包的函数,可以实现简单的日志打印功能。
函数系列 | 作用 |
---|---|
单纯的打印日志 | |
panic | 打印日志,抛出panic异常 |
fatal | 打印日志,强制结束程序(os.Exit(1)),defer函数不会执行 |
标准log配置
默认情况下log只会打印出时间,但是实际情况下我们可能还需要获取文件名,行号等信息,log包提供给我们定制的接口。
log包提供两个标准log配置相关方法
func Flags() int //返回标准log输出配置
func SetFlags(flag int) //设置标准log输出配置
实例
package main
import (
"fmt"
"log"
"os"
)
func init() {
log.SetFlags(log.Ldate | log.Ltime | log.Llongfile) //设置格式
log.SetPrefix("MyLog: ") //设置前缀
file, err := os.OpenFile("test1.log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0664)
if err != nil {
log.Fatal("日志文件错误")
}
log.SetOutput(file)
}
func main() {
log.Println("my log")
}
errors包实现了操作错误的函数。语言使用error类型来返回函数执行过程中遇到的错误,如果返回的error值为nil,则表示未遇到错误,否则error会返回一个字符串,用于说明遇到了什么错误。
error结构
type error interface {
Error() string
}
error可以是任何一个类型,可以包含任意信息,不一定是字符串。
error不一定表示一个错误,可以表示任何信息,例如io包中就用error类型的io.EOF表示数据读取结束。
errors包用法很简单,用New函数,生成一个最简单的error对象。
package main
import (
"errors"
"fmt"
)
func check(s string) (string, error) {
if s == "" {
err := errors.New("字符串不能为空")
return "", err
} else {
return s, nil
}
}
func main() {
s, err := check("hello")
if err != nil {
fmt.Println(err.Error())
} else {
fmt.Println(s)
}
}
time包提供测量和显示时间的功能。
在编程中时间戳的应用很广泛,例如在web开发中做cookies有效期,接口加密,Redis中的key有效期等。
Go语言中获取时间戳操作如下:
func main() {
now := time.Now()
fmt.Printf("TimeStamp type:%T,TimeStamp:%v", now.Unix(), now.Unix())
}
时间格式化
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
fmt.Println(now.Format("2006/01/02 15:03"))
}
这个包可以实现json的编码和解码,就是将json字符串转换为struct,或者将struct转换为json。
两个核心函数
将struct编码成json,可以接受任意类型。
func Marshal(v interface{}) ([]byte, error)
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string
Age int
Email string
}
func marshal() {
p := Person{
Name: "tom",
Age: 20,
Email: "[email protected]",
}
b, _ := json.Marshal(p)
fmt.Printf("b:%v\n", string(b))
}
func main() {
marshal()
}
将json编码成struct结构体。
func Unmarshal(data []byte, v interface{}) error
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string
Age int
Email string
}
func main() {
b := []byte(`{"Name":"tom","Age":20,"Email":"[email protected]"}`)
var p Person
json.Unmarshal(b, &p)
fmt.Printf("p:%v\n", p)
}
两个核心结构体
从输入流读取并解析json
type Decoder struct {
//contains filtered or unexported fields
}
写json到输出流
type Encoder struct {
//contains filtered or unexported fields
}
两个核心函数
将struct编码成xml,可以接收任意类型
func Marshal(v interface{}) ([]byte, error)
将xml转码成struct结构体
func Unmarshal(data []byte, v interface{}) error
两个核心结构体
从输入流读取并解析xml
type Decoder struct {
//contains filtered or unexported fields
}
写xml到输出流
type Encoder struct {
//contains filtered or unexported fields
}
下载安装MySQL数据库
//启动MySQL服务 管理员运行cmd
net start mysql
//数据库登录
mysql -u root -p
//回车输入密码
创建一个go_db数据库
create databse go_db;
打开数据库
use go_db;
创建表
create table user_tbl(
id integer primary key auto_increment,
username varchar(20),
password varchar(20)
);
添加模拟数据
INSERT INTO user_tbl(username,password) VALUES("tom","123");
INSERT INTO user_tbl(username,password) VALUES("kite","456");
安装MySQL驱动
go get -u github.com/go-sql-driver/mysql //终端路径运行
数据库连接
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
// 初始化数据库
var db *sql.DB
func initDB() (err error) {
dsn := "root:jayish@tcp(127.0.0.1:3306)/go_db?charset=utf8mb4&parseTime=True"
db, err = sql.Open("mysql", dsn)
if err != nil {
return err
}
return nil
}
func main() {
/*db, err := sql.Open("mysql", "root:jayish@/go_db")
if err != nil {
panic(err)
}
// See "Important settings" section.
db.SetConnMaxLifetime(time.Minute * 3)
db.SetMaxOpenConns(10)
db.SetMaxIdleConns(10)
fmt.Printf("db:%v\n", db)*/
err := initDB()
if err != nil {
fmt.Println("err:", err)
} else {
fmt.Println("连接成功!")
}
}
插入、更新和删除操作都使用Exec方法
func (db *DB) Exec(query string,args ...interface{}) (Result, error)
插入数据
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
// 初始化数据库
var db *sql.DB
func initDB() (err error) {
dsn := "root:jayish@tcp(127.0.0.1:3306)/go_db?charset=utf8mb4&parseTime=True"
db, err = sql.Open("mysql", dsn)
if err != nil {
return err
}
return nil
}
func insert1() {
s := "insert into user_tbl (username,password) values (?,?)"
r, err := db.Exec(s, "zhangsan", "zs123")
if err != nil {
fmt.Println("err:", err)
} else {
i, _ := r.LastInsertId()
fmt.Println("i:", i)
}
}
func insert2(username string, password string) {
s := "insert into user_tbl (username,password) values (?,?)"
r, err := db.Exec(s, username, password)
if err != nil {
fmt.Println("err:", err)
} else {
i, _ := r.LastInsertId()
fmt.Println("i:", i)
}
}
func main() {
err := initDB()
if err != nil {
fmt.Println("err:", err)
} else {
fmt.Println("连接成功!")
}
//insert1()
insert2("李四", "ls123")
}
查询数据
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
// 初始化数据库
var db *sql.DB
func initDB() (err error) {
dsn := "root:jayish@tcp(127.0.0.1:3306)/go_db?charset=utf8mb4&parseTime=True"
db, err = sql.Open("mysql", dsn)
if err != nil {
return err
}
return nil
}
type user struct {
id int
username string
password string
}
func queryOneRow() {
s := "select * from user_tbl where id = ?"
var u user
err := db.QueryRow(s, 1).Scan(&u.id, &u.username, &u.password)
if err != nil {
fmt.Println("err:", err)
} else {
fmt.Println(u)
}
}
func queryManyRow() {
s := "select * from user_tbl"
r, err := db.Query(s)
var u user
defer r.Close()
if err != nil {
fmt.Println("err:", err)
} else {
for r.Next() {
r.Scan(&u.id, &u.username, &u.password)
fmt.Println(u)
}
}
}
func main() {
err := initDB()
if err != nil {
fmt.Println("err:", err)
} else {
fmt.Println("连接成功!")
}
//queryOneRow()
queryManyRow()
}
更新数据
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
// 初始化数据库
var db *sql.DB
func initDB() (err error) {
dsn := "root:jayish@tcp(127.0.0.1:3306)/go_db?charset=utf8mb4&parseTime=True"
db, err = sql.Open("mysql", dsn)
if err != nil {
return err
}
return nil
}
func update() {
s := "update user_tbl set username=?,password=? where id=?"
r, err := db.Exec(s, "jack", "123456", 2)
if err != nil {
fmt.Println(err)
} else {
a, _ := r.RowsAffected()
fmt.Println(a)
}
}
func main() {
err := initDB()
if err != nil {
fmt.Println("err:", err)
} else {
fmt.Println("连接成功!")
}
update()
}
删除数据
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
// 初始化数据库
var db *sql.DB
func initDB() (err error) {
dsn := "root:jayish@tcp(127.0.0.1:3306)/go_db?charset=utf8mb4&parseTime=True"
db, err = sql.Open("mysql", dsn)
if err != nil {
return err
}
return nil
}
func delete() {
s := "delete from user_tbl where id=?"
r, err := db.Exec(s, 1)
if err != nil {
fmt.Println(err)
} else {
i, _ := r.RowsAffected()
fmt.Println(i)
}
}
func main() {
err := initDB()
if err != nil {
fmt.Println("err:", err)
} else {
fmt.Println("连接成功!")
}
delete()
}
下载安装驱动
https://www.mongodb.com/download-center/community
安装可参考此文章https://www.cnblogs.com/bornforthis/p/17024616.html
打开客户端
mongo //在bin目录下打开cmd,MongoDB6.0以上版本需要到官网下载mongosh
创建数据库
use go_db;
创建集合
db.createCollection("student");
下载驱动
go get go.mongodb.org/mongo-driver/mongo
连接MongoDB
package main
import (
"context"
"fmt"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"log"
)
var client *mongo.Client
func initDb() {
//设置客户端连接配置
co := options.Client().ApplyURI("mongodb://localhost:27017")
//连接到MongoDB
c, err := mongo.Connect(context.TODO(), co)
if err != nil {
log.Fatal(err)
}
//检查连接
err2 := c.Ping(context.TODO(), nil)
if err2 != nil {
log.Fatal(err2)
}
fmt.Println("connect success!")
client = c
}
func main() {
initDb()
}
添加一条数据
package main
import (
"context"
"fmt"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"log"
)
type Student struct {
Name string
Age int
}
var client1 *mongo.Client
func initDb1() {
//设置客户端连接配置
co := options.Client().ApplyURI("mongodb://localhost:27017")
//连接到MongoDB
c, err := mongo.Connect(context.TODO(), co)
if err != nil {
log.Fatal(err)
}
//检查连接
err2 := c.Ping(context.TODO(), nil)
if err2 != nil {
log.Fatal(err2)
}
fmt.Println("connect success!")
client1 = c
}
func insert() {
s1 := Student{
"tom",
20,
}
collection := client1.Database("go_db").Collection("Student")
ior, err := collection.InsertOne(context.TODO(), s1)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(ior.InsertedID)
}
}
func main() {
initDb1()
insert()
}
添加多条数据
package main
import (
"context"
"fmt"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"log"
)
type Student1 struct {
Name string
Age int
}
var client2 *mongo.Client
func initDb2() {
//设置客户端连接配置
co := options.Client().ApplyURI("mongodb://localhost:27017")
//连接到MongoDB
c, err := mongo.Connect(context.TODO(), co)
if err != nil {
log.Fatal(err)
}
//检查连接
err2 := c.Ping(context.TODO(), nil)
if err2 != nil {
log.Fatal(err2)
}
fmt.Println("connect success!")
client2 = c
}
func insertMany() {
s1 := Student1{
"tom",
20,
}
s2 := Student1{
"jack",
21,
}
stus := []interface{}{s1, s2}
collection := client2.Database("go_db").Collection("Student")
many, err := collection.InsertMany(context.TODO(), stus)
if err != nil {
fmt.Println(err)
}
fmt.Println(many.InsertedIDs)
}
func main() {
initDb2()
insertMany()
}
查询文档
package main
import (
"context"
"fmt"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"log"
)
var client3 *mongo.Client
func initDb3() {
//设置客户端连接配置
co := options.Client().ApplyURI("mongodb://localhost:27017")
//连接到MongoDB
c, err := mongo.Connect(context.TODO(), co)
if err != nil {
log.Fatal(err)
}
//检查连接
err2 := c.Ping(context.TODO(), nil)
if err2 != nil {
log.Fatal(err2)
}
fmt.Println("connect success!")
client3 = c
}
func main() {
initDb3()
ctx := context.TODO()
defer client3.Disconnect(ctx) //关闭
collection := client3.Database("go_db").Collection("Student")
cur, err := collection.Find(ctx, bson.D{})
if err != nil {
log.Fatal(err)
}
defer cur.Close(ctx)
for cur.Next(ctx) {
var result bson.D
cur.Decode(&result)
fmt.Println(result.Map())
}
}
更新文档
package main
import (
"context"
"fmt"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"log"
)
var client4 *mongo.Client
func initDb4() {
//设置客户端连接配置
co := options.Client().ApplyURI("mongodb://localhost:27017")
//连接到MongoDB
c, err := mongo.Connect(context.TODO(), co)
if err != nil {
log.Fatal(err)
}
//检查连接
err2 := c.Ping(context.TODO(), nil)
if err2 != nil {
log.Fatal(err2)
}
fmt.Println("connect success!")
client4 = c
}
func main() {
initDb4()
ctx := context.TODO()
defer client4.Disconnect(ctx)
c := client4.Database("go_db").Collection("Student")
update := bson.D{{"$set", bson.D{{"name", "rose"}, {"age", 22}}}}
ur, err := c.UpdateMany(ctx, bson.D{{"name", "tom"}}, update)
if err != nil {
log.Fatal(err)
}
fmt.Printf("ur.ModifiedCount:%v", ur.ModifiedCount)
}
删除文档
package main
import (
"context"
"fmt"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"log"
)
var client5 *mongo.Client
func initDb5() {
//设置客户端连接配置
co := options.Client().ApplyURI("mongodb://localhost:27017")
//连接到MongoDB
c, err := mongo.Connect(context.TODO(), co)
if err != nil {
log.Fatal(err)
}
//检查连接
err2 := c.Ping(context.TODO(), nil)
if err2 != nil {
log.Fatal(err2)
}
fmt.Println("connect success!")
client5 = c
}
func main() {
initDb5()
ctx := context.TODO()
defer client5.Disconnect(ctx)
c := client5.Database("go_db").Collection("Student")
dr, err := c.DeleteMany(ctx, bson.D{{"age", 21}})
if err != nil {
log.Fatal(err)
}
fmt.Println(dr.DeletedCount)
}
安装
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql
CRUD
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type Product struct {
gorm.Model
Code string
Price uint
}
func create(db *gorm.DB) {
//创建表
db.AutoMigrate(&Product{})
}
func insert(db *gorm.DB) {
//插入数据
p := Product{
Code: "1001",
Price: 100,
}
db.Create(&p)
}
func query(db *gorm.DB) {
//查询数据
var product Product
db.First(&product, 1) //查询主键为1的
db.First(&product, "code=?", "1001") //查找code字段值为1001的记录
fmt.Println(product)
}
func update(db *gorm.DB) {
//更新数据
var product Product
db.First(&product, 1)
db.Model(&product).Update("Price", 200)
db.Model(&product).Updates(Product{Price: 1003, Code: "1003"}) //更新多个字段
}
func delete(db *gorm.DB) {
//删除数据 只添加删除标记
var product Product
db.Delete(&product, 1)
}
func main() {
//连接MySQL
dsn := "root:jayish@tcp(127.0.0.1:3306)/golang_db?charset=utf8&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
//create(db)
//insert(db)
//query(db)
//update(db)
delete(db)
}
// 删除 user 时,也删除 user 的 account
db.Select(“Account”).Delete(&user)
// 删除 user 时,也删除 user 的 Orders、CreditCards 记录
db.Select(“Orders”, “CreditCards”).Delete(&user)
// 删除 user 时,也删除用户所有 has one/many、many2many 记录
db.Select(clause.Associations).Delete(&user)
// 删除 user 时,也删除 user 的 account
db.Select(“Account”).Delete(&users)
关联标签
标签 | 描述 |
---|---|
foreignKey | 指定外键 |
references | 指定引用 |
polymorphic | 指定多态类型 |
polymorphicValue | 指定多态值、默认表名 |
many2many | 指定连接表表名 |
joinForeignKey | 指定连接表的外键 |
joinReferences | 指定连接表的引用外键 |
constraint | 关系约束,例如:OnUpdate 、OnDelete |
GORM 允许在 Preload
的其它 SQL 中直接加载关系,例如:
type User struct {
gorm.Model
Username string
Orders []Order
}
type Order struct {
gorm.Model
UserID uint
Price float64
}
// 查找 user 时预加载相关 Order
db.Preload("Orders").Find(&users)
// SELECT * FROM users;
// SELECT * FROM orders WHERE user_id IN (1,2,3,4);
db.Preload("Orders").Preload("Profile").Preload("Role").Find(&users)
// SELECT * FROM users;
// SELECT * FROM orders WHERE user_id IN (1,2,3,4); // has many
// SELECT * FROM profiles WHERE user_id IN (1,2,3,4); // has one
// SELECT * FROM roles WHERE id IN (4,5,6); // belongs to
Joins 预加载
Preload
在一个单独查询中加载关联数据。而 Join Preload
会使用 inner join 加载关联数据,例如:
db.Joins("Company").Joins("Manager").Joins("Account").First(&user, 1)
db.Joins("Company").Joins("Manager").Joins("Account").First(&user, "users.name = ?", "jinzhu")
db.Joins("Company").Joins("Manager").Joins("Account").Find(&users, "users.id IN ?", []int{1,2,3,4,5})
注意
Join Preload
适用于一对一的关系,例如:has one
,belongs to
预加载全部
与创建、更新时使用 Select
类似,clause.Associations
也可以和 Preload
一起使用,它可以用来 预加载
全部关联,例如:
type User struct {
gorm.Model
Name string
CompanyID uint
Company Company
Role Role
Orders []Order
}
db.Preload(clause.Associations).Find(&users)
clause.Associations
不会预加载嵌套的关联,但你可以使用嵌套预加载 例如:
db.Preload("Orders.OrderItems.Product").Preload(clause.Associations).Find(&users)
带条件的预加载
GORM 允许带条件的 Preload 关联,类似于内联条件
// 带条件的预加载 Order
db.Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)
// SELECT * FROM users;
// SELECT * FROM orders WHERE user_id IN (1,2,3,4) AND state NOT IN ('cancelled');
db.Where("state = ?", "active").Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)
// SELECT * FROM users WHERE state = 'active';
// SELECT * FROM orders WHERE user_id IN (1,2) AND state NOT IN ('cancelled');
自定义预加载SQL
您可以通过 func(db *gorm.DB) *gorm.DB
实现自定义预加载 SQL,例如:
db.Preload("Orders", func(db *gorm.DB) *gorm.DB {
return db.Order("orders.amount DESC")
}).Find(&users)
// SELECT * FROM users;
// SELECT * FROM orders WHERE user_id IN (1,2,3,4) order by orders.amount DESC;
嵌套预加载
db.Preload("Orders.OrderItems.Product").Preload("CreditCard").Find(&users)
// 自定义预加载 `Orders` 的条件
// 这样,GORM 就不会加载不匹配的 order 记录
db.Preload("Orders", "state = ?", "paid").Preload("Orders.OrderItems").Find(&users)
Git是一个开源的分布式控制软件,用来管理项目版本。
下载Git
https://git-scm.com/download/win
安装Git (默认选项)
使用Git
//右击桌面 Git Bash Here
git --version
git --help
Git本地有四个工作区域:工作目录(Working Directory)
、暂存区(Stage/Index)
、资源库(Repository)
或(Git Directory)
、git
仓库(Remote Directory)
。文件在四个区域之间的转换关系如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BIyBQK9t-1685532074199)(D:\GoogleDownload\Git工作区域.png)]
WorkSpace
:工作区,就是你平时存放项目代码的地方。Index/Stage
:暂存区,用于临时存放你的改动,事实上它只是一个文件,保存即将提交到文件列表信息。Repository
:仓库区(或版本库),就是安全存放数据的位置,这里面有你提交的所有版本数据。其中HEAD
指向最新放入仓库的版本。Remote
:远程仓库,托管代码的服务器,可以简单的任务是你项目组中的一台电脑用于远程数据交换。Git的工作流程:
因此,Git管理的文件有三种状态:已修改(modified)
、已暂存(staged)
、已提交(committed)
。
版本控制就是对文件的版本控制,要对文件进行修改、提交等操作,首先要知道文件当前在什么状态,不然可能会提交了现在还不想提交的文件,或者要提交的文件还没提交上。
Git不关心文件两个版本之间的具体差别,而是关心文件的整体是否有改变,若文件被改变,在添加提交时就生成文件新版本的快照,而判断文件整体是否改变的方法就是用SHA-1
算法计算文件的校验和。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ze5ogZdb-1685532074201)(C:\Users\周洪杰\AppData\Roaming\Typora\typora-user-images\image-20230316190210455.png)]
Untracked:未跟踪,此文件在文件夹中,但并没有加入到git
库,不参与版本控制,通过git add
状态变为Staged。
Unmodify:文件以及入库,未修改,即版本库中的文件快照内容与文件夹中完全一致。这种类型的文件有两种去处,如果它被修改,而变为Modified
。如果使用git rm
移出版本库,则成为Untracked
文件。
Modified:文件已修改,仅仅是修改,并没有进行其他的操作,这个文件也有两个去处,通过git add
可进入暂存staged
状态,使用git checkout
则丢弃修改过,返回到unmodify
状态,这个git checkout
即从库中取出文件,覆盖当前修改。
Staged:暂存状态,执行git commit
则将修改同步到库中,这时库中的文件和本地文件又变为一致,文件为unmodify
状态,执行git reset HEAD filename
取消暂存,文件状态为Modified
。
git init // 初始化 在工作路径上创建主分支
git clone 地址 // 克隆远程仓库
git clone -b 分支名 地址 // 克隆分支的代码到本地
git status // 查看状态
git add 文件名 // 将某个文件存入暂存区
git checkout -- file // 撤销工作区的修改 例如git checkout -- readMe.txt 将本次readMe.txt在工作区的修改撤销掉
git add b c //把b和c存入暂存区
git add . // 将所有文件提交到暂存区
git add -p 文件名 // 一个文件分多次提交
git stash -u -k // 提交部分文件内容 到仓库 例如本地有3个文件 a b c 只想提交a b到远程仓库 git add a b 然后 git stash -u -k 再然后git commit -m "备注信息" 然后再push push之后 git stash pop 把之前放入堆栈的c拿出来 继续下一波操作
git commit -m "提交的备注信息" // 提交到仓库
若已经有若干文件放入仓库,再次提交可以不用git add和git commit -m "备注信息" 这2步, 直接用
git commit -am "备注信息" // 将内容放至仓库 也可用git commit -a -m "备注信息"
* git commit中的备注信息尽量完善 养成良好提交习惯 例如 git commit -m "变更(范围):变更的内容"
//初始化版本库,并提交到远程服务器端
mkdir WebApp
cd WebApp
git init //本地初始化
touch README
git add README //添加文件
git commit -m 'first commit'
git remote add origin [email protected]:daixu/WebApp.git
作用:如果想要将本地的项目提交的远程仓库的话,必须要设置签名。签名的作用就是用来表示用户,以区分不同的开发人员。
设置方式:
配置格式
方法一:单个仓库有效
git config user.name //用户名
git config user.email //邮箱
方法一配置完,信息会保存在当前仓库目录下的.git/config
文件中。
方法二:全局有效
git config --global user.name //用户名
git config --global user.email //邮箱
方法二配置完,信息会保存在系统盘的系统用户目录下的.gitconfig
文件中。
一般情况下都是配置成全局有效,不用为每个仓库都设置签名。当需要为某个仓库配置不同的信息时,只需要单独再为这个仓库按照方式配置即可。
初始化命令
git init
Git目录
Git目录是为你的项目存储所有历史和元信息的目录,包括所有的对象(commits,trees,blobs,tags)
,这些对象指向不同的分支。
每一个项目只能有一个Git目录,这个叫.git
目录在项目的根目录下。
搭建一个web服务器
package main
import (
"fmt"
"net/http"
"strings"
"log"
)
func sayhelloName(w http.ResponseWriter, r *http.Request) {
r.ParseForm() // 解析参数,默认是不会解析的
fmt.Println(r.Form) // 这些信息是输出到服务器端的打印信息
fmt.Println("path", r.URL.Path)
fmt.Println("scheme", r.URL.Scheme)
fmt.Println(r.Form["url_long"])
for k, v := range r.Form {
fmt.Println("key:", k)
fmt.Println("val:", strings.Join(v, ""))
}
fmt.Fprintf(w, "Hello astaxie!") // 这个写入到 w 的是输出到客户端的
}
func main() {
http.HandleFunc("/", sayhelloName) // 设置访问的路由
err := http.ListenAndServe(":9090", nil) // 设置监听的端口
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
CSS的三种导入方式
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
<style>
h3{
color: green;
}
style>
<link rel="stylesheet" href="../css/class01.css">
head>
<body>
<h1>我是一级标题,外部样式h1>
<h2 style="color: aqua">我是二级标题,行内样式h2>
<h3>我是三级标题,内部样式h3>
body>
html>
三种基本选择器
层次选择器
后代选择器:在某个元素的后面
body p{
background: green;
}
子选择器
body>p{
background: green;
}
相邻兄弟选择器;向下只有一个标签生效
.active + p{
background: green;
}
通用选择器;向下所有的兄弟标签都生效
.active~p{
background: green;
}
结构伪类选择器
ul li:first-child{
background: green;
}
ul li:last-child{
background: red;
}
p:nth-child(2){
background: blue;
}
p:nth-of-type(1){
background: yellow;
}
属性选择器
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
<style>
.demo a{
float: left;
display: block;
height: 50px;
width: 50px;
border-radius: 10px;
background: green;
text-align: center;
color: gainsboro;
text-decoration: none;
margin-right:10px;
font: bold 20px/50px Arial;
}
/*a[id]{*/
/* background: yellow;*/
/*}*/
a[id=first]{
background: blue;
}
/*a[class*=links]{*/
/* background: yellow;*/
/*}*/
/*a[href^=http]{*/
/* background: yellow;*/
/*}*/
/*以什么结尾*
a[href$=pdf]{
background: yellow;
}
style>
head>
<body>
<p class="demo">
<a href="http://www.baidu.com" class="links item first" id="first">1a>
<a href="" class="links item active" target="_blank" title="test">2a>
<a href="images/123.html" class="links item">3a>
<a href="images/123.jpg" class="links item">4a>
<a href="images/123.png" class="links item">5a>
<a href="abc" class="links item">6a>
<a href="/a.pdf" class="links item">7a>
<a href="/abc.pdf" class="links item">8a>
<a href="a.doc" class="links item">9a>
<a href="abc.doc" class="links item last">10a>
p>
body>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
<style>
body{
font-family:楷体;
color: green;
}
h1{
font-size: 50px;
text-align: center;
text-decoration: line-through;
}
.p1{
font-weight: bold;
text-indent: 2em;
}
.p2{
font-weight: bold;
text-indent: 2em;
}
a{
text-decoration: none;
}
style>
head>
<body>
<h1>故事介绍h1>
<p class="p1">
平静安详的元泱境界,每隔333年,总会有一个神秘而恐怖的异常生物重生,它就是魁拔!魁拔的每一次出现,都会给元泱境界带来巨大的灾难!即便是天界的神族,也在劫难逃。在天地两界各种力量的全力打击下,魁拔一次次被消灭,但又总是按333年的周期重新出现。魁拔纪元1664年,天神经过精确测算后,在第六代魁拔苏醒前一刻对其进行毁灭性打击。但谁都没有想到,由于一个差错导致新一代魁拔成功地逃脱了致命一击。很快,天界魁拔司和地界神圣联盟均探测到了魁拔依然生还的迹象。因此,找到魁拔,彻底消灭魁拔,再一次成了各地热血勇士的终极目标。
p>
<p class="p2">
在偏远的兽国窝窝乡,蛮大人和蛮吉每天为取得象征成功和光荣的妖侠纹耀而刻苦修炼,却把他们生活的村庄搅得鸡犬不宁。村民们绞尽脑汁把他们赶走。一天,消灭魁拔的征兵令突然传到窝窝乡,村长趁机怂恿蛮大人和蛮吉从军参战。然而,在这个一切都凭纹耀说话的世界,仅凭蛮大人现有的一块杂牌纹耀,不要说参军,就连住店的资格都没有。受尽歧视的蛮吉和蛮大人决定,混上那艘即将启程去消灭魁拔的巨型战舰,直接挑战魁拔,用热血换取至高的荣誉。
p>
<p>
When I wake up in the morning,You are all I see;
p>
<p>
When I think about you, And how happy you make me You're everything I wanted;
p>
You're everything I need;I look at you and know; That you are all to me.
p>
<a href="">a标签a>
body>
html>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KJ8JB6LY-1685532074203)(C:\Users\周洪杰\AppData\Roaming\Typora\typora-user-images\image-20230323140941802.png)]
必填字段
if len(r.Form["username"][0])==0{
// 为空的处理
}
数字
getint,err:=strconv.Atoi(r.Form.Get("age"))
if err!=nil{
// 数字转化出错了,那么可能就不是数字
}
// 接下来就可以判断这个数字的大小范围了
if getint >100 {
// 太大了
}
正则表达式判断
if m, _ := regexp.MatchString("^[0-9]+$", r.Form.Get("age")); !m {
return false
}
中文
if m, _ := regexp.MatchString("^\\p{Han}+$", r.Form.Get("realname")); !m {
return false
}
英文
if m, _ := regexp.MatchString("^[a-zA-Z]+$", r.Form.Get("engname")); !m {
return false
}
电子邮件地址
if m, _ := regexp.MatchString(`^([\w\.\_]{2,10})@(\w{1,})\.([a-z]{2,4})$`, r.Form.Get("email")); !m {
fmt.Println("no")
}else{
fmt.Println("yes")
}
手机号码
if m, _ := regexp.MatchString(`^(1[3|4|5|8][0-9]\d{4,8})$`, r.Form.Get("mobile")); !m {
return false
}
下拉菜单
<select name="fruit">
<option value="apple">appleoption>
<option value="pear">pearoption>
<option value="banana">bananaoption>
select>
slice:=[]string{"apple","pear","banana"}
v := r.Form.Get("fruit")
for _, item := range slice {
if item == v {
return true
}
}
return false
单选按钮
<input type="radio" name="gender" value="1">男
<input type="radio" name="gender" value="2">女
slice:=[]string{"1","2"}
for _, v := range slice {
if v == r.Form.Get("gender") {
return true
}
}
return false
复选框
<input type="checkbox" name="interest" value="football">足球
<input type="checkbox" name="interest" value="basketball">篮球
<input type="checkbox" name="interest" value="tennis">网球
slice:=[]string{"football","basketball","tennis"}
a:=Slice_diff(r.Form["interest"],slice)
if a == nil{
return true
}
return false
上面这个函数
Slice_diff
包含在我开源的一个库里面 (操作 slice 和 map 的库)github.com/astaxie/beeku
日期和时间
t := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.Local)
fmt.Printf("Go launched at %s\n", t.Local())
身份证号码
// 验证 15 位身份证,15 位的是全部数字
if m, _ := regexp.MatchString(`^(\d{15})$`, r.Form.Get("usercard")); !m {
return false
}
// 验证 18 位身份证,18 位前 17 位为数字,最后一位是校验位,可能为数字或字符 X。
if m, _ := regexp.MatchString(`^(\d{17})([0-9]|X)$`, r.Form.Get("usercard")); !m {
return false
}
git做版本控制
git init . //git初始化
git add . //添加
git commit -m "content_target" //提交版本 可用中文表示
Go API
go install golang.org/x/tools/cmd/godoc@latest //安装godoc文档
godoc -http=:6060 //在终端运行此命令
//在浏览器输入http://localhost:6060/pkg/ 即可实时查看API文档
新手入门必会
标头
Content-Type:
响应标头是告知客户端内容的类型,客户端再根据这个信息将内容正确地呈现给用户。
常见的内容类型有:
text/html
—— HTML 文档text/plain
—— 文本内容text/css
—— CSS 样式文件text/javascript
—— JS 脚本文件application/json
—— JSON 格式的数据application/xml
—— XML 格式的数据image/png
—— PNG 图片 w.Header().Set("Content-Type", "text/html; charset=utf-8")
Web数据响应
Web 的响应与请求结构是类似的,响应分为三个部分:响应行、响应头部、响应体。
响应状态码的有固定取值和意义:
Go Modules
go env -w GOPROXY=https://goproxy.cn,direct
命令 | 作用 |
---|---|
go mod init | 生成 go.mod 文件 |
go mod download | 下载 go.mod 文件中指明的所有依赖 |
go mod tidy | 整理现有的依赖 |
go mod graph | 查看现有的依赖结构 |
go mod edit | 编辑 go.mod 文件 |
go mod vendor | 导出项目所有的依赖到 vendor 目录 |
go mod verify | 校验一个模块是否被篡改过 |
go mod why | 查看为什么需要依赖某模块 |
st
linux操作命令
mkdir [-mp] 目录名称
rm -rf FileName 删除文件
ps -ef|grep nginx 查询nginx状态
kill -9 pid 杀死进程
netstat -anop|grep 运行名称 查看运行项目的信息
nohup 始终运行项目
Nginx目录 /usr/local/webserver/nginx/sbin
存放web项目路径 /data/www/
根目录使用下面的指令可以静态编译 Linux
平台 amd64
架构的可执行文件。Mac 或 Linux 系统:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o goblog
交叉编译成Linux可执行的二进制文件
go env -w CGO_ENABLED=0
go env -w GOOS=linux
go env -w GOARCH=amd64
go build -o name
按顺序执行:
CGO_ENABLED
:设置是否在 Go 代码中调用 C 代码。0 为关闭,采用纯静态编译;GOOS
: 目标操作系统GOARCH
: 目标操作系统的架构-o
是产出的目标文件名称上传到服务器的/data/www/目录下
给项目二进制文件赋予执行权限 chmod +x filename
长期运行项目命令 nohup ./jayblog_linux & 会输出日志文件;nohup command &
停止项目
ps aux | grep jayblog_linux
kill -9 pid