スタート地点
①ProgateでHTML/CSSを修了後、Javaに挑戦(メソッド、クラスまでやり、理解が不十分)
②Udemyの動画を参考にandroidstudioでアプリ作成を試みるもよくわからなく挫折
③何かを作るためにJavaの基礎を一通り覚えたいと考え、入門書で四則演算や文字の入力、出力まで学習
④学習した内容のアウトプットを開始←ココからスタート
第3章 条件分岐と繰り返し
リスト3−1 天気による行動の変化の行動【if文】
public class Main{
public static void main(String[] args){
boolean tenki = true;
if (tenki == true){
System.out.println("洗濯");
System.out.println("散歩");
} else {
System.out.println("DVDを見る");
}
}
}
+++++++++++++++++++++++++++実行結果++
洗濯
散歩
++++++++++++++++++++++++++++++++++++
if文はエクセルの関数とかと考え方が同じような感じがするので理解しやすい。
elseのあとにifを書いてさらに条件式を加えることが可能。
リスト3−2 トイレの空き時間を待つ繰り返し処理【while】
public class Main {
public static void main(String[] args) {
boolean doorClose = true;
while(doorClose == true) { //このブロックではdoorCloseがtrueの場合無限ループになる
System.out.println("ノックする");
System.out.println("1分待つ");
}
System.out.println("トイレに入る");//doorCloseがfalseの場合ループを抜けてこちらが実行される
}
}
while(条件式){条件式がtrueの間この中の処理をループさせる}
っていう理解で良いのかな。
ifやwhileは制御構文と言う。
制御構文の特徴
①条件式がある
②ブロック({}で囲む処理のあつまり)がある
ちなみにブロックには以下2つのルールがあるらしい。
①ブロック内が1行であれば{}を省略可能
②ブロック内で宣言した変数はブロック外で使用できない(変数を利用できる範囲=スコープという)
①の{}省略は推奨されていないという。なのになぜこんなルールがあるのか・・・
スコープは覚えておこう。
【do-while】
基本的なwhile文は条件式を評価してブロックが実行されるが、whileの前にdo{}を書くとdoのブロックを実行後に条件式を評価してtrueならループする。
public class Main {
public static void main(String[] args) {
boolean doorClose = true;
do {
System.out.println("トイレに入る");
}
while(doorClose == true);
}
}
上の式だと、”トイレに入る”を出力してからwhile文のループに入る。
リスト3−6 占い文【switch】
public class Main {
public static void main(String[] args) {
System.out.println("あなたの運勢を占います");
int fortune = 1; //常に1
switch(fortune) {
case 1:
System.out.println("大吉");
break;
case 2:
System.out.println("中吉");
break;
case 3:
System.out.println("吉");
break;
default:
System.out.println("凶");
}
}
}
+++++++++++++++++++++++++++実行結果++
あなたの運勢を占います
大吉
++++++++++++++++++++++++++++++++++++
条件式に当てはまるcaseラベルが実行される。
break;を忘れると次のラベルの処理も実行される。例えばcase1のbreak;を消すと、
fortuneが1の場合、大吉と中吉が両方表示される。
リスト3−7 基本的なfor分のサンプル【for】
public class Main {
public static void main(String[] args) {
for(int i = 0; i < 10; i++) {
System.out.println("こんにちは");
}
}
}
+++++++++++++++++++++++++++実行結果++
こんにちは
||こんにちはが10回表示される
こんにちは
++++++++++++++++++++++++++++++++++++
for(int i = 0; i < 10; i++)がミソ
①int i = 0
整数型の変数iを初期化
②i < 10
10のところに繰り返したい回数を入れる
③i++
変数iに1を足す(for文のブロック実行後に自動的に実行される文)
for文のバリエーション
●for(int i = 1; i < 10; i++){}
ループを1からスタート
●for(int i = 0; i < 10; i+=2){}
ループ変数を2ずつ増やす
●for(int i = 10; i > 0; i--){}
ループ変数を10から1ずつ減らす
●for(; i < 10; i++){}
ループ変数を初期化しない
●for(int i = 1; i < 10){}
繰り返し時の処理を行わない
制御構造のネスト(入れ子)
リスト3−9 for文のループを2重にして九九の表を出力
public class Main {
public static void main(String[] args) {
for (int w = 1; w < 10; w++){
for (int h = 1; h < 10; h++){
System.out.print(w * h + " ");
}
System.out.println("");
}
}
}
+++++++++++++++++++++++++++実行結果++
1 2 3 4 5 6 7 8 9
2 4 6 8 10 12 14 16 18
3 6 9 12 15 18 21 24 27
4 8 12 16 20 24 28 32 36
5 10 15 20 25 30 35 40 45
6 12 18 24 30 36 42 48 54
7 14 21 28 35 42 49 56 63
8 16 24 32 40 48 56 64 72
9 18 27 36 45 54 63 72 81
++++++++++++++++++++++++++++++++++++
実行の流れを整理してみると
1.【1の段】
①ループ変数wのfor文の中のループ変数hのfor文の中の処理の実行
ループ変数はどちらとも初期値1なので1*1を出力(改行なし)
このあとhに1がプラスされる
②ループ変数hのfor文2周目
hは2となったので1*2を出力(改行なし)
このあとhに1がプラスされる
③ループ変数hのfor文3周目
hは3となったので1*3を出力(改行なし)
このあとhに1がプラスされる
④ループ変数hのfor文をh=9まで繰り返す(1の段が完成)
⑤ループ変数wの処理である改行を出力
このあとwに1がプラスされる
2.【2の段】
①ループ変数wのfor文の中のループ変数hのfor文の中の処理の実行
ループ変数wは2、ループ変数hは初期化されるので2*1を出力(改行なし)
このあとhに1がプラスされる
②ループ変数hのfor文2周目
hは2となったので2*2を出力(改行なし)
このあとhに1がプラスされる
③ループ変数hのfor文3周目
hは3となったので2*3を出力(改行なし)
このあとhに1がプラスされる
④ループ変数hのfor文をh=9まで繰り返す(2の段が完成)
⑤ループ変数wの処理である改行を出力
このあとwに1がプラスされる
上記をループ変数w=9まで繰り返して処理を抜ける。
書いてはみたが、この流れを後で見返した時に理解できるものなのか不安。
【break】と【continue】
【break】
for (int i = 1; i < 10; i++){
if(i == 3){
break;
}
System.out.println(i);
}
//breakの場合はi == 3の時点で繰り返し処理を中断。
【continue】
for (int i = 1; i < 10; i++){
if(i == 3){
continue;
}
System.out.println(i);
}
//continueの場合はi == 3の時点で一度繰り返し処理を中断し、次の週から繰り返しを再開する。
3章の練習問題
練習3−1
①if(weight == 60)
②if((age1 + age2)* 2 > 60)
③if(age % 2 != 0)
④if(name.equals("湊"))
練習3−3
int seibetsu = 0;
int age = 25;
System.out.println("こんにちは。");
if(seibetsu == 0){
System.out.println("私は男です。");
System.out.println(age + "歳です。");
}else{
System.out.println("私は女です。");
}
System.out.println("よろしくおねがいします。");
+++++++++++++++++++++++++++実行結果++
こんにちは。
私は男です。
25歳です。
よろしくおねがいします。
++++++++++++++++++++++++++++++++++++
練習3−5
①System.out.println("[メニュー]:1:検索2:登録3:削除4:変更>]");
②int selected = new java.util.Scanner(System.in).nextInt();
③switch(selected){
case 1:
System.out.println("検索します");
break;
case 2:
System.out.println("登録します");
break;
case 3:
System.out.println("削除します");
break;
case 4:
System.out.println("変更します");
break;
}
+++++++++++++++++++++++++++実行結果++
[メニュー]:1:検索2:登録3:削除4:変更>]
検索します
++++++++++++++++++++++++++++++++++++
練習3−6
System.out.println("【数あてゲーム】");
int ans = new java.util.Random().nextInt(9);
for(int i = 0; i < 5; i++){
System.out.println("0〜9の数字を入力してください");
int num = new java.util.Scanner(System.in).nextInt();
if (num == ans){
System.out.println("アタリ!");
break;
}else{
System.out.println("違います。");
}
}
System.out.println("ゲームを修了します");
+++++++++++++++++++++++++++実行結果++
〜あたった場合〜
【数あてゲーム】
0〜9の数字を入力してください
違います。
0〜9の数字を入力してください
アタリ!
ゲームを修了します
++++++++++++++++++++++++++++++++++++
〜あたらなかった場合〜
【数あてゲーム】
0〜9の数字を入力してください
違います。
0〜9の数字を入力してください
違います。
0〜9の数字を入力してください
違います。
0〜9の数字を入力してください
違います。
0〜9の数字を入力してください
違います。
ゲームを修了します
++++++++++++++++++++++++++++++++++++
第4章 配列
配列の作成と代入
基本形
int[] score;
score = new int[5];
省略記法
/*①*/int[] score = new int[] {20, 30, 40, 50, 80};
/*②*/int[] score = {20, 30, 40, 50, 80};
配列を回す
public class Main {
public static void main(String[] args) {
int[] score = { 20, 30, 40, 50, 80 };
for(int i = 0; i < score.length; i++) { // 配列名.lengthで要素数を得る
System.out.println(score[i]); // 要素を指定してとりだす必要がある
}
}
}
配列を回す(拡張for文)
public class Main {
public static void main(String[] args) {
int[] score = { 20, 30, 40, 50, 80 };
for(int value : score) { // ループが1周するたびに次の要素がvalueに入る
System.out.println(value); // valueをそのまま使える
}
}
}
【配列の参照の仕組み】
public class Main {
public static void main(String[] args) {
/*①*/int[] a = { 1, 2, 3 };
/*②*/int[] b;
b = a;
/*③*/b[0] = 100;
/*④*/System.out.println(a[0]);
}
}
+++++++++++++++++++++++++++実行結果++
100
++++++++++++++++++++++++++++++++++++
上記のプログラムでは
①aの配列変数にそれぞれ1,2,3を代入
②bの配列を作成後、bにaを代入
③その後、bの要素0に100を代入
④aの要素0を出力
という流れである。配列は「参照型」といい、メモリ上の番地を参照しているため、
配列変数bにaを代入した段階でa及びbの要素はメモリ上の同じ番地を参照している事となる。
なので、③でbの要素0に100を代入すると、bの要素はaの要素と同じ場所にあるので、aの要素0の中身も100となる。
【length】
lengthを使うと、配列の文字数だけでなく
文字列変数の文字数も得ることができる。
String s = "java";
System.out.println(s.length()); //文字列変数に対して使用するときは()が必要!
+++++++++++++++++++++++++++実行結果++
4
++++++++++++++++++++++++++++++++++++
多次元配列
【2次元配列の宣言】
要素の型[][] 配列変数名= new 要素の型[行数][列数];
【2次元配列の要素の利用】
配列変数名[行の添字][列の添字]
【2次元配列:兄弟2人の3科目のテストの点数】
public class Main {
public static void main(String[] args) {
int[][] scores = new int[2][3]; //またはint[][] scores = {{40,50,60},{80,60,70}};
scores[0][0] = 40;
scores[0][1] = 50;
scores[0][2] = 60;
scores[1][0] = 80;
scores[1][1] = 60;
scores[1][2] = 70;
System.out.println(scores[1][1]);
}
}
+++++++++++++++++++++++++++実行結果++
60
++++++++++++++++++++++++++++++++++++
上記配列のイメージ
[40][50][60]
[80][60][70]
4章の練習問題
練習4−1
int[] pints = new int[4];
double[] wights = new double[5];
boolean[] answers = new boolean[3];
String[] names = new String[3];
練習4-2
public class Main {
public static void main(String[] args){
int[] moneyList = {121902, 8302, 55100};
for (int i = 0; i < 3; i++){
System.out.println(moneyList[i]);
}
for(int value : moneyList){
System.out.println(value);
}
}
}
+++++++++++++++++++++++++++実行結果++
121902
8302
55100
121902
8302
55100
+++++++++++++++++++++++++++++++++++
練習4−3
public class Main {
public static void main(String[] args){
int [] numbers = {3, 4, 9};
System.out.println("「1桁の数字を入力してください」");
int input = new java.util.Scanner(System.in).nextInt();
for (int value: numbers){
if (input == value){
System.out.println("アタリ!");
}
}
}
}
+++++++++++++++++++++++++++実行結果++
「1桁の数字を入力してください」
アタリ! //数字の3,4,9を入力した場合。
++++++++++++++++++++++++++++++++++++
第5章 メソッド
【メソッドの定義】
public static /*戻り値の型 メソッド名 (引数リスト)*/ {
//メソッドが呼び出されたときに実行される具体的な処理
}
//例
public static void hello() {
System.out.println("こんにちは");
}
【メソッドの呼び出し】
//メソッド名(引数リスト);
//例 (helloメソッドの呼び出し)
public class Main{
public static void main(String[] args){
System.out.println("メソッドを呼び出します");
hello();
System.out.println("メソッドの呼び出しがおわりました");
}
public static void hello() {
System.out.println("こんにちは");
}
}
+++++++++++++++++++++++++++実行結果++
メソッドを呼び出します
こんにちは
メソッドの呼び出しがおわりました
++++++++++++++++++++++++++++++++++++
※メソッドがいくつ定義されていても、main()より上に別メソッドが定義されていても、
プログラムは必ずmain()から動き始める
【引数の利用】
【引数を1つ渡す例】
public class Main{
public static void main(String[] args){
System.out.println("メソッドを呼び出します");
hello("湊"); //引数は湊
hello("朝香"); //引数は朝香
hello("菅原"); //引数は菅原
System.out.println("メソッドの呼び出しがおわりました");
}
public static void hello(String/*引数の型*/ name/*受け取る引数が入る変数名*/) {
System.out.println(name + "さん、こんにちは");
}
}
+++++++++++++++++++++++++++実行結果++
メソッドを呼び出します
湊さん、こんにちは
朝香さん、こんにちは
菅原さん、こんにちは
メソッドの呼び出しがおわりました
++++++++++++++++++++++++++++++++++++
【引数を複数渡す例】
public class Main{
public static void main(String[] args){
add(100, 20); //引数は100と20
add(200, 50); //引数は200と50
}
//複数の値を受け取るaddメソッド
public static void add(int/*引数の型*/ x/*受け取る引数が入る変数名*/,
int/*2つめの引数の型*/ y/*2つ目の引数が入る変数名*/) {
int ans = x + y;
System.out.println(x + "+" + y + "=" + ans);
}
}
+++++++++++++++++++++++++++実行結果++
100+20=120
200+50=250
++++++++++++++++++++++++++++++++++++
※渡す引数(実引数)と受け取る変数(仮引数)の方が一致しない場合はコンパイルエラーとなる。
【return】
【値の戻し方】
public static 戻り値の型 メソッド名(引数リスト){
メソッドが実行されたときに動く処理
return 戻り値;
}
【メソッドを呼び出し、戻り値を受け取る】
型 変数名 = メソッド名(引数リスト);
※文に=(代入演算子)があるため、右辺(メソッドの呼び出し)から評価され、
評価されて戻ってきた値に置き換わる
結果として、左辺の変数に戻り地が代入される
【戻り値の例】
public class Main{
public static int add(int x, int y){
int ans = x + y;
return ans/*戻り値*/;
}
public static void main(String[] args){
int and = add(100, 10)/*右辺が戻り値(ans)に置き換わる*/;
System.out.println("100 + 10 = " + ans);
}
}
+++++++++++++++++++++++++++実行結果++
100 + 10 = 110
++++++++++++++++++++++++++++++++++++
【戻り値をそのまま使う】
public class Main{
public static int add(int x, int y){
int ans = x + y;
return ans/*戻り値*/;
}
public static void main(String[] args){
System.out.println(add(add(10, 20), add(30, 40)));
//①addメソッドの実引数(10, 20)が30に置き換わる
//②addメソッドの実引数(30, 40)が70に置き換わる
//③addメソッドの実引数(①30, ②70)が100に置き換わる
}
}
+++++++++++++++++++++++++++実行結果++
100
++++++++++++++++++++++++++++++++++++
※return文はメソッドを終了させるため、
return文の後に書かれた処理は実行されないことに注意する
【オーバーロード】
基本的に同じメソッド名をつけることはできないが、仮引数の個数か型が異なれば同じ名前のメソッドを複数定義することができる。
【引数や戻り値に配列を用いる】
【戻り値をそのまま使う引数や戻り値に配列を用いる】
public class Main {
// int型配列を受け取り、すべての要素を表示するメソッド
public static void printArray(int[] array) {
for( int element : array ){
System.out.println(element);
}
}
public static void main(String[] args){
int[] array = {1,2,3};
printArray(array); // 配列を渡す
}
}
+++++++++++++++++++++++++++実行結果++
1
2
3
++++++++++++++++++++++++++++++++++++
【戻り値に配列を用いる】
public class Main {
public static int[] makeArray(int size/*②①によりsizeに3を代入*/) {
int[] newArray = new int[size]; //③配列変数newArrayに3つの要素を代入
for( int i = 0; i < newArray.length; i++ ){ //④③newArrayの配列数分なので3回繰り返す
newArray[i] = i; //※繰り返される処理 ⑤newArray[0]に0を代入、newArray[1]に1を代入、newArray[2]に2を代入
}
return newArray; //⑥配列変数newArray{0, 1, 2}を返す
}
public static void main(String[] args){
int[] array = makeArray(3)/*①makeArrayメソッドに実引数3を渡して呼び出す結果を配列変数arrayに代入*/;
for(int i : array) { //⑥配列変数arrayを回す
System.out.println(i);※繰り返される処理 ⑦0, 1, 2を出力
}
}
}
+++++++++++++++++++++++++++実行結果++
0
1
2
++++++++++++++++++++++++++++++++++++
【コマンドライン引数】
public static void main(String[] args){
mainメソッドは文字列配列を引数として受け取るように定義されていて、
javaコマンドを使ってプログラムを実行する際に、プログラム名の後ろに引数を指定することができる。
パソコン名:~ ユーザー名$ java プログラム名 引数リスト(半角スペース区切り)
パソコン名:~ ユーザー名$ java main 菅原 湊 朝香
上記の例でプログラムを実行した場合、mainメソッドのargs[0]には「菅原」、args[1]には「湊」、args[2]には「朝香」が格納され、args.lengthは3となる。
【5章の練習問題】
public class Main {
public static void main(String[] args){
introduceOneself();
email("タイトル","test@gmail.com","テスト1");
email("test@gmail.com", "テスト2");
System.out.println("三角形の底辺の長さが10.0cm、高さが5.0cmの場合、面積は" + calcTriangleArea(10.0, 5.0) + "cm2");
System.out.println("円の半径が5.0cmの場合、面積は" + calcCircleArea(5.0) + "cm2");
}
public static void introduceOneself(){
String name = "name";
int age = 25;
double height = 160;
char gender = '男';
System.out.println("私の名前は" + name + "です。年齢は" + age + "歳です。身長は" + height + "cmです。性別は" + gender + "です。" );
}
public static void email(String title, String address, String text){
System.out.println("「" + address + "」に、以下のメールを送信しました");
System.out.println("件名:「" + title + "」");
System.out.println("本文:「" + text + "」");
}
public static void email(String address, String text){
System.out.println("「" + address + "」に、以下のメールを送信しました");
System.out.println("件名:無題");
System.out.println("本文:「" + text + "」");
}
public static double calcTriangleArea(double bottom, double height){
double ans = bottom * height / 2;
return ans;
}
public static double calcCircleArea(double radius){
double ans = radius * radius * 3.14;
return ans;
}
}
+++++++++++++++++++++++++++実行結果++
私の名前はnameです。年齢は25歳です。身長は160.0cmです。性別は男です。
「test@gmail.com」に、以下のメールを送信しました
件名:「タイトル」
本文:「テスト1」
「test@gmail.com」に、以下のメールを送信しました
件名:無題
本文:「テスト2」
三角形の底辺の長さが10.0cm、高さが5.0cmの場合、面積は25.0cm2
円の半径が5.0cmの場合、面積は78.5cm2
++++++++++++++++++++++++++++++++++++
付録A JDKを用いた開発
JDKのダウンロード及びインストールが終了したので
iTermでHello world!を表示させる。
①テキストエディタで以下のテキストを作成
ファイルの場所:Users/ユーザー名/Desktop/Java\ \ source\ codes/Java\ source\ no.1
ファイル名:Main.java
コード:
public class Main {
public static void main(String[] args){
System.out.println("Hello World!");
}
}
②iTermを起動
・cd /Users/ユーザー名/Desktop/Java\ \ source\ codes/Java\ source\ no.1
ファイル名:Main.javaを実行
・lsを実行
→Main.java と表示される。
・javac Main.javaを実行 【コンパイル】
・lsを実行
→Main.class Main.java と表示される。 【Main.classの作成を確認】
・java Main.java 【Javaの実行】
→Hello world!と表示される。
第6章 複数クラスを用いた開発
計算プログラムを以下のように
・出力担当
・計算担当
の2つに分割
public class Calc{
public static void main(String[] args) {
int a = 10;
int b = 2;
int total = CalcLogic.tasu(a, b);
int delta = CalcLogic.hiku(a, b);
System.out.println("足すと" + total + "、引くと" + delta);
}
}
public class CalcLogic{
public static int tasu(int a, int b){
return (a + b);
}
public static int hiku(int a, int b){
return (a - b);
}
}
パソコン名:~ ユーザー名$ cd /Users/ユーザー名/Desktop/Java\ \ source\ codes/Java\
source\ no.2
パソコン名:Java source no.2 ユーザー名$ ls
Calc.java CalcLogic.java
パソコン名:Java source no.2 ユーザー名$ javac Calc.java CalcLogic.java 【2つともコンパイル】
パソコン名:Java source no.2 ユーザー名$ ls
Calc.class Calc.java CalcLogic.class CalcLogic.java 【classファイルの作成を確認】
パソコン名:Java source no.2 ユーザー名$ java Calc 【mainクラスがあるファイルを実行する】
足すと12、引くと8 【結果】
上記のように、Javaのプログラムを実行するためには、mainメソッドが含まれているクラスファイルを指定する必要がある。
このため複数の完成クラスファイルを配布・納品するときには、すべてのクラスファイルを渡すのとあわせて、どのクラスにmainメソッドが含まれているかを伝える必要がある。
クラスが膨大な数になった場合、コンパイルの際に一つずつクラス名を入力するのは不可能そうだが、どういう方法があるのだろう。
パッケージの利用
クラス数が増えた場合、各クラスをパッケージ(package)と言うグループにまとめて管理・分類することが可能。
【package】
package 所属させたいパッケージ名;
※パッケージ文はソースコードの先頭に記載する必要がある。
【パッケージを呼び出す時(FQCN)】
あるクラスから別パッケージのクラスを利用する場合、下記のように、パッケージ名を頭につけた完全なクラス名を指定する必要がある。
完全なクラス名(FQCN)
パッケージ名.クラス名
先程の計算プログラムをそれぞれパッケージに所属させる場合
package calcapp.main; //所属パッケージ名を指定
public class Calc{
public static void main(String[] args) {
int a = 10;
int b = 2;
int total = calcapp.logics.CalcLogic.tasu(a, b);
//クラスを呼び出すときにも、パッケージ名を頭につけた完全なクラス名(FQCN)の指定が必要
int delta = calcapp.logics.CalcLogic.hiku(a, b);
//クラスを呼び出すときにも、パッケージ名を頭につけた完全なクラス名(FQCN)の指定が必要
System.out.println("足すと" + total + "、引くと" + delta);
}
}
package calcapp.logics; //所属パッケージ名を指定
public class CalcLogic{
public static int tasu(int a, int b){
return (a + b);
}
public static int hiku(int a, int b){
return (a - b);
}
}
推奨されるパッケージ名
パッケージ名の重複を避けるため、パッケージ名は
「保有するインターネットドメインを前後逆順にしたもの」
から始める。
例 ドメイン名:foo.example.com
パッケージ名:com.example.fooから始める
【import】
パッケージ名の入力を省略する方法
package calcapp.main;
import calcapp.logics.CalcLogic;
//CalcLogicという表記があったら、calcapp.logics.CalcLogicと解釈せよの意
import calcapp.logics.*;
//calcapp.logics内のすべてのクラスを一度にインポートするには上記の書き方も使える
public class Calc{
public static void main(String[] args) {
int a = 10;
int b = 2;
int total = CalcLogic.tasu(a, b);
//FQCNでなくてもエラーにならない
int delta = calcapp.logics.CalcLogic.hiku(a, b);
//FQCNで記述しても良い
System.out.println("足すと" + total + "、引くと" + delta);
}
}
クラスファイルを探す仕組み
Javaでは、使用したいクラスは、使用するときに、都度読み込まれるが、クラスファイルがどのフォルダにあるかということは指定しなくても実行ができるが、以下の方法でクラスファイルの場所として読み込みに行くべきフォルダの場所(クラスパス)を指定する事ができる。
【方法1:起動時にjavaコマンドで指定】 (/var/javadevをクラスパスとする場合)
パソコン名:~ ユーザー名$ javac -classpath /var/javadev
【方法2:検索場所をOSに登録しておく】 (/var/javadevをクラスパスとする場合)
パソコン名:~ ユーザー名$ export CLASSPATH =/var/javadev
なお、複数のクラスパスを指定する場合、macでは下記のように:(コロン)でつなぐ。
パソコン名:~ ユーザー名$ javac -classpath /var/javadev:/var/javadev2
【方法3:特に指定しない】
-classpathオプションの指定や、OSにクラスパスの指定をしなかった場合は、
javaコマンドが実行されたフォルダがクラスパスとなる。
→作成したプログラムが上手く起動できない場合は、クラスパスを確認する。
【クラスパスの指定について参考にしたわかりやすい記事】
http://kuwwta.hatenablog.com/entry/2016/05/12/141246
練習6−1
import comment.Zenhan;
public class Main {
public static void main(String[] args) throws Exception{
Zenhan.doWarusa();
Zenhan.doTogame();
comment.Kouhan.callDeae();
comment.Kouhan.showMondokoro();
}
}
package comment;
public class Zenhan {
public static void doWarusa() {
System.out.println("きなこでござる。食えませんがの。");
}
public static void doTogame() {
System.out.println("この老いぼれの目はごまかせませんぞ");
}
}
package comment;
public class Kouhan {
public static void callDeae() {
System.out.println("えぇい、こしゃくな。くせ者だ!であえい!");
}
public static void showMondokoro() throws Exception {
System.out.println("飛車さん、角さん。もういいでしょう。");
System.out.println("この紋所が目にはいらぬか!");
Thread.sleep(3000); //【練習6−3】
comment.Zenhan.doTogame();
}
}
練習6−2
パソコン名:~ ユーザー名$ cd /Users/ユーザー名/Desktop/Java\ \ source\ codes/Java\ source\ no.3
パソコン名:~ ユーザー名$ ls
Main.java comment
パソコン名:~ ユーザー名$javac Main.java Zenhan.java Kouhan.java
パソコン名:~ ユーザー名$ ls
Main.java comment
パソコン名:~ ユーザー名$ javac Main.java comment/Zenhan.java comment/Kouhan.java
パソコン名:~ ユーザー名$ ls
Main.class Main.java comment
パソコン名:~ ユーザー名$ java Main
きなこでござる。食えませんがの。
この老いぼれの目はごまかせませんぞ
えぇい、こしゃくな。くせ者だ!であえい!
飛車さん、角さん。もういいでしょう。
この紋所が目にはいらぬか!
この老いぼれの目はごまかせませんぞ
6−1で、ソースコードを3分割にするのは比較的すぐできたが、実際にプログラムを実行するまでにとても時間がかかった。
コンパイルは難なくできたが、javaコマンドを実行すると、下記のエラーが出て、お手上げであった。
パソコン名:~ ユーザー名$ javac Kouhan.java Main.java Zenhan.java
パソコン名:~ ユーザー名$ ls
Kouhan.class Main.class Zenhan.class
Kouhan.java Main.java Zenhan.java
パソコン名:~ ユーザー名$ java Main
Exception in thread "main" java.lang.NoClassDefFoundError: comment/Zenhan
at Main.main(Main.java:4)
Caused by: java.lang.ClassNotFoundException: comment.Zenhan
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:604)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
... 1 more
①エラーを頑張って読んでみる→よくわからなかった。
②エラーをコピペしてググる→同じ練習問題で躓いてる人が質問サイトで質問していたが、解決せず。
③第15章:例外を読んでみる→3つのエラーの種類があるところまでしか理解できず。
④練習問題の少し前を読み返すと、解説が書いてあり、解決。
解決まで1週間ほどかかってしまった。
パッケージに属したクラスの実行方法
【javaコマンドの正確な構文】
>java 起動したいクラスの完全限定クラス名(FQCN)
【クラスファイルの正しい配置】
今回のケース、commentパッケージに属するZenhanクラス及びKouhanクラスを実行するためには、
Mainクラスと同じ階層にcommentディレクトリを作成し、その中にZenhanクラス及びKouhanクラスを格納すると実行ができた。
つまり、パッケージ名のディレクトリの中に、そのパッケージに属するクラスファイルを保存しておく必要があった。
練習6−3
練習6−1のコード内
練習6−4
①c:¥work¥ex64の中にMain.classを配置
②c:¥work¥ex64¥commentの中にZenhan.class及びKouhan.classを配置
練習6−5
c:¥javaapp¥koumon
javaとjavacについて
javac実行時→.javaをつけて入力
「どのソースをコンパイルするか」をファイル名で指定して実行するため
java 実行時→拡張子は不要
「どのクラスのmainメソッドを起動するか」をクラス名(FQCN)で指定して実行するため
第7章 オブジェクト指向をはじめよう
オブジェクト指向は、「人間が把握しきれない複雑さ」を克服するために生まれた。
そもそもプログラムやシステムは現実世界における何らかの活動を自動化するためのものであり、
オブジェクト指向の本質は、**「現実世界の登場人物とその振る舞いを、コンピュータ内の仮想世界で再現すること」**である。
オブジェクト指向プログラミングでは、現実世界に出てくる登場人物(オブジェクト)の単位で、プログラムをクラスに分割する、そうして部品化したそれぞれの部品に登場人物の「責務」をプログラムとして書き込む。
◆オブジェクトの責務
・情報保持責任
・行動責任
オブジェクトはそれぞれの責務を果たすため、
・【属性】その登場人物に関する情報を覚えておく箱
・【操作】その登場人物が行う行動や動作の手順
を持っている。
◆オブジェクト指向の3大機能(詳しくは後で学習する)
・【カプセル化】属性や操作を、一部の相手からは利用禁止にする機能
・【継承】過去に作った部品を流用し、新しい部品を簡単に作れる機能
・【多様性】似ている2つの部品を「同じようなもの」とみなし、「いいかげん」に利用できる機能
第8章 インスタンスとクラス
インスタンス
インスタンスは、仮想世界で活動するオブジェクトであり、
インスタンスを生み出すための金型的なものがクラスである。
Javaプログラムの組成に必要なクラス
・mainメソッドを含む一つの「神様のクラス」
・現実世界の登場人物を模した複数の「登場人物のクラス」
属性の宣言方法【フィールド】
クラス内に宣言された変数をフィールドという。
public class Hero {
String name; //nameフィールドの宣言
int hp; //hpフィールドの宣言
}
定数フィールド
次のようにフィールド宣言と同時に初期値の設定も可能。
フィールド宣言の先頭にfinalをつけると、値の書き換えが不可能な定数フィールドとなる。
(定数フィールドの名前は大文字で記述する)
public class Hero {
String name; //nameフィールドの宣言
int hp = 100; //hpフィールドの宣言&初期値の設定
final int LEVEL = 10; //LEVELフィールド(定数フィールド)の宣言
}
操作の宣言方法【メソッド】
thisは「自分自身のインスタンス」を意味している。
.(ドット)は「の」という意味。
public class Hero {
String name;
int hp;
void sleep() { //【操作の名前:眠る】
this.hp = 100; //this.hpは自分自身のhpフィールドのこと【処理結果】
System.out.println(this.name + "は、眠って回復した!"); //this.nameは自分自身のnameフィールドのこと【処理結果】
}
}
クラス型変数
上記のように、Heroクラスを定義することで、「Heroクラスから仮想世界に生み出されたインスタンスを入れることができるHero型」が使用可能になる。
クラス型変数を使用することによって、仮想世界に複数存在しうる同名インスタンスの中から特定のインスタンスを識別する事ができる。
インスタンスの生成方法&フィールドへの値の代入
public class Main {
public static void main(String[] args) {
Hero h = new Hero(); //勇者インスタンスを生成
h.name = "ミナト"; //変数hのnameに代入
}
}
8章のRPGプログラム
public class Main {
public static void main(String[] args) {
Hero h = new Hero(); //勇者インスタンスを生成
h.name = "ミナト"; //変数hのnameに代入
h.hp = 100; //変数hのhpに代入
System.out.println("勇者" + h.name + "を生み出しました!");
h.sit(5); //5秒座れ
h.slip(); //転べ
h.sit(25); //25秒座れ
h.run(); //逃げろ
}
}
public class Hero {
String name;
int hp;
void sleep() { //【操作の名前:眠る】
this.hp = 100; //this.hpは自分自身のhpフィールドのこと【処理結果】
System.out.println(this.name + "は、眠って回復した!"); //this.nameは自分自身のnameフィールドのこと【処理結果】
}
void sit(int sec) { //【操作の名前:座る】
this.hp += sec;
System.out.println(this.name + "は、" + sec + "秒座った!");
System.out.println("HPが" + sec + "ポイント回復した。");
}
void slip() { //【操作の名前:転ぶ】
this.hp -= 5;
System.out.println(this.name + "は、転んだ!");
System.out.println("5のダメージ");
}
void run() { //【操作の名前:走る】
System.out.println(this.name + "は、逃げ出した!");
System.out.println("GAMEOVER");
System.out.println("最終HPは" + this.hp + "でした");
}
}
8章の練習問題
public class Main {
public static void main (String[] args) {
Cleric c = new Cleric();
c.name = "主人公";
System.out.println(c.name + "が誕生した!");
System.out.println("HP:" + c.hp);
System.out.println("MP:" + c.mp);
c.selfAid();
c.pray(6);
}
}
public class Cleric {
//【属性】
String name;
int hp = 50;
final int MAX_HP = 50;
int mp = 10;
final int MAX_MP = 10;
//【操作】
//セルフエイド
public void selfAid() {
System.out.println(this.name + "はセルフエイドを唱えた!");
this.hp = this.MAX_HP;
System.out.println("HPを全回復した。 HP:" + this.hp);
this.mp -= 5;
System.out.println("MPを5消費した。 MP:" + this.mp);
}
//祈り
public int pray(int second) {
System.out.println(this.name + "は" + second + "秒間祈りを捧げた!");
//MP回復分の計算(0〜2のランダム数+祈りの秒数)
int reflesh = new java.util.Random().nextInt(3) + second;
//MP回復処理
int after = this.mp + reflesh;
//最大MPを超えないようにする処理
if (after > this.MAX_MP) {
after = this.MAX_MP;
}
//回復した値を格納する変数の定義
int difference = after - this.mp;
System.out.println("MPを" + difference + "回復した。 MP:" + after);
return reflesh;
}
}
+++++++++++++++++++++++++++実行結果++
主人公が誕生した!
HP:50
MP:10
主人公はセルフエイドを唱えた!
HPを全回復した。 HP:50
MPを5消費した。 MP:5
主人公は6秒間祈りを捧げた!
MPを5回復した。 MP:10
++++++++++++++++++++++++++++++++++++
第9章 さまざまなクラス機構
ヒープ(heap)
Javaのプログラム実行時にJVMが準備するメモリ領域(通常数MB〜数GB)
イメージ的には、Javaのプログラムにより作り出される仮想世界で、
インスタンスが生成されるたびにその情報を格納するためにヒープの一部の領域が確保(通常数十〜数百バイト)される。つまり、インスタンスとは、ヒープの中の1部のメモリ領域のことである。
参照の解決
「クラス型」も「配列型」と同じように、「実際のデータが保存されているメモリ領域の先頭番地」が変数に入っている。
変数から番地地黄法を取り出し、実際にその番地にアクセスする。という処理を参照の解決またはアドレス解決という。
インスタンスの独立性
同じクラスから生まれても、異なるインスタンスは互いに影響を受けない。
クラス型をフィールドに用いる
リスト9−3
public class Sword {
String name; // 剣の名前
int damage; // 剣の攻撃力
}
public class Hero {
String name;
int hp;
Sword sword; //勇者が装備している剣の情報
void attack(){
System.out.println(this.name + "は攻撃した!");
System.out.println("敵に5ポイントのダメージをあたえた!");
}
void sleep() { /* 眠る(sleepメソッド) */
this.hp = 100;
System.out.println(this.name + "は、眠って回復した!");
}
/* 何秒座るか引数で受け取る */
void sit(int sec) { /* 座る(sitメソッド) */
this.hp += sec; /* 座る秒数だけHPを増やす */
System.out.println(this.name + "は、" + sec + "秒すわった!");
System.out.println("HPが" + sec + "ポイント回復した.");
}
void slip() { /* 転ぶ(slipメソッド) */
this.hp -= 5;
System.out.println(this.name + "は転んだ!");
System.out.println("5のダメージ!");
}
void run() { /* 逃げる(runメソッド) */
System.out.println(this.name + "は、逃げ出した!");
System.out.println("GAMEOVER");
System.out.println("最終HPは" + this.hp + "でした");
}
}
リスト9−4
public class Main {
public static void main(String[] args) {
Sword s = new Sword();
s.name = "炎の剣";
s.damage = 10;
Hero h = new Hero();
h.name = "ミナト";
h.hp = 100;
h.sword = s; // swordフィールドに生成済みの剣インスタンス(の番地)を代入
System.out.println("現在の武器は" + h.sword.name);
// ↑ 勇者「の」剣「の」名前
}
}
上記のHero.java4行目のように、「あるクラスが別のクラスをフィールドとして利用している関係」をhas-aの関係という。
Hero has-a Swordとなる。(勇者は剣を持っている)
変数hには、勇者インスタンスのアドレス情報が入っている。勇者インスタンスにはSword領域があり、剣インスタンスのアドレス情報が格納されている。
クラス型をメソッド引数や戻り値に用いる
リスト9−5
public class Wizard {
String name;
int hp;
void heal(Hero h) { // 引数はHero型
h.hp += 10; // 勇者のHPに10を加える
System.out.println(h.name + "のHPを10回復した!");
}
}
リスト9−6
public class Main {
public static void main(String[] args) {
Hero h1 = new Hero();
h1.name = "ミナト";
h1.hp = 100;
Hero h2 = new Hero();
h2.name = "アサカ";
h2.hp = 100;
Wizard w = new Wizard();
w.name = "スガワラ";
w.hp = 50;
w.heal(h1); // ミナトを回復させる(HP: 100 -> 110)
w.heal(h2); // アサカを回復させる(HP: 100 -> 110)
w.heal(h2); // アサカを回復させる(HP: 110 -> 120)
}
}
String型の真実
String型は、Hero型と同じ「クラス型」である。
java.langパッケージに属するStringクラスであるため、インポート不要で利用できる。
コンストラクタ
フィールド初期値を自動設定する
リスト9−8
public class Hero {
int hp;
String name;
Sword sword;
void attack(){
System.out.println(this.name + "は攻撃した!");
System.out.println("敵に5ポイントのダメージをあたえた!");
}
void sleep() { /* 眠る(sleepメソッド) */
this.hp = 100;
System.out.println(this.name + "は、眠って回復した!");
}
/* 何秒座るか引数で受け取る */
void sit(int sec) { /* 座る(sitメソッド) */
this.hp += sec; /* 座る秒数だけHPを増やす */
System.out.println(this.name + "は、" + sec + "秒すわった!");
System.out.println("HPが" + sec + "ポイント回復した.");
}
void slip() { /* 転ぶ(slipメソッド) */
this.hp -= 5;
System.out.println(this.name + "は転んだ!");
System.out.println("5のダメージ!");
}
void run() { /* 逃げる(runメソッド) */
System.out.println(this.name + "は、逃げ出した!");
System.out.println("GAMEOVER");
System.out.println("最終HPは" + this.hp + "でした");
}
// 「このクラスがnewされた直後に自動的に実行される処理」を書いたメソッド
Hero(){
this.hp = 100; // 自分自身のhpフィールドを100で初期化
}
}
上記プログラムには、Hero()メソッドがあり、Heroクラスがnewされた直後に自動的に実行される。
このようなメソッドをコンストラクタという。
※コンストラクタはh.Hero();のように直接呼び出すことはできない。
コンストラクタとみなされる条件
①メソッド名がクラス名と完全に等しい
②メソッド宣言に戻り値が宣言されていない(void)も不可
コンストラクタに情報を渡す
リスト9−10
public class Hero {
int hp;
String name;
Sword sword;
void attack(){
System.out.println(this.name + "は攻撃した!");
System.out.println("敵に5ポイントのダメージをあたえた!");
}
void sleep() { /* 眠る(sleepメソッド) */
this.hp = 100;
System.out.println(this.name + "は、眠って回復した!");
}
/* 何秒座るか引数で受け取る */
void sit(int sec) { /* 座る(sitメソッド) */
this.hp += sec; /* 座る秒数だけHPを増やす */
System.out.println(this.name + "は、" + sec + "秒すわった!");
System.out.println("HPが" + sec + "ポイント回復した.");
}
void slip() { /* 転ぶ(slipメソッド) */
this.hp -= 5;
System.out.println(this.name + "は転んだ!");
System.out.println("5のダメージ!");
}
void run() { /* 逃げる(runメソッド) */
System.out.println(this.name + "は、逃げ出した!");
System.out.println("GAMEOVER");
System.out.println("最終HPは" + this.hp + "でした");
}
Hero(String name){ // 引数として文字列を1つ受け取る
this.hp = 100;
this.name = name; // 引数の値でnameフィールドを初期化
}
}
リスト9−11
public class Main {
public static void main(String[] args) {
Hero h = new Hero("ミナト"); // こう書いておけばコンストラクタには「ミナトが渡される
System.out.println(h.hp); // 100と表示される
System.out.println(h.name); // ミナトと表示される
}
}
リスト9−12
public class Hero {
int hp;
String name;
Sword sword;
void attack(){
System.out.println(this.name + "は攻撃した!");
System.out.println("敵に5ポイントのダメージをあたえた!");
}
void sleep() { /* 眠る(sleepメソッド) */
this.hp = 100;
System.out.println(this.name + "は、眠って回復した!");
}
/* 何秒座るか引数で受け取る */
void sit(int sec) { /* 座る(sitメソッド) */
this.hp += sec; /* 座る秒数だけHPを増やす */
System.out.println(this.name + "は、" + sec + "秒すわった!");
System.out.println("HPが" + sec + "ポイント回復した.");
}
void slip() { /* 転ぶ(slipメソッド) */
this.hp -= 5;
System.out.println(this.name + "は転んだ!");
System.out.println("5のダメージ!");
}
void run() { /* 逃げる(runメソッド) */
System.out.println(this.name + "は、逃げ出した!");
System.out.println("GAMEOVER");
System.out.println("最終HPは" + this.hp + "でした");
}
Hero(String name){ // 以前からあったコンストラクタ1
this.hp = 100;
this.name = name;
}
Hero(){ // 新しく作ったコンストラクタ2
this.hp = 100;
this.name = "名無しさん"; // ダミーの名前を設定する
}
}
コンストラクタに引数を指定しなくてもエラーにならないように、引数がないコンストラクタも作成しておく。
リスト9−13
public class Main {
public static void main(String[] args) {
Hero h1 = new Hero("ミナト"); // 文字列引数があるのでコンストラクタ1が呼び出される
System.out.println(h1.name); // 画面に「ミナト」と表示される
Hero h2 = new Hero(); // 引数ないのでコンストラクタ2が呼び出される
System.out.println(h2.name); // 画面に「名無しさん」と表示される
}
}
本来、すべてのクラスは必ず何らかのコンストラクタを実行する事になっている。
コンストラクタを1つも定義していないクラスに限り、引数がなく、何も処理をしないコンストラクタ(デフォルトコンストラクタ)の定義がコンパイル時に自動的に追加される。。
同一クラスの別コンストラクタの呼び出しに関するルール
「this.クラス名(引数);」と記述することはできない。
「this(引数);」と記述する。
リスト9−16
Hero(String name){ // 以前からあったコンストラクタ1
this.hp = 100;
this.name = name;
}
Hero(){ // 新しく作ったコンストラクタ2
this("ダミー"); // コンストラクタ1を呼び出す
}
}
静的メンバ(static member)
リスト9-18
public class Hero {
int hp;
String name;
static int money; // 静的フィールド
︙
}
静的フィールド
staticキーワードを指定したフィールドは静的フィールドという。
静的フィールドの効果
①フィールド変数の実態がクラスに準備される
※インスタンスではなく、クラスに対して準備される変数のため、使用するには、
クラス名.静的フィールド名と記述する。
②全インスタンスに、静的フィールドの分身が準備される
※インスタンス変数名.静的フィールド名と書いても、
クラス名.静的フィールド名と同じ意味になる。
③インスタンスを一つも生み出さなくても使用可能
※静的フィールドは、クラスに所属するフィールドであるため、クラス変数ということもある。
静的メソッド
staticキーワードがついているメソッドは、静的メソッドまたはクラスメソッドと呼ばれ、
静的フィールドとあわせて静的メンバと総称される
静的メソッドの効果
①メソッド事態がクラスに属するようになる
※インスタンスではなく、クラスに属するメソッドとなり、使用するには、
**クラス名.静的メソッド名();**と記述する。
②全インスタンスに、静的メソッドの分身が準備される
※**インスタンス変数名.静的メソッド名();**と書いても、
**クラス名.静的メソッド名();**と同じ意味になる。
③インスタンスを一つも生み出さなくても使用可能
静的メソッドの成約
静的メソッドの中に記述するコードでは、staticがついていないフィールドやメソッドは利用できない。
※静的メソッドは、一つもインスタンスがない状況でも呼び出される可能性があるメソッドであり、
その状況ではインスタンスに所属するフィールドやメソッドを記述しても処理できないため。
9章の練習問題
public class Main {
public static void main (String[] args) {
Cleric c = new Cleric("john", 40, 8);
System.out.println(c.name + "が誕生した!");
System.out.println("HP:" + c.hp);
System.out.println("MP:" + c.mp);
c.selfAid();
c.pray(6);
}
}
public class Cleric {
//【属性】
String name;
int hp;
static final int MAX_HP = 50; //①静的フィールドの指定
int mp;
static final int MAX_MP = 10; //①静的フィールドの指定
//【コンストラクタ】
Cleric(String name, int hp, int mp){ //2 A)名前・HP・MPを指定してインスタンス化
this.name = name;
this.hp = hp;
this.mp = mp;
}
Cleric(String name, int hp){ //2 B)名前・HPを指定してインスタンス化
this(name, hp, Cleric.MAX_MP); //上の2 A)のコンストラクタの呼び出し+ClericクラスのMAX_MP(静的フィールド)にアクセス
}
Cleric(String name){ //2 C)名前・HPを指定してインスタンス化
this(name, Cleric.MAX_HP); //上の2 B)のコンストラクタの呼び出し+ClericクラスのMAX_HP(静的フィールド)にアクセス
}
//【操作】
//セルフエイド
public void selfAid() {
System.out.println(this.name + "はセルフエイドを唱えた!");
this.hp = this.MAX_HP;
System.out.println("HPを全回復した。 HP:" + this.hp);
this.mp -= 5;
System.out.println("MPを5消費した。 MP:" + this.mp);
}
//祈り
public int pray(int second) {
System.out.println(this.name + "は" + second + "秒間祈りを捧げた!");
//MP回復分の計算(0〜2のランダム数+祈りの秒数)
int reflesh = new java.util.Random().nextInt(3) + second;
//MP回復処理
int after = this.mp + reflesh;
//最大MPを超えないようにする処理
if (after > this.MAX_MP) {
after = this.MAX_MP;
}
//回復した値を格納する変数の定義
int difference = after - this.mp;
System.out.println("MPを" + difference + "回復した。 MP:" + after);
return reflesh;
}
}
+++++++++++++++++++++++++++実行結果++
johnが誕生した!
HP:40
MP:8
johnはセルフエイドを唱えた!
HPを全回復した。 HP:50
MPを5消費した。 MP:3
johnは6秒間祈りを捧げた!
MPを6回復した。 MP:9
++++++++++++++++++++++++++++++++++++
別コンストラクタの呼び出しを理解するのに苦労した。
今でも少し曖昧かもしれない。
【クラスパスの指定について参考にしたわかりやすい記事】
Java Master > Java コンストラクタの使い方と概要
http://kuwwta.hatenablog.com/entry/2016/05/12/141246
第10章 カプセル化
メンバ(フィールドおよびメソッド)に対する4つのアクセス制御レベル
・private アクセス修飾子:private
自分自身のクラスのみアクセス許可
※ただし、自分のクラスからthis.〜での読み書きは可能
・package private アクセス修飾子:何も書かない
自分と同じパッケージに属するクラスをアクセス許可
・protected アクセス修飾子:protected
自分と同じパッケージに属するか、自分を継承した子クラスをアクセス許可
・public アクセス修飾子:public
すべてのクラスをアクセス許可
フィールドのアクセス制御
アクセス修飾子 フィールド宣言;
メソッドのアクセス制御
アクセス修飾子 メソッド宣言{・・・}
【メンバに関するアクセス修飾の定石】
・フィールドはすべてprivate
・メソッドはすべてpublic
・クラスは特に理由がない限りpublic
【getterとsetter】
getterメソッド
自分のクラスのフィールドを他クラスから呼び出せるようにするために、
フィールドの中身を返すだけのメソッドをgetterメソッドと言う。
getterメソッドの定石
public 値を取り出すフィールドの型 getフィールド名() {
return this.フィールド名;
}
setterメソッド
ある特定のフィールドに、指定された値を代入するだけの
メソッドをgetterメソッドと言う。
setterメソッドの定石
public void setフィールド名(フィールドの型 任意の変数名) {
this.フィールド名 = 任意の変数名;
}
カプセル化のメリット
1.ReadOnly、WriteOnlyのフィールドを用意できる。
外部から読めるが書き換えられないReadOnlyフィールドと、
外部から自由に書き換えできるが、読めないwriteOnlyフィールドを作成可能。
2.クラスの内部設計を自由に変更できる。
他クラスからのフィールドの読み書きはgetter/setterを使用するため、
元々のフィールド名を自由に変更できる。
3.フィールドへのアクセスを検査できる。
元々のフィールド情報の書き換えはsetterを通さないとできないため、
setterで設定されている値が妥当かの検査ができる。
クラスに対する4つのアクセス制御レベル
・package private アクセス修飾子:何も書かない
自分と同じパッケージに属するクラスのみ許可
・public アクセス修飾子:public
すべてのクラスを許可
非publicクラス(package privateクラス)の特徴
①クラス名がソースファイルと異なっても良い
②1つのソースファイルに複数宣言しても良い
第11章 継承
継承を用いたクラスの定義
class クラス名 extends 元となるクラス名 {
親クラスとの差分メンバ
}
☆継承された親クラス(スーパークラス)と子クラス(サブクラス)の関係を継承関係という。
※Javaでは、複数のクラスを親として1つの子クラスを定義すること(多重継承)はできない。
オーバーライド
親クラスのメンバを子クラス側で再定義(上書き)することを**オーバーライド(override)**という。
つまり、子クラスに宣言されたメンバは、
①親クラスに同じメンバがなければ、そのメンバは「追加」になる。
②親クラスに同じメンバがあれば、そのメンバは「上書き変更」される。
継承やオーバーライドの禁止
◆クラス宣言にfinalをつけると、継承禁止
◆メソッド宣言にfinalをつけると、オーバーライド禁止
親インスタンス部へのアクセス
◆親インスタンス部のフィールドを利用する
super.フィールド名
◆親インスタンス部のメソッドを呼び出す
super.メソッド名(引数)
※祖父母クラス、親クラス、子クラスというふうな3重構造の継承関係のとき、
子クラスから祖父母クラスへの直接のアクセスはできない。
親クラスのコンストラクタの呼び出し
Javaでは、すべてのコンストラクタは、その先頭で必ず内部インスタンス部(=親クラス)
のコンストラクタを呼び出さなければならないというルールがあるため、
子クラスのコンストラクタを定義する際、最初の行に次のような記述をする必要がある。
つまり、コンストラクタは、内側のインスタンス部分のものから順に呼び出される。
super(引数);
※コンストラクタの最初の行にしか記述できない
※記述していない場合、コンパイラによって自動的に挿入される
このため、親クラスのコンストラクタに引数が指定されている場合は、
小クラスのsuper()に引数を入力しないとエラーになる
正しい継承、間違った継承
####is-aの原則
子クラスis-a親クラス(小クラスは、親クラスの一種である)
例えば、名前や値段のフィールドを持つItemクラスを継承して住所や間取り、家の名前や家の値段フィールド
を持つHouseクラスを作ることは原理上可能だが、is-aの原則に当てはめると、「家はアイテムの一種です」
というのは不自然だ。
◆将来、クラスを拡張していった場合に現実世界との矛盾が生じる(オブジェクト指向の原則から外れる)
◆オブジェクト指向の3大機能の1つである「多様性」を利用できなくなる
上記の理由から、is-aの原則が成立しない場合は、継承を使ってはならない。
第12章 高度な継承
抽象クラス
未来の開発者のために、継承の材料となるクラスを作成するとき、下記の危険性がある。
①未来の開発者が詳細未定メソッドのオーバーライドを忘れる危険
②何もしないメソッドとの区別がつかない
③詳細未定メソッド(抽象メソッド)が含まれているのに、newされてしまう危険
そのために、Javaには詳細未定メソッドを記述する構文がある。
詳細未定メソッド(抽象メソッド)の宣言
アクセス修飾子 abstract 戻り値 メソッド名(引数リスト);
抽象メソッドを含むクラス(抽象クラス)の宣言
アクセス修飾子 abstract class クラス名{}
抽象メソッドを1つでも含むクラスは、このようにabstractをつけた構文で宣言しなければならない。
そして、抽象クラスはnewによるインスタンスが禁止される
このような仕組みがあるため、上記の①〜③の危険性はない。
①abstractがついた抽象クラスを継承した場合、親クラスの抽象メソッドも継承されているため、
子クラスも抽象クラスとして宣言するか、その抽象メソッドをオーバーライドしなければエラーとなる
そのため、未来の開発者が抽象メソッドのオーバーライドを忘れる危険はない。
②abstractによって、詳細未定な抽象メソッドと判断できる
③抽象クラスはnewによるインスタンスができない
インターフェース
下記の条件を満たすクラスをインターフェースとして定義することができる。
①すべてのメソッドが抽象メソッド
②基本的にフィールドを1つも持たない
※ただし、public static finalがついた定数フィールドだけは宣言が許される
その場合、public static finalを省略できる。
インターフェースの宣言
アクセス修飾子 interface インターフェース名{}
そして、インターフェースに宣言されたメソッドは、自動的にpublic abstractとなるので、
public abstractを省略できる。
インターフェースの実装
アクセス修飾子 class クラス名 implements インターフェース名{}
インターフェースの効果
①同じインターフェースをimplementsする複数の子クラスに、
共通のメソッド群を実装するように強制できる
②あるクラスがインターフェースを実装していれば、
少なくともそのインターフェースが定めたメソッドは持っていることが保証される
インターフェースによる多重継承
異なる実装が衝突する問題が発生しないため、複数の親インターフェースによる多重継承は認められている
アクセス修飾子 class クラス名 implements 親インターフェース名1,親インターフェース名2, {}
インターフェースの継承
既存のインターフェースを拡張子して新しいインターフェースを定義することもできる。
public interface インターフェース名 extends 既存インターフェース名 {}
extendsとimplementsの両方を使ったクラス定義
アクセス修飾子 class クラス名 extends 親クラス implements 親インターフェース1, 親インターフェース2,{}
public class Fool extends Character implements Human {
// CharacterからhpやgetName()等のメンバを継承している
// Characterから継承した抽象メソッド attack()を実装
public void attack(Matango m){
System.out.println(this.getName() + "は、戦わずに遊んでいる。");
}
// さらにHumanから継承した4つの抽象メソッドを実装
public void talk() {/* ... */}
public void watch() {/* ... */}
public void hear() {/* ... */}
public void run() {/* ... */}
}
インターフェースメソッドのデフォルト実装
default 戻り値 メソッド名(引数){
処理のデフォルト実装
}
このようにして定義された抽象メソッドは、継承先でオーバーライドされなかった場合、
デフォルト実装として定めた処理内容でオーバーライドされたものとみなされる。
つまりnewでインスタンス化できる。
第13章 多様性
抽象クラスやインターフェースの型
抽象クラスやインターフェースからインスタンスを生み出すことはできないが、
型を利用することはできる。
→小クラスのインスタンスは親クラスの型に代入可能
親クラスの型 A = new 子クラス();
親クラスの型に代入した小クラスのインスタンスは、
親クラスが持っているメソッドしか利用できない。
「箱の型」は、どのメソッドを「呼べるか」を決定する。
「中身の型」は、メソッドが呼ばれたら、「どう動くか」を決定する。
子クラスのメソッドを呼び出すために、親クラスの型に入っているインスタンスを
子クラスのものとして再認識させるには、キャストを使う。
今回のように、「親クラスに入っている子クラスのインスタンスを、子クラスの型に代入する」場合、
ダウンキャストという。
子クラス 任意インスタンス名 = (子クラスの型) 親クラスに入っている子クラスのインスタンス;
キャストの失敗
親クラスに入っている子クラスのインスタンスを、ダウンキャストを使って
別の子クラスの型に代入しようとした場合、コンパイルは成功するが、動作させると、
ClassCastExceptionというエラーが発生する。
インスタンスを代入可能かチェックする
ClassCastExceptionを回避するために、安全にキャストできるかを判定するために
instanceof演算子というものがある。
変数 instanceof 型名
例)
if (子クラスのインスタンス instanceof 子クラスの型){
子クラスの型 任意インスタンス名 = (子クラスの型) 子クラスのインスタンス;
}
同一視して配列を利用する
public final class A extends Y {
public void a( ) { System.out.print("Aa"); }
public void b( ) { System.out.print("Ab"); }
public void c( ) { System.out.print("Ac"); }
}
public class B extends Y {
public void a( ) { System.out.print("Ba"); }
public void b( ) { System.out.print("Bb"); }
public void c( ) { System.out.print("Bc"); }
}
public interface X {
void a( );
}
public abstract class Y implements X{
public abstract void a( );
public abstract void b( );
}
public class Main {
public static void main(String[] args) {
Y[] array = new Y[2]; //Y(親要素)を配列変数として、まとめて扱うための配列
array[0] = new A(); //AクラスをYとして、まとめて扱う
array[1] = new B(); //BクラスをYとして、まとめて扱う
for(Y y : array){ //配列を回す
y.b(); //それぞれのインスタンスのbが実行される
}
}
}
第14章 Javaを支える標準クラス
日付を扱う
Javaでは、日付情報を表すために使う型が1つではない。
形式1:long型の数値
基本日時である1970年1月1日0分0秒(エポック)から経過したミリ秒(1/1000秒)で日時情報を表現する方法。
例えば、1316622225935というlong値は、「2011年9月22日1時23分45秒」を意味する。
System.currentTimeMillis()メソッドを呼べば、現在日時をlong型で得られるため、
次のような「処理時間の計測」を簡単に行える。
public class Main {
public static void main(String[] args){
long start = System.currentTimeMillis();
/* ここでなんらかの時間がかかる処理 */
long end = System.currentTimeMillis();
System.out.println("処理にかかった時間は..." +
(end-start) + "ミリ秒でした");
}
}
形式2:Date型のインスタンス java.util.Dateクラス
Javaで日時情報を扱い場合に最も利用される形式。
現在日時を持つDateインスタンスの生成
Date d = new Date();
指定時点の日時を持つDateインスタンスの生成
Date d = new Date(long 値);
import java.util.Date;
public class Main{
public static void main(String[] args){
Date now = new Date();
System.out.println(now);
System.out.println(now.getTime());
Date past = new Date(1316622225935L);
System.out.println(past);
}
}
形式3:人間が読みやすいString型のインスタンス
人間にとって読みやすいのは「2011年9月22日1時23分45秒」のような文字列だが、
場合によって、「2011/9/22 1:23:45」等、違う表示形式を取りたい場合もある。
形式4:人間が指定しやすい「6つのint」形式
ある特定の日時情報を人間が入力する場合には、「年・月・日・時・分・秒」をそれぞれ整数(int値)として指定することが一般的。
Calendarクラスの利用 java.util.Calendarクラス
Date型と、6つのint値の相互変換
◆6つのint値からDateインスタンスを生成する方法
Calendar c = Calendar.getInstance();
c.set(年,月,日,時,分,秒);
またはc.set(calendar.YEAR,MONTH,DAY_OF_MONTH,HOUR,MINUTE,DAY);
Date d = c.getTime();
◆Dateインスタンスから6つのint値を生成する方法
Calendar c = Calendar.getInstance();
c.setTime(d); //dはDate型変数
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH);
int day = c.get(Calendar.DAY);
int hour = c.get(Calendar.HOUR);
int minute = c.get(Calendar.MINUTE);
int second = c.get(Calendar.SECOND);
import java.util.Calendar;
import java.util.Date;
public class Main {
public static void main(String[] args) {
// 現在の年を表示する
Date now = new Date();
Calendar c = Calendar.getInstance();
c.setTime(now);
int y = c.get(Calendar.YEAR);
System.out.println("今年は" + y + "年です");
// 指定した日のDate型の値を得る
c.set(2010, 8, 22, 1, 23, 45);
c.set(Calendar.YEAR, 2011);
Date past = c.getTime();
}
}
※Calendarを用いて「月」の情報を取得・設定する場合、
1〜12ではなく、0〜11で指定する。
2月を設定したい場合「c.set(Calendar.MONTH,1)」とする。
SimpleDateFormatクラスの利用 java.util.SimpleDateFormatクラス
◆DateからStringを生成する方法
SimpleDateFormat f = new SimpleDateFormat(※"yyyy/MM/dd");
String s = f.format(d); //dはDate型変数
◆StringからDateを生成する方法
SimpleDateFormat f =new SimpleDateFormat(※"yyyy年MM月dd日");
Date d = f.parse(文字列);
※"yyyy/MM/dd"は書式文字列という。
import java.text.SimpleDateFormat;
import java.util.Date;
public class Main {
public static void main(String[] args) throws Exception {
// 本日の日時を表示する
Date now = new Date();
SimpleDateFormat f = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
String s = f.format(now);
System.out.println(s);
// 指定日時の文字列を解析しDate型として得る
Date d = f.parse("2011/09/22 01:23:45");
}
}
書式文字列として利用可能な文字
文字 | 意味 |
---|---|
y | 年 |
M | 月 |
d | 日 |
E | 曜日 |
a/p | 午前/午後 |
H | 時(0〜23) |
K | 時(0〜11) |
m | 分 |
s | 秒 |
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class Main {
public static void main(String[] args){
Date now = new Date(); //①現在の日付をDate型で取得
Calendar c = Calendar.getInstance();
c.setTime(now); //②取得した日時をCalendarにセット
int day = c.get(Calendar.DAY_OF_MONTH); //③Calendarから「日」を取得
day += 100; //④取得した「日」に100をプラス
c.set(Calendar.DAY_OF_MONTH, day); //④取得した「日」に100をプラスした値をCalendarの「日」にセット
Date date = c.getTime(); //⑤Calendarの日付情報をDate型に変換
SimpleDateFormat result = new SimpleDateFormat("西暦yyyy年MM月dd日");
String anser = result.format(date); ⑥//SimpleDateFormatを用いて、Dateインスタンスの内容をanserに代入
System.out.println(anser); //結果を表示
}
}
第15章 例外
【3種類の不具合と対処方法】
【Syntax Error】文法エラー
セミコロン忘れや、変数名の間違い、privateメソッドを外部から呼び出すなど、
文法の誤りによりコンパイルが失敗するエラー。
コンパイル時にエラーが発生するためコンパイル時に気づく。
コンパイラが指摘したコードの箇所を修正して解決する。
>javac SyntaxError.java
SyntaxError.java4:';'がありません。
}
^
エラー1個
【Runtime Error】実行時エラー
配列の範囲外要素へのアクセス、0での割り算、存在しないファイルのオープンなど、
文法の問題がなく、コンパイルも実行もできるが、実行中に何らかの異常事態が発生し、
エラーメッセージが表示されて強制終了するエラー。
実行すると途中で終了するため、そこで気づく
予め「エラーが発生したときの対応策」を記述しておく。
>javac RuntimeError.java
>java RuntimeError
プログラムを開始します。処理を3つ実行します。
処理1を完了。
処理2を完了。
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsExcepsion: 17
at RuntimeError.main(RuntimeError.java:11)
【LogicalError】論理エラー
Javaの文法に問題はなく、強制終了もしないが、プログラムの実行結果が想定と違う場合のエラー。
実行後に、想定外の処理結果となり気づく。
原因箇所を自力で探し、コードを修正して解決する。
>javac LogicalError.java
>java LogicalError
プログラムを開始します。
3+5を計算します。
計算完了: 答えは35
プログラムを正常終了します。
【例外的状況】
実行時エラーの際「プログラム実行中に発生した想定外の事態」
を例外的状況(exceptionalsituation)または例外(exception)という。
例
◆パソコンのメモリがたりなくなった
◆存在すべきファイルが見つからない
◆nullが入っている変数を利用しようとした
例外的状況に備えて準備した対策を、その状態に陥った場合に実施することを
例外処理(exception handing)という。
例外処理
例外的状況発生(プログラム実行時にエラーが発生)時に行う処理。
try-catch文
例外が発生する可能性がある処理に使う。try-catchを使うと、例外が発生しない場合の処理、例外が発生したときの処理を分けることができる。
※finallyを使って例外の有無に関わらず必ず実行される処理を記述することもできる
try {
//例外が発生する可能性のある処理
} catch (例外クラス 変数名) {
//例外が発生した場合の処理(例外が発生しなければ行われない処理)
} finally {
//例外の有無に関わらず、最後に必ず実行される処理
}
例外の種類
①Error系例外
java.lang.Errorの子孫。
回復の見込みがない致命的な状況を表すクラス。
例)OutOfMemoryError(メモリ不足)
ClassFormatError(クラスファイルが壊れている)
※通常はこれらのエラーをキャッチところで打つ手がなく、キャッチする必要はない。
②Exception系例外
java.lang.Exceptionの子孫(RuntimeExceptionを除く)
その発生を十分に想定して対処を考える必要がある例外的状況を表すクラス。
例)IOException(ファイルなどが読み書きできない)
ConnectException(ネットワークに接続できない)
※try-catch文でキャッチしないとコンパイルエラーとなる
③RuntimeException系例外
java.lang.Exceptionクラスの子孫。
必ずしも常に発生を想定すべきとまでは言えない例外的状況を表すクラス。
例)NullPointerException(変数がnullである)
ArrayIndexOutOfBoundsException(配列の添字)
※いちいち想定しているときりがないようなものが多い
try-catch文でキャッチするかは任意
例外の伝播
例)Mainメソッドがsub()メソッドを呼び出し、さらにsub()メソッドがsubsub()メソッドを呼び出す
プログラムがあり、そのsubsub()メソッド内で例外が発生した場合、
subsub()メソッドにtry-catch文がなければ呼び出し元のsub()メソッドに例外の対応が求められ、
sub()メソッドにもtry-catch文がなければ呼び出し元のMainメソッドに対応が求められ、
Mainメソッドにもtry-catch文がなければ強制終了する。
上記のような減少を例外の伝播という。
Exception系例外に関しては、try-catch文がないとコンパイルエラーとなるため、
基本的に例外の伝播は起こらないが、**スロー宣言(throws)**を行うことで、発生するチェック例外を
呼び出し元へと伝播させることができる。
スロー宣言による例外伝播の許可
アクセス修飾 戻り値 メソッド名(引数リスト)
throws例外クラス1、例外クラス2///{
メソッドの処理内容
}
このようにスロー宣言を行うと、IOExceptionのような例外が発生する可能性がある場合でも
コンパイルエラーにはならない。
※スロー宣言を行ったメソッドの呼び出し元のメソッドに、try-catchを記述する義務が生まれる
例外インスタンスを自分で投げる
throw new 例外クラス名("エラーメッセージ");
下記のプログラムのように、例外をチェックするプログラムを作成することもできる。
public class Person{
int age;
public void setAge(int age) {
if(age < 0) { // ここで引数をチェック
throw new IllegalArgumentException("年齢は正の数を指定すべきです。指定値=" + age);
}
this.age = age; // 問題ないなら、フィールドに値をセット
}
}
public class Main{
public static void main(String[] args){
Person p = new Person();
p.setAge(-128); //誤った値のセットを試みる→例外発生
}
}