Go part 7 反射,反射类型对象,反射值对象
反射
反射是指在程序运行期间对程序本身进行访问和修改的能力,(程序在编译时,变量被转换为内存地址,变量名不会被编译器写入到可执行部分,在运行程序时,程序无法获取自身的信息)
支持反射的语言可以在程序编译期间将变量的反射信息,如字段名称、类型等信息整合到可执行文件中,并给程序提供接口访问反射信息,这样就可以在程序运行期间获取类型的反射信息,并且有能力修改它们
Go 程序在运行期间使用 reflect 包访问程序的反射信息
像 Python,JavaScript 类动态语言,由于本身的语法特性就可以让代码运行期间访问程序自身的值和类型信息,因此不需要反射系统
反射类型对象(reflect.Type)
使用 reflect.TypeOf() 函数可以获取变量的反射类型对象(reflect.Type)
typeOfTest := reflect.TypeOf(test)
通过反射类型对象获取类型信息
通过反射类型对象可以获取自身的类型信息,使用 Name() 方法获取类型名称,使用 Kind() 方法获取类型归属的种类
package main
import (
"fmt"
"reflect"
) func GetReflectInfo(a interface{}) {
// 获取变量 a 的类型对象,类型对象的类型是 reflect.Type
var typeOfA reflect.Type = reflect.TypeOf(a)
fmt.Printf("%T\n", typeOfA)
// 打印类型名 和 种类
fmt.Println(typeOfA.Name(), typeOfA.Kind())
} func main() {
GetReflectInfo("666")
} 运行结果:
*reflect.rtype
string string
发现类型和种类都是 string,很奇怪是不是,接着看下面的例子 ...
理解反射类型对象的类型(Type)和种类(Kind)
编程中,使用最多的是类型,但在反射中,当需要区分一个大品种的类型时,就会用到种类(Kind)
1)类型(Type)
类型指的是系统原生数据类型,如 int、string、bool、float32 等类型,以及使用 type 关键字自定义的类型,自定义类型需要指定类型名称,例如,type A struct {},类型名就是 A
2)种类(Kind)
种类指的是对象归属的大品种,在 reflect 包中有如下定义:
type Kind uint const (
Invalid Kind = iota // 非法类型
Bool // 布尔型
Int // 有符号整型
Int8 // 有符号8位整型
Int16 // 有符号16位整型
Int32 // 有符号32位整型
Int64 // 有符号64位整型
Uint // 无符号整型
Uint8 // 无符号8位整型
Uint16 // 无符号16位整型
Uint32 // 无符号32位整型
Uint64 // 无符号64位整型
Uintptr // 指针
Float32 // 单精度浮点数
Float64 // 双精度浮点数
Complex64 // 64位复数类型
Complex128 // 128位复数类型
Array // 数组
Chan // 通道
Func // 函数
Interface // 接口
Map // 映射
Ptr // 指针
Slice // 切片
String // 字符串
Struct // 结构体
UnsafePointer // 底层指针
)
Map、Slice、Chan 属于引用类型,使用起来类似于指针,但是在种类常量定义中仍然属于独立的种类,不属于 Ptr
type A struct{} 结构体属于 Struct 种类,*A 属于 Ptr 种类
3)从自定义的类型对象中获取类型名称和类型种类(加强理解)
package main import (
"fmt"
"reflect"
) type Cat struct{}
type Enum int func main(){
var cat Cat = Cat{}
var num Enum = 1 //对 cat 变量使用反射
var typeOfCat reflect.Type= reflect.TypeOf(cat)
fmt.Println(typeOfCat.Name(), typeOfCat.Kind()) //对 num 变量使用反射
typeOfNum := reflect.TypeOf(num)
fmt.Println(typeOfNum.Name(), typeOfNum.Kind())
} 运行结果:
Cat struct
Enum int
通过反射类型对象获取指针指向的元素类型
对指针类型的变量获取反射类型对象后,可以通过 reflect.Elem() 方法获取这个指针指向的元素类型,这个过程被称之为取元素,等效于对指针类型变量做了一个 * 操作
dmeo:cat 指针变量的反射类型对象的类型名称是 空字符串(为什么不是 *cat),种类是 ptr,获取值之后的类型名是 cat,种类是 struct
package main
import (
"fmt"
"reflect"
) type Cat struct {} func GetPointerReflectInfo(a interface{}) {
//获取指针类型的反射类型对象
typeOfA := reflect.TypeOf(a)
fmt.Printf("NameType:%T Name:%v Kind:%v\n", typeOfA.Name(), typeOfA.Name(), typeOfA.Kind()) //取指针类型的元素
typeOfA = typeOfA.Elem()
fmt.Printf("Name:%v Kind:%v\n", typeOfA.Name(), typeOfA.Kind())
} func main(){
//创建 cat 类型的指针实例
var cat *Cat = new(Cat)
GetPointerReflectInfo(cat)
} 运行结果:
NameType:string Name: Kind:ptr
Name:Cat Kind:struct
通过反射类型对象获取结构体字段的类型信息
变量通过 reflect.TypeOf() 函数获取反射类型对象后,如果反射类型对象的种类是 struct,那么可以通过反射类型对象(reflect.Type)的 NumField() 和 Field() 等方法获得结构体字段的详细信息,以结构体字段类型(StructField)返回
种类是 struct 的反射类型对象(reflect.Type)可以通过调用下面的方法来获取:
方法 | 说明 |
---|---|
Field(i int) StructField | 根据索引,返回索引对应的StructField。当值不是结构体或索引超界时发生宕机 |
NumField() int | 返回结构体成员字段数量。当类型不是结构体或索引超界时发生宕机 |
FieldByName(name string) (StructField, bool) | 根据给定字符串返回字符串对应的StructField。没有找到时 bool 返回 false,当类型不是结构体或索引超界时发生宕机 |
FieldByIndex(index []int) StructField | 多层成员访问时,根据 []int 提供的每个结构体的字段索引,返回StructField。没有找到时返回零值。当类型不是结构体或索引超界时 发生宕机 |
FieldByNameFunc( match func(string) bool) (StructField,bool) | 根据匹配函数匹配需要的字段。当值不是结构体或索引超界时发生宕机 |
结构体字段类型(StructField)也是一个结构体种类,里面包含的字段有 Name、PkgPath、Type 等,StructField 的结构如下:
type StructField struct {
Name string // 字段名
PkgPath string // 字段路径
Type Type // 字段反射类型对象
Tag StructTag // 字段的结构体标签
Offset uintptr // 字段在结构体中的相对偏移
Index []int // Type.FieldByIndex中的返回的索引值
Anonymous bool // 是否为匿名字段
}
demo:实例化一个结构体,并给字段赋值,然后获取该结构体变量的反射类型对象(reflect.Type),然后调用 FieldByName() 方法查找结构体中指定 Name 的字段,最后直接获取到了字段的结构体数据
package main
import (
"fmt"
"reflect"
) type Cat struct {
Name string
Type int `json:"type" id:"100"`
} func main(){
//创建猫的结构体实例
var cat Cat = Cat{"tom", 66}
//获取反射类型对象
typeOfCat := reflect.TypeOf(cat) //通过索引遍历结构体字段
for i:=0; i<typeOfCat.NumField(); i++ {
var catField reflect.StructField = typeOfCat.Field(i)
fmt.Printf("%v, %v\n", catField.Name, catField.Tag)
} //通过 Type 字段查找字段信息
catTagField, ok := typeOfCat.FieldByName("Type")
if ok {
//从 Tag 字段中通过 Get() 方法获取到指定的 tag,没有取到默认为 空 string
fmt.Printf("'%v', '%v', '%v'\n", catTagField.Tag.Get("json"), catTagField.Tag.Get("id"), catTagField.Tag.Get("nil"))
}
} 运行结果:
Name,
Type, json:"type" id:"100"
'type', '100', ''
反射值对象(reflect.Value)
反射不仅可以获取变量的类型信息,还可以动态的获取 和 修改变量的值
使用 reflect.ValueOf() 函数得到变量的反射值对象(reflect.Value),进而获取 和 修改变量的值
valueOfTest := reflect.ValueOf(test)
从反射值对象中获取值
可以通过下面几种方法从反射值对象(reflect.Value)中获取值,如下表:
方法名 | 说 明 |
---|---|
Interface() interface {} | 将值以 interface{} 类型返回,可以通过类型断言转换为指定类型 |
Int() int64 | 将值以 int 类型返回,所有有符号整型均可以此方式返回 |
Uint() uint64 | 将值以 uint 类型返回,所有无符号整型均可以此方式返回 |
Float() float64 | 将值以双精度(float64)类型返回,所有浮点数(float32、float64)均可以此方式返回 |
Bool() bool | 将值以 bool 类型返回 |
Bytes() []bytes | 将值以字节数组 []bytes 类型返回 |
String() string | 将值以字符串类型返回 |
demo:首先获取到反射值对象(reflect.Value),然后通过两种方式获取值,一个是调用 反射值对象的 Interface() 方法,这个方法返回的是 interface{} 类型,需要经过类型断言获取值,另外一个是直接调用反射值对象的 Int() 方法,获取到 int64 的值
package main
import (
"fmt"
"reflect"
) func GetReflectValue(a interface{}) {
var valueOfA reflect.Value = reflect.ValueOf(a)
fmt.Printf("%v, %T\n", valueOfA.Interface(), valueOfA.Interface()) //通过类型断言,将 interface{} 转换为类型
var getA int = valueOfA.Interface().(int)
//反射值对象直接调用 Int() 获取64位的值
var getInt64A int64 = valueOfA.Int()
fmt.Printf("%v, %v\n", getA, getInt64A)
} func main() {
var num int = 1024
GetReflectValue(num) //var str string = "hello"
//GetReflectValue(str)
} 运行结果:
1024, int
1024, 1024
但如果 a 变量是 string,会触发 panic,是不合理的,目前认为需要做断言判断处理
通过反射值对象获取指针指向的元素的值
对指针类型变量获取反射值对象后,可以通过 Elem() 方法获取这个指针指向的元素类型,这个过程被称之为取元素,等效于对指针类型变量做了一个 * 操作
demo:cat 指针变量的反射值对象的值是一个指针类型的结构体,获取元素(*)之后的值是一个空的结构体,然后通过 FieldByName() 方法获取到 Name 的值,并设置 Name 的值
package main
import (
"fmt"
"reflect"
) type Cat struct {
Name string
} func GetPointerReflectValue(cat interface{}) {
valueOfCat := reflect.ValueOf(cat)
fmt.Printf("%T %v\n", valueOfCat, valueOfCat) //获取指针类型的元素
valueOfCat = valueOfCat.Elem()
//打印指针内元素的类型,发现是一样的
fmt.Printf("%T %v\n", valueOfCat, valueOfCat)
//打印结构体元素中 Name 字段的值
fmt.Printf("'%v'\n", valueOfCat.FieldByName("Name"))
//拿到结构体元素中 Name 字段的值
valueFieldName := valueOfCat.FieldByName("Name")
//设置值,再次打印结构体元素中 Name 字段的值,设置成功了
valueFieldName.SetString("johny")
fmt.Printf("'%v'\n", valueOfCat.FieldByName("Name"))
} func main(){
var cat *Cat = new(Cat)
GetPointerReflectValue(cat)
} 运行结果:
reflect.Value &{}
reflect.Value {}
''
'johny'
通过反射值对象访问结构体字段的值信息
变量通过 reflect.ValueOf() 函数获取反射值对象(reflect.Value)后,反射值对象提供访问结构体的方法,通过这些方法可以访问到结构体中任意字段的值
方 法 | 备 注 |
---|---|
Field(i int) Value | 根据索引,返回索引对应的结构体字段的反射值对象。当值不是结构体或索引超界时发生宕机 |
NumField() int | 返回结构体字段数量。当值不是结构体或索引超界时发生宕机 |
FieldByName(name string) Value | 根据给定字符串返回字符串对应的结构体字段。没有找到时返回无效的值<invalid reflect.Value>,当值不是结构体或索引超界时发生宕机 |
FieldByIndex(index []int) Value | 多层成员访问时,根据 []int 提供的每个结构体的字段索引,返回字段的值。 没有找到时返回零值,当值不是结构体或索引超界时发生宕机 |
FieldByNameFunc(match func(string) bool) Value | 根据匹配函数匹配需要的字段。找到时返回零值,当值不是结构体或索引超界时发生宕机 |
demo:下面构造了一个结构体包含不同类型的成员,通过上面提供的反射值对象的方法,可以获取到结构体的值信息
package main
import (
"fmt"
"reflect"
) type Person struct {
a int
b string
float64
bool next *Person
} func GetStructReflectValue(person interface{}) {
//对结构体进行反射(值包装结构体)
var valueOfPerson reflect.Value = reflect.ValueOf(person) //遍历反射值对象
for i := 0; i < valueOfPerson.NumField(); i++ {
personField := valueOfPerson.Field(i) //根据索引获取字段值信息
//输出字段值的类型
fmt.Println(personField.Type())
} //根据名字查找字段
personFieldA := valueOfPerson.FieldByName("a")
fmt.Println(personFieldA.Type()) //多层成员访问,根据 []int 提供的索引查找字段
personFieldInner := valueOfPerson.FieldByIndex([]int{4, 2})
fmt.Println(personFieldInner.Type(), personFieldInner.Float()) } func main() {
var person Person = Person{next: &Person{}}
GetStructReflectValue(person)
} 运行结果:
int
string
float64
bool
*main.Person
int //根据名字查找字段的值类型
float64 0 //多层成员访问,得到的字段的值类型
多层成员访问中:[]int{4,2} 中的 4 表示,在 Person 结构体中索引值为 4 的成员,也就是 next。next 的类型为 Person,也是一个结构体,因此使用 []int{4,2} 中的 2 继续在 next 值的基础上索引,结构为 Person 中索引值为 2 的匿名字段 float64,所以打印的类型为 float64,初始值是 0
思考:获取结构体字段的值有个前提,是需要知道字段的类型,然后调用对应的方法,比如字段是 float64类型,那么我可以使用 Float() 方法获取,要是不知道呢?那是否需要做分支判断了?
判断反射值的 Nil 和 Valid
反射值对象(reflect.Value)提供两个方法进行 空值 和 有效性 的判断
方 法 | 说 明 | |
---|---|---|
IsNil() bool | 返回值是否为 nil。如果变量类型不是 channel、函数、接口、map、指针或 切片时发生 panic,类似于语言层的v== nil 操作 |
|
IsValid() bool | 判断值是否有效。 当值本身非法时,返回 false,例如 reflect.Value不包含任何值,值为 nil 等。 |
IsNil() 常被用于判断指针是否为空;IsValid() 常被用于判定返回值是否有效,使用好这两个方法,可以规避 panic
通过反射值对象修改值
使用反射值对象(reflect.Value)修改值,需要遵循一些规则,如果没有按照规则编码代码,轻则无法修改成功,重则程序在运行时会发生宕机
修改值分两步,首先调用方法取到指针或地址,然后调用修改方法
1)通过反射值对象(reflect.Value)获取指针、地址的方法见下表:
方法名 | 备 注 |
---|---|
Elem() Value | 取值指向的元素值,类似于语言层* 操作。当值类型不是指针或接口时发生宕机,空指针时返回 nil 的 Value |
Addr() Value | 对可寻址的值返回其地址,类似于语言层& 操作。当值不可寻址时发生宕机 |
CanAddr() bool | 表示值是否可寻址 |
CanSet() bool | 返回值能否被修改。要求值可寻址且是导出的字段,返回 false 时,仍然修改值时会发生宕机 |
2)通过反射值对象(reflect.Value)修改值的方法见下表:
Set(x Value) | 将值设置为传入的反射值对象的值 |
---|---|
Setlnt(x int64) | 使用 int64 设置值。当值的类型不是 int、int8、int16、 int32、int64 时会发生宕机 |
SetUint(x uint64) | 使用 uint64 设置值。当值的类型不是 uint、uint8、uint16、uint32、uint64 时会发生宕机 |
SetFloat(x float64) | 使用 float64 设置值。当值的类型不是 float32、float64 时会发生宕机 |
SetBool(x bool) | 使用 bool 设置值。当值的类型不是 bool 时会发生宕机 |
SetBytes(x []byte) | 设置字节数组 []bytes值。当值的类型不是 []byte 时会发生宕机 |
SetString(x string) | 设置字符串值。当值的类型不是 string 时会发生宕机 |
3)值可以被修改的条件之一:可被寻址
简单来说,就是参数必须传递指针类型,实例代码如下:
package main
import (
"fmt"
"reflect"
) func ModifyReflectValue(a interface{}) {
valueOfA := reflect.ValueOf(a)
valueOfA.SetInt(666)
} func main() {
var num int = 1024
ModifyReflectValue(num)
fmt.Println(num)
} 运行结果:
panic: reflect: reflect.Value.SetInt using unaddressable value
报错是说:SetInt 正在使用一个不能被寻址的值,是因为传入的是 a 的值,而不是 a 的地址,修改一下,传入 a 的指针就好了
package main
import (
"fmt"
"reflect"
) func ModifyReflectValue(a interface{}) {
valueOfA := reflect.ValueOf(a) //先取出 a 地址的值
valueA := valueOfA.Elem()
//设置 a 的值
valueA.SetInt(666)
} func main() {
var num int = 1024
ModifyReflectValue(&num)
fmt.Println(num)
} 运行结果:
666
4)值可以被修改的条件之二:可被导出
结构体成员中,如果字段不能被导出,就不能被修改,代码如下:
package main
import (
"fmt"
"reflect"
) type Dog struct {
name string
} func ModifyReflectValue(dog interface{}) {
valueOfDog := reflect.ValueOf(dog) //先取到 dog 实例地址的元素
valueOfDog = valueOfDog.Elem()
//通过名称取到 name 字段的值信息
valueFieldName := valueOfDog.FieldByName("name") //设置 a 的值
valueFieldName.SetString("二哈")
} func main() {
var dog *Dog = &Dog{"xiaobai"}
ModifyReflectValue(dog)
fmt.Println(dog.name)
} 运行结果:
panic: reflect: reflect.Value.SetString using value obtained using unexported field
报错是说:SetString 正在使用值来自一个未导出的字段,为了能修改这个值,需要该字段支持被导出,即 name 首字母需大写 Name,让反射可以访问,正确代码如下:
package main
import (
"fmt"
"reflect"
) type Dog struct {
Name string
} func ModifyReflectValue(dog interface{}) {
valueOfDog := reflect.ValueOf(dog) //先取到 dog 实例地址的元素
valueOfDog = valueOfDog.Elem()
//通过名称取到 name 字段的值信息
valueFieldName := valueOfDog.FieldByName("Name") //设置 a 的值
valueFieldName.SetString("二哈")
} func main() {
var dog *Dog = &Dog{"xiaobai"}
ModifyReflectValue(dog)
fmt.Println(dog.Name)
} 运行结果:
二哈
创建反射类型对象
无法创建反射值对象
当已知反射类型对象(reflect.Type)时,可以动态的创建这个类型的实例,类型为指针类型,例如 reflect.Type 的类型名为 Cat 时,创建 Cat 的指针,即 *Cat,代码如下:
package main
import (
"fmt"
"reflect"
) type Cat struct {
Name string
} func CreateReflectObject(cat interface{}){
//获取反射类型对象
typeOfCat := reflect.TypeOf(cat)
fmt.Printf("'%v' '%v'\n", typeOfCat.Name(), typeOfCat.Kind()) //typeOfCat.Type undefined
//如果没有获取元素,下面会直接 New 反射类型对象的指针
typeOfCat = typeOfCat.Elem()
fmt.Printf("'%v' '%v'\n", typeOfCat.Name(), typeOfCat.Kind()) //typeOfCat.Type undefined
//手动创建反射类型对象,实例的类型为指针
newTypeOfCat := reflect.New(typeOfCat)
fmt.Printf("%v %v\n", newTypeOfCat.Type(), newTypeOfCat.Kind())
//获取指针所指向的元素
newTypeOfCat = newTypeOfCat.Elem()
//打印元素的类型和种类
fmt.Printf("%v %v\n", newTypeOfCat.Type(), newTypeOfCat.Kind())
//fmt.Printf("%v %v\n", typeOfCatCopy.Name(), typeOfCatCopy.Kind()) //typeOfCatCopy.Name undefined
} func main(){
var cat *Cat = new(Cat)
CreateReflectObject(cat)
} 运行结果:
'' 'ptr'
'Cat' 'struct'
*main.Cat ptr
main.Cat struct
反射值对象调用函数
当反射值对象(reflect.Value)中的值类型为函数时,可以使用反射值对象调用该函数,使用时,需要将参数使用反射值对象的切片 []reflect.Value 构造后传入 Call() 方法中,调用完成时,函数的返回值通过反射值对象切片([]reflect.Value)返回
demo:下面的代码中声明一个加法函数,传入两个整型值,返回两个整型值的和,将函数保存在反射值对象(reflect.Value)中,然后将两个整型的值构造成反射值对象切片([]reflect.Value),调用 Call() 方法,返回也是反射值对象切片([]reflect.Value)
package main
import (
"fmt"
"reflect"
) func add(a, b int) int {
return a + b
} func main(){
//将函数包装为反射值对象
valueOfAdd := reflect.ValueOf(add) //构造函数参数,传入两个整型的值
var paramSlice []reflect.Value = []reflect.Value{reflect.ValueOf(100), reflect.ValueOf(200)} //调用函数,返回值也是 []reflect.Value
result := valueOfAdd.Call(paramSlice)
fmt.Println(result[0].Int())
} 运行结果:
300
反射调用函数的过程需要构造大量的 reflect.Value 和中间变量,对函数参数值进行逐一检查,还需要将调用参数复制到调用函数的参数内存中。调用完毕后,还需要将返回值转换为 reflect.Value,用户还需要从中取出调用值。因此,反射调用函数的性能问题尤为突出,不建议大量使用反射函数调用
end ~
Go part 7 反射,反射类型对象,反射值对象的更多相关文章
- redis 系列15 数据对象的(类型检查,内存回收,对象共享)和数据库切换
一. 概述 对于前面的五章中,已清楚了数据对象的类型以及命令实现,其实还有一种数据对象为HyperLogLog,以后需要用到再了解.下面再了解类型检查,内存回收,对象共享,对象的空转时长. 1.1 ...
- [0] DDD领域驱动设计(二) 之 值对象
DDD中实体对象与值对象的解释比较抽象.主要根据持续性与 ID 识别来区分. ID并非某一对象的直观自然属性,而是在分析建模之 后,赋给模型中的实体类,来达到跟踪,区别,存储目的的一个特值. 结合项目 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (44) ------ 第八章 POCO之POCO中使用值对象和对象变更通知
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 8-4 POCO中使用值对象(Complex Type--也叫复合类型)属性 问题 ...
- 应用程序框架实战十六:DDD分层架构之值对象(介绍篇)
前面介绍了DDD分层架构的实体,并完成了实体层超类型的开发,同时提供了验证方面的支持.本篇将介绍另一个重要的构造块——值对象,它是聚合中的主要成分. 如果说你已经在使用DDD分层架构,但你却从来没有使 ...
- DDD 领域驱动设计-三个问题思考实体和值对象(续)
上一篇:DDD 领域驱动设计-三个问题思考实体和值对象 说实话,整理现在这一篇博文的想法,在上一篇发布出来的时候就有了,但到现在才动起笔来,而且写之前又反复读了上一篇博文的内容及评论,然后去收集资料, ...
- 正确理解DTO、值对象和POCO
今天推荐的文章比较技术化也比较简单,但是对于一些初学者而言,可能也是容易搞混的概念:就是如何理解DTO.值对象和POCO之间的区别. 所谓DTO就是数据传输对象(Data Transfer Objec ...
- DDD分层架构之值对象(介绍篇)
DDD分层架构之值对象(介绍篇) 前面介绍了DDD分层架构的实体,并完成了实体层超类型的开发,同时提供了验证方面的支持.本篇将介绍另一个重要的构造块——值对象,它是聚合中的主要成分. 如果说你已经在使 ...
- DDD理论学习系列(7)-- 值对象
DDD理论学习系列--案例及目录 1.引言 提到值对象,我们可能立马就想到值类型和引用类型.而在C#中,值类型的代表是strut和enum,引用类型的代表是class.interface.delega ...
- ABP官方文档翻译 3.2 值对象
值对象 介绍 值对象基类 最佳实践 介绍 "展现领域描述性层面且没有概念性身份的对象称之为值对象."(Eric Evans). 和实体相反,实体有身份标示(Id),值对象没有身份标 ...
- DDD 领域驱动设计-“臆想”中的实体和值对象
其他博文: DDD 领域驱动设计-三个问题思考实体和值对象 DDD 领域驱动设计-三个问题思考实体和值对象(续) 以下内容属于博主"臆想",如有不当,请别当真. 扯淡开始: 诺兰的 ...
随机推荐
- pytorch设置多GPU运行的方法
1.DataParallel layers (multi-GPU, distributed) 1)DataParallel CLASS torch.nn.DataParallel(module, de ...
- RestSharp - Ignore SSL errors
项目启动时,添加下面代码: 项目启动时,添加 public App() { ServicePointManager.ServerCertificateValidationCallback += (se ...
- 【Java】阿里巴巴Java开发手册
阿里巴巴Java开发手册 下载地址:https://github.com/alibaba/p3c 阿里巴巴代码规范检查插件p3c 下载地址:https://github.com/alibaba/p3c
- 报错:Error starting Jetty. JSON Metrics may not be available.java.net.BindException:地址已在使用
报错背景: 刚在CDH中集成Flume插件,启动报错 报错现象: Error starting Jetty. JSON Metrics may not be available. java.net.B ...
- SpringBoot 获取配置 @Value
@Value注解可以在代码中直接取到相应的值 如在application.yml中 # 自定义属性 leysen: xcx: url: aaa 1.java代码里的属性值是非静态的,直接在属性上加@V ...
- spring 理解Spring AOP 一个简单的约定游戏
应该说AOP原理是Spring技术中最难理解的一个部分,而这个约定游戏也许会给你很多的帮助,通过这个约定游戏,就可以理解Spring AOP的含义和实现方法,也能帮助读者更好地运用Spring AOP ...
- 单独配置的nginx mysql 重启
/usr/local/mysql/bin/mysqld_safe /usr/local/nginx/sbin/nginx -s stop /usr/local/nginx/sbin/nginx
- 【超分辨率】—(ESRGAN)增强型超分辨率生成对抗网络-解读与实现
一.文献解读 我们知道GAN 在图像修复时更容易得到符合视觉上效果更好的图像,今天要介绍的这篇文章——ESRGAN: Enhanced Super-Resolution Generative Adve ...
- 【ARM-LInux开发】利用scp 远程上传下载文件/文件夹
利用scp 远程上传下载文件/文件夹 scp [-1246BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file] [-l limit] [-o s ...
- 读取以key=value形式存储的txt文件
代码片段(假设只有3个key=value): public static void main(String[] args) throws IOException { BufferedReader br ...