go语言基础学习

目录

概述

Go语言优势

Go适合用来做什么?

环境搭建

安装和配置

第01天(基本类型、流程控制)

1.1 变量和常量

1.1.1 声明变量:

1.1.2声明常量:

1.1.3 iota枚举

1.2 基本数据类型

1.3 fmt包的格式化输出输入

1.4 类型转换和别名

1.4.1 类型转换

1.4.2 类型别名

1.5 运算符

1.6 流程控制

1.6.1 if语句

1.6.2 switch

1.6.3 for循环

1.6.4 Range迭代

1.6.5 break和continue的区别

 

第02天(函数、工程管理)

2.1 自定义函数

2.1.1 无参/有参 无返回值的函数

2.1.2 有返回值的函数

2.2 递归函数

2.3 函数类型

2.4 匿名函数与闭包

2.5 延迟调用defer

2.6 获取命令行参数

2.7 工程管理

2.7.1 工作区

2.7.2  导入包

第03天(复合类型)

3.1 指针

3.2 数组

3.3 slice切片

3.3.1 append函数

3.3.2 copy函数

3.3.3 切片的值传递

3.4 map

3.4.1 map的创建与初始化

3.4.2 map遍历和删除元素

3.5 结构体

3.5.1 结构体的创建

结构体的继承

3.5.2 结构体的值传递

3.5.3 可见性规则

第04天(面向对象编程)

4.1 匿名组合

4.2 结构体的方法

4.3 接口

4.3.1  创建

4.3.2 接口继承

4.3.3 通过if / switch实现类型断言

第05天(异常、文本文件处理)

5.1 error接口、panic、recover

5.2 字符串处理

5.3 正则表达式

5.4 JSON处理

5.5 文件操作

第06天(并发编程)

6.1 概述

6.2 goroutine

6.3 channel

6.4 select

第07天(网络概述、socket编程)

7.1 网络概述

7.2 Socket编程

7.3 案例:并发的聊天室服务器

第08天(HTTP编程)

8.1 Web工作方式

8.2 Http报文格式

8.3 Http编程

8.4 案例:网络爬虫


 

概述

Go语言是云计算时代的C语言。专门针对多处理器系统应用程序的编程进行了优化,会用Go编译的程序可以媲美C或C++的速度,而且更加安全,支持并行进程。Go不仅提供了高性能的语言,同时也让开发更快速。

GO语言中文社区,GO语言标准库

Go语言优势

  1. 可直接编译成机器码,不依赖其他库,glibc的版本有一定要求,部署就是扔一个文件上去就完成了。
  2. 静态类型语言,但是有动态语言的感觉,静态类型的语言就是可以在编译的时候检查出来隐藏的大多数问题,动态语言的感觉就是有很多的包可以使用,写起来的效率很高。
  3. 语言层面支持并发,这个就是Go最大的特色,天生的支持并发。Go就是基因里面支持的并发,可以充分的利用多核,很容易的使用并发。
  4. 内置runtime,支持垃圾回收,这属于动态语言的特性之一吧,虽然目前来说GC(内存垃圾回收机制)不算完美,但是足以应付我们所能遇到的大多数情况,特别是Go1.1之后的GC。
  5. 简单易学,Go语言的作者都有C的基因,那么Go自然而然就有了C的基因,那么Go关键字是25个,但是表达能力很强大,几乎支持大多数你在其他语言见过的特性:继承、重载、对象等。
  6. 丰富的标准库,Go目前已经内置了大量的库,特别是网络库非常强大。
  7. 内置强大的工具,Go语言里面内置了很多工具链,最好的应该是gofmt工具,自动化格式化代码,能够让团队review变得如此的简单,代码格式一模一样,想不一样都很困难。
  8. 跨平台编译,如果你写的Go代码不包含cgo,那么就可以做到window系统编译linux的应用,如何做到的呢?Go引用了plan9的代码,这就是不依赖系统的信息。
  9. 内嵌C支持,Go里面也可以直接包含C代码,利用现有的丰富的C库,让开发更快速。

Go适合用来做什么?

  • 服务器编程,例如处理日志、数据打包、虚拟机处理、文件系统等。
  • 分布式系统,数据库代理器等。
  • 网络编程,目前应用最广,包括Web应用、API应用、下载应用。
  • 内存数据库,如google开发的groupcache、couchbase的部分组件。
  • 云平台,目前国外很多云平台采用Go开发,CloudFoundy的部分组件、前VMare的技术总监自己出来高德apcera云平台。

环境搭建

安装和配置

配置Go语言开发运行环境(环境变量是自动配置的)。参考教程

Hello.go

// 1)go语言以包作为管理单位。
// 2)每个文件必须先声明包。
// 3)程序必须有一个main包。
package main

import "fmt"

// 入口函数 main(),左括号必须与函数名同行
func main() {
    //打印。Println() 会自动换行。
    //调用函数,大部分都需要导入包。
    // go语言语句结尾没有分号。
	fmt.Printf("hello world\n")
}

命令行运行程序

# 格式化go文件
 go fmt 01_Hello.go

# 编译,成功后会生成exe可执行文件
go build 01_Hello.go
# 运行
.\01_Hello.exe

# 编译运行,但不生成exe可执行文件
go run 01_Hello.go

 

第01天(基本类型、流程控制)

1.1 变量和常量

1.1.1 声明变量:

// 变量名是a,类型是int(可以省略),初始化值是10(默认值是0)
var a int = 10
var b,c int

// 直接声明变量值,并根据 变量值 推导出变量的类型
var c1 = 20
fmt.Printf("c1=%d",c1)

d := 30
fmt.Printf("d type is %T",d)

// 多重复值
e,f,g := 10,20,30

// 交换两个变量的值
i,j := 10,20
i,j = j,i
fmt.Printf("i=%d,j=%d\n",i,j)
  • fmt.Println()  ,输出打印行。fmt.Println("a=",a,"b=",b,"c=",c):即 逗号 作为连接符。
  • fmt.Printf(),格式化输出。fmt.Printf("a=%d,b=%d,c=%d \n",a,b,c)。
  • := ,定义变量并初始化,据初始化值,自动推导变量类型。
  • %T,格式化输出函数中,表示输出变量的类型。

1.1.2声明常量:

const a int = 10
const b = 3.14

1.1.3 iota枚举

func main() {
	const {
		a=iota // 0
		b=iota // 1
		c=iota // 2
	}
	fmt.Printf("a=%d,b=%d,c=%d\n",a,b,c)

    const {
		i = iota // 0
		j1,j2,j3 = iota,iota,iota // 1
		k = iota // 2
	}
}
  • iota常量自动生成器,从0开始,自动累加1。用于给常量赋值。
  • 当重新使用 const 关键字声明常量时,iota重置为0。
  • 在同一个const关键字中,一行iota,只会累加1。

1.2 基本数据类型

go语言中的基本数据类型
类型 名称 长度 零值/初始值 说明
bool 布尔类型 1 false true或false
byte 字节型 1 0 uint8别名
rune 字符类型 4 0 专用于鵆unicode编码,等价于uint32
int,uint 整型 4或8 0 32位或64位
int8,uint8 整型 1 0 -128~127,0~255
int16,uint16 整型 2 0 -32768~32767,0~65535
int32,uint32 整型 4 0 -21亿~21亿,0~42亿
int64,uint64 整型 8 0  
float32 浮点数 4 0.0 小数位精确到7位
float64 浮点数 8 0.0 小数位精确到15位
complex64 复数类型 8    
complex128 复数类型 16    
uintptr 整型 4或8   足以存储指针的uint32或uint64整数
string 字符串   "" utf-8字符串

 

1.3 fmt包的格式化输出输入

输入格式

func main() {
	var a int
	fmt.Printf("请输入变量a: ")
        // fmt.Scanf("%d",&a)
	fmt.Scan(&a)
	fmt.Println("a=",a)
}
fmt的格式化
格式 含义
%% 一个%字面量
%b 一个二进制整数值(基数为2,)或者是一个(高级的)用科学计数法表示的指数为2的浮点数
%c 字符型,可以把数字按照ASCII码转成对应的字符
%d 十进制数
%e 科学计数法,e表示浮点数或者复数值
%E 科学计数法,E表示浮点数或者复数值
%f 标准计数法表示的浮点数或复数值
%g,%G

以%e或者%f表示的浮点数或者复数,任何一个都以最为紧凑的方式输出

%o 八进制数
%p 十六进制数,前缀为0x,字符是小写的a-f
%q 转义
%s 字符串
%t 布尔值,true或false
%T 值的类型
%U Unicode编码表示的整型码点,默认为4个数字字符
%v

使用默认格式输出的内置或自定义类型的值,或者是使用其类型的String()方式输出的自定义值,如果该方法存在的话。

%x,%X 十六进制证书,分别是小写字母表示、大写字母表示

 

1.4 类型转换和别名

1.4.1 类型转换

func main() {
	ch := 'c'
	var a = int(ch)
	fmt.Println("a=",a)
}

 

1.4.2 类型别名

type bigint int64 //int64类型改名为bigint
var x bigint = 100
type {
    myint int //int改名为myint
    mystr string //string改名为mystr
}

1.5 运算符

  • &  取地址运算符 , 如:&a ,即是变量a的地址。
  • *   取值运算符,如:*a,即指针变量a所指向内存的值。

略。

1.6 流程控制

1.6.1 if语句

func main() {
	// if
	var a = 3
	if a == 3 {
		fmt.Println("a==3")
	}
	
	// if 中的 变量定义
	if b := 3; b == 3 {
		fmt.Printf("b==3\n")
	}

	// if-else
	if x := 3; x == 4 {
		fmt.Println("a==4")
	} else {
		fmt.Println("a!=4")
	}

	// if-elseif-if
	if y := 0; y == 3 {
		fmt.Println("y==0")
	} else if y == 1 {
		fmt.Println("y==1")
	} else {
		fmt.Println("y!=0或1")
	}
}

1.6.2 switch

func main() {
	var num int
	fmt.Printf("请输入num的值:")
	fmt.Scan(&num)
	switch num {
	case 1:
		fmt.Println("num=1")
		break
	case 2:
		fmt.Println("num=2")
		break
	case 3:
		fmt.Println("num=3")
		break
	default:
		fmt.Println("num错误")
	}
}

1.6.3 for循环


func main() {
	sum:=0
	for i:=1;i<=100;i++{
		sum += i
	}
	fmt.Println("sum=",sum)
}

1.6.4 Range迭代

func main() {
	str := "abc"
	//迭代打印,默认返回两个值:元素位置、元素本身
	for i,data := range str{
		fmt.Printf("str[%d]=%c\n",i,data)
	}

	// 第2个返回值默认丢弃,只有i接收到元素位置
	for i := range str{
		fmt.Printf("str[%d]\n",i)
	}
}

 

1.6.5 break和continue的区别

func main() {
	i := 0
	for{ // 死循环
		i++
		if i==5{
			// 跳过之后的步骤,继续执行循环体
			continue
		}
		if i==10{
			// 打破/跳出循环体
			break
		}
		fmt.Println("i=",i)
	}
}

 

第02天(函数、工程管理)

函数组成:

  • func:函数关键字,由此声明。
  • funcName:函数名称
  • 参数列表:可有可无,格式为:变量名 类型,多个参数逗号分隔,不支持默认参数
  • 返回类型:若有返回值,必须使用return语句。

2.1 自定义函数

2.1.1 无参/有参 无返回值的函数

package main

import "fmt"

// 无参无返回值
func fun1(){
	a:=6
	fmt.Println("a=",a)
}

// 有参无返回值
func fun2(a int){
	fmt.Println("a=",a)
}

// 不定参数 函数
func fun3(a int,args ... int){
	fmt.Printf("a = %d \n",a)
	for i,data := range args {
		fmt.Printf("args[%d]=%d \n",i,data)
	}
}

// 不定参数函数 的 不定参 传递
func fun4(args ... int){
	//fun3(args[0],args ...)
	// 从第0个元素开始,传递前2个元素
	//fun3(args[0],args[:2] ...)
	// 从第2个元素开始,传递包括第2个元素以及之后的所有元素
	fun3(args[0],args[2:] ...)
}

func main() {
	//fun1()
	//fun2(2)
	//fun3(1,2,3)
	fun4(9,8,7,6)
}

2.1.2 有返回值的函数

// 需要 给 返回值 声明一个变量值
func fun1() (result int) {
	result = 6
	return
}

// 多个返回值
func fun2() (a int,b int,c int){
	a,b,c = 1,2,3
	return
}

func main() {
	a := fun1()
	fmt.Println("a=",a)
	x,y,_ := fun2()
	fmt.Printf("x=%d,y=%d,",x,y)
	_,_,z := fun2()
	fmt.Println("z=",z)
}
func getMax(a, b int) (max, min int) {
	if a >= b {
		max = a
		min = b
	} else {
		max = b
		min = a
	}
	return
}

func main() {
	max, min := getMax(10, 20)
	fmt.Printf("max=%d,min=%d", max, min)
}

2.2 递归函数

func fibonacci(item int) (result int) {
	if item == 1 || item == 2 {
		result = 1
		return
	} else if item > 2 {
		result = fibonacci(item-1) + fibonacci(item-2)
		return
	} else {
		result = -1
		return
	}
}

func main() {
	item := 20
	result := fibonacci(item)
	fmt.Printf("斐波那契的第%d个元素是:%d", item, result)
}

 

2.3 函数类型

// 函数类型,函数也是一种数据类型。
// type 关键字,FuncType 是一个函数类型的自定义名称
type FuncType func(int, int) int

func Add(a,b int) int{
	return a+b
}

func Minus(a,b int) int{
	return a-b
}

// 回调函数,函数有一个参数是 函数类型,这个函数就是回调函数。声明一个 fTest 的函数类型作为形参
// 实现一个计算器,可以进行四则运算
// 多态
func Calc(a, b int, fTest FuncType) (result int) {
	fmt.Println("Calc")
	result = fTest(a, b)
	return
}

func main() {
	a := Calc(1,1,Add)
	//a := Calc(1,1,Minus)
	fmt.Println("a=",a)
}

2.4 匿名函数与闭包

func main() {
	// 定义匿名函数,并调用 打印变量值
	func1 := func(a int,str string){
		fmt.Println("a=",a)
		fmt.Println("str=",str)
	}
	func1(1,"Jack")
}
func main() {
	a := 10
	str := "Milk"
	func() {
		// 闭包 是 以 引用方式 获取外部变量的,当引用值被改变后,闭包外部的值也会被改变
		a = 6
		str = "go"
		fmt.Printf("【内部】a=%d,str=%s\n", a, str)
	}() // ()表示 直接调用
	fmt.Printf("【外部】a=%d,str=%s\n", a, str)
}
func fun() func() int {
	var x int
	return func() int {
		// 闭包中的变量,不会因为重新调用函数而被重新初始化
		x++
		return x * x
	}
}

func main() {
	result := fun()
	fmt.Println("result=",result())
	fmt.Println("result=",result())
	fmt.Println("result=",result())
	fmt.Println("result=",result())
}

 

2.5 延迟调用defer

关键字 defer 用于延迟一个函数或者方法(或者当前所创建的匿名函数)的执行。注意,defer语句只能出现爱你在函数或方法的内部。

defer语句经常被用于处理成对的操作,比如开&关,连接&断开,加锁&释放锁等。通过defer机制,不论函数逻辑多复杂,都能保证在任何执行路径下,资源被释放,释放资源的defer应该直接跟在请求资源的语句后。

func main() {
	defer fmt.Println("延迟执行,main函数执行结束后才被执行。")
	fmt.Println("直接被执行。")
}

多个defer的执行顺序

先进后出,写在前边的defer,最后被执行。哪怕之前有函数发生了错误,defer修饰的方法也会被执行。

defer和匿名函数结合使用

func main() {
	a := 10
	b := 20
	defer func() {
		fmt.Printf("【内部】a=%d, b=%d\n",a,b)
	}()
	a=111
	b=222
	fmt.Printf("【外部】a=%d, b=%d\n",a,b)
}

 

2.6 获取命令行参数

需要以命令行的形式执行,因为要获取 命令行输入的参数。idea打开terminal,执行 go run 01_Hello.go a b c d

func main() {
	// 接收用户传递的参数,都是以字符串方式传递的
	list := os.Args
	for i,data := range list{
		fmt.Printf("lislt[%d]=%s\n",i,data)
	}
}

 

2.7 工程管理

2.7.1 工作区

Go代码必须放在工作区中。工作区是一个对应于特定工程的目录,包含三个子目录:src目录、pkg目录和bin目录。

  • src目录:存放Go源码文件。
  • pkg目录:存放 go install命令构建安装后的代码包(包含Go库源码文件)的“.a”归档文件。
  • bin目录:与pkg目录类似。在通过go install命令完成安装后,保存由Go命令源码文件生成的可执行文件。
  • 当GoPath中只包含一个工作区的目录路径时,go install命令才会把命令源码安装到当前工作区的bin目录下。若环境变量GoPath中包含多个工作区的目录路径,像这样执行 go install 命令就会失败,必须设置环境变量GoBin。

GoPath设置:为了能构建工程,需要先把所需工程的概目录加入到环境变量GoPath中,否则即使处于同一工作目录,代码之间也无法通过绝对代码包路径完成调用。

实际开发环境中,工作目录往往有多个,这些工作目录的路径都需要添加至GoPath。有多个目录时,需注意分隔符(Windows是分好,Linux是冒号),当有多个GoPath时,默认会将go get的内容放到第一个目录下。

 

2.7.2  导入包

导包的方式

所有Go语言的程序都会组织成多干组文件,每组文件被称为一个包。包的代码可以作为最小复用单元,被其他项目引用。

//点操作。在调用包中的函数时,可以省略前缀
import {
	. "fmt"
}
func main() {
	Println("Hello go")
}

// 别名操作,调用的包可以自定义别名
import {
	io "fmt"
}
func main() {
	io.Println("Hello go")
}

// 忽略导入包。有时用户可能需要导入一个包,但是不需要引用这个包的标识符。此时可以使用空白标识符_来重命名这个导入包。
// _操作其实是引入该包,但不直接使用包里的函数,而是调用了该包里面的init函数,init函数下面就介绍
import {
	_ "fmt"
}
func main() {
	
}

 

包(类似于Java的类)中的 init函数和main函数

在main入口文件中,导入其它包时,会先执行该包的init函数,其它函数只有调用时才会执行。

 

第03天(复合类型)

复合类型分类
类型 名称 长度 默认值 说明
pointer 指针   nil  
array 数组   0  
slice 切片   nil 引用类型
map 字典   nil 引用类型
struct 结构体      

3.1 指针

  • 默认值nil,没有NULL常量;
  • 操作符“&”取变量地址,“*”通过指针访问目标对象;
  • 不支持指针运算,不支持“->”运算符,直接用“.”访问目标成员。

// 值传递
func swap1(a, b int) {
	a, b = b, a
	fmt.Printf("【swap】a=%d,b=%d\n", a, b)
}

// 指针地址传递,交换值
func swap2(a, b *int) {
	*a, *b = *b, *a
	fmt.Printf("【swap】a=%d,b=%d\n", *a, *b)
}

func main() {
	a, b := 10, 20
	swap2(&a, &b)
	fmt.Printf("【main】a=%d,b=%d\n", a, b)
}

 

3.2 数组

func main() {
	// 创建随机数,设置种子。只需设置一次。以当前系统时间作为种子参数
	rand.Seed(time.Now().UnixNano())

	var a [10]int
	for i:=0; i<10;i++  {
		//随机数很大
		//a[i] = rand.Int()
		//限定随机数的范围
		a[i] = rand.Intn(10)
	}
	fmt.Println("a=",a)

	// 二维数组
	var b [3][4]int
	for i := 0; i < 3; i++ {
		for j := 0; j < 4; j++ {
			b[i][j] = i + j
		}
	}
	fmt.Println("b=",b)
	c := [2][3] int {
		{1,2,3},
		{2,3,4},
	}
	fmt.Println("c=",c)
}
// 冒泡排序,值传递
func bubble(a [5]int) {
	fmt.Println("【排序前】", a)

	for i := 0; i < len(a); i++ {
		for j := i + 1; j < len(a); j++ {
			if a[i] > a[j] {
				a[i], a[j] = a[j], a[i]
			}
		}
	}
	fmt.Println("【排序后】", a)
}

// 冒泡排序,指针引用传递
func pointer(a *[5]int) {

	for i := 0; i < len(a); i++ {
		for j := i + 1; j < len(a); j++ {
			if a[i] > a[j] {
				a[i], a[j] = a[j], a[i]
			}
		}
	}

}

func main() {
	a := [5]int{5, 3, 8, 6, 9}
	//bubble(a)
	fmt.Println("【排序前】", a)
	pointer(&a)
	fmt.Println("【排序后】", a)
}

3.3 slice切片

切片:通过内部指针和相关属性引用数组片段,已实现数组变长方案。弥补数组的定长。

  • 切片是数组的一个引用,因此切片是引用类型,在进行传递时,遵守引用传递的机制。
  • 切片的使用和数组类似,遍历切片、访问切片的元素和求切片长度len(slice)都一样。
  • 切片的长度是可以变化的,因此切片是一个可以动态变化数组。
// 切片的基础,main函数
func mainBackup() {
	// 切片的创建过程中,[]是空的,表示长度不固定。
	a := []int{1, 2, 3, 4, 5}
	// 切片截取时,格式为[low:high:max],下标从low开始,截取到下标high(不包括),容量cap=max-low。
	// low、high的下标值必须 在 切片a 元素下标的范围内,并且 max >= high-low && max < len(a)
	// 下例中,cap容量: 5-1=4。
	s := a[1:4:5]
	fmt.Println("s=", s)
	fmt.Printf("len(s)=%d, cap(s)=%d\n", len(s), cap(s))

	y := []int{}
	// 切片中 添加元素
	y = append(s, 11)
	fmt.Println(y)
}

// 创建切片
func main() {
	//创建切片 1
	a := []int{1,2,3,4,5}
	fmt.Println(a)

	// 创建切片 2
	b := make([]int,5,10)
	fmt.Printf("len(b)=%d, cap(b)=%d \n",len(b),cap(b))
	fmt.Println(b)
}

切片截取的常用方法:

切片截取
操作 含义
s[n] 切片s中索引位置为n的元素
s[:] 切片s的索引位置0 到 len(s)-1 处 截取的切片
s[low:]

切片s的索引位置 low 到 len(s)-1 处 截取的切片

s[:high] 切片s的索引位置 0 到 high 处 截取的切片
s[low:high] 切片s的索引位置 low 到 high 处 截取的切片
s[low:high:max] 切片s的索引位置 low 到 high 处 截取的切片,长度len=high-low,容量cap=max-low
len(s) 切片s的长度,总是<=cap(s)
cap(s) 切片s的容量,总是>=len(s)

3.3.1 append函数

向切片的末尾添加元素。

特点:扩容时,若超过底层数组的容量长度,通常以2倍的容量扩容,并复制原来的数组到新的底层数组中。

func main() {
	a := []int{1, 2, 3}
	a = append(a, 5)
	a = append(a, 5)
	a = append(a, 5)
	fmt.Printf("len=%d, cap=%d\n", len(a), cap(a))
	fmt.Println("[a]=", a)
}

3.3.2 copy函数

copy可以在两个slice间复制数据,复制长度以len小的为准,两个slice可指向同一底层数组。


func main() {
	data := [...]int{0,1,2,3,4,5,6,7,8,9}
	a := data[8:]
	b := data[:5]
	//将a切片的值,拷贝到b切片中,并覆盖掉b原先的值。
	copy(b,a)
	fmt.Println(b)
}

 

3.3.3 切片的值传递

//初始化切片
func InitData(s []int) {
	rand.Seed(time.Now().UnixNano())
	for i := 0; i < len(s); i++ {
		s[i] = rand.Intn(100)
	}
}

func BubbleSort(s []int) {
	n := len(s)
	for i := 0; i < n-1; i++ {
		for j := 0; j < n-i-1; j++ {
			if s[j] > s[j+1] {
				s[j], s[j+1] = s[j+1], s[j]
			}
		}

	}
}

func main() {
	n := 10
	s := make([]int, n)
	InitData(s)
	fmt.Println("排序前:", s)
	BubbleSort(s)
	fmt.Println("排序后:", s)
}

 

3.4 map

3.4.1 map的创建与初始化

func main() {
	var m map[int]string
	fmt.Printf("len=%d\t", len(m))
	fmt.Println("m=", m)

	m2 := make(map[int]string)
	for i := 0; i < 10; i++ {
		m2[i] = "Jack" + strconv.Itoa(i)
	}
	fmt.Println("m2=", m2)

	m3 := make(map[string]string, 2)
	m3["a"] = "Java"
	m3["b"] = "Go"
	m3["c"] = "C"
	fmt.Println("len=", len(m3), "\t m3=", m3)

	m4 := map[int]string{1: "python", 2: "C++", 3: "JavaScript"}
	fmt.Println("m4=", m4)
}

3.4.2 map遍历和删除元素

// map作为函数参数传递
func operator(m map[int]string){
	// map 删除key-value
	delete(m,3)
	fmt.Println("m=",m)
}

func main() {
	m := make(map[int]string)
	for i := 0; i < 10; i++ {
		m[i] = "Jack" + strconv.Itoa(i)
	}
	//遍历,无序的
	for key,value := range m{
		fmt.Printf("%d—>%s \n",key,value)
	}
	operator(m)
}

 

3.5 结构体

3.5.1 结构体的创建

type Student struct {
	id   int
	name string
	sex  byte
	age  int
	addr string
}

func main() {
	var stu = Student{1, "Jack", 'm', 18, "北京"}
	fmt.Println("stu=", stu)

	var stu2 = Student{name: "Michael", addr: "天津"}
	fmt.Println("stu2=", stu2)

	stu3 := &Student{age: 22, id: 101, name: "Mike"}
	fmt.Println("stu3=", *stu3)
	stu3.id = 102
	fmt.Println("stu3.id=", stu3.id)
}

结构体的继承

type Person struct {
	name string
	sex  byte
	age  int
}

type Student struct {
	id int
	Person
	addr string
}

func main() {
	//var stu = Student{128,Person{"Jack",'w',22},"河南"}
	var stu = Student{Person: Person{name: "Michael"}, addr: "天津"}
	fmt.Println("stu=", stu)
}

 

3.5.2 结构体的值传递

type Student struct {
	id   int
	name string
	sex  byte
	age  int
	addr string
}

func test1(stu Student){
	stu.id=102
	fmt.Println("【test1】stu=", stu)
}

func test2(stu *Student){
	stu.id=102
	fmt.Println("【test2】stu=", stu)
}

func main() {
	var stu = Student{name: "Michael", addr: "天津"}
	//test1(stu)
	test2(&stu)
	fmt.Println("【main】stu=", stu)
}

3.5.3 可见性规则

可见性:类似于Java中的public、private、protected的概念。如果想使用其他包的函数、结构体类型、结构体成员、函数名、类型名、结构体成员变量名,那么其首字母必须大写,才能可见。若是小写,只能在同一个包中使用。

 

第04天(面向对象编程)

4.1 匿名组合

略。

4.2 结构体的方法

type Person struct {
	name string
	sex  byte
	age  int
}

// 方法:结构体的 打印函数
func (tmp Person) PrintInfo() {
	fmt.Println("tmp=", tmp)
}

// 结构体的 初始化赋值 函数
func (p *Person) SetInfo(n string, s byte, a int) {
	p.name = n
	p.sex = s
	p.age = a
}

func main() {
	var p Person
	(&p).SetInfo("Mike", 'm', 28)
	p.PrintInfo()
}

type Person struct {
	name string
	sex  byte
	age  int
}

// 接收者 为 普通变量, 值传递
func (p Person) SetInfoValue(n string, s byte, a int) {
	p.name = n
	p.sex = s
	p.age = a
}

// 接收者为 指针变量 , 引用传递
func (p *Person) setInfoPointer(n string, s byte, a int) {
	p.name = n
	p.sex = s
	p.age = a
}

func main() {
	p := Person{"Jack",'m',12}
	fmt.Println("&p=",&p) // 打印地址

	p.SetInfoValue("Mike",'w',18)
	fmt.Println("p=",p)

	p.setInfoPointer("Rose",'w',27)
	fmt.Println("p=",p)

	(&p).setInfoPointer("Rose",'w',28)
	fmt.Println("p=",p)
}

4.3 接口

go中,接口命名习惯以er结尾。

空接口:不包含任何方法的接口。所有类型都可以认为是实现了空接口。可以存储任意类型的数值。

func main() {
	// 创建一个空接口
	i := make([]interface{}, 3)
	i[0] = 1
	i[1] = "hello"
	i[2] = Student{25, "Jack"}
	fmt.Println(i)
}

4.3.1  创建

// 定义接口类型,并声明接口方法
type Humaner interface {
	sayHi()
}

type Student struct {
	id   int
	name string
}

// 实现 接口方法
func (tmp *Student) sayHi() {
	fmt.Printf("Student[%s,%d] sayhi\n", tmp.name, tmp.id)
}

type Teacher struct {
	addr  string
	group string
}

func (tmp *Teacher) sayHi() {
	fmt.Printf("Teacher[%s,%s] sayhi\n", tmp.addr, tmp.group)
}

func main() {
	var i Humaner
	s := &Student{13, "Jack"}
	i = s
	i.sayHi()

	t := &Teacher{"bj", "go"}
	i = t
	i.sayHi()
}

4.3.2 接口继承

// 定义接口类型,并声明接口方法
type Humaner interface {
	sayHi()
}

// 继承 Humaner接口
type Personer interface {
	Humaner
	sing(lrc string)
}

type Student struct {
	id   int
	name string
}

// 实现 接口方法
func (tmp *Student) sayHi() {
	fmt.Printf("Student[%s,%d] sayhi\n", tmp.name, tmp.id)
}

func (tmp *Student) sing(lrc string) {
	fmt.Printf("Student在唱:%s\n", lrc)
}

func main() {
	// 接口继承 案例
	var i Personer
	s := &Student{13, "Jack"}
	i = s
	i.sayHi()
	i.sing("好久不见")

	//接口转换
	var hunamer Humaner
	var personer Personer
	personer = &Student{22,"Rose"}
	hunamer = personer
	hunamer.sayHi()
}

4.3.3 通过if / switch实现类型断言

type Student struct {
	id   int
	name string
}

func main() {
	// 创建一个空接口
	i := make([]interface{}, 3)
	i[0] = 1
	i[1] = "hello"
	i[2] = Student{25, "Jack"}
	// if 断言类型
	for index,data := range i{
		if value,ok := data.(int);ok==true{
			fmt.Printf("i[%d] 类型为int,内容为%d\n",index,value)
		}else if value,ok := data.(string);ok==true{
			fmt.Printf("i[%d] 类型为string,内容为%s\n",index,value)
		}else if value,ok := data.(Student);ok==true{
			fmt.Printf("i[%d] 类型为Student,内容为 name=%s,id=%d\n",index,value.name,value.id)
		}
	}

	// switch 断言类型
	for index,data := range i{
		switch value := data.(type) {
		case int:
			fmt.Printf("i[%d] 类型为int,内容为%d\n",index,value)
		case string:
			fmt.Printf("i[%d] 类型为string,内容为%s\n",index,value)
		case Student:
			fmt.Printf("i[%d] 类型为Student,内容为 name=%s,id=%d\n",index,value.name,value.id)
		}
	}
}

 

第05天(异常、文本文件处理)

5.1 error接口、panic、recover

在Go语言中,程序的错误由内建的error接口支持,errors标准包提供了最基本的错误处理方法,用户还可自定义错误处理。而程序的异常处理通常由panic和recover实现触发和终止。

5.1.1 错误处理:error

// Error接口
func createErr() {
	err1 := fmt.Errorf("%s", "this is normal error")
	fmt.Println("err1=", err1)
	err2 := errors.New("this is normal err2")
	fmt.Println("err2=", err2)
}

func MyDiv(a, b int) (result int, err error) {
	if b == 0 {
		err = errors.New("分母不能为0")
		return
	}
	result = a / b
	return
}

func main() {
	createErr()
	result, err := MyDiv(1, 1)
	if err != nil {
		fmt.Println("err=", err)
	} else {
		fmt.Println("result=", result)
	}
}

5.1.2 异常处理:panic

当遇到不可恢复的错误状态,比如空指针引用、下标越界、向空map添加键值等,应该调用panic。当panic异常发生时,程序会中断运行,随后程序崩溃并输出日志信息,日志信息把包括panic value和函数调用的堆栈跟踪信息。

func test1() {
	fmt.Println("aaaaa")
}
func test2() {
	//主动调用 panic
	panic("this is a panic test")
}
func test3() {
	fmt.Println("ccccc")
}

// 数组越界
func test4(x int) {
	var a [10]int
	a[x] = 111
}

func main() {
	//test1()
	//test2()
	//test3()
	test4(20)
}

 

5.1.3 异常处理:recover

panic会导致程序崩溃(crash/终止运行)。

func test1() {
	fmt.Println("aaaaa")
}
func test2(x int) {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()
	var a [10]int
	a[x] = 111
}
func test3() {
	fmt.Println("ccccc")
}

func main() {
	test1()
	test2(11)
	test3()
}

 

5.2 字符串处理

5.2.1 字符串常用操作

字符串常用函数
函数名 返回值 使用例子 说明
contains bool(true/false) strings.Contains("seafood","food") 字符串是否包含其他字符串
join string s:=[]string{"good","morning","Jack"}
result:=strings.Join(s," ")
fmt.Println(result)
将数组中的字符串进行拼接
index int strings.Index("Morning", "ing") 查找执行字符串所在的位置,不存在返回-1
repeat string "ba"+strings.Repeat("na",2) 重复拼接字符串多次。
replace string strings.Replace("banana","na","bala",-1) 字符串替换,n表示替换次数,小于0表示全部替换
split slice切片 strings.Split("Let us go to school"," ") 字符串按照 某字符 进行分割,返回slice切片
trim string strings.Trim("oppo","o") 头尾去除指定字符串
fields slice切片 strings.Fields(" it's to far from here ") 去除字符串的空格符,并按照空格分割,返回slice切片类型

 

5.2.2 字符串转换

字符串转换
函数名 使用例子 说明
Append系列函数 func main() {
    str := make([]byte,0,100) // 初始化一个空数组
    str = strconv.AppendInt(str,1234,10) // 以10进制方式追加
    str = strconv.AppendQuote(str,"abcdefg")
    str = strconv.AppendQuoteRune(str,'崔')
    fmt.Println(string(str))
}
将整数等转换为字符串后,添加到现有的字节数组中。
func main() {
	var str string
	str = strconv.FormatBool(false)
	//'f'—— 打印格式:小数方式,-1——小数点位数(紧缩模式),64以float64处理
	str = strconv.FormatFloat(3.14, 'f', -1, 64)

	//整型转字符串,常用
	str = strconv.Itoa(666)
	fmt.Println("str=", str)
	// 字符串转整型,常用
	a,_ := strconv.Atoi("456")
	fmt.Println("a=",a)

	// 字符串转其他类型
	var flag bool
	var err error
	flag, err = strconv.ParseBool("true")
	if err == nil {
		fmt.Println("flag=", flag)
	} else {
		fmt.Println("err=", err)
	}
}

 

5.3 正则表达式

// 字符串匹配
func test1() {
	buf := "abc azc a7c aac 888 a9c tac"
	// 1. 解释规则,它会解析正则表达式,如果成功返回解释器
	reg1 := regexp.MustCompile(`a.c`)
	if reg1 == nil { // 解释失败,返回nil
		fmt.Println("err=")
		return
	}

	//2. 根据规则提取关键信息,获取符合 a*c 格式 的字符串。-1获取所有匹配的。
	result1 := reg1.FindAllStringSubmatch(buf, 3)
	fmt.Println("result1=", result1)
}

// 字符串中的 小数 匹配
func test2() {
	buf := "43.15 568 asdf 1.25 3.14 7. 8.9 asqe 6.66 .5 8.9"
	reg := regexp.MustCompile(`\d+\.\d+`)
	if reg == nil {
		fmt.Println("MustCompile err")
		return
	}
	//result := reg.FindAllString(buf, -1)
	// 提取后 分组
	result := reg.FindAllStringSubmatch(buf,-1)
	fmt.Println("result=", result)
}

func main() {
	//test1()
	test2()
}

 

5.4 JSON处理

5.4.1 结构体和JSON的相互转换

type IT struct {
	Company  string `json:"公司"` // json的二次编码
	Subjects []string
	IsOK     bool    `json:"-"` // - 表示 转换成json时 忽略该字段
	Price    float64 `json:"价格"`
	Age      int     `json:",string"` // 定义 JSON 输出的格式
}

// 结构体 转 JSON
func struct2json() {
	//定义一个结构体变量
	s := IT{"cuiCompany", []string{"java", "c", "c++"}, true, 100.35, 28}

	//buf, err := json.Marshal(s)
	// JSON格式化
	buf, err := json.MarshalIndent(s, "", "")

	if err != nil {
		fmt.Println("err=", err)
		return
	}
	fmt.Println("byf=", string(buf))
}

// JSON 转 结构体
func json2struct() {
	jsonBuf := `{
		"公司": "cuiCompany",
		"Subjects": [
			"java",
			"c",
			"c++"
		],
		"价格": 100.35,
		"Age": "28"
	}`
	var tmp IT
	err := json.Unmarshal([]byte(jsonBuf), &tmp)
	if err != nil {
		fmt.Println("err=", err)
		return
	}
	fmt.Println("tmp=", tmp)
	fmt.Printf("tmp=%+v\n", tmp)
}

func main() {
	//struct2json()
	json2struct()
}

5.4.2 Map和JSON的相互转换

// map 转 JSON
func map2json() {
	m := make(map[string]interface{}, 4)
	m["company"] = "cuiCompany"
	m["isok"] = true
	m["subject"] = []string{"go", "c", "java", "c++"}
	m["price"] = 3.1415
	//result, err := json.Marshal(m)
	// json格式化
	result, err := json.MarshalIndent(m, "", "	")
	if err != nil {
		fmt.Println("err=", err)
		return
	}
	fmt.Println("result=", string(result))
}

// JSON 转 map
func json2map() {
	jsonBuf := `{
		"company": "cuiCompany",
		"isok": true,
		"price": 3.1415,
		"subject": [
			"go",
			"c",
			"java",
			"c++"
		]
	}`
	tmp := make(map[string]interface{}, 4)
	err := json.Unmarshal([]byte(jsonBuf), &tmp)
	if err != nil {
		fmt.Println("err=", err)
		return
	}
	fmt.Println("tmp=", tmp)
	fmt.Printf("tmp=%+v\n", tmp)

	// map类型断言
	var str string
	for key, value := range tmp {
		switch data := value.(type) {
		case string:
			str = data
			fmt.Printf("map[%s]类型是string,value=%s\n", key, str)
		case bool:
			fmt.Printf("map[%s]bool,value=%v\n", key, data)
		case float64:
			fmt.Printf("map[%s]float64,value=%v\n", key, data)
		case []string:
			fmt.Printf("map[%s][]string,value=%v\n", key, data)
		case []interface{}:
			fmt.Printf("map[%s][]interface,value=%v\n", key, data)
		}
	}
}

func main() {
	//map2json()
	json2map()
}

 

5.5 文件操作

// 写入文件
func WriteFile(path string) {
	//打开/新建文件
	f, err := os.Create(path)
	if err != nil {
		fmt.Println("err=", err)
		return
	}
	//关闭文件
	defer f.Close()

	var buf string
	for i := 0; i < 10; i++ {
		// "i=1" 字符串存储在buf中
		buf = fmt.Sprintf("i = %d\n", i)

		// 开始写入文件
		n, err := f.WriteString(buf)
		if err != nil {
			fmt.Println("err=", err)
			return
		}
		fmt.Println("n=", n)
	}
}

// 读取文件
func ReadFile(path string) {
	//打开文件
	f, err := os.Open(path)
	if err != nil {
		fmt.Println("err=", err)
		return
	}
	//关闭文件
	defer f.Close()
	buf := make([]byte, 1024*2) //2K大小
	n, err1 := f.Read(buf)
	if err1 != nil && err1 != io.EOF { //文件出错,并还没到文件结尾
		fmt.Println("err1=", err1)
		return
	}
	fmt.Println("buf=\n", string(buf[:n]))
}

// 按行读取文件
func ReadLine(path string) {
	//打开文件
	f, err := os.Open(path)
	if err != nil {
		fmt.Println("err=", err)
		return
	}
	//关闭文件
	defer f.Close()
	// 新建缓冲区,暂时存放内容
	r := bufio.NewReader(f)
	for {
		buf, err := r.ReadBytes('\n')
		if err != nil {
			if err == io.EOF { //文件结束
				break
			}
			fmt.Println("err=", err)
		}
		fmt.Printf("buf=#%s#\n", string(buf))
	}
}

func main() {
	path := "./demo.txt"
	//WriteFile(path)
	//ReadFile(path)
	ReadLine(path)
}

文件拷贝

从命令行输入 源文件、目的文件 参数,进行拷贝

PS E:\ideaWorkspace\HelloGo> go build .\01_Hello.go
PS E:\ideaWorkspace\HelloGo> .\01_Hello.exe .\demo.txt abc.txt

 

func main() {
	list := os.Args //获取命令行参数
	if len(list) != 3 {
		fmt.Println("usage: xxx srcFile dstFile")
		return
	}
	srcFile := list[1]
	dstFile := list[2]
	if srcFile == dstFile {
		fmt.Printf("源文件和目的文件不能同名")
		return
	}
	// 只读方式打开源文件
	sF, err1 := os.Open(srcFile)
	if err1 != nil {
		fmt.Println("err1=", err1)
		return
	}
	//新建目的文件
	dF, err2 := os.Create(dstFile)
	if err2 != nil {
		fmt.Println("err2=", err2)
		return
	}

	// 关闭文件
	defer sF.Close()
	defer dF.Close()

	// 读取源文件内容,写入目的文件内容
	buf := make([]byte, 4*1024) //4K的 临时缓冲区
	for {
		n, err := sF.Read(buf)
		if err != nil {
			if err == io.EOF { //读取完毕
				break
			}
			fmt.Println("err=", err)
		}
		// 写
		dF.Write(buf[:n])
	}
}

 

第06天(并发编程)

6.1 概述

 

 

6.2 goroutine

 

 

6.3 channel

 

 

6.4 select

 

 

第07天(网络概述、socket编程)

7.1 网络概述

 

 

7.2 Socket编程

 

 

7.3 案例:并发的聊天室服务器

 

第08天(HTTP编程)

8.1 Web工作方式

 

 

8.2 Http报文格式

 

 

8.3 Http编程

 

 

8.4 案例:网络爬虫

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(go语言)