# Day01 基本数据类型
# 本节关键词
搭建Golang环境
变量类型初始化
字符串本质 byte与rune
流程控制
# 搭建开发环境
# 下载地址
- Go官网下载地址:https://golang.org/dl/ (opens new window)
- Go官方镜像站(推荐):https://golang.google.cn/dl/ (opens new window)
如 Mac 版本,点击下一步安装到 /usr/local/go
# 查看Go版本:
❯ go version
go version go1.19 darwin/arm64
# 配置GOPROXY
GOROOT="/usr/local/go"
安装 Go 开发包路径GOPATH="/Users/nining/go"
Go 1.14版本开始启用Go Moudule模式后,不一定非要将代码写到GOPATH目录下,保持默认值即可- 配置
GOPROXY
国内可以访问
go env -w GOPROXY=https://goproxy.cn,direct
# VSCode安装Go插件:
- Chinese(简体中文)
- Go(Rich Go language support for Visual Studio Code)
- IntelliJ IDEA Keybindings (快捷键)
- 点右下角 Install All
# 创建第一个Go程序
- 新建项目目录 hello
进到目录中编写一个main.go文件
执行初始化命令:
go mod init hello
- hello是你的项目名
- 只需要在创建项目的时候在项目的根目录下执行一次!
- 一个可执行的main.go程序
package main // 声明 main 包,表明当前是一个可执行程序
import "fmt" // 导入内置 fmt 包
func main(){ // main函数,是程序执行的入口
fmt.Println("Hello World!") // 在终端打印 Hello World!
}
- 编译可执行程序
在项目的根目录下执行
go build
指定编译后的文件名称
go build -o xxx
# 多个go文件
一个go项目下可以存在多个go文件。
此时,如果使用go run
执行,那么就需要把所有的源文件都带上。
go run const.go int.go hello.go
否则就容易出现以下问题:
❯ go run hello.go
# command-line-arguments
./hello.go:63:14: undefined: c4
./hello.go:64:14: undefined: d3
./hello.go:64:18: undefined: d4
无论是VsCode还是Goland 推荐大家一个窗口打开一个项目!
无论是VsCode还是Goland 推荐大家一个窗口打开一个项目!
无论是VsCode还是Goland 推荐大家一个窗口打开一个项目!
# 变量 var
变量(Variable)的功能是存储数据
- Go语言中每一个变量都有自己的类型,并且变量必须经过声明才能开始使用
- Go语言中同一作用域内不支持重复声明,并且Go语言的变量声明后必须使用
package main
import "fmt"
var version string
var age11 = 18
// name12 := "小王子" // 函数外语句必须以关键字开头
func main() {
fmt.Println("Hello world!")
/*
多行注释
*/
// 变量的声明
var name string // 声明变量
// 批量声明
var (
age int // 0
isOk bool // false
)
// var age int
// var isOk bool
age = 100 // 变量赋值
fmt.Println(name, age, isOk)
var age2 int = 18 // 声明变量并赋值
fmt.Println(age2)
// 没有指定类型?类型推导
var name3, age3 = "linda", 28
// var (
// name3 string = "linda"
// age3 int = 28
// )
fmt.Println(name3, age3)
var age4 int8 = 28 // 如果不想用编译器推导的类型,就需要显式指定变量的类型
fmt.Println(age4)
// 双引号表示字符串,单引号表示字符
var x byte = 'a' // 字符
var s string = "a" // 字符串
fmt.Println(x, s)
// 短变量声明
s2 := "linda" // var s2 string s2="linda"
fmt.Println(s2)
s2 = "小王子"
fmt.Println(s2)
// var x2 string
// x2 = 18 // 只能给变量赋正确类型的值
// fmt.Println(x2)
}
- 每个变量会被初始化成其类型的默认值。整型和浮点型变量的默认值为
0
,字符串变量的默认值为空字符串
, 布尔型变量默认为false
,切片、函数、指针变量的默认为nil
- 函数外的每个语句都必须以关键字开始(var、const、func等),也就是 :=不能使用在函数外
- 匿名变量不占用命名空间,不会分配内存,所以匿名变量之间不存在重复声明,_多用于占位,表示忽略值
# 常量 const
相对于变量,常量是恒定不变的值,常量在定义的时候必须赋值
// const.go
package main
const pi = 3.14
// v v2 v3 v4都是相同的值
const (
v = "v1.0"
v2
v3
v4
)
const (
week1 = 1
week2 = 2
week3 = 3
)
const (
n1 = iota // 0
n2 // 1
n3
n4
n5
)
const (
z1 = iota // 0
)
// 利用iota声明存储的单位常量
const (
_ = iota // 0
KB = 1 << (10 * iota) // 1<<10 <=> 10000000000
MB = 1 << (10 * iota) // 1<<20
GB = 1 << (10 * iota) // 1<<30
TB = 1 << (10 * iota)
PB = 1 << (10 * iota)
)
// 声明中插队
const (
c1 = iota // 0
c2 // 1
c3 = 100 // 插队
c4 = iota // 3
)
const (
d1, d2 = iota + 1, iota + 2 // 1,2
d3, d4 = iota + 1, iota + 2 // 2,3
)
iota
是Go语言的常量计数器
iota
在const关键字出现时将被重置为0- const中每新增一行常量声明将使
iota
计数一次(iota
可理解为const语句块中的行索引,即使iota没出现在那一行也会加1)
# 基本数据类型
# 整型
整型分为以下两个大类:
- 按长度分为:int8、int16、int32、int64
- 对应的无符号整型:uint8、uint16、uint32、uint64
- 另外uint8就是byte型,unit和int根据平台32位和64位不同而不同
var (
i1 int8 = 0b1001 // 二进制
i2 uint64 = 0o644 // 八进制
i3 = 0x123 // 十六进制 默认为int
i4 int32 = 123_456 // _分隔让数字更直观
)
// 利用fmt.Printf 格式化打印
v11 := 123
fmt.Println("自带换行")
fmt.Printf("十进制:%d \n", v11)
fmt.Printf("二进制:%b\n", v11)
fmt.Printf("八进制:%o\n", v11)
fmt.Printf("十六进制:%x\n", v11)
# 浮点型
- Go语言支持两种浮点型数:
float32
和float64
计算机中浮点数都是不精确的!
计算机中浮点数都是不精确的!
计算机中浮点数都是不精确的!
实际写业务遇到浮点数运算都是转成整型来计算的
func f1() {
fmt.Printf("%.2f\n", math.MaxFloat32)
}
# 布尔型
Go语言中以bool
类型进行声明布尔型数据,布尔型数据只有true(真)
和false(假)
两个值
var b11 = true
var b12 bool // false
注意:
- 布尔类型变量的默认值为
false
; - Go 语言中不允许将整型强制转换为布尔型;
- 布尔型无法参与数值运算,也无法与其他类型进行转换;
# 字符串
Go语言里的字符串的内部实现使用UTF-8
编码,字符串的值为双引号(")中的内容,多行时反引号(`)
package main
import (
"fmt"
"strings"
)
func f2() {
// filename 表示windows下一个文件路径
filename := "C:\\go\\hello\\hello.exe"
fmt.Println(filename)
s11 := "永远不要高估自己"
fmt.Println(s11)
s12 := "\"永远不要高估自己\""
fmt.Println(s12)
// 多行字符串
s13 := `多行
字符串
测\n试
`
fmt.Println(s13)
// 字符串操作
fmt.Println(len(s11))
// 字符串拼接
name1 := "linda"
value1 := "过年好"
fmt.Println(name1 + value1)
ret := fmt.Sprintf("大家好,%s祝大家%s", name1, value1)
fmt.Println(ret)
// strings
s14 := "你:好:呀"
fmt.Println(strings.Split(s14, ":"))
fmt.Println(strings.Contains(s14, "你"))
fmt.Println(strings.HasPrefix(s14, "你:")) // true
fmt.Println(strings.HasSuffix(s14, "啊")) // false
fmt.Println(strings.Index(s14, ":")) // 3
fmt.Println(strings.LastIndex(s14, ":")) // 7
// 拼接
slice1 := []string{"你", "我", "他"}
fmt.Println(strings.Join(slice1, "-"))
// 字符和字符串
y1 := '中' // 字符
y2 := "中" // 字符串
fmt.Println(y1, y2)
// byte 和rune
fmt.Println([]rune(s14))
fmt.Println([]byte(s14))
// for range循环
idx := 0
for _, r := range s14 { // rune表示一个汉字
if r == ':' {
fmt.Println(idx)
break
}
idx++
}
}
- len("你好") --> 6
# 字符
组成每个字符串的元素叫做"字符",可以通过遍历或者单个获取字符串元素获得字符,Go 语言的字符有以下两种:
- uint8类型,或者叫
byte
型,代表一个ASCII码字符 - int32类型,或者叫
rune
类型,代表一个 UTF-8字符
byte: 常见的a、b、c等字符
rune: 是用来表示中文、日文等复合字符的
// 遍历字符串
func traversalString() {
s := "hello世界"
for i := 0; i < len(s); i++ { //byte
fmt.Printf("%v(%c) ", s[i], s[i])
}
fmt.Println()
for _, r := range s { //rune
fmt.Printf("%v(%c) ", r, r)
}
fmt.Println()
}
要修改字符串,需要先将其转换成[]rune
或[]byte
,完成后再转换为string
,无论哪种转换,都会重新分配内存,并复制字节数组
func changeString() {
s1 := "big"
// 强制类型转换
byteS1 := []byte(s1)
byteS1[0] = 'p'
fmt.Println(string(byteS1))
s2 := "白萝卜"
runeS2 := []rune(s2)
runeS2[0] = '红'
fmt.Println(string(runeS2))
}
# 类型转换
只能在两个类型之间支持相互转换的时候使用
// 类型转换
// T()
func f3() {
var i11 int8 = 1
i12 := int64(i11) // int8 -> int64
fmt.Printf("i12: %T \n", i12) // int64
f11 := 12.34 // float64
f12 := int64(f11) // float64 -> int64
fmt.Printf("f12: %T \n", f12) // int64
// bool(1) // int -> bool 🚫
}
func sqrtDemo() {
var a, b = 3, 4
var c int
// math.Sqrt()接收的参数是float64类型,需要强制转换
c = int(math.Sqrt(float64(a*a + b*b)))
fmt.Println(c)
}
# 五种运算符
五种运算符
- 算术运算符
+ - * / %
- 关系运算符
== != > >= < <=
- 逻辑运算符
&& || !
- 位运算符 位运算符对整数在内存中的二进制位进行操作
&
参与运算的两数各对应的二进位相与,(两位均为1才为1)|
参与运算的两数各对应的二进位相或,(两位有一个为1就为1)^
参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1,(两位不一样则为1)<<
左移n位就是乘以2的n次方,a<<b 是把a的各二进位全部左移b位,高位丢弃,低位补0>>
右移n位就是除以2的n次方,a>>b 是把a的各二进位全部右移b位
- 赋值运算符
= += /= ...
注意:++
(自增)和--
(自减)在Go语言中是单独的语句,并不是运算符
# 补充问题:
很多很多个数字中,除了某个数字只出现一次外,其他数字均出现了两次。问如何找出只出现一次的数字?
// 一堆数找出只出现一次的那个
func f11() {
nums := []int{17, 4, 3, 3, 9, 11, 9, 11, 17}
if len(nums)%2 == 0 {
return
}
ret := nums[0]
for _, num := range nums[1:] {
ret ^= num // 异或
}
fmt.Println(ret)
}
# 流程控制语句
流程控制是每种编程语言控制逻辑走向和执行次序的重要部分,流程控制可以说是一门语言的"经脉"
# if语句
package main
import "fmt"
// if条件判断分支
func f5() {
score := 89 // 假设从数据库中查询出一个同学的分数
if score > 90 {
fmt.Println("A")
} else if score > 65 {
fmt.Println("勉强留下")
} else {
fmt.Println("明年再来")
}
fmt.Println(score)
}
func f6() {
// score只在if分支中有效
// 因为它只在if分支中声明了score,外部不可见
if score := 89; score > 90 {
fmt.Println("A")
} else if score > 65 {
fmt.Println(score)
fmt.Println("勉强留下")
} else {
fmt.Println("明年再来")
}
// fmt.Println(score)
}
# for语句
package main
import "fmt"
// for循环
func f7() {
// 1.标准for循环
for i := 0; i <= 10; i++ {
fmt.Println(i) // 0 1 2 ... 10
}
// fmt.Println(i) // 不可访问i
// 2.初始语句省略
i := 0
for ; i <= 10; i++ {
fmt.Println(i) // 0 1 2 ... 10
}
fmt.Println(i) // ? 11
// 3.初始语句和结束语句都可以省略
j := 0
for j < 10 { // 类似Python中 while j < 10: pass
fmt.Println(j)
j++ // 10
}
fmt.Println(j) // ? 10
// 4. 无限循环
for {
if j > 12 {
break // 跳出循环
}
fmt.Println("...")
j++
}
// for range 循环
s := "golang"
for i, v := range s {
fmt.Printf("%v:%c \n", i, v)
}
}
Go语言中可以使用for range
遍历数组、切片、字符串、map 及通道(channel)
- 数组、切片、字符串返回索引和值
- map返回键和值
- 通道(channel)只返回通道内的值
# switch语句
package main
import "fmt"
// switch
func f8() {
finger := 3 // 从外界获取的一个值
switch finger {
case 1:
fmt.Println("大拇指")
case 2:
fmt.Println("食指")
case 3:
fmt.Println("🖕🏻")
case 4:
fmt.Println("无名指")
case 5:
fmt.Println("小拇指")
default:
fmt.Println("无效的输入")
}
num := 9
switch num {
case 1, 3, 5, 7, 9:
fmt.Println("奇数")
case 2, 4, 6, 8:
fmt.Println("偶数")
}
switch {
case num%2 != 0:
fmt.Println("奇数")
case num%2 == 0:
fmt.Println("偶数")
default:
fmt.Println("num=0")
}
}
- 每个switch只能有一个default分支
- 一个分支可以有多个值,多个case值中间使用英文逗号分隔
fallthrough
语法可以执行满足条件的case的下一个case,即满足上下两个case
# goto语句
package main
import "fmt"
// goto 语句
func gotoDemo1() {
var breakFlag bool
for i := 0; i < 10; i++ {
for j := 0; j < 10; j++ {
if j == 2 {
// 设置退出标签
breakFlag = true
break
}
fmt.Printf("%v-%v\n", i, j)
}
// 外层for循环判断
if breakFlag {
break
}
}
}
func gotoDemo2() {
for i := 0; i < 10; i++ {
for j := 0; j < 10; j++ {
if j == 2 { // 退出整个两层for循环
goto breakLabel
}
fmt.Printf("%v-%v\n", i, j)
}
}
breakLabel:
//
}
# continue语句
package main
import "fmt"
func f9() {
for i := 0; i < 10; i++ {
if i%2 == 0 {
continue // 结束本轮循环,继续下一次循环
}
fmt.Println(i)
}
}
# 课后作业
- 九九乘法表
package main
import "fmt"
// 九九乘法表
func f10() {
for i := 1; i < 10; i++ {
for j := i; j < 10; j++ {
fmt.Printf("%d*%d=%d\t", j, i, j*i)
}
fmt.Println()
}
for i := 1; i < 10; i++ {
for j := 1; j <= i; j++ {
fmt.Printf("%d*%d=%d\t", j, i, j*i)
}
fmt.Println()
}
}
← Go课程笔记 Day02 数组切片映射 →