学习 java.lang.System 类

本文深入探讨 Java 中的 System 类,包括其提供的标准输入输出流、重定向功能以及加载文件和库的方法。文章详细介绍了如何使用 System 类进行输入输出操作,并讨论了如何改变默认的输入输出目标。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

java.lang.System类应该说是JDK提供的一个很好的工具类。这个类设计成final,就是不让你继承。不提供构造函数,就是说不让你实例化。它却给我们提供了很多有用的方法和属性。

[size=medium][color=blue][b]System 类提供了标准输入(in),输出(out)和错误(err)流。[/b][/color][/size]
什么是标准输入输出流?这是一个Unix概念,用来表示被程序使用的单个的信息流。还不不明白。还有什么不标准的吗?哈哈。其实我看了wiki上的解释后认为翻译成默认的流更好。停下来想一想。当你想在你的程序里面输出一个字符串的时候,操作系统怎么知道你要输出到哪里去呢?现在你当然会想当然的认为输出到显示器上啊。那时你习惯了。其实很久以前的操作系统中,在你准备输出之前,你要先连接好输出设备,是不是很麻烦。所以OS后来就预先为你连接了一个设备用于你的输入输出,通常都是控制台了。这样你就不用连接就可以用了。所以我认为理解成默认的更好。当然既然大家都认为它是默认的,那他不就是标准的了?
还有一个问题,既然都是输出,为什么还分out和err。这个问题好!要知道,系统既然可以默认,程序就能修改,也就是从定向。就是说通过设置,让System.out把信息输出到你想要的地方,比如文件,也就成了日志文件。你想想,一般的信息和错误信息能同等对待吗?那必须得分开啊。比如你可以把一般的信息和错误信息打印到不同的文件里面,你就可以很快找到错误信息,而不至于被淹没在大量的一般信息里面。这就是为什么你的系统日志分一般日志和错误日志。
还有一个区别是,out是缓存输出的,err是不缓存的,因为它紧急嘛。刻不容缓。呵呵。

这里注意是字节流,out和err都是java.io.PrintStream,继承了java.io.OutputStream。
而err是InputStream。关于字节流和字符流以后再说,这里不展开。
你用的最多的应该就是System.out这个静态属性了。利用这个标准输出流对象,你可以把字符串打印到控制台。

public static void main(String[] args) {
System.out.println("Hello System.out!");
System.err.println("Hello System.err!");
}

然而,他还有连个姐妹,System.in和System.err。System.err也可以让你把字符串打印到控制台,不同的是它打印出来的是红色的字体。
[quote]
Hello System.out!
[color=red]Hello System.err![/color]
[/quote]

log4j 的实现ConsoleAppender中就是用了System.out和System.err来输出日志到控制台上:


public class ConsoleAppender extends WriterAppender {

private static class SystemOutStream extends OutputStream {
...
public void flush() {
System.out.flush();
}
public void write(final byte[] b) throws IOException {
System.out.write(b);
}
}

private static class SystemErrStream extends OutputStream {
...
public void flush() {
System.err.flush();
}

public void write(final byte[] b) throws IOException {
System.err.write(b);
}

}

...
}



System.in与out相反,当然就是接收你从控制台的输入。当你做一个交互性程序的时候就会用到了。这个类比较有意思,我们多说一点。先看看怎么用它读一个整数:


try {
int input = System.in.read();
System.out.println("read int: " + input);
} catch (IOException e) {
e.printStackTrace();
}


System.in.read()会从标准输入流中读出下一个字节,如果你用System.out打印出来,应该是整数的asc码,而不是你想要的整数。为什么呢?看看他们的类型就知道,out和err是PrintStream,而in则是InputStream,那么初始化的时候具体是什么对象给了in呢?下面是System的片段:

private static void initializeSystemClass() {

FileInputStream fdIn = new FileInputStream(FileDescriptor.in);
FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
FileOutputStream fdErr = new FileOutputStream(FileDescriptor.err);
setIn0(new BufferedInputStream(fdIn));
setOut0(new PrintStream(new BufferedOutputStream(fdOut, 128), true));
setErr0(new PrintStream(new BufferedOutputStream(fdErr, 128), true));

}


我们得停下来想一下。读和写看起来是对称的,但他们的行为方式并不是那么对称,尤其是从API角度看。
PrintStream,本身是一个字节流,这个类专门把字符流按照平台的编码算法转化成字节流输出来。他做了一个编码转换的工作,既然牵涉到了字符流和字节流之间的转换,就少不了用
OutputStreamWriter。最终的打印工作就是由他的方法完成:

private final StreamEncoder se;

public void write(char cbuf[], int off, int len) throws IOException {
se.write(cbuf, off, len);
}


从上面的过程来看,标准输出的意图是明确的。但是他却不知道你要输入什么,他开了个窗户,你要填进来什么他并不知道。他更不知道你要以什么格式获取,你可能想得到字节数组,可能想得到字符串。可能想得到整数。所以需要你自己来包装这个in。

再来了解一下Console,其实控制台是一个字符输入设备。所以在没有任何包装的情况下,你输入的整数会被看做是字符,它自然给吧asc码打印出来。

其实不管是输入还是输出你都得用buffer,以来io设备慢于内存速度很多,而来在你对字符和字节转化的时候他们的个数已不是一对一的。也就是说你可能需要几个字节才能生产出一个字符。同理,一个字符你需要几个字节来保存。

byte[] buff = new byte[4];
System.out.print("input:");
System.in.read(buff);
String s = new String(buff);
System.out.println("output String: " + (s+2));
int i = Integer.valueOf(s);
System.out.println("output int: " + (i+2));


[quote]
input:1234567
output String: 12342
output int: 1236
[/quote]
从上面的输出你可以看出,in是从Console读到了一串字符,填充到buff里面,注意哦,因为buff只有4个字节大小,你知道一个字节就可以表示一个字符,所以只是接收到了前四个字符。利用这个字节数组你就可以构建你想要的数据类型,但是先要构建一个字符串。然后你就可以进而转化成其他类型的数据了。
其实现实中不用这么麻烦,因为jdk已经给我们提供了很多基于字符的Reader类,你可以利用这些类包装成你想要的格式。不过注意,因为in是字节流,那么要包装成字符流自然要用到那个桥,InputStreamReader:

InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);
String line = br.readLine();
System.out.print(line);


既然提到了控制台,就顺便说一下Console。从jdk1.6开始提供了java.io.Console。利用这个类可以方便的从Console读写。System提供了方法可以获取这个对象:

Console console = System.console();
String line = console.readLine();
System.out.print(line);

需要注意的是,Console 是指没有从定向过的控制台,一般是OS的命令行。如果在IDE比如eclipse中运行,会得到空指针异常,那是因为IDE把标准IO从定向到自己的IDE里面去了。Console还提供了console.readPassword()方法,这样当你在Console输入的时候没有回显,这样在输入密码的时候就安全了哦。

[size=medium][color=blue][b]顺着再说一下从定向[/b][/color][/size]
Java System类允许你根须需要把标准IO从定向。他提供了方法:

public static void setIn(InputStream in);
public static void setOut(PrintStream out);
public static void setErr(PrintStream err);

下面的代码就是让你把字符串写到日志文件里面去:

FileOutputStream fos = new FileOutputStream("d:\\system.out.log");
PrintStream out = new PrintStream(fos);
System.setOut(out);
System.out.println("Hello new out");


[size=medium][color=blue][b]System 类提供了load文件和库的方法。[/b][/color][/size]
当你用到native方法,也就是你写了一些本地方法的时候,你可能需要用这个方法load你的dll。


[size=medium][color=blue][b]更新中...[/b][/color][/size]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值