2014年12月26日金曜日

PHPでDBアクセス

PDO(PHP Data Objects)

データーベース抽象化レイヤ(DBの違いを気にせずに使える)


エラー通知

以下の記述をしておくと、SQLの構文エラーを通知してくれる

$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

※$dbはPDOオブジェクト



DBを開く

new PDO(データベース接続文字列, ユーザー名, パスワード)



データベース接続文字列

mysql:host=ホスト名; dbname=データベース名; charset=文字コード



DBを閉じる

PDOオブジェクトを破棄する(NULLにする)
※こうしなくてもスクリプトが終了すれば自動でDB接続が切断される



例)ローカルDBのデータベース名php10を文字コードutf8で開く

//開く
$db = new PDO('mysql:host=localhost;dbname=php10;charset=utf8', 'phpusr', 'phppass');

//閉じる
$db = NULL;




例外処理

try {
  例外が発生する可能性のあるコード
} catch (例外の種類 変数名) {
  例外発生時の処理
}


例)
try {
  $db = new PDO('mysql:host=localhost;dbname=php10;charset=utf8', 'phpusr', 'phppass');
} catch (PDOException $e) {
  die('エラーメッセージ:' . $e->getMessage());
}



SQLの実行

以下の手順が必要

・SQLの準備
・SQLにパラメータをセット
・SQL実行



SQLの準備

PDOオブジェクト->prepare(SQL文);

※PDOStatementオブジェクトが返ってくる




SQLにパラメータをセット

SQL文の中にプレイスホルダ(:名前)を使った場合、そこに値をセットする

※プレイスホルダを使わないとセキュリティ上の問題(SQLインジェクション)がある

PDOStatementオブジェクト->bindValue(パラメータ名, 値);


※セットする値が数値(int)の場合は第三引数に「PDO::PARAM_INT」を付けた方がよい

例)
$stt->bindValue(':age',$arr['age'],PDO::PARAM_INT);




SQL実行

PDOStatementオブジェクト->execute();


例)INSERTを実行

//DB接続
$db = new PDO('mysql:host=localhost;dbname=php10;charset=utf8', 'phpusr', 'phppass');

//SQLの準備
$stt = $db->prepare('INSERT INTO schedule(title) VALUES(:title)');

//SQLにパラメータをセット
$stt->bindValue(':title', $_POST['title']);

//SQL実行
$stt->execute();

//DB切断
$db = NULL;




DBからレコードを取得


SELECT文をexecuteメソッドを実行するとその結果がPDOStatementに保存される




取得したレコードの表示

1行ずつフェッチ(読み込み)をする

PDOStatementオブジェクト->fetch();




フェッチの形式を変更

fetchメソッドは規定で配列または連想配列でアクセスできるように返してくれる

変更したい場合はfetchメソッドに以下の引数を渡す

PDO::FETCH_NUM…配列($row[1])
PDO::FETCH_ASSOC…連想配列($row['title'])
PDO::FETCH_BOTH…配列または連想配列[規定]($row[1] or $row['title'])
PDO::FETCH_OBJ…オブジェクト($row->title)



例)

$db = new PDO('mysql:host=localhost;dbname=php10;charset=utf8', 'phpusr', 'phppass');
$stt = $db->prepare('SELECT title FROM schedule');
$stt->execute();

//全てのレコードをフェッチ
while ($row = $stt->fetch()) {
  //タイトル列を表示
  print($row['title']);
}
$db = NULL;




文字列→日付変換

strtotime(日付時刻文字列);

日付の文字列がUnixタイムスタンプ(1970/1/1 00:00:00から経過した秒数)に変換される

例)
$ts = strtotime('2014/12/26');




日付をフォーマット

フォーマットした日付を文字列で取得

date(フォーマット文字列 [, Unixタイムスタンプ]);

※タイムスタンプは省略可能、その場合は現在日時・時刻となる

例)現在日時をYYYY/MM/DDの形で取得
$str = date('Y/m/d');




タイムスタンプ取得

現在日時・時刻のUnixタイムスタンプを取得

time()

例)
$ts = time;




任意のタイムスタンプ取得

任意の日付・時刻のUnixタイムスタンプを取得

mktime(時, 分, 秒, 月, 日, 年);


例) 2015/1/6 9:18:45秒のタイムスタンプを取得
$ts = mktime(9, 18, 45, 1, 6, 2015);





日付書式

Y4桁の年。例:2014
y2桁の年。例:14
L閏年。1:閏年 0:閏年ではない
mゼロ詰めの月。01~12
nゼロなしの月。1~12
Fフルスペルの月。例:January
M3文字形式の月。例:Jan
t月の日数。28~31
dゼロ詰めの日。01~31
jゼロなしの日。1~31
z年間の通算日。0~365
g12時間単位の時(ゼロなし)。1~12
G24時間単位の時(ゼロなし)。0~23
h12時間単位の時。01~12
H24時間単位の時。00~23
i分。00~59
s秒。00~59
a午前:am 午後:pm
A午前:AM 午後:PM
D3文字形式の曜日。例:Mon
lフルスペルの曜日。例:Monday
NISO-8601形式の曜日。1(月)~7(日)
w曜日。0(日)~6(土)



isset,empty,is_null
if($var)issetemptyis_null
$var=1TRUETRUEFALSEFALSE
$var="";FALSETRUETRUEFALSE
$var="0";FALSETRUETRUEFALSE
$var=0;FALSETRUETRUEFALSE
$var=NULL;FALSEFALSETRUETRUE
$varFALSEFALSETRUETRUE
$var=array()FALSETRUETRUEFALSE
$var=array(1)TRUETRUEFALSEFALSE

リダイレクト

以下のように書けば強制的に別のページに移動する

header('Location: リダイレクト先のURL');

※headerの前に出力(echoなど)をしてはいけない


例) yahooへリダイレクトさせる
header('Location: http://yahoo.co.jp/');




ファイルを開く

fopen(ファイルのパス, オープンモード)

※戻り値としてファイルハンドルを返す


オープンモード

rb…読み取り専用
wb…書き込み専用
w+b…読み書き
ab…追記書き込み
a+b…読み書き(追記)




ファイルを閉じる

fclose(ファイルハンドル);



ファイルに書き込む

fputs(ファイルハンドル, 書き込む文字列);

※fwriteでもOK



ファイルのロック

flock(ファイルハンドル, ロックの種類)

LOCK_SH…共有ロック(書き込み禁止)
LOCK_EX…排他ロック(読み書き禁止)
LOCK_UN…ロックの解除


例)
//ファイルをオープン
$file = fopen('guest.dat', 'ab');

//ファイルをロック
flock($file, LOCK_EX);

//ファイルに書き込み
fputs($file, 'abc');

//ロックを解除
flock($file, LOCK_UN);

//ファイルを閉じる
fclose($file);



@演算子

処理の実行に失敗した場合でも警告を発生させない

※その場合、エラーがあったかどうか分からないので「or die…」を付ける



CSVファイルの読み込み

fgetcsv(ファイルハンドル, 読み込む最大文字列, 区切り文字)

例)タブ区切りファイルの読み込み
$file = @fopen('guest.dat', 'rb') or die('ファイルが開けませんでした');
while ($row = fgetcsv($file, 1024, "\t")) {
  echo $row[0]));
}
fclose($file);


ファイルの読み込み

fgets(ファイルハンドル)



文字を分割

explode(区切り文字, 文字列)




トランザクション処理

DBへの複数回に渡る追加、更新をまとめて処理すること

※大量の件数の処理を行う場合はトランザクションを使ったほうが早い

※InnoDBではないと場合はトランザクションは使えない




トランザクション開始

beginTransaction



トランザクション完了

commit


トランザクションの巻き戻し

rollback


例)CSVファイルの中身を全件DBへ登録
try {
  $db = new PDO('mysql:host=localhost;dbname=php10;charset=utf8', 'phpusr', 'phppass');

  //トランザクション開始
  $db->beginTransaction();

  $file = @fopen('ejdic-hand-utf8.txt', 'rb') or die(' failure!');

  //CSVファイルの中身を全件登録
  while ($row = fgetcsv($file, 5000, ",")) {
    $stmt = $db->prepare('INSERT INTO ejdic (word, mean) VALUES(:word, :mean) ');
    $stmt->bindValue(':word', $row[0]);
    $stmt->bindValue(':mean', $row[1]);
    $stmt->execute();
  }

  //トランザクションを完了
  $db->commit();
} catch (PDOException $e) {

  //例外が発生した場合処理を巻き戻す
  $db->rollBack();
  echo $e->getMessage();
}

PHPクラス関係

クラス定義

class クラス名{
  public メンバ変数名;

  function メンバメソッド名(){
    // クラスの中で行う処理
  }
}


インスタンスの生成

new クラス名(引数1,・・・)



フィールドアクセス


$オブジェクト名->インスタンス変数名;



メソッドの呼び出し方

$オブジェクト名->メンバメソッド名();


//クラス定義
class A {
  public  $a;
  function printA() {
    print($this->a);
  }
}
//クラスAのインスタンスを生成
$temp = new A();

//フィールドに値をセット
$temp->a = 'abcd';

//メソッドを実行
$temp->printA();



コンストラクタ

class クラス名(){
  function __construct(変数, 変数, ...){
    // コンストラクタ内で行いたい処理
  }
}



クラス変数、クラスメソッド

class クラス名{
  public static メンバ変数名;

  static function メンバメソッド名(){
    // クラスの中で行う処理
  }
}


クラス変数、メソッドへのアクセス

$クラス名::クラス変数名

$クラス名::クラスメソッド名()



アクセス修飾子

public     クラス内、クラス外のどこからでもアクセス可能
private    同じクラス内からのみアクセス可能
protected  同じクラス、及び子クラスからアクセス可能



自分自身のインスタンス

$this

class A {
  private  $a;
  function printA() {
    print($this->a);
  }
}



自分自身のクラス

self

※クラス定義でしか使えない

self

class A {
  static $a = 'abc';
  function printa() {
    print(self::$a);
  }
}


スーパークラスにアクセス

//スーパークラスのコンストラクタを呼ぶ
parent::__constract();

//スーパークラスのファンクションfunc1を呼ぶ
parent::func1();



継承

サブクラス名 exteds スーパークラス名

class SmallDog extends Dog{
  ・・・
}

2014年12月25日木曜日

データベース(SQL)

MySQL

オープンソースのリレーショナルデータベース



リレーショナルデータベース(RDB)

データを表形式で表すデータベース



phpMyAdmin

MySQLの管理ツール



DBMS

DataBase Management Systemの略でデータベース管理システム



照合順序

DBでの文字の並び替え順、比較などを決めるものでDBの文字コードもこれで決まる



ユーザー

DBにアクセスする人を区別し、権限を与えて検索や更新を許可する



テーブル

データを保存する二次元の表のようなもの



カラム

列をカラム、またはフィールドという



レコード

行単位のデータをレコードという



データ型

数値

INT 整数
FLOAT,DOUBLE 小数


文字列

VARCHAR(len) 可変長文字列
text 文字列(文字数が多い場合)


日付

DATETIME 日付時刻
DATE 日付
TIME 時刻


バイナリ

BLOB


設定情報

デフォルト値、NULLを許可、A/I(オートインクリメント)、インデックス(索引)



主キー(プライマリーキー)

レコードを一意に識別する列、全てのテーブルに設定すべき



SQL


基本

・大文字、小文字を区別しない
・キーワードの区切りは空白
・文の末尾はセミコロン(;)
・識別子(列名など)をバッククォートで囲んでもよい


SELECT
  `sid`
FROM
  `schedule`




利用できるデータベースの表示

SHOW DATABASES;



INSERT

テーブルに新規データを挿入する


INSERT INTO テーブル名 (
  フィールド名1,
  フィールド名2,
  ・・・
)
VALUES (
  値1,
  値2,
  ・・・
);


例)タイトル「打ち合わせ」日付「2014/12/25」のレコードを挿入

INSERT INTO schedule (
  title,
  sdate
)
VALUES (
  '打ち合わせ',
  '2014-12-25'
);


複数の行を一度に挿入

VALUESの後の値をカンマ区切りで複数指定する

INSERT INTO schedule
(title, sdate)
VALUES
('打ち合わせ', '2014-12-25'),
('会議', '2014-12-27');



自動でセットされる値

INSERTで値をセットしなかった値には以下の優先順で値がセットされる

1.自動連番(オートインクリメント)
2.デフォルト値
3.NULL



文字列、日付

文字列、日付は値をシングルクォート(')で囲まなければならない

INSERT INTO schedule (sid, title, sdate)
VALUES (7, 'WINGS忘年会', '2012-11-23');



UPDATE

データを更新する


UPDATE テーブル名
SET
  フィールド名1 = 値1,
  フィールド名2 = 値2,
  ・・・
WHERE
  条件式


例)sidが「2」のレコードのタイトル「面接」を時刻を「18:00」に更新

UPDATE schedule
SET
  title = '面接',
  stime = '18:00:00'
WHERE
  sid = 2;


※条件式は省略可だが、その場合は全てのレコードが更新される



DELETE

データを削除する

DELETE
FROM
  テーブル名
WHERE
  条件式

※条件式は省略可だが、その場合は全てのレコードが削除される


例)sidが「3」のレコードを削除

DELETE
FROM
  schedule
WHERE
  sid = 3;



SELECT

データを取得する


SELECT
  フィールド名1,
  フィールド名2,
FROM
  テーブル名
WHERE
  条件式
ORDER BY
  ソート式

例)sidが「2~8」のレコードを取得(日付の降順で「タイトル」「日付」のみ)

SELECT
  title,
  sdate
FROM
  schedule
WHERE
  sid BETWEEN 2 AND 8
ORDER BY
  sdate DESC


アスタリスク

フィールド名を「*」だけにすると全ての列が表示される


WHERE句

取り出すデータを絞り込む


比較演算子

BETWEEN X AND Y …X~Yの範囲
IN (X, Y, Z) …X,Y,Zのいずれか
IS NULL …NULLであるか
LIKE 文字列  …含まれるか(%をワイルドカードとして使う)
  例)LIKE '%山%' …山が含まれているかどうか



ORDER BY句

列名と並び順を指定して並び替える


並び順

ASC…昇順
DESC…降順
※並び順を省略した場合はASC(昇順)



GROUP BY句

特定の列でレコードをまとめて集計などをするためにグループ化し、集計関数と共に使う


集計関数

AVG…平均
COUNT…件数
MAX…最大値
MIN…最小値
SUM…合計


例)同じ日付の件数を集計する

SELECT
  sdate,
  COUNT(*)
FROM
  schedule
GROUP BY
  sdate



句の並び順

WHERE → GROUP BY → ORDER BY



列別名

SELECT句の列名に別の名前を付けることができる

SELECT
  列名 as 列別名
FROM
  ・・・


例)memoをdescriptionという別名で表示する

SELECT
  memo as description
FROM
  schedule



LIMIT句

取得したレコードの件数を制御する

※LIMIT句が無いDBもあるので注意(Oracleは無い)


LIMIT [開始位置,] 取得する行数

※開始位置は省略可


例)データを5件取得
SELECT  *
  FROM  schedule
 LIMIT  5

例)データを3件目から10件取得
SELECT  *
  FROM  schedule
 LIMIT  3, 10



HAVING句

グループ化したデータを絞り込む

※WHERE句はグループ化したデータには使えない為


例)平均年齢が30以上のデータを取得
SELECT  blood, AVG(age)
FROM  persons
GROUP BY  blood
HAVING  AVG(age) >= 30


2014年12月12日金曜日

Java継承、その他(12章~終わり)

継承

クラスのフィールドやメソッドを継承して、新しいクラスを派生させることができる

派生されたサブクラスは継承元のフィールド、メソッドを全て引き継ぎ、新しく追加することもできる

※Javaではクラスの継承時に直属の親クラスは1つしか持てない(多重継承不可)

class 派生クラス名 extents 継承元クラス名 {
     ・・・
}


//継承元クラス
class A {
  int a;
  void printA() {
    System.out.println(a);
  }
}

//Aを派生させたクラス
class B extends A {
  //新たなフィールドを追加
  int b;

  //新たなメソッドを追加
  void printB() {
    System.out.println(b);
  }

  void printAB() {
    //親クラスのメソッドを呼ぶことも可能
    printA();
    System.out.println(b);
  }

  void printAPlusB() {
    //親クラスのフィールドにもアクセス可能
    System.out.println(a + b);
  }
}

class Test {
  public static void main(String[] args) {
 
    //クラスAを生成
    A clsA = new A();
    clsA.a = 111;
 
    //printAメソッドを実行
    //「111」と表示
    clsA.printA();
 
 
    //Aを継承したクラスBを生成
    B clsB = new B();
 
    //親クラスのフィールドaを設定
    clsB.a = 222;
 
    //親クラスのprintAメソッドを実行
    //「222」と表示
    clsB.printA();
 
    //新たに定義したフィールドに設定
    clsB.b = 333;
 
    //新たに定義したメソッドを実行
    //「333」と表示
    clsB.printB();
 
    //「222」「333」と表示
    clsB.printAB();
 
    //「555」
    clsB.printAPlusB();
  }
}


呼称

派生元のクラス…親クラス、基底クラス、スーパークラス
派生したクラス…子クラス、派生クラス、サブクラス


メンバ

フィールドとメソッドをまとめて「メンバ」と呼ぶ


派生とコンストラクタ

クラスの派生において、コンストラクタ静的初期化子インスタンス初期化子は継承されない



「super(…)」によってスーパークラスのコンストラクタを呼び出せる
※ただし、コンストラクタの先頭のみで、「this(…)」とどちらかしか呼べない


サブクラス内のコンストラクタ内では、「super()」が自動的に挿入される


自動で作られたデフォルトコンストラクタには「super();」が追加されている
コンストラクタを定義しないクラスは、そのスーパークラスが引数なしのコンストラクタを持っていなければならない


「super.」によって、スーパークラスのメンバにアクセスできる
→スーパークラスと同じ名前のフィールドやメソッドを定義可能で、それらに名前のみでアクセスするとサブクラスの方となる



インスタンス生成時の処理の流れ

全てのフィールドをデフォルト値で初期化
上位クラスの初期化子
上位クラスの引数なしのコンストラクタ
下位クラスの初期化子
下位クラスのコンストラクタ



is-Aの関係

継承関係は「サブクラスは一種のスーパークラスである」と成り立つ、これを「is-Aの関係」という





継承の特徴

継承ではメンバを新たに追加することはできるが削除はできない

メンバの変更は「オーバーライド」と「隠蔽」によって行う




オーバーライド

スーパークラスのメソッドと同じシグネチャのメソッドをサブクラスで再定義して上書きすること



隠蔽

スーパークラスのフィールドと同じ名前のフィールド(型は違ってもよい)をサブクラスで定義し覆い隠してしまうこと
→隠蔽されたフィールドは「super.」でアクセスできる

※いろいろと紛らわしいので隠蔽を使うべきではない


アノテーション

オーバーライド時に「@Override」と記載しておくと、コーディングミスが防げる
→メソッド名や引数などが間違っている場合に警告が出るようになる

※「@Deplicate」アノテーションで推奨外と定義できる


class A {
  int a = 1;
  void printA() {
    System.out.println("sp.a=" + a);
  }
}

class B extends A {
  //フィールドを隠蔽
  int a = 2;

  //オーバーライド
  @Override
  void printA() {
    System.out.println("sub.a=" + a);
  }

  void printSP() {
    //スーパークラスのフィールドを表示
    System.out.println("super.a=" + super.a);
 
    //スーパークラスのメソッドを実行
    super.printA();
  }
}

class Test {
  public static void main(String[] args) {
 
    //クラスAを生成
    A clsA = new A();
 
    //printAメソッドを実行
    //「sp.a=1」と表示
    clsA.printA();
 
 
    //Aを継承したクラスBを生成
    B clsB = new B();
 
    //オーバーライドしたメソッドを実行
    //「sub.a=2」と表示(隠蔽した値を表示)
    clsB.printA();
 
    //スーパークラスの値を表示するメソッド
    //「super.a=1」「sp.a=1」と表示
    clsB.printSP();
  }
}


ポリモーフィズム

同じ名前のメソッドで異なる挙動を実現することが可能でやり方は以下のとおりである

継承関係にある型(クラス)同士であれば、上位の型を持つ変数に下位の型を持つインスタンスを代入できる

その場合、呼び出せるメンバは変数の型にあるもののみだが、実際に呼び出すのはインスタンスのメンバである(動的結合


class Base {
  void print() {
    System.out.println("base");
  }
}

class Sub1 extends Base {
  //オーバーライド
  void print() {
    System.out.println("sub1");
  }

  void printSub() {
    System.out.println("111");
  }
}

class Sub2 extends Base {
  //オーバーライド
  void print() {
    System.out.println("sub2");
  }

  void printSub() {
    System.out.println("222");
  }
}

class Test {
  public static void main(String[] args) {
 
    //Sub1のインスタンスを生成
    Sub1 s1 = new Sub1();
 
    //Sub2のインスタンスを生成
    Sub2 s2 = new Sub2();
 
    //Baseクラスの配列
    Base b[] = new Base[2];
 
    //Base配列にSub1のインスタンスを入れる
    b[0] = s1;
 
    //Base配列にSub2のインスタンスを入れる
    b[1] = s2;
 
    for (Base base : b) {
      //ポリモーフィズム
      //「sub1」「sub2」と表示される
      base.print();
   
      //Baseに無いメソッドは呼べない
      //コンパイルエラーとなる!!
      //base.printSub();
    }
 
  }
}



finalクラス

finalを頭に付けて宣言されたクラスの派生クラスを定義することはできない(継承できない)


finalメソッド

finalを頭に付けたメソッドはサブクラスでオーバーライドすることはできない



オーバーライドとメソッドのアクセス性

メソッドをオーバーライドする場合は上書きするメソッドのアクセス修飾子よりも、privateに近い方のアクセス修飾子をつけることはできない


class Base {
  //protectedアクセスのメソッド
  protected void print() {
    System.out.println("Print");
  }
}

class Sub1 extends Base {
  //同じアクセス修飾子はOK
  protected void print() {
    System.out.println("Print1");
  }
}

class Sub2 extends Base {
  //publicに近づけるのはOK
  public void print() {
    System.out.println("Print2");
  }
}

class Sub3 extends Base {
  //privateに近づけるのは×
  private void print() {
    System.out.println("Print3");
  }
}

class Sub4 extends Base {
  //privateに近づけるのは×
  void print() {
    System.out.println("Print3");
  }
}


抽象メソッド

具体的な機能を持たない空のメソッドで「abstract メソッド名(引数);」と宣言する


抽象クラス

abstract クラス名 {
    ・・・・
}

抽象メソッドを含んだクラスで、これを継承するサブクラスは抽象メソッドをオーバーライドする必要がある
→全ての抽象メソッドを実装しない場合はサブクラスも抽象クラスになる

※抽象クラスは継承されることを前提としたものであり、そのインスタンスを直接生成することはできない

※抽象メソッドを持たないクラスでも抽象クラスにすることはできる

※スーパークラスの非抽象メソッドを抽象メソッドとしてオーバーライドできる


//抽象クラス
abstract class Base {
  //抽象メソッド
  abstract void print();
}

//抽象クラスを継承
class Sub1 extends Base {
  //抽象メソッドを実装(オーバーライド)
  void print() {
    System.out.println("sub1");
  }
}

class Test {
  public static void main(String[] args) {
 
    //Sub1のインスタンスを生成
    Sub1 s1 = new Sub1();
 
    //「sub1」と表示される
    s1.print();
 
    //抽象クラスのインスタンスは生成できない
    //Base b = new Base();
  }
}



アクセス修飾子

public

どこからでもアクセス可能



protected

同一パッケージ内と、違うパッケージの継承先(サブクラス)がアクセス可能



デフォルト(無し)

同一パッケージ内のみアクセス可能


private

自分のクラス内のみアクセス可能(他のクラスからはアクセスできない)



※メソッドのオーバーライドや抽象メソッドの実装時に、元のメソッドのアクセス修飾子よりも厳しいアクセス制限(privateより)にすることはできない


class Base {
  //protectedアクセスのメソッド
  protected void printA() {
    System.out.println("A");
  }
}

class Sub1 extends Base {
  //制限を緩める(publicより)のはOK
  public void printA() {
    System.out.println("S1");
  }
}

class Sub2 extends Base {
  //同じアクセス修飾子はOK
  protected void printA() {
    System.out.println("S2");
  }
}

class Sub3 extends Base {
  //コンパイルエラー
  //制限を厳しくする(privateより)のは×
  void printA() {
    System.out.println("S3");
  }
}

class Sub4 extends Base {
  //コンパイルエラー
  //制限を厳しくする(privateより)のは×
  private void printA() {
    System.out.println("S3");
  }
}



インターフェイス


全てのメソッドが抽象メソッドのクラスのようなもので、複数実装することができる
→単一継承しか認めない抽象クラスの使用の欠点を補うモノである


※インターフェイス内のメソッドは全てpublicかつabstractとなる
メソッドを実装する場合はpublicにする必要がある



インターフェイスで宣言されたフィールドは「public static final」となる
→つまり、フィールドには定数しか宣言できない


interface インターフェイス名 {
    ・・・・
}




インターフェイスの実装

class クラス名 implements インターフェイス名(, インターフェイス名) {
    ・・・・
}

※複数実装する場合はカンマで区切る


//インターフェイス
interface If1 {
  void print1();
}

//インターフェイス
interface If2 {
  void print2();
}

//インターフェイスを実装
class Imp1 implements If1 {

  //オーバーライドしなければならない!
  @Override
  public void print1() {
    System.out.println("Imp1");
  }
}

//インターフェイスを複数実装
class Imp2 implements If1, If2 {

  //オーバーライドしなければならない!
  @Override
  public void print1() {
    System.out.println("Imp2-1");
  }

  //オーバーライドしなければならない!
  @Override
  public void print2() {
    System.out.println("Imp2-2");
  }
}

class Test {
  public static void main(String[] args) {
 
    //インターフェイスのインスタンスは作れない
    //コンパイルエラー!
    //If1 if0 = new If1();
 
    //インターフェイス型に実装クラスを入れるのはOK
    If1 if1 = new Imp1();
 
    //「Imp1」と表示される
    if1.print1();
 
    //多重実装したクラスを生成
    Imp2 imp2 = new Imp2();
 
    //「Imp2-1」「Imp2-2」と表示される
    imp2.print1();
    imp2.print2();
 
    //If1型に入れることもできる
    If1 if1a = imp2;
 
    //「Imp2-1」と表示される
    if1a.print1();
 
    //print2は呼べない(コンパイルエラー)
    //if1a.print2();
 
    //If2型に入れることもできる
    If2 if2a = imp2;
 
    //「Imp2-2」と表示される
    if2a.print2();
 
    //print1は呼べない(コンパイルエラー)
    //if2a.print1();
  }
}


extentsとimplementsの両方がある場合、extendsを先に書かなければならない


//インターフェイス
interface If1 {
  void print1();
}

//親クラス
class Base {
  void print2() {
    System.out.println("Print2");
  }
}

//継承かつ実装
class Sub extends Base implements If1 {
  @Override
  public void print1() {
    System.out.println("Print1");
  }
}



インターフェイスの派生

インターフェイスもクラスと同様に継承により派生することができる
→クラスと同様に多重継承はできない

interface If1 {
  void print1();
}

//If1を継承
interface If2 extends If1 {
  void print2();
}

//If2を実装
class Imp implements If2 {
  //If1のメソッドを実装
  public void print1() {
    System.out.println("If1");
  }

  //If2のメソッドを実装
  public void print2() {
    System.out.println("If2");
  }
}


class Test {
  public static void main(String[] args) {
 
    //インターフェイスの実装クラスを生成
    If2 if2 = new Imp();
 
    //「If1」「If2」と表示
    if2.print1();
    if2.print2();
  }
}



抽象クラス or インターフェイス

どちらを使うか迷ったらインターフェイスだが、共通の処理がある場合は抽象クラス



Objectクラス

全てのクラスの親となるクラスで、明示的に派生していないクラスは自動的にObjectクラスのサブクラスとなる


Objectクラスのメソッド

public String toString() … オブジェクトを文字列で表現する

public boolean equals(Object obj) … 等しいかどうか

public int hashCode() … equalsメソッドを修正する場合、こちらも要修正




例外処理

例外とはアプリケーション実行時に発生するエラーのことで、それを対処する必要がある


実行時エラー

リソースの状態によって発生するエラーなどで、ソースコードのミスではない場合のエラー
→エラーでプログラムが終了してしまわない為にも例外処理が必要



try~catch

例外が投げられた(スローされた)場合、その例外を捕捉(キャッチ)して、例外毎に対応する処理を行う

※catchは複数書くこともできるし無くてもよい

※catchは上から順番に捕捉できるか判定され、キャッチできない場合は次のcatchを判定する

※catchに書かれた例外クラスのサブクラスも捕捉される


finally

tryブロックの後に例外が発生する・しないに関わらず必ず実行される処理を記載する
→tryブロックの中で利用したリソースの後始末などに利用する

※finallyは無くてもよい


try {

  例外が発生する可能性のあるコード

} catch (例外クラス1 変数1) {

  例外1が発生した場合の処理

} catch (例外クラス2 変数2) {

  例外2が発生した場合の処理

} finally {
  tryブロックの後に必ず実行したい処理
}




try {

  //例外が発生する可能性がある処理
  FileReader reader = new FileReader("./test.txt");

} catch (FileNotFoundException e) {

  //ファイルが存在しない場合の処理
  e.printStackTrace();
  ・・・・

} catch (IOException e) {

  //入出力エラーの場合の処理
  e.printStackTrace();
  ・・・・

} catch (Exception e) {

  //その他の例外全てをここで捕捉
  e.printStackTrace();
  ・・・・

} finally {

  //最後に行いたい処理
  ・・・・
}



例外クラスの型


Error … このクラスのサブクラスは致命的なエラーで例外処理もできない
Exception … このクラスのサブクラス(RuntimeException系以外)は例外処理が必須
RuntimeException … このクラスのサブクラスの例外処理は任意




アップキャスト

親クラスの型にサブクラスのインスタンスを変換すること
暗黙的(自動的)に変換される(明示的なキャストは不要)


ダウンキャスト

親クラスの型からサブクラスの型に変換すること
明示的なキャストが必要(暗黙的に変換されない)

※ダウンキャストは失敗する可能性があるので「instanceof」で確認する


インスタンス変数 instanceof ダウンキャストしたいクラス型



class Base {
  void print() {
    System.out.println("base");
  }
}

class Sub extends Base {
  void printSub() {
    System.out.println("sub1");
  }
}

class Test {
  public static void main(String[] args) {
    //親クラス型
    Base b;
 
    //サブクラス型
    Sub s;
 
 
    s = new Sub();
 
    //アップキャスト
    b = s;
 
    //サブクラス型に子クラスを入れるのは×
    //コンパイルエラー!!
    //s = new Base();
 
    //明示的にキャストしてもこれは×
    //実行時エラー(変換できない)
    //Base b2 = new Base();
    //Sub s2 = (Sub)b2;
 
 
    //ベース型にサブのインスタンスを入れる
    Base b3 = new Sub();
 
    //b3に入っているインスタンスがSub型かどうか?
    if (b3 instanceof Sub) {
      //b3のインスタンスがSub型ならば、
      //このダウンキャストはOK
      Sub s3 = (Sub)b3;
      s3.printSub();
    }
  }
}




データの種類

CSV(comma-separated values)

データをカンマで区切った形式でExcelで読み込める

赤,red,#ff0000



プログラムで取得する場合はsplitメソッドを使うとよい

String str = "赤,red,#ff0000";

//カンマごとに分割し配列に格納
String[] ary = str.split(",");

for (String s : ary) {
  System.out.println(s);
}



TSV(tab-separated values)

データをタブで区切った形式

赤[\t]red[\t]#ff0000


xml(Extensible Markup Language)

xmlというマークアップ言語で記載した形式

<person>
  <name>yamada</name>
  <email>ym@xxx.xx</mail>
</person>


json(JavaScript Object Notation)

JavaScriptのオブジェクト記法を利用した形式

{
  person:[
    name:yamada,
    email:ym:xxx.xx
  ]
}


Perse(パース)

データを解析してプログラムで使える形式に変換する処理



スクレイピング

元のデータから必要な部分を抽出すること



ファイル書き込み

FileWriter→BufferedWriter→PrintWriterと徐々に便利なクラスに拡張していく


PrintWriter writer = null;

try {
  //sample.txtというファイルを開く(無ければ生成)
  writer = new PrintWriter(new BufferedWriter(new FileWriter("sample.txt")));

  //追記の場合は第二引数を付ける
  //writer = new PrintWriter(new BufferedWriter(new FileWriter("sample.txt", true)));

  //書き込み
  writer.write("Hello");

  //printlnも使える
  writer.printf("abcd");

  //printfも使える
  writer.printf("%d\n",123);

} catch (IOException e) {

  e.printStackTrace();

} finally {

  if (writer != null) {
    //ファイルを閉じる
    writer.close();
  }

}



ファイルの読み込み

FileReader→BufferedReaderと徐々に便利なクラスに拡張していく


BufferedReader reader = null;

try {

  //ファイルを開く
  reader = new BufferedReader(new FileReader("sample.txt"));
  String line;

  //ファイルを一行づつ読み込み
  while ((line = reader.readLine()) != null) {
    //一行分を出力
    System.out.println(line);
  }
} catch (FileNotFoundException e) {

  e.printStackTrace();

} catch (IOException e) {

  e.printStackTrace();

} finally {
  if (reader != null) {
    try {
      //ファイルを閉じる
      reader.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}




文字

文字は整数値のコードで表され、JavaではUnicodeを採用しており、最初の128文字はASCIIコードと一致する



16進数

1~9abcdefで表し16で桁上がりする数値で頭に「0x」を付ける

0x32…10進数では50(16*3 + 2)
0xff…10進数では255(16*15 + 15)



文字リテラル

シングルクォート「'」で囲んだ文字で、char型の変数に入れられる



文字列とString


文字列リテラルはString型インスタンスへの参照であり、フィールドとしてchar型の配列を持っていてそこに1文字ずつ格納されている



文字列の比較

Stringクラスのインスタンスメソッドequalsを使用する

変数名.equals(比較する文字列);

s1 = "ABC";
s2 = "ABC";

if (s1.equals(s2)) {
  System.out.print("等しい");
} else {
  System.out.print("異なる");
}



文字列連結

StringBuilderクラスを使用すると効率的にメモリを使って連結できる

StringBuilder sb = new StringBuilder();
sb.append("abc");
sb.append("defg");

//「abcdefg」と表示される
System.out.println(sb);




コマンドライン引数


javaコマンドに引数を入力すると、mainメソッドのString[]型の引数として渡される


ソース

public class PrintArgs {
  public static void main(String[] args) {
    for (int i = 0; i < args.length; i++) {
      System.out.printf("args[%d]=%s\n", i, args[i]);
    }
  }
}


コマンドプロンプト

>java PrintArgs aaa bbb


実行結果

args[0]=aaa
args[1]=bbb



文字列から基本型への変換

基本型のラッパークラスのpase…メソッドで文字列から基本型への変換ができる


//文字列から数値に変換
int num = Integer.parseInt("5");

//文字列から浮動小数に変換
double d = Double.parseDouble("1.23");







スレッド(Thread)

複数の処理を並行して実行させるための機能


シングルスレッド

1つのプログラムで1つの処理だけを行う


マルチスレッド

1つのプログラムで複数の処理を同時に行う



スレッドの実装

Threadクラスを継承、Runnableインターフェイスを実装の2つの方法がある



Threadクラスを継承

1.Threadクラスをextends
2.runメソッドをオーバーライド
3.サブクラスのインスタンスを生成
4.startメソッドでスレッド開始(runが実行される)

public class ThreadBasic extends Thread {

  private String name;

  public ThreadBasic(String name) {
    this.name = name;
  }

  @Override
  public void run() {
    super.run();
 
    for (int i = 0; i < 50; i++) {
      System.out.printf("%s:%d,", name, i);
    }
  }

  public static void main(String[] args) {
 
    Thread t1 = new ThreadBasic("t1");
    Thread t2 = new ThreadBasic("t2");
    Thread t3 = new ThreadBasic("t3");
 
    t1.start();
    t2.start();
    t3.start();
 
  }
}


実行結果

t2:0,t3:0,t1:0,t1:1,t2:1,t3:1,t1:2,t2:2,t3:2,t2:3,t1…



Runnableインターフェイスを実装


1.Runnableインターフェイスをimplements
2.runメソッドを実装
3.実装クラスのインスタンスを引数にThreadクラスのインスタンスを生成
4.startメソッドでスレッド開始(runが実行される)

public class RunnableBasic implements Runnable {

  private String name;

  public RunnableBasic(String name) {
    this.name = name;
  }

  @Override
  public void run() {
    for (int i = 0; i < 50; i++) {
      System.out.printf("%s:%d,", name, i);
    }
  }

  public static void main(String[] args) {
 
    Thread t1 = new Thread(new RunnableBasic("t1"));
    Thread t2 = new Thread(new RunnableBasic("t2"));
    Thread t3 = new Thread(new RunnableBasic("t3"));
 
    t1.start();
    t2.start();
    t3.start();
  }
}


実行結果

t2:0,t3:0,t1:0,t1:1,t2:1,t3:1,t1:2,t2:2,t3:2,t2:3,t1…




匿名クラス(無名クラス)

名前を持たないクラスで以下のようなメリットがある


・関連するコードをシンプルに表現できる
・かたまりを把握しやすい
・式が許されている箇所であればどこにでも記述できる
・名前が無いので、名前空間を汚さない
※その性質上、定義したクラスを後から利用しないことがわかっている(=その場限りの)クラスを定義するのに利用する



匿名クラスを使ったスレッドの実装例

public class AnoymouseThread {

  public static void main(String[] args) {
 
    Thread t1 = new Thread() {
      @Override
      public void run() {
        for (int i = 0; i < 50; i++) {
          System.out.printf("%s:%d\n", "t1", i);
        }
      }
    };
 
    Thread t2 = new Thread(new Runnable() {
      @Override
      public void run() {
        for (int i = 0; i < 50; i++) {
          System.out.printf("%s:%d\n", "t2", i);
        }
      }
    });
 
    t1.start();
    t2.start();
  }
}


コレクション

配列よりもコレクションを優先して使うべきである
→配列よりも機能に乏しく、コレクションとの親和性も低いため



リスト(Listインターフェイス)

インデックスで管理、要素の重複OK

ArrayList:可変長配列クラス
LinkedList:リンク構造のリストクラス(要素の挿入・削除が高速)



セット(Setインターフェイス)

要素に順番なし、重複NG

HashSet:任意の順序で格納
LinkedHashSet:格納順序を保障
TreeSet:キーにより要素を自動的にソート



キュー(Queueインターフェイス)

待ち行列の機能を提供、先頭・末尾の要素だけを追加・削除できる

ArrayDeque:両端キュークラス
LinkedList:ListのLinkedListと同じ



マップ(Mapインターフェイス)

キーと値の組み合わせで管理、キーの重複はNG

HashMap:基本的なMap
TreeMap:キーにより要素を自動的にソートするMap



基本的な使い方

インターフェイス型に実装クラスのインスタンスを入れて使う

List<String> list = new ArrayList<String>();


インターフェイス型を用いる目的は、クラスへの依存を最小限にするため

ただし、Android開発においては以下の書き方の方がよい
→ポリモーフィズムはオーバーヘッドがあるため

ArrayList<String> list = new ArrayList<String>();



匿名クラスを使った初期化

List<String> list = new ArrayList<String>() {
  //インスタンス初期化子を定義
  {
    add("Java");
    add("C#");
    add("Ruby");
  }
};



配列→リスト変換


String[] str = {"Java", "C#", "Ruby"};

List<String> list1 = Arrays.asList(str);

//0番目の要素を更新
list1.set(0, "ジャバ");

//Listを表示
//[ジャバ, C#, Ruby]と表示される
System.out.println(list1);

//元の配列を表示
//[ジャバ, C#, Ruby]と表示される
System.out.println(Arrays.toString(str));

//エラー
list1.add("VB");

//エラー
list1.remove(0);

※この変換の仕方では要素の追加削除はできない




String[] str2 = {"Java", "C#", "Ruby"};
List<String> list2 = new ArrayList<String>();
Collections.addAll(list2, str2);

list2.set(0, "ジャバ");
list2.add("Perl");
list2.remove(1);

//[ジャバ, Ruby, Perl]と表示
System.out.println(list2);

//[Java, C#, Ruby]と表示
System.out.println(Arrays.toString(str2));

※この変換なら要素の追加、更新もOK



リスト→配列変換


List<String> list3 = new ArrayList<String>() {
  {
    add("Java");
    add("C#");
    add("Ruby");
  }
};

String[] str3 = new String[list3.size()];
list3.toArray(str3);
list3.set(0, "ジャバ");

//[Java, C#, Ruby]と表示
System.out.println(Arrays.toString(str3));

//[ジャバ, C#, Ruby]と表示
System.out.println(list3);



リストの順番処理

基本は拡張for文を使うがこれはイテレーターを利用したシンタックスシュガー(糖衣構文)
※糖衣構文:人間が読み書きしやすいように用意された構文


List<String> list = new ArrayList<String>() {
  {
    add("Java");
    add("C#");
    add("Ruby");
  }
};

for (String s : list) {
  System.out.println(s);
}



イテレーター

イテレーターはIteratorインターフェイスで定義され、コレクションからiteratorメソッドを利用することで、それぞれの実装に応じたiteratorオブジェクトを取得できる


Iteratorの主なメソッド

boolean hasNext():次の要素が存在するか?
E next():次の要素を取得
void remove():現在の要素を削除



List<String> list = new ArrayList<String>() {
  {
    add("Java");
    add("C#");
    add("Ruby");
  }
};


for (Iterator<String> ite = list.iterator(); ite.hasNext();) {
  System.out.println(ite.next());
}



Iteratorを使うメリット

拡張for文ではできないことができる場合がある


List<String> list = new ArrayList<String>() {
  {
    add("Java");
    add("C#");
    add("Ruby");
  }
};

for (String s : list) {
  System.out.println(s);

  //リストの要素を削除する
  //拡張for文では実行時エラーとなる
  //list.remove(s);
}

for (Iterator<String> ite = list.iterator(); ite.hasNext();) {
  System.out.println(ite.next());

  //リストの要素を削除する
  //iteratorを使用する場合はOK
  ite.remove();
}



ArrayList or LinkedList

要素の挿入削除が多い場合はLinkedList、そうではない場合はArrayList




Setの使い方

//初期値を匿名クラスの初期化子で追加
Set<String> s = new HashSet<String>() {
  {
    add("Java");
    add("C#");
    add("Ruby");
  }
};

//要素の追加
s.add("PHP");

//重複した要素なので何も起こらない
s.add("Java");

//ArrayListに入っている要素を追加
s.addAll(new ArrayList<String>() {
  {
    add("PHP");
    add("JavaScript");
  }
});

//[C#, PHP, Ruby, JavaScript, Java]と表示
System.out.println(s);

//要素を削除
s.remove("Perl");

//全要素を削除
s.clear();




Mapの使い方

//<キーの型,値の型>
Map<String, String> map = new HashMap<String, String>(){
  //匿名クラスの初期化子で初期値を追加
  {
    //追加
    put("red", "赤");
    put("green", "緑");
    put("blue", "青");
  }
};

//キーが含まれているか?
//「true」と表示
System.out.println(map.containsKey("red"));

//値が含まれているか?
//「false」と表示
System.out.println(map.containsValue("紫"));

//キーを順番に取り出す
for (String key : map.keySet()) {
  System.out.println(key);
}

//値を順番に取り出す
for (String val : map.values()) {
  System.out.println(val);
}

//キーと値の両方を取り出す
for (Map.Entry<String, String> entry : map.entrySet()) {
  System.out.printf("%s:%s\n", entry.getKey(), entry.getValue());
}



キュー(Queue)

先入れ先出し(FIFO:First in First Out)と呼ばれるデータ構造で「待ち行列」とも呼ばれる




スタック(Stack)

後入れ先出し(LIFO:Last in First Out)と呼ばれるデータ構造



//キュー(FIFO:First in First Out)
Deque<Integer> dq = new ArrayDeque<Integer>();


//追加
dq.addLast(1);
dq.addLast(2);
dq.addLast(3);

//[1, 2, 3]と表示
System.out.println(dq);

//最初の要素を取り出し、削除
//1と表示
System.out.println(dq.removeFirst());

//[2, 3]と表示
System.out.println(dq);


//スタック(LIFO:Last in First Out)
Deque<Integer> dq2 = new ArrayDeque<Integer>();

//追加
dq2.addLast(1);
dq2.addLast(2);
dq2.addLast(3);

//[1, 2, 3]と表示
System.out.println(dq2);

//最後の要素を取り出し削除
//3と表示
System.out.println(dq2.removeLast());

//[1, 2]と表示
System.out.println(dq2);

2014年12月11日木曜日

Javaクラス関係(8章~11章パッケージまで)

クラス

クラスとは変数(フィールド)とメソッドをまとめたもの


クラス宣言

「class クラス名」で宣言する


フィールド

クラス宣言の後の「{}」の中に宣言した変数



以下は3つのフィールドを持つAccountクラス

class Account {
  String name;
  String no;
  long balance;
}

※複数のクラスを1つのファイルに記載する場合、mainメソッドのあるクラス名をファイル名にする



インスタンス

「new クラス名()」で生成したクラス型の実体のこと

インスタンスを生成することをインスタンス化という

//Accountクラス型の変数xにインスタンスを代入
Account x = new Account();

※クラスの各フィールドは各々の規定値(0やnull)で初期化される



オブジェクト

クラスのインスタンスと配列の本体の総称



インスタンス変数

インスタンス化したフィールドのこと



フィールド(インスタンス変数)へのアクセス

メンバアクセス演算子(ドット演算子)「.」を使う

//Accountクラスのインスタンスをxに代入
Account x = new Account();

//インスタンス変数「name」に値を代入
x.name = "山田太郎";

//インスタンス変数「name」の値をstrに代入
String str = x.name;

//山田太郎と表示される
System.out.print(str);




コンストラクタ


インスタンス生成時に呼び出されるメソッドでクラス名と同じ
→主にフィールドの初期化などを行う


普通のメソッドと違い、値を返すことができない
→宣言時に戻り値の型を指定しない

//Dogクラス
class Dog {
  String name;
  double weight;

  //コンストラクタ
  //(クラス名と同じ、戻り値型の指定なし)
  Dog(String str) {
    name = str;
  }
}


class DogTester {
  public static void main(String[] args) {
 
    //コンストラクタで名前を設定
    Dog dog1 = Dog("Pochi");
 
  }
}



デフォルトコンストラクタ

コンストラクタを全く定義していない場合、引数なしのデフォルトコンストラクタが自動的に作られる


//コンストラクタが無いクラス
class Dog {
  String name;
  double weight;

}

この場合、以下のデフォルトコンストラクタが自動的に作られている

======================
  Dog() {
 
  }
======================


だから、定義していないコンストラクタを呼んでもエラーにならない

class DogTester {
  public static void main(String[] args) {
 
    //デフォルトコンストラクタでインスタンスを生成
    Dog dog1 = Dog();
 
  }
}



this

自分自身のインスタンスのことを「this」と表現する

クラスのフィールドと同じ変数や仮引数をつくることができる
→その場合でフィールドにアクセスするには「this.」を変数名の前につける


class Dog {

  String name;
  double weight;

  //仮引数とフィールドが同じ名前
  Dog(String name) {
 
    //this.nameでフィールドにアクセスできる
    this.name = name;
  }
}


自分のクラス内で自分のコンストラクタを呼ぶ場合「this」を使う

class Dog {
  String name;
  double weight;

  //コンストラクタ1
  Dog(String str) {
    name = str;
  }

  //コンストラクタ2
  Dog(String str, double wt) {

    //自分自身のコンストラクタ1をthisで呼ぶ
    this(str);

    weight = wt;
  }
}


メソッド


クラスにはメソッドを持たせることができる


インスタンスメソッド

インスタンスが実行するメソッドでstaticをつけない



class Dog {

  String name;

  Dog(String name) {
    this.name = name;
  }

  void bow() {
    System.out.print("Bow wow!");
  }
}


class DogTester {
  public static void main(String[] args) {
 
    Dog dog1 = new Dog("Pochi");
 
    //インスタンスメソッドを実行
    dog1.bow(); //Bow wow!と表示される
  }
}






クラスの配列

クラス型は基本型と同じように配列にできる

class Dog {
  void bow() {
    System.out.print("Bow wow!");
  }
}

class DogTester {
  public static void main(String[] args) {

    //Dogクラス型の配列(要素数3)
    Dog[] dogAry = new Dog[3];

    for (int i = 0; i < dogAry.length; i++) {
      //Dogクラス型の配列にインスタンスを格納
      dogAry[i] = new Dog();
    }

    //配列の各要素を変数dogに取り出す
    for (Dog dog : dogAry) {
      //各要素毎にインスタンスメソッドを実行
      dog.bow();
    }
  }
}


クラス変数(静的フィールド)


staticを付けて宣言したフィールドのことで、インスタンスがいくつ生成されても関係無く1つのクラスだけに属する

他のクラスがアクセスする場合は「クラス名.」でアクセスする(自分のクラスのクラス変数の場合は省略可)

class Student {

  //クラス変数
  static int count = 0;

  //コンストラクタ
  Student() {
    //インスタンス生成する毎にカウントアップ
    //自分のクラス変数は「クラス名.」がいらない
    count++;
  }
}

class StudentTester {
  public static void main(String[] args) {
 
    //クラス変数はインスタンスが無くても使える
 
    //カウントを表示(0と表示される)
    //他のクラスのクラス変数は「クラス名.」が要る
    System.out.println(Student.count);
 
    Student st1 = new Student();
 
    //カウントを表示(1と表示される)
    System.out.println(Student.count);
 
    Student st2 = new Student();
 
    //カウントを表示(2と表示される)
    System.out.println(Student.count);
  }
}




クラスメソッド(静的メソッド)

staticを付けて宣言したメソッドのことで、インスタンスがいくつ生成されても関係無1つのクラスだけに属する

他のクラスがクラスメソッドを呼ぶ場合は「クラス名.」で呼び出す(自分のクラスのクラスメソッドの場合は省略可)

クラスメソッドはインスタンス変数、インスタンスメソッドにアクセスすることはできない
(クラスからはどのインスタンスの変数、メソッドのことだか分からない為)

一方、インスタンスメソッドからは全部の変数、メソッドにアクセスできる

class Student {

  //クラス変数
  static int count = 0;

  //インスタンス変数
  int no;

  //コンストラクタ
  Student() {
    //インスタンスメソッドからstatic変数はアクセス可
    count++;

    //インスタンスメソッドからインスタンス変数もアクセス可
    no = count;
  }

  void showNo() {
    //インスタンスメソッドは両方アクセス可
    System.out.printf("%d/%d\n", no, count);
  }

  static void showCount() {

    //staticメソッドからはstatic変数はアクセス可
    System.out.printf("total:%d", count);

    //staticメソッドからはインスタンス変数は×
    //System.out.print(no);
  }

  static void showCount2() {
    System.out.print("***");
 
    //staticメソッドからstaticメソッドを呼ぶのはOK
    //(自分のクラス内なので「クラス名.」は省略)
    showCount();
 
    System.out.println("***");
 
 
    //staticメソッドからはインスタンスメソッドは×
    //showNo();
  }
}

class StudentTester {
  public static void main(String[] args) {

    Student st1 = new Student();

    //インスタンスメソッド(1/1と表示)
    st1.showNo();

    Student st2 = new Student();

    //インスタンスメソッド(2/2と表示)
    st2.showNo();

    //クラスメソッドは「クラス名.」で呼び出す
    //(***total:2***と表示)
    Student.showCount2();
  }
}



デフォルトコンストラクタの注意点

コンストラクタが1つでも定義されている場合、デフォルトコンストラクタは自動的に作成されない
→つまりコンストラクタが1つも無い場合のみデフォルトコンストラクタは作られる

class Dog {

  String name;

  //コンストラクタ
  Dog(String name) {
    this.name = name;
  }
}

class DogTester {
  public static void main(String[] args) {
 
    //定義されてるコンストラクタ
    Dog dog1 = new Dog("Poch");
 
    //コンパイルエラー!!
    //デフォルトコンストラクタは無い!
    Dog dog2 = new Dog();
 
  }
}



アクセス修飾子


フィールドやメソッドの前に「private」と付けると他のクラスからアクセスすることができなくなる
自分のクラスからアクセスすることはOK


フィールドやメソッドの前に「public」と付けると他のクラスにも公開され、全てのクラスがアクセス可能となる


何も付けないと同一パッケージのクラスからのみアクセスが可能となる
→異なるパッケージのクラスからはアクセスできない


class Dog {

  //プライベートな変数
  private String name;

  //コンストラクタ
  Dog(String name) {
 
    //同じクラス内ならアクセス可能
    this.name = name;
  }

  void showName() {
 
    //同じクラス内ならアクセス可能
    System.out.println(name);
  }
}

class DogTester {
  public static void main(String[] args) {
 
    //コンストラクタ
    Dog dog1 = new Dog("Pochi");
 
    dog1.showName();
 
    //コンパイルエラー
    //他のクラスからは書き換えできない
    dog1.name = "Taro";
 
    //コンパイルエラー
    //他のクラスからはアクセスできない
    System.out.print(dog1.name);
  }
}



アクセサ(アクセッサ)

フィールドの値を設定したり取得したりするメソッドのことで、主にプライベートなフィールドにアクセスするために作成する


セッタ(setter)

フィールドの値を設定するメソッドのことでフィールド名の前に「set」をつける



ゲッタ(getter)

フィールドの値を取得するメソッドのことでフィールド名の前に「get」をつける


class Dog {

  //フィールドname(プライベート)
  private String name;

  //セッタ(nameを設定するメソッド)
  void setName(String name) {
    this.name = name;
  }

  //ゲッタ(nameを取得するメソッド)
  String getName() {
    return name;
  }
}

class DogTester {
  public static void main(String[] args) {
 
    //コンストラクタ
    Dog dog1 = new Dog();
 
    //セッタでnameを設定
    dog1.setName("Pochi");
 
    //ゲッタでnameを取得
    String dogName = dog1.getName();
 
    //Pochiと表示される
    System.out.print(dogName);
  }
}



カプセル化

フィールドをprivateにして、その値の更新はアクセッサなどを使用(直接アクセスしない)すること



オブジェクト指向の三大要素

カプセル化継承ポリモーフィズム(多相性、多態性、多様性)



1クラス1ファイル

基本的に1つのファイルには1つのクラスのみを記述して、独立性を高めるべきである
→複数のクラスを1つのファイルに書くのはテスト的なもののみにする



4つのアクセス修飾子

public(公開),protected(パッケージ+継承先),無し(パッケージ),private(非公開)




コレクション

たくさんのインスタンスを管理する目的を持った、クラスやインターフェイスの総称(java.utilパッケージ)
→便利な配列のようなもの



ArrayList

コレクションの代表格


宣言

<型>(ジェネリクス)に入れたい型を書く

ArrayList<String> list=new ArrayList<String>();
→Stringを入れるArrayList



ジェネリクスには基本型が指定できないのでラッパークラスを使う

int→Integer, double→Double,・・・

ArrayList<Integer> list=new ArrayList<Integer>();
→Int(int)を入れるArrayList



要素の追加、取得

add(追加したい値)メソッドとget(取得したいindex)を使う


ArrayList<String> list=new ArrayList<String>();
list.add("Alice");
//「Alice」と表示
System.out.print(list.get(0));



要素数の取得

sizeメソッドで取得

ArrayList<String> list=new ArrayList<String>();

list.add("Alice");
list.add("Bob");
list.add("Chris");
list.add("Elmo");

for(int i=0;i<list.size();i++){
  System.out.println(i+":"+list.get(i));
}


素の削除、置き換え

remove(削除したい値)、remove(index)、
set(index, 置き換える値)を使う


//値が「Alice」の要素を削除
list.remove("Alice");

//インデックスが「3」の要素を削除
list.remove(3);

//インデックス「0」の要素を「Bob」に置換
list.set(0, "Bob");



要素にある値が含まれているかどうか

containsメソッドで判定


ArrayList<String> list=new ArrayList<String>();

list.add("Alice");
list.add("Bob");

if(list.contains("Alice")){
  System.out.println("含まれています");

}else{
  System.out.println("含まれていません");
}



オートボクシング、アンボクシング

ラッパークラスを使用時に自動変換してくれる


ArrayList<Integer> list=new ArrayList<Integer>();

//本来はこのように書く必要がある
list.add(new Integer(123));

//オートボクシング
list.add(456);


//本来はこのように書く必要がある
Integer num = list.get(0);


//アンボクシング
int num2 = list.add(1);




API

Application Programming Interface

システム同士がお互いにやり取りする決め事のこと





クラス型の変数の代入


クラス型の変数にインスタンスを代入すると、そのインスタンスへの参照(アドレス)がコピーされる
インスタンスの実体はコピーされない!!



同じ参照を持つクラス型変数の参照先を変更すると、他の参照先も変更される
→同じ1つの実体に複数の参照が紐づいているから


class ClassA {
  int num;
  ClassA(int num) {
    this.num = num;
  }
}

class Test1 {
  public static void main(String[] args) {
 
    //インスタンスを生成
    ClassA a = new ClassA(123);
 
    //123と表示される
    System.out.println(a.num);
 
    //クラス型変数を代入
    ClassA b = a;
 
    //bのnumを変更したのに…
    b.num = 567;
 
    //aの内容も変更されてしまう
    //567と表示される
    System.out.println(a.num);
 
  }
}



クラス型変数の比較


クラス型変数を「==」で比較しても同じ参照(アドレス)かどうかを比較するだけ
→同じクラスで全く同じフィールド値のインスタンス同士を比較してもtrueにはならない!




引数としてのクラス型変数


メソッドの引数にクラス型を渡す場合は、そのインスタンスの参照(アドレス)を渡すことになる
→インスタンスの実体が渡されるわけではない!



クラス型の配列

クラス型の配列を作ることができ、要素として格納されるのはインスタンスの参照(アドレス)である
→インスタンスの実体が格納されるわけではない!


class ClassA {
  int num;
  ClassA(int num) {
    this.num = num;
  }
}

class Test1 {
  public static void main(String[] args) {
 
    //要素数3のクラス型の配列を生成
    ClassA[] ary1 = new ClassA[3];
 
    //インスタンスを生成
    ClassA a = new ClassA(1);
 
    //クラス型の配列にインスタンスを格納
    ary1[0] = a;
 
    //生成と同時に格納もできる
    ary1[1] = new ClassA(2);
 
 
    //インスタンスを生成
    ClassA b = new ClassA(3);
    ClassA c = new ClassA(4);
    ClassA d = new ClassA(5);
 
    //配列を生成と同時に初期化もできる
    ClassA[] ary2 = {b, c, d};
 
    //インスタンスを生成しながら初期化もできる
    ClassA[] ary3 = {
      new ClassA(6),
      new ClassA(7),
      new ClassA(8),
    };
 
  }
}



同一クラス内のコンストラクタの呼び出し


this(・・・)という形式で同一クラスのコンストラクタを呼べる、ただしコンストラクタ内の先頭にしか書くことができない
→それ以外で呼ぼうとするとコンパイルエラー

class ClassA {
  int num1;
  int num2;
  ClassA(int num1) {
    this.num1 = num2;
  }

  ClassA(int num1, int num2) {
 
    //ここで呼ぶのはOK
    this(num1);
 
    this.num2 = num2;
 
    //ここで呼ぶとエラー!!
    this(num1);
  }

}


toStringメソッド

public String toString()」を定義すると、そのクラス型の変数をprintなどで表示しようとしたり、文字列を連結しようとするときに自動でこのメソッドが呼ばれる


class ClassA {
  int num;
  ClassA(int num) {
    this.num = num;
  }

  //toStringメソッドを実装
  public String toString() {
    return "-->" + num + "<--";
  }
}

class Test1 {
  public static void main(String[] args) {
 
    //インスタンスを生成
    ClassA a = new ClassA(1);
 
    //printしようとするとtoStringが自動で呼ばれる
    //「-->1<--」と表示される
    System.out.println(a);
 
    //文字列連結でもtoStringメソッドが自動で呼ばれる
    String str = "***" + a + "***";
 
    //「***-->1<--***」と表示される
    System.out.println(str);
  }
}



クラス型のフィールド


クラス型のフィールドに格納されるのはインスタンスの参照(アドレス)である
→複数のインスタンスに同じ参照を代入すると、1つ変更しただけで全て変更されてしまう!

class ClassA {
  int num;

  ClassA(int num) {
    this.num = num;
  }
}

class ClassB {

  //クラス型のフィールド
  ClassA a;

  //引数にクラス型
  ClassB(ClassA a) {
    this.a = a;
  }

  void printNum() {
    System.out.println(a.num);
  }

  void setNum(int num) {
    a.num = num;
  }
}

class Test1 {
  public static void main(String[] args) {
 
    //Aのインスタンスを生成
    ClassA a = new ClassA(1);
 
    //aを引数にBのインスタンスbを生成
    ClassB b = new ClassB(a);
 
    //「1」と表示される
    b.printNum();
 
    //aを引数にBのインスタンスcを生成
    ClassB c = new ClassB(a);
 
    //インスタンスcのnumを変更すると…
    c.setNum(22);
 
    //「22」と表示される
    c.printNum();
 
    //インスタンスbのnumも変更されてしまう
    //「22」と表示される
    b.printNum();
 
    //引数としたaのnumを変更すると…
    a.num = 333;
 
    //bとcのnumも変更されてしまう
 
    //「333」と表示される
    b.printNum();
 
    //「333」と表示される
    c.printNum();
 
    //このようにすれば問題ない
    ClassB d = new ClassB(new ClassA(555));
    ClassB e = new ClassB(new ClassA(666));
    d.setNum(777);
 
    //「777」と表示される
    d.printNum();
 
    //「666」と表示される
    e.printNum();
  }
}



クラス型を返すメソッド

クラス型のフィールドの値をメソッドの戻り値として返すと、そのフィールドへの参照(アドレス)を返すことになる
→その参照を通じてフィールドにアクセスできてしまうので、セキュリティ的に問題あり!


class ClassA {
  int num;

  ClassA(int num) {
    this.num = num;
  }
}

class ClassB {

  //クラス型のフィールド
  private ClassA a;

  ClassB(ClassA a) {
    this.a = a;
  }

  //クラス型のフィールド値を返す
  ClassA getClassA() {
    return a;
  }

  void printNum() {
    System.out.println(a.num);
  }

}

class Test1 {
  public static void main(String[] args) {
 
    //インスタンスを生成
    ClassB b = new ClassB(new ClassA(1));
 
    //「1」と表示される
    b.printNum();
 
    //クラス型のフィールドの値を取得
    ClassA a = b.getClassA();
 
    //aのnumを書き換えると…
    a.num = 22;
 
    //プライベートなbのnumが変更される
    //「22」と表示される
    b.printNum();
 
  }
}



============================


class ClassB {

  //このようにすれば問題なし
  ClassA getClassA() {
    return new ClassA(a.num);
  }


============================



has-A

あるクラスがその一部として別のクラスを持つこと
→クラス型のフィールドを定義することで実現できる


コンポジション

あるインスタンスが内部に別のインスタンスを持っている構造のこと
コンポジションはhas-Aの関係となる



コンポジションにより、あるクラスを拡張したクラスを作ることができる


class ClassA {

  void sayHello() {
    System.out.println("Hello");
  }
}

class ClassB {

  //ClassAのインスタンスを持つ
  private ClassA a = new ClassA();

  //ClassAのメソッドを呼ぶ
  void sayHello() {
    a.sayHello();
  }

  //ClassBだけのメソッドを追加
  void sayHoge() {
    System.out.println("hoge");
  }
}

class Test1 {
  public static void main(String[] args) {
 
    //インスタンスを生成
    ClassA a = new ClassA();
 
    //「Hello」と表示
    a.sayHello();
 
    //コンポジションでClassAを拡張したClassB
    ClassB b = new ClassB();
 
    //ClassAと同じメソッドを持っていて…
    //「Hello」と表示
    b.sayHello();
 
    //ClassB独自のメソッドを拡張できる
    //「hoge」と表示
    b.sayHoge();
 
  }
}



クラス変数

staticを付けて宣言されたフィールドで、そのクラスの全インスタンスで共有すべき変数
→クラス変数の実体は1つ宣言する毎に1個だけつくられる



クラス変数のアクセス


異なるクラスのクラス変数にアクセスする場合は「クラス名.」を頭に付ける
→自分のクラス内ならば変数名だけでOK

※ただし、ローカル変数や仮引数名としてクラス変数と同じ名前を宣言できてしまうので、その場合は「クラス名.」を頭に付けてアクセスする


「インスタンス変数名.」でもアクセスできるが意味合い的にも紛らわしいのでやらない!!


class ClassA {

  //クラス変数
  static int num = 123;

  static void display() {
    //自分のクラス内ならnumでOK
    System.out.println(num);
  }

  static void display(int num) {
    //仮引数に同じ名前が使われている
    System.out.println(ClassA.num + num);
  }

  static void display2() {
    //ローカル変数に同じ名前が使われている
    int num = 567;
    System.out.println(ClassA.num + num);
  }
}

class ClassB {

  public static void main(String[] args) {
 
    //他のクラスのクラス変数にアクセス
    //「123」と表示される
    System.out.println(ClassA.num);
 
    //インスタンスを生成
    ClassA a = new ClassA();
 
    //「インスタンス変数.」でも呼べる
    //しかし、紛らわしいのでやらない!!
    System.out.println(a.num);
  }
}



publicかつfinalなクラス変数の公開


クラスの利用者に公開すべき便利な定数はpublic static finalで宣言するとよい
→例)Math.PI…円周率




クラスメソッド

staticを付けたメソッドでクラス全体に関わる処理などを記載する
→呼び出し方はクラス変数と同じで「クラス名.」で呼べる、その他の注意点も同じ


オーバーロードはクラスメソッドとインスタンスメソッドにまたがって行える


クラスメソッド内ではインスタンス変数とインスタンスメソッドにはアクセスできない
→どのインスタンスの変数やメソッドにアクセスするのか分からない為


class ClassA {

  //クラス変数
  static int num = 123;

  //インスタンス変数
  int cnt = 999;

  //クラスメソッド
  static void display() {
 
    //クラスメソッド内でクラス変数はOK
    System.out.println(num);
 
    //クラスメソッド内でインスタンス変数は×
    //System.out.println(cnt);
 
    //クラスメソッド内でクラスメソッドはOK
    hoge();
 
    //クラスメソッド内でインスタンスメソッドは×
    //display(1);
  }

  //クラスメソッド
  static void hoge() {
    System.out.println("hoge");
  }


  //インスタンスメソッド
  //staticにまたがりオーバーロード
  void display(int n) {
    System.out.println(n);
  }

}

class ClassB {

  public static void main(String[] args) {
 
    //他のクラスのクラスメソッドを呼ぶ
    ClassA.display();
 
    //インスタンスを生成
    ClassA a = new ClassA();
 
    //「インスタンス変数.」でも呼べる
    //しかし、紛らわしいのでやらない!!
    a.display();
  }
}



ユーティリティクラス


インスタンス変数を持たずにクラスメソッドのみを提供するクラス
→代表例としてMathクラス



クラス初期化子(イニシャライザー)

クラス宣言内のstaticを付けたブロック「{・・・}」のことで、クラスを初めて利用する前に実行されている処理
→クラス変数を初期化するために使うとよい

※クラス初期化子は一度しか実行されない


class ClassA {

  //クラス変数
  static int num = 0;

  //クラス初期化子
  static {
    num += 123;
  }
}

class ClassB {

  public static void main(String[] args) {
 
    //クラス初期化子が実行される
    //123と表示される
    System.out.println(ClassA.num);
 
    //クラス初期化子は一度きり
    //123と表示される
    System.out.println(ClassA.num);
 
  }
}



インスタンス初期化子(イニシャライザー)

クラス宣言内のブロック「{・・・}」のことで、インスタンス生成時(コンストラクタの前)に実行される処理
→全てのコンストラクタで共通に行う処理を記載するとよい

class ClassA {

  //クラス変数
  static int count = 0;

  int num = 0;

  //インスタンス初期化子
  {
    count++;
    num = count;
  }

  //コンストラクタ
  ClassA() {
  }

  //コンストラクタ(引数あり)
  ClassA(int num) {
    this.num += num;
  }
}

class ClassB {

  public static void main(String[] args) {
 
    //インスタンス初期化子が実行
    ClassA a1 = new ClassA();
 
    //「1」と表示
    System.out.println(a1.num);
 
    //インスタンス初期化子が実行
    ClassA a2 = new ClassA(10);
 
    //「12」と表示
    System.out.println(a2.num);
 
  }
}



パッケージ


パッケージはクラスやインターフェイスをフォルダ階層にまとめたもので、名前の重複回避などの意味がある
→重複しないようにインターネットアドレスの逆順に並べたものを使うとよい(jp.co.yahooなど)


[java]
   |--[util]
   |     |--Scanner
   |     |--Random



単純名、完全限定名


クラス名のみが単純名、頭にパッケージ名を付けるのが完全限定名

java.utilパッケージのScannerクラスを例にすると…

単純名:Scanner
完全限定名:java.util.Scanner



単一型インポート宣言

「import 完全限定名」でインポートすると、その完全限定名で指定したモノがソース内で単純名で利用できる



オンデマンド型インポート宣言

「import パッケージ名.*」でインポートすると、そのパッケージ内のクラスやインターフェイスが単純名で利用できる

※オンデマンド型インポートはクラス名が重複する可能性があるので多用すべきではない!!

※必要に応じてインポートされるので、そのパッケージの全てをインポートするわけではない!



java.langパッケージの自動インポート

java.langパッケージはインポートしなくても、いきなり単純名で使用できる




静的インポート宣言

静的なメンバであるクラス変数、クラスメソッドをインポートでき、宣言の方法は2種類ある


単一インポート宣言

import static 型名.識別子名;


オンデマンド静的インポート宣言

import static 型名.*;




import java.util.Scanner;
import static java.lang.Math.PI;

class Test {
  public static void main(String[] args) {
 
    //インポートしているので単純名で使える
    Scanner stdIn = new Scanner(System.in);
 
    //静的インポートしているので変数名で使える
    System.out.printf("%.2f", PI);
  }
}



パッケージ宣言

「packege パッケージ名;」の形式で行い、ソースコードの先頭に書かなければならない

※パッケージ宣言は無くてもよい、2個以上書くことはできない

※パッケージ名は全て小文字とすべき(クラス名と被らないようにする為)


無名パッケージ

パッケージ宣言を書かない場合は、無名パッケージに所属することになる

※テスト的なモノ以外はパッケージに所属させるべきである



同一のパッケージ内のクラスは単純名で使える



パッケージとディレクトリ

パッケージを作成する場合は、パッケージ名と同一のディレクトリの中にファイルを置く



クラスのアクセス制御

publicクラス

public宣言されたクラスでパッケージとは無関係に利用できる

※publicクラスは1ファイルに1個



非publicクラス

publicを付けずに宣言されたクラスで同一パッケージ内以外は利用できない



メンバのアクセス制御


publicクラスの場合、publicを付けたメンバが公開アクセスとなる

非publicクラスの場合、publicを付けたメンバはパッケージアクセスとなる