0、切片陷阱
num := []int{1, 2, 3, 4, 5}
var num2 []int
if true {
num2 = num[0:3]
} else {
num2 = append(num2, num[0:3]...)
}
num2 = append(num2, 99)
fmt.Println("hello", num) //对num2做操作,num的值却被改掉了
1、函数内嵌
func main() {
abc := 9
var f1 = func(n int) int {
fmt.Println(abc, n) //内嵌函数可以访问外部的局部变量,避免使用入参或全局变量
return 0
}
f1(2)
//下面这种递归调用,需要先声明函数,否则递归调用的语法检查说没有这个函数
var f2 func(n int) int
f2 = func(n int) int {
if n == 0 {
return 1
}
return n * f2(n-1)
}
fmt.Println(f2(5))
}
package 和 import
go和java中,都有这两个关键字。
java要求package必须和目录结构相同,比如目录net/csdn/blog/, package net.csdn.blog
go不要求package和目录结构同名(建议同名),比如 blog/, package blog
java导入一个类。需要写 import net.csdn.blog.Util
go导入一个包。需要写 import "blog" //注意这是目录名
一个远程包,需要写 import "github.com/study/blog" //表示github.com/study/网址下的blog目录
实验如下:
src/abc/ --主目录,package必须是 main,必须有main函数 func main()
abc.exe* abc.go
src/pp/qq/ --库目录 package uu
kk.go pp.go
src/pp/yy/ --库目录 package lul
yp.go
import pp "pp/qq" //用pp这个别名代理这个目录的包名uu,
import "pp/yy" //这个目录的包为lul
fmt.Println(pp.F1()) //这里用包的别名
fmt.Println(pp.F2())
fmt.Println(lul.Ff3()) //这里用包名
Go Module 工程化实践(一):基础概念篇
Go语言重新开始,Go Modules 的前世今生与基本使用 - 腾讯云技术社区 - OSCHINA - 中文开源技术交流社区
https://segmentfault.com/a/1190000018398763
2021-8-22 E:\go16.6\gf
1、mkdir src
2、cd src
3、go mod init gf --创建模块,即go.mod
4、编写abc.go import "github.com/dutchcoders/goftp"
5、在go.mod中增加 require github.com/dutchcoders/goftp master
6、go env -w GOPROXY=https://goproxy.cn
7、go mod tidy
go: downloading github.com/dutchcoders/goftp v0.0.0-20170301114626-20491571c64b
被下载到C:\Users\21565\go\pkg\mod\github.com\dutchcoders\goftp@v0.0.0-20170301114626-20491571c64b
1、使用module机制
export GO111MODULE=on off auto 默认是auto
目录结构必须是src下有 go.mod, main.go,其它包比如 util/, 必须在src目录下执行go build命令。
注意 main.go import 模块名/路径 即"vmp/util" 而不是"util"
go install 会安装到 C:\Users\yourname\go\bin 下,vmp.exe
如果使用gopath机制,go build出来的文件名默认是src.exe,
如果使用gopath机制,是支持这样的目录结构的 main/ util/ , main.go放到main目录下
2、go.mod replace机制, go.mod文件内容如下
module app
require golang.org/x/text v0.3.0
replace golang.org/x/text => github.com/golang/text v0.3.0
go mod tidy 后(无GOPATH),包下载到 ~/go/pkg/mod/cache/download/github.com/golang/text/@v
3、国内下载不到golang.io上的包(比如我引用echo库,它依赖了golang.io上的包)
可以用代理, export GOPROXY=https://goproxy.io
然后 go build, 会自动下载依赖的包
1、go模块名和目录名是不一样的。
2、import时, 填写的是 “模块名/文件夹路径”,不要文件名
3、package名 和 文件夹名 不一致时,需要在import时,显示的写一个包名,
可以是原来的包名,也可以重新定义一个包名
/app2/zte/vm/test.go
package main
import (
"fmt"
gf "hello/zte/vm" --ok
gf2 "hello/zte/vm" --ok
gf "app2/zte/vm" -- error
)
func main() {
fmt.Println("" + gf.Hello())
fmt.Println("" + gf2.Hello())
}
1、go 工程目录结构、go build、go install 、GOPATH
打开git bash
$ mkdir go3
$ export GOPATH=/f/gaofeng/go3/ --必须用export,表示环境变量,然后go才能读取到这个变量,普通变量子进程是无法读取的。
乱写一个包,编译一下试试
$ go install hesd
can't load package: package hesd: cannot find package "hesd" in any of:
D:\Go\src\hesd (from $GOROOT)
F:\gaofeng\go3\src\hesd (from $GOPATH)
说明不管用户当前在哪个目录,go都会到$GOROOT和$GOPATH的src目录下去找hesd这个文件夹进行编译。
然后在src下建立pkg1和pkg2两个目录,写代码
$ cat src/pkg2/abc.go
package abc
import "fmt"
func Tt(){
fmt.Print("hello2")
}
go build -v pkg2 --可以编译成功,但是看不到编译后的pkg2.a文件
go install -v pkg2 --编译并安装,自动在go3目录下创建了pkg目录
$ ls pkg/windows_386/
pkg2.a
$ cat src/pkg1/m.go --编写一个main包,导入pkg2目录
package main
import ("pkg2";"fmt")
func main(){
fmt.Print("main")
abc.Tt()
}
go install -v pkg1 --编译安装成功,自动创建了bin目录
$ ls bin/
pkg1.exe*
2、对象编程
Go语言面向对象编程 - Go语言中文网 - Golang中文社区
func main() {
var s = NewStudent2("gaofeng")
s.SetName("22")
s.Say1()
}
type say interface {
Say1()
SetName(name string)
}
type Student struct {
name string
}
func NewStudent(name string) *Student {//返回指针,只是方便对接口赋值,对接口赋值必须是地址类型
return &Student{name}
}
func NewStudent2(name2 string) *Student {
return &Student{name: name2}
}
func (s Student) Say1() {//s是一份拷贝,s.name="11",只是修改了这个临时拷贝,没有修改原对象
fmt.Println(s.name, "say1")
}
func (s *Student) SetName(name string) {//s是一个指针,可以修改对象内容
s.name = name
}
3、并发编程
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go ss(ch1, 2)
go ss(ch2, 4)
fmt.Println(<-ch1)
fmt.Println(<-ch2)
}
func ss(ch1 chan int, data int) {
time.Sleep(time.Second * time.Duration(data))
ch1 <- data
}
4、defer关键字
defer resp.Body.Close()
如果有多条语句需要执行,可以用一个匿名函数
defer func() {
fmt.Println("defer")
...
}()
5、http
import (
"fmt"
"io"
"net/http"
"os"
)
func Test1() {
resp, err := http.Get("http://www.baidu.com")
if err != nil {
fmt.Println("err:", err)
return
}
defer resp.Body.Close()
io.Copy(os.Stdout, resp.Body)
}
package main
import "fmt"
import "net/http"
import "time"
import "reflect"
import "bytes"
func main() {
http.HandleFunc("/", handle)
http.HandleFunc("/book1", handle1)
http.HandleFunc("/book2", handle2)
fmt.Println("running on 127.0.0.1:8080")
err := http.ListenAndServe(":8080", nil) //如果监听服务运行成功,则阻塞在这里。
if err != nil {
fmt.Print(err.Error())
}
}
//curl http://127.0.0.1:8080/ -d "name=Jim"
func handle(w http.ResponseWriter, r *http.Request) {
r.ParseForm() //默认不解析,Form为空。解析后,Form中有内容。
fmt.Println()
fmt.Println(r.Host, r.Method, r.URL)
fmt.Println(r.Form)
fmt.Println(r.Header)
fmt.Fprint(w, "ppp! "+r.Form["name"][0])
}
//curl http://127.0.0.1:8080/book1?name=Jim
func handle1(w http.ResponseWriter, r *http.Request) {
time.Sleep(3 * time.Second)
r.ParseForm() //默认不解析,Form为空。解析后,Form中有内容。
fmt.Println()
fmt.Println(r.Host, r.Method, r.URL)
fmt.Println(r.Form)
fmt.Println(r.Header)
fmt.Fprint(w, "hello world! "+r.Form["name"][0])
}
//curl http://127.0.0.1:8080/book1 -d "{name:Jim}"
func handle2(w http.ResponseWriter, r *http.Request) {
fmt.Println(reflect.TypeOf(r.Body)) // *http.gzipReader
buf := bytes.NewBuffer(make([]byte, 0, 512))
length, _ := buf.ReadFrom(r.Body)
fmt.Println(len(buf.Bytes()))
fmt.Println(length)
fmt.Println(string(buf.Bytes()))
fmt.Println()
fmt.Println(r.Host, r.Method, r.URL)
fmt.Println(r.Header)
fmt.Fprint(w, "ppp! ")
}
6、错误处理
一般用err返回错误就可以了。如果发生了严重问题,可以panic,然后程序就结束了,称之为快错。也可以用recover捕获panic。
go的错误处理机制,官方建议用err,和C差不多。panic+recover的组合类似于try-catch。
func main() {
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
err := httptest.Test2()
if err == nil {
fmt.Println("OK")
} else {
fmt.Println(err)
}
}
func Test2() error {
fmt.Println(11)
// panic("hehe")
fmt.Println(22)
return nil
// return fmt.Errorf("ggg")
}
7、map和slice
v7 := make(map[string]int)
v8 := map[string]string{
"t1": "ttu",
"t2": "oo",
}
v7["bb"] = 2
xx, ok := v7["bb"] //key是否存在
fmt.Println(len(v8), xx, ok)
for k, v := range v7 {
fmt.Println(k, v)
}
v9 := []string{"ddd", "rr"}
fmt.Println(v9)
v10 := make([]string, 5, 10)
fmt.Println(len(v10), cap(v10))
fmt.Println(v10[4]) //正常
fmt.Println(v10[5]) //异常
8、make只能给map、slice、channl分配内存; new给所有类型分配内存,new返回指针
9、类型转换 GO interface显示类型转换方法 GO interface显示类型转换方法 - Joker_88 - 博客园
go:interface{}、断言与类型转换 go:interface{}、断言与类型转换 - hershell - 博客园
接口类型向普通类型的转换称为类型断言(运行期确定)-------摘自《Go语言的类型转换和类型断言》 http://my.oschina.net/chai2010/blog/161418
type gg interface {
GetKey() string
}
type mygg struct {
abc int
}
func (*mygg) gf() string{
return "hello"
}
func tg() interface{} { //这里无法返回gg类型
return &mygg{45}
}
func testgg() {
g:= tg()
vv := g.(*mygg) //类型断言,即类型转换
fmt.Println(vv.gf(),vv.abc)
}
//2024-1-2
type BPitem struct {
key int64
val interface{}
}
func main() {
item1 := BPitem{89, "45"}
fmt.Println("b+tree", item1.key, item1.val.(string))
item2 := BPitem{89, 456}
fmt.Println("b+tree", item2.key, item2.val.(int))
item3 := BPitem{89, map[string]string{"name1": "gaofeng", "name2": "yangyang"}}
m := item3.val.(map[string]string)
fmt.Println("b+tree", item3.key, m)
}
10、 不知道类型时,如何转换? 用switch case判断
func f1() {
var a = f2()
fmt.Println(a)
switch v := a.(type) {
case int:
fmt.Println("int")
default:
fmt.Println("ggg:", v, a)
}
}
func f2() interface{} {
return "5"
}
11、go https server
Go和HTTPS - Go语言中文网 - Golang中文社区
package main
import "fmt"
import "net/http"
func hello1(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "\nhello,%s,%s,%s", r.Method, r.UserAgent(), r.Header["Content-Type"])
}
func hello2(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "\nhello2222")
}
func main() {
http.HandleFunc("/hello1", hello1)
http.HandleFunc("/hello2", hello2)
// http.ListenAndServe(":9090", nil)
http.ListenAndServeTLS(":9090", "server.crt", "server.key", nil)
}
openssl genrsa -out server.key 2048
openssl req -new -x509 -key server.key -out server.crt -days 365
13、上传文件
https://www.jianshu.com/p/981623bb8dd6
14、go redis
使用的是github.com/garyburd/redigo/redis 这个第三方redis客户端库
本次实验,使用gopath机制
go get github.com/garyburd/redigo/redis将这个库下载到G:\gaofeng\go3\src
(gopath为G:\gaofeng\go3\)
package main
import (
"fmt"
"github.com/garyburd/redigo/redis"
)
func main() {
c, err := redis.Dial("tcp", "127.0.0.1:6379")
if err != nil {
fmt.Println(err)
return
}
defer c.Close()
v, err := c.Do("SET", "name", "red")
fmt.Println("set", v, err)
v, err = redis.String(c.Do("GET", "a"))
fmt.Println("get", v, err)
vv, err2 := redis.Values(c.Do("lrange", "pp", "0", "100")) //list
fmt.Println("lrange", len(vv), err2)
fmt.Printf("v1 type:%T\n", vv)
for i, v := range vv {
fmt.Println(i, string(v.([]byte)))
}
vv, err2 = redis.Values(c.Do("keys", "*"))
for i, v := range vv {
fmt.Println(i, string(v.([]byte)))
}
}