Go语言学习篇01

Go语言

Golang开山篇

Golang学习方向

Go语言我们可以简单地写成 Golanguage,简写成Golang。

学习语言的目的,Golang能干神马?

  • 区块链研发工程师【货币、金融…】
  • Go服务器端/游戏软件工程师 【美团,游戏…】
  • Golang分布式/云计算软件工程师 【盛大云cnd、京东…】

Golang的应用领域

  • 区块链技术
    • 简称BT(Blockchain technology) ,又称为分布式账本技术,去中心化、公开透明…
  • 美团后台流量支撑程序
    • 支撑后台流量 (排序,推荐,搜索等)
  • 仙侠道
    • 通讯、逻辑、数据存储
  • 云计算/云服务后台应用
    • 京东消息推送云服务
      • 京东后台所有的服务全部用Golang实现的
      • 计算能力强

Golang的核心开发团队

Ken Thompson(肯.汤普森):

​ Golang核心开发人员之一,也是图灵奖获得者(C语言开发者)

​ C语言开创者—>飞行员—>B语言---->Go语言开发者

Rob Pike(罗布.派克)

​ 奥运会射箭银牌---->天文学家---->UTF-8字元编码开创者---->Go语言开发者

Robert Griesemer

​ Hotspot编译器---->Chorme浏览器JavaScript V8引擎—>Go语言开发者

Golang为什么出现

  1. 多核多CPU,CPU利用率极低
  2. 缺乏一个简洁高效的编程语言
    1. 风格不统一
    2. 计算能力不够好
    3. 处理大并发不够好
  3. 运行速度快,编译慢

Golang的特点

  • 静态编译语言的安全和性能
  • 动态语言开发维护的高效率
  • Go语言的一个文件归属于一个包
  • 借鉴C语言,垃圾回收机制,内存自动回收
  • 天然并发
    • 从语言层面支持并发
    • goroutine,轻量级线程,可实现大并发处理,高效利用多核
    • 基于CPS并发模型(Communicating Sequential Process)实现
  • 管道通信机制
    • Go语言特有的管道 channel
    • 通过channel 实现 不同的 goroute 之间的通信
  • Go函数支持返回多个值

在这里插入图片描述

  • 新的创新 :例如 切片 slice 、延时执行 defer 等

Golang开发工具

visual studio Code 【没有提示】

goland 【有提示】

Vim文本编织器

emacs编制神器

Golang开发环境搭建

  1. 安装SDK (Software Development Kit 软件开发工具包
  2. SDK下载
  3. 测试Go SDK安装成功
# 查看SDK版本
D:\Environment\Go\bin>go version
go version go1.15.4 windows/amd64

4.配置环境变量
在这里插入图片描述

在这里插入图片描述

  • GOPATH 项目存放的目录
 D:\Environment\Go\goProject\src\goCode\project01\main 的目录

2020/11/09  15:26    <DIR>          .
2020/11/09  15:26    <DIR>          ..
2020/11/09  15:29                77 hello.go
               1 个文件             77 字节
               2 个目录 815,235,309,568 可用字节

# go build hello.go编译
D:\Environment\Go\goProject\src\goCode\project01\main>go build hello.go

D:\Environment\Go\goProject\src\goCode\project01\main>dir
 驱动器 D 中的卷是 Data
 卷的序列号是 2C37-C14D

 D:\Environment\Go\goProject\src\goCode\project01\main 的目录

2020/11/09  15:30    <DIR>          .
2020/11/09  15:30    <DIR>          ..
2020/11/09  15:30         2,139,648 hello.exe
2020/11/09  15:29                77 hello.go
               2 个文件      2,139,725 字节
               2 个目录 815,233,163,264 可用字节
# 执行

D:\Environment\Go\goProject\src\goCode\project01\main>hello.exe
hello Go!

D:\Environment\Go\goProject\src\goCode\project01\main>
# 方法1:直接运行源码
D:\Environment\Go\goProject\src\goCode\project01\main>go run hello.go
hello Go!

# 方法2:先编译再运行+指定文件名
go build -o first.exe hello.go
first.exe

Golang执行流程分析

在这里插入图片描述

Golang两种执行方式的区别

  • 编译运行

    • 生成Go.exe可执行文件
    • 没有Go环境SDK下可执行
  • 源码运行

    • 需要Go 的软件开发工具包SDK

Golang常用转译字符

\t 一个制表位

\n 一个换行符

\\ 一个\

\" 一个"

\r 一个回车

Golang注释

  • 行注释

    • Ctrl + /
    • //
  • 块注释

    • Ctrl + Shift + /

API官网:aplication program interface:应用程序编程接口,就是我们GO语言的函数

API中文网点击这里

Dos命令

1)文件目录操作

# 查看当前目录
dir

# 切换到C盘
cd \d C:

# 切换到当前盘的其它目录
cd java-windows【相对路径】
cd D:\Environment\java-windows【绝对路径】

# 切换到根目录
CD /

# 新建目录
md 文件夹名
md 文件1 文件2

# 删除空目录
rd 文件夹名

# 删除目录不带询问
# /q 安静模式
# /s 层级式的
rd /q/s 文件夹名

# 删除目录待询问
rd /s 文件夹名

2)文件操作

# 新建abc.txt--->内容是:hello
echo hello1 > d:\Environment\java-windows\abc1.txt 【绝对路径】
echo hello2 > abc2.txt 【相对路径】

# 移动
move abc1.txt d:\Environment\java 【绝对路径】

# 复制
copy abc1.txt d:\Environment\java 【绝对路径】
copy abc1.txt d:\Environment\java\ok.txt 【绝对路径+重命名】

# 删除
del abc.txt
del *.txt 删除所有txt文件

# 查看
type abc.txt

3)常用命令

# 清屏
cls (苍老师)
# 退出dos
exit

Golang变量

package main

import "fmt"

func main() {
    
    
	//Define a variable
	var i int
	// Assignment
	i = 10
	// Using variables
	fmt.Println("i=", i)
}

What’s type about value ?

//Typeof int
var num = 10

//Typeof String
var num = "10"

// var name String name = "tom"
name := "tom"

name := 1
fmt.Println(reflect.TypeOf(name))

int

在这里插入图片描述

// 方式1
var x, y, z int

// 方式2
var x, y, z = 1, "1", 1.01

//方式3
x, y, z := 1, "1", 1.01
package main

import (
	"fmt"
	"reflect"
)

//Define Global variables 【全局变量】
var (
	x = 1
	y = "2"
	z = 1.1
)

func main() {
    
    
	fmt.Println(reflect.TypeOf(z))
	fmt.Println(x,y,z)
}
1.该区域的数值可以在同一类型范围内不断变化
2.变量不可重名
3.默认赋初始值 (小数 0

Golang +号使用

1.数字 + 数字 = 加法运算
2.String + String = 字符串拼接

Golang数据类型篇

Golang整数类型+数据类型查看

有符号整型

  • 第一个位置是正负符号,后面7位才是存值

在这里插入图片描述

无符号整型

  • 所用位置均用来存值,没有负号

在这里插入图片描述

特殊整型

在这里插入图片描述

我是64位操作系统,int是64的

  • 查看数据类型
func main() {
    
    
	fmt.Println(reflect.TypeOf(z))
    
	fmt.Printf("y的类型 %T",z)
    fmt.Println(reflect.TypeOf(z))
}
  • 查看占用的字节数
func main() {
    
    
	fmt.Printf("y的数据类型是 %T y占用的字节数是 %d", y, unsafe.Sizeof(y))
}

Golang小数类型

又叫浮点型,用于存放小数

在这里插入图片描述

单双精度的区别:大小

浮点数都是有符号的
尾数部分造成精度丢失
64位精度大于32位【保存高精度float64】
符号位 + 指数位 + 尾数位
    
    Golang默认定义float64位
    0.52 可以使用 .53表示
    Golang支持科学计数法 5.122e2 102次方
科学计数法 
5.12E-2 --->5.12 / 10^2 = 0.0512
5.12E2  --->5.12 * 10^2 = 512

Golang字符类型

  1. Golang中没有专门的字符,如果要存储单个字符(字母),一般使用byte来保存。

  2. Java字符串就是一串固定长度的字符连接起来的字符序列。Golang的字符串是由单个字节连接起来的

  3. UTF-8 包含 ASCII

  4. Golang使用UTF-8来做的

  5. 字符型存储到计算机中,需要将字符对应的码值找出来

    • 存储:字符—>对应码值—>二进制—>存储
    • 读取:二进制—>码值—>字符—>读取
  1. Go语言的编码统一UTF-8,没有中文乱码问题

Golang布尔类型

1)bool 类型只能是true和false

2)bool 类型占一个字节

3)bool类型适用于逻辑运算

Golang字符串类型

1)Go语言中由单个字节连接的,UTF-8处理的

2)没有中文乱码问题

3)字符串不可变

var a string = "中国"
fmt.Println("b=", b)
fmt.Println("b 占用的字节数:", unsafe.Sizeof(b))

b= 长城
b 占用的字节数: 16

4)字符串的两种使用形式

  • “” 双引号,遇特殊字符 \进行转译
  • `` 瓢符号,直接打印文本
b := `"中国"\b\n\n\n\n\\n\n\\`
fmt.Println("b=", b)

//输出结果
b= "中国"\b\n\n\n\n\\n\n\\

5)字符串拼接

在这里插入图片描述

6)Default value

在这里插入图片描述

Gloang基本数据类型转换

Go语言不同变量之间赋值时需要显示转换,也就是说Golang中不能自动转换

基本语法 T(v)

var i int32 = 100
//将 int32 转成 float32 类型
var n = float32(i)
//将 int32 转成 int64 类型
var m = int64(i)

#被转换的是i的数值,但i变量类型没有变

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C2cfUb7h-1605951880056)(Go语在这里插入图片描述

在这里插入图片描述

int8 -128~127范围

Golang基本数据类型和string 的转换

基本数据类型转 string

  • 方法1:func Sprintf("%参数",表达式)
func Sprintf(format string, a ...interface{}) string

Sprintf根据format参数生成格式化的字符串并返回该字符串。

var num1 =99
var num2 float32 = 23.456
var b = false
var myChar byte = 'y'
var str string

//使用第一种方式来转换 fmt.Sprintf方法
str = fmt.Sprintf("%d", num1)
//输出结构
fmt.Printf("str type %T str=%q\n", str, str)

str = fmt.Sprintf("%f", num2)
fmt.Printf("str type %T str=%q\n", str, str)

str = fmt.Sprintf("%t", b)
fmt.Printf("str type %T str=%q\n", str, str)

str = fmt.Sprintf("%c", myChar)
fmt.Printf("str type %T str=%v\n", str, str)

//输出结果
str type string str="99"
str type string str="23.455999"
str type string str="false"
str type string str=y
  • 方法2: strconv 包的函数
func FormatBool(b bool) string

func FormatInt(i int64, base int) string
func Itoa(i int) string

func FormatUint(i uint64, base int) string

func FormatFloat(f float64, fmt byte, prec, bitSize int) string
//使用第二种方式
var num1 =99
var num2 float32 = 23.456
var b = false
var str string

str = strconv.FormatInt(int64(num1),10)
fmt.Printf("str type %T str = %v\n", str, str)

str = strconv.Itoa(num1)
fmt.Printf("str type %T str = %v\n", str, str)

str = strconv.FormatFloat(float64(num2),'f',-1,32)
fmt.Printf("str type %T str = %v\n", str, str)

str = strconv.FormatBool(b)
fmt.Printf("str type %T str = %v\n", str, str)

//输出结果
str type string str = 99
str type string str = 99
str type string str = 23.456
str type string str = false

string 转基本数据类型

注意确保string类型能够转成有效的数据

func ParseBool(str string) (value bool, err error)

func ParseInt(s string, base int, bitSize int) (i int64, err error)
func Atoi(s string) (i int, err error)

func ParseUint(s string, base int, bitSize int) (n uint64, err error)

func ParseFloat(s string, bitSize int) (f float64, err error)
var str1 = "true"
var str2 = "1234567"
var b bool
var num1 int
var num2 int64
b, _ = strconv.ParseBool(str1)
fmt.Printf("b type %T b = %v\n",b, b)

//只能转成int
num1, _ = strconv.Atoi(str2)
fmt.Printf("num1 type %T num1 = %v\n",num1, num1)

//只能转成int64
num2, _ = strconv.ParseInt(str2, 10, 46)
fmt.Printf("num2 type %T num2 = %v\n",num2, num2)

//输出结果
b type bool b = true
num1 type int num1 = 1234567
num2 type int64 num2 = 1234567

注意事项

“hello”转成int类型

var str = "hello"
var num = 20

//只能转成int
num, _ = strconv.Atoi(str)
fmt.Printf("num type %T num = %v",num, num)

结果:

//输出结果--->如果不成功,变成默认0
num type int num = 0

Golang指针篇

基本介绍

  1. 基本数据类型,变量存的就是值,也叫值类型
  2. 获取变量的地址,用&,比如: var num int,获取num的地址:&num
  3. 指针类型,变量存的是一个地址,这个地址指向的空间存的才是值,比如:var ptr *int = &num
  4. 获取指针类型所指向的值,使用:*,比如:var *ptr int,使用 *ptr获取p指向的值
  5. 举例说明

取指针地址

&ptr

var i = 20
fmt.Println("i的地址是:",&i)

//1.ptr是一个指针变量
//2.ptr指向的是i变量的类型
//3.ptr本身的值是地址
var ptr *int = &i
fmt.Printf("指针ptr是:%v\n",ptr)
fmt.Printf("指针ptr的地址是:%v\n\n",&ptr)

var j = 1
fmt.Println("j的地址是:",&j)

ptr = &j
fmt.Printf("指针ptr是:%v\n",ptr)
fmt.Printf("指针ptr的地址是:%v\n",&ptr)

结果:

i的地址是: 0xc00000a0b0
指针ptr是:0xc00000a0b0
指针ptr的地址是:0xc000006030

j的地址是: 0xc00000a0b8
指针ptr是:0xc00000a0b8
指针ptr的地址是:0xc000006030

在这里插入图片描述

取指针指向的值

*ptr

var i = 20
fmt.Println("i的地址是:",&i)

var ptr *int = &i
fmt.Printf("指针ptr是:%v\n",ptr)
fmt.Printf("指针ptr的地址是:%v\n",&ptr)
fmt.Printf("指针ptr的指向的值是:%v\n\n",*ptr)

var j = 1
fmt.Println("j的地址是:",&j)

ptr = &j
fmt.Printf("指针ptr的内容是:%v\n",ptr)
fmt.Printf("指针ptr的地址是:%v\n",&ptr)
fmt.Printf("指针ptr的指向的值是:%v\n",*ptr)

结果:

i的地址是: 0xc00000a0b0
指针ptr是:0xc00000a0b0
指针ptr的地址是:0xc000006030
指针ptr的指向的值是:20

j的地址是: 0xc00000a0b8
指针ptr的内容是:0xc00000a0b8
指针ptr的地址是:0xc000006030
指针ptr的指向的值是:1

通过指针改变值

*ptr = 20

var num int = 10
var ptr *int = &num

fmt.Printf("num的内存地址是:%v\n", &num)
fmt.Printf("num的内存存的值是:%v\n", num)
fmt.Printf("ptr的内存地址是:%v\n", &ptr)
fmt.Printf("ptr的内存存的值是:%v\n", ptr)
fmt.Printf("ptr指针指向的值是:%v\n\n", *ptr)

*ptr = 20
fmt.Printf("num的内存地址是:%v\n", &num)
fmt.Printf("num的内存存的值是:%v\n", num)
fmt.Printf("ptr的内存地址是:%v\n", &ptr)
fmt.Printf("ptr的内存存的值是:%v\n", ptr)
fmt.Printf("ptr指针指向的值是:%v\n", *ptr)

结果:

//地址没有发生改变,只是改变了值
num的内存地址是:0xc00000a0b0
num的内存存的值是:10
ptr的内存地址是:0xc000006028
ptr的内存存的值是:0xc00000a0b0
ptr指针指向的值是:10

num的内存地址是:0xc00000a0b0
num的内存存的值是:20
ptr的内存地址是:0xc000006028
ptr的内存存的值是:0xc00000a0b0
ptr指针指向的值是:20

指针细节说明

​ 指针类型包括:基本数据类型 int系列、float系列、bool、string、数组结构体struct

值类型和引用类型

1)值类型:基本数据类型 int系列、float系列、bool、string、数组和结构体struct

2)引用类型:指针、slice切片、map、管道channel、interface等都是引用类型

在这里插入图片描述

在这里插入图片描述

标识符命名规范

在这里插入图片描述

在这里插入图片描述

所以int可以作为标识符,但是绝不要使用

标识符命名注意事项

在这里插入图片描述

但是:

在这里插入图片描述

Golang运算符篇

基本介绍

  1. 算术运算符
  2. 赋值运算符
  3. 比较运算符/关系运算符
  4. 逻辑运算符
  5. 位运算符
  6. 其它运算符

算术运算符

在这里插入图片描述

除法

在这里插入图片描述

两个整数相除,得到结果亦为整数

1 / 10 = 0
1.0 / 10 = 0.1

在这里插入图片描述

在这里插入图片描述

取余

//公式:a % b = a -(a / b * b)
fmt.Println("10%3=",10 % 3)
fmt.Println("-10%3=",-10 % 3)
fmt.Println("10%-3=",10 % -3)
fmt.Println("-10%-3=",-10 % -3)

结果:

10 % 3 = 1
-10 % 3 = -1
10 % -3 = 1
-10 % -3 = -1

关系运算符

在这里插入图片描述

逻辑运算符

在这里插入图片描述

赋值运算符

在这里插入图片描述

在这里插入图片描述

1.赋值运算:从右向左运算
2.赋值运算符:左边只能是变量,右边可以是变量、表达式、常量值
3.表达式:任何有值的都是表达式

面试题

//两个变量不允许使用中间变量
var a = 40
var b = 2
fmt.Printf("交换前a=%d, b=%d\n",a,b)
a = a + b
b = a - b	//b = a + b - b
a = a - b	//a = a + b - (a + b - b)
fmt.Printf("交换后a=%d, b=%d\n",a,b)

结果:

交换前a=40, b=2
交换后a=2, b=40

位运算符

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

a := 1 >> 2   0000 0001 =>  0000 0000
b := 1 << 2   0000 0001 => 0 000 0100 【符号位不变】

在这里插入图片描述

a := 10
fmt.Println("a地址:", &a)
var ptr *int = &a
fmt.Println("ptr指针指向的值:", *ptr)

Go语言不支持三元运算符

n = 10 > 1 ? true : false //Go语言不支持

位运算思考题

思考题1:

fmt.Println(2&3)		//2
fmt.Println(2|3)		//3
fmt.Println(13&7)		//5
fmt.Println(5|4)		//5
fmt.Println(-3^3)		//-2

运算符优先级

在这里插入图片描述

括号 ++ --

单目 + - ! (type) * & sizeof 【右到左】

算术 * / % + -

移位 << >>

关系 < <= == > >=& ^ |

逻辑 && ||

赋值 = += -= *= %= /= >>= <<= &= ^= |= 【右到左】

逗号 ,

顺口溜

括号别单带,算术要移位;

关系看位置,逻辑是其次;

赋值有多少,逗号说了算!

控制台输入数据

var a int
var b int
var c int
fmt.Println("请输入a的值:")
//等待用户输入
fmt.Scanln(&a)
fmt.Println("请输入b的值:")
fmt.Scanln(&b)
fmt.Println("请输入c的值:")
fmt.Scanln(&c)

var max int
if a > b {
    
    
    max = a
}else {
    
    
    max = b
}

if c > max {
    
    
    max = c
}

fmt.Println("三个数中最大值是:", max)

结果:

请输入a的值:
1
请输入b的值:
3
请输入c的值:
2
三个数中最大值是: 3

Golang进制篇

进制

二进制:0-121, 在Golang中不能直接使用二进制来表示一个整数,它沿用了C语言的特点
八进制:0-781, 以数字0开头都是八进制
十六进制:0-9及A-F 满161,0x或0X开头的都是十六进制 【此处A-F不区分大小写】
//二进制只能输出%b
var i = 5
fmt.Printf("%b\n", i)

//八进制:0-7 以0开头
var j = 012
fmt.Println(j)

//十六进制:0-9及A-F 以0X开头 不分大小写
var k = 0X11
fmt.Println(k)

结果:

101
10
17

进制转换

其它进制转十进制

在这里插入图片描述

二进制转十进制

在这里插入图片描述

八进制转十进制

在这里插入图片描述

十六进制转十进制

在这里插入图片描述

十进制转其它进制

在这里插入图片描述

十进制转二进制

在这里插入图片描述

十进制转十六进制

在这里插入图片描述

二进制转八、十六进制

在这里插入图片描述

二进制转八进制

在这里插入图片描述

二进制转十六进制

在这里插入图片描述

八、十六进制转二进制

在这里插入图片描述

八进制转二进制

在这里插入图片描述

十六进制转二进制

在这里插入图片描述

原码、反码、补码

对于有符号而言

  • 二进制的最高位是符号位:0表示正数,1表示负数
1 ---> 二进制[0000 0001]
-1 --> 二进制[1000 0001]
  • 正数的原码,反码,补码都是一样的

  • 0的反码、补码都是0

  • 负数的反码 = 符号位不变,其它位取反

  • 负数的补码 = 反码 + 1

1 ---> 原码[0000 0001] 反码[0000 0001] 补码[0000 0001]
-1---> 原码[1000 0001] 反码[1111 1110] 补码[1111 1111]

计算机计算过程使用补码进行算术运算

Golang程序流程控制篇

三大流程控制语句

1)顺序控制

2)分支控制 if else

3)循环控制

顺序控制

向前引用

在这里插入图片描述

分支控制

  1. 单分支
if 条件判定 {
    
    

}
if a := 10; a > 18 {
    
    
    fmt.Println("您的年龄大于18")
}
  1. 双分支
if 条件判定 {
    
    

} else {
    
    

}
if a := 10; a > 18 {
    
    
    fmt.Println("您的年龄大于18")
} else {
    
    
    fmt.Println("你还是太年轻了")
}
var year int
fmt.Println("请输入年份:")
fmt.Scanln(&year)
if (year % 4 == 0 && year % 100 != 0) || year % 400 ==0 {
    
    
    fmt.Println(year,"年是闰年")
} else {
    
    
    fmt.Println(year,"年是平年")
}
  1. 多分支
if 条件判定1 {
    
    
    fmt.Println("符合条件1")
} else if 条件判定2 {
    
    
	fmt.Println("符合条件2")
} else {
    
    
    fmt.Println("以上条件都不成立")
}
var year = 2000
if year % 4 == 0 && year % 100 != 0 {
    
    
    fmt.Println(year,"年是闰年")
} else if year % 400 ==0 {
    
    
    fmt.Println(year,"年是闰年")
} else {
    
    
    fmt.Println(year,"不属于闰年")
}
  1. 嵌套分支
if 条件表达式1 {
    
    
    if 条件表达式2{
    
    
        fmt.Println("最多三层嵌套")
    }
}

在这里插入图片描述

Switch分支结构

  • case唯一
  • 默认break

基本语法

在这里插入图片描述

快速入门案例

var day byte
fmt.Println("请输入a-c之间的字母")
fmt.Scanf("%c",&day)

switch day {
    
    
    case 'a':
    fmt.Println("星期1")
    case 'b':
    fmt.Println("星期2")
    case 'c':
    fmt.Println("星期3")
    default:
    fmt.Println("您的输入有误")
}
  • switch的表达式和case中的数据类型必须一致【因为Golang中类型是显示转换
  • case后面可以有多个表达式
  • case常量不可重复,变量随意
  • default可以有可以没有
  • switch穿透-fallthrough, 会继续执行下一个case【默认穿透一层】

特殊使用

switch day {
    
    
    case 'a', 'd', 'e', 'f': //多个表达式用逗号隔开
    	fmt.Println("星期1")
    case 'b':
    	fmt.Println("星期2")
    case 'c':
    	fmt.Println("星期3")
    default:
    	fmt.Println("您的输入有误")
}
//其他编程语言不可以
//方式1
var day = 70
switch {
    
    
    case day==30:
    	fmt.Println("day==30")
    case day >= 60:
    	fmt.Println("day>=60")
    default:
    	fmt.Println("没有匹配到")
}

//方式2--->【不推荐】
switch day:=70; {
    
    
    case day==30:
    	fmt.Println("day==30")
    case day >= 60:
    	fmt.Println("day>=60")
    default:
    	fmt.Println("没有匹配到")
}
switch day:=30; {
    
    
    case day==30:
    	fmt.Println("day==30")
    	fallthrough		//穿透一层,下一个case直接执行,不进行判断
    case day >= 60:
    	fmt.Println("day>=60")
    case day <= 20:
    	fmt.Println("day>=60")
    default:
    fmt.Println("没有匹配到")
}

result

day==30
day>=60

switch 和 if 的比较

  • 具体的数值不多的情况下,用switch
  • 区间的判断,bool值判断

for循环控制

for循环顺序

1)执行循环变量初始化,比如 i := 1

2)执行循环条件,比如 i <= 10

3)如果循环条件为真,就执行循环操作,比如:fmt.Println(“hello world +”, i)

4)执行循环变量迭代,比如 i++

5)反复执行 2、3、4步骤,直到循环条件为false

案例:打印10句 “hello world”

//输出10句 hello world
//方式1
var i int
for i = 1; i <= 10; i++ {
    
    
    fmt.Println("hello world +", i)
}
//i最后是 11
fmt.Println("i=",i)

//方式2
var i int = 0
for i <= 10 {
    
    
    fmt.Println("hello world +", i)
    i++
}

//方式3
var i = 0
for {
    
     //死循环,与break搭配使用
    if i <= 10 {
    
    
        fmt.Println("hello world +", i)
    } else {
    
    
        break
    }
    i++
}

字符串遍历

//数组遍历,传统方式
var str = "hello world"
for i := 0; i <len(str); i++ {
    
    
    fmt.Printf("%c\n", str[i])
}

//数组遍历 for-range遍历方式
for index, val := range str {
    
    
    fmt.Printf("index=%d, val=%c \n", index, val)
}

在这里插入图片描述

上述代码细节讨论

​ 如果我们的字符串含有中文,那么传统的遍历字符串出现字符串乱码!原因是传统的对字符串的遍历是按字节遍历的,而汉字在utf-8编码是对应3个字节

​ 解决方案 需要将 str 转化成 [] rune 切片

​ for-range方式遍历不会出现乱码,原因for-range 默认是按照字符遍历,因此有中文也OK

while 与 do…while

​ Golang中没有while和do…while语法

​ 利用for循环实现

1)while循环的实现

for {
    
    
    if 循环条件表达式 {
    
    
     break //跳出for循环   
    }
    循环操作(语句)
    循环变量迭代
}
var i int = 1 //初始化
for {
    
    
    if i > 10 {
    
     //循环条件表达式
        break
    }
    fmt.Printf("hello world +%d\n",i) //循环操作(语句)
    i++ //循环变量迭代
}
  • 先判断,再执行

2)do…while循环实现

for {
    
    
    循环操作(语句)
    循环变量迭代
    if 循环条件表达式 {
    
    
        break
    }
}
var i int = 11
for {
    
    
    fmt.Printf("hello world +%d\n",i)
    i++
    if i > 10 {
    
    
        break
    }
}
  • 先执行,再判断
  • 至少执行一次

多重循环控制、案例

  • 内存循环、外层循环
  • 嵌套循环就是把 “内层循环” 当成 “外层循环” 的循环体
  • 由内而外,由简到难,先死后活

成绩统计

统计3个班的成绩情况,每个班5名学生,
求出每个班的平均分,和所有班级的平均分!

在这里插入图片描述

在这里插入图片描述

打印金字台

  • 编程思路
    • 1、打印一个矩形
    • 2、打印半个金字塔
    • 3、打印整个金子塔
      • 规律
        • 芯号 = 2 x 层数 -1
        • 空格 = 总层数 - 当前层数
    • 4、写活(做变量)
    • 5、打印空心金子塔
      • 规律
        • 每层的第一个和最后一个打印*
        • 否则就打印空格
        • 底层例外

打印空心菱形

//三角形的层数
var height = 25

//正放的三角形 height层
for i := 1; i <= height; i++ {
    
    
    //打印空格
    for k := 1; k <= height - i; k ++ {
    
    
        fmt.Print(" ")
    }
    //打印*号
    for j := 1; j <= 2 * i - 1; j++ {
    
    
        if j == 1 || j == 2 * i - 1 {
    
    
            fmt.Print("*")
        } else {
    
    
            fmt.Print(" ")
        }
    }
    fmt.Println()
}
//倒放的三角形 height-1层
for i := height - 1; i >= 1; i-- {
    
    
    //打印空格
    for k := 1; k <= height - i; k ++ {
    
    
        fmt.Print(" ")
    }
    //打印*号
    for j := 1; j <= 2 * i - 1; j++ {
    
    
        if j == 1 || j == 2 * i - 1 {
    
    
            fmt.Print("*")
        } else {
    
    
            fmt.Print(" ")
        }
    }
    fmt.Println()
}

结果

     *
    * *
   *   *
  *     *
 *       *
*         *
 *       *
  *     *
   *   *
    * *
     *

打印久久乘法表

var number = 9
for i := 1; i <= number; i++ {
    
    
    for j := 1; j <= i; j++ {
    
    
        fmt.Printf("%vx%v=%v\t", j, i, i*j)
    }
    fmt.Println()
}

结果

1x1=1	
1x2=2	2x2=4	
1x3=3	2x3=6	3x3=9	
1x4=4	2x4=8	3x4=12	4x4=16	
1x5=5	2x5=10	3x5=15	4x5=20	5x5=25	
1x6=6	2x6=12	3x6=18	4x6=24	5x6=30	6x6=36	
1x7=7	2x7=14	3x7=21	4x7=28	5x7=35	6x7=42	7x7=49	
1x8=8	2x8=16	3x8=24	4x8=32	5x8=40	6x8=48	7x8=56	8x8=64	
1x9=9	2x9=18	3x9=27	4x9=36	5x9=45	6x9=54	7x9=63	8x9=72	9x9=81	

跳转控制语句 break

  • 结束最近的循环
  • 标签跳转

案例:随机生成1-100之间的数字,直到生成99为止,看看你一共用了几次?

var num int = 0
// 随机种子
rand.Seed(time.Now().Unix())
// 生成 20 个 [0, 100) 范围的伪随机数。
for {
    
    
    result := rand.Intn(100)+1
    fmt.Println(result)
    num++
    if result == 99 {
    
    
        break
    }
}
fmt.Printf("随机了%v次", num)
for i := 1; i < 4; i++ {
    
    
    for j :=1; j < 4; j++ {
    
    
        //结束本次循环
        if j == 2 {
    
    
            break
        }
        fmt.Println("i=", i, "j=", j)
    }
}

结果

i= 1 j= 1
i= 2 j= 1
i= 3 j= 1
here:
for i := 1; i < 4; i++ {
    
    
    for j :=1; j < 4; j++ {
    
    
        if j == 2 {
    
    
            //结束循环
            break here
        }
        fmt.Println("i=", i, "j=", j)
    }
}

结果

i= 1 j= 1

跳转控制语句 continue

  • 结束本次循环,继续执行下次循环
  • continue后的语句不执行,循环变量迭代
  • 标签转出
for i := 1; i < 4; i++ {
    
    
    for j :=1; j < 4; j++ {
    
    
        //不输出2,继续下次循环
        if j == 2 {
    
    
            continue
        }
        fmt.Println("i=", i, "j=", j)
    }
}

结果

i= 1 j= 1
i= 1 j= 3

i= 2 j= 1
i= 2 j= 3

i= 3 j= 1
i= 3 j= 3
here:
for i := 1; i < 4; i++ {
    
    
    for j :=1; j < 3; j++ {
    
    
        //没有2的输出,继续下次循环
        if j == 2 {
    
    
            continue here
        }
        fmt.Println("i=", i, "j=", j)
    }
}

结果

i= 1 j= 1
i= 2 j= 1
i= 3 j= 1

练习题

路口缴费:

​ 现金<50,000每经过路口上交5%

​ 现金>= 50,000时,每次交1000

​ 请问100,000 现金能过多少个路口

var money float64 = 100000.0
var count int
for true {
    
    
    if money > 50000 {
    
    
        money -= money*5.0/100.0
        count++
        continue
    }
    money -= 1000
    if money < 0 {
    
    
        break
    }
    count++
}
fmt.Printf("可以经过%v个路口", count)

跳转控制语句 goto

  • 汇编语言中使用较多
  • 无条件转移到指定行
  • 条件转移、跳出循环体
  • Go程序设计中一般不主张使用goto语句
fmt.Println("OK1")
goto label1
fmt.Println("OK2")
fmt.Println("OK3")
fmt.Println("OK4")
label1:
fmt.Println("OK5")

//与if绑定使用
var n int = 20
fmt.Println("OK1")
if n ==20 {
    
    
    goto label1
}
fmt.Println("OK2")
fmt.Println("OK3")
fmt.Println("OK4")
//标签
label1:
fmt.Println("OK5")

结果

OK1
OK5

跳转语句 return

  • 不再执行任何循环
  • 终止所有
  • 终止main函数,程序退出
var n int = 20
fmt.Println("OK1")
if n ==20 {
    
    
    return
}
fmt.Println("OK2")
fmt.Println("OK3")
fmt.Println("OK4")
fmt.Println("OK5")

结果

OK1

Golang函数篇

为什么需要函数?

  1. 代码冗余(代码块多次出现)
  2. 不利于代码维护

案例:

​ 输入两个数,再输入一个运算符,得到结果

//1.此处需要两数相除运算
var n1 float64 = 10.1
var n2 float64 = 10.1
var operator byte = '/'
var result float64
switch operator {
    
    
    case '+':
    result = n1 + n2
    case '-':
    result = n1 - n2
    case '*':
    result = n1 * n2
    case '/':
    result = n1 / n2
    default:
    fmt.Println("您的操作符有误...")
}
fmt.Printf("运算结果是:%2f\n", result)


//2.此处需要两数相加
n1 = 20
n2 = 30
operator = '+'
switch operator {
    
    
    case '+':
    result = n1 + n2
    case '-':
    result = n1 - n2
    case '*':
    result = n1 * n2
    case '/':
    result = n1 / n2
    default:
    fmt.Println("您的操作符有误...")
}
fmt.Printf("运算结果是:%2f", result)

同一个main函数,多处代码块一致,代码冗余

函数简介

为了完成某一项功能的程序指令(语句)的集合,成为函数

在Go中,函数分为:自定义函数系统函数(查看Go编程手册)

基本语法

func 函数名 (形参列表) (返回值列表) {
    
    
    执行语句
    return 返回值列表
}    

1)形参列表:表示函数的输入

2)函数中的语句:表示为了实现某一功能的代码块

3)函数可以有返回值,也可以没有返回值

//函数 可以有返回值,可以没有返回值
func cal (n1 float64, n2 float64, operator byte) float64 {
    
    
	var result float64
	switch operator {
    
    
	case '+':
		result = n1 + n2
	case '-':
		result = n1 - n2
	case '*':
		result = n1 * n2
	case '/':
		result = n1 / n2

	default:
		fmt.Println("操作符有误...")
	}
	return result
}

func main() {
    
    
	//1.此处需要两数相除运算
	var n1 float64 = 10.1
	var n2 float64 = 10.1
	var operator byte = '/'
	var result float64
	result = cal(n1, n2, operator)
	fmt.Printf("运算结果是:%2f\n", result)


	//2.此处需要两数相加
	n1 = 20
	n2 = 30
	operator = '+'
	result = cal(n1, n2, operator)
	fmt.Printf("运算结果是:%2f", result)
}

问题

函数写在一个main函数里面???

是否可以专门写一个文件来存函数

包的使用

说明:go的每一个文件都属于一个包的,也就是说go是以包的形式来管理文件和项目目录结构

包三大作用:

1)区分相同名字的函数、变量等标识符

2)当程序文件很多时,可以很好的管理项目

3)控制函数、变量等访问范围,即作用域

包的相关说明

1)打包基本语句:

​ package “包名”

2)引包基本语句:

​ import “包路径”

3)package 指令在文件的的第一行 ,然后是import 指令

4)在import包时,路径从$GOPATHsrc 下开始,不用带src,编译器会自动从src下开始引用

5)函数名首字母大写,才能跨包访问

6)包名过长,可以取别名

7)Go语言没有重载,在同一个包下,函数名不能相同

import (
	"fmt"
	u "module/utils" //别名是 u
)

在这里插入图片描述

在这里插入图片描述

一般包名与文件夹名保持一致

打包exe

1)编译时需要编译main包所在的文件夹

2)项目的目录结构最好规范使用

3)编译后生成一个可执行文件,在$GOPATH目录下,可以指定文字和目录

​ 比如:放在bin目录下 D:Work\Goland\Go\project01>go build -o bin/my.exe module/chapter01

函数调用机制

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-23RMYhdQ-1605951880110)(Go语言.assets/image-20201117171419118.png)]

函数的递归调用

一个函数在函数体内又调用的函数本身,我们称之为递归调用

递归过程生成新栈,每个栈变量提前变化

递归必须向退出递归条件逼近,否则就无限循环

函数结果返回时或执行完毕,函数就会被销毁(系统、编译器)

func test (n int) {
    
    
	if n > 2 {
    
    
		n--
		test(n)
	}
	fmt.Println("n=",n)
}

func main () {
    
    
    test(4)
}

结果:

n= 2
n= 2
n= 3

在这里插入图片描述

test4栈中,由于发生了n–,所以该栈的n—>3

func test (n int) {
    
    
	if n > 2 {
    
    
		n--
		test(n)
    } else {
    
    
        fmt.Println("n=",n)
    }
}
func main () {
    
    
    test(4)
}

结果:

n= 2

斐波那契数

请使用递归的方式,求出斐波那契数 1,1,2,3,5,8,13…

给你一个整数n,求出它的斐波那契数是多少?
思路:

n == 1 || n==2 输出 f(n) = 1

n > 2 输出 f(n) = f(n-1) + f(n-2)

代码:

//函数4---斐波那契数
func Fbn (n int) int{
    
    
	if n == 1 || n == 2 {
    
    
		return 1
	} else {
    
    
		return Fbn(n-1) + Fbn(n-2)
	}
}

猴子吃桃子

有一堆桃子,猴子第一天吃其中的一半,并再多吃了一个!以后猴子都吃其中的一半,然后再多吃一个。

当吃到第十天时,想再吃时(还没吃),发现只有一个桃子。

问题:最初共有多少个桃子?

思路

1)第10天只有一个桃子

2)第9天有几个桃子 = (第10天桃子数 + 1)x 2

3)规律:第n天的桃子数 peach(n) = ( peach(n - 1) + 1 ) x 2

代码

//函数5---猴子吃桃子
func Monkey (n int) int {
    
    
	if n == 10 {
    
    
		return 1
	} else {
    
    
		return (Monkey(n+1)+1) * 2
	}
}

函数注意事项1

1)函数的形参列表可以是多个的 【参数】,返回值列表也可以是多个

2)形参列表返回值列表数据类型可以是值类型,也可以是引用类型:(Map)

3)函数的命名规范:首字母不能是数字首字母大写该函数可以被本包其它包文件使用

4)函数中的变量是局部的,函数外不生效

5)基本数据类型数组默认是值传递,即进行值拷贝【内存】。函数内修改不影响原来的值

6)Java中数组是引用传递

7)如果希望函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内以指针方式操作变量

8)Go函数不支持函数重载

值传递

//将main函数中n的值拷贝到函数中去
func test (n int) {
    
    
    n--
    fmt.Println("test n=", n)
}

func main() {
    
    
    var n int = 10
    test(n)
    fmt.Println("main n=", n)
}

结果

test n = 9
main n = 10 //经过函数,但是值未发生改变

引用类型/指针传递

//n是指针类型
func test (n *int) {
    
    
    *n--
    fmt.Println("test n=", *n)
}

func main() {
    
    
    var n int = 10
    test(&n)
    fmt.Println("main n=", n)
}

结果

test n= 9
main n= 9 //函数内通过指针进行改变

函数注意事项2

1)函数也是一种数据类型,可以赋值给变量

2)函数可以作为形参列表

3)为了简化数据类型定义,Go支持自定义数据类型

4)支持函数返回值命名

函数是数据类型,可以赋值给变量

func test (n int) {
    
    
	n--
	fmt.Println("test n=", n)
}

func main() {
    
    
    //a就等价于test函数
	a := test
	fmt.Printf("a的数据类型:%T\ntest的数据类型:%T\n", a, test)
	a(10)
}

结果

a的数据类型:func(int)
test的数据类型:func(int)
test n= 9

自定义数据类型

type myInt int //给int取了别名myInt,在Go中两者虽然都是int,但是他们是不同的数据类型
var n myInt
n = 10
fmt.Println(n)

var x int
x = int(n)//不同数据类型,需要强转
fmt.Println(x)

函数取别名

//函数取别名【全局】
type myTest func (int) int

func test (n int) int {
    
    
	n--
	fmt.Println("test n=", n)
	return n
}

func myFun (test myTest,n int) int {
    
    
	return test(n)
}

func main() {
    
    
	x := myFun(test,10)
	fmt.Println(x)
}

结果

test n= 9
9

函数返回值命名

func test(n1 int, n2 int) (sub int, sum int) {
    
    
	sum = n1 + n2
	sub = n1 - n2
	return
}
func main() {
    
    
	a, b := test(10, 1)
	fmt.Println("sum=",a)
	fmt.Println("sub=",b)
}

结果

sum= 11
sub= 9

函数注意事项3

1)使用 _标识符,忽略返回值

2)Go支持可变参数,放于最后位置

函数可变参数

//支持0-多个参数
func sum (args...int) sum int {
    
    
    
}

//支持1-多个参数
func sum (n1 int, args...int) sum int {
    
    
    
}
args是slice切片,通过args[index]可以访问到各个值

案例:

编写一个sum函数,可以求出 1到多个int的和

代码

func sum (n1 int, args... int)  int {
    
    
	sum := n1
	for i := 0; i < len(args); i++ {
    
    
		sum += args[i]
	}
	return sum
}
func main() {
    
    
	result := sum(1,-1,10,-5)
	fmt.Println(result)
}

结果

5

函数注意案例4

在这里插入图片描述

案例

编写一个两数交换的函数

思路

1)基本类型是值传递

2)要想函数中交换值,必须需进行指正运算

代码

func sum (n1 ,n2 *int) {
    
    
	temp := *n1
	*n1 = *n2
	*n2 = temp
}

func main() {
    
    
	var n1 int = 10
	var n2 int = 20
	fmt.Printf("交换前n1=%v n2=%v\n", n1, n2)
	sum(&n1, &n2)
	fmt.Printf("交换后n1=%v n2=%v\n", n1, n2)
}

结果

交换前n1=10 n2=20
交换后n1=20 n2=10

init函数/执行顺序

1)每一个源文件中都可以包含一个init函数

2)该init函数会在main函数执行前,被Go运行框架调用

3)也就是说init函数会在main函数之前别调用

4)用来做初始化

5)如果一个文件同时包含全局定义、init函数和main函数,执行顺序全局定义 -> init函数 -> main 函数

6)引入包先定义 > 全局定义 -> init函数 ->函数

//Global variable --->initialize with function
var age int = test()

func test () int {
    
    
	fmt.Println("firstly: test function...")
	return 10
}

//init function ---> initialization operator
func init (){
    
    
	fmt.Println("secondly: init initialization...")
}

func main() {
    
    
	fmt.Println("end: main function...")
}

result

firstly: test function...
secondly: init initialization...
end: main function...

在这里插入图片描述

No.1: utils package init function...
firstly: test function...
secondly: init initialization...
end: main function...
age is 10
age is 廖述幸

匿名函数

1)Go支持匿名函数,如果我们希望这个函数只使用一次,我们就可以考虑使用匿名函数,当然也可以实现多次调用

2)没有名字的函数

3)在main函数中定义使用

4)如果匿名函数赋值给全局变量,那么这个匿名函数,就成为一个全局匿名函数,可以在程序有效

定义匿名函数

只能调用一次的匿名函数

代码

//variables initialize
n1 := 10
n2 := 20

//Anonymous function
result := func (n1, n2 int) int {
    
    
    return n1+n2
}(n1, n2)

//output
fmt.Println(result)

结果

30

将匿名函数交给变量进行调用

代码

//Anonymous function to variable
a := func (n1 int, n2 int) int {
    
    
    return n1 - n2
}

result = a(20,10)
fmt.Println(result)

结果

10

全局匿名函数

代码

//Hand over anonymous functions to global variables and become global anonymous functions
var (
	fun1 = func (n1 int, n2 int) int {
    
    
		return n1 * n2
	}
)

func main() {
    
    
	num := fun1(9,9)
	fmt.Println(num)
}

结果

81

闭包

基本介绍:

​ 闭包就是一个函数和与其相关的引用环境组合的一个整体(实体)

//accumulator
func AddUpper() func (int) int {
    
    
	var n int = 10//闭包n只初始化一次
	return func (x int) int {
    
    
		n = n + x//与变量n相关
		return n
	}
}

func main() {
    
    
	// Using accumulator
	f := AddUpper()
	fmt.Println(f(1)) //11
	fmt.Println(f(2)) //13
    fmt.Println(f(3)) //16
}

对上面代码的说明和总结

1)AddUpper 是一个函数,返回的数据类型是 func (int) int

2)闭包的说明:返回的是一个匿名函数,但是这个匿名函数引用到函数外的n,因此这个匿名函数就和 n 组成一个整体构成闭包

3)反复调用f,n只会初始化一次

案例

请编写一个程序,具体要求如下

1)编写一个函数 makeSuffix( suffix string) 可以接受一个文件后缀名 .jpg ,并返回一个闭包

2)调用闭包,可以传入一个文件名,如果该文件名没有指定的后缀(比如 .jpg),则返回 文件名.jpg,如果已经有.jpg后缀,就返回文件名

3)要求使用闭包的方式完成

4)strings.HasSuffix

func HasSuffix

func HasSuffix(s, suffix string) bool

判断s是否有后缀字符串suffix。

代码

//Add suffix function
func makeSuffix(suffix string) func(string) string {
    
    
	//The returned anonymous function and the outside suffix form a closure function
	return func(name string) string {
    
    
		//Determine whether the suffix is correct
		if !strings.HasSuffix(name,suffix) {
    
    
			//Forcibly add a suffix
			return name + suffix
		} else {
    
    
			//Return the original file name
			return name
		}
	}
}

func main() {
    
    
	//The first step: assign makeSuffix function to variable f
	f := makeSuffix(".jpg")
	//Use variable f
	result := f("carter")
	//Output result
	fmt.Println(result)
}

说明

1)返回的匿名函数和makeSuffix (suffix string) 的suffix 组成 了闭包,因为返回函数引用了 suffix

2)使用闭包只需要传入一次后缀, suffix会被保存 ,传统方法每次都要传入

3)匿名函数引用到的变量传入一次,反复使用

4)函数不行,函数调用一次,它的栈就销毁了,原先的变量就没有了,除非是全局变量

关键字 defer

1、为什么需要defer?

​ 在程序中常常需要创建资源,(比如:数据库连接、文件句柄、锁等),为了在函数执行完毕后及时释放资源,Go的设计者提供了defer(延时机制)

  • 函数执行完毕后,紧接着按照先进后出的方式执行defer栈中的指令

2、快速入门案例

func sum (n1 int, n2 int) int {
    
    
	//defer statement
	//压入到defer栈中,暂不执行,当函数执行完后,按先入后出的方式执行
	defer fmt.Println("n1=",n1)
	//Push into the defer stack, temporarily not executed,when the function is executed, it is executed 	in a first-in-last-out manner
	defer fmt.Println("n2=",n2)
	result := n1 + n2
	fmt.Println("result=",result)
	return result
}

func main (){
    
    
	res := sum(10, 20)
	fmt.Println("res=",res)
}

结果

firstly: result= 30 //函数
secondly: n2= 20 //defer栈后入先出者
thirdly: n1= 10 //defer栈先入后出者
finally: res= 30 //main函数中

分析

1)当Go执行到一个defer时,不会立即执行defer后的语句,而是将defer后的语句压入到一个栈中,然后执行函数下一条语句

2)当函数执行完毕后,再从defer栈中,依次从栈顶取出语句执行(先入后出)

3)在defer 将语句放入栈时,也会将相关的值拷贝同时入栈

4)Golang中编程的通常做法,创建资源,比如(打开文件、获取数据库连接、或者是资源锁等),可执行defer file.close()

5)在defer后,可以继续使用创建好的资源

6)函数执行完毕后,系统会依次从defer栈中,取出语句来关闭资源

7)这种机制,非常简洁,程序员不用再为什么时候关闭资源而烦心了

func sum (n1 int, n2 int) int {
    
    
	//defer statement
	//压入到defer栈中,暂不执行,当函数执行完后,按先入后出的方式执行
	defer fmt.Println("n1=",n1)
	//Push into the defer stack, temporarily not executed,when the function is executed, it is executed 	in a first-in-last-out manner
	defer fmt.Println("n2=",n2)
	//Add statement
	n1++
	n2++
	result := n1 + n2
	fmt.Println("result=",result)
	return result
}

func main (){
    
    
	res := sum(10, 20)
	fmt.Println("res=",res)
}

结果

result= 32
n2= 20		//n2的值拷贝defer栈中
n1= 10		//Copy the value of n1 in the defer stack
res= 32

值传递、引用传递

1)值传递:基本数据类型 int、float、bool、string、数组结构体struct

2)引用类型:指针、slice切片、map、管道chan、interface等

3)引用传递传递 地址& ,往往效率。 引用传递 > 值传递

4)值传递:变量直接存储,内存通常分配在

5)引用类型:变量存地址,一般在上分配,没有任何变量引用,会被GC回收

6)如果函数改变值,参数传地址

函数变量作用域

1)局部变量:函数中定义,作用域:函数内部使用

2)全局变量:函数外定义,作用域:整个包有效;首字母大写,整个程序有效

3)代码块变量:在for 、if 中定义,作用域:该代码块

4)赋值语句只能出现在函数

//全局变量定义
Name := "tom" 等价于 var Name string//声明语句   Name = "tom"//赋值语句
//赋值语句只能出现在函数中

在这里插入图片描述

Golang字符串常用的系统函数

1)统计字符串、数组的长度,按字节 len (str),汉字占用3个字节

2)字符串遍历,同时处理有中文乱码问题 r := [ ]rune(str)切片化

3)字符串转整数:n,error := strconv.Atoi(“12”)

4)整数转字符串:n,error := strconv.Itoa(12)

5)字符串转byte[]:写入二进制文件

6)byte[]转成字符串:二进制文件转字符,str = string([]byte{104, 101, 108, 108, 111, 32, 71, 111})

7)10进制转2,8,16进制:str = strconv.FormatInt(123, 2)

8)查找子串是否在指定字符串中: strngs.Contains(“seafood”,“foo”)

9)统计一个字符串中有几个指定的子串:strings.Count(“ceheese”, “e”)

10)不区分大小写的字符串比较(==是区分大小写): strings.EqualFold(“abc”, “AbC”)

11)返回子串在字符串中第一次出现的index值,如果没有返回-1:strings.Index(“aabcabc”, “abc”),找下标

12)返回字符串在字符串最后一次出现的index,如果没有返回-1:strings.LastIndex(“aabcabc”, “abc”)

13)将指定的子串替换成 另外一个子串:strings.Replace(“go go”, “go”, “语言”, n) n表示你要替换几个?-1全部替换

14)按照指定的某个字符,为分割标识,将一个字符串拆分成字符串数组:strings.Split(“hello,world”, “,”)

15)将字符串进行大小写转换:strings.ToLower(“GO”) ,strings.ToUpper(“go”)

16)将字符串两边的空格去除:strings.TrimSpace(" hello ")

17)将字符串左右两边指定字符去掉:strings.Trim("! hello!", “! h”) //结果: ello

18)将字符串左边指定字符去掉:strings.TrimLeft("!hello!", “!”)

19)将字符串右边指定字符去掉:strings.TrimRight("!hello!", “!”)

20)判断字符串是否以指定字符串开头:strings.HasPrefix(“http://www.baidu.com”, “http”)

21)判断字符串是否以指定字符串结束:strings.HasSuffix(“carter.jpg”, “.jpg”)

str := "hello中"
fmt.Println(len(str))//按字节计算--->8
如果是按字符输出----->6

[ ]rune( )中文乱码处理

str := "hello中国"
r := []rune(str)//防止中文乱码--->切片化
fmt.Printf("r的类型是:%T\n", r)
for i := 0; i < len(r); i++ {
    
    
    fmt.Printf("字节=%c\n", r[i])
}

结果

r的类型是:[]int32
字节=h
字节=e
字节=l
字节=l
字节=o
字节=中
字节=

strconv.Atoi字符串转Int型

n, error := strconv.Atoi("hello")
if error != nil {
    
    
    fmt.Println("conversion error",error)
} else {
    
    
    fmt.Println("The result of the conversion is", n)
}

结果

conversion error strconv.Atoi: parsing "hello": invalid syntax

字符串转byte[]

var bytes = []byte("hello Go")\\转二进制
fmt.Printf("bytes=%v", bytes)

结果

bytes=[104 101 108 108 111 32 71 111]

byte[]转成字符串

str := string([]byte{
    
    104, 101, 108, 108, 111, 32, 71, 111})
fmt.Printf("str=%v\n", str)

结果

str=hello Go

10进制转2,8,16进制

str := strconv.FormatInt(123, 16)
fmt.Printf("str type = %T str = %v\n", str, str)

结果

str type = string str = 7b

查找子串是否在指定字符串中

flag := strings.Contains("food","fo")
fmt.Println(flag)

结果

true

字符串分割

var sss []string = strings.Split("hello,world", ",")
for i := 0; i < len(sss); i++ {
    
    
    fmt.Println(sss[i])
}

结果

hello
world

左右两边指定字符去掉

str = strings.Trim("!hello! ", " !h")//去掉左右两边的 空格+!+字母h
fmt.Println(str)

结果

ello

时间、日期相关函数

1)需要导入time

//Get current time
now := time.Now()
fmt.Printf("%v\n", now)

//Get other relevant dates
year := now.Year()
month := now.Month()
day := now.Day()
hour := now.Hour()
minute := now.Minute()
second := now.Second()
fmt.Printf("%v-%v-%v %v:%v:%v\n",year, month, day, hour, minute, second)
fmt.Printf("%v-%v-%v %v:%v:%v\n",year, int(month), day, hour, minute, second)

结果

2020-11-19 16:58:09.4389182 +0800 CST m=+0.003990301
2020-November-19 16:58:9
2020-11-19 16:58:9

格式化日期和时间

//Get current time
now := time.Now()
fmt.Printf(now.Format("2006-01-02 15:04:05"))//时间是固定值,详情参考Go语言官方文档
fmt.Printf(now.Format("month:01\n"))
fmt.Printf(now.Format("day:02\n"))

结果

2020-11-19 17:12:28
month:11
day:19

时间常量

表示方式

const (
    Nanosecond  Duration = 1						//纳秒
    Microsecond          = 1000 * Nanosecond		//微秒
    Millisecond          = 1000 * Microsecond		//毫秒
    Second               = 1000 * Millisecond		//秒
    Minute               = 60 * Second				//分钟
    Hour                 = 60 * Minute				//小时
)

休眠

1)每隔0.1秒中打印一个数字,打印到100时退出

for i := 0; i <= 20; i++ {
    
    
    fmt.Println(i)
    //dormant
    //time.Sleep(time.Second)//--->1秒
    time.Sleep(time.Millisecond * 100)//0.1秒
}

时间戳

now := time.Now()
fmt.Printf("Unix时间戳=%v\nUnixNano时间戳=%v", now.Unix(), now.UnixNano())

结果

Unix时间戳=1605778195					秒级别
UnixNano时间戳=1605778195493826300		纳秒级别

内置函数

什么是内置函数?

Golang设计者为了使编程方便,提供了一些函数,这些函数可以直接使用,我们称之为内置函数

1)len( ):用来求长度,比如:string、array、slice、map、channel等

2)new( ):用来分配内存,主要分配值类型,比如:int、float、struct

3)make:用来分配内存,主要用来分配引用类型,比如:chan、map、slice

在这里插入图片描述

在这里插入图片描述

错误处理

1)默认情况下,当发生错误,就会退出程序

2)我们希望:发生错误后捕获异常,并进行处理,保证程序可以继续执行

2)捕捉错误后,给管理员一个提示(邮件、短信…)

基本说明:

1)Go语言追求简洁优雅,所以,Go语言不支持传统的try … catch … finally

2)Go语言引入的处理方式:defer、panic、recover

3)使用场景:Go中抛出panic异常,然后在defer中通过recover捕获这个异常,然后正常处理

defer+recover处理

func test01() {
    
    
	//使用defer+recover的方式处理异常
	//Use anonymous function
	defer func() {
    
    
		error := recover()
		if error != nil{
    
    
            //Send error message to the Administrator or some operator...
			fmt.Println(error)
            fmt.Println("发送邮件给管理员...")
		}
	}()
	n1 := 10
	n2 := 0
	result := n1/n2
	fmt.Println(result)
}

func main() {
    
    
	test01()
	fmt.Println("错误后面的代码...")
}

结果

runtime error: integer divide by zero
错误后面的代码...

自定义错误处理

Go语言中也支持自定义错误,使用errors.New 和 panic 内置函数。

1)errors.New(“错误说明”),会返回一个error类型的值,表示一个错误

2)panic内置函数,接收一个interface{}类型的值(也就是任何值)作为参数。可以接收error类型的变量,输出错误信息,并退出程序

errors + panic处理

  • 读取配置文件
  • 读取失败,停止程序
//warning
//if reading the configuration file fails,
//the program will stop
func readConf(name string)(err error) {
    
    
	if name == "config.ini" {
    
    
		//Reading...
		return nil
	} else {
    
    
		//Return a custom error
		return errors.New("error reading configuration file")
	}
}

func checked(fileName string) {
    
    
	err := readConf(fileName)
	if err != nil{
    
    
		//if there is an error in reading the file,Output the error and terminate the program
		panic(err)
	}
	fmt.Println("the code behind test01() continues to execute...")
}

func main() {
    
    
	checked("config.in")
}

结果

panic: error reading configuration file

goroutine 1 [running]:
main.test01(0xb2ad7e, 0x9)
	D:/Work/Goland/Go/project01/src/chapter03/error/main.go:25 +0x11c
main.main()
	D:/Work/Goland/Go/project01/src/chapter03/error/main.go:31 +0x3d

Golang数组和切片篇

数组

var arr [1]float64
arr[0] = 12.0 
for i := 0; i < len(arr); i++ {
    
    
    fmt.Printf("arr[%v]=%0.2f\n", i, arr[i])
}

数组定义和内存布局

//int 占8个字节
//地址 = 前一个地址 + 8
//数组地址是连续的---->【可以直接利用地址取值,超级快】
var intArr [3]int
intArr[0] = 0
intArr[1] = 1
intArr[2] = 2
fmt.Printf("intArr[0] %p", &intArr[0])

在这里插入图片描述

总结

1)int 占8个字节

2)数组地址可以通过数组来获取 &intArr

3)数组第一个元素的地址,就是数组的首地址

4)数组各个元素的地址间隔是依据数据类型决定的

5)数组地址是连续的---->【可以直接利用地址取值,超级快】

4种数组初始化的方式

//Way 1
var intArr01 [3]int = [3]int{
    
    1, 2, 3}
fmt.Println("intArr01=", intArr01)

//Way 2
var intArr02 = [3]int{
    
    5, 6, 7}
fmt.Println("intArr02=", intArr02)

//Way 3
var intArr03= [...]int{
    
    8, 9, 10}
fmt.Println("intArr03=", intArr03)

//Way 4
var intArr04 = [...]int{
    
    2: 900, 1: 700, 0: 600}
fmt.Println("intArr04=", intArr04)

数组的遍历

方式1:常规遍历

方式2:for - range

for...range基本语法

for index, value := range intArr {
    
    
    ...
}

说明

1)第一个返回值index是数组的下标

2)第二个返回值value是在该下标位置的值

3)它们都是仅在 for 循环内部可见的局部变量

4)遍历数组的时候,如果不需要index,可以使用 _ 进行忽律

5)index 和 value 的名称不是固定的,只是一般命名为 index 和 value

代码

var intArr04 = [...]int{
    
    2: 900, 1: 700, 0: 600}

for index, value := range intArr04 {
    
    
    fmt.Printf("%v--->%v\n", index, value)
}

结果

0--->600
1--->700
2--->900

数组注意事项和细节

1)数组是多个相同类型的数据的组合,一个数组一旦声明/定义了,其长度是稳固的,不能变化的

2)var intArr []int 这时 intArr 是切片 slice;var intArr [3]int,这时 intArr 是数组

3)数组中的元素可以是任何数据类型,包括值类型或者引用类型,但是不能混用

4)数组创建后,如果没有赋值,有默认值

​ 数值类型:默认值 0

​ 字符串类型:默认值 “”

​ bool类型:默认值为false

5)数组使用的步骤:

​ 1、声明数组并开辟空间

​ 2、给数组各个元素赋值

​ 3、使用数组

6)数组的下标是从0开始

7)数组下标必须在指定范围内使用

8)Go的数组是值类型,默认情况下是值传递,因此会进行值拷贝数组间不会互相影响

9)如果在其它函数中,去修改原来的数组,可以使用引用传递(指针方式)

10)长度是数组类型的一部分,在传递函数参数时,需要考虑数组的长度

//函数通过指针改变数组的值
func test(intArr *[3]int) {
    
    
	(*intArr)[0] = 1		//注意如何进行取值
	(*intArr)[1] = 2
	(*intArr)[2] = 3
	for index, value := range *intArr {
    
    
		fmt.Printf("index=%v value=%v\n", index, value)
	}
}

func main() {
    
    
	var intArr = [...]int{
    
    2: 900, 1: 700, 0: 600}
	for index, value := range intArr {
    
    
		fmt.Printf("%v--->%v\n", index, value)
	}
	test(&intArr)
	fmt.Println(intArr)
}

结果

0--->600
1--->700
2--->900
index=0 value=1
index=1 value=2
index=2 value=3
[1 2 3]
[1 2 3]

在这里插入图片描述

长度是数组类型的重要组成
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/IT_Carter/article/details/109903471