我们花了两年学会说话,却要花上六十年来学会闭嘴。 by 佚名
引用于:http://wufazhuce.com/one/1384
结构体定义
type identifier struct {
field1 type1
field2 type2
...
}
使用 new 初始化结构体
使用 new 函数给一个新的结构体变量分配内存,它返回指向已分配内存的指针:var t *T = new(T)
或者t := new(T)
,另外,用&T{}
,也同样等价于用new
,因为底层仍然会调用new(T)
。
type Point struct {
x int
y int
}
func Test_demo1(t *testing.T) {
intr1 := new(Point)
intr1.x = 2
intr1.y = 6
intr2 := &Point{0, 3}
intr3 := &Point{x: 5, y: 1}
intr4 := &Point{y: 5}
fmt.Println(intr1) //&{2 6}
fmt.Println(intr2) //&{0 3}
fmt.Println(intr3) //&{1 5}
fmt.Println(intr4) //&{0 5}
}
实例化结构体两种常用形式:
1:用 new(T) 实例化。
2:用 &T{} 实例化。(推荐)
使用工厂模式创建结构体实例
func NewPoint(x int, y int) *Point {
if x < 0 || y < 0 {
return nil
}
return &Point{x, y}
}
func Test_demo2(t *testing.T) {
p1 := NewPoint(-1, 2)
fmt.Println(p1) //nil
p2 := NewPoint(2, 2)
fmt.Println(p2) //&{2 2}
}
上面的示例演示的就是工厂模式创建结构体实例,在Go标准包有很多地方应用的该模式。可以把这种模式等同为面向对象语言中的构造函数。另外,如果把 Point 结构体,命名变成小写,即point
,就可以作为私有变量,强制实例化结构体必须使用工厂模式。
反射获取结构体的标签
type Point struct {
x int "this is x"
y int "this is y"
}
func Test_demo3(t *testing.T) {
//p := &Point{1, 2} //会报运行时错误
p := Point{1, 2}
pType := reflect.TypeOf(p)
pField := pType.Field(0)
fmt.Printf("%v\n", pField.Tag) //this is x
}
结构体的方法
一个类型加上它的方法等价于面向对象中的一个类。通常情况下,这个类型一般定义为结构体类型。
定义方法的格式:
func (recv receiver_type) methodName(parameter_list) (return_value_list) { ... }
其中,recv
可称之为这个方法methodName
的接收者,receiver_type
为接收者类型。该类型可以是除了interface
类型的任何类型,但是通常都用结构体类型。
在接收者是指针时,方法可以改变接收者的值(或状态),当然这点函数也可以做到(当参数作为指针传递,即通过引用调用时,函数也可以改变参数的状态)。但如果接受者不是指针,方法是无法改变接收者的状态的,方法内部应用的接收者状态,仅是接收者实例的拷贝。
指针方法和值方法都可以在指针或非指针上被调用,Go会自动转换。
type Info struct {
address string
phone string
}
//接收者是非指针
func (this Info) changeInfo() {
this.address = "上海"
this.phone = "10086"
fmt.Println(this)
}
//接收者是指针
func (this *Info) changeInfo2() {
this.address = "上海"
this.phone = "10086"
fmt.Println(this)
}
func Test_demo5(t *testing.T) {
info := &Info{"北京", "10086"}
//指针调用,但接收者自动被转换成了非指针,changeInfo函数应用的是info的拷贝。
info.changeInfo() // {上海 10086}
fmt.Println(info) //&{北京 10086}
info2 := Info{"北京", "10086"}
//非指针调用,但接收者自动被转换成了指针,changeInfo函数应用的是真正的info实例。
info2.changeInfo2() // &{上海 10086}
fmt.Println(info2) //{上海 10086}
}
在 Test_demo5 中第一种调用接收者是指针类型,但调用changeInfo
方法时,Go自动转换成非指针类型,即实例的拷贝,所以在方法体内的改变Info的属性值,其实修改的是拷贝版,原实例并不受影响。而第二种调用接收者是非指针类型,但是Go自动转换成指针类型,所以内部的修改会影响实例本身属性状态。
所以:**如果想通过方法改变接收者实例本身的属性值或者状态,那么方法的接收者必须是指针类型。
结构体方法定义的常用规范:
*func (this T) methodName(parameter_list) (return_value_list) { ... }
类的公有/私有属性和方法
Go规定命名的属性如果首字母大写,即可以被非本go文件程序调用。所有公有的属性和方法,命名首字母必须大写。反之,便是私有的。
类的继承
type Human struct {
Name string
}
func (this *Human) eat() {
fmt.Println(this.Name + "要吃饭.")
}
type Man struct {
Human //继承了Human类
}
func (this *Man) chageName(name string) {
this.Name = name
}
func Test_demo6(t *testing.T) {
man := &Man{Human{"人类"}}
man.chageName("男人")
man.eat() //男人要吃饭.
}
读取运行内存
func Test_demo7(t *testing.T) {
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf("%d Kb\n", m.Alloc/1024) //单位Kb
}