【Go 快速入门】包及依赖管理 | Go 第三方包发布 | 接口 | 反射

文章目录

    • 包和依赖管理
      • 依赖管理
        • go mod
        • go get
      • go.mod 文件
      • go.sum 文件
      • Go Modules 发布包
    • 接口
      • 空接口
      • 接口值
      • 类型断言
    • 反射
      • reflect.TypeOf
      • reflect.ValueOf
      • 结构体反射

项目代码地址:04-PackageInterfaceReflection

包和依赖管理

Go 使用包来支持代码模块化和代码复用,一个包由多个 .go 文件组成。

基本格式:package packagename,其中 packagename 表示包名

  • 一个文件夹直接包含的文件只能属于一个包
  • 包名为 main 的包是程序入口包,仅该包编译后会得到一个可执行文件
  • 首字母大/小写控制标识符是否对外可见/不可见,大写则对包外可见
  • 引入的包都会被编译到可执行文件中

包的引入

  • 禁止循环导入包
  • 导入的包必须都有使用
  • 三种常见的引入包模式,._别名

每个包在初始化时都会先执行依赖包中声明的 init 函数,再执行当前包中声明的 init 函数。

import (
	_ "00-LocalPackage"                       // 匿名
	Calc "04-PackageInterfaceReflection/calc" // 别名
	. "fmt"                                   // 不带前缀,不建议使用,容易重名
)

func function01() {
	Printf("%s\n", "不带前缀")
	Println(Calc.Add(1, 2))
}

date 包初始化ing~
不带前缀
3

  1. 匿名导入包会先执行其 init 函数
  2. 使用 . 导入包,则不需要使用该包名作为前缀调用,PrintfPrintln
  3. 使用别名 Calc 导入包,调用该包下的函数需要使用别名

上述示例中,00-LocalPackage 包是本地没被发布到其他任何代码仓库的本地包,04-PackageInterfaceReflection/calc 则是当前项目下的包,结构如下:

- 04-PackageInterfaceReflection
	|- calc
		|- add.go
	|- go.mod
	|- main.go
- 00-LocalPackage
	|- go.mod
	|- date.go

当前项目内的其他包,使用很简单,如上述直接 import 项目名/包名 导入即可。

本地包导入如下,可以在 go.mod 文件中使用 replace 语句将依赖临时替换为本地包的相对路径:

04-PackageInterfaceReflection/go.mod

require 00-LocalPackage v0.0.0
replace 00-LocalPackage => ../00-LocalPackage

依赖管理

Go 1.16 版本默认开启 Go Modules 管理依赖

GOPROXY

设置 Go 模块代理,使 Go 在后续拉取模块版本时脱离传统 vcs 方式,直接通过镜像站点快速拉取。

go env -w GOPROXY=https://goproxy.cn,direct

GOPRIVATE

设置了 GOPROXY 后,go 命令会从配置的代理地址拉取和校验依赖包。当项目中引入非公开的包(私有仓库或公司内部 git 仓库),就无法正常从代理拉取,需要配置 GOPRIVATE。

GOPRIVATE 用来告诉 go 命令哪些仓库是私有的,不必通过代理服务器拉取和校验。

go env -w GOPRIVATE=gitee.com

这样就可以正常拉取 gitee.com 为前缀的依赖包了


go mod

常用的 go mod 命令:

指令介绍
go mod download下载依赖的module到本地cache(默认为$GOPATH/pkg/mod目录)
go mod edit编辑go.mod文件
go mod graph打印模块依赖图
go mod init初始化当前文件夹, 创建go.mod文件
go mod tidy增加缺少的module,删除无用的module
go mod vendor将依赖复制到vendor下
go mod verify校验依赖
go mod why解释为什么需要依赖

我们在代码中删除依赖代码后,相关的依赖库并不会在 go.mod 文件中自动移除。这种情况下我们可以使用 go mod tidy 命令更新 go.mod 中的依赖关系。

go get

在项目中执行 go get 命令可以下载依赖包,并且还可以指定下载的版本。

  • 运行 go get -u 将会升级到最新的次要版本或者修订版本(x.y.z, z是修订版本号, y是次要版本号)
  • 运行 go get -u=patch 将会升级到最新的修订版本
  • 运行 go get package@version 将会升级到指定的版本号 version

go.mod 文件

go.mod 文件记录了当前项目中所有依赖包的相关信息

声明依赖的基本格式:require module/path v1.2.3

  • require: 声明依赖关键字

  • module/path: 依赖包的引入路径

  • v1.2.3: 依赖包的版本号

    • latest: 最新版本

    • v1.2.3: 详细版本号

      • 主版本号:发布了不兼容的版本迭代时递增
      • 次版本号:发布了功能性更新时递增
      • 修订号:发布了 bug 修复类更新时递增
    • commit hash: 指定某次 commit hash

go.sum 文件

在 Go Modules 下载依赖后生成,记录依赖包及其 hash 值。Go 采用分布式方式管理包,为了防止依赖包被非法篡改,Go Modules 引入了 go.sum 机制对依赖包进行校验。

基本格式:

<module> <version> <hash>
<module> <version>/go.mod <hash>

Go Modules 发布包

  1. gitee.com 创建仓库,并下载到本地

    • git clone git@gitee.com:Cauchy_AQ/hello.git
  2. hello 项目目录下初始化,设置项目路径

    • go mod init gitee.com/Cauchy_AQ/hello
  3. 创建 hello.go 文件,提供方法 SayHello()

    •   package hello
        
        import "fmt"
        
        func SayHello() {
        	fmt.Println("你好,我是Cauchy!")
        }
      
  4. 项目代码 push 到远端 master 分支

    • git add .
    • git commit -m "SayHello() v0.1.0"
    • git push origin master
  5. 为代码包打上标签

    • git tag -a v0.1.0 -m "release version v0.1.0"
  6. 项目代码 push 到远端标签分支

    • git push origin v0.1.0
  7. 迭代版本 v2.0.0

    •   func SayHello(name string) {
        	fmt.Printf("你好%s,我是Cauchy!", name)
        }
      
  8. go.mod 修改当前包的引入路径,添加后缀 v2

    • module gitee.com/Cauchy_AQ/hello/v2
  9. 发布新的主版本

    • git add .
    • git commit -m "SayHello(string) v2.0.0"
    • git push
    • git tag -a v2.0.0 -m "release version v2.0.0"
    • git push origin v2.0.0
  10. 项目使用自己发布的包

    • 设置 GOPRIVATE

      • go env -v GOPRIVATE=gitee.com
    • 项目导入包

      • go get gitee.com/Cauchy_AQ/hello/v2@v2.0.0
      • go mod tidy

执行完上述操作后,就成功发布包在 gitee 平台,并且作为第三方库,可以随时获取使用。

import (
	hello "gitee.com/Cauchy_AQ/hello"
	helloV2 "gitee.com/Cauchy_AQ/hello/v2"
)

func function02() {
	hello.SayHello() // 你好,我是Cauchy!
	helloV2.SayHello("AQ") // 你好AQ,我是Cauchy!
}

需要注意:

从 v2 版本开始,主要版本必须出现在模块路径末尾(…/v2)。


接口

Go 1.18 版本开始,接口分为 一般接口基础接口,在此讨论基本接口,后续谈及泛型在讨论一般接口。

一个接口类型的定义中只包含方法,称为基本接口。

基本格式:

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

实现接口

如下所示:

  • 类型 Dog 实现了接口 Animal 的 Move、Say 方法,即 Dog 实现了接口 Animal
  • 接口 FlyAnimal 包含了接口 Animal 所有的方法,即 FlyAnimal 实现了接口 Animal

在这里插入图片描述

// Interface
type Animal interface {
	Move()
	Say()
}

// 
type FlyAnimal interface {
	Animal
	Fly()
}

type Dog struct{}

func (d Dog) Move() {
	Println("dog move")
}

func (d Dog) Say() {
	Println("dog say")
}

func function03() {
	var a Animal // 接口类型变量
	var d Dog = Dog{}
	a = d    // 能存储所有实现了该接口的类型变量
	a.Move() // dog move
	a.Say()  // dog say
}

Go 有对指针求值的语法糖:

Dog 是值接收者,赋值给接口类型变量 a 时,无论是值还是指针都可以直接赋值。Cat 是指针接收者,赋值给 a 时,只能赋值地址。

  • 值接收者实现的接口,可用值类型和指针类型
  • 指针接收者实现的接口,只能用指针类型
var d *Dog = &Dog{}
a = d
var c Cat = Cat{}
a = &c
type Dog struct{}

func (d Dog) Move() {
	Println("dog move")
}

func (d Dog) Say() {
	Println("dog say")
}

type Cat struct{}

func (c *Cat) Move() {
	Println("cat move")
}

func (c *Cat) Say() {
	Println("cat say")
}

只要类型实现了接口的所有方法,那么该类型就实现了接口

  • 多种类型实现同一接口
type HomeWork interface {
	DoA()
	DoB()
}

type Week1 struct{}

func (w Week1) DoA() {}

type Week2 struct {
	Week1
}

func (w Week2) DoB() {}
  • 接口组合
type A interface{
	PlayA()
}
type B interface{
	PlayB()
}

type AB interface {
	A
	B
}

空接口

空接口指没有定义任何方法的接口类型,因此任何类型都可以视为实现了空接口。所以空接口的类型变量可以存储任意类型的值。

func function04() {
	a := make(map[int]interface{})
	a[1] = [...]int{1, 2, 3}
	a[2] = "Golang"
	a[3] = 123

	for k, v := range a {
		Println(k, v)
	}
	/*
		2 Golang
		3 123
		1 [1 2 3]
	*/
}

Go 中只要是可比较类型都可以作为 key ,除了 slice ,map,function 这几种类型,其他类型都可以作为 map 的 key。因此 interface{} 空接口类型也不可以作为 key。

Go 中的 any 类型实际就是空接口类型:type any = interface{}

func function05() {
	var a interface{}
	a = []int{1, 2, 3}
	Println(a) // [1 2 3]
	
	var b any
	b = []int{0}
	Println(b) // [0]

	func(a interface{}) {
		switch a.(type) {
		case int:
			Println("int")
		case string:
			Println("string")
		default:
			Println("None")
		}
	}("123") // string
}

接口值

接口值由类型(type)和值(value)组成,这两部分根据存入值不同而发生变化,也称之为接口的动态类型和动态值。

// 接口值
func function06() {
	var c Animal
	Println(c == nil) // true

	c = new(Cat)
	Println(c == nil) // false
	Printf("%T\n", c) // *main.Cat
}

类型断言

接口值可能被赋为任意类型的值,通过类型断言从接口值获取其存储的具体数据类型。

基本语法:x.(Type),返回两个参数:第一个是转化为 Type 类型的变量;第二个是一个 bool 值,true 表示断言成功。

// 类型断言
func function07() {
	var x interface{}
	x = []int{1, 2, 3}

	switch v := x.(type) {
	case int:
		Println("int", v)
	case string:
		Println("string", v)
	case []int:
		Println("[]int", v) // []int [1 2 3]
	default:
		Println("failed")
	}
}

tips

下述代码可以在程序编译阶段验证某一结构是否满足特定的接口类型

type IRouter interface{...}
type RouterGroup struct {...}
var _ IRouter = (*RouterGroup)(nil) // 确保 RouterGroup 实现了接口 IRouter

反射

上文通过类型断言,获取空接口类型的动态值和类型。那么也可以通过反射,在程序运行时动态的获取一个变量的类型信息和值信息。

Go 语言反射相关功能由内置的 reflect 包提供,任何接口值在反射中都可以理解为由 reflect.Typereflect.Value 两部分组成。分别由 reflect.TypeOfreflect.ValueOf 函数来获取任意值的 TypeValue 信息。

reflect.TypeOf

函数签名:func TypeOf(i interface{}) Type

通过反射得到的类型信息分为 TypeKind 两种,

  • Type 指声明的类型,名称由 reflect.TypeName 方法得到

    • 数组、切片、Map、指针等类型变量的 Type 名称都是空字符串
  • Kind 指语言底层的类型,名称由 reflect.TypeKind 方法得到

    • 当需要区分指针、结构体等大品种类型,就用到 Kind
func reflectType(x interface{}) {
	v := reflect.TypeOf(x)
	Printf("type:%v kind:%v\n", v.Name(), v.Kind())
}

type MyInt int

func function08() {
	var a *float32
	var b MyInt
	var c int64

	reflectType(a) // type: kind:ptr
	reflectType(b) // type:MyInt kind:int
	reflectType(c) // type:int64 kind:int64

	type ABC struct {
		name string
	}
	var d = ABC{"123"}
	reflectType(d) // type:ABC kind:struct

	e := &struct {
		ID int
	}{1010}
	reflectType(e) // type: kind:ptr
}

reflect.ValueOf

函数签名:func ValueOf(i any) Value

reflect.ValueOf 返回 reflect.Value 类型,它是结构体类型,包含了原始值信息,可以与原始值之间互相转换。

func reflectValue(x interface{}) {
	v := reflect.ValueOf(x) // reflect.Value
	k := v.Kind()
	switch k {
	case reflect.Int:
		Println("Int", v.Int())
	case reflect.Int32:
		Println("Int32", int32(v.Int()))
	case reflect.Float64:
		Println("Float64", v.Float())
	default:
		Println("None")
	}
}

func function09() {
	var a int
	var c int32 = 1
	b := 0.1
	reflectValue(a) // Int 0
	reflectValue(c) // Int32 1
	reflectValue(b) // Float64 0.1
}

结构体反射

任意值通过 reflect.Typeof() 获得反射对象信息后,如果它的类型是结构体,可以通过反射值对象(reflect.Type)的NumField()Field() 方法获得结构体成员的详细信息。

方法说明
Field(i int) StructField根据索引,返回索引对应的结构体字段的信息
NumField() int返回结构体成员字段数量
FieldByName(name string) (StructField, bool)根据给定字符串返回字符串赌赢的结构体字段信息
FieldByIndex(index []int) StructField多层成员访问时,根据 []int 提供的每个结构

遍历 Person 结构体,根据 tag 获取对应的名称,并获取对应字段的值:

  • reflect.Typereflect.Value 类型,如果是指针类型(reflect.Ptr),可以通过 Elem 方法获取指针对应的值
  • reflect.TypeNumFieldField 获得结构体成员的详细信息(StructField
  • reflect.ValueFieldByName 通过字段名字获取对应的值
  • reflect.ValueFieldByIndex 通过 []int 切片指定字段的层级获取具体某个字段的值,比如 []int{0, 1} 表示结构体第一个字段内部的第二个字段
type Person struct {
	Name string `info:"name"`
	Age  int8   `info:"age"`
}

func function10() {
	cauchy := &Person{
		"cauchy",
		20,
	}
	t := reflect.TypeOf(cauchy)
	v := reflect.ValueOf(cauchy)

	if t.Kind() == reflect.Ptr && v.Kind() == reflect.Ptr {
		Println("reflect.Ptr") // reflect.Ptr
	}

	for i := 0; i < t.Elem().NumField(); i++ {
		f := t.Elem().Field(i)
		Println(f.Index, f.Name, f.Type)
		Println(f.Tag.Get("info"), v.Elem().FieldByName(f.Name))
	} /*
		[0] Name string
		name cauchy
		[1] Age int8
		age 20
	*/

	value0 := v.Elem().FieldByIndex([]int{0})
	value1 := v.Elem().FieldByIndex([]int{1})
	Println(value0, value1) // cauchy 20
}

方法说明
FieldByNameFunc(match func(string) bool) (StructField, bool)根据传入的匹配函数匹配需要的字段
NumMethod() int返回该类型的方法集中方法的数目
Method(int) Method返回该类型方法集中的第 i 个方法
MethodByName(string) (Method, bool)根据方法名返回该类型方法集中的方法

获取结构体方法,并进行调用:

  • reflect.TypeNumMethod 可以获取该类型可导出的方法(首字母大写)

    • 指针类型(reflect.Ptr)可以获取该类型的指针、值接收者的所有方法
    • 值类型仅可以获取该类型的值接收者的所有方法
  • reflect.TypeMethod 通过下标 index 获取该类型的对应的方法 Method

  • reflect.ValueMethodByName 通过方法名称获取对应可执行方法,实际返回是一个 reflect.Value 类型

    • 返回的类型可以通过 IsValidIsNil 判断是否有效,是否为空

    • 类型存在,则调用 Call 可以调用该类型实际的对应的方法

      • Call 接收参数是 []reflect.Value 类型
func (p *Person) SetName(name string) {
	p.Name = name
}

func (p Person) GetName() string {
	return p.Name
}

func function11() {
	p := &Person{
		"cauchy",
		20,
	}

	t := reflect.TypeOf(p)
	v := reflect.ValueOf(p)

	Println(t.Kind(), t.Elem().Kind()) // ptr struct

	// 获取的方法必须是可导出的,首字母大写
	// 指针类型获取(t.NumMethod()),所有接收者方法
	// 值类型获取(t.Elem().NumMethod()),值接收者方法
	ptrN, N := t.NumMethod(), t.Elem().NumMethod()
	Println(ptrN, N) // 2, 2

	for i := 0; i < ptrN; i++ {
		m := t.Method(i)
		Println(m.Name, m.Type, m.Func)
	} /*
		GetName func(*main.Person) string 0x1ddf80
		SetName func(*main.Person, string) 0x1ddb80
	*/

	// 通过名称获取函数
	f := v.MethodByName("GetName")
	// 如果函数存在
	if f.IsValid() && !f.IsNil() {
		ret := f.Call(nil) // 调用,不用参数 nil
		Println(ret)       // [cauchy]
	}

	// 参数必须是 []reflect.Value 类型
	v.MethodByName("SetName").Call([]reflect.Value{reflect.ValueOf("AQ")})
	ret := v.MethodByName("GetName").Call(nil)
	Println(ret) // [AQ]
}

上述代码需要注意的一点,要想修改一个反射对象,那么它必须是可设置的。((p *SetName)

读者可自行运行上述代码,做相应修改测试是否是预期结果。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Django开发】前后端分离美多商城项目:项目准备和搭建(附代码,文档)

本系列文章md笔记&#xff08;已分享&#xff09;主要讨论django商城项目开发相关知识。本项目利用Django框架开发一套前后端不分离的商城项目&#xff08;4.0版本&#xff09;含代码和文档。功能包括前后端不分离&#xff0c;方便SEO。采用Django Jinja2模板引擎 Vue.js实现…

分表过多引起的问题/Apache ShardingSphere元数据加载慢

目录 环境 背景 探寻 元数据的加载策略 如何解决 升级版本到5.x 调大max.connections.size.per.query max.connections.size.per.query分析 服务启动阶段相关源码 服务运行阶段相关源码 受到的影响 注意事项&#xff08;重要&#xff09; 其他 环境 Spring Boot 2…

数据结构3、基于栈的后缀算术表达式求值

1 题目描述 图1 中缀表达式转化为后缀表达式题目描述 图2 基于栈的后缀算术表达式求值题目描述 2 题目解读 借助一个运算符栈&#xff0c;可将中缀表达式转化为后缀表达式&#xff1b;借助一个运算数栈&#xff0c;可对后缀表达式求值。借助一个运算符栈和一个运算数栈&#xf…

MongoDB安装以及卸载

查询id&#xff1a; docker ps [rootlocalhost ~]# docker stop c7a8c4ac9346 c7a8c4ac9346 [rootlocalhost ~]# docker rm c7a8c4ac9346 c7a8c4ac9346 [rootlocalhost ~]# docker rmi mongo sudo docker pull mongo:4.4 sudo docker images 卸载旧的 sudo docker stop mong…

Win10无法完成更新正在撤销更改的解决方法

在Win10电脑操作过程中&#xff0c;用户看到了“无法完成更新正在撤销更改”的错误提示&#xff0c;这样系统就不能成功完成更新&#xff0c;不知道如何操作才能解决此问题&#xff1f;以下小编分享最简单的解决方法&#xff0c;帮助大家轻松解决Win10电脑无法完成更新正在撤销…

BIO、NIO编程与直接内存、零拷贝

一、网络通信 1、什么是socket&#xff1f; Socket 是应用层与 TCP/IP 协议族通信的中间软件抽象层&#xff0c;它是一组接口&#xff0c;一般由操作 系统提供。客户端连接上一个服务端&#xff0c;就会在客户端中产生一个 socket 接口实例&#xff0c;服务端每接受 一个客户端…

【Linux网络编程】网络编程套接字(1)

【Linux网络编程】网络编程套接字(1) 目录 【Linux网络编程】网络编程套接字(1)源IP地址和目的IP地址端口号端口号和进程ID的关系 网络通信TCP协议UDP协议网络字节序socket编程接口简单的UDP网络程序 作者&#xff1a;爱写代码的刚子 时间&#xff1a;2024.1.29 前言&#xff1…

SV-7101T网络音频终端 网络对讲终端

SV-7101是一款IP网络广播终端&#xff0c;主要作为网络播放器使用&#xff0c;其接收网络的音频数据&#xff0c;提供音频输出。SV-7101与服务器主控软件、有源音箱配套使用可实现主控室对HG7101终端进行定时打铃、实时语音广播和紧急广播等功能。 淘宝速购&#xff1a; SV-701…

Android中属性property_get和property_set的详细用法介绍

1&#xff0c;property_get和property_set的作用说明 在Android操作系统中&#xff0c;property_get和property_set是用于获取和设置系统属性的函数。这些属性通常用于存储和读取配置信息&#xff0c;例如设备配置、网络设置、系统参数等。 property_get函数用于获取指定属性…

websocket 通信协议

websocket是什么 答: 它是一种网络通信协议&#xff0c;是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。 意思就是服务器可以主动向客户端推送信息&#xff0c;客户端也可以主动向服务器发送信息 属于服务器推送技术的一种. 为什么需要websocket? 疑问?…

(五)MySQL的备份及恢复

1、MySQL日志管理 在数据库保存数据时&#xff0c;有时候不可避免会出现数据丢失或者被破坏&#xff0c;这样情况下&#xff0c;我们必须保证数据的安全性和完整性&#xff0c;就需要使用日志来查看或者恢复数据了 数据库中数据丢失或被破坏可能原因&#xff1a; 误删除数据…

MySQL原理(二)存储引擎(3)InnoDB

目录 一、概况&#xff1a; 1、介绍&#xff1a; 2、特点&#xff1a; 二、体系架构 1、后台线程 2、内存池&#xff08;缓冲池&#xff09; 三、物理结构 1、数据文件&#xff08;表数据和索引数据&#xff09; 1.1、作用&#xff1a; 1.2、共享表空间与独立表空间 …

【C/C++ 05】快速排序

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序算法&#xff0c;其基本思想是&#xff1a;任取待排序序列中的某元素作为基准值&#xff0c;按照该基准值将待排序集合分割成两个子序列&#xff0c;左子序列中所有元素均小于基准值&#xff0c;右子序列中所有元素均大于…

MySQL原理(二)存储引擎(1)概述

一、存储引擎介绍 1、概念&#xff1a; &#xff08;1&#xff09;MySQL中的数据用各种不下同的技术存储在文件中&#xff0c;每一种技术都使用不同的存储机制、索引技巧、锁定水平并最终提供不同的功能和能力&#xff0c;这些不同的技术以及配套的功能在MySQL中称为存储引擎…

【数据结构与算法】7.详解队列的基本操作

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》|《数据结构与算法》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ &#x1f64f;小杨水平有限&#xff0c;欢…

2024年最新 MySQL的下载、安装、启动与停止

一、MySQL的下载 MySQL最常用的2个版本&#xff1a; 社区版&#xff1a;免费开源&#xff0c;自由下载&#xff0c;不提供官方技术支持&#xff0c;大多数普通用户选择这个即可。企业版&#xff1a;需要付费&#xff0c;不能在线下载&#xff0c;可以使用30天&#xff0c;提供…

ctfshow web72

下载源码&#xff1a; 开启环境&#xff1a; 本题设置了 open_basedir()&#xff0c;将php所能打开的文件限制在指定的目录树中&#xff0c;包括文件本身。 因为 ini_set() 也被限制了&#xff0c;所以 open_basedir() 不能用 ini_set() 重新设置绕过。 使用 php 伪协议 glob:…

【网络】:网络套接字(UDP)

网络套接字 一.网络字节序二.端口号三.socket1.常见的API2.封装UdpSocket 四.地址转换函数 网络通信的本质就是进程间通信。 一.网络字节序 我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分,网…

数据结构----链表介绍、模拟实现链表、链表的使用

文章目录 1. ArrayList存在的问题2. 链表定义2.1 链表的概念及结构2.2 链表的组合类型 3. 链表的实现3.1 单向、不带头、非循环链表的实现3.2 双向、不带头节点、非循环链表的实现 4.LinkedList的使用4.1 什么是LinkedList4.2 LinkedList的使用4.2.1. LinkedList的构造4.2.2. L…

npm 淘宝镜像正式到期

由于node安装插件是从国外服务器下载&#xff0c;如果没有“特殊手法”&#xff0c;就可能会遇到下载速度慢、或其它异常问题。 所以如果npm的服务器在中国就好了&#xff0c;于是我们乐于分享的淘宝团队干了这事。你可以用此只读的淘宝服务代替官方版本&#xff0c;且同步频率…
最新文章