1.除0
代碼:
- System.out.println(1.0d / 0);
- System.out.println(0.0d / 0);
- System.out.println(1 / 0);
- System.out.println(0 / 0);
輸出:
Infinity
NaN
java.lang.ArithmeticException: / by zero
at test.ww.Test.main(Test.java:27)
java.lang.ArithmeticException: / by zero
at test.ww.Test.main(Test.java:32)
原因:
因爲 IEEE 754 有規定無窮大是怎麼表示的,因此被除數不爲 0,除數是 0 的話計算結果是正無窮或者是負無窮,如果被除數和除數都是 0 的話,那麼計算結果是 NaN
整數不在是 IEEE 754 規定的,也沒有無窮大的表示,因此只能拋出異常了
2. Arrays.asList?可變長參數?
- int[] arr = new int[]{1,2,3};
- System.out.println(Arrays.asList(arr).contains(1));
輸出什麼?
大多數人的回答肯定是true,顯而易見麼。。但它輸出的卻是false,爲什麼,哪裏出錯了麼?
分解一下表達式,先調用Arrays.asList(arr),通過遍歷或者debug你會發現裏面的元素個數爲1,這又是什麼原因呢,查看一下API或者源代碼,發現它聲明爲List<T> Arrays.asList(T... args),難道是可變長參數有問題?
做一個測試:
- public static void main(String[] args) {
- int[] arr = new int[]{1, 2, 3 };
- test1(arr);
- }
- private static void test1(Object... values) {
- System.out.println(values.length);
- }
輸出
1
把int數組改爲Integer數組後,輸出
3
看來是基本類型的數組被當作了一個對象,而對象類型的數組的每個元素才能分別作爲可變長參數方法的參數。
3.詭異的三元表達式
三元表達式,註釋部分是該行的輸出,爲啥?
- char x = 'X';
- int i = 0;
- System.out.println(true ? x : 0);// X
- System.out.println(false ? i : x);// 88
- char x = 'x';
- System.out.println(true?120:x);
原因:
true ? x : 0返回的是x的類型,即char型,調用char的toString方法,輸出X
false ? i : x返回的是i的類型,即int型,先把‘X’轉爲整型(88),再輸出。
對於三目運算符中的兩個結果,如果一個是常量,一個是類型T的變量,則常量會被轉型爲類型T,這個據說是java編程規範中規定的,反正我是沒看過,就此記住一條。所以常量120被轉型爲char,對應於x(小寫)
即三元運算符返回類型以第二個變量爲主(問號後第一個)
4.我的錢呢?
total是你兜裏的所有的錢,price是你要買的東西的價格,remaining是剩餘的錢。count是你最多買的東西的數量,請問下面的代碼有什麼問題,輸出什麼?
- double total = 2.0;
- double price = 0.1;
- double remaining = total;
- int count = 0;
- while (remaining >= price) {
- count++;
- remaining -= price;
- }
- System.out.println(count);
- System.out.println(remaining);
代碼看起來沒有問題,判斷兜裏的錢大於物品的價格就買一個,直到買不起爲止,看起來應該輸出20和0.
可運行後會發現輸出19和0.09999999999999937(可能根據不同的機器這個餘數略有差異)。
錢怎麼少了?
這個原因,和二進制無法精確的表示某些浮點數有關,2.0-1.1結果是0.899999999999。
所以,如果你想要精確的浮點數,請使用BigDecimal類進行運算,當然可能會慢一些,需要自己權衡。
5.x+=i等同於x = x + i?
1)
- short x = 1;
- int i = 1;
- x += i;
- x = x + i;
當你試圖編譯上面代碼的時候,編譯器會報錯,在x=x+i的地方:Type mismatch: cannot convert from int to short
2)
- Object x = 1;
- String i = "";
- x += i;
- x = x + i;
當你試圖編譯上面代碼的時候,編譯器會報錯,在x += i;的地方:The operator += is undefined for the argument type(s) Object, String
6.字符連接???
System.out.println('N' + 'C');
當你運行上面代碼的時候,它不會如你所願輸出NC,而會輸出145,這是怎麼了?
char型變量的加法不是連接字符,而是和int類似,但相加的是它的ASCII碼,本例中輸出78+67=145
7.E呢?
- StringBuffer sb = new StringBuffer('E');
- sb.append('M');
- sb.appent('S');
- System.out.println(sb.toString());
當你滿心歡喜的運行上面代碼的時候,會發現它並沒有如你所願的輸出EMS,而是輸出了MS,E呢?
看來是StringBuffer的構造器出問題,當你查看StringBuffer的構造函數的時候,你會驚奇的發現它並不提供char型參數的構造器,這是怎麼回事?
如果你使用eclipse或者其他IDE點進入的時候,會發現new StringBuffer('E');進入的是StringBuffer(int)的構造器,這個構造器只是初始化了一下緩衝區大小。
到這裏應該都明白了,所以使用char的時候要注意,它有時候更像是int而不是String。
8.單例?
相同虛擬機下下面的類什麼時候能得到多於一個實例?
- public class Test implements Seraziable{
- private static Test instance = null;
- private Test (){
- }
- public static Test getInstance() {
- if(instance == null) {
- instance = new Test();
- }
- }
- }
第一個想到的應該是多線程,兩個線程同時訪問的時候可能會產生兩個實例。
細心的人會發現這個類實現了序列化接口,也就是序列化反序列化的時候也可能產生多個實例:
- public class TestSerializable implements Serializable {
- private static final TestSerializable instance = new TestSerializable();
- private String name = "t1";
- private TestSerializable() {
- System.out.println("**********TestSerializable************");
- }
- public static TestSerializable getInstance() {
- return instance;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public static void main(String[] args) throws Exception {
- TestSerializable singleton = TestSerializable.getInstance();
- FileOutputStream fos = new FileOutputStream("E:\\test.txt");
- ObjectOutputStream oos = new ObjectOutputStream(fos);
- oos.writeObject(TestSerializable.getInstance());
- oos.flush();
- oos.close();
- FileInputStream fis = new FileInputStream("E:\\test.txt");
- ObjectInputStream ois = new ObjectInputStream(fis);
- TestSerializable singleton2 = (TestSerializable)ois.readObject();
- singleton.setName("111");
- System.out.println(singleton.getName());
- System.out.println(singleton2.getName());
- }
- }
輸出:
111
t1
解決這個問題首先要知道反序列化的時候怎麼執行的,通過查找資料,知道了反序列化的時候調用了方法
private Object readResolve()
jdk有個默認的反序列化方式,會從序列化文件讀取信息,並創建實例(不通過構造函數),然後對變量賦值。
如果不考慮遠程傳數據,下面的辦法可以保證當前jvm只有一個實例:
在類中增加方法:
- private Object readResolve(){
- return instance;
- }
輸出:
**********TestSerializable************
111
111
但是這樣僅僅保證了單例,而丟失了序列化應有的功能。
所以大家在設計可序列化接口的類時要多多考慮。
9.泛型?
編譯下面的類,會發現編譯器報錯:Method test(List<String>) has the same erasure test(List<E>) as another method in type Test001
- public class Test001 {
- public void test(List<String> lst) {
- }
- public void test(List<Object> lst) {
- }
- }
這就是泛型的類型擦除,也就是泛型會在編譯後消失。
但是看下面的代碼,卻能編譯通過,難道java中返回類型也作爲方法簽名;不可能!絕對不可能!java規範明明寫着方法簽名不包括返回值,這到底怎麼了?
另外如果把泛型去掉這個類也不能通過編譯,看來還是和泛型相關:
- public class Test001 {
- public static void test(List<Object> lst) {
- System.out.println("test(List<Object> lst)");
- }
- public static String test(List<String> lst) {
- System.out.println("test(List<String> lst)");
- return "ttttt";
- }
- }
調用一下試試?
- public class Test001 {
- public static void main(String[] args) {
- List<String> aaa = new ArrayList<String>();
- aaa.add("1");
- test(aaa);// 如果List不指定<String>,則編譯報錯
- }
- public static void test(List<Object> lst) {
- System.out.println("test(List<Object> lst)");
- }
- public static String test(List<String> lst) {
- System.out.println("test(List<String> lst)");
- return "ttttt";
- }
- }
正如註釋縮寫,如果調用的時候不用泛型,java也無法找到該調用哪個。
這到底怎麼了,泛型不是被擦除了麼?怎麼會這樣??
直接查看字節碼javap -verbose:
- public class test.Test001 extends java.lang.Object
- SourceFile: "Test001.java"
- minor version: 0
- major version: 49
- Constant pool:
- const #1 = class #2; // test/Test001
- const #2 = Asciz test/Test001;
- const #3 = class #4; // java/lang/Object
- const #4 = Asciz java/lang/Object;
- const #5 = Asciz <init>;
- const #6 = Asciz ()V;
- const #7 = Asciz Code;
- const #8 = Method #3.#9; // java/lang/Object."<init>":()V
- const #9 = NameAndType #5:#6;// "<init>":()V
- const #10 = Asciz LineNumberTable;
- const #11 = Asciz LocalVariableTable;
- const #12 = Asciz this;
- const #13 = Asciz Ltest/Test001;;
- const #14 = Asciz main;
- const #15 = Asciz ([Ljava/lang/String;)V;
- const #16 = class #17; // java/util/ArrayList
- const #17 = Asciz java/util/ArrayList;
- const #18 = Method #16.#9; // java/util/ArrayList."<init>":()V
- const #19 = String #20; // 1
- const #20 = Asciz 1;
- const #21 = InterfaceMethod #22.#24; // java/util/List.add:(Ljava/la
- ng/Object;)Z
- const #22 = class #23; // java/util/List
- const #23 = Asciz java/util/List;
- const #24 = NameAndType #25:#26;// add:(Ljava/lang/Object;)Z
- const #25 = Asciz add;
- const #26 = Asciz (Ljava/lang/Object;)Z;
- const #27 = Method #1.#28; // test/Test001.test:(Ljava/util/List;)Ljava/la
- ng/String;(2)調用的方法
- const #28 = NameAndType #29:#30;// test:(Ljava/util/List;)Ljava/lang/String;(3)方法簽名,可這裏卻包含了返回值。。。
- const #29 = Asciz test;
- const #30 = Asciz (Ljava/util/List;)Ljava/lang/String;;
- const #31 = Asciz args;
- const #32 = Asciz [Ljava/lang/String;;
- const #33 = Asciz aaa;
- const #34 = Asciz Ljava/util/List;;
- const #35 = Asciz LocalVariableTypeTable;
- const #36 = Asciz Ljava/util/List<Ljava/lang/String;>;;
- const #37 = Asciz (Ljava/util/List;)V;
- const #38 = Asciz Signature;
- const #39 = Asciz (Ljava/util/List<Ljava/lang/Object;>;)V;
- const #40 = Field #41.#43; // java/lang/System.out:Ljava/io/PrintS
- tream;
- const #41 = class #42; // java/lang/System
- const #42 = Asciz java/lang/System;
- const #43 = NameAndType #44:#45;// out:Ljava/io/PrintStream;
- const #44 = Asciz out;
- const #45 = Asciz Ljava/io/PrintStream;;
- const #46 = String #47; // test(List<Object> lst)
- const #47 = Asciz test(List<Object> lst);
- const #48 = Method #49.#51; // java/io/PrintStream.println:(Ljava/l
- ang/String;)V
- const #49 = class #50; // java/io/PrintStream
- const #50 = Asciz java/io/PrintStream;
- const #51 = NameAndType #52:#53;// println:(Ljava/lang/String;)V
- const #52 = Asciz println;
- const #53 = Asciz (Ljava/lang/String;)V;
- const #54 = Asciz lst;
- const #55 = Asciz Ljava/util/List<Ljava/lang/Object;>;;
- const #56 = Asciz (Ljava/util/List<Ljava/lang/String;>;)Ljava/lang/String;
- ;
- const #57 = String #58; // test(List<String> lst)
- const #58 = Asciz test(List<String> lst);
- const #59 = String #60; // ttttt
- const #60 = Asciz ttttt;
- const #61 = Asciz SourceFile;
- const #62 = Asciz Test001.java;
- {
- public test.Test001();
- Code:
- Stack=1, Locals=1, Args_size=1
- 0: aload_0
- 1: invokespecial #8; //Method java/lang/Object."<init>":()V
- 4: return
- LineNumberTable:
- line 6: 0
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 5 0 this Ltest/Test001;
- public static void main(java.lang.String[]);
- Code:
- Stack=2, Locals=2, Args_size=1
- 0: new #16; //class java/util/ArrayList
- 3: dup
- 4: invokespecial #18; //Method java/util/ArrayList."<init>":()V
- 7: astore_1
- 8: aload_1
- 9: ldc #19; //String 1
- 11: invokeinterface #21, 2; //InterfaceMethod java/util/List.add:(Ljava/lan
- g/Object;)Z
- 16: pop
- 17: aload_1
- 18: invokestatic #27; //Method test:(Ljava/util/List;)Ljava/lang/String; //(1)調用入口
- 21: pop
- 22: return
- LineNumberTable:
- line 9: 0
- line 10: 8
- line 11: 17
- line 12: 22
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 23 0 args [Ljava/lang/String;
- 8 15 1 aaa Ljava/util/List;
- LocalVariableTypeTable: length = 0xC
- 00 01 00 08 00 0F 00 21 00 24 00 01
- public static void test(java.util.List);
- Signature: length = 0x2
- 00 27
- Code:
- Stack=2, Locals=1, Args_size=1
- 0: getstatic #40; //Field java/lang/System.out:Ljava/io/PrintStream;
- 3: ldc #46; //String test(List<Object> lst)
- 5: invokevirtual #48; //Method java/io/PrintStream.println:(Ljava/lang/St
- ring;)V
- 8: return
- LineNumberTable:
- line 15: 0
- line 16: 8
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 9 0 lst Ljava/util/List;
- LocalVariableTypeTable: length = 0xC
- 00 01 00 00 00 09 00 36 00 37 00 00
- public static java.lang.String test(java.util.List);
- Signature: length = 0x2
- 00 38
- Code:
- Stack=2, Locals=1, Args_size=1
- 0: getstatic #40; //Field java/lang/System.out:Ljava/io/PrintStream;
- 3: ldc #57; //String test(List<String> lst)
- 5: invokevirtual #48; //Method java/io/PrintStream.println:(Ljava/lang/St
- ring;)V
- 8: ldc #59; //String ttttt
- 10: areturn
- LineNumberTable:
- line 19: 0
- line 20: 8
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 11 0 lst Ljava/util/List;
- LocalVariableTypeTable: length = 0xC
- 00 01 00 00 00 0B 00 36 00 24 00 00
- }
上面標註的(1),(2),(3)可以看到,java字節碼中的方法簽名竟然有返回值(位置(3)),。
如果是非泛型類,即使方法簽名帶返回值,也無法區分該調用哪個方法(因爲調用時可以不要返回值)
而是泛型類的話,通過編譯器(編譯的時候通過泛型可以知道該調用哪個方法)和帶返回值的方法聲明的結合,卻可以找到調用的方法。
另外注意:以上帶返回值的泛型方法在有些編譯器下也是不能通過編譯的(sun的可以)
10.靠,出錯了?
- public static void main(String[] args) {
- List<String> aaa = new ArrayList<String>();
- aaa.add("1");
- test(aaa);
- }
- public static void test(List<Object> lst) {
- System.out.println("List<Object> lst");
- }
- public static void test(Object obj) {
- System.out.println("Object obj");
- }
當你想當然的認爲輸出List<Object> lst的時候,卻輸出了Object obj,難道List<String>不是List<Object>的子類?
通過查看規範發現確實List<String>不是List<Object>的子類,它倆沒啥關係。。。編譯器直接就指向了test(Object obj)
11.騙子!
- public static void main(String[] args) {
- List<Object> strList = new ArrayList<Object>();
- strList.add("1");
- strList.add("2");
- String[] arrA1 = strList.toArray(new String[0]);
- System.out.println(arrA1.length);
- String[] arrA2 = (String[]) strList.toArray(new Object[0]);
- System.out.println(arrA2.length);
- }
上面的代碼你可能認爲第一個toArray會報錯,因爲list聲明爲Object,轉化爲String會錯誤,但運行你會發現它正確的輸出了2,這是因爲上面說的泛型的類型擦除的原因,運行時沒有Object的聲明,只會根據運行態的類型進行轉化。
而第二個toArray卻會報錯(類型轉化異常),爲啥?
這種寫法相當於
- Object[] o = new Object[]{"1", "2"};
- String[] arrO = (String[]) 0;
- System.out.println(arrO.length);
這回看出來了吧,要轉化的數組和聲明後的數組類型不一樣(雖然存儲的數據確實是String的),直接就報數組的類型轉化錯誤。
12.replaceAll的使用
String str="34.ff4.455434";
String mt=str.replaceAll(".","");結果到測試時候才發現得到的結果是個空的字符串
改成String mt=str.replaceAll("\\.","");好了