深入理解 go reflect - 反射基本原理

这篇具有很好参考价值的文章主要介绍了深入理解 go reflect - 反射基本原理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

反射概述

反射是这样一种机制,它是可以让我们在程序运行时(runtime)访问、检测和修改对象本身状态或行为的一种能力。
比如,从一个变量推断出其类型信息、以及存储的数据的一些信息,又或者获取一个对象有什么方法可以调用等。
反射经常用在一些需要同时处理不同类型变量的地方,比如序列化、反序列化、ORM 等等,如标准库里面的 json.Marshal

反射基础 - go 的 interface 是怎么存储的?

在正式开始讲解反射之前,我们有必要了解一下 go 里的接口(interface)是怎么存储的。
关于这个问题,在我的另外一篇文章中已经做了很详细的讲解 go interface 设计与实现,
这里不再赘述。但还是简单说一下,go 的接口是由两部分组成的,一部分是类型信息,另一部分是数据信息,如:

var a = 1
var b interface{} = a

对于这个例子,b 的类型信息是 int,数据信息是 1,这两部分信息都是存储在 b 里面的。b 的内存结构如下:

深入理解 go reflect - 反射基本原理,go,golang,开发语言,后端

在上图中,b 的类型实际上是 eface,它是一个空接口,它的定义如下:

type eface struct {
    _type *_type
    data  unsafe.Pointer
}

也就是说,一个 interface{} 中实际上既包含了变量的类型信息,也包含了类型的数据。
正因为如此,我们才可以通过反射来获取到变量的类型信息,以及变量的数据信息。

反射对象 - reflect.Type 和 reflect.Value

知道了 interface{} 的内存结构之后,我们就可以开始讲解反射了。反射的核心是两个对象,分别是 reflect.Typereflect.Value
它们分别代表了 go 语言中的类型和值。我们可以通过 reflect.TypeOfreflect.ValueOf 来获取到一个变量的类型和值。

var a = 1
t := reflect.TypeOf(a)

var b = "hello"
t1 := reflect.ValueOf(b)

我们去看一下 TypeOfValueOf 的源码会发现,这两个方法都接收一个 interface{} 类型的参数,然后返回一个 reflect.Typereflect.Value 类型的值。这也就是为什么我们可以通过 reflect.TypeOfreflect.ValueOf 来获取到一个变量的类型和值的原因。

深入理解 go reflect - 反射基本原理,go,golang,开发语言,后端

反射定律

在 go 官方博客中关于反射的文章 laws-of-reflection 中,提到了三条反射定律:

  1. 反射可以将 interface 类型变量转换成反射对象。
  2. 反射可以将反射对象还原成 interface 对象。
  3. 如果要修改反射对象,那么反射对象必须是可设置的(CanSet)。

关于这三条定律,官方博客已经有了比较完整的阐述,感兴趣的可以去看一下官方博客的文章。这里简单阐述一下:

反射可以将 interface 类型变量转换成反射对象。

其实也就是上面的 reflect.Typereflect.Value,我们可以通过 reflect.TypeOfreflect.ValueOf 来获取到一个变量的反射类型和反射值。

var a = 1
typeOfA := reflect.TypeOf(a)
valueOfA := reflect.ValueOf(a)

反射可以将反射对象还原成 interface 对象。

我们可以通过 reflect.Value.Interface 来获取到反射对象的 interface 对象,也就是传递给 reflect.ValueOf 的那个变量本身。
不过返回值类型是 interface{},所以我们需要进行类型断言。

i := valueOfA.Interface()
fmt.Println(i.(int))

如果要修改反射对象,那么反射对象必须是可设置的(CanSet)。

我们可以通过 reflect.Value.CanSet 来判断一个反射对象是否是可设置的。如果是可设置的,我们就可以通过 reflect.Value.Set 来修改反射对象的值。
这其实也是非常场景的使用反射的一个场景,通过反射来修改变量的值。

var x float64 = 3.4
v := reflect.ValueOf(&x)
fmt.Println("settability of v:", v.CanSet()) // false
fmt.Println("settability of v:", v.Elem().CanSet()) // true

那什么情况下一个反射对象是可设置的呢?前提是这个反射对象是一个指针,然后这个指针指向的是一个可设置的变量。
在我们传递一个值给 reflect.ValueOf 的时候,如果这个值只是一个普通的变量,那么 reflect.ValueOf 会返回一个不可设置的反射对象。
因为这个值实际上被拷贝了一份,我们如果通过反射修改这个值,那么实际上是修改的这个拷贝的值,而不是原来的值。
所以 go 语言在这里做了一个限制,如果我们传递进 reflect.ValueOf 的变量是一个普通的变量,那么在我们设置反射对象的值的时候,会报错。
所以在上面这个例子中,我们传递了 x 的指针变量作为参数。这样,运行时就可以找到 x 本身,而不是 x 的拷贝,所以就可以修改 x 的值了。

但同时我们也注意到了,在上面这个例子中,v.CanSet() 返回的是 false,而 v.Elem().CanSet() 返回的是 true
这是因为,v 是一个指针,而 v.Elem() 是指针指向的值,对于这个指针本身,我们修改它是没有意义的,我们可以设想一下,
如果我们修改了指针变量(也就是修改了指针变量指向的地址),那会发生什么呢?那样我们的指针变量就不是指向 x 了,
而是指向了其他的变量,这样就不符合我们的预期了。所以 v.CanSet() 返回的是 false

v.Elem().CanSet() 返回的是 true。这是因为 v.Elem() 才是 x 本身,通过 v.Elem() 修改 x 的值是没有问题的。

深入理解 go reflect - 反射基本原理,go,golang,开发语言,后端

Elem 方法

不知道有多少读者和我一样,在初次使用 go 的反射的时候,被 Elem 这个方法搞得一头雾水。
Elem 方法的作用是什么呢?在回答这个问题之前,我们需要明确一点:reflect.Valuereflect.Type 这两个反射对象都有 Elem 方法,既然是不同的对象,那么它们的作用自然是不一样的。

reflect.Value 的 Elem 方法

reflect.ValueElem 方法的作用是获取指针指向的值,或者获取接口的动态值。也就是说,能调用 Elem 方法的反射对象,必须是一个指针或者一个接口。
在使用其他类型的 reflect.Value 来调用 Elem 方法的时候,会 panic:

var a = 1
// panic: reflect: call of reflect.Value.Elem on int Value
reflect.ValueOf(a).Elem()

// 不报错
var b = &a
reflect.ValueOf(b).Elem()

对于指针很好理解,其实作用类似解引用。而对于接口,还是要回到 interface 的结构本身,因为接口里包含了类型和数据本身,所以 Elem 方法就是获取接口的数据部分(也就是 ifaceeface 中的 data 字段)。

指针类型:

深入理解 go reflect - 反射基本原理,go,golang,开发语言,后端

接口类型:

深入理解 go reflect - 反射基本原理,go,golang,开发语言,后端

reflect.Type 的 Elem 方法

reflect.TypeElem 方法的作用是获取数组、chan、map、指针、切片关联元素的类型信息,也就是说,对于 reflect.Type 来说,
能调用 Elem 方法的反射对象,必须是数组、chan、map、指针、切片中的一种,其他类型的 reflect.Type 调用 Elem 方法会 panic

示例:

t1 := reflect.TypeOf([3]int{1, 2, 3}) // 数组 [3]int
fmt.Println(t1.String()) // [3]int
fmt.Println(t1.Elem().String()) // int

需要注意的是,如果我们要获取 map 类型 key 的类型信息,需要使用 Key 方法,而不是 Elem 方法。

m := make(map[string]string)
t1 := reflect.TypeOf(m)
fmt.Println(t1.Key().String()) // string

Interface 方法

这也是非常常用的一个方法,reflect.ValueInterface 方法的作用是获取反射对象的动态值
也就是说,如果反射对象是一个指针,那么 Interface 方法会返回指针指向的值。

简单来说,如果 var i interface{} = x,那么 reflect.ValueOf(x).Interface() 就是 i 本身,只不过其类型是 interface{} 类型。

Kind

说到反射,不得不提的另外一个话题就是 go 的类型系统,对于开发者来说,我们可以基于基本类型来定义各种新的类型,如:

// Kind 是 int
type myIny int
// Kind 是 Struct
type Person struct {
    Name string
    Age int
}

但是不管我们定义了多少种类型,在 go 看来都是下面的基本类型中的一个:

type Kind uint

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
	Pointer
	Slice
	String
	Struct
	UnsafePointer
)

也就是说,我们定义的类型在 go 的类型系统中都是基本类型的一种,这个基本类型就是 Kind
也正因为如此,我们可以通过有限的 reflect.TypeKind 来进行类型判断。
也就是说,我们在通过反射来判断变量的类型的时候,只需要枚举 Kind 中的类型,然后通过 reflect.TypeKind 方法来判断即可。

Type 表示的是反射对象(Type 对象是某一个 Kind,通过 Kind() 方法可以获取 Type 的 Kind),Kind 表示的是 go 底层类型系统中的类型。

比如下面的例子:

func display(path string, v reflect.Value) {
	switch v.Kind() {
	case reflect.Invalid:
		fmt.Printf("%s = invalid\n", path)
	case reflect.Slice, reflect.Array:
		for i := 0; i < v.Len(); i++ {
			display(fmt.Sprintf("%s[%d]", path, i), v.Index(i))
		}
	case reflect.Struct:
		for i := 0; i < v.NumField(); i++ {
			fieldPath := fmt.Sprintf("%s.%s", path, v.Type().Field(i).Name)
			display(fieldPath, v.Field(i))
		}
	case reflect.Map:
		for _, key := range v.MapKeys() {
			display(fmt.Sprintf("%s[%s]", path, formatAny(key)), v.MapIndex(key))
		}
	case reflect.Pointer:
		if v.IsNil() {
			fmt.Printf("%s = nil\n", path)
		} else {
			display(fmt.Sprintf("(*%s)", path), v.Elem())
		}
	case reflect.Interface:
		if v.IsNil() {
			fmt.Printf("%s = nil\n", path)
		} else {
			fmt.Printf("%s.type = %s\n", path, v.Elem().Type())
			display(path+".value", v.Elem())
		}
	default:
		fmt.Printf("%s = %s\n", path, formatAny(v))
	}
}

我们在开发的时候非常常用的结构体,在 go 的类型系统中,通通都是 Struct 这种类型的。

addressable

go 反射中最后一个很重要的话题是 addressable。在 go 的反射系统中有两个关于寻址的方法:CanAddrCanSet

CanAddr 方法的作用是判断反射对象是否可以寻址,也就是说,如果 CanAddr 返回 true,那么我们就可以通过 Addr 方法来获取反射对象的地址。
如果 CanAddr 返回 false,那么我们就不能通过 Addr 方法来获取反射对象的地址。对于这种情况,我们就无法通过反射对象来修改变量的值。

但是,CanAddrtrue 并不是说 reflect.Value 一定就能修改变量的值了。
reflect.Value 还有一个方法 CanSet,只有 CanSet 返回 true,我们才能通过反射对象来修改变量的值。

那么 CanAddr 背后的含义是什么呢?它意味着我们传递给 reflect.ValueOf 的变量是不是可以寻址的。
也就是说,我们的反射值对象拿到的是不是变量本身,而不是变量的副本。
如果我们是通过 &v 这种方式来创建反射对象的,那么 CanAddr 就会返回 true
反之,如果我们是通过 v 这种方式来创建反射对象的,那么 CanAddr 就会返回 false

如果想更详细的了解可以参考一下鸟窝的这篇文章 go addressable 详解。

获取类型信息 - reflect.Type

概述

reflect.Type 是一个接口,它代表了一个类型。我们可以通过 reflect.TypeOf 来获取一个类型的 reflect.Type 对象。
我们使用 reflect.Type 的目的通常是为了获取类型的信息,比如类型是什么、类型的名称、类型的字段、类型的方法等等。
又或者最常见的场景:结构体中的 jsontag,它是没有语义的,它的作用就是为了在序列化的时候,生成我们想要的字段名。
而这个 tag 就是需要通过反射来获取的。

通用的 Type 方法

在 go 的反射系统中,是使用 reflect.Type 这个接口来获取类型信息的。reflect.Type 这个接口有很多方法,下面这些方法是所有的类型通用的方法:

// Type 是 Go 类型的表示。
//
// 并非所有方法都适用于所有类型。
// 在调用 kind 具体方法之前,先使用 Kind 方法找出类型的种类。因为调用一个方法如果类型不匹配会导致 panic
//
// Type 类型值是可以比较的,比如用 == 操作符。所以它可以用做 map 的 key
// 如果两个 Type 值代表相同的类型,那么它们一定是相等的。
type Type interface {
	// Align 返回该类型在内存中分配时,以字节数为单位的字节数
	Align() int
	
	// FieldAlign 返回该类型在结构中作为字段使用时,以字节数为单位的字节数
	FieldAlign() int
	
	// Method 这个方法返回类型方法集中的第 i 个方法。
	// 如果 i 不在[0, NumMethod()]范围内,就会 panic。
	// 对于非接口类型 T 或 *T,返回的 Method 的 Type 和 Func 字段描述了一个函数,
	// 其第一个参数是接收者,并且只能访问导出的方法。
	// 对于一个接口类型,返回的 Method 的 Type 字段给出的是方法签名,没有接收者,Func字段为nil。
	// 方法是按字典序顺序排列的。
	Method(int) Method

	// MethodByName 返回类型的方法集中具有该名称的方法和一个指示是否找到该方法的布尔值。
	// 对于非接口类型 T 或 *T,返回的 Method 的 Type 和 Func 字段描述了一个函数,
	// 其第一个参数是接收者。
	// 对于一个接口类型,返回的 Method 的 Type 字段给出的是方法签名,没有接收者,Func字段为nil。
	MethodByName(string) (Method, bool)

	// NumMethod 返回使用 Method 可以访问的方法数量。
	// 对于非接口类型,它返回导出方法的数量。
	// 对于接口类型,它返回导出和未导出方法的数量。
	NumMethod() int

	// Name 返回定义类型在其包中的类型名称。
	// 对于其他(未定义的)类型,它返回空字符串。
	Name() string

	// PkgPath 返回一个定义类型的包的路径,也就是导入路径,导入路径是唯一标识包的类型,如 "encoding/base64"。
	// 如果类型是预先声明的(string, error)或者没有定义(*T, struct{}, []int,或 A,其中 A 是一个非定义类型的别名),包的路径将是空字符串。
	PkgPath() string

	// Size 返回存储给定类型的值所需的字节数。它类似于 unsafe.Sizeof.
	Size() uintptr

	// String 返回该类型的字符串表示。
	// 字符串表示法可以使用缩短的包名。
	// (例如,使用 base64 而不是 "encoding/base64")并且它并不能保证类型之间是唯一的。如果是为了测试类型标识,应该直接比较类型 Type。
	String() string

	// Kind 返回该类型的具体种类。
	Kind() Kind

	// Implements 表示该类型是否实现了接口类型 u。
	Implements(u Type) bool

	// AssignableTo 表示该类型的值是否可以分配给类型 u。
	AssignableTo(u Type) bool

	// ConvertibleTo 表示该类型的值是否可转换为 u 类型。
	ConvertibleTo(u Type) bool

	// Comparable 表示该类型的值是否具有可比性。
	Comparable() bool
}

某些类型特定的 Type 方法

下面是某些类型特定的方法,对于这些方法,如果我们使用的类型不对,则会 panic

type Type interface {
	// Bits 以 bits 为单位返回类型的大小。
	// 如果类型的 Kind 不属于:sized 或者 unsized Int, Uint, Float, 或者 Complex,会 panic。
	Bits() int

	// ChanDir 返回一个通道类型的方向。
	// 如果类型的 Kind 不是 Chan,会 panic。
	ChanDir() ChanDir

	// IsVariadic 表示一个函数类型的最终输入参数是否为一个 "..." 可变参数。如果是,t.In(t.NumIn() - 1) 返回参数的隐式实际类型 []T.
	// 更具体的,如果 t 代表 func(x int, y ... float64),那么:
	// t.NumIn() == 2
	// t.In(0)是 "int" 的 reflect.Type 反射类型。
	// t.In(1)是 "[]float64" 的 reflect.Type 反射类型。
	// t.IsVariadic() == true
	// 如果类型的 Kind 不是 Func,IsVariadic 会 panic
	IsVariadic() bool

	// Elem 返回一个 type 的元素类型。
	// 如果类型的 Kind 不是 Array、Chan、Map、Ptr 或 Slice,就会 panic
	Elem() Type

	// Field 返回一个结构类型的第 i 个字段。
	// 如果类型的 Kind 不是 Struct,就会 panic。
	// 如果 i 不在 [0, NumField()) 范围内也会 panic。
	Field(i int) StructField

	// FieldByIndex 返回索引序列对应的嵌套字段。它相当于对每一个 index 调用 Field。
	// 如果类型的 Kind 不是 Struct,就会 panic。
	FieldByIndex(index []int) StructField

	// FieldByName 返回给定名称的结构字段和一个表示是否找到该字段的布尔值。
	FieldByName(name string) (StructField, bool)

	// FieldByNameFunc 返回一个能满足 match 函数的带有名称的 field 字段。布尔值表示是否找到。
	FieldByNameFunc(match func(string) bool) (StructField, bool)

	// In 返回函数类型的第 i 个输入参数的类型。
	// 如果类型的 Kind 不是 Func 类型会 panic。
	// 如果 i 不在 [0, NumIn()) 的范围内,会 panic。
	In(i int) Type

	// Key 返回一个 map 类型的 key 类型。
	// 如果类型的 Kind 不是 Map,会 panic。
	Key() Type

	// Len 返回一个数组类型的长度。
	// 如果类型的 Kind 不是 Array,会 panic。
	Len() int

	// NumField 返回一个结构类型的字段数目。
	// 如果类型的 Kind 不是 Struct,会 panic。
	NumField() int

	// NumIn 返回一个函数类型的输入参数数。
	// 如果类型的 Kind 不是Func.NumIn(),会 panic。
	NumIn() int

	// NumOut 返回一个函数类型的输出参数数。
	// 如果类型的 Kind 不是 Func.NumOut(),会 panic。
	NumOut() int

	// Out 返回一个函数类型的第 i 个输出参数的类型。
	// 如果类型的 Kind 不是 Func,会 panic。
	// 如果 i 不在 [0, NumOut()) 的范围内,会 panic。
	Out(i int) Type
}

创建 reflect.Type 的方式

我们可以通过下面的方式来获取变量的类型信息(创建 reflect.Type 的方式):

深入理解 go reflect - 反射基本原理,go,golang,开发语言,后端

获取值信息 - reflect.Value

概述

reflect.Value 是一个结构体,它代表了一个值。
我们使用 reflect.Value 可以实现一些接收多种类型参数的函数,又或者可以让我们在运行时针对值的一些信息来进行修改。
常常用在接收 interface{} 类型参数的方法中,因为参数是接口类型,所以我们可以通过 reflect.ValueOf 来获取到参数的值信息。
比如类型、大小、结构体字段、方法等等。

同时,我们可以对这些获取到的反射值进行修改。这也是反射的一个重要用途。

reflect.Value 的方法

reflect.Value 这个 Sreuct 同样有很多方法:具体可以分为以下几类:

  1. 设置值的方法:Set*SetSetBoolSetBytesSetCapSetComplexSetFloatSetIntSetLenSetMapIndexSetPointerSetStringSetUint。通过这类方法,我们可以修改反射值的内容,前提是这个反射值得是合适的类型。CanSet 返回 true 才能调用这类方法
  2. 获取值的方法:InterfaceInterfaceDataBoolBytesComplexFloatIntStringUint。通过这类方法,我们可以获取反射值的内容。前提是这个反射值是合适的类型,比如我们不能通过 complex 反射值来调用 Int 方法(我们可以通过 Kind 来判断类型)。
  3. map 类型的方法:MapIndexMapKeysMapRangeMapSet
  4. chan 类型的方法:CloseRecvSendTryRecvTrySend
  5. slice 类型的方法:LenCapIndexSliceSlice3
  6. struct 类型的方法:NumFieldNumMethodFieldFieldByIndexFieldByNameFieldByNameFunc
  7. 判断是否可以设置为某一类型:CanConvertCanComplexCanFloatCanIntCanInterfaceCanUint
  8. 方法类型的方法:MethodMethodByNameCallCallSlice
  9. 判断值是否有效:IsValid
  10. 判断值是否是 nilIsNil
  11. 判断值是否是零值:IsZero
  12. 判断值能否容纳下某一类型的值:OverflowOverflowComplexOverflowFloatOverflowIntOverflowUint
  13. 反射值指针相关的方法:AddrCanAddrtrue 才能调用)、UnsafeAddrPointerUnsafePointer
  14. 获取类型信息:TypeKind
  15. 获取指向元素的值:Elem
  16. 类型转换:Convert

Len 也适用于 slicearraychanmapstring 类型的反射值。

创建 reflect.Value 的方式

我们可以通过下面的方式来获取变量的值信息(创建 reflect.Value 的方式):

深入理解 go reflect - 反射基本原理,go,golang,开发语言,后端文章来源地址https://www.toymoban.com/news/detail-802989.html

总结

  • reflect 包提供了反射机制,可以在运行时获取变量的类型信息、值信息、方法信息等等。
  • go 中的 interface{} 实际上包含了两个指针,一个指向类型信息,一个指向值信息。正因如此,我们可以在运行时通过 interface{} 来获取变量的类型信息、值信息。
  • reflect.Type 代表一个类型,reflect.Value 代表一个值。通过 reflect.Type 可以获取类型信息,通过 reflect.Value 可以获取值信息。
  • 反射三定律:
    • 反射可以将 interface 类型变量转换成反射对象。
    • 反射可以将反射对象还原成 interface 对象。
    • 如果要修改反射对象,那么反射对象必须是可设置的(CanSet)。
  • reflect.Valuereflect.Type 里面都有 Elem 方法,但是它们的作用不一样:
    • reflect.TypeElem 方法返回的是元素类型,只适用于 array、chan、map、pointer 和 slice 类型的 reflect.Type
    • reflect.ValueElem 方法返回的是值,只适用于接口或指针类型的 reflect.Value
  • 通过 reflect.ValueInterface 方法可以获取到反射对象的原始变量,但是是 interface{} 类型的。
  • TypeKind 都表示类型,但是 Type 是类型的反射对象,Kind 是 go 类型系统中最基本的一些类型,比如 intstringstruct 等等。
  • 如果我们想通过 reflect.Value 来修改变量的值,那么 reflect.Value 必须是可设置的(CanSet)。同时如果想要 CanSet 为 true,那么我们的变量必须是可寻址的。
  • 我们有很多方法可以创建 reflect.Typereflect.Value,我们需要根据具体的场景来选择合适的方法。
  • reflect.Typereflect.Value 里面,都有一部分方法是通用的,也有一部分只适用于特定的类型。如果我们想要调用那些适用于特定类型的方法,那么我们必须先判断 reflect.Typereflect.Value 的类型(这里说的是 Kind),然后再调用。

到了这里,关于深入理解 go reflect - 反射基本原理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Golang | reflect】利用反射实现方法的调用

    go语言中,如果某个数据类型实现了一系列的方法,如何批量去执行呢,这时候就可以利用反射里的 func (v Value) Call(in []Value) []Value 方法。 Call 方法实际使用时主要有以下两种调用方式: 注: 1、这里说明下,为什么使用 Func 调用 Call 时第一个入参是对应receiver本身 method.Func

    2024年02月07日
    浏览(55)
  • go 笔记 十二章 断言 assertion 和 反射 reflect

    断言 把一个接口类型指定为它的原始类型 反射 官方说法:在编译时不知道类型的情况下,可更新变量、运行时查看值、调用方法以及直接对他们的布局进行操作的机制,称为反射。 通俗说法:可以知道变量原始数据类型和内容、方法等,并且可以进行一定的操作 为什么要

    2024年02月17日
    浏览(45)
  • 深入理解索引B+树的基本原理

    目录 1. 引言 2. 为什么要使用索引? 3. 索引的概述 4. 索引的优点是什么? 4.1 降低数据库的IO成本,提高数据查找效率 4.2 保证数据库每一行数据的唯一性 4.3 加速表与表之间的连接 4.4 减少查询中分组与排序的执行时间  5. 索引的缺点是什么? 5.1 创建索引和维护索引非常耗费

    2024年02月12日
    浏览(44)
  • 深入了解Golang中的反射机制

    目录 反射 反射的分类 值反射 类型反射 运行时反射 编译时反射 接口反射 结构体反射 常用函数 值反射 类型反射 值反射和类型反射的区别 结构体反射 示例代码         反射是指在程序运行时动态地检查和修改对象的能力。在Go语言中,通过反射可以在运行时检查变量的

    2024年02月06日
    浏览(48)
  • Go语言入门记录:从channel的池应用、sync的Pool、benchmark、反射reflect、json处理、http、性能分析和一些编程习惯

    channel的一对一会阻塞,添加buffer不会阻塞。 buffered Channel实现对象池。 sync.Pool 的介绍。 获取时先去私有对象中获取,如果不存在就在相同Processor中的共享池中获取,如果还没有,则去其他Processor中去获取。 存放时,如果私有对象不存在,就放在私有对象中,如果存在就放在

    2024年02月10日
    浏览(48)
  • 【netty系列-01】深入理解网络通信基本原理和tcp/ip协议

    Netty系列整体栏目 内容 链接地址 【一】深入理解网络通信基本原理和tcp/ip协议 https://zhenghuisheng.blog.csdn.net/article/details/136359640 【二】深入理解Socket本质和BIO https://zhenghuisheng.blog.csdn.net/article/details/136549478 在最初的网络中,是借鉴于这个OSI七层网络模型,而在实际开发应用中

    2024年03月17日
    浏览(59)
  • 【网络层+数据链路层】深入理解IP协议和MAC帧协议的基本原理

    文章目录 前言 一、IP协议 二、MAC帧协议 1.以太网 2.以太网帧(MAC帧)格式报头 3.基于协议讲解局域网转发的原理 总结 为什么经常将TCP/IP放在一起呢?这是因为IP层的核心工作就是通过IP地址来定位主机的,具有将一个数据报从A主机跨网络发送到B主机的能力,而TCP所提供的策

    2024年02月13日
    浏览(36)
  • Java开发 - 深入理解Redis哨兵机制原理

    Redis的主从、哨兵模式、集群模式,在前文中都已经有了详细的搭建流程,可谓是手把手教程,也得到了很多朋友的喜欢。由于前文偏向于应用方面,就导致了理论知识的匮乏,我们可能会用了,但却不明所以,所以今天,博主就通过接下里的几篇博客给大家分别讲解Redis哨兵

    2024年02月17日
    浏览(38)
  • 【Java基础】深入理解反射、反射的应用(工厂模式、代理模式)

    Java 反射机制是指在 运行时动态地获取和操作类的信息、调用对象的方法和访问对象的属性的能力 。通过反射,可以在程序运行时分析和修改类的结构、行为和状态。 Java 反射机制提供了以下功能: 获取类的信息:可以获取类的名称、修饰符、父类、实现的接口等。 创建对

    2024年02月09日
    浏览(56)
  • 深入理解 Webpack 热更新原理:提升开发效率的关键

    🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_ CSDN 博客专家、23年度博客之星前端领域TOP1 🕠 牛客 高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课 签约作者、上架课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你

    2024年03月10日
    浏览(81)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包