Javaを最速でマスターするための予備知識7項目
- 作者: Jim Waldo,矢野勉,笹井崇司
- 出版社/メーカー: オライリージャパン
- 発売日: 2011/02/24
- メディア: 大型本
- 購入: 3人 クリック: 148回
- この商品を含むブログ (36件) を見る
index
1.Compile
Javac
JavaのCompileにはjavacを利用します。javacコマンドの実行により.javaなどのjavaソースファイルを.classファイルにコンパイルします。javacの使用についてはhelpが参考になるので内容を見てみます。
$ java -version java version "1.7.0_01" Java(TM) SE Runtime Environment (build 1.7.0_01-b08) Java HotSpot(TM) 64-Bit Server VM (build 21.1-b02, mixed mode) $ javac -help 使用方法: javac <options> <source files> 使用可能なオプションには次のものがあります。 -g すべてのデバッグ情報を生成する -g:none デバッグ情報を生成しない -g:{lines,vars,source} いくつかのデバッグ情報のみを生成する -nowarn 警告を発生させない -verbose コンパイラの動作についてメッセージを出力する -deprecation 推奨されないAPIが使用されているソースの位置を出力する -classpath <path> ユーザー・クラス・ファイルおよび注釈プロセッサを検索する位置を指定する -cp <path> ユーザー・クラス・ファイルおよび注釈プロセッサを検索する位置を指定する -sourcepath <path> 入力ソース・ファイルを検索する位置を指定する -bootclasspath <path> ブートストラップ・クラス・パスの位置をオーバーライドする -extdirs <dirs> インストール済み拡張機能の位置をオーバーライドする -endorseddirs <dirs> 推奨規格パスの位置をオーバーライドする -proc:{none,only} 注釈処理やコンパイルを実行するかどうかを制御します。 -processor <class1>[,<class2>,<class3>...] 実行する注釈プロセッサの名前。デフォルトの検出処理をバイパス -processorpath <path> 注釈プロセッサを検索する位置を指定する -d <directory> 生成されたクラス・ファイルを格納する位置を指定する -s <directory> 生成されたソース・ファイルを格納する場所を指定する -implicit:{none,class} 暗黙的に参照されるファイルについてクラス・ファイルを生成するかどうかを指定する -encoding <encoding> ソース・ファイルが使用する文字エンコーディングを指定する -source <release> 指定されたリリースとソースの互換性を保つ -target <release> 特定のVMバージョン用のクラス・ファイルを生成する -version バージョン情報 -help 標準オプションの概要を出力する -Akey[=value] 注釈プロセッサに渡されるオプション -X 非標準オプションの概要を出力する -J<flag> <flag>を実行システムに直接渡す -Werror 警告が発生した場合にコンパイルを終了する @<filename> ファイルからの読取りオプションおよびファイル名よく使いそうなオプションは-verbose、-deprecation、-classpath、-d、-s、-encodingでしょうか。以下ではverboseとencoding(UTF-8で指定)についてサンプルを載せておきます。
$ javac -verbose Request.java [RegularFileObject[Request.java]を構文解析開始] [17ミリ秒で構文解析完了] [ソース・ファイルの検索パス: .] (略) [ZipFileIndexFileObject[/usr/java/jdk1.7.0_01/lib/ct.sym(META-INF/sym/rt.jar/java/io/InputStream.class)]を読込み中] [ZipFileIndexFileObject[/usr/java/jdk1.7.0_01/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Object.class)]を読込み中] [ZipFileIndexFileObject[/usr/java/jdk1.7.0_01/lib/ct.sym(META-INF/sym/rt.jar/java/lang/String.class)]を読込み中] (略) [RegularFileObject[Request.class]を書込み完了] [合計991ミリ秒] $ javac -encoding utf-8 Request.java特定のディレクトリ以下で複数のjavaファイルを一度にCompileしたい時は直列でファイルを複数指定するか、まとめてアスタリスク(*)を使う事も出来ます。
$ javac file1.java file2.java $ javac *.javaMaking jar
jarはJava classファイルのアーカイブ(Java Archive)で、ファイルを一つずつ管理する手間が省けます。jarファイルを作成するにはjarコマンドを使います。一般的な圧縮ファイルに用いられるUnixコマンドのtarと同じような形式で実行できます。下はJarのhelpコマンドです。
$ jar 使用方法: jar {ctxui}[vfm0Me] [jar-file] [manifest-file] [entry-point] [-C dir] files ... オプション: \ -c アーカイブを新規作成する \ -t アーカイブの内容を一覧表示する \ -x 指定の(またはすべての)ファイルをアーカイブから抽出する \ -u 既存アーカイブを更新する \ -v 標準出力に詳細な出力を生成する \ -f アーカイブ・ファイル名を指定する \ -m 指定のマニフェスト・ファイルからマニフェスト情報を取り込む \ -e 実行可能jarファイルにバンドルされたスタンドアロン・アプリケーションの \ エントリ・ポイントを指定する \ -0 格納のみ。ZIP圧縮を使用しない \ -M エントリのマニフェスト・ファイルを作成しない \ -i 指定のjarファイルの索引情報を生成する \ -C 指定のディレクトリに変更し、以下のファイルを取り込む ファイルがディレクトリの場合は再帰的に処理されます。 マニフェスト・ファイル名、アーカイブ・ファイル名およびエントリ・ポイント名は、 フラグ'm'、'f'、'e'の指定と同じ順番で指定する必要があります。 例1: 2つのクラス・ファイルをアーカイブclasses.jarに保存する: \ jar cvf classes.jar Foo.class Bar.class 例2: 既存のマニフェスト・ファイル'mymanifest'を使用し、foo/ディレクトリの \ 全ファイルを'classes.jar'にアーカイブする: \ jar cvfm classes.jar mymanifest -C foo/下はjarのサンプルClassです。SampleJar.javaとして保存します。保存したファイルをコンパイルするとSampleJar.class SampleJar2.classというclassファイルが2つできるのでそれをjarコマンドでまとめます。まとめたファイルに対してjava -cpコマンドで実行します。また実行できるようになったJarファイルの中身を見てみます。jar xvfコマンドでjarファイルを解凍できます。
class SampleJar { public static void main( String[] args ) { System.out.println( "Hello! SampleJar" ); } } class SampleJar2 { public static void main( String[] args ) { System.out.println( "Hello! SampleJar2" ); } }$ javac SampleJar.java $ jar cvf SampleJarTest.jar SampleJar.class SampleJar2.class マニフェストが追加されました SampleJar.classを追加中です(入=428)(出=290)(32%収縮されました) SampleJar2.classを追加中です(入=430)(出=291)(32%収縮されました) $ java -cp SampleJarTest.jar SampleJar Hello! SampleJar $ ava -cp SampleJarTest.jar SampleJar2 Hello! SampleJar2 $ jar xvf SampleJarTest.jar META-INF/が作成されました \META-INF/MANIFEST.MFが展開されました \SampleJar.classが展開されました \SampleJar2.classが展開されました $ tree . |-- META-INF | `-- MANIFEST.MF |-- SampleJar.class |-- SampleJar2.class `-- SampleJarTest.jarMETA-INF/MANIFEST.MFにMain-Class: SampleJarと加えるとmainで呼び出せるクラスを指定できます。書き直したサンプルを以下に記します。またjarコマンドでmanifestファイルを指定し、作成されたjarファイルを実行してみます。
$ diff -u MANIFEST.MF MANIFEST.MF.bak --- MANIFEST.MF 2012-04-24 02:05:12.000000000 +0900 +++ MANIFEST.MF.bak 2012-04-24 02:11:10.000000000 +0900 @@ -1,3 +1,2 @@ Manifest-Version: 1.0 Created-By: 1.7.0_01 (Oracle Corporation) -Main-Class: SampleJar $ jar cvfm SampleJarTest.jar META-INF/MANIFEST.MF *.class マニフェストが追加されました SampleJar.classを追加中です(入=428)(出=290)(32%収縮されました) SampleJar2.classを追加中です(入=430)(出=291)(32%収縮されました) $ java -jar SampleJarTest.jar Hello! SampleJar $ java -cp SampleJarTest.jar SampleJar Hello! SampleJar $ java -cp SampleJarTest.jar SampleJar2 Hello! SampleJar2
2.Class
Relations
Basic、Inner、Static Nested、Local、AnonymousといったClassの関係性を以下に示します。Inner ClassとStatic Nested Classは同じNested Classに属しますが、それぞれ異なった役割をもちます。またLocal ClassとAnonymous ClassはInner Classに属します。
- Basic Class
- Nested Class
- Static Nested Class
- Inner Class
- Local Class
- Anonymous Class
Basic Class
JavaはClassを定義しないと何も始まりません。main関数でさえClass内部に記述します。main関数はpublic static void main( String[] args )として定義します。通常は1Javaファイルに対して1Classを書きます。後で詳細を載せようと思いますが、public staticとするとインスタンス化しなくてもコンパイルだけで実行可能になります。classを定義する時に他の言語と同様にコンストラクタが定義可能ですが、Javaにはデストラクタが存在しないようです。代わりにガーベージコレクションが発生したタイミングで呼び出されるfinalizerというものがあるようですが、常に呼び出される訳ではないので定義する時は注意が必要です。
// SampleClassを利用するClient class SampleClient { // staticを付けないとクラス内から呼び出せない private static boolean flag = false; public static void main( String[] args ) { SampleClass sc = new SampleClass( "John", 5 ); System.out.println( sc.getName() ); System.out.println( sc.getNumber() ); setFlag(); System.out.println( getFlag() ); System.gc(); } // staticを付けないとクラス内から呼び出せない private static void setFlag() { flag = true; } // staticを付けないとクラス内から呼び出せない public static boolean getFlag() { return flag; } } class SampleClass { // メンバ変数 private String Name; private int Number; // コンストラクタ SampleClass( String name, int number ) { System.out.println( "called constructor" ); this.Name = name; this.Number = number; } // デストラクタはJavaでは不要 代わりとしてfinalizerが存在する public void finalize() { System.out.println( "called finalaizer" ); } // Stringを返すメソッド public String getName() { return this.Name; } // intを返すメソッド public int getNumber() { return this.Number; } // stringを設定するメソッド public void setName( String name ) { this.Name = name; } // intを設定するメソッド public void setNumber( int number ) { this.Number = number; } }実行結果
$ java SampleClient called constructor John 5 trueNested Class
JavaはPythonと同様にClassの入れ子階層を作る事ができます。入れ子階層の外側のクラスをOuter Class、内側のクラスをNested Classと呼びます。Nested Classの種類はInner Class、Static Nested Classの2つがあります。Inner Classは更にLocal、Anonymous Classに分類されます。
Static Nested Class
Static Nested ClassはOuter Classと関係性が無い非Inner Classと言われています。Static Nested ClassはOuter Classの静的メンバクラスでしかないのでInstanceを生成しなくてもアクセスが可能です。
// Outer Class class OuterClass { // Static Nested Class static class StaticNestedClass { // staticで定義する static String name = "John"; static int number = 5; public static String getName() { return name; } public static int getNumber() { return number; } } } // Classを呼び出すClient class ClassClient { public static void main( String[] args ) { System.out.println( OuterClass.StaticNestedClass.getName() ); System.out.println( OuterClass.StaticNestedClass.getNumber() ); } }Inner Class
Classの中に更なる非StaticなClassを定義する事が可能です。Inner Classを必要とするケースですが外部向けに新たにClassを作るよりはOuter Classのデータを享受しながら拡張できるInnerClassを定義する場合等に応用できると思います。
public class OuterClass { public class InnerClass { public class Inner2Class { // Inner2Class method public void echo() { System.out.println( "Inner2Class Hello!" ); } } // InnerClass method public void echo() { System.out.println( "InnerClass Hello!" ); } } // OuterClass method public void echo() { System.out.println( "OuterClass Hello!" ); } // OuterClass method public static void main( String[] args ) { OuterClass outer = new OuterClass(); OuterClass.InnerClass inner = outer.new InnerClass(); OuterClass.InnerClass.Inner2Class i2 = inner.new Inner2Class(); i2.echo(); } }実行結果
$ java OuterClass Inner2Class Hello!Local Class
Inner ClassのうちClassに名前をつけるのがLocal Classです。Local ClassとAnonymous Classに共通して言える事はstaticメンバを持つ事ができません。上のInner Classを次のようにstaticなメンバ変数を追加するとCompileエラーが出ます。
public class OuterClass { public OuterClass() { System.out.println( "call OuterClass constructor" ); //メソッド内の内部クラス class InnerClass { // コンパイルエラー static String name = "John"; public String returnString() { return "InnerClass returnString"; } } System.out.println( new InnerClass().returnString() ); } // main public static void main(String[] args) { new OuterClass(); //匿名クラス System.out.println((new Object() { public String echoString() { return "AnonymousClass echoString"; } }).echoString()); } }Compile実行結果
$javac OuterClass.java OuterClass.java:6: エラー: 内部クラスInnerClassの静的宣言が不正です static String name = "John"; ^ 修飾子'static'は定数および変数の宣言でのみ使用できます エラー1個Anonymous Class
Inner Classは名前をつける必要がありません。特定のメソッド内にてnew Object() { 定義メソッド }.定義メソッド()と呼び出せばClassの名前定義を省けます。
public class OuterClass { public OuterClass() { System.out.println( "call OuterClass constructor" ); //メソッド内の内部クラス class InnerClass { public String returnString() { return "InnerClass returnString"; } } System.out.println( new InnerClass().returnString() ); } // main public static void main(String[] args) { new OuterClass(); //匿名クラス System.out.println((new Object() { public String echoString() { return "AnonymousClass echoString"; } }).echoString()); } }実行結果
call AnonymousClass constructor InnerClass returnString AnonymousClass echoString
3.NameSpace
Classファイルの名前空間を指定するにはpackageを使います。これにより自分で作成したClass名がimportしたpackageのClass名と重なっても問題を引き起こしません。packageの宣言は慣習的にjp.co.sitename;と言ったようにトップレベルドメインから記載します。package名はディレクトリの構成に依存し、jp.co.sitenameの場合javaファイルをjp/co/sitename/以下に設置します。packageの呼び出し元javaファイルはclasspath設定のディレクトリに配置します。classpath設定のディレクトリで呼び出し元javaファイルをjavacでコンパイルすると、呼び出し側、package側の両方でclassファイルが作成されます。
PackageClass.classpackage jp.co.sitename; public class PackageClass { public String returnString() { return "PackageClass retrunString"; } }PackageClient.java
import jp.co.sitename.PackageClass; class PackageClient { public static void main( String[] args ) { PackageClass pc = new PackageClass(); System.out.println( pc.returnString() ); } }Compile&実行
$ tree . |-- PackageClient.java //呼び出し側 |-- jp `-- co `-- sitename `-- PackageClass.java // packageとして定義したjava $ javac PackageClient.java $ tree . |-- PackageClient.class |-- PackageClient.java |-- jp `-- co `-- sitename |-- PackageClass.class `-- PackageClass.java $ java PackageClient PackageClass retrunString
4.Annotation
Java SE 5から登場したAnnotationという機能によりソースコードに注釈を加える事ができます。注釈と言ってもただの説明文ではなく、Annotationの記述よってプログラムの動作を変更したり、コンパイル時のエラー出力を制御できたりします。Annotationを積極的に利用する事でコードに統一感を持たせる事が出来るので、メンテナンス運用の助けとなると思います。以下標準のAnnotationの種類です。Annotaionはjava.lang.Annotationの継承なので、それを更に継承してAnnotationを自作する事も可能なようです。ここでは例として
Annotation 説明 @Deprecated Classやメソッドが非推奨 @Override SuperClassのメソッドをOverrideしている事を示す @SuppressWarnings 引数で指定した特定の警告メッセージを無視 @Target 定義Annotationの適用箇所を差す @Retention Annotationの配置方法設定 // import import java.lang.annotation.Annotation; // 親クラス class SuperClass { protected String name; public SuperClass() { this.name = "Super Class"; } public void echoName() { System.out.println( this.name ); } } // 継承クラス class ChildClass extends SuperClass { public ChildClass() { this.name = "Child Class"; } @Override public void echoName() { System.out.println( "Child Class method call " ); super.echoName(); } } // client class ClassClient { public static void main( String[] args ) { ChildClass cc = new ChildClass(); cc.echoName(); } }実行結果
$ java ClassClient Child Class method call Child Class
5.Generics
GenericsはJava SDK1.5から導入された文法でC++のtemplateと同じようなもので、Classやmethodの定義はデータ型を汎用化し、そのClass利用側で型の制約をつける事ができる機能です。Class/methodの定義はそれぞれの定義はclass class名<型パラメータリスト>や public T method名(仮型引数)といった記述を行います。パラメータリストや仮型引数などTやEなどの大文字一文字を用いるのが慣習のようです。下の例では様々なデータ型を定義できる汎用的なクラスを定義しておいて、利用者側で
、 といった実型引数をつけて型を明確化します。これにより型の定義が分かりやすくなります。 // 型パラメータリストTで定義 class GenericsClass<T> { private T val; // 仮型引数Tで定義 public void setValue( T val ) { this.val = val; } // 型パラメータリストTで定義 public T getValue() { return this.val; } } // ClientClass class GenericsClient { public static void main( String[] args ) { // Stringで定義 GenericsClass<String> gcs = new GenericsClass<String>(); gcs.setValue( "String value" ); System.out.println( gcs.getValue() ); // Integerで定義 GenericsClass<Integer> gci = new GenericsClass<Integer>(); gci.setValue( 10 ); System.out.println( gci.getValue() ); } }実行結果
String value 10
6.Extension for
JDK1.5前のjavaは連想配列のkey,valueの値をforで取得する場合にはiteratorを使わなければなりませんでした。JDK1.5以降は拡張for文というものが使え、幾分簡単にkey,valueが取得できます。以下にMapで定義されたkey、valueに対する操作プログラムを記述します。
import java.util.*; class ExtensionFor { public static void main( String[] args ) { Map<String, String> map = new HashMap<String, String>(); map.put( "1", "value1" ); map.put( "2", "value2" ); map.put( "3", "value3" ); System.out.println( "Before JDK1.5" ); // JDK1.5前 for( Iterator<String> i = map.keySet().iterator(); i.hasNext(); ) { Object key = i.next(); System.out.println( key + ":" + map.get( key ) ); } System.out.println( "After JDK1.5" ); // JDK1.5後 for(Map.Entry<String, String> e : map.entrySet() ) { System.out.println( e.getKey() + ":" + e.getValue() ); } } }実行結果
Before JDK1.5 3:value3 2:value2 1:value1 After JDK1.5 3:value3 2:value2 1:value1
7.Variables
JDK1.5前は関数の引数が固定で定義しなければなりませんでしたが、1.5以降は可変長で変数定義できます。型名 ピリオド3つ(...) 変数名と定義することで変数の個数の制限がなくなります。これにより関数利用者側へのIF提供が柔軟になります。
class VariableClass { // 可変長引数定義 ピリオド3つ public void printVariables( String ... args ) { for( String s : args ) { System.out.println( s ); } } } class ClassClient { public static void main( String[] args ) { VariableClass vc = new VariableClass(); //可変長引数設定 vc.printVariables( "a", "b", "c" ); vc.printVariables( "d", "f" ); vc.printVariables( "g" ); vc.printVariables( ); } }