1、安装go
1、安装 我安装在了D:\Go
添加 D:\GoWork 目录,并在目录下增加src,此目录为写代码目录
2、配置环境变量
path中添加以下内容
3、重启电脑,打开 cmd 输入: go version可以查看安装的版本
4、安装vscode ,并添加Chinese和Go两个插件
5、添加代码提示
cmd中执行以下语句:
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.io,direct
执行完后,vscode中右下角点击install all
使用goland直接修改配置即可:
5、编写helloworld
在D:\GoWork\src目录下添加main.go文件,并写入以下代码
package main
import "fmt"
func main (){
fmt.Println("hello world!")
}
编译代码(会生成main.exe文件):go build main.go
执行编译后的exe文件:./main.exe
2、代码
1、变量声明
1、package:
1、main包为应用程序的入口包
2、包名可以与其目录不同名
3、一个目录下的同级文件归属一个包。
2、关键字
为go语言中具有含义的字,不可作为变量名使用
3、变量与常量
声明方式为:var 变量名 变量类型
需要注意的是,函数内变量一旦声明,必须使用
1、变量声明
var name string //默认为""
var age int //默认为0
var isOk bool //默认为false
//变量赋值
name = "小明"
age = 1
isOk = true
2、多变量声明
当多变量声明时,第一个有值,以下的没有值,自动使用第一个的值
var (
name string
age int
isOk bool
)
//以下所有的值都为1,都使用了name的值
const (
name int = 1
age
isOk
)
3、简短变量赋值 自动判断类型
name := "aaa"
4、匿名变量 _ ,如果不需要该值则直接使用下划线代替,占位使用
5、、常量
程序运行中数据不再改变,定义之后,不能再次赋值了
const status = 1
6、 iota常量计数器(只能在常量中使用)
当iota关键字出现时,该值会被重置为0,每新增一行常量将会被计数一次
下方代码中三个值从上到下分别为0,1,2
const (
name int = iota
age
isOk
)
2、基本数据类型
1、整形
1、 整形分为两类,有符号类型(int)和无符号类型(uint),可以理解为uint8就是byte类型,uint16就是short类型,uint64就是long类型
2、特殊类型
主要区别于放在32或者64位系统会占用的数量
2、浮点数
只支持flout32和flout64,默认flout定义都是flout64
amount float64 = 1.3
flout32和flout64这两个类型都不能转换
3、布尔值
bool,和java中的boolean一致,判断true和false
4、string字符串
string也是原生类型,内部实现使用的是utf-8,string只能用双引号包裹,单引号包裹的是字符
str := "123" 普通字符串
str := `啊啊啊` 可换行的字符串
1、常用操作
split举例:
str := "123"
arr := strings.Split(str,"2")
fmt.Println(arr)
2、字符串分为两种类型
1、byte类型,表示ASCII中的字符
2、rune类型,表示一个UTF-8的字符
3、字符串修改
字符串类型本身不能修改,需要先将字符串转换为rune类型,修改完字符后,再将转换为string类型才可以
str := "白萝卜"
str1 := []rune(str)
str1[0] = '红' //需要是单引号 因为rune是字符
fmt.Println(string(str1))
3、流程控制
1、if
age := 19
if(age <= 10){
fmt.Println("小于10")
}else if(age >10 && age <=19){
fmt.Println("大于10并小于19")
}else{
fmt.Println("大于等于19")
}
2、for
1、for循环
for i := 0; i < 19; i++ {
fmt.Println(i)
}
2、无限循环
for {
fmt.Println('i')
}
3、range循环 ,i是角标,v是rune类型的值
str := "哈哈哈"
for i,v := range str {
fmt.Println(i,v,string(v))
}
4、复杂数据类型
1、数组
1、声明数组长度 :arr := [3]string{"你好","你好2","你好3"}
2、不声明数组长度:arr := [...]string{"你好","你好2","你好3"}
3、多维数组:
//一个数组包含3个子数组,子数组两个值
arr := [3][2]string{
[2]string{"第一个数组","第一个数组"},
[2]string{"第二个数组","第二个数组"},
[2]string{"第三个数组","第三个数组"},
}
//
for i, v := range arr {
for i1 := 0; i1 < len(v); i1++ {
v1 := v[i1]
fmt.Println(i1,v1)
}
fmt.Println(i,v)
}
2、切片(slice)
切片是拥有相同元素类型的可变长度的序列,是基于数组的封装,支持自动扩容
切片是一个引用类型,内部包含地址,长度,容量。一般用于操作集合
1、定义: var arr = []int{1,2,3}
2、判断是否为空:arr == nil
3、查看长度和容量:len(arr),cap(arr)
4、对slice进行切割:
1角标后的数据:arr1 = arr[1:]
2角标前的数据:arr1 = arr[:2]
5、扩容
给arr追加1,2,3,并重新赋值给arr:arr = append(arr,1,2,3)
给一个arr追加arr2里的内容,...表示拆开:
var arr = []int{1,2,3}
var arr2 = []int{1,2,3}
arr = append(arr,arr2...)
fmt.Println(arr)
6、赋值
从切片a复制到切片b,需要注意,切片b长度必须大于等于切片a
var arr = []int{1,2,3}
arr2 := make([]int ,3,3)
copy(arr2,arr) // 第一个参数是目的 第二个参数是源头
fmt.Println(arr2)
7、删除切片中的内容
没有删除切片的专用方法,例如要删除切片中2角标的值,需要append2之前的值和2之后的值,间接删除了2角标
var arr = []int{10,20,30,40}
arr = append(arr[:1],arr[2:]...) //arr[:1]内容:[10], arr[2:]内容:[30,40]
fmt.Println(arr)
8、值转换为内存并转换回来
value := "123"
p := &value
fmt.Println("内存地址为:",p)
v := *p
fmt.Println("值为:",v)
3、map
1、定义key为string,value为int的map:map1 := make(map[string]int,1)
2、传入值:map1["aaa"] = 1
3、判断key是否存在:
if ok {
fmt.Println(value ,"存在")
}else {
fmt.Println(value ,"不存在")
}
4、遍历:
for key, val := range map1 {
fmt.Println("key:",key,"value:",val)
}
5、根据key删除:delete(map1,"aaa")
6、对map进行排序
//加入到切片中
var keys []int
for k := range map1 {
keys = append(keys, k)
}
//对key排序
sort.Ints(keys)
for _, k := range keys {
fmt.Println("Key:", k, "Value:", map1[k])
}
7、切片内部是个map
arr := make([]map[string]int,10,10) // map[string]int,10,10前面加了一个[]代表是个切片
arr[0] = make(map[string]int,1) // 0的位置初始化一个map,否则会报错
arr[0]["aaa"] = 1 //给切片0位置上的map赋值
fmt.Println(arr)
8、map内部是个切片
map1 := make(map[string][]int,10)
map1["arr1"] = []int{1,2,3,4}
map1["arr2"] = []int{5,6,7,8}
fmt.Println(map1)
4、函数
1、定义:
//简洁的写法
func sum(x int,y int) int{
return x + y
}
//可以返回多个值的写法
func sum(x int,y int) (ret int,ret2 int){
ret = x + y
ret2 = x + y
return
}
//返回多个值
func sum(x int,y int) (int,int){
v := x + y
return v,v
}
2、闭包
闭包是一个函数,这个函数包含了外部作用域的变量,其实就是函数在go中可以作为变量使用
func main() {
f1(f2)
}
func f2(f func()) {
f()
}
func f1(f func(func())) {
f(func() {
fmt.Println("111")
})
}
3、defer延迟调用
代码前增加defer会在代码最后再执行,以下代码f1和f2会在最后打印
fmt.Println("start")
defer f1()
defer f2()
fmt.Println("end")
会先返回后执行内部函数
func main() {
fmt.Println(f1())//最后结果是6
}
func f1() (x int){
defer func() {
x++
fmt.Println(x,"1")
}()
fmt.Println(x,"2")
return 5
}
4、panic 和 recover
panic:抛出一个严重的错误,系统直接报错,不能再往下执行,就使用panic
recover:尝试解决错误,让程序继续可以执行之后的函数,当前函数就不再运行了
func main() {
f1()
f2()
f3()
}
func f1() {
fmt.Println("111")
}
func f2() {
defer func() {
err := recover()
fmt.Println(err,"执行了recover")
}()
panic("出现了一个严重的错误")
fmt.Println("222")
}
func f3() {
fmt.Println("333")
}
5、fmt
1、print 打印
fmt.Print() //打印
fmt.Println() //打印+换行
fmt.Printf("%d%T") //格式化
var age = 17;
fmt.Printf("%T\n", age) //查看类型
fmt.Printf("%v\n", age) //查看值
fmt.Printf("%#v\n", age) //查看值,并加上类型标识
fmt.Printf("%b\n", age) //查看二进制
fmt.Printf("%d\n", age) //查看十进制
fmt.Printf("%o\n", age) //查看八进制
fmt.Printf("%x\n", age) //查看十六进制
var name = "123";
fmt.Printf("%s\n", name) //查看字符串
2、fmt.Scan 获取用户的输入
var a string
fmt.Scan(&a) //将地址传给scan
fmt.Println(a)
6、自定义类型(type)
定义一个自己的类型,主要是为了限制类型
1、定义: type myInt int
7、结构体(struct)
等同于java中的类
1、定义
type student struct {
name string
age int
}
2、使用
var student Student
student.name = "名字"
student.age = 1
student := &Student{
"a",
1,
}
student2 := &Student{
name:"a",
age:1,
}
8、方法和接收者
方法是作用于特定类型的函数,不能让所有人调用
1、定义
func (student Student)chifan() {
fmt.Println(student.name , "吃饭")
}
2、调用
var student Student
student.name = "小黄"
student.age = 10
student.chifan()
3、继承
在a类中添加b类,可以直接调用b类的参数以及方法,也就实现了继承的特性
func main() {
dog := Dog{
Animal: Animal{"小黄"},
age: 100,
}
dog.move()
}
type Animal struct {
name string
}
type Dog struct {
Animal
age int
}
func (a Animal)move() {
fmt.Println(a.name,"会动")
}
4、序列化和反序列化
转换为json:json.Marshal
需要注意,由于只有大写才能被其他包识别,所以类中的字段必须是首字母大写,才可以正常转换为json,如果想要转换为json的是小写,需要在字段后增加·json:"小写的字段名"
func main() {
dog := Dog{
Age: 100,
}
json,_ := json.Marshal(dog)
fmt.Println(string(json))
}
type Dog struct {
Age int `json:"age"`
}
反序列化:json.Unmarshal
j := "{\"age\":100}"
var dog1 Dog
json.Unmarshal([]byte(string(j)),&dog1) //传内存地址是为了在内部修改dog1的值
fmt.Printf("%#v",dog1)
9、接口
接口用于规定实现的方法名,只要实现了这个方法名,就可以当参数使用
一个接口可以有多个方法名,一个类只要全部实现了该方法名,则可以理解为这个类型的变量
func main() {
var dog Dog
var cat Cat
da(dog)
da(cat)
}
//定义类
type Dog struct {}
type Cat struct {}
//定义方法,传入接口
func da(speakInterface SpeakInterface) {
speakInterface.speak()
}
//定义类的名称 需要与接口里的名称一致
func (c Cat)speak() { fmt.Println("猫叫") }
func (c Dog)speak() { fmt.Println("狗叫") }
//定义接口
type SpeakInterface interface {
speak() //只要有这个方法即可
}
10、指针接收和值接收的区别
1、指针接收的接口只能接收指针类型,值接收的接口是值或者直接都可以接收。
func main() {
dog1 := &Dog{"小狗"} //必须指针传入,如果想值传入,需要删除方法上的*
var dogInter DogInter = dog1
dogInter.chi2()
}
type DogInter interface {
chi()
chi2()
}
//添加*只能接收指针类型
func (d *Dog)chi() {
fmt.Println("chi")
}
func (d *Dog)chi2() {
fmt.Println("chi2")
}
2、指针接收将数据修改,会直接影响类本身。值接收,方法修改,不会影响类本身
func main() {
dog1 := Dog{"小狗"} //值传入
dog1.chi()
fmt.Println(dog1) //小狗
dog1.chi2()
fmt.Println(dog1) //chi
}
func (d Dog)chi() {
d.name = "chi"
}
func (d *Dog)chi2() {
d.name = "chi"
}
11、空接口
任何类型都实现了空接口,作用为传参时什么都可以传入,例如fmt.Println(),就是什么都可以传
也可以作为slice或者map的值,可以理解为java中的Object
定义:interface{}
12、package
包中的标识符,如果首字母小写,表示私有,如果是大写,就是公有的
只有main包才能编译成可执行文件
1、定义:package 包名
2、goland添加gopath才可正常引入包
13、init方法
init方法是特殊的方法,在引入包的时候会自动调用,不能手动调用
先执行导入的包的init,再执行本类
中的init
14、文件操作
io工具包执行读取文件和导出文件操作
//io工具类方式
func f3() {
file1, err := ioutil.ReadFile("src/demo/day1/aaa.txt")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(file1))
//打印
ioutil.WriteFile("src/demo/day1/bbb.txt",file1,os.FileMode(777))
}
//缓冲流方式
func f2() {
fileObj, err := os.Open("src/demo/day1/aaa.txt")
if err != nil {
fmt.Println(err, fileObj)
return
}
reader := bufio.NewReader(fileObj)
for {
line, err := reader.ReadString('\n')
if err != nil {
return
}
fmt.Print(line)
}
fileObj.Close()
}
//os方式
func f1() {
fileObj, err := os.OpenFile("src/demo/day1/aaa.txt",os.O_APPEND | os.O_CREATE,0644)
if err != nil {
fmt.Println(err, fileObj)
return
}
//读文件
var tmp = make([]byte, 128)
for {
//将文件中的内容读取到tmp中,返回读了多少个字节
n, err := fileObj.Read(tmp[:])
if err != nil {
fmt.Println(err, fileObj)
return
}
fmt.Println(string(tmp[:n]))
if n < 128 {
return
}
}
//关闭流
defer fileObj.Close()
}
15、反射
1、获取变量的类型:reflect.TypeOf(a)
func main() {
var student student
v := reflect.TypeOf(student)
fmt.Println("名字:",v.Name()) //名字
//获取字段名
for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
fmt.Println("字段名:",field.Name,"字段类型:",field.Type,"获取标签:",field.Tag.Get("hahaha"))
}
}
type student struct {
name string `json:"Name" hahaha:"哈哈哈"`
}
16、strconv类型转换
将string转换为int,float,bool类型使用
//字符串转换为数字类型 10进制 64位的
a := "111"
parseInt, err := strconv.ParseInt(a, 10, 64)
//strconv.ParseBool() 转换为
//strconv.ParseFloat()
if err != nil {
return
}
fmt.Println(parseInt)
//数字类型转换为字符串
b := 123
str := fmt.Sprint(b)
fmt.Printf("%#v",str)
5、并发编程
1、goroutine(个肉体)
只需要在调用函数的前面加一个go关键字,就是创建了一个goroutine.main函数本身就是一个goroutine。会根据main函数的终止而终止
func main() {
for i := 0; i < 100; i++ {
go f1(i)
}
fmt.Println("go ")
time.Sleep(time.Second)//等待f1函数结束
}
func f1(i int) {
fmt.Println("你好",i)
}
1、等待所有的goroutine全部关闭,再往下执行
//添加goroutine到wg中,等待wg计数器为0,再往下执行
var wg sync.WaitGroup
func main() {
for i := 0; i < 100; i++ {
//计数
wg.Add(1)
go f1(i)
}
//等待
wg.Wait()
fmt.Println("go ")
}
func f1(i int) {
fmt.Println("你好",i)
//关闭
defer wg.Done()
}
2、channel
可以在多个goroutine之间传递数据
1、定义:a := make(chan int)
2、发送值:a <- 1
3、接收值:x := <- a
4、关闭通道:close()
//创建一个有16缓存区的通道
a := make(chan int,16)
wg.Add(1)
go func() {
//从管道中获取数据
x := <- a
fmt.Println("f1接收到了",x)
defer wg.Done()
}()
//将数据放到管道中
time.Sleep(10000)
a <- 10
fmt.Println("通道里放了一个10",a)
wg.Wait()
close(a)
3、sync
加锁,保证线程同步
1、互斥锁 :var lock sync.Mutex
var lock sync.Mutex
func add() {
lock.Lock()
for i := 0; i < 100000; i++ {
a = a + 1
}
wg.Done()
lock.Unlock()
}
2、读写互斥锁:var rwLock sync.RWMutex
3、只执行一次的代码:sync.Once
4、并发安全的map:sync.Map,如果只用map大于20个同时存入,会出现并发安全的问题
store:设置值
load:取值
5、atomic原子类,安全的添加
atomic.AddInt32()
6、网络编程
1、tcp开发
接收端
func main() {
//本地端口启动
listen, err := net.Listen("tcp","127.0.0.1:8888")
if err != nil {
fmt.Println("启动失败",err)
return
}
fmt.Println("启动接收")
for{
conn, err := listen.Accept()
if err != nil {
fmt.Println("接收失败",err)
return
}
var tmp [128]byte
n, err := conn.Read(tmp[:])
if err != nil {
fmt.Println("read失败",err)
return
}
fmt.Println(string(tmp[:n]))
}
}
发送端
func main() {
//建立链接
conn, err := net.Dial("tcp", "127.0.0.1:8888")
if err != nil {
fmt.Println("建立失败",err)
return
}
conn.Write([]byte("发送成功啦哈"))
conn.Close()
}
2、http
启动服务
func f1(response http.ResponseWriter,request *http.Request) {
a := "你好"
response.Write([]byte(a))
}
func main() {
//启动的地址
http.HandleFunc("/aaa",f1)
//启动的ip
http.ListenAndServe("127.0.0.1:8080",nil)
}
获取服务器的数据
resp, err := http.Get("http://127.0.0.1:8080/aaa")
if err != nil {
return
}
all, err := ioutil.ReadAll(resp.Body)
if err != nil {
return
}
fmt.Println(string(all))
3、单元测试
文件名必须以_test.go为结束
1、Test开头为测试函数,参数为T *testing.T
func TestSplit(t *testing.T) {
ret := Split("123,aaa,bbb")
want := []string{"123","aaa","bbb"}
if reflect.DeepEqual(ret,want) {
t.Errorf("ret:%v;want:%v",ret,want)
}
}
func Split(str string) []string {
split := strings.Split(str, ",")
return split
}
4、连接mysql
1、下载依赖 :go get github.com/Go-SQL-Driver/MySQL
2、数据库连接以及增删改查
import (
"database/sql"
"fmt"
_ "github.com/Go-SQL-Driver/MySQL"//需要空导入
)
var db *sql.DB
func init() {
initDb()
}
func main() {
insert()
}
func insert() {
tx, err := db.Begin() //开启事务
if err != nil {
fmt.Println("开启事务失败")
return
}
sqlStr := "insert into course(name) values (?)"
p, err := db.Prepare(sqlStr)
if err != nil {
tx.Rollback()
fmt.Println("格式化失败")
return
}
defer p.Close()
_, execErr := p.Exec("111")
if execErr != nil {
tx.Rollback()
fmt.Println("執行sql失败")
return
}
tx.Commit()
}
func getById(id int) (Course){
var cource Course
sqlStr := "select * from course where id = ?"
rowObj := db.QueryRow(sqlStr, id)
//执行返回数据
rowObj.Scan(&cource.ID,&cource.NAME)
return cource
}
func getAll() ([]Course){
courseList := []Course{}
sqlStr := "select * from course"
rows, err := db.Query(sqlStr)
if err != nil {
fmt.Println("查询失败")
return courseList
}
for rows.Next(){
var cource Course
rows.Scan(&cource.ID,&cource.NAME)
courseList = append(courseList,cource)
}
defer rows.Close()
return courseList
}
type Course struct {
ID int
NAME string
}
//初始化db
func initDb() (err error) {
//链接数据库
dsn := "root:1234@tcp(127.0.0.1:3306)/book"
db, err = sql.Open("mysql", dsn)
if err != nil {
fmt.Println("链接失败",err,db)
return
}
pingErr := db.Ping()
if pingErr != nil {
fmt.Println("链接失败",pingErr,db)
return
}
fmt.Println("链接数据库成功")
return nil
}
遇到问题
1、如果出现找不到的包,直接网上找到下载到对应文件下即可
2、使用go mod vendor 下载包到当前
3、先在go.mod中添加包其他地方才可使用
require (
github.com/ylywyn/jpush-api-go-client latest
)