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. Hdu2039 三角形

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2039 三角形 Time Limit: 2000/1000 MS (Java/Others)    Me ...

  2. Poj3176 Cow Bowling (动态规划 数字三角形)

    Description The cows don't use actual bowling balls when they go bowling. They each take a number (i ...

  3. Linux 安装vsftpd和ftp客户端

    1.下载安装包:ftp-0.17-54.el6.x86_64.zip和vsftpd-2.2.2-11.el6_4.1.x86_64.zip 可以直接在Linux底下用yum install vsftp ...

  4. Redis学习-list数据类型

    list类型是一个链表结构,主要功能有push,pop等.而且list是一个双向链表,可以通过相关操作进行集合的头部或者尾部添加,删除元素. lpush key string 在key对应的list的 ...

  5. 剑指offer(62)二叉搜索树的第K个节点

    题目描述 给定一棵二叉搜索树,请找出其中的第k小的结点.例如, (5,3,7,2,4,6,8)    中,按结点数值大小顺序第三小结点的值为4. 题目分析 首先,我们可以先画图.画完图后我们要想办法从 ...

  6. 分类统计的controller和service

    SpringMVC框架下的 部分代码: Controller控制器: @Resource ReviewTitleService reviewTitleService;//调用ReviewTitleSe ...

  7. 王之泰201771010131《面向对象程序设计(java)》第六周学习总结

    第一部分:理论知识学习部分 第五章 第五章学习内容主要分为七个模块,分别为: 1.类.超类和子类: a. 类继承的格式: class 新类名extends已有类名. b. 已有类称为:超类(super ...

  8. Maven提示找不到dependency依赖包

    最近C盘爆炸,又重装了一下系统,打开之前创建的Maven项目,提示找不到仓库里的jar包了 我更新了Maven,重新加载都没用,我去系统盘查看的时候发现了这么一个情况: 我在装WIN10的时候用的是U ...

  9. 简单粗暴的上传项目至 Github

    嗯,写了一个项目,觉得还OK,就想放在 Github 继续维护和方便使用,那么如何简单快速的将代码上传至 Github 上? 1.  你得有自己的 Github账号,如何创建账号这里就不说了.因为.. ...

  10. Kafka学习之二 Kafka安装和使用

    部署环境Linux(Centos 6.5),JDK 1.8.0,zookeeper-3.4.12,kafka_2.11-2.0.0. 1. 单机环境     官方建议使用JDK 1.8版本,因此本文使 ...