likes
comments
collection
share

1.go语言入门深入解析

作者站长头像
站长
· 阅读数 24

目录

  1. 1. 关键5大问题解读
  2. 2. Go语言简介
  3. 3. Go语言能做什么
  4. 4. 实现原理
  5. 5. 使用Go的步骤
  6. 6. 最佳实践及注意事项
  7. 7. 代码示例

关键5大问题解读

问题1:Go到底是静态类型语言还是动态类型语言?

静态类型语言是指在编译阶段就确定变量的类型,如果将不同类型的数据赋值给变量,就会在编译阶段报错。这种类型的语言能提供更强的类型检查,预防了许多运行错误,同时也能提供更好的性能。

动态类型语言则是在运行阶段才确定变量的类型,通常具备更高的灵活性,但可能在运行过程中发生类型错误。

Go语言虽然是静态类型语言,但是也提供了一些动态语言的特性,比如接口(interface)类型提供了对动态类型的支持,可以接收任何实现了特定接口的类型实例。这让Go语言在提供静态类型的性能和安全性的同时,也具有一定的灵活性

问题2:Go编译后的是什么文件?

因为Go语言是编译型语言,源代码在运行前会先编译成机器码,然后操作系统直接运行这个机器码。这个机器码组成的就是那个可执行文件

同时,Go语言编译出的二进制文件是静态链接的,这意味着所有的依赖库都会被包含在最后生成的二进制文件中。因此你可以直接运行这个二进制文件,而不需要关心在目标机器上安装依赖库等问题。这也是Go语言得以在Docker等容器化环境中流行的原因之一

问题3: Go语言的并发模型是什么?

Go语言的并发模型是goroutine和channel。goroutine是Go语言并发的核心,它是一种轻量级的线程,可以在Go语言运行时调度器上并发执行。channel是goroutine之间的通信方式,可以用来在goroutine之间传递数据,实现数据共享和同步

问题4:Go语言的垃圾回收机制是什么?

Go语言的垃圾回收机制是自动的,开发者无需关心内存管理。Go语言的垃圾回收器会自动检测不再使用的内存,并回收这些内存。Go语言的垃圾回收器是基于标记-清除算法实现的,它会在程序运行时自动进行垃圾回收,释放不再使用的内存,避免内存泄漏

问题5:Go语言的接口和继承是怎么实现的?

Go语言没有传统的面向对象语言中的继承的概念,但是有接口这一概念。Go语言的接口是一种抽象类型,它定义了一组方法的集合,只要一个类型实现了这些方法,就可以看作是这个接口的实现类型。这种方式实现了接口的多态性,使得Go语言可以实现类似继承的效果

Go语言简介

Go是一门静态类型、编译型的开源编程语言。Go的设计者和主要的实现者都是Google,并且Go依靠Google的强有力支持稳步发展。它的设计目标是“解决开发人员在处理大型代码项目时常常遇到的一些问题”。而Go语言的出现正是为了将这一目标具体化,它通过在语言级别提供并发支持、垃圾回收、强大的标准库、强类型等特性,使开发者可以更高效,更舒适地编写代码

Go语言能做什么

  • • 性能优化:Go语言在语言核心层面支持并行计算,能有效利用多处理器、多核心的计算能力,使得CPU密集型或者并发性强的系统获得更好的性能。
  • • 部署简单:编译后的Go应用是静态的二进制文件,无需任何外部依赖,可以直接运行,使得部署极其简便。
  • • 内建网络库:Go内建了丰富的网络库,对于网络编程非常友好,因此很适合后端开发。
  • • 内容丰富、便捷的标准库:Go语言的标准库丰富又实用,可以解决你日常大部分的问题。

实现原理

与传统的编译型语言如C,C++等不同,Go是以平台无关的中间代码分发的,当安装程序或者运行程序时,编译成特定平台的机器代码,这也是Go执行速度快的主要原因。

  • • 编译速度:Go采用了包级别的编译方式,编译速度极快。
  • • 并发:goroutine和channel:Golang原生支持并发,goroutine是Go并发的核心,相比于线程,它的初始栈内存非常小,且运行时可动态增长,创建和销毁的成本低廉。channel是goroutine之间的通信方式,使用它可以很方便地处理并发之间的数据共享和同步问题。
  • • 面向接口编程:Go语言没有传统的面向对象中的继承的概念,但是有接口这一概念,而且数据类型(不论是结构体还是别的类型)只要实现了该接口的所有方法,就可以看作是这个接口的实现类型。
  • • 垃圾回收:Go语言自带垃圾回收机制,开发者无需关心内存管理,这与C,C++不同,降低了开发门槛

使用Go的步骤

  • • 安装:包括下载Go语言安装包,配置GOPATH环境变量等。
    • • 配置go env -w GO111MODULE=on用来开启Go Modules,这是Go语言的依赖管理工具,可以更好地管理项目依赖。
    • • 配置go env -w GOPROXY=mirrors.aliyun.com/goproxy/用来设…
  • • 编程:开发工具可以使用VSCode、Goland等,编写Go语言程序。
  • • 编译:通过go build命令编译Go程序,生成可执行文件;通过go run命令直接运行Go程序。
  • • 测试:Go语言内建提供了testing包,非常方便编写测试,并且提供了一个轻量级的测试框架和功能。
  • • 部署:Go的部署非常简单,编译生成的程序不依赖其他文件,只需要将生成的可执行文件部署到服务器并执行即可。

最佳实践及注意事项

最佳实践

  • • Go是静态强类型的语言,使用var声明变量,:=进行简短声明,它要求明确每个变量的类型。
  • • Go的控制结构中不需要在条件或循环结构后添加括号,其语句块必须用花括号切需要在开头和结尾的花括号中。
  • • 函数的返回值可以为多个,并且可以将返回值明确命名。
  • • Go语言使用defer注册延迟调用,这些调用直到return前才被执。你可以在deferm后面一系列的函数注册,它们会在函数结束时按注册顺序的相反顺序被执行。

注意事项

  • • 并发控制:尽管Go语言对并发进行了内建的支持,会使用并发,并能够控制并发,编写并发控制良好的程序,是编程人员需要掌握的技能。
  • • 使用纯函数:由于函数在Go中属于一等公民,可以作为其它函数的参数或者返回值,闭包的存在提供了很多“潜力”,所以,我们应该尽量使用和编写纯函数。
  • • 资源清理:Go提供了defer关键字实现“延迟调用”,主要用于进行资源清理。
  • • 错误处理:Go语言的错误处理机制是通过返回值来实现的,函数返回值中的最后一个值通常是error类型,如果函数执行成功,error的值为nil,否则为错误信息。

代码示例

简单运行

下面是一个简单的Go语言程序,它输出了"Hello, World!"。

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

直接通过go run命令运行这个程序,就可以看到输出结果了,也可以通过go build命令编译成可执行文件,然后运行。

接口与继承

下面是使用Go语言编写的一个简单的示例,主要涉及到interface(接口)和embedding(嵌入),这在go中模拟实现了继承的效果。

package main
import "fmt"
// 定义一个数据的操作接口DataOperation
type DataOperation interface {
    Add()
    Subtract()
}
// 定义基本的共享结构体BaseData
type BaseData struct {
    value int
}
// 基本结构体的Add方法
func (data *BaseData) Add() {
    data.value += 1
}
// 基本结构体的Subtract方法
func (data *BaseData) Subtract() {
    data.value -= 1
}
// 定义一个继承自BaseData的结构体AData
type AData struct {
    // 使用内嵌字段,实现对BaseData的继承
    BaseData
}
// 定义一个继承自BaseData的结构体BData
type BData struct {
    // 使用内嵌字段,实现对BaseData的继承
    BaseData
}
func main() {
    // 创建一个AData对象
    a := AData{BaseData{value: 10}}
    // 创建一个BData对象
    b := BData{BaseData{value: 5}}
    // 将对象赋值给对应的接口,这样我们就可以把多种不同的结构体以相同的方式去处理
    var i DataOperation
    i = &a
    fmt.Println(i.Add())
    i = &b
    fmt.Println(i.Subtract())
}

首先定义了一个接口DataOperation,它规定了所有实现该接口的结构体都需要有Add()和Subtract()两个方法。然后定义了一个基础结构体BaseData,这个结构体实现了DataOperation接口,它有一个int型值value和两个方法,一个用于增加value的值,另一个用于减少value的值。

接下来定义了两个新的结构体AData和BData,它们都内嵌了BaseData,因此也都继承了BaseData的方法和属性,这就是Go语言中的继承。最后,在main函数中,我们创建了AData和BData的实例,并将这些实例赋值给了DataOperation接口。这样,我们就可以使用接口来操作这些对象,这是Go语言中接口的使用。

goroutine协程的使用

Go的协程(goroutine)是Go并发执行机制的核心。它们是用户级线程,由Go运行时进行管理,可以让函数并发执行。特点是创建与运行成本低,数以万计的goroutine可以同时存在

package main
import (
    "fmt"
    "time"
)
// 函数f就是一个goroutine运行的主体
func f(from string) {
    for i := 0; i < 3; i++ {
        // 打印出引入的消息
        fmt.Println(from, ":", i)
        // 此处的Sleep,使得进程挂起,切换到其他goroutine
        time.Sleep(time.Second)
    }
}
func main() {
    // 使用常规的同步方式调用函数f
    f("direct")
    // 使用go关键字来创建并启动一个goroutine
    go f("goroutine")
    // 也可以为匿名函数启动一个goroutine
    go func(msg string) {
        fmt.Println(msg)
    }("going")

    // 我们需要等待两个goroutine运行结束
    // 这里我们为了等待,故意Sleep了4秒,实际开发中我们可以使用channel或者sync.WaitGroup来同步goroutine
    time.Sleep(time.Second * 4)
    //程序运行结果可能不同,因为goroutines的调度可能以不同的顺序进行
    fmt.Println("done")
}

示例输出:

direct : 0
direct : 1
direct : 2
going
goroutine : 0
goroutine : 1
goroutine : 2
done

"going" 和 "goroutine" 的打印顺序可能会变,因为 Go 的 goroutine 调度是非确定性的。其他部分的打印顺序则是固定的,始终按照代码顺序执行。

转载自:https://juejin.cn/post/7374683456716914727
评论
请登录