Go 语言(Golang)基础学习笔记(一)

教程推荐:

1.Go 语言教程
2.Go语言圣经(中文版)
3.Go语言高级编程
4.Go语言中文网
5.8小时转职Golang工程师(如果你想低成本学习Go语言)

1、Hello World

package main //包声明

import "fmt" //引入包

func main() { //函数
	fmt.Println("Hello, World!") //语句 & 表达式
}

在这里插入图片描述

2、基础语法

<1> 字符串连接

Go 语言的字符串可以通过+ 实现:

package main //包声明

import "fmt" //引入包

func main() {
	fmt.Println("Hello, " + "World")
}

<2> 格式化字符串

package main

import (
	"fmt"
)

func main() {
	var stockCode = 123
	var endDate = "2020-12-31"
	// %d 表示整型数字,%s 表示字符串
	var url = "Code=%d&endDate=%s"
	var targetUrl = fmt.Sprintf(url, stockCode, endDate)
	fmt.Println(targetUrl)
}

在这里插入图片描述

3、数据类型

类型描述例子
布尔型true 或者 falsevar b bool = true
数字类型整型 和浮点型var stockCode int
字符串类型字符序列var endDate string

派生类型:

(a) 指针类型(Pointer)
(b) 数组类型
(c) 结构化类型(struct)
(d) Channel 类型
(e) 函数类型
(f) 切片类型
(g) 接口类型(interface(h) Map 类型

fmt格式占位符

fmt格式占位符

普通占位符
占位符     说明                           举例                   输出
%v      相应值的默认格式。            Printf("%v", people)   {zhangsan}%+v     打印结构体时,会添加字段名     Printf("%+v", people)  {Name:zhangsan}
%#v     相应值的Go语法表示            Printf("#v", people)   main.Human{Name:"zhangsan"}
%T      相应值的类型的Go语法表示       Printf("%T", people)   main.Human
%%      字面上的百分号,并非值的占位符  Printf("%%")            %
布尔占位符
占位符       说明                举例                     输出
%t          truefalsePrintf("%t", true)       true
整数占位符
占位符     说明                                  举例                       输出
%b      二进制表示                             Printf("%b", 5)             101
%c      相应Unicode码点所表示的字符              Printf("%c", 0x4E2D)%d      十进制表示                             Printf("%d", 0x12)          18
%o      八进制表示                             Printf("%d", 10)            12
%q      单引号围绕的字符字面值,由Go语法安全地转义 Printf("%q", 0x4E2D)        '中'
%x      十六进制表示,字母形式为小写 a-f         Printf("%x", 13)             d
%X      十六进制表示,字母形式为大写 A-F         Printf("%x", 13)             D
%U      Unicode格式:U+1234,等同于 "U+%04X"   Printf("%U", 0x4E2D)         U+4E2D

4、变量

Go 语言变量名由字母、数字、下划线组成,其中首个字符不能为数字。
声明变量的一般形式是使用 var 关键字:

<1> 变量声明var

仅指定变量类型,没有初始化,则变量默认为零值。

package main
import "fmt"
func main() {
	// 声明一个变量并初始化
	var a = "go"
	fmt.Println(a)

	// 没有初始化就为零值
	var b int
	fmt.Println(b)

	// bool 零值为 false
	var c bool
	fmt.Println(c)
}

在这里插入图片描述
根据值自行判定变量类型:

var d = true
fmt.Println(d)

<2> 变量声明:=

package main
import "fmt"
func main() {
	str := "go"
	fmt.Println(str)
}

如果变量已经使用 var 声明过了,再使用 := 声明变量,就产生编译错误,格式:

str := "go" 相等于:var str = "go"

<3> 多变量声明

package main

import "fmt"

var x, y int
var (  // 这种因式分解关键字的写法一般用于声明全局变量
	a int
	b bool
)

var c, d int = 1, 2
var e, f = 123, "hello"

func main(){
	//这种不带声明格式的只能在函数体中出现
	g, h := 123, "hello"
	fmt.Println(x, y, a, b, c, d, e, f, g, h)
}

在这里插入图片描述

<4> 值类型和引用类型

所有像 int、float、bool 和 string 这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值:
在这里插入图片描述

当使用等号 = 将一个变量的值赋值给另一个变量时,如:j = i,实际上是在内存中将 i 的值进行了拷贝:
在这里插入图片描述
你可以通过&i 来获取变量 i 的内存地址,例如:0xf840000040(每次的地址都可能不一样)。值类型的变量的值存储在栈中。

package main

import "fmt"

func main(){
	str := "go"
	fmt.Println(str)
	fmt.Println(&str)
}

在这里插入图片描述

<5> 常量

【1】const

const声明常量

const WIDTH int = 5  

【2】iota

iota是一个特殊常量,可以认为是一个可以被编译器修改的常量。

(1)默认使用
package main

import "fmt"

const (
	// 可以在const() 添加一个关键字 iota, 每行的iota都会累加1, 第一行的iota的默认值是0
	ID1  = iota  //iota = 0
	ID2   //iota = 1
	ID3  //iota = 2
)

func main() {
	fmt.Printf("ID1:%d\n", ID1)
	fmt.Printf("ID2:%d\n", ID2)
	fmt.Printf("ID3:%d\n", ID3)
}

在这里插入图片描述

(2)构造表达式:
package main

import "fmt"

const (
	// 可以在const() 添加一个关键字 iota, 每行的iota都会累加1, 第一行的iota的默认值是0
	ID1  = 10*iota
	ID2
	ID3
)

func main() {
	fmt.Printf("ID1:%d\n", ID1)
	fmt.Printf("ID2:%d\n", ID2)
	fmt.Printf("ID3:%d\n", ID3)
}

在这里插入图片描述

(3)每行累加1的特性
const (
	a, b = iota+1, iota+2 // iota = 0, a = iota + 1, b = iota + 2, a = 1, b = 2
	c, d                  // iota = 1, c = iota + 1, d = iota + 2, c = 2, d = 3
	e, f                  // iota = 2, e = iota + 1, f = iota + 2, e = 3, f = 4

	g, h = iota * 2, iota *3  // iota = 3, g = iota * 2, h = iota * 3, g = 6, h = 9
	i, k                       // iota = 4, i = iota * 2, k = iota * 3 , i = 8, k = 12
)

iota 只能够配合const() 一起使用, iota只有在const进行累加效果。

5 、运算符

Go 语言运算符

6、条件语句

<1> if else

package main

import "fmt"

func main() {
	a := 2
	if a == 0{

	}else if a == 1{

	}else{
		fmt.Println(a)
	}
}

<2> switch

方式一:switch {}

package main

import "fmt"

func main() {
	var grade string = "B"

	switch {
	case grade == "A":
		fmt.Printf("优秀!\n")
	case grade == "B", grade == "C":
		fmt.Printf("良好\n")
	case grade == "D":
		fmt.Printf("及格\n")
	case grade == "F":
		fmt.Printf("不及格\n")
	default:
		fmt.Printf("差\n")
	}
}

在这里插入图片描述

方式二:switch v_name{}

package main

import "fmt"

func main() {
	var grade string = "B"

	switch grade {
	case "A":
		fmt.Printf("优秀!\n")
	case "B", "C":
		fmt.Printf("良好\n")
	case "D":
		fmt.Printf("及格\n")
	case "F":
		fmt.Printf("不及格\n")
	default:
		fmt.Printf("差\n")
	}
}

<3>select 语句

select 是 Go 中的一个控制结构,类似于用于通信的 switch 语句。每个 case 必须是一个通信操作,要么是发送要么是接收。

select 随机执行一个可运行的 case。如果没有 case 可运行,它将阻塞,直到有 case 可运行。一个默认的子句应该总是可运行的。

7、循环语句

package main

import (
	"fmt"
	"strconv"
)

func main() {
	for i :=0;i<10;i++  {
		fmt.Printf(strconv.Itoa(i))
	}
}

在这里插入图片描述

8、函数

<1> 函数定义

package main

import "fmt"

func main() {
	/* 定义局部变量 */
	var a int = 100
	var b int = 200
	var ret int

	/* 调用函数并返回最大值 */
	ret = max(a, b)

	fmt.Printf( "最大值是 : %d\n", ret )
}

/* 函数返回两个数的最大值 */
func max(num1, num2 int) int {
	/* 定义局部变量 */
	var result int

	if num1 > num2 {
		result = num1
	} else {
		result = num2
	}
	return result
}

在这里插入图片描述

<2> 函数返回多个值:

package main

import "fmt"
// (x, y string) : 要传入的参数及类型
// (string, string) : 要返回的类型
func swap(x, y string) (string, string) {
	return y, x
}

func main() {
	a, b := swap("Google", "Runoob")
	fmt.Println(a, b)
}

在这里插入图片描述

<3> 函数的多返回值三种写法

【1】匿名的返回值

//返回多个返回值,匿名的
func foo2(a string, b int) (int, int) {
    //...
    return 666, 777
}

【2】有形参名称的返回值

  • 返回 (r1 int, r2 int) ,此return后就不用再写了
//返回多个返回值, 有形参名称的
func foo3(a string, b int) (r1 int, r2 int) {
    //...

    //给有名称的返回值变量赋值
    r1 = 1000
    r2 = 2000
 
    return
}

返回值的类型相返回值还可以这样写

//返回多个返回值, 有形参名称的
func foo3(a string, b int) (r1 , r2  int) {
    //...

    //给有名称的返回值变量赋值
    r1 = 1000
    r2 = 2000
 
    return
}

当然 传入参数值的类型相返回值也可以这样写

//返回多个返回值, 有形参名称的
func foo3(a , b int) (r1 , r2  int) {
    //...

    //给有名称的返回值变量赋值
    r1 = 1000
    r2 = 2000
 
    return
}

<3>函数参数

函数如果使用参数,该变量可称为函数的形参。形参就像定义在函数体内的局部变量。调用函数,可以通过两种方式来传递参数:

传递类型描述
值传递值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
引用传递(传地址)引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

引用传递:

package main

import "fmt"

func main() {
	/* 定义局部变量 */
	var a int = 100
	var b int= 200

	fmt.Printf("交换前,a 的值 : %d\n", a )
	fmt.Printf("交换前,b 的值 : %d\n", b )

	/* 调用 swap() 函数 交换地址
	 * &a 指向 a 指针,a 变量的地址
	 * &b 指向 b 指针,b 变量的地址
	 */
	swap(&a, &b)

	fmt.Printf("交换后,a 的值 : %d\n", a )
	fmt.Printf("交换后,b 的值 : %d\n", b )
}

func swap(x *int, y *int) {
	var temp int
	temp = *x    /* 保存 x 地址上的值 */
	*x = *y      /* 将 y 值赋给 x */
	*y = temp    /* 将 temp 值赋给 y */
}

在这里插入图片描述

<4> 函数用法

函数用法描述
函数作为另外一个函数的实参函数定义后可作为另外一个函数的实参数传入
闭包闭包是匿名函数,可在动态编程中使用
方法方法就是一个包含了接受者的函数

[1] 函数作为另外一个函数的实参

package main

import (
	"fmt"
	"math"
)

func main(){
	/* 声明函数变量 */
	getSquareRoot := func(x float64) float64 {
		return math.Sqrt(x)
	}

	/* 使用函数 */
	fmt.Println(getSquareRoot(9))

}

在这里插入图片描述

[2] 闭包

Go 语言支持匿名函数,可作为闭包。匿名函数是一个"内联"语句或表达式。匿名函数的优越性在于可以直接使用函数内的变量,不必申明。

package main

import "fmt"

func getSequence() func() int {
	i:=0
	return func() int {
		i+=1
		return i
	}
}

func main(){
	/* nextNumber 为一个函数,函数 i 为 0 */
	nextNumber1 := getSequence()

	/* 调用 nextNumber 函数,i 变量自增 1 并返回 */
	fmt.Println(nextNumber1()) // 输出 1
	fmt.Println(nextNumber1()) // 输出 2
	fmt.Println(nextNumber1()) // 输出 3

	/* 创建新的函数 nextNumber1,并查看结果 */
	nextNumber2 := getSequence()
	fmt.Println(nextNumber2()) // 输出 1
	fmt.Println(nextNumber2()) // 输出 2
}

在这里插入图片描述

[3] 方法

Go 语言中同时有函数和方法。一个方法就是一个包含了接受者的函数,接受者可以是命名类型或者结构体类型的一个值或者是一个指针。

package main

import (
	"fmt"
)

// Circle  定义结构体 
type Circle struct {
	radius float64
}

func main() {
	var c1 Circle
	c1.radius = 10.00
	fmt.Println("圆的面积 = ", c1.getArea())
}

//该 method 属于 Circle 类型对象中的方法
func (c Circle) getArea() float64 {
	//c.radius 即为 Circle 类型对象中的属性
	return 3.14 * c.radius * c.radius
}

在这里插入图片描述

9、数组

<1> 初始化固定长度数组

var n [10]int /* n 是一个长度为 10 的数组 */

var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
var balance = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}

//  将索引为 1 和 3 的元素初始化
balance := [5]float32{1:2.0,3:7.0}
package main

import "fmt"

func main() {
	var n [10]int /* n 是一个长度为 10 的数组 */
	var i,j int

	/* 为数组 n 初始化元素 */
	for i = 0; i < 10; i++ {
		n[i] = i + 100 /* 设置元素为 i + 100 */
	}

	/* 输出每个数组元素的值 */
	for j = 0; j < 10; j++ {
		fmt.Printf("Element[%d] = %d\n", j, n[j] )
	}
}

在这里插入图片描述

<2> 编历方式

for i = 0; i < 10; i++ {
	fmt.Println("index = ", i, ", value = ", myArray[i])
}
for index, value := range myArray {
	fmt.Println("index = ", index, ", value = ", value)
}

_ 表示匿名变量

for _, value := range myArray {
	fmt.Println("index = ", index, ", value = ", value)
}

<3> 固定长度数组传参

package main

import "fmt"

func printData(arr [12]int){
	fmt.Println(arr)
}

func main() {
	myArray := [12]int{1,2,4,5}
	printData(myArray)
}

在这里插入图片描述

若长度不匹配则报错
在这里插入图片描述
所以此时最用动态数组。

<4> 动态数组

动态数组其实就是切片,请看12、Go 语言切片(Slice)

package main

import "fmt"

func printData(arr []int){
	fmt.Println(arr)
}

func main() {
	myArray := []int{1,2,4,5}
	printData(myArray)
}

在这里插入图片描述

10、 指针

<1> 指针定义

一个指针变量指向了一个值的内存地址。

package main

import "fmt"

func main() {
	var a int= 20   /* 声明实际变量 */
	var ip *int        /* 声明指针变量 */

	ip = &a  /* ip的值赋为a的存储地址 */

	fmt.Printf("a 变量的值: %d\n", a)

	fmt.Printf("a 变量的地址是: %x\n", &a  )

	/* 指针变量的存储地址 */
	fmt.Printf("ip 变量的值: %x\n", ip )

	/* 使用指针访问值 */
	fmt.Printf("*ip 变量的值: %d\n", *ip )
}

在这里插入图片描述
空指针:当一个指针被定义后没有分配到任何变量时,它的值为 nil。
nil 指针也称为空指针。
nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。
一个指针变量通常缩写为 ptr。
空指针判断:

if(ptr != nil)     /* ptr 不是空指针 */
if(ptr == nil)    /* ptr 是空指针 */

<2> 指向指针的指针

如果一个指针变量存放的又是另一个指针变量的地址,则称这个指针变量为指向指针的指针变量。

当定义一个指向指针的指针变量时,第一个指针存放第二个指针的地址,第二个指针存放变量的地址:

指向指针的指针变量声明格式如下:

var ptr **int;
package main

import "fmt"

func main() {
	var a int
	var ptr *int
	var pptr **int

	a = 3000
	ptr = &a
	pptr = &ptr

	fmt.Printf("a = %d\n", a)
	fmt.Printf("ptr = %d\n", ptr)
	fmt.Printf("*ptr = %d\n", *ptr)
	fmt.Printf("pptr = %d\n", pptr)
	fmt.Printf("*pptr = %d\n", *pptr)
	fmt.Printf("**pptr = %d\n", **pptr)
}

在这里插入图片描述

<3> 指针作为函数参数

package main

import "fmt"

func main() {
	/* 定义局部变量 */
	var a int = 100
	var b int= 200

	fmt.Printf("交换前 a 的值 : %d\n", a )
	fmt.Printf("交换前 b 的值 : %d\n", b )

	/* 调用函数用于交换值
	 * &a 指向 a 变量的地址
	 * &b 指向 b 变量的地址
	 */
	swap(&a, &b);

	fmt.Printf("交换后 a 的值 : %d\n", a )
	fmt.Printf("交换后 b 的值 : %d\n", b )
}

func swap(x *int, y *int) {
	var temp int
	temp = *x    /* 保存 x 地址的值 */
	*x = *y      /* 将 y 赋值给 x */
	*y = temp    /* 将 temp 赋值给 y */
}

在这里插入图片描述

11、 结构体(struct)

声明一种行的数据类型 myint, 是int的一个别名

type myint int

<1> 定义一个结构体

package main

import "fmt"

// Books 定义一个结构体
type Books struct {
	title   string
	author  string
	subject string
	bookId int
}

func main() {

	// 创建一个新的结构体
	book1 := Books{"Go 语言", "www.runoob.com", "Go 语言教程", 6495407}
	fmt.Println(book1)

	// 也可以使用 key => value 格式
	book2 := Books{title: "Go 语言", author: "www.runoob.com", subject: "Go 语言教程", bookId: 6495407}
	fmt.Println(book2)

	// 忽略的字段为 0 或 空
	book3 := Books{title: "Go 语言", author: "www.runoob.com"}
	fmt.Println(book3)
}

在这里插入图片描述

<2> 访问结构体成员

package main

import "fmt"

type Books struct {
	title string
	author string
	subject string
	bookId int
}

func main() {
	var Book1 Books        /* 声明 Book1 为 Books 类型 */
	var Book2 Books        /* 声明 Book2 为 Books 类型 */

	/* book1.描述 */
	Book1.title = "Go 语言"
	Book1.author = "www.runoob.com"
	Book1.subject = "Go 语言教程"
	Book1.bookId = 6495407

	/* book2.描述 */
	Book2.title = "Python 教程"
	Book2.author = "www.runoob.com"
	Book2.subject = "Python 语言教程"
	Book2.bookId = 6495700

	/* 打印 Book1 信息 */
	fmt.Printf( "Book1.title : %s\n", Book1.title)
	fmt.Printf( "Book1.author : %s\n", Book1.author)
	fmt.Printf( "Book1.subject : %s\n", Book1.subject)
	fmt.Printf( "Book1.bookId : %d\n", Book1.bookId)

	/* 打印 Book2 信息 */
	fmt.Printf( "Book2.title : %s\n", Book2.title)
	fmt.Printf( "Book2.author : %s\n", Book2.author)
	fmt.Printf( "Book2.subject : %s\n", Book2.subject)
	fmt.Printf( "Book2.bookId : %d\n", Book2.bookId)
}

在这里插入图片描述

<3> 结构体作为函数参数

package main

import "fmt"

type Books struct {
	title string
	author string
	subject string
	book_id int
}

func main() {
	var Book1 Books        /* 声明 Book1 为 Books 类型 */

	/* book 1 描述 */
	Book1.title = "Go 语言"
	Book1.author = "www.runoob.com"
	Book1.subject = "Go 语言教程"
	Book1.book_id = 6495407

	/* 打印 Book1 信息 */
	printBook(Book1)
}

func printBook( book Books ) {
	fmt.Printf( "Book title : %s\n", book.title)
	fmt.Printf( "Book author : %s\n", book.author)
	fmt.Printf( "Book subject : %s\n", book.subject)
	fmt.Printf( "Book book_id : %d\n", book.book_id)
}

在这里插入图片描述

<3> 结构体指针

package main

import "fmt"

type Books struct {
	title string
	author string
	subject string
	book_id int
}

func main() {
	var Book1 Books        /* 声明 Book1 为 Books 类型 */

	/* book 1 描述 */
	Book1.title = "Go 语言"
	Book1.author = "www.runoob.com"
	Book1.subject = "Go 语言教程"
	Book1.book_id = 6495407

	/* 打印 Book1 信息 */
	printBook(&Book1)
}
func printBook( book *Books ) {
	fmt.Printf( "Book title : %s\n", book.title)
	fmt.Printf( "Book author : %s\n", book.author)
	fmt.Printf( "Book subject : %s\n", book.subject)
	fmt.Printf( "Book book_id : %d\n", book.book_id)
}

在这里插入图片描述

12、Go 语言切片(Slice)

Go 语言切片是对数组的抽象

Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片(“动态数组”)与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大

<1> 切片初始化

声明一个未指定大小的数组来定义切片:

数组初始化:

array := [3]int {1,2,3 }

切片初始化:

slice := []int {1,2,3 } 

直接初始化切片,[] 表示是切片类型,{1,2,3} 初始化值依次是 1,2,3,其 cap=len=3

【1】slice切片的4种声明定义方式

slice := []int {1,2,3 } 

2&3.切片不需要说明长度。或使用make() 函数(开辟空间)来创建切片:

var slice1 []type = make([]type, len)
也可以简写为
slice1 := make([]type, len)

3.也可以指定容量,其中 capacity 为可选参数。

make([]T, length, capacity)

<2>切片:把数组切成片

slice:= array[startIndex:endIndex] 
package main

import "fmt"

func main() {
	array := [5]int {1,2,3,4,5}

	slice := array[1:4]
	fmt.Println(slice)
}

在这里插入图片描述

<3> len() 和 cap() 函数

  • 切片是可索引的,并且可以由 len()方法获取长度。

  • 切片提供了计算容量的方法cap() 可以测量切片最长可以达到多少。

package main

import "fmt"

func main() {
	arrayInt := []int{1,2,3,4}
	fmt.Printf("len=%d cap=%d slice=%v\n",len(arrayInt),cap(arrayInt),arrayInt)
}

在这里插入图片描述

<4> 空(nil)切片

package main

import "fmt"

func main() {
	var numbers []int

	if numbers == nil {
		fmt.Printf("切片是空的")
	}
}

在这里插入图片描述

<5> 切片截取

package main

import "fmt"

func main() {
	arrayInt := [] int {0,1,2,3,4,5,6}

	fmt.Println(arrayInt[1:4])
	//[:4] => [0:4]
	fmt.Println(arrayInt[:4])
	// [1:] => [1:len(arrayInt)]
	fmt.Println(arrayInt[1:])
}

在这里插入图片描述

<6> append() 和 copy() 函数


package main

import "fmt"

func main() {
	var numbers []int
	printSlice(numbers)

	/* 允许追加空切片 */
	numbers = append(numbers, 0)
	printSlice(numbers)

	/* 向切片添加一个元素 */
	numbers = append(numbers, 1)
	printSlice(numbers)

	/* 同时添加多个元素 */
	numbers = append(numbers, 2,3,4)
	printSlice(numbers)

	/* 创建切片 numbers1 是之前切片的两倍容量*/
	numbers1 := make([]int, len(numbers), (cap(numbers))*2)

	/* 拷贝 numbers 的内容到 numbers1 */
	copy(numbers1,numbers)
	printSlice(numbers1)
}

func printSlice(x []int){
	fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}

在这里插入图片描述

13、范围(Range)

Go 语言中 range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对。

package main
import "fmt"
func main() {
	//这是我们使用range去求一个slice的和。使用数组跟这个很类似
	nums := []int{2, 3, 4}
	sum := 0
	//我们不需要使用该元素的序号,所以我们使用空白符"_"省略了
	for _, num := range nums {
		sum += num
	}
	fmt.Println("sum:", sum)
	//在数组上使用range将传入index和值两个变量。
	for i, num := range nums {
		if num == 3 {
			fmt.Println("index:", i)
		}
	}
	//range也可以用在map的键值对上。
	kvs := map[string]string{"a": "apple", "b": "banana"}
	for k, v := range kvs {
		fmt.Printf("%s -> %s\n", k, v)
	}
	//range也可以用来枚举Unicode字符串。第一个参数是字符的索引,第二个是字符(Unicode的值)本身。
	for i, c := range "go" {
		fmt.Println(i, c)
	}
}

在这里插入图片描述

13、Map(键值对集合)

Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。

Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。

<1> 定义 Map

可以使用内建函数 make 也可以使用 map 关键字来定义 Map:
一般都要初始化值或用make开辟空间,开辟空间少了空间也会自动增

【1】仅声明map:

如果不初始化 map,那么就会创建一个 nil map。nil map 不能用来存放键值对,一般都要初始化值或用make开辟空间

// 声明变量,默认 map 是 nil 
var myMapName map[键的数据类型]值的数据类型
var myMap map[string]string

【2】声明并初始化map:

 myMap3 := map[string]string{
	"one":   "php",
	"two":   "c++",
	"three": "python",
}

【3】用make开辟空间:

// 使用 make 函数,
myMapName := make(map[键的数据类型]值的数据类型)
myMap := make(map[string]string)

myMap := make(map[string]string,len)
myMap := make(map[string]string,10)

<2> 通历Map

package main

import "fmt"

func main() {
	capitalMap := make(map[string]string)

	/* map插入key - value对,各个国家对应的首都 */
	capitalMap [ "France" ] = "巴黎"
	capitalMap [ "Italy" ] = "罗马"
	capitalMap [ "Japan" ] = "东京"
	capitalMap [ "India " ] = "新德里"

	/*使用键输出地图值 */
	for key,value := range capitalMap {
		fmt.Println(key, "首都是", value)
	}
}

在这里插入图片描述

<3> 增删改查 和 delete() 函数

//增
capitalMap[ "France" ] = "巴黎"

//删
delete(countryCapitalMap, "France")

//改
capitalMap[ "France" ] = "巴黎2"

//查
fmt.Println(capitalMap[ "France" ])

delete() 函数:

package main

import "fmt"

func main() {
	/* 创建map */
	countryCapitalMap := map[string]string{"France": "Paris", "Italy": "Rome", "Japan": "Tokyo", "India": "New delhi"}

	fmt.Println("原始地图")

	/* 打印地图 */
	for country := range countryCapitalMap {
		fmt.Println(country, "首都是", countryCapitalMap [ country ])
	}

	/*删除元素*/ 
	delete(countryCapitalMap, "France")
	fmt.Println("法国条目被删除")

	fmt.Println("删除元素后地图")

	/*打印地图*/
	for country := range countryCapitalMap {
		fmt.Println(country, "首都是", countryCapitalMap [ country ])
	}
}

在这里插入图片描述

<4> 引用传递

package main

import "fmt"

func ChangeValue(cityMap map[string]string) {
	cityMap["France"] = "巴黎2"
}

func main() {
	capitalMap := make(map[string]string)
	/* map插入key - value对,各个国家对应的首都 */
	capitalMap [ "France" ] = "巴黎"
	capitalMap [ "Italy" ] = "罗马"
	capitalMap [ "Japan" ] = "东京"
	capitalMap [ "India " ] = "新德里"

	ChangeValue(capitalMap)

	fmt.Println(capitalMap)
}

在这里插入图片描述

14、 递归函数

递归,就是在运行的过程中调用自己。

<1> 阶乘

package main

import "fmt"

func Factorial(n uint64)(result uint64) {
	if n > 0 {
		result = n * Factorial(n-1)
		return result
	}
	return 1
}

func main() {
	var i int = 3
	fmt.Printf("%d 的阶乘是 %d\n", i, Factorial(uint64(i)))
}

<2> 斐波那契数列

package main

import "fmt"

func fibonacci(n int) int {
	if n < 2 {
		return n
	}
	return fibonacci(n-2) + fibonacci(n-1)
}

func main() {
	var i int
	for i = 0; i < 10; i++ {
		fmt.Printf("%d\t", fibonacci(i))
	}
}

在这里插入图片描述

15 类型转换

类型转换用于将一种数据类型的变量转换为另外一种类型的变量。
go 不支持隐式转换类型

package main

import "fmt"

func main() {
	var sum int = 17
	var count int = 5
	var mean float32

	mean = float32(sum)/float32(count)
	fmt.Printf("mean 的值为: %f\n",mean)
}

在这里插入图片描述

16、接口

Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。

定义接口

package main

import (
	"fmt"
)
// NokiaPhone 定义一个结构体
type NokiaPhone struct {
}
// IPhone 定义一个结构体
type IPhone struct {
}

// Phone 定义了一个接口Phone
type Phone interface {
	call()
}

func (nokiaPhone NokiaPhone) call() {
	fmt.Println("This is NokiaPhone!")
}

func (iPhone IPhone) call() {
	fmt.Println("This is IPhone!")
}

func main() {
	var phone Phone

	//根据传入参数的类型调用不同的方法
	phone = new(NokiaPhone)
	phone.call()

	phone = new(IPhone)
	phone.call()

}

在这里插入图片描述

17、错误处理

Go 语言通过内置的错误接口提供了非常简单的错误处理机制。
error类型是一个接口类型,这是它的定义:

type error interface {
    Error() string
}

我们可以在编码中通过实现 error 接口类型来生成错误信息。

函数通常在最后的返回值中返回错误信息。使用errors.New 可返回一个错误信息:

func Sqrt(f float64) (float64, error) {
    if f < 0 {
        return 0, errors.New("math: square root of negative number")
    }
    // 实现
}

实例:

package main

import (
	"fmt"
)

// DivideError 定义一个 DivideError 结构
type DivideError struct {
	dividee int
	divider int
}

// 实现 `error` 接口
func (de *DivideError) Error() string {
	strFormat := `
    Cannot proceed, the divider is zero.
    dividee: %d
    divider: 0
`
	return fmt.Sprintf(strFormat, de.dividee)
}

// Divide 定义 `int` 类型除法运算的函数
func Divide(varDividee int, varDivider int) (result int, errorMsg string) {
	if varDivider == 0 {
		dData := DivideError{
			dividee: varDividee,
			divider: varDivider,
		}
		errorMsg = dData.Error()
		return
	} else {
		return varDividee / varDivider, ""
	}

}

func main() {

	// 正常情况
	if result, errorMsg := Divide(100, 10); errorMsg == "" {
		fmt.Println("100/10 = ", result)
	}
	// 当除数为零的时候会返回错误信息
	if _, errorMsg := Divide(100, 0); errorMsg != "" {
		fmt.Println("errorMsg is: ", errorMsg)
	}

}

在这里插入图片描述

18 、并发

<1> go 函数并发

并发:并行出发、并行执行
Go 语言支持并发,我们只需要通过 go 关键字来开启 goroutine 即可。
goroutine 是轻量级线程,goroutine 的调度是由 Golang 运行时进行管理的。
goroutine 语法格式

go 函数名( 参数列表 )

Go 允许使用 go 语句开启一个新的运行期线程, 即 goroutine,以一个不同的、新创建的 goroutine 来执行一个函数。 同一个程序中的所有 goroutine 共享同一个地址空间。

package 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")
}

执行以上代码,你会看到输出的 hello 和 world 是没有固定先后顺序。因为它们是两个 goroutine 在执行:

package 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("new 1")
	say("new 2")
}

在这里插入图片描述

<2> 通道(channel)

通道(channel)是用来传递数据的一个数据结构。
通道可用于两个 goroutine(线程) 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <- 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。

ch <- v    // 把 v 发送到通道 ch
v := <-ch  // 从 ch 接收数据,并把值赋给 v

【1】定义通道

定义一个通道很简单,我们使用chan关键字即可,通道在使用前必须先创建:

ch := make(chan int)

注意:默认情况下,通道是不带缓冲区的。发送端发送数据,同时必须有接收端相应的接收数据。

以下实例通过两个 goroutine(线程) 来计算数字之和,在 goroutine(线程) 完成计算后,它会计算两个结果的和:

package main

import "fmt"

func sum(s []int, c chan int) {
        sum := 0
        for _, v := range s {
                sum += v
        }
        c <- sum // 把 sum 发送到通道 c
}

func main() {
        s := []int{7, 2, 8, -9, 4, 0}

        c := make(chan int)
        go sum(s[:len(s)/2], c)
        go sum(s[len(s)/2:], c)
        x, y := <-c, <-c // 从通道 c 中接收

        fmt.Println(x, y, x+y)
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值