@@ -707,4 +707,291 @@ public class OddDecider {
707
707
708
708
## 3.3.5 邏輯、位元運算 ##
709
709
710
- 待續 ...
710
+ 大於、小於的運算會了,但如果想要同時進行兩個以上的條件判斷呢?例如分數大於 80 「且」小於 90 的判斷。在邏輯上有 所謂的「且」(And)、「或」(Or)與「反」(Inverse),在 Java 中也提供這幾個基本邏輯運算所需的「邏輯運算子」(Logical operator),分別為「且」(&&)、「或」(||)及「反相」(!)三個運算子。
711
+ 來看看範例 3.17 會輸出什麼?
712
+
713
+ #### ** 範例3.17 LogicalOperator.java**
714
+ ``` java
715
+ public class LogicalOperator {
716
+ public static void main (String [] args ) {
717
+ int number = 75 ;
718
+ System . out. println((number > 70 && number < 80 ));
719
+ System . out. println((number > 80 || number < 75 ));
720
+ System . out. println(! (number > 80 || number < 75 ));
721
+ }
722
+ }
723
+ ```
724
+
725
+ 三段陳述句分別會輸出 true、false 與 true 三種結果,分別表示 number 大於 70「且」小於 80 為真、number 大於 80「或」小於 75 為假、number 大於 80「或」小於 75 的「相反」為真。
726
+
727
+ 接下來看看「位元運算子」(Bitwise operator),在數位設計上有 AND、OR、NOT、XOR 與補數等運算,在 Java 中提供這些運算的就是位元運算子,它們的對應分別是&(AND)、|(OR)、^(XOR)與~ (補數)。
728
+
729
+ 如果您不會基本的位元運算,可以從範例3.18中瞭解各個位元運算的結果。
730
+
731
+ #### ** 範例3.18 BitwiseOperator.java**
732
+ ``` java
733
+ public class BitwiseOperator {
734
+ public static void main (String [] args ) {
735
+ System . out. println(" AND運算:" );
736
+ System . out. println(" 0 AND 0\t\t " + (0 & 0 ));
737
+ System . out. println(" 0 AND 1\t\t " + (0 & 1 ));
738
+ System . out. println(" 1 AND 0\t\t " + (1 & 0 ));
739
+ System . out. println(" 1 AND 1\t\t " + (1 & 1 ));
740
+
741
+ System . out. println(" \n OR運算:" );
742
+ System . out. println(" 0 OR 0\t\t " + (0 | 0 ));
743
+ System . out. println(" 0 OR 1\t\t " + (0 | 1 ));
744
+ System . out. println(" 1 OR 0\t\t " + (1 | 0 ));
745
+ System . out. println(" 1 OR 1\t\t " + (1 | 1 ));
746
+
747
+ System . out. println(" \n XOR運算:" );
748
+ System . out. println(" 0 XOR 0\t\t " + (0 ^ 0 ));
749
+ System . out. println(" 0 XOR 1\t\t " + (0 ^ 1 ));
750
+ System . out. println(" 1 XOR 0\t\t " + (1 ^ 0 ));
751
+ System . out. println(" 1 XOR 1\t\t " + (1 ^ 1 ));
752
+ }
753
+ }
754
+ ```
755
+
756
+ 執行結果就是各個位元運算的結果:
757
+
758
+ AND運算:
759
+ 0 AND 0 0
760
+ 0 AND 1 0
761
+ 1 AND 0 0
762
+ 1 AND 1 1
763
+
764
+ OR運算:
765
+ 0 OR 0 0
766
+ 0 OR 1 1
767
+ 1 OR 0 1
768
+ 1 OR 1 1
769
+
770
+ XOR運算:
771
+ 0 XOR 0 0
772
+ 0 XOR 1 1
773
+ 1 XOR 0 1
774
+ 1 XOR 1 0
775
+
776
+ Java 中的位元運算是逐位元運算的,例如 10010001 與 01000001 作 AND 運算,是一個一個位元對應運算,答案就是 00000001;而補數運算是將所有的位元 0 變 1,1 變 0,例如 00000001 經補數運算就會變為 11111110,例如下面這個程式所示:
777
+
778
+ byte number = 0;
779
+ System.out.println((int)(~number));
780
+
781
+ 這個程式會在主控台顯示 -1,因為 byte 佔記憶體一個位元組,它儲存的 0 在記憶體中是 00000000,經補數運算就變成 11111111,這個數在電腦中用整數表示則是 -1。
782
+
783
+ 要注意的是,邏輯運算子與位元運算子也是很常被混淆的,像是'&&'與'&','||'與 '|',初學時可得多注意。
784
+
785
+ 位元運算對初學者來說的確較不常用,但如果用的恰當的話,可以增進不少程式效率,例如範例 3.19 可以判斷使用者的輸入是否為奇數:
786
+
787
+ #### ** 範例3.19 OddDecider2.java**
788
+ ``` java
789
+ import java.util.Scanner ;
790
+
791
+ public class OddDecider2 {
792
+ public static void main (String [] args ) {
793
+ Scanner scanner = new Scanner (System . in);
794
+
795
+ System . out. print(" 請輸入數字: " );
796
+ int number = scanner. nextInt();
797
+ System . out. println(" 是否為奇數? " +
798
+ ((number& 1 ) != 0 ? ' 是' : ' 否' ));
799
+ }
800
+ }
801
+ ```
802
+
803
+ 執行結果:
804
+
805
+ 請輸入數字: 66
806
+ 是否為奇數? 否
807
+
808
+ 範例 3.19 得以運作的原理是,奇數的數值若以二進位來表示,其最右邊的位元必為 1,而偶數最右邊的位元必為 0,所以您使用1來與輸入的值作 AND 運算,由於 1 除了最右邊的位元為 1 之外,其它位元都會是 0,與輸入數值 AND 運算的結果,只會留下最右邊位元為 0 或為 1 的結果,其它部份都被 0 AND 運算遮掉了,這就是所謂「位元遮罩」,例如 4 與 1 作 AND 運算的結果會是 0,所以判斷為偶數:
809
+
810
+ 整數4:00000100
811
+ 整數1:00000001
812
+ AND 運算後:00000000
813
+
814
+ 而 3 與 1 作 AND 運算的結果是 1,所以判斷為奇數:
815
+
816
+ 整數3:00000011
817
+ 整數1:00000001
818
+ AND運算後:00000001
819
+
820
+ XOR 的運算較不常見,範例 3.20 舉個簡單的 XOR 字元加密例子。
821
+
822
+ #### ** 範例3.20 XorCode.java**
823
+ ``` java
824
+ public class XorCode {
825
+ public static void main (String [] args ) {
826
+ char ch = ' A' ;
827
+ System . out. println(" 編碼前:" + ch);
828
+
829
+ ch = (char )(ch^ 7 );
830
+ System . out. println(" 編碼後:" + ch);
831
+
832
+ ch = (char )(ch^ 7 );
833
+ System . out. println(" 解碼:" + ch);
834
+ }
835
+ }
836
+ ```
837
+
838
+ 0x7 是 Java 中整數的 16 進位寫法,其實就是 10 進位的 7,將位元與 1 作 XOR 的作用其實就是位元反轉,0x7 的最右邊三個位元為 1,所以其實就是反轉 ch 變數的最後兩個位元,如下所示:
839
+
840
+ ASCII 中的 'A' 字元編碼為 65:01000001
841
+ 整數 7:00000111
842
+ XOR 運算後:01000110
843
+
844
+ 01000110 就是整數 70,對應 ASCII 中的字元 'F' 之編碼,所以用字元方式顯示時會顯示 'F' 字元,同樣的,這個簡單的 XOR 字元加密,要解密也只要再進行相同的位元反轉就可以了,看看範例 3.20 的執行結果:
845
+
846
+ 編碼前:A
847
+ 編碼後:F
848
+ 解碼:A
849
+
850
+ > ** 良葛格的話匣子** 要注意的是,我雖然在說明時都只寫下 8 個位元的值來說明,這只是為了解說方便而已。實際的位元長度在運算時,需依資料型態所佔的記憶體長度而定,例如在使用 int 型態的 0 作運算時,要考慮的是 32 個位元長度,而不是只有 8 個位元,因為 int 佔有 4 個位元組,也就是實際上是 00000000 00000000 00000000 00000000。
851
+
852
+ 在位元運算上,Java 還有左移(<<)與右移(>>)兩個運算子,左移運算子會將所有的位元往左移指定的位數,左邊被擠出去的位元會被丟棄,而右邊會補上0;右移運算則是相反,會將所有的位元往右移指定的位數,右邊被擠出去的位元會被丟棄,至於最左邊補上原來的位元,如果左邊原來是 0 就補 0,1 就補 1。另外還有 >>> 運算子,這個運算子在右移後,一定在最左邊補上 0。
853
+
854
+ 範例 3.21 使用左移運算來作簡單的2次方運算示範。
855
+
856
+ #### ** 範例3.21 ShiftOperator.java**
857
+ ``` java
858
+ public class ShiftOperator {
859
+ public static void main (String [] args ) {
860
+ int number = 1 ;
861
+ System . out. println( " 2的0次: " + number);
862
+
863
+ number = number << 1 ;
864
+ System . out. println(" 2的1次: " + number);
865
+
866
+ number = number << 1 ;
867
+ System . out. println(" 2的2次: " + number);
868
+
869
+ number = number << 1 ;
870
+ System . out. println(" 2的3次:" + number);
871
+ }
872
+ }
873
+ ```
874
+
875
+ 執行結果:
876
+
877
+ 2的0次: 1
878
+ 2的1次: 2
879
+ 2的2次: 4
880
+ 2的3次:8
881
+
882
+ 實際來左移看看就知道為何可以如此作次方運算了:
883
+
884
+ 00000001 -> 1
885
+ 00000010 -> 2
886
+ 00000100 -> 4
887
+ 00001000 -> 8
888
+
889
+ > ** 良葛格的話匣子** 位元運算對於沒有學過數位邏輯的初學者來說,會比較難一些,基本上除了像是資訊工程、電機工程相關領域的開發人員會比較常使用位元運算之外,大部份的開發人員可能不常使用位元運算,如果您的專長領域比較不需要使用位元運算,則基本上先瞭解有位元運算這個東西就可以了,不必在這個部份太過鑽研。
890
+
891
+ ## 3.3.6 遞增、遞減運算 ##
892
+
893
+ 遞增(Increment)、遞減(Decrement)與指定(Assignment)運算子,老實說常成為初學者的一個惡夢,因為有些程式中若寫得精簡,這幾個運算子容易讓初學者搞不清楚程式的真正運算結果是什麼;事實上,使用這幾種運算子的目的除了使讓程式看來比較簡潔之外,還可以稍微增加一些程式執行的效率。
894
+ 在程式中對變數遞增1或遞減1是很常見的運算,例如:
895
+
896
+ int i = 0;
897
+ i = i + 1;
898
+ System.out.println(i);
899
+ i = i - 1;
900
+ System.out.println(i);
901
+
902
+ 這段程式會分別顯示出 1 與 0 兩個數,您可以使用遞增、遞減運算子來撰寫程式:
903
+
904
+ #### ** 範例3.22 IncrementDecrement.java**
905
+ ``` java
906
+ public class IncrementDecrement {
907
+ public static void main (String [] args ) {
908
+ int i = 0 ;
909
+ System . out. println(++ i);
910
+ System . out. println(-- i);
911
+ }
912
+ }
913
+ ```
914
+
915
+ 其中寫在變數 i 之前的 ++ 與 -- 就是「遞增運算子」(Increment operator)與「遞減運算子」(Decrement operator),當它們撰寫在變數之前時,其作用就相當於將變數遞增 1 與遞減 1:
916
+
917
+ ++i; // 相當於 i = i + 1;
918
+ --i; // 相當於 i = i - 1;
919
+
920
+ 您可以將遞增或遞減運算子撰寫在變數之前或變數之後,但其實兩種寫法是有差別的,將遞增(遞減)運算子撰寫在變數前時,表示先將變數的值加(減)1,然後再傳回變數的值,將遞增(遞減)運算子撰寫在變數之後,表示先傳回變數值,然後再對變數加(減)1,例如:
921
+
922
+ #### ** 範例3.23 IncrementDecrement2.java**
923
+ ``` java
924
+ public class IncrementDecrement2 {
925
+ public static void main (String [] args ) {
926
+ int i = 0 ;
927
+ int number = 0 ;
928
+
929
+ number = ++ i; // 相當於i = i + 1; number = i;
930
+ System . out. println(number);
931
+ number = -- i; // 相當於i = i - 1; number = i;
932
+ System . out. println(number);
933
+ }
934
+ }
935
+ ```
936
+
937
+ 在這段程式中,number 的值會前後分別顯示為 1 與 0,再看看範例 3.24。
938
+
939
+ #### ** 範例3.24 IncrementDecrement3.java**
940
+ ``` java
941
+ public class IncrementDecrement3 {
942
+ public static void main (String [] args ) {
943
+ int i = 0 ;
944
+ int number = 0 ;
945
+
946
+ number = i++ ; // 相當於number = i; i = i + 1;
947
+ System . out. println(number);
948
+ number = i-- ; // 相當於 number = i; i = i - 1;
949
+ System . out. println(number);
950
+ }
951
+ }
952
+ ```
953
+
954
+ 在這段程式中,number 的值會前後分別顯示為 0 與 1。
955
+
956
+ 接下來看「指定運算子」(Assignment operator),到目前為止您只看過一個指定運算子,也就是 '=' 這個運算子,事實上指定運算子還有以下的幾個:
957
+
958
+ #### ** 表3.3 指定運算子**
959
+
960
+ | 指定運算子| 範例 | 結果
961
+ | :-------- | :----- | :------
962
+ | += | a += b | a = a + b
963
+ | -= | a -= b | a = a - b
964
+ | * = | a * = b | a = a * b
965
+ | /= | a /= b | a = a / b
966
+ | %= | a %= b | a = a % b
967
+ | &= | a &= b | a = a & b
968
+ | \| = | a \| = b | a = a \| b
969
+ | ^= | a ^= b | a = a ^ b
970
+ | <<= | a <<= b | a = a << b
971
+ | \> >= | a >>= b | a = a >> b
972
+
973
+ 每個指定運算子的作用如上所示,但老實說若不是常寫程式的老手,當遇到這些指定運算子時,有時可能會楞一下,因為不常用的話,這些語法並不是那麼的直覺。
974
+
975
+ 使用 ++、- - 或指定運算子,由於程式可以直接在變數的記憶體空間中運算,而不用取出變數值、運算再將數值存回變數的記憶體空間,所以可以增加運算的效率,但以現在電腦的運算速度來看,這一點的效率可能有些微不足道,除非您這類的運算相當的頻繁,否則是看不出這點效率所帶來的改善,就現在程式撰寫的規模來看,程式的易懂易讀易維護會比效率來的重要,可以的話儘量將程式寫的詳細一些會比較好,千萬不要為了賣弄語法而濫用這些運算子。
976
+
977
+ 就單一個陳述而言,使用 ++、- - 或指定運算子是還算可以理解,但與其它陳述結合時可就得考慮一下,例如:
978
+
979
+ int i = 5;
980
+ arr[--i %= 10] = 10;
981
+
982
+ 像這樣的式子,要想知道變數 i 是多少,以及陣列的指定索引位置在哪可就得想一下了(有興趣算一下的話,i 最後會是 4,而陣列的指定索引也是 4),總之,如何使用與何時使用,自己得拿捏著點。
983
+
984
+ > ** 良葛格的話匣子** 在撰寫程式時,可以在運算子的左右或是逗號 ',' 之後適當的使用一些空白,讓程式看來不那麼擁擠,不僅程式看來比較美觀,閱讀起來也比較容易,比較一下就可以體會:
985
+ >
986
+ > int i=0;
987
+ > int number=0;
988
+ > number=i++;
989
+ > number=i--;
990
+ >
991
+ > 下面的寫法會比較好讀一些:
992
+ >
993
+ > int i = 0;
994
+ > int number = 0;
995
+ > number = i++;
996
+ > number = i--;
997
+
0 commit comments