利用Java實(shí)現(xiàn)讀寫bmp文件的示例代碼
一、項(xiàng)目背景詳細(xì)介紹
位圖(Bitmap,BMP)是一種最原始、最簡單的圖像文件格式,由微軟和 IBM 在 1980 年代聯(lián)合制定,用于 Windows 操作系統(tǒng)。與 JPEG、PNG 等壓縮格式相比,BMP 文件存儲的是未經(jīng)壓縮的原始像素數(shù)據(jù),文件頭結(jié)構(gòu)也相對簡單,包含 BMP 文件頭(14 字節(jié))和 DIB 信息頭(通常 40 字節(jié))的元數(shù)據(jù),后面直接跟隨像素數(shù)據(jù)。由于無壓縮且像素排列規(guī)則,BMP 文件成為圖像處理入門的首選格式,也是許多學(xué)習(xí)圖像算法、文件格式解析的范例。
在 Java 生態(tài)中,雖然 ImageIO 支持讀取和寫入 BMP,但其實(shí)現(xiàn)并不支持所有 BMP 變種(如帶調(diào)色板的 8 位 BMP、壓縮的 RLE 格式、位域 BI_BITFIELDS)。更重要的是,通過手寫 BMP 解析與生成,可以深入理解二進(jìn)制文件結(jié)構(gòu)、字節(jié)對齊、像素存儲順序、色彩通道排列、大小端問題,以及 Java NIO、ByteBuffer、DataInputStream/DataOutputStream 等 API 的使用。
本項(xiàng)目旨在用純 Java 從零實(shí)現(xiàn)一個輕量級的 BMP 文件讀寫庫,支持以下功能:
讀取常見的 24 位真彩色 BMP 文件,解析文件頭、信息頭、像素數(shù)據(jù);
將內(nèi)存中的像素數(shù)據(jù)(ARGB 或 RGB 數(shù)組)寫出為標(biāo)準(zhǔn) BMP 文件;
支持帶調(diào)色板的 8 位灰度 BMP 讀寫;
支持行字節(jié)對齊與填充;
提供簡單易用的 API:
BmpImage read(File)、void write(BmpImage, File);包含單元測試與示例,便于學(xué)習(xí)和集成。
通過本項(xiàng)目,您將掌握二進(jìn)制文件解析、內(nèi)存與磁盤數(shù)據(jù)映射、圖像像素處理、文件 I/O、字節(jié)序與對齊等核心技術(shù),既可用于圖像算法學(xué)習(xí),也可在不依賴第三方庫的情況下完成基礎(chǔ)圖像處理需求。
二、項(xiàng)目需求詳細(xì)介紹
核心功能
BMP 讀取
解析 BMP 文件頭(14 字節(jié)),獲取文件大小、像素數(shù)據(jù)偏移;
解析 DIB 信息頭(至少 BITMAPINFOHEADER,40 字節(jié)),獲取寬度、高度、位深、壓縮方式、像素數(shù)據(jù)大??;
支持 24 位(無調(diào)色板)和 8 位(帶調(diào)色板)兩種常見格式;
讀取調(diào)色板數(shù)據(jù)(8 位 BMP);
讀取像素數(shù)據(jù),并根據(jù)行對齊規(guī)則計算實(shí)際字節(jié)長度,轉(zhuǎn)換為
int[][]或byte[][]數(shù)組表示。
BMP 寫入
將內(nèi)存中像素數(shù)據(jù)構(gòu)造 BMP 文件頭和 DIB 頭,計算文件大小與偏移;
支持將
int[][](24 位真彩)或byte[][](8 位灰度)像素數(shù)據(jù)寫入文件;自動填充行尾對齊字節(jié)(行長度必須是 4 的倍數(shù));
寫入調(diào)色板(灰度表),寫入像素數(shù)據(jù)。
API 設(shè)計
class BmpImage:封裝寬度、高度、位深、調(diào)色板(可選)、像素數(shù)據(jù);class BmpReader:靜態(tài)方法BmpImage read(File) throws IOException, BmpParseException;class BmpWriter:靜態(tài)方法void write(BmpImage, File) throws IOException;自定義異常
BmpParseException用于格式錯誤。
擴(kuò)展需求
支持 32 位帶 Alpha 通道 BMP(可選擴(kuò)展);
支持 RLE 壓縮的 8 位 BMP(高階擴(kuò)展);
提供
BmpImage toBufferedImage()方法轉(zhuǎn)換為java.awt.image.BufferedImage;提供從
BufferedImage構(gòu)建BmpImage的工廠方法;
性能與健壯性
使用
BufferedInputStream、BufferedOutputStream或 NIOFileChannel進(jìn)行高效 I/O;對所有讀取步驟進(jìn)行合法性校驗(yàn),格式不符時拋出
BmpParseException;單元測試覆蓋寬高、位深、對齊、調(diào)色板、異常路徑。
文檔與測試
完整 Javadoc 注釋;
JUnit 5 單元測試,測試案例包括小尺寸 BMP、大尺寸 BMP、無效文件、非 BMP 文件;
示例主程序演示讀取 BMP 文件并保存為另一個 BMP。
三、相關(guān)技術(shù)詳細(xì)介紹
BMP 文件結(jié)構(gòu)
Bitmap File Header(BITMAPFILEHEADER):14 字節(jié)
bfType(2 bytes): 固定為 “BM” (0x42 0x4D);bfSize(4 bytes): 文件總大?。ㄗ止?jié));bfReserved1、bfReserved2(各 2 bytes):保留,通常為 0;bfOffBits(4 bytes): 像素數(shù)據(jù)在文件中的偏移量(字節(jié)位置)。
DIB Header(BITMAPINFOHEADER):40 字節(jié)
biSize(4 bytes): DIB 頭大小,通常為 40;biWidth(4 bytes)、biHeight(4 bytes):圖像寬度、高度(像素);biPlanes(2 bytes): 顏色平面數(shù),固定為 1;biBitCount(2 bytes): 每像素位數(shù),如 1、4、8、16、24、32;biCompression(4 bytes):壓縮方式(0 = BI_RGB 無壓縮;1 = BI_RLE8;2 = BI_RLE4;3 = BI_BITFIELDS);biSizeImage(4 bytes):像素數(shù)據(jù)大?。ㄗ止?jié)),可為 0;biXPelsPerMeter、biYPelsPerMeter(各 4 bytes):水平/垂直分辨率;biClrUsed(4 bytes):調(diào)色板中顏色數(shù),0 表示默認(rèn);biClrImportant(4 bytes):重要顏色數(shù),0 表示全部重要。
Color Table(可選):當(dāng)
biBitCount≤ 8 時存在,每條 4 字節(jié)(B, G, R, Reserved)Pixel Array:按行從下到上(BMP 默認(rèn)),每行左到右;每行長度需填充到 4 字節(jié)對齊。
Java I/O 與 NIO
DataInputStream/DataOutputStream:方便讀取/寫入基本類型大端或小端;ByteBuffer:調(diào)整字節(jié)序(order(ByteOrder.LITTLE_ENDIAN));FileChannel+MappedByteBuffer:可選內(nèi)存映射加速;BufferedInputStream/BufferedOutputStream:緩沖字節(jié)流提高效率。
字節(jié)對齊
BMP 每行像素數(shù)據(jù)占用字節(jié)數(shù) =
((width * bitsPerPixel + 31) / 32) * 4對齊后每行末尾填充 0x00。
錯誤處理
當(dāng)
bfType不是 “BM” 或biBitCount不支持時,拋出BmpParseException;當(dāng)文件過短、偏移超出或數(shù)據(jù)不完整時,拋出異常。
Java2D 互操作
將
BmpImage轉(zhuǎn)為BufferedImage:
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); for (y,h) ... img.setRGB(x,y,pixel);
從 BufferedImage 構(gòu)造 BmpImage:
int rgb = img.getRGB(x,y); // 分離 R,G,B 通道
四、實(shí)現(xiàn)思路詳細(xì)介紹
數(shù)據(jù)模型定義
class BmpImage:
public class BmpImage {
int width, height;
short bitCount; // 8 或 24
int[][] pixels24; // [row][col] 每像素 0x00RRGGBB
byte[][] pixels8; // [row][col] 調(diào)色板索引
int[] palette; // 8 位調(diào)色板,length = colorsUsed
}- 只存儲必要字段,其它 DIB 信息頭字段可忽略或保留。
讀取流程(BmpReader)
- 打開文件,使用
DataInputStream包裝BufferedInputStream; 讀取并校驗(yàn) BMP 文件頭:
readUnsignedShortLE()(小端),檢查 “BM”;讀取文件大小、保留字段、像素偏移;
讀取 DIB 頭長度,判斷格式,僅處理
biSize == 40;讀取寬、高、平面數(shù)、位深、壓縮方式;
計算行占用字節(jié)數(shù)
rowBytes = ((width * bitCount + 31) / 32) * 4;若
bitCount == 8,讀取colorsUsed條調(diào)色板,每條 4 字節(jié),存入palette;根據(jù)
height正負(fù)判斷存儲方向(正值從下往上,負(fù)值自頂向下);分行讀取像素數(shù)據(jù),解碼 24 位真彩色或 8 位索引,存入
pixels24或pixels8;
寫入流程(BmpWriter)
根據(jù)
BmpImage字段,計算rowBytes與pixelDataSize = rowBytes * abs(height);bfSize = 14 + dibSize + paletteSize + pixelDataSize;使用
DataOutputStream,按小端順序?qū)懭?BITMAPFILEHEADER;寫入 BITMAPINFOHEADER 各字段;
若 8 位,寫入調(diào)色板;
按行填充寫入像素數(shù)據(jù),注意 4 字節(jié)對齊,寫入行尾填充字節(jié);
輔助方法
readUnsignedShortLE()、readIntLE():讀取小端數(shù);writeShortLE()、writeIntLE():寫入小端;padZeros(int count):寫入指定數(shù)量的 0;
與 BufferedImage 互操作
BmpImage toBufferedImage():構(gòu)造BufferedImage并填充像素;static BmpImage fromBufferedImage(BufferedImage img, boolean usePalette);
異常與校驗(yàn)
在各讀取階段檢查可用字節(jié)數(shù);
對不支持的格式或參數(shù),立即拋
BmpParseException;在寫入前驗(yàn)證
bitCount、數(shù)據(jù)數(shù)組與寬高一致。
五、完整實(shí)現(xiàn)代碼
// ===================================================
// 文件:src/main/java/com/example/bmp/BmpImage.java
// ===================================================
package com.example.bmp;
import java.awt.image.BufferedImage;
/**
* BMP 圖像數(shù)據(jù)模型
*/
public class BmpImage {
public int width;
public int height;
public short bitCount; // 8 或 24
public int[][] pixels24; // 每像素 0x00RRGGBB
public byte[][] pixels8; // 每像素調(diào)色板索引
public int[] palette; // 調(diào)色板,length = colorsUsed
/** 轉(zhuǎn)換為 BufferedImage */
public BufferedImage toBufferedImage() {
BufferedImage img = new BufferedImage(width, Math.abs(height),
bitCount == 24 ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_BYTE_INDEXED);
if (bitCount == 24) {
for (int y = 0; y < Math.abs(height); y++) {
for (int x = 0; x < width; x++) {
img.setRGB(x, y, pixels24[y][x]);
}
}
} else {
// 8 位,需創(chuàng)建 IndexColorModel(此處略)
// 簡單填充為灰度圖
for (int y = 0; y < Math.abs(height); y++) {
for (int x = 0; x < width; x++) {
int idx = pixels8[y][x] & 0xFF;
int c = palette[idx];
img.setRGB(x, y, c);
}
}
}
return img;
}
}
// ===================================================
// 文件:src/main/java/com/example/bmp/BmpParseException.java
// ===================================================
package com.example.bmp;
/** BMP 解析異常 */
public class BmpParseException extends Exception {
public BmpParseException(String msg) { super(msg); }
}
// ===================================================
// 文件:src/main/java/com/example/bmp/BmpReader.java
// ===================================================
package com.example.bmp;
import java.io.*;
import java.nio.ByteOrder;
/**
* BMP 文件讀取器
*/
public class BmpReader {
public static BmpImage read(File file) throws IOException, BmpParseException {
try (DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream(file)))) {
// 1. 讀取文件頭
int bfType = readUnsignedShortLE(dis);
if (bfType != 0x4D42) throw new BmpParseException("非 BMP 文件");
int bfSize = readIntLE(dis);
dis.skipBytes(4); // reserved
int bfOffBits = readIntLE(dis);
// 2. 讀取 DIB 頭
int dibSize = readIntLE(dis);
if (dibSize != 40) throw new BmpParseException("不支持的 DIB 頭大小: " + dibSize);
int width = readIntLE(dis);
int height = readIntLE(dis);
short planes = readShortLE(dis);
short bitCount = readShortLE(dis);
int compression = readIntLE(dis);
if (compression != 0) throw new BmpParseException("不支持壓縮: " + compression);
int imageSize = readIntLE(dis);
dis.skipBytes(16); // 跳過分辨率與顏色信息
int colorsUsed = readIntLE(dis);
if (colorsUsed == 0 && bitCount <= 8) {
colorsUsed = 1 << bitCount;
}
// 構(gòu)造 BmpImage
BmpImage img = new BmpImage();
img.width = width;
img.height = height;
img.bitCount = bitCount;
// 3. 讀取調(diào)色板(8 位)
if (bitCount == 8) {
img.palette = new int[colorsUsed];
for (int i = 0; i < colorsUsed; i++) {
int b = dis.readUnsignedByte();
int g = dis.readUnsignedByte();
int r = dis.readUnsignedByte();
dis.readUnsignedByte(); // 保留
img.palette[i] = (r << 16) | (g << 8) | b;
}
img.pixels8 = new byte[Math.abs(height)][width];
} else if (bitCount == 24) {
img.pixels24 = new int[Math.abs(height)][width];
} else {
throw new BmpParseException("僅支持 8 位和 24 位 BMP");
}
// 4. 跳轉(zhuǎn)到像素數(shù)據(jù)偏移
long skipped = dis.skip(bfOffBits - 14 - dibSize - (bitCount==8 ? colorsUsed*4 : 0));
// 5. 計算行長度(字節(jié))對齊到 4 字節(jié)
int rowBytes = ((width * bitCount + 31) / 32) * 4;
// 6. 讀取像素數(shù)據(jù)
boolean bottomUp = height > 0;
int absHeight = Math.abs(height);
for (int row = 0; row < absHeight; row++) {
int y = bottomUp ? absHeight - 1 - row : row;
byte[] rowData = new byte[rowBytes];
dis.readFully(rowData);
ByteArrayInputStream rowIn = new ByteArrayInputStream(rowData);
for (int x = 0; x < width; x++) {
if (bitCount == 24) {
int b = rowIn.read();
int g = rowIn.read();
int r = rowIn.read();
img.pixels24[y][x] = (r << 16) | (g << 8) | b;
} else {
int idx = rowIn.read();
img.pixels8[y][x] = (byte) idx;
}
}
}
return img;
}
}
// 小端讀取輔助
private static int readUnsignedShortLE(DataInputStream dis) throws IOException {
int b1 = dis.readUnsignedByte();
int b2 = dis.readUnsignedByte();
return (b2 << 8) | b1;
}
private static short readShortLE(DataInputStream dis) throws IOException {
int u = readUnsignedShortLE(dis);
return (short) u;
}
private static int readIntLE(DataInputStream dis) throws IOException {
int b1 = dis.readUnsignedByte();
int b2 = dis.readUnsignedByte();
int b3 = dis.readUnsignedByte();
int b4 = dis.readUnsignedByte();
return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1;
}
}
// ===================================================
// 文件:src/main/java/com/example/bmp/BmpWriter.java
// ===================================================
package com.example.bmp;
import java.io.*;
/**
* BMP 文件寫入器
*/
public class BmpWriter {
public static void write(BmpImage img, File file) throws IOException {
try (DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) {
int width = img.width;
int absHeight = Math.abs(img.height);
int bitCount = img.bitCount;
int rowBytes = ((width * bitCount + 31) / 32) * 4;
int imageSize = rowBytes * absHeight;
int paletteSize = (bitCount == 8 ? img.palette.length * 4 : 0);
int bfOffBits = 14 + 40 + paletteSize;
int bfSize = bfOffBits + imageSize;
// 1. 寫文件頭
writeShortLE(dos, 0x4D42); // "BM"
writeIntLE(dos, bfSize);
writeShortLE(dos, 0);
writeShortLE(dos, 0);
writeIntLE(dos, bfOffBits);
// 2. 寫 DIB 頭(BITMAPINFOHEADER)
writeIntLE(dos, 40);
writeIntLE(dos, width);
writeIntLE(dos, img.height);
writeShortLE(dos, 1); // planes
writeShortLE(dos, bitCount);
writeIntLE(dos, 0); // BI_RGB
writeIntLE(dos, imageSize);
writeIntLE(dos, 0); writeIntLE(dos, 0); // 分辨率
writeIntLE(dos, bitCount == 8 ? img.palette.length : 0);
writeIntLE(dos, 0);
// 3. 寫調(diào)色板
if (bitCount == 8) {
for (int c : img.palette) {
int r = (c >> 16) & 0xFF;
int g = (c >> 8) & 0xFF;
int b = c & 0xFF;
dos.writeByte(b);
dos.writeByte(g);
dos.writeByte(r);
dos.writeByte(0);
}
}
// 4. 寫像素數(shù)據(jù)
byte[] pad = new byte[rowBytes - (width * bitCount / 8)];
for (int row = absHeight - 1; row >= 0; row--) {
if (bitCount == 24) {
for (int x = 0; x < width; x++) {
int rgb = img.pixels24[row][x];
dos.writeByte(rgb & 0xFF); // B
dos.writeByte((rgb >> 8) & 0xFF); // G
dos.writeByte((rgb >> 16) & 0xFF); // R
}
} else {
for (int x = 0; x < width; x++) {
dos.writeByte(img.pixels8[row][x]);
}
}
dos.write(pad);
}
}
}
// 小端寫入輔助
private static void writeShortLE(DataOutputStream dos, int v) throws IOException {
dos.writeByte(v & 0xFF);
dos.writeByte((v >> 8) & 0xFF);
}
private static void writeIntLE(DataOutputStream dos, int v) throws IOException {
dos.writeByte(v & 0xFF);
dos.writeByte((v >> 8) & 0xFF);
dos.writeByte((v >> 16) & 0xFF);
dos.writeByte((v >> 24) & 0xFF);
}
}
// 文件:src/main/java/com/example/bmp/Main.java
package com.example.bmp;
import java.io.File;
public class Main {
public static void main(String[] args) throws Exception {
// 讀取 BMP
BmpImage img = BmpReader.read(new File("input.bmp"));
System.out.println("讀取完成: " + img.width + "x" + Math.abs(img.height) + " 位深=" + img.bitCount);
// 轉(zhuǎn)換為 BufferedImage 并另存為 PNG(示例)
// ImageIO.write(img.toBufferedImage(), "png", new File("out.png"));
// 修改像素:反轉(zhuǎn)顏色示例
if (img.bitCount == 24) {
for (int y = 0; y < Math.abs(img.height); y++) {
for (int x = 0; x < img.width; x++) {
int rgb = img.pixels24[y][x];
int r = 255 - ((rgb >> 16) & 0xFF);
int g = 255 - ((rgb >> 8) & 0xFF);
int b = 255 - (rgb & 0xFF);
img.pixels24[y][x] = (r << 16) | (g << 8) | b;
}
}
}
// 寫入 BMP
BmpWriter.write(img, new File("output.bmp"));
System.out.println("寫入完成");
}
}六、代碼詳細(xì)解讀
BmpImage:封裝 BMP 圖像的核心數(shù)據(jù),包括寬度、高度、位深、調(diào)色板(8 位)或真彩色像素數(shù)組,以及與
BufferedImage互操作的方法。BmpParseException:自定義解析異常,用于格式校驗(yàn)失敗時拋出。
BmpReader:
讀取 BMP 文件頭(小端),檢查“BM”標(biāo)識;
讀取 DIB 頭中的寬高、位深、壓縮方式,并校驗(yàn)僅支持無壓縮 8/24 位;
讀取調(diào)色板(8 位),或分配像素數(shù)組;
跳轉(zhuǎn)到像素數(shù)據(jù)偏移位置,按行讀取像素并考慮 4 字節(jié)對齊;
BmpWriter:
計算行長度、像素數(shù)據(jù)大小和文件總大小;
寫入文件頭與 DIB 頭(小端),包括必要字段;
寫入調(diào)色板(8 位)或跳過;
按行自下而上寫入像素數(shù)據(jù),并填充行尾對齊字節(jié);
Main:示例演示 BMP 文件讀取、像素修改(反色)、BMP 寫入,以及與
BufferedImage的互操作。
七、項(xiàng)目詳細(xì)總結(jié)
功能完整:支持讀取和寫入最常見的 8 位帶調(diào)色板 BMP 和 24 位真彩色 BMP;
純 Java 實(shí)現(xiàn):無第三方依賴,便于集成到任意 Java 項(xiàng)目;
對齊與字節(jié)序處理:正確實(shí)現(xiàn)行對齊及小端字節(jié)序,確??缙脚_一致性;
易用 API:提供
BmpReader.read()、BmpWriter.write()兩個靜態(tài)方法,簡潔明了;性能可控:使用緩沖流和按行處理,內(nèi)存占用可控;
可擴(kuò)展:后續(xù)可加入 32 位 Alpha 通道、RLE 壓縮、性能優(yōu)化的 NIO 實(shí)現(xiàn)。
八、項(xiàng)目常見問題及解答
Q:為何 BMP 讀取時要按行倒序?
A:BMP 默認(rèn)自下而上存儲像素,高度字段若為正值表示倒序;Q:如何支持其它 DIB 頭格式?
A:在解析時根據(jù)biSize分支處理不同頭結(jié)構(gòu),如 BITMAPV2INFOHEADER(52 字節(jié));Q:寫入 32 位帶 Alpha BMP?
A:將bitCount設(shè)為 32,寫入 BGRA 順序像素,DIB 頭中的位域需設(shè)置 BI_BITFIELDS;Q:如何提高大文件讀寫性能?
A:可使用 NIOFileChannel與MappedByteBuffer,一次映射全部或部分文件;Q:寫入時如何生成灰度調(diào)色板?
A:調(diào)色板數(shù)組palette[i] = (i << 16)|(i<<8)|i,0-255 等級灰度。
九、擴(kuò)展方向與性能優(yōu)化
NIO 內(nèi)存映射:使用
FileChannel.map()將文件映射到內(nèi)存,使用ByteBuffer直接讀取寫入,減少復(fù)制與方法調(diào)用;并行讀取與處理:對大圖分塊并行讀取和像素處理,提高多核利用率;
支持更多格式:擴(kuò)展到 RLE 壓縮的 8 位 BMP、16 位 RGB565、32 位 BI_BITFIELDS;
動態(tài)調(diào)色板生成:支持自定義調(diào)色板或從圖像均衡化生成偽彩色;
與 Java2D 整合:提供直接在
Graphics2D上繪制 BMP 數(shù)據(jù)的優(yōu)化方法;流式 API:提供從
InputStream和OutputStream讀取寫入的重載方法,方便網(wǎng)絡(luò)傳輸;內(nèi)存優(yōu)化:使用壓縮存儲結(jié)構(gòu)、按需加載行數(shù)據(jù),處理超大圖像防止 OOM;
測試與基準(zhǔn):使用 JMH 對比
ImageIO與本實(shí)現(xiàn)的讀寫性能差異,并進(jìn)行調(diào)優(yōu);工具類集成:將項(xiàng)目打包為 Maven 依賴,提供 CLI 工具快速轉(zhuǎn)換 BMP 格式。
以上就是利用Java實(shí)現(xiàn)讀寫bmp文件的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于Java讀寫bmp文件的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java解析照片拿到GPS位置數(shù)據(jù)的詳細(xì)步驟
這篇文章主要介紹了Java解析照片拿到GPS位置數(shù)據(jù),本文給大家介紹代碼環(huán)境及核心代碼,代碼簡單易懂,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-03-03
Java?stream?sorted使用?Comparator?進(jìn)行多字段排序的方法
這篇文章主要介紹了Java?stream?sorted使用?Comparator?進(jìn)行多字段排序,主要講解使用Java?Stream流排序器Comparator對List集合進(jìn)行多字段排序的方法,包括復(fù)雜實(shí)體對象多字段升降序排序方法,需要的朋友可以參考下2023-03-03
java 裝飾模式(Decorator Pattern)詳解
這篇文章主要介紹了java 裝飾模式(Decorator Pattern)詳解的相關(guān)資料,需要的朋友可以參考下2016-10-10
Spring Boot 自動參數(shù)校驗(yàn)的實(shí)現(xiàn)步驟
在 Spring Boot中實(shí)現(xiàn)參數(shù)自動校驗(yàn)主要依靠 Java Bean Validation API(JSR 380)和 Spring 的集成支持,下面給大家介紹Spring Boot 自動參數(shù)校驗(yàn)的實(shí)現(xiàn)步驟,感興趣的朋友一起看看吧2025-06-06

