Golang基本知识点汇总

文章目录

一.基本介绍

Go中文文档: https://www.topgoer.com/
官方Go练习指南: https://tour.go-zh.org/welcome/1
Golang标准库文档: https://studygolang.com/pkgdoc

二.编写HelloWorld

项目目录

一般语言是没有规定的项目结构,Go语言在这方面做了规定,为了保持一致性,做到统一、规则化比较明确

--demo
	--bin
	--pkg
	--src
		--project01
		--project02

  • bin存放编译后的可执行文件
  • pkg存放编译后的包文件
  • src存放项目源文件,多个项目则分多个文件夹

代码

package main

import "fmt"

func main() {
    
    
	fmt.Println("Hello world")
}

  1. go文件的后缀是 .go
  2. package main
    表示:该hello.go文件所在的包是main,每个文件都必须归属一个包
  3. import “fmt”
    表示:引入一个包,包名fmt,引入后,即可使用该包的函数,例如:fmt.Println
  4. func main(){}
    func 是一个关键字,表示后面是一个函数,mian代表是一个主函数,即程序的入口

编译执行

  1. 进入到文件目录,执行go build,执行完成后生成执行文件hello.exe,执行exe文件即可看到效果
go build hello.go
.\hello.go
  1. 直接编译并运行
go run hello.go

执行流程

go build、go run
在这里插入图片描述

  1. 如果先编译成可执行文件,则把执行文件拷贝到其他没有go环境的机器,也可以直接执行
  2. 如果是源文件,则要执行的机器上也必须有go开发环境,否则无法执行
  3. 下图中可以看出,源文件1KB,编译后的执行文件为2046KB,这是因为,编译时,编译器会将程序依赖运行的库文件包含在可执行文件中,所以执行文件变大了很多
    在这里插入图片描述

三.包,变量和函数

1.包

每个 Go 程序都是由包构成的。

程序从 main 包开始运行。

按照约定,包名与导入路径的最后一个元素一致。例如,“math/rand” 包中的源码均以 package rand 语句开始。

package main

import (
	"fmt"
	"math/rand"
)

func main() {
    
    
	fmt.Println("My favorite number is", rand.Intn(100))
}

2.导入

此代码用圆括号组合了导入,这是“分组”形式的导入语句。
当然你也可以编写多个导入语句

package main

import (
	"fmt"
	"math"
)
/* 等同于上面的组合导入
 import "fmt"
 import "math"
 */

func main(){
    
    
	fmt.Printf("7的平方根为: %g\n",math.Sqrt(7))
}

3.导出名

在 Go 中,如果一个名字以大写字母开头,那么它就是已导出的。例如,Pizza 就是个已导出名,Pi 也同样,它导出自 math 包。

pizza 和 pi 并未以大写字母开头,所以它们是未导出的。

在导入一个包时,你只能引用其中已导出的名字。任何“未导出”的名字在该包外均无法访问。

package main

import (
	"fmt"
	"math"
)

func main() {
    
    
	fmt.Println(math.Pi)
}

4.函数

函数可以没有参数或接受多个参数。
在本例中,add 接受两个 int 类型的参数。
注意类型在变量名 之后。

package main

import "fmt"

func add(x int, y int) int {
    
    
	return x + y
}

//当连续两个或多个函数的已命名形参类型相同时,除最后一个类型以外,其它都可以省略
func add2(x, y int) int {
    
    
	return x + y
}

func main() {
    
    
	fmt.Println(add(2, 16))
	fmt.Println(add(18, 16))
}

函数可以返回任意数量的返回值。
swap 函数返回了两个字符串。

package main

import "fmt"

func swap(x, y string) (string, string) {
    
    
	return y, x
}

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

5.命名返回值

package main
import "fmt"

//没有参数的 return 语句返回已命名的返回值。也就是 直接 返回
func split(sum int) (x, y int){
    
    
	x = sum * 4 / 9
	y = sum - x
	return
}

func main(){
    
    
	fmt.Println(split(17))
}

6.变量

var 语句用于声明一个变量列表,跟函数的参数列表一样,类型在最后。

就像在这个例子中看到的一样,var 语句可以出现在包或函数级别。

package main

import "fmt"

var c, python, java bool

func main() {
    
    
	var i int
	fmt.Println(i, c, python, java)
}

变量声明可以包含初始值,每个变量对应一个。

如果初始化值已存在,则可以省略类型;变量会从初始值中获得类型。

package main

import "fmt"

var i, j int = 1, 2

func main() {
    
    
	var c, python, java = true, false, "no!"
	fmt.Println(i, j, c, python, java)
}

在函数中,简洁赋值语句 := 可在类型明确的地方代替 var 声明。

函数外的每个语句都必须以关键字开始(var, func 等等),因此 := 结构不能在函数外使用。

package main

import "fmt"

func main() {
    
    
	var i, j int = 1, 2
	k := 3
	c, python, java := true, false, "no!"

	fmt.Println(i, j, k, c, python, java)
}

7.基本类型

Go 的基本类型有

  • bool
  • string
  • int int8 int16 int32 int64
  • uint uint8 uint16 uint32 uint64 uintptr
  • byte // uint8 的别名
  • rune // int32 的别名 表示一个 Unicode 码点
  • float32 float64
  • complex64 complex128

int, uint 和 uintptr 在 32 位系统上通常为 32 位宽,在 64 位系统上则为 64 位宽。 当你需要一个整数值时应使用 int 类型,除非你有特殊的理由使用固定大小或无符号的整数类型。

package main

import (
	"fmt"
	"math/cmplx"
)

var (
	ToBe   bool       = false
	MaxInt uint64     = 1<<64 - 1
	z      complex128 = cmplx.Sqrt(-5 + 12i)
)

func main() {
    
    
	fmt.Printf("Type: %T Value: %v\n", ToBe, ToBe)
	fmt.Printf("Type: %T Value: %v\n", MaxInt, MaxInt)
	fmt.Printf("Type: %T Value: %v\n", z, z)
}

8.基本数据类型和String的转换

8.1 基本类型

通用:

%v 值的默认格式表示
%+v 类似%v,但输出结构体时会添加字段名
%#v 值的Go语法表示
%T 值的类型的Go语法表示 %% 百分号

布尔值:

%t 单词true或false

整数:

%b 表示为二进制
%c 该值对应的unicode码值
%d 表示为十进制
%o 表示为八进制
%q 该值对应的单引号括起来的go语法字符字面值,必要时会采用安全的转义表示
%x 表示为十六进制,使用a-f
%X 表示为十六进制,使用A-F
%U 表示为Unicode格式:U+1234,等价于"U+%04X"

浮点数与复数的两个组分:

%b 无小数部分、二进制指数的科学计数法,如-123456p-78;参见strconv.FormatFloat
%e 科学计数法,如-1234.456e+78
%E 科学计数法,如-1234.456E+78
%f 有小数部分但无指数部分,如123.456
%F 等价于%f
%g 根据实际情况采用%e或%f格式(以获得更简洁、准确的输出)
%G 根据实际情况采用%E或%F格式(以获得更简洁、准确的输出)

字符串和[]byte:

%s 直接输出字符串或者[]byte
%q 该值对应的双引号括起来的go语法字符串字面值,必要时会采用安全的转义表示
%x 每个字节用两字符十六进制数表示(使用a-f)
%X 每个字节用两字符十六进制数表示(使用A-F)

指针:

%p 表示为十六进制,并加上前导的0x

没有%u。整数如果是无符号类型自然输出也是无符号的。类似的,也没有必要指定操作数的尺寸(int8,int64)。

宽度通过一个紧跟在百分号后面的十进制数指定,如果未指定宽度,则表示值时除必需之外不作填充。精度通过(可选的)宽度后跟点号后跟的十进制数指定。如果未指定精度,会使用默认精度;如果点号后没有跟数字,表示精度为0。举例如下:

%f: 默认宽度,默认精度
%9f 宽度9,默认精度
%.2f 默认宽度,精度2
%9.2f 宽度9,精度2
%9.f 宽度9,精度0

8.2 转换

使用 fmt.Sprintfstrconv 进行转换

package main

import (
	"fmt"
	"strconv"
)

func main() {
    
    
	sprintfTest()
	strconvTest()
	stringToDef()
}

// 使用fmt.Sprintf进行转换
func sprintfTest() {
    
    
	var a int = 1
	var b float64 = 1.2
	var c bool = false
	var d byte = 'c'
	var str string

	str = fmt.Sprintf("%d", a)
	fmt.Printf("str type %T str=%q\n", str, str)

	str = fmt.Sprintf("%f", b)
	fmt.Printf("str type %T str=%q\n", str, str)

	str = fmt.Sprintf("%t", c)
	fmt.Printf("str type %T str=%q\n", str, str)

	str = fmt.Sprintf("%c", d)
	fmt.Printf("str type %T str=%q\n", str, str)
}

// 使用strconv包的函数
func strconvTest() {
    
    
	var a int = 1
	var b float64 = 1.2
	var c bool = false
	var str string

	str = strconv.FormatInt(int64(a), 10)
	fmt.Printf("str type %T str=%q\n", str, str)

	str = strconv.FormatFloat(b, 'f', 10, 64)
	fmt.Printf("str type %T str=%q\n", str, str)

	str = strconv.FormatBool(c)
	fmt.Printf("str type %T str=%q\n", str, str)
}

// String类型转基本数据类型
func stringToDef() {
    
    
	var a string = "1"
	var aa int64
	aa, _ = strconv.ParseInt(a, 10, 64)
	fmt.Printf("str type %T str=%v\n", aa, aa)

	var b string = "1.2"
	var bb float64
	bb, _ = strconv.ParseFloat(b, 64)
	fmt.Printf("str type %T str=%v\n", bb, bb)

	var c string = "false"
	var cc bool
	cc, _ = strconv.ParseBool(c)
	fmt.Printf("str type %T str=%v\n", cc, cc)
}

9.零值

没有明确初始值的变量声明会被赋予它们的 零值。

零值是:

  • 数值类型为 0,
  • 布尔类型为 false,
  • 字符串为 “”(空字符串)。
package main

import "fmt"

func main() {
    
    
	var i int
	var f float64
	var b bool
	var s string
	fmt.Printf("%v %v %v %q\n", i, f, b, s)
}

10.类型转换

package main

import (
	"fmt"
	"math"
)

func main() {
    
    
	var x, y int = 3, 4
	var f float64 = math.Sqrt(float64(x * x + y * y))
	var z uint = uint(f)
	fmt.Println(x, y, f, z)
	typeChange()
	typeChange2()
}

//Go 在不同类型的项之间赋值时需要显式转换
func typeChange() {
    
    
	var i int = 42
	var f float64 = float64(i)
	//隐式转换会失败
	// var f float64 = i
	var u uint = uint(f)
	fmt.Println(i, f, u)
}

func typeChange2() {
    
    
	i := 42
	f := float64(i)
	u := uint(f)
	fmt.Println(i, f, u)
}

11.类型推导

在声明一个变量而不指定其类型时(即使用不带类型的 := 语法或 var = 表达式语法),变量的类型由右值推导得出。

当右值声明了类型时,新变量的类型与其相同:

var i int
j := i // j 也是一个 int

不过当右边包含未指明类型的数值常量时,新变量的类型就可能是 int, float64 或 complex128 了,这取决于常量的精度:

i := 42           // int
f := 3.142        // float64
g := 0.867 + 0.5i // complex128
package main
import "fmt"

//在声明一个变量而不指定其类型时(即使用不带类型的 := 语法或 var = 表达式语法),变量的类型由右值推导得出
func main() {
    
    
	v := 42
	fmt.Printf("v is of type %T\n", v)
	typesIs()
	typesIs2()
}

func typesIs(){
    
    
	var i int
	j := i
	fmt.Printf("j的类型为%T\n", j)
}

func typesIs2() {
    
    
	i := 42           // int
	f := 3.142        // float64
	g := 0.867 + 0.5i // complex128
	fmt.Printf("i的类型为%T\n", i)
	fmt.Printf("f的类型为%T\n", f)
	fmt.Printf("g的类型为%T\n", g)
}

12.常量

常量的声明与变量类似,只不过是使用 const 关键字。
常量可以是字符、字符串、布尔值或数值。
常量不能用 := 语法声明。

package main

import "fmt"

const Pi = 3.14

func main() {
    
    
	const World = "世界"
	fmt.Println("Hello", World)
	fmt.Println("Happy", Pi, "Day")

	const Truth = true
	fmt.Println("Go rules?", Truth)
}

13.数值常量

数值常量是高精度的 值。

一个未指定类型的常量由上下文来决定其类型。

再尝试一下输出 needInt(Big) 吧。

(int 类型最大可以存储一个 64 位的整数,有时会更小。)

(int 可以存放最大64位的整数,根据平台不同有时会更少。)

package main

import "fmt"

const (
	// 将 1 左移 100 位来创建一个非常大的数字
	// 即这个数的二进制是 1 后面跟着 100 个 0
	Big = 1 << 100
	// 再往右移 99 位,即 Small = 1 << 1,或者说 Small = 2
	Small = Big >> 99
)

func needInt(x int) int {
    
     return x*10 + 1 }
func needFloat(x float64) float64 {
    
    
	return x * 0.1
}

func main() {
    
    
	fmt.Println(needInt(Small))
	fmt.Println(needFloat(Small))
	fmt.Println(needFloat(Big))
}

14.值类型和引用类型

14.1 值类型和引用类型的说明

  1. 值类型
    1、基本数据类型:int32系列,float系列,string,bool,数组,结构体struct
    2、通常在栈区分配

  2. 引用类型
    1、引用类型:指针,slic切片,map,管道chan,interface
    2、通常在堆区,分配空间

14.2 值类型和引用类型的使用特点

  1. 值类型: 变量直接存储值,内存通常在栈中分配
  2. 引用类型: 变量存储的是一个地址,这个地址对应的空间才真正存储数据(值),内存通常在堆中分配,当没有任何变量引用这个地址时,该地址对应的数据空间就变成为一个垃圾,由GC来回收.

四.指针

Go 拥有指针。指针保存了值的内存地址。

类型 *T 是指向 T 类型值的指针。其零值为 nil。

var p *int

& 操作符会生成一个指向其操作数的指针。

i := 42
p = &i

*操作符表示指针指向的底层值。

package main

import "fmt"

func main() {
    
    
	i, j := 42, 2701

	p := &i         // 指向 i
	fmt.Println(*p) // 通过指针读取 i 的值
	*p = 21         // 通过指针设置 i 的值
	fmt.Println(i)  // 查看 i 的值

	p = &j         // 指向 j
	*p = *p / 37   // 通过指针对 j 进行除法运算
	fmt.Println(j) // 查看 j 的值
}

五.运算符

1.键盘输入语句

调用fmt的fmt.Scanln()fmt.Scanf()

package main

import "fmt"

func main() {
    
    
	// scanlnTest()
	scanfTest()
}

func scanlnTest() {
    
    
	var name string
	var age byte
	var sal float32
	var isPass bool

	fmt.Println("输入姓名:")
	fmt.Scanln(&name)

	fmt.Println("输入年龄:")
	fmt.Scanln(&age)

	fmt.Println("输入薪资:")
	fmt.Scanln(&sal)

	fmt.Println("是否通过考试:")
	fmt.Scanln(&isPass)

	fmt.Println("姓名:", name, "年龄:", age, "薪资:", sal, "是否通过考试:", isPass)
}

func scanfTest() {
    
    
	var name string
	var age byte
	var sal float32
	var isPass bool

	fmt.Println("请输入你的姓名, 年龄, 薪资, 是否通过考试, 使用空格隔开")
	fmt.Scanf("%s %d %f %t", &name, &age, &sal, &isPass)
	fmt.Println("姓名:", name, "年龄:", age, "薪资:", sal, "是否通过考试:", isPass)
}

2.进制

对于整数有4种表示方式

  1. 二进制: 0,1 满2进1 在golang中不能直接使用二进制来表示一个整数,它沿用了c的特点
  2. 十进制: 0-9, 满10进1
  3. 八进制: 0-7 满8进1 以数字0开头
  4. 十六进制:0-9 及 A-F 满16进1 以0x或0X开头表示 此处的A-F不区分大小写

在这里插入图片描述
在这里插入图片描述

2.1 其他进制转十进制

2.1.1 二进制转十进制

规则:从最低位开始(右边的),将每个位上的数提取出来,乘以2的(位数-1)次方,然后求和

1011 = 1 * 2 ^ 0 + 1 * 2^1 + 0 * 2 ^ 2 + 1* 2 ^ 3 = 11

2.1.2八进制转十进制

规则:从最低位开始(右边的),将每个位上的数提取出来,乘以8的(位数-1)次方,然后求和

0123 = 3 * 8 ^ 0 + 2 * 8^1 + 1 * 8 ^ 2 = 83

2.1.2十六进制转十进制

规则:从最低位开始(右边的),将每个位上的数提取出来,乘以8的(位数-1)次方,然后求和

0X34A= 10 * 16 ^ 0 + 4 * 16^1 + 3 * 16 ^ 2 = 842

2.2 十进制转其他进制

2.2.1 十进制转二进制

规则:将改数不断除以2,直到商为0为止,然后将每步得到的余数倒过来,就是对应的二进制

在这里插入图片描述

2.2.2 十进制转八进制

规则:将改数不断除以8,直到商为0为止,然后将每步得到的余数倒过来,就是对应的二进制

在这里插入图片描述

2.2.3 十进制转十六进制

规则:将改数不断除以16,直到商为0为止,然后将每步得到的余数倒过来,就是对应的二进制

请将356转成16进制:
在这里插入图片描述

2.3 二进制转八进制,十六进制

2.3.1 二进制转八进制

规则: 将二进制每三位一组(从低位开始组合),转成对应的八进制数即可。(因为三位数字的二进制最多可以表示为7)

请将二进制11010101转成八进制:

11010101 = 11 010 101 = 3 2 5 = 0325

2.3.2 二进制转十六进制

规则: 将二进制每四位一组(从低位开始组合),转成对应的十六进制数即可。(因为四位数字的二进制最多可以表示为15)

请将二进制11010101转成十六进制:

11010101 = 1101 0101 = 13 5 = 0XD5

2.4 八进制,十六进制转二进制

2.4.1 八进制转二进制

规则:将八进制每1位,转成对应的1个三位的二进制数即可

请将0237转成二进制
0237 = 10 011 111 = 10011111

2.4.2 十六进制转二进制

规则:将十六进制每1位,转成对应的1个四位的二进制数即可

请将0X237转成二进制
0X237 = 10 0011 0111 = 1000110111

2.5 原码反码补码

在这里插入图片描述

2.6 位运算和移位运算符

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

package main

import "fmt"

func main() {
    
    
	var a int = 1 >> 2 // 0000 0001 => 0000 0000 => 0
	fmt.Println(a)

	b := -1 >> 2 //1111 1111 => 1111 1111 => -1
	fmt.Println(b)

	c := 1 << 2 // 0000 0001 => 0000 0100 => 4
	fmt.Println(c)

	d := -1 << 2 //1000 0001 => 1000 0100 => -4
	fmt.Println(d)
}

六. 程序流程控制

6.1 if

Go 的 if 语句与 for 循环类似,表达式外无需小括号 ( ) ,而大括号 { } 则是必须的。

package main

import (
	"fmt"
	"math"
)

func sqrt(x float64) string {
    
    
	if x < 0 {
    
    
		return sqrt(-x) + "i"
	}
	return fmt.Sprint(math.Sqrt(x))
}

func main() {
    
    
	fmt.Println(sqrt(2), sqrt(-4))
}

同 for 一样, if 语句可以在条件表达式前执行一个简单的语句。

该语句声明的变量作用域仅在 if 之内。

package main

import (
	"fmt"
	"math"
)

func pow(x, n, lim float64) float64 {
    
    
	if v := math.Pow(x, n); v < lim {
    
    
		return v
	}
	return lim
}

func main() {
    
    
	fmt.Println(
		pow(3, 2, 10),
		pow(3, 3, 20),
	)
}

6.2 if 和 else

在 if 的简短语句中声明的变量同样可以在任何对应的 else 块中使用。

(在 main 的 fmt.Println 调用开始前,两次对 pow 的调用均已执行并返回其各自的结果。)

package main

import (
	"fmt"
	"math"
)

func pow(x, n, lim float64) float64 {
    
    
	if v := math.Pow(x, n); v < lim {
    
    
		return v
	} else {
    
    
		fmt.Printf("%g >= %g\n", v, lim)
	}
	// 这里开始就不能使用 v 了
	return lim
}

func main() {
    
    
	fmt.Println(
		pow(3, 2, 10),
		pow(3, 3, 20),
	)
}

6.3 switch

switch 是编写一连串 if - else 语句的简便方法。它运行第一个值等于条件表达式的 case 语句。

Go 的 switch 语句类似于 C、C++、Java、JavaScript 和 PHP 中的,不过 Go 只运行选定的 case,而非之后所有的 case。 实际上,Go 自动提供了在这些语言中每个 case 后面所需的 break 语句。 除非以 fallthrough 语句结束,否则分支会自动终止。 Go 的另一点重要的不同在于 switch 的 case 无需为常量,且取值不必为整数。

package main

import (
	"fmt"
	"runtime"
)

func main() {
    
    
	fmt.Print("Go runs on ")
	switch os := runtime.GOOS; os {
    
    
	case "darwin":
		fmt.Println("OS X.")
	case "linux":
		fmt.Println("Linux.")
	default:
		// freebsd, openbsd,
		// plan9, windows...
		fmt.Printf("%s.\n", os)
	}
}

6.3.1 没有条件的 switch

没有条件的 switch 同 switch true 一样。

这种形式能将一长串 if-then-else 写得更加清晰。

package main

import (
	"fmt"
	"time"
)

func main() {
    
    
	t := time.Now()
	switch {
    
    
	case t.Hour() < 12:
		fmt.Println("Good morning!")
	case t.Hour() < 17:
		fmt.Println("Good afternoon.")
	default:
		fmt.Println("Good evening.")
	}
}

6.3.2 switch穿透

fallthrough : switch穿透,如果在case语句块后加fallthrough ,则会执行下一个case,也叫switch穿透

//输出结果为: 当前值为10 当前值为20
func fallthroughTest() {
    
    
	var i int = 10

	switch i {
    
    
	case 10:
		fmt.Println("当前值为10")
		fallthrough
	case 20:
		fmt.Println("当前值为20")
	case 30:
		fmt.Println("当前值为30")
	default:
		fmt.Println("无匹配")
	}
}

6.4 for

Go 只有一种循环结构:for 循环。

基本的 for 循环由三部分组成,它们用分号隔开:

初始化语句:在第一次迭代前执行
条件表达式:在每次迭代前求值
后置语句:在每次迭代的结尾执行
初始化语句通常为一句短变量声明,该变量声明仅在 for 语句的作用域中可见。

一旦条件表达式的布尔值为 false,循环迭代就会终止。

package main

import "fmt"

func main() {
    
    
	sum := 0
	for i := 0; i < 10; i++ {
    
    
		sum += i
	}
	fmt.Println(sum)
}

初始化语句和后置语句是可选的。

package main

import "fmt"

func main() {
    
    
	sum := 1
	for ; sum < 1000; {
    
    
		sum += sum
	}
	fmt.Println(sum)
}

6.4.1无限循环

如果省略循环条件,该循环就不会结束,因此无限循环可以写得很紧凑。
通常搭配break使用,来跳出循环

package main

func main() {
    
    
	for {
    
    
	}
}

6.4.2 for - range

func test2() {
    
    
	var textString string = "Hello world 你好"
	for index, val := range textString {
    
    
		fmt.Printf("index=%d val=%c \n", index, val)
	}
}

七 函数

7.1 函数细节详解

  • 基本语法
func 函数名 (形参列表) (返回值类型列表) {
    
    
    执行语句..
    return + 返回值列表
} 
  • 返回值,特点就是go语言可以返回多个参数
package main
import "fmt"func cal (num1 int, num2 int) (int, int) {
    
    
    var sum int = num1 + num2
    var sub int = num1 - num2
​
    return sum, sub
}func main(){
    
    
    sum, sub := cal(66, 33)
    //也可以用下划线忽略
    sum1, _ := cal(11, 22)
    fmt.Println(sum, sub) //99, 33
    fmt.Println(sum1) //33
}

7.2 包的细节详解

  • package进行包的声明,建议:包的声明这个包和所在的文件夹同名

  • main包是程序的入口包,一般main函数会放在这个包下。main函数一定要放在main包下,否则不能编译执行

  • 引入包的语法:import “包的路径”。包名是从$GOPATH/src/后开始计算的,使用/进行路径分隔。(配置完变量要重启IDE)

  • 如果有多个包,建议一次性导入,格式如下:

import(
    "fmt"
    "gocode/testproject01/unit5/demo09/crm/dbutils"
    //utils "gocode/testproject01/unit5/demo09/crm/dbutils"  //这里表示对包起别名使用
)
  • 在函数调用的时候前面要定位到所在的包
  • 函数名,变量名首字母大写,函数,变量可以被其它包访问
  • 一个目录下不能有重复的函数
  • 包名和文件夹的名字,可以不一样
  • 一个目录下的同级文件归属一个包
  • 同级别的源文件的包的声明必须一致
  • 可以给包取别名,取别名后,原来的包名就不能使用了

包到底是什么:

  • 在程序层面,所有使用相同 package 包名 的源文件组成的代码模块
  • 在源文件层面就是一个文件夹

7.3 init函数

  • init函数:初始化函数,可以用来进行一些初始化的操作 每一个源文件都可以包含一个init函数,该函数会在main函数执行前,被Go运行框架调用
  • 全局变量定义,init函数,main函数的执行流程?
package main
import "fmt"var num int = test();func test() int {
    
    
    fmt.Println("test函数被调用执行")	//先输出
    return 10
}func init() {
    
    
    fmt.Println("init函数被调用执行")	//其次输出
}func main() {
    
    
    fmt.Println("main函数被调用执行")	//最后输出
}

多个源文件都有init函数的时候,如何执行?

在这里插入图片描述

7.4 匿名函数

Go支持匿名函数,如果我们某个函数只是希望使用一次,可以考虑使用匿名函数
匿名函数使用方式:
在定义匿名函数时就直接调用,这种方式匿名函数只能调用一次(用的多)

package main
import "fmt"func main() {
    
    
    //定义匿名函数,定义的同时调用
    result := func (num1 int, num2 int) int {
    
    
        return num1 + num2
    } (10, 20) //直接输入参数
​
    fmt.Println(result) //30
}

将匿名函数赋给一个变量(该变量就是函数变量了),再通过该变量来调用匿名函数(用的少)

package main
import "fmt"func main() {
    
    
    //定义匿名函数,定义的同时调用
    result := func (num1 int, num2 int) int {
    
    
        return num1 + num2
    }
​
    result1 := result(10, 20) //调用result匿名函数
​
    fmt.Println(result1) //30
}

如何让一个匿名函数,可以在整个程序中有效呢?将匿名函数给一个全局变量就可以了

package main
import "fmt"var add = func (num1 int, num2 int) int {
    
    
    return num1 + num2
}func main() {
    
    
​
    result := add(10, 20) //调用add
​
    fmt.Println(result) //30
}

7.5 闭包

  • 什么是闭包?
    闭包就是一个函数和与其相关的引用环境组合的一个整体

  • 代码展示

package main
import "fmt"
//函数功能:求和
//函数的名字:getSum 参数为空
//getSum函数返回值为一个函数,这个函数的参数是一个int类型的参数,返回值也是int类型
func getSum() func (int) int {
    
    
    var sum int = 0
    return func (num int) int{
    
    
        sum = sum + num 
        return sum
    }
}
//闭包:返回的匿名函数+匿名函数以外的变量num
func main(){
    
    
    f := getSum()
    fmt.Println(f(1))//1 
    fmt.Println(f(2))//3
    fmt.Println(f(3))//6
    fmt.Println(f(4))//10
}

匿名函数中引用的那个变量会一直保存在内存中,可以一直使用

  • 闭包的本质

    • 闭包本质依旧是一个匿名函数,只是这个函数引入外界的变量/参数
    • 匿名函数+引用的变量/参数 = 闭包
  • 特点

    • 返回的是一个匿名函数,但是这个匿名函数引用到函数外的变量/参数 ,因此这个匿名函数就和变量/参数形成一个整体,构成闭包。
    • 闭包中使用的变量/参数会一直保存在内存中,所以会一直使用—>意味着闭包不可滥用(对内存消耗大)

7.6 defer关键字

  • 作用

    • 在函数中,程序员经常需要创建资源,为了在函数执行完毕后,及时的释放资源,Go的设计者提供defer关键字
  • 代码展示

package main
import "fmt"var add = func (num1 int, num2 int) int {
    
    
    //在Golang中,程序遇到defer关键字,
    //不会立即执行defer后的语句,而是先将语句压入一个栈中,然后继续执行后面的语句
    defer fmt.Println("num1=", num1) //33
    defer fmt.Println("num2=", num2) //66
    
    sum := num1 + num2
    fmt.Println("sum=", sum) //99
    return sum
}func main() {
    
    
    fmt.Println(add(33, 66)) //99
}

在这里插入图片描述

  • 应用场景
    比如你想关闭某个使用的资源,在使用的时候直接随手defer,因为defer有延迟执行机制(函数执行完毕再执行defer压入栈的语句),所以你用完随手写了关闭,比较省心,省事

7.7 字符串常用系统函数

  • 统计字符串长度 len (按字节)
  • 字符串遍历,同时处理有中文的问题 []rune(str)
	var b string = "hello 世界"
	r := []rune(b)
	for i := 0; i < len(r); i++ {
    
    
		fmt.Printf("字符 = %c \n", r[i])
	}
  • 字符串转整数 n, err := strconv.Atoi(“12”)
  • 整数转字符串 str = strconv.Itoa(12345)
  • 字符串转[]byte: var bytes = []byte(“hello go”)
  • []byte转字符串 str = string([]byte{87, 98, 99})
  • 10进制转2,8,16进制 str = strconv.FormatInt(32, 2) //=>8 16
  • 查询子串是否在指定的字符串中 strings.Contains(“abcde”, “bcd”)
  • 统计一个字符串中有几个子串 strings.Count(“aaaabbcds”, “b”)
  • 不区分大小写的字符串比较 strings.EqualFold(“abc”, “AbC”)
  • 返回子字符串第一次/最后一次出现的位置 strings.Index(“abc”, “b”) LastIndex
  • 字符串替换 strings.Replace(“abc”, “b”, “bbb”, n) n代表替换几个 -1全部替换
  • 字符串分隔成数组 strings.Split(“a b cc dd”, " ")
  • 字符串的大小写替换 strings.ToUpper(“a b cc dd”) ToLower
  • 去除字符串左右空格 strings.TrimSpace(" a b cc dd ")
  • 去除字符串左右两边指定字符 strings.Trim(" a b cc dd", “d”) TrimLeft/TrimRight
  • 判断字符串是否以指定字符开头/结尾 strings.HasPrefix(“a b cc dd”, “d”) HasSuffix

7.8 日期时间相关

  • 获取当前时间 (time.Time类型)
time.Now()
	now := time.Now()

	fmt.Println(now.Year())
	fmt.Println(now.Month())
	fmt.Println(int(now.Month()))
	fmt.Println(now.Day())
	fmt.Println(now.Hour())
	fmt.Println(now.Minute())
	fmt.Println(now.Second())
  • 格式化日期时间
  1. 使用Sprintf格式化
	fmt.Println(fmt.Sprintf("%d-%d-%d %d:%d:%d", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second()))

  1. 使用Format格式化:
fmt.Println(now.Format("2006-01-02 15:04:05"))  //2006-01-02 15:04:05 是固定的,不能更改
  • 获取时间戳
	fmt.Println(now.Unix())
	fmt.Println(now.UnixNano())

八.错误处理

8.1 defer+recover机制处理错误

  • 错误处理/捕获机制:
    go中追求代码优雅,引入机制:defer+recover机制处理错误

内置函数recover: 可以捕获到异常

func test2() {
    
    

	defer func() {
    
    
		err := recover()
		if err != nil {
    
    
			fmt.Println("报错信息为:", err)
		}
	}()
	a := 0
	b := 10
	fmt.Println(a / b)
	fmt.Println(b / a)

	//后面的逻辑不会再执行
	fmt.Println("我是报错后的操作")	
}

8.2 自定义错误

package main

import (
	"errors"
	"fmt"
)

func main() {
    
    
	test1()
	fmt.Println("我是最后的输出逻辑")
}

func test1() {
    
    
	err := test2()
	if err != nil {
    
    
		fmt.Println("自定义错误:", err)
		panic(err) //panic会结束程序的执行
	}
}

func test2() (err error) {
    
    
	num1 := 10
	num2 := 0
	if num2 == 0 {
    
    
		//抛出自定义错误:
		return errors.New("除数不能为零!!!")
	} else {
    
    
		result := num1 / num2
		fmt.Println(result)
		//如果没有错误,返回零值
		return nil
	}
}

九 数组与切片

9.1 数组

  1. 数组的初始化方式
package main
import "fmt"
func main(){
    
    
    //第一种:
    var arr1 [3]int = [3]int{
    
    3,6,9}
    fmt.Println(arr1)
    //第二种:
    var arr2 = [3]int{
    
    1,4,7}
    fmt.Println(arr2)
    //第三种:
    var arr3 = [...]int{
    
    4,5,6,7}
    fmt.Println(arr3)
    //第四种:定义对应下标
    var arr4 = [...]int{
    
    2:66,0:33,1:99,3:88}
    fmt.Println(arr4)
}
  1. 数组的遍历
  • 普通for循环
  • 键值循环
    (键值循环)for range结构是Go语言特有的一种的迭代结构,在许多情况下都非常有用,for range 可以遍历数组、切片、字符串、map 及通道,for range 语法上类似于其它语言中的 foreach 语句,一般形式为:
for key, val := range coll {
    
    
    ...
}
package main
import "fmt"
func main(){
    
    
    //实现的功能:给出五个学生的成绩,求出成绩的总和,平均数:
    //给出五个学生的成绩:--->数组存储:
    //定义一个数组:
    var scores [5]int
    //将成绩存入数组:(循环 + 终端输入)
    for i := 0; i < len(scores);i++ {
    
    //i:数组的下标
        fmt.Printf("请录入第个%d学生的成绩",i + 1)
        fmt.Scanln(&scores[i])
    }
    //方式2:for-range循环
    for key,value := range scores {
    
    
        fmt.Printf("第%d个学生的成绩为:%d\n",key + 1,value)
    }
}
  1. 数组的引用
func test3() {
    
    
	arrayTest1 := [5]int{
    
    1, 2, 3, 4, 5}
	fmt.Println("原始数组:", arrayTest1)
	changeArray(&arrayTest1)
	fmt.Println("改变后的数组:", arrayTest1)
}

func changeArray(arrayTest *[5]int) {
    
    
	(*arrayTest)[0] = 12
}

9.1.1 二维数组

package main

import (
	"fmt"
)

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

func test() {
    
    
	var arr [4][6]int
	arr[1][2] = 1
	arr[2][2] = 2
	arr[2][3] = 3
	fmt.Println(arr)
}

func test2() {
    
    
	arr := [3][4]int{
    
    
		{
    
    1, 2, 3, 4},
		{
    
    1, 2, 3, 4},
		{
    
    1, 2, 3, 4},
	}

	//使用双重循环进行遍历
	for i := 0; i < len(arr); i++ {
    
    
		for j := 0; j < len(arr[i]); j++ {
    
    
			fmt.Printf("第 %d 行第 %d 列的数为 %d \n", i+1, j+1, arr[i][j])
		}
	}

	//使用for range进行遍历
	for key, value := range arr {
    
    
		for nextKey, nextValue := range value {
    
    
			fmt.Printf("第 %d 行第 %d 列的数为 %d \n", key+1, nextKey+1, nextValue)
		}
	}
}

9.2 切片

9.2.1 切片的基本介绍

  • 切片(slice)是golang中一种特有的数据类型
  • 数组有特定的用处,但是却有一些呆板(数组长度固定不可变),所以在 Go 语言的代码里并不是特别常见。相对的切片却是随处可见的,切片是一种建立在数组类型之上的抽象,它构建在数组之上并且提供更强大的能力和便捷。
  • 切片(slice)是对数组一个连续片段的引用,所以切片是一个引用类型。这个片段可以是整个数组,或者是由起始和终止索引标识的一些项的子集。需要注意的是,终止索引标识的项不包括在切片内。切片提供了一个相关数组的动态窗口。

9.2.2 切片的语法:

var 切片名 []类型 = 数组的一个片段引用
package main

import (
	"fmt"
)

func main() {
    
    
	var test1 [5]int = [...]int{
    
    1, 2, 3, 4, 5}
	slice := test1[1:3]
	fmt.Println(slice)
	fmt.Println("slice的元素:", slice)   //[2, 3]
	fmt.Println("切片的长度=", len(slice)) //2
	fmt.Println("切片的容量", cap(slice))  //4 容量可以动态变化
}

9.2.3 切片的定义

//定义数组:
var intarr [6]int = [6]int{
    
    3, 6, 9, 1, 4, 7}
//切片构建在数组之上
//方式1:定义一个切片,然后让切片去引用一个已经创建好的数组
slice1 := intarr[1 : 3]//方式2:通过make内置函数来创建切片。基本语法: var 切片名 []type = make([]type , len,[cap])
//PS : make底层创建一个数组,对外不可见,所以不可以直接操作这个数组,
//要通过slice去间接的访问各个元素,不可以直接对数组进行维护/操作
slice2 := make([]int , 4, 20)//方式3:定一个切片,直接就指定具体数组,使用原理类似make的方式
slice3 := []int{
    
    1, 4, 7}

9.2.4 切片的遍历

package main
import "fmt"
func main(){
    
    
    //定义切片:
    slice := make([]int,4,20)
    slice[0] = 66
    slice[1] = 88
    slice[2] = 99
    slice[3] = 100
    //方式1:普通for循环
    for i := 0;i < len(slice);i++ {
    
    
        fmt.Printf("slice[%v] = %v \t" ,i,slice[i])
    }
    fmt.Println("\n------------------------------")
    //方式2:for-range循环:
    for i,v := range slice {
    
    
        fmt.Printf("下标:%v ,元素:%v\n" ,i,v)
    }
}

9.2.5 切片的简写和再切片

  • 简写方式:
var slice = arr[0:end]  ----> var slice = arr[:end]
var slice = arr[start:len(arr)]  ---->  var slice = arr[start:]
var slice = arr[0:len(arr)]   ----> var slice = arr[:]
  • 切片可以继续切片
var intarr [6]int = [6]int{
    
    3, 6, 9, 1, 4, 7}
slice := intarr[1 : 5]
//再切片
slice2 := slice[1 : 3]
fmt.Println(slice2) //[9 1]

9.2.6 切片的扩容append和拷贝copy

  • 切片可以扩容 append
package main
import "fmt"
func main(){
    
    
    //定义数组:
    var intarr [6]int = [6]int{
    
    1,4,7,3,6,9}
    //定义切片:
    var slice []int = intarr[1:4] //4,7,3
    fmt.Println(len(slice))
    slice2 := append(slice,88,50)
    fmt.Println(slice2) //[4 7 3 88 50]
    fmt.Println(slice)
    //底层原理:
    //1.底层追加元素的时候对数组进行扩容,老数组扩容为新数组:
    //2.创建一个新数组,将老数组中的4,7,3复制到新数组中,在新数组中追加88,50
    //3.slice2 底层数组的指向 指向的是新数组 
    //4.往往我们在使用追加的时候其实想要做的效果给slice追加:
    slice = append(slice,88,50)
    fmt.Println(slice)
    //5.底层的新数组 不能直接维护,需要通过切片间接维护操作。
}
  • 切片的拷贝 copy
//定义切片:
var a []int = []int{
    
    1,4,7,3,6,9}
//再定义一个切片:
var b []int = make([]int,10)
//拷贝:
copy(b,a) //将a中对应数组中元素内容复制到b中对应的数组中
fmt.Println(b)

9.2.7 修改字符串

如果要修改字符串,可以先将string => []byte 或者 []rune => 修改 =>重新转换成字符串

func test4() {
    
    
	var string1 string = "Hello world"

	slice1 := string1[2:]
	fmt.Println(slice1)
	fmt.Printf("slice1的类型为:%T \n", slice1)

	slice2 := []byte(string1)
	fmt.Printf("%c", slice2)
	slice2[6] = 'W'
	fmt.Printf("%c", slice2)

	arr1 := []rune(string1)
	fmt.Printf("%c", arr1)
	arr1[0] = '好'
	str := string(arr1)
	fmt.Println(str)
}

十.排序和查找

10.1 冒泡排序

原理:

  • 冒泡排序就是从序列中的第一个元素开始,依次对相邻的两个元素进行比较,如果前一个元素大于后一个元素则交换它们的位置。如果前一个元素小于或等于后一个元素,则不交换它们;这一比较和交换的操作一直持续到最后一个还未排好序的元素为止。

  • 当这样的一趟操作完成时,序列中最大的未排序元素就被放置到了所有未排序的元素中最后的位置上,它就像水中的石块一样沉到了水底。而其它较小的元素则被移动到了序列的前面,就像水中的气泡冒到了水面一样。这就是为什么该算法被叫做冒泡排序的原因。

package main

import (
	"fmt"
)

func main() {
    
    
	arr := [5]int{
    
    10, 9, 8, 7, 6}
	fmt.Println("原始数据:", arr)
	sort(&arr)
	fmt.Println("排序完成后数据:", arr)

}

func sort(arr *[5]int) {
    
    
	ls := 0
	for i := 0; i < len(*arr)-1; i++ {
    
    
		for j := 0; j < len(*arr)-1-i; j++ {
    
    
			if (*arr)[j] > (*arr)[j+1] {
    
    
				ls = (*arr)[j]
				(*arr)[j] = (*arr)[j+1]
				(*arr)[j+1] = ls
			}
		}
		fmt.Println(i+1, "轮排序后的数组为:", *arr)
	}
}

在这里插入图片描述

图解:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

10.2 二分查找

  • 介绍:

二分查找(Binary
search)也称折半查找,是一种效率较高的查找方法。但是,二分查找要求线性表中的记录必须按关键码有序,并且必须采用顺序存储。

  • 原理
    二分查找算法的原理如下:
    1. 设置查找区间:low = 0;high= n;
    2. 若查找区间[low, high]不存在,则查找失败;否则转步骤3
    3. 取中间位mid = (low + high) / 2;比较 target 与 arr[mid],有以下三种情况:
    3.1 若 target < arr[mid],则high = mid - 1;查找在左半区间进行,转步骤2;
    3.2 若 target > arr[mid],则low = mid + 1;查找在右半区间进行,转步骤2;
    3.3 若 target = arr[mid],则查找成功,返回 mid 值;
package main

import (
	"fmt"
)

func main() {
    
    
	arr := [9]int{
    
    3, 17, 24, 36, 41, 56, 71, 80, 100}
	key, findRes := findNumber(arr, 0, len(arr)-1, 101)
	if findRes {
    
    
		fmt.Println("找到的数据下标为:", key)
	} else {
    
    
		fmt.Println("查找结束,根本没有!!!")
	}
}

func findNumber(arr [9]int, leftIndex, rightIndex int, findN int) (int, bool) {
    
    
	fmt.Println("本轮查找的数组为:", arr)
	if leftIndex > rightIndex {
    
    
		return 0, false
	}

	middleKey := (rightIndex + leftIndex) / 2
	fmt.Println("中间数下标:", middleKey)
	if arr[middleKey] == findN {
    
    
		fmt.Println("找到了!!!!")
		return middleKey, true
	}

	if arr[middleKey] > findN {
    
    
		rightIndex = middleKey - 1
	} else {
    
    
		leftIndex = middleKey + 1
	}
	return findNumber(arr, leftIndex, rightIndex, findN)
}

十一. map 映射

11.1 map的引入

  • 映射(map), Go语言中内置的一种类型,它将键值对相关联,我们可以通过键 key来获取对应的值 value。 类似其它语言的集合
  • 基本语法
var map变量名 map[keytype]valuetype 如:
var a map[int]string

PS:key、value的类型:bool、数字、string、指针、channel 、还可以是只包含前面几个类型的接口、结构体、数组
PS:key通常为int 、string类型,value通常为数字(整数、浮点数)、string、map、结构体
PS:key:slice、map、function不可以

代码

  • map集合在使用前一定要make
  • map的key-value是无序的
  • key是不可以重复的,如果遇到重复,后一个value会替换前一个value
  • value可以重复的
package main
import "fmt"
func main(){
    
    
        //定义map变量:
        var a map[int]string
        //只声明map内存是没有分配空间
        //必须通过make函数进行初始化,才会分配空间:
        a = make(map[int]string,10) //map可以存放10个键值对
        //将键值对存入map中:
        a[20095452] = "张三"
        a[20095387] = "李四"
        a[20097291] = "王五"
        a[20095387] = "朱六"
        a[20096699] = "张三"
        //输出集合
        fmt.Println(a)
}

11.2 map的创建方式

package main
import "fmt"
func main(){
    
    
    //方式1:
    //定义map变量:
    var a map[int]string
    //只声明map内存是没有分配空间
    //必须通过make函数进行初始化,才会分配空间:
    a = make(map[int]string,10) //map可以存放10个键值对
    //将键值对存入map中:
    a[20095452] = "张三"
    a[20095387] = "李四"
    //输出集合
    fmt.Println(a)
    //方式2:
    b := make(map[int]string)
    b[20095452] = "张三"
    b[20095387] = "李四"
    fmt.Println(b)
    //方式3:
    c := map[int]string{
    
    
        20095452 : "张三",
        20098765 : "李四",
    }
    c[20095387] = "王五"
    fmt.Println(c)
}

11.3 map的增删改查

func curdAction() {
    
    
	students := make(map[string]map[string]string)

	//添加数据
	students["小明"] = map[string]string{
    
    
		"姓名": "小明",
		"性别": "男",
	}
	students["李华"] = map[string]string{
    
    
		"姓名": "李华",
		"性别": "女",
	}
	fmt.Println(students)

	//删除数据
	delete(students, "李华")
	fmt.Println(students)

	//一次性删除方式
	// students = make(map[string]map[string]string)
	// fmt.Println(students)

	//更新数据
	students["小明"]["性别"] = "女"
	fmt.Println(students)

	//查找数据是否存在
	val, ok := students["小明"]
	if ok {
    
    
		fmt.Println("小明的信息为:", val)
	} else {
    
    
		fmt.Println("小明不存在")
	}

}

11.4 map的遍历

可以使用 for range

	for key, val := range students {
    
    
		println("key为:", key)
		for key1, val1 := range val {
    
    
			println("key1为:", key1)
			println("val1为:", val1)
		}
	}

11.5 map切片

func sliceMap() {
    
    
	var students []map[int]string
	students = make([]map[int]string, 2)
	if students[0] == nil {
    
    
		students[0] = make(map[int]string)
		students[0][0] = "小明"
	}

	if students[1] == nil {
    
    
		students[1] = make(map[int]string)
		students[1][1] = "小黄"
	}
	fmt.Println(students)

	newStudent := map[int]string{
    
    
		2: "Hello",
	}
	students = append(students, newStudent)
	fmt.Println(students)
}

十二 结构体 struct

一个结构体(struct)就是一组字段(field)。

12.1 结构体的定义

package main

import "fmt"

type Vertex struct {
    
    
	X int
	Y int
}

func main() {
    
    
	vertex := Vertex{
    
    1, 2}
	vertex.Y = 3
	fmt.Println(vertex)
	fmt.Println(vertex.Y)
}

func test(){
    
    
	//1.直接声明
	var vertext Vertex
	
	//第2种
	var vertex Vertex = Vertex{
    
    1, 2}

	//3.结构体指针定义
	var vertex Vertex = new(Vertex)

	//4 结构体指针
	var vertex Vertex = &Vertex{
    
    1, 2}
}


12.2 结构体指针

结构体字段可以通过结构体指针来访问。

如果我们有一个指向结构体的指针 p,那么可以通过 (*p).X 来访问其字段 X。不过这么写太啰嗦了,所以语言也允许我们使用隐式间接引用,直接写 p.X 就可以。

package main

import "fmt"

type Vertex struct {
    
    
	X int
	Y int
}

func main() {
    
    
	v := Vertex{
    
    1, 2}
	p := &v
	p.X = 1e9
	fmt.Println(v)
}

12.3 方法

Go 没有类。不过你可以为结构体类型定义方法。

方法就是一类带特殊的 接收者 参数的函数。

方法接收者在它自己的参数列表内,位于 func 关键字和方法名之间。

在此例中,Abs 方法拥有一个名为 v,类型为 Vertex 的接收者。

package main

import (
	"fmt"
	"math"
)

type Vertex struct {
    
    
	X, Y float64
}

func (v Vertex) Abs() float64 {
    
    
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
    
    
	v := Vertex{
    
    3, 4}
	fmt.Println(v.Abs())
}

12.4 指针接收者

你可以为指针接收者声明方法。

这意味着对于某类型 T,接收者的类型可以用 *T 的文法。(此外,T 不能是像 *int 这样的指针。)

例如,这里为 *Vertex 定义了 Scale 方法。

指针接收者的方法可以修改接收者指向的值(就像 Scale 在这做的)。由于方法经常需要修改它的接收者,指针接收者比值接收者更常用

若使用值接收者,那么 Scale 方法会对原始 Vertex 值的副本进行操作。(对于函数的其它参数也是如此。)Scale 方法必须用指针接受者来更改 main 函数中声明的 Vertex 的值。

package main

import (
	"fmt"
	"math"
)

type Vertex struct {
    
    
	X, Y float64
}

func (v Vertex) Abs() float64 {
    
    
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func (v *Vertex) Scale(f float64) {
    
    
	v.X = v.X * f
	v.Y = v.Y * f
}

func main() {
    
    
	v := Vertex{
    
    3, 4}
	v.Scale(10)
	fmt.Println(v.Abs())
}

12.5 结构体的序列化和反序列化 tag

package main

import (
	"encoding/json"
	"fmt"
)

type Persion struct {
    
    
	Name string `json:"name"`
	Age  int    `json:"age"`
}

func main() {
    
    
	persion := Persion{
    
    Name: "小明", Age: 12}
	fmt.Println(persion)

	//将persion变量序列化为json格式字符串
	jsonstr, err := json.Marshal(persion)
	if err != nil {
    
    
		fmt.Println("json处理错误:", err)
	}
	fmt.Println(jsonstr)
	//需要把byte类型转为string进行输出
	fmt.Println(string(jsonstr))
}

在这里插入图片描述

实现String方法

package main

import (
	"encoding/json"
	"fmt"
)

type Persion struct {
    
    
	Name string `json:"name"`
	Age  int    `json:"age"`
}

func main() {
    
    
	persion := Persion{
    
    Name: "小明", Age: 12}
	fmt.Println(persion)

	//如果实现的&Persion类型的String方法,就会自动调用
	fmt.Println(&persion)
}

// 给*persion实现方法String
func (persion *Persion) String() string {
    
    
	str := fmt.Sprintf("Name=【%v】,Age=【%v】", persion.Name, persion.Age)
	return str
}

猜你喜欢

转载自blog.csdn.net/qq_36228377/article/details/129821902