Category: Java

4.クラスの基礎

4章 クラスの基礎
1.クラスとは
 クラスとは、プログラムをある実世界のモノとして考え、それに必要なデータと働き(メソッド)をひとまとめにしたものである。
 クラスは、「System」クラスと「String」クラスがある。Systemクラスはディスプレイに文字を表示させるための標準出力、標準エラー出力に表示させるために用いてきた。「String」クラスは、文字列を担うクラスである。そのでーた(クラス変数)として文字列そのものを持ち、メソッドとして検索したり、つなげたり、部分文字列を抽出したりといったメソッドが定義されている。
 このように、データとそれらを扱うメソッドを作成したクラスは、そのクラスの内部がどのようになっているのか知らなくても、部品のように使用することができる。つまり、SystemクラスやStringクラスの中身を知らなくても(ソースコードを見なくても)、そのクラスを使って、プログラムを作ることができる。さらに、その部品(クラス)は他のプログラムを作成するときにも使いまわしができる。
 
2.クラスの作成
 ここでは、Mypoingクラスを作ってみる。Mypointクラスは座標を担うクラスで、メンバー変数として、X座標とY座標を持つ。メソッドとしては、2点間の座標を求めるメソッドと、ディスプレイに表示するためにString型に変換するメソッドを持つ。
 クラスを作成する場合、基本的に1ファイルに1クラスでファイル名はクラス名と同じにする必要がある。
MyPoint.java

class MyPoint{
    double m_x;
    double m_y;

    // 文字列に変換する
    public String toString(){
         return "X:"+m_x+"  Y:"+m_y;
    }

    // 距離を計算する
    public double calcDistance(MyPoint pnt){
          // Mathクラスは数学関係を担うクラス
          // Math.pow累乗を計算するメソッド
          // Math.sqrtはルートを計算するメソッド
     return Math.sqrt(Math.pow((m_x – pnt.m_x), 2) + Math.pow((m_y – pnt.m_y), 2));
    }
}

 

Test.java

import  java.io.*;
   
class  Test{
    public static void main(String args[ ] ){
         int n;
         MyPoint pnt1 = new MyPoint( );                 // (1)
         MyPoint pnt2 = new MyPoint( );                 // (1)

         pnt1.m_x = 10;
         pnt1.m_y = 20;

         pnt2.m_x = 50;
         pnt2.m_y = -20;

    System.out.println("pnt1 " + pnt1.toString( ));
    System.out.println("pnt1 " + pnt1.toString( ));
    System.out.println("距離:" + pnt1.calcDistance(pnt2));
    }
}
 

C:\java>javac Test.java

C:\java>javac Test
pnt1 X:10.0   Y:20.0
pnt2 X:50.0   Y:-20.0

距離:56.568542494923804

C:\java>

 クラスを定義した(MyPoint.javaを作成しただけ)では、クラスを使えるようにはならない。
Test.javaのように「new」をしてはじめてクラスを使えるようになる。このnewをインスタンスを作るまたはオブジェクトを作ると言う。
newを何個も行うことで、インスタンスをたくさん作ることができる。これらのインスタンスは別のものであるが、変数の値もインスタンスが異なれば違う値を保持する。この場合、pnt1のメンバー変数のm_xとpnt2のm_xは違う値を保持している。それはこのためである。
 
      __pnt1__    __pnt2__   
    |_____m_x = 10  |    |   m_x = 50   |
       |     m_y = 20  |    |   m_y = -20 |
        ↑          ↑

        |          |
        |          |

両者は違うメモリーに格納される。このため、別のものとなる。

 
3.コンストラクタ
 クラスには、インスタンスが作られた時に一度だけ実行される「コンストラクタ」というものがある。一般にコンストラクタにはそのクラスインスタンスを初期化するために使用される。
MyPoint.java

class  MyPoint{
    double m_x;
    double m_y;

    // コンストラクタ
    public Mypoint( ){
         System.out.println("コンストラクタ1が呼ばれました");
         m_x = 10;
         m_y = 20;
   }

    // コンストラクタ2
    public Mypoint(double x, double y ){
         System.out.println("コンストラクタ2が呼ばれました");
         m_x = x;
         m_y = y;
   }
 
    // 文字列に変換する
    public double clacDistance(MyPoint pnt ){
       // Matchクラスは数字関係を担うクラス
       // Match.pow累乗を計算するメソッド
       // Match.sqrtはルートを計算するメソッド
       return Math.sqrt(Math.pow((m_x – pnt.m_x),2) + Math.pow((m_y – pnt.m_y), 2));
   }
}
 

Test.java

import  java.io.*;

    public static void main(String args[ ] ) {
    int n;
         MyPoint pnt1 = new MyPoint();
         MyPoint pnt2 = new MyPoint(50, -20);

//     pnt1.m_x = 10;
//     pnt1.m_y = 20;

//     pnt2.m_x = 50;
//     pnt2.m_y = -20;

         System.out.println("pnt1 " + pnt1.toString());
         System.out.println("pnt2 " + pnt2.toString());
         System.out.println("距離:" + pnt1.calcDistance(pnt2));
   }
}
 

C:\java>javac Test.java

C:\java>javac Test
コンストラクタ1が呼ばれました
コンストラクタ2が呼ばれました

pnt1 X:10.0   Y:20.0
pnt2 X:50.0   Y:-20.0
距離:56.5685424923804

C:\java>

 

5.演算子

5章 演算子
1.演算子の種類
 ●演算子
   ○算術演算子
    ・四則演算子         +,-,*,/,%
    ・符号変換           -
   ○比較演算子
    ・大小比較           <,>,<=,>=
    ・等価、非等価         ==,!=
   ○論理演算子
    ・論理否定           !
    ・論理積            &&
    ・論理和            ∥
   ○インクリメント演算子     ++
   ○デクリメント演算子      –
   ○ビット演算子
    ・論理積            &
    ・論理和            |
    ・排他的論理和        ^
    ・論理否定(1の補数)    ~
   ○シフト演算子
    ・左シフト            <<
    ・右シフト            >>
   ○代入演算子
    ・代入              =
    ・四則演算           +=,-=,*=,/=,%=
    ・シフト演算           <<=,>>=,>>>=
    ・ビット演算           &=,|=,^=
   ○cast演算子           (型) 例えば、(int),(char)など
   ○順次演算子            ,
   ○条件演算子(三項演算子)   ? : 
   ○instanceof演算子       instanceof
 
2.インクリメント演算子、デクリメント演算子
   int i;
       for( i = 0 ; i < 10 ; i = i +1 )
             System.out.println(i);
上記の例のように、今まで変数を1増やす場合「 i = i + 1 」と書いてきた。しかし「変数を1増やす」という作業は頻繁に行われるため、特別の演算子が用意されている。上の例は、その演算子を使用すると下記のようになる。
   int i;
       for( i = 0 ; i < 10 ; i++ )
             System.out.println(i);
この「++」がインクリメント演算子である。逆に「変数の値を1減らす」演算子はデクリメント演算子といい「――」である。
この演算子は「i++」のように変数の後に書くことも、「++i」のように変数の前に書くこともできる。しかし「j=i++」のように式の中に組み込むと、変数の前に書いた場合と、変数の後に書いた場合の動作が異なる。変数の前に書いた場合は、式を評価する前にインクリメントする。逆に変数の後に書いた場合は、式を評価した後にインクリメントする。
import java.io.*;

    public static void main( String args[ ] ){
        int a,b;
        a = 3;
        b = ++a;

        System.out.println("a=" + a + " b=" + b);
    }
}

import java.io.*;

class Test{
     public static void main(String args[ ] ){
        int a, b;
        a = 3;
        b = a++;

        System.out.println("a=" + a + " b=" + b);
    }
}

D:\java>javac Test.java

D:\java>java Test

a=4 b=4

D:\java>

D:\java>javac Test.java

D:\java>java Test

a=4 b=4

D:\java>

 一つの式の中に同じ変数に対するインクリメント演算子が複数あってはならないということである。もちろんデクリメント演算子も同じ注意がある。
例えば、「b=a++ +a++」というのはダメ。予期しない結果になることが多く、バグを招き兼ねない。「printf("%d %d\n",++a,++a);」というのもダメ。
import java.io.*;

class Test{
    public static void main(String args[ ] ){
       int a, b;
       a = 0;
       b = ++a + ++a;

        System.out.println("a=" + a + " b=" + b);
    }
}

import java.io.*;

class Test{
     public static void main(String args[ ] ){
        int a, b;
        a = 0;
        b = a++ + a++;

        System.out.println("a=" + a + " b=" + b);
    }
}

D:\java>javac Test.java

D:\java>java Test

a=2 b=3

D:\java>

D:\java>javac Test.java

D:\java>java Test

a=2 b=1

D:\java>

 
3.複合代入演算子
 複合代入演算子とは、演算と代入を一つの演算子にまとめたものである。インクリメント演算子やデクリメント演算子も複合代入演算子の一種ということができる。
複合代入演算子 使用例 意味 普通の演算子
+= a+=b; aにbを加えた値をaに代入する a-a+b
-= a-=b; aからbを引いた値をaに代入する a=a-b
*= a*-b; aとbを乗じた値をaに代入する a=a*b
/= a/=b; a÷bをaに代入する a=a/b
%= a%=b; a÷bの余りをaに代入する a=a%b
>>>= a>>>=b; aをbビット右にシフトした値をaに代入する(符号なし整数、ビット列) a-a>>b
>>= a>>=b; aをbビット右にシフトした値をaに代入する(符号付き整数) a=a>>b
<<= a<<=b; aをbビット左にシフトした値をaに代入する a=a<<b
&= a&=b; aとbの論理積をaに代入する a=a&b
|= a|=b; aとbの論理和をaに代入する a=a|b
^= a^=b; aとbの排他的論理和をaに代入する a=a^b
         
4.ビット演算子
演算子 意味 使用法
& ビット積(AND) a=b&c; bとcの各ビット毎の論理積をとり、その結果をaに代入する
| ビット和(OR) a=b|c; bとcの各ビット毎の論理和をとり、その結果をaに代入する
^ ビット排他的論理和(XOR) a=b^c; bとcの各ビット毎の排他的論理和をとり、その結果をaに代入する
~ ビット否定(NOT) a=~b; bの各ビットを反転しその結果をaに代入する
AND      
b c 結果(a)  
0 0 0  
0 1 0  
1 0 0  
1 1 1  
AND演算はビットを0でマスクするのに使用する。マスクとはあるビットを0もしくは1に変えること。例えば、奇数か偶数かを調べるためには、最下位を残して0でマスクする。その値が1なら奇数、0なら偶数ということになる。
import java.io.*;

class Test{
     public static void main(String args[ ]) {
         int i = readNumber( );

         if ( i = & 1 == 1 ) {
              System.out.pringtln("奇数です");
    }
         else{
              System.out.println("偶数です");
    }
  }

    // キーボードから数字を入力するメソッド
  public static int readNumber( ){
        byte b[ ] = byte [100];

        try{
              System.out.println.in.read(b);
            return Integer.parseInt((new String( (b).trim( ) ).trim( ) );
        }catch(Exception e) {
                return 0;  
          }
     }
}

OR      
b c 結果(a)  
0 0 0  
0 1 1  
1 0 1  
1 1 1  
OR演算はビットを1でマスクするのに使用する。
XOR      
b c 結果(a)  
0 0 0  
0 1 1  
1 0 1  
1 1 0  
XOR演算はビットを必要な部分だけ反転させるのに使用する。
NOT      
b 結果(a)    
0 1    
1 0    
NOT演算は1の補数を表す。1の補数とはすべてのビットを反転する。
 
5.シフト演算子
 シフト演算とは論理演算と同じようにビットを操作する命令である。具体的には、2進数の各ビットをずらすことである。右にずらすか左にずらすか、符号なしか、符号付きかによって3種類の演算子が用意されている。
演算子  意味 使用法
<< 左にシフト a = b << c bを左にcビットずらす
>>  右にシフト a = b >> c bを右にcビットずらす(符号付き)
>>> 右にシフト a = b >> c bを右にcビットずらす(符号なし)
0000 0001 = 1        1111 1111 = -1
   ↓                         ↓
0000 0010 = 2        1111 1110 = -2
       ↓            ↓
0000 0100 = 4           1111 1100 = -4

      整数          負数
このように左にずらすと、その値は2倍になる。
 
6.キャスト演算子
 キャストとは「型変換」のこと。下の例のように代入元と代入先の変数の型が異なる場合、型変換を行う必要がある。一般に「byte」型から「int」型への変換など、小さい入れ物から大きな入れ物へは自動的にキャストされる。これを「暗黙の型変換」という。
   int    i = 100;
   byte b = 100;

  i = b;            // 暗黙の型変換

 
 逆に大きな入れ物から小さな入れ物へのキャストは、データが失われる可能性があるため、暗黙の型変換は行われずえにエラーが出てしまう。このような場合、コンパイラに「データが失われるかもしれないのは、わかっているよ」と教えなければならない。これを「明示的な型変換」という。
   int    i = 100;
   byte b = 100;

  b = i;            // エラー
  b = (byte)i;  // 明示的なキャスト

 
また、整数と小数(実数)の関係も場合によっては明示的な型変換が必要になる。下の左の例では、明示的な型変換はしていない。右の例では明示的な型変換をしている。キャストしなければ割り算の結果は整数になってしまう。
import java.io.*;

class Test{
    public static void main(String args[ ] ){
       int a = 300;
       float f = a / 7;
 
       System.out.println( f );
    }
}

import java.io.*;

class Test{
     public static void main(String args[ ] ){
        int a = 300;
        float f = (float)a /7;

        System.out.println( f );
    }
}

D:\java>javac Test.java

D:\java>java Test
42.0

D:\java>

D:\java>javac Test.java

D:\java>java Test
42.857143

D:\java>

 
7.順次演算子
 カンマで区切られたいくつかの式を左から順に実行する。このカンマのことを順次演算子という。
import java.io.*;

class Test{
      public static void main(String args[ ]){
      int i;
      int j;

      for ( i = 0, j = 10 ; i < 10 ; i++, j– )
            System.out.println(i*j);
      }
}

 

C:\java>javac Test.java

C:\java>java Test
0
9
16
21
24
25
24
21
16
9

C:\java>java

 

6.オブジェクト指向とは

 

6章 オブジェクト指向とは
1.オブジェクト指向とクラス
 「オブジェクト指向」では、処理は二の次である。一番に考えるのはオブジェクトである。オブジェクトというのは「モノ」であり、実際に存在するもののことをいう。クラスとオブジェクトは等価。オブジェクトには決められた仕事や処理が決まっている。これがクラスで定義するメンバーメソッドである。
例)色鉛筆は実際に存在するものである。したがって、オブジェクトつまり「色鉛筆クラス」になる。(色鉛筆構造体)色鉛筆には様々な色があるメンバー変数として「色」「芯の長さ」を決める。
class Color{
    
int R;
    
int G;
    
int B;
}

class ColorPen{
    Color color;
    
int  length;
}

色鉛筆は、絵を描くことができる。メンバーメソッドを定義する。 
class Color{
    
int R;
    
int G;
    
int B;
}

class ColorPen{
    Color   color;
    
int    length;

     void draw( ){
       :
       :
    }
}

 オブジェクト指向では、このように「モノ」と「それを扱う関数(メソッド)」が一緒にクラスとして定義されているので、「色鉛筆で食べ物を食べる」というミスは文法的に書けないことになっている。

 整理すると「オブジェクト指向」では「モノ(データ、デバイスなど)を第1優先に考える」という分析・設計・実装方法である。

 
2.継承
 色鉛筆は鉛筆の一種であると考えられる。鉛筆でも絵を描くことができるが、色は黒一色である。色鉛筆では様々な色で絵を描くことができる。
class Color{
    
int R;
    
int G;
    
int B;
}

class Pencil{
    
int    length;

     // 黒で絵をかく
     void draw( ){
       :
       :
    }
}

class ColorPen extends Pencil{
       Color color;

// int  length; スーパークラスで定義されているので、ここでは定義しない

     // colorで絵をかく
    void draw( ){
       :
       :
    }
}

 このように定義すると「鉛筆クラスは鉛筆クラスの一種」という意味になる。このように、既存のクラスの一種であるクラスを定義することを「継承」という。色鉛筆クラスには「長さ」定義されていないが、鉛筆クラスで定義されているの
で、色鉛筆クラスにも「長さ」が定義されていることになる。

 この場合、色鉛筆クラスからみて鉛筆クラスを「スーパークラス」、「基底クラス」あるいは「親クラス」といい、鉛筆クラスから見て色鉛筆クラスを「サブクラス」あるいは「子クラス」という。

 上のクラス図は、基底クラスとして「哺乳類クラス」、その子クラスとして「犬クラス」「猫クラス」そして「象クラス」を定義している。さらに「猫クラス」の子クラスとして「三毛猫クラス」「シャム猫クラス」を定義している。
 しかし、「犬クラス」の子クラスとして「盲導犬クラス」を定義することはあまりよくない。なぜならこの場合、動物の種類として継承しているため、そこに「警察犬」「盲導犬」といった機能で継承することになるからである。
 Javaでは、機能で継承させたい場合には、継承ではなくインターフェースという機能を使う。詳細は後述。

 
3.カプセル化
 クラスのメンバー変数、メンバーメソッドは誰が触っても良いものと、触るクラスを限定したい場合がある。誰が触っても良いものを「public」、自分と同じパッケージ内のクラスからもしくはサブクラスからしか触れられない物を「protected」、そして自分自身のクラスからしかさわれない物を「private」という。
 すべてのメンバー変数、メンバーメソッドをpublicにして、プログラマーたちが決め事としてさわらないようにと決めても良いが、中には決め事を守らないプログラマーもいるため、public、protected、privateを決めておけば文法的にコンパイラでチェックしてくれる。
 何故そんなことをするのだろう。これは、作ったプログラムの一部を部品化しやすいようにするためである。オブジェクト指向では、作ったプログラムを部品化しておき、別のプログラムを作る際に再利用できるようにする。例えばインターネットブラウザのプログラムを作った場合、ネットワーク関係の部品は、次に作るメーラーに再利用ができる。このように部品化したクラスやパッケージは使い方さえ知っていればそのソースコードを知らない人でも使うことができる。ところが、内部(ソースコード)を知らないプログラマーが出てくるとバグの原因になる。そこで、protectedであれば勝手なことはされなくて済む。
 基本的にメンバー変数はすべてprivateかprotectedにする。そして、参照するためのメソッドと代入するメソッドを用意する。このようなメソッドをアクセサメソッドと呼ぶ。なぜ、こんな面倒なことをするのか、理由は3つある。

 1、メンバー変数の型を「int」から「double」に変更しようとした場合、参照する場合と代入する場合の2つに影響してくる。publicな変数だとプログラムすべてに渡ってこの変数を参照する場所と代入する場所を探すのは面倒な作業である。それに対しメソッドを探すのは比較的容易だからである。
2、メンバー変数は他のクラスから参照しても良いが値を変更するのは×という変数があったとする。この場合、参照するメソッドはpublicに、代入するメソッドはportectedかprivateにすればよい。
3、マルチスレッドプログラムにした場合、複数のスレッドが同じ変数にアクセスする場合がある。例えば、あるスレッドがリストをソース中に、別のスレッドがそのリストを参照した場合、矛盾が生じてしまう。このような場合は「セマフォ」というリストを参照する権利を与え権利があるスレッドだけがリストを参照したりさわったりできるうようにする。あらかじめ参照するためのメソッドを用意しておけばセマフォの処理を組み込む場合もそのメソッドをちょっと直すだけなので簡単である。
 このように変数やメソッドにアクセスできるクラスを制御することで、あるクラスの部品のようにすることができる。部品のようになったクラスは別のプログラムを作るときにも利用できる。例えば、Javaでは標準でGUIや通信mその他さまざまなクラスがたくさん用意されており、通信をしたいと思えばそのためのクラスをファイル操作したければそのためのクラスをプログラマーが使用するだけだから、プログラムが簡単に書ける。また、携帯電話用のソフトを書きたければ携帯電話会社が公開しているクラス群が利用できる。
 また、通信をするためのクラスを利用しようと思った場合、通常その中身がどんなプログラムになっているのか、プログラマーは知る必要がない。つまり、通信をするためのクラスの中身はブラックボックスということになる。
 このように、中身を知らなくても部品のように使えるように設計することを「カブセル化」という。
 
4.ポリモーフィズム
 ポリモーフィズムとは、ギリシャ語で「多数の形態」という意味である。プログラムで「ポリモーフィズム」というと同じメソッドを定義することをいう。
 例に例えると「鉛筆クラス」の「絵を描くメソッド」と「色鉛筆クラス」の「絵を描くメソッド」は、若干機能がことなるが「絵を描くメソッド」というメソッドの名前は同じである。
 例えばint型の絶対値を求めるメソッドを「abs」と名づけると小数も扱えるようなメソッドを追加しようとした場合、「abs_float」と名前を変えなくてはならなかった。Javaではポリモーフィズムによって、引数が異なれば同じ名前のメソッドを複数定義することもできる。このように同じ名前のメソッド(またはコンストラクタ)を作ることを、メソッドをオーバーロードするという。