Go 语言快速开发入门(基础语法详解,第一节)
一、基础语法详解,第一节
1、HelloWorld
1.1、代码编写和解释
代码如下,对下图代码进行解释:
1、代码中的package main,表示这是可以执行main方法的文件,如果Test.go文件不放在main目录下,也可以写成package main,不过通常是放在main包中。
2、import “fmt” 表示引入一个包,包名 fmt, 引入该包后,就可以使用 fmt 包的函数,比如:fmt.Println。
3、func main() {} 表示一个main方法,类似java中的main方法,是go的入口。
4、fmt.Println(“hello,world”) 表示调用Println输出。
5、程序运行,如下图,右键单击即可运行。
1.2、go语言注意事项
- Go 源文件以 “go” 为扩展名。
- Go 应用程序的执行入口是 main()函数,但是main函数的所在包必须是package main。
- Go 语言严格区分大小写。
- Go 方法由一条条语句构成,每个语句后不需要分号(Go 语言会在每行后自动加分号),这也体现出 Golang 的简洁性。也就是说go的一行只能写一句代码。
- go 语言定义的变量或者 import 的包如果没有使用到,代码不能编译通过且开发工具会自定删除掉。
- 大括号都是成对出现的,缺一不可。且面朝右面的大括号左边必须有代码。
2、Go 语言的转义字符&&Go 语言注释
2.1、常用制表符
- \t : 表示一个制表符,通常使用它可以排版。
- \n :换行符
- \\ :一个\
- \" :一个"
- \r :一个回车 fmt.Println(“a\rb”);
2.2、注释
Golang 中注释有两种形式
1、行注释
//注释内容
2、块注释(多行注释)
/*
注释内容
*/
下图代码举例
func main() {
//一个制表符的距离
fmt.Println("刘备\t猴子")
//换行
fmt.Println("姜子牙\n高端局")
//\r回车, \r后面的覆盖掉前面的
fmt.Println("王者荣耀\r英雄联盟")
/*
这是多行注释
这是多行注释
*/
}
3、变量
3.1、变量声明方式
import "fmt"
/**
变量声明和使用
*/
//变量声明六:一次声明多个全局变量(方法外的变量是全局变量)
var (
n3 = 100
n4 = 400
name3 = "zg"
)
//一次声明一个全局变量
var name4 = "zgg"
func main() {
//变量声明方式一,如果不指定变量值,则使用默认值
var i int
i = 90
fmt.Println(i)
//变量声明方式二:根据值自行判定变量类型(类型推导)
var age = 20
fmt.Println(age)
//变量声明方式三:省略 var, 注意 :=左侧的变量不应该是已经声明过的,否则会导致编译错误,只能在当前函数作用域中使用,不能作为全局变量
name := "zhanggang"
fmt.Println(name)
//变量声明方式四: var 变量名 类型 = 表达式
var name2 string = "zhanggang"
fmt.Println(name2)
//变量声明方式五:一次声明多个变量
n1, n2, vmId := 1, 2, "fe7772b4-8dfc-4b23-8916-616d79c53b31"
fmt.Println(n1, n2, vmId)
fmt.Println(n3, n4, name3, name4)
}
3.2、常量声明方式
常量的定义: 相对于变量,常量是恒定不变的值,多用于定义程序运行期间不会改变的那些值。常量的声明和变量声明非常类似,只是把 var 换成了 const,常量在定义的时候必须赋值。
func main() {
//声明单个常量
const pi1 = 3.1415
//声明多个常量
const (
pi2 = 3.1415
e = 2.7182
)
//const 同时声明多个常量时,如果省略了值则表示和上面一行的值相同,N1=N2=N3=100
const (
N1 = 100
N2
N3
)
}
4、基本数据类型
Go 语言中数据类型分为: 基本数据类型和复合数据类型。
基本数据类型有: 整型、浮点型、布尔型、字符串
复杂数据类型有: 指针、数组、切片、结构体、函数、map、通道(channel)、接口等。
4.1、整型
1、整型分为以下两个大类
有符号整形按长度分为:int8、int16、int32、int64
对应的无符号整型:uint8、uint16、uint32、uint64
2、特殊整型
注意: 在使用 int 和 uint 类型时,不能假定它是 32 位或 64 位的整型,而是考虑int 和uint
可能在不同平台上的差异。在涉及到二进制传输、为了保持文件的结构不会受到不同编译目标平台字节长度的影响,不要使用 int 和 uint。
3、代码演示
import (
"fmt"
"unsafe"
)
/**
基本数据类型
*/
func main() {
//整数类型
var num = 123
fmt.Printf("值:%v 类型%T ", num, num)
//返回 num 变量占用的字节数
fmt.Println(unsafe.Sizeof(num))
}
4.2、浮点型
注意:
1、Go 语言支持两种浮点型数:float32 和 float64。
2、Go 语言中浮点数默认是 float64。
3、Golang 中 float 精度丢失,几乎所有的编程语言都有精度丢失这个问题,这是典型的二进制浮点数精度损失问题,在定长条件下,二进制小数和十进制小数互转可能有精度丢失。
代码演示
func main() {
num := 1.1
//Go 语言中浮点数默认是 float64
fmt.Printf("值:%v--类型:%T", num, num) //值:1.1--类型:float64
m1 := 8.2
m2 := 3.8
fmt.Println(m1 - m2) // 期望是 4.4,结果打印出了 4.399999999999999,这就是丢失精度
//Golang 科学计数法表示浮点类型
num8 := 5.1234e2 // ? 5.1234 * 10 的 2 次方
num9 := 5.1234e2 // ? 5.1234 * 10 的 2 次方 shift+alt+向下的箭头
num10 := 5.1234e-2 // ? 5.1234 / 10 的 2 次方 0.051234
fmt.Println("num8=", num8, "num9=", num9, "num10=", num10)
}
4.3、布尔值
Go 语言中以 bool 类型进行声明布尔型数据,布尔型数据只有 true(真)和false(假)两个值。
注意:
- 布尔类型变量的默认值为 false。
- Go 语言中不允许将整型强制转换为布尔型.
- 布尔型无法参与数值运算,也无法与其他类型进行转换。
import ( "fmt"
"unsafe"
)
func main() {
var b = true
fmt.Println(b, "占用字节:", unsafe.Sizeof(b))
}
4.4、字符串
注意: Go 语言中的字符串属于基础数据类型。
声明字符串代码演示
func main() {
//声明一个字符串变量
s1 := "hello"
//多行字符串,就是一个很长的字符串,需要换行,必须使用反引号字符
//反引号间换行将被作为字符串中的换行,但是所有的转义字符均无效,文本将会原样输出。
s2 := `你好
漂亮的go语言`
fmt.Println(s1, s2)
}
字符串API常用方法
func main() {
//len(str)求字符串的长度
var str = "this is str"
fmt.Println(len(str))
//拼接字符串
var str1 = "你好"
var str2 = "golang"
fmt.Println(str1 + str2)
var str3 = fmt.Sprintf("%v %v", str1, str2)
fmt.Println(str3)
//strings.Split 分割字符串
var strSpi = "123-456-789"
var arr = strings.Split(strSpi, "-")
fmt.Println(arr)
//判断一个字符串是否包含另一个字符串
var str4 = "this is golang"
var flag = strings.Contains(str4, "golang")
fmt.Println(flag)
//判断首字符尾字母是否包含指定字符
var str5 = "this is golang"
var flag2 = strings.HasPrefix(str5, "this")
fmt.Println(flag2)
}
4.5、基本数据类型默认值
代码演示
func main() {
var a int
var b float64
var c bool
var d string
fmt.Println(a, b, c, d)
}
5、复杂数据类型
复杂数据类型有: 指针、数组、切片、结构体、函数、map、通道(channel)、接口等。
5.1、指针
注意:
1、对于基本数据类型来说,变量存的就是值,也叫值类型,而指针就是指向值的一个地址。 举例说明下,var num int = 999,内存中会有两块地方存储,一块存储值10,另一块存储值10的内存地址。通过&num可以获取num的内存地址,var ptr *int = &num 这就是声明一个指向num内存的指针。
2、*int表示这个指针的类型
3、值类型都有对应的引用类型,
值类型包括:基本数据类型 int 系列, float 系列, bool, string 、数组和结构体struct
引用类型:指针、slice 切片、map、管道 chan、interface 等都是引用类型
代码演示
func main() {
var i int = 999
fmt.Println("i的地址是=", &i)
//*int表示这个指针的类型
var ptr *int = &i
fmt.Printf("ptr=%v\n", ptr)
fmt.Printf("ptr的地址是=%v\n", &ptr)
fmt.Printf("ptr指向的值是=%v", *ptr)
}
下图的指针在内存中的指向
一个小案列,通过指针修改变量的值
func main() {
var i int = 999
fmt.Println("i的值是=", i)
var ptr *int = &i
*ptr = 100 //在内存中,*ptr指向的值就是i的,所以能修改i的值
fmt.Println("i的值是=", i)
}
5.2、数组
5.2.1、一维数组
数组定义: var 数组变量名 [元素数量]T
定义一个长度为 3 元素类型为 int 的数组 b 并赋值。
var b [3]int
b[0] = 80
b[1] = 100
b[2] = 96
//根据数组下表获取值
fmt.Println(b[0])
数组的初始化 方法一
var testArray [3]int //数组会初始化为int 类型的零值var
var numArray = [3]int{1, 2} //使用指定的初始值完成初始化
var cityArray = [3]string{"北京", "上海", "深圳"} //使用指定的初始值完成初始化
fmt.Println(testArray) //[0 0 0]
fmt.Println(numArray) //[1 2 0]
fmt.Println(cityArray) //[北京 上海 深圳]
数组的初始化 方法二
按照方法一的方法每次都要确保提供的初始值和数组长度一致,一般情况下我们可以让编译器根据初始值的个数自行推断数组的长度。
var testArray [3]int
var numArray = [...]int{1, 2}
var cityArray = [...]string{"北京", "上海", "深圳"}
fmt.Println(testArray) //[0 0 0]
fmt.Println(numArray) //[1 2]
fmt.Printf("type of numArray:%T\n", numArray) //type of numArray:[2]int
fmt.Println(cityArray) //[北京 上海 深圳]
fmt.Printf("type of cityArray:%T\n", cityArray) //type of cityArray:[3]string
数组的初始化 方法三
使用下标索引初始化
a := [...]int{1: 1, 3: 5}
fmt.Println(a) // [0 1 0 5]
fmt.Printf("type of a:%T\n", a) //type of a:[4]int
遍历数组的两种写法
var a = [...]string{"北京", "上海", "深圳"}
// 遍历 1:for 循环遍历
for i := 0; i < len(a); i++ {
fmt.Println(a[i])
}
// 遍历 2:range 循环遍历
for index, value := range a {
fmt.Println(index, value)
}
5.2.2、二维数组
var 数组变量名 [元素数量][元素数量]T
二维数组定义
func main() {
a := [3][2]string{
{"北京", "上海"}, {"广州", "深圳"}, {"成都", "重庆"}}
fmt.Println(a) //[[北京 上海] [广州 深圳] [成都 重庆]]
fmt.Println(a[2][1]) //支持索引取值:重庆
}
二维数组遍历
func main() {
a := [3][2]string{
{"北京", "上海"}, {"广州", "深圳"}, {"成都", "重庆"}}
for _, v1 := range a {
for _, v2 := range v1 {
fmt.Printf("%s\t", v2)
}
fmt.Println()
}
}
注意:
1、Go 语言是支持多维数组的,我们这里是以二维数组为例。
2、多维数组只有第一层可以使用…来让编译器推导数组长度。
3、数组是值类型,传参相当于值拷贝,不改变原有的值。
5.3、切片
切片(Slice)是一个拥有相同类型元素的可变长度的序列。它是基于数组类型做的一层封装。它非常灵活,支持自动扩容。
因为数组的长度是固定的并且数组长度属于类型的一部分,所以数组有很多的局限性,所以我们才有了切片。
举例说明数组的权限性:
func main() {
a := [4]int{1, 2, 3, 4}
println(arraySum(a))
//b := [5]int{1, 2, 3, 4, 5}
//println(arraySum(b)) //错误,函数只能接受[4]int 类型,其他的都不支持。所以传入长度为5 的数组的时候就会报错。
}
func arraySum(x [4]int) int {
sum := 0
for _, v := range x {
sum = sum + v
}
return sum
}
切片定义
var name []T
name是变量名称
T是表示切片中的元素类型
注意: nil相等于java中的null, 当你声明了一个变量 , 但却还并没有赋值时 , go 会自动给你的变量赋值一个默认零值。这是每种类型对应的默认值,基本类型有自己对应的默认值,复杂类型默认都是nil
func main() {
// 声明切片类型
var a []string //声明一个字符串切片
var b = []int{} //声明一个整型切片并初始化
var c = []bool{false, true} //声明一个布尔切片并初始化
var d = []bool{false, true} //声明一个布尔切片并初始化
fmt.Println(a) //[]
fmt.Println(b) //[]
fmt.Println(c) //[false true]
fmt.Println(a == nil) //true
fmt.Println(b == nil) //false
fmt.Println(c == nil) //false
fmt.Println(c == d) //报错,切片是引用类型,不支持直接比较,只能和nil 比较
}
切片的循环遍历
func main() {
var a = []string{"北京", "上海", "深圳"}
// 方法 1:for 循环遍历
for i := 0; i < len(a); i++ {
fmt.Println(a[i])
}
// 方法 2:for range 遍历
for index, value := range a {
fmt.Println(index, value)
}
}
6、值类型和引用类型
值类型包括: 基本数据类型 int 系列, float 系列, bool, string 、数组和结构体struct。
引用类型: 指针、slice 切片、map、管道 chan、interface 等都是引用类型。
值类型概念:
变量直接存储值,内存通常在栈中分配。
引用类型概念:
变量存储的是一个地址,这个地址对应的空间才真正存储数据(值),内存通常在堆上分配,当没有任何变量引用这个地址时,该地址对应的数据空间就成为一个垃圾,由GC来回收。
7、运算符
7.1算术运算符
就是简单的加减乘除,下面是计算范例,这里就不用代码演示了
7.2关系运算符
7.3逻辑运算符
注意:
&&也叫短路与:如果第一个条件为 false,则第二个条件不会判断,最终结果为false。
||也叫短路或:如果第一个条件为 true,则第二个条件不会判断,最终结果为true。
7.4 赋值运算符
8、流程控制
8.1、if流程
代码演示
func main() {
age := 30
if age > 30 {
fmt.Println("大于30")
} else if age == 30 {
fmt.Println("等于30")
} else {
fmt.Println("小于30")
}
}
8.2、switch流程
9、函数和包
9.1函数
拥有一个返回值的函数
func main() {
n1 := 1
n2 := 2
//计算n1+n2并返回值
result := cal1(n1, n2)
fmt.Println(result)
}
func cal1(n1 int, n2 int) int {
i := n1 + n2
return i
}
拥有两个返回值的函数
func main() {
n1 := 1
n2 := 2
/**
计算n1+n2并返回值
判断n1是否大于n2
*/
result, flag := cal2(n1, n2)
fmt.Println(result, flag)
}
func cal2(n1 int, n2 int) (int, bool) {
i := n1 + n2
return i, n1 > n2
}
有参数没返回值
func main() {
cal("hello,函数")
}
func cal(str string) {
fmt.Println("cal函数被调用了", str)
}
没参数没返回值
func main() {
cal3()
}
func cal3() {
fmt.Println("cal函数被调用了")
}