本記事は、Java にアレルギー反応が出る人が、少しだけ Java を読み書きできるようになることを目的としています。
今回は Java で躓く原因である、オブジェクト指向プログラミングの用語に焦点を当て、ざっくり解説します。(厳密な定義を知りたい方は別のところで調べてください)
なお、知らない Java の構文が出てきたら以下のサイトを見るのがおすすめです。
また、本記事は以下の Java 入門用の記事の初回です。他の記事については以下をご覧ください。
- 【Java 入門1】オブジェクト指向プログラミングの基本用語
- 【Java 入門2】ガベージコレクションの仕組みとは
- 【Java 入門3】JDBC (Java DataBase Connectivity)
- 【Java 入門4】Gradle (ビルド自動化システム)
オブジェクト指向の基本用語
各用語の全体関連図
以降の例では、ソースコードの配置場所が src/main/java 配下の環境を想定しています。
Gradleは、製品のソースコードが
http://gradle.monochromeroad.com/docs/userguide/tutorial_java_projects.htmlsrc/main/java
に、テストのソースコードがsrc/test/java
にあることを想定しています。
メンバ変数(フィールド)
メンバ変数は、型(数字や文字など)を指定して値を格納するものです。
後述する"クラス"の中に書かれた変数です。
public class Test {
String memberVariable = "メンバ変数";//これがメンバ変数です
public static void main(String[] args) {
}
}
String 型の memberVariable メンバ変数には、文字列を格納できます。
メソッド
メソッドは関数(処理をひとまとめにしたもの)です。
後述する"クラス"の中に書かれた関数です。
public class Test { public static void main(String[] args) { //main メソッドです。 hello(); //メソッドの呼び出しです。 } private static void hello(){ // hello メソッドです。 System.out.println("hello"); } }
hello メソッドでは、画面に hello と出力する処理をまとめています。(この例では1行ですが)
メソッド名はキャメルケース(先頭を小文字、単語区切りで大文字)で記載します。
ローカル変数
ローカル変数はメソッドの中で定義される変数です。メソッドの中でしか参照できません。
public class Test { public static void main(String[] args) { //main メソッドです。 hello(); //メソッドの呼び出しです。 } String memberVariable = "メンバ変数";//これがメンバ変数です private static void hello(){ // hello メソッドです。 String localVariable = "ローカル変数"; //ローカル変数 } }
クラス class
クラスは、複数のメンバ変数とメソッドとクラスをひとまとめにしたものです。
public class Test {//これが Test クラスです。
public static void main(String[] args) { //main メソッドです
hello();
}
String memberVariable = "メンバ変数";// メンバ変数
private static void hello(){ // hello メソッドです。
System.out.println("Hello World");
}
}
Hello World
ファイル名はクラス名と同じにします。
例えば、「public class TestClass」クラスの場合は、「TestClass.java」というファイル名になります。
また、クラス名は Pascal 記法(単語の先頭を大文字)を使用します。
内部クラス
内部クラスは、クラスの中に作成したクラスのことです。
package test;
class Test {
public static void main(String[] args) {
class InnerClass {
String var= "内部クラス";
}
}
}
内部クラス
パッケージ package
パッケージは1つ以上のクラスをまとめたものです。
名前空間の機能(同じパッケージ内では、同じクラス名を使うことは出来ません。異なるパッケージ間では、同じクラス名を使うことが出来ます。)を持ちます。
src/main/java 配下に以下のように配置すると、test フォルダがパッケージに相当します。
test/ ├ Test.java └ ExtraClass.java
自分の所属するパッケージは package <パッケージ名>; で記載します。
package test; //test パッケージです。
public class Test {//これが Test クラスです。
public static void main(String[] args) { //main メソッドです
System.out.println("hello");
}
}
インスタンス new
インスタンスはクラスを変数に代入したものです。
つまり、インスタンスは複数の「メンバ変数」と「メソッド」を持ちます。
ExtraClass クラスを extraClass 変数に代入することで、extraClass インスタンス作成します。
test/ ├ Test.java └ ExtraClass.java
package test; class ExtraClass {//ExtraClass クラス public void hello(String word){ //hello メソッド System.out.println(word); } }
package test; public class Test { //Test クラスです。 public static void main(String[] args) { //main メソッドです。 ExtraClass extraClass = new ExtraClass();// new を利用して ExtraClass クラス型の extraClass インスタンスを作成します。 extraClass.hello("クラスの持つメソッドの呼び出し");//インスタンスはクラスの持つメソッドを呼び出せます。 } }
他のパッケージに存在するクラスを利用する場合、パッケージ名を含める必要があります。
test2 パッケージと AnotherPackageClass クラスを用意します。
.test/ └ Test.java .test2/ └ AnotherPackageClass.java
package test2;
public class AnotherPackageClass {
public void hello(){ //hello メソッド
System.out.println("Another World");
}
}
package test; public class Test { //Test クラスです。 public static void main(String[] args) { //main メソッドです。 //自分と異なるパッケージのクラスを利用する場合、完全修飾名である(パッケージ名を含める)必要があります。 test2.AnotherPackageClass anotherPackageClass = new test2.AnotherPackageClass(); anotherPackageClass.hello(); } }
オブジェクト
オブジェクトは複数のメンバ変数とメソッドをまとめたものです。
つまり、Java におけるオブジェクトはクラスもしくはインスタンスを指します。
import
import は、自分の所属しないパッケージのクラスでインスタンスを作成する際に、パッケージ名を省略できる命令です。
package test2; public class AnotherPackageClass { public void hello(){ //hello メソッド System.out.println("Another World"); } }
package test; import test2.AnotherPackageClass; public class Test { //Test クラスです。 public static void main(String[] args) { //main メソッドです。 //パッケージ名"test2"が要らない AnotherPackageClass anotherPackageClass = new test2.AnotherPackageClass(); //test2 というパッケージ名を省略可能 anotherPackageClass.hello(); } }
クラス変数 (static 変数) static
クラス変数は、メンバ変数のうち修飾子に static がついた変数です。
インスタンスを作成しなくても変数にアクセス可能な特徴を持ちます。
package test;
public class ExtraClass {
static String classVariable = "クラス変数";//クラス変数です
}
package test;
public class Test {
public static void main(String[] args) { //main メソッドです。
System.out.println(ExtraClass.classVariable);//クラス変数の呼び出し
}
}
インスタンス変数
インスタンス変数は、メンバ変数のうち修飾子に static がついていない変数です。
変数にアクセスするために、インスタンスを作成する必要があります。
package test;
public class ExtraClass {
String instanceVariable = "インスタンス変数";//インスタンス変数です
}
package test; public class Test { public static void main(String[] args) { //main メソッドです。 ExtraClass extraClass = new ExtraClass();//インスタンス変数を利用するためにはインスタンスを作成する必要があります。 System.out.println(extraClass.classVariable);//インスタンス変数の呼び出し } }
コンストラクタ
コンストラクタは、インスタンスを作成したタイミングで実行されるメソッドです。コンストラクタのメソッド名はクラス名と同じであり、インスタンスの初期化処理に使います。
package test; public class ExtraClass { //コンストラクタのメソッドはクラスの名前と同じ String instanceVariable = "インスタンス変数です";//インスタンス変数です。 ExtraClass(){ //コンストラクタです。 System.out.println("コンストラクタで初期化!"+instanceVariable); } }
package test; public class Test { public static void main(String[] args) { //main メソッドです。 ExtraClass extraClass = new ExtraClass(); } }
main メソッドでは ExtraClass クラスのメソッドを呼び出していませんが、インスタンス作成時に ExtraClass() メソッドが呼び出されます。
(実行結果が「コンストラクタで初期化!インスタンス変数です」となります。)
インターフェイス interface
インターフェイスはクラスに必要なメンバ変数とメソッドを定義した仕様書のようなものです。
メソッドの中身は空なので、インターフェイスだけでは使えません。
package test; interface Itransform { //Transform インターフェイスです。 //transformString() で変換した文字列を格納 String transformedWord = null; //String 型 word を引数として文字列を変換し、String 型を戻り値とする public String transformString(String word); //メソッドの中身は空 //文字列をprintするメソッド。引数は String 型で戻り値は void (無し) public void print(String transformedWord); } public class Test {//これが Test クラスです。 public static void main(String[] args) { //main メソッドです。 } }
例えば、上記の例では「transformString()」に「文字列を変換する処理」を実装すればよいことがわかります。
インターフェイスはあくまで input と output を確認する仕様書なので、この段階ではメソッドの中を実装しなくてもよいです。
実装 implements
実装とは、インターフェイスのメソッドの処理を実際に実装することです。
Transform クラスを実装します。
- transform メソッドは、引数の語尾に「変換!!」をつけます
- print メソッドは、引数を print します
package test; interface Itransform { //Transform インターフェイスです。 //transformString() で変換した文字列を格納 String transformedWord = null; //String 型 word を引数として文字列を変換し、String 型を戻り値とする public String transformString(String word); //メソッドの中身は空 //文字列をprintするメソッド。引数は String 型で戻り値は void (無し) public void print(String transformedWord); } class ImplementsTest implements Itransform{ String transformedWord = null; public String transformString(String word){ //語尾に「変換!!」をつける実装 transformedWord = word + "変換!!"; return transformedWord; } public void print(String transformedWord){ // 引数を print する System.out.println(transformedWord); } } public class Test {//これが Test クラスです。 public static void main(String[] args) { //main メソッドです。 ImplementsTest implementsTest = new ImplementsTest(); String transformedWord = implementsTest.transformString("テスト"); implementsTest.print(transformedWord); } }
結果は「テスト変換!!」と出力されます。
また、実装 (implements) は複数のインターフェイスを指定できます。
package test; interface ITest1{ String foo="null"; } interface ITest2{ String bar=null; } class ImplementsTest implements ITest1, ITest2{ String foo="hoge"; String bar="tech"; } public class Test {//これが Test クラスです。 public static void main(String[] args) { //main メソッドです。 ImplementsTest implementsTest = new ImplementsTest(); System.out.println(implementsTest.foo); //Itest1 の実装 System.out.println(implementsTest.bar); //Itest2 の実装 } }
継承 extends
継承とは、元のクラスのコピーを作成し、新しいクラスを定義することです。
コピーしたクラスには新しいメンバ変数やメソッドを追加できます。
package test; public class ExtraClass { //継承元のクラス public void original(){ //original メソッド System.out.println("必須機能"); } }
package test; class ExtendClass extends ExtraClass{ //継承 public void option(){ //メソッドを追加 System.out.println("追加機能"); } } public class Test {//これが Test クラスです。 public static void main(String[] args) { //main メソッドです。 ExtendClass extendClass = new ExtendClass(); extendClass.original(); //もともとあったメソッドを継承 extendClass.option(); //継承したクラスに追加したメソッド } }
親クラス(スーパークラス)
親クラス(スーパークラス)は、継承元のクラスのことです。
子クラス(サブクラス)
子クラス(サブクラス)は、継承先(親クラスをコピーした)のクラスのことです。
super キーワードを利用することで親クラス (スーパークラス) にアクセスできます。
package test; public class ExtraClass { //親クラス public void original(){ //original メソッド System.out.println("親クラス"); } }
package test;
class ExtendClass extends ExtraClass{ //子クラス
public void option(){ //メソッドを追加
System.out.println("子クラス");
//super は親クラスである ExtraClass クラスを表します
super.original(); //ExtraClass.original() と等価
}
}
public class Test {//これが Test クラスです。
public static void main(String[] args) { //main メソッドです。
ExtendClass extendClass = new ExtendClass();
extendClass.option(); //継承したクラスに追加したメソッド
}
}
抽象クラス abstract
抽象クラスとは、子クラスで実装すべきメンバ変数とメソッドを定義したクラスです。
予めメソッドを実装することもできるため、メソッドを再利用するために利用します。
抽象クラスは、継承して子クラスを作成しないと使えません。
例えば、「文字列を変換して画面に表示する」というクラスを以下のように再利用する抽象クラスを作ります。
- 再利用するメソッド:文字列を画面に表示する
- 個別に実装するメソッド:文字列を変換する
package test; abstract class AbstractTransform{ //メソッドを実装することが可能 void print(String word){ System.out.println(word); } //メソッドを実装しないことも可能 abstract String transformString(String word); } public class Test {//これが Test クラスです。 public static void main(String[] args) { //main メソッドです。 } }
利用するには extends で継承した上で、abstract メソッドがある場合は実装する必要があります。
package test; abstract class AbstractTransform{ //メソッドを実装することが可能(再利用したいメソッド) void print(String word){ System.out.println(word); } //メソッドを実装しないことも可能(個別に実装したいメソッド) abstract String transformString(String word); } //print メソッドを再利用しつつ、transformStringは子クラスで個別に実装 class CsvTransform extends AbstractTransform{ //継承した abstract メソッドは実装する必要があります String transformString(String word){ return word + "CSV に変換"; } } //print メソッドを再利用しつつ、transformStringは子クラスで個別に実装 class JsonTransform extends AbstractTransform{ //継承した abstract メソッドは実装する必要があります String transformString(String word){ return word + "JSON に変換"; } } public class Test {//これが Test クラスです。 public static void main(String[] args) { //main メソッドです。 CsvTransform csvTransform = new CsvTransform(); JsonTransform jsonTransform = new JsonTransform(); //print メソッドを再利用しつつ、transformStringは子クラスで個別に実装 csvTransform.print(csvTransform.transformString("test を")); jsonTransform.print(jsonTransform.transformString("test を")); } }
test をCSV に変換 test をJSON に変換
インターフェイスとの利用用途の違いは、「抽象クラスではクラスの一部メソッドの再利用」・「インターフェイスはクラスの仕様の定義」です。
オーバーライド
オーバーライドとは、親クラスのメソッドを子クラスで上書きすることです。
子クラスで親クラスと全く同じメソッドを作成することでオーバーライド(上書き)できます。
package test;
public class ExtraClass { //親クラス
public void original(){ //上書き前のメソッド
System.out.println("親クラス");
}
}
package test; class ExtendClass extends ExtraClass{ //子クラス @Override public void original(){ //オーバーライド System.out.println("子クラスに上書きしたよ");// } } public class Test {//これが Test クラスです。 public static void main(String[] args) { //main メソッドです。 ExtendClass extendClass = new ExtendClass(); extendClass.original(); //オーバーライド後のメソッドが呼ばれます。 } }
子クラスに上書きしたよ
アノテーション @
アノテーションとは、プログラムに事前に定義した影響を与えるコメントのことです。
例えば、オーバーライドのソースコードで使用した「@Override」は「親クラスにないメソッドをエラーにする」という機能を持ったコメントです。
アクセス修飾子
アクセス修飾子は、メンバ変数やクラスのアクセス可能な範囲を決定します。
protected
protected アクセス修飾子は、別のパッケージからメンバ変数やメソッドやクラスを呼び出すことができません。
以下のようなパッケージ構成を例に考えます。
./test/ └ Test.java ./test2/ └ AnotherPackageClass.java
package test2; public class AnotherPackageClass { public String public_var = "public"; protected String protect_var = "protected"; }
package test; import test2.AnotherPackageClass; class Test { public static void main(String[] args) { AnotherPackageClass anotherPackageClass = new AnotherPackageClass(); //System.out.println(anotherPackageClass.protect_var); //NG コメントを消すとコンパイルエラー System.out.println(anotherPackageClass.public_var); //OK } }
protect_var は異なるパッケージのメンバ変数なので、protected アクセス修飾子の場合はアクセスできません。
アクセス修飾子なし
アクセス修飾子無しは、protected の制約に加え、子クラス (サブクラス)の継承元が別のパッケージの場合も、メンバ変数やメソッドやクラスを呼び出すことができません。
package test2; public class AnotherPackageClass { protected String protect_var = "protected"; String none_var = "none"; }
package test; import test2.AnotherPackageClass; class ChildClass extends AnotherPackageClass{ //別のパッケージにあるクラスを継承 public void childMethod(){ //System.out.println(none_var); //NG 親クラスが別のパッケージなのでアクセス不可能 System.out.println(protect_var); //OK } } public class Test { public static void main(String[] args) throws Exception { ChildClass childClass = new ChildClass(); childClass.childMethod(); } }
none_var は、異なるパッケージにあるクラスを継承した子クラスが持つメンバ変数なので、アクセス修飾子が無い場合はアクセスできません。
private
private アクセス修飾子は、アクセス修飾子なしの制約に加え、異なるクラスからもメンバ変数やメソッドやクラスを呼び出すことができません。
package test; class AnotherClass { private String private_var = "Private"; String none_var = "none"; } class Test { public static void main(String[] args) { AnotherClass anotherClass = new AnotherClass(); //System.out.println(anotherClass.private_var); //NG 異なるクラスから呼び出しているため System.out.println(anotherClass.none_var); //OK } }
private_var は、異なるクラスが持つメンバ変数なので、private アクセス修飾子の場合はアクセスできません。
オーバーロード
オーバーロードは、引数の異なる同じ名前のメソッドを定義することです。
名前のよく似ているオーバーライドとは関係ありません。
package test; class Test2 { public void methodTest() { //オーバーロード System.out.println("引数無し"); } public void methodTest(String word) { //オーバーロード System.out.println("引数あり:" + word); } } public class Test {//これが Test クラスです。 public static void main(String[] args) { //main メソッドです。 Test2 test2 = new Test2(); test2.methodTest(); //オーバーロード test2.methodTest("オーバーロード"); //オーバーロード } }
引数無し 引数あり:オーバーロード