Go语言新手村:轻松理解变量、常量和枚举用法

发布于:2025-07-30 ⋅ 阅读:(17) ⋅ 点赞:(0)

Go语言新手村:轻松理解变量、常量和枚举用法

1. 介绍

1.1 发展

2007年,Google开始着手开发Go语言。

2009年11月,Google发布了Go语言的第一个稳定版本,即Go 1.0。这标志着Go语言正式面世,并开始逐渐受到广泛关注和应用。

2011年5月,Go 1.0.2发布,这是Go语言的第一个正式稳定版本,其中包括了一些重要的bug修复和改进。

2018年8月,Go 1.11发布,引入了模块支持、WebAssembly支持、延迟函数调用等特性,这些特性使得Go语言在项目管理、跨平台应用和异步编程等方面有了更加强大的能力。

2021年2月,Go 1.16发布,加入了对模块和错误处理的改进、垃圾回收器的性能提升等特性,这些改进使得Go语言在性能、可靠性和开发体验等方面有了更大的提升。

1.2 特点

  • 高效性:Go语言具有高效的编译速度、并发处理能力和内存管理机制,适合处理高并发、高吞吐量的场景。
  • 并发性:Go语言提供了原生的并发编程模型,通过协程(goroutine)和通道(channel)实现了轻量级的线程管理和通信,使得编写并发程序变得更加容易。
  • 安全性:Go语言内置了安全性特性,例如强类型、空指针检查、内存自动管理等,减少了安全漏洞和错误的可能性。
  • 跨平台:Go语言支持多种操作系统和CPU架构,可以轻松地在不同平台之间进行开发和部署。

2. 基础概念

2.1 内建变量类型

Go 语言中内建变量类型分为三种,分别是布尔类型、数值类型和字符串类型

  1. 布尔类型:布尔类型的关键字是bool,取值范围是true 或 false,其中默认值为false
  2. 数值类型:Go 语言中数值类型很多,包括整数、浮点数、复数。这三种数值类型还有不同的长度。其中整型不仅有不同长度,还分为有符号整数(int)和无符号整数(uint)
类型 描述 大小 范围
int 平台相关有符号整数 32位或64位 根据平台变化
int8 8位有符号整数 8位 -128 到 127
int16 16位有符号整数 16位 -32768 到 32767
int32 32位有符号整数 32位 -2147483648 到 2147483647
int64 64位有符号整数 64位 -9223372036854775808 到 9223372036854775807
uint 平台相关无符号整数 32位或64位 根据平台变化
uint8 (byte) 8位无符号整数 8位 0 到 255
uint16 16位无符号整数 16位 0 到 65535
uint32 32位无符号整数 32位 0 到 4294967295
uint64 64位无符号整数 64位 0 到 18446744073709551615
uintptr 存储指针的整数 平台相关 足够容纳指针值
rune Unicode码点 32位 等同于 int32
float32 单精度浮点数 32位 指数范围:-126到+127
float64 双精度浮点数 64位 指数范围:-1022到+1023
complex64 实部虚部分别32位的复数 64位 范围同float32
complex128 实部虚部分别64位的复数 128位 范围同float64
  1. 字符串类型:关键字string,默认值为空字符串

在 Go 语言中还有两个特殊的类型,runebyte,它们本质上其实是类型别名。rune 是 int32 的别名,常用于处理文本字符,尤其是非ASCII字符;byte 是 uint8 的别名,常用于处理字节、二进制文本。

2.2 变量

Go 语言中使用 var 关键字定义变量,实例代码如下

func main() {
	var a int
	var b string
	var c bool
	// 输出结果是该类型的零值(默认值)
	fmt.Printf("a = %d, b = %q,c = %t \n", a, b, c)

	a = 100
	b = "hello"
	c = true
	// 延迟赋值
	fmt.Printf("a = %d, b = %q,c = %t \n", a, b, c)
	// a = 100, b = "hello",c = true 

	var name string = "张三"
	var age int = 18
	// 立刻赋值
	fmt.Printf("name = %s, age = %d \n", name, age)
	// name = 张三, age = 18 

	var country = "中国"
	var price = 100
	// 类型推导
	fmt.Printf("country = %s, price = %d \n", country, price)
	// country = 中国, price = 100 

	// 短变量声明
	year := 2025
	province, city := "江苏省", "苏州市"
	fmt.Printf("year = %d, province = %s, city = %s \n", year, province, city)
	// year = 2025, province = 江苏省, city = 苏州市 
  
	x, _ := anonymous()
	fmt.Println(x)
	// 李四
	_, y := anonymous()
	fmt.Println(y)
	// 20
}

func anonymous() (string, int) {
	return "李四", 20
}

在这一段代码中,展示了Go语言中的变量声明核心概念,下面来详细介绍一下。

  1. 零值初始化:Go为所有基础类型提供零值保障,避免未初始化风险
var a int     // 未显式赋值 → 默认零值:0
var b string  // 未显式赋值 → 默认零值:空字符串 ""
var c bool    // 未显式赋值 → 默认零值:false

fmt.Printf("a = %d, b = %q, c = %t \n", a, b, c) 
// 输出:a = 0, b = "", c = false
  1. 运行时动态修改值:变量在声明后随时修改变量值,但是变量类型固定后不可变更。
a = 100        // 整型赋值
b = "hello"    // 字符串赋值
c = true       // 布尔赋值

fmt.Printf("a = %d, b = %q, c = %t \n", a, b, c)
// 输出:a = 100, b = "hello", c = true
  1. 声明时赋值:在声明变量时立刻给变量赋值
var name string = "张三"  // 显式声明string类型
var age int = 18         // 显式声明int类型

fmt.Printf("name = %s, age = %d \n", name, age)
// 输出:name = 张三, age = 18
  1. 类型推导:声明时省略变量类型且立刻赋值,编译器根据赋值自动推断类型
var country = "中国"  // 自动推导为string类型
var price = 100      // 自动推导为int类型

fmt.Printf("country = %s, price = %d \n", country, price)
// 输出:country = 中国, price = 100
  1. 短变量声明:在函数内部快速声明并初始化变量的方式,并能自动推导类型
	year := 2025			// 等价于 var year int = 2025
	province, city := "江苏省", "苏州市"		// 多变量同时声明

	fmt.Printf("year = %d, province = %s, city = %s \n", year, province, city)
	// year = 2025, province = 江苏省, city = 苏州市
  1. 匿名变量:在使用多重赋值时,如果想要忽略某个值,可以使用匿名变量。 匿名变量用一个下划线_进行占位。匿名变量不占用命名空间,不会分配内存
func anonymous() (string, int) {
	return "李四", 20
}

func main {
  x, _ := anonymous()			
	fmt.Println(x)
	// 李四
	_, y := anonymous()
	fmt.Println(y)
	// 20
}

2.3 常量

常量使用 const 关键字进行声明。其基本语法与 var 声明类似。常量是在编译时就确定其值,并且在程序运行期间不可更改的标识符,所以常量在声明时必须赋值,且不可更改。

枚举

在 Go 语言中,虽然没有像其他编程语言(如 Java 或 C#)那样直接提供 enum 关键字来定义枚举类型,但可以通过使用 const 关键字结合 iota 标识符实现类似的功能。所以枚举值也可以看作一种特殊的常量类型。

iota 是 Go 语言中的一个特殊标识符,仅在 const 声明块中有效。它代表了从 0 开始的连续整数值。每当 const 声明块中出现新的一行(以换行符分隔),iota 的值就自动递增 1。这种机制非常适合用来定义枚举值。

// 定义星期几的枚举值
const (
  Sunday    = iota // 0
  Monday           // 1
  Tuesday          // 2
  Wednesday        // 3
  Thursday         // 4
  Friday           // 5
  Saturday         // 6
)

func main() {
  fmt.Println("Sunday =", Sunday)
  fmt.Println("Monday =", Monday)
  fmt.Println("Tuesday =", Tuesday)
  fmt.Println("Wednesday =", Wednesday)
  fmt.Println("Thursday =", Thursday)
  fmt.Println("Friday =", Friday)
  fmt.Println("Saturday =", Saturday)
}

除了简单的递增序列,iota 还可以与其它表达式结合使用,生成更加复杂的枚举值。

  1. 指定起始值
const (
    a = iota + 1 // 1
    b            // 2
    c            // 3
)
  1. 跳过某些值
const (
    _ = iota // 忽略第0个值
    a        // 1
    _        // 忽略第2个值
    b        // 3
)
  1. 位操作
const (
	b = 1 << (10 * iota)				// 1
	kb													// 1024
	mb													// 1048576
	gb													// 1073741824
	tb													// 1099511627776
	pb													// 1125899906842624
)

2.4 作用域

Go 也是一种静态语言,静态语言在使用变量之前需要先声明。Go 语言和常见的静态语言一样,存在全局和局部两种作用域,其中全局作用域在Go语言中也被称为包级作用域。无论是变量 (var) 还是常量 (const),它们的作用域都遵循完全相同的规则,主要取决于它们声明的位置

包级作用域 (Package-Level Scope)

  • 位置:在任何函数、方法或代码块之外的顶层声明。
  • 生命周期:从程序启动时创建,到程序结束时销毁。

局部作用域 (Local Scope)

  • 位置:在函数内部方法内部代码块内部(如 if, for, switch 语句的 {} 内部)声明。
  • 生命周期:从声明/初始化点开始,到包含它的函数、方法或代码块执行结束时终止。

接下来通过示例介绍一下包级变量的声明以及赋值,常量同理。

package main

import "fmt"

// 单独声明(使用零值)
var a int // 零值为 0

// 声明并显式赋值
var b string = "hello"

// 声明并使用类型推导
var c = true // 推导为 bool

// 使用 var() 块集中声明
var (
    name     string        // 仅声明,零值 ""
    age      int           // 仅声明,零值 0
    country  string = "中国" // 声明并赋值
    province, city = "江苏", "苏州" // 多变量声明并赋值,类型推导
)

func main() {
    // 包级变量在整个包内(包括main函数)都可访问
    fmt.Printf("a = %d, b = %s, c = %t \n", a, b, c)
    // 输出:a = 0, b = hello, c = true 

    // 可以在函数内部修改包级变量的值
    name = "张三"
    age = 18

    fmt.Printf("name = %s, age = %d, country = %s, province = %s, city = %s \n", name, age, country, province, city)
    // 输出:name = 张三, age = 18, country = 中国, province = 江苏, city = 苏州
}

这里主要介绍了四种包级变量的声明方式

在前一节中有一个短变量声明,短变量声明只能声明局部变量

方式 示例 适用场景
零值声明 var a int 需要默认值时
显式类型初始化 var b string = "hello" 明确指定类型避免歧义
类型推导 var c = true 右侧值类型明确时简化代码
分组声明 var (...) 组织相关变量,增强可读性
包级变量的导出

在 Go 语言中,包级变量是指那些在包的顶层声明的变量,即不在任何函数内部声明的变量。这些变量可以在整个包内访问,并且如果它们的名字以大写字母开头,则还可以被其他包通过导入的方式访问。这种机制是 Go 实现封装和模块化的重要部分。

假设我们有一个名为 other 的包,其中包含了一些可导出的变量。

package other

// 这是一个可导出的包级变量,因为它以大写字母开头
var ExportedVar = "This is an exported variable"

// 这个变量不可导出,因为它以小写字母开头
var unexportedVar = "This is a unexported variable"

然后,在另一个文件或项目中,你可以这样导入并使用 other 包中的可导出变量:

package main

import (
	"fmt"
	"go-learning/other"
)

func main() {

	fmt.Printf("可导出变量:%s \n", other.ExportedVar)
  // 可导出变量:This is an exported variable
  
	// 尝试访问不可导出的包级变量(会导致编译错误)
  // fmt.Println(other.unexportedVar) 
  // Error:Cannot use the unexported variable 'unexportedVar' in the current package
  	
	other.ExportedVar = "这是修改后的可导出变量"
	fmt.Printf("修改后的可导出变量:%s \n", other.ExportedVar)
  // 修改后的可导出变量:这是修改后的可导出变量
  
}
包级变量的修改

直接修改其他包中的变量虽然可行,但不推荐,因为这会导致代码的耦合性增加,难以维护。通常,我们通过函数来封装对变量的访问和修改,这样可以加入额外的逻辑(如验证、加锁等)。

package other

import "sync"

// 这是一个可导出的包级变量,因为它以大写字母开头
var ExportedVar = "This is an exported variable"

// 这个变量不可导出,因为它以小写字母开头
var unexportedVar = "This is a unexported variable"

var (
	mu sync.Mutex          	// 内部锁(不可导出)
)

// UpdateVal 安全修改配置的函数
func UpdateVal(newString string) {
	mu.Lock()
	defer mu.Unlock()
	ExportedVar = newString
}

package main

import (
	"fmt"
	"go-learning/other"
)

func main() {

	fmt.Printf("可导出变量:%s \n", other.ExportedVar)

	other.UpdateVal("这是安全修改后的可导出变量")
	fmt.Printf("安全修改后的不可导出变量:%s \n", other.ExportedVar)
  // 安全修改后的不可导出变量:这是安全修改后的可导出变量 
}

3. 小结

本文介绍了Go语言的基础类型、变量与常量的定义和作用域。讲解了包级与局部作用域的区别,以及可导出变量/常量的规则。


网站公告

今日签到

点亮在社区的每一天
去签到