struct结构体

  • struct用来自定义复杂数据结构,可以包含多个字段属性,可以嵌套;
  • go中的struct类型理解为类,可以定义方法,和函数定义有些许区别;
  • struct类型是值类型.

struct定义

type User struct {
Name string
Age int32
mess string
} var user User
var user1 *User = &User{}
// new 会分配结构空间,并初始化为清空为零,不进一步初始化
// new之后需要一个指针来指向这个结构
// make会分配结构空间及其附属空间,并完成其间的指针初始化
// make返回这个结构空间,不另外分配一个指针
var user2 *User = new(User)

struct使用

下面示例中user1和uesr2为指针类型,访问的时候编译器会自动把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)
fmt.Println(user1.Name,(*user1).Name) var user2 *User = new(User)
user2.Name = "suoning"
}

构造函数

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("suoning",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)
fmt.Printf("Name:%p\n", &user.Name)
fmt.Printf("Age: %p\n", &user.Age)
fmt.Printf("mess: %p\n",&user.mess)

方法

方法是作用在特定类型的变量上,因此自定义类型,都可以有方法,而不仅仅是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 string
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)
}

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 {
// Sprintf可以格式化字符串,但不输出结果,并且返回一个字符串。
return fmt.Sprintf("This is String(%s).",this.string)
} func main() {
n := new(name1)
fmt.Println(n) //This is String().
n.string = "suoning"
d := fmt.Sprintf("%s",n) // This is String(suoning)
fmt.Println(d)
}

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
}

接口interface

interface类型可以定义一组方法,但是这些不需要实现。并interface不能包含任何变量.

interface类型默认是一个指针.

Interface定义

type Car interface {
NameGet() string
Run(n int)
Stop()
}

Interface实现

  • 1.Golang中的接口,不需要显示的实现。只要一个变量,含有接口类型中的所有方法,那么这个变量就实现这个接口。因此golang中没有implement类似的关键字;
  • 2.如果一个变量含有了多个interface类型的方法,那么这个变量就实现了多个接口;如果一个变量只含有了1个interface的方部分方法,那么这个变量没有实现这个接口。
  • 3.空接口interface{}: 空接口没有任何方法,所以所有类型都实现了空接口。
var a int
var b interface{} // 空接口
b = a

多态

一种事物的多种形态,都可以按照统一的接口进行操作。

例子:

type Car interface {
NameGet() string
Run(n int)
Stop()
} type BMW struct {
Name string
} func (this *BMW) NameGet() string {
return this.Name
} func (this *BMW) Run(n int) {
fmt.Printf("BMW is running of num is %d \n",n)
} func (this *BMW) Stop() {
fmt.Printf("BMW is stop \n")
} type Benz struct {
Name string
} func (this *Benz) NameGet() string {
return this.Name
} func (this *Benz) Run(n int) {
fmt.Printf("Benz is running of num is %d \n",n)
} func (this *Benz) Stop() {
fmt.Printf("Benz is stop \n")
} func (this *Benz) ChatUp() {
fmt.Printf("ChatUp \n")
} func main() {
var car Car
fmt.Println(car) var bmw BMW = BMW{Name: "宝马"}
car = &bmw
fmt.Println(car.NameGet()) // 宝马
car.Run(1)
car.Stop() benz := &Benz{Name: "大奔"}
car = benz
fmt.Println(car.NameGet()) // 大奔
car.Run(2)
car.Stop()
}

Interface嵌套

一个接可以嵌套在另外的接口

即需要实现2个接口的方法。

type Car interface {
NameGet() string
Run(n int)
Stop()
} type Used interface {
Car
Cheap()
}

类型断言

类型断言,由于接口是一般类型,不知道具体类型,

如果要转成具体类型,可以采用以下方法进行转换:

var t int
var x interface{}
x = t y = x.(int)
y, ok = x.(int)

例子一

func test(i interface{}) {
// n := i.(int)
n, ok := i.(int)
if !ok {
fmt.Println("error")
return
}
n += 10
fmt.Println(n)
} func main() {
var t1 int
test(t1)
}

例子二

swithc & type

type Student struct {
Name string
} func judgmentType(items ...interface{}) {
for k, v := range items {
switch v.(type) {
case string:
fmt.Printf("string, %d[%v]\n", k, v)
case bool:
fmt.Printf("bool, %d[%v]\n", k, v)
case int, int32, int64:
fmt.Printf("int, %d[%v]\n",k, v)
case float32, float64:
fmt.Printf("float, %d[%v]\n",k, v)
case Student:
fmt.Printf("Student, %d[%v]\n",k, v)
case *Student:
fmt.Printf("Student, %d[%p]\n",k, v)
}
}
} func main() {
stu1 := &Student{Name: "nick"}
judgmentType(1, 2.2, "learing", stu1)
}

例子三

判断一个变量是否实现了指定接口

type Stringer interface {
String() string
} type Mystruct interface { } type Mystruct2 struct { } func (this *Mystruct2) String() string {
return ""
} func main() {
var v Mystruct
var v2 Mystruct2
v = &v2 if sv, ok := v.(Stringer); ok {
fmt.Printf("%v implements String(): %s\n",sv.String());
}
}

反射reflect

reflect包实现了运行时反射,允许程序操作任意类型的对象。

典型用法是用静态类型interface{}保存一个值,

通过调用TypeOf获取其动态类型信息,该函数返回一个Type类型值 。

调用ValueOf函数返回一个Value类型值,该值代表运行时的数据。

func TypeOf(i interface{}) Type

TypeOf返回接口中保存的值的类型,TypeOf(nil)会返回nil。

func ValueOf(i interface{}) Value

ValueOf返回一个初始化为i接口保管的具体值的Value,ValueOf(nil)返回Value零值。

reflect.Value.Kind
获取变量的类别,返回一个常量
const (
Invalid Kind = iota
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr
Float32
Float64
Complex64
Complex128
Array
Chan
Func
Interface
Map
Ptr
Slice
String
Struct
UnsafePointer
)
reflect.Value.Interface()
转换成interface{}类型
【变量<-->Interface{}<--->Reflect.Value】

获取变量的值

reflect.ValueOf(x).Int()
reflect.ValueOf(x).Float()
reflect.ValueOf(x).String()
reflect.ValueOf(x).Bool()

通过反射的来改变变量的值

reflect.Value.SetXX相关方法,比如:
reflect.Value.SetInt(),设置整数
reflect.Value.SetFloat(),设置浮点数
reflect.Value.SetString(),设置字符串

例子一

import "reflect"

func main() {
var x float64 = 5.21
fmt.Println("type:",reflect.TypeOf(x)) // type: float64 v := reflect.ValueOf(x)
fmt.Println("value:",v) // value: 5.21
fmt.Println("type:",v.Type()) // type: float64
fmt.Println("kind:",v.Kind()) // kind: float64
fmt.Println("value:",v.Float()) // value: 5.21 fmt.Println(v.Interface()) // 5.21
fmt.Println("value is %1.1e\n", v.Interface()) // value is 5.2e+00
y := v.Interface().(float64)
fmt.Println(y)
}

例子二(修改值)

setXX(x)因为传递的是x的值的副本,所以SetXX不能够改x,改动x必须向函数传递x的指针,

SetXX(&x)

// 错误代码
// panic: reflect: reflect.Value.SetFloat using unaddressable value
func main() {
var a float64
fv := reflect.ValueOf(&a)
fv.SetFloat(520.00)
fmt.Printf("%v\n",a)
}
// 正确的,传指针
func main() {
var a2 float64
fv2 := reflect.Value(&a2)
fv2.Elem().SetFloat(520.00)
fmt.Printf("%v\n",a2) // 520
}

反射操作结构体

  • 1.reflect.Value.NumField() 获取结构体中字段的个数
  • 2.reflect.Value.Method(n).Call(nil) 来调用结构体中的方法

例子一(通过反射操作结构体)

import "reflect"

type NotknownType struct {
s1 string
s2 string
s3 string
} func (n NotknownType) String() string {
return n.S1 + " & " + n.S2 + " & " + n.S3
} var secret interface{} = NotKnownType{"Go","C","Python"} func main() {
value := reflect.ValueOf(secret)
fmt.Println(value) // Go & C & Python
typ := reflect.TypeOf(secret)
fmt.Println(typ) // main.NotknownType knd := value.Kind()
fmt.Println(knd) // struct for i := 0; i < value.NumField(); i++ {
fmt.Printf("Field %d: %v\n",i, value.Field(i))
} results := value.Method(0).Call(nil)
fmt.Println(results) // [Go & C & Python]
}

例子二(通过反射修改结构体)

import "reflect"

type T struct {
A int
B string
} func main() {
t := T{18, "nick"}
s := reflect.ValueOf(&t).Elem()
typeOfT := s.Type() for i := 0; i < s.NumField(); i++ {
f := s.Field(i)
fmt.Printf("%d: %s %s = %v\n",i,
typeOfT.Field(i).Name,f.Type(), f.Interface())
} s.Field(0).SetInt(25)
s.Field(1).SetString("nicky")
fmt.Println(t)
}
-----
/*
输出:
0: A int = 18
1: B string = nick
{25 nicky}
*/
import "reflect"
type test struct {
S1 string
s2 string
s3 string
} var s interface{} = &test{
S1: "s1",
s2: "s2",
s3: "s3",
} func main() {
val := reflect.ValueOf(s)
fmt.Println(val)
fmt.Println(val.Elem())
fmt.Println(val.Elem().Field(0))
val.Elem().Field(0).SetString("hehe")
}

例子三(struct tag内部实现)

package main
import (
"fmt"
"reflect"
)
type User struct {
Name string `json:"user_name"`
} func main() {
var user User
userType := reflect.TypeOf(user)
jsonString := userType.Field(0).Tag.Get("json")
fmt.Println(jsonString)
}

golang结构体、接口、反射的更多相关文章

  1. golang 结构体中的匿名接口

    golang 结构体中的匿名接口 代码示例 golang 中,可以给结构体增加匿名field,可参考 unknwon 大神的书. 匿名字段和内嵌结构体 但,golang同时也可以给结构体定义一个匿名i ...

  2. Golang结构体值的交换

    Golang结构体值的交换 一.添加结构体,多if暴力 最先遇到这个问题是在比编写PUT方法的接口时遇到. (我公司编写http put方法,是先解析json至StudentInput结构体中,通过i ...

  3. golang结构体json格式化的时间格式

    golang结构体json格式化的时间格式 在我们开发中,经常会解析time.Time 往往前台传过来的时候,是个string 但是我们希望在结构体转成time.Time type Param str ...

  4. GO开发[五]:golang结构体struct

    Go结构体struct Go语言的结构体(struct)和其他语言的类(class)有同等的地位,但Go语言放弃了包括继承在内的大量面向对象特性,只保留了组合(composition)这个最基础的特性 ...

  5. golang结构体

    声明结构体 定义结构体使用struct关键字.在结构体内部定义它们的成员变量和类型.如果成员变量的类型相同还可以把它们写到同一行. struct里面可以包含多个字段(属性) struct类型可以定义方 ...

  6. 【转】golang 结构体和方法

    原文:https://www.jianshu.com/p/b6ae3f85c683 ---------------------------------------------------------- ...

  7. Golang结构体struct的使用(结构体嵌套, 匿名结构体等)

    转自: https://studygolang.com/articles/11313 golang中是没有class的,但是有一个结构体struct,有点类似,他没有像java,c++中继承的概念,但 ...

  8. [Go] golang结构体成员与函数类型

    package main import ( "fmt" ) //定义一个类型 type tsh struct { //定义成员,类型是func() string test func ...

  9. golang结构体排序 - 根据下载时间重命名本地文件

    喜M拉Y下载音频到手机,使用ximalaya.exe 解密[.x2m]为[.m4a]根据文件下载创建时间,顺序重命名文件,方便后续播放. 源码如下:package main import ( &quo ...

随机推荐

  1. Linux服务器---百科mediawiki

    Mediawiki         Mediawiki是一个强大的维基软件,可以实现页面编辑.图像和多媒体管理. 1.下载mediawiki软件(“https://www.mediawiki.org/ ...

  2. docker启动后忘记挂载nvidia-docker-volume的解决方法

    进入 docker 目录 删除 volumes 下的所有文件 重新启动docker 运行nvidia-docker run XXX 来生成volume 修改所有 docker目录下containers ...

  3. js cookie缓存处理

    function setCookie(cnameList,cvalueList,exdays){ var d = new Date(); d.setTime(d.getTime()+(exdays*2 ...

  4. Java集群优化——使用Dubbo对单一应用服务化改造

    之前,我们讨论过Nginx+tomcat组成的集群,这已经是非常灵活的集群技术,但是当我们的系统遇到更大的瓶颈,全部应用的单点服务器已经不能满足我们的需求,这时,我们要考虑另外一种,我们熟悉的内容,就 ...

  5. UUID简介

    UUID简介如下:1.简介UUID含义是通用唯一识别码 (Universally Unique Identifier),这 是一个软件建构的标准,也是被开源软件基金会 (Open Software F ...

  6. CentOS7查看systemctl 控制的服务的相关配置

    例如,启动配置文件 [root@Docker_Machine_192.168.31.130 ~]# systemctl show --property=FragmentPath docker Frag ...

  7. python 排序算法

    冒泡排序: 一. 冒泡排序的定义 冒泡排序(英语:Bubble Sort)是一种简单的排序算法.它重复地遍历要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.遍历数列的工作是重复地进 ...

  8. tomcat下面web应用发布路径配置 ( 即虚拟目录配置 )

    https://blog.csdn.net/AnQ17/article/details/52122236

  9. DAY8 文件操作(二)

    一.写 1.1写文件 # w:没有文件新建文件,有文件就清空文件 w = open('1.txt', 'w', encoding='utf-8') w.write('000\n') # 在写入大量数据 ...

  10. [Android - QPST] 高通刷机/QPST刷机

    参考网站: https://forum.xda-developers.com/zuk-z2-pro/how-to/howto-flash-stock-rom-t3435109 http://ask.l ...