14.2 系统工具
System类实现很多系统工具。在14.1节中已经介绍了一些。这一小节介绍一些其他的系统工具。
14.2.1 命令行I/O对象
System提供若干预定义的I/O对象,它们适用于需要从命令行启动的Java应用程序。这些对象实现大多数操作系统提供的标准I/O流,以及适用于输入密码的控制台对象。关于更多信息,参见10.1.5节。
14.2.2 系统属性
在14.1.1节中,我们分析了应用程序使用Properties对象维护其配置的方式。Java平台本身使用一个Properties对象维护它自己的配置。System类维护一个描述当前工作环境的配置的Properties对象。系统属性包括的信息和当前用户、Java运行时的当前版本以及用于分隔文件路径名部分的字符有关。
表14-1描述一些最重要的系统属性。
表14-1 一些重要的系统属性
|
键 |
含 义 |
|
"file.separator" |
分隔文件路径部分的字符。在UNIX中是“/”;在Windows中是“\” |
|
"java.class.path" |
用于查找包含类文件的目录和JAR文件的路径。类路径的元素由path.separator属性中指定的平台特定的字符分隔 |
|
"java.home" |
Java运行时环境(Java Runtime Environment,JRE)的安装目录 |
|
"java.vendor" |
JRE厂商名称 |
|
"java.vendor.url" |
JRE厂商的URL |
|
"java.version" |
JRE版本号 |
|
"line.separator" |
操作系统用于分隔文本文件中的行的序列 |
|
"os.arch" |
操作系统架构 |
|
"os.name" |
操作系统名称 |
|
"os.version" |
操作系统版本 |
|
"path.separator" |
java.class.path中使用的路径分隔符 |
|
"user.dir" |
用户工作目录 |
|
"user.home" |
用户主目录 |
|
"user.name" |
用户账户名 |
安全考虑 安全管理器限制对系统属性的访问。这在applet中经常是一个问题,它经常禁止读取某些系统属性,并且禁止写入任何系统属性。关于在applet中访问系统属性的更多信息,请参阅14.2.2节的第1小节。
1. 读取系统属性
System类有两个用于读取系统属性的方法:getProperty和getProperties。
System类的getProperty方法有两个不同版本。两个版本都获得实参列表中指定的属性的值。两个getProperty方法中比较简单的那个采用一个属性键作为实参。例如,为了获得path.separator的值,可以使用如下语句:
System.getProperty("path.separator");
getProperty方法返回包含这个属性的值的字符串。如果属性不存在,getProperty的这个版本返回null。
getProperty的另一个版本需要两个String实参:第一个实参是要搜索的键,如果找不到键或者键没有值,那么第二个实参就是返回的默认值。例如,下面的getProperty调用搜索名为subliminal.message的System属性。这不是合法的系统属性,所以这个方法没有返回null,而是返回第二个实参提供的默认值:“Buy StayPuft Marshmallows!”:
System.getProperty("subliminal.message",
"Buy StayPuft Marshmallows!");
System类提供的访问属性值的最后一个方法是getProperties,它返回一个Properties对象。这个对象包含系统属性定义的完整集合。
2. 写系统属性
为了修改现有的系统属性集合,需要使用System.setProperties。这个方法采用Properties对象作为实参,这个对象被初始化为包含要设置的属性。这个方法使用Properties对象代表的新集合替换整个系统属性集合。
警告 改变系统属性有潜在的危险性,并且应该慎重进行。很多系统属性在启动后不重新读取,并且用于提供信息。改变一些属性可能造成不可预计的副作用。
下一个示例PropertiesTest创建一个Properties对象,并且从myProperties.txt初始化它:
subliminal.message=Buy StayPuft Marshmallows!
然后PropertiesTest使用System.setProperties安装新的Properties对象作为当前系统属性的集合:
import java.io.FileInputStream;
import java.util.Properties;
public class PropertiesTest {
public static void main(String[] args) throws Exception {
// set up new properties object
// from file "myProperties.txt"
FileInputStream propFile = new FileInputStream(
"myProperties.txt");
Properties p = new Properties(System.getProperties());
p.load(propFile);
// set the system properties
System.setProperties(p);
// display new properties
System.getProperties().list(System.out);
}
}
注意PropertiesTest是如何创建Properties对象p的,它被用作setProperties的实参:
Properties p = new Properties(System.getProperties());
这个语句使用当前系统属性的集合,初始化新的属性对象p,这个集合在这个小应用程序示例中是运行时系统初始化的属性集合。然后,应用程序把附加的属性从文件myProperties.txt加载到p中,并且设置系统属性为p。结果是把myProperties.txt中列出的属性添加到了启动运行时系统创建的属性集合。注意,应用程序可以不使用任何默认的Properties对象创建p,比如:
Properties p = new Properties();
另外注意,系统属性的值可以被覆盖!例如,如果myProperties.txt包含如下一行,那么系统属性java.vendor就被覆盖了:
java.vendor=Acme Software Company
总的来说,注意不要覆盖系统属性。
setProperties方法改变当前运行中的应用程序的系统属性集合。这些改变不具有持久性。也就是说,即使在一个应用程序中改变系统属性,也不会影响Java解释器将来对这个应用程序或者其他应用程序的调用。运行时系统每次启动时重新初始化系统属性。如果对系统属性的改动需要持久化,那么应用程序就必须在退出之前把值写入某个文件,并且在启动时再次读取它们。
14.2.3 安全管理器
安全管理器(security manager)是定义应用程序安全策略的对象。这一策略指定不安全或者敏感的操作。安全策略不允许的任何操作都会抛出SecurityException异常。应用程序也可以查询其安全管理器,从而了解允许哪些操作。
典型情况下,Web applet在运行时使用浏览器或者Java Plug-in提供的安全管理器。其他类型的应用程序一般运行不带有安全管理器,除非应用程序本身定义了安全管理器。如果不带安全管理器,那么应用程序就没有安全策略,并且其操作不受限制。
这一节讲解应用程序如何和现有的安全管理器进行交互。更详细的信息,包括如何设计安全管理器的信息,请参阅安全指南(Security Guide)。
1. 和安全管理器进行交互
安全管理器是SecurityManager类型的对象,为了获得对这个对象的引用,需要调用System.getSecurityManager:
SecurityManager appsm = System.getSecurityManager();
如果没有安全管理器,这个方法则返回null。
应用程序具有了对安全管理器对象的引用之后,就可以请求进行特定操作的许可。标准库中的很多类都是这样。例如,System.exit使用退出状态终止Java虚拟机,它调用SecurityManager.checkExit确保当前线程具有关闭应用程序的许可。
SecurityManager定义了很多其他的用于验证其他类型操作的方法。例如,Security- Manager.checkAccess验证线程访问,SecurityManager.checkProperty Access验证对指定的属性的访问。每个操作或者操作组都有其自己的checkXXX()方法。
另外,checkXXX()方法的集合表示已经属于安全管理器保护范围的操作集合。典型情况下,应用程序不必直接调用任何checkXXX()方法。
2. 识别安全违规
很多平常不带有安全管理器的操作,当带有安全管理器运行时,可以抛出Security- Exception异常。即使调用没有指定为抛出SecurityException异常的方法,也是如此。例如,分析下面用于读取文件的代码:
reader = new FileReader("xanadu.txt");
没有安全管理器的情况下,这个语句的执行不出现错误,前提是xanadu.txt文件存在并且可读。但是,假设这个语句被插入到一个Web applet中,典型情况下Web applet在不允许文件输入的安全管理器之下运行。可能会出现下面的错误消息:
appletviewer fileApplet.html
Exception in thread "AWT-EventQueue-1"
java.security.AccessControlException: access denied
(java.io.FilePermission characteroutput.txt write)
...
注意这种情况下抛出的特定异常,java.security.AccessControlException是SecurityException的子类。
14.2.4 系统中的杂项方法
这一小节介绍前面几节没有提到的System中的一些方法。
arraycopy方法高效地在数组之间复制数据。关于更多信息,请参阅3.1.3节。
currentTimeMillis和nanoTime方法非常适于测量应用程序执行过程中的时间间隔。为了测量以毫秒为单位的时间间隔,可以在间隔的开始和结束时两次调用currentTimeMillis,然后从返回的第二个值中减去第一个值。类似的,可以两次调用nanoTime测量以纳秒为单位的时间间隔。
注意 currentTimeMillis和nanoTime的精度受到操作系统提供的时间服务的限制。不要假设currentTimeMillis精确到最接近的毫秒,或者nanoTime精确到最接近的纳秒。另外,不应该使用currentTimeMillis和nanoTime确定当前时间。可以使用高级方法,比如java.util.Calendar.getInstance。
exit方法使Java虚拟机关闭,带有实参指定的整数退出状态。退出状态可以被启动应用程序的进程使用。按照约定,退出状态0表示应用程序正常终止,而任何其他值都是错误码。






