■クラス
メンバ
- フィールド
- クラスやそのオブジェクトに付属するデータ変数で状態保持などに使用
- メソッド
- オブジェクトの振る舞いを定義
- ネストしたクラス(インターフェース)
- 入れ子になっているクラスやインターフェースの宣言
class Body { // フィールド public long number; // メソッド public void hello(String str) { } // ネストしたクラス public class { } }
■フィールド
以下のようにしてフィールドを初期化することができる。
class Body { double zero = 0.0; double sum = 1.1 + 2.2; double zero2 = zero; double four = Math.sqrt(2); Dog pochi = new Dog(); }
但し、例外を投げるような処理を右辺に書くことはできない。フィールドが初期化されなかった場合、型に応じたデフォルトの初期値が代入される。
boolean | false |
---|---|
char | ‘\u0000’ |
byte, short, int, long | 0 |
float, double | +0.0 |
オブジェクト参照 | null |
個人的には明示しておいた方が読みやすいと思う。
staticフィールド
実体が1つだけのフィールドを実現する。クラス変数とも呼ばれる。
class Dog { long nextID = 0;// コンストラクタで生成ごとに+するとか }
finalフィールド
初期化後に「不変」であるフィールド。
class Dog { int numOfLegs = 4;// 変わらないよ }
- オブジェクトの不変な属性であるか
- オブジェクトが生成されたときに決まっているか
- オブジェクトが生成されたときに任意の値を設定するのは実用的かつ適切か
■アクセス制御
- private
- クラス自身からのみアクセス可能
- 修飾子無し
- 同一パッケージ内からアクセス可能
- protected
- クラス自身とサブクラスからアクセス可能
- public
- メンバーが定義されているクラスにアクセスできるクラスからアクセス可能
privateとprotected
メンバのみに指定可能。メンバ以外のクラスやインターフェースには指定不可。
publicとprotected
制御が及ばないコードによって自身の実装の変更が不可能になる可能性がある。
パッケージアクセスとprivate
実装の一部であり外部のクラスから隠蔽される。
パッケージ
同一パッケージのクラスは関連しているべきである。
■オブジェクトの生成と初期化
// オブジェクトを参照する変数の宣言で生成はしてない Dog shiro; // 生成を伴う Dog pochi = new Dog();
コンストラクタ
newによってオブジェクトが返される前に実行される。
class Dog { private String mName; Dog(String name) { mName = name; } }
- 引数をとることができる
- クラス名と同名である
- アクセス修飾可能である
- クラスのメンバではない
コンストラクタは複数定義することが可能であり、以下のように自身のコンストラクタをinvokeすることもできる。
class Dog { private String mName; Dog() { // 任意の処理 } Dog(String name) { this(); mName = name; } }
publicでないコンストラクタはオブジェクトを生成できるクラスを制限できる。
メモ
- 生成時に必要な領域を割り当てて領域を初期化し新しいオブジェクトへの参照を返す
- 生成時に必要な空きメモリを確保できない場合はOutOfMemoryError例外を投げる
- 生成にはnewを用いるが明示的に削除はせず、GCを使用してメモリ管理する
- オブジェクトが不要になった場合、そのオブジェクトを参照しないようにしなければならない
- メソッド内のローカル変数ならばメソッドの処理が終わるだけで参照されなくなる
- フィールドのような場合はnullを代入することで参照されなくなる
■初期化ブロック
以下のようにすることでコンストラクタよりも前に複雑な処理を行うことができる。
class Dog { private String mName; { // 任意の処理 } Dog(String name) { mName = name; } public String cry() { return "bow"; } }
コンストラクタを持つことができない無名内部クラスを書く場合に有用である。
- 複数のブロックを記述することができる
- 例外aを投げることができる(全てのコンストラクタがaの例外を投げると宣言されているときに限る)
初期化ブロックをstaticで修飾することもできる。
■メソッド
staticメソッド
クラスメソッドのこと。staticメソッドからはstaticでないメンバにはアクセスできない。
class Util { private String str1 = "sample1";// helloメソッドからアクセス不可能 private static String str2 = "sample2";// helloメソッドからアクセス可能 public static String hello() { return "Hello!" } }
メソッドの呼び出し
メソッドはドット演算子を使用し、参照を通してオブジェクトに対する操作として呼び出される。
reference.method(arguments);
インスタンスメソッドの呼び出しは以下のようになる。
Dog pochi = new Dog("Pochi"); pochi.cry();
staticメソッド(クラスメソッド)の呼び出しは以下のようになる。
String hello = Util.hello();
toStringメソッド
オブジェクトの状態を表す文字列表現を返すメソッド。
class Dog { private String mName; { // 任意の処理 } Dog(String name) { mName = name; } public String cry() { return "bow"; } @Override public String toString() { return "Dog{name : " + mName + "}"; } }
可変長引数を持つメソッド
メソッドの最後の引数は、特定の型のシーケンスであることを宣言できる。
class Mail { public static void send(String... to) { for(int i = 0; i < to.length; i++) } }
メソッドの実行と戻り値
以下の条件の時に、メソッドの処理が終了する。
- return文が実行される
- 一つの宣言された戻り値型に代入可能な値を返すか、例外を投げる必要がある
- メソッドの終わりに処理が達する
- 戻り値型はvoidとなる
- catchできない例外が投げられる
- throwsを記述する必要がある
引き数の値
class PassRef { public static void main(String[] args) { Dog pochi = new Dog("Pochi"); System.out.println(pochi.getName()); commonName(pochi); System.out.println(pochi.getName()); pochi = null;// おまけ } public static void commonName(Dog dogRef) { dogRef.name = "Shiro"; dogRef = null; } } /* Pochi Shiro */
pochiと(commonName内の)dogRefは同じ実体のオブジェクトを参照しているので、nameを変更すると実体のオブジェクトのnameフィールドが変更される。
一方で、(commonName内の)dogRefは参照をコピーしたものであるため、nullを代入しても実体がnullにはならない。
ちなみにpochiにnullを代入すると実体を参照している変数がなくなるので、GCの対象となる。
- 引き数は全て値渡しであり、オブジェクトは常に参照である
- Javaに参照渡しというものは存在しない
- 引き数の宣言時にfinalを指定して不変にできる
■this
フィールドの識別子が引き数と同名であることなどにより隠蔽されている時、カレントオブジェクトへの参照として使用する。
class Dog { private String name; Dog(String name) { this.name = name; } public String cry() { return this.name + " : bow"; } }
■メソッドのオーバーロード
メソッドはシグネチャ(名前とパラメータの数とパラメータの型)を持つ。従って、同名のメソッドを複数持つことが可能であり、このことをオーバーロードという。(オーバーライドとは違う)
class Dog { private String name; private int age; Dog(String name) { this.name = name; } Dog(String name, int age) { this.name = name; this.age = age; } }
メソッドでなくコンストラクタでも同様である。但し、可変長引数を持つときは注意する必要がある。
■staticメンバー名のインポート
static double tanh(double x) { return (Math.exp(x) - Math.exp(-x)) / (Math.exp(x) + Math.exp(-x)); }
以下のようなstatic import文を用いることによって
import static java.lang.Math.exp
以下のように簡略化して可読性の高いコードを記述することができる。
static double tanh(double x) { return (exp(x) - exp(-x)) / (exp(x) + exp(-x)); }
オンデマンドstatic import文
コンパイラが知らない名前が見つかった場合、指定された型が特定の名前のstaticメンバを持つかどうか調べる。
import static java.lang.Math.*
タイプの手間を省くために使用するのではなく、可読性と明瞭性を改善する目的で使用する。
■mainメソッド
プログラムを起動する場合に実行される最初のメソッドでpublic static void main(String[] args)でなければならない。
class Echo { public static void mail(String[] args) { for(int i = 0; i < args.length; i++){ System.out.println(args[i]); } } }
以下のコマンドで実行することができる。
java Echo hoge fuga #hoge #fuga
個々のクラスはmainメソッドを持って構わないので、アプリケーションは複数のmainメソッドを持つことができる。
■nativeメソッド
Javaで書かれていない既存のコードを使用するプログラムを書く必要がある場合やハードウェアを直接操作する必要がある場合、JavaからCやC++で記述されたメソッドを呼び出すことができる。
public native int getCPUID();
ネイティブメソッドはfinal, static, synchronized, public, protected, privateで修飾することができるが、abstract, strictfpで修飾することはできない。