はじめてのJava勉強@超基礎的な部分のメモ

はじめてのJavaの勉強メモ

参考書籍

わかりやすいJava入門編

他の参考書籍よりかなり細かいことを易しく書いてある感じがする本。 丁寧で分かりやすく、練習問題などもあるし学習にいい感じ。 でも初心者程度にプログラムが分かるならカンタンすぎるかも。 私はPHPとJavascriptをすこし知ってる程度だけど、 Javaはこいつらと違ってかなり堅い感じなので、 これくらい丁寧な内容の書籍でよかったかな?分からん。

プログラミング言語とJava

プログラミング言語はコンピュータに指図するための人工的な言語。 プログラミング言語で命令すれば俺の言うがままにコンピュータは動いてくれるのだ!

世界初のFORTRAN、事務用のCOBOLなどが初期のプログラム言語。

でもプログラムが書かれるといろいろ問題が出てきて、構造化プログラミング言語(ALOGL、Pascal、Cなど)、オブジェクト指向言語(Simula、Smalltalk、C++、Javaなど)と改良されていった。で、Javaはサーブレットとして最初に使用できるようになったオブジェクト指向言語らしい。

JavaはJCPにより標準化されている。JavaバイトコードにコンパイルしたものをJavaVMにより実行する。マルチスレッド昨日、国際化機能、JDBC、GCなどの機能を持つ。

メモ:サブルーチン、構造化定理、ALGOL、Pascal、C、Simula、Smalltalk

データ型、変数

基本データ型

型はいろいろある。

  • 文字型 char Unicode番号、整数型と同様な扱いが可能。16bit : 0 ~ 2^16 - 1 (65535)
  • 整数型
    • byte | 8bit : -2^7 ~ 2^7 - 1
    • short | 16bit : -2^15 ~ 2^15 - 1
    • int | 32bit : -2^31 ~ 2^31 - 1
    • long | 64bit : -2^63 ~ 2^63 - 1
  • 浮動小数点型 float 32bit, double 64bit
  • 論理型 boolean 1bit?
  • 文字列 String 参照型のクラス型。だけど基本データ型のように扱える。なにそれって思ったけど、String型のインスタンスの中身は変更することができないため基本データ型のように扱えるらしい。参照型には配列型・クラス型・インタフェース型がある。

変数

  • 大文字小文字は区別
  • 予約語は使えない。予約語は全て小文字のみの文字列
  • 識別子は [a-zA-Z$][\w$]*
  • いちおう日本語が使える。つまり上の正規表現は正確ではない。でも日本語なんて使わないよね。
  • コーディング規約は 頑健なJavaプログラムの書き方 などを参考に。

変数の宣言

  • 変数の宣言時に型を決める必要がある。一度決めたら変えられない。
  • 同じ*識別子*の変数を2度宣言するとコンパイルエラー
  • 違う型のリテラルを変数に入れることはできない(ただし自動型変換が行われるものを除く、つまり入れることができることもかなりある。)

    float f1;
    f1 = 1.23f;
    
    float f2 = 456F;
    
    long l1 = 0L, l2;

float型のリテラルには f or F, long型のリテラルには l or L を数値の後ろにつけなければならない。変数に代入するだけなら型の自動変換が働いてコンパイルエラーにはならないけどね…

char型

char型は16ビット幅で、0~2^16までの正の整数値を扱える。 char型のリテラルは シングルクオートで囲う。phpなどと違ってシングルクオートで囲った文字は必ずchar型となる(ダブルクオートは String型)

String s;
char ch1, ch2, ch3, ch4;
ch1 = 'あ';
ch2 = '\u3042'; // シングルクオートで囲まないとダメ
s = "\u3042"; // Stringもユニコードエスケープ使える
ch3 = 0x3042;
ch4 = 12354;
if (ch1 == ch2 && ch2 == ch3 && ch3 == ch4) {
  System.out.println(ch1); // 「あ」
  System.out.println(s); // 「あ」
}
ch1++; // ch1 = ch1 + 1; とするとコンパイルエラー
System.out.println(ch1); // 「ぃ」
System.out.println( (int) ch1); // 「12355」

// ch1 = 'aa'; // コンパイルエラー 16bit以上は無理

結果

あ
あ
ぃ
12355

自動型変換とキャスト

自動型変換は boolean型と参照型を除く基本データ型 のリテラルで行われる。 変数の型と違うリテラルを変数に代入しようとしたとき… 型を変換しても問題が無い(絶対に情報が失われない)ときは自動型変換が行われる。 情報が失われる可能性がある場合は自動型変換が行われないため代入はできない(コンパイルエラー)けど、 キャストによって自動変換ができない型への変換を行うことができる。 キャストは自動変換できないから使うのだけど自動変換できる場合もキャストできる。

…という理解をしていたんだけど 良く考えてみたら浮動少数店型へと変換が働いたときに

数の精度が落ちる可能性があるじゃないか。どういうことだ。

単純に変換先の型の値の範囲より小さい場合に型変換ができるってことかな…

整数型のBit幅と自動型変換の図

演算子と自動型変換

整数型の値を + や - などで演算するときは int型 に変換してから実行される(long型は除外)。だから以下のコードはコンパイルエラーになる。

char ch = 1, _ch = 1;
ch = ch + 1; // コンパイルエラー
ch = _ch; // 代入するだけなら問題ない

long型の場合は long型 に変換され、longはlongのままらしい。

long l = 0, ll;
l += Math.pow(2, 63) - 1;
ll = 1 + l - 1;
System.out.println( l == ll );

結果

true

booleanとdouble型以外の基本データ型はすべてdouble型への自動変換が可能。

double x = 10 / 3;
System.out.println( x );

結果

3.0

浮動小数点型は…右辺の計算時に 整数型が浮動小数点型に自動変換されるから問題なし

float f = 0;
f = f + 1L;
double x = 0;
x = x + 1L;

複合演算子 の場合は勝手にキャストしてくれる。だから以下のコードはコンパイルエラーにならない。

int n = 0;
n += 777L;

char ch = 0;
ch += 255;
ch *= 99999.9;

文字列結合演算子

System.out.println(true + "" + true); // この + は 文字列結合演算子
// System.out.println( true + true + "" ); // コンパイルエラー

int n[] = {};
System.out.println("配列" + n);
  • の右か左に String がある場合は、文字列結合演算子として働く。演算時に String 以外は String型へと変換される。

    public class Hoge {
    public static void main(String[] args) {
    System.out.println(new Hoge());
    }
    
    public String toString() {
    return "インスタンスは toString() が実行されて変換されている";
    }
    }

結果

インスタンスは toString() が実行されて変換されている

条件演算子(三項演算子)

phpはかなり柔軟な書き方ができるけども、Javaの場合は代入演算子や return と一緒に使わなければ「トークン "==" に構文エラーがあります。AssignmentOperator が無効です」といわれてコンパイルエラーになる。

また三項演算子の優先順位が代入演算子よりも高いので注意が必要。

boolean t = true, f = false, b, bb, bbb;

b = t ? t : f;
System.out.println(b);

// 式を入れられる
// 優先順位の関係で丸括弧が必要
// 右端の括弧をはずすとコンパイルエラー
b = !(bbb = t) ? (bb = t) : (bb = f);

// 上の式と等価。真ん中部分は丸括弧をはずしても問題ない。
// でもなんか見た目に不安定
b = !(bbb = t) ? bb = t : (bb = f);
System.out.println(b);
System.out.println(bb);
System.out.println(bbb);

結果

true
false
false
true

関係演算子 論理演算子

  • 関係演算子には === や !== は存在しない。
  • 文字列の比較は String.equals、String.equalsIgnoreCase, String.compareTo などを使用する。
  • 文字列の比較での == は『同じオブジェクトかどうかを調べられる』だそうな。。同じインスタンスの参照かどうかってことかな?
  • 数値の比較では型を気にしなくてもいい。違う型同士で比較可能。
  • 論理演算士は boolean型の値にのみ使える
  • ^ は排他的論理輪
  • &, | は非短絡演算子、&&, || は短絡演算子

標準クラス

JAVA + API」でググる

制御構文

if, else if, else

if (false) {
   // コンパイルは通る。IDEに「デッドコード」って言われるかも
} else if (true) {

} else {
   // コンパイルは通る。IDEに「デッドコード」って言われるかも
}

switch文

  • switchの後ろの丸括弧は longを除く整数型の値のみ受け付ける。変数じゃなくてリテラルを入れてもいい、到達不能でコンパイルエラーにならない。
  • ラベルは longを除く整数型の値のみ受け付ける。でも変数を含む式やメソッドは受け付けない(定数式のみ可能)。
  • long や float などを使えないのは case式では int型 を使用するから? int型に変換できないものはコンパイルエラー
  • case式でのラベルと、ループでのラベルは別物と考えたほうがいい。

    int n = 7;
    
    switch ( (short) Math.pow(n, n) ) {
    case 'A' + 'B':
    System.out.println("C");
    break;
    
    default:
    // default が途中にきても問題なし
    System.out.println("DEFAULT");
    break;
    
    case 777:
    System.out.println("777");
    
    case 1:
    // case n: 変数は無理だよコンパイルエラー
    // case Math.abs(100): 定数式じゃないとダメだよ
    // case (char) 777: 重複case によるコンパイルエラー
    // case (float) 0: floatはintに変換できずコンパイルエラー
    System.out.println("1");
    // break; 最後の break に実行上の意味は無い
    }

break を省略することもできるが動作を理解していないとマズイ。

case に当てはまる場所から break が出てくるまでのプログラムは全て実行される。

for (int i = 0; i < 4; i++) {

  System.out.print("[" + i + "]");

  switch ( i ) {
  case 4:
    System.out.print("4");
  case 3:
    System.out.print("3");
  case 2:
    System.out.print("2");
  case 1:
    System.out.print("1");
  case 0:
    System.out.print("0");
  default:
    System.out.print("\n");
  }

}

結果

[0]0
[1]10
[2]210
[3]3210
for (int i = 0; i < 4; i++) {

  System.out.print("[" + i + "]");

  switch ( i ) {
  case 4:
    System.out.print("4");
  case 3:
    System.out.print("3");
  case 2:
    System.out.print("2");
    break;
  case 1:
    System.out.print("1");
  case 0:
    System.out.print("0");
  default:
    System.out.print("\n");
  }

}

結果

[0]0
[1]10
[2]2[3]32

for文, 拡張for文

  • 下のコードの変数 i は ループ制御変数 という
  • for文の条件部は ( 初期設定; 反復条件; 後処理 )

    for( int i = 0; i < 100; i++ ){
    break;
    i++; // 到達不可コードのコンパイルエラー
    }
  • 拡張for文 = foreach。コンパイル時に通常の for文に変換されるので動作的には普通のfor文と変わりない。

  • for( 配列の中身を入れる変数の宣言 : 配列 ) 変数の宣言なので型を決めなければならない。

    int n[][] = { { 1, 2 }, { 3, 4 }, };
    
    for (int m[] : n)
    {
    for (int l : m)
    {
    System.out.println("" + n + " / " + m + " / " + l);
    }
    }

結果

[[I@6e1408 / [I@e53108 / 1
[[I@6e1408 / [I@e53108 / 2
[[I@6e1408 / [I@f62373 / 3
[[I@6e1408 / [I@f62373 / 4

for, (do-)while と ラベル

for, (do-)while つまりループ にはラベルをつけることができ、break や continue で行き先を指定できる。switchのcase式で使ったラベルとは違うので注意。ラベルはどこにでも付けられるけど、case, for, (do-)while 以外では何の役にも立たない。使い道無し。偉い人でも分かる。

また、ラベルは2重につけることができる。

flag1:
flag2:
for (;;)
  while (true)
    do {
      double x = Math.random();
      if (x < 0.5)
        break flag1;
      else
        break flag2;
    } while (true);

for, (do-)while と 到達不能コード

ループ文中の break や continue により、それ以下のコードが実行されないことが明確なときは 到達不能コード でコンパイルエラーとなる。

if と switch については明らかに到達不能なコードがあってもコンパイルエラーにはならない。IDEが教えてくれることはある。

配列

  • phpとjavascriptの配列にしか触れたこと無かったのでかなり混乱した。Javaの配列は知らない配列だった…。自分が知っている配列はJavaでは hashList とかその辺のクラスっぽい
  • 配列は参照型(オブジェクト)の配列型
  • オブジェクトはJVMのヒープ領域で管理される
  • 配列が持つリテラル、オブジェクトの型は1つだけ
  • はじめにサイズを決めるが、サイズを決定したら後から変更は不可能
  • 以下のコードの意味が分かれば問題ないかな。。

    int[] n[][] = {};
    n = null;
    n = new int[2][][];
    n[0] = new int[2][];
    
    int m[][] = n[0];
    m[0] = new int[] { 3, 6, 9 };
    System.out.println(n[0][0][0] + n[0][0][1] + n[0][0][2]);
    m[1] = new int[0];
    System.out.println(m[1]);
    
    m = n[1];
    m = new int[1][1];
    m[0][0] = 777;
    try {
    int nn = n[1][0][0];
    } catch (java.lang.NullPointerException e) {
    System.out.println(e);
    }
    
    try {
    int[] nn = {};
    System.out.println(nn[0]);
    } catch (java.lang.ArrayIndexOutOfBoundsException e) {
    System.out.println(e);
    }

結果

18
[I@6e1408
java.lang.NullPointerException
java.lang.ArrayIndexOutOfBoundsException: 0

配列定数は「イニシャライザーにおいてのみ使用可能です」とIDEに怒られた。

波括弧は変数の宣言時にのみしか使えないのか…

double x[] = {};
x = {};

下のコードが意味が分からない。どうなってんの。

String[][] s1 = {}, s2 = new String[0][10];

System.out.println(s1);
System.out.println(s2);

結果

[[Ljava.lang.String;@6e1408
[[Ljava.lang.String;@e53108

機会があったらこっちも読んでみたいな

わかりやすいJava_オブジェクト指向編

Share
関連記事