S-JIS[2007-02-11/2009-12-31] 変更履歴

リソース・プロパティー・環境変数

Javaでは、リソース(プロパティーファイル)を扱う方法が用意されている。

リソースとは、実行時に使用する(classファイル以外の)データのこと。プロパティーファイルや画像ファイルなど。

プロパティーファイルとは、「キー=値」という形式で定数値を保持しているファイルのこと。 (Windowsのiniファイルのようなもの)


リソースを読み込む方法

リソースは、classファイルと同じ場所に置いてあるのが前提。
Eclipse3の場合、ソースディレクトリの下に“拡張子java以外のファイル”を書き込むと、自動的にclassの出力先のディレクトリにコピーしてくれる。

通常のクラスディレクトリーに置いてあるリソースファイルも、jarファイル化した際のjarファイル内に置かれたリソースファイルも、読み込むことが出来る。

プログラムからリソースを読み込む場合、リソースファイルの場所は、絶対位置で指定するか、クラスファイルからの相対位置で指定する。
相対位置指定では、自分より下のディレクトリ(サブディレクトリ)にあるファイルを使用できる。
..」を使うことによって相対位置で自分より上にあるディレクトリも指定できるが、通常のファイルの場合でしか読み込めない。すなわち、jarファイル内のファイルは読み込めない。

リソースの指定例 説明
test.jpg 自分のクラスと同じディレクトリに在るリソース
res/test.jpg 自分のクラスと同じ場所のresディレクトリの中に在るリソース
../test.jpg 自分の1つ上のディレクトリに在るリソース(jarファイルでは使えない)
/jp/hishidama/sample/res/test.jpg 絶対位置指定
	public void printTextResource() throws Exception {
		Class c = this.getClass();			←このクラスの位置からの相対パスを指定
		URL url = c.getResource("resource.txt");
		InputStream is = url.openStream();
		BufferedReader br = new BufferedReader(new InputStreamReader(is));
		while (br.ready()) {
			System.out.println(br.readLine());
		}
		br.close();
		is.close();
	}

リソースファイルをInputStreamとして取得できるので、後はリソースファイルの種類に応じて必要なメソッドを呼び出せばよい。
※本来はclose()はfinallyの中で行うべきだが、この例では端折っている。

InputStreamの取得の部分は、以下のように書くことも出来る。

		InputStream is = c.getResourceAsStream("resource.txt");

プロパティーファイルを読み込む方法

プロパティーは、PropertiesクラスResourceBundleクラスで扱う。[/2007-12-07]
いずれも、キーを指定して値を取得したりセットしたりする。
違いは、リソースバンドルはユーザー向けに表示する為の文言(言葉)を扱うプロパティーだということ。つまり、対象となる環境(使用国・言語)に応じていくつかプロパティーファイルを用意しておけば、実行時には環境に合ったプロパティーファイルが読み込まれる。

プロパティークラスを使う例:

	public void printTextProperty() throws Exception {
		Class c = this.getClass();
		InputStream is = c.getResourceAsStream("property.txt");

		Properties p = new Properties();
		p.load(is);
		is.close();

		for (Iterator i = p.keySet().iterator(); i.hasNext();) {
			String key = (String) i.next();
			String val = p.getProperty(key);
			System.out.println(key + "=" + val);
		}
	}
プロパティーファイルは、1つのプロパティーにつき1行で、「キー=値」という形式で書いていく。[2007-12-13]
行末に「\」を付けると、値を次の行に続けて書ける。(読み込むとつながって1行になる)

リソースバンドルクラスを使う例:

リソースバンドルの場合、ファイル名の指定の仕方は少し特殊になる。
絶対パスのみ指定可能で、一番先頭の「/」は取り除き、途中の「/」は「.」に置き換え、拡張子は省略する(拡張子propertiesのファイルのみ読み込み可能)。
要するに、クラスをパッケージ名つきで表現した(FQCN)のと同じ形式。

/jp/hishidama/sample/res/test.properties

 jp.hishidama.sample.res.test

	public void printResourceBundle() {
		ResourceBundle rb = ResourceBundle.getBundle("jp.hishidama.sample.res.test");

		for (Enumeration e = rb.getKeys(); e.hasMoreElements();) {
			String key = (String) e.nextElement();
			String val = rb.getString(key);
			System.out.println(key + "=" + val);
		}
	}

[2007-12-07]
リソースバンドルでは、まず、実行環境の地域情報(ロカール)を取得する。getBundle()でロカールを指定しない場合、デフォルトロカール(Locale#getDefault()) が使われる。
デフォルトロカールは、システムプロパティーのuser.language(日本ならja)、user.country(日本ならJP)、user.variant(日本なら空文字列)を使って生成される。
日本の場合は「ja_JP」になる。

実行環境が日本の場合、まず、ファイル名に「_ja_JP」が付いたプロパティーファイルがあればそれを読み込む。
無い場合、ファイル名に「_ja」が付いたプロパティーファイルがあればそれを読み込む。
(ja_JPやjaは、大文字でも小文字でも可能)
それも無ければ、ファイル名そのもののプロパティーファイルを読み込む。

各ファイル内に同一のキーがある場合、先に読み込んだファイルに書かれている内容が優先される。
したがって、例えばja_JP付きのファイルに日本語を書いておき、何も付いていないファイルに英語を書いておくとかという使い方をする。
で、さらにfr_FRとかko_KRとか、別の言語のファイルを用意しておくわけだ。
ただしリソースバンドルは英語以外のエンコードに対応していないので、プロパティーファイルはnative2ascii等で変換しておく必要がある。

取得される値の例
プロパティーファイル 値の取得 取得できる値
test_ja_jp
.properties
test_ja
.properties
test
.properties
rb = ResourceBundle.
     getBundle("test");
key1=abc key1=xxx key1=123 rb.getString("key1") abc
  key2=yyy key2=456 rb.getString("key2") yyy
    key3=789 rb.getString("key3") 789

参考: ITproのResourceBundleの新機能 … リソースバンドルではファイルの代わりにクラスが指定できるとか、もっと高機能らしい
クラスが指定できるもんだから、プロパティーファイル名の指定方法がFQCNと同じだったんだね〜。


プロパティーの操作方法

取得したプロパティー(リソースバンドル)は、以下のようなメソッドで操作できる。

操作 Properties p ResourceBundle rb
値の取得 String val = p.getProperty(key); String val = rb.getString(key);
値の設定 p.setProperty(key,val);  
キーの一覧を取得 Set set = p.keySet(); Enumeration e = rb.getKeys();
キーと値の一覧を表示 p.list(System.out);  
キーと値をファイル出力 os = new FileOutputStream("ファイル名");
p.store(os,"comment");
 

プロパティーの文字化けの解決方法

キーや値に日本語を使用したプロパティーファイルを読み込むと、文字化けする。
上記の読み込み方法では、ASCII文字にしか対応していない。
(Eclipse3.2の場合(デフォルトでは)、拡張子がpropertiesのファイルに日本語を書き込むと、エラーになってファイルを保存できない)

こういうときの為にnative2asciiというツールがある。
これは、日本語をプロパティー(リソースバンドル)クラスで読み込めるような形式に変換してくれる。(日本語の文字を「\uxxxx」という形式の文字列に変換する)
antnative2asciiというタスクがあるので、それを使うのが一般的。つまり、プログラムの実行前にantを実行して プロパティーファイルを変換しておく。

Eclipseの場合、そういう変換を自動的にやってくれるプラグインがあるので、それを使うのもいいかも。


しかし、JDK1.6のPropertiesの場合は もっとエレガントな解決方法がある。

上記の方法ではProperties#load(InputStream)を使用したが、新設されたProperties#load(Reader)を使用すればよい。
InputStreamからReaderを作ってエンコードを指定するだけ。(エンコードはデフォルトのままでもたぶんOK)
日本語もそのまま使えるし、今までの形式の「\uxxxx」も使える。

保存の為のstore()にも、同じくWriterを引数にとるメソッドが追加されている。

もっと早くこうしてくれればよかったのに(嘆)


システムプロパティー

(プロパティーファイルではないが、)JavaVMの実行時に設定されるシステムプロパティーが在る。[2007-06-28]
どのようなシステムプロパティーが存在するかは、SystemクラスのJavadocに載っている。


システムプロパティーの取得方法

システムプロパティーをStringで取得する方法の他、booleanやIntegerで取得する方法が用意されている。[2009-12-31]
(とは言っても、なぜBooleanやIntegerクラスにあるのか…getBoolean()とかparseBoolean()とか、紛らわしい)

取得方法 概要 備考
String String value = System.getProperty("キー"); Properties p = System.getProperties();
String value = p.getProperty("キー");
無い場合はnull
String value = System.getProperty("キー", "デフォルト値"); Properties p = System.getProperties();
String value = p.getProperty("キー", "デフォルト値");
 
Properties p = System.getProperties();
String value = p.getProperty("キー");
Hashtable#get("キー") 無い場合や Stringでない場合はnull
Properties p = System.getProperties();
String value = p.getProperty("キー", "デフォルト値");
 
boolean boolean value = Boolean.getBoolean("キー"); String s = System.getProperty("キー");
boolean value = Boolean.parseBoolean(s);
無い場合や 解釈できない場合はfalse
Integer Integer value = Integer.getInteger("キー"); String s = System.getProperty("キー");
Integer value = Integer.decode(s);
無い場合や 数値でない場合はnull
Integer value = Integer.getInteger("キー", デフォルト値);  
Long Long value = Long.getLong("キー"); String s = System.getProperty("キー");
Long value = Long.decode(s);
無い場合や 数値でない場合はnull
Long value = Long.getLong("キー", デフォルト値);  

全システムプロパティーを出力する例1:

	System.getProperties().list(System.out);

※値が長すぎる場合、その値は途中で切られるので注意

全システムプロパティーを出力する例2:

	Properties p = System.getProperties();
	for (Object key : p.keySet()) {		←キーはオブジェクト型
		//String val = p.getProperty((String) key);
		//Object val = p.get(key);
		String val = System.getProperty((String) key);
		System.out.println(key + "=" + val);
	}

System#getProperty()は、セキュリティーがチェックされる(セキュリティーチェックが有効の場合)。
Properties#getProperty()はチェックされない。


システムプロパティーの設定方法

システムプロパティーは、setProperty()で設定することが出来る。[2009-12-23]

	System.setProperty("キー", "値");

ただし、値にnullを指定することは出来ない。(指定するとNullPointerExceptionが発生する)
nullにしたい場合は、プロパティーを削除することで代用する。

	System.getProperties().remove("キー");	//JDK1.4
	System.clearProperty("キー");		//JDK1.5以降

システムプロパティーは、javaコマンドの実行時に-Dオプションを付けることで自由に指定することが出来る。[2008-07-30]

>java -Dtest=テスト -Dhoge=ほげ jp.hishidama.prop.Sample

jp/hishidama/prop/Sample.java:

package jp.hishidama.prop;

public class Sample {
	public static void main(String[] args) {
		System.out.println(System.getProperty("test"));
		System.out.println(System.getProperty("hoge"));
	}
}

環境変数

(プロパティーでも何でも無いが、)JDK1.5以降では、JavaVM実行時の環境変数を取得する事が出来る。[2008-07-30]
Windowsの環境変数UNIXの環境変数

全環境変数を出力する例:

	System.out.println(System.getenv());
//	Map<String, String> env = System.getenv();

指定した環境変数を取得する例:

	String path = System.getenv("PATH");

System#getenv(String)は、JDK1.4では非推奨メソッドであり、内部も実装されていなかった。
(呼び出すとjava.lang.Error(getenv no longer supported, use properties and -D instead(getenvはサポートしてねーから、-Dを指定してプロパティーを使えや))が発生する)

環境変数PATHに関してはSystem.getProperty("java.library.path")で代用して取得できるが、
その他のものに関しては 自分でjavaコマンドのオプション-Dを使って値を指定してSystem#getProperty()で取得してやらないといけない。

環境変数代替プロパティー
プロパティー 環境変数 備考
Windows UNIX
java.home JAVA_HOME JAVA_HOME  
java.class.path CLASSPATH CLASSPATH  
java.library.path PATH PATH
LD_LIBRARY_PATH
 
user.dir CD PWD  
user.name USERNAME USER  
user.home USERPROFILE
HOMEDRIVE
 HOMEPATH
HOME WindowsXPの場合、「C:\Documents and Settings\ユーザー名」
java.io.tmpdir TMPまたはTEMP
(TMPが優先)
  テンポラリーファイル作成

でも要望が強かった為か、JDK1.5以降ではgetenv()は非推奨メソッドでなくなり、ちゃんと使えるようになった。
(今のところ唯一、非推奨になったけど非推奨でなくなった例らしい)


Java目次へ戻る / 新機能へ戻る / 技術メモへ戻る
メールの送信先:ひしだま