fileoutputstream.write(int)仅写入参数的低8位(b & 0xff),高位丢弃;传0x1a、26或0x11a效果相同,均写入0x1a;多字节应使用write(byte[])避免频繁系统调用。

FileOutputStream.write(int) 写入的是低8位,不是直接传十六进制字面量
调用 write(int) 方法时,参数虽然是 int 类型,但实际只取其最低一个字节(即 b & 0xFF),高位全部丢弃。所以传 0x1A、26 或 0x100 + 0x1A 效果完全一样,都会写入字节 0x1A。
常见错误是误以为要“先转成 byte 再传”,结果写成 fos.write((byte)0x1A) —— 这没问题,但多此一举;更危险的是写成 fos.write(0x1A00),你以为写的是 0x00,其实写的是 0x00(因为 0x1A00 & 0xFF == 0x00),但逻辑已经混乱。
- 直接传十六进制字面量:例如
fos.write(0x2F)→ 写入 ASCII'/' - 传十进制等效值也完全正确:例如
fos.write(47)和上面等价 - 避免传大于 255 的数,除非你明确想取模(比如
0x1FF & 0xFF == 0xFF)
写入多个十六进制字节时,别用多次 write(int),改用 write(byte[])
如果目标是写入一串固定字节,比如 0x48, 0x65, 0x6C, 0x6C, 0x6F(即 "Hello"),逐个调用 write(int) 不仅啰嗦,还可能因 I/O 频繁降低性能。JVM 不会帮你合并,每次调用都是一次系统调用准备开销。
正确做法是构造 byte[],一次性写入:
byte[] data = { (byte)0x48, (byte)0x65, (byte)0x6C, (byte)0x6C, (byte)0x6F };
fos.write(data);
注意:必须显式强转为 byte,否则 0x48 是 int 字面量,编译不通过。
-
0xFF及以上值必须加(byte),否则编译报错:“possible loss of precision” - 如果数据来自配置或计算,可先存入
int[]再批量转byte[],但要注意符号扩展问题(如0xFF转byte是-1,再转回int会变成0xFFFFFFFF)
写入负值十六进制(如 0xFF)时,Java byte 是有符号的,但文件里存的就是 0xFF
Java 中 byte 范围是 -128 ~ 127,所以 0xFF 无法直接写成 byte b = 0xFF(编译失败)。必须写成 (byte)0xFF,此时 b 的值是 -1,但这只是 JVM 内部表示;写入文件时,底层仍按 8 位原样输出,也就是二进制 11111111,即十六进制 0xFF。
验证方式:用十六进制编辑器打开文件,你会看到 FF,而不是什么补码解释结果。
- 不要用
String.getBytes()替代手动构造字节,编码可能改变原始值(如 UTF-8 把0xFF编成多字节) - 如果从网络或硬件读来的是无符号字节流,写入时保持
(byte)x转换即可,无需“还原成正数”
close() 前务必 flush()?其实 write(int) 不带缓冲,但 write(byte[]) 可能隐式缓冲
FileOutputStream 的单字节 write(int) 默认不缓冲,数据基本直通内核(取决于 OS 层),所以不 flush() 通常也不丢;但 write(byte[]) 在某些 JDK 版本或大数组场景下,底层可能启用临时缓冲(非标准保证),因此最稳妥的做法仍是显式 flush() 后再 close()。
- 写完立刻关流:一般够用,
close()会自动 flush - 写过程中需确保落盘(如日志关键字段):在
close()前加fos.flush() - 不要依赖 try-with-resources 自动 flush 的“及时性”——它只保证关闭前 flush,不保证立即写磁盘(OS 层仍有缓存)
(byte),或者用了 Integer.toHexString(b) 去查值,结果看到 ff 就以为是正数,其实变量里存的是 -1。











