1.1Go语言特性
1.高性能、高并发 2.语法简单、学习曲线平缓:一个熟练的的开发者可以在一周时间使用Go从无到有的进行开发
3.丰富的标准库:Go的标准库功能强大 4.完善的工具链
5.静态链接 6.快速编译:Go拥有静态语言中几乎最快的编译速度
7.跨平台
8.垃圾回收
1.2 Go语言入门
开发环境:安装Go语言——Golang,安装IDE(VSCode,Goland)
1.2.2Go的基础语法——以Hello World为例
Go的Hello World大概长这样子:
package main //代表这个文件属于main包的一部分,main包是程序入口包
import (
"fmt"
) //导入标准库中的format包,主要功能是屏幕输入输出字符串,字符串格式化等
func main(){//这是main函数
fmt.Println("hello world!")//调用了fmt包中的Println来进行字符串输出
}
//要注意的是,此处导入包以及输出字符串都是用的双引号
运行结果:
1.2.3 Go的基础语法——变量
Go语言是一种强类型语言,每一个变量都有其自己的类型
常见的包括:字符串,整数型,浮点型,布尔型等
Go语言中字符串是内置类型,可以直接使用加号进行拼接,也可以直接使用等号进行是否相等的比较
package main
import(
"fmt"
"math"
)
func main(){
var a="initial"
var b,c int=1,2
var d=true
var e float64
f :=float32(e)
g:=a+"foo"//使用“+”连接两个字符串
fmt.Println(a,b,c,d,e,f)
fmt.Println(g)
const s string="constant"
const h=50000000
const i=3e20/h
fmt.Println(s,h,i,math.Sin(h),math.Sin(i))
}
重点看一下Golang里面变量的声明:
在Golang里面,有两种变量声明方法
1.var name =value 一般会自动推导类型,有时也可以自己写出类型,如:
var e float64
- 另一种方法是name :=value来进行声明,如:g :=a+”foo”
常量的声明:将var改为const即可,值得注意的是,常量没有固定的类型,它会根据使用的上下文自动确定类型
1.2.3Go的基础语法——分支结构if else
Go中的if else与C++的非常类似,有两点不同:
1.选择条件不需要加括号,加上了编译器会自动去掉
2.if else后面必须是大括号,不能像C++一样将语句写在同一行
package main
import (
"fmt"
)
func main() {
//输入一个数字
var num int
fmt.Println("请输入一个数字:")
fmt.Scanln(&num)
//判断奇偶
if num%2 == 0 { //在Go中,if语句的条件表达式没有括号,但是大括号是必须的,不能像C语言那样省略
fmt.Println("这是一个偶数")
} else {
fmt.Println("这是一个奇数")
}
}
1.2.4Go的基础语法——循环结构
Go中的for循环有多种:
1.最简单的 for加大括号什么也不写,代表一个死循环
2.最经典的
for i:=0;i<5;i++{
fmt.Println(i)
}
for循环的条件分三段,每一段都可以省略
在循环中可以用continue进入下一次循环,也可以使用break结束本轮循环
1.2.5Go的基础语法——分支结构switch
Go的switch结构与C++也类似
需要注意的是,在每个case中如果C++不加break就会往下执行,而Golang不会,就不需要加break
var a int
fmt.Println("请输入数字a)
fmt.Scanln(&a)
switch a {
case 1:
fmt.Println("a=1")
case 2:
fmt.Println("a=2")
default:
fmt.Println("a不等于1也不等于2")
}
switch可以取代任意的if else语句,在switch后面不加变量,case后面加条件即可
t:=time.Now()
switch{
case t.Hour()<12:
fmt.Println("not noon")
default:
fmt.Println("after noon")
}
1.2.6Go的基础语法——数组
数组是一个长度固定的元素序列
在Go中声明数组是有两种方式:
声明但不赋值:var name [length] class
声明且赋值:name;=[length]class{value1,value2….valuen}
package main
import (
"fmt"
)
func main() {
var a [5]int
a[4] = 100 //这里为什么不是:=100呢?因为 := 是声明并赋值,而这里a[4]已经声明了,所以只需要赋值就可以了
fmt.Println(a[4], len(a))
b := [5]int{1, 2, 3, 4, 5}
fmt.Println(b)
var twoD [2][3]int
for i := 0; i < 2; i++ {
for j := 0; j < 3; j++ {
twoD[i][j] = i + j
}
}
fmt.Println("2D:", twoD)
}
在实际开发中我们一般不使用数组,因为其长度是固定的
1.2.7Go的基础语法——切片
切片是可变长度的数组,你可以任意时刻去更改长度
在Golang中切片可以使用make关键字来进行声明,也可以声明并赋值的方式创建。
s:=make([]class,length)/var s=make([]class,length)
s:=[]class{value1,…valuen}
package main
import (
"fmt"
)
func main() {
//切片的学习
//使用make创建切片
var s1 = make([]string, 3)
s1[0] = "a"
s1[1] = "b"
s1[2] = "c"
fmt.Println("get", s1[2])
fmt.Println("len", len(s1))
//使用append添加元素
s1 = append(s1, "d")
s1 = append(s1, "e", "f")
fmt.Println("apd", s1)
//使用copy复制切片
s2 := make([]string, len(s1))
copy(s2, s1)
fmt.Println("cpy", s2)
//切片支持通过slice[low:high]语法进行“切片”操作
fmt.Println("sl1", s1[2:5])
fmt.Println("sl2", s1[:5])
fmt.Println("sl3", s1[2:])
//声明并初始化一个切片
t := []string{"g", "h", "i"} //这里的[]string是必须的,不然会报错,相当于不固定长度的数组
fmt.Println("dcl", t)
//切片可以组成多维数据结构
twoD := make([][]int, 3)
for i := 0; i < 3; i++ {
innerLen := i + 1
twoD[i] = make([]int, innerLen)
for j := 0; j < innerLen; j++ {
twoD[i][j] = i + j
}
}
fmt.Println("2D", twoD)
}
切片允许类似python列表的索引[start:end:step]
1.2.8Go的基础语法——map
map即键值对类型,相当于HashMap
在Golang里map有两种创建方式:
1.使用make去创建空map
m:=make(map[key_class]value_class)
要注意的是空map只能使用make关键字创建,不能用var+map
2.声明并赋值
m:=map[key_class]value_class{k1:v1,…kn:vn}
使用以key为索引的map获取对应的value值
package main
import (
"fmt"
)
func main() {
//map的声明
m1 := make(map[int]string) //[]里面是key的类型,后面是value的类型
m1[1] = "mike"
m1[2] = "go"
fmt.Println(m1)
//map的声明和初始化
m2 := map[int]string{1: "mike", 2: "go"}
fmt.Println(m2)
fmt.Println("one", m2[1])
//map的删除,删除key为1的元素
delete(m2, 1)
//map的遍历
for key, value := range m2 {
fmt.Println(key, value)
}
//map在遍历的时候是一个随机的顺序
//在map中一个key只能对应一个value,如果要对应多个value,可以将value设置为一个切片
r, ok := m2[1]
fmt.Println(r, ok) //如果key存在,ok为true,否则为false
//空map只能用make来声明,不能用var来声明
}
1.2.9Go的基础语法——range
对于一个slice或者map可以使用range来进行遍历,这样可以使得代码更加简洁
for i,num:=range nums(索引和值)
for key,value:=range map(键与值)
package main
import "fmt"
func main() {
nums := []int{2, 3, 4}
sum := 0
for i, num := range nums {
sum += num
if num == 2 {
fmt.Println("index:", i, "num:", num)
}
}
fmt.Println("sum:", sum)
m := map[string]string{"苹果": "apple", "香蕉": "banana"}
for k, v := range m {
fmt.Println("k:", k, "v:", v)
}
}
1.2.10Go的基础语法——函数
在Golang中函数与C++类似,但其基本类型是后置的
func name(name class,…..) (class1,….classn)
在Golang中函数可以返回多个值,需要与声明的类型一一对应
package main
import "fmt"
func add(a int, b int) int {
return a + b
}
func add2(a, b int) int {
return a + b
}
func exists(m map[string]string, k string) (string, bool) {
v, ok := m[k] //如果key存在,ok为true,否则为false,前面的_表示不需要这个值
return v, ok
}
func main() {
res := add(1, 2)
fmt.Println(res)
v, ok := exists(map[string]string{"a": "apple", "b": "banana"}, "a")
fmt.Println(v, ok)
v2, _ := exists(map[string]string{"a": "apple", "b": "banana"}, "a")
fmt.Println(v2)
}
Golang中可以使用_来表示不在下方使用的变量
1.2.11Go的基础语法——指针
当进行函数传参,参数为不可变类型时,会产生值传递,函数不改变原有的变量值(因为是对拷贝对象进行操作)
这时如果传入指针变量就会产生引用传递
在Golang中指针的声明
var p *int=&a
package main
import (
"fmt"
)
func add_2(a int) {
a += 2
}
func add2pt(a *int) { //这里的*表示指针类型,a为指针变量
*a += 2 //此处*为解引用操作符,表示取指针所指向的变量的值
}
func main() {
var a int = 3
add_2(a)
fmt.Println(a)
add2pt(&a) //&为取地址操作符,表示取变量的地址
fmt.Println(a)
var b *int = &a //指针变量的声明
fmt.Println(b)//打印b的值
fmt.Println(*b)//打印b所指向的变量的值
}
几个注意的点:
在指针声明中:*class表示某种变量类型的指针,&表示取地址符,用于取得某变量的地址
而在变量获取中:*为解引用符,后面跟指针表示取出指针所指变量
在Golang中切片的传参为引用传递,而且不会丢失长度
package main
import (
"fmt"
)
// 原来var nums []int是一个切片,不是数组
func Sort(nums []int) {
for i := 0; i < len(nums); i++ {
for j := 0; j < len(nums)-i-1; j++ {
if nums[j] > nums[j+1] {
nums[j], nums[j+1] = nums[j+1], nums[j]
}
}
}
}
func Sort_1(nums []int, len int) {
for i := 0; i < len; i++ {
for j := 0; j < len-i-1; j++ {
if nums[j] > nums[j+1] {
nums[j], nums[j+1] = nums[j+1], nums[j]
}
}
}
}
func main() {
var nums []int //这里的nums是一个切片,不是数组
//检验nums是否为切片,是的话,输出true
fmt.Println("nums是否为切片:", nums == nil)
var len int
fmt.Println("请输入数组长度:")
fmt.Scanln(&len)
for i := 0; i < len; i++ {
var num int
fmt.Println("请输入数组元素:")
fmt.Scanln(&num)
nums = append(nums, num)
}
fmt.Println("排序前:", nums)
Sort(nums)
fmt.Println("Sort排序后:", nums)
Sort_1(nums, len)
fmt.Println("Sort_1排序后:", nums)
//两个排序结果一样,说明在Golang中,切片的引用传递不会使得长度丢失
//但是在C语言中,数组的引用传递会使得长度丢失
}
//在Golang中数组是指针吗?不是,数组是值传递,切片是引用传递
1.2.12Go的基础语法——结构体
结构体是多种类型字段的集合
结构体的声明为:type name struct{}
结构体中包含多个字段
可以使用结构体名称来初始化一个结构体变量,同时要传入初始值;也可使用var来创建一个空结构体变量再对各个字段赋值
可以使用var_name.feature_name来获取某结构体变量的特定字段的属性值
结构体作为参数传入时也有两种用法:指针与非指针
以指针形式传入时可以实现对结构体的修改
结构体可以定义一些结构体方法,类似于成员方法
使用func (var_name struct_name/var_name *struct_name) func_name(parameters) class{}
上面的var_name其实相当于self
定义之后可直接使用var_name.func_name去调用某结构体变量的结构体方法
package main
import (
"fmt"
)
type user struct {
name string
password string
}
func (u *user) checkPassword(password string) bool {//这里的*表示指针类型,u为指针变量,这样可以修改u的值
return u.password == password
}
func main() {
a := user{"tom", "2568"}
b := user{name: "mike", password: "1024"}
var c user
c.name = "jerry"
c.password = "2048"
fmt.Println(a.checkPassword("2568"))
fmt.Println(b.checkPassword("2568"))
fmt.Println(c.checkPassword("2048"))
}
1.2.13Go的基础语法——错误处理
在Golang中,错误处理是使用一个单独的返回值来说明错误信息
在函数中新增一个error类型的返回值,使用名称加括号(里面使用字符串填充错误信息)初始化
使用if选择分支进行处理,当err!=nil时输出错误信息,反之则进行下一步
package main
import (
"errors"
"fmt"
)
type user struct {
name string
password string
}
func findName(users []user, name string) (*user, error) { //返回一个指向user类型的指针和一个error类型表示错误
for _, user := range users {
if user.name == name {
return &user, nil //nil在go语言中表示空
}
}
return nil, errors.New("can not find the user") //我们不能将nil用作返回user类型,
//因为nil并不是user类型,所以我们可以返回一个指向user类型的指针,这样就可以返回nil了
}
func main() {
users := []user{{"tom", "2568"}, {"mike", "1024"}, {"jerry", "2048"}}
user1, err := findName(users, "tom")
if err != nil {
panic(err)
}
println(user1.name)
userlist := make([]user, 0)
userlist = append(userlist, user{"tom", "2568"})
userlist = append(userlist, user{"mike", "1024"})
user2, err2 := findName(userlist, "li")
if err2 != nil {
fmt.Println(err2)
}
println(user2.name)
}
1.2.14Go的基础语法——字符串操作
在Golang中有对字符串进行操作的库——strings
下面有一些方法
package main
import (
"fmt"
"strings"
)
func main() {
//字符串操作
var str string = "hello world"
//是否含有
fmt.Println(strings.Contains(str, "hello"))
//字符数目
fmt.Println(strings.Count(str, "l"))
//前缀
fmt.Println(strings.HasPrefix(str, "hello"))
//后缀
fmt.Println(strings.HasSuffix(str, "world"))
//重复
fmt.Println(strings.Repeat(str, 2))
//替换
fmt.Println(strings.Replace(str, "world", "golang", 1)) //最后一个参数表示替换几个,如果为-1表示全部替换
//分割
fmt.Println(strings.Split(str, " "))
//大小写
fmt.Println(strings.ToLower(str))
fmt.Println(strings.ToUpper(str))
//去除两头的空格
fmt.Println(strings.TrimSpace(" hello world "))
//去除两头的指定字符
fmt.Println(strings.Trim(" hello world ", " "))
//去除左边的指定字符
fmt.Println(strings.TrimLeft(" hello world ", " "))
//去除右边的指定字符
fmt.Println(strings.TrimRight(" hello world ", " "))
//判断是否为空
fmt.Println(strings.TrimSpace(" "))
//长度
fmt.Println(len(str))
}
1.2.15Go的基础语法——字符串格式化
在Golang中可以使用fmt.Printf进行字符串的格式化输出,类似于C语言的printf函数
但是在Golang中可以用%v来打印任意类型的变量,而不需要区分类型
对于打印一个结构体可以使用%+v来使得信息更加详细,而%#v的话更进一步
对于浮点数保留x位小数,可以使用%.xf来实现
package main
import "fmt"
type location struct {
x int
y int
}
func main() {
s := "hello"
a := 123
l := location{1, 2}
fmt.Printf("%v\n", s)
fmt.Printf("%v\n", a)
fmt.Printf("%v\n", l)
fmt.Printf("%+v\n", l)
fmt.Printf("%#v\n", l)
f := 3.1415926
fmt.Printf("%.5f\n", f)
fmt.Printf("%T\n", f) //这里的T表示类型
//按总长度为10,右对齐,不足部分用空格补齐的形式输出
fmt.Printf("%10d\n", a) //这里的d表示十进制整数
}
//在格式化串中\n表示换行,还有一些其他的转义字符
//占位符是%v,表示按照变量的值输出,如果是结构体,会按照字段顺序输出
//好像golang除了v之外也可以用c语言的占位符,比如%d,%f,%s等等
1.2.16Go的基础语法——JSON处理
只要保证在结构体里每一个字段的第一个字母是大写,这个结构体的变量就可以用json.Marshal来进行json化(buf,err=json.Marshal(var_name),要将json打印则需要将buf转为字符串),同样的json可以通过json.Unmarshal反json化转化为结构体变量(err=json.Unmarshal(buf,&(你要将buf转化为的类型的同类型变量))
如果需要改变大小写,则需要在结构体字段的后面加上json:”小写”的tag,如‘json:”age“’
使用json.MarshalIndent()
package main
import (
"encoding/json"
"fmt"
)
type userInfo struct {
Name string `json:"name"`
Age int `json:"age"` //这里的json表示在json中的字段名
Hobby []string `json:"hobby"`
}
func main() {
a := userInfo{"tom", 18, []string{"basketball", "football"}}
buf, err := json.Marshal(a)
if err != nil {
panic(err)
}
fmt.Println(buf)
fmt.Println(string(buf))
buf, err = json.MarshalIndent(a, "", "\t") //MarshalIndent可以将json格式化输出
if err != nil {
panic(err)
}
fmt.Println(string(buf))
var b userInfo
err = json.Unmarshal(buf, &b) //Unmarshal将json转换为结构体,用法为Unmarshal(json, &struct)
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", b)
}
1.2.17Go的基础语法——时间处理
Golang中的time库有很多关于事件处理的方法
最常用的事件处理:time.Now()
也可以使用time.Date()构造一个带时区的时间
使用t.Year(),t.Month()等去获取时间相关信息
可以使用t.Format()来对时间变量进行格式化(使用一个固定的时间)
使用t.Sub()计算两个时间点的差距
使用time.Parse()将时间字符串解析成时间变量(前面还是以那个固定的时间作为format)
使用now:=time.Now()
now.Unix()获取时间戳
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
fmt.Println(now)
t := time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC) //这里的time.UTC表示使用UTC时区
fmt.Println(t)
fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), t.Location())
fmt.Println(t.Format("2006-01-02 15:04:05")) //这里的2006-01-02 15:04:05是固定的,不能改变
diff := now.Sub(t)
fmt.Println(diff)
fmt.Println(diff.Hours(), diff.Minutes(), diff.Seconds(), diff.Nanoseconds())
t2, err := time.Parse("2006-01-02 15:04:05", "2019-01-01 00:00:00")
if err != nil {
panic(err)
}
fmt.Println(t2)
fmt.Println(now.Unix())//时间戳
}
1.2.18Go的基础语法——数字解析
在Golang中使用strconv库来进行数字与字符串的转换
使用ParseInt或ParseFloat来解析一个字符串
ParseInt三个参数:字符串,进制(如果传0为自动推测),精度大小(64,32)
可以使用Atoi来快速将一个十进制字符串转化为数字
可能会报错,所以需要有两个变量(包括_)来接收返回值
package main
import (
"fmt"
"strconv"
)
func main() {
a, _ := strconv.ParseInt("123", 10, 64)
fmt.Println(a)
b, _ := strconv.ParseInt("0x1000", 0, 64)
fmt.Println(b)
c, _ := strconv.ParseFloat("3.1415926", 64)
fmt.Println(c)
d, err := strconv.Atoi("AAA")
if err != nil {
fmt.Println(err)
}
fmt.Println(d)//这里的d是0,因为转换失败了,变量的默认值是0
}
1.2.19Go的基础语法——进程信息
使用os库来获取进程相关信息
os.Args来获取进程执行时的一些命令行参数
可以使用os.Getenc和os.Setenv来获取和写入环境变量
使用exec.Command()来快速启动子进程,进行控制台命令执行并获取输出
package main
import (
"fmt"
"os"
"os/exec"
)
func main() {
fmt.Println(os.Args)
fmt.Println(os.Getenv("PATH")) //这里的PATH是环境变量,不是参数
//可以使用exec包来执行外部命令
buf, err := exec.Command("grep", "127.0.0.1", "D:\\GoWorkSpace\\project1\\jinchneg.go").CombinedOutput()
if err != nil {
fmt.Println(err)
}
fmt.Println(string(buf))
}