文章目錄
- 前言
- 一、數組的基本概念
- 1.1 數組的創建和初始化
- 1.2 數組的基本使用
- 二、數組是引用類型
- 2.1 初始JVM的內存分布
- JVM內存劃分(按功能分區)
- 2.2 基本類型變量與引用類型變量的區別
- 2.3 再談引用變量
- 2.4 認識null
- 三、數組作為函數的參數和返回值
- 四、數組的應用場景
- 4.1 數組轉字符串
- 4.2 數組的拷貝
- 4.3 深拷貝和淺拷貝問題
- 4.3 冒泡排序的小優化
- 4.4 數組逆序
- 4.5 數組逆序
- 4.6 數組練習
- 五、二維數組
- 5.1 普通二維數組
- 5.2 不規則的二維數組
- 總結
前言
提示:這里可以添加本文要記錄的大概內容:
在Java編程的征途中,數組作為基礎卻至關重要的數據結構,是每個開發者必須掌握的核心技能。它不僅是存儲同類型元素的??高效容器??,更是算法實現、數據處理的??底層基石??。從簡單的成績管理系統到復雜的矩陣運算,數組以其簡潔的內存模型和快速的隨機訪問能力,奠定了程序邏輯的基本骨架。本篇博客將深入剖析Java數組的特性、應用場景及性能要點,幫助您從語法本質到實戰技巧全面駕馭這一編程利器。
注意:和c語言內容大致相同的部分會簡單帶過或者不講解
提示:以下是本篇文章正文內容,下面案例可供參考
一、數組的基本概念
1.1 數組的創建和初始化
數組的創建:
T[] 數組名 = new T[N];
T:表?數組中存放元素的類型
T[]:表?數組的類型
N:表?數組的?度
public class array {public static void main(String[] args) {int[] array1=new int[10];double[] array2=new double[10];String[] array3=new String[10];}
}
數組的初始化:
數組的初始化主要分為動態初始化以及靜態初始化
- 動態初始化:在創建數組時,直接指定數組中元素的個數,只開辟空間但是不賦初值
- 靜態初始化:在創建數組時不直接指定數據元素個數,?直接將具體的數據內容進?指定
示例代碼:
public class array {public static void main(String[] args) {int[] array1=new int[]{0,1,2,3,4,5,6};double[] array2=new double[]{1.0,2.0,3.0,4.0,5.0,6.0};String[] array3=new String[]{"hello"};int[] array4={1,2,3,4};}
}
【注意事項】:
- 靜態初始化雖然沒有指定數組的?度,編譯器在編譯時會根據{}中元素個數來確定數組的?度。
- 靜態初始化時,{}中數據類型必須與[]前數據類型?致。
- 靜態初始化可以簡寫,省去后?的new T[]。
**
靜態和動態初始化也可以分為兩步,但是省略格式不可以。
示例代碼如下:**
public class array {public static void main(String[] args) {int[] array1;array1=new int[4];int[] array2;array2=new int[]{1,2,3};//下面方式不可行int[] array3;array3={1,2,3};}
}
如果沒有對數組進?初始化,數組中元素有其默認值:
如果數組中存儲元素類型為引?類型,默認值為null
1.2 數組的基本使用
通過代碼來演示數組的使用:
public class array {public static void main(String[] args) {String[] array1=new String[]{"hhh","aaa","zzz"};array1[2]="dzj";for (String str: array1) {System.out.println(str);}}
}
遍歷數組:
public class array {public static void main(String[] args) {String[] array1=new String[]{"hhh","aaa","zzz"};array1[2]="dzj";//遍歷方式一for (String str: array1) {System.out.println(str);}//遍歷方式二for (int i = 0; i < array1.length; i++) {System.out.println(array1[i]);}}
}
二、數組是引用類型
2.1 初始JVM的內存分布
JVM也對所使用的內存按照功能的不同進行了劃分
JVM內存劃分(按功能分區)
-
程序計數器(PC Register)
- 占用極小的內存空間
- 核心功能:保存下一條要執行的指令地址
- 線程私有,每個線程獨立存儲
-
虛擬機棧(JVM Stack)
- 存儲方法調用相關的運行時數據
- 方法執行機制:
? 每個方法執行時創建對應的棧幀
? 棧幀包含五部分:- 局部變量表(方法參數和局部變量)
- 操作數棧(執行引擎的工作區)
- 動態鏈接(指向運行時常量池的方法引用)
- 返回地址(方法結束后的返回位置)
- 附加信息(如調試數據)
- 生命周期:
? 方法開始執行 → 棧幀入棧
? 方法執行結束 → 棧幀出棧銷毀
-
本地方法棧(Native Method Stack)
- 為Native方法(本地方法) 提供服務
- 功能與虛擬機棧類似
- 特殊說明:在HotSpot等JVM實現中與虛擬機棧合并
-
堆(Heap)
- JVM管理的最主要內存區域(占比最大)
- 存儲特性:
? 所有new創建的對象都在堆上分配(包括數組對象)
? 對象存儲要求:new int[]{1, 2, 3} // 堆內存分配
- 被引用對象不會被銷毀
- 未被引用對象由GC回收
- 生命周期管理:
? JVM啟動時創建
? 程序運行期間動態調整大小
? JVM退出時銷毀
-
方法區(Method Area)
- 核心存儲內容:
? 已被加載的類元數據(類名/字段/方法/父類/接口等)
? 運行時常量池(字符串常量等)
? 靜態變量(static修飾的類變量)
? 即時編譯器編譯后的代碼(JIT優化產物) - 特殊說明:字節碼指令(編譯后的方法代碼)存儲在此區域
- 核心存儲內容:
2.2 基本類型變量與引用類型變量的區別
基本數據類型創建的變量,稱為基本變量,該變量空間中直接存放的是其所對應的值;
?引?數據類型創建的變量,?般稱為對象的引?,其空間中存儲的是對象所在空間的地址。
注意:java中是不能獲取到變量的地址的,也就是說java中并沒有指針的概念
2.3 再談引用變量
public class array {public static void main(String[] args) {int[] array2 = new int[]{1,2,3,4};int[] array1=array2;array1[0]=100;array1[1]=200;array1[2]=300;array1[3]=400;for (int i = 0; i < array2.length; i++) {System.out.println(array2[i]);}}
}
注意:這里的array1=array2是地址賦值,所以改變array1中的數組值所以array2中的內容也會發生改變,兩個引用變量此時指向了同一塊內存空間
2.4 認識null
null 在Java中表?"空引?",也就是?個不指向對象的引用
int[] arr = null;System.out.println(arr[0]);// 執?結果Exception in thread "main" java.lang.NullPointerExceptionat Test.main(Test.java:6)
null 的作?類似于C語?中的NULL(空指針),都是表??個無效的內存位置.因此不能對這個內存進?任何讀寫操作.?旦嘗試讀寫,就會拋出NullPointerException.
JAVA中并沒有規定null是零地址
三、數組作為函數的參數和返回值
參數傳數組類型(引用數據類型)
public class array {public static void main(String[] args) {int[] arr={1,2,3,4,5,6};fix(arr);for (int x:arr) {System.out.println(x);}}public static void fix(int[] arr){for (int i = 0; i < arr.length; i++) {arr[i]=i*100;}}
}
總結:所謂的"引用"本質上只是存了?個地址.Java將數組設定成引用類型,這樣的話后續進行數組參數傳參,其實只是將數組的地址傳入到函數形參中.這樣可以避免對整個數組的拷貝(數組可能?較長,那么拷貝開銷就會很大).
數組作為函數的返回值
比如:獲取斐波那契數列的前N項
public class array {public static void main(String[] args) {int[] array=fib(20);assert array != null;for (int x:array) {System.out.println(x);}}public static int[] fib(int n){if(n<=0){return null;}int[] array=new int[n];array[0]=array[1]=1;for (int i = 2; i <array.length ; i++) {array[i]=array[i-1]+array[i-2];}return array;}
}
四、數組的應用場景
4.1 數組轉字符串
import java.util.Arrays;
import java.util.Scanner;public class array {public static void main(String[] args) {int[] array={1,2,3,4,5,6};String ret=Arrays.toString(array);System.out.println(ret);}public static void main1(String[] args) {Scanner scanner=new Scanner(System.in);int[] array = {2,6,4,1};System.out.println(sequence(array));}public static boolean sequence(int[] array) {boolean flag = true;int count=0;for (int i = 0; i <array.length-3 ; i++) {if(array[i]%2!=0){count++;if(count==3){return true;}}else{count=0;}}return false;}
}
4.2 數組的拷貝
public static void main(String[] args) {int[] array={1,2,3,4,5,6};int[] array1=Arrays.copyOf(array,array.length);//擴容操作int[] array2=Arrays.copyOf(array,array.length*2);int[] array3=Arrays.copyOfRange(array,2,5);String ret1=Arrays.toString(array1);System.out.println(ret1);String ret2=Arrays.toString(array2);System.out.println(ret2);String ret3=Arrays.toString(array3);System.out.println(ret3);}
注意:數組當中存儲的是基本類型數據時,不論怎么拷?基本都不會出現什么問題,但如果存儲的是引?數據類型,拷?時需要考慮深淺拷?的問題,
源碼部分展示:
4.3 深拷貝和淺拷貝問題
深拷貝和淺拷貝通常只出現在引用數據類型的范疇之上,基本數據類型不存在深拷貝和淺拷貝問題!!
當拷貝內容為引用數據類型的時候,淺拷貝指的是拷貝數組的內容僅僅是拷貝變量的值,而沒有存儲對象本身所存儲的內容,而深拷貝恰恰相反。
圖片左邊是淺拷貝,右邊是深拷貝
4.3 冒泡排序的小優化
public static void main(String[] args) {int[] array={4,6,3,2,1,7,8,11,23,22,42,21};bubbleSort(array);for (int x: array) {System.out.print(x+" ");}}public static void bubbleSort(int[] array){for(int i=0;i<array.length-1;i++){boolean flg=false;for (int j = 0; j < array.length-1-i; j++) {if(array[j]>array[j+1]){int tmp=array[j];array[j]=array[j+1];array[j+1]=tmp;flg=true;}}if(!flg){return;}}}
冒泡排序本身時間復雜度比較高,使用java內置的sort函數即可:
public static void main(String[] args) {int[] array={4,6,3,2,1,7,8,11,23,22,42,21};Arrays.sort(array);for (int x: array) {System.out.print(x+" ");}}
4.4 數組逆序
public static void main(String[] args) {int[] array={1,2,3,4,5,6};reverse(array);for (int x: array) {System.out.print(x+" ");}}public static void reverse(int[] array){int left=0;int right=array.length-1;while(left<right){int tmp=array[left];array[left]=array[right];array[right]=tmp;left++;right--;}}
4.5 數組逆序
調整數組順序使得奇數位于偶數之前。調整之后,不關心大小順序。
如數組:[1,2,3,4,5,6]
調整后可能是:[1, 5, 3, 4, 2, 6]
public static void main(String[] args) {int[] array={1,2,3,4,5,6};change(array);for (int x: array) {System.out.print(x+" ");}}public static void change(int[] array){int left=0;int right=array.length-1;while(left<right){while(left<right&&array[left]%2!=0){left++;}while(left<right&&array[right]%2==0){right--;}int tmp=array[left];array[left]=array[right];array[right]=tmp;}}
4.6 數組練習
異或的特點是:
1、n ^ n = 0;即兩個相同的數字異或是0
2、0 ^ n = n;即0和任何數字進行異或,結果就是那個任何數字。
public static void main(String[] args) {int[] array={4,1,2,1,2};int num=getNum(array);System.out.println(num);}public static int getNum(int[] array){int ret=0;for (int i = 0; i < array.length; i++) {ret^=array[i];}return ret;}public static void main6(String[] args) {int[] array={1,2,3,4,5,6};change(array);for (int x: array) {System.out.print(x+" ");}}
五、二維數組
5.1 普通二維數組
基本語法:
數據類型[][] 數組名稱 = new 數據類型 [?數][列數]{ 初始化數據 };
注意:二維數組行不可以省略,但是列可以省略
public static void main(String[] args) {int[][] array = new int[3][];}
代碼示例:
public static void main(String[] args) {int[][] array = {{1,2,3},{4,5,6},{7,8,9}};for (int i = 0; i < array.length; i++) {for (int j = 0; j < array[i].length; j++) {System.out.print(array[i][j]+" ");}System.out.println();}}
5.2 不規則的二維數組
不規則的?維數組指的是,?維數組的列在定義的時候,沒有確定。
int[][] array = new int[3][];
public static void main(String[] args) {int[][] array = new int[2][];array[0]=new int[3];array[1]=new int[5];for (int i = 0; i < array.length; i++) {for (int j = 0; j < array[i].length; j++) {System.out.print(array[i][j]+" ");}System.out.println();}}
總結
作為Java編程的??基礎支柱??,數組的價值遠超出其簡單的語法形式。它不僅提供了數據的高效組織方式,更通過多維數組支持復雜數據建模,借助Arrays工具類實現強大操作能力。盡管集合框架在現代開發中日益普及,但在性能敏感的底層系統、算法實現以及內存優化場景中,數組仍展現著不可替代的優勢。真正掌握數組的內存機制、遍歷技巧與異常處理,將使您的代碼在嚴謹性和效率上實現質的飛躍——這正是構建健壯程序的關鍵一步。