Ch3. 演算子

演算子

演算処理は左から

    System.out.println("Hello" + 10 + 20);//'Hello1020'
    System.out.println(10 + 20 + "Hello");//'30Hello'
    System.err.println("Hello" + (10 + 20));//'hello30'

単項演算子は付け方によって順番に注意する

    //単項演算子
    int n1 = 10;
    int n2 = 20;
    int o1;
    int o2;

    System.out.println(n1++);//'10'
    System.out.println(++n2);//'21'

    System.out.println(n1);//'11'
    System.out.println(n2);//'21'

    o1 = n1++;//代入が実施されてから足し座
    o2 = ++n2;//足し算した値を代入

    System.out.println(o1);//'11'
    System.out.println(o2);//'22'

なお、後置のインクリメント演算子が同じ式に2回以上出てきた場合、2回目に現れる変数では1回目のインクリメント演算子の結果が反映されていることに注意する。

論理演算子&&&の違いに注意する。

    boolean a = ++x < 0 & ++y > 0; //左辺がfalseなので右辺を評価する意味はないが
    System.out.println("boolean: " + a);
    System.out.println("x: " + x);//'1'
    System.out.println("y: " + y);//'1' 評価はされる

    x = 0;
    y = 0;
    boolean b = ++x < 0 && ++y > 0; //左辺がfalseなので右辺を評価する意味はない
    System.out.println("boolean: " + b);
    System.out.println("x: " + x); //'1'
    System.out.println("y: " + y); //'0'

    x = 0;
    y = 0;
    boolean c = ++x > 0 || ++y > 0; //左辺がtrueなので右辺を評価する意味はない
    System.out.println("boolean: " + b);
    System.out.println("x: " + x);//'1'
    System.out.println("y: " + y);//'0'

    x = 0;
    y = 0;
    boolean d = ++x > 0 | ++y > 0; //左辺がtrueなので右辺を評価する意味はないが
    System.out.println("boolean: " + b);
    System.out.println("x: " + x);//'1'
    System.out.println("y: " + y);//'1'

    //直観的には&&または||の方がしっくりくる演算子

Rubyでは見たことは無かったがboolean ^ booleanで使うこのような演算子もある。

x = 0;
    y = 0;
    boolean e = ++x > 0 ^ ++y > 0;  //boolean ^ boolean で使う
    boolean f = ++x > 0 ^ ++y == 0; //左辺がtrue 右辺がfalseなのでfはtrue

    System.out.println("boolean e:" + e);//'false'
    System.out.println("boolean f:" + f);//'true'

代入演算子は左右のオペラントの演算が終わってからでないと機能しない。
順番がわからなくなったら分割して考えればよい。

int a;
int b = 100;

a = b += 1
a = b = b + 1
a = b = 101
b = 101
a = 101

StringとStringBuilder

String

jdk/String.java at master · openjdk/jdk · GitHub

Stringは参照型であり、byte(の配列)に対する参照しか保持していない。 そのため、直接プリミティブ文字列を操作することは出来ない。内容を変更したければ別の文字列に対する参照にする。 以下、挙げるのは全て参照先の文字列事態を操作しない"非破壊的"メソッドである。

    String name = "  Haneda Taro";
    boolean startwWithH = name.startsWith("Hi");
    System.out.println("The name of start with Hi? : " + startwWithH); //'false'
    System.out.println("4 to end strings is.. : " + name.substring(4));//'neda Taro'
    System.out.println("trim your name " + name.trim());//'Haneda Taro'(文字列前後の空白の除去)

StringBuilder

jdk/StringBuilder.java at master · openjdk/jdk · GitHub

文字列代入の度に新しい参照先を設定してしまうStringでは例えば大量に文字列連結を繰り返す場合などにつらい。(毎回文字列オブジェクトを生成して参照先に設定している)GCに負担がかかるということか。 StringBuilderで同じ(文字列)インスタンスに大して操作したほうが優しい

    StringBuilder sb = new StringBuilder(); //空で長さが16のStringBuilder
    StringBuilder sb2 = new StringBuilder(100); //空で長さが100のStringBuilder
    StringBuilder sb3 = new StringBuilder("Inspiration of Japan");

    sb3.reverse();
    System.out.println(sb3);//'napaJ fo noitaripsnI'
    sb2.append("Spirit of Australia");
    System.out.println(sb2);//'Spirit of Australia'

String/StringBuilderのdeleteメソッドは少し変わった特性を持つことに注意する。

指定された start から始まり、インデックス end - 1 の文字まで を削除する。

StringBuilder (Java Platform SE 7)

値の比較

比較の際は、参照型の変数が指し示す先に注意する。

    System.out.println("array1 == array2 ? " + (array1 == array2));//'false'(別の配列インスタンスを指しているから)
    int[] array3 = array2; //一見すると配列の入れ子になりそうだが、単なる参照先の設定
    System.out.println("array3 == array2 ? " + (array3 == array2));//'true'

比較演算子<>=は数値型とCharでしか利用できない。 booleanでは利用できず、ラッパークラスのBooleanでも利用できない。また文字列の比較もできない。

jdk8u-dev-jdk/Character.java at master · frohoff/jdk8u-dev-jdk · GitHub (>で呼び出しているのに、実際にメソッドを呼び出しているのかどうかはかなり気になる。)

文字列インスタンスの比較は、更に注意を要する。

    String newString1 = new String("Heart beat moters!");
    String newString2 = new String("Heart beat moters!");

    String newString3 = "Inspire the next";
    String newString4 = "Inspire the next";

    //newを利用したときは別のインスタンスが利用される
    System.out.println("Compare the same string : " + (newString1 == newString2));//'false'
    //通常のStringの初期化では、メモリ上に同じ文字列が存在していればインスタンスは使いまわす。
    System.out.println("Compare the same string : " + (newString3 == newString4));//'true'

   // String Classは参照型で、参照先の文字列は操作できない。
    String newString5 = "Shaping tomorrown with";
    String newString6 = newString5.concat(" you."); //この時点で参照先オブジェクトが変更されている。
    System.out.println("Same instance? : " + (newString5 == newString6));//'false'

    //String Builderは同じ文字列インスタンスに対する操作が可能
    StringBuilder newString7 = new StringBuilder("AMBITIOUS");
    StringBuilder newString8 = newString7.append(" Japan !");

    System.out.println("Same instance? : " + (newString8 == newString7));//'true'

StringBuilderで指し示す、文字列オブジェクトが同じということは、同じ文字列オブジェクトを指す他の参照型も当然影響を受ける。

    newString7.append("!!!");//指し示す先が同じなので
    System.out.println("changes on newString7 affenct newString8 ? : " + newString8);//'AMBITIOUS Japan !!!!' (newString8も同じ変更を受ける)

Object Classのequalsメソッドはオーバーライドすることを前提にしていてObjectクラスの型を引数にとり、自分自身との同一性(==)をチェックして返す。 jdk7u-jdk/Object.java at master · openjdk-mirror/jdk7u-jdk · GitHub

ここで気を付けたいのは、メソッドのオーバーライドには引数の型を継承元のメソッドと同じにしなければならないという点である。 equalsメソッドをオーバーライドしようとして、引数に自分自身のクラスのオブジェクトを取るよう宣言した場合は全く別のメソッド(オーバーロード)となる。

Arrayは領域確保が行われていなければ、nullと変わらない。

    int[] array = null;
    System.out.println("non-initialized Array is null? : " + (array == null));//'true'

    array = new int[10]; //領域が確保された空の配列
    System.out.println("initialized Array is null? : " + (array == null));//'false'

条件分岐

Rubyの後置Ifがなぜか好きだったなあ

If文

    //Javaで後置ifは出来ないけどこれならできる。
    if(newString7.toString().equals("AMBITIOUS JAPAN !!!!"));
      System.out.println("この一文は実行される");
      System.out.println("この一文は分岐に関係なく実行される");

    //三項演算子も使える
    String bool = false ^ true ? "Ternary Operator: True" : "Ternary Operator: False";
    System.out.println(bool);

条件分の中身は[式の戻り値がboolean]の場合だけ、代入でもよい。

    //ifの中身は"結果がbooleanならよい"
    boolean a1;
    boolean a2 = true;
    if(a1 = a2)
      System.out.println("result is true");//実行される

    //falusが代入対象だと条件式もfalseになる
    a2 = false;
    if(a1 = a2)
      System.out.println("won't be executed");//実行されない

swich文

Switch分の判定対象に利用できるのは以下のデータ型のみ byte, char, short, int, enmm, String

StringはSE 7から利用可能になった switch 文の文字列

    switch("HelloWorld"){
      default:
        System.out.println("Default can be declare on anywhere on Switch statement");
        break; //これがない場合、次のcase節も(条件判定に当てはまらなくても)続けて実行してしまう。
      case "HelloWorld1":
        System.out.println("Hello World!");
      //case new String("HelloWorld"): //新しいオブジェクトを定義することは出来ない
        //System.out.println("Hello World");
    }

【breakを書かないと次の節も実行してしまう】という仕様は不自然に見えるが、こんな綺麗な書き方もできるんので案外悪くない。

    switch(50 + 50){
      case 50:
      case 70:
      case 99:
        System.out.println("less than 100");
        break;
      default:
        System.out.println("more than 170"); //実行されない
        break;
      case 100:
      case 150:
      case 170:
        System.out.println("more than 100");
        break;        
    }

Switch分の条件式にとれるのは以下の3種類の型だけである。 * 整数型とそのラッパークラス(byte, Byte, int, Integer, short, Short) * 文字と文字列とそのラッパークラス(char, Character, String) * enum

また、Switch分の条件には変数は利用できない。final宣言がされた定数、またはリテラルである必要がある。 リテラル2 * 50などの式でもよい。実行時に値が変化しなければ問題なく利用できる。

値の変換

    int floatToInt = (int)3.1415;
    int LongToInt  = (int)(3 * 10L);
    byte bitToInt   = 0b1000000;

   System.out.println(floatToInt + " " + LongToInt + " " + bitToInt);//'3 30 64'

2進数表記で数値リテラルを表現することは可能である。 また、キャストすることでより精度の高い数値からより精度の低い数値に変換することもできる。(情報は失われる。)

小数点に関しては更に配慮が必要になる。 小数点のデータ型は精度の高い順にlong > double > floatとなる。
そしてデフォルトではdouble型で設定される。