GO语言学习笔记

GO语言学习笔记

1.GO语言基础

1.1常量声明

const limit = 512           
const top uint16 = 1421     
const Pi float64 = 3.1415926
const x,y int = 1,3 //多重赋值

const (
    Cyan = 0
    Black = 1
    White = 2
)

const (
    a = iota  //a == 0
    b = iota  //b ==1
    c = iota  //c == 2
)
const d = iota //d==0,因为const的出现,iota被重置为0

1.2变量声明

var a int
var b string
var c float64
var d [5] int  //数组
var e [] int   //数组切片
var f * int    //正确
var v1 int = 5 //正确
var v2 = 5     //正确,编译器自动推导出V2类型
v3 := 5        //正确,编译器自动推导出V3的类型

//多重赋值
i := 2
j := 3
i, j = j, i  //交换i和j的值,此时i == 3, j == 2

1.3 数据类型

1.3.1整型
类型说明
byte等同于 uint8
int依赖于不同平台下的实现,可以是 int32 或者 int64
int8[-128, 127]
int16[-32768, 32767]
int32[-2147483648, 2147483647]
int64[-9223372036854775808, 9223372036854775807]
rune等同于 int32
uint依赖于不同平台下的实现,可以是 uint32 或者 uint64
uint8[0, 255]
uint16[0, 65535]
uint32[0, 4294967295]
uint64[0, 18446744073709551615]
uintptr一个可以恰好容纳指针值的无符号整型(对 32 位平台是 uint32, 对 64 位平台是 uint64)

例子:

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    a := 12
    fmt.Println("length of a: ", unsafe.Sizeof(a))
    var b int = 12
    fmt.Println("length of b(int): ", unsafe.Sizeof(b))
    var c int8 = 12
    fmt.Println("length of c(int8): ", unsafe.Sizeof(c))
    var d int16 = 12
    fmt.Println("length of d(int16): ", unsafe.Sizeof(d))
    var e int32 = 12
    fmt.Println("length of e(int32): ", unsafe.Sizeof(e))
    var f int64 = 12
    fmt.Println("length of f(int64): ", unsafe.Sizeof(f))
}

Output:

length of a:  8
length of b(int):  8
length of c(int8):  1
length of d(int16):  2
length of e(int32):  4
length of f(int64):  8
1.3.2浮点型
类型说明
float32±3.402 823 466 385 288 598 117 041 834 845 169 254 40x1038 计算精度大概是小数点后 7 个十进制数
float64±1.797 693 134 862 315 708 145 274 237 317 043 567 981x1038 计算精度大概是小数点后 15 个十进制数
complex32复数,实部和虚部都是 float32
complex64复数,实部和虚部都是 float64
1.3.3布尔型
var a bool
a = true
b := (2 == 3) //b也会被推导为bool类型

//错误示范
var b bool
b = 1 //编译错误
b = bool(1) //编译错误
1.3.4字符串

Go 语言中的字符串是 UTF-8 字符的一个序列(当字符为 ASCII 码时则占用 1 个字节,其它字符根据需要占用 2-4 个字节)。UTF-8 是被广泛使用的编码格式,是文本文件的标准编码,其它包括 XML 和 JSON 在内,也都使用该编码。由于该编码对占用字节长度的不定性,Go 中的字符串也可能根据需要占用 1 至 4 个字节,这与其它语言如 C++、Java 或者 Python 不同。Go 这样做的好处是不仅减少了内存和硬盘空间占用,同时也不用像其它语言那样需要对使用 UTF-8 字符集的文本进行编码和解码。

Go 语言中字符串的可以使用双引号( " )或者反引号( ` )来创建。双引号用来创建可解析的字符串字面量,所谓可解析的是指字符串中的一些符号可以被格式化为其他内容,如\n在在输出时候会被格式化成换行符, 如果需要按照原始字符输出必须进行转义。而反引号创建的字符串原始是什么样,那输出还是什么,不需要进行任何转义。以下是几个例子:

语法描述
s += t将字符串 t 追加到 s 末尾
s + t将字符串 s 和 t 级联
s[n]从字符串 s 中索引位置为 n 处的原始字节
s[n:m]从位置 n 到位置 m-1 处取得的字符(字节)串
s[n:]从位置 n 到位置 len(s)-1 处取得的字符(字节)串
s[:m]从位置 0 到位置 m-1 处取得的字符(字节)串
len(s)字符串 s 中的字节数
len([]rune(s))字符串 s 中字符的个数,可以使用更快的方法 utf8.RuneCountInString()
[ ]rune(s)将字符串 s 转换为一个 unicode 值组成的串
string(chars)chars 类型是[]rune 或者[]int32, 将之转换为字符串
[ ]byte(s)无副本的将字符串 s 转换为一个原始的字节的切片数组,不保证转换的字节是合法的 UTF-8 编码字节
1.3.4.1格式化字符串

Go 语言标准库中的fmt包提供了打印函数将数据以字符串形式输出到控制台,文件,其他满足io.Writer接口的值以及其他字符串。目前为止我们使用了fmt.Printf和fmt.Println,对于前者的使用,就像 C 语言中的 printf 函数一样,我们可以提供一些格式化指令,让 Go 语言对输出的字符串进行格式化。同样的我们可以使用一些格式化修饰符,改变格式化指令的输出结果, 如左对齐等。常用的格式化指令如下:

格式化指令含义
%%%字面量
%b一个二进制整数,将一个整数格式化为二进制的表达方式
%c一个 Unicode 的字符
%d十进制数值
%o八进制数值
%x小写的十六进制数值
%X大写的十六进制数值
%U一个 Unicode 表示法表示的整形码值,默认是 4 个数字字符
%s输出以原生的 UTF-8 字节表示的字符,如果 console 不支持 UTF-8 编码,则会输出乱码
%t以 true 或者 false 的方式输出布尔值
%v使用默认格式输出值,或者使用类型的 String()方法输出的自定义值,如果该方法存在的话
%T输出值的类型

常用的格式化指令修饰符如下:

语法描述
空白如果输出的数字为负,则在其前面加上一个减号"-"。如果输出的是整数,则在前面加一个空格。使用 %x 或者 %X 格式化指令输出时,会在结果之间添加一个空格。例如 fmt.Printf("% X", “实”)输出 E5 AE 9E
%#o输出以 0 开始的八进制数据
%#x输出以 0x 开始的十六进制数据
+让格式化指令在数值前面输出+号或者-号,为字符串输出 ASCII 字符(非 ASCII 字符会被转义),为结构体输出其字段名
-让格式化指令将值向左对齐(默认值为像右对齐)
0让格式指令以数字 0 而非空白进行填充
1.3.5数组

Go 语言的数组是一个定长的序列,其中的元素类型相同。多维数组可以简单地使用自身为数组的元素来创建。数组的元素使用操作符号[ ]来索引,索引从 0 开始,到 len(array)-1 结束。数组使用以下语法创建:

[length]Type
[N]Type{value1, value2, …, valueN}
[…]Type{value1, value2, …, valueN}
如果使用了…(省略符)操作符,Go 语言会为我们自动计算数组的长度。在任何情况下,一个数组的长度都是固定的并且不可修改。数组的长度可以使用len()函数获得。由于数组的长度是固定的,因此数组的长度和容量都是一样的,因此对于数组而言cap()和len()函数返回值都是一样的。数组也可以使用和切片一样的语法进行切片,只是其结果为一个切片,而非数组。同样的,数组也可以使用range进行索引访问。

1.3.6切片

一般而言,Go 语言的切片比数组更加灵活,强大而且方便。数组是按值传递的(即是传递的副本),而切片是引用类型,传递切片的成本非常小,而且是不定长的。而且数组是定长的,而切片可以调整长度。创建切片的语法如下:

make([ ]Type, length, capacity)
make([ ]Type, length)
[ ]Type{}
[ ]Type{value1, value2, …, valueN}
内置函数make()用于创建切片、映射和通道。当用于创建一个切片时,它会创建一个隐藏的初始化为零值的数组,然后返回一个引用该隐藏数组的切片。该隐藏的数组与 Go 语言中的所有数组一样,都是固定长度,如果使用第一种语法创建,那么其长度为切片的容量capacity;如果是第二种语法,那么其长度记为切片的长度length。一个切片的容量即为隐藏数组的长度,而其长度则为不超过该容量的任意值。另外可以通过内置的函数append()来增加切片的容量。切片可以支持以下操作:

package main

import (
    "fmt"
)

func main() {
    a := [...]int{1, 2, 3, 4, 5, 6, 7}
    fmt.Printf("len and cap of array %v is: %d and %d\n", a, len(a), cap(a))
    fmt.Printf("item in array: %v is:", a)
    for _, value := range a {
        fmt.Printf("% d", value)
    }

    fmt.Println()

    s1 := a[3:6]
    fmt.Printf("len and cap of slice: %v is: %d and %d\n", s1, len(s1), cap(s1))
    fmt.Printf("item in slice: %v is:", s1)
    for _, value := range s1 {
        fmt.Printf("% d", value)
    }

    fmt.Println()

    s1[0] = 456
    fmt.Printf("item in array changed after changing slice: %v is:", s1)
    for _, value := range a {
        fmt.Printf("% d", value)
    }

    fmt.Println()

    s2 := make([]int, 10, 20)
    s2[4] = 5
    fmt.Printf("len and cap of slice: %v is: %d and %d\n", s2, len(s2), cap(s2))
    fmt.Printf("item in slice %v is:", s2)
    for _, value := range s2 {
        fmt.Printf("% d", value)
    }

    fmt.Println()
}

以上代码中,我们首先创建了一个数组,数组的长度是由 Go 语言自动计算出的(省略号语法),然后通过切片操作从数组a中创建了切片s1,接着我们修改了该切片的第一个位置的数值,然后发现数组a中的值也发生了变化。最后我们通过make()函数创建了一个切片,该切片的长度和容量分别为 10 和 20,还可以发现 Go 语言将未初始化的项自动赋予零值。运行代码输出如下:

len and cap of array [1 2 3 4 5 6 7] is: 7 and 7
item in array: [1 2 3 4 5 6 7] is: 1 2 3 4 5 6 7
len and cap of slice: [4 5 6] is: 3 and 4
item in slice: [4 5 6] is: 4 5 6
item in array changed after changing slice: [456 5 6] is: 1 2 3 456 5 6 7
len and cap of slice: [0 0 0 0 5 0 0 0 0 0] is: 10 and 20
item in slice [0 0 0 0 5 0 0 0 0 0] is: 0 0 0 0 5 0 0 0 0 0

1.4包

这一节我们介绍 Go 语言基础的最后一个知识点——包。前面我们了解过 Go 语言组织代码的方式是包,包是各种类型和函数的集合。在包中,如果标示符(类型名称,函数名称,方法名称)的首字母是大写,那这些标示符是可以被导出的,也就是说可以在包以外直接使用。前面我们也提到了$GOPATH环境变量(指向一个或多个目录),以及其子目录src目录的,当我们使用import关键字导入包的时候,Go 语言会在$GOPATH,GOROOT目录中搜索包。

我们创建的自定义的包最好放在$GOPATH的src目录下,如果这个包只属于某个应用程序,可以直接放在应用程序源代码的子目录下,但如果我们希望这个包可以被其他的应用程序共享,那就应该放在$GOPATH的src目录下,每个包单独放在一个目录里,如果两个不同的包放在同一目录下,会出现名字冲突的编译错误。作为惯例,包的源代码应该放在一个同名的文件夹下面。同一个包可以有任意多的源文件,文件名的名字也没有任何规定

2.并发编程

你很可能从某种途径听说过 Go 语言。它越来越受欢迎,并且有充分的理由可以证明。 Go 快速、简单,有强大的社区支持。学习这门语言最令人兴奋的一点是它的并发模型。 Go 的并发原语使创建多线程并发程序变得简单而有趣。

2.1 并发与并行

并发指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,通过 cpu 时间片轮转使多个进程快速交替的执行。而并行的关键是你有同时处理多个任务的能力。并发和并行都可以是很多个线程,就看这些线程能不能同时被(多个)cpu 执行,如果可以就说明是并行,而并发是多个线程被(一个)cpu 轮流切换着执行。一个经典且通俗易懂的例子这样解释并发与并行的区别:并发是两个队列,使用一台咖啡机并行是两个队列,使用两台咖啡机。如果串行,一个队列使用一台咖啡机,那么哪怕前面那个人有事出去了半天,后面的人也只能等着他回来才能去接咖啡,这效率无疑是最低的。

2.2 协程

协程也叫轻量级线程。与传统的进程和线程相比,协程最大的优点就在于其足够“轻”,操作系统可以轻松创建上百万个协程而不会导致系统资源枯竭,而线程和进程通常最多不过近万个。而多数语言在语法层面上是不支持协程的,一般都是通过库的方式进行支持,但库的支持方式和功能不够完善,经常会引发阻塞等一系列问题,而 Go 语言在语法层面上支持协程,也叫goroutine。这让协程变得非常简单,让轻量级线程的切换管理不再依赖于系统的进程和线程,也不依赖 CPU 的数量。

2.3 goroutine

goroutine是 Go 语言并行设计的核心。goroutine是一种比线程更轻量的实现,十几个goroutine可能在底层就是几个线程。 不同的是,Golang 在 runtime、系统调用等多方面对goroutine调度进行了封装和处理,当遇到长时间执行或者进行系统调用时,会主动把当前goroutine的 CPU (P) 转让出去,让其他goroutine能被调度并执行,也就是 Golang 从语言层面支持了协程。要使用goroutine只需要简单的在需要执行的函数前添加go关键字即可。当执行goroutine时候,Go 语言立即返回,接着执行剩余的代码,goroutine不阻塞主线程。下面我们通过一小段代码来讲解go的使用:

//首先我们先实现一个 Add()函数
func Add(a, b int) {
    c := a + b
    fmt.Println(c)
}

go Add(1, 2) //使用go关键字让函数并发执行

Go 的并发执行就是这么简单,当在一个函数前加上go关键字,该函数就会在一个新的 goroutine 中并发执行,当该函数执行完毕时,这个新的 goroutine 也就结束了。不过需要注意的是,如果该函数具有返回值,那么返回值会被丢弃。所以什么时候用go还需要酌情考虑。

package main

import "fmt"

func Add(a, b int) {
    c := a + b
    fmt.Println(c)
}

func main() {
    for i := 0; i < 10; i++ {
        go Add(i, i)
    }
}

执行 goroutine.go 文件会发现屏幕上什么都没有,但程序并不会报错,这是什么原因呢?原来当主程序执行到 for 循环时启动了 10 个goroutine,然后主程序就退出了,而启动的 10 个goroutine还没来得及执行 Add()函数,所以程序不会有任何输出。也就是说主goroutine并不会等待其他goroutine执行结束。那么如何解决这个问题呢?Go 语言提供的信道(channel)就是专门解决并发通信问题的.

2.4 Channel

channel是goroutine之间互相通讯的东西。类似我们 Unix 上的管道(可以在进程间传递消息),用来goroutine之间发消息和接收消息。其实,就是在做goroutine之间的内存共享。channel是类型相关的,也就是说一个channel只能传递一种类型的值,这个类型需要在channel声明时指定。

2.4.1 声明与初始化

channel的一般声明形式:var chanName chan ElementType 与普通变量的声明不同的是在类型前面加了channel关键字,ElementType则指定了这个channel所能传递的元素类型。示例:

var a chan int //声明一个传递元素类型为int的channel
var b chan float64
var c chan string

初始化一个channel也非常简单,直接使用 Go 语言内置的make()函数,示例:

a := make(chan int) //初始化一个int型的名为a的channel
b := make(chan float64)
c := make(chan string)

channel最频繁的操作就是写入和读取,这两个操作也非常简单,示例:

a := make(chan int)
a <- 1  //将数据写入channel
z := <-a  //从channel中读取数据
2.4.2 select

select用于处理异步 IO 问题,它的语法与switch非常类似。由select开始一个新的选择块,每个选择条件由case语句来描述,并且每个case语句里必须是一个channel操作。它既可以用于channel的数据接收,也可以用于channel的数据发送。如果select的多个分支都满足条件,则会随机的选取其中一个满足条件的分支。

2.4.3 超时机制

通过前面的内容我们了解到,channel 的读写操作非常简单,只需要通过<-操作符即可实现,但是channel的使用不当却会带来大麻烦.我们先来看之前的一段代码:

a := make(chan int)
a <- 1
z := <-a

观察上面三行代码,第 2 行往channel内写入了数据,第 3 行从channel中读取了数据,如果程序运行正常当然不会出什么问题,可如果第二行数据写入失败,或者channel中没有数据,那么第 3 行代码会因为永远无法从a中读取到数据而一直处于阻塞状态。相反的,如果channel中的数据一直没有被读取,那么写入操作也会一直处于阻塞状态。如果不正确处理这个情况,很可能会导致整个goroutine锁死,这就是超时问题。Go 语言没有针对超时提供专门的处理机制,但是我们却可以利用select来巧妙地实现超时处理机制,下面看一个示例:

t := make(chan bool)
go func {
    time.Sleep(1e9) //等待1秒
    t <- true
}

select {
    case <-ch:  //从ch中读取数据

    case <-t:  //如果1秒后没有从ch中读取到数据,那么从t中读取,并进行下一步操作
}

这样的方法就可以让程序在等待 1 秒后继续执行,而不会因为 ch 读取等待而导致程序停滞,从而巧妙地实现了超时处理机制,这种方法不仅简单,在实际项目开发中也是非常实用的。

2.4.4 channel 的关闭

channel的关闭非常简单,使用 Go 语言内置的close()函数即可关闭channel,示例 :

ch := make(chan int)
close(ch)

关闭了channel后如何查看channel是否关闭成功了呢?很简单,我们可以在读取channel时采用多重返回值的方式,示例:

x, ok := <-ch

通过查看第二个返回值的 bool 值即可判断channel是否关闭,若为false则表示channel被关闭,反之则没有关闭。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
疫情居家办公系统管理系统按照操作主体分为管理员和用户。管理员的功能包括办公设备管理、部门信息管理、字典管理、公告信息管理、请假信息管理、签到信息管理、留言管理、外出报备管理、薪资管理、用户管理、公司资料管理、管理员管理。用户的功能等。该系统采用了MySQL数据库,Java语言,Spring Boot框架等技术进行编程实现。 疫情居家办公系统管理系统可以提高疫情居家办公系统信息管理问题的解决效率,优化疫情居家办公系统信息处理流程,保证疫情居家办公系统信息数据的安全,它是一个非常可靠,非常安全的应用程序。 管理员权限操作的功能包括管理公告,管理疫情居家办公系统信息,包括外出报备管理,培训管理,签到管理,薪资管理等,可以管理公告。 外出报备管理界面,管理员在外出报备管理界面中可以对界面中显示,可以对外出报备信息的外出报备状态进行查看,可以添加新的外出报备信息等。签到管理界面,管理员在签到管理界面中查看签到种类信息,签到描述信息,新增签到信息等。公告管理界面,管理员在公告管理界面中新增公告,可以删除公告。公告类型管理界面,管理员在公告类型管理界面查看公告的工作状态,可以对公告的数据进行导出,可以添加新公告的信息,可以编辑公告信息,删除公告信息
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值