在 Swing 應用中將控制台輸出(System.out.println()、e.printStackTrace())重導向至 JTextArea 顯示,需自訂 PrintStream 覆寫 write() 方法,並確保 UI 更新在事件調度執行緒 (EDT) 執行。
public class ConsoleRedirectOutputStream extends OutputStream {
private final JTextArea textArea;
private final StringBuilder buffer = new StringBuilder();
public ConsoleRedirectOutputStream(JTextArea textArea) {
this.textArea = textArea;
}
@Override
public void write(int b) throws IOException {
char c = (char) b;
if (c == '\n' || c == '\r') {
flushBuffer();
} else {
buffer.append(c);
}
}
private void flushBuffer() {
final String text = buffer.toString();
buffer.setLength(0);
// 必須在 EDT 更新 UI
SwingUtilities.invokeLater(() -> {
textArea.append(text + "\n");
textArea.setCaretPosition(textArea.getDocument().getLength()); // 自動捲動到底
});
}
@Override
public void flush() throws IOException { flushBuffer(); }
@Override
public void close() throws IOException { flushBuffer(); }
}
// 使用方式
JTextArea consoleArea = new JTextArea();
consoleArea.setEditable(false);
JScrollPane scrollPane = new JScrollPane(consoleArea);
PrintStream consoleStream = new PrintStream(new ConsoleRedirectOutputStream(consoleArea), true);
System.setOut(consoleStream);
System.setErr(consoleStream); // 也導向錯誤輸出
public class ConsoleWindow extends JFrame {
public ConsoleWindow() {
setTitle("應用程式控制台");
setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
setSize(600, 400);
setLocationRelativeTo(null);
JTextArea textArea = new JTextArea();
textArea.setEditable(false);
textArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
add(new JScrollPane(textArea), BorderLayout.CENTER);
// 重導向
PrintStream ps = new PrintStream(new ConsoleRedirectOutputStream(textArea), true);
System.setOut(ps);
System.setErr(ps);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new ConsoleWindow().setVisible(true));
// 測試輸出
new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println("背景執行緒輸出: " + i);
try { Thread.sleep(500); } catch (InterruptedException ignored) {}
}
}).start();
}
}
textArea.append() 必須包裹在 SwingUtilities.invokeLater() 中。write(int) 逐字元呼叫,需自行緩衝至換行再刷新,避免大量 invokeLater 導致效能問題。JTextArea 最大行數,定期移除舊內容(textArea.replaceRange("", 0, endIndex))。System.setOut(originalOut) 還原標準輸出。