LoginSignup
165
182

More than 5 years have passed since last update.

ざっくりJava~クラスとか継承とか~

Last updated at Posted at 2014-02-04

書いてる人

プログラミング学習サービスやら、ペットサロン予約サービス、風俗検索サービスなど色々とやっている「かずきち」です。
■運営サービス一部
http://crazy-wp.com/
http://webukatu.com/
新宿のホストから不動産・保険の営業を経て、HTMLって何?という状態から3ヶ月独学でプログラミングやデザインを学び、IT業界で1年間実務経験を積んで年収は1本超え。現在は起業家としてサービス運営やら不動産運営をしています。
Qiita内にそれ系の記事も書いてます。
エンジニアで稼ぐために大切な13のコト
WEBサービスで起業したい人に読んで欲しい18のコト

クラス

クラスは金型というかテンプレート的なもの。「クラス名」と「ファイル名」を同じにする。
RPGでいえば、「モンスター」というテンプレートを作って、「名前」「HP」「攻撃力」などを設定できる箱(変数)を作る。
オブジェクトを定義する。とかっていう。

クラスには「フィールド」と「メソッド」が定義できる。
フィールドにはクラスが持つ情報を変数として定義する。
モンスタークラスを作り、それを後述する「インスタンス化」することで実体化(金型からポンッと取り出す的な)させて「名前」「HP」など設定して色々なモンスターを作り出す。

※フィールド変数は、「メンバ変数」ともいう
※クラス内に定義されるメソッドを「インスタンスメソッド」という
※javaファイル1つにつき基本的には1クラスを書く(Monsterクラス、Itemクラスなど作りたければ別ファイルにする)

public class Monster{
  String name; //モンスターオブジェクトが持つ情報(フィールド)を定義する。
  int hp;
  public void attack(){
      //メソッドも定義できる(引数も返り値も指定できる)
       //「オブジェクト自体が持っている動き」を定義する(今回だと攻撃する、守る、とか)
  }
}

クラス型変数が必要な理由

なぜクラス型の変数にインスタンスを格納して使うのかというとクラスを元にnewしてインスタンスを2つ作った場合、インスタンス(実体)が2つ存在することになる。(同じ人間が2人いる状態)
その状態で、mainメソッドから指令を出そうにもどっちのインスタンスか区別がつかない。
そこで、クラス型変数へそれぞれ格納することで、クラス型変数aのインスタンスとクラス型変数bのインスタンスというように区別が出来る様になる。

オブジェクトの作成

定義したクラスから実体化(金型ポンっ)するのをオブジェクトを作成するっていう。
「インスタンス化」ともいう。
newすることでインスタンス化するが、インスタンス化するとJVM(javaを実行するプログラム)がヒープ領域(メモリ・倉庫的な)の一部を使ってオブジェクトを作成する。
倉庫に実体化されたモンスターを格納してるイメージ。
オブジェクト作成、実体化、インスタンス化は一緒。
複数インスタンス化すれば、複数の変数とメソッドが作成される。

型(クラス名) 変数 = new クラス名();
Monster ms1 = new Monster(); //変数ms1のことをインスタンス変数という

※Monster型のインスタンス変数ms1とかって言い方をする
※ms1には作ったオブジェクトが格納されているメモリ番地(倉庫内の場所)が入っている

オブジェクトに値を入れる

オブジェクトを作ったら、情報を入れてあげる。

public class Monster{
  public static void main(String[] args){ //JVMはこの1行が書かれた固まりの中を実行する
    String name; 
     int hp;

    Monster ms1 = new Monster();

    ms1.name = "スライム"; //「インスタンス変数.フィールド変数」で値を入れられる
    ms1.hp = 1;

    Monster ms2 = new Monster(); //インスタンス化してどんどんモンスターを作れる

    ms2.name = "ゴブリン";
    ms2.hp = 5;
    ms2.attack(); //メソッドを指定して処理を行うこともできる
  }
}

コンストラクタ

コンストラクタは初期化をする機能。
インスタンス化と同時に自動的に初期値が設定されるようにするなどの時に使う。
new Monster()などしてインスタンスを作成した直後には、デフォルトコンストラクタが自動作動して初期値「null」や「0」、「false」をインスタンス変数に入れていくれている。

コンストラクタの書き方
public class Monster{
  String name; 
  int hp;
  int attack;

   public Monster(String a, int b){ //戻り値はつけない(voidもだめ)。publicは省略してもいい。メソッド名はクラス名と同じにする。
     name = a;
     hp = b;
     attack = 10; //固定値で初期化してもいい
      System.out.println("初期化しました"); //return文以外なら何を書いてもいい
  }
}

※コンストラクタは各クラス内に定義する。戻り値は指定しない。(指定するとただのメソッドになる)
※引数の名前だけ変えてもオーバーロードにはならない(型や並び、引数の数を変えないとダメ)

コンストラクタを実行する
Monster ms1 = new Monster("スライム",1) //引数で初期化する値を渡してあげる

※コンストラクタを使えば、いちいちインスタンス化して値を設定して~と書かずに1行で済む。

コンストラクタのオーバーロード

引数の並び順や数を変えていくつもコンストラクタを作ることもできる。(オーバーロード)
色々な初期化の仕方を設定しておける。

コンストラクタのオーバーロード
Monster(){ //コンストラクタ①
  this.hp = 10;
}

Monster(String name){ //コンストラクタ②
  this.name = name;
  this.hp = 10;
}

Monster ms1 = new Monster(); //コンストラクタ①が呼ばれる
Monster ms2 = new Monster("スライム"); //コンストラクタ②が呼ばれる

コンストラクタからコンストラクタを呼び出す

コンストラクタを複数設定すると、hpの初期化など重複する部分が出てくる。
もし後々修正するということになるとそれぞれのコンストラクタの初期値なりを修正しないといけないので非効率。
そこで、コンストラクタの中でコンストラクタを呼んであげる。

コンストラクタからコンストラクタを呼び出す
Monster(String name){ //コンストラクタ①
 this.hp = 10;
 this.name = name;
}
Monster(){ //コンストラクタ②
 this("名前はまだない"); //コンストラクタ①を呼び出す
}

クラスメソッド

インスタンスメソッドと反対に、mainに直接書いて実行するメソッドをクラスメソッドという。
※変数やメソッドに「static」をつけるとnewしても新規作成されず、同じ変数を参照することになる。
※staticがついた変数やメソッドのあるクラスをインスタンス化すると、staticがついた変数やメソッドだけは複製されない。(ずっと1つ。そのメソッドや変数は使えるが、複数のインスタンスから操作すると同じ中身をいじることになる)

パッケージとか

パッケージはクラスの入ったフォルダ。
クラスの入ったjavaファイルが多くなった時に整理するために関連のあるファイルをひとまとめにして、パッケージというかたちで整理する。
パッケージ同士はお互い中は見えない。
もし、あるクラスから他のパッケージ内にあるクラスなど使いたい場合は「import」文を記述する。
※読み込みたいクラスがpublicじゃないとダメ
※パッケージは見た目階層っぽくなるが、親子関係などなく全く独立したもの。
jp.co.sample.testjp.co.sample.filejp.co.sampleが親なわけじゃない)
※全世界でjavaファイルは出回っているので、出回っているファイルと自分が作っているクラス名がだぶってしまうと開発時に困る。パッケージで分けられるが、パッケージ名も自由につけてしまうとダブる可能性があるので、暗黙的に自分の持つインターネットドメインを逆にしたものが使われている。
(ドメインは世界にひとつしかないのでダブることはない)

importの記述例
//import パッケージ名.クラス名 (パッケージ内の全クラス使いたいならクラス名を*にする)
import java.awt.*; 

public class Sample{
     //・・・・
}

package

今書いているファイルのある場所を指定するために「package」を使う

packageの記述例
package sample.java
public class Test{
    //・・・・・
}

sampleフォルダのjavaフォルダの中にTest.javaファイルがあるよってことを指定してる。

ライブラリ

複数のクラスファイルの集まりのこと。
javaファイルをコンパイルして、複数のクラスファイルにし、JAR形式にアーカイブして利用するのが一般的。

javaファイルとclassファイル

javaファイルをコンパイル(javac)すると.classのクラスファイルが出来る。
もし、javaファイル内にクラスをいくつも定義してた場合、それぞれの.classファイルが出来る。
※javaファイル内にはpublicのつくclassは1つしか定義できない。(publicなしなら複数定義OKではあるけど1ファイル1クラスの方がいい)

アクセス修飾子

クラスや変数、メソッドなどの定義時に先頭につけるもの。
クラスやその中身が、他のクラスからどのくらい見えるのか(使えるのか)指定する。
※クラスがデフォルトなのにフィールドを「public」にしても、元々クラス自体がデフォルトなので意味ない
※逆にクラスが public でもフィールドが デフォルトだと、フィールドはパッケージ外からは見れない

デフォルト(何も書いてない)・・・パッケージの中だけで見える。
private・・・同じクラス内でしか見えない(クラスにはつけられない)
protected・・・デフォルトと同じ(パッケージ内)+サブクラス(パッケージ外でも)なら見える
abstract ・・・抽象クラス。まだ完全に定義されていないってことなので、継承時にオーバーライドが必要
         newできないので、サブクラスでオーバーライドして初めてオブジェクト作成できる。
         オーバーライドしてなくても、クラス型の変数は作れる。

カプセル化

クラスを定義した際にフィールドの修飾子をpublicなどにしておくと外部からいくらでも操作出来てしまうので、privateにして外部からはいじれなくする。
その代わり、ゲッターメソッド、セッターメソッドを用意することで、ブラックボックス化させる。

public class Monster{
   private String name;
   public void set(String name){ //privateの変数へ値をセットするためのメソッド(セッター)
       this.name = name; //this.nameは自分のクラスのnameを表す。ただのnameは引数のname。
   }
   public String get(){ //privateの変数の値を取得するためのメソッド(ゲッター)
       return this.name;
   }
}

継承

今までに作ったクラス(変数、メソッド)を拡張して、さらに変数やメソッドを追加したいという時には
同じようにクラスを作って~変数定義して~とやらずに、前に作ったクラスを「継承」して
足らない部分だけ定義する。
継承元を「スーパークラス(親クラス)」、継承先を「サブクラス(子クラス)」という。
継承すると、サブクラスはスーパークラスで定義されてた変数やメソッドが使える。

public class Boss extends Monster{ // extends 継承したクラス名 で指定
  int mp; //マジックポイントを追加
  public void criticalAtack(){ //必殺攻撃を追加
  }
}
public class Execute{
  public static void main(String[] args){
    Boss bs = new Boss();
    bs.attack();  //スーパークラスのメソッドもそのまま呼び出せる
     Monster ms = bs; //スーパークラスのMonster型変数へBossインスタンスを入れることもできる
     ms.criticalAtack(); //スーパークラスへ自動的に型が変わっているので、サブクラスで定義したものは使えない。
 }
}

※継承は1つしかできない。いくつか継承したい時には「implements」というインターフェースを使う。
※スーパークラスにあったコンストラクタやstatic、final、privateなものは継承されない。
※サブクラスではスーパークラスのメソッドをオーバーロードすることもできる

オーバーライド

オーバーライドは継承した元クラスにあるメソッドなどを再定義(カスタマイズ)して使うこと。
オーバーライドした後にメソッドを呼ぶとスーパークラスのメソッドではなく、オーバーライドされたメソッドが呼ばれる。
スーパークラスのメソッドをあえて呼びたいなら、「super.メソッド名();」で呼べる。

※下記のようにスーパークラス型でインスタンス化したものは、スーパークラスのメソッドが呼ばれる

public class Boss extends Monster{ 
  public void Atack(){ //オーバーライドする
  }
}
public class Execute{
  public static void main(String[] args){
    Boss bs = new Monster();
    bs.attack();  //スーパークラスのメソッドが呼ばれる
 }
}

※インスタンス変数の型がサブクラスだったとしても、参照しているのはスーパークラス型のインスタンスなので、スーパークラスのメソッドが呼ばれる。

抽象クラス

例えば、OneBoxクラスやSedanクラスを作る場合、重複するフィールドやメソッドが出てくる。
そこで、その大本になるCarクラスを作り、そのCarクラスにOneBoxクラスやSedanで必ず必要になるフィールドやメソッドを定義しておいて、それを継承して使った方が効率的。

public class Car{
  private color;
  private speed;

 public void run(){
    System.out.println("時速"+this.speed+"で進みます");
  }
}

ただ、スピードは継承するクラスによって違うので、Carクラス制作時には定義できない。
また、解決策としてrunメソッドをなくしてしまうとオブジェクト指向にそぐわない。(走る機能のない車が出来てしまう)
また、runメソッドの中身を空にして、継承時にオーバーライドしてもらう方法にするとオーバーライドし忘れ(エラーも出ず空のメソッドが実行される)や文字の打ち間違いによってオーバーライドしたと思ったら新しいメソッドを作ってしまっていたり(ranメソッドとやってしまうなど)が起こる可能性がある。
また、中身が空なのはそういう仕様なのか、間違いなのかが判断できなくなってしまう。
そして、「継承して使う」ためのクラスのはずが普通にインスタンス化出来てしまう。(未完成のオブジェクトが動いてしまう)

そこで、抽象クラス・抽象メソッドを使う。
これは、継承しないと使えず、抽象メソッドとして定義したメソッドをオーバーライドしないと使えないもの。
だから、上記のような間違いが起こらなくなる。

抽象クラス・抽象メソッド
public abstract class Car{ //abstractとつけることで、継承しないと使えない抽象クラスになる
  //・・・
  public abstract void run(); //abstractとつけることでオーバーライドしないと使えない抽象メソッドになる
  //・・・
}

※抽象メソッド(未完成な部分)を持つクラスは必ず抽象クラスになっていないとエラーになる
※抽象クラスを継承した子クラスは、抽象メソッドをオーバーライド(未完成の部分を完成させないと)しないとエラーになる

インターフェース

継承は1つの親しか持てないが、インターフェースならいくつも持てる。
継承とは反対に「実装」するという。
インターフェースのクラスは「public」で定義しないとダメ。
インターフェースにインターフェースを「extends」することも出来るが、オーバーライドなどは出来ず、
ただ取り込めるだけ。

※インターフェースには定数と抽象メソッドしか定義できない。
※抽象メソッドは実装した際にオーバーライドする必要がある

インターフェースの定義と実装
public Interface Animal{
   int MAX = 1000; //定数定義。自動でpublic finalがつく
   void sample(); //抽象メソッド。自動でpublic abstractがつく
}

public class Monster implements Animal{
    public void sample(){ //オーバーライド。publicをつけること
       System.out.println("おーばーらいど");
    }
}

クラスパスの設定

Javaはクラスパスで指定された場所にあるクラスファイルしか探さない。
JVMはまず指定されたクラスファイルを読み込み(mainメソッドのあるクラス)、その中にあるmainメソッドを実行。
mainメソッド内で呼び出されている他クラスをその時その時必要な時に読み込む。
JVMの中でクラスファイルの読み込みは「クラスローダー」が担当している。
JVMは必要なクラス名をクラスローダーに伝えるが、クラスローダーはクラス名しか分からず、HDのどこにあるのかまでは分からない。
そこで、クラスローダーは「クラスパス(CLASSPATH)」内のパス情報を元にクラスを探す。
また、JARファイルの場合でもその中のクラスファイルまでは読みにいってくれないので、別途指定する必要がある。
(JARファイルはフォルダみたいな感じ)
クラスパスを指定する方法は3つ

1.環境変数CLASSPATHに宣言する
2.javacやjavaコマンド実行時に-cpオプションをつける
3.何も指定しない(現在のフォルダ「.」を指定したのと同じになる)

※クラスローダーはクラスパスで指定した階層のフォルダ内にあるクラスファイルを探すので、指定した階層と同じようにフォルダを作り、その中に指定のクラスファイルを配置する必要がある。

インナークラス

クラスの中で定義するクラスのこと。「内部クラス」ともいう。
宣言場所によって「メンバクラス」「ローカルクラス」「匿名クラス」の3種類がある。

メンバクラス ➡ クラスブロックの中で宣言したもの
ローカルクラス ➡ クラスブロック内のメソッドブロックの中で宣言したもの
匿名クラス ➡ 式の一部で宣言したもの(a = new Object(){ ・・・ }みたいな)

自分でオリジナルのクラスを作った時にすること

1.commons-langを使う

Java言語の基本機能を強化するクラスが色々入っている。
他にもログ出力を簡単に行うための「commons-logging」やコレクションを便利に使うための「commons-collections」というものもある。

1.以下よりダウンロード

http://commons/apache.org/lang

2.DLした圧縮ファイルを解凍し、中のJARファイルを適当なフォルダにコピー
3.CLASSPATH変数へパス追加

2.equals()をオーバーライドする

equalsメソッドは「等価」かどうかを判定する。
「等価(equals)」は同じ内容かどうか(同じアドレスじゃなくていい)、「等値(==)」は完全に同じか(同じアドレス)を判定するもの。
「何をもって等価か?」を定義しないと、等価判定ができないので、removeを使った時にきちんと削除されなかったりという不具合が出る。

なので、equalsメソッドをオーバーライドして再定義する。

import org.apache.commons.lang3.builder.*;
public class Sample{
  public boolean equals(Object o){
     return EqualsBuilder.reflectionEquals(this, o);
  }
}

3.hashCodeメソッドをオーバーライド

HashSetでremove()する時に正しく削除されるようにする。

import org.apache.commons.lang3.builder.*;
class Sample{
  public int hashCode(){
      return HashCodeBuilder.reflectionHashCode(this);
  }
}

4.Comparableインターフェースを実装する

並び替えをする際など、「どういう順番でか?」を定義しておく必要がある。
そのため、Comparableをimplementsして、compareToメソッド(大小関係の判定)を定義する必要がある。

class Sample implements Comparable<Sample>{
   public int compareTo(Sample obj){
      if(this.number < obj.number) return -1; //自分よりobjの方が大きいなら負
      if(this.number > obj.number) return 1; //自分よりobjの方が小さいなら正
      return 0; //自分とobjが同じなら0
   }
}

5.clone()をオーバーライドする

class Sample implements Cloneable{ //Cloneableを実装して、複製をサポートしていることを明示
   String name;
public Sample clone(){ //clone()をオーバーライドする
   Sample result = new Sample();
   result.name = this.name;
   //・・・・と複製するものを記述していく
   return result;
}
}

コマンドライン引数

mainメソッドにある引数のString[] argsにはプログラム起動時にコマンドライン引数として値を渡せる。
プログラム起動時にJVMが半角で区切られた情報を配列に格納する。

public static void main(String[] args){

}
java プログラム名 引数(半角スペースで区切って複数渡せる)

多様性を使う

多様性とは「ざっくり」捉えること。
現実世界では様々なものもざっくり捉える事で上手くいっている。
例えば、車の運転も車種が変わっても運転は出来る。
でも、機械では「ハンドルを何度切って、座標xyにあるシフトを〜」と細かく決めないといけないので、車種が変わると途端に運転出来ない。
そこで、機械でもざっくり捉えるようにする。
実際にはインスタンスを入れるクラス型変数を親以降上位のクラス型変数へ格納することで、色々なメリットが得られる。
※子クラスのインスタンスを親のクラス型に格納すると、子クラスで新たに定義したフィールドやメソッドは使えない。
メソッドを呼んだ時には親クラス型に格納したとしても、オーバーライドした方の子クラスのメソッドが呼ばれる

Monster m = new Slime();

※MonsterクラスはSlimeクラスの親クラスだとする

メリット1:一括処理が出来る

Siime s = new Slime();
Gost g = new Gost();
Draky d = new Draky();

//それぞれのクラスの親クラスで継承したgetHp()メソッドを持ってたとして、それをそれぞれのモンスターが行う処理をすると
s.getHp();
g.getHp();
d.getHp();
//と同じ様な記述を全インスタンスに行わないといけない。重複したコードが多くなる
多様性を使った場合
Monster[] m = new Monster[3]; //さっきのインスタンスの親クラスの配列を作る

m[0] = new Slime();
m[1] = new Gost();
m[2] = new Draky();

for(Monster o : m){ 
  o.getHp();
}
//モンスターの種類は違っても、同じモンスター型という親を継承しているので、ざっくり同じモンスターと見なしてfor文などで一括で処理ができる

メリット2:メソッドの重複が防げる

メソッドの引数をざっくりとした型にして受け取る事で、メソッドの重複が防げる

多様性を使わない場合
public class Character{
  public void attack(Slime s){ //スライム攻撃用メソッド
    s.hp -=5;
  }
  public void attack(Gost g){ //ゴースト攻撃用メソッド
    g.hp -=5;
  }
}
多様性を使わない場合
public class Character{
  //ざっくり攻撃相手はモンスターだと捉え、引数をMonster型にすることで1つのメソッドでスライムでもゴーストにでも攻撃可能になる
  public void attack(Monster m){ 
    m.hp -=5;
  }
}

WEBプログラミングから起業までを一貫して学べるオンライン動画総合学習サービス『ウェブカツ!!』

様々なプログラミング学習サイトやサービスでは正直な所、自分でWEBサービスを作れるようにはならないため、「自分でWEBサービスを作れるようになる!」をゴールとしたオンライン動画総合学習サービス『ウェブカツ!!』を立ち上げました。興味ある方は登録よろしくお願いいたします。
http://webukatu.com/

165
182
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
165
182