go 从入门到了解

一,GO的安装与配置

官网:https://golang.org/dl/

镜像:https://golang.google.cn/dl/

1,GOPATH

GOPATH在windows上的默认值:%USERPROFILE%/go

Go1.14及之后的版本中启用了Go Module模式之后,不一定非要将代码写到GOPATH目录下,所以也就不需要我们再自己配置GOPATH了

Go1.14之前的版本GOPATH 即为工作目录:本示例设置为 E:workgo

workgo                  // my-go为GOPATH目录
  -- bin
     -- myApp1        // 编译生成
     -- myApp2        // 编译生成
     -- myApp3        // 编译生成
  -- pkg                  // 依赖包,编译后的归档文件
  -- src
     -- MyApp1        // 项目1
        -- models
        -- controllers
        -- others
        -- main.go 
     -- MyApp2        // 项目2
        -- models
        -- controllers
        -- others
        -- main.go 

2,Proxy

2.1,设置代码

默认GoPROXY配置是:GOPROXY=https://proxy.golang.org,direct,
https://proxy.golang.org访问不是很可靠,常用的两个代理镜像:
代理一:go env -w GOPROXY=https://goproxy.io,direct
代理二:go env -w GOPROXY=https://goproxy.cn,direct

2.2,设置私仓库

私有库设置,不走代理
go env -w GOPRIVATE=*.gitlab.com,*.gitee.com

2.3,验证包的有效性

go mod vendor,验证包的有效性时,Get https://sum.golang.org/lookup/xxxxxx: dial tcp 216.58.200.49:443: i/o timeout
是因为 sum.golang.org 网站不能访问,可以关闭验证包的有效性
go env -w GOSUMDB=off
也可以使用国内的网站验证包的有效性
go env -w GOSUMDB="sum.golang.google.cn"

3,go环境的常用命令:

go run src/main.go

go build src/main.go

./main.exe

go version

go env 

3,项目目录结构

4,开发工具:

4.1,GoLand (关键在于怎么长期使用)

GoLand 下载地址:https://www.jetbrains.com/go/download/other.html

4.2,VS Code

Vs Code 下载地址:https://code.visualstudio.com/Download

Ctrl + Shift + P  安装 go:install  相关命令

5,包管理

1.5版本之前,依赖包放在GOPATH下
1.5版本及之后,采用了vendor机制,主要解决包的版本管理
1.9版本推出了包管理工具,dep
1.11版本推出了包管理工具,modules 简称 mod

初始化一个项目的mod, go mod init <项目名>
go.mod定义:

module用来定义包名
require用来定义依赖包及版本
indirect表示间接引用
在goland中可以用alt+enter来下载依赖包

同一个项目的包引用:
"xcj.com/gose01/src/mypackage"
不同项目的包引用:
a,在go.mod里定义:
require "xcj.com/gose02" v0.0.0
replace "xcj.com/gose02" => "../gose02"
b,在使用的文件里定义:
"xcj.com/gose02/src/testpackage"



指定安装某个版本:go get package@version
例:go get github.com/astaxie/beego@v1.11.1

 跨平台编译:

winows下编译linux可执行文件:
SET CGO_ENABLED=0 // 禁用CGO
SET GOOS=linux // 目标平台是linux
SET GOARCH=amd64 // 目标处理器架构是amd64

windows下编译苹果可执行文件:
SET CGO_ENABLED=0
SET GOOS=darwin
SET GOARCH=amd64

变量声明:

var name string
var age int
var isOk bool

var (
a string
b int
c bool = true
d float32
) // 通过用小括号包含的方式写到多行
var a, b, c int // 变量a、b、c的类型都是int 若不赋值,用零值来初始化
var x, y, z = true, 10.24, "randy" // 推导出变量x、y、z的类型分别为bool、float64、string
var f, err = os.Open(name) // 调用os.Open函数返回一个打开的文件和一个错误信息
短变量:
i, j := 0, 1 // 简短声明一组变量,用于函数内部
a,b: = 200,"hello" // 简短声明一组变量,用于函数内部

匿名变量:

定义:
func Foo(str string) (int, string) {
return 10, str
}
调用:
x, _ := mypackage.Foo("xcj")
_, y := mypackage.Foo("yy")
fmt.Println("x=", x)
fmt.Println("y=", y)

// iota是go语言的常量计数器,只能在常量的表达式中使用
// iota的行记数,只在当前const内生效
// iota在const关键字出现时将被重置为0 ,主要是用于定义枚举
// const同时声明多个常量时,如果省略了值则表示和上面一行的值相同
const (
n1 = iota
n2 = iota
n3 = 0
n4
)
const n5 = iota //0

fmt.Println(n1,n2,n3,n4,n5)     // 输出结果  0 1 0 0 0

格式化输出:

var people interface{} = struct {
name string
age int
}{"sam", 20}
fmt.Printf("%v ",people) // %v 相应值的默认格式 {sam 20}
fmt.Printf("%+v ",people) // %+v 打印结构体时,会添加字段名 {name:sam age:20}
fmt.Printf("%#v ",people) // %#v 相应值的Go语法表示 struct { name string; age int }{name:"sam", age:20}
fmt.Printf("%T ",people) // %T 相应值的类型的Go语法表示 struct { name string; age int }
fmt.Printf("%v%% ",100) // %% 字面上的百分号,并非值的占位符 100%

go的数据类型:

除了基本的整型、浮点型、布尔型、字符串外,还有数组、切片、结构体、函数、map、通道(channel)等。

nil是预定义的标识符,代表指针、通道、函数、接口、映射或切片的零值,是预定义好的一个变量

// 浮点数不是一种精确的表达方式
x := 0.1
y := 0.2

res := x + y
fmt.Println(res) // 0.30000000000000004
fmt.Println(res == 0.3) // false

数组与切片:

切片可以理解成一种特殊的数组,切片可以改变存储大小,数组是值拷贝而切片是引用 

// 切片添加值
//var slice []int = []int{3, 6}
//fmt.Println(slice)
//slice = append(slice, 1, 2, 3, 5, 8)
//fmt.Println(slice)

fmt.Println("数组***********************************")
var arr1 [3]int = [3]int{1, 2, 3}
var arr2 [3]int = arr1
fmt.Println(arr1, arr2)
arr2[0] = 10002
fmt.Println(arr1, arr2)

fmt.Println("切片***********************************")
var slice1 []int = []int{1, 2, 3}
var slice2 []int = slice1
fmt.Println(slice1, slice2)
slice2[0] = 10002
fmt.Println(slice1, slice2)

数组的大小是类型的一部分,数组大小不可改变
var a [4]int=[4]int{1,2,3,4}
var a =[4]int{1,2,3,4}
a :=[4]int{1,2,3,4}

interface{} 类型数组,可以包含任意类型

让编译器根据初始值的个数自行推断数组的长度
var numArray = [...]int{1, 2}

使用指定索引值的方式来初始化数组
a := [...]int{1: 1, 3: 5}
fmt.Println(a) // [0 1 0 5]

多组数组
a := [...][2]string{
{"北京", "上海"},
{"广州", "深圳"},
{"成都", "重庆"},
}

切片的本质就是对底层数组的封装,它包含了三个信息:底层数组的指针(地址)、切片的长度(len)和切片的容量(cap)。
var a[9]int = [9]int{0,1,2,3,4,5,6,7,8}
fmt.Println(a)
var b[]int = a[1:2] // 使用数组构建切片
fmt.Println(b)
fmt.Println(len(b))
fmt.Println(cap(b))

a := make([]int, 2, 10)
fmt.Println(a) //[0 0]
var b []int = make([]int, 3)

var citySlice []string
citySlice = append(citySlice, "北京")

流程控制:

// if 用法
if score := 65; score >= 90 {
fmt.Println("A")
} else if score > 75 {
fmt.Println("B")
} else {
fmt.Println("C")
}

// for
// for循环可以通过break、goto、return、panic语句强制退出循环。
i := 0
for i < 10 {
fmt.Println(i)
i++
}

// 循环数组
//names:=[4]string{"张三","李四","王五"}
//for i,v:=range names{
// fmt.Println(i,v)
//}

// 循环切片
//names:=[]string{"张三","李四","王五"}
//for i,v:=range names{
// fmt.Println(i,v)
//}

// 循环字符串
//for i,v:=range "hello你好"{
// fmt.Printf("%d %q ",i,v)
//}


// map返回键和值
//ages:=map[string]int{
// "张三":20,
// "李四":30,
//}
//for i,v:=range ages{
// fmt.Println(i,v)
//}

fallthrough 语法可以执行满足条件的case的下一个case,无条件执行下一个case(穿透)是为了兼容C语言中的case设计的。
s := "a"
switch {
case s == "a":
fmt.Println("a")
fallthrough // 无条件执行下一个case(穿透)
case s == "b":
// 上一个条件满足之后会执行这条语句 fallthrough
fmt.Println("b")
case s == "c":
fmt.Println("c")
default:
fmt.Println("...")
}

goto跳转:

for i := 0; i < 10; i++ {
for j := 0; j < 10; j++ {
if j == 2 {
// 设置退出标签
goto breakTag
}
fmt.Printf("%v-%v ", i, j)
}
}
return
// 标签
breakTag:
fmt.Println("结束for循环")

函数作为参数传递:

x11 :=calc(calc(10,20), calc(20,30))
print(x11)

func calc(x, y int) (int) {
sum := x + y
return sum
}

func intSum(x ...int) int {
fmt.Println(x) //x是一个切片
sum := 0
for _, v := range x {
sum = sum + v
}
return sum
}

我们可以使用type关键字来定义一个函数类型
type calculation func(int, int) int
func add(x, y int) int {
return x + y
}

func sub(x, y int) int {
return x - y
}


函数作为参数:
func add(x, y int) int {
return x + y
}
func calc(x, y int, op func(int, int) int) int {
return op(x, y)
}

ret2 := calc(10, 20, add)
fmt.Println(ret2) //30

闭包指的是一个函数和与其相关的引用环境组合而成的实体

func adder() func(int) int {
var x int
return func(y int) int {
x += y
return x
}
}
func main() {
var f = adder() // f = func(y int) int{...}
fmt.Println(f(10)) //10
fmt.Println(f(20)) //30
fmt.Println(f(30)) //60

f1 := adder()
fmt.Println(f1(40)) //40
fmt.Println(f1(50)) //90
}


defer延迟处理,异步处理,最后被defer的语句,最先被执行。
fmt.Println("start")
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
fmt.Println("end")


start
end
3
2
1


Go语言中的指针操作非常简单,只需要记住两个符号:&(取地址)和*(根据地址取值)
Go语言中使用&字符放在变量前面对变量进行“取地址”操作。
int、float、bool、string、array、struct
*int、*float、*bool、*string、*array、*struct

//指针取值
a := 10
b := &a // 取变量a的地址,将指针保存到b中
fmt.Printf("type of b:%T ", b) // type of b:*int
c := *b // 指针取值(根据指针去内存取值)
fmt.Printf("type of c:%T ", c) //type of c:int
fmt.Printf("value of c:%v ", c) //value of c:10


func modify1(x int) {
x = 100
}

func modify2(x *int) {
*x = 100
}

func main() {
a := 10
modify1(a)
fmt.Println(a) // 10
modify2(&a)
fmt.Println(a) // 100
}

// 结构体名字or结构体中的字段名字首字母大写表示公开的、可以被其他包导入,小写表示私有、仅在定义当前结构体的包中可访问
// 使用type和struct关键字来定义结构体
// 方法与函数的区别是, 函数不属于任何类型, 方法属于特定的类型。
//Person 结构体
type Person struct {
name string
age int8
}

//NewPerson 构造函数
func NewPerson(name string, age int8) *Person {
return &Person{
name: name,
age: age,
}
}

//Dream Person做梦的方法
func (p Person) Dream() {
fmt.Printf("%s的梦想是学好GO! ", p.name)
}

func main() {
p1 := NewPerson("路人A", 25)
p1.Dream()
}

原文地址:https://www.cnblogs.com/xcj26/p/15412224.html