go语言基础

80 阅读10分钟

目标

简单介绍一下 GO 语言的诞生背景,使用场景,目前使用方案 简单介绍一下 GO的使用,GO的基础语法,简单过一下一些GO的语言例子 着重介绍一下GO的特性,为什么大家都使用GO语言,GO的内存结构、为什么都说GO快 论述清楚为什么大家都在开始使用GO 1 Go语言概述

  1. 1Go语言的诞生背景 超线程技术和CPU多核化的普及为并行计算提供了技术支持和编程需求,程序的并发度有了极大的提升。 但是反观编程语言领域却没有什么大的动作,在多核 CPU 高效安全的协作方面,主流语言(C、C++、Java)能做的并不是很多。 Google工程师为了解决多核 CPU 高效安全地协作的问题,同时提高开发效率,便开始开发了Go语言。 Go语言是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。

静态强类型:静态强类型语言是指在编译阶段就确定每个变量的数据类型的编程语言,并且这个数据类型在后续的代码中是不允许改变的。 编译型:代码在执行前需要经过一个转换过程,也就是编译。编译器会将源代码(人类可读的高级指令)转换成目标代码(机器可执行的低级指令),并生成一个可执行文件。这个可执行文件可以在没有源代码的情况下运行。Java是一种既编译又解释的语言。

1.2 Go语言的使用场景 Go在诞生之初,就以出色的性能和高效的并发处理能力,被广泛应用在以下的场景:

服务器端开发:Go语言非常适合用于构建大型、高性能的服务器端应用程序。它的并发处理能力和高效的I/O操作使其成为处理高并发请求的理想选择。越来越多的公司开始拥抱Go语言。 分布式系统:Go语言具有对并发的天然支持,其内置的goroutine和channel使得并发编程变得简单而高效。这使得Go语言成为构建分布式系统的理想选择,可以方便地实现并发的任务分发和数据同步。 云计算:Go语言具有快速编译、高效执行和低资源消耗等特点,非常适合用于构建云计算平台、容器编排工具、云存储等。许多知名的云计算项目,如Docker、Kubernetes等,都是使用Go语言开发的。 数据库和存储系统、系统编程、DevOps工具等 2 Go语言基础 2.1 Go语言的基础语法 以一个经典的Hello World示例:

// 指定包名 package main

// 导包 import "fmt"

// 函数定义 函数名(参数) 返回值 func main() { // 使用标准库进行打印 fmt.Println("Hello, World!") // 结束分句不需要 ; } 1 2 3 4 5 6 7 8 9 10 11 12 与Java相比有几点差异:

不需要分号:在Go语言中,一行代码的结束不需要分号,这与Java和许多其他C系列语言不同。 强制大括号:在Java中,如果if,for等语句的主体只有一行代码,可以省略大括号,但在Go语言 > 中,无论主体部分有多少行代码,都必须使用大括号。 缩进风格:Go语言采用的是Tab键进行缩进,而Java则是使用四个空格。 错误处理:Go语言没有异常处理,而是通过多值返回和错误接口进行错误处理,这与Java的try-> catch-finally异常处理方式不同。 变量声明:Go语言的变量声明方式也与Java不同,Go语言使用 var 关键字声明变量,同时Go语言还支持 “:=” 形式的短变量声明和初始化。 公有和私有:Go语言中,首字母大写的函数、变量是公有的,首字母小写的是私有的,而在Java中,公有和私有由关键字public和private表示。 类型声明:在Go语言中,类型声明放在变量名之后,而在Java中,类型声明放在变量名之前。 资源管理:Go语言使用defer关键字进行资源管理,使得资源的释放操作可以紧跟在资源的获取操作之后,而不需要去关心何时进行资源的释放。 2.2 Go语言的基本数据类型 // 布尔类型 // var : go关键字,用来声明变量,类型可加可不加,会进行类型推导 // 若不需要指定变量类型,也可以指定为:b := true,表示声明变量且赋值 var b bool = true cBool := true // 数字类型 var i int = 10 var f float32 = 12.34 // 字符串类型 var s string = "Hello Go" var arr1 [5]int // 声明了一个int类型的数组 arr2 := [3]int{1,2,3} // 声明了一个长度为3的int数组 arr3 := [...]int{2, 4, 6, 8, 10} // 声明了一个长度为5的int数组,元素是2, 4, 6, 8, 10

// 切片会自动扩容 var mySlice []int //声明一个int切片 mySlice = []int{2, 3, 5, 7, 11, 13} // 创建包含6个元素的切片 mySlice = []int{2, 3, 5, 7, 11, 13}[1:4] // 创建包含原切片元素1-4的切片

// map类型 var m map[string]int

m["key1"] = 1

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Go语言提供了一种简单、直接、灵活的方式来处理数据类型,可以根据实际需要来构造出复杂的数据类型。

2.3 Go语言的控制结构 if else 结构 package main

import"fmt" func main() { var a int = 100 if a < 20 { fmt.Printf("a 小于 20\n" ) } else { fmt.Printf("a 不小于 20\n" ) } } 1 2 3 4 5 6 7 8 9 10 11 12 循环结构只有for循环,没有while关键字: // 标准的for循环语句,初始化变量、循环判断条件,后处理,这三个部分都可以不指定 for i := 0; i < 10; i++ { fmt.Println(i) }

// 类似while循环的使用 i := 0 for i < 10 { fmt.Println(i) i++ }

// 无限循环 for { fmt.Println("This loop will run forever.") }

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 2.4 Go语言的函数 package main

import "fmt"

// 定义函数 函数名(参数A, 参数B) 返回值 func add(x int, y int) int { return x + y }

func main() { // 调用函数 fmt.Println(add(42, 13)) } 1 2 3 4 5 6 7 8 9 10 11 12 13 2.5 Go语言的错误处理方式 package main

import ( "fmt" "errors" )

// 我们定义了一个f1函数,该函数在参数等于42时返回一个错误,否则返回参数加3的结果。 func f1(arg int) (int, error) { if arg == 42 { return -1, errors.New("can't work with 42") } return arg + 3, nil }

func main() { // 对f1函数的返回值进行了错误处理,如果返回错误,打印"f1 failed:“,否则打印"f1 worked:”。 for _, i := range []int{7, 42} { if r, e := f1(i); e != nil { fmt.Println("f1 failed:", e) } else { fmt.Println("f1 worked:", r) } } }

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 2.7 Go语言的面向对象编程 2.7.1 结构体(Structs) Go语言没有“类”这个概念,但可以通过结构体实现类似的功能。 在Go语言中,结构体是一种复合的数据类型,可以包含零个或多个任意类型的值。我们可以把结构体看作是类的一种简化形式。

type Person struct { Name string Age int }

func main() { p := Person{Name: "Alice", Age: 20} fmt.Println(p.Name) // 输出: Alice } 1 2 3 4 5 6 7 8 9 2.7.2 方法(Methods) 在Go语言中,可以给任意类型(包括结构体)定义方法。方法的定义形式和函数类似,只是在函数名前多了一个接收者参数,接收者可以是任意类型的变量。这样我们就可以在这个变量上调用这个方法。

type Rectangle struct { Width, Height float64 }

// 为Rectangle定义Area方法 func(r Rectangle) Area() float64 { return r.Width * r.Height }

func main() { r := Rectangle{Width: 10, Height: 5} fmt.Println(r.Area()) // 输出: 50 } 1 2 3 4 5 6 7 8 9 10 11 12 13 2.7.3 接口(Interfaces) 在Go语言中,接口是一种类型,它定义了一组方法,但这些方法不包含实现代码。 实现接口的类型(也就是实现了接口中的所有方法的类型),可以被看作是那个接口类型的变量。 这与Java等语言的接口有一定的差异,Go语言的接口更为灵活,实现接口的类型不需要显式声明它实现了哪些接口,只要实现了接口中的所有方法就可以。

type Shape interface { Area() float64 }

type Shape2 interface { Area() float64 }

type Circle struct { Radius float64 }

// Circle实现Shape接口 func (c Circle) Area() float64 { return math.Pi * c.Radius * c.Radius }

func main() { c := Circle{Radius: 5} var s Shape2 = c fmt.Println(s.Area()) // 输出: 78.53981633974483 }

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 2.7.4 继承与组合 Go语言没有明确的继承机制,但可以通过组合的方式达到类似的效果。 在Go语言中,可以在一个结构体中嵌入其他的结构体或接口,被嵌入的结构体或接口就像是该结构体的一部分,该结构体可以直接访问被嵌入的结构体的字段和方法,从而实现了类似继承的功能。

type Person struct { Name string Age int }

type Student struct { Person // 嵌入Person School string }

func main() { s := Student{Person: Person{Name: "Alice", Age: 20}, School: "MIT"} fmt.Println(s.Name) // 输出: Alice } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 2.7.5 多态 Go语言通过接口实现多态,使用了一种鸭子类型(Duck Typing)的思想来实现接口。 简单来说,鸭子类型就是“如果它走起路来像鸭子,叫起来也像鸭子,那么它就是鸭子”。 当一个类型实现了某个接口的所有方法,那么这个类型的变量就可以被看作是这个接口类型的变量,可以被赋值给这个接口类型的变量。 这样,我们就可以使用接口类型的变量来调用实现类型的方法,实现多态。 定义一个Walker接口。

type Walker interface { Walk() } 1 2 3 然后我们定义了一个Duck类型,并为其实现了Walk()方法:

type Duck struct {}

func(d Duck) Walk() { fmt.Println("Duck walks") } 1 2 3 4 5 这样,Duck就被认为实现了Walker接口,即使我们并没有显式声明Duck实现了Walker接口。然后我们就可以把Duck类型的对象赋值给Walker类型的变量,通过这个变量调用Walk()方法:

var w Walker = Duck{} w.Walk() // 输出:Duck walks 1 2 如果一个类型需要实现多个接口,并且这些接口中有相同的方法,那么这个类型只能提供一个实现,这个实现会被所有调用这个方法的接口共享。