本章要点

  • 使用File类访问本地文件系统
  • 使用文件过滤器
  • 理解IO流的模型和处理方式
  • 使用IO流执行输入、输出操作
  • 使用转换流将字节流转换为字符流
  • 推回流的功能和用法
  • 重定向标准输入、输出
  • 访问其他进程的输入、输出2
  • RandomAccessFile的功能和用法
  • 对象序列化机制和作用
  • 通过实现Serializable接口实现序列化
  • 实现定制的序列化
  • 通过实现Externalizable接口实现序列化
  • JavaIO的概念和作用
  • 使用BufferChannel完成输入、输出
  • Charset的功能和用法
  • FireLock的功能和用法
  • NIO.2的文件IO和文件系统
  • 通过NIO.2监控文件变化
  • 通过NIO.2访问、修改文件属性

输入输出有什么用

使用输入机制

  • 程序可以读取外部数据(包括来自磁盘、光盘等存储设备的数据)、
  • 程序可以读取用户输入的数据;

使用输出机制

  • 程序可以记录运行状态,可以将数据输出到磁盘、光盘等存储设备中。

字节流 字符流

JavaIO通过java.io包下的类和接口来支持,在java.io包下主要包括输入输出两种IO流。
每种输入、输出流又可分为字节流字符流两大类。其中

  • 字节流以字节为单位来处理输入、输出操作,
  • 而字符流则以字符来处理输入、输出操作。

节点流 处理流

除此之外,JavaIO流使用了一种装饰器设计模式,它将IO流分成底层节点流和上层处理流,其中

  • 节点流用于和底层的物理存储节点直接关联:不同的物理节点获取节点流的方式可能存在一定的差异,
  • 程序可以把不同的物理节点流包装成统一的处理流,从而允许程序使用统一的输入、输出代码来读取不同的物理存储节点的资源.

Java7的NIO 2

Java 7java.nio及其子包下提供了一系列全新的API,这些API是对原有新IO的升级,因此也被称为NIO 2,通过这些NIO 2,程序可以更高效地进行输入、输出操作。本章也会介绍Java 7所提供的NIO 2

对象序列化

除此之外,本章还会介绍java对象的序列化机制,使用序列化机制可以把内存中的java对象转换成二进制字节流,这样就可以把java对象存储到磁盘里,或者在网络上传输java对象。这也是java提供分布式编程的重要基础。

7.9 本章小结

  • 本章介绍了运行Java程序时的参数,并详细解释了main方法签名的含义。
  • 为了实现字符界面程序与用户交互功能,本章介绍了两种读取键盘输入的方法。
  • 本章还介绍了SystemRuntimeStringStringBufferStringBuilderMathBigDecimal,RandomDateCalendarTimeZone等常用类的用法。
  • 本章重点介绍了JDK1.4所新增的正则表达式支持,包括如何创建正则表达式,以及使用PatternMatcherString等类来使用正则表达式。
  • 本章还详细介绍了程序国际化的相关知识,包括消息、日期、时间国际化以及格式化等内容。
  • 除此之外,本章详细介绍了Java8新增的日期、时间包,以及Java8新增的日期、时间格式器。

7.8.2 使用DateTimeFormatter解析字符串

为了使用DateTimeFormatter将指定格式的字符串解析成日期、时间对象(LocalDateLocalDate,TimeLocalTime等实例),可通过日期、时间对象提供的parse(CharSequence text, DateTimeFormatter formatter)方法进行解析。

程序示例

如下程序示范了使用DateTimeFormatter解析日期、时间字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.time.*;
import java.time.format.*;

public class NewFormatterParse {
public static void main(String[] args) {
// 定义一个任意格式的日期时间字符串
String str1 = "2014==04==12 01时06分09秒";
// 根据需要解析的日期、时间字符串定义解析所用的格式器
DateTimeFormatter fomatter1 = DateTimeFormatter.ofPattern("yyyy==MM==dd HH时mm分ss秒");
// LocalDateTime类的parse解析得到日期时间对象
LocalDateTime dt1 = LocalDateTime.parse(str1, fomatter1);
System.out.println(dt1); // 输出 2014-04-12T01:06:09

// ---下面代码再次解析另一个字符串---
String str2 = "2014$$$4月$$$13 20小时";
DateTimeFormatter fomatter2 = DateTimeFormatter.ofPattern("yyy$$$MMM$$$dd HH小时");
LocalDateTime dt2 = LocalDateTime.parse(str2, fomatter2);
System.out.println(dt2); // 输出 2014-04-13T20:00

}
}
1
2
2014-04-12T01:06:09
2014-04-13T20:00

上面程序中定义了两个不同格式的日期、时间字符串,为了解析它们,程序分别使用对应的格式字符串创建了DateTimeFormatter对象,这样DateTimeFormatter即可按该格式字符串将日期、时间字符串解析成LocalDateTime对象。
编译、运行该程序,即可看到两个日期、时间字符串都被成功地解析成LocalDateTime

7.8 Java8新增的日期 时间格式器

Java8新增的日期、时间API里不仅包括了InstantLocalDateLocalDateTimeLocalTime等代表日期、时间的类,而且在Java.time.format包下提供了一个DateTimeFormatter格式器类,该类相当于前面介绍的DateFormatSimpleDateFormat的合体,功能非常强大。
DateFormatSimpleDateFormat类似, DateTimeFormatter不仅可以将日期、时间对象格式化成字符串,也可以将特定格式的字符串解析成日期、时间对象。

获取DateTimeFormatter对象的方式

为了使用DateTimeFormatter进行格式化或解析,必须先获取DateTimeFormatter对象,获取DateTimeFormatter对象有如下三种常见的方式。

  1. 直接使用静态常量创建DateTimeFormatter格式器。 Date Time Formatter类中包含了大量形如ISO_LOCAL_DATEISO_LOCAL_TIMEISO_LOCAL_DATE_TIME等静态常量,这些静态常量本身就是DateTimeFormatter实例。
  2. 使用代表不同风格的枚举值来创建DateTimeFormatter格式器。在FormatStyle枚举类中定义了FULLLONGMEDIUMSHORT四个枚举值,它们代表日期、时间的不同风格.
  3. 根据模式字符串来创建DateTimeFormatter格式器。类似于SimpleDateFormat,可以采用模式字符串来创建DateTimeFormatter,如果需要了解DateTimeFormatter支持哪些模式字符串,则需要参考该类的API文档。

7.8.1 使用DateTimeFormatter完成格式化

使用DateTimeFormatter将日期、时间(LocalDateLocalDateTimeLocalTime等实例)格式化为字符串,可通过如下两种方式。

  1. 调用DateTimeFormatterformate(TemporalAccessor temporal)方法执行格式化,其中LocalDate,LocalDateTimeLocalTime等类都是TemporalAccessor接口的实现类。
  2. 调用LocalDateLocalDate TimeLocalTime等日期、时间对象的format(DateTimeFormatter formatter)方法执行格式化。

程序示例

上面两种方式的功能相同,用法也基本相似,如下程序示范了使用DateTimeFormatter来格式化日期、时间:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.time.*;
import java.time.format.*;

public class NewFormatterTest {
public static void main(String[] args) {
// 创建DateTimeFormatter对象数组
DateTimeFormatter[] formatters = new DateTimeFormatter[] {
// 直接使用常量创建DateTimeFormatter格式器
DateTimeFormatter.ISO_LOCAL_DATE, DateTimeFormatter.ISO_LOCAL_TIME,
DateTimeFormatter.ISO_LOCAL_DATE_TIME,
// 使用本地化的不同风格来创建DateTimeFormatter格式器
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.MEDIUM),
DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG),
// 根据模式字符串来创建DateTimeFormatter格式器
DateTimeFormatter.ofPattern("Gyyyy%%MMM%%dd HH:mm:ss") };

LocalDateTime date = LocalDateTime.now();
// 依次使用不同的格式器对LocalDateTime进行格式化
for (int i = 0; i < formatters.length; i++) {
// 下面两行代码的作用相同
System.out.println(date.format(formatters[i]));
System.out.println(formatters[i].format(date));
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
2019-10-06
2019-10-06
18:57:24.3196636
18:57:24.3196636
2019-10-06T18:57:24.3196636
2019-10-06T18:57:24.3196636
2019年10月6日星期日 下午6:57:24
2019年10月6日星期日 下午6:57:24
2019年10月6日
2019年10月6日
公元2019%%10月%%06 18:57:24
公元2019%%10月%%06 18:57:24

DateTimeFormatter功能更强大

使用DateTimeFormatter进行格式化时不仅可按系统预置的格式对日期、时间进行格式化,也可使用模式字符串对日期、时间进行自定义格式化,由此可见, DateTimeFormatter的功能完全覆盖了传统的DateFormatSimpleDateFormate的功能。

DateTimeFormatter怎么转DateFormat

有些时候,读者可能还需要使用传统的DateFormat来执行格式化, DateTimeFormatter则提供了一个toFormat()方法,该方法可以获取DateTimeFormatter对应的Format对象。

7.7.9 使用SimpleDateFormat格式化日期

前面介绍的DateFormatparse()方法可以把字符串解析成Date对象,但实际上DateFormatparse方法不够灵活——它要求被解析的字符串必须满足特定的格式!
为了更好地格式化日期、解析日期字符串,Java提供了SimpleDateFormat类。
SimpleDateFormatDateFormat的子类,SimpleDateFormatDateFormat更简单,功能更强大。
SimpleDateFormat可以非常灵活地格式化Date,也可以用于解析各种格式的日期字符串。创建SimpleDateFormat对象时需要传入一个pattern字符串,这个pattern是一个日期模板字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.text.*;
import java.util.*;

public class SimpleDateFormatTest {
public static void main(String[] args) throws ParseException {
Date d = new Date();
// 创建一个SimpleDateFormat对象
SimpleDateFormat sdf1 = new SimpleDateFormat("Gyyyy年中第D天");
// 将d格式化成日期,输出:公元2017年中第282天
String dateStr = sdf1.format(d);
System.out.println(dateStr);
// 一个非常特殊的日期字符串
String str = "14###3月##21";
SimpleDateFormat sdf2 = new SimpleDateFormat("y###MMM##d");
// 将日期字符串解析成日期,输出:Fri Mar 21 00:00:00 CST 2014
System.out.println(sdf2.parse(str));
}
}
1
2
公元2019年中第279天
Fri Mar 21 00:00:00 CST 2014

使用SimpleDateFormat可以将日期格式化成形如”公元2014年中第101天”这样的字符串,也可以把形如”14#三月##21”这样的字符串解析成日期,功能非常强大。
SimpleDateFormat把日期格式化成怎样的字符串,以及能把怎样的字符串解析成Date,完全取决于创建该对象时指定的pattern参数, pattern是一个使用日期字段占位符日期模板
如果读者想知道SimpleDateFormat支持哪些日期、时间占位符,可以查阅APl文档中SimpleDateFormat类的说明.

7.7.8 使用DateFormat格式化日期 时间

NumberFormat相似的是, DateFormat也是一个抽象类,它也提供了如下几个类方法用于获取DateFormat对象。

方法 描述
getDateInstance() 返回一个日期格式器,它格式化后的字符串只有日期,没有时间。该方法可以传入多个参数,用于指定日期样式和Locale等参数;如果不指定这些参数,则使用默认参数。
getTimeInstance() 返回一个时间格式器,它格式化后的字符串只有时间,没有日期。该方法可以传入多个参数,用于指定时间样式和Locale等参数;如果不指定这些参数,则使用默认参数。
getDateTimeInstance() 返回一个日期、时间格式器,它格式化后的字符串既有日期,也有时间。该方法可以传入多个参数,用于指定日期样式、时间样式和Locale等参数;如果不指定这些参数,则使用默认参数。

上面三个方法可以指定日期样式、时间样式参数,它们是DateFormat的4个静态常量:FULLLONGMEDIUMSHORT,通过这4个样式参数可以控制生成的格式化字符串。

示例

看如下例子程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import java.util.*;
import java.text.*;
import static java.text.DateFormat.*;

public class DateFormatTest
{
public static void main(String[] args)
throws ParseException
{
// 需要被格式化的时间
Date dt = new Date();
// 创建两个Locale,分别代表中国、美国
Locale[] locales = {Locale.CHINA, Locale.US};
DateFormat[] df = new DateFormat[16];
// 为上面两个Locale创建16个DateFormat对象
for (int i = 0 ; i < locales.length ; i++)
{
df[i * 8] = DateFormat.getDateInstance(SHORT, locales[i]);
df[i * 8 + 1] = DateFormat.getDateInstance(MEDIUM, locales[i]);
df[i * 8 + 2] = DateFormat.getDateInstance(LONG, locales[i]);
df[i * 8 + 3] = DateFormat.getDateInstance(FULL, locales[i]);
df[i * 8 + 4] = DateFormat.getTimeInstance(SHORT, locales[i]);
df[i * 8 + 5] = DateFormat.getTimeInstance(MEDIUM , locales[i]);
df[i * 8 + 6] = DateFormat.getTimeInstance(LONG , locales[i]);
df[i * 8 + 7] = DateFormat.getTimeInstance(FULL , locales[i]);
}
for (int i = 0 ; i < locales.length ; i++)
{
String tip = i == 0 ? "----中国日期格式----":"----美国日期格式----";
System.out.println(tip);
System.out.println("SHORT格式的日期格式:"
+ df[i * 8].format(dt));
System.out.println("MEDIUM格式的日期格式:"
+ df[i * 8 + 1].format(dt));
System.out.println("LONG格式的日期格式:"
+ df[i * 8 + 2].format(dt));
System.out.println("FULL格式的日期格式:"
+ df[i * 8 + 3].format(dt));
System.out.println("SHORT格式的时间格式:"
+ df[i * 8 + 4].format(dt));
System.out.println("MEDIUM格式的时间格式:"
+ df[i * 8 + 5].format(dt));
System.out.println("LONG格式的时间格式:"
+ df[i * 8 + 6].format(dt));
System.out.println("FULL格式的时间格式:"
+ df[i * 8 + 7].format(dt));
}

// String str1 = "2017/10/07";
// String str2 = "2017年10月07日";
// // 下面输出 Sat Oct 07 00:00:00 CST 2017
// System.out.println(DateFormat.getDateInstance().parse(str2));
// // 下面输出 Sat Oct 07 00:00:00 CST 2017
// System.out.println(DateFormat.getDateInstance(SHORT).parse(str1));
// // 下面抛出 ParseException异常
// System.out.println(DateFormat.getDateInstance().parse(str1));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
----中国日期格式----
SHORT格式的日期格式:2019/10/6
MEDIUM格式的日期格式:2019年10月6日
LONG格式的日期格式:2019年10月6日
FULL格式的日期格式:2019年10月6日星期日
SHORT格式的时间格式:下午4:12
MEDIUM格式的时间格式:下午4:12:39
LONG格式的时间格式:CST 下午4:12:39
FULL格式的时间格式:中国标准时间 下午4:12:39
----美国日期格式----
SHORT格式的日期格式:10/6/19
MEDIUM格式的日期格式:Oct 6, 2019
LONG格式的日期格式:October 6, 2019
FULL格式的日期格式:Sunday, October 6, 2019
SHORT格式的时间格式:4:12 PM
MEDIUM格式的时间格式:4:12:39 PM
LONG格式的时间格式:4:12:39 PM CST
FULL格式的时间格式:4:12:39 PM China Standard Time

DateFormat具有国际化能力

正如Number Format提供了国际化的能力一样, DateFormat也具有国际化的能力,同一个日期使用不同的Locale格式器格式化的效果完全不同,格式化后的字符串正好符合Locale对应的本地习惯。

setLenient方法

获得了DateFormat之后,还可以调用它的setLenient(boolean lenient)方法来设置该格式器是否采用严格语法。举例来说,
如果采用不严格的日期语法(该方法的参数为true),对于字符串"2004-2-31"将会转换成2004年3月2日:
如果采用严格的日期语法,解析该字符串时将抛出异常。

字符串转Date对象

DateFormatparse()方法可以把一个字符串解析成Date对象,但它要求被解析的字符串必须符合日期字符串的要求,否则可能抛出ParseException异常。

例如,如下代码片段:

1
2
3
4
5
6
7
8
String str1 = "2017/10/07";
String str2 = "2017年10月07日";
// 下面输出 Sat Oct 07 00:00:00 CST 2017
System.out.println(DateFormat.getDateInstance().parse(str2));
// 下面输出 Sat Oct 07 00:00:00 CST 2017
System.out.println(DateFormat.getDateInstance(SHORT).parse(str1));
// 下面抛出 ParseException异常
System.out.println(DateFormat.getDateInstance().parse(str1));

上面代码中最后一行代码解析日期字符串时引发ParseException异常,因为"2017/10/7"是一个SHORT样式的日期字符串,必须用SHORT样式的DateFormat实例解析,否则将抛出异常。

7.7.7 使用NumberFormat格式化数字

MessageFormat是抽象类Format的子类, Format抽象类还有两个子类:NumberFormatDateFormat,它们分别用以实现数值、日期的格式化。 NumberFormatDateFormat可以将数值、日期转换成字符串,也可以将字符串转换成数值、日期。

图7.9显示了NumberFormatDateFormat的主要功能。
这里有一张图片

NumberFormatDateFormat都包含了format()parse()方法,其中

  • format()用于将数值、日期格式化成字符串,
  • parse()用于将字符串解析成数值、日期

如何获取NumberFormat对象

NumberFormat也是一个抽象基类,所以无法通过它的构造器来创建NumberFormat对象,它提供了如下几个类方法来得到NumberFormat对象。

方法 描述
getCurrencyInstance() 返回默认Locale货币格式器。如果要获取指定Locale的货币格式器,则在调用该方法时传入指定的Locale
getIntegerInstance() 返回默认Locale整数格式器。如果要获取指定Locale的整数格式器,则在调用该方法时传入指定的Locale
getNumberInstance() 返回默认Locale的通用数值格式器。也可以在调用该方法时传入指定的Locale,从而则获取指定Locale的通用数值格式器。
getPercentInstance() 返回默认Locale的百分数格式器。也可以在调用该方法时传入指定的Locale,获取指定Locale的百分数格式器。

一旦取得了NumberFormat对象后,就可以调用它的format()方法来格式化数值,包括整数和浮点数。

程序示例

如下例子程序示范了NumberFormat的三种数字格式化器的用法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import java.util.*;
import java.text.*;

public class NumberFormatTest {
public static void main(String[] args) {
// 需要被格式化的数字
double db = 1234000.567;
// 创建四个Locale,分别代表中国、日本、德国、美国
Locale[] locales = { Locale.CHINA, Locale.JAPAN, Locale.GERMAN, Locale.US };
NumberFormat[] nf = new NumberFormat[12];
// 为上面四个Locale创建12个NumberFormat对象
// 每个Locale分别有通用数值格式器、百分比格式器、货币格式器
for (int i = 0; i < locales.length; i++) {
nf[i * 3] = NumberFormat.getNumberInstance(locales[i]);
nf[i * 3 + 1] = NumberFormat.getPercentInstance(locales[i]);
nf[i * 3 + 2] = NumberFormat.getCurrencyInstance(locales[i]);
}
for (int i = 0; i < locales.length; i++) {
String tip = i == 0 ? "----中国的格式----"
: i == 1 ? "----日本的格式----" : i == 2 ? "----德国的格式----" : "----美国的格式----";
System.out.println(tip);
System.out.println("通用数值格式:" + nf[i * 3].format(db));
System.out.println("百分比数值格式:" + nf[i * 3 + 1].format(db));
System.out.println("货币数值格式:" + nf[i * 3 + 2].format(db));
}
}
}

运行结果:

1
2
3
4
5
6
7
8
9
10
11
----中国的格式----
通用数值格式:1,234,000.567
百分比数值格式:123,400,057%
货币数值格式:¥1,234,001
----德国的格式----
通用数值格式:1.234.000,567
百分比数值格式:123.400.057 %
货币数值格式:1.234.000,57 ¤
----美国的格式----
百分比数值格式:123,400,057%
货币数值格式:$1,234,000.57

NumberFormat也有国际化的作用

德国的小数点比较特殊,它们采用逗号(,)作为小数点;
中国、日本使用作为货币符号,而美国则采用$作为货币符号。
同样的数值在不同国家的写法是不同的,而NumberFormat的作用就是把数值转换成不同国家的本地写法,所以NumberFormat其实也有国际化的作用。

7.7.6 Java9新增的日志API

Java9强化了原有的日志API,这套日志API只是定义了记录消息的最小API,开发者可将这些日志消息路由到各种主流的日志框架(如SLF4JLog4J等),否则默认使用Java传统的java.util.logging日志API
这套日志API的用法非常简单,只要两步即可:

  • 调用System类的getLogger(String name)方法获取System.Logger对象。
  • 调用System.Logger对象的log()方法输出日志。该方法的第一个参数用于指定日志级别。

为了与传统java.util.logging日志级别、主流日志框架的级别兼容。Java9定义了如下表所示的日志级别。

Java9日志级别 对应的传统日志级别 说明
ALL ALL 最低级别,系统将会输出所有日志信息。因此将会生成非常多、非常冗余的日志信息。
TRACE FINER 输出系统的各种跟踪信息,也会生成很多、很冗余的日志信息
DEBUG FINE 输出系统的各种调试信息,会生成较多的日志信息
INFO INFO 输出系统内需要提示用户的提示信息,生成中等冗余的日志信息
WARNING WARNING 只输出系统内警告用户的警告信息,生成较少的日志信息
ERROR SEVERE 只输出系统发生错误的错误信息,生成很少的日志信息
OFF OFF 关闭日志输出

使用日志的好处

该日志级别是一个非常有用的东西:在开发阶段调试程序时,可能需要大量输出调试信息;在发布软件时,又希望关掉这些调试信息。此时就可通过日志来实现,只要将系统日志级别调高,所有低于该级别的日志信息就都会被自动关闭,如果将日志级别设为OFF,那么所有日志信息都会被关闭。

示例 java9新增的日志API

例如,如下程序示范了Java9新增的日志API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.logging.*;

public class LoggerTest {
public static void main(String[] args) throws Exception {
// 获取System.Logger对象
System.Logger logger = System.getLogger("fkjava");
// 设置系统日志级别(FINE对应DEBUG)
// Logger.getLogger("fkjava").setLevel(Level.FINE);
// Logger.getLogger("fkjava").setLevel(Level.INFO);
Logger.getLogger("fkjava").setLevel(Level.SEVERE);
// 设置使用a.xml保存日志记录
Logger.getLogger("fkjava").addHandler(new FileHandler("a.xml"));
logger.log(System.Logger.Level.DEBUG, "debug信息");
logger.log(System.Logger.Level.INFO, "info信息");
logger.log(System.Logger.Level.ERROR, "error信息");
}
}

Java9日志API国际化

除简单使用之外,Java9的日志API也支持国际化:
System类除使用简单的getLogger( String name)方法获取System.Logger对象之外,还可使用getLogger(String name, ResourceBundle bundle)方法来获取该对象,该方法需要传入一个国际化语言资源包,这样该Logger对象即可根据key来输出国际化的日志信息。

先为美式英语环境提供一个logMess_en_US.properties文件,该文件的内容如下:

1
2
3
debug=Debug Message
info=Plain Message
error=Error Message

再为简体中文环境提供一个logMess_zh_CN.properties文件,该文件的内容如下:

1
2
3
debug=调试信息
info=普通信息
error=错误信息

接下来程序可使用ResourceBundle先加载该国际化语言资源包,然后就可通过Java9的日志API来输出国际化的日志信息了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.util.logging.*;
import java.util.*;

public class LoggerI18N {
public static void main(String[] args) throws Exception {
// 加载国际化资源包
ResourceBundle rb = ResourceBundle.getBundle("logMess", Locale.getDefault(Locale.Category.FORMAT));
// 获取System.Logger对象
System.Logger logger = System.getLogger("fkjava", rb);
// 设置系统日志级别(FINE对应DEBUG)
Logger.getLogger("fkjava").setLevel(Level.INFO);
// 设置使用a.xml保存日志记录
Logger.getLogger("fkjava").addHandler(new FileHandler("a.xml"));
// 下面3个方法的第二个参数是国际化消息的key
logger.log(System.Logger.Level.DEBUG, "debug");
logger.log(System.Logger.Level.INFO, "info");
logger.log(System.Logger.Level.ERROR, "error");
}
}

该程序与前一个程序的区别就是粗体字代码,这行粗体字代码获取System.Logger时加载了ResourceBundle资源包。接下来调用System.Loggerlog()方法输出日志信息时,第二个参数应该使用国际化消息key,这样即可输出国际化的日志信息。
在简体中文环境下运行该程序,将会看到a.xml文件中的日志信息是中文信息;在美式英文环境下运行该程序,将会看到a.xml文件中的日志信息是英文信息

7.7.5 使用类文件代替资源文件

除使用属性文件作为资源文件外,Java也允许使用类文件代替资源文件,即将所有的key-value对存入class文件,而不是属性文件。
使用类文件来代替资源文件必须满足如下条件。
该类的类名必须是baseName_language_country,这与属性文件的命名相似。
该类必须继承ListResourceBundle,并重写getContents()方法,该方法返回Object数组,该数组的每一项都是key-value对.

1
2
3
4
5
6
7
8
9
10
11
12
import java.util.*;

public class myMess_zh_CN extends ListResourceBundle {
// 定义资源
private final Object myData[][] = { { "msg", "{0},你好!今天的日期是{1}" } };

// 重写方法getContents()
public Object[][] getContents() {
// 该方法返回资源的key-value对
return myData;
}
}

如果系统同时存在资源文件、类文件,系统将以类文件为主,而不会调用资源文件。对于简体中文的Locale, ResourceBundle搜索资源文件的顺序是:

  1. baseName_zh_CN.class
  2. baseName_zh_CN.prroperties
  3. baseName_zh.class
  4. baseName_zh.properties
  5. baseName.class
  6. baseName.properties

系统按上面的顺序搜索资源文件,如果前面的文件不存在,才会使用下一个文件。如果一直找不到对应的文件,系统将抛出异常

7.7.4 使用MessageFormat处理包含占位符的字符串

MessageFormat

方法 描述
format(String pattern, Object.values) 返回后面的多个参数值填充前面的pattern字符串,其中pattern字符串不是正则表达式,而是一个带占位符的字符串。