目录
1 结构体
1.1 简介
Go 语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型。
结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。
1.2 定义结构体
结构体定义需要使用 type
和 struct
语句。struct
语句定义一个新的数据类型,结构体中有一个或多个成员。type 语句设定了结构体的名称。结构体的格式如下:
type struct_variable_type struct {
member definition
member definition
...
member definition
}
一旦定义了结构体类型,它就能用于变量的声明,语法格式如下:
variable_name := structure_variable_type {value1, value2...valuen}
或
variable_name := structure_variable_type { key1: value1, key2: value2..., keyn: valuen}
实例如下:
package main
import "fmt"
type Books struct {
title string
author string
subject string
book_id int
}
func main() {
// 创建一个新的结构体
fmt.Println(Books{"Go 语言", "www.test.com", "Go 语言教程", 6495407})
// 也可以使用 key => value 格式
fmt.Println(Books{title: "Go 语言", author: "www.test.com", subject: "Go 语言教程", book_id: 6495407})
// 忽略的字段为 0 或 空
fmt.Println(Books{title: "Go 语言", author: "www.test.com"})
}
1.3 声明结构体
1.3.1 new声明
在 Go 中,结构体可以通过 new
函数直接创建。new
是 Go 内置的函数,用来分配内存并返回指向该类型的指针。
new 的使用,其中 T
是结构体类型。
p := new(T)
new
为类型 T
分配一块内存(零值初始化),返回指向 T
的指针(类型为 *T
)
1.3.2 直接声明
除了使用 new
,还可以通过直接声明或者字面量初始化结构体:
var b Book
fmt.Println(b) // 输出:{ 0}
字面量初始化
b := Book{Title: "Go Programming", Author: "John Doe", Pages: 300}
nick := Person{"nick", 28, "nickli@xxx.com"}
fmt.Println(b) // 输出:{Go Programming John Doe 300}
1.3.3 与 new 的对比
方法 | 分配内存 | 返回值 | 是否为指针 |
---|---|---|---|
new | 是 | 返回指针,类型为 *T | 是 |
直接声明 | 是 | 返回值,类型为 T | 否 |
字面量初始化 | 是 | 返回值,类型为 T 或 *T(取决于语法) | 可选 |
1.3.4 new 和 & 操作符的区别
通过 &
也可以创建结构体指针,它们的行为与 new
类似,但更灵活。
b := &Book{Title: "Go Programming", Author: "John Doe", Pages: 300}
fmt.Println(b) // 输出:&{Go Programming John Doe 300}
区别:
new
只能分配内存并返回零值的结构体指针。&
可以用于字面量初始化,并返回指针,语法更简洁
1.3.5 结构体指针
可以定义指向结构体的指针类似于其他指针变量,格式如下:var struct_pointer *Books
定义的指针变量可以存储结构体变量的地址。查看结构体变量地址,可以将 &
符号放置于结构体变量前:struct_pointer = &Book1
使用结构体指针访问结构体成员,使用 .
操作符:struct_pointer.title
1.4 结构体标签
在 Go 中,结构体字段后面的反引号(`)部分是 结构体标签(Tag),用于为字段添加额外的元信息。这些标签可以在运行时通过反射(reflect 包)读取,以实现特定功能。
1.4.1 标签的语法
标签是一个字符串
,必须用反引号包裹
,一般格式是:key:"value"
,多个标签可以用空格
分隔:
type Person struct {
Name string `json:"name" xml:"name"`
Age int `json:"age" db:"age_column"`
}
1.4.2 标签的工作机制
读取标签: 使用 Go 的 reflect
包可以读取结构体的标签。
package main
import (
"fmt"
"reflect"
)
type Person struct {
Name string `json:"name" xml:"name_xml"`
Age int `json:"age"`
}
func main() {
t := reflect.TypeOf(Person{})
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
fmt.Printf("Field: %s, JSON Tag: %s, XML Tag: %s\n",
field.Name,
field.Tag.Get("json"),
field.Tag.Get("xml"),
)
}
}
输出:
Field: Name, JSON Tag: name, XML Tag: name_xml
Field: Age, JSON Tag: age, XML Tag:
1.4.3 常见用途
1.4.3.1 JSON 序列化和反序列化
标签 json:"name"
用于指定字段在 JSON
格式中的键名。常见于 encoding/json
包:
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
p := Person{Name: "Alice", Age: 30}
// 序列化为 JSON
jsonData, _ := json.Marshal(p)
fmt.Println(string(jsonData)) // 输出:{"name":"Alice","age":30}
// 反序列化 JSON
var p2 Person
json.Unmarshal([]byte(`{"name":"Bob","age":25}`), &p2)
fmt.Println(p2) // 输出:{Bob 25}
}
1.4.3.2 数据库映射
标签用于指定字段与数据库表中列名的映射,常见于 ORM
框架(如 GORM
):
type User struct {
ID int `gorm:"primaryKey"`
Name string `gorm:"column:user_name"`
}
1.4.3.3 表单处理
用于指定字段的表单名称,常见于 net/http
包的表单解析:
type LoginForm struct {
Username string `form:"username"`
Password string `form:"password"`
}
1.5 结构体作为函数参数
可以像其他数据类型一样将结构体类型作为参数传递给函数。
注意
:结构体是作为参数的值传递
package main
import "fmt"
type Books struct {
title string
author string
subject string
book_id int
}
func changeBook(book Books) {
book.title = "book1_change"
}
func main() {
var book1 Books
book1.title = "book1"
book1.author = "zuozhe"
book1.book_id = 1
changeBook(book1)
fmt.Println(book1)
}
如果想在函数里面改变结果体数据内容,需要传入指针
package main
import "fmt"
type Books struct {
title string
author string
subject string
book_id int
}
func changeBook(book *Books) {
book.title = "book1_change"
}
func main() {
var book1 Books
book1.title = "book1"
book1.author = "zuozhe"
book1.book_id = 1
changeBook(&book1)
fmt.Println(book1)
}
1.6 嵌套结构体
在结构体中嵌套结构体时使用匿名字段
可以更加简便获取内层结构体的字段,当内层结构体的字段和外层结构体的字段没有重复时可以直接获取
,如果有重复时需要加上内层结构体名
才能正常获取
这其实也是 Go 实现继承
的方式
如果结构体中的字段也是一个结构体
,那么这个结构体字段称为提升字段(Promoted fields)
,因为可以从外部结构体变量直接访问结构体类型中的字段
,就像这些字段原本属于外部结构体一样。
package main
import "fmt"
type foo struct {
field1 int
field2 string
}
type bar struct {
foo
field1 string
field3 int
}
func main() {
foobar := bar{ }
foobar.foo.field1 = 1
foobar.field2 = "hello"
foobar.field1 = "world"
foobar.field3 = 2
fmt.Printf("%v", foobar)
}
1.7 匿名
1.7.1 匿名结构体
可以定义一个没有类型名称的结构体,这种结构体叫做匿名结构体(Anonymous Structures
)
var employee struct {
firstName, lastName string
age int
}
给匿名struct类型定义方法
var cache = struct {
sync.Mutex
mapping map[string]string
}{
mapping : make(map[string]string),
}
func Lookup(key string) string {
cache.Lock() // sync.Mxter的方法
v := cache.mapping[key]
cache.Unlock()
return v
}
声明的同时进行赋值
emp3 := struct {
firstName, lastName string
age, salary int
}{
firstName: "Andreah",
lastName: "Nikola",
age: 31,
salary: 5000,
}
1.7.2 匿名字段
在创建结构体时,我们也可以只指定类型而不指定字段名
。这些字段被称为匿名字段
。
下面的代码片就创建了一个 Person,该结构体有俩个匿名字段 string 和 int。
尽管匿名字段并没有名称,默认情况下,它的数据类型
就是它的字段名称
。因此,Person 结构体拥有俩个字段,其名称分别为:string 和 int
数据类型定义,仅能存在一次,如果有两个string 或 两个int 的话会冲突
type Person struct {
string
int
}
func main() {
p := Person{"Naveen", 50}
fmt.Println(p)
}
1.8 构造函数
Go没有提供构造函数。如果一个类型的零值是不合法的,应该将该类型设置为不导出,防止在其他包中导出该类型,并且需要提供一个函数NewT(parameters)
去初始化带参数的T类型的的变量。Go中的一个约定是,应该把创建 T
类型变量的函数命名为 NewT(parameters)
。这就类似于构造器了。如果一个包只含有一种类型,按照 Go
的约定,应该把函数命名为 New(parameters)
, 而不是 NewT(parameters)
。
package employee
import (
"fmt"
)
type employee struct {
firstName string
lastName string
totalLeaves int
leavesTaken int
}
func New(firstName string, lastName string, totalLeave int, leavesTaken int) employee {
e := employee {
firstName, lastName, totalLeave, leavesTaken}
return e
}
func (e employee) LeavesRemaining() {
fmt.Printf("%s %s has %d leaves remaining", e.firstName, e.lastName, (e.totalLeaves - e.leavesTaken))
}
2 接口
2.1 简介
接口(interface
)是 Go 语言中的一种类型,用于定义行为的集合,它通过描述类型必须实现的方法,规定了类型的行为契约。
接口把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。
2.1.1 接口的特点
隐式实现:
- Go 中没有关键字显式声明某个类型实现了某个接口。
- 只要一个类型实现了接口要求的所有方法,该类型就自动被认为实现了该接口。
接口类型变量:
- 接口变量可以存储实现该接口的任意值。
- 接口变量实际上包含了两个部分:
- 动态类型:存储实际的值类型。
- 动态值:存储具体的值。
- 零值接口:接口的零值是
nil
一个未初始化的接口变量其值为nil
,且不包含任何动态类型或值。 - 空接口:定义为
interface{}
,可以表示任何类型
2.1.2 常见用法
- 多态:不同类型实现同一接口,实现多态行为。
- 解耦:通过接口定义依赖关系,降低模块之间的耦合。
- 泛化:使用空接口
interface{}
表示任意类型。
2.2 接口操作
2.2.1 接口定义
接口定义使用关键字 interface
,其中包含方法声明。
定义接口
type interface_name interface {
method_name1 [return_type]
method_name2 [return_type]
method_name3 [return_type]
...
method_namen [return_type]
}
定义结构体
type struct_name struct {
/* variables */
}
实现接口方法
func (struct_name_variable struct_name) method_name1() [return_type] {
/* 方法实现 */
}
...
func (struct_name_variable struct_name) method_namen() [return_type] {
/* 方法实现*/
}
示例
package main
import (
"fmt"
"math"
)
// 定义接口
type Shape interface {
Area() float64
Perimeter() float64
}
// 定义一个结构体
type Circle struct {
Radius float64
}
// Circle 实现 Shape 接口
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
func (c Circle) Perimeter() float64 {
return 2 * math.Pi * c.Radius
}
func main() {
c := Circle{Radius: 5}
var s Shape = c // 接口变量可以存储实现了接口的类型
fmt.Println("Area:", s.Area())
fmt.Println("Perimeter:", s.Perimeter())
}
2.2.2 空接口
空接口 interface{}
是 Go 的特殊接口
,表示所有类型
的超集
,任意类型都实现了空接口。
常用于需要存储任意类型数据的场景,如泛型容器、通用参数等。
当接口变量的动态类型和动态值都为 nil 时,接口变量为 nil
接口变量实际上包含了两部分:
动态类型
:接口变量存储的具体类型动态值
:具体类型的值
2.2.3 类型断言
类型断言用于从接口类型中提取其底层值。
基本语法:value := iface.(Type)
iface
:是接口变量。Type
:是要断言的具体类型。
如果类型不匹配,会触发 panic。
为了避免 panic,可以使用带检查的类型断言:value, ok := iface.(Type)
ok 是一个布尔值,表示断言是否成功。如果断言失败,value 为零值,ok 为 false。
func main() {
var a interface{ }
var b int
a = b
// 断言赋值
c := a.(int)
fmt.Printf("类型:%T\n",c)
}
2.2.4 类型选择(type switch)
type switch
是 Go 中的语法结构
,用于根据接口变量的具体类型执行不同的逻辑。
package main
import "fmt"
func printType(val interface{}) {
switch v := val.(type) {
case int:
fmt.Println("Integer:", v)
case string:
fmt.Println("String:", v)
case float64:
fmt.Println("Float:", v)
default:
fmt.Println("Unknown type")
}
}
func main() {
printType(42)
printType("hello")
printType(3.14)
printType([]int{1, 2, 3})
}
2.3 接口与结构体
接口定义行为:
- 接口定义了一组方法。
- 一个结构体(或其他类型)只要实现了接口中定义的所有方法,就自动满足接口(称为
接口的实现
)。 - 不需要显式地声明
继承
或实现
关系。
Go 的接口机制特性
- 隐式实现:
不需要显式声明结构体实现了某个接口,只要满足方法签名
即可。
这种设计避免了类和接口之间的紧耦合。 - 多态:
通过接口实现动态行为。
结构体赋值给接口变量后,接口变量可以动态调用结构体实现的方法。 - 接口变量:
接口变量实际上由两部分组成:
动态类型
:接口实际存储的值的类型。
动态值
:实际存储的值。
var a Animal
a = Dog{}
fmt.Printf("动态类型:%T, 动态值:%v\n", a, a) // 动态类型:main.Dog, 动态值:{}
2.4 示例
2.4.1 接口传参
package main
import "fmt"
type Phone interface {
call(param int) string
takephoto()
}
type Huawei struct {
}
func (huawei Huawei) call(param int) string{
fmt.Println("i am Huawei, i can call you!", param)
return "damon"
}
func (huawei Huawei) takephoto() {
fmt.Println("i can take a photo for you")
}
func main(){
var phone Phone
phone = new(Huawei)
phone.takephoto()
r := phone.call(50)
fmt.Println(r)
}
2.4.2 修改属性
若想要通过接口方法修改属性,需要在传入指针的结构体才行
type fruit interface{
getName() string
setName(name string)
}
type apple struct{
name string
}
func (a *apple) getName() string{
return a.name
}
func (a *apple) setName(name string) {
a.name = name
}
func testInterface(){
a:=apple{"红富士"}
fmt.Print(a.getName())
a.setName("树顶红")
fmt.Print(a.getName())
}
2.4.3 多态示例
为不同数据类型的实体提供统一的接口
package main
import "fmt"
// 学生
type Student struct {
Name string
Age int
Score float32
}
// 教师
type Teacher struct {
Name string
Age int
Class int
}
// 接口定义
type Test interface {
Print()
Sleep()
}
// 学生实现接口
func (p Student) Print() {
fmt.Println("name:",p.Name)
fmt.Println("age:",p.Age)
fmt.Println("score:",p.Score)
}
func (p Student) Sleep() {
fmt.Println("学生在睡觉")
}
// 教师实现接口
func (t Teacher) Print() {
fmt.Println("name:",t.Name)
fmt.Println("age:",t.Age)
fmt.Println("class:",t.Class)
}
func (t Teacher) Sleep() {
fmt.Println("教师在休息")
}
func main() {
var t Test
var stu Student = Student {
Name: "zhangsan",
Age: 18,
Score: 90,
}
var tea Teacher = Teacher {
Name: "lisi",
Age: 50,
Class: 01,
}
// 多态表现
t = stu
t.Print()
t.Sleep()
fmt.Println("---------------------------")
t = tea
t.Print()
t.Sleep()
}
name: zhangsan
age: 18
score: 90
学生在睡觉
---------------------------
name: lisi
age: 50
class: 1
教师在休息
标签:string,fmt,struct,接口,func,Go,type,结构
From: https://www.cnblogs.com/jingzh/p/18638384