1、數組不是集合,它只能保存同種類型的多個原始類型或者對象的引用。數組保存的僅僅是對象的引用,而不是對象本身。
2、數組本身就是對象,Java中對象是在堆中的,因此數組無論保存原始類型還是其他對象類型,數組對象本身是在堆中的。
3、數組聲明的兩種形式:一、int[] arr; 二、int arr[]; 推薦使用前者,這符合Sun的命名規范,而且容易了解到關鍵點,這是一個int數組對象,而不是一個int原始類型。
數組初始化可以在聲明是進行,int[] arr = {1,2,3}或者int[] arr = new int[3]{1,2,3}。
4、在數組聲明中包含數組長度永遠是不合法的!如:int[5] arr; 。因為,聲明的時候並沒有實例化任何對象,只有在實例化數組對象時,JVM才分配空間,這時才與長度有關。
5、在數組構造的時候必須指定長度,因為JVM要知道需要在堆上分配多少空間。反例:int[] arr = new int[];
6、多維數組的聲明。int[][][] arr; 是三維int型數組。
7、一維數組的構造。形如:String[] sa = new String[5];
或者分成兩句:String[] sa; sa = new String[5];
8、原始類型數組元素的默認值。對於原始類型數組,在用new構造完成而沒有初始化時,JVM自動對其進行初始化。默認值:byte、short、 int、long--0 float--0.0f double--0.0 boolean--false char--'"u0000'。(無論該數組是成員變量還是局部變量)
9、對象類型數組中的引用被默認初始化為null。如:Car[] myCar = new Car[10]; 相當於從myCar[0]到myCar[9]都這樣被自動初始化為myCar[i] = null;
10、對象類型的數組雖然被默認初始化了,但是並沒有調用其構造函數。也就是說:Car[] myCar = new Car[10];只創建了一個myCar數組對象!並沒有創建Car對象的任何實例!
11、多維數組的構造。float[][] ratings = new float[9][]; 第一維的長度必須給出,其余的可以不寫,因為JVM只需要知道賦給變量ratings的對象的長度。
12、數組索引的范圍。數組中各個元素的索引是從0開始的,到length-1。每個數組對象都有一個length屬性,它保存了該數組對象的長度。(注意和String對象的length()方法區分開來,這兩者沒有統一起來是很遺憾的。)
13、Java有數組下標檢查,當訪問超出索引范圍時,將產生ArrayIndexOutOfBoundsException運行時異常。注意,這種下標檢查不是在編譯時刻進行的,而是在運行時!也就是說int[] arr = new int[10]; arr[100] = 100; 這么明顯的錯誤可以通過編譯,但在運行時拋出!
Java的數組下標檢查是需要額外開銷的,但是出於安全的權衡還是值得的,因為很多語言在使用數組時是不安全的,可以任意訪問自身內存塊外的數組,編譯運行都不會報錯,產生難以預料的后果!
使用reflict.Array創建Array實例
import java.util.*; import java.lang.reflect.*; public class ArrayTest { public static void main(String...args) { int[] arr = (int[])Array.newInstance(Integer.TYPE, 10); Random r = new Random(100); for(int i =0; i<arr.length; i++) { arr[i] = r.nextInt(100); } System.out.println(Arrays.toString(arr)); } }
1、 不能用“==”比較兩個字符串內容相等。
2、 對list做foreach循環時,循環代碼中不能修改list的結構。
java foreach只能用於只讀的情況.如果需要刪除操作,請用迭代器或者直接遍歷List.
3、 空指針異常。
4、 數組下標越界。
// 獲取一個數組對象
String[] cIds = ContentService.queryByName(name);
if(null != cIds)
{
// 只是考慮到cids有可能為null的情況,但是cids完全有可能是個0長度的數組,因此cIds[0]有可能數組下標越界
5、 將字符串轉換為數字時沒有捕獲NumberFormatException異常。
6、 對文件、IO、數據庫等資源進行操作后沒有及時、正確進行釋放。
7、 循環體編碼時不考慮性能,循環體中包含不需要的重復邏輯。
8、 數據類沒有重載toString()方法。
9、 嵌套使用try-catch,或者try-catch后面沒有必要的finally操作。
10、 equals操作時沒有將常量放在equals操作符的左邊。
java低級錯誤
- Calendar的錯誤使用
Calendar從星期日開始到星期六為一個周期,數字表示依次為:1,2,3……7;MONTH的表示是從數字0開始,所以月份應該是該數字+1。所以我們在使用的時候一定要仔細的閱讀API文檔,避免類似的陷阱。
- 序列化類的多版本問題
Java的序列化機制是通過在運行時判斷類的serialVersionUID來驗證版本一致性的。在進行反序列化時,JVM會把傳來的字節流中的serialVersionUID與本地相應實體(類)的serialVersionUID進行比較,如果相同就認為是一致的,可以進行反序列化,否則就會出現序列化版本不一致的異常。當實現java.io.Serializable接口的實體(類)沒有顯式地定義serialVersionUID時,Java序列化機制會根據編譯的class自動生成一個serialVersionUID作序列化版本比較用,這種情況下,只有同一次編譯生成的 class才會生成相同的serialVersionUID
---解決方法:可以在程序一生成一個
serialVersionUID屬性,這樣運行
就不會出錯。
- 正確定義hashcode
- 解讀
類A覆寫了hashCode方法,並采用A的屬性value來生成A的hashCode。使用HashSet作為集合對象,把類A的一個對象a加入到HashSet中后,如果對象a的屬性value發生了變化,則a的hashCode()方法返回的值也就發生變化,則無法將對象a從HashSet中刪除。
預防措施如下:
1、如果覆寫了equals方法,必須同時覆寫hashCode方法。
2、equals方法和hashCode方法應避免使用易變的屬性,避免該對象的hashCode頻繁變化。
3、當對象被加入到HashSet(或作為Key加入到HashMap)后,應該避免該對象的hashCode發生變化。
4.正確理解Java的淺clone和深clone
淺clone和深clone都是clone,它們本質區別是對象內部的成員屬性(非原生類型屬性,如int等)在clone時是否處理為引用。如果仍然保留為引用,則稱為淺clone,反之稱為深clone。淺clone方式得到clone對象即可,深clone方式在得到clone對象后,還需要對引用的成員屬性進行“clone”處理。使用Vector等容器的clone方法一定要注意:對於不變屬性可以直接拷貝,對於可變屬性需要手動寫方法實現深層拷貝防止引用調用。深拷貝要深到什么程度,答案就是一直深到你想要的程度,包括深到不能再深的程度。
- 為什么定義equals方法的同時也要定義hashCode方法
equals方法用於實現對象之間邏輯上是否相等的判斷,而不是判斷兩個引用是否指向同一個對象,hashCode用於返回對象的哈希碼(也有翻譯成散列碼的),邏輯上相等(equals比較相等)的兩個不同對象它們返回的hashCode值肯定不相等。 Java規范中規定:如果兩個對象根據equals(Object)方法是相等的,那么調用這兩個對象中任一個對象的hashCode方法必須產生同樣的整數結果,所以定義equals的同時一定要定義hashCode,並且要保證equals比較相等的對象,hashCode返回值也必須相同。
如果定義了equals方法,沒有定義hashCode,兩個對象的hashCode卻不同,Map查找對象的時候會首先比較hashCode值,然后再使用equals比較,造成Map無法找到期望的對象。
- 大小寫轉換的正確處理
String提供有大小寫轉換方法:String.toUpperCase()和String.toLowerCase(),另外還有帶Locale參數的大小寫轉換方法:String.toUpperCase(Locale locale)和String.toLowerCase(Locale locale)。 String.toUpperCase(Locale locale)和String.toLowerCase(Locale locale)之所以要帶Locale參數,就是希望你指定使用的是哪種語言,不帶參數的String.toUpperCase()和String.toLowerCase()使用的是系統缺省的語言,例如操作系統的當前語言。有些語言的大小寫轉換使用了較特殊的規則,甚至不是1:1的字符對應關系,也就是說轉換前和轉換后的字符串長度不一定相等。
---如果在Eclipse的run對話框中顯示告訴VM我用的是Turkish,那最后輸出的結果已經不是我預期的大寫的I了,而是另外一個字符(İ)。
這個例子充分說明,大小寫轉換因語言環境的不同,轉換結果可能並非如你所預期的。
如果大小寫轉換之后你是要用於字符串比較(這樣的轉換純粹是為了比較的方便),也就是在我們的26個字母通常大小寫轉換預期內,則強烈建議你顯示指定“英文”的Locale(Local.US)。
如果你的大小寫轉換就是希望遵循當前使用軟件的國家語言大小寫轉換習慣,則使用不帶參數的String.toUpperCase()和String.toLowerCase()更好一些,這樣的代碼反而能夠適應不同的語言環境。
- 使用包裝器對象帶來的低效問題
解讀
每個基本類型(primitive)都有相應的包裝器(wrapper)對象:Integer、Long、Float、Double、Short、Byte、Character和Boolean,我們在使用時不要直接new Integer對象(這樣的做法是低效的),而應該調用包裝器對象的valueOf方法
為什么以上代碼是低效的,而要使用Integer result = Integer.valueOf(-1)或者Integer result = -1來代替?我們來看看JDK的Integer.valueOf方法實現
public static Integer valueOf(int i) {
final int offset = 128;
if (i >= -128 && i <= 127) { // must cache
return IntegerCache.cache[i + offset];
}
return new Integer(i);
}
可以看到內存中已經緩存有-128到127這個區間的256個Integer對象,如果方法傳入的i值介於這個范圍就不用再new Integer對象了,省去了運行時間,也節省了內存資源。另外的幾個包裝器對象( Long、Short、Byte、Character和Boolean )類似。
- 對於Map元素的遍歷使用entrySet還是KeySet?
JDK實現的數據結構中常用的Map有兩類:HashMap和TreeMap。keySet和entrySet在Map元素數較少時(小於10000)在查詢速度上的區別不大,它們對於程序性能的影響可以忽略不計。但在元素較多時(大於100000)時entrySet的速度要明顯快於keySet,尤其是TreeMap更明顯。
- 對InputStream.skip()返回值的處理(隱藏的異常)
java.io.InputStream.skip(long n):跳過和放棄此輸入流中的 n 個數據字節,返回的是跳過的實際字節數。如果skip方法的返回值小於要跳過得字節數,則說明有異常發生,此時需要對異常情況進行處理。
需要比較skip的返回值和輸入參數,如果兩者不相等時,需做特殊處理。
兩者不相等的情況可能的原因有:
1)在跳過 n 個字節之前已到達文件的末尾;
2)輸入參數為負;
- 正確理解String/StringBuffer/StringBuilder的區別
StringBuffer的內部實現方式和String不同,所以StringBuffer在進行字符串處理時,不生成新的對象,在內存使用上要優於String類。
StringBuilder提供一個與StringBuffer兼容的API,該類被設計用作 StringBuffer 的一個簡易替換,用在字符串緩沖區被單個線程使用的時候(這種情況很普遍)。如果可能,建議優先采用該類,因為在大多數實現中,它比 StringBuffer 要快。兩者的方法基本相同。
11.
- Java中流的操作,不要使用匿名流對象,以免出現異常,沒有句柄,無法關閉
例子:
try
{
File file = new File("test.exe");
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
//之后只關閉了一個流bis,匿名流new FileInputStream(file)沒有關閉
........
}
改寫之后:
try
{
File file = new File("test.exe");
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
//之后關閉兩個流fis和bis
........
}
這個效率相對低點
m.keySet().iterator()
以后最好用下面這個
jmap.entrySet().iterator()