Golang笔记——语言基础知识

devtools/2025/1/12 11:11:34/

大家好,这里是Good Note,关注 公主号:Goodnote,专栏文章私信限时Free。本文详细介绍Go语言的基础知识,包括数据类型,深浅拷贝,编程范式,Go语言是一种静态(静态类型语言 和 静态语言)强类型、编译型、并发型,并具有垃圾回收功能的编程语言。

在这里插入图片描述

文章目录

    • 1. Go 语言基础知识
      • 数据类型
        • 基本数据类型
        • 基本数据类型
        • 复合数据类型
      • 深拷贝与浅拷贝
        • 浅拷贝(Shallow Copy)
        • 深拷贝(Deep Copy)
      • 函数中传递数据
        • 值传递(Pass by Value)
        • 引用传递(Pass by Reference)
        • Go 中都是值传递的优势
      • 总结
    • 2. Go 语言与编程范式对比
      • 编译型语言 vs 解释型语言
        • 编译型语言
          • 以Go为例
        • 解释型语言
      • 动态语言 vs 静态语言
        • 动态语言
        • 静态语言
      • 动态类型语言 vs 静态类型语言
        • 动态类型语言
        • 静态类型语言
      • 强类型语言 vs 弱类型语言
        • 强类型语言
        • 弱类型语言
      • 动静态语言与动静态类型的区分
    • 3. Go 语言面向对象特性与 Java 对比
      • 1. 继承
        • Java
        • Go
      • 2. 封装
        • Java
        • Go
      • 3. 多态
        • Java
        • Go
      • 结论
    • GOROOT、GOPATH和 Go Modules
      • GOROOT 和 GOPATH 的区别
      • Go Modules 引入后的变化
        • Go Modules 的变化
        • 示例:使用 Go Modules
      • 示例结构
    • 历史文章
      • MySQL数据库
      • Redis
      • Golang

1. Go 语言基础知识

下面将详细介绍 Go 语言的理论知识,包括数据类型、深拷贝与浅拷贝、以及如何在函数中传递数据。

数据类型

Go 语言是强类型的,意味着变量在使用时必须明确指定类型。Go 语言有很多内建的数据类型,主要可以分为以下几类:

基本数据类型
  • 布尔类型(bool):表示真(true)或假(false)。
  • 数字类型
    • 整数:intint8int16int32int64uintuint8uint16uint32uint64
    • 浮点数:float32float64
    • 复数:complex64complex128
  • 字符类型(rune 和 byte)
    • runeint32 的别名,表示一个 Unicode 字符,通常用于表示多字节字符(如中文字符)。
    • byteuint8 的别名,表示单个字节(8 位数据),用于处理 ASCII 字符、字节流、二进制数据等。
  • 字符串类型(string):由一系列字符组成,Go 中的字符串是不可变的。
基本数据类型
  • 布尔型(bool): 用于表示truefalse值。
  • 整型(int、int8、int16、int32、int64): 用于表示整数,可以是有符号的整数(int)或无符号的整数(uint)。
  • 浮点型(float32、float64): 用于表示带小数的数字。
  • 字符型(rune): 用于表示Unicode字符,rune实际上是int32的别名。
  • 复数型(complex64、complex128): 用于表示复数,复数由实部和虚部组成。
  • 字符串(string): 用于表示文本数据,是一个不可变的字符序列。
复合数据类型
  • 非引用类型:

    • 数组(Array):Go 中的数组具有固定大小,数组类型由元素类型和数组长度共同决定。例如:var arr [5]int
    • 结构体(Struct):Go 中的结构体是一种自定义的数据类型,用于聚合不同类型的多个字段。
      • 定义结构体:
      type Person struct {Name stringAge  int
      }
      
  • 引用类型:

    • 指针: 存储变量的内存地址。
    • 切片(Slice):切片是 Go 中动态数组的抽象。切片并不包含数组的长度信息,它是一个指向数组的引用,可以动态改变大小。
    • 映射(Map):Go 中的映射类似于其他语言中的哈希表(字典),用于存储键值对。可以通过 make 创建一个映射。
    • 通道(channel): 是一种用于在 goroutine 之间进行通信的引用类型。它是一种管道,可以让一个 goroutine 发送数据到另一个 goroutine,从而实现数据传递和同步。
    • 函数: 函数本身也可以作为类型,传递和使用。
  • 接口(Interface):Go 中的接口是一种类型,定义了一组方法,但不需要实现这些方法。任何类型只要实现了接口中的所有方法,就隐式地实现了该接口。

深拷贝与浅拷贝

在 Go 中,“拷贝” 是指将一个变量的值传递到另一个变量。根据拷贝的方式不同,可以分为浅拷贝和深拷贝。

浅拷贝(Shallow Copy)

浅拷贝是指直接复制一个变量的值,如果这个变量是引用类型(如切片、映射、指针等),则复制的是引用地址,即新变量和原变量指向同一内存地址,修改其中一个变量的内容会影响到另一个。

  • 对于切片、映射、指针等引用类型,执行浅拷贝时,两个变量会共享底层的数据结构。

示例:

a := []int{1, 2, 3}
b := a // 浅拷贝,b 和 a 指向同一个切片
b[0] = 100
fmt.Println(a[0]) // 输出 100,a 和 b 是共享内存的
深拷贝(Deep Copy)

深拷贝是指创建一个完全独立的新变量,并且复制变量的值以及其所有引用的对象。如果变量是引用类型,深拷贝会复制其底层的数据,而不是引用。

  • 需要手动实现深拷贝,尤其是对于复杂的结构体(例如结构体内包含切片、映射等引用类型)。

示例:

a := []int{1, 2, 3}
b := make([]int, len(a))
copy(b, a) // 通过 copy 函数实现深拷贝
b[0] = 100
fmt.Println(a[0]) // 输出 1,a 和 b 是独立的

对于结构体,如果结构体包含引用类型字段,也可以通过 deepcopy 函数实现深拷贝。

函数中传递数据

在 Go 语言中,所有的函数参数传递都是 值传递,也就是说,传递的是参数的副本,即使是传递指针,传递的也是指针的副本。Go中的引用传递间接实现的。

  • Go中的引用传递,通常是指通过传递指针来间接修改原始数据,传递的是指针的副本,而不是原始的内存地址。
  • 两个指针的地址是不同的,一个是原始指针的地址,另一个是指针副本的地址。
  • 它们指向的是相同的内存区域,也就是说,指针副本和原始指针都指向同一块数据的内存地址。,因此通过指针可以间接修改原始数据。修改的是 指针指向的内存内容,而不是指针本身。
值传递(Pass by Value)

值传递意味着在函数调用时,将变量的副本传递给函数。如果函数修改了参数的值,它不会影响到原始变量。

示例:

func modify(x int) {x = 10
}func main() {a := 5modify(a)fmt.Println(a) // 输出 5,a 的值未被改变
}
引用传递(Pass by Reference)

引用传递是间接实现的,意味着将指针的副本传递给函数。函数可以通过副本指针访问和修改原始变量的值。

传递的是a 的指针的副本p,p的地址和a的地址不一样,但是p的值是a的地址。

示例:

package mainimport "fmt"func modify(p *int) {// 打印指针 p 的地址:p 变量的内存地址fmt.Printf("指针 p 的地址:%p\n", &p) // 打印指针变量 p 的内存地址// 打印指针 p 的值:即指针 p 存储的内存地址(p 的值应该是 a 的地址)fmt.Printf("指针 p 的值(应该是 a 的地址):%p\n", p) // 打印指针 p 存储的地址(即 p 指向的内存地址)// 修改指针 p 指向的值*p = 10
}func main() {a := 5// 打印 a 的值fmt.Println("传递之前 a 的值:", a)// 打印 a 的地址:a 变量存储的内存地址fmt.Printf("a 的地址:%p\n", &a)// 传递 a 的地址给 modify 函数modify(&a)// 打印修改后的 a 的值fmt.Println("传递之后 a 的值:", a)
}
Go 中都是值传递的优势

Go 中所有函数参数的传递机制都是值传递(pass-by-value),但 Go 提供了指针机制,允许通过指针修改数据。这种机制与很多其他语言(如 C)不同,后者通常会有显式的“引用传递”(pass-by-reference)。

  • 简洁性:值传递的方式比较简单,避免了很多指针错误,如悬空指针、内存泄漏等问题。
  • 内存安全性:Go 中的垃圾回收机制让指针的使用更加安全,通过值传递避免了很多复杂的内存管理问题。

总结

  • 数据类型:Go 提供了多种基本数据类型、复合数据类型(如切片、数组、映射、结构体等)和接口类型。
  • 深拷贝与浅拷贝:浅拷贝指的是对引用类型的复制,复制的是引用地址,深拷贝则是完全独立的复制,复制的是数据本身。
  • 函数传参:Go 支持值传递和引用传递。值传递会传递数据副本,而引用传递会传递数据的地址(指针),从而允许修改原始数据。

这种设计理念让 Go 语言在并发编程、高效内存管理和简洁易用性方面都表现优异。

2. Go 语言与编程范式对比

Go语言是一种静态(静态类型语言 和 静态语言)强类型、编译型、并发型,并具有垃圾回收功能的编程语言。

编译型语言 vs 解释型语言

编译型语言
  • 编译型语言是需要通过编译器将源代码编译成机器码,之后才能执行的语言。编译过程通常包括编译(compile)和链接(linking)两个步骤。
    • 编译:把源代码编译成机器码
    • 链接:把各个模块的机器码和依赖库串连起来生成可执行文件。
以Go为例

Go语言从源文件到可执行目标文件的转化过程如下:
来源于:Go 程序员面试笔试宝典
在这里插入图片描述

这张图描述了 Go 语言 编译系统的整个工作流程,将 Go 源代码 转换为 可执行目标程序 的过程。它分为四个主要阶段:编译汇编链接 和生成最终的可执行程序。

  • 编译:翻译为汇编代码。
  • 汇编:转换为二进制机器码。
  • 链接:解决依赖,生成完整的可执行文件。

各模块及其功能:

  1. 源代码(hello.go):Go 编写的源代码。
  2. 编译器:将源代码转换为汇编代码(hello.s)。
  3. 汇编器:将汇编代码转换为二进制的目标文件(hello.o)。
  4. 链接器:将目标文件 hello.o 与依赖库(如 fmt.o)进行链接,生成最终的可执行程序(hello)。

各阶段详细解释如下:

  1. 源代码阶段(hello.go)
  • 输入:Go 源代码文件 hello.go
  • 文本格式:源代码是可读的文本文件,包含 Go 语言编写的代码。
  • 任务:提供程序的逻辑与功能实现。

  1. 编译器阶段(hello.s)
  • 编译器的作用:Go 编译器会将 Go 源代码(hello.go)翻译成 汇编程序hello.s)。
  • 输出hello.s 文件,包含汇编代码,是与机器底层指令相关的文本。
  • 文本格式:汇编代码仍是人类可读的,但更加接近于底层硬件的操作。

  1. 汇编器阶段(hello.o)
  • 汇编器的作用:将汇编代码(hello.s)转换为 二进制机器码,生成目标文件(hello.o)。
  • 输出hello.o 文件(目标程序),是 可重定位目标程序,尚未与库进行链接。
  • 二进制格式hello.o 是不可直接运行的二进制文件,需要进一步链接。

  1. 链接器阶段(hello 和 fmt.o)
  • 链接器的作用
    • hello.o 文件与标准库(如 fmt.o)或其他依赖的目标文件进行链接。
    • 解决函数调用和变量引用,将所有二进制代码组合为一个完整的程序。
  • 输入
    • hello.o:主程序的可重定位目标文件。
    • fmt.o:标准库 fmt 的目标文件(用于 fmt.Println 等函数)。
  • 输出:最终的可执行文件 hello
  • 二进制格式:生成的 hello 是一个可以直接运行的二进制目标程序。

解释型语言
  • 解释型语言的程序不需要预先编译,相比编译型语言省了道工序,解释性语言在运行程序的时候才逐行翻译。

编译型语言包括:C、C++、Delphi、Pascal、Fortran、Go
解释型语言包括:Basic、javascript、python、PHP

说明:

  • Java 并不是完全的编译型语言。Java 实际上是 编译-解释型语言,它首先将源代码编译成字节码(.class 文件),然后通过 Java 虚拟机(JVM)解释执行字节码。Java 的编译过程并不像 C/C++ 那样直接生成机器码,所以它不应被归类为纯粹的编译型语言。
  • Go 是编译型语言,它直接将源代码编译成机器码,并不依赖于虚拟机等中间层。
  • PHPPython 是解释型语言,但有一些现代的优化手段,例如 Python 使用 .pyc 文件和 JIT(即时编译)技术提升性能。

动态语言 vs 静态语言

动态语言
  • 动态语言:在运行时可以修改代码结构,如新增函数、修改对象等。
静态语言
  • 静态语言:在运行时代码结构不可改变。

动态语言:JavaScript、PHP、Python、Ruby、Erlang
静态语言:Java、C、C++、C#、Objective-C、Go

动态类型语言 vs 静态类型语言

动态类型语言
  • 动态类型语言:在运行时进行数据类型检查。
静态类型语言
  • 静态类型语言:在编译时确定数据类型。

动态类型语言:Python、Ruby、JavaScript、PHP、Perl
静态类型语言:C、C++、C#、Java、Go、Objective-C、Swift

强类型语言 vs 弱类型语言

强类型语言
  • 强类型语言:类型严格,变量的数据类型一旦确定就不能更改,不能随便进行隐式类型转换。
弱类型语言
  • 弱类型语言:类型较为宽松,可以进行隐式类型转换。

强类型语言:Java、C#、Python、Objective-C、Ruby、Go、C、C++(注意:C 和 C++ 在某些情况下表现为弱类型,但总体上它们是强类型语言)
弱类型语言:JavaScript、PHP

说明:

  • C 和 C++ 确实可以进行隐式类型转换(如将字符赋值给整型变量),这使得它们在某些情况下表现得像 弱类型语言。但整体上它们仍然是 强类型语言,因为在大多数情况下,它们要求显式地进行类型转换,且类型检查比较严格。

动静态语言与动静态类型的区分

动静态语言:是指运行时代码结构是否可以改动
动静态类型:是检查数据类型的时机是在运行时还是运行前
强弱类型:是指数据类型是否可以改变。

3. Go 语言面向对象特性与 Java 对比

1. 继承

Java

Java 继承是通过父类和子类的关系来实现,子类通过 extends 关键字继承父类后,子类拥有父类所有非 private 的属性和方法。

区别(重载 vs 重写)

  • 重载(Overloading):是方法名相同,但参数不同(包括参数类型、个数、顺序)。这发生在同一个类中。
  • 重写(Overriding):是子类重新定义父类的某个方法,方法签名(名称、参数类型、返回值类型)必须相同。
Go

Go 中没有显式的继承机制,但可以通过 组合(embedding)来实现类似继承的功能。在 Go 中,struct 可以嵌入其他 struct,嵌入的 struct 的字段和方法可以直接访问(interface也可以继承,但大多数情况下,interface用做多态)。

示例代码

package mainimport "fmt"type People struct {Name stringAge  int
}type Student struct {People  // 结构体 People 的嵌入Score int
}func main() {var lcc Studentlcc.People.Age = 18lcc.Name = "lichuachua"lcc.Score = 100fmt.Println(lcc) // {{lichuachua 18} 100}
}

2. 封装

Java

Java 通过 publicprivateprotected 控制对类的成员的访问权限,实现封装。

Go

Go 通过首字母是否大写来判断访问权限:

  • 首字母大写为 public,可以被其他包访问。
  • 首字母小写为 private,它是私有的,只能在同一包内访问。

3. 多态

Java

Java 的多态通过继承、重写和父类引用指向子类对象实现。

  • 继承(或接口实现):使得子类继承或实现父类/接口的行为。
  • 方法重写:允许子类提供特定的行为,重写父类的行为。
  • 父类引用指向子类对象:通过父类或接口引用指向子类对象,动态调用子类的方法,从而实现多态性。
Go

Go通过接口(interface) 来实现多态,结构体实现接口的所有方法,就可以认为它实现了该接口。这使得 Go 的多态更加灵活和松耦合。
Go 中没有类的概念,只有接口(interface)和结构体(struct)。一个结构体只要实现了接口中的所有方法,就可以被认为实现了该接口,这与 Java 的 implements 关键字不同。

示例代码

package mainimport "fmt"type Animals interface {Say()
}type Dog struct{}
type Cat struct{}func (d Dog) Say() {fmt.Println("wangwang")
}func (c Cat) Say() {fmt.Println("miaomiao")
}func main() {var d Dogd.Say()  // 输出:wangwangvar c Catc.Say()  // 输出:miaomiao// 使用接口变量,可以接受任何实现了 Say() 方法的类型var a Animalsa = da.Say()  // 输出:wangwanga = ca.Say()  // 输出:miaomiao
}

说明

  • 通过接口 Animals 和实现了 Say() 方法的 DogCat 类型,Go 实现了多态的特性。在 main 函数中,接口类型的变量 a 可以存储任何实现了 Say() 方法的类型(如 DogCat)。

结论

  • 继承:Go 使用组合(embedding)来实现类似继承的效果,Java 使用类继承。Go 中没有直接的继承机制。
  • 封装:Java 使用访问修饰符控制访问权限,Go 使用首字母大小写来控制访问权限。
  • 多态:Java 通过继承、重写和父类引用来实现多态,Go 通过接口(interface)来实现多态,Go 的多态更加灵活,没有显式的 implements 关键字。

GOROOT、GOPATH和 Go Modules

  • GOROOT:Go 的安装目录,通常不需要修改。
  • GOPATH:Go 项目的工作目录,存放源代码、依赖和编译结果。在 Go 1.11 以后,Go Modules 的引入使得 GOPATH 变得不再是强制要求,开发者可以自由选择工作目录。
  • Go Modules:在 Go 1.11 以后成为默认的依赖管理方式,可以绕过 GOPATH,直接在任何目录下进行开发

GOROOT 和 GOPATH 的区别

属性GOROOTGOPATH
作用指定 Go 的安装目录,包括 Go 编译器和标准库。指定 Go 项目的工作目录,包含源代码、依赖包、可执行文件等。
目录结构包含 Go 编译器、标准库、工具等。包含项目代码、第三方包、编译结果等。
设置方式通常由 Go 安装自动配置。由用户设置并用于存放代码和依赖。
修改权限通常不需要修改。需要开发者根据项目需要设置并修改。
目录位置默认安装在 /usr/local/go(Linux/Mac) 或 C:\Go(Windows)。默认是 $HOME/go(Linux/Mac) 或 %USERPROFILE%\go(Windows)。
包含内容Go 的核心工具和标准库。Go 项目的源代码、第三方库、编译后的可执行文件等。

Go Modules 引入后的变化

自 Go 1.11 引入 Go Modules 后,GOPATH 的使用发生了一些变化。Go Modules 可以让你在任何目录下工作,而不再需要将项目放在 GOPATH 内。Go Modules 改变了 Go 的包管理方式,使得 GOPATH 的作用逐渐减小,Go 开发者可以脱离 GOPATH 目录来管理项目。

Go Modules 的变化
  • 不再依赖于 GOPATH:你可以将代码存放在任何地方,而不必在 GOPATH/src 中。
  • 项目级依赖管理:使用 go mod 来管理项目的依赖关系。

Go 1.16 后,Go Modules 成为了默认的依赖管理方式,GOPATH 变得不再那么重要,新的 Go 项目几乎都推荐使用 Go Modules。

示例:使用 Go Modules

在 Go Modules 模式下,你可以在任何目录下初始化项目并开始使用:

go mod init <module-name>  # 初始化 Go Modules
go mod tidy               # 下载并整理依赖

示例结构

假设你有一个基本的 Go 项目,使用 Go Modules 时,项目结构可能如下:

myproject/
├── go.mod       # Go Modules 文件,记录模块的依赖
├── main.go      # Go 源代码文件
└── go.sum       # Go Modules 校验文件

在 Go 1.11 之前,你的项目需要像这样放在 GOPATH/src 下:

GOPATH/
└── src/└── myproject/└── main.go

但随着 Go Modules 的引入,这种要求不再存在,你可以将项目放在任意目录中,只需使用 go mod init 来初始化项目。

历史文章

MySQL数据库

MySQL数据库

Redis

Redis数据库笔记合集

Golang

  1. Golang笔记——切片与数组
  2. Golang笔记——hashmap

http://www.ppmy.cn/devtools/149857.html

相关文章

【json】

JSON JSON是一种轻量级的,按照指定的格式去组织和封装数据的数据交互格式。 本质上是一个带有特定格式的字符串(py打印json时认定为str类型) 在各个编程语言中流通的数据格式&#xff0c;负责不同编程语言中的数据传递和交互,类似于计算机普通话 python与json关系及相互转换…

MySQL 视图 存储过程与存储函数

第十四章_视图、第十五章 _存储过程与存储函数 1.常见的数据库对象 1. 表&#xff08;Table&#xff09; 用于存储结构化数据的基本对象&#xff0c;由行&#xff08;记录&#xff09;和列&#xff08;字段&#xff09;组成。 2. 视图&#xff08;View&#xff09; 基于一…

通过Apache、Nginx限制直接访问public下的静态文件

一、Apache 在public目录下的.htaccess文件中添加如下规则&#xff0c;来拒绝除了指定文件类型之外的所有请求 <FilesMatch "\.(?!(jpg|jpeg|png|gif|css|js|ico)$)[^.]$">Order Allow,DenyDeny from all </FilesMatch> 上述配置表示仅允许访问.jpg …

科大讯飞前端面试题及参考答案 (下)

除了 echarts 还了解其它画图工具吗? 除了 Echarts,还有不少优秀的画图工具可供选择使用。 Highcharts:它是一款功能强大且应用广泛的图表绘制工具,支持多种常见的图表类型,像柱状图、折线图、饼图、散点图等,同时也能创建较为复杂的图表,比如仪表盘图表、极坐标图等。H…

Rust 1.84.0 发布

Cargo 依赖版本选择改进 稳定了最小支持 Rust 版本&#xff08;MSRV&#xff09;感知的解析器&#xff0c;该解析器会优先选择与项目声明的 MSRV 兼容的依赖版本&#xff0c;减少了维护者支持旧工具链的工作量&#xff0c;无需手动为每个依赖选择旧版本。可以通过.cargo/config…

用Python写个待办事项网页应用

来源&#xff1a;Python大数据分析 费弗里 大家好我是费老师&#xff0c;今天要给大家分享的项目是完全基于Python编写的「待办事项」应用&#xff0c; 之所以可以完全基于Python轻松开发出这个应用&#xff0c;要归功于Python生态中非常强大的网页应用开发框架Dash&#xff…

新时期下k8s 网络插件calico 安装

1、k8s master节点初始化完毕以后一直处于notreadey状态&#xff0c;一直怀疑是安装有问题或者是初始化有问题&#xff08;当然&#xff0c;如果真有问题要先解决这些问题&#xff09;&#xff0c;经过不断探索才发现是网络插件没有安装导致的&#xff0c;根据建议安装calico插…

LLM - Llama 3 的 Pre/Post Training 阶段 Loss 以及 logits 和 logps 概念

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/145056912 Llama 3 是 Meta 公司发布的开源大型语言模型&#xff0c;包括具有 80 亿和 700 亿参数的预训练和指令微调的语言模型&#xff0c;支持…