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. react-router 4.0(二)传参

    import React from 'react'; import ReactDOM from 'react-dom' import {Link,Route,HashRouter} from 'rea ...

  2. FFT 快速傅里叶变换 学习笔记

    FFT 快速傅里叶变换 前言 lmc,ikka,attack等众多大佬都没教会的我终于要自己填坑了. 又是机房里最后一个学fft的人 早背过圆周率50位填坑了 用处 多项式乘法 卷积 \(g(x)=a ...

  3. 浅谈String中的==和对象中引用对象类型的==

    @Test public void test02() { StringBuffer sb = new StringBuffer(); sb.append('a'); sb.append(11); Sy ...

  4. python检测服务器端口

    import socket sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sk.settimeout(10) try: sk.conne ...

  5. windows平台下安装linux

    win10系统下,可直接安装linux系统,且可打开linux的命令行 安装 1.开始--->微软应用商店 进入之后,在搜索框,搜索linux 在搜索结果中,选择要安装的 linux 系统 这里 ...

  6. LOG4NET用法(个人比较喜欢的用法)

    LOG4NET用法(个人比较喜欢的用法) http://fanrsh.cnblogs.com/archive/2006/06/08/420546.html

  7. eclipse get set 自动添加注释

    编码的时候通常要用到 JavaBean ,而在我们经常把注释写在字段上面,但生成的Get/Set方法不会生成,通过修改Eclipse源码可解决,直接上例子: /** * 员工ID */ private ...

  8. 『计算机视觉』Mask-RCNN_训练网络其二:train网络结构&损失函数

    Github地址:Mask_RCNN 『计算机视觉』Mask-RCNN_论文学习 『计算机视觉』Mask-RCNN_项目文档翻译 『计算机视觉』Mask-RCNN_推断网络其一:总览 『计算机视觉』M ...

  9. MongoDB 教程(八):查询文档、条件操作符

    MongoDB 查询文档 MongoDB 查询文档使用 find() 方法. find() 方法以非结构化的方式来显示所有文档. MongoDB 查询数据的语法格式如下: db.collection. ...

  10. java泛型的理解

    总体介绍泛型: 1.泛型是计算机程序中一种重要的思维方式,它将数据结构和算法与数据类型相分离,使得同一套数据结构和算法,能够应用于各种数据类型,而且还可以保证类型安全,提高可读性.在Java中,泛型广 ...