基本數據類型vs對象類型
當你閱讀這篇文章的時候,可能已經知道了Java是雙類型的系統,也就是基本數據類型和對象類型,簡稱基本類型和對象。Java中有8個預定義的基本類型,它們的名字都是保留的關鍵字。常見的基本類型有int、double和boolean。Java中所有其他的類型包括用戶自定義的類型,它們必然也是對象類型(我說”必然”是因為數組類型有點例外,與基本類型比數組更像是對象類型)。每一個基本類型都有一個對應的對象包裝類,比如int的包裝類是Integer,double的包裝類是Double,boolean的包裝類是Boolean。
基本類型基於值,而對象類型則基於引用。與基本類型相關的爭議都源於此。為了說明它們的不同,先來看一下兩個聲明語句。第一個語句使用的是基本類型,第二個使用的是包裝類。
int n1 = 100;
Integer n2 = new Integer(100);
使用新添加到JDK5的特性自動裝箱以后,第二個聲明可以簡化成:
Integer n2 = 100;
但是,底層的語義並沒有發生改變。自動裝箱簡化了包裝類的使用,減少了程序員的編碼量,但是對運行時並沒有任何的改變。
圖1展示了基本類型n1和包裝對象類型n2的區別。
n1持有一個整數的值,但是n2持有的是對一個對象的引用,即那個對象持有整數的值。除此之外,n2引用的對象也包含了一個對Double對象的引用。
內存的使用
Java中的double總是占據內存的64個比特,但是引用類型的字節數取決於JVM。我的電腦運行64位Win7和64位JVM,因此在我的電腦上一個引用占用64個比特。根據圖1,一個double比如n1要占用8個字節(64比特),一個Double比如n2要占用24個字節——對象的引用占8個字節,對象中的double的值占8個字節,對象中對Double對象的引用占8個字節。此外,Java需要使用額外的內存來支持對象的垃圾回收,但是基本類型不需要。
package mytest; import java.util.Date; public class Test { /** * @param n 矩陣n*n * @param type 類型 1:double 2:Double * @return 獲得存儲所使用的字節數 */ public static long getBytesUsingPrimitives(int n,int type){ System.gc();// force garbage collection long memStart = Runtime.getRuntime().freeMemory(); // put some random values in the matrix switch(type){ case 1: double[][] a = new double[n][n]; for (int i = 0; i < n; ++i){ for (int j = 0; j < n; ++j) a[i][j] = Math.random(); } break; case 2: Double[][] ax=new Double[n][n]; for (int i = 0; i < n; ++i){ for (int j = 0; j < n; ++j) ax[i][j] = Math.random(); } break; default: break; } long memEnd = Runtime.getRuntime().freeMemory(); return memStart - memEnd; } //創建double類型矩陣n*m public static double[][] create(int n,int m){ double[][] a = new double[n][m]; for (int i = 0; i < n; ++i){ for (int j = 0; j < n; ++j) a[i][j] = Math.random(); } return a; } //創建Double類型矩陣n*m public static Double[][] createD(int n,int m){ Double[][] a=new Double[n][n]; for (int i = 0; i < n; ++i){ for (int j = 0; j < n; ++j) a[i][j] = Math.random(); } return a; } /** * 創建矩陣,並使兩個矩陣相乘 * 返回 運行時長 */ public static long multiply(int x1,int y1,int x2,int y2,int type){ Date d1=null; switch(type){ case 1: double[][] a=create(x1,y1); double[][] b=create(x2,y2); d1=new Date(); if (!checkArgs(y1, x2)) throw new IllegalArgumentException("Matrices not compatible for multiplication"); int nRows = a.length; int nCols = b[0].length; double[][] result = new double[nRows][nCols]; for (int rowNum = 0; rowNum < nRows; ++rowNum){ for (int colNum = 0; colNum < nCols; ++colNum){ double sum = 0.0; for (int i = 0; i < a[0].length; ++i) sum += a[rowNum][i]*b[i][colNum]; result[rowNum][colNum] = sum; } } break; case 2: Double[][] ax=createD(x1,y1); Double[][] bx=createD(x2,y2); d1=new Date(); if (!checkArgs(y1, x2)) throw new IllegalArgumentException("Matrices not compatible for multiplication"); int nRowsx = ax.length; int nColsx = bx[0].length; Double[][] resultx = new Double[nRowsx][nColsx]; for (int rowNum = 0; rowNum < nRowsx; ++rowNum){ for (int colNum = 0; colNum < nColsx; ++colNum){ Double sum = 0.0; for (int i = 0; i < ax[0].length; ++i) sum += ax[rowNum][i]*bx[i][colNum]; resultx[rowNum][colNum] = sum; } } break; default: break; } Date d2=new Date(); return d2.getTime()-d1.getTime(); } private static boolean checkArgs(int y1, int x2) { if(y1!=x2) return false; return true; } public static void main(String[] args){ //運行結果,並不是保持不變,這里是平均值 System.out.println(getBytesUsingPrimitives(1000, 1));//字節數統計 7585424 System.out.println(getBytesUsingPrimitives(1000, 2));// 28646800 System.out.println(multiply(1000,1000,1000,1000,1));//所用時間 12773 System.out.println(multiply(1000,1000,1000,1000,2));// 39017 } }