Go——面向对象

一. 匿名字段

        go支持只提供类型而不写字段名的方式,也就是匿名字段,也称为嵌入字段。

  • 同名字段的情况

  • 所以自定义类型和内置类型都可以作为匿名字段使用

  • 指针类型匿名字段

二.接口

        接口定义了一个对象的行为规范,但是定义规范不实现,由具体的对象实现规范的细节。

        2.1 接口类型

        在Go语言中接口(interface)是一种类型,一种抽象类型。

        interface是一组method的集合,接口做的事情就像定义一个协议,不关心属性(数据),只关心行为(方法)。

        2.2 为什么使用接口

        查看下面的图片,只是由于类型不同,就需要定义两个逻辑一样的函数。如果后面出现了其它动物,也会需要定义函数。

        Go语言为了解决类似上面的问题,就设计了接口这个概念。接口区别于我们之前所有的具体类型,接口是一个抽象类型。当你看到一个接口类型是,你不知道他是什么,唯一知道的是通过它的方法能做什么。 

        2.3 接口定义

        Go语言提倡面向接口编程。

  • 接口在底层实现上包含两部分,即类型(type)和数据(data)。
  • 接口是一个或多个方法签名的集合
  • 任何类型的方法集中只要拥有该接口对应的全部方法,就表示它实现了该接口,无须在该类型上显示声明实现了那个接口。这称为Structural Typing。
  • 所谓对应方法,是指有相同名称,参数列表(不包括参数名)以及返回值。
  • 当然,该类型还可以有其它方法。
  • 接口只有方法声明,没有实现,没有数据字段(属性)
  • 接口可以匿名嵌入其它接口,或者嵌入到其它结构中。
  • 对象赋值给接口时,会发生拷贝,而接口内部存储的是指向这个复制品的指针,即无法修改复制品的状态,也无法获取指针。即如果对象是结构体或者基本数据类型,它会被值拷贝到接口中。如果对象是指针类型,这个指针指向的结构体实现了接口,那么接口中存储的是指针的副本,而不是指针本身。
  • 只有当接口存储的类型和对象都为nil时,接口才为nil。
  • 接口调用不会做receiver的自动转换。
  • 接口同样支持匿名字段方法。
  • 接口可以实现类似面向对象编程(OOP)的多态。
  • 空接口可以作为任何类型数据的容器。空接口是没有声明任何方法的接口。但是,也无法通过空接口来调用对象的方法或访问其属性。
  • 一个类型可以实现多个接口。
  • 接口命名习惯以er结尾。

        每一个接口由数个方法组成,接口的定义格式如下:

type 接口类型名 interface{方法1(参数列表1)返回值列表1方法2(参数列表2)返回值列表2...
}

        其中:

  • 接口名:使用type将接口名定义为自定义的类型名。Go语言的接口在命名时,一般会在单词后面添加er,接口名最好要能突出该接口的类型含义。
  • 方法名:当方法名首字母是大写且接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。
  • 参数列表和返回值列表:参数列表和返回值列表中的参数变量名可以省略。

        举个例子:

type writer interface{Write([]byte) error
}

        当看到这个接口的时候,你不知道他是什么,唯一知道的是可以通过它的Write方法来做一些事情。

        2.4 实现接口的条件

        一个对象只要全部实现了接口的方法,那么就实现了这个接口。换句话说,接口就是一个需要实现的方法列表。

package mainimport "fmt"type Sayer interface {Say()
}type Cat struct{}//实现了Sayer接口
func (c *Cat) Say() {fmt.Println("喵喵喵...")
}type Dog struct{}//实现了Sayer接口
func (d *Dog) Say() {fmt.Println("汪汪汪...")
}

        2.5 接口类型变量

        接口类型变量能够存储所有实现了该接口的实例。

        需要指针传入是因为方法的receiver为指针类型,对象的指针类型(*T)的方法集为值类型(T)和指针类型(*T)。

        2.6 值接收者和指针接收者实现接口的区别

        值接收者和指针接收者实现接口区别在于:方法集的不同。接口接收值类型(T)对象方法集为值接收者(T)实现的方法。接口接收指针类型对象(*T)方法集为值接收者(T)和指针接收者(*T)现象的方法。

  • 值接收者

  •  指针接收者

        2.7 类型与接口的关系

        2.7.1 一个类型实现多个接口

        一个类型可以同时实现多个接口,而接口间彼此独立,不知道对方的实现。

                2.7.2 多个类型实现同一个接口

        Go语言中不同的类型还可以实现同一接口。

        一个接口的方法不一定需要由一个类型完全实现,接口的方法可以通过在类型中嵌入其它类型或者结构体来实现。

        2.7.3 接口嵌套

         接口与接口之间可以通过嵌套创造出新的接口。

type Sayer interface {Say()
}type Mover interface {Move()
}type Animal interface {SayerMover
}

        嵌套的接口的使用和普通接口一样,这里实现嵌套的接口。

        2.8 空接口

        2.8.1 空接口的定义

        空接口是指没有定义任何方法的接口。因此任何类型都实现了空接口。

        空接口类型变量可以存储任意类型的变量。

        2.8.2 空接口的应用

  • 空接口作为函数参数

        使用空接口实现可以接收任意类型的函数参数。

  • 空接口作为map的值

        使用空接口实现可以保存任意值的字典。

         2.8.3 类型断言

        空接口可以存储任意类型的值,那我们如何获取其存储的具体数据呢?

  • 接口值

        一个接口的值(简称接口值)是由一个具体类型和具体类型的值俩个部分组成。这两部分分别称为接口的动态类型和动态值。

        我们来看一个例子:

package mainimport ("bytes""io""os"
)func main() {var w io.Writerw = os.Stdoutw = new(bytes.Buffer)w = nil
}

        图解: 

        想要判断空接口中的值这个时候就可以使用类型断言,其语法格式:

x.(T)

        其中:

  • x:表示类型为interface{}的变量
  • T :表示断言x可能是的类型

        该语法返回两个参数,第一个参数是x转化为T类型后的变量,第二个是布尔值,若为true表示断言成功,为false则表示断言失败。

        举个例子:

        自定义类型: 

        我们还可以使用switch语句来实现多个类型的断言:

        因为空接口可以存储任意类型值的特点,所以空接口在Go语言中使用十分广泛。

         但是接口需要注意的是,只有当有两个或者两个以上的具体类型必须以相同的方式进行处理时才需要定义接口。不要为了接口而写接口,那样会增加不必要的抽象,导致不必要的运行时消耗。

三. 接口的底层实现

  • 案例
package maintype EInterface interface{}
type IInterface interface {Do()
}type IInterfaceImpl struct{}func (imp1 IInterfaceImpl) Do() {}func main() {var impl1 EInterface = IInterfaceImpl{}var impl2 IInterface = IInterfaceImpl{}println(impl1)println(impl2)
}

        3.1 数据结构

        golang中的接口非为带方法的接口和不带方法的空接口,带方法的接口在底层使用iface表示,空接口的底层则是eface表示。

  • eface

        eface是空接口类型的底层实现,源码如下:

type eface struct {_type *_typedata  unsafe.Pointer
}

两个字段都是指针类型,含义分别是:

  • _type:指向实际的类型。上面案例是IInterfaceImpl
  • data:指向实际的值。上面案例是IInterfaceImpl结构体的值

        注意:var v interface{} = (*int)nil,变量v其实使用的eface结构表示。其中_type的类型对应的是int类型的指针,而data部分为nil,所以整体变量v != nil。 

  • iface

        iface是非空接口类型的底层实现,源码如下:

type iface struct {tab  *itabdata unsafe.Pointer
}

        上面的案例impl2就是非空接口类型的变量,两个字段也是指针类型,含义分别是:

  • tab:指向itab结构体,itab结构体存储了接口所有方法列表。
  • data:指向对应的值。上面案例为IInterfaceImpl结构体的值。 

  • _type结构

        该结构于golang的类型系统有关,无论是内置类型还是自定义数据类型,都用_type结构表示其元信息。

type _type struct {size       uintptrptrdata    uintptr // size of memory prefix holding all pointershash       uint32tflag      tflagalign      uint8fieldAlign uint8kind       uint8// function for comparing objects of this type// (ptr to object A, ptr to object B) -> ==?equal func(unsafe.Pointer, unsafe.Pointer) bool// gcdata stores the GC type data for the garbage collector.// If the KindGCProg bit is set in kind, gcdata is a GC program.// Otherwise it is a ptrmask bitmap. See mbitmap.go for details.gcdata    *bytestr       nameOffptrToThis typeOff
}

        以int32类型的指针为例,具体的字段含义及其作用如下:

  • size:类型的大小(字节数)。*int32类型的大小是64位系统下8字节,32位系统下4字节。
  • ptrdata:所有指针内存前缀大小,指向int32类型的实例。
  • hash:类型的哈希值,即_type.hash。
  • tflag:类型标记,表示类型的特性。
  • align:类型的对齐方式,*int32类型32位系统下按4字节对齐,64位系统下8字节对齐。
  • fieldAlign:字段对齐方式,*int32类型的字段对齐方式 32位系统下按4字节对齐,64位系统下8字节对齐。
  • kind:类型的种类。用于区分基本类型,结构体,接口等。
  • equal:比较函数,用于区分两个*int32类型的实例是否相等。
  • gcdata:与GC有关。
  • str:类型名称的偏移。
  • ptrToThis:指向该类型的指针。

         _type是一个很复杂的结构,这里只需要知道,通过该结构能获取到结构体实现的所有方法。

        下面是iface.go中的init方法中的一段代码,_type结构的uncommon方法会返回一个指针,在此基础上加一个偏移量(moff)就能得到实际结构体实现的方法列表。

func (m *itab) init() string {inter := m.intertyp := m._typex := typ.uncommon()// both inter and typ have method sorted by name,// and interface names are unique,// so can iterate over both in lock step;// the loop is O(ni+nt) not O(ni*nt).ni := len(inter.mhdr)nt := int(x.mcount)// 实际类型的方法数组xmhdr := (*[1 << 16]method)(add(unsafe.Pointer(x), uintptr(x.moff)))[:nt:nt]...
}
  • itab结构

        itab结构是golang非空接口iface中一个非常重要的字段,类型的赋值,断言等都离不开该字段。

type itab struct {// 接口类型的指针,比如对于 io.Reader 接口,记录的是接口类型的信息(如接口定义的方法,Read 方法)inter *interfacetype// 实际结构类型的指针,记录的是实际类型的信息,比如 os.File 类型,实现了 io.Reader 接口_type *_typehash  uint32 // copy of _type.hash. Used for type switches._     [4]byte// 变长数组,fun[0]==0 表示 _type 没有实现 inter 接口fun   [1]uintptr
}type interfacetype struct {// 接口类型元信息typ     _type// 包路径pkgpath name// 接口的所有方法列表mhdr    []imethod
}

        itab结构的字段含义:

  • inter记录的是非空接口类型的元信息,其中mhdr是接口的方法表
  • _type记录的是实际类型的指针,即实现接口的类型
  • fun保存的是实际类型中实现的方法的地址。当fun[0] == 0时,表示该类型没有实现该接口;当fun[0]!=0时,则代表该类型实现了接口的所有方法,这时候就可以通过偏移量调用具体类型对象的方法。

        3.2 itab关键方法

        通过上述对itab结构的描述,不难理解,itab其实就是一个缓存,用于快速判断具体类型是否实现了某个接口。

        一般情况下,如果要判断需要对接口的具体类型的方法集进行比较。当如果每次都这样比较,效率会很低。通过将比较结果缓存起来,下次再判断的时候,就能直接根据itab快速得出结论了。

        go源码的runtime里定义了全局变量itebTable,用户缓存itab。

// 用于缓存 itab
itabTable     = &itabTableInit
itabTableInit = itabTableType{size: itabInitSize}// 全局的 itab 表
type itabTableType struct {size    uintptr             // entries 的长度count   uintptr             // 当前 entries 的数量,即 itab 数量entries [itabInitSize]*itab // 保存 itab 的哈希表
}

        这里其实是一个全局的哈希表,哈希表的key就是interfacetype + _type,value就是对应的itab。

        判断某个类型是否实现了接口时,只需要传入接口的接口类型interfacetype和实际类型_type即可:

  • 如果在itabTable中没有找到对应的itab,则需要依次比较方法集,生成itab并缓存到itabTable中
  • 如果找到了对应的itab,则判断func[0],如果等于0则说明该类型没有实现该接口 

        有了哈希表,还要考虑如何向其中添加数据和获取数据,也就是下面两个方法。

  • getitab

        该函数的作用是:通过interfacetype和_type,也就是接口类型和实际结构类型,从表中获取对应itab。

func getitab(inter *interfacetype, typ *_type, canfail bool) *itab {...var m *itab// 尝试从 itabTable 表中获取 itab,获取到直接返回t := (*itabTableType)(atomic.Loadp(unsafe.Pointer(&itabTable)))if m = t.find(inter, typ); m != nil {goto finish}// 没有找到,获取锁再次查找lock(&itabLock)if m = itabTable.find(inter, typ); m != nil {unlock(&itabLock)goto finish}// 如果 itabTable 中没有找到,则新建一个 itab,并调用 itabAdd 将其缓存到 itabTable 中m = (*itab)(persistentalloc(unsafe.Sizeof(itab{})+uintptr(len(inter.mhdr)-1)*goarch.PtrSize, 0, &memstats.other_sys))m.inter = interm._type = typm.hash = 0m.init()itabAdd(m)unlock(&itabLock)
finish:// m.fun[0] != 0 表示该类型实现了接口的所有方法,可以返回 itabif m.fun[0] != 0 {return m}//canfail 用于控制类型转换失败的行为。比如 v := s.(Dst),这里的 canfail == false,那么在断言失败时,会 panicif canfail {return nil}panic(&TypeAssertionError{concrete: typ, asserted: &inter.typ, missingMethod: m.init()})
}

        这里主要的逻辑是:

  • 更具interfacetype和_type,尝试从itabTable中获取itab
  • 如果itabTable没有找到itab,则新创建一个itab并将其缓存到itabTable中
  • 判断该类型是否实现了接口的所有方法(m.fun[0]!=0)
  • 如果该类型没有实现接口的所有方法,则根据canfail判断是否panic,canfail为false则会panic。比如类型断言时,如果不接受第二个返回值,则断言失败会panic

        首次调用getitab方法获取时,哈希表中是没有对应数据的。此时不仅要创建itab结构,还要对其涉及到的接口和类型的方法集进行判断,初始化等。相关代码在init中。

func (m *itab) init() string {inter := m.intertyp := m._typex := typ.uncommon()// 接口定义的方法数量ni := len(inter.mhdr)// 实际类型的方法数量nt := int(x.mcount)// 实际类型的方法数组xmhdr := (*[1 << 16]method)(add(unsafe.Pointer(x), uintptr(x.moff)))[:nt:nt]j := 0// 保存接口的第i个方法对应的实际类型的方法的地址methods := (*[1 << 16]unsafe.Pointer)(unsafe.Pointer(&m.fun[0]))[:ni:ni]var fun0 unsafe.Pointer
imethods:// 遍历接口方法列表for k := 0; k < ni; k++ {// 接口的方法i := &inter.mhdr[k]// 接口的方法类型itype := inter.typ.typeOff(i.ityp)// 接口的方法名称name := inter.typ.nameOff(i.name)// 接口的方法名iname := name.name()// 接口的包路径ipkg := name.pkgPath()if ipkg == "" {ipkg = inter.pkgpath.name()}// 根据接口方法查找实际类型的方法for ; j < nt; j++ {// 实际类型的方法t := &xmhdr[j]// 实际类型的方法名tname := typ.nameOff(t.name)// 比较接口的方法名和实际类型的方法是否一致,包括名称和类型if typ.typeOff(t.mtyp) == itype && tname.name() == iname {pkgPath := tname.pkgPath()if pkgPath == "" {pkgPath = typ.nameOff(x.pkgpath).name()}// 如果是导出方法或在同一个包,则将将方法保存到 itab 中if tname.isExported() || pkgPath == ipkg {if m != nil {// 实际类型的方法指针,通过该指针可以调用实际类型的方法ifn := typ.textOff(t.ifn)if k == 0 {fun0 = ifn // we'll set m.fun[0] at the end} else {methods[k] = ifn}}continue imethods}}}// 该类型没有实现接口// 如果每个接口方法都被实现了,则每次都会走到 continue 的逻辑,不会将 fuc[0] 置为 0m.fun[0] = 0return iname}m.fun[0] = uintptr(fun0)return ""
}

        主要逻辑是,依次遍历接口的所有方法,并在实际类型的接口列表中查找对应的实现,只要有一个接口方法没有被实现,则将itab的fun[0]置为0,表示该类型没有实现该接口。

  • itabadd

        getitab方法如果没有找到itab,会新建一个itab并调用itabAdd方法将其缓存到itabTable中。

func itabAdd(m *itab) {...t := itabTable// 容量超过 75% 时会触发扩容if t.count >= 3*(t.size/4) { // 75% load factor// 扩容为原哈希表的 2 倍大小t2 := (*itabTableType)(mallocgc((2+2*t.size)*goarch.PtrSize, nil, true))t2.size = t.size * 2// 将原哈希表的元素复制到新哈希表// 复制过程中,其他的线程可能会尝试从原哈希表中获取 itab,但找不到。此时会尝试获取锁(会阻塞)后再次获取。iterate_itabs(t2.add)-】if t2.count != t.count {throw("mismatched count during itab table copy")}// 使用原子操作将 itabTable 的引用指向新扩容的内存atomicstorep(unsafe.Pointer(&itabTable), unsafe.Pointer(t2))// Adopt the new table as our own.t = itabTable// Note: the old table can be GC'ed here.}// 将新的 itab 缓存到 itabTable 中t.add(m)
}

        3.3 接口赋值

        将某一具体类型赋值给接口类型时,本质其实时如何填充eface和iface结构体。

        对eface结构体来说,由于只有_type和data字段,因此只需要进行字段赋值即可。

        对于iface结构体来说,需要通过itab判断类型值是否实现了接口的所有方法(itab可能不存在,会走一遍getitab的流程),然后初始化iface结构的tab和data字段。

        底层会调用runtime.convTXXX转换为iface或eface的data字段。

func convT(t *_type, v unsafe.Pointer) unsafe.Pointer {...// 分配`_type`所需的内存x := mallocgc(t.size, t, true)// 将v的值复制到刚分配的内存typedmemmove(t, x, v)return x
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/813951.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Matlab之过球面一点的平面方程

这篇文章描述2件事情&#xff1a; 1、已知球面上任意点&#xff0c;求过该点、地心、与北极点的平面方程&#xff08;即过该点的经线平面方程&#xff09;&#xff1b; 2、绕过球心的任意轴旋转平面得到新平面的方程 一、已知球面上任意点&#xff0c;求过该点、地心、与北极点…

javase_进阶 day10 集合(泛型,数据结构)

1.泛型 1.1泛型概述 泛型的介绍 ​ 泛型是JDK5中引入的特性&#xff0c;它提供了编译时类型安全检测机制 泛型的好处 把运行时期的问题提前到了编译期间避免了强制类型转换 泛型的定义格式 <类型>: 指定一种类型的格式.尖括号里面可以任意书写,一般只写一个字母.例如:…

CopyTranslator下载地址及安装教程

CopyTranslator是一款免费且开源的机器翻译工具&#xff0c;旨在提供快速、便捷的翻译服务。它采用了先进的神经网络机器翻译技术&#xff0c;能够提供准确、流畅的翻译结果。 CopyTranslator的特点和功能如下&#xff1a; 多语言翻译&#xff1a;支持多种常见的语言对&#…

【随笔】Git 高级篇 -- 项目里程碑 git tag(二十)

&#x1f48c; 所属专栏&#xff1a;【Git】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f496; 欢迎大…

leetcode142 环形链表2

题目 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部…

基于51单片机篮球24秒倒计时设计( proteus仿真+程序+设计报告+原理图+讲解视频)

基于51单片机篮球24秒倒计时设计( proteus仿真程序设计报告原理图讲解视频&#xff09; 基于51单片机篮球24秒倒计时设计 1. 主要功能&#xff1a;2. 讲解视频&#xff1a;3. 仿真设计4. 程序代码5. 设计报告6. 原理图7. 设计资料内容清单&&下载链接下载链接 仿真图pro…

(3)(3.1) 英特尔Realsense深度摄像头(三)

文章目录 前言 10 系统概述 11 手动设置配套计算机 前言 本文介绍如何将英特尔 Realsense 深度摄像头(Intel Realsense Depth Camera)与 ArduPilot 配合使用&#xff0c;以实现避障(obstacle avoidance)。该方法使用在配套计算机上运行的 Python 脚本&#xff08;非 ROS&am…

HackTheBox-Machines--MonitorsTwo

文章目录 0x01 信息收集0x02 CVE-2022-46169 漏洞利用0x03 权限提升0x04 提升到root权限 MonitorsTwo 测试过程 0x01 信息收集 a.端口扫描: 发现22、80端口    b.信息收集: 1.2.22 Cacti信息收集 nmap -sC -sV 10.129.186.1321.访问 10.129.186.132&#xff0c;为 1.2.22 Ca…

社交革命的引领者:探索Facebook的创新策略

1. 引言&#xff1a;社交媒体的崛起 社交媒体的兴起标志着信息时代的到来&#xff0c;它不仅改变了人们的生活方式&#xff0c;也影响着整个社会结构。作为社交媒体的先驱者&#xff0c;Facebook以其创新的策略和领先的技术&#xff0c;成为了这场社交革命的引领者。从2004年马…

itop4412编译内核时garbage following instruction -- `dmb ish‘ 解决方案

王德法 没人指导的学习路上磕磕绊绊太耗费时间了 今天编译4412开发板源码时报 garbage following instruction – dmb ish’ 以下是解决方案&#xff1a; 1.更新编译器 sudo apt-get install gcc-arm-linux-gnueabi 更新后修改Makefile 中编译器路径如下图 2.你以为更新完就可…

SqlServer功能性配置选择

功能性配置 下面的是必选的

传输层协议——UDP/TCP协议

目录 端口号 端口号范围 pidof UDP协议 UDP协议格式 UDP特点 UDP缓冲区 UDP的注意事项 基于UDP的应用层协议 TCP协议 TCP协议格式 序号与确认序号 窗口大小 6个标记位 紧急指针 确认应答机制 连接管理机制 三次握手 四次挥手 超时重传机制 流量控制 滑动…

【计算机网络】ip子网划分--超详细例题解析

Hello!这一篇主要是计算机网络中的ip地址子网划分的例题&#xff0c;这里例举了四个题型。保证即便从0也可以掌握&#xff01;(前面是一些预备知识&#xff0c;不熟悉的小伙伴一定要看下学习下哦&#xff5e;) 这也是博主的学习过程&#xff0c;做题中仅仅我的理解哦。若文章中…

Unity之C#面试题(二)

内容将会持续更新&#xff0c;有错误的地方欢迎指正&#xff0c;谢谢! Unity之C#面试题&#xff08;二&#xff09; TechX 坚持将创新的科技带给世界&#xff01; 拥有更好的学习体验 —— 不断努力&#xff0c;不断进步&#xff0c;不断探索 TechX —— 心探索、心进取&a…

langchain 文本向量化存储,并检索相似 topK

目录 chroma 检索 faiss 检索 检索器 相似性 最大相关性mmr 相似数阈值 txt 有多行&#xff0c;我的这份数据有 67 行&#xff0c;样例如下&#xff1a; 字段1\t值1\n 字段2\t值2\n ... chroma 检索 pip install langchain-chroma 在本地下载了 embedding 模型&…

SpringBoot 集成H2数据库,启动执行sql, 中文乱码

目录 H2数据库介绍 SpringBoot版本&#xff1a;SpringBoot 2.1.12.RELEASE 快速集成H2&#xff0c;maven依赖 快速集成H2&#xff0c;数据源及关键参数配置 spring.datasource.schema参数&#xff08;建表SQL脚本&#xff09; spring.datasource.data参数&#xff08;更新、…

Golang | Leetcode Golang题解之第19题删除链表的倒数第N个结点

题目&#xff1a; 题解&#xff1a; func removeNthFromEnd(head *ListNode, n int) *ListNode {dummy : &ListNode{0, head}first, second : head, dummyfor i : 0; i < n; i {first first.Next}for ; first ! nil; first first.Next {second second.Next}second.N…

Nacos-默认token.secret.key-配置不当权限绕过漏洞复现

漏洞描述&#xff1a; Nacos 身份认证绕过漏洞(QVD-2023-6271)&#xff0c;开源服务管理平台 Nacos在默认配置下未对 token.secret.key 进行修改&#xff0c;导致远程攻击者可以绕过密钥认证进入后台&#xff0c;造成系统受控等后果。 漏洞信息 公开时间&#xff1a;2023-03…

11 Php学习:函数

PHP 内建函数Array 函数 PHP Array 函数是 PHP 核心的组成部分。无需安装即可使用这些函数。 创建 PHP 函数 当您需要在 PHP 中封装一段可重复使用的代码块时&#xff0c;可以使用函数。下面详细解释如何创建 PHP 函数并举例说明。 创建 PHP 函数的语法 PHP 函数的基…

生成式AI对UiPath来说是机遇还是挑战?

企业争相通过技术革新来领跑市场&#xff0c;机器人流程自动化&#xff08;RPA&#xff09;技术更是将企业的效率和成本控制推向了新的高度。但当人工智能&#xff08;AI&#xff09;的最新进展——生成式AI登上舞台时&#xff0c;它不仅带来了变革的可能&#xff0c;还提出了一…