当前位置: 首页 > news >正文

【go语言】reflect包与类型推断

reflect 包的核心概念

Go 中的反射涉及两个核心概念:

  • Type:表示一个类型的结构体,reflect.Type 是类型的描述。
  • Value:表示一个值的结构体,reflect.Value 是一个具体值的包装。

反射让我们能够动态地访问对象的类型和数据,并根据需要对其进行操作。

常用类型

reflect.Type

reflect.Type 是对 Go 类型的描述。可以通过它获取有关类型的信息,比如类型名、类型的种类、是否是指针、结构体的字段等。

常见方法:

  • t.Kind():获取 reflect.Type 的底层类型(如 intstructslice 等)。
  • t.Name():获取类型的名称,仅对命名类型有效。
  • t.NumField():获取结构体类型的字段数。
  • t.Field(i):获取结构体的第 i 个字段。

reflect.Value

reflect.Value 代表一个变量的值,它包含了具体的值,可以通过它获取或修改数据。

常见方法:

  • v.Kind():获取 reflect.Value 的底层类型(如 intstructslice 等)。
  • v.Interface():将 reflect.Value 转换为 interface{} 类型。
  • v.Set():修改 reflect.Value 的值(需要是可修改的,即传入指针)。
  • v.Type():获取 reflect.Value 的类型。
  • v.String():获取 reflect.Value 的字符串表示。

常见的反射操作

获取类型和值

使用 reflect.TypeOf 获取类型,使用 reflect.ValueOf 获取值。

package mainimport ("fmt""reflect"
)func main() {var x int = 42// 获取类型t := reflect.TypeOf(x)// 获取值v := reflect.ValueOf(x)fmt.Println("Type:", t)     // 输出:Type: intfmt.Println("Value:", v)    // 输出:Value: 42
}

动态修改值

reflect 允许我们在运行时动态修改值。要修改值,必须传递指向变量的指针。

package mainimport ("fmt""reflect"
)func main() {var x int = 42p := reflect.ValueOf(&x) // 传入指针// 修改值p.Elem().SetInt(100)fmt.Println("Modified value:", x) // 输出:Modified value: 100
}

获取结构体字段

使用 reflect 获取结构体字段名和值。

package mainimport ("fmt""reflect"
)type Person struct {Name stringAge  int
}func printStructFields(s interface{}) {val := reflect.ValueOf(s)if val.Kind() == reflect.Struct {for i := 0; i < val.NumField(); i++ {field := val.Field(i)fmt.Printf("%s: %v\n", val.Type().Field(i).Name, field)}}
}func main() {p := Person{"Alice", 30}printStructFields(p)
}

使用反射调用方法

反射不仅可以获取类型和值,还能动态调用方法。

package mainimport ("fmt""reflect"
)type Person struct {Name string
}func (p *Person) SayHello() {fmt.Println("Hello, my name is", p.Name)
}func main() {p := &Person{Name: "Alice"}// 获取反射对象v := reflect.ValueOf(p)// 获取方法并调用method := v.MethodByName("SayHello")method.Call(nil)
}

反射与类型断言的对比

类型断言与反射在用途上有很大区别:

  • 类型断言:通常用于接口类型的断言,快速检查和转换接口类型为具体类型。
  • reflect:允许动态地操作类型和值,可以用于获取更多类型信息或修改值。

示例:类型断言

package mainimport "fmt"func printType(i interface{}) {if str, ok := i.(string); ok {fmt.Println("String:", str)} else if num, ok := i.(int); ok {fmt.Println("Integer:", num)} else {fmt.Println("Unknown type")}
}func main() {printType("Hello")printType(42)printType(3.14)
}

示例:使用 reflect 获取类型和值

package mainimport ("fmt""reflect"
)func main() {var x interface{} = 42v := reflect.ValueOf(x)t := reflect.TypeOf(x)fmt.Println("Type:", t) // 输出:Type: intfmt.Println("Value:", v) // 输出:Value: 42
}

总结:类型断言与反射对比

特性类型断言reflect 包
用途用于接口类型的类型转换用于动态类型检查、修改值、获取字段等
性能高效,编译时确定类型较慢,涉及运行时类型解析
语法简洁性简单直观语法较复杂
类型安全类型安全,编译时检查无类型安全,运行时可能出错
灵活性灵活性较低,仅适用于接口类型断言高度灵活,可动态修改、调用方法等

  • 案例
package _caseimport ("fmt""reflect"
)type student struct {Name string `json:"name,omitempty" db:"name2"`Age  int    `json:"age,omitempty"` // omitempty Zero-Value不序列化
}type User struct {Id   intName stringAge  int
}// 匿名字段
type Boy struct {UserAddr string
}func (u User) Hello(name string) {fmt.Println("hello", name)
}func ReflectCase1() {//reflectTest1()//reflectType("cz")//reflectValue(55.6)//reflectTest2()//u := User{1, "chen", 18}//Poni(u)//m := Boy{User{1, "sa", 20}, "bj"}//reflectTest3(m)//fmt.Println(u)//setValue(&u)//fmt.Println(u)//userMethod(u)//var s student//getTag(&s)
}func getTag(o any) {v := reflect.ValueOf(o)// 返回reflect.TypeOf类型t := v.Type()// 获取字段for i := 0; i < t.Elem().NumField(); i++ {f := t.Elem().Field(i)fmt.Print(f.Tag.Get("json"), "\t")fmt.Println(f.Tag.Get("db"))}
}func userMethod(o any) {v := reflect.ValueOf(o)// 获取方法m := v.MethodByName("Hello")// 有参数的话需要传一个Value类型切片args := []reflect.Value{reflect.ValueOf("666")}// 没有参数只需要:var args []reflect.Value// m.Call()m.Call(args)
}func setValue(o any) {v := reflect.ValueOf(o)// 获取指针指向的元素v = v.Elem()// 取字段f := v.FieldByName("Name")if f.Kind() == reflect.String {f.SetString("zhen")}
}func reflectTest3(o any) {t := reflect.TypeOf(o)fmt.Println(t)// Anoymous:匿名fmt.Printf("%#v\n", t.Field(0))// 值信息fmt.Printf("%#v\n", reflect.ValueOf(o).Field(0))
}func Poni(o any) {t := reflect.TypeOf(o)fmt.Println("类型:", t)fmt.Println("字符串类型:", t.Name())// 获取值v := reflect.ValueOf(o)fmt.Println(v)// 获取所有属性for i := 0; i < t.NumField(); i++ {f := t.Field(i)fmt.Printf("%s : %v, ", f.Name, f.Type)// 获取字段值信息val := v.Field(i).Interface()fmt.Println("val:", val)}fmt.Println("==method==")for i := 0; i < t.NumMethod(); i++ {m := t.Method(i)fmt.Println(m.Name)fmt.Println(m.Type)}
}// 在处理处理少量已知类型时,使用类型断言+switch性能更好,reflect性能低
// 相较于使用interface{} + switch + 类型推断处理结构体时无法获取详细的字段或标签信息。
// reflect处理复杂结构体内的字段,具有优势可以获取结构体的字段、标签、方法等详细信息。
// reflect使用场景:处理大量动态、未知的复杂数据类型,且这些类型在编译时无法预知,使用 reflect 可以在运行时获取这些类型信息
// 实现通用代码
func reflectTest2() {stu := student{Name: "chenzhen",Age:  19,}v := reflect.ValueOf(stu)// 获取struct字段数量fmt.Println("NumFields:", v.NumField())// 获取字段Name值:// 1.v.Field(指定字段序号) -> 适用于不知道字段名(或者结合for遍历操作)// 2.v.FieldByName("指定字段名") -> 适用于知道字段名fmt.Println("Name value:", v.Field(0).String(), ", ", v.FieldByName("Name").String())// 字段类型fmt.Println("Name type:", v.Field(0).Type())t := reflect.TypeOf(stu)for i := 0; i < t.NumField(); i++ {// 获取字段名name := t.Field(i).Namefmt.Println("Field Name:", name)// 获取tagif fieldName, ok := t.FieldByName(name); ok {tag := fieldName.Tagfmt.Println("tag-", tag, ", ", "json:", tag.Get("json"), ", id", tag.Get("id"))}}
}func reflectTest1() {x := 1.2345fmt.Println("TypeOf==")// TypeOf()返回接口中保存值的类型t := reflect.TypeOf(x)fmt.Println("type:", t)fmt.Println("kind:", t.Kind())fmt.Println("ValueOf==")v := reflect.ValueOf(x)fmt.Println("value:", v)fmt.Println("type:", v.Type())fmt.Println("kind:", v.Kind())// Float传入一个Value类型值,返回一个float64类型fmt.Println("value:", v.Float())z := v.Interface() // Interface()返回一个any类型值fmt.Println(z)fmt.Printf("value is %g\n", z)x1 := []int{1, 2, 3}v1 := reflect.ValueOf(x1)fmt.Println("type:", v1.Type())fmt.Println("kind:", v1.Kind())x2 := map[string]string{"test1": "1", "test2": "2"}v2 := reflect.ValueOf(x2)fmt.Println("type:", v2.Type())fmt.Println("kind:", v2.Kind())fmt.Println("kind==")// Kind()返回类型种类,与Type()区别为:如下案例,Kind返回更底层type MyInt intm := MyInt(5)v3 := reflect.ValueOf(m)fmt.Println("type:", v3.Type())fmt.Println("kind:", v3.Kind())
}func reflectType(a any) {t := reflect.TypeOf(a)fmt.Println("类型是:", t)// kind()获取具体类型k := t.Kind()fmt.Println(k)switch k {case reflect.Float64:fmt.Println("a is float64")case reflect.String:fmt.Println("string")default:panic("unhandled default case")}
}func reflectValue(a any) {v := reflect.ValueOf(a)fmt.Println(v)fmt.Println(v.Type())switch k := v.Kind(); k {case reflect.Float64:fmt.Println("a is ", v.Float())default:panic("unhandled default case")}
}

package _caseimport ("errors""fmt""reflect"
)func ReflectCase2() {type user struct {ID    int64Name  stringHobby []string}type outUser struct {ID    int64Name  stringHobby []string}u := user{ID: 1, Name: "nick", Hobby: []string{"篮球", "羽毛球"}}out := outUser{}// 需求1:使用reflect动态copy structrs := copy(&out, u)fmt.Println(rs, out)// 需求2:sliceUser := []user{{ID: 1, Name: "nick", Hobby: []string{"篮球", "羽毛球"}},{ID: 2, Name: "nick1", Hobby: []string{"篮球1", "羽毛球1"}},{ID: 3, Name: "nick2", Hobby: []string{"篮球2", "羽毛球2"}},}slice := sliceColumn(sliceUser, "Hobby")fmt.Println(slice)
}// 从一个切片或结构体中提取指定字段(colu)的值,并返回一个包含这些值的切片
// 每次 t = t.Elem() 或 v = v.Elem() 都是为了处理某一层的指针解引用问题,以便获取实际的值或类型。
// 如果传入的切片类型涉及指针,例如 *[]*Struct,就需要多次解引用才能得到实际的元素类型和值。// 对于四次t = t.Elem()解释
// reflect.Elem(),顾名思义,是取得变量的元素部分
// 在Golang中,变量的元素部分指的是指针指向的变量本身。
// 第一个 t = t.Elem() 处理传入 slice 是指针的情况。
// 第二个 t = t.Elem() 获取切片元素的类型。
// 第三个 t = t.Elem() 处理切片元素是指针的情况,获取指针指向的实际类型。
// o.Elem() 处理遍历时元素是指针的情况,解引用以访问字段。// 我的理解:对于
//
//	 if t.Kind() == reflect.Ptr {
//			t = t.Elem()
//			v = v.Elem()
//		}
//		第一个t = t.Elem()这是为了处理传入时传入的是切片地址的情况,如果传入的 slice 不是指针,比如 []Struct,这一段代码不会执行,因此不会影响后面的逻辑。
//		而如果传入的是切片,则会在第二个t = t.Elem()生效,这是因为切片打印出来是指向其第一个元素的地址,我们要的是其值,
//		所以要t = t.Elem()而接下来的
//		if t.Kind() == reflect.Ptr {
//			t = t.Elem()
//		}则是为了应对其在切片内部还有一个切片指针的情况,需要获取其值而最后的:
//		if o.Kind() == reflect.Ptr {
//				v1 := o.Elem()
//				val := v1.FieldByName(colu)
//				s = reflect.Append(s, val)
//			}则是处理切片中的切片中的field中指针的情况。
func sliceColumn(slice any, colu string) any {t := reflect.TypeOf(slice)v := reflect.ValueOf(slice)// 因为这里传入一个切片,切片值为指向其第一个元素的地址,所以要elemif t.Kind() == reflect.Ptr {t = t.Elem()v = v.Elem()}// 如果直接传入的slice是一个结构体,那么直接返回要找的colu对应值if v.Kind() == reflect.Struct {val := v.FieldByName(colu)return val.Interface()}// 处理切片情况if v.Kind() != reflect.Slice {return nil}t = t.Elem()// 如果还是一个指针,要找value,我们期望他是一个structif t.Kind() == reflect.Ptr {t = t.Elem()}f, _ := t.FieldByName(colu)// 获取要找字段的类型sliceT := reflect.SliceOf(f.Type)// 根据类型创建切片s := reflect.MakeSlice(sliceT, 0, 0)for i := 0; i < v.Len(); i++ {// index(i)返回v持有值的第i个元素。如果v的Kind不是Array、Chan、Slice、String,或者i出界,会panico := v.Index(i)if o.Kind() == reflect.Struct {val := o.FieldByName(colu)s = reflect.Append(s, val)}if o.Kind() == reflect.Ptr {v1 := o.Elem()val := v1.FieldByName(colu)s = reflect.Append(s, val)}}return s.Interface()
}func copy(dest any, source any) error {// 对sorece的reflect处理sT := reflect.TypeOf(source)sV := reflect.ValueOf(source)// 但是如果source传入的是指针,那么还要多操作一次,获取它的值if sT.Kind() == reflect.Ptr {sT = sT.Elem()sV = sV.Elem()}// 对于dest的reflect处理dT := reflect.TypeOf(dest)dV := reflect.ValueOf(dest)// 因为dest要被修改,所以传入的一定是指针if dT.Kind() != reflect.Ptr {return errors.New("target对象必须为指针类型")}dT = dT.Elem()dV = dV.Elem()// source必须为struct或者struct指针if sV.Kind() != reflect.Struct {return errors.New("sorce必须为struct或者struct指针")}// dest必须为struct指针if dV.Kind() != reflect.Struct {return errors.New("dest对象必须为struct指针")}// New()返回一个Value类型值,该值持有一个指向类型为传入类型的新申请的零值的指针,返回值的Type为PtrTo(typ)// 这里destObj是待复制对象,所以new出zero-valuedestObj := reflect.New(dT)for i := 0; i < dT.NumField(); i++ {// 每字段dField := dT.Field(i)if sField, ok := sT.FieldByName(dField.Name); ok {if dField.Type != sField.Type {continue}// 取sV中与dField.Name同名的Value赋给valuevalue := sV.FieldByName(dField.Name)// 设置destObj(指针)对应dField.Name的字段的值为valuedestObj.Elem().FieldByName(dField.Name).Set(value)}}dV.Set(destObj.Elem())// error nilreturn nil
}

相关文章:

【go语言】reflect包与类型推断

reflect 包的核心概念 Go 中的反射涉及两个核心概念&#xff1a; Type&#xff1a;表示一个类型的结构体&#xff0c;reflect.Type 是类型的描述。Value&#xff1a;表示一个值的结构体&#xff0c;reflect.Value 是一个具体值的包装。 反射让我们能够动态地访问对象的类型和…...

3.python运算符

Python 提供了多种运算符&#xff0c;用于执行算术、比较、逻辑等各种操作。以下是 Python 中常见的运算符类型及其用法&#xff1a; 文章目录 1. 算术运算符2. 比较运算符3. 逻辑运算符4. 赋值运算符5. 位运算符6. 成员运算符7. 身份运算符8. 运算符优先级 1. 算术运算符 算…...

【竞技宝】CS2-上海major:spirit力克MOUZ niko梦碎

北京时间2024年12月15日,CS2上海major正在如火如荼的进行中,昨日迎来两场半决赛MOUZ对阵spirit以及FAZE对阵G2。Spirit和MOUZ和各自赢下了自己的选图之后,spirit双子星在图三抗住压力帮助队伍杀入决赛。而G2和FAZE的比赛中,FAZE依然延续上一场的火热手感完全压制了G2,G2的明星选…...

【Leetcode 每日一题】3266. K 次乘运算后的最终数组 II

问题背景 给你一个整数数组 n u m s nums nums&#xff0c;一个整数 k k k 和一个整数 m u l t i p l i e r multiplier multiplier。 你需要对 n u m s nums nums 执行 k k k 次操作&#xff0c;每次操作中&#xff1a; 找到 n u m s nums nums 中的 最小 值 x x x&a…...

etcd集群常见日志

1、节点失去领导者 {"level":"info","ts":"2024-05-07T01:54:04.948Z","logger":"raft","caller":"etcdserver/zap_raft.go:77","msg":"raft.node: 9afce9447872453 lost le…...

【漫话机器学习系列】005.神经网络的结构(architecture on the neural network)

神经网络&#xff08;Neural Network&#xff09;是一种模拟人脑神经系统的计算模型&#xff0c;由大量相互连接的神经元&#xff08;节点&#xff09;组成&#xff0c;广泛应用于深度学习和机器学习领域。以下是神经网络的基本结构及关键组成部分。 1. 神经网络的基本组成 一…...

基于 Couchbase 数据仓库元数据管理的可行性方案

在大数据体系中&#xff0c;元数据管理是数据治理的关键一环。以下是一套元数据管理的可行性方案&#xff0c;适合你的当前架构设计&#xff08;基于 Couchbase 数据仓库&#xff09;并支持高效管理数据的分层与结构。 1. 元数据管理的目标 统一数据管理&#xff1a;清晰描述 …...

SpringBoot:快速构建微服务应用

一、SpringBoot简介 什么是SpringBoot 是由Pivotal团队提供的快速开发框架。它基于Spring框架&#xff0c;可以用于快速构建微服务应用程序。SpringBoot提供了一种快速、便捷的方式来启动和配置一个基于Spring的应用程序&#xff0c;它封装了很多常用的配置&#xff0c;简化了开…...

汽车嵌入式软件构建高效技术团队的全面思考

在汽车嵌入式软件开发领域&#xff0c;构建一支高效的通用技术团队至关重要。这类团队负责为各种项目提供可复用、标准化的技术基石&#xff0c;从而提高开发效率、降低成本并确保产品质量。构建这样的团队需要从技术能力、角色分工、标准化与复用、流程管理与质量保证、工具和…...

【跨库查询、多库查询】.NET开源 ORM 框架 SqlSugar 系列

文章目录 一、跨库方式1&#xff1a;跨库导航二、手动跨库查询三、同服务器&#xff1a;自动查询跨库查询3.1 Mysql和SqlServer自动3.2 自动: PgSql跨Scheme查询3.3 其他库同服务器 四、跨服务器&#xff1a;自动跨库查询4.1 配置SqlServer dblink4.2 配置 Oracle dblink4.3 配…...

智能人体安全防护:3D 视觉技术原理、系统架构与代码实现剖析

随着工业化程度的提高&#xff0c;生产安全已成为企业关注的重点。尤其是在一些存在禁区的工业厂区和车间&#xff0c;人员误入或违规进入将带来严重的安全隐患。为了解决这一问题&#xff0c;迈尔微视推出了智能人体安全检测解决方案&#xff0c;为企业提供全方位的人员安全监…...

第24周:文献阅读

目录 摘要 Abstract 一、现有问题 二、提出方法 三、创新点 模型结构创新 强化学习与GAN结合 属性特征与通顺性优化 四、方法论 生成对抗网络&#xff08;GAN&#xff09; 强化学习&#xff08;RL&#xff09; 模型组件 五、实验研究 数据集 数据预处理 评价指…...

yolov8 转华为昇腾om脚本

目录 yolov8 转华为昇腾 om脚本 测试ok 推理demo: yolov8 转华为昇腾 om脚本 测试ok import sys import osos.chdir(os.path.dirname(os.path.abspath(__file__)))import torchcurrent_dir = os.path.dirname(os.path.abspath(__file__))paths = [os.path.abspath(__file__)…...

分布式事物XA、BASE、TCC、SAGA、AT

分布式事务——Seata 一、Seata的架构&#xff1a; 1、什么是Seata&#xff1a; 它是一款分布式事务解决方案。官网查看&#xff1a;Seata 2.执行过程 在分布式事务中&#xff0c;会有一个入口方法去调用各个微服务&#xff0c;每一个微服务都有一个分支事务&#xff0c;因…...

域名信息收集(小迪网络安全笔记~

附&#xff1a;完整笔记目录~ ps&#xff1a;本人小白&#xff0c;笔记均在个人理解基础上整理&#xff0c;若有错误欢迎指正&#xff01; 2.1 域名信息收集 引子&#xff1a;上一章介绍了服务器的信息收集。本篇则介绍在面对存在Web资产企业时&#xff0c;其域名信息该如何收…...

力扣-图论-13【算法学习day.63】

前言 ###我做这类文章一个重要的目的还是给正在学习的大家提供方向和记录学习过程&#xff08;例如想要掌握基础用法&#xff0c;该刷哪些题&#xff1f;&#xff09;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关键点&#xff0c;力扣上的大佬们的题解质量是非…...

【设计模式】如何用C++实现观察者模式【发布订阅机制】

【设计模式】如何用C实现观察者模式【发布订阅机制】 一、问题背景 代码质量影响生活质量。最近工作中频繁接触各种设计模式&#xff0c;深刻体会到优秀的设计模式不仅能显著降低后续维护的压力&#xff0c;还能提升开发效率。观察者模式作为一种降低耦合度、提高扩展性的利器…...

【LC】2717. 半有序排列

题目描述&#xff1a; 给你一个下标从 0 开始、长度为 n 的整数排列 nums 。 如果排列的第一个数字等于 1 且最后一个数字等于 n &#xff0c;则称其为 半有序排列 。你可以执行多次下述操作&#xff0c;直到将 nums 变成一个 半有序排列 &#xff1a; 选择 nums 中相邻的两…...

AI智算-k8s部署大语言模型管理工具Ollama

文章目录 简介k8s部署OllamaOpen WebUI访问Open-WebUI 简介 Github&#xff1a;https://github.com/ollama/ollama 官网&#xff1a;https://ollama.com/ API&#xff1a;https://github.com/ollama/ollama/blob/main/docs/api.md Ollama 是一个基于 Go 语言开发的可以本地运…...

CloudberryDB(二) 演化路线图

CloudberryDB 制定了演化路线图&#xff08;https://github.com/orgs/cloudberrydb/discussions/369&#xff09;并在逐步改进&#xff0c;这是 Cloudberry Database 发挥独特价值之处。 计划、正在进行或已完成的一些工作。 支持轻松升级 PostgreSQL 内核版本。 原有 Greenp…...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例

文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...

生成 Git SSH 证书

&#x1f511; 1. ​​生成 SSH 密钥对​​ 在终端&#xff08;Windows 使用 Git Bash&#xff0c;Mac/Linux 使用 Terminal&#xff09;执行命令&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" ​​参数说明​​&#xff1a; -t rsa&#x…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课&#xff0c;做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程&#xff0c;它的核心机制是 Goroutine 协程、Channel 通道&#xff0c;并基于CSP&#xff08;Communicating Sequential Processes&#xff0…...

C++中string流知识详解和示例

一、概览与类体系 C 提供三种基于内存字符串的流&#xff0c;定义在 <sstream> 中&#xff1a; std::istringstream&#xff1a;输入流&#xff0c;从已有字符串中读取并解析。std::ostringstream&#xff1a;输出流&#xff0c;向内部缓冲区写入内容&#xff0c;最终取…...

PL0语法,分析器实现!

简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

R 语言科研绘图第 55 期 --- 网络图-聚类

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式&#xff1a; R 语言科研绘图模板 --- sciRplothttps://mp.…...