Golang 也称为 Go,是由 Google 开发的一种开源、编译和静态类型的编程语言。

Go 是一种编译的、并发的、静态类型的通用编程语言,具有简单的语法和健壮的标准库。 Go 改进了命令式和面向对象编程等概念,从而简化了开发人员的工作环境。

在本指南中,您将了解开始使用 Go 构建实际应用程序所需了解的一切。

为什么要学习 Golang?

现在你可能还在问我为什么要学习 Golang 和所有其他的替代方案。以下是 Go 编程语言的一些优点列表。

  • 简单的语法 - Go 具有简洁明了的语法,使编写可读和可维护的代码变得容易。

  • 编译语言

  • 静态链接 - 编译器支持静态链接,这意味着您可以将项目静态链接到一个庞大的二进制文件中,然后简单地将其部署到云或服务器上。

  • 开源 - Go 是开源的,因此您可以阅读源代码并为存储库做出贡献。

现在您已经了解了 Golang 是什么以及它带来了什么,让我们进入安装和基础知识。

安装

Go 可以安装在所有三个主要平台 Windows、Linux 和 Mac 上。

视窗:

下载最新的Windows Golang MSI并在您的机器上执行它。按照说明提示在您的根目录中安装 Go 并自动为您的终端设置环境变量。

Linux:

在 Linux 上可以从官网下载 tar 文件并解压到 /usr/local。

苹果机:

在 Mac 上,您可以从官网下载 Mac 安装程序,然后双击执行。

验证安装:

您可以通过在终端中运行以下命令来验证安装。

go --version

安装后,您将继续查看 Golang 提供的基本语法和功能。

基本结构

让我们先来看看 Golang 程序的基本结构。为此,我们将看一个 hello world 示例。

package main

import "fmt"

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

所有的 Go 程序都必须是包的一部分,在这里你使用 main 来使文件可执行。 main 包需要一个 main 函数,运行文件时会调用该函数。

在这种情况下,您必须导入您使用的所有包,包括 fmt 等所有标准库。

在终端中使用以下命令运行该文件:

go run main.go

变量

在本节中,您将学习声明和初始化变量的不同方法。

Golang 是静态类型的,这意味着变量在程序运行之前显式或隐式分配类型。

声明变量:

可以使用 war 关键字声明变量,后跟 _ 变量名称 _ 和 datatype

var i int
var s string

初始化变量:

声明变量后,可以使用 \u003d 运算符对其进行初始化。

i = 20
s = "Some String"

在一行中声明和初始化变量同样有效。

var k int = 35

您也可以在声明时初始化变量时省略类型。

var score = 10

短变量声明:

var 关键字也可以使用 :u003d 短变量声明符省略。

j := 50
str := "Some String!"

声明多个变量:

也可以在一行代码中声明多个变量。

firstName, lastName := "FirstName", "LastName"

变量声明块:

变量声明也可以组合在一起以获得更好的可读性和更简洁的代码。

var (
    name = "Donald Duck"
    age  = 50
)

常数

常量是一个在任何情况下都不能改变的具有固定值的变量。

声明一个常量:

使用 const 关键字而不是 var 声明常量。您必须在声明时分配该值,因为该值将在此之后修复它。

const PI float64 = 3.14159265359
const VALUE = 1000

注意:常量变量的名称通常用大写字母书写。

使用声明块声明:

也可以使用声明块声明常量以提高可读性。

const (
	PRODUCT  = "Ice Cream"
	QUANTITY = 50
)

数据类型

如前所述,Golang 是一种静态类型的编程语言,这意味着变量始终具有无法更改的类型。

以下是 Golang 中可用的所有数据类型的列表:

uint8       unsigned  8-bit integers (0 to 255)
uint16      unsigned 16-bit integers (0 to 65535)
uint32      unsigned 32-bit integers (0 to 4294967295)
uint64      unsigned 64-bit integers (0 to 18446744073709551615)
int8        signed  8-bit integers (-128 to 127)
int16       signed 16-bit integers (-32768 to 32767)
int32       signed 32-bit integers (-2147483648 to 2147483647)
int64       signed 64-bit integers (-9223372036854775808 to 9223372036854775807)

float32     IEEE-754 32-bit floating-point numbers
float64     IEEE-754 64-bit floating-point numbers
complex64   complex numbers with float32 real and imaginary parts
complex128  complex numbers with float64 real and imaginary parts

byte        alias for uint8
rune        alias for int32

uint     unsigned, either 32 or 64 bits
int      signed, either 32 or 64 bits
uintptr  unsigned integer large enough to store the uninterpreted bits of a pointer value

您可以在变量名之后设置变量的数据类型。

// Integer
var i int = 5
fmt.Println(i)

// String
var s string = "Hello World!"
fmt.Println(s)

// Float
var f float64 = 3.14159265359
fmt.Println(f)

// Boolean
var b bool = true
fmt.Println(b)

转换数据类型

转换也称为转换数据类型,在需要精确类型时是一个重要的概念。本节将向您展示最常见的类型转换。

要浮动的字符串:

最常见的转换之一是将字符串转换为浮点数。为此,您使用 strconv 包,它是将字符串转换为基本数据类型的标准包。

s := "3.1415926535"
f, err := strconv.ParseFloat(s, 8)

if err != nil {
    fmt.Println("Error convert string to integer", err)
}

fmt.Println(reflect.TypeOf(f))

浮点到字符串:

将浮点数转换为字符串会更容易一些,因为它们不会是错误的。

var flo float64 = 3.1415926535
var strFlo string = strconv.FormatFloat(flo, 'E', -1, 32)
fmt.Println(reflect.TypeOf(strFlo))

浮点到整数:

基本数据类型(如 int 和 floats)的转换要容易得多,可以使用以下语法完成。

var f32 float32 = 3.1415926535
fmt.Println(reflect.TypeOf(f32))

i32 := int32(f32)
fmt.Println(reflect.TypeOf(i32))

操作员

运算符是告诉 Go 编译器执行特定操作的符号。以下代码块将向您展示 Go 编程语言的所有基本运算符以及如何在应用程序中使用它们。

算术运算符:

算术运算符用于执行常见的算术运算,例如加减数字。以下是最常见的算术运算符列表:

  • '+' (加法) - 添加到操作数

  • '-' (减法) - 通过相减来构建两个数字的总和

  • '*' (乘法) - 将两个值相乘

  • '/' (除法) - 将两个值相除

  • '%' (模数) - 获取整数除法后的余数

让我们看看如何在应用程序中实际使用这些运算符。

package main

import "fmt"

func main() {
    var i int = 10
    var k int = 20

    // Arithmetic Operators
    fmt.Printf("i + k = %d\n", i+k)
    fmt.Printf("i - k = %d\n", i-k)
    fmt.Printf("i * k = %d\n", i*k)
    fmt.Printf("i / k = %d\n", i/k)
    fmt.Printf("i mod k = %d\n", i%k)
}

运行上述程序的输出应该是:

# Output
i + k = 30
i - k = -10
i * k = 200
i / k = 0
i mod k = 10

比较运算符:

比较运算符用于将两个值相互比较。

  • 'u003du003d'(等于) - 如果 x 等于 y,则返回 true

  • '!u003d' (不等于) - 如果 x 不等于 y,则返回 true

  • '<' (小于) - 如果 x 小于 y,则返回 true

  • '<u003d'(小于等于) - 如果 x 小于或等于 y,则返回 true

  • '>' (大于) - 如果 x 大于 y,则返回 true

  • '>u003d' (大于等于) - 如果 x 大于或等于 y,则返回 true

以下是这些操作符的示例。

package main

import "fmt"

func main() {
    var i int = 10
    var k int = 20

    // Comparison Operators
    fmt.Println(i == k)
    fmt.Println(i != k)
    fmt.Println(i < k)
    fmt.Println(i <= k)
    fmt.Println(i > k)
    fmt.Println(i >= k)
}

运行程序时,您应该看到以下输出:

# Output
false
true
true
true
false
false

逻辑运算符:

逻辑运算符用于组合多个条件或补充原始条件的评估。

  • '&&'(逻辑与) - 如果所有条件都为真,则返回真

  • '||' (逻辑或)- 如果至少一个条件为真,则返回真

  • '!' (逻辑非)- 反转条件的结果,例如真到假,反之亦然

这是一个示例,向您展示操作员的实际操作:

package main

import "fmt"

func main() {
    var i int = 10
    var k int = 20
    var z int = 30

    // Logical Operators
    fmt.Println(i < z && i > k)
    fmt.Println(i < z || i > k)
    fmt.Println(!(i == z && i > k))
}

运行程序时,您应该看到以下输出:

# Output
false
true
true

赋值运算符:

赋值运算符用于将特定值分配给变量。

  • 'u003d'(简单赋值) - 将值分配给变量

  • '+u003d' (添加赋值) - 将值添加到变量的当前值

  • '-u003d'(减去赋值) - 将值减去变量的当前值

  • '*u003d'(乘法赋值) - 将变量的当前值与新值相乘

  • '/u003d' (除法赋值) - 将变量的当前值除以新值

  • '%u003d' (模数赋值) - 取两个值的模数并将结果赋值给左操作数

这是赋值运算符的一个实际示例:

package main

import "fmt"

func main() {
    // Assignment Operators
    var x, y = 15, 25
    x = y
    fmt.Println("= ", x)

    x = 15
    x += y
    fmt.Println("+=", x)

    x = 50
    x -= y
    fmt.Println("-=", x)

    x = 2
    x *= y
    fmt.Println("*=", x)

    x = 100
    x /= y
    fmt.Println("/=", x)

    x = 40
    x %= y
    fmt.Println("%=", x)
}

运行程序时,您应该看到以下输出:

# Output
=  25
+= 40
-= 25
*= 50
/= 4
%= 15

功能

函数是代码块,可帮助您将程序划分为可重用且易于阅读的较小代码包。在本节中,您将学习开始在 Golang 中编写函数所需了解的所有内容,包括声明、参数、返回值等等。

声明:

可以使用 func 关键字声明函数,后跟函数名称、一对括号和包含该功能的代码块。

然后可以使用函数名后跟括号来调用该函数。这是一个基本的 hello world 函数的示例,它不带参数也不返回任何值。

package main

import "fmt"

func main() {
	helloWorld()
}

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

参数:

有时您需要将信息传递给您的函数,这可以使用参数来完成。参数在第一对括号之后指定,基本上只是变量。

package main

import "fmt"

func main() {
	hello("Go")
	add(20, 30)
}

func hello(x string) {
	fmt.Printf("Hello %s\n", x)
}

func add(x int, y int) {
	fmt.Println(x + y)
}

运行程序时,您应该看到以下输出:

Hello Go
50

注意:您可以传递任意数量的参数。但是最好将高于 4 或 5 的所有内容移动到某种对象或结构中以获得更好的可读性。

返回值:

从函数返回值是另一个基本概念,可以通过在第一对括号后提供返回类型来完成。然后,您可以使用 return 语句,如以下示例所示。

package main

import "fmt"

// Returning a single value of type int
func add(x int, y int) int {
	return x + y
}

func main() {
	// Accepting return value in variable
	sum := add(20, 30)
	fmt.Println("Sum: ", sum)
}

命名返回值:

您还可以定义具有命名返回类型的函数,其优点是不在返回语句中提供变量名称。

package main

import "fmt"

// Named return value
func getArea(l int, b int) (area int) {
	area = l * b
	return // Return without specify variable name
}

func main() {
	// Accepting a named return value
	area := getArea(10, 10)
	fmt.Println("Area: ", area)
}

返回多个值:

Golang 还允许您返回多个参数,这可以在许多实际场景中提供帮助,例如在返回第二个变量时进行错误处理。

package main

import "fmt"

// Returning multiple name values
func rectangle(l int, b int) (area int, parameter int) {
	parameter = 2 * (l + b)
	area = l * b
	return
}

func main() {
	// Accepting multiple return values
	area, parameter := rectangle(10, 10)
	fmt.Println("Area: ", area)
	fmt.Println("Parameter", parameter)
}

将地址传递给函数:

也可以将变量的地址传递给函数,然后使用函数内部的引用修改它们。

package main

import "fmt"

// Passing addresses to a function
func addValue(x *int, y *string) {
	*x = *x + 5
	*y = *y + " World!"
	return
}

func main() {
	var number = 20
	var text = "Hello"
	fmt.Println("Before:", text, number)

	addValue(&number, &text)

	fmt.Println("After:", text, number)
}

匿名函数:

匿名函数是不包含任何名称的函数,如果您想创建内联函数,它会很有用。

package main

import "fmt"

func main() {
  func(name string) {
		fmt.Println("Hello ", name)
  }("Everyone!")
}

将函数分配给变量:

匿名函数允许您将函数分配给变量,如下例所示。

package main

import "fmt"

// Defining a anonymous function
var (
	area = func(l int, b int) int {
		return l * b
	}
)

func main() {
	area := area(10, 10)
	fmt.Println(area)
}

关闭功能:

闭包函数是匿名函数的一种特殊情况,您可以在其中访问外部变量。

package main

import "fmt"

func main() {
	l := 10
	b := 10

	// Closure functions are a special case of a anonymous function where you access outside variables
	func() {
		var area int
		area = l * b
		fmt.Println(area)
	}()
}

可变函数:

可变参数函数是一种特殊类型的函数,可以采用任意数量的参数。

package main

import (
	"fmt"
	"reflect"
)

func main() {
	printMultipleStrings("Hello", "World", "!")
}

// Passing multiple atributes using a variadic function
func printMultipleStrings(s ...string) {
	for i := 0; i < len(s); i++ {
		fmt.Println(s[i])
	}
}

可变参数函数还允许您使用空接口传入多个不同的变量类型。

package main

import (
	"fmt"
	"reflect"
)

func main() {
	printMultipleVariables(1, "green", false, 1.314, []string{"foo", "bar", "baz"})
}

// Pass multiple different datatypes
func printMultipleVariables(i ...interface{}) {
	for _, v := range i {
		fmt.Println(v, "--", reflect.ValueOf(v).Kind())
	}
}

延迟函数调用:

Defer 是一个独特的 Go 语句,它安排函数调用在函数完成后运行。考虑以下示例:

package main

import "fmt"

func first() {
	fmt.Println("First Function")
}

func second() {
	fmt.Println("Second Function")
}

// Defer is a spezial statement that schedules functions to be executed after the function completes
func main() {
	// The Second function will be called after the first
	defer second()
	first()
}

运行代码后,您应该看到第一个方法在第二个方法之前执行,即使第二个方法首先被调用。

# Output
First Function
Second Function

高阶函数:

高阶函数是接收函数作为参数或返回函数作为返回值的函数。考虑以下示例:

package main

import "fmt"

func multiply(x, y int) int {
	return x * y
}

// Function that returns another function
func partialMultiplication(x int) func(int) int {
	return func(y int) int {
		return multiply(x, y)
	}
}

func main() {
	multiple := partialMultiplication(10)
	fmt.Println(multiple(10))
}

控制结构

控制结构用于指定是否应该执行代码块。在本节中,您将了解控制结构以及如何在应用程序中使用它们。

如果-否则:

If 语句仅用于在指定条件评估为真时执行代码。

package main

import (
	"fmt"
)

func main() {
	// If Statement
	x := true
	if x == true {
		fmt.Println("True")
	}
}

if 语句后面可以跟一个 else 语句,当 if 语句的计算结果为 false 时将执行该语句。

package main

import (
	"fmt"
)

func main() {
	// If-Else Statement
	y := 100
	if y > 80 {
		fmt.Println("Greater than 80")
	} else {
		fmt.Println("Lesser than 80")
	}
}

If 语句也可以使用 else if 语句链接,如下例所示。

package main

import (
	"fmt"
)

func main() {
	// If-Elseif Statement
	grade := 5
	if grade == 1 {
		fmt.Println("You have an A")
	} else if grade > 1 && grade < 5 {
		fmt.Println("You have no A but you are positiv")
	} else {
		fmt.Println("Your grade is negativ")
	}
}

Golang 中的 if 语句还可以在条件表达式之前包含一个简短的变量声明。

package main

import (
	"fmt"
)

func main() {
	// If statement initialization
	if a := 10; a == 10 {
		fmt.Println(a)
	}
}

开关:

switch 语句采用指定的表达式并将其与多个案例的列表进行比较。一旦 case 匹配表达式,它将执行代码块。

package main

import (
	"fmt"
)

func main() {
	// Switch Statement
	num := 1
	switch num {
	case 1:
		fmt.Println("One")
	case 2:
		fmt.Println("Two")
	default:
		fmt.Println("Many")
	}
}

多个案例也可以执行单个代码块,如下面的代码块所示。

package main

import (
	"fmt"
)

func main() {
	// Switch Statement with multiple cases
	switch num {
	case 1, 2, 3, 4, 5:
		fmt.Println("Some")
	case 6, 7, 8, 9:
		fmt.Println("More")
	default:
		fmt.Println("Many")
	}
}

贯穿关键词:

fallthrough 关键字可用于强制执行流通过连续的 case 块

package main

import (
	"fmt"
)

func main() {
	// Switch fallthrough case statement (forces the execution of the next statement)
	dayOfWeek := 3
	switch dayOfWeek {
	case 1:
		fmt.Println("Go to work")
		fallthrough
	case 2:
		fmt.Println("Buy some bread")
		fallthrough
	case 3:
		fmt.Println("Visit a friend")
		fallthrough
	case 4:
		fmt.Println("Buy some food")
		fallthrough
	case 5:
		fmt.Println("See your family")
	default:
		fmt.Println("No information available for that day.")
	}
}

这意味着如果执行案例一,它也会自动执行以下案例。运行程序后,您应该会看到以下输出。

# Output
Visit a friend
Buy some food
See your family

切换条件语句:

Switch 还允许您在案例中使用条件语句。

package main

import (
	"fmt"
)

func main() {
	// Switch using conditional statements
	switch {
	case num < 5:
		fmt.Println("Smaller than 5")
	case num == 5:
		fmt.Println("Five")
	case num > 5:
		fmt.Println("Bigger than five")
	default:
		fmt.Println("No information about the number")
	}
}

开关初始化器:

switch 语句还允许您直接在 switch 关键字之后初始化变量,该关键字只能在语句内部访问。

package main

import (
	"fmt"
)

func main() {
	// Switch initializer statement
	switch number := 5; {
	case number < 5:
		fmt.Println("Smaller than 5")
	case number == 5:
		fmt.Println("Five")
	case number > 5:
		fmt.Println("Bigger than five")
	default:
		fmt.Println("No information about the number")
	}
}

循环

循环用于迭代序列或执行代码块一定次数。 Golang 只包含一个循环类型,即 for 循环。 for 循环还取代了其他语言中的 while 循环的功能。

对于循环:

for 循环最基本的用例是当您事先知道要循环的频率时。这是从 0 到 9 循环并打印输出的示例。

package main

import (
	"fmt"
)

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

您还可以定义一个无限循环并使用循环内的 break 语句停止它,如本示例所示。

package main

import (
	"fmt"
)

func main() {
	// Infinite loop
	i := 0
	for {
		fmt.Println("Hello World!")
		// Breaks/Stops the infinite loop
		if i == 10 {
			break
		}
		i++
	}
}

range 关键字用于迭代计算结果为数组、切片、映射或字符串的表达式。

package main

import (
	"fmt"
)

func main() {
	// Ranges
	strings := []string{"Hello", "World", "!"}

	// Get the index and value while looping through the range
	for i, val := range strings {
		fmt.Printf("%d: %s \n", i, val)
	}

	// Only getting the index
	for i := range strings {
		fmt.Println(i)
	}

	// Only getting the value
	for _, val := range strings {
		fmt.Println(val)
	}
}

当循环:

如上所述,for 循环也可以用作 while 循环。只要条件为真,就会执行此循环。

package main

import "fmt"

func main() {
	// Basic while loop
	x := 0
	for x < 10 {
		fmt.Println(x)
		x++
	}
}

可以通过在 for 循环中使用条件和 break 语句来实现 do-while 循环。

package main

import "fmt"

func main() {
	// Do while loop
	num := 0
	for {
		// Work
		fmt.Println(num)

		if num == 10 {
			break
		}
		num++
	}
}

数组

数组是单一类型元素的集合。它用于同时保存多个值。这些值称为数组元素,可以使用特定索引访问。数组的大小是固定的,因此不能增长或缩小。

声明数组:

要声明一个数组,您首先需要指定元素的数量,然后是数组包含的元素的数据类型。

package main

import "fmt"

func main() {
	// Declaring an Array
	var intArray [5]int
}

此示例创建一个长度为 5 的整数数组。

赋值和访问元素:

可以使用索引号访问和分配值,该索引号将在方括号中指定。

package main

import "fmt"

func main() {
	// Declaring an Array
	var intArray [5]int

	// Assigning values
	intArray[0] = 10
	intArray[1] = 2

	// Accessing the elements
	fmt.Println(intArray[0])
	fmt.Println(intArray[1])
}

使用数组字面量初始化数组:

数组也可以使用数组字面量用预定义的值初始化。为此,您可以指定元素的数量、元素的数据类型,然后是用大括号括起来的预定义值。

package main

import "fmt"

func main() {
	// Initialize Array using Array literals
	x := [5]int{0, 5, 10, 15, 20}
	var y [5]int = [5]int{0, 5, 10, 15, 20}

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

您还可以使用以下语法为特定值初始化元素。

package main

import "fmt"

func main() {
	// Initialize values for specific array elements
	a := [5]int{1: 1, 4: 25}

	fmt.Println(a)
}

用省略号初始化数组:

椭圆语法可用于根据数组声明中指定的元素自动检测数组的大小。

package main

import "fmt"

func main() {
	// Initializing an Array with ellipses
	k := [...]int{10, 20, 30}

	fmt.Println(len(k))
}

复制数组:

可以使用两种方式复制数组。您可以复制其所有值或复制数组的引用。

  • 复制值 - 复制的数组具有相同的值但独立于原始数组

  • 复制参考 - 对原始数组的更改将反映在复制的数组中,反之亦然

这是一个更好理解的例子。

package main

import "fmt"

func main() {
	x := [5]int{0, 5, 10, 15, 20}

	// Copy array values
	y := x

	// Copy by reference
	z := &x

	fmt.Printf("x: %v\n", x)
	fmt.Printf("y: %v\n", y)

	x[0] = 1

	fmt.Printf("x: %v\n", x)
	fmt.Printf("y: %v\n", y)
	fmt.Printf("z: %v\n", *z)
}

在此示例中,我们将数组 x 复制到两个新数组中,然后更改原始数组中的一项。复制原始引用的数组也会更改值,而复制值的数组将保持不变。

遍历数组:

可以使用每种循环来循环遍历数组元素。这里有一些例子。

package main

import "fmt"

func main() {
	x := [5]int{0, 5, 10, 15, 20}

	// Standard for loop
	for i := 0; i < len(x); i++ {
		fmt.Println(x[i])
	}

	// Getting index and value using range
	for index, element := range x {
		fmt.Println(index, "=>", element)
	}

	// Only getting value using range
	for _, value := range x {
		fmt.Println(value)
	}

	// Range and counter
	j := 0
	for range x {
		fmt.Println(x[j])
		j++
	}
}

切片

切片是动态数组,可以按照您认为合适的方式增长和缩小。像数组一样,切片也使用可索引并具有长度。

创建切片:

可以使用多种技术创建切片:

  • 通过发出方括号中的长度来定义基本切片

  • 使用内置 make() 函数创建切片,该函数将数据类型、长度和容量作为参数

  • 使用切片文字初始化切片

  • 使用新关键字创建切片

以下是所有列出的技术的示例:

package main

import (
	"fmt"
	"reflect"
)

func main() {
	// Create an empty slice
	var x []int
	fmt.Println(reflect.ValueOf(x).Kind())

	// Creating a slice using the make function
	var y = make([]string, 10, 20)
	fmt.Printf("y \tLen: %v \tCap: %v\n", len(y), cap(y))

	// Initialize the slice with values using a slice literal
	var z = []int{10, 20, 30, 40}
	fmt.Printf("z \tLen: %v \tCap: %v\n", len(z), cap(z))
	fmt.Println(z)

	// Creating a Slice using the new keyword
	var a = new([50]int)[0:10]
	fmt.Printf("a \tLen: %v \tCap: %v\n", len(a), cap(a))
	fmt.Println(a)
}

添加项目:

可以使用 append() 方法将项目添加到切片中,该方法将切片和值作为参数。

package main

import (
	"fmt"
	"reflect"
)

func main() {
	// Add items using the append function
	var b = make([]int, 1, 10)
	fmt.Println(b)
	b = append(b, 20)
	fmt.Println(b)
}

如果底层切片的空间足够,则将值保存在切片中。如果没有,则创建一个新切片来存储该值。

访问项目:

作为数组,切片也使用索引来访问值。

package main

import (
	"fmt"
	"reflect"
)

func main() {
	// Access slice items
	var c = []int{10, 20, 30, 40}
	fmt.Println(c[0])
	fmt.Println(c[0:3])
}

更改项目值:

可以通过引用特定索引并使用 \u003d 运算符设置新值来更改值。

package main

import (
	"fmt"
	"reflect"
)

func main() {
	// Change item values
	var d = []int{10, 20, 30, 40}
	fmt.Println(d)
	d[1] = 35
	fmt.Println(d)
}

复制和附加项目:

可以使用内置的 copy() 函数复制切片,该函数将一个切片的数据复制到另一个切片。您还可以使用 append() 函数和扩展运算符将切片的所有项目附加到另一个切片。

package main

import (
	"fmt"
	"reflect"
)

func main() {
	// Copy slice into another slice
	var e = []int{10, 20, 30, 40}
	var f = []int{50, 60, 70, 80}
	copy(e, f)
	fmt.Println("E: ", e)

	// Append a slice to an existing one
	var g = []int{10, 20, 30, 40}
	var h = []int{50, 60, 70, 80}

	g = append(g, h...)
	fmt.Println(g)
}

地图

map 是一个无序集合,它允许您存储与 Java 中的 HashMap 或 Python 字典相当的键/值对。然后可以使用充当数组索引的键快速检索该值。

声明:

声明地图有多种技术。以下是最常用方法的一小部分:

  • 声明一个空地图然后添加对象

  • 使用一些默认值初始化地图

  • 使用 make 函数声明地图

以下是所有这些技术的示例:

package main

import "fmt"

func main() {
	// Declaring empty map
	var shopingList = map[string]int{}
	fmt.Println(shopingList)

	// Initializing a map
	var people = map[string]int{"Elon": 10, "Jeff": 15}
	fmt.Println(people)

	// Map declaration using make function
	var peopleList = make(map[string]int)
	peopleList["Elon"] = 10
	peopleList["Jeff"] = 15
	fmt.Println(peopleList)
}

访问和操作地图项:

访问和操作地图与数组非常相似。可以通过在方括号中提供键来访问项目。添加新项目是通过向映射提供新键并为变量分配值来完成的。更新的工作方式相同,但是您可以使用要更新的现有密钥来代替新密钥。可以使用删除函数删除项目并将地图和密钥作为函数参数传递。然后该函数将删除地图实例本身中的项目,因此没有返回值。

package main

import "fmt"

var m = map[string]string{
	"c": "Cyan",
	"y": "Yellow",
	"m": "Magenta",
	"k": "Black",
}

func main() {
	// Accessing items
	fmt.Println(m["c"])

	// Adding items
	m["b"] = "black"
	fmt.Println(m)

	// Updating items
	m["y"] = "lemon yellow"
	fmt.Println(m)

	// Deleting items
	delete(m, "b")
	fmt.Println(m)
}

遍历地图:

使用 range 关键字的 for 循环可用于快速迭代地图并获取键和值变量。请记住,地图是无序的集合,无法预测返回项目的顺序。

package main

import "fmt"

var m = map[string]string{
	"c": "Cyan",
	"y": "Yellow",
	"m": "Magenta",
	"k": "Black",
}

func main() {
	// Iterating over a map
	for k, v := range m {
		fmt.Printf("Key: %s, Value: %s", k, v)
	}
}

检查项目是否存在:

从地图中获取项目时,您还可以使用第二个返回值检查地图中是否存在项目。该值是一个布尔值,如果地图不包含您搜索的值,将返回 false。

package main

import "fmt"

var m = map[string]string{
	"c": "Cyan",
	"y": "Yellow",
	"m": "Magenta",
	"k": "Black",
}

func main() {
	// Test if an item exists
	c, ok := m["y"]
	fmt.Println("\nc: ", c)
	fmt.Println("ok: ", ok)
}

结构

结构是具有预定义类型的数据字段的集合,允许您定义自己的数据类型,类似于 Java 中的类。结构的每个数据字段都使用预定义的类型(内置或用户定义)声明。

结构提高了应用程序的模块化并帮助您定义复杂对象的结构,可以将其传递给函数或以其他方式使用。

声明一个结构:

通过在实际名称之前使用类型语句和在名称之后使用结构语句来声明结构。然后您可以定义对象内每个字段的字段和数据类型。

package main

import "fmt"

// Declaring a Struct
type Animal struct {
	name   string
	weight int
}

func main() {
}

在这里,我们声明了一个名为 ,,Animal" 的结构,它有两个字段 - 字符串类型的名称和 int 类型的权重。

创建一个结构:

声明结构后,有多种创建实例的方法。以下是最常见的创建过程列表:

  • 以结构为数据类型声明一个变量而不初始化它

  • 使用 struct literate 创建实例

  • 使用 new 关键字创建实例

  • 使用指针地址运算符创建新实例

以下是展示这些方法的示例:

package main

import "fmt"

// Declaring a Struct
type Animal struct {
	name   string
	weight int
}

func main() {
	// Creating an instance of a struct
	var dog Animal
	dog.name = "Dog"
	dog.weight = 40
	fmt.Println(dog)

	// Creating an instance using struct literate
	var cat = Animal{name: "Cat", weight: 5}
	fmt.Println(cat)

	// Creating an instance using the new keyword
	var bird = new(Animal)
	bird.name = "Bird"
	bird.weight = 1
	fmt.Println(bird)

	// Creating an instance using the pointer address operator
	var monkey = &Animal{name: "Monkey", weight: 10}
	fmt.Println(monkey)
}

比较结构实例:

在 Go 中,\u003du003d 运算符或 reflect.DeepEqual() 方法可用于比较同一结构的两个实例。两者都将首先检查两个对象是否是同一结构的实例,然后继续比较字段的值。如果所有值都相同,则将返回 true。

package main

import "fmt"

// Declaring a Struct
type Animal struct {
	name   string
	weight int
}

func main() {
	// Creating an instance
	var bird = new(Animal)
	bird.name = "Bird"
	bird.weight = 1
	fmt.Println(bird)

	var monkey = &Animal{name: "Monkey", weight: 10}
	fmt.Println(monkey)

	// Comparing struct instances
	fmt.Println(bird == monkey)

    // Comparing struct instances using DeepEqual
    fmt.Println(reflect.DeepEqual(bird, monkey))
}

复制结构:

当您想要具有相同值但不引用原始实例的第二个实例时,复制结构的实例很重要。这意味着当您更改其中一个实例时,另一个实例不会受到影响。

package main

import "fmt"

// Declaring a Struct
type Animal struct {
	name   string
	weight int
}

func main() {
	// Creating an instance using the pointer address operator
	var monkey = &Animal{name: "Monkey", weight: 10}
	fmt.Println(monkey)

	// Copying struct type using pointer reference
	monkey2 := monkey
	monkey2.name = "Monkey2"
	fmt.Println(monkey2)
}

结构方法:

通过在方法的头部定义关联类型,可以将方法与结构关联。在以下示例中,ConfInfo() 函数与 Config 结构相关联,并且可以被 Config 结构的每个实例使用。

package main

import "fmt"

type Config struct {
	Env   string
	Proxy ProxyInfo
}

type ProxyInfo struct {
	Address string
	Port    string
}

func (conf Config) ConfInfo() string {
	fmt.Println("Env: ", conf.Env)
	fmt.Println("Proxy: ", conf.Proxy)
	return "----------------------"
}

func main() {
	c := &Config{
		Env: "DEBUG:TRUE",
		Proxy: ProxyInfo{
			Address: "addr",
			Port:    "port",
		},
	}

	fmt.Println(c.ConfInfo())
}

嵌套结构:

通过给一个结构的字段赋予另一个结构的类型,结构也可以相互嵌套。这使您可以创建可以轻松重用的复杂数据格式。

package main

import "fmt"

// Nested Struct
type Configuration struct {
	Env   string
	Proxy Proxy
}

type Proxy struct {
	Address string
	Port    string
}

func main() {
	// Creating an instance of a nested struct
	c := &Configuration{
		Env: "DEBUG:TRUE",
		Proxy: Proxy{
			Address: "addr",
			Port:    "port",
		},
	}
	fmt.Println(c)
	fmt.Println(c.Proxy.Address)
}

在这里,我们使用 Configuration 结构中的 Proxy 结构作为数据类型。

JSON 字段标签:

声明结构时,您还可以指定可选的字段标签(例如 JSON 或 YAML)。这些将帮助您将结构的实例格式化为特定格式,反之亦然。

package main

import (
	"encoding/json"
	"fmt"
)

type Dog struct {
	Name   string `json:"name"`
	Weight string `json:"weight"`
}

func main() {
	json_string := `
    {
        "name": "Rocky",
        "weight": "45"
    }`

	rocky := new(Dog)
	json.Unmarshal([]byte(json_string), rocky)
	fmt.Println(rocky)

	spot := new(Dog)
	spot.Name = "Spot"
	spot.Weight = "20"
	jsonStr, _ := json.Marshal(spot)
	fmt.Printf("%s\n", jsonStr)
}

上面的示例向您展示了如何使用它来将 JSON 字符串转换为 Dog 结构的实例,以及如何将 Dog 结构的实例转换回 JSON。

接口

接口是 Golang 是一种定义一组方法签名的类型。如果一个类型实现了接口提供的所有方法签名,那么它就满足接口。

定义接口:

您可以在类型名称后使用 interface 关键字定义接口,如下所示。

package main

import (
	"fmt"
)

// Defining a interface
type User interface {
	PrintName(name string)
}

type Vehicle interface {
	Alert() string
}

func main() {

}

用户界面定义了一个名为 PrintName() 的方法签名。因此,任何实现了 PrintName() 函数的类型现在都可以满足用户界面。

实现接口:

在结构中实现接口时,您无需显式指定实现的接口。 Go 将通过检查结构是否实现接口中定义的所有方法签名来自动确定该结构是否实现了接口。

package main

import (
	"fmt"
)

// Defining a interface
type User interface {
	PrintName(name string)
}

type Vehicle interface {
	Alert() string
}

// Create type for interface
type Usr int

type Car struct{}

// Implement interface function in type
func (usr Usr) PrintName(name string) {
	fmt.Println("User Id:\t", usr)
	fmt.Println("User Name:\t", name)
}

func (c Car) Alert() string {
	return "Hup! Hup!"
}

func main() {
	var user1 User
	user1 = Usr(1)
	user1.PrintName("Gabriel")

	c := Car{}
	fmt.Println(c.Alert())

	Print(20)
}

定义一个空接口:

还有一种方法可以定义一个空接口,该接口通常用于接受任何数据类型的函数。它还经常与允许传递任意数量的参数的 ... 扩展运算符结合使用。

package main

import (
	"fmt"
)

// Define an empty interface - Often used for functions that accepts any type
func Print(a ...interface{}) (n int, err error) {
	return fmt.Println(a...)
}

func main() {

}

此示例中的 Print() 方法由于接口为空而接受任何类型的参数,并且由于展开运算符,还允许用户传递无限参数。

进一步的步骤

您现在应该熟悉 Go 编程语言的基础知识,但仍有很多概念需要学习,并且您需要练习已经学过的技能。

我建议通过构建真实世界的项目来开始练习。您可以在以下Github 存储库中找到所有代码和大量真实 Go 项目的集合,这将帮助您成为更好的 Go 开发人员。

我还可以推荐Manning Go in Action 书,我个人阅读这本书是为了快速了解 Golang(免责声明:附属链接)。

来源

以下是用于撰写本文的资源:

  • 使用 Go

  • Golang 程序

  • 教程Point Golang

  • GeekForGeek Go 运营商

结论

你一直坚持到最后!我希望本文能帮助您了解 Go 编程语言的基础知识以及如何在应用程序中使用它。

如果您觉得这很有用,请考虑向其他开发人员推荐和分享它,并订阅我的时事通讯。如果您有任何问题或反馈,请使用我的联系表告诉我,或通过Twitter与我联系。