头条真实面试题

struct结构体能不能比较

结构体不可以比较,但是同一类型的结构体的值可以比较是否相等的(不可以比较大小):

  • 结构体所有字段的值都相等,两个结构体才相等

  • 比较的两个结构体必须是相同类型才可以,也就是说他们字段的顺序、名称、类型、标签都相同才可以

结构体struct

struct 用来自定义复杂数据结构,可以包含多个字段(属性),可以嵌套;

go中的struct类型理解为类,可以定义方法,和函数定义有些许区别;

struct类型是值类型。


struct定义

type User struct {
Name string
Age int32
mess string
}
var user User
var user1 *User = &User{}
var user2 *User = new(User)



struct使用

下面示例中user1和user2为指针类型,访问的时候编译器会自动把 user1.Name 转为 (*user1).Name

func main() {
var user User
user.Name = "nick"
user.Age = 18
user.mess = "lover"


var user1 *User = &User{
Name: "dawn",
Age: 21,
}
fmt.Println(*user1) //{dawn 21 }
fmt.Println(user1.Name, (*user1).Name) //dawn dawn


var user2 *User = new(User)
    user2.Name = "suolong"
user2.Age = 18
    fmt.Println(user2)                     //&{suolong 18 }
    fmt.Println(user2.Name, (*user2).Name) 
}


构造函数

golang中的struct没有构造函数,可以伪造一个

type User struct {
Name string
Age int32
mess string
}


func NewUser(name string, age int32, mess string) *User {
return &User{Name:name,Age:age,mess:mess}
}


func main() {
//user := new(User)
    user := NewUser("suolong", 18, "lover")
fmt.Println(user, user.mess, user.Name, user.Age)
}


内存布局

struct中的所有字段在内存是连续的

    var user User
user.Name = "nick"
user.Age = 18
user.mess = "lover"


fmt.Println(user) //{nick 18 lover}
fmt.Printf("Name:%p\n", &user.Name) //Name:0xc420016180
fmt.Printf("Age: %p\n", &user.Age) //Age: 0xc420016190
fmt.Printf("mess:%p\n", &user.mess) //mess:0xc420016198 8字节为内存对齐



方法

方法是作用在特定类型的变量上,因此自定义类型,都可以有方法,而不仅仅是struct。

方法的访问控制也是通过大小写控制。

init函数是通过传入指针实现,这样改变struct字段值,因为是值类型。

type User struct {
Name string
Age int
sex string
}


func (this *User) init(name string, age int, sex string) {
this.Name = name
this.Age = age
this.sex = sex
}


func (this User) GetName() string {
return this.Name
}


func main() {
var user User
user.init("nick", 18, "man")
//(&user).init("nick", 18, "man")
name := user.GetName()
fmt.Println(name)
}


匿名字段

如果有冲突的, 则最外层的优先

type User struct {
Name stirng
Age int
}


type Lover struct {
User
sex time.Time
int
Age int
}



继承 & 多重继承

一个结构体继承多个结构体,访问通过点。继承字段以及方法。

可以起别名,如下面 u1(user1),访问 user.u1.Age。

如果继承的结构体都拥有同一个字段,通过user.name访问就会报错,必须通过user.user1.name来访问。

type user1 struct {
name string
Age int
}


type user2 struct {
name string
age int
sex time.Time
}


type User struct {
u1 user1 //别名
user2
Name string
Age int
}


func main() {
var user User
user.Name = "nick"
user.u1.Age = 18
fmt.Println(user) //{{ 18} { 0 {0 0 <nil>}} nick 0}
}


tag

在go中,首字母大小写有特殊的语法含义,小写包外无法引用。由于需要和其它的系统进行数据交互,例如转成json格式。这个时候如果用属性名来作为键值可能不一定会符合项目要求。tag在转换成其它数据格式的时候,会使用其中特定的字段作为键值。

import "encoding/json"


type User struct {
Name string `json:"userName"`
Age int `json:"userAge"`
}


func main() {
var user User
user.Name = "nick"
user.Age = 18

conJson, _ := json.Marshal(user)
fmt.Println(string(conJson)) //{"userName":"nick","userAge":0}
}


String()

如果实现了String()这个方法,那么fmt默认会调用String()。

type name1 struct {
int
string
}


func (this *name1) String() string {
return fmt.Sprintf("This is String(%s).", this.string)
}


func main() {
n := new(name1)
fmt.Println(n) //This is String().
    n.string = "suolong"
    d := fmt.Sprintf("%s", n) //This is String(suolong).
fmt.Println(d)
}



recover()

defer 所有错误

func myE() (str string, err error) {
defer func() {
if p := recover(); p != nil {
str, ok := p.(string)
if ok {
err = errors.New(str)
} else {
err = errors.New("panic")
}
//debug.PrintStack()
}
}()
panic("this is panic message")
return "hello girl", err
}