- KMC (京大マイコンクラブ) で Android の勉強会を行うために作られた資料
- 第2・3回 Java の文法パート
- 文の区切りには
;
が必要 - 基本的には1ファイル1クラス
- クラス名 == ファイル名
- ファイル名と同じクラスがあれば一つのファイルに複数クラス記述できるが、あまり使わない
- 基本的に CamelCase で命名する
- OK:
hogePiyo
- NG:
hoge_piyo
- クラス名は一般的に大文字始まり
- 変数名は一般的に小文字始まり
- OK:
// これはコメントです
/**
* これもコメントです
*/
/* これもコメントです */
変数宣言は、変数宣言をする前にオブジェクトの型を書く。その後に変数名を書いて代入する。
String hogehoge = "文字列";
int a = 0;
基本的には自由に再代入できる。型の前に final
修飾子をつけるとその後変数に再代入できない。
final String hogehoge = "文字列";
hogehoge = "再代入"; // => コンパイルエラー
if (0 == 1) {
System.out.println("それはおかしい");
} else if (0 > 3) {
System.out.println("それもおかしい");
} else if (0 <= -1) {
System.out.println("こんなことにもならない");
} else {
System.out.println("はい");
}
注意してほしいのは、==
はオブジェクトが同じであれば True
になるが、オブジェクトの中の値が同じだからといって True
になるとは限らない。
String a = "ほげほげ";
a == a; // => これは true (同じオブジェクト)
a == "ほげほげ"; // => これは false (違うオブジェクト)
a.equals("ほげほげ"); // => これは true (equals の中で中の値が同じであれば True を返すようになっている)
int a = 0;
a == 0 // => true (プリミティブ型はこのように比較可能)
int a = 0;
switch(a) {
case 0:
System.out.println("a が 0 の場合");
break;
case 1:
System.out.println("a が 1 の場合");
break;
default:
System.out.println("どの case にも当てはまらなかった場合");
break;
}
Java7 時点では String
, int
, enum
しか引数に入れられない。
break
を書かないとそれ以降の記述も実行されてしまうので注意!
int a = 0;
while(a < 4) {
a = a + 1;
System.out.println("こんにちは");
}
int a = 0;
while(true) {
a = a + 1;
System.out.println("こんにちは");
if (a > 4) {
break;
}
}
int a = 0;
while(true) {
a = a + 1;
if (a > 4) {
// a > 4 のとき "こんにちは" が出力されない
continue;
}
System.out.println("こんにちは");
}
for (int i = 0; i < 10; i++) {
System.out.println("こんにちは");
}
プリミティブ型(オブジェクトではない物)を参照する場合はリテラルを使う。
int a = 20;
boolean a = true;
boolean b = false;
// 'a' は char (文字) であり、文字列ではない。
// String (文字列) を使いたい場合 "" で囲う
char a = 'a';
null は特別なリテラルの一つ。何もないことを示す。
変数に null を入れると変数は作られるが、そこにオブジェクトはない。
String a = null;
a == null; // => true
null に対して関数を呼ぼうとすると NullPointerException
が発生する (これが有名な 「ぬるぽ」)。
String a = null;
a.indexOf("a"); // => NullPointerException (a is null)
オブジェクトを作成する時は new
を初めに付けて、コンストラクタを呼ぶ。
// オブジェクト作成
new Hoge();
new Manga("ゆるゆり", "なもり", "一迅社", "百合姫コミックス", "第1巻", 600, "最高", "最高です");
// オブジェクトを作成して変数に代入
Hoge hoge = new Hoge();
Manga yuruyuri2 = new Manga("ゆるゆり", "なもり", "一迅社", "百合姫コミックス", "第2巻", 600, "最高最高", "最高最高です");
String は "aaa"
で特別に String オブジェクトが作られる(文字列リテラルという)。
// クラスの宣言の前には final / public などの修飾子を付ける。
public class MyClass {
// 以下に示すものはメンバ変数といい、クラス内からアクセスできる。
// クラス外からアクセスできるかどうかをアクセス修飾子で設定する。(アクセス修飾子は修飾子の一つ)
// public: どこからでもアクセス可能
public String hoge;
// protected: 継承された (後述) クラスのみアクセス可能
protected String piyo;
// private: クラス内のみ
private String fuga;
// 修飾子なし: 同一パッケージ (後述) のクラスのみアクセス可能
String peco;
// 初期値を入れることもできる (初期値がない場合 null)
public String hello = "hello, world!";
// 以下のような記述をコンストラクタという。(名前はクラス名と同じ)
// コンストラクタではオブジェクトの初期化をする
// 例: コンストラクタで hoge に代入する用の String を引数に取って初期化する
public Hoge(String hoge) {
// 変数名がメンバ変数と引数などの変数で被った場合、メンバ変数にアクセスするときは this を使う。
this.hoge = hoge;
}
// コンストラクタはいくつでも作成可能
public Hoge(String hoge, String piyo) {
// コンストラクタ内で他のコンストラクタを呼ぶ場合は this() を使う
this(hoge);
this.piyo = piyo;
}
// コンストラクタもアクセス修飾子で制御できる
private Hoge(String hoge, String piyo, String fuga) {
this(hoge, piyo);
this.fuga = fuga;
}
// メソッドは修飾子、返すクラス、名前、引数の順番で記述
public String getHoge() {
return hoge;
}
public String getPiyo() {
return piyo;
}
// 何も値を返さない場合は void を使う
public void setHoge(String hoge) {
this.hoge = hoge;
}
// クラスの中にクラスを作ることもできる
public class Piyo {
public String piyo
}
}
このようなクラスを作ると、以下のようにオブジェクトを作成・メソッド呼び出し・メンバ変数の参照を行うことができる。
Hoge hoge1 = new Hoge("aaa");
String h1 = hoge1.getHoge(); // => h1 = "aaa"
String h2 = hoge1.hoge; // コンパイルエラー (private なのでアクセスできない)
String hello = hoge1.hello; // => hello = "hello, world!"
String p1 = hoge1.getPiyo(); // => p1 = null
Hoge hoge2 = new Hoge("aaa", "ppp");
String h2 = hoge2.getHoge(); // => h2 = "aaa"
String p2 = hoge2.getPiyo(); // => p2 = "ppp"
hoge2.setHoge("sss"); // (返り値なし)
String h3 = hoge2.getHoge(); // => h3 = "sss"
Hoge hoge2 = new Hoge("aaa", "ppp", "zzz"); // コンパイルエラー (private なのでアクセスできない)
補足: コンストラクタを作成しない場合は無引数のデフォルトコンストラクタが作成される。
例:
class Hoge {
}
Hoge hogehoge = new Hoge();
コンストラクタを作成した場合、デフォルトコンストラクタは生成されない。
static は「静的」とも呼ばれる。それぞれのオブジェクトに固有__でない__クラス/変数/メソッドのこと。 static 修飾子をつけることで、static クラス / 変数 / メソッドとなる。
クラスの中にクラスを作った時、 static でない場合そのクラスはオブジェクトに紐ついているので、外部から直接オブジェクトを作成するなどはできない。
public class Hoge {
private Piyo piyo;
public Hoge() {
piyo = new Piyo();
}
public Piyo getPiyo() {
return piyo;
}
public class Piyo {
}
}
Hoge hoge = new Hoge();
Piyo piyo1 = hoge.getPiyo; // エラーではない
Piyo piyo2 = new Hoge.Piyo(); // コンパイルエラー
static クラスであれば、直接オブジェクトを作成できる。
しかし、static クラスは元のオブジェクトに固有__でない__ので、中のクラスから元のクラスの変数にはアクセスできない。
public class Hoge {
private Piyo piyo;
private String hoge;
public Hoge() {
piyo = new Piyo();
}
public Piyo getPiyo() {
return piyo;
}
pulbic static class Piyo {
public Piyo() {
System.out.println(hoge); // コンパイルエラー
}
}
}
// コンパイルエラーの部分を無くすと…
Hoge hoge = new Hoge();
Piyo piyo1 = hoge.getPiyo; // 可能
Piyo piyo2 = new Hoge.Piyo(); // 可能
基本的には、クラスに定義されたメソッドを呼ぶときはオブジェクトを作成して呼ぶ必要がある。
public class Hoge {
private String piyo;
public Piyo getPiyo() {
return piyo;
}
public String getAAA() {
return "aaa";
}
}
Hoge hoge = new Hoge();
String aaa1 = hoge.getAAA(); // 可能
String aaa2 = Hoge.getAAA(); // コンパイルエラー
static メソッドであれば、オブジェクトを作らなくても、直接メソッドを呼べる。
しかし、static メソッドはオブジェクトに固有__でない__ので、static メソッドからクラスの変数にはアクセスできない。
public class Hoge {
private String piyo;
public static Piyo getPiyo() {
return piyo; // コンパイルエラー
}
public static String getAAA() {
return "aaa"; // これはコンパイルエラーではない
}
}
// コンパイルエラーを除くと…
Hoge hoge = new Hoge();
String aaa1 = hoge.getAAA(); // 可能
String aaa2 = Hoge.getAAA(); // 可能
static なメンバ変数はオブジェクトごとに作成され__ない__。static 変数は同じ場所を指しているため、同じ変数はオブジェクト間で同じ値となる。
また、そのような性質のためにオブジェクトを作成せずとも直接値を取得できる。
public class Hoge {
// 今回は外部からアクセスできるようにする
public static String fuga = "a";
public String getFuga() {
a = a + "a";
return piyo;
}
}
// 直接取得
String fuga1 = Hoge.fuga; // "a"
Hoge hoge1 = new Hoge();
String fuga2 = hoge1.getFuga(); // "aa"
String fuga3 = hoge1.fuga; // "aa"
Hoge hoge2 = new Hoge();
String fuga4 = hoge2.getFuga(); // "aaa"
String fuga5 = hoge1.fuga; // "aaa"
String fuga6 = hoge2.fuga; // "aaa"
String fuga7 = hoge1.getFuga(); // "aaaa"
String fuga8 = hoge2.fuga; // "aaaa"
String fuga9 = Hoge.fuga; // "aaaa"
static final
を用いて定数を定義することがある。
public class Hoge {
// 定数の場合全て大文字かつ SnakeCase にすることが多い
public static final String HOGE_FUGA = "a";
}
String hogeFuga = Hoge.HOGE_FUGA; // "a"
第三回スライドはこちら: https://speakerdeck.com/nonylene/androidapuriwozuo-ru-di-3hui/
スライド参照 https://speakerdeck.com/nonylene/androidapuriwozuo-ru-di-3hui/
スライド参照 https://speakerdeck.com/nonylene/androidapuriwozuo-ru-di-3hui/
全てのクラスの親クラス。Object を型に指定するとなんでも入れることができる。
toString()
メソッドはここに定義されているので全てのオブジェクトで呼べる。
クラス名を宣言せずにクラスを作る。名前がないので無名クラス・匿名クラスと呼ばれる。
class Hoge {
public String piyo() {
return "hogehoge~";
}
}
// 例: Hoge を継承してなにか作る
// Hoge を継承しているので型は Hoge だと分かっている
Hoge anonymousHoge = new Hoge() {
// オーバーライドする
@Override
public String piyo() {
return "anonymous!!!1";
}
// 一応独自の関数を定義できる(ほとんどやらない)
public String foo() {
return "foo";
}
}
よくあるのは、クリックされた時に呼ばれるオブジェクトを作成するために、インターフェースを実装した無名クラスを作る。
// View.OnClickListener はインターフェース
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
System.out.println("clicked");
}
});
こういう時にクラスを定義してオブジェクトを作って代入することはあまりない。
特別なクラスの形態。列挙型といい、オブジェクトの値を初めに列挙する。それ以外の値にはならない (全て定数)。
// 一般的に値は大文字
public enum Hoge {
// メソッドなどを定義しない場合最後に , 付けてもok
FOO, BAR, BAZ,
}
Hoge hogeObject = Hoge.FOO;
Hoge hogeObject2 = Hoge.BAR;
クラスなのでメソッドやメンバ変数も定義できる。(詳細は省略)
switch 文で使える。
// hogeObject は Hoge と宣言されているとする
switch(hogeObject) {
case FOO:
System.out.println("foo!");
break;
case BAR:
System.out.println("bar!");
break;
case BAZ:
System.out.println("baz!");
break;
}
変数やメソッド、クラスなどに付加することができる、コードの注釈として意味を持たせることができるもの。内部では特別なインターフェース。
コンパイル時に警告を抑制するのに使ったり、非推奨メソッドであることを表すのに使ったりする。
アノテーション定義時に設定をすればコードを実行するときにも設定したアノテーションがついているかを判別することができ、それを使ったライブラリもある。
// 非推奨を表す (こうするとこのクラスを使った時にコンパイラやIDEに注意される)
@Deprecated
public class Hoge extends Piyo {
// 絶対 null にならないことを示す
@NonNull
public String foo = "";
// オーバーライドしていることを示す
@Override
public String bar() {
return "aa";
}
}
throw {{例外}};
と記述すると例外(エラー)が投げられる。例外を捕捉していない場合スレッドが終了する。
例外は Throwable
インターフェースを実装しており、一般的には Throwable
を実装した Exception
を継承している。
try
の中に記述すると、 try
の内部で例外が発生すれば catch
でそれを捕捉することができる。また、例外が発生すると発生場所以降は実行されないが、 finally
の中に記述すると必ず実行される。
catch
では捕捉する例外の型を選択する。
try {
throw new IllegalArgumentException("なんかエラーでたぞ!");
} catch(Exception e) {
// Exception を捕捉
// IllegalArgumentException は Exception の子クラスなので捕捉できる
// 例: エラー内容を出力する
e.printStackTrace();
} finally {
System.out.println("終わりました");
}
チェック例外と非チェック例外があり、チェック例外はエラーが起きる可能性が仕様として存在しているので、チェック例外を投げるメソッドでは投げる例外のクラスが明示される。また、そのメソッドを呼ぶ際は必ず try
で囲む必要がある。
public String getHoge() throws Exception {
if (hoge == null) {
throw new Exception("nullだよ");
} else {
return hoge;
}
}
非チェック例外は RuntimeException
の子クラスとなっている。 (例: NullPointerException
)
String[] stringArray = {"aa", "aaa"};
String[] stringArray2 = new String[]{"aa", "aaa"};
// 空(null)の要素が2つの配列
String[] stringArray3 = new String[2];
// プリミティブ型でも可
// array はオブジェクト
int[] intArray = new int[2];
配列は初めから長さが決まっている。
リストはオブジェクトを順序付きで保持できる可変長のオブジェクト。(データ構造等に関しては省略)
配列にはプリミティブ型を入れられるが、リストには入れられない。
List
自体はインターフェースなので直接作成はできない。インターフェースを実装した ArrayList
や LinkedList
によってオブジェクトを作成する。
それぞれのリストによって内部のデータ構造が違うが、基本的には ArrayList
を使って良い。
// この <String> は型引数といい、型を引数として使っている。 (ジェネリックという、詳細は省略)
// これで "String の リスト" ということが表される。
// また ArrayList の <> は <String> の省略形。 (List の方で <String> であることが分かっているため)
List<String> stringList = new ArrayList<>();
stringList.set(0, "文字");
stringList.set(1, null);
// 0番目の文字取得
String str = stringList.get(0); // このとき, get() から出るのは String ということが保証されている (List<String> のため)
stringList.size(); // サイズを返す
stringList.isEmpty(); // 空かどうか
リストの他にも辞書 / 連想配列の Map
や順序なしの集合である Set
など。
// String オブジェクトがキー、 Hoge オブジェクトが値
Map<String, Hoge> hogeMap = new HashMap();
hogeMap.put("key1", new Hoge("aaa"));
hogeMap.put("key2", new Hoge("aaa"));
hogeMap.get("key1");
名前空間。パッケージの通りにディレクトリや Java ファイルを設置する必要がある。
net/nonylene/hoge/Hoge.java -> net.nonylene.hoge パッケージ
net/nonylene/piyo/Piyo.java -> net.nonylene.piyo パッケージ
Java ファイルの先頭にはパッケージを宣言する必要がある。
package net.nonylene.hoge;
さらに機能毎にパッケージを分割することがある。
package net.nonylene.hoge.book;
同一パッケージに同じ名前のクラス / インターフェースを作成することはできない。
基本的にベースとなるパッケージはプログラム・ライブラリごとに一意。
同一パッケージのクラスは、特になにもしなくてもアクセスできる。
しかし、別のパッケージでは import
を行うか、パッケージをクラスの前に付加する必要がある。
package net.nonylene.hoge;
import net.nonylene.piyo.Piyo;
public class Hoge {
public Piyo piyopiyo;
public net.nonylene.foo.Foo foofoo;
}
*
を用いるとパッケージ直下の全てをインポートする。
package net.nonylene.hoge;
import net.nonylene.piyo.*;
import net.nonylene.foo.*;
public class Hoge {
public Piyo piyopiyo;
public Foo foofoo;
}
static メソッドや変数は import static
を用いて直接インポートできる。
package net.nonylene.hoge;
import net.nonylene.piyo.Piyo;
import static net.nonylene.piyo.Piyo.printPiyo;
public class Hoge {
public Piyo piyopiyo;
public net.nonylene.foo.Foo foofoo;
public Hoge() {
printPiyo();
}
}
プリミティブ型ではジェネリクスが使えないなど困ることがある。プリミティブ型の値をオブジェクトとして扱いたい場合は以下に示すプリミティブ型に対応したラッパークラスを使う。
これらのクラスは内部でプリミティブ型の値を保持している。
int -> Integer (32bit)
byte -> Byte
short -> Short (16bit)
long -> Long (64bit)
float -> Float
double -> Double
boolean -> Boolean
char -> Character
- これらのクラスにはそれぞれに関係するメソッドがある
- オブジェクトなので
null
を使える - ジェネリクスに使える (
List<Integer>
) ==
は通常のオブジェクトと同じ働きになるので、値が同じだけではtrue
にならない.equals()
を使う
- ラッパクラスとプリミティブ型を変換する際にキャスト等は不要(オートボクシング・アンボクシング)
// オートボクシング
Integer integerObject = 1;
Boolean booleanObject = true;
// アンボクシング
int intPrimitive = integerObject;
boolean booleanPrimitive = booleanObject;
基本的にはプリミティブ型を使い、オブジェクトが必要な時にラッパークラスのオブジェクトに変換することが多い。
Android アプリ開発勉強会のために書いた Java の入門文書 - https://gist.github.com/nobuoka/6546813