- 继承
继承通过结构体中的匿名字段实现,示例如下,Add和Sub引用了BaseNum结构体,所以Add和Sub是BaseNum的子类。
type Add struct {
BaseNum
}
type Sub struct {
BaseNum
}
type BaseNum struct {
num1 int
num2 int
}
如果一个struct嵌套了另一个匿名结构体,那么这个结构可以直接访问匿名结构体的方法,从而实现继承
如果一个struct嵌套了另一个【有名】的结构体,那么这个模式叫做组合
如果一个struct嵌套了多个匿名结构体,那么这个结构可以直接访问多个匿名结构体的方法,从而实现多重继承
封装是把抽象出来的字段和对字段的操作封装在一起,数据被保护在内部,程序的其它包只能通过被授权的方法,才能对字段进行操作。
package main
import "fmt"
type person struct{
Name string
age int
sal float64
}
func NewPerson(name string) *person{
return &person{
Name :name,
}
}
//只能调用SetAge/GetAge/setSal/GetSal才能对person下字段进行操作
func (p *person) SetAge(age int) {
if age > 0 && age <100 {
p.age=age
}else{
fmt.Println("age not correct")
}
}
func (p * person)GetAge() int{
return p.age
}
func (p *person)setSal(sal float64){
if sal >0 && sal <30000{
p.sal=sal
}else{
fmt.Println("sal not correct")
}
}
func (p *person)GetSal() float64{
return p.sal
}
func main() {
p := NewPerson("amy")
p.setSal(3000)
p.SetAge(18)
fmt.Println(*p)
fmt.Println(p.GetAge())
}
数组:具有相同类型且长度固定的一组数据序列,这组数据项序列对应存放在内存中的一块连续区域中。
数组包含index(下标),value,和长度
var s [3]string
s[0]="a"
s[1]="b"
s[2]="c"
var t = [3]string{"a","b","c"}
for k,v :=range t {
fmt.Println("index:",k,"value:",v)
}
for i:=0;i<range(len(t));i++{
fmt.Println("index:",i,"value:",t[i])
}
切片是围绕动态数组的概念构建的,可以按需自动增长和缩小,使用append实现。切片并不存储任何元素而只是对现有数组的引用。
切片结构包括:地址、长度和容量
地址:切片的地址一般指切片中第一个元素所指向的内存地址,用十六进制表示。
长度:切片中实际存在的元素个数;
容量:从切片的起始元素开始到其底层数组中的最后一个元素的个数,一旦长度达到容量数,容量自动翻倍。
a := []string{}
fmt.Println(a,len(a),cap(a))
a=append(a,"hi")
fmt.Println(a,len(a),cap(a))
差异:切片是可变长度、可变容量
断言:(Type Assertion)是一个使用在接口值上的操作,用于检查接口类型变量所持有的值是否实现了期望的接口或者具体的类型。
value, ok := x.(T)
其中,x 表示一个接口的类型,T 表示一个具体的类型(也可为接口类型)。
该断言表达式会返回 x 的值(也就是 value)和一个布尔值(也就是 ok),可根据该布尔值判断 x 是否为 T 类型:
- 如果 T 是具体某个类型,类型断言会检查 x 的动态类型是否等于具体类型 T。如果检查成功,类型断言返回的结果是 x 的动态值,其类型是 T。
- 如果 T 是接口类型,类型断言会检查 x 的动态类型是否满足 T。如果检查成功,x 的动态值不会被提取,返回值是一个类型为 T 的接口值。
- 无论 T 是什么类型,如果 x 是 nil 接口值,类型断言都会失败。
var x interface{}
x = "Hello"
value := x.(string)
fmt.Println(value)
反射的一个主要功能是检查存储在接口变量中的类型和值。
var i int
var j float64
i = 10
j =10
fmt.Println(reflect.TypeOf(i),reflect.ValueOf(i),reflect.TypeOf(j),reflect.ValueOf(j))
w:=reflect.DeepEqual(i,j)
fmt.Println(w)
int 10 float64 10
false
方法在定义的时候,会在func
和方法名之间增加一个参数,这个参数就是接收者,这样我们定义的这个方法就和接收者绑定在了一起,称之为这个接收者的方法,如下,String就是一个方法,person的一个方法。
type person struct {
name string
}
func (p person) String() string{
return "the person name is "+p.name
}
函数与方法的差异在于是否有接受者,如下add、Sub就是函数(因为没有接受者),而add因为是开头小写它的作用域只属于所声明的包内,即只能在当前声明的包下使用,不能被其他包使用,而Sub能够被其他包引用。
func main() {
sum := add(1, 2)
fmt.Println(sum)
}
func add(a, b int) int {
return a + b
}
func Sub(a, b int) int {
return a - b
}