go语言基础学习
## Go 语言背景
Go 是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易。
Go 是从2007年末由 Robert Griesemer, Rob Pike, Ken Thompson 主持开发,后来还加入了 Ian Lance Taylor, Russ Cox 等人,并最终于2009年11月开源,在2012年早些时候发布了 Go 1稳定版本。现在 Go 的开发已经是完全开放的,并且拥有一个活跃的社区。
### go 语言特色
- 简洁、快速、安全
- 并行、有趣、开源
- 内存管理、数组安全、编译迅速
### go 语言环境安装:
Go 语言支持以下系统:
- Linux
- FreeBSD
- Mac OS X(也称为 Darwin)
- Windows
安装包下载地址为:[https://go.dev/dl/](https://go.dev/dl/)。
如果打不开可以使用这个地址:[https://golang.google.cn/dl/](https://golang.google.cn/dl/)。
#### windows 下安装:
Windows 下可以使用 .msi 后缀(在下载列表中可以找到该文件,如 go1.4.2.windows-amd64.msi)的安装包来安装。
默认情况下 **.msi** 文件会安装在 **c:\Go** 目录下。你可以将 **c:\Go\bin** 目录添加到 **Path** 环境变量中。添加后你需要重启命令窗口才能生效。
#### linux 类系统安装:
以下介绍了在 UNIX/Linux/Mac OS X, 和 FreeBSD 系统下使用源码安装方法:
1、下载二进制包:go1.4.linux-amd64.tar.gz。
2、将下载的二进制包解压至 /usr/local目录。
tar -C /usr/local -xzf go1.4.linux-amd64.tar.gz
3、将 /usr/local/go/bin 目录添加至 PATH 环境变量:
export PATH=$PATH:/usr/local/go/bin
以上只能暂时添加 PATH,关闭终端下次再登录就没有了。
我们可以编辑 ~/.bash_profile 或者 /etc/profile,并将以下命令添加该文件的末尾,这样就永久生效了:
export PATH=$PATH:/usr/local/go/bin
添加后需要执行:
source ~/.bash_profile
或
source /etc/profile
> **注意:**MAC 系统下你可以使用 **.pkg** 结尾的安装包直接双击来完成安装,安装目录在 **/usr/local/go/** 下。
## go 语言程序编写
### Go 的语言结构:
Go 的语言基础组成由以下几个部分:
1. 包声明;
2. 引入包;
3. 函数;
4. 变量;
5. 语句&表达式;
6. 注释
第一个 hello. go
```
package main
import "fmt"
func main() {
/* 这是我的第一个简单的程序 */
fmt.Println("Hello, World!")
}
```
1. 第一行代码 _package main_ 定义了包名。你必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package main。package main 表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包。
2. 下一行 _import "fmt"_ 告诉 Go 编译器这个程序需要使用 fmt 包(的函数,或其他元素),fmt 包实现了格式化 IO(输入/输出)的函数。
3. 下一行 _func main()_ 是程序开始执行的函数。main 函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数(如果有 init() 函数则会先执行该函数)。
4. 下一行 /*...*/ 是注释,在程序执行时将被忽略。单行注释是最常见的注释形式,你可以在任何地方使用以 // 开头的单行注释。多行注释也叫块注释,均已以 /* 开头,并以 */ 结尾,且不可以嵌套使用,多行注释一般用于包的文档描述或注释成块的代码片段。
5. 下一行 _fmt.Println(...)_ 可以将字符串输出到控制台,并在最后自动增加换行字符 \n。
使用 fmt.Print("hello, world\n") 可以得到相同的结果。
Print 和 Println 这两个函数也支持使用变量,如:fmt.Println(arr)。如果没有特别指定,它们会以默认的打印格式将变量 arr 输出到控制台。
6. 当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 protected )。
运行代码:
go run hello.go
生成二进制文件:
go build hello.go
注意:
1. 需要注意的是 { 不能单独放在一行。
2. 文件名与包名没有直接关系,不一定要将文件名与包名定成同一个。
3. 文件夹名与包名没有直接关系,并非需要一致。
4. 同一个文件夹下的文件只能有一个包名,否则编译报错。
### go 的基础语法
#### 行分隔符:
go 跟 python 一样不使用;作为行分隔符。
如果时多个语句写在同一行那就需要;作为分隔。
#### 注释:
go 的注释和其他语言一样。
#### 标识符:
go 的标识符也和其他语言差不多。
标识符用来命名变量、类型等程序实体。一个标识符实际上就是一个或是多个字母(A~Z 和 a~z)数字(0~9)、下划线_组成的序列,但是第一个字符必须是字母或下划线而不能是数字。
#### 字符串:
go 的字符串连接和 python 的一样。
Go 语言的字符串连接可以通过 + 实现:
```
fmt.Println("Google" + "Runoob")
```
#### 关键字:
25 个关键字和保留字:
![[Pasted image 20230720112138.png]]
36 个预定义标识符:
![[Pasted image 20230720112214.png]]
#### go 语言中的空格:
跟其他语言差不多。
#### 格式化字符串:
go 中使用 fmt. Sprintf 或 fmt. Printf 格式化字符串并复制给新串:
Sprintf 根据格式化参数生成格式化的字符串并返回该字符串。
Printf 根据格式化参数生成格式化的字符串并写入标准输出。
```
var stockcode=123
var enddate="2020-12-31"
var url="Code=%d&endDate=%s"
var target_url=fmt.Sprintf(url,stockcode,enddate)
fmt.Println(target_url)
var stockcode=123
var enddate="2020-12-31"
var url="Code=%d&endDate=%s"
fmt.Printf(url,stockcode,enddate)
输出结果为:Code=123&endDate=2020-12-31
```
### go 语言数据类型
在 go 中,数据类型用于声明函数和变量。
数据类型的出现是为了把数据分成所需内存大小不同的数据,需要用大数据的时候申请大内存,不需要可以申请小内存,提高内容利用率。
按类别 go 有下面几种数据类型:
1、布尔型
布尔型的值只可以是 true 或者 false。 var b bool=true
2、数字类型
整形 int 浮点型 float32 和 float 64,go 支持整形和浮点型数字,并支持复数,位的运算采用补码。
3、字符串类型
go 的字符串由单个字节拼接,使用 utf-8 编码标识的 unicode 文本。
4、派生类型
1. 指针类型
2. 数组类型
3. 结构化类型(struct)
4. channel 类型
5. 函数类型
6. 切片类型
7. 接口类型
8. map 类型
#### 数字类型:
##### 整型:
uint 8:无符号 8 位整型(2 到 255)
uint 16:无符号 16 位整型(0 到 65535)
uint 32:无符号 32 位整型(0 到 4294967295)
int 8:有符号 8 位整型(-127 到 127)
int 16:有符号 16 位整型(-32768 到 32767)
int 32:有符号 32 位整型(-2147483648 到 2147483647)
int 64:有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)
##### 浮点型:
float 32:IEEE-754 标准 32 位浮点型整数;
float 64:IEEE-754 标准 64 位浮点数;
complex:32 位实数和虚数;
complex 128:64 位实数和虚数;
##### 其他数字类型:
byte:类似 uint8;
rune:类似 int32;
uint:32 位或 64 位;
int:和 uint 一样大小;
uintptr:无符号整型用于存放一个指针;
注意:
在 go1.9 版本后,无需定义 int、float 32、float 64,系统会自动识别,类似 python 的处理方式。
### go 语言变量
go 语言变量名由字母、数字、下划线组成,其中首个字符不能为数字。
声明变量一般使用 var 关键字,可以一次声明多个变量。
变量声明:
1、智能变量类型,如果没有初始化,则变量默认为零值(没有做初始化时系统默认设置的值)。
对于零值:
- 数值类型(包括 complex64/128)为 **0**
- 布尔类型为 **false**
- 字符串为 **""**(空字符串)
- 以下几种类型为 **nil**:
- var a *int
- var a []int
- var a map[string] int
- var a chan int
- var a func(string) int
- var a error // error 是接口
2、根据值自行判定变量类型
3、如果变量已经使用 var 声明过了,再使用 := 声明变量,就产生编译错误。
### go 语言常量
常量是一个简单值的标识符,在程序运行时,不会被修改的量。常量中的数据类型只可以时布尔型、数字型(整数型、浮点数型和复数)和字符串型。
> const identifier [type] = value
- 显式类型定义: `const b string = "abc"`
- 隐式类型定义: `const b = "abc"`
**iota**
iota,特殊常量,可以认为是一个可以被编译器修改的常量。
iota 在 const 关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const 语句块中的行索引)。
### go 语言运算符
go 语言内置,和其他语言基本一致。
- 算术运算符
- 关系运算符
- 逻辑运算符
- 位运算符
- 赋值运算符
- 其他运算符
算术运算符:
![[Pasted image 20230720145941.png]]
关系运算符:
![[Pasted image 20230720150018.png]]
逻辑运算符:
![[Pasted image 20230720150053.png]]
位运算符:
![[Pasted image 20230720150437.png]]
![[Pasted image 20230720150451.png]]
赋值运算符:
![[Pasted image 20230720150517.png]]
其他运算符:
![[Pasted image 20230720150550.png]]
运算符优先级:
![[Pasted image 20230720150658.png]]
注意:
go 的自增自检智能作为表达式使用,不能用于赋值语句,不然会编译出错。
### go 语言条件语句:
![[Pasted image 20230720151010.png]]
switch 语句:
```
switch var1 {
case val1:
...
case val2:
...
default:
...
}
```
select 语句:
```
Select {
Case <- channel 1:
// 执行的代码
Case value := <- channel 2:
// 执行的代码
Case channel 3 <- value:
// 执行的代码
// 你可以定义任意数量的 case
Default:
// 所有通道都没有准备好,执行的代码
}
```
### go 语言循环语句
#### for 循环:
跟 c 语言中的一样
```
For init; condition; post { }
```
- init: 一般为赋值表达式,给控制变量赋初值;
- condition: 关系表达式或逻辑表达式,循环控制条件;
- post: 一般为赋值表达式,给控制变量增量或减量。
多层嵌套:
```
for [condition | ( init; condition; increment ) | Range]
{
for [condition | ( init; condition; increment ) | Range]
{
statement(s);
}
statement(s);
}
```
#### 循环中的 break:
在 Go 语言中,break 语句用于终止当前循环或者 switch 语句的执行,并跳出该循环或者 switch 语句的代码块。
break 语句可以用于以下几个方面:。
- 用于循环语句中跳出循环,并开始执行循环之后的语句。
- break 在 switch 语句中在执行一条 case 后跳出语句的作用。
- break 可应用在 select 语句中。
- 在多重循环中,可以用标号 label 标出想 break 的循环。
```
Package main
Import "fmt"
Func main () {
For i := 0; i < 10; i++ {
If i == 5 {
Break // 当 i 等于 5 时跳出循环
}
Fmt.Println (i)
}
}
```
#### continue 语句:
Go 语言的 continue 语句有点像 break 语句。但是 continue 不是跳出循环,而是跳过当前循环执行下一次循环语句。
for 循环中,执行 continue 语句会触发 for 增量语句的执行。
在多重循环中,可以用标号 label 标出想 continue 的循环。
#### goto 语句:
Go 语言的 goto 语句可以无条件地转移到过程中指定的行。
goto 语句通常与条件语句配合使用。可用来实现条件转移, 构成循环,跳出循环体等功能。
但是,在结构化程序设计中一般不主张使用 goto 语句, 以免造成程序流程的混乱,使理解和调试程序都产生困难。
```
goto label;
..
.
label: statement;
```
```
Package main
Import "fmt"
Func main () {
/* 定义局部变量 */
Var a int = 10
/* 循环 */
LOOP: for a < 20 {
If a == 15 {
/* 跳过迭代 */
A = a + 1
Goto LOOP
}
Fmt.Printf ("a 的值为 : %d\n", a)
A++
}
}
```
### go 语言函数:
函数定义:
```
func function_name( [parameter list] ) [return_types] {
函数体
}
```
函数定义解析:
- func:函数由 func 开始声明
- function_name:函数名称,参数列表和返回值类型构成了函数签名。
- parameter list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
- return_types:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types 不是必须的。
- 函数体:函数定义的代码集合。
```
/* 函数返回两个数的最大值 */
func max(num1, num2 int) int {
/* 声明局部变量 */
var result int
if (num1 > num2) {
result = num1
} else {
result = num2
}
return result
}
```
函数里面也要注意变量操作,在函数里面改了变量,主函数也会受影响。
![[Pasted image 20230720152721.png]]
### go 语言变量作用域
- 函数内定义的变量称为局部变量
- 函数外定义的变量称为全局变量
- 函数定义中的变量称为形式参数
### go 语言数组
```
var arrayName [size]dataType
```
初始化数组:
```
var numbers [5]int
var numbers = [5]int{1, 2, 3, 4, 5}
numbers := [5]int{1, 2, 3, 4, 5}
```
如果长度不确定,可以使用... 来代替数据的长度,编译器会根据元素个数自行推断数组的长度。
```
var balance = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
或
balance := [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
```
### go 语言指针
go 语言指针和 c 语言中的类似
### go 语言结构体
结构体有点类似类的概念。
定义结构体
结构体定义需要使用 type 和 struct 语句。struct 语句定义一个新的数据类型,结构体中有一个或多个成员。type 语句设定了结构体的名称。结构体的格式如下:
```
Type struct_variable_type struct {
Member definition
Member definition
...
Member definition
}
```
```
Variable_name := structure_variable_type {value 1, value 2... Valuen}
或
Variable_name := structure_variable_type { key 1: value 1, key 2: value 2..., keyn: valuen}
```
### go 语言切片
切片时数组的衍生抽象。
go 数组长度不可变,于是切片来实现一种动态数组的功能。
**定义切片:**
```
var identifier []type
```
```
Var slice 1 []type = make ([]type, len)
也可以简写为
Slice 1 := make ([]type, len)
也可以指定容量,capacity 是可选参数
Make ([]T, length, capacity)
```
**切片初始化:**
```
s :=[] int {1,2,3 }
直接初始化切片,[] 表示是切片类型,**{1,2,3}** 初始化值依次是 1,2,3,其 **cap=len=3**。
s := arr[:]
初始化切片 **s**,是数组 arr 的引用。
s := arr[startIndex:endIndex]
将 arr 中从下标 startIndex 到 endIndex-1 下的元素创建为一个新的切片。
s := arr[startIndex:]
默认 endIndex 时将表示一直到arr的最后一个元素。
s := arr[:endIndex]
默认 startIndex 时将表示从 arr 的第一个元素开始
s1 := s[startIndex: endIndex]
通过切片 s 初始化切片 s1。
s :=make([]int,len,cap)
通过 make()函数初始化
```
len() 方法获取长度
cap() 可以测量切片最长可以达到多少
一个切片在未初始化之前默认为 nil,长度为 0
切片扩容方法:从拷贝切片的 copy 方法和向切片追加新元素的 append 方法
### go 语言范围(range)
go 里面的 range 和 python 里面的 range 类似。
```
for key, value := range oldMap {
newMap[key] = value
}
```
如果只想读 key,也可以
```
for key := range oldMap
for key, _ := range oldMap
```
如果只想读 value
```
for _, value := range oldMap
```
### go 语言 map(集合)
go 中的 map 类似 python 里面的 tupple 元组。
map 是一种无序的键值对的集合,可以通过 key 来快速检索数据,key 类似于索引,指向数据的值。
map 是一种集合,可以像迭代数组那样进行迭代,但它是无序的,遍历 map 时返回的键值对的顺序不确定。
在获取 map 的值时,如果键不存在,则反馈该类型的零值,如 int 类型的零值是 0,string 类型的零值是""。
**定义 map:**
```
/* 使用 make 函数 */
Map_variable := make (map[KeyType]ValueType, initialCapacity)
```
```
// 创建一个空的 Map
M := make (map[string]int)
// 创建一个初始容量为 10 的 Map
M := make (map[string]int, 10)
```
```
// 使用字面量创建 Map
M := map[string]int{
"apple": 1,
"banana": 2,
"orange": 3,
}
获取元素:
// 获取键值对
V 1 := m["apple"]
V 2, ok := m["pear"] // 如果键不存在,ok 的值为 false,v 2 的值为该类型的零值
修改元素:
// 修改键值对
M["apple"] = 5
获取 map 的长度:
// 获取 Map 的长度
Len := len (m)
遍历 map:
// 遍历 Map
For k, v := range m {
Fmt.Printf ("key=%s, value=%d\n", k, v)
}
删除元素:
// 删除键值对
Delete (m, "banana")
```
### go 语言递归函数
go 的递归函数跟其他语言类似。
### go 语言类型转换
整体上跟其他语言类似。
```
Type_name (expression)
type_name 为类型,expression 为表达式。
```
**数值类型转换:**
```
Var a int = 10
Var b float 64 = float 64 (a)
```
**字符串类型转换:**
```
Var str string = "10"
Var num int
Num, _ = strconv.Atoi (str)
以上代码将字符串变量 str 转换为整型变量 num。
注意,strconv.Atoi 函数返回两个值,第一个是转换后的整型值,第二个是可能发生的错误,我们可以使用空白标识符 _ 来忽略这个错误
```
**接口类型转换:**
接口类型转换有两种情况**:类型断言**和**类型转换**。
类型断言用于将接口类型转换为指定类型,其语法为:
```
Value. (type)
或者
Value. (T)
```
其中 value 是接口类型的变量,type 或 T 是要转换成的类型。
如果类型断言成功,它将返回转换后的值和一个布尔值,表示转换是否成功。
```
Package main
Import "fmt"
Func main () {
Var i interface{} = "Hello, World"
str, ok := i.(string)
If ok {
Fmt.Printf ("'%s' is a string\n", str)
} else {
Fmt.Println ("conversion failed")
}
}
```
以上实例中,我们定义了一个接口类型变量 i,并将它赋值为字符串 "Hello, World"。然后,我们使用类型断言将 i 转换为字符串类型,并将转换后的值赋值给变量 str。最后,我们使用 ok 变量检查类型转换是否成功,如果成功,我们打印转换后的字符串;否则,我们打印转换失败的消息。
类型转换用于将一个接口类型的值转换为另一个接口类型,其语法为:
```
T(value)
```
T 是目标接口类型,value 是要转换的值。
在类型转换中,我们必须保证要转换的值和目标接口类型之间是兼容的,否则编译器会报错。
```
Package main
Import "fmt"
Type Writer interface {
Write ([]byte) (int, error)
}
Type StringWriter struct {
Str string
}
Func (sw *StringWriter) Write (data []byte) (int, error) {
Sw. Str += string (data)
Return len (data), nil
}
Func main () {
Var w Writer = &StringWriter{}
sw := w.(*StringWriter)
Sw. Str = "Hello, World"
Fmt.Println (sw. Str)
}
```
以上实例中,我们定义了一个 Writer 接口和一个实现了该接口的结构体 StringWriter。然后,我们将 StringWriter 类型的指针赋值给 Writer 接口类型的变量 w。接着,我们使用类型转换将 w 转换为 StringWriter 类型,并将转换后的值赋值给变量 sw。最后,我们使用 sw 访问 StringWriter 结构体中的字段 str,并打印出它的值。
### go 语言接口
接口真的就感觉像类的概念。
Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。
接口可以让我们将不同的类型绑定到一组公共的方法上,从而实现多态和灵活的设计。
Go 语言中的接口是隐式实现的,也就是说,如果一个类型实现了一个接口定义的所有方法,那么它就自动地实现了该接口。因此,我们可以通过将接口作为参数来实现对不同类型的调用,从而实现多态。
```
Package main
Import (
"fmt"
)
Type Phone interface {
Call ()
}
Type NokiaPhone struct {
}
Func (nokiaPhone NokiaPhone) call () {
Fmt.Println ("I am Nokia, I can call you!")
}
Type IPhone struct {
}
Func (iPhone IPhone) call () {
Fmt.Println ("I am iPhone, I can call you!")
}
Func main () {
Var phone Phone
phone = new(NokiaPhone)
phone.call()
phone = new(IPhone)
phone.call()
}
```
在上面的例子中,我们定义了一个接口 **Phone**,接口里面有一个方法 call()。然后我们在 **main** 函数里面定义了一个 **Phone** 类型变量,并分别为之赋值为 **NokiaPhone** 和 **IPhone**。然后调用 call() 方法,输出结果如下:
### go 错误处理
go 语言有一个内置的简单错误处理机制。
error 类型是一个接口类型:
```
Type error interface {
Error () string
}
func Sqrt(f float64) (float64, error) {
if f < 0 {
return 0, errors.New("math: square root of negative number")
}
// 实现
}
```
### go 并发
go 支持并发,只需要通过 goroutine 即可。
goroutine 是轻量级线程,它的调度是 go 运行时进行管理的。
goruntine 语法格式:
```
go 函数名( 参数列表 )
go f(x, y, z)
```
f(x, y, z),声明之后,每一个调用都是一个新的线程,但是所有线程都是共享一个地址空间,这里需要注意。
P
```
ackage main
Import (
"fmt"
"time"
)
Func say (s string) {
For i := 0; i < 5; i++ {
Time.Sleep (100 * time. Millisecond)
Fmt.Println (s)
}
}
Func main () {
Go say ("world")
Say ("hello")
}
```
**通道(channel)**
通道是用来传递数据的一个数据结构,一个通道相当于一个先进先出的队列。
通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 `<-` 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。
```
ch <- v // 把 v 发送到通道 ch
v := <-ch // 从 ch 接收数据
// 并把值赋给 v
```
声明一个通道很简单,我们使用chan关键字即可,通道在使用前必须先创建:
```
ch := make(chan int)
```
单向通道:
```
Var WriteChan = make (chan<- interface{}, 1) // 只能发送不能接收的通道
Var ReadChan = make (<-chan interface{}, 1) // 只能接收不能发送的通道
```
**注意**:默认情况下,通道是不带缓冲区的。发送端发送数据,同时必须有接收端相应的接收数据。
**通道缓冲区:**
```
Ch 1 := make (chan string, 3)
- `chan` 是表示通道类型的关键字
- `string` 表示该通道类型的元素类型
- `3` 表示该通道的容量为 3,最多可以缓存 3 个元素值。
```
带缓冲区的通道允许发送端的数据发送和接收端的数据获取处于异步状态,就是说发送端发送的数据可以放在缓冲区里面,可以等待接收端去获取数据,而不是立刻需要接收端去获取数据。
不过由于缓冲区的大小是有限的,所以还是必须有接收端来接收数据的,否则缓冲区一满,数据发送端就无法再发送数据了。
**注意**:如果通道不带缓冲,发送方会阻塞直到接收方从通道中接收了值。如果通道带缓冲,发送方则会阻塞直到发送的值被拷贝到缓冲区内;如果缓冲区已满,则意味着需要等待直到某个接收方获取到一个值。接收方在有值可以接收之前会一直阻塞。
**非缓冲通道:**
非缓冲通道是容量为0的通道,不能缓存数据。
非缓冲通道的数据传递是同步的,发送操作或者接收操作在执行后就会阻塞,需要对应的接收操作或者发送操作执行才会继续传递。由此可以看出缓冲通道使用的是异步方式进行数据传递。
```
Package main
Import (
"fmt"
)
Func main () {
Str 1 := []string{"hello","world", "!"}
Ch 1 := make (chan string, 0)
go func() {
for _, str := range str1 {
ch1 <- str
}
}()
for i := 0; i < len(str1); i++ {
elem := <- ch1
fmt.Println(elem)
}
}
```
**通道关闭:**
可以使用close()方法来关闭通道,通道关闭后,不能再对通道进行发送操作,可以进行接收操作。
如果通道关闭时,里面还有元素,进行接收操作时,返回的通道关闭标志仍然为 true,由于通道的这种特性,可以让发送方来关闭通道。
```
Package main
Import (
"fmt"
)
Func main () {
Str 1 := []string{"hello","world", "!"}
Ch 1 := make (chan string, 0)
go func() {
for _, str := range str1 {
ch1 <- str
}
close(ch1)
}()
for i := 0; i < len(str1); i++ {
elem := <- ch1
fmt.Println(elem)
}
}
```