当你发现自己的才华撑不起野心时,就请安静下来学习吧。

Personal site

↑点击传送

GoLang基础

GoLang介绍

#1 Go 即Golang,是Google公司2009年11月正式对外公开的一门编程语言
#2  解释型,编译型
Go是静态(编译型)强类型语言,是区别于解析型语言的弱类型语言(静态:类型固定 强类型:不同类型不允许直接运算)。
python动态强类型语言
#3 哪些是编译,哪些是解释
编译:java,c,c++,c#,go
解析型:python,js,php...
编译型语言涉及到跨平台,因为它需要编译成该平台的可执行文件,java--》运行在jvm之上
go:跨平台编译,交叉编译,在windows平台编译出mac上可执行
解释型:不存在跨平台问题,有解释器

#4 特性
跨平台的编译型语言,交叉编译
管道(channel),切片(slice),并发(routine)
有垃圾回收的机制
支持面向对象和面向过程的编程模式(go的面向对象没有类的概念)

# 5 发展(go是用c写起来的)
2009年11月7日 weekly.2009-11-06 —— 早期的版本
2015年8月19日 go1.5 —— 实现的架构变化,同时保留了和旧版本的兼容性,本次更新中移除了”最后残余的C代码”。# 从此以后,自举,自己写自己
2018年8月24日 go 1.11  :modules,包管理

2020 年 8 月 go 1.15

# Go语言应用
中国的互联网公司,多多少少都会用,有些没用的,都在准备用
##### docker  k8s   蓝鲸 云计算  百度  小米:falcon
##### 七牛云 
## 应用领域,go适合做什么
服务的开发,微服务开发,运维相关,区块链,云平台
第一款开源区块链产品是用go写的

# Go语言发展前景,为什么火

很新,生态不完善
完美契合当下高并发的互联网生态
语法简单,速度快
云计算和区块链的火,互联网企业高并发的需求

开发环境搭建

#1  ide,集成开发环境(goland等同于pycharm)
	-goland(jetbrains全家桶),vscode
    -推荐用goland,pycharm,idea,androidstudio
    一路下一步
#2 开发环境 sdk
一路下一步
# 测试安装成功
go version  把版本打印出来就装成功了

# 3注意事项(重点)
	-goland创建项目,选择go的安装路径(默认选中了)
    -gopath:所有的go代码必须放在这个路径下的src文件夹下,否则无法执行,默认创建到用户家目录下的go文件夹(mac,windows,linux)
   	-创建项目路径,go文件都不要出现中文
 
# 3 go命令

# 必须记住的
go env  # go的环境变量
	-GO111MODULE=空的,现在没有使用MODULE模式
    -GOPATH=C:\Users\oldboy\go #代码存放路径
    -GOROOT=c:\go # go sdk安装路径
go build  # 编译型语言,需要先编译再执行,编译成可执行文件,执行可执行文件
go run  # 编译并执行,开发阶段用,两步并作一步

# 其他不太需要记得
go get # 下载并安装包和依赖等同于pip install
go version
go fmt #运行gofmt进行格式化(go fmt :自动将代码格式)

Hello World


//go语言的注释
//单行注释
/*
多行注释
多行注释
 */


// 重点
//go(所有编译型语言)项目要运行,必须有一个入口
//go的入口是main包下的main函数

// main包下可不可以有多个main函数:不可以

package main   //声明包名,包名是main,每一个go文件都属于某个包

import "fmt"    //导入包,内置包

func main() {   //定义了一个main函数,大括号包裹是函数体的内容
	fmt.Println("hello world") //打印函数等同与print()
}



// 编译
go build s1.go
// 执行
s1.exe

// 编译并执行
go run s1.go
// 在goland中,右键,运行即可

变量

package main

import "fmt"

// 变量
package main

func main() {
	//1 定义变量的第一种方式:全定义
	//var关键字 变量名 变量类型 = 变量值
	var age int =10   //在go中,变量定义了就必须使用,如果不使用就报错
	fmt.Println(age)

	//2 定义变量的第二种方式:类型推导(类型不需要写了)
	var age =10
	age=100
	var name ="lqz"
	fmt.Println(age)  //打印并换行
	fmt.Print(name)   //打印不换行
	fmt.Printf("%T\n",age)  //查看变量类型
	fmt.Printf("%T",name)
	fmt.Printf("%p",&name)

	//3 定义变量的第三种方式:简略声明(类型和var关键字都不写)这种用的多,冒号和等号是一家
	a:=10
	var a int =100  //重复定义,报错
	var a =99    //重复定义,报错
	a := 99        //重复定义,报错
	a=99
	fmt.Println(a)

	//4 其他变形方式
	//4.1  只定义不赋值
	var age int   //定义变量	//如果是只定义,不赋值,只能用这种
	var age      //只定义,不赋值,不行

	//4.2 声明多个变量
	var width, height int = 100, 50 // 声明多个变量
	var width,height =100,50      // 声明多个变量
	var width,height =100,"lqz"      // 声明多个变量
	width,height :=100,";qz"           // 声明多个变量

	//4.3 声明多个变量,并赋初值
	var (
		name   = "naveen"
		age  int  = 29
		height int
	)
	fmt.Println(name,age,height)


	//4.4 小坑
	var a int =10
	//var b =99
	b,a:=99,100    //这个不报错,我们觉得是重复定义,冒号左侧,只要有一个没有定义过的变量,就可以
	fmt.Println(b)
	fmt.Print(a)



	/*
		总结:
			1 变量类型在定义阶段就确定了,一旦确定,不允许改变
			2 变量不可以重复定义
			3 变量要先定义再使用
	*/
}

变量定义规范

/*
变量命名规范
	-变量命令建议用驼峰,(大小写有特殊意义)
	-go文件命名建议用 下划线
    - 一个名字必须以一个字母(Unicode字母)或下划线开头,后面可以跟任意数量的字母、数字或下划线
    -大写字母和小写字母是不同的:Name和name是两个不同的变量
    -关键字和保留字都不建议用作变量名
 */

/*
Go语言中关键字有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

go语言中有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
 */

类型

	/*
		基础数据类型
		数字:
			有符号整形
				-int:在32位机器是int32,在64位机器是int64
				-int8:表示整数范围是:8个比特位,8个bit是1byte ,负数和0, 2的7次方-1 的范围
				-int16 2的15次方减一
				-int32
				-int64
			无符号整型
				-uint8   2的8次方减一  定义一个人的年龄
				-uint16
				-uint32
				-uint64

			浮点型(小数),表示小数点后长度多少位
				-float32
				-float64
			复数
				-complex64
				-complex128
			byte:是int8的别名  单引号包裹
			rune:是int32的别名  单引号包裹
		字符串
			双引号包裹
			反引号包裹  ` `


		布尔
			bool true 和 false

		数据类型默认值:
			数字类型是0
			字符串类型是 空字符串
			布尔类型   false

	*/

/*
byte:int8
short :int16
int:int32
long:int64
float:float32
double:float64
*/

常量

package main

//常量
func main() {
	//常量的定义,第一种
	//const  变量名  变量类型 = 变量值
	//const  age int8 = 99
	////修改就报错
	//age=199
	//fmt.Println(age)

	//第二种方式类型推导
	//const age = 99
	//age=88
	//fmt.Println(age)

	//其他定义方式
	//const name,age = "zhangsan",99
	//const (
	//	name string ="lqz"
	//	age =99
	//)
	//const (
	//	s1  =iota
	//	s2 =iota
	//	s3
	//	s4 =99
	//	s5 =iota
	//)
	//fmt.Println(s1)
	//fmt.Println(s2)
	//fmt.Println(s3)
	//fmt.Println(s4)
	//fmt.Println(s5)




}
//const 关键字定义,不允许改变

函数基础

package main

import "fmt"

//给类型命别名
type MyFunc func(a,b int)(int,string)

type Myint  int

//函数
func main() {

	var a int =10
	var b Myint=9
	fmt.Println(a+int(b))


	//定义方式
	/*
		func 名字(参数名 类型,参数名 类型)(返回值类型,返回值类型){
			函数体内容
			return 返回值1,返回值2
		}
	*/
	//1 调用函数
	//add(2,3,"xxx")
	//var a int =add(2,3)
	//a := add(2, 3)
	//fmt.Println(a)
	//多返回值就需要用多变量接收
	//a,b:=add(3,4)
	//fmt.Println(a,b)
	//多返回值,忽略一个返回值
	//a,_:=add(3,4)
	//fmt.Println(a)
	//fmt.Println(_)


	//匿名函数(定义在函数内部的函数,不能是有名函数),头等函数
	//var a func()
	//a = func (){
	//	fmt.Println("我是匿名函数")
	//}
	//a()
	//函数返回值是函数
	//a:=test()
	//fmt.Println(a)  // 函数内存地址
	//a()
	//8 函数返回值为函数,返回的函数带参数
	//a:=test()
	//a("xxx")

	//9 函数返回值为函数,返回的函数带参数,带返回值
	//var a func(a,b int)int
	//a=test()
	//fmt.Println(a(2,3))

	//10 函数参数为函数类型,返回值为带参数,带返回值的函数类型
	//a,b:=test(func() {
	//	fmt.Println("我是函数参数")
	//})(3,4)
	//f:= func() {
	//	fmt.Println("我是函数参数")
	//}
	//
	//f1:=test(f)
	//a,b:=f1(3,4)
	//fmt.Println(a,b)

	//闭包函数的使用
	//a:=test(19)
	////a是闭包函数
	//a()

	//装饰器是闭包函数的典型应用(go中没有装饰器的语法糖),通过闭包实现装饰器

	//类型重命名
	//var a MyFunc =test()
	//c,d:=a(1,2)
	//fmt.Println(c,d)


}

//1 有参数无返回值(定义函数)
//func add(a int,b int)  {
//	fmt.Println(a+b)
//}

//2 有参数无返回值,有多个相同类型参数
//func add(a ,b int)  {
//	fmt.Println(a+b)
//}
//3 有参数无返回值,有多个相同类型参数,也有不同类型
//func add(a ,b int,msg string)  {
//	fmt.Println(a+b)
//	fmt.Print(msg)
//}

//4 多个参数,一个返回值
//func add(a, b int) int {
//	return a + b
//}


//4 多个参数,多个返回值
//func add(a, b int) (int,int) {
//	return a + b,a*b
//}

//5 命名返回值
//func add(a, b int) (c int,d int) {
//	c=a+b
//	d=a*b
//	//return时,不需要再写c,d了
//	return
//}


//6  函数是一等公民,函数可以赋值给变量

//7 函数返回值为函数
//func test() func() {
//	return func() {
//		fmt.Println("我是返回函数")
//	}
//}

//8 函数返回值为函数,返回的函数带参数
// 类型只要有不一样的地方,就不是一个类型
//func test() func(msg string) {
//	return func(msg string) {
//		fmt.Println(msg)
//	}
//}

//9 函数返回值为函数,返回的函数带参数,带返回值
//func test() func(a,b int) int{
//	return func(a,b int) int {
//		return a+b
//	}
//}

//10 函数参数为函数类型,返回值为带参数,带返回值的函数类型
//func test(f func()) func(a,b int) (int,int){
//	return func(a,b int) (int,int) {
//		f()
//		return a+b,a*b
//	}
//}

//11 闭包函数:1 定义在函数内部  2对外部作用域有引用
// 闭包函数就是多了一种函数传参的方法,包进去了
//func test(age int) func()  {
//	a:= func() {
//		fmt.Println(age)
//	}
//	return a
//}

//12
func test()MyFunc  {
	return func(a,b int)(int,string) {
		fmt.Println("xxx")
		return 10,"ok"
	}

}

变量作用域范围

package main

import "fmt"

//在同一个包下,函数名不能重名
//var a int   //全局变量,全局有效,只要改了,就是改了
//func main() {
//	fmt.Println(a)  //0
//	a=19
//	fmt.Println(a) //19
//	test1()  //99
//	fmt.Println(a) //99
//}
//
//func test1()  {
//	a=99
//	fmt.Println(a)
//
//}


//变量的作用域范围

var a int   //全局变量,全局有效,只要改了,就是改了
func main() {
	var a int
	fmt.Println(a)  //0
	a=19
	fmt.Println(a) //19
	test1()  // 0
	fmt.Println(a) //19
}

func test1()  {
	fmt.Println(a)

}

包管理

#1  包:模块的意思
#2 自定义包
	-go语言的代码必须放在gopath的src路径下
    -包导入是从gopath的src路径下开始检索(开始找)
    -除了main包以外,建议包名就叫文件夹名,一个文件夹下的包名必须一致
    -同一个包下,变量,函数只能定义一次
    -同一个包下的变量和函数可以直接使用
    -包内的函数或变量,想让外部包使用,必须首字母大写
    -以后下的第三方包都是放在gopath的src路径下
# 3 init函数(特殊函数)
	-不需要调用就会执行
    -可以定义多个
# 4 包导入的几种方式
	-import "day02/mypackage"
    -给包重命名
    	-import 名字 "day02/mypackage"
        名字.变量/函数
    -包只导入,不使用
    import _ "day02/mypackage"
 
# 5 go语言没有一个统一包管理的地址,大家都放到github上

# 6 采用go mode模式
	-两种创建方式之一
    	-命令行下输入:go mod init 项目名   在当前路径下创建出go.mod(该项目依赖go的版本,第三方包版本)
        -项目路径的cmd窗口,go get 第三方包,就会在go.mod中加入依赖
        -以后把项目copy给别人,go install
        -自己写的包,就放在自己项目路径下
        -加代理的方式:手动写,goland中配置
   	-在goland中创建项目时,直接指定modules,可以配置环境变量(加代理)

if-else语句

package main

import "fmt"

//if-else
func main() {
	//1 if else
	a := test()
	//if a>10{
	//	fmt.Println("a大于10")
	//}else {
	//	fmt.Println("a小于10")
	//}

	//2 if --else if--- else
	if a > 10 {
		fmt.Println("dayu 10")
	} else if a == 10 {
		fmt.Println("10101010")
	} else {
		fmt.Println("xiaoyu 10")
	}
	//3 条件语句后不能回车换行
	//if a > 10 {
	//	fmt.Println("dayu 10")
	//} else if a == 10 {
	//	fmt.Println("10101010")
	//} else {
	//	fmt.Println("xiaoyu 10")
	//}

}

func test() int {

	return 100
}

循环

1 没有while循环,没有do while循环,只有一个for循环

package main

//for 循环
/* for 后面三部分,都可以省略
for 变量初始化;条件;变量自增/自减 {
	循环体内容
}

 */
func main() {
	//1 基本使用
	//for i:=0;i<10;i++{
	//	fmt.Println(i)
	//}

	//2 省略第一部分
	//i:=0   //作用域范围大,不止在for内部,外部也可以用
	//for ;i<10;i++{
	//	fmt.Println(i)
	//}
	//2 省略第三部分
	//i:=0   //作用域范围大,不止在for内部,外部也可以用
	//for ;i<10;{
	//	fmt.Println(i)
	//	i++
	//}

	//3 省略一和三部分的简略写法(这就是while循环)  for 条件{ 循环体内容}
	//i:=0
	//for i<10{
	//	fmt.Println(i)
	//	i++
	//}

	//4 死循环

	//for {
	//	fmt.Println("ssssss")
	//}

	//5 只是演示开多协程
	//for i:=0;i<4000;i++{
	//	go test2()
	//}

	//6 break :结束本次for循环,continue结束本次循环,继续下一次循环
}

//func test2()  {
//	for  {
//		fmt.Println("sssss")
//	}
//
//}

switch语句

// switch 是一个条件语句,用于将表达式的值与可能匹配的选项列表进行比较,并根据匹配情况执行相应的代码块。它可以被认为是替代多个 if else 子句的常用方式


package main

func main() {
	//1 基本使用
	//num:=4
	//switch num {
	//case 1:
	//	fmt.Println("1")
	//case 2:
	//	fmt.Println("2")
	//case 3:
	//	fmt.Println("3")
	//case 4:
	//	fmt.Println("4")
	//}

	//2 默认情况
	//num:=40
	//switch num {
	//case 1:
	//	fmt.Println("1")
	//case 2:
	//	fmt.Println("2")
	//case 3:
	//	fmt.Println("3")
	//case 4:
	//	fmt.Println("4")
	//default:
	//	fmt.Println("我没有匹配")
	//}

	//3 多表达式判断
	//num:=40
	//switch num {
	//case 1,2,3,4,5,6,7,8:
	//	fmt.Println("1")
	//case 10,11,16:
	//	fmt.Println("2")
	//case 30:
	//	fmt.Println("3")
	//case 40,44,45:
	//	fmt.Println("4")
	//default:
	//	fmt.Println("我没有匹配")
	//}

	//4 无表达式的 switch
	//num:=80
	//switch  {
	//case num==12,num==13:
	//	fmt.Println("12,13")
	//case num==40,num==41:
	//	fmt.Println("40,41")
	//default:
	//	fmt.Println("我没有匹配")
	//}


	//5 Fallthrough
	//num:=12
	//switch  {
	//case num==12,num==13:
	//	fmt.Println("12,13")
	//	fallthrough
	//case num==40,num==41:
	//	fmt.Println("40,41")
	//	//fallthrough  //穿透,只要看到fallthrough,无条件执行下一个case或者default
	//default:
	//	fmt.Println("我没有匹配")
	//}
}


goto:饱受诟病  java 保留字,没有实际作用

数组

package main

import (
	"fmt"
)

//数组:数组是同一类型元素的集合。可以放多个值,但是类型一致,内存中连续存储
// Go 语言中不允许混合不同类型的元素
func main() {

	//1 数组的定义,数组的大小,在定义阶段就确定了,而且不能改
	//定义了一个大小为3的string类型数组,里面可以放3个字符串
	//var names [3]string
	//var ages [3]int8
	//fmt.Println(ages)

	//2 数组赋值
	//var ages [3]int8
	//ages[0]=99
	//ages[2]=88
	//fmt.Println(ages)
	//fmt.Println(ages[1])

	//3 定义并初始化,
	//var ages [3]int=[3]int{1,2,3}
	//var ages [3]int=[3]int{1,2}
	//var ages [3]int=[3]int{}
	//var ages=[3]int{}
	//ages:=[3]int{1,3,4,7}  //不允许多放
	//fmt.Println(ages)

	//4 数组定义并初始化的其他(了解)数组只要定义,长度就固定了,。。。,后面放几个值,数组大小是多少
	//var ages [9]int=[...]int{1,2,3,4,5,6,7,8}   //不支持这个
	//var ages =[...]int{1,2,3,4,5,6,7,8}
	//ages :=[...]int{1,2,3,4,8}
	//fmt.Println(len(ages))

	//5 数组的大小是类型的一部分
	//var a [2]int=[2]int{1,2}
	//var b [2]int=[2]int{1,3}
	//b=a   //如果不是同一种类型,不允许相互赋值
	//fmt.Println(b)

	//6 数组是值类型
	//var a [2]int=[2]int{1,2}
	//fmt.Println(a)
	//test5(a)  //因为数组是值类型,go函数传参,都是copy传递,如果是值类型,函数内改了,不会影响原来的
	//fmt.Println(a)

	//7 数组长度  len()  数组长度在定义阶段已经固定
	//var a [2]int=[2]int{1,2}
	//fmt.Println(len(a))

	//8 数组循环
	//var a =[...]int{7,4,3,5,6,7}
	//fmt.Println(a[99])
	//fmt.Println(len(a))
	//普通循环
	//for i:=0;i<len(a);i++{
	//	fmt.Println(a[i])
	//}

	//通过range来循环  (range不是内置函数,是一个关键字,for,if,else),打印出索引
	//for i:=range a{
	//	fmt.Println(i)
	//}

	//如果用一个变量来接收,这个值是可迭代的索引
	//如果用两个变量来接收,这两个变量,一个是索引,一个具体的值
	//for i,value:=range a{
	//	fmt.Println(i)
	//	fmt.Println(value)
	//}
	//把数组循环打印出来
	//for _,value:=range a{
	//	fmt.Println(value)
	//}

	// 9 多维数组
	//var a [3][3]int  //定义
	//a[0][1]=99      //使用
	//fmt.Println(a)

	//定义并赋初值
	//var a [3][3]int=[3][3]int{{1},{1,2,3},{4,4,4}}
	//var s =[3][3]string{{"lqz","xxx","yyy"},{},{}}
	//fmt.Println(s)
	//
	////循环多维数组
	//for _,value:=range s{
	//	for _,in_value:=range value{
	//		fmt.Println(in_value)
	//	}
	//}

	//10 数组定义并指定位置初始化
	//var names [100]int=[100]int{10:99,99:99}
	//var names [100]int=[100]int{10,11,2,44,99:99,45:88}
	//fmt.Println(names)



}

func test5(a [2]int)  {
	a[0]=99
	fmt.Println(a)

}

切片基础

package main

import "fmt"

//切片:切片是由数组建立的一种方便、灵活且功能强大的包装(Wrapper)。切片本身不拥有任何数据。它们只是对现有数组的引用

func main() {
	//1 切片定义的第一种方式
	//定义一个数组
	//var a =[10]int{9,8,7,6,5,4,3,2,1,0}
	////基于数组,做一个切片
	////[]int 中括号中不带东西,就是切片类型
	//var b []int
	//b=a[:]
	//fmt.Println(b)
	//fmt.Printf("%T",b)
	//fmt.Println()
	//fmt.Printf("%T",a)
	//fmt.Println()
	//
	////2 使用切片
	//fmt.Println(b[0])
	//fmt.Println(b[1])
	//
	////3 修改切片,会影响数组
	//b[0]=999
	//fmt.Println(b)
	////数组会被修改
	//fmt.Println(a)
	//
	////4 修改数组,是否会影响切片?会
	//a[3]=333
	//fmt.Println(a)  //数组
	//fmt.Println(b) //切片

	// 5 切片只切数组一部分
	//var a =[10]int{9,8,7,6,5,4,3,2,1,0}
	//var b []int=a[3:5]
	//fmt.Println(b)
	////修改切片
	//b[0]=999
	//fmt.Println(b)
	//fmt.Println(a)
	//a[4]=888
	//fmt.Println(b)
	//fmt.Println(a)

	//6 当多个切片共用相同的底层数组时,每个切片所做的更改将反映在数组中
	//var a =[10]int{9,8,7,6,5,4,3,2,1,0}
	//var b []int=a[3:5]
	//var c []int=a[4:6]
	//fmt.Println(a)
	//fmt.Println(b)
	//fmt.Println(c)
	//b[1]=555
	//fmt.Println(a)
	//fmt.Println(b)
	//fmt.Println(c)

	//7 切片的长度和容量
	var a =[10]int{9,8,7,6,5,4,3,2,1,0}
	//var b []int=a[3:5]
	var b []int=a[7:8]
	fmt.Println(a)
	fmt.Println(b)
	// 切片长度
	fmt.Println(len(b))
	// 切片容量(我最多能存多少值)
	fmt.Println(cap(b))

	//8 切片追加值
	b=append(b,3)
	b=append(b,999)
	//到了临界点再追加
	b=append(b,888)
	fmt.Println(b)
	fmt.Println(a)

	fmt.Println(len(b))// 长度是4
	fmt.Println(cap(b))  //容量是6
	b[0]=111
	fmt.Println(b)
	fmt.Println(a)


}

切片

package main

func main() {
	//1 定义切片 (对底层数组的引用),基于数组切出切片
	//var a [10]int =[10]int{9,8,7,6,5,4,3,2,1}
	//b:=a[6:9]
	////fmt.Println(b)
	////fmt.Println(len(b))
	////fmt.Println(cap(b))
	////2 切片和数组是相互影响的
	////b[0]=99
	////fmt.Println(b)
	////fmt.Println(a)
	//
	////3 切片追加值 append
	//b=append(b,99)
	//fmt.Println(b)
	//fmt.Println(len(b))
	//fmt.Println(cap(b))
	//fmt.Println(a)
	////切片到容量了,再追加,会在原有容量的基础上翻倍
	//b=append(b,88)
	//fmt.Println(b)
	//fmt.Println(len(b))
	//fmt.Println(cap(b))
	//fmt.Println(a)
	////把切片第0个位置改成111
	//b[0]=111
	//fmt.Println(b)
	//fmt.Println(a)
	//
	////继续追加
	//b=append(b,77,66,55)
	//fmt.Println(b)
	//fmt.Println(len(b))
	//fmt.Println(cap(b))
	//
	//b=append(b,44)
	//fmt.Println(b)
	//fmt.Println(len(b))
	//fmt.Println(cap(b))


	//2 通过make创建切片(后期引用类型初始化都是用make)
	//var a []int    //定义没有初始化,它空的---》nil      所有引用类型空值都是nil
	//fmt.Println(a)
	//fmt.Println(a==nil)
	//初始化,从数组切
	//通过make创建,初始化,初始化以后就不是nil
	//a=make([]int,3,4)  //第一个参数是类型,第二个参数是切片长度,第三个参数是切片容量
	//fmt.Println(a)
	//fmt.Println(len(a))
	//fmt.Println(cap(a))
	//a=make([]int,0,4)  //底层创建了一个大小为4的数组,然后让a指向了它
	//fmt.Println(a)
	//fmt.Println(a==nil)
	//a=append(a,3)
	//fmt.Println(a)

	//3 append的其他用法
	//a:=make([]int,3,4)
	//var b[]int=[]int{3,4,5}  //切片初始化
	//fmt.Println(b)
	//fmt.Println(len(b))
	//fmt.Println(cap(b))
	////a=append(a,2,3,4)
	//a=append(a,b...)  //打算了,传过去
	//fmt.Println(a)

	//切片的函数传递(切片是引用类型),函数传递都是copy传递  {指针:地址,长度:3,容量:3}
	//函数中修改会影响原来的(不要使用append操作,如果要使用,确认好不要超容量,否则再修改,就不会影响原来的了)
	//var b[]int=[]int{3,4,5}
	//fmt.Println(b)
	//test(b)
	//fmt.Println(b)
	//fmt.Println(len(b))
	//fmt.Println(cap(b))

	//var b[]int=make([]int,2,4)
	//fmt.Println(b)
	//test(b)
	//fmt.Println(b)
	//fmt.Println(len(b))
	//fmt.Println(cap(b))


	//4 多维切片
	//var a [][]int=make([][]int,3,4)
	//fmt.Println(a)
	//fmt.Println(a[0]==nil)
	//a[0]=make([]int,0,4)
	////fmt.Println(a[0][0])
	//a[1]=make([]int,0,3)
	//fmt.Println(a)
	//var b[]int=[]int{1,2,3}
	//a[2]=b
	////fmt.Println(a)
	////fmt.Println(a[2][2])
	//for _,v:=range a{
	//	for _,v1:=range v{
	//		fmt.Println(v1)
	//	}
	//}
	//var a[][]string=[][]string{{"xx","yy"},{},{"44"}}
	//a的长度是3,容量是3
	//fmt.Println(len(a))
	//fmt.Println(cap(a))
	//fmt.Println(a)
	//fmt.Println(a[0])
	//fmt.Println(len(a[0]))
	//fmt.Println(cap(a[0]))

	//fmt.Println(a[1])
	//fmt.Println(len(a[1]))
	//fmt.Println(cap(a[1]))

	//var c []string=make([]string,0,0)
	//var d []string
	//fmt.Println(len(c))
	//fmt.Println(cap(c))
	//
	//fmt.Println(len(d))
	//fmt.Println(cap(d))

	//4 copy内置函数,把一个切片copy给另一个切片
	//var b []int=[]int{3,4,5,67,7,8,88}
	//var c []int=make([]int,2,8)
	////把b copy给c
	////fmt.Println(c)
	////a:=copy(c,b)
	////fmt.Println(c)
	////fmt.Println(a)
	////fmt.Println(c[2])  //报错,拿不到长度以外的值
	//copy(c,b)
	//fmt.Println(c[2])  //报错,下标只能取到长度,不能取到容量
}

//func test(b []int)  {
//	fmt.Println(b)
//	b[0]=999
//	b=append(b,88)
//	b[1]=888
//	fmt.Println(len(b))
//	fmt.Println(cap(b))
//	fmt.Println(b)
//}

可变长参数

package main

import "fmt"

//函数可变长参数
func main() {
	//fmt.Println(1,2,3,4,6,"xxxx")
	//test1(1,2,3,4,5,6,7,78)

	//可不可以把切片直接传过去
	//var a []int = []int{1, 2, 3}
	//test1(a...)  //把切片a打散了传过来

	//fmt.Println(find(4,1,2,3,4,5,6,7,8))
	//b:=[]int{1,2,3,4,5,6,7,8}
	//fmt.Println(find(44,b...))

	//welcome := []string{"hello", "world"}
	//change(welcome...)
	//fmt.Println(welcome)

	welcome := []string{"hello", "world"}
	change(welcome...)
	fmt.Println(welcome)
}

//func test1(a ...int) {
//	//a是什么?切片
//	//fmt.Println(a)
//	//fmt.Printf("%T",a)
//	//fmt.Println(len(a))
//	//fmt.Println(cap(a))
//	fmt.Println(a[0])
//}

//func find(a int,b ...int)bool  {
//	flag:=false
//	for _,v:=range b{
//		if a==v{
//			flag=true
//		}
//	}
//	return flag
//}
//
//func change(s ...string) {
//	s[0] = "Go"
//}


func change(s ...string) {
	//s[0] = "Go"
	s = append(s, "playground")
	s[0] = "Go"
	fmt.Println(s)
}

map

package main

import (
	"fmt"
)

//map:map 是在 Go 中将值(value)与键(key)关联的内置类型。通过相应的键可以获取到值,value值的类型是一样的
func main() {
	//1 定义一个map
	//var m map[int]string    //定义了一个map类型,key是int类型,value是string类型
	//fmt.Println(m==nil)
	//m[99]="xxx"  //报错,因为没有初始化,是nil类型
	//fmt.Println(m)

	//m=make(map[int]string)
	//fmt.Println(m==nil)
	//2 map的零值是nil,它是一个引用类型,如果当作参数传递,修改会影响原来的

	//3 使用map,放值
	//m[1]="lqz"
	//m[2]="egon"
	//m[3]="zhangsan"
	//fmt.Println(m)
	//fmt.Println(m[1])


	//4 研究(了解)
	//m:=make(map[int]string)  //影响初始map的长度
	//m[1]="lqz"
	//m[2]="egon"
	//m[3]="zhangsan"
	//fmt.Println(m)

	//5 取值
	//定义并初始化,放点值
	//a:=map[string]string{"name":"lqz","age":"18","sex":"男"}
	//a:=map[string]int{"name":1}
	//a:=map[string][]int{"name":[]int{1,2,3}}
	//fmt.Println(a["name"])
	//fmt.Println(a["name"][0])
	//取一个不存在值,不会报错,会取到value值的空值
	//fmt.Println(a["name1"]==nil)
	//fmt.Println(a["name1"])

	//如何判断一个key是否再字典中
	//value,flag:=a["name"]
	//fmt.Println(flag)  //true
	//fmt.Println(value)  //lqz

	//value,flag:=a["name1"]
	//fmt.Println(flag)  //false
	//fmt.Println(value)  //value值的空值

	//3 循环map,无序(python 3.6以后,字典有序?为什么有序?底层原理如何实现?)
	//a:=map[string]string{"name":"lqz","age":"18","sex":"男"}
	////for key,value:=range a{
	//for _,value:=range a{
	//	//fmt.Println(key)
	//	fmt.Println(value)
	//}

	//4 删除元素
	//a:=map[string]string{"name":"lqz","age":"18","sex":"男"}
	////内置函数
	//delete(a,"sex")
	//delete(a,"sss")  //没有不会报错
	//fmt.Println(a)

	//5 长度
	//a:=map[string]string{"name":"lqz","age":"18","sex":"男"}
	//fmt.Println(len(a))
	//fmt.Println(cap(a))  //不让你看,看源码

	//6 map是引用类型
	//a:=map[string]string{"name":"lqz","age":"18","sex":"男"}
	//a:=make(map[string]string,3)
	//a["name"]="lqz"
	//a["age"]="15"
	//a["sex"]="男"
	//test5(a)
	//fmt.Println(a)

	//7 Map 的相等性,map不支持等号比较,只支持等号和nil比较
	//a:=map[string]string{"name":"lqz"}
	////b:=map[string]string{"name":"lqz"}
	////fmt.Println(a==b)
	//fmt.Println(a==nil)

	//long a =10L   //对象
	//long b=10L    //对象
	////if(a==b){
	//if(a.equals(b)){
	//	System.out.println("pass")
	//}else{
	//	System.out.println("xxx")
	//}

	//map的value值是map
	//var a map[string]map[string]string= make(map[string]map[string]string)
	//fmt.Println(a==nil)
	////第二层的map没有初始化
	////a["name"]["name"]="lqz"
	//a["name"]= make(map[string]string)
	//a["name"]["name"]="lqz"
	//fmt.Println(a)


}

//再go中实现map有序,定义一个切片,每放要给,把key往切片追加一个
func test5(a map[string]string)  {
	a["name"]="egon"

	a["xxx"]="xxx"
	a["xxx1"]="xxx"
	a["xxx2"]="xxx"
	a["xxx3"]="xxx"
	a["xxx4"]="xxx"
	a["xxx5"]="xxx"
	a["xxx6"]="xxx"
	a["xxx7"]="xxx"
	a["xxx8"]="xxx"
	a["xxx9"]="xxx"
	a["xxx10"]="xxx"
	fmt.Println(a)

}

指针

package main

import "fmt"

//指针:指针是一种存储变量内存地址的变量。
// & 取地址符号,把它加在变量前,表示取该变量的地址
// * 放在类型前,表示指向这个类型的指针
// * 放在变量前,表示解引用,取出指针指向真正的值
func main() {
	//a:=10
	//s:="xxxx"
	////把a的地址取出来,赋值给一个变量,那这个变量就是指针
	//var b *int //定义了一个指向int类型的指针
	//b=&a
	//var b1 *string
	//b1=&s   //类型不一致  定义一个指向string类型的指针,b和b1不是同一个类型
	//
	//
	//fmt.Println(b)
	//fmt.Println(b1)


	// 2 定义一个指向int类型指针的指针
	//var sss **int
	//sss=&b
	//var ssss ***int
	//ssss=&sss
	//fmt.Println(sss)
	//fmt.Println(ssss)

	//3 通过地址,拿到实际值
	//a:=10
	//var b *int //定义了一个指向int类型的指针
	//b=&a
	//fmt.Println(b)
	////根据b,把a的值打印出来
	//fmt.Println(*b)  //解引用

	//a:=10
	//b:=&a
	//c:=&b
	//var d ***int
	//d=&c
	//fmt.Println(d)
	//fmt.Println(*d)
	//fmt.Println(**d)
	//fmt.Println(***d)
	//fmt.Println(***d)

	//3 指针的零值(nil),引用类型
	//var b *int
	//fmt.Println(b)

	//4 向函数传递指针参数,函数传参都是copy传递
	//a:=10
	//b:=&a
	//test7(b)
	//fmt.Println(a)

	//5 不要向函数传递数组的指针,而应该使用切片
	//var a [7]int
	//fmt.Println(a)
	////test8(&a)
	//test9(a[:])
	//fmt.Println(a)

	//6 Go 不支持指针运算
	//var a=10
	//var b=&a
	//b++

	//c语言为什么要支持指针运算


	//7 数组指针和指针数组
	//数组指针:指向数组的指针
	//指针数组:数组内放一个个指针
	//数组指针
	//var a *[5]int=&[5]int{1,2,4}
	//fmt.Println(a)
	//
	////指针数组
	//x,y,z:=33,444,55
	//var b [5]*int=[5]*int{&x,&y,&z}
	//fmt.Println(b)

}
func test8(a *[5]int)  {
	//解引用以后再使用
	//(*a)[0]=99
	a[0]=99  //这样使用也可以,语言层面处理了,不需要解引用再使用,直接使用就可以
	fmt.Println(a)  //打印出来,显示效果不是内存地址,&[99 0 0 0 0]
}

func test9(a []int)  {
	a[0]=99
	fmt.Println(a)
}
//func test7(b *int)  {
//	*b=99
//}

字符串

package main

import (
	"fmt"
)

//字符串:Go 语言中的字符串是一个字节切片
func main() {
	//1 循环打印字符串
	//s:="hello world中国"
	//fmt.Println(len(s))  //字节长度
	//fmt.Println(utf8.RuneCountInString(s))
	////printByte(s)  //循环字节打印出来
	//printZF(s)     //循环字符打印出来

	//2字符串是不可变的 不允许改值

	//3 字符串跟数字转换
	// 字符串转数字(可能会转错)
	//var s="10ff"
	//var a int
	//a,err:=strconv.Atoi(s)
	//fmt.Println(err)
	//fmt.Println(a)

	// 数字转字符串
	//var a int=99
	////s:=string(a)  // 99对的assii码转过来
	//s:=strconv.Itoa(a)
	//fmt.Println(s)
	//s := "hello world中国"
	//printZF(s)


	runeSlice := []rune{22269,22269}
	byteSlice := []byte{97,98}
	str := string(runeSlice)
	str2 := string(byteSlice)
	fmt.Println(str)
	fmt.Println(str2)
}
func printByte(s string) {
	for i := 0; i < len(s); i++ {
		fmt.Println(s[i])   //s[i] 是byte  int8
		fmt.Println(string(s[i]))
	}
}

func printZF(s string) {
	for _, v := range s {
		fmt.Println(v)
		fmt.Printf("%T", v)  //rune  int32
		fmt.Println(string(v))

	}
}

结构体基础

package main

import "fmt"

//面向对象:结构体:一系列属性的集合
//面向对象三大特性:继承封装多态   但是没有类的概念

//1 定义一个结构体
//type 结构体名字 struct{属性...}
type Person struct {
	name string
	age int
	sex string
}


func main() {
	//1 定义结构体变量
	//var per Person
	////2 结构体零值(值类型),没有赋值,所以是结构体内部属性的零值
	//fmt.Println(per)
	//3 使用结构体(把名字改成lqz)
	//per.name="lqz"
	//per.age=18
	//per.sex="男"
	//fmt.Println(per)
	//fmt.Println(per.name)

	//4 定义结构体变量并赋初值
	var per Person
	//指名道姓的赋值,只给部分
	//per=Person{name:"lqz",age:18,sex:"男"}
	//per=Person{name:"lqz",sex:"男"}
	per=Person{"lqz",18,"男"}
	//per=Person{"lqz",18} //按位置传,必须传全了
	fmt.Println(per)
	fmt.Println(per.name)

}

结构体

package main

import "fmt"

//结构体:一系列属性的集合
//定义结构体(在包中定义了)


//7 定义一个结构体,内涵匿名字段(字段没有名字)  匿名字段类型就是字段名,所有类型不能重复
//type Person struct {
//	string
//	int
//	sex string
//}


//8 嵌套结构体(结构体中套结构体)

//type Person struct {
//	Name string
//	Age int
//	sex string
//	Hobby Hobby
//}
//type Hobby struct {
//	HobbyId int
//	HobbyName string
//}

//9 字段提升
//type Person struct {
//	Name string
//	Age int
//	sex string
//	Hobby
//}
//type Hobby struct {
//	HobbyId int
//	HobbyName string
//}

//type Person struct {
//	Name string
//	Age int
//	sex string
//	Hobby
//}
//type Hobby struct {
//	Id int
//	Name string
//}


//11 结构体相等性

type Person struct {
	Name string
	Age int
	sex string
	//包含不可比较的字段
	AAA []int
}

func main() {
	//1 结构体的使用  值类型
	//var per entity.Person
	//fmt.Println(per)
	//per.Name="lqz"
	//per.Age=19
	//fmt.Println(per)

	//2 定义并赋初值
	//var per entity.Person=entity.Person{Name:"lqz"}   //不按位置,少传
	//var per2 entity.Person=entity.Person{"lqz",19,"男"}   //按位置,全穿
	//fmt.Println(per2)
	//per2.Age=20
	//fmt.Println(per2)

	//3 匿名结构体(定义在内部(函数,结构体),只使用一次,没有名字)
	// 有什么用?当定义多个变量(想一次使用),就可以把这多个变量放到匿名结构体中
	//a := struct {
	//	HobbyId   int64
	//	HobbyName string
	//}{HobbyId: 1, HobbyName: "篮球"}
	//
	//fmt.Println(a.HobbyName)
	//a.HobbyName="足球"
	//fmt.Println(a.HobbyName)

	//4 结构体的零值,值类型
	//var per entity.Person
	//fmt.Println(per)  //属性的零值,值类型,参数传递,copy传递,在函数中修改,不会影响原来的
	//test2(per)
	//fmt.Println(per)

	//5 访问结构体字段 ,通过 . 来访问  注意大小写

	//6 结构体的指针
	//var per *entity.Person
	//fmt.Println(per)

	//定义并初始化
	//var per *entity.Person=&entity.Person{}
	//fmt.Println(per)
	////把per的名字改成lqz
	//(*per).Name="lqz"
	////支持直接使用(数组也是这样,自动帮你处理了)
	//per.Name="egon"
	//fmt.Println(per)


	//7 匿名字段(字段没有名字,只有类型)
	//【变量提升/提升字段】面向对象的继承
	//per:=Person{"lqz",19,"男"}
	//per:=Person{string:"lqz",int:19,sex:"男"}  //字段匿名,类型就是字段名
	//fmt.Println(per)
	//fmt.Println(per.string)
	//fmt.Println(per.int)

	//8 嵌套结构体(结构体中套结构体)
	//var per Person =Person{"lqz",19,"男",Hobby{1,"篮球"}}
	//var per Person =Person{Name: "lqz",Age: 19,sex: "男"}
	//var per Person =Person{Name: "lqz",Age: 19,sex: "男",Hobby:Hobby{1,"足球"} }
	//var per Person =Person{Name: "lqz",Age: 19,sex: "男",Hobby:Hobby{HobbyId: 1,HobbyName: "足球"}}
	//fmt.Println(per.Name)
	//fmt.Println(per.Hobby.HobbyName)

	//9 字段提升
	//var per Person =Person{"lqz",19,"男",Hobby{1,"篮球"}}
	//var per Person =Person{Name: "lqz",Age: 19,sex: "男",Hobby:Hobby{HobbyId: 1,HobbyName: "足球"}}
	////打印爱好的名字(Hobby是一个匿名字段,会字段提升)
	//fmt.Println(per.HobbyName)
	//fmt.Println(per.Hobby.HobbyName)  //per.Hobby 类似于面向对象中的super()
	////像什么?像面向对象的继承  子类继承父类(结构体嵌套,匿名字段),子类可以直接调用父类中的属性或方法

	//var per Person =Person{Name: "lqz",Age: 19,sex: "男",Hobby:Hobby{ 1, "足球"}}
	//fmt.Println(per.Name)  //优先使用自己的
	////打印出hobby的名字
	//fmt.Println(per.Hobby.Name)

	//10 导出结构体和字段 大写字母开头,在外部包可以使用

	//11 结构体相等性
	//结构体是值类型。
	//如果它的每一个字段都是可比较的,则该结构体也是可比较的。 如果两个结构体变量的对应字段相等,则这两个变量也是相等的
	//如果结构体包含不可比较的字段,则结构体变量也不可比较。

	//值类型,可以直接==比较,引用类型只能跟nil用==比较
	//per1:=Person{Name: "lqz"}
	//per2:=Person{Name: "lqz",Age:19}
	//fmt.Println(per1==per2)

	//per1:=Person{Name: "lqz"}
	//per2:=Person{Name: "lqz",Age:19}
	//fmt.Println(per1==per2) //包含不可比较的属性
	fmt.Println("lqz nb")

}
//func test2(per entity.Person)  {
//	per.Age=99
//	fmt.Println(per)
//}

方法

package main

//方法:特殊函数,在函数的基础上加了一些东西
//在 func 这个关键字和方法名中间加入了一个特殊的接收器类型,接收器可以是结构体类型,也可以是非结构体类型
//type Person2 struct {
//	Name string
//	Age int
//	Sex string
//}
//
////def add(self):
////	self.name
////	pass
////定义一个方法  : (p Person2) 绑定给了Person2结构体的对象
//func (p Person2) printName()  {
//	//在方法内可以使用p
//	fmt.Println(p.Name)
//}
////修改名字
//func (p Person2) changeName(name string)  {
//	p.Name=name
//	fmt.Println(p)
//}
////修改年龄方法
//func (p *Person2) changeAge(age int)  {
//	//fmt.Println(p.Age)  //推荐用这个
//	p.Age=age
//}
//
//func printName(p Person2)  {
//	fmt.Println(p.Name)
//}


// 匿名字段的方法
//type Person2 struct {
//	Name string
//	Age int
//	Sex string
//	Hobby  //匿名字段
//}
//type Hobby struct {
//	Id int
//	Name string
//}
//
////给结构体绑定方法
//func (p Person2)printName()  {
//	fmt.Println(p.Name)
//}
////func (h Hobby)printHobbyName()  {
//func (h Hobby)printName()  {
//	fmt.Println(h.Name)
//}



/// //6 在方法中使用值接收器 与 在函数中使用值参数
//type Person2 struct {
//	Name string
//	Age int
//	Sex string
//}
////在方法中使用值接收器
//func (p Person2)printName()  {
//	fmt.Println(p.Name)
//}
//func (p Person2)changeName(name string)  {
//	p.Name=name
//	fmt.Println(p)
//}
////在函数中使用值参数
//func printName(p Person2)  {
//	fmt.Println(p.Name)
//}


//7 在方法中使用指针接收器 与 在函数中使用指针参数
//type Person2 struct {
//	Name string
//	Age int
//	Sex string
//}
////在方法中使用值接收器
//func (p *Person2)printName()  {
//	fmt.Println(p.Name)
//}
//func (p *Person2)changeName(name string)  {
//	p.Name=name
//	fmt.Println(p)
//}
////在函数中使用指针参数
//func printName(p *Person2)  {
//	//fmt.Println((*p).Name)
//	fmt.Println(p.Name)
//}



//8 非结构体上的方法(不允许)自己定义的类型可以绑定方法
//在int类型上绑定一个add方法
//不允许
//func (i int)add(){
//	i=i+1
//	i++
//	//i+=1
//}

// 可以在自定义的类型上绑定方法
type Myint  int
func (i *Myint)add(){
	(*i)=(*i)+1
	//i++
	//i+=1
}
func main() {

	//1 方法的定义和使用
	//per:=Person2{}
	//per.Name="lqz"
	//per.printName()  //绑定给对象的方法
	//
	//per1:=Person2{Name: "egon"}
	//per1.printName()

	//2 为什么我们已经有函数了还需要方法呢?
	//per1:=Person2{Name: "egon"}
	////per1.printName()  //方法的特殊之处,可以自动传值
	////
	////printName(per1)
	//per1.changeName("lqz")
	//
	////并没有改
	//fmt.Println(per1)

	//3 指针接收器与值接收器
	//per1:=Person2{Name: "egon",Age: 18}
	//fmt.Println(per1)
	//per1.changeAge(99)
	//fmt.Println(per1)

	//4 时候使用指针接收器,什么时候使用值接收器:想改原来的,就用指针,不想改原来的就用值(指针用的多)

	//5 匿名字段的方法(方法提升)

	//per1:=Person2{Name: "lqz",Hobby:Hobby{1,"足球"}}
	////per1.printHobbyName()  //Hobby是个匿名字段,方法也提升了
	////如果方法名冲了,优先用该结构体自己的
	//per1.printName()
	//per1.Hobby.printName()

	//6 在方法中使用值接收器 与 在函数中使用值参数
	//per1:=Person2{Name: "lqz"}
	//per1.printName()
	//printName(per1)

	//per1:=&Person2{Name: "lqz"}  //per1是个指针
	//per1.printName()
	//printName(*per1)

	//小研究
	//per1:=&Person2{Name: "lqz"}  //per1是个指针
	//per1.changeName("egon")
	//fmt.Println(per1)

	//值收器:可以用值来调,也可以用指针来调
	//函数的值参数,只能传值

	//7 在方法中使用指针收器 与 在函数中使用指针参数

	//per1:=Person2{Name: "lqz"}
	//per1.printName()  //值可以来调用
	//printName(&per1)

	//per1:=&Person2{Name: "lqz"}
	//per1.printName()  //指针可以来调用
	//printName(per1)

	//小研究
	//per1:=Person2{Name: "lqz"}
	//per1.changeName("egon")
	//fmt.Println(per1)

	//per1:=&Person2{Name: "lqz"}
	//per1.changeName("egon")
	//fmt.Println(per1)

	//总结:不管是值类型接收器还是指针类型接收器,都可以用值来调用,或者指针来调用
	//总结:不管是值还是指针来调用,只要是值类型接收器,改的就是新的,只要是指针类型接收器,改的是原来的
	//a:=1
	//a+=1
	//a++
	//a=a+1
	//++a  //java中有++a
	//fmt.Println(a)

	//8 非结构体上绑定方法
	//var a Myint =10
	//fmt.Println(a)
	//a.add()
	//a.add()
	//a.add()
	//a.add()
	//fmt.Println(a)
	//



	//var b =11
	////fmt.Println(a+b)  //类型不匹配
	//
	//c:=a+Myint(b)
	//fmt.Println(a+Myint(b))  //类型匹配
	//d:=int(a)+b
	//fmt.Println(int(a)+b)  //类型匹配


}

接口

package main

//go也是鸭子类型:我现在有个鸭子类,内有speak方法   有run方法,  子类只要实现了speak和run,我就认为子类是鸭子
//在java中,子类必须实现鸭子类的所有方法,子类才叫鸭子
//接口:面向对象的领域里,接口一般这样定义:接口定义一个对象的行为,规范子类对象的行为
//接口:是一系列方法的集合(规范行为)

//1 定义接口(定义一个鸭子接口,speak方法,run方法)
//type Duck interface {
//	speak()  //speak()方法
//	run()
//}
//
////定义一个普通鸭子结构体
//type PDuck struct {
//	name string
//	sex string
//	age int
//}
//
////定义一个唐老鸭结构体
//type TDuck struct {
//	name string
//	sex string
//	age int
//	wife string
//}
////让唐老鸭和普通鸭子都实现Duck接口
////结构体实现一个接口:只要绑定接口中的所有方法,就叫实现该接口
//func (p PDuck)speak()  {
//	fmt.Println("普通鸭子嘎嘎叫,普通鸭子名字叫",p.name)
//}
//func (p PDuck)run()  {
//	fmt.Println("普通鸭子歪歪扭扭走了,普通鸭子名字叫",p.name)
//}
//
////唐老鸭也实现Duck接口
//func (p TDuck)speak()  {
//	fmt.Println("唐老鸭说人话,唐老鸭子名字叫",p.name)
//}
//func (p TDuck)run()  {
//	fmt.Println("唐老鸭人走路,唐老鸭子名字叫",p.name)
//}
//
//
////6 空接口定义
//type Empty interface {
//}

func main() {
	//1 得到一个普通鸭子对象
	//pduck:=PDuck{"黑子","男",1}
	//pduck.run()
	//pduck.speak()
	////2 得到一个堂老鸭子对象
	//tduck:=TDuck{"egon","男",1,"刘亦菲"}
	//tduck.run()
	//tduck.speak()

	//侵入式接口(接口没了,子类报错)和非侵入是接口(接口没了,不影响代码,go语言中的接口是非侵入式的)

	//2 接口的实际用途(接口也是一个类型)
	//var duck Duck
	////pduck:=PDuck{"黑子","男",1}
	//tduck:=TDuck{"egon","男",1,"刘亦菲"}
	////duck=pduck
	//duck=tduck  //多态,同一类事务多种形态
	//duck.run()

	//3 接口内部表示
	//我们可以把接口看作内部的一个元组 (type, value)。
	//type 是接口底层的具体类型(Concrete Type),而 value 是具体类型的值。

	//4 把接口类型转成struct,属性,自有方法也有了,类型断言
	//类型断言
	//var duck Duck =TDuck{"egon","男",1,"刘亦菲"}
	////断言是TDuck类型
	////v, ok := duck.(TDuck)
	//////断言成功,ok是true,v就是TDuck结构体对象
	////fmt.Println(v)
	////fmt.Println(v.name)
	////fmt.Println(ok)
	//
	////断言失败
	//var v PDuck
	//var ok bool
	//v, ok = duck.(PDuck)
	////断言失败,ok是false,v是PDuck类型的空置,因为没有复制
	//fmt.Println(ok)
	//fmt.Println(v)

	//5 类型选择(通过switch)
	//var duck Duck =TDuck{"egon","男",1,"刘亦菲"}
	////var duck Duck =PDuck{"egon","男",1}
	//test4(duck)

	//6 空接口(没有任何方法,所有数据类型都实现了空接口)
	//var a int=10
	//var b string="lqz"
	//var c [3]int
	//var e Empty  //空接口类型
	//e=a
	//e=b
	//e=c
	//fmt.Println(e)
	//fmt.Println(1,"xxx")
	//test5(a)
	//test5(b)
	//test5(c)

	//7 匿名空接口 没有名字的空接口  一般用在形参上
	//test6(10)
	//test6("lll")
	//var duck TDuck =TDuck{"egon","男",1,"刘亦菲"}
	//test6(duck)

	//8 之前学过的集合类型,都可以放接口类型
	//var a[3]Duck
	//a[1]=PDuck{}
	//a[2]=TDuck{}
	//var a map[string]interface{}= make(map[string]interface{})
	//a["name"]="lqz"
	//a["age"]=19
	//a["duck"]=PDuck{}


}
//func test6(b interface{})  {
//	fmt.Println(b)
//}



//func test5(b Empty)  {
//	switch v:=b.(type) {
//	case string:
//		fmt.Println("我是字符串")
//		fmt.Println(v)
//	case int:
//		fmt.Println("我是int")
//		fmt.Println(v)
//	case [3]int:
//		fmt.Println("我是数组")
//		fmt.Println(v)
//	}
//}



//func test4(duck Duck)  {
//	if v,ok:=duck.(TDuck);ok{
//		fmt.Println("我是普通鸭子")
//		fmt.Println(v)
//	}else if v,ok:=duck.(PDuck);ok {
//		fmt.Println("我是普通鸭子")
//		fmt.Println(v)
//	}
//}

//使用switch,选择成功,拿到结构体对象
//func test4(duck Duck)  {
//	switch v:=duck.(type) {
//	case PDuck:
//		fmt.Println(v.name)
//		fmt.Println("我是普通鸭子")
//	case TDuck:
//		fmt.Println(v.wife)
//		fmt.Println("我是唐老鸭")
//	default:
//		fmt.Println(v)
//		fmt.Println("我是鸭子这个类")
//
//	}
//}

自定义集合类型

package main

import "fmt"

//定义MySet类型
type MySet map[interface{}]bool
//判断元素是否存在
func (m MySet) isExist(a interface{}) bool {
	return m[a]
}
//返回set长度
func (m MySet) len() int {
	return len(m)
}
//设置值
func (m MySet) set(a interface{}) {
	m[a] = true
}
//删除值
func (m MySet) delete(a interface{}) {
	delete(m, a)
}
//测试代码
func main() {
	//创建一个set
	var a MySet = make(MySet)
	//相当于
	//var a MySet = make(map[interface{}]bool)
	//打印set的长度
	//fmt.Println(a.len())
	//放入一个值
	a.set(1)
	//放入一个相同值
	a.set(1)
	a.set("lqz")
	a.set("lqz")
	a.set("lqz")
	a.set("lqz")
	a.set("lqz")
	a.set("lqz")
	//打印长度,还是1
	//fmt.Println(a.len())
	//判断1是否存在
	//fmt.Println(a.isExist(2))
	////删除1
	a.delete(1)
	////判断1是否存在
	fmt.Println(a.isExist(1))
	fmt.Println(a.len())

	for i,_:=range a{
		fmt.Println(i)
	}
}



make和new的区别

package main

//make和new的区别
type PDuck1 struct {
	name string
	sex string
	age int
}
func main() {
	//make是引用类型初始化的时候用的
	//var per *PDuck1 =new(PDuck1)    //new 是返回指向这个类型的指针
	//fmt.Println(per)
	//
	//
	//var per1 =&PDuck1{}
	//fmt.Println(per1)

	//var per2 = make([]int,3,4)  //make是具体的造引用类型  //new是造指向这个类型的指针
	//var per2 *[]int= new([]int)
	//fmt.Println(per2)
	//(*per2)=append((*per2),99)
	//fmt.Println(per2)


}



结构体取代类

package main

import (
	person "day04/Person"
	"fmt"
)

func main() {
	per :=person.New("lqz",19,"男")

	//var per Person = new Person("lqz",19,"男")
	fmt.Println(per)
	per.PrintName()
}

并发和并行

1 并发:同一时间段内,多个任务在执行(单个cpu,执行多个任务)
2 并行:同一时刻,多个任务在执行(多个cpu的支持)

goroutine

package main

import (
	"fmt"
	"runtime"
	"time"
)

//goroutine--->协程---2kb大小,100
//线程----》几个m
//go协程会复用线程

// goroutine之间通信,通过 信道channel 通信
//go推崇用信道通信,而不推崇用共享变量通信(锁,死锁)


//启动一个goroutine

func test()  {
	fmt.Println("go go go")
}
//func main() {
//	fmt.Println("主线程开始执行")
//	go test()
//	go test()
//	go test()
//	go test()
//	go test()
//	go test()
//	go test()
//	go test()
//	time.Sleep(1*time.Second)
//	fmt.Println("主线程结束执行")
//	//go语言中,主线程不会等待goroutine执行完成,要等待它结束需要自己处理
//}

//func main() {
//	fmt.Println("主线程开始执行")
//	for i:=0;i<10;i++ {
//		go func(){
//			fmt.Println("go go go ")
//
//		}()
//	}
//	time.Sleep(1*time.Second)
//	fmt.Println("主线程结束执行")
//	//go语言中,主线程不会等待goroutine执行完成,要等待它结束需要自己处理
//}


// go 关键字开启goroutine,一个goroutine只占2kb

/*
go语言的GMP模型
	-G:开的goroutine
	-M:M当成操作系统真正的线程,实际上是用户线程(用户线程)
	-P:Processor:现在版本默认情况是cpu核数(可以当做cpu核数)

	用户线程,操作系统线程
	python中,开的线程开出用户线程,用户线程跟操作系统线程1:1的对应关系
	某些语言,用户线程和操作系统线程是n:1的关系
	go语言,用户线程和操作系统线程是 n:m的关系
 */


//举个例子

func main() {
		//设置P的大小,认为是cpu核数即可
		runtime.GOMAXPROCS(1)
		fmt.Println("主线程开始执行")
		go func() {
			for  {
				fmt.Println("xxxx")

			}
		}()
		//for i:=0;i<10;i++ {
		//	go func(){
		//		for  {
		//			fmt.Println("我是死循环")
		//
		//		}
		//
		//	}()
		//}
		time.Sleep(10*time.Second)
		fmt.Println("主线程结束执行")
}

信道(通道)

package main

import (
	"fmt"
	"time"
)

//不同goroutine之间通信
//通过channel实现

func main() {
	//1 定义channel
	var c chan int
	//2 信道的零值(引用类型,空值为nil,当做参数传递时,不需要取地址,改的就是原来的,需要初始化再使用)
	fmt.Println(c)
	//3 信道初始化
	c=make(chan int)  //数字暂时先不关注
	//4 信道的放值  (注意赋值和放值)
	//c<-1
	//c=12  //赋值报错
	//5 信道取值
	//<-c
	//6 取出来赋值给一个变量 int
	//var a int
	//a=<-c
	//a:=<-c

	//7 信道默认不管放值还是取值,都是阻塞的

	//c是引用类型
	go test1(c)

	a:=<-c  //阻塞   不但实现了两条协程之间通信,还实现了等待协程执行结束
	fmt.Println(a)

}

func test1(a chan int)  {
	fmt.Println("go go go ")
	time.Sleep(1*time.Second)
	//往信道中放一个值
	a<-10 //阻塞


}


package main

//信道小例子
//程序有一个数中 每一位的平方和与立方和,然后把平方和与立方和相加并打印出来

import (
	"fmt"
	"time"
)

func calcSquares(number int, squareop chan int) {
	sum := 0  //总和
	for number != 0 {
		digit := number % 10   //589对10取余数,9   8   5
		sum += digit * digit  //sum=9*9   8*8     5*5
		number /= 10         //num=58    5       0
	}
	time.Sleep(2*time.Second)
	squareop <- sum
}

func calcCubes(number int, cubeop chan int) {
	sum := 0
	for number != 0 {
		digit := number % 10
		sum += digit * digit * digit
		number /= 10
	}
	time.Sleep(1*time.Second)
	cubeop <- sum
}

//func calcSquares(number int, squareop chan int) int{
//	sum := 0  //总和
//	for number != 0 {
//		digit := number % 10   //589对10取余数,9   8   5
//		sum += digit * digit  //sum=9*9   8*8     5*5
//		number /= 10         //num=58    5       0
//	}
//	time.Sleep(2*time.Second)
//	return sum
//}
//
//func calcCubes(number int, cubeop chan int) int{
//	sum := 0
//	for number != 0 {
//		digit := number % 10
//		sum += digit * digit * digit
//		number /= 10
//	}
//	time.Sleep(2*time.Second)
//	return sum
//}

func main() {
	ctime:=time.Now().Unix()
	fmt.Println(ctime)
	number := 589
	sqrch := make(chan int)
	cubech := make(chan int)
	//num1:=calcSquares(number, sqrch)
	//num2:=calcCubes(number, cubech)
	go calcSquares(number, sqrch)
	go calcCubes(number, cubech)
	squares, cubes := <-sqrch, <-cubech
	//squares:= <-sqrch
	//cubes:=<-cubech
	fmt.Println("Final output", squares + cubes)
	ttime:=time.Now().Unix()
	fmt.Println(ttime)
	fmt.Println(ttime-ctime)
}
package main

import "fmt"

//信道的死锁现象,默认都是阻塞的,一旦有一个放,没有人取  或者一个人取,没有人放,就会出现死锁

//func main() {
//	//var c chan int =make(chan int )
//
//	//c<-1  //其实放不进去,阻塞在这,就死锁了
//	//<-c     //没有,取不到,阻塞在这,就死锁了
//
//}


//单向信道
//func sendData(sendch chan<- int) {
//	sendch <- 10
//}
//
//func main() {
//	//sendch := make(chan<- int)   //定义了一个只写信道
//	sendch := make(chan int)   //定义了一个可读可写信道
//	go sendData(sendch)        //传到函数中转成只写信道,在goroutine中,只负责写,不能往外读,主协程读
//	fmt.Println(<-sendch)   //只写信道一旦读,就有问题
//}



///3 关闭信道
//func main() {
//	sendch := make(chan int)
//	//关闭信道
//	//close(sendch)
//
//	//信道可以用for循环循环
//}


// 信道关闭close(sendch) ,for循环循环信道,如果不关闭会报死锁,如果关闭了,放不进去,循环结束
func producer(chnl chan int) {
	for i := 0; i < 100; i++ {
		fmt.Println("放入了",i)
		chnl <- i
	}
	close(chnl)
}
func main() {
	ch := make(chan int)
	go producer(ch)
	for v := range ch {
		fmt.Println("Received ",v)
	}
}

缓冲信道

package main

import (
	"fmt"
	"sync"
	"time"
)

//缓冲信道:只在缓冲已满的情况,才会阻塞向缓冲信道,只有在缓冲为空的时候,才会阻塞从缓冲信道接收数据

//func main() {
//	var c chan int =make(chan int,6)  //无缓冲信道数字是0
//	c<-1
//	c<-2
//	c<-3
//	c<-4
//	c<-5
//	c<-6
//	//c<-7  //死锁
//	<-c
//	<-c
//	//<-c
//	//<-c
//	//<-c
//	//<-c
//	//<-c // 取空了,死锁(一个goroutine中会出现)
//
//
//	// 2 长度 vs 容量
//	//fmt.Println(len(c))  //目前放了多少
//	//fmt.Println(cap(c)) //可以最多放多少
//
//
//	
//}
//3 WaitGroup  等待所有goroutine执行完成
//4  使用信道如何实现?
func process1(i int,wg *sync.WaitGroup)  {
	fmt.Println("started Goroutine ", i)
	time.Sleep(2 * time.Second)
	fmt.Printf("Goroutine %d ended\n", i)
	//一旦有一个完成,减一
	wg.Done()
}

func main() {
	var wg sync.WaitGroup   //没有初始化,值类型,当做参数传递,需要取地址
	//fmt.Println(wg)
	for i:=0;i<10;i++ {
		wg.Add(1) //启动一个goroutine,add加1
		go process1(i,&wg)
	}

	wg.Wait() // 一直阻塞在这,知道调用了10个done,计数器减到零
}

Select

package main

import (
	"fmt"
	"time"
)

//func server1(ch chan string) {
//	time.Sleep(6 * time.Second)
//	ch <- "from server1"
//}
//
//
//func server2(ch chan string) {
//	time.Sleep(3 * time.Second)
//	ch <- "from server2"
//
//}
//
//func main() {
//	output1 := make(chan string)
//	output2 := make(chan string)
//	//开启两个协程执行server
//	go server2(output1)
//	go server2(output2)
//	select {
//	case s1 := <-output1:
//		fmt.Println(s1,"ddddd")
//	case s2 := <-output2:
//		fmt.Println(s2,"yyyy")
//	}
//}

//func process(ch chan string) {
//	time.Sleep(10500 * time.Millisecond)
//	ch <- "process successful"
//}
//
//func main() {
//	ch := make(chan string)
//	go process(ch)
//	for {
//		time.Sleep(1000 * time.Millisecond)
//		select {
//		case v := <-ch:
//			fmt.Println("received value: ", v)
//			return
//		default:
//			// 可以干其他事,模拟非阻塞式io
//			fmt.Println("no value received")
//		}
//	}
//
//}

//死锁
//func main() {
//	ch := make(chan string)
//	select {
//	case <-ch:
//	}
//}

// 随机选取
func server1(ch chan string) {
	ch <- "from server1"
}
func server2(ch chan string) {
	ch <- "from server2"

}
func main() {
	output1 := make(chan string)
	output2 := make(chan string)
	go server1(output1)
	go server2(output2)
	time.Sleep(1 * time.Second)
	select {
	case s1 := <-output1:
		fmt.Println(s1)
	case s2 := <-output2:
		fmt.Println(s2)
	}
}

mutex

package main

import (
	"fmt"
	"sync"
)

// 使用锁的场景:多个goroutine通过共享内存在实现数据通信
// 临界区:当程序并发地运行时,多个 [Go 协程]同时修改共享资源的代码。这些修改共享资源的代码称为临界区。

//如果在任意时刻只允许一个 Go 协程访问临界区,那么就可以避免竞态条件。而使用 Mutex 可以达到这个目的


//var x  = 0  //全局,各个goroutine都可以拿到并且操作
//func increment(wg *sync.WaitGroup,m *sync.Mutex) {
//	m.Lock()
//	x = x + 1
//	m.Unlock()
//	wg.Done()
//}
//func main() {
//	var w sync.WaitGroup
//	var m sync.Mutex  //是个值类型,函数传递需要传地址
//	fmt.Println(m)
//	for i := 0; i < 1000; i++ {
//		w.Add(1)
//		go increment(&w,&m)
//	}
//	w.Wait()
//	fmt.Println("final value of x", x)
//}

// 通过信道来做
var x  = 0
func increment(wg *sync.WaitGroup, ch chan bool) {
	ch <- true  // 缓冲信道放满了,就会阻塞
	x = x + 1
	<- ch
	wg.Done()
}
func main() {
	var w sync.WaitGroup
	ch := make(chan bool, 1)  //定义了一个有缓存大小为1的信道
	for i := 0; i < 1000; i++ {
		w.Add(1)
		go increment(&w, ch)
	}
	w.Wait()
	fmt.Println("final value of x", x)
}


// 不同goroutine之间传递数据:共享变量,  通过信道
// 如果是修改共享变量,建议加锁
//如果是协程之间通信,用信道

异常处理

package main

import (
	"fmt"
	"os"
)

// 异常处理
//defer:延迟执行,并且即便程序出现严重错误,也会执行
//panic:主动抛出异常 raise
//recover:恢复程序,继续执行
//
//func main() {
//
//	defer fmt.Println("我最后执行")  //注册一下,并不执行,等main函数执行完了以后,从下往上执行defer定义的东西
//	defer fmt.Println("我倒数第二个打印")
//	fmt.Println("我先执行")
//	//var a []int
//	//fmt.Println(a[10])
//	panic("我出错了")
//	fmt.Println("ccccc")
//
//	//假设打开一个文件
//	//f:=open()
//	//defer f.close()
//	//
//	//出错了
//	//
//
//}


//
//func f1(){
//	fmt.Println("f1 f1")
//}
//func f2(){
//	defer func() {   //这个匿名函数永远会执行
//		//error:=recover()  //恢复程序继续执行
//		//fmt.Println(error) //如果没有错误,执行recover会返回nil    如果有错误,,执行recover会放错误信息
//		if error:=recover();error!=nil{
//			//表示出错了,打印一下错误信息,程序恢复了,继续执行
//			fmt.Println(error)
//		}
//		// 相当于finally
//		fmt.Println("我永远会执行,不管是否出错")
//	}()
//	fmt.Println("f2 f2")
//	//panic("主动抛出错误")
//}
//func f3(){
//	fmt.Println("f3 f3")
//}
//
//func main() {
//	//捕获异常,处理异常,让程序继续运行
//
//	f1()
//
//	f2()
//	f3()
//}

/*
try:
	可能会错误的代码
except Exception as e:
	print(e)
finally:
	无论是否出错,都会执行
 */
/*现在这么写
defer func() {
	if error:=recover();error!=nil{
		//except的东西
		fmt.Println(error)
	}
	//相当于finally,无论是否出错,都会执行
}()
可能会错误的代码
 */


// go的错误处理
func main() {
	f, err := os.Open("/test.txt")
	if err != nil {
		fmt.Println(err)
		return
	}

	if err != nil {
		fmt.Println(err)
		return
	}


	fmt.Println(f.Name(), "opened successfully")
}
posted @ 2020-11-01 16:27  Joab-0429  阅读(135)  评论(0编辑  收藏  举报