System2.java

System.out.println(""); System.err.println("");

复制代码
package further.util;

import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;

/**
 * 控制台同步输出(项目扩展,与 {@link java.lang.System} 不同包名,避免替 JDK 引导类)。
 * <p>
 * <strong>顺序:</strong>Eclipse 等对 stdout / stderr <strong>两条管道分别缓冲</strong>,即使 JVM 里加锁、逐行 flush,
 * 控制台仍常出现「先整块黑字、再整块红字」,与源码交替调用顺序不一致------这在 IDE 侧无法仅靠 Java 根治。
 * 要保证<strong>显示顺序与调用顺序一致</strong>,只能<strong>合并为一条管道</strong>(只写 {@link FileDescriptor#out}),见 {@link #installOrderedConsole()}。
 * </p>
 * <p>
 * <strong>红色:</strong>IDE 的红字来自<strong>真实 stderr(fd2)</strong>,见 {@link #installDualFileDescriptorConsole()};
 * 与「合并管道保顺序」二者在 Eclipse 内置控制台里<strong>不可兼得</strong>。合并模式下 {@link Kind#ERR} 时默认加
 * {@code [stderr] } 前缀区分错误语义;若控制台支持 ANSI,可加 {@code -Dsyncconsole.ansiErr=true} 尝试红色转义(部分 Eclipse 版本仍不会着色)。
 * </p>
 *
 * @author ZengWenFeng
 * @mobile 13805029595
 * @email 117791303@qq.com
 * @date 2026.05.08
 */
public final class System2
{
	/**
	 * 打印目标:标准输出 / 标准错误(语义),用于 {@link #println(Kind, String)} 第一个参数。
	 */
	public enum Kind
	{
		/** 标准输出 */
		OUT,
		/** 标准错误 */
		ERR
	}

	/** 与 {@link Kind#OUT} 相同,便于书写 {@code System2.println(System2.OUT, ...)} */
	public static final Kind OUT = Kind.OUT;

	/** 与 {@link Kind#ERR} 相同 */
	public static final Kind ERR = Kind.ERR;

	private static final Object CONSOLE_LOCK = new Object();

	private static final String STDERR_PREFIX = "[stderr] ";

	private static final String ANSI_RED = "\u001B[31m";

	private static final String ANSI_RESET = "\u001B[0m";

	private static volatile boolean installed;

	/** true:合并管道模式;false:双 fd(JDK 式)。 */
	private static volatile boolean mergedStdoutMode;

	private static volatile PrintStream outStream;

	private static volatile PrintStream errStream;

	private static volatile PrintStream unifiedStream;

	private static volatile boolean ansiErrRed = Boolean.parseBoolean(java.lang.System.getProperty("syncconsole.ansiErr", "false"));

	private System2()
	{
	}

	/**
	 * <strong>默认推荐:</strong>只使用标准输出 fd,{@link java.lang.System#out} 与 {@link java.lang.System#err} 指向同一 {@link PrintStream},
	 * 控制台收到的字节顺序与 {@link #println(Kind, String)} 调用顺序一致。{@link Kind#ERR} 时默认带 {@code [stderr] } 前缀;
	 * 需要 ANSI 红字时可 {@code -Dsyncconsole.ansiErr=true}(视控制台是否解析转义而定)。
	 */
	public static void installOrderedConsole()
	{
		installMergedStdoutInternal();
	}

	/**
	 * 与 {@link #installOrderedConsole()} 相同实现(合并管道、顺序优先)。
	 */
	public static void installMergedStdoutStrictOrder()
	{
		installMergedStdoutInternal();
	}

	private static void installMergedStdoutInternal()
	{
		synchronized (CONSOLE_LOCK)
		{
			if (installed)
			{
				return;
			}
			try
			{
				OutputStream raw = new FileOutputStream(FileDescriptor.out);
				OutputStream gated = new GatedOutputStream(raw);
				PrintStream ps = new PrintStream(gated, true, "UTF-8");
				unifiedStream = ps;
				java.lang.System.setOut(ps);
				java.lang.System.setErr(ps);
				mergedStdoutMode = true;
				installed = true;
			}
			catch (UnsupportedEncodingException e)
			{
				throw new IllegalStateException("UTF-8 must be supported", e);
			}
		}
	}

	/**
	 * JDK 式双 fd:{@link java.lang.System#err} 走真实 stderr,在 Eclipse 中一般为红色,但<strong>不要期望</strong>与 {@link Kind#OUT} 交替时
	 * 显示顺序与源码一致(IDE 分管道缓冲)。
	 */
	public static void installDualFileDescriptorConsole()
	{
		synchronized (CONSOLE_LOCK)
		{
			if (installed)
			{
				return;
			}
			try
			{
				FileOutputStream fosOut = new FileOutputStream(FileDescriptor.out);
				FileOutputStream fosErr = new FileOutputStream(FileDescriptor.err);
				outStream = new PrintStream(fosOut, true, "UTF-8");
				errStream = new PrintStream(fosErr, true, "UTF-8");
				java.lang.System.setOut(outStream);
				java.lang.System.setErr(errStream);
				mergedStdoutMode = false;
				installed = true;
			}
			catch (UnsupportedEncodingException e)
			{
				throw new IllegalStateException("UTF-8 must be supported", e);
			}
		}
	}

	/**
	 * @deprecated 语义同 {@link #installDualFileDescriptorConsole()}
	 */
	@Deprecated
	public static void installPrintStreamsLikeSystemClass()
	{
		installDualFileDescriptorConsole();
	}

	public static void setAnsiErrRedEnabled(boolean enabled)
	{
		ansiErrRed = enabled;
	}

	public static boolean isAnsiErrRedEnabled()
	{
		return ansiErrRed;
	}

	/**
	 * 按通道输出一行(换行)。示例:{@code System2.println(System2.OUT, "")}、{@code System2.println(System2.ERR, "msg")}。
	 *
	 * @param kind {@link #OUT} 或 {@link #ERR},不可为 {@code null}
	 * @param message 文本;{@code null} 时与 {@link PrintStream#println(String)} 一致
	 */
	public static void println(Kind kind, String message)
	{
		if (kind == null)
		{
			throw new IllegalArgumentException("kind is null");
		}
		if (kind == Kind.OUT)
		{
			printlnOut(message);
		}
		else
		{
			printlnErr(message);
		}
	}

	/**
	 * @param kind {@link #OUT} 或 {@link #ERR}
	 * @param obj 将 {@link String#valueOf(Object)} 再写出
	 */
	public static void println(Kind kind, Object obj)
	{
		println(kind, String.valueOf(obj));
	}

	/**
	 * @param kind {@link #OUT} 或 {@link #ERR}
	 * @param message 不换行;{@code null} 当作 ""
	 */
	public static void print(Kind kind, String message)
	{
		if (kind == null)
		{
			throw new IllegalArgumentException("kind is null");
		}
		if (kind == Kind.OUT)
		{
			printOut(message);
		}
		else
		{
			printErr(message);
		}
	}

	private static void printlnOut(String message)
	{
		ensureInstalled();
		synchronized (CONSOLE_LOCK)
		{
			if (mergedStdoutMode)
			{
				unifiedStream.println(message);
			}
			else
			{
				outStream.println(message);
				outStream.flush();
			}
		}
	}

	private static void printlnErr(String message)
	{
		ensureInstalled();
		synchronized (CONSOLE_LOCK)
		{
			if (mergedStdoutMode)
			{
				if (ansiErrRed)
				{
					unifiedStream.print(ANSI_RED);
					unifiedStream.println(message);
					unifiedStream.print(ANSI_RESET);
				}
				else
				{
					unifiedStream.print(STDERR_PREFIX);
					unifiedStream.println(message);
				}
			}
			else
			{
				errStream.println(message);
				errStream.flush();
			}
		}
	}

	private static void printOut(String message)
	{
		ensureInstalled();
		synchronized (CONSOLE_LOCK)
		{
			if (mergedStdoutMode)
			{
				unifiedStream.print(message == null ? "" : message);
			}
			else
			{
				outStream.print(message == null ? "" : message);
				outStream.flush();
			}
		}
	}

	private static void printErr(String message)
	{
		ensureInstalled();
		synchronized (CONSOLE_LOCK)
		{
			if (mergedStdoutMode)
			{
				if (ansiErrRed)
				{
					unifiedStream.print(ANSI_RED);
					unifiedStream.print(message == null ? "" : message);
					unifiedStream.print(ANSI_RESET);
				}
				else
				{
					unifiedStream.print(STDERR_PREFIX);
					unifiedStream.print(message == null ? "" : message);
				}
			}
			else
			{
				errStream.print(message == null ? "" : message);
				errStream.flush();
			}
		}
	}

	public static void flush()
	{
		synchronized (CONSOLE_LOCK)
		{
			if (!installed)
			{
				return;
			}
			if (mergedStdoutMode)
			{
				if (unifiedStream != null)
				{
					unifiedStream.flush();
				}
			}
			else
			{
				if (outStream != null)
				{
					outStream.flush();
				}
				if (errStream != null)
				{
					errStream.flush();
				}
			}
		}
	}

	private static void ensureInstalled()
	{
		if (!installed)
		{
			throw new IllegalStateException("Call System2.installOrderedConsole() (or installMergedStdoutStrictOrder()) first");
		}
	}

	private static final class GatedOutputStream extends OutputStream
	{
		private final OutputStream delegate;

		GatedOutputStream(OutputStream delegate)
		{
			this.delegate = delegate;
		}

		@Override
		public void write(int b) throws IOException
		{
			synchronized (CONSOLE_LOCK)
			{
				delegate.write(b);
			}
		}

		@Override
		public void write(byte[] b, int off, int len) throws IOException
		{
			synchronized (CONSOLE_LOCK)
			{
				delegate.write(b, off, len);
			}
		}

		@Override
		public void flush() throws IOException
		{
			synchronized (CONSOLE_LOCK)
			{
				delegate.flush();
			}
		}
	}
}
相关推荐
石山代码3 小时前
ArrayList / HashMap / ConcurrentHashMap
java·开发语言
AskHarries4 小时前
系统提示词、开发者指令和用户输入的优先级
java·前端·数据库
daidaidaiyu5 小时前
ThingsBoard 规则链系统源码分析和自定义定时器
java
小毛驴8505 小时前
spring-boot-maven-plugin,maven-compiler-plugin 功能对比
java·python·maven
csdn_aspnet6 小时前
Java 霍尔分区算法(Hoare‘s Partition Algorithm)
java·开发语言·算法
霸道流氓气质6 小时前
通义灵码 IDEA 插件完全使用指南
java·ide·intellij-idea
诸葛务农6 小时前
道路行驶条件下电动汽车永磁电机的有效使用寿命及永磁体的失效和回收再利用(下)
java·开发语言·算法
Percep_gan6 小时前
Java8中的stream的测试使用
java
砍材农夫6 小时前
物联网实战:Spring Boot MQTT | MQTT 设备模拟器演示(附源码)
java·spring boot·后端·物联网·spring·netty
EAIReport7 小时前
Spring AI 详解:Java 开发者快速落地 AI 应用
java·人工智能·spring