目录
补充:java内存划分简介——5个部分
(1)栈(Stack):存放方法中的局部变量,一旦超出作用域就会从栈中消失。方法一定是在栈中运行的。
(2)堆(Heap):凡是被new出来的都在堆中,即栈中只保存变量名以及f变量值在堆中的地址(16进制),而堆中则保存实际的变量值。
(3)方法区(Method Area):保存.class相关信息,包含方法信息。
(4)本地方法栈(Native Method Stack)
(5)寄存器(PC Register)
1.数组的特点
(1)数组是一种引用数据类型。
(2)数组当中的数据类型必须统一。
(3)数组的长度在初始化时已经给定,在程序运行时不可修改。
2.数组的初始化
(1)静态初始化(给定内容)
(2)动态初始化(给定长度)
public class Demo1 {
public static void main(String[] args){
//静态初始化数组
int array1[] = new int[]{1,3,2,4,6,8,};
int array2[];
array2 = new int[]{1,3,2,4,6,8,};
//可以简写为如下形式,但此时不可拆分
int array3[] = {1,3,2,4,6,8};
//动态初始化数组
/*
1.必须给定数组长度,此处为5
2.new int[5]代表为数组array在内存中开辟了一个数组长度为5的连续内存空间
*/
int array[] = new int[5];//可拆分
for(int i = 0;i < array.length;i++) {
System.out.print(array[i] + " ");//查看数组之中的内容
}
System.out.println();
//为array赋值
array[1] = 1;
array[2] = 3;
//array[5] = 6;//数组越界,因为长度为5的数组下标为0-4,没有索引为5的元素
for(int i = 0;i < array.length;i++) {
System.out.print(array[i] + " ");//再次查看数组之中的内容
}
}
}
结果为:
0 0 0 0 0
0 1 3 0 0
3.数组操作的内存示意图
public class Demo2 {
//主方法,存放于方法区中,运行时调入栈内
public static void main(String[] args){
//若此时在栈内主方法开始运行
/*
1.定义一个名为arrayA的数组,并存放于栈内
主方法区
*/
int arrayA[];
/*
2.在堆内存中为arrayA开辟一块长度为5的连续空间,
其中每个元素值默认为0,
并把该空间的基地址保存在栈内的arrayA中
*/
arrayA = new int[5];
/*
3.通过栈中的arrayA找到堆中的基地址,
找到逻辑地址为2的内存块,把其中的值改为3
*/
arrayA[2] = 3;
/*
4.对于引用型变量的赋值,实际是把变量的基地址赋值给
赋值对象,即两个变量共享一组堆内存块
*/
int arrayB[] = arrayA;
/*
5.此时若改变arrayB的值,那么arrayA的值肯定也会
发生变化
*/
arrayB[2] = 4;
System.out.println(arrayA[2]); //此时结果为4
//输出arrayA和arrayB的地址,地址必定相同,因为共用地址
System.out.println(arrayA);
System.out.println(arrayB);
}
}
结果:
4
[I@1b6d3586
[I@1b6d3586
以上步骤的执行过程如图所示
4.数组的扩充
因为数组长度不可变,因此想要直接扩充一个数组的长度是不可能实现的,但是我们可以通过重新定义一个更长的数组,并把它的基地址赋值给要扩充的数组,以此来达到数组扩充的目的。
鉴于java的垃圾自动回收机制,我们无需考虑废弃的堆内存占用内存空间的问题。
public class Demo3 {
public static void main(String[] args){
int[] array = new int[3];
array[0] = 2;
array[1] = 3;
array[2] = 5;
//此时已达到最大长度,若想再添加2个数一个必须扩充数组
//我们可以先定义一个新的数组
int[] extend = new int[5];
//之后把原数组的元素赋值给该数组
for(int i = 0;i < array.length;i++){
extend[i] = array[i];
}
//之后添加新的元素
extend[3] = 4;
extend[4] = 6;
//最后把新数组赋值给原数组(即原数组的基地址指向新数组的基地址)
array = extend;
//至此,我们完成了一个数组的扩充
//此时我们打印一下array数组的元素
for(int i = 0;i < array.length;i++){
System.out.print(array[i] + " ");
}
}
}
结果:
2 3 5 4 6
5.数组的应用
针对数组的算法有很多,尤其是排序和查找算法。下面介绍几个简单的算法实现:
public class Demo4 {
//求数组中的最大值
public static int maxNum(int[] array){
int maxNum = 0;//每次循环后的最大值
for(int i : array){
if(i > maxNum){
maxNum = i;
}
}
return maxNum;
}
//数组元素反转
public static int[] reserve(int[] array){
int front = 0;//要交换的第一个数的索引,初始为第一个
int rear = array.length-1;//要交换的第二个数的索引,初始为最后一个
int temp = 0;//中间变量,用于寄存值
while(front < rear){//控制交换到最中间时停止
temp = array[front];
array[front] = array[rear];
array[rear] = temp;
front++;//先前走
rear--;//先后走
}
return array;
}
//数组从小到大排序(此处用冒泡排序)
public static int[] bubbleSort(int[] array){
int temp = 0;//寄存
for(int i = 0;i < array.length - 1;i++){
boolean flag = false;//用于判断每次是否交换过元素
for(int j = array.length - 1;j > i;j--){
if(array[j-1] > array[j]){
temp = array[j-1];
array[j-1] = array[j];
array[j] = temp;
flag = true;
}
}
if(flag == false){//没有交换过就停止
break;
}
}
return array;
}
//遍历数组
public static void ergodic(int[] array){
for(int i : array){
System.out.print(i + " ");
}
}
//运行主方法
public static void main(String[] args){
int[] array = {2,4,6,1,7,0,2,4,6,7,};
//求最大值
int maxNum = maxNum(array);
//反转数组
int[] array1 = reserve(array);
ergodic(array1);//显示结果
System.out.println();
//排序
int[] array2 = bubbleSort(array);
ergodic(array2);//显示结果
}
结果为:
7
7 6 4 2 0 7 1 6 4 2
0 1 2 2 4 4 6 6 7 7
}
6.二维数组
二维数组在物理上理解为一个一维数组的每个数组元素又为一个数组,在逻辑上可以看做一个矩阵。
(1)二维数组的定义
public static void main(String[] args){
/*定义一个3行三列的二维矩阵
第一个[3]表示每行3列
第二个[3]表示有三行
我们可以理解为一个一维数组他的元素也是数组
即前面的int[]表示数据类型为int类型的数组,后面的[]表示这是一个数组
*/
int a[][] = new int[3][3];
}
(2)二维数组的内存表示
(3)二位数组的应用举例:定义一个有向图(详细的理论知识在数据结构图的章节中)
我们先定义一个图的结构:
public class Graph {
int edges[][];//图的邻接矩阵
int n,e;//n为顶点数,e为边数
Vertex[] vex;//顶点的信息
}
//定义顶点
class Vertex{
int no;//顶点编号
char info;//顶点信息
}
下面我们创建上图中的有向图:
//创建一个图
Graph graph = new Graph();
//给出图的邻接矩阵
graph.edges = new int[4][4];//矩阵大小为6✖6
//[1][0]表示从顶点编号为1的点到顶点编号为0的点是否有有向边,
// 没有为0,有为1(也可以为边的权值)
graph.edges[0][0] = 0;graph.edges[0][1] = 1;
graph.edges[0][2] = 0;graph.edges[0][3] = 1;
graph.edges[1][0] = 0;graph.edges[1][1] = 0;
graph.edges[1][2] = 1;graph.edges[1][3] = 1;
graph.edges[2][0] = 0;graph.edges[2][1] = 0;
graph.edges[2][2] = 0;graph.edges[2][3] = 1;
graph.edges[3][0] = 0;graph.edges[3][1] = 0;
graph.edges[3][2] = 0;graph.edges[3][3] = 0;
graph.n = 4;//4个顶点
graph.e = 5;//5个有向边
graph.vex = new Vertex[4];//4个顶点信息,此图顶点无信息,因此不必添加
//下面打印一下这个矩阵
for(int i = 0;i < graph.n;i++){
for(int j = 0;j < graph.n;j++){
System.out.print(graph.edges[i][j] + " ");
}
System.out.println();
}
结果为:
0 1 0 1
0 0 1 1
0 0 0 1
0 0 0 0
从结果明显可以看出,0到1有边,0到3有边,1到2有边,1到3有边,2到3有边,证明我们创建的图是正确的。