在作業系統中移動文件,我們習慣用剪貼或拖拽,幾乎瞬間完成。但在 Java 程式設計中,許多開發者仍採用「複製到目標 → 刪除來源」的笨辦法。對於 KB 級小文件尚無明顯感知,一旦遇到數 GB 甚至數十 GB 的大文件,這種方式會佔用大量 I/O 頻寬與暫存空間,導致操作緩慢、系統資源浪費。
本文將介紹一種更優雅、高效的方案:利用 java.io.File 類的 renameTo() 方法,直接在文件系統層面修改目錄項,實現「瞬移」效果——哪怕是超大文件,也能在毫秒級完成。
Swing 事件處理常需跨組件訪問,且事件參數幾乎固定為 ActionEvent。因此,即便使用 GUI 設計器自動生成代碼,也應將關鍵組件提升為類的成員變量,便於事件方法直接引用。本例包含:
JPanel:主容器JLabel:提示標籤JTextField:顯示源文件列表與目標目錄路徑JButton:選擇源文件、選擇目標目錄、執行移動、關閉程式JScrollPane + JTextArea:滾動顯示操作日誌File[] files:用戶選中的多個源文件File dir:用戶指定的目標目錄JFileChooser chooser = new JFileChooser();
chooser.setMultiSelectionEnabled(true);
if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
files = chooser.getSelectedFiles();
// 將文件名用「、」連接顯示於文本框
StringBuilder sb = new StringBuilder();
for (File f : files) sb.append("、").append(f.getName());
sourceFolderField.setText(sb.length() > 0 ? sb.substring(1) : "");
}
JFileChooser chooser = new JFileChooser();
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
dir = chooser.getSelectedFile();
targetFolderField.setText(dir.getAbsolutePath());
}
if (files != null && files.length > 0 && dir != null) {
for (File file : files) {
File target = new File(dir, file.getName());
infoArea.append(file.getName() + "\t移動到\t" + dir + "\n");
boolean ok = file.renameTo(target); // 關鍵:原子級重命名/移動
infoArea.append((ok ? "------完成" : "------失敗") + "\n");
}
infoArea.append("##################操作完成###################\n");
}
System.exit(0);
完整代碼已整合於下方,複製即可運行(需 JDK 8+)。介面採用 GridBagLayout 精確佈局,支援跨平台顯示中文。
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class QuickMoveFiles extends JFrame {
private static final long serialVersionUID = -666045931923008374L;
private JPanel contentPane;
private JTextArea infoArea;
private JTextField sourceFolderField, targetFolderField;
private File[] files;
private File dir;
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
new QuickMoveFiles().setVisible(true);
} catch (Exception e) { e.printStackTrace(); }
});
}
public QuickMoveFiles() {
setTitle("快速批量移動文件");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 520, 320);
contentPane = new JPanel(new GridBagLayout());
contentPane.setBorder(new EmptyBorder(5,5,5,5));
setContentPane(contentPane);
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(0,0,5,5);
gbc.fill = GridBagConstraints.HORIZONTAL;
// 行 0:源文件
gbc.gridx = 0; gbc.gridy = 0; gbc.anchor = GridBagConstraints.EAST;
contentPane.add(new JLabel("選擇源文件:"), gbc);
gbc.gridx = 1; gbc.gridwidth = 3; gbc.weightx = 1.0;
sourceFolderField = new JTextField(20); contentPane.add(sourceFolderField, gbc);
gbc.gridx = 4; gbc.gridwidth = 1; gbc.weightx = 0;
JButton btnSrc = new JButton("瀏覽"); contentPane.add(btnSrc, gbc);
btnSrc.addActionListener(this::chooseSourceFiles);
// 行 1:目標目錄
gbc.gridx = 0; gbc.gridy = 1; gbc.gridwidth = 1; gbc.anchor = GridBagConstraints.EAST;
contentPane.add(new JLabel("目標文件夾:"), gbc);
gbc.gridx = 1; gbc.gridwidth = 3; gbc.weightx = 1.0;
targetFolderField = new JTextField(20); contentPane.add(targetFolderField, gbc);
gbc.gridx = 4; gbc.gridwidth = 1; gbc.weightx = 0;
JButton btnDst = new JButton("瀏覽"); contentPane.add(btnDst, gbc);
btnDst.addActionListener(this::chooseTargetDir);
// 行 2:日誌區
gbc.gridx = 0; gbc.gridy = 2; gbc.anchor = GridBagConstraints.EAST;
contentPane.add(new JLabel("操作記錄:"), gbc);
gbc.gridx = 1; gbc.gridwidth = 4; gbc.fill = GridBagConstraints.BOTH; gbc.weightx = 1.0; gbc.weighty = 1.0;
infoArea = new JTextArea(); infoArea.setEditable(false);
contentPane.add(new JScrollPane(infoArea), gbc);
// 行 3:按鈕
gbc.gridy = 3; gbc.gridwidth = 1; gbc.fill = GridBagConstraints.NONE; gbc.weightx = 0; gbc.weighty = 0; gbc.anchor = GridBagConstraints.CENTER;
gbc.gridx = 1; JButton btnMove = new JButton("移動"); contentPane.add(btnMove, gbc);
btnMove.addActionListener(this::doMove);
gbc.gridx = 2; JButton btnClose = new JButton("關閉"); contentPane.add(btnClose, gbc);
btnClose.addActionListener(e -> System.exit(0));
}
private void chooseSourceFiles(ActionEvent e) {
JFileChooser chooser = new JFileChooser();
chooser.setMultiSelectionEnabled(true);
if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
files = chooser.getSelectedFiles();
StringBuilder sb = new StringBuilder();
for (File f : files) sb.append("、").append(f.getName());
sourceFolderField.setText(sb.length() > 0 ? sb.substring(1) : "");
} else {
files = new File[0];
sourceFolderField.setText("");
}
}
private void chooseTargetDir(ActionEvent e) {
JFileChooser chooser = new JFileChooser();
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
dir = chooser.getSelectedFile();
targetFolderField.setText(dir.getAbsolutePath());
} else {
dir = null;
targetFolderField.setText("");
}
}
private void doMove(ActionEvent e) {
if (files == null || files.length == 0 || dir == null) return;
for (File file : files) {
File target = new File(dir, file.getName());
infoArea.append(file.getName() + "\t移動到\t" + dir + "\n");
boolean ok = file.renameTo(target);
infoArea.append((ok ? "------完成" : "------失敗(可能跨磁盤或目標已存在)") + "\n");
}
infoArea.append("##################操作完成###################\n");
}
}

renameTo() 在同一分區/磁盤時為原子操作,極快;跨磁盤會失敗(返回 false),需回退到「複製+刪除」方案。target.exists() 並決定覆蓋策略。java.nio.file.Files.move(src, dst, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING) 提供更豐富的語義與異常信息,生產環境推薦使用 NIO.2 API。善用 File.renameTo()(或 NIO.2 Files.move)可避免大文件複製的性能開銷,配合簡潔的 Swing 介面,即可構建出專業級的批量文件移動工具。核心代碼不超過 20 行,卻能輕松應對 GB 級文件的秒級遷移。