Golang学习笔记(上)

Golang学习笔记(上)

golang 的图像结果

安装Golang

来源linux 安装 golang - 知乎 (zhihu.com)

由于我用的是linux系统,所以本文采用linux的安装方式介绍,如果你使用的是Windows/Mac 也可以看下该文章,或者自己去下列地址进行操作。

Download and install - The Go Programming Language (google.cn)

该地址是golang中国官网,你可以放心的按照指示安装。

安装环境: ubuntu20.04操作系统

一、下载golang包

All releases - The Go Programming Language (google.cn)

在这里插入图片描述

根据不同的系统进行下载。

二、安装

2.1 删除 /usr/local/go 目录, 根据官方说法,如果之前有安装过go,那么需要将该位置的go目录删除掉
$ rm -rf /usr/local/go
2.2 解压并安装
# 切换到 golang.tar.gz 存放目录, 已经在跟目录就执行 cd Downloads/
$ cd /Download
# 解压golang到 /usr/local 下
$ sudo tar -C /usr/local -xzf go1.14.3.linux-amd64.tar.gz
2.3 设置环境变量
# 方式一:
# 修改 $HOME/.profile 或 /etc/profile 文件
# 这里可能会出现权限不足(ubuntu需要加sudo, centos需要切换成root权限)
# vi 进去后可能会出现键盘乱码,这是因为没有安装vim, 可以自行百度一下
$ sudo vi /etc/profile
# 在该文件最后一行插入(进入后,按 i键进入编辑模式)
$ export PATH=$PATH:/usr/local/go/bin
$ source /etc/profile
# 按 esc 退出编辑模式, 按 :wq 保存文件
$ go version 

# 方式二:
# 修改~/.bashrc 文件
$ sudo gedit ~/.bashrc 
# 在该文件最后一行插入
$ export PATH=$PATH:/usr/local/go/bin
$ source ~/.bashrc
$ go version 
2.4 永远的"Hello, World!"
# 创建hello.go 并进入编辑模式, vim 命令可以自行百度或者后期我再写一篇文章
$ vi hello.go

复制以下内容(vi中的操作上面有讲,就不重复了)

package main

import "fmt"

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

控制台切换到 hello.go文件所在目录

$ go run ./hello.go

在这里插入图片描述

到此golang安装并测试完成

三、工具

3.1 代理

由于种(万)种(米)原(高)因(qiang), 我们安装golang的一些辅助工具会经常失败,这里就需要配置国内镜像

linux 用户按一下操作

# 控制台中输入一下命令
$ go env -w GO111MODULE=on
$ go env -w GOPROXY=https://goproxy.cn,direct
# 查看go所有相关配置
$ go env
# 修改 ~/.profile
$ echo "export GO111MODULE=on" >> ~/.profile
$ echo "export GOPROXY=https://goproxy.cn" >> ~/.profile
# 重启配置, 如果是在vscode中下载辅助工具失败后在配置的,重启vscode,再次重新下载即可
$ source ~/.profile
GO111MODULE

GO111MODULE 环境变量用于启用或禁用Go模块支持,它的值可以是以下几个之一:

  • off:禁用模块支持,Go命令会使用旧的基于GOPATH的机制。
  • on:启用模块支持,Go命令会忽略GOPATH,只使用模块。
  • auto(默认值):Go命令会根据当前目录来决定是否启用模块支持。如果当前目录在GOPATH/src之外,并且包含go.mod文件,则使用模块。
GOPROXY

GOPROXY 环境变量用于指定Go命令在下载模块时使用的代理服务器。它的默认值是 https://proxy.golang.org,这是Go官方提供的模块代理服务。在中国大陆,由于网络原因,你可能需要设置一个国内的代理服务器,例如:

  • https://goproxy.cn:中国大陆的一个公共Go模块代理。

  • https://mirrors.aliyun.com/goproxy/:阿里云提供的Go模块代理服务。

windows/mac用户前往该地址查看(git地址访问比较慢)

goproxy.cn/README.zh-CN.md at master · goproxy/goproxy.cn (github.com)【可靠的Go模块代理】

3.2 开发工具推荐使用免费的 vscode

Visual Studio Code - Code Editing. Redefined

最后,如果有不正确的地方欢迎大家指正。

Go语言基础

一、Go语言的优点

  1. 简单易用,Go语言上手非常容易,许多零基础的初学者在学习大约一周的时间后就可以使用Go语言完成些既定的任务。

  2. 编译度快,在Go编程语言工程结构简单,没有头文件和不允许包的交叉编译等规则,这也在很大程度上减少了编译所需的时间。

  3. 运行很快,虽然Go编译后的二进制文件与C编译后的二进制文件相比,执行速度要慢一些,但对于大多数应用程序来说。

  4. 支持并发,Go语言最主要的特性就是从语言层面原生支持并发。

  5. 垃圾回收,Go语言采用了垃圾回收技术,从而使程序员更加专注于业务本身,不用关心内存管理问题。

二、标识符

  1. 字符区分大小写:name与Name是两个不同的标识符。

  2. 首字符,可以是下画线()或字母,但不能是数字。

  3. 除首字符外其他字符,可以由下画线()、字母和数字构成。

  4. 关键字不能作为标识符。

三、关键字

Go语言的关键字是Go语言预定义的,具有特定意义的单词,不能用作变量名、函数名或其他标识符。

关键字说明
break用于跳出循环
default用于switch语句的默认情况
func用于定义函数
interface用于定义接口
select用于选择发送或接收操作的通道
case用于switch语句的条件分支
defer用于延迟函数的执行直到包含它的函数返回
go用于创建并发执行的goroutine
map用于定义映射类型
struct用于定义结构体
chan用于定义通道类型
else用于if语句的else分支
goto用于无条件跳转到标签
package用于定义包名
switch用于多条件分支
const用于定义常量
fallthrough用于在switch语句中继续执行下一个case
if用于条件语句
range用于遍历数组、切片、映射或通道
type用于定义类型
continue用于跳过当前循环的剩余代码,继续下一次循环
for用于循环语句
import用于导入包
return用于从函数返回
var用于声明变量

四、语句

在Go语言中,一般情况下一行代码表示一条语句,语句结束可以加分号,也可以省略分号。

多条语句会构成代码块也称复合语句,Go中的代码块是放到左右大括号({})中,语句块中可以有0~n条语句。

五、变量

在Go语言中,变量是用来存储数据值的标识符。Go语言是静态类型语言,这意味着所有变量在声明时都必须指定一个明确的数据类型

5.1标准声明
var 变量名 数据类型 = 表达式
5.2简短声明(只能在函数内部使用)
变量名:=表达式
5.3批量声明
var (
    变量名1 类型1 = 表达式1
    变量名2 类型2 = 表达式2
    // ...
 )
5.5类型推断

在声明变量时,如果初始化表达式被提供了,那么可以省略变量类型,由编译器自动推断。

var 变量名 = 表达式

Go语言的变量有以下几个特点:

  • 变量必须使用后才能访问:Go语言中的变量声明后必须至少赋值一次才能使用,否则编译器会报错。
  • 变量类型不可变:一旦一个变量被声明为某个类型,它的类型就不能改变。
  • 零值初始化:如果变量声明时没有初始化,它们会自动初始化为其类型的零值(如int类型的零值为0string类型的零值为空字符串)。
    变量的作用域由其声明位置决定,如果在函数外部声明,则为全局变量(包级变量),作用域为整个包;如果在函数内部声明,则为局部变量,作用域为函数内部。

六、常量

const 常量名 数据类型 = 表达式

七、格式化输出

7.1格式转换符

使用格式转换符(verb)来指定输出数据的格式。可以在 fmt.Printffmt.Sprintf 和其他格式化输出函数中使用,以控制输出的格式和内容。

转换符描述
%v默认格式的占位符,会自动选择变量的默认表现形式。
%+v当输出结构体时,会添加字段名。
%#vGo语法格式的值,用于输出值的Go语言表示。
%T输出一个值的数据类型。
%%输出一个百分号 %
%b二进制表示。
%c字符(rune)(Unicode码点)。
%d十进制表示。
%o八进制表示。
%x, %X十六进制表示,%X使用大写字母。
%e, %E科学计数法表示,%E使用大写字母 E
%f, %F小数点表示,无小数部分会省略小数点。
%g, %G根据实际情况选择 %e%f%G 使用大写字母。
%s字符串或切片的无解译字节。
%q带引号的字符串或字符,必要时会进行转义。
%x, %X十六进制,每个字节用两位表示,%X 使用大写字母。
%p十六进制表示的地址值。
%t布尔值(truefalse)。
%n将输出到目前为止已写的字节数写入到指定的整数指针中。

格式化输出主要使用fmt包中的几个函数,其中最常用的是PrintlnPrintfPrint。这些函数通过标准输出(通常是终端或控制台)显示信息。

7.2Println

Println函数会自动添加空格分隔参数,并在末尾添加换行符。

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

输出:

Hello World
7.3Printf

Printf函数允许你使用格式字符串来控制输出的格式。

package main
import "fmt"
func main() {
    name := "Alice"
    age := 25
    fmt.Printf("Name: %s, Age: %d\n", name, age)
}

输出:

Name: Alice, Age: 25

格式占位符包括但不限于:

  • %s:字符串

  • %d:十进制整数

  • %f:浮点数

  • %t:布尔值

  • %v:任何值的默认格式

  • %T:值的类型

  • %%:字面上的百分号字符

7.4Print

Print函数类似于Println,但它不会在参数之间添加空格,也不会在末尾添加换行符。

package main
import "fmt"
func main() {
  fmt.Print("Hello ")
  fmt.Print("World")
}

输出:

Hello World
7.5其他函数

fmt包还提供了其他一些函数,如FprintFprintlnFprintf等,这些函数与上述函数类似,但它们将输出写入指定的io.Writer接口,例如文件。
使用这些函数,你可以根据需要格式化和输出信息。在实际编程中,根据不同的场景选择合适的函数可以使代码更加清晰和易于维护。

八、注释

单行注释:(//)

多行注释:(/*……*/)

在Go语言中,注释是用来解释代码和增加文档的非执行文本。Go支持两种类型的注释:

  1. 单行注释:以 // 开头,直到行尾的所有内容都被视为注释。单行注释只能注释紧跟其后的代码行。

    // 这是一个单行注释
    var x int = 10 // 这里也是一个单行注释
    
  2. 多行注释:以 /* 开始,以 */ 结束。多行注释可以跨越多行,但不能嵌套。

    /*
    这是一个多行注释
    它可以跨越多行
    */
    func main() {
        /* 这里也是一个多行注释 */
        println("Hello, World!")
    }
    

    注释对于代码的可读性和维护性非常重要。它们可以帮助其他开发者(和未来的你)理解代码的意图和功能。在Go中,注释也可以用来生成文档。如果一个包、函数、类型或变量的声明前面有一个以 // 开头的注释,那么这个注释可以用来自动生成文档。这些注释通常被称为文档注释。
    文档注释的惯例是以 // 后跟一个或多个空格,然后是描述性的文本。文档注释通常以被注释的元素的行为或目的开始,并且是完整的句子。

    // Package math provides basic constants and mathematical functions.
    package math
    // Sqrt returns the square root of x.
    func Sqrt(x float64) float64 {
     // ...
    }
    

    Go的文档工具,如godoc,可以从这些注释中提取信息,并生成在线文档或HTML页面。

九、包

9.1声明包

声明包是每个Go源文件的第一行,使用关键字package后面跟上包的名字。包是Go语言中代码组织和编译的单位,它将一组相关的源文件集合在一起,并提供了一种命名空间隔离的机制。
包声明的一般形式如下:

package 包名

例如,如果你正在编写一个名为math的包,你的Go源文件将这样开始:

package math

在Go中,包名通常与其目录名匹配,但这不是强制的。包名应该使用小写字母,并且通常是单个单词。如果包名包含多个单词,通常使用驼峰命名法。
当你导入包时,你会使用包的导入路径,这是包在仓库中的目录路径。例如,标准库中的fmt包的导入路径是fmt,而如果你创建了一个名为math的包,在仓库中的路径可能是github.com/yourusername/math
在Go项目中,通常会将相关的源文件放在同一个目录中,并共同组成一个包。这些源文件可以相互访问包内的变量、类型和函数,而不需要导出它们(即首字母大写)。如果想要在其他包中使用这些变量、类型或函数,必须导出它们,这意味着它们的名字必须以大写字母开头。
包的声明是Go语言中最基本的组织单位,它有助于保持代码的模块化和可重用性。

注意事项: 1.package语句应该放代码文件的第一行。

                2.在每个代码文件中只能有一个包声明语句。
9.2导入包

在Go语言中,导入包是通过 import 语句实现的。import 语句用于在当前文件中导入其他包,以便可以使用这些包中定义的函数、变量、类型和接口。导入语句可以位于包声明(package语句)之后,在任何函数之外。

导入单个包

导入单个包时,可以直接指定包的路径:

import "fmt"

导入多个包

如果需要导入多个包,可以每行导入一个包,也可以在一行中导入多个包,用括号包裹:

import (
    "fmt"
    "math"
)

导入别名

如果两个或多个包有相同的名字,或者你想为导入的包指定一个更短的名称,可以使用别名:

import (
    "fmt"
    m "math"
)

在这个例子中,math 包被导入了,但是给它指定了一个别名 m。这样,在当前文件中,你可以通过 m 来引用 math 包中的内容,而不是直接使用 math

导入特定包成员

Go 1.9 引入了导入声明的新特性,允许你只导入包中的特定成员,而不是整个包。这可以通过点(.)操作符来实现:

import (
    "fmt"
    _ "math/rand" // 导入但不使用
    "os"
    "path/filepath"
)
func main() {
    _, _ = os.Open("file.txt")
    _ = filepath.Abs("file.txt")
}

在这个例子中,math/rand 包被导入了,但是使用下划线(_)作为别名,这意味着包中的内容在当前文件中不可用。这通常用于包的初始化函数,即使不直接使用包中的其他内容,也会执行初始化。

导入本地包

如果你正在导入本地仓库中的包,需要指定包的相对路径或绝对路径:

import (
    "github.com/user/repo/subpkg"
)

确保你的 Go 工作区(GOPATH)设置了正确的路径,以便 Go 编译器可以找到这些本地包。

导入标准库包

标准库中的包可以直接导入,不需要指定完整的路径,只需使用包名:

import "fmt"

标准库中的包通常都有唯一的名字,因此不需要别名。
正确导入包是编写 Go 代码的基础,它使得代码组织更加清晰,并且可以方便地重用其他包中的功能。

十、数据类型

10.1基本类型
整型
  1. 平台无关整型类型,他们数据占用的内存空间与平台相关,它占用空间分别是:8位、16 位、32位和64 位他们又分为:有符号的整数和无符号的整数。

  2. 平台相关整数类型,数据类型占用的内存空间是由系统决定的。

数据类型占用空间(单位:位)
int88
int1616
int3232
uint与平台相关
int与平台相关
int6464
uint88
uint1616
uint3232
uint6464
byte等价于 uint8
uintptr无符号的指针
rune等价于 int32

整数表示方式

Go整数类型默认是为int,例如19表示的十进制整数。那么其他进制,如二进制数、八进制数和十六进制整数表示方式如下:

  1. 二进制数:以0b或0B为前缀,注意0是阿拉伯数字,例如0B10011表示十进制19。

  2. 八进制数,以0o或0O为前缀,第一个字符是阿拉伯数字0,第二个字符是英文字母o或O,例如0o23表示十进制19。

  3. 十六进制数:以0x或0X为前缀,注意0是阿拉伯数字,例如0X13表示十进制19。

浮点型

浮点型包括float32, float64。可用科学计数法表示浮点数

var float1 float32 =1213.4
var float2 = float64 = 3.12e-2        // e前e后必有数,e后必为整
复数类型
  1. complex128(64位实部和虚部)

  2. complex64(32位实部和虚部),其中complex128为复数的默认类型。

var complex1 = complex128 = complex(2, -3) // 声明实部为2,虚部为-3的复数
var complex2 complex64 = complex(9, 2)        // 通过complex()函数创建复数
布尔类型

Go中布尔类型为bool,表示布尔值,有两值true和flase。

字符串类型

Go中布尔类型为string,表示字符串,是不可变的字节序列。

Go语言中字符串是用双引号(")包裹起来表示的。

字符转义

字符表示Unicode编码说明
\t\u0009水平制表符tab
\n\u000a换行
\r\u000d回车
"\u0022双引号
\u0027单引号
\\u005c反斜线

原始字符串

  1. 原始字符串 (rawstring) 表示,始字符串使用反引号 ( ` ) 把字符串包裹起来。

  2. 原始字符串中的特殊字符不需要转义。

// 声明变量
// 采用普通字符串表示文件路径,其中的反斜杠需要转义
const filepath1 = "c:\\Users\\tony\\Documents\\readme.txt"
// 采用原始字符串表示文件路径,其中的反斜杠不需要转义
const filepath2 = `C:\Users\tony\Documents\readme.txt`
const str = `wdfcw                                        // 支持多行字符串
efwrv  erjp seeeeeeeeeeeeesf 
fwe ebt`

字符串常用函数

import "strings"        // 导入字符串包
  1. func Contains(s,substr string) bool,判断字符串s中是否包含字符串substr。

  2. func Replace(s,old,new string,nint)string,用string替换字符串s中old字符串,返回替换后的字符串,参数n是指定替换的个数。

  3. func ToUpper(s string)string,将字符串s中所有字符转换为大写字符。

  4. func ToLower(s string) string,将字符串s中的所有字符转换为小写字符。

  5. func Split(s,sep string)[lstring,将字符串s,按照sep进行分割,返回字符切片。

Go语言是一种静态类型语言,这意味着所有变量在声明时都必须指定一个明确的数据类型。Go语言提供了丰富的数据类型,包括基本类型、复合类型和接口类型。

10.2复合类型
  1. 数组 ([n]T):具有固定长度 n 的元素类型为 T 的数组。

    • 一致性:数组只能保存相同数据类型元素,元素的数据类型可以是任何相同的数据类型。

    • 有序性:数组中的元素是有序的,通过下标访问。

    • 不可变性:数组一旦初始化,则长度“(数组中元素的个数)不可变。

    // 长变量声明方式
    var 数组变量名 = [length]datatype{values}        // 指定数组长度,未赋值的元素赋0
    var 数组变量名 = [...]datatype{values}            // 数组长度根据元素个数推导出来
    // 短变量声明方式
    数组变量名 := [length]datatype{values}        // 指定数组长度
    数组变量名 := [...]datatype{values}            // 数组长度根据元素个数推导出来
    // Eg:
    var arr1 = [3]int{1,2,3}
    var arr2 = [...]float32{123.34,2.2334,3.0}
    

    访问数组元素:1.数组的下标是从0开始的,事实上很多计算机语言的数组下标都是从0开始的。
    2. Go数组下标访问运算符是中括号,如intArray[0],表示访问intArray数组的第一个元素,0是第一个元素的下标。

  2. 切片 ([]T):动态长度、可变的元素类型为 T 的序列。

    • 在实际编程时数组使用得并不多,这主要是因为数组是不可变的。

    • 在实际编程时常常会使用可变数组数据获得数据一一切片 (Slice)。

    声明切片

    • 可以将其声明为数组,只是无需指定其大小。

    • 用make()函数来创建切片,make()函数语法如下:

      make([] T,len,cap)
      import "fmt"
      func main() {
          // 声明字符串切片
          strSlicel := []string{"沃尔沃","宝马","福特","奔驰"}
          // 声明int类型切片
          intSlicel := []int{1,2, 3, 4, 5, 6}
          // 使用make()函数创建int切片
          var intSlice2 = make([] int, 10)
          // 使用make()函数创建字符串切片
          var strSlice2 = make([] string, 10,20)
          fmt.Println(intSlicel)
          fmt.Println(intSlice2)
          fmt.Println(strSlicel)
          fmt.Println(strSlice2)
      }
      

    使用切片操作符
    切片名[startIndex:endIndex],startIndex默认为0。包括索引为startIdenx的元素,不包括索引为endIndex的元素。
    切片名[:],切下所有元素。
    添加元素slice = append(slice,elem1,elem2,...);把slice2追加到slice1后slice1 = append(slice1, slice2...)

  3. 映射 (map[T1]T2):键类型为 T1、值类型为 T2 的键值对集合。

    声明映射

    • var 映射变量名= map [key_data_type]value_data_type{key:value…}

    • 映射变量名 = make(map[key_data_type]value_data_type)

    import "fmt"
    func main () {
        // 1、通过map关键字声明映射
        var countryCodeMap = map[string]string{"CN":"中国","RU":"俄罗斯","US":"美国","JP":"日本"}
        fmt.Printf("%v n", countryCodeMap)
        // 2、通过make()函数声明映射
       var classMap = make(map[int]string,
        classMap[102] = "张三"
        classMap[105] = "李四"
        classMap[109] = "王五"
        classMap[110] = "董六"
        fmt.Printf("%vn",classMap)
    }
    

    访问映射元素value,result = 映射变量名[key]
    删除元素:要映射中的元素可以通过delete(映射变量,要删除的键)函数实现,该函数是按照键删除对应的值。

    1. 结构体 (struct):由多个字段组成的复合类型。
      结构体是一种用户自定义的类型,结构体是不同类型数据集合,而数组是相同类型数据集合。
      声明结构体
    type 结构体类型名 struct{
        成员1 数据类型
        成员2 数据类型
        成员3 数据类型
        ...
    }
    

    举例

    type Student struct {
        id     int    // 学号成员
        name   string // 姓名成员
        age    int    // 年龄成员
        city   string // 所在城市
        gender string // 性别成员,M男性,F女性
    }
    
    // 相同数据类型的字段可以放在一起声明
    type Student1 struct {
        id, age            int
        name, city, gender string
    }
    

    实例化结构体

    func main() {
        // 实例化Student创建stu1实例
        var stu1 Student
        // 实例化Student1创建stu2实例
        stu2 := Student1{101, 18, "张三", "上海", "F"}
        // 实例化Student创建stu2实例
        stu3 := Student{name: "李四", age: 18, city: "上海", gender: "F", id: 201}
        fmt.Println(stu1, stu2, stu3)
    }
    

    结构体指针
    获得结构体实例的地址方式

    • 通过取地址运算符(&)运算符获得结构体实例地址

    • 通过new关键字获得创建实例,并返回指向实例的指针

      func main() {
      // 结构体指针
        // 通过取地址符号(&)运算符获得结构体实例地址
        stu4 := &Student{name: "王五", age: 18, city: "上海", gender: "F", id: 301}
        // 使用“.”运算符访问成员
        fmt.Println(stu4)
        // 使用new关键字获得实例化实例地址,返回指向实例的指针
        stu5 := new(Student)
        stu5.id = 401
        stu5.name = "老六"
        stu5.age = 17
        stu5.city = "北京"
        stu5.gender = "M"
        fmt.Println(stu5)
      }
      

      结构体嵌套

      // 结构体嵌套
      type Book struct {
        isbn      string    // ISBN号
        title     string    // 书名
        price     float32   // 定价
        authors   []Author  // 作者,是Author结构体数组
        publisher Publisher // 出版社,是Publisher结构体
      }
      
      // 定义作者结构体
      type Author struct {
        id   int
        name string
      }
      
      // 定义出版社结构体
      type Publisher struct {
        name  string
        email string
      }
      

      为结构体添加方法

      // 为结构体添加方法
      type Rectangle struct {
      	height, width int
      }
      
      // 声明结构体方法
      func (rect Rectangle) Area() int {
      	return rect.height * rect.width
      }
      
      func (rect Rectangle) DoubleArea() int {
      	return rect.height * rect.width * 2
      }
      func main() {
        // 为结构体添加方法
      	rect := Rectangle{20, 20}
      	var1 := rect.Area()
      	var2 := rect.DoubleArea()
      	fmt.Println(var1, var2)
      }
      
  4. 通道 (chan T):用于在不同 goroutine 之间传递类型为 T 的值的管道。

遍历容器

数组、切片和映射都属于容器数据,他们包含了很多元素,因此一个经常的操作就遍历容器中的元素。
Go语言提供了一个range关键字,它可以帮助迭代: 数组、切片、映射和通道(channel)等容器数据中的元素。

使用range关键字迭代不同的类型数据时,会返回不同数据,下面根据不同的容器类型分别介绍一下。

  1. 数组、切片。返回两个值,其中第一个值是索引,第二个值是元素。

  2. 映射。返回两个值,其中第一个值是键,第二个值是值。

  3. 字符串。由于返回两个值,第一个值是索引,第二个值是字符。

var strl = "Hello world."

for i, item := range strl {
    fmt.Printf("str1[%d] = %c\n",i,item)
}
10.3接口类型

接口类型 (interface) 是一种抽象类型,它定义了一组方法。任何实现了这些方法的类型都满足这个接口。

接口的定义

// 定义几何图形接口
type Shape interface {
	area() float32     // 计算面积
	permeter() float32 // 计算周长
}

接口的实现

// 定义矩形结构体
type Rectangle1 struct {
	height, width float64
}

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

// 声明Rectangle结构体方法,实现Shape接口的area方法
func (r Rectangle) area() float64 {
	return r.height * r.width
}

// 声明Rectangle结构体方法,实现Shape接口的permeter方法
func (r Rectangle) permeter() float64 {
	return 2*r.height + 2*r.width
}

func main() {
	rect := Rectangle1{20, 10}
	fmt.Println(rect.area(), rect.permeter())
}
area(), rect.permeter())
}

10.4 其他类型
  1. 指针类型 (*T):指向类型 T 的值的指针。

    var 变量名 *变量类型
    main() {
        // 声明变量x
        var x int = 100
        fmt.Printf("x变量的内存地址: %xn",&x)
        // 声明并初始化指针变量ptr
        var ptr *int = &x
        fmt.Printf("指针变量pt值是: %\n",*ptr)
    }
    

    空指针:果指针变量没有初始化,那么它所指向的内存地址为0,表示变量没有分配内存空间,这就是空指针。

    func main() {
        var ptr *int
        fmt.Printf("指针ptr的值是: %xn",ptr)
        // 判断ptr是否为空指针
        if ptr == nil {
            fmt.Println("ptr 是空指针”)
        }
    }
    

    二级指针:指向指针变量的指针变量。

    func main() {
        // 声明整数变量x
        var x int
        var ptr *int // 声明指针变量
        var pptr **int // 声明二级指针变量
        x = 300
        // 初始化变量x
        ptr = &x // 获取x变量地址
        pptr = &ptr // 获取ptr变量地址
        fmt.Printf("x:%d\n", x)
        fmt.Printf("*ptr = %d\n", *ptr)
        fmt.Printf("**pptr =%d\n", **pptr)
    }
    
  2. 函数类型 (func):表示函数类型,可以包含参数和返回值。

  3. 错误类型 (error):表示错误值的接口类型。

10.5类型断言

类型断言用于检查接口值的实际类型是否为某个特定的类型。如果类型断言成功,表达式返回接口值的实际类型的值;如果失败,表达式会导致运行时恐慌(panic)。

聊一聊 Go 语言中的类型:断言

10.6类型转换

类型转换用于将一个类型的值转换为另一个类型的值。Go 语言中的类型转换需要显式进行,使用 T(v) 的形式,其中 T 是目标类型,v 是要转换的值。

目标数据类型(表达式)
var var1 := float32(123)
10.7类型别名

类型别名是 Go 1.9 引入的一个特性,它允许为现有的类型定义一个新的名字。类型别名与类型定义(type definition)不同,类型别名只是为现有类型提供了一个新的名字,而类型定义则创建了一个全新的类型。

// 将NewInt定义为int类型
type NewInt int

Go语言type关键字(类型别名)

十一、运算符

11.1算数运算符
  1. 一元算数运算符,包括++和–。

  2. 二元算数运算符,包括+、-、*、/ 和 % 等。

运算符名称例子说明
+x + y求x加y的和
-x - y求x减y的差
*x * y求x乘以y的积
/x / y求x除以y的商
%取余x % y求x除以y的余数
++自加一加1后返回x++
自减一减1后返回x–
11.2关系运算符
运算符名称例子说明
==等于x == yx等于y 时返回 true, 否则返回 false
!=不等于x != y与==相反
>大于x > yx大于y 时返回 true, 否则返回 false
<小于x < yx小于y 时返回 true, 否则返回 false
>=大于等于x >= yx大于等于y 时返回 true, 否则返回 false
<=小于等于x <= yx小于等于y 时返回 true, 否则返回 false
11.3逻辑运算符
运算符名称例子说明
!逻辑非!xx 为 true 时,值为 false,a 为 false 时,值为 true
&&逻辑与x && yxy 全为 true 时,计算结果为 true,否则为 false
||逻辑或x||yxy 全为 false 时,计算结果为 false,否则为 true

短路特性(如果左边的表达式确定整个表达式的结果,则右边的表达式不计算):&&:左假右不看;||:左真右不看。

11.4位运算符
运算符名称例子说明
&位与x&yx 与 y 位进行位与运算
|位或x|yx与y位进行位或运算
^位异或x^yx 与 y 位进行位异或运算
>>右移x>>yx 右移 y 位,高位采用 0 补位
<<左移x<<yx 左移 y 位,低位用 0 补位
11.5赋值运算符
运算符名称例子
+=加赋值a += b、a += b+3
-=减赋值a -= b
*=乘赋值a *= b
/=除赋值a /= b
%=取余赋值a %= b
&=位与赋值x &= y
|=位或赋值x|=y
^=位异或赋值x ^= y
<<=左移赋值x <<= y
>>=右移赋值x >>= y
11.6其他运算符
运算符名称例子描述
&取地址运算符&a获得变量a 的地址
*间接寻址运算符*a声明指针变量
11.7运算符优先级
优先级分类运算符
1逗号运算符,
2赋值运算符=、+=、-=、*=、/=、%=、>=、< <=、&=、 -=、
3逻辑或||
4逻辑与&&
5按位或|
6按位异或^
7按位与&
8相等/不等==、!=
9关系运算符<、<=、>、>=
10位移运算符<<、>>
11加法/减法+、-
12乘法/除法/取余*(乘号)、/、%
13一元运算符!、*(指针)、&、++、–、+(正号)、 -(负号)
14后缀运算符()、[ ]、->

十二、语句

12.1条件语句

if语句

三种结构:

  • if 结构

    package main
    
    import "fmt"
    
    func main() {
        if x := 10; x > 5 {
            fmt.Println("x is greater than 5")
        }
    }
    
  • if-else 结构

    package main
    
    import "fmt"
    
    func main() {
        x := 10
        if x > 5 {
            fmt.Println("x is greater than 5")
        } else {
            fmt.Println("x is not greater than 5")
        }
    }
    
  • if-else-if 结构

    package main
    
    import "fmt"
    
    func main() {
        x := 10
        if x > 15 {
            fmt.Println("x is greater than 15")
        } else if x > 10 {
            fmt.Println("x is greater than 10")
        } else {
            fmt.Println("x is not greater than 10")
        }
    }
    

switch语句

switch(表达式) {
    case value1:
        语句1
    case valu2:
        语句2
    case value3:
        语句3
    ...
    case valuen:
        语句n
    default:
        语句n+1
}

与c语言中switch的区别,这个执行case后直接退出,不会执行下面的case语句了。

使用switch语句注意如下问题

  • switch语句中“表达式”计算结果主要是布尔类型和整数类型

  • “表达式”必须与case值具有相同的数据类型

  • 默认情况下每一个case语句组执行结束后,则switch语句结束

  • default语句可以省略,default语句应该置于在switch语句未尾

  • 一个case可以有多个值

使用fallthrough贯穿case

Go中的switch语句每一个case分支的代码块执行完成后,就结束switch语句,但是如果想在一个case分支执行完成后,不结束switch语句,而是进入下一个case,那么可以使用fallthrough关键字实现,fallthrough会强制执行后面的case语句,fallthrough不会判断是否与下一条 case 值匹配。

package main

import "fmt"

func main() {
    switch num := 1; num {
    case 1:
        fmt.Println("Number is 1")
        fallthrough // 继续执行下一个case,即使条件不满足
    case 2:
        fmt.Println("Number is 2")
        fallthrough // 继续执行下一个case,即使条件不满足
    case 3:
        fmt.Println("Number is 3")
        fallthrough // 继续执行下一个case,即使条件不满足
    default:
        fmt.Println("Number is not 1, 2, or 3")
    }
}
12.2循环语句

基本形式for循环

for 初始值;循环条件;迭代{
    语句组
}

简化的for循环

  • 省略初始化和迭代语句

  • 省略条件部分

package main
import "fmt"
func main() {
    i := 1 // 初始化语句置于for语句之前
    for i < 10 {
        fmt.Printf("%d x %d = %d", i, i, i*i)
        //打印一个换行符,实现换行
        i++ // 选代语句置于循环体中
    }
    // --------------
    i := 1 // 初始化语句置于for语句之前
    for {
        fmt.Printf("%d x %d = %d", i, i, i*i)
        //打印一个换行符,实现换行
        fmt.Print("\n")
        i++ // 选代语句置于循环体中
        if i = 10{  // 条件满终止循环
            break //该语句会终止循环    
          }
    }
}
12.3跳转语句

break语句

break语句可用于for循环结构,它的作用是强行退出循环体,不再执行循环体中剩余的语句。

使用标签的break语句

break语句还可以配合标签使用,带标签的break语句使程序跳出标签所指的循环体,语法格式如下。

break label

----------------- 
package main
import "fmt"

func main(){

OuterLoop:
    for x:=0;x<5;x++ {
        for y:=5;y>0;y-- {
            if y==x {
                // 跳转到OuterLoop指向的循环
                break OuterLoop
            }
            fmt.Printf("(x,y)=(%d,%d)\n",x,y)
        }
    }
fmt.Println("Game Over!")
}

continue语句

continue语句用来结束本次循环,跳过循环体中尚未执行的语句,接着进行终止条件的判断,以决定是否继续循环。对于for语句,在进行终止条件的判断前,还要先执行迭代语句。

使用标签的continue语句

continue语句也可以带有标签,语法格式如下。

continue label
----------------------------
package main

import "fmt"

func main() {
    outer:
    for i := 0; i < 3; i++ {
        for j := 0; j < 3; j++ {
            fmt.Printf("(%d, %d) ", i, j)
            if j == 1 {
                continue outer // 跳过外层循环的剩余代码,并开始下一次迭代
            }
        }
        fmt.Println()
    }
}

goto语句

goto语句是无条件跳转语句,使用goto语句可跳转到标签所指的代码行,由于goto语句使得程序的控制流难以跟踪,如果goto语句使用得不当可能会导致程序出现错误,跳转推荐使用break和continue替代goto。

package main

import "fmt"

func main() {
    i := 0
    label:
    for {
        fmt.Println("Iteration:", i)
        i++
        if i == 3 {
            goto end // 无条件跳转到标签end
        }
    }
end:
    fmt.Println("Exiting the loop")
}

十三、函数

13.1用户自定义函数
func 函数名(形式参数列表)(返回值列表) {
    函数体
}
13.2单一返回值
func area(width int, height int) int {
    ar := width * height
    return ar
}
13.3多个返回值
// 自定义计算矩形面积和周长函数
func calcRect(width int, height int) (int, int) {
    // 计算矩形面积
    area := width * height
    // 计算矩形周长
    perimeter := 2 * (width + height)
    return area, perimeter
}
13.4命名函数返回值
// 自定义函数计算矩形的面积
func calcRectArea(width, height int) (area int) { // 返回值变量命名为area
    ar := width * height
    // 给返回值变量赋值
    area = ar
    return // 不返回任何数据类型
}
13.5变参函数
// 定义一个求和的变参函数
func sum(numbers ...int) int {
    total := 0
    for _, number := range numbers {
        total += number
    }
    return total
}
13.6函数式编程
  1. 函数是”一等公民”:是指函数与其他数据类型是一样的,处于平等的地位。函数可以作为其他函数的参数传入,也可以作为其他函数的返回值返回。

  2. 高阶函数:函数式编程支持高阶函数,所谓高阶函数就是一个函数可以作为另外一个函数的参数或返回值。

  3. 无副作用:是指函数执行过程会返回一个结果,不会修改外部变量,这就是“纯函数”,同样的输入参数一定会有同样的输出结果。

13.7匿名函数

如果函数没有名字,称为“函数”,匿名函数也称lambda函数。

package main
import "fmt"

func main() {
    // 匿名函数
    add := func(a, b int) int {
        return a + b
    }
    sub := func(a, b int) int {
        return a - b
    }
    var a, b = 10, 5
    fmt.Printf("%d+%d=%d;数据类型:%T\n", a, b, add(a, b), add)
    fmt.Printf("%d-%d=%d;数据类型:%T\n", a, b, sub(a, b), sub)
}
13.8函数作为返回值使用

函数类型

函数类型是函数式编程的关键

函数类型与其他的数据类型一样都可以声明变量、参数类型和返回值类型。

// 声明变量,它是函数类型
var res func(int, int) int
-----------------------------
// 函数作为返回值使用
func calculate(opr string) func(int, int) int {
    // 声明变量,它是函数类型
    var res func(int, int) int
    if opr == "+" {
        // 声明相加函数
        res = func(a, b int) int {
            return a + b
        }
    } else {
        // 声明相减函数
        res = func(a, b int) int {
            return a - b
        }
    }
    // 返回值res变量是函数类return res
}
func main() {
  // 函数作为返回值
    f1 := calculate("+")
    f2 := calculate("-")
    fmt.Printf("%d+%d=%d,数据类型:%T\n", a, b, f1(a, b), f1)
    fmt.Printf("%d-%d=%d,数据类型:%T\n", a, b, f2(a, b), f1)
}
13.9函数作为参数使用
// 函数作为参数
// 定义一个计算面积的函数
func getAreaByFunc(funcName func(float32, float32) float32, a, b float32) float32 {
    return funcName(a, b)
}

13.10闭包与捕获变量

闭包 (closure) 是一种特殊的函数,它可以访问函数体之外的变量,这个变量和函数一同存在,即使已经离开了它的原始作用域也不例外。

闭包访问函数体之外的变量的称为“捕获变量”,闭包捕获变量后,这些变量被保存在一个特殊的容器中被存储起来,即便是声明这些变量的原始作用域已经不存在,闭包体中仍然可以访问这些变量。

// 闭包函数
// 定义一个增量器函数,返回值是"func() int"函数类型
func incrementor() func() int {
    i := 0 // 声明局部变量
    // 返回匿名函数
    return func() int { // 闭包
        i++
        return i
    }
}
func main() {
  // 闭包函数的使用,自增器
    next := incrementor()
    fmt.Println(next()) // 打印1
    fmt.Println(next()) // 打印2
    fmt.Println(next()) // 打印3
}

十四、错误处理

  • 29
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

childish_tree

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值