1 Go语言介绍

1 golang--》Go---》谷歌公司 2009年
    golang:指go语言,指的go的sdk
    goland:软件,ide:集成开发环境  Java写的
2 Go是静态强类型语言
	静态:需要编译再执行
			C、C#、Java:编译成不同平台的可执行文件在不同平台执行
			c语言:编译慢
			Go:编译快、并且可跨平台编译
	动态:python php nodejs	
		需要一个解释器
		边解释边运行
		对运维很麻烦
		pyinstaller 弄成可执行文件
	强类型:
		不同类型之间不允许直接运算(所以要强制类型转换)
		go如何强制类型转换:显式类型转换
		Java:可以字符串+数字 但是是强类型语言--隐式类型转换、内部自动转换
	弱类型:js
		比如可以:字符串+数字
3 Go语言特性
	跨平台的编译型语言(交叉编译)
	语法接近C语言
	管道(channel),切片(slice),并发(routine)--》线程池+协程
	有垃圾回收的机制(GC机制)
	支持面向对象和面向过程的编程模式
		(继承,封装,多态)写法有点古怪
4 Go语言发展(版本/特性)
	// go语言也是用c写的
	//java,python都是用c写的
	2012年3月28日 go1 —— 稳定版版的发布
	2015年8月19日 go1.5 —— 实现的架构变化,同时保留了和旧版本的兼容性,本次更新中移除了”最后残余的C代码”---》自举
	2018年8月24日 go1.11 —— modules支持,摒弃掉go path的包管理
	2020 年 2 月 go 1.14— — 现在 Go Module 已经可以用于生产环境
	至今:1.17.6     开始支持泛型(额外操作)

5 go应用领域
	-服务端开发   高并发
	-微服务方向
	-区块链开发  第一款开源的区块链软件是用go写的
	-自动化运维 docker,k8s二开(docker、k8s是go写的,对go友好)
	-云平台
	-各个互联网公司都会招go开发
		-中小型公司跟风
	-前景:很广阔
		-以后自身有很好的未来:python、go、java(热门语言)都要会
	-go简单易学


2 静态和动态语言

c语言:按照c的语法写代码---->把源代码拿到Linux机器编译--》编译成Linux的可执行文件---》在Linux平台运行
java:一处编码处处运行,编译型语言
	-java的编译不是编译成可执行文件----》字节码文件,jar,war---》不能直接运行在操作系统之上(不是可执行文件)
	-java虚拟机:不同平台有不同平台的虚拟机,字节码文件运行在虚拟机之上
	-java的跨平台本质是不同平台装了不同平台的虚拟机---》有的人把它当成解释器
python:解释型----》python的源代码,运行在不同平台的解释器之上----》天然跨平台

php:解释型:php的解释器
nodejs: node环境


go:编译型,交叉编译(跨平台编译),只需要拿到编译好的可执行文件,直接运行即可


3 go开发环境搭建

// 搭建开发环境
// go的运行环境:不需要搭建---》可执行文件


// go的sdk----》内置函数,包,编译go源码(编译成可执行文件)
	-1.17 最新 ,向下兼容
	-下载地址:The Go Programming Language
	-go1.17.6.windows-amd64.msi 一路下一步
	-安装完成后,会把安装路径加入到环境变量
		-go 命令,可以找到(如果没有装sdk,go可执行文件是没有的)

// 集成开发工具:推荐 goland
	-goland:推荐,收费,免费试用30天,老版本可以卡bug,无限试用30天
		-学生免费(教育邮箱)
		-开源软件作者免费(github开源持续多长时间提交代码)
		-花钱:单软件,多软件授权,多账号
		-破解:pycharm如何破,这个一样
		-下载地址:
			-https://www.jetbrains.com/zh-cn/go/download/other.html---》2021.1--》好破解
		-goland,pycharm,webstorm。。一家公司出的 Jetbrains
			-快捷键都一样,使用习惯都一样
		-安装:一路下一步
	-vs code
		-免费,有部分公司用,不涉及到版权问题
	-vim
		-装各种插件

// 验证环境搭建
	-打开cmd创建,输入 
		-go version  // 查看版本信息
		-go env     // 查看go的环境变量



4 第一个go程序

// 新建的go项目内,有go.mod 文件,后面会讲,做包管理的


// 代码写好---》go是编译型语言----》编译成可执行文件----》运行可执行文件
// 运行方法:
	- go build go文件名字   // 编译go文件,编译成   go文件名字.exe
	- go文件名字.exe  可执行文件运行即可

// 开发阶段----》编译--》运行,比较麻烦,所以:
	-goland提供了,编译并运行---》右键---》run
	-原生提供了一个命令 (编译并运行的命令),但是编译后的文件,不在项目根路径下
		-go run go文件名字   



// ************第一个helloworld的代码***********


package main   // 指定包名,任何一个go文件都要有这句话,包名可以不一样,目前必须叫main

import "fmt" //导入内置包,fmt包,只要使用了fmt包,会自动导入,goland自动导入的,如果其他编辑器,需要手动导入

func main() {   // 定义一个函数,函数名叫 main,目前必须叫main
	fmt.Println("hello world")  // 真正的在控制台输出 hello world,必须用双引号
}


// 第一行,必须指定是main包
// 需要导入fmt包
// 程序要运行,必须写一个main函数,在main函数中打印helloworld

// 注意:go的项目入口:必须是main包下的main函数

// fmt.Println(a...)  是 goland给提示的东西,其实根本不存在,函数的参数提示,不用关注

5 语法和命名规范

// 第一行必须是包名
// 程序要运行,必须是main包下的main函数
	-一个项目中有一个main包,有两个main函数,它就不能运行,以后写项目肯定不能有俩main函数
	-学基础阶段,可以在一个项目下有多个main函数,但是必须以file形式运行,不能以包形式运行
// 包导入,goland可以自动导入,(后期可以手动导入)
// go build 只编译(在什么平台下就编译成什么平台的可执行文件)----》交叉编译
// go run   编译并运行

5.1 25 个关键字

break      default       func     interface   select
case       defer         go       map         struct
chan       else          goto     package     switch
const      fallthrough   if       range       type
continue   for           import   return      var

// func 定义函数
// import 导入包
// package 声明包名
// var 定义变量的关键字

// 关键字不能作为变量的名字


5.2 37个保留字(对应内建的常量、类型和内置函数)

内建常量: true false iota nil

内建类型:  int int8 int16 int32 int64
          uint uint8 uint16 uint32 uint64 uintptr
          float32 float64 complex128 complex64
          bool byte rune string error

内建函数: make len cap new append copy close delete
          complex real imag
          panic recover


// 以后定义变量时,不能以数字开头,可以使用数字字母下划线组成的字符串来定义
// 变量的大小写,有特殊含义,区分大小写
// 变量定义推荐用驼峰体
	-AgeOfPerson   go语言和java语言推荐,python不推荐
	-age_of_person  下划线方式,不推荐(go不推荐,python推荐)
// go文件的名字,建议使用下划线(参照源码)
	-java语言中:变量和文件名都推荐用驼峰
	-Python中:变量名和文件名都推荐用下划线

6 变量定义和使用


6 个点
	-变量完整定义
	-变量类型推导
	-变量简略声明
	-变量的使用,修改值,打印值
	-同时定义多个变量
	-变量要先定义,在使用,并且只能定义一次


// 变量的定义和使用

package main

import "fmt"

//var name = "lqz" // 作用域范围是全局,所有函数内部都可以使用

func main() {
	// 方式一:完整定义  var关键字 变量名 类型 = 变量值
	var NameOfCx string = "cx"  // 定义不使用会报错、编译阶段报错
	fmt.Println(NameOfCx)

	var age int = 19
	fmt.Println(age)

	// 方式二:类型推导(不需要写类型) var关键字 变量名 = 变量值
	var age2 = 19  // 没写类型、是推导出来的、也是固定不能改变
	fmt.Println(age2)

	var NameOfCx2 = "cx"
	fmt.Println(NameOfCx2)

	// 如何查看变量类型:Printf
	fmt.Printf("变量的类型:%T,变量值:%s", NameOfCx2,NameOfCx2)

	// 方式三:简略声明 变量名 := 变量值
	//age = 19  // 报错 变量没有定义
	age3 := 19
	fmt.Println(age3)
	fmt.Printf("%T  %d", age3, age3)


	// 变量的使用:修改值、打印值
	var name string = "cx"
	// 修改值 只能相同类型修改、在定义阶段就定义了类型、不能改类型
	name = "wyz"
	fmt.Println(name)

	//变量可以先定义再赋值
	var age4 int  // 只能使用方式一定义方式、不能推导
	age4 = 100
	fmt.Println(age4)

	// 一行同时定义多个变量:推导
	var name5, age5, gender5 = "cx", 19, "男"
	fmt.Println(name5, age5, gender5)

	// 一行同时定义多个变量:简略声明
	name6, age6, gender6 := "cx", 19, "男"
	fmt.Println(name6, age6, gender6)

	// 一行同时定义多个变量:var()
	var (
		name7 = "cx"
		age7 = 19
		gender7 = "男"
	)
	fmt.Println(name7, age7, gender7)

	// 变量要先定义 然后使用 并且只能定义一次
	// 使用简略声明可以定义多次,但是实际是修改值
	var age8 = 19
	name8,age8 := "cx",20  // 有定义、有修改、修改的定义是修改
	fmt.Println(age8, name8)
}

7 基础数据类型、变量类型

// 基础数据类类型
	-数字
		-整型
			-int
			-int8
			-int16
			-int32
			-int64
		-正整数:无符号整型
			-uint
			-uint8
			-uint16
			-uint32
			-uint64
		-浮点型
			-float32
			-float64
		-复数(正常开发中用不到)
			-complex128 
			-complex64
	-字符串
		-string
		-双引号或三引号包裹   (单引号不行:单引号表示数字)
	-布尔
		-bool
		-true false  (都是小写)

	-byte:uint8 的别名
	-rune:int32 的别名


// 复合数据类型
	-数组
	-切片
	-map
// 高级数据类型
	-自定义的类型
	-函数类型
	-指针类型


// 基础的变量类型
package main

func main() {
	// 1 整型(表示正负整数 和 0)
    
	// var age int = 19
	// int8 和其他区别在于,表示整数的范围不一样
	// var age int8 = -128
	// 一个int8 占8个比特位,8个小格只能放0或1,一个字节,所有它有2的8次方中变化,表示数字的大小2的7次方-1种变化
	// 正负2的7次方-1  -128----+127
	// int16 :正负2的15次方-1
	// int32:正负2的31次方-1
	// int64:正负2的63次方-1
	// int :分平台,在32位机器,表示int32,在64位机器表示int64
	// 以后,如果要定义一个变量,表示人的年龄
	// var age int = 9223372036854775807
	// fmt.Println(age)
    

	// 2 无符号整型
    
	// var age uint =19
	// fmt.Println(age)

	// uint8: 2的8次方-1   0---255之间
	// var age uint8=255
	// fmt.Println(age)
	// uint16:2的16次方-1
	// uint32:2的32次方-1
	// uint64:2的64次方-1
	// uint 在32位机器是 uint32,在64位机器是uint64


	// 3 浮点型(小数),表示精度不一样
	// var salary float32= 10.123456789   // 小数点后6位
	// var salary2 float64= 10.123456789123456789  // 小数点后15位
	// fmt.Println(salary)
	// fmt.Println(salary2)


	// 4 复数 ---》有实部6 和 虚部 7
    // var c complex64= 6 + 7i
    // var d complex64= 2 + 3i
    // fmt.Println(c+d)
    // 结果:实部相加、虚部相加


	// 5 字符串---》用双引号或反引号包裹---》反引号可以换行---->等同于python的三引号
    //	var  name = "lqz \n" +"is \n" + "nb"
    //	var  name2 = `彭于晏
    //				is
    //					very
    //  handsome`
    //	fmt.Println(name)
    //	fmt.Println(name2)


	// 6 单引号,表示的类型是什么? 数字类型,int32,这个字符对应的unicode编码的数字,所以单引号只能放一个字符
	//var a ='A'   // unicode编码你对应的数字
	//var a int32='A'   // unicode编码你对应的数字
	//fmt.Println(a)
	//fmt.Printf("a的类型是:%T",a)  // a的类型是int32,它能够表示一个字符

    
	// 7 布尔类型---->if判断中经常用到
	//var b bool = true
	//var b1 bool = false
	//fmt.Println(b)
	//fmt.Println(b1)


	// 8 byte:uint8 的别名    -rune:int32 的别名
	//var a byte ='1'   // 最大长度 1个字节,8个比特位,255种变化,只能表示ascii
	//var b rune ='你'  // 4个字节,表示一个字符,可以放所有的象形文字
	//fmt.Println(a)
	//fmt.Println(b)
}

/*
Java与Go数字类型比较:

java:  byte   short   int    long      float      double
go:    int8   int16   int32  int64      float32    float64
python:int  float 不是基础数据类型,是对象
 */

8 常量和iota

// 常量:不变的量、定义后就不变了
//
package main

func main() {
	// 常量定义格式:
	// 1、const关键字 常量名 类型 = 值
	// 2、const关键字 常量名 = 值
	const name string="cx"
	const age = 19

	// 同时定义多个常量
	const name2, age2 = "cx", 19
	const (
		name3 = "cx"
		age3 = 18
	)
	const (
		a4 = 1
		b4  // 如果不赋值则遵从上面的赋值、相当于b4 = 1
		c4
	)

	// iota:是个自建常量  每换一行都会自增
	const (
		a5 = iota  // 等同于 a5=0
		b5
		c5
	)
	const (
		a6 = iota
		b6 = 100
		c6
		d6
		e6 = iota
	)  // 0 100 100 100 4

// 注意:
//同时定义多个常量,如果下一行只写常量名,遵从上一行的赋值方式
// iota每换一行,就会自增1,无论用还是不用
}

9 函数基础

// 函数基础

package main

import "fmt"

/*
注意:函数定义后可以不使用
1 定义函数基础语法:
	func关键字 函数名(形参1 形参1类型,形参2 形参2类型)(返回值类型){
		函数体内容(与缩进无关、括号内都是函数体内容)
	}
*/

// 2 简单的函数定义、无参数无返回值
func test() {
	fmt.Println("我是test")
}

// 3 定义有参数、无返回值函数、参数类型相同
func test2(x int,y int)  {
	fmt.Println(x)
}

// 4 定义有参数、无返回值、参数类型相同
func test3(a, b int, c,d string)  {
	fmt.Println(a,b,c,d)
}

// 5 定义有2参数、有1个返回值 -- return
func test4(a,b int) int {
	c := a+b
	return c

}

// 6 定义2个参数、2个返回值
func test5(a, b int) (int,int) {
	c := a+b
	d := a*b
	return c,d
}

// 7 命名返回值,函数体不用重新定义
func test6(a, b int) (add int, mul int) {
	add = a+b
	mul = a*b
	return  // return可以直接省略add、mul
}

func main() {
	// 在main调用函数
	fmt.Println("s5开始执行了")
	test()
	test2(10, 20)  // 只有位置传参、没有关键字传参、没有默认参数(有几个参数就传几个)
	test3(10,20,"cx","ll")
	// 接收1个返回值
	var a = test4(5,6)
	b := test4(1,2)
	fmt.Println(a,b)
	// 接收两个返回值
	a2,b2 := test5(6,6)
	fmt.Println(a2,b2)
	// 有多个返回值只接收1个、如果用变量接收但不实用、会报错
	// _接收,不使用也不会报错
	a3,_:=test5(4,5)
	fmt.Println(a3)

	// 命名返回值的使用
	add,mul := test6(1,2)
	fmt.Println(add,mul)
}

10 函数高级

// 函数高级

package main

import "fmt"

// 注意:Go以包作为最小单位、所以函数名不能和同个包的函数重名
// 1 可变长参数
// 可接收任意长度int类型的参数
func test7(a ...int) {
	fmt.Println(a)  // 切片
}

// 2 匿名函数:在main里

/*
3 函数也是一种类型--是一等公民/头等函数  go、python是  Java不是
	指:可以把函数当变量使用

4 函数是变量 就可以当参数传递、当返回值返回

5 闭包函数:必须符合两个条件:
	1、定义在函数内部
	2、对外部作用域有引用
	go有闭包函数、可以做出装饰器来 但是没有语法糖语法
*/

func test8() func() {
	name := "cx"
	a:=func(){
		fmt.Println("内层函数")
		fmt.Println(name)
	}
	return a

}

// 6 尽量要看懂:函数是变量 就可以当参数传递、当返回值返回
// 函数是一种类型、函数的参数和返回值都是类型的一部分
func test9(aa int) func(a int,b func())(int,func()){

	return func(a int,b func())(int,func()) {
		fmt.Println("我是内层函数")
		return a, func() {}
	}
}

// 7 给类型重命名
type MyFunc func(a int,b func())(int,func())

func test10() MyFunc {
	return func(a int,b func())(int,func()) {
		fmt.Println("我是内层函数")
		return a, func() {}
	}
}


func main() {
	// 与python
	test7(1,2,3,4,5)

	// 2 定义在函数内部的函数,必须是匿名函数
	// 2.1 无参数
	func (){
		fmt.Println("我是匿名函数")
	}()  // 要执行否则报错
	// 2.2 有参数
	func (a,b int){
		fmt.Println("我是匿名函数")
	}(1,2)
	// 2.3 有参数 有返回值
	var a,b = func (a,b int)(int,int){
		fmt.Println("我是匿名函数")
		return a,b
	}(1,2)
	fmt.Println(a,b)

	// 3 函数是一等公民 --> 可以把函数赋值给一个变量
	var f = func (){
		fmt.Println("我是匿名函数")
	}
	fmt.Println(f)  // 内存地址
	f()  // 调用
	fmt.Printf("type:%T", f)

	// 4、5闭包
	var f2 func()=test8()
	f2()

	// 调用test9
	// 方式一:
	var f3 func(a int,b func())(int,func()) = test9(11)
	f3(1,f2)
	// 方式二:
	var f4=test9(11)
	f4(1,f2)
	// 方式三:
	f5:=test9(11)  // 简略声明会方便很多、最常用
	f5(1,f2)
}


注意:每一篇文章的内容都比较多,请慢慢消化,不要急于求成