【Golang基础】基础知识(上)

Go

本文旨在作者快速介入Go语言学习,故仅保留最基础的部分,存在部分知识点缺失,且相对较为粗略,还请有意阅读者谅解,可自行查阅资料补齐其余知识点,日后作者有计划对相应内容进行补充。

1. Go语言介绍

Go语言 全称Golang,由Google在2009年发布。

  • 种类:静态强类型编译型并发型

  • 语法:C语言高性能 (语法类似) + Python/Javscript开发效率 (写法类似)

  • 应用:云计算微服务分布式系统

  • 特点

    • 便捷并发(Goroutine)

    • 无视语言性跨平台编译(Windows可直接编译Linux可执行文件)

    • 标准库强大 Web友好

    • 静态类型语言

  • 经典项目

    • Docker:用于快速构建、测试和部署的应用程序
    • Not Simple Queue (NSQ):高可用、分布式、实时的消息队列
    • Prometheus:开源的监控、告警系统
    • CockroachDB:分布式关系型数据库

使用Go语言的公司:Google、UBER、NETFLIX、FaceBook、哔哩哔哩、字节跳动......

2. 环境安装

2.1 Windows安装

Go语言官网 Go语言国内下载网

Go语言环境配置

从官网下载并且完成安装

环境变量位于"计算机-属性/高级系统设置-环境变量"

在系统变量中path添加bin目录以及go.exe所在目录即可完成配置。

2.2 Linux安装

利用命令下载并解压

shell 复制代码
cd /opt
wget  https://studygolang.com/dl/golang/go1.21.3.linux-amd64.tar.gz
tar -xcf go1.21.3.linux-amd64.tar.gz

编辑环境变量(将bin目录添加到环境变量)

shell 复制代码
vim /etc/profile

# 在文件后追加以下内容
export GOPROXY=httlps://goproxy.cn
export GOROOT=/opt/go
export PATH=$PATH:$GOROOT/bin
export GOPATH=/opt/go/pkg
export PATH=$PATH:$GOPATH/bin

#  退出并保存,刷新环境变量
source /etc/profile

利用``go version`检查安装是否成功

2.3 开发工具选择

3. 变量定义

Go语言的变量声明使用var关键字,这一点与Javascript比较类似。

3.1 先声明后赋值

go 复制代码
package main

import "fmt"

func main() {
    // 先声明(必须指定类型)
    var name string
    // 再赋值(必须使用 declared and not used: name)
    name = "XXUU"
    fmt.Println(name)
}

3.2 声明并赋值

go 复制代码
package main

import "fmt"

func main() {
    // 声明并赋值
    var name string = "XXUU"
    fmt.Println(name)
}

如果使用IDE,类型名称会变成昵称灰色,这意味着我们这里可以省略,即基本数据类型可以自动匹配:

go 复制代码
package main

import "fmt"

func main() {
    // 声明并赋值(允许自动匹配类型)
    var name = "XXUU"
    fmt.Println(name)
}

3.3 方法内变量赋值简写

全局变量不支持变量赋值简写

go 复制代码
package main

import "fmt"

// 错误定义方法
// age1 := 13

局部变量支持变量赋值简写

go 复制代码
package main

import "fmt"

func main() {
    // 声明并赋值 短声名符号
    name := "XXUU"
    fmt.Println(name)
}

3.4 全局变量/常量

go 复制代码
package main

import "fmt"

var age = 12

var (
    s1 string = "1"
    s2 string = "2"
)

const NAME = "myName"

const (
    GENDER = "MAN!"
    PHONE = "000-88888888"
)

3.5 全局变量/常量跨包访问

另一包中定义常量:

go 复制代码
package pkg

const PkgConst1 string = "ABCDE"
const pkgConst2 string = "ABFFE"

在main中访问,只能访问到首字母为大写的变量(类似Java语言中public关键字与Python语言中前缀没有下划线):

go 复制代码
package main

import (
    "fmt"
    "new_demo/pkg"
)


func main() {
    // 这样就可以跨包访问变量了
    fmt.Println(pkg.PkgConst1);
}

4. 输入输出

4.1 输出

4.1.1 普通输出

普通输出意味着不换行,该方法基本等同于Java的System.out.print()和Python的print()

go 复制代码
package main

import "fmt"

func main()  { 
    fmt.Print("你好", "AA")
    fmt.Print("你好", "BB")
}
4.1.2 跨行输出

跨行输出意味着伴随着换行输出,该方法基本等同于Java的System.out.println()和Python的print(..., "\n")

go 复制代码
package main

import "fmt"

func main()  { 
    fmt.Println("你好", "AA")
    fmt.Println("你好", "BB")
}
4.1.3 格式化输出

跨行输出意味着伴随着换行输出,该方法基本等同于Java的System.out.printf()、Python的print(f'{[定义变量]} {[定义变量]}')/print("%s %d" % ("a", 12))/print("{name} {age}".format(name="a", age=12)))

go 复制代码
package main

import "fmt"

func main()  { 
    fmt.Printf("%v\n", "你好")             // 可以作为任何值打印
    fmt.Printf("%v %T\n", "大大", "小小")  // 输出打印类型
    fmt.Printf("%d\n", 3)                 // 适用于输出整数
    fmt.Printf("%.2f\n", 3.1415926)       // 适用于输出小数
    fmt.Printf("%s\n", "大家好") 			// 适用于输出字符串
    fmt.Printf("%#v\n", "")	 			  // 适用于输出空字符串
}

4.2 输入

与其他语言类似,Go语言同样提供控制台信息输入的功能。

go 复制代码
package main

import "fmt"

func main()  { 
    fmt.Println("请输入你的信息:")
    var info string
    fmt.Scan(&info)
    fmt.Println(info)
}
go 复制代码
package main

import "fmt"

func main()  { 
    fmt.Println("请输入你的信息:")
    var info string
    n, err := fmt.Scan(&info)
    fmt.Println(info)
}
go 复制代码
package main

import "fmt"

func main()  { 
    fmt.Println("请输入你的信息:")
    var info int
    n, err := fmt.Scan(&info)
    fmt.Println(n, err, info)
}

5. 基本数据类型

Go语言有5种基本数据类型:

  • int:整数型
  • float:浮点数型
  • string:字符型
  • bool:布尔型
  • complex:复数型

5.1 整数型

Go语言支持的整数类型如下:

go 复制代码
package main

import "fmt"

func main()  { 
	var n1 uint8 = 2
	var n2 uint16 = 2
	var n3 uint32 = 2
	var n4 uint64 = 2
	var n5 uint = 2
	var n6 int8 = 2
	var n7 int16 = 2
	var n8 int32 = 2
	var n9 int64 = 2
	var n10 int = 2
}

总结:

  • 默认整数为int,不赋值int默认为0
  • 带u为无符号,仅限正整数
  • 无u为有符号,绝对值范围为带u数½(其中一位被表示正负 迫使一半范围转向负轴)
  • 后面数字为二进制位数
  • uint8 还有一个别名为byte(1byte=8bit)
  • int类型大小取决于所使用的平台

5.2 浮点数型

Go语言支持的浮点数类型:float32 和 float64(其他语言的Double)

  • float32的浮点数最大范围为3.4e38,可以使用常量定义:math.MaxFloat32
  • float64的浮点数最大范围为1.8e308,可以使用常量定义:math.MaxFloat64

默认浮点数为float64

go 复制代码
package main

import "fmt" 

func main() {
    var f1 = 3.1415926
}

5.3 字符型

5.3.1 字符

字符赋值为单引号

两个类型:byte(单字节字符)和 rune(多字节字符)

go 复制代码
package main

import "fmt" 

func main() {
    var c1 = 'a'
    var c2 = 97
    
    fmt.Println(c1)
    fmt.Println(c2)
    
    fmt.Printf("%c %c\n", c1, c2)
    
    var r1 rune = '中'
}
  • Go中字符本质是一个整数,直接输出为Unicode UTF-8编码的码值
  • 可以直接赋值数字,将其格式化为对应字符
  • 故可将其视作数字进行运算
5.3.2 字符串

字符串赋值为双引号,字符串类型变量默认为"",即空字符串。

字符串类型

go 复制代码
package main

import "fmt" 

func main() {
    var str string = "你好"
    var str2 = "你好"
    fmt.Println(str)
}

转义字符

go 复制代码
package main

import "fmt" 

func main() { 
    fmt.Printf("%c\n", r1)
    fmt.Println("'单引号可以直接括住'")
    fmt.Println("而斜线\\双引号则需要进行转义\"")
    fmt.Println("制表符\t换行符\n回到行首\r也需要进行转义\"")
}

多行字符串

go 复制代码
package main

import "fmt" 

func main() { 
    var str = `今天
    天气
    真好
    `
    fmt.Println(str)
}

5.4 布尔型

布尔类型只有 true(真) 和 false(假) 两个值

  • 布尔类型变量默认为false
  • Go语言中布尔类型不允许由数字转换,同时也不允许其直接参与运算
go 复制代码
package main

import "fmt" 

func main() { 
    var b bool = true 
    fmt.Println(b)
}

5.5 复数型

复数型用complex关键字定义,其常用于数学计算。

go 复制代码
package main

import (
    "fmt"
)

func main() {
    // 获取实部虚部
    c := complex(3, 4) 		// 创建一个复数 3 + 4i
    fmt.Println(real(c))	 // 输出: 3.0
    fmt.Println(imag(c))	 // 输出: 4.0
}

6. 容器

6.1 数组

数组标准定义

go 复制代码
package main

import "fmt"

func main() {
	// 定义数组
	var list [3]string = [3]string{"A", "B", "C"}
	fmt.Println(list)
	fmt.Println(list[0])	// 索引取值
}

同样,数组可以省略前面的类型声明

go 复制代码
// 定义数组
var list [3]string = [3]string{"A", "B", "C"}
var list = [3]string{"A", "B", "C"}

Go语言中不支持负向索引(如Python中arr[-1]实际上等同于arr[len(arr) - 1]

Go语言中数组不支持更改其长度,所以推出了"可变数组"(Java中utils.list以及Python中的list),即可变数组。

go 复制代码
package main

import "fmt"

func main() {
	// 定义数组
	var arr [3]string
	arr = [3]string{"A", "B", "C"}
	fmt.Println(arr)

    // 定义可变数组(这种定义方式看起来像是未指定长度的数组)
	var list []string
	list = append(list, "A")
	list = append(list, "B")
	fmt.Println(list)
}

数组非nil初始化方法

go 复制代码
package main

import "fmt"

func main() {
    // 1
    var list []string = []string{}
    // 2
    var list = []string{}
    // 3
    list := []string{}
	// 4
    var list []string
    list = make([]string, 0)  // 0表示指定长度
}

数组切片及排序

go 复制代码
package main

import "fmt"

func main() {
    var list = []int{0, 1, 2}
	var slice = list[:]
	fmt.Println(slice)
	var slice2 = list[1:]
	fmt.Println(slice2)
}

数组排序

go 复制代码
package main

import (
	"fmt"
	"sort"
)

func main() {
	var list = []int{1, 0, 2}
	fmt.Println("原数组:", list)
	sort.Ints(list)
	fmt.Println("升序数组:", list)
	sort.Sort(sort.Reverse(sort.IntSlice(list)))
	fmt.Println("降序数组:", list)
}

利用切片功能删除元素

go 复制代码
var list = []int{1, 0, 2}
fmt.Println("原数组:", list)
sort.Ints(list)
fmt.Println("升序数组:", list)
sort.Sort(sort.Reverse(sort.IntSlice(list)))
fmt.Println("降序数组:", list)

list = append(list[:1], list[2:]...)
fmt.Println("降序后数组删除元素:", list)

6.2 映射

映射标准定义

go 复制代码
package main

import "fmt"

func main() {
	var userMap map[int]string = map[int]string{
		0: "用户A",
		1: "用户B",
	}
	fmt.Println(userMap)
}

同样,映射可以省略前面的类型声明

go 复制代码
package main

import "fmt"

func main() {
	var userMap = map[int]string{
		0: "用户A",
		1: "用户B",
	}
	fmt.Println(userMap)
}

根据key拿取映射中的value:

go 复制代码
import "fmt"

func main() {
	var userMap map[string]string = map[string]string{
		"A0": "用户A",
		"A1": "用户B",
	}
	fmt.Println(userMap["A0"])
}

通过双值接收,判断是定义空串还是不存在的默认空串。

go 复制代码
package main

import "fmt"

func main() {
	var userMap map[string]string = map[string]string{
		"A0": "用户A",
		"A1": "用户B",
		"A2": "",
	}
	fmt.Println(userMap["A0"])
	fmt.Printf("%#v\n", userMap["A2"])
	fmt.Printf("%#v\n", userMap["A3"])
	value2, ok2 := userMap["A2"]
	value3, ok3 := userMap["A3"]
	fmt.Println(value2, ok2)
	fmt.Println(value3, ok3)
}

可以使用delete方法删除元素

go 复制代码
delete(userMap, "A2")

映射类型必须初始化,两种初始化方法

go 复制代码
var myMap = map[string]string{}
var myMap = make(map[string]string)

映射默认线程不安全,这个后面会提到。

7. 语句控制

7.1 逻辑控制(IF & SWTICH)

7.1.1 IF-ELSEIF-ELSE
go 复制代码
package main

import "fmt" 

func main() { 
    var age = 21
    if age <= 0 {
        fmt.Println("未出生")
    } else if age > 0 && age <= 18 {
        fmt.Println("未成年")
    } else if age > 19 && age <= 35 {
        fmt.Println("青年")
    } else {
        fmt.Println("中年及以上")
	}
}
7.1.2 SWTICH-CASE

Go语言的 SWTICH-CASE 基本与其他语言无异,唯一的区别在于无需break,如需穿透执行可使用fallthrough关键字。

数值判断

go 复制代码
package main

import "fmt" 

func main() { 
    var age = 21
    switch {
    case age <= 0:
        fmt.Println("未出生")
    case age <= 18:
        fmt.Println("未成年")
    case age <= 35:
        fmt.Println("青年")
    default:
        fmt.Println("中年及以上")
    }
}

类型判断

go 复制代码
package main

import "fmt"

func main() {
    var x interface{}

	switch i := x.(type) {
	case nil:
		fmt.Printf("类型 :%T", i)
	case int:
		fmt.Printf("int 型")
	case float64:
		fmt.Printf("float64 型")
	case func(int) float64:
		fmt.Printf("func(int) 型")
	case bool, string:
		fmt.Printf("bool / string 型")
	default:
		fmt.Printf("未知型")
	}
}

判断穿透

go 复制代码
package main

import "fmt"

func main() {
	var num = 2
    
    switch num {
    case 0:
            fmt.Println("1")
            fallthrough
    case 1:
            fmt.Println("2")
            fallthrough
    case 2:
            fmt.Println("3")
            fallthrough
    case 3:
            fmt.Println("4")
        	fallthrough
    case 4:
            fmt.Println("5")
            fallthrough
    default:
            fmt.Println("6")
    }
}

7.2 循环控制(FOR)

7.2.1 计数循环

以求和为例:

go 复制代码
package main

import "fmt" 

func main() { 
    var sum = 0
    for i := 0; i <= 100; i++ {
        sum += i
    }
    fmt.Println(sum)
}
7.2.2 类while循环

Go语言中没有while循环,但是可以利用for循环执行相同的功能:

go 复制代码
package main

import "fmt"

func main() {
    var a = 1
	for a < 10 {
		a++
		fmt.Println(a)
	}
}

无限循环

go 复制代码
package main

import "fmt"

func main() {
    for true  {
        fmt.Printf("这是无限循环。\n");
    }
}
7.2.3 range 循环

Go语言的循环控制允许使用range进行,这一点与Python比较类似,形式上类似range,但用法更接近 迭代器(iterator),Java也有类似的 功能实现。

go 复制代码
package main

import "fmt"

func main() {
    var list = []string{"你好", "我是ABC"}
    for index, item := range list {
        fmt.Println(index, item)
    }
}
7.2.4 continue和break

功能与其他语言类似:

  • continue见了换路绕开(后面不执行 进行下一次循环)
  • break见了再也不见(退出最近的内层循环)
  • goto跳转到被标记的语句(一般不用)

8. 函数

8.1 函数介绍

Go语言函数关键字为func,并支持函数嵌套,基本的函数结构如下:

go 复制代码
type Student struct {
    Name string
    Age int
}

func ([方法所属结构体])funcName([输入参数及类型]) ([返回参数类型])([嵌套返回参数类型])  {
    // 执行结构
}

当函数前几项类型一致时,可以仅保留最后一项的参数类型:

go 复制代码
// 初始形式
func funcName1(param1 string, param2 string, param3 string, param4 int) string  {
    return "ABC"
}
// 简化为如下形式
func funcName2(param1, param2, param3 string, param4 int) string  {
    return "ABC"
}

定义一个用于求和的方法:

go 复制代码
package main

import "fmt"

func main() {
   /* 定义局部变量 */
   var a int = 100
   var b int = 200
   var result int

   /* 调用函数并返回求和值 */
   result = sum(a, b)

   fmt.Printf( "二者求和为 : %d\n", result )
}

func sum(num1, num2 int) int {
   return num1 + num2
}

函数也可以返回多个值,并直接进行解包,这一点与Python语言类似。

go 复制代码
package main

import "fmt"

func main() {
   vA, vB := twoReturn()
   fmt.Println( vA, vB )
}

func twoReturn() (string, string) {
   return "ABC", "BCD"
}

闭包

设计一个函数,先传一个参数表示延时延缓,后面再次将求和函数整体传参。

go 复制代码
/* 函数返回数组/切片内数字求和 */
func awaitAdd(awaitTime int) func(...int) int {
    time.Sleep(time.Duration(awaitTime) * time.Second)
    return func(numList ...int) int {
        var sum int
        for _, i2 := range numList {
            sum += i2;
        }
		return sum
    }
}

调用检查其延缓效果

go 复制代码
package main

import "fmt"

func main() {
    t1 := time.Now()
    sum := awaitAdd(2)(1,2,3)
    subTime := time.Since(t1)
    fmt.Println(sum, subTime)
}

8.2 值传递 & 引用传递

函数值传递

go 复制代码
package main

import "fmt"

func main() {
	str := "ABC"
	fmt.Printf("原有变量:%p\n", &str)
	str2 := funcDefine(str)
	fmt.Printf("返回变量:%p\n", &str2)
}

func funcDefine(str string) string {
	fmt.Printf("方法内变量:%p\n", &str)
	return str
}

函数引用传递

go 复制代码
package main

import "fmt"

func main() {
	str := "ABC"
	fmt.Printf("原有变量:%p\n", &str)
	str2 := funcDefine(&str)
	fmt.Printf("返回变量:%p\n", &str2)
}

func funcDefine(str *string) string {
	fmt.Printf("方法内变量:%p\n", str)
	return *str
}

8.3 init函数 & defer函数

8.3.1 init函数

init()函数可用于初始化,其执行先于main()函数,自动被调用,init()函数不能作为参数传入,且无法传参返回(无意义)。

Go文件可以有多个init函数,并且依照先后顺序执行,形式上可视作完整init()分散在各地的代码碎片,这种灵活的组合方式提升了代码可读性。

go 复制代码
package main

import (
	"fmt"
)
// main方法后的init
func init() {
	fmt.Println("INIT_1")
}

// main方法后的init
func init() {
	fmt.Println("INIT_2")
}

func main() {
	fmt.Println("main")
}

// main方法后的init
func init() {
	fmt.Println("INIT_3")
}

为更好展现运行顺序,我们为全局变量赋值

go 复制代码
package main

import (
	"fmt"
)

var number int

func init() {
	number = 100
	fmt.Println("INIT_1")
}

func init() {
	fmt.Println("INIT_2")
	fmt.Println(number)
}

func main() {
	number = 50
	fmt.Println("main")
	fmt.Println(number)
}

func init() {
	number = 80
	fmt.Println("INIT_3")
	fmt.Println(number)
}
8.3.2 defer函数

defer()函数在return之前被调用,多个defer按照先进后出(栈)的方式执行,举例return近的的defer优先被执行。

但defer使用方法内的变量仍需定义在defer之前,defer可用于进行资源清理。

go 复制代码
package main

import "fmt"

func main() {
	// 定义匿名函数及函数体并调用(最后一个括号表示调用)
	defer func() {
		fmt.Println("DEFER_4")
	}()
	// 调用显式函数(最后一个括号表示调用)
	defer fmt.Println("DEFER_3")
	defer deferUse2()
	defer deferUse1()
	return
}

func deferUse1() {
	fmt.Println("DEFER_1")
}

func deferUse2() {
	fmt.Println("DEFER_2")
}

9. 结构体

9.1 结构体介绍

go 复制代码
type 结构体名称 struct {
    名称 类型 //成员表属性
}

定义结构体并创建实例

go 复制代码
package main

import "fmt"

// Student 定义结构体
type Student struct {
    Name string 
    Age  int
}

func main() {
	stu1 := Student{Name: "学生A", Age: 12}
	fmt.Println(stu1)
}

定义实例方法并调用

go 复制代码
package main

import "fmt"

type Student struct {
	Name string
	Age  int
}

func (s Student) study() {
	fmt.Println(s.Name, " 正在学习")
}

func main() {
	stu1 := Student{Name: "学生A", Age: 12}
	stu1.study()

	stu2 := Student{Name: "学生R", Age: 16}
	stu2.study()
}

为结构体增加附属方法:【对比区别】

go 复制代码
func (s Student)getName() (string) {
    return s.Name;
}

func (s *Student)getRealName() (string){
    return s.Name;
}

9.2 继承 & 结构体指针

Go语言的继承形式上更接近其他面向对象语的组合,因为形式上更接近组合,故将班级作为学生的属性定义。

go 复制代码
package main

import "fmt"

type Class struct {
	Name string
}

type Student struct {
	Class
	Name string
	Age  int
}

func (s Student) info() {
	fmt.Printf("%s 所属班级:%s\n", s.Name, s.Class)
}

func main() {
	class1 := Class{Name: "三年一班"}

	stu1 := Student{Name: "学生A", Age: 12, Class: class1}
	stu1.info()

	stu2 := Student{Name: "学生R", Age: 16, Class: class1}
	stu2.info()
}

以上示例中,Class作为实例输出了(花括号),这不符合我们想要输出的结果。

Go语言也支持直接选择"属性内属性",示例如下。

go 复制代码
package main

import "fmt"

type Class struct {
	Name      string
	ClassName string
}

type Student struct {
	Class
	Name string
	Age  int
}

func (s Student) info() {
	fmt.Printf("%s 所属班级:%s\n", s.Name, s.Class.Name)
	fmt.Printf("%s 所属班级:%s\n", s.Name, s.ClassName)
}

func main() {
	class1 := Class{Name: "三年一班", ClassName: "三年一班"}

	stu1 := Student{Name: "学生A", Age: 12, Class: class1}
	stu1.info()

	stu2 := Student{Name: "学生R", Age: 16, Class: class1}
	stu2.info()
}

而通过方法更改对象属性则需要传递其引用,否则将等效于在方法内临时拷贝了一份副本,原始结构体实例属性无法更改成功。

go 复制代码
package main

import "fmt"

type Class struct {
	Name      string
	ClassName string
}

type Student struct {
	Class
	Name string
	Age  int
}

func (s Student) setName() {
	s.Name += " 学霸"
	fmt.Println(s.Name)
}

func (s Student) info() {
	fmt.Printf("%s 所属班级:%s\n", s.Name, s.Class.Name)
	fmt.Printf("%s 所属班级:%s\n", s.Name, s.ClassName)
}

func main() {
	class1 := Class{Name: "三年一班", ClassName: "三年一班"}

	stu1 := Student{Name: "学生A", Age: 12, Class: class1}
	stu1.info()

	stu2 := Student{Name: "学生R", Age: 16, Class: class1}
	stu2.setName()
	stu2.info()
}

只需要将引用传入,便可解决此问题。

go 复制代码
func (s *Student) setName() {
	s.Name += " 学霸"
	fmt.Println(s.Name)
}

9.3 结构体tag

在Go语言中,json:"..."是结构体字段上的‌**结构体标签(Tag)‌,用于控制encoding/json包在 序列化(Marshal)反序列化(Unmarshal)**过程中如何映射字段与JSON键。‌

结构体tag常见用途:

  • 别名
  • 隐藏
go 复制代码
package main

import (
	"encoding/json"
	"fmt"
)

type Student struct {
	Name       string
	Age        int
	SecretInfo string
}

func main() {
	stu := Student{Name: "学生R", Age: 16, SecretInfo: "秘密基地在冥王星北极点冰冠下,有我数万年前星际旅行时留下的秘密宝藏。"}
	byteData, _ := json.Marshal(stu)
	fmt.Println(string(byteData))
}
go 复制代码
type Student struct {
	Name       string `json:"name"`
	Age        int    `json:"age"`
	SecretInfo string `json:"-"`
}

防止属性为默认值时输出的选项。

go 复制代码
package main

import (
	"encoding/json"
	"fmt"
)

type Student struct {
	Name       string `json:"name,omitempty"`
	Age        int    `json:"age,omitempty"`
	SecretInfo string `json:"-"`
}

func main() {
	stu := Student{Name: "学生R", Age: 16, SecretInfo: "秘密基地在冥王星北极点冰冠下,有我数万年前星际旅行时留下的秘密宝藏。"}
	byteData, _ := json.Marshal(stu)
	fmt.Println(string(byteData))
}

10. 自定义数据类型 & 接口

10.1 自定义数据类型

Go语言中,可以利用type关键字定义新的类型,可定义类型包括基本数据类型、结构体、函数等。利用自定义数据类型可以更好地封装数据,使代码易维护。

go 复制代码
// 网络动态码返回函数
func webServer(name string) int {
   if name == "1" {
      return 1001
   }
   if name == "2" {
      return 1002
   }
   return 0
}

这样就会出现经典的"魔鬼数字"问题,且代码非常难以维护,一种好的解决方法如下。

go 复制代码
package main

// 自定义类型
type Code int

// 自定义类型则可以自定义方法
func (c Code) getMsg() string {
	switch c {
	case SuccessCode:
		return "成功"
	case ServiceErrCode:
		return "服务错误"
	case NetworkErrCode:
		return "网络错误"
	default:
		return "其他"

	}
}

// 自定义类型则可以自定义方法
func (c Code) getCodeWithMsg() (code Code, msg string) {
	return c, c.getMsg()
}

const (
	SuccessCode    Code = 0
	ServiceErrCode Code = 1001
	NetworkErrCode Code = 1002
)

// 网络动态码返回函数
func webServer(name string) (code Code, msg string) {
	if name == "1" {
		return ServiceErrCode.getCodeWithMsg()
	}
	if name == "2" {
		return NetworkErrCode.getCodeWithMsg()
	}
	return SuccessCode.getCodeWithMsg()
}

// main方法
func main() {
	webServer("1")
}

10.2 类型别名

类似自定义类型,但本质上是原始类型的"小名",不用转换类型。

go 复制代码
package main

import "fmt"

type MyCode int
type MyAlias = int

const MyCodeInstance MyCode = 1
const MyAliasCodeInstance MyAlias = 1

func main() {
	fmt.Printf("%v, %T\n", MyCodeInstance, MyCodeInstance)
	fmt.Printf("%v, %T\n", MyAliasCodeInstance, MyAliasCodeInstance)
}

10.3 接口

接口是一组见包含方法名、参数、返回值的未具体实现的方法的集合。

go 复制代码
package main

import "fmt"

// 接口是  值+原始类型
type SingInterface interface {
	Sing()
	GetName() string
}

type Chicken struct {
	Name string
}

func (c Chicken) Sing() {
	fmt.Println(c.Name, "在唱歌")
}
func (c Chicken) GetName() string {
	return c.Name
}

type Cat struct {
	Name string
}

func (c Cat) Sing() {
	fmt.Println(c.Name, "在唱歌")
}

func (c Cat) GetName() string {
	return c.Name
}

// sing
func sing(c SingInterface) {
	// 类型断言
	//ch, ok := c.(Chicken)

	c.Sing()
	fmt.Println(c.GetName())

	// 一般做法
	switch server := c.(type) {
	case Chicken:
		fmt.Println(server)
	case Cat:
		fmt.Println(server)
	default:
		fmt.Println("其他")
	}
}

空接口等同于任意类型:

go 复制代码
// 空接口:可以接受任何类型
type EmptyInterface interface{}  // type any interface{}

// 简介写法:匿名空接口
func Print(val interface{}) {
	fmt.Println(val)
}
func PrintSame(val any) {
	fmt.Println(val)
}
相关推荐
黎雁·泠崖2 小时前
C 语言联合体与枚举:共用内存 + 常量枚举 + 实战
c语言·开发语言·python
yousuotu2 小时前
基于Python实现亚马逊销售数据分析与预测
开发语言·python·数据分析
张登杰踩2 小时前
django后台管理配置教程
后端·python·django
无知就要求知2 小时前
golang实现ftp功能简单又实用
java·前端·golang
卜锦元2 小时前
Golang后端性能优化手册(第四章:异步处理与消息队列)
开发语言·后端·docker·容器·性能优化·golang·团队开发
lsx2024062 小时前
HTML 脚本:深入解析与实际应用
开发语言
lkbhua莱克瓦242 小时前
基础-SQL的通用语法、分类以及DDL
开发语言·数据库·笔记·sql·mysql·ddl
IT艺术家-rookie2 小时前
golang--性能分析pprof
golang
十五年专注C++开发2 小时前
librf: 一款基于 C++11/14/17 标准实现的轻量级无栈协程库
开发语言·c++·分布式·异步io