• 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:Usersoldboygo #代码存放路径
        -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
    ",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
    ", 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")
    }
    
  • 相关阅读:
    Oracle序列使用:建立、删除
    struts1.x入门
    SQL的四种连接-左外连接、右外连接、内连接、全连接
    eclipse更改文件编码方式
    使用links方式安装Eclipse插件
    JAVA:Eclipse代码自动提示
    MyEclipse注释配置
    全面理解SQL
    一秒去除Win7快捷方式箭头
    Eclipse快捷键大全(转载)
  • 原文地址:https://www.cnblogs.com/linqiaobao/p/13909945.html
Copyright © 2020-2023  润新知