news 2026/10/3 2:07:23

Java 转 go 学习 - 类型转换

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java 转 go 学习 - 类型转换

文章目录

  • 类型转换
    • 1. 数值类型之间的转换
    • 2. 字符串和字节切片
    • 3. 字符串和数字之间的转换(最常用)
    • 4. 字符串和 bool 的转换
    • 5. 接口类型转换
      • 5.1 接口类型断言
      • 5.2 接口 -> 接口转换
    • 6. 切片类型转换的问题
    • 7. 指针转换
    • 8. 小结

本系列文章:

  • Java 转 go 学习 - 项目管理
  • Java 转 go 学习 - 基本语法

类型转换

1. 数值类型之间的转换

转换方向说明风险
int→int64小转大✅ 安全
int64→int大转小⚠️ 可能溢出
int→float64整数转浮点✅ 安全
float64→int浮点转整数⚠️ 丢失小数部分
int→byte转8位⚠️ 可能溢出

例子如下:

packagemainimport"fmt"funcmain(){// 语法:目标类型(值)variint=10varffloat64=float64(i)// int → float64varbbyte=byte(i)// int → bytevari64int64=int64(i)// int → int64fmt.Println(f,b,i64)// 10 10 10// 精度丢失varbigint64=1000varsmallint8=int8(big)fmt.Println(big,small)// 1000 -24// 浮点数转整数, 小数丢失varfloatfloat64=10.5fmt.Println(int(float))// 10}

2. 字符串和字节切片

其实就是 string 和 byte[] 的转换,类比 Java 的 String 和 byte[] 转换。

functest2(){// 字符串 → []bytes:="hello"b:=[]byte(s)// []byte → 字符串b2:=[]byte{104,101,108,108,111}s2:=string(b2)// 字符串 → []rune (处理 Unicode)s3:="你好"runes:=[]rune(s3)fmt.Println(b,s2,runes)// [104 101 108 108 111] hello [20320 22909]}

3. 字符串和数字之间的转换(最常用)

下面用 ai 帮我整理了个表格,比较直观看到类型的转换函数,看到这些函数又想起了当年学 c 的日子:

转换方向目标类型推荐函数 (高效/标准)备选函数 (通用/格式化)说明与注意事项
任意 → 字符串
bool→stringstringstrconv.FormatBool(b)fmt.Sprintf("%v", b)FormatBool返回 “true” 或 “false”。
int→stringstringstrconv.Itoa(i)fmt.Sprintf("%d", i)Itoa是FormatInt(i, 10)的简便写法,性能更好。
int64→stringstringstrconv.FormatInt(i, base)fmt.Sprintf("%d", i)base通常为 10。支持 2~36 进制转换。
uint→stringstringstrconv.FormatUint(i, base)fmt.Sprintf("%d", i)用于无符号整数。
float→stringstringstrconv.FormatFloat(f, 'f', -1, 64)fmt.Sprintf("%g", f)'f': 定点数;-1: 自动精度;64: float64。可用'g'自动去零。
字符串 → 任意
string→boolboolstrconv.ParseBool(s)-仅接受:"1", "t", "T", "true", "TRUE", "True"(及其 false 对应值)。
string→intintstrconv.Atoi(s)fmt.Sscanf(s, "%d", &i)Atoi是ParseInt(s, 10, 0)的简便写法,返回int。最常用。
string→int64int64strconv.ParseInt(s, base, bitSize)-base: 进制(10);bitSize: 位数(0=int, 64=int64)。
string→uintuintstrconv.ParseUint(s, base, bitSize)-解析无符号整数。
string→floatfloat64strconv.ParseFloat(s, bitSize)fmt.Sscanf(s, "%f", &f)bitSize通常为 64。若需 float32 则传 32。
其他实用转换
[]byte↔string互转string(b)/[]byte(s)-零拷贝 (逻辑上),Go 特有语法,无需 strconv。
int→[]byte[]bytestrconv.AppendInt([]byte{}, i, 10)-避免中间 string 分配,直接追加到字节切片,高性能场景推荐。

下面简单看下一些例子:

functest4(){// --- 数字转字符串 ---n:=123// 一般都是用这个函数, 转换最快, int -> strings1:=strconv.Itoa(n)fmt.Println(s1,n)// 123 123// 支持格式化 (如加前缀、补零)s2:=fmt.Sprintf("%05d",n)fmt.Println(s2)// 00123// int64 或指定进制 (如二进制), 第二个参数就是指定进制s3:=strconv.FormatInt(1000,2)s4:=strconv.FormatInt(1000,8)s5:=strconv.FormatInt(1000,16)fmt.Println(s3,s4,s5)// 1111101000 1750 3e8// --- 字符串转数字 ---str:="456"// 专用于 string -> int, 一般都是用这个val1,err1:=strconv.Atoi(str)iferr1==nil{fmt.Println(val1)// 456}// 需要处理 int64 或特定进制时使用, 第二个参数就是指定进制数val2,err2:=strconv.ParseInt(s3,2,64)fmt.Println(err2,val2)// <nil> 1000val3,err2:=strconv.ParseInt(s4,8,64)fmt.Println(err2,val3)// <nil> 1000val4,err2:=strconv.ParseInt(s5,16,64)fmt.Println(err2,val4)// <nil> 1000// 浮点数fStr:="3.14159"fVal,_:=strconv.ParseFloat(fStr,64)fmt.Println(fVal)// 3.14159// Parse 系列函数不会自动去除空格atoi,err:=strconv.Atoi(" 123 ")println(err.Error(),atoi)// strconv.Atoi: parsing " 123 ": invalid syntax 0// 会报错,需先通过 TrimSpace 清空atoi1,err1:=strconv.Atoi(strings.TrimSpace(" 123 "))println(err1,atoi1)// (0x0,0x0) 123}

⚠️ 在循环中大量拼接数字时,直接使用 Append 系列函数可以减少内存分配(GC 压力):

// 不推荐:每次循环都分配新的 stringfori:=0;i<1000;i++{s:=strconv.Itoa(i)// ...}// 推荐直接操作 []bytebuf:=make([]byte,0,64)fori:=0;i<1000;i++{buf=strconv.AppendInt(buf,int64(i),10)buf=append(buf,',')}result:=string(buf)// 最后再转一次 string

最后有一个注意点:

fmt.Println(string(65))

这行代码最终会输出 A,而不是 65,所以将 int 转 string,需要用strconv.Itoa或fmt.Sprintf。


4. 字符串和 bool 的转换

ParseBool 可以将字符串转成 bool,支持:"1", "t", "T", "true", "TRUE", "True"等字符串,下面是示例:

functest5(){b:=true// bool -> strings:=strconv.FormatBool(b)fmt.Println(s)// true// string -> bool// 支持: "1", "t", "T", "true", "TRUE", "True" 等b2,err:=strconv.ParseBool("True")b3,err:=strconv.ParseBool("1")b4,err:=strconv.ParseBool("t")b5,err:=strconv.ParseBool("T")b6,err:=strconv.ParseBool("true")b7,err:=strconv.ParseBool("True")fmt.Println(b7,b2,b3,b4,b5,b6,err)// true true true true true true <nil>}

在循环中大量拼接数字时,直接使用 Append 系列函数可以减少内存分配(GC 压力):

5. 接口类型转换

5.1 接口类型断言

假设现在有一个空接口,上一篇文章就说明了空接口其实就相当于 Java 的 Object 类,可以存储任何类型的数据,类型断言就是说:我知道这个接口变量里实际存的是某种具体类型,我要把它‘还原’成那个具体类型,以便使用该类型特有的方法或属性。

🔹 语法如下:

value,ok:=interfaceVariable.(ConcreteType)
  • interfaceVariable:接口类型的变量,比如var i interface{} = "hello"。
  • ConcreteType:接口具体类型,比如上面的string。
  • value:断言成功后,转换后的具体类型的值。
  • ok:bool 类型,是否断言成功,如果断言失败就是这个类型的零值,比如string的空字符串,int的 0。

下面有两种方式去断言,一种是判断 ok,一种就是不管 ok:

用法代码示例行为推荐场景
安全断言 (双值)s, ok := i.(string)如果类型不对,ok为false,程序继续运行。绝大多数场景,特别是处理外部输入、JSON解析、不确定类型时。
直接断言 (单值)s := i.(string)如果类型不对,程序直接 Panic (崩溃)。你100% 确定类型正确,或者希望类型错误时直接让程序崩溃以暴露 Bug。

下面举个例子,首先是安全断言:

functest6(){variinterface{}="hello"// 1. 断言为 strings,ok:=i.(string)ifok{fmt.Println(s,len(s))// hello 5}// 2. 尝试断言为 int (实际上里面是 string)n,ok:=i.(int)if!ok{fmt.Println("断言失败:这确实不是一个整数",n)// 断言失败:这确实不是一个整数 0// n 的值现在是 0 (int 的零值),但程序没挂}else{fmt.Println(n)}}

可以看到通过 ok 判断是否断言不会程序异常,下面是直接断言,这种就比较危险,断言失败就会程序退出。

functest7(){variinterface{}="hello"// 如果里面真的是 string,没问题s:=i.(string)fmt.Println(s)// 如果里面不是 int,下面这行会让程序直接崩溃 (panic)n:=i.(int)// panic: interface conversion: interface {} is string, not intfmt.Println(n)}

当然如果不知道接口是什么类型,可以通过 switch 去判断:

funcdescribe(iinterface{}){// v 是根据 case 自动转换后的具体类型的值switchv:=i.(type){caseint:// 在这里 v 是 int 类型fmt.Printf("整数: %d\n",v*2)casestring:// 在这里 v 是 string 类型fmt.Printf("字符串: %q\n",v)casebool:fmt.Printf("布尔: %v\n",v)default:// 未知类型,v 保持 interface{} 类型fmt.Printf("未知类型: %T\n",v)}}

5.2 接口 -> 接口转换

这种接口到接口的转换就是隐式类型转换或接口赋值,比如下面的例子:

functest8(){typeReaderinterface{Read(p[]byte)(nint,errerror)}typeWriterinterface{Write(p[]byte)(nint,errerror)}// ReadWriter 嵌入了 Reader 和 Writer,所以它必须同时实现 Read 和 WritetypeReadWriterinterface{Reader;Writer}// 这是一个大接口变量varrw ReadWriter// rw 肯定实现了 Read 方法,所以它可以被视为 Readervarr Reader=rw// rw 肯定实现了 Write 方法,所以它可以被视为 Writervarw Writer=rw}

这种就是不需要通过rw.(Reader)来断言,因为 ReadWriter 的定义里明确包含了 Reader,编译器在编译阶段就能 100% 确定:任何能放进 ReadWriter 变量的值,一定也能放进 Reader 变量。这是一种安全的、隐式的向上转型,类比成 java 就是下面的情况,只是 Java 中的接口不能实现接口:

publicclassMain{publicstaticvoidmain(String[]args){Ac1=newC();Bc2=newC();}}interfaceA{}interfaceB{}classCimplementsA,B{}

可以看到 C 实现了 A、B 接口,所以 C 可以赋值给 A,也可以赋值给 B。


6. 切片类型转换的问题

下面先来看一个例子:

functest9(){varints[]int=[]int{1,2,3}// 下面这行会报错:Cannot use 'ints' (type []int) as the type []interface{}varinterfaces[]interface{}=ints}

虽然单个int可以转成interface{},但是int切片就不行了,需要使用下面的写法:

functest9(){varints[]int=[]int{1,2,3}varinterfaces[]interface{}for_,v:=rangeints{// 每个 int 自动转为 interface{}interfaces=append(interfaces,v)}fmt.Println(interfaces[0].(int),interfaces[1].(int),interfaces[2].(int))// 1 2 3}

🤔 为什么会这样?

  • 🚀[]int是一个连续内存块,每个元素是 固定大小的 int(比如 8 字节)。
  • 🚀[]interface{}每个元素是一个 接口值,包含两个指针:类型信息指针、数据指针,在 64 位系统上,每个 interface{} 占 16 字节,不能直接赋值。

7. 指针转换

还是一样先看下面的例子:

functest10(){variint32=0x12345678// 十六进制整数fmt.Printf("原始值: %x\n",i)// 原始值: 12345678// 获取指向 i 的 unsafe.Pointerp:=unsafe.Pointer(&i)// 转为 *byte 指针,访问第一个字节b:=(*byte)(p)// 修改第一个字节(小端序下是最低有效字节)*b=0xFF// 修改后: 123456fffmt.Printf("修改后: %x\n",i)}

💡 在小端序机器上(如x86/x64),0x12345678 的内存布局是[78][56][34][12],修改第一个字节就是改0x78 → 0xFF,结果变成0x123456FF。

在 Go 中普通的指针比如*int、*string类型是安全的,因为:

  • *int只能指向int类型变量。
  • 不能把*int直接赋给*float64,编译器会报错。

⚠️ 但是 unsafe.Pointer 比较特殊,是一个万能指针,可以指向任何类型的数据,同时也可以被强转成任何其他类型的指针,用不好会导致数据混乱,程序崩溃。

上面的代码首先获取了指向 i 的 unsafe.Pointer,然后转成 byte 指针,最后访问 byte 数据,修改第一个字节的地址。

最后再来介绍下uintptr,有时候需要对指针做算术运算(比如偏移几个字节),这时要用到 uintptr,如下面的例子,我们定义一个结构体,然后通过 uintptr 拿到结构体的地址,再通过算数运算进行访问和修改结构体里面的属性:

// 定义一个结构体typeDatastruct{IDint32// 占 4 字节Flagbool// 占 1 字节 (但通常会有填充 padding)Valueint64// 占 8 字节}functest11(){d:=Data{ID:100,Flag:false,Value:9999,}fmt.Printf("原始数据: %+v\n",d)fmt.Printf("原始 Value 地址: %p\n",&d.Value)// 1. 获取结构体起始地址,转成 unsafe.PointerbasePtr:=unsafe.Pointer(&d)// 2. 将 unsafe.Pointer 转成 uintptr, 整数, 可以进行指针地址加减baseAddr:=uintptr(basePtr)// 3. 获取 Value 这个字段的偏移量, 也就是结构体的起始位置 + x 才能到 Value 这个字段的位置offset:=unsafe.Offsetof(d.Value)// 4. 计算偏移量, 求出 Value 字段的地址targetAddr:=baseAddr+offset// 5. 转成指针valuePointer:=unsafe.Pointer(targetAddr)// 6. 转成具体类型的指针intPointer:=(*int64)(valuePointer)// 7. 访问 Value 值fmt.Println(strconv.FormatInt(int64(targetAddr),16),*intPointer)// 8. 修改值*intPointer=8888fmt.Println(d)// 最终输出如下:// 原始数据: {ID:100 Flag:false Value:9999}// 原始 Value 地址: 0xc00000a0a8// c00000a0a8 9999// {100 false 8888}}

8. 小结

本文重点学习了 go 的类型转换,里面的例子使用千问 + 豆包生成,跟 Java 比起来还是很像的,下一篇文章就来学习下 go 的控制结构,也就是 if、for、switch 等。





如有错误,欢迎指出!!!!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 2:04:17

DRV8818+STM32F042双极步进电机工业驱动实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/3 2:00:21

Python 密码学最佳实践:语言边界、RSA 陷阱与 FFI 突围路径

【免费下载链接】publications Publications from Trail of Bits 项目地址&#xff1a; https://gitcode.com/GitHub_Trending/pu/publications 点击查看 免费下载 本文基于 Trail of Bits 首席安全工程师 Paul Kehrer 在 PyCon AU 2019 的演讲《Best Practices for Cryptogra…

作者头像 李华