内存

进入正题前首先要知道的是Java程序运行在JVM(Java Virtual Machine,Java虚拟机)上,可以把JVM理解成Java程序和操作系统之间的桥梁,JVM实现了Java的平台无关性,由此可见JVM的重要性。所以在学习Java内存分配原理的时候一定要牢记这一切都是在JVM中进行的,JVM是内存分配原理的基础与前提。

将内存分为了五块:栈内存、堆内存、方法区、本地方法栈、寄存器

栈内存

存储变量,执行代码块的。变量存储在栈内存中不会自动赋予初始值,变量使用完成之后立即移除出栈内存,释放空间

堆内存

存储对象,对象在堆内存中会自动赋予初始值。对象在使用完成之后,不会立即移除,而是在不定的某个时刻被回收。---初始值:byte/short/int---0,long---0L,float---0.0f

double---0.0,char--- ‘\u0000’,boolean--false,其他的引用数据类型的初始值是null

方法区:

静态成员、构造函数、常量池、线程池 

常量池:JVM为每个已加载的类型维护一个常量池,常量池就是这个类型用到的常量的一个有序集合。包括直接常量(基本类型,String)和对其他类型、方法、字段的符号引用。池中的数据和数组一样通过索引访问。由于常量池包含了一个类型所有的对其他类型、方法、字段的符号引用,所以常量池在Java的动态链接中起了核心作用。

本地方法栈

window系统占用

寄存器

JVM内部虚拟寄存器,存取速度非常快,程序不可控制。

Java 数组是线程安全嘛 java数组是在堆内存吗_数组

Java 数组是线程安全嘛 java数组是在堆内存吗_二维数组_02

Java 数组是线程安全嘛 java数组是在堆内存吗_Java 数组是线程安全嘛_03

Java 数组是线程安全嘛 java数组是在堆内存吗_一维数组_04


ArrayIndexOutOfBoundsException---数组下标越界异常---编译没有问题---说明明语法没有问题---逻辑上或者事实上不符合--运行出错

NullPointorException---空指针异常---编译没有问题---对于null而言,不能做任何操作

注意:对于基本类型而言,传值传的实际的数据;对于引用类型而言,传值传的是地址。

数组

存储一组同一个类型数据的容器---数组

数组对每一个存入的数字都会自动编号,编号是从0开始的---下标

定义格式

1.int[] arr = new int[5]; --- 表示定义了一个能存储5个int类型的数据的数组

Java中对数组中的元素进行了自动的编号。---编号是从0开始的。---下标

arr[2] = 15;
System.out.println(arr[2]);

2.数据类型[] 数组名 = new 数据类型[]{元素1, 元素2,……,元素n};---同时也就规定了数组的大小就是元素的个数

int[] arr = new int[]{3,5,1,2,5};
arr[3] = 10;

注意:数组一旦定义好,大小不可变

3.数据类型[] 数组名 = {元素1, 元素2,……,元素n};

int[] arr = {2,6,4,7,9};

数组的初始化

详细看内存部分的图片例子

Java 数组是线程安全嘛 java数组是在堆内存吗_二维数组_05

数组的应用

1. 获取数组中的元素---数组名[下标]

2. 获取数组的长度---数组名.length

3. 遍历数组---

for(int i = 0; i < arr.lenth; i++){}
for(int i : arr){}

//注意增强for循环不能改变数组中原来的元素

4. 获取数组中的最值---最大值

5. 对数组元素进行排序

冒泡排序

Java 数组是线程安全嘛 java数组是在堆内存吗_二维数组_06

for(int i = 1 ; i < arr.length ; i++){  //控制轮数
	for(int j = 1; j <= arr.length - i; j++){ //控制每轮的次数,以及下标
		if(arr[j - 1] > arr[j] ){
			int temp = arr[j - 1];
			arr[j - 1] = arr[j];
			arr[j] = temp;
		}    }    }

选择排序

Java 数组是线程安全嘛 java数组是在堆内存吗_Java 数组是线程安全嘛_07

for(int i = 1; i < arr.length ; i++){  //控制轮数和起始下标
	for(int j = i - 1; j < arr.length; j++){ //控制次数,后边那一位的下标的变化
		if(arr[i - 1] >  arr[j]){
			int temp =  arr[i - 1];
			arr[i  - 1] = arr[j];
			arr[j] = temp;
		}    }    }

快速排序、希尔排序、堆排序

import java.util.Arrays;

Arrays.sort(数组); //只能升序排列---从小到大

String s = Arrays.toString(arr);---将数组中的元素依次取出拼接成了一个字符串

6. 查找数组中元素

无序数组---for循环

有序数组---折半查找

Java 数组是线程安全嘛 java数组是在堆内存吗_一维数组_08

问题:折半查找中mid为何+1,mid为何-1?

Java 数组是线程安全嘛 java数组是在堆内存吗_一维数组_09

这种情况下,如果mid不加1的话,程序会陷入一个死循环。

7. 数组的反转

利用新的数组,也可以利用两个变量来同时操作数组的两端

8.数组的复制和扩容

System.arraycopy(要复制的数组, 要复制的起始下标, 要存放的数组, 存放的起始位置, 复制的元素的个数);

Arrays.copyOf(要扩容的数组, 扩容之后的大小);---表面上看起来对数组的长度做了改变,实际上数组已经发生了变化,已经是一个新的数组。数组在扩容完成之后,堆内存的地址已经发生了改变

Arrays.copyOf(arr,len);
int[] arr2 = new int[len];
if(len < arr.length){
System.arraycopy(arr,0,arr2,0,len);
} else {
System.arraycopy(arr,0,arr2,0,arr.length);
}

二维数组

是一组数组的容器---数组的数组

定义格式

1.数据类型[][] 数组名 = new 数据类型[二维数组的长度/包含的一维数组的个数][每个一维数组的长度];

int[][] arr = new int[3][5];---定义了一个整型的二维数组,其中包含3个一维数组,每个一维数组可以存储5个整数

arr[0]---下标为0的位置上的一维数组

arr[1][3]---如果要获取具体的元素需要两个下标

2.数据类型[][] 数组名 = new 数据类型[二维数组的长度/包含的一维数组的个数][];

int[][] arr = new int[3][];----表示一个包含了三个整型的一维数组的二维数组

Java 数组是线程安全嘛 java数组是在堆内存吗_二维数组_10

Java 数组是线程安全嘛 java数组是在堆内存吗_一维数组_11

注意:针对null的任何操作都会出现空指针异常

3.数据类型[][] 数组名 = {{元素},{元素1, 元素2},……};

int[][] arr = {{2,5},{1},{3,2,4},{1,7,5,9}};

面试题:已知int[] x,y[]均已初始化,那么下列各项正确的是:

A. y = x;
B. y[0] = x;
C. y[0][0] = x;
D. y[0] = x[0]
E. y[0][0] = x[0]

注意:[]在变量名之前紧跟的是数据类型,以后定义的每一个变量都会拥有这个[];如果[]在变量名之后,那么[]只属于当前的变量。

二维数组的应用

二维数组的长度:数组名.length ---每个一维数组:数组名[下标].length

二维数组的遍历---两重for循环

for(int i = 0; i < arr.length; i++){ //遍历二维数组,遍历出来的每一个元素是一个一维数组
for(int j = 0; j < arr[i].length; j++){ //遍历对应位置上的一维数组
System.out.println(arr[i][j]);
}
}

二维数组的反转---头尾交换

for(int start = 0, end = arr.length -1; start < end; start++,end--){
int[] temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
}

练习:杨辉三角

1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
arr[i][j] = arr[i - 1][j] + arr[i - 1][j - 1];
输入一个数n,输出前n行
//从控制台获取行数
Scanner s = new Scanner(System.in);
int row = s.nextInt();
//根据行数定义好二维数组,由于每一行的元素个数不同,所以不定义每一行的个数
int[][] arr = new int[row][];
//遍历二维数组
for(int i = 0; i < row; i++){
	//初始化每一行的这个一维数组
	arr[i] = new int[i + 1];
	//遍历这个一维数组,添加元素
	for(int j = 0; j <= i; j++){
		//每一列的开头和结尾元素为1,开头的时候,j=0,结尾的时候,j=i
		if(j == 0 || j == i){
			arr[i][j] = 1;
		} else {//每一个元素是它上一行的元素和斜对角元素之和
			arr[i][j] = arr[i -1][j] + arr[i - 1][j - 1];
		}
		System.out.print(arr[i][j] + "\t");
	}
	System.out.println();
}