《Go小技巧易错点100例》第三十七篇
本期分享:
1.Go结构体非导出字段不会被序列化
2.Go语言的字符串是不可变
3.代码块{}的作用范围
Go结构体非导出字段不会被序列化
在Go语言中,结构体字段的可见性(是否以大写字母开头)会影响JSON序列化和反序列化的行为。这是Go语言的一个核心特性,与包的可见性规则一致。
基本规则
可导出字段(大写字母开头)会被JSON包序列化和反序列化,而非导出字段(小写字母开头)会被JSON包忽略。
示例代码
package mainimport ("encoding/json""fmt"
)type Person struct {Name string `json:"name"` // 可导出,会被序列化age int `bson:"age"` // 非导出,会被忽略
}func main() {// 序列化p := Person{Name: "Alice", age: 30}jsonData, _ := json.Marshal(p)fmt.Println(string(jsonData)) // 输出: {"Name":"Alice"}// 反序列化var p2 Person_ = json.Unmarshal([]byte(`{"Name":"Bob","age":40}`), &p2)fmt.Printf("%+v\n", p2) // 输出: {Name:Bob age:0}
}
关键点说明
序列化时只有Name字段被包含在JSON输出中age字段由于是非导出的,不会被包含
反序列化时即使JSON数据中包含age字段,由于结构体中对应字段是非导出的,该值会被忽略反序列化后age字段保持其零值(int类型的零值是0)
如果需要处理非导出字段,可以考虑以下方法:
- 使用json:"-"标签显式忽略字段
- 实现自定义的MarshalJSON和UnmarshalJSON方法
这种设计是Go语言刻意为之的,它强制开发者明确哪些字段应该被暴露给外部系统,有助于提高代码的安全性和可维护性。
Go语言中的字符串是不可变
下面这段Go代码的执行会报错,因为Go语言中的字符串是不可变的(immutable)。具体分析如下:
func main() {str := "hello"str[0] = 'x' // 这里会编译错误fmt.Println(str)
}
编译时会直接报错:cannot assign to str[0]
(不能对字符串的字符进行赋值)
原因是Go中的字符串是只读的字节切片,不能直接修改其中的字符,如果你想修改字符串中的字符,可以先将字符串转换为[]byte或[]rune切片,修改后再转回字符串:
func main() {str := "hello"bytes := []byte(str)bytes[0] = 'x'str = string(bytes)fmt.Println(str) // 输出: xello
}
在Go中,字符串的不可变性是语言设计的一个重要特性,这有助于保证字符串操作的安全性和线程安全性。
代码块{}的作用范围
func main() {x := 1{x := 2fmt.Print(x)}fmt.Println(x)
}
这段Go代码的执行结果是:
21
详细解释:
首先看代码结构分析:
func main() {x := 1 // 外层x,值为1{x := 2 // 内层x,与外层x是不同的变量fmt.Print(x) // 打印内层x的值2}fmt.Println(x) // 打印外层x的值1
}
执行过程:
首先声明外层变量x
并赋值为1
,进入内部代码块,声明新的变量x并赋值为2
(这是一个新的变量,与外层x
同名但不同一)
打印内部x
的值2
(fmt.Print
不换行)
退出内部代码块后,打印外层x
的值1
(fmt.Println
会换行)
Go语言的变量作用域规则:
使用:=
会在当前作用域创建新变量,内部代码块中的x := 2
是新建了一个变量,不是修改外层的x
,如果想修改外层x
的值,应该使用赋值=
而不是声明:=
如果想修改外层变量,代码应该这样写:
func main() {x := 1{x = 2 // 注意这里使用=而不是:=fmt.Print(x)}fmt.Println(x)
}
输出: 22
本篇结束~