Javaの学習は以下の順序で進めます: 基礎編->進階編->高度編->向上編
1. 基礎編:
(1) オブジェクト指向:
C言語を学んだとき、問題解決やシステム開発は必要な機能に基づいてメソッドをプログラミングするという考え方でした。これは手続き型プログラミングと呼ばれます。
その後Javaを学ぶ前に、コンピュータ系の教授が「コンピュータ入門」の講義で人間もクラス(Javaのclass)であり、教授は人間のインスタンス、つまり実体のオブジェクトであることを説明しました。人間は手、足、目などのメンバー変数(プロパティ)を持ち、踊る、食べるなどの行動はメンバー関数(動作)です。つまり、現実の世界のオブジェクトを観察する際には、そのオブジェクトが持つ属性や行動に焦点を当てることで、オブジェクト指向の考え方を持つことができます。
クラスとオブジェクトの関係性について補足します: クラスはオブジェクトの抽象であり、オブジェクトはクラスのインスタンスです。たとえば、人間は抽象的な概念であり、四肢を持つ、頭脳を持つ、思考能力を持つものが人間であると感じますが、教授は実体の人間であり、人間のオブジェクトです。
(2) 三つの特性:
- カプセル化: カプセル化とはメンバー関数や変数を外部から直接アクセスできないようにすることです。Javaではprivate修飾子を使用してプライバシーを保ちます。スマートフォンが提供する電話機能を考えると、具体的なハードウェアやソフトウェアの仕組みを理解せずに電話をかけることができ、これがカプセル化の役割です。
- 継承: 自動車クラスがあり、自動車の構造や走行機能を持っています。しかし、カスタマイズされたセダンも自動車の構造や走行機能を持ちますが、独自の機能もあります。このような場合、自動車クラスを継承し、自動車の機能を持ちつつ、他の特性や用途についてはセダン独自のものを追加することでコードの再利用が可能になります。
- ポリモーフィズム: 同じ動作が異なる形で現れることをポリモーフィズムと言います。例えば、絵を描くという動作ですが、カラフルなペンで描いたり、鉛筆で描いたり、抽象芸術的に描いたり、リアリスティックに描いたりなど、同じ動作でも異なる結果が得られます。
以下に動物クラスを使ったポリモーフィズムの例を示します。
abstract class Animal {
abstract void eat();
}
class Cat extends Animal {
public void eat() {
System.out.println("魚を食べる");
}
public void work() {
System.out.println("ネズミを捕まえる");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("骨を食べる");
}
public void work() {
System.out.println("家を守る");
}
}
(3) キーワード:
Javaのキーワードは50個あり、2つは現在未使用です: goto(特定の場所へジャンプ)、const(定数、C言語にも同じキーワードがあります)。また、true、false、nullという3つの特殊なリテラルもあります。
| キーワード | 意味 |
|---|---|
| abstract | クラスやメンバーメソッドが抽象的であることを示す |
| assert | プログラムデバッグ用のアサーション |
| boolean | 基本データタイプの一つ、ブール型を宣言するキーワード |
| break | ブロックを早期に脱出する |
| byte | 基本データタイプの一つ、バイト型 |
| case | switch文内で使用され、ある一分岐を示す |
| catch | 例外処理内で使用され、例外をキャッチする |
| char | 基本データタイプの一つ、キャラクタ型 |
| class | クラスを宣言する |
| const | 予約キーワードで、現在のところ具体的な意味がない |
| continue | ブロックの開始部分に戻る |
| default | デフォルト値を示す。switch文内で使用される。Java8からはインターフェースの関数のデフォルト実装を宣言するためにも使用される |
| do | do-whileループ構造内で使用される |
| double | 基本データタイプの一つ、ダブル精度浮動小数点型 |
| else | 条件文内で使用され、条件が成り立たないときの分岐を示す |
| enum | 列挙型 |
| extends | あるタイプが別のタイプのサブタイプであることを示す。クラスの場合、他のクラスや抽象クラスになることができる。インターフェースの場合、他のインターフェースになることができる |
| final | 最終的なプロパティを示すために使用され、クラスが派生できないことを、またはメンバーメソッドがオーバーライドできないことを、またはメンバーフィールドの値が変更できないことを示す。定数を定義するためにも使用される |
| finally | 例外処理内で使用され、基本的には実行されることが保証されているステートメントブロックを宣言する |
| float | 基本データタイプの一つ、シングル精度浮動小数点型 |
| for | ループ構造の導入詞 |
| goto | 予約キーワードで、現在のところ具体的な意味がない |
| if | 条件文の導入詞 |
| implements | あるクラスが指定されたインターフェースを実装していることを示す |
| import | 指定されたクラスやパッケージにアクセスすることを示す |
| instanceof | オブジェクトが指定されたタイプのインスタンスであるかどうかをテストする |
| int | 基本データタイプの一つ、整数型 |
| interface | インターフェース |
| long | 基本データタイプの一つ、ロング整数型 |
| native | コンピュータに関連する言語(C/C++/FORTRANなど)で実装されていることを示すメソッドを宣言するために使用される |
| new | 新しいインスタンスオブジェクトを作成するために使用される |
| package | パッケージ |
| private | アクセスコントロールの一種: プライベートモード |
| protected | アクセスコントロールの一種: プロテクトモード |
| public | アクセスコントロールの一種: パブリックモード |
| return | メンバーメソッドからデータを返す |
| short | 基本データタイプの一つ、ショート整数型 |
| static | 静的プロパティを持つことを示す |
| strictfp | 単精度またはダブル精度浮動小数点表現がIEEE 754算術規格に従うことを宣言するために使用される |
| super | 現在のオブジェクトの親タイプの参照や親タイプのコンストラクタを示すために使用される |
| switch | 分岐文構造の導入詞 |
| synchronized | コードブロックが同期的に実行されることを示す |
| this | 現在のインスタンスオブジェクトへの参照を指す |
| throw | 例外を投げる |
| throws | 現在定義されているメンバーメソッドで投げられるすべての例外を宣言する |
| transient | シリアライズしないメンバーフィールドを宣言する |
| try | 例外を投げることができる可能性のあるプログラムブロックを試みる |
| void | メンバーメソッドが戻り値を持たないことを宣言する |
| volatile | 二つ以上の変数が同時に変化する必要があることを示す |
| while | ループ構造内で使用される |
キーワードはすべて小文字で識別されます。用途に応じて以下のグループに分けられます。
- データタイプ: boolean、byte、char、double、false、float、int、long、new、short、true、void、instanceof
- 文: break、case、catch、continue、default、do、else、for、if、return、switch、try、while、finally、throw、this、super
- 修飾: abstract、final、native、private、protected、public、static、synchronized、transient、volatile
- メソッド、クラス、インターフェース、パッケージ、例外: class、extends、implements、interface、package、import、throws
(4) 基本データタイプ:
整数:
- byte: 8ビットの整数型で、Numberを継承し、Comparableを実装しています。
- short: 2バイト16ビットの整数型で、byteと同様にNumberを継承し、Comparableを実装しています。
- int: 整数型。
- long: 長い整数型。
浮動小数点:
- float: 単精度浮動小数点型。
- double: ダブル精度浮動小数点型。
キャラクタ:
- char: 文字型。
ブール:
- boolean: 真偽値を表す型。
(5) String:
Stringクラスは文字列を表します。Javaプログラム内のすべての文字列リテラル(例: "abc")はこのクラスのインスタンスとして実装されます。
文字列は不変です。作成後、その値は変更できません。Stringバッファは可変文字列をサポートします。Stringオブジェクトは不変であるため共有できます。
(6) コレクション:
Collectionの下層にあるList: Vector(スレッドセーフ)、LinkedList/ArrayListについて説明します。
ArrayList:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
// 省略...
}
ArrayListの主なコンストラクタは以下の通りです。
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: " +
initialCapacity);
}
}
public ArrayList(Collection extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
this.elementData = EMPTY_ELEMENTDATA;
}
}
LinkedList:
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable {
transient Node<E> first;
transient Node<E> last;
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
void linkBefore(E e, Node<E> succ) {
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
}
Vector:
ArrayListと似ていますが、同期化されたメソッドが含まれています。
HashMap:
ハッシュテーブルを使用したデータ構造です。
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node[] tab; Node p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1)
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) {
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
Hashtable:
同期化されたメソッドが含まれています。
ConcurrentMap:
セグメントロックを使用し、Hashtableの全体をロックする代わりに特定のセグメントのみをロックします。
(7) 列挙型:
列挙型の書き方は2つあります。
public enum Color {
RED(1), GREEN(2), BLUE(3), YELLOW(4);
private int colorCode;
Color(int colorCode) {
this.colorCode = colorCode;
}
public int getColorCode() {
return colorCode;
}
}
public static void main(String[] args) {
System.out.println(Color.YELLOW.getColorCode());
}
もう1つの書き方は以下の通りです。
public enum Status {
NOT_STARTED(1, "Not Started"),
IN_PROGRESS(2, "In Progress"),
COMPLETED(3, "Completed");
private int statusCode;
private String statusDescription;
Status(int statusCode, String statusDescription) {
this.statusCode = statusCode;
this.statusDescription = statusDescription;
}
public int getStatusCode() {
return statusCode;
}
public String getStatusDescription() {
return statusDescription;
}
public static void main(String[] args) {
System.out.println(Status.IN_PROGRESS.getStatusDescription());
}
}
(8) ラッパークラス:
基本データタイプについて確認します。
(9) リフレクション:
リフレクションに関する詳細はこちらの記事をご覧ください。
(10) 動的プロキシ:
動的プロキシに関する詳細はこちらの記事をご覧ください。
(11) シリアライズ:
public class MySerializable implements Serializable {
private static final long serialVersionUID = 1L;
private String name = "example";
private transient int age = 25;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public static void main(String[] args) {
try {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("data.ser"));
MySerializable obj = new MySerializable();
out.writeObject(obj);
out.close();
} catch (IOException e) {
e.printStackTrace();
}
deserialize();
}
private static void deserialize() {
try {
ObjectInputStream in = new ObjectInputStream(new FileInputStream("data.ser"));
MySerializable obj = (MySerializable) in.readObject();
in.close();
System.out.println("Name: " + obj.getName());
System.out.println("Age: " + obj.getAge());
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
(12) アノテーション:
アノテーションに関する詳細はこちらの記事をご覧ください。
(13) ジェネリクス:
ジェネリクスに関する概念的な説明はこちらの記事をご覧ください。
(14) SPI:
SPI(Service Provider Interface)は、Javaが提供する拡張やコンポーネントの置き換えを可能にするインターフェースです。SPIの役割は、これらの拡張されたAPIのサービス実装を見つけることです。
(15) 例外:
例外はキャッチ可能なExceptionであり、エラーはErrorです(例: StackOverflowError、OutOfMemoryError)。
(16) I/O ストリーム:
I/O ストリームはバイトストリームと文字ストリームに分けられます。詳細はこちらの記事をご覧ください。
(17) その他:
反コンパイルについて補足します。Test.javaを作成します。
public class Test {
public static void main(String[] args) {
String a = "hello";
}
}
javac Test.java && javap -c Test を実行すると以下のようになります。
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String hello
2: astore_1
3: return
}
ldc は文字列を定数プールに格納し、hello がプールに書き込まれます。