九、数组
9.1、JVM初探究
- 程序计数器:当前线程所执行的字节码的行号的指示器
- 本地方法栈:为虚拟机使用的native方法服务
- 方法区:线程共享的内存区域,存储已经被虚拟机加载的类的信息、常量、静态变量,这个区域的内存回收的目标主要是针对常量池的回收和对类型的卸载
- Java虚拟机栈:简称栈,每个方法被执行的同时会创建一个栈帧用于存储该方法的局部变量、操作栈、动态链接、方法出口等信息
- Java堆:被所有线程共享的一块内存区域,在虚拟机创建的时候启动,
所有对象的实例(new出来的对象)
以及数组
都要在堆上分配内存空间,所以堆占的内存空间远比栈大
- 每当调用一个方法时,创建一个栈帧,存放了当前方法的局部变量,当方法调用完毕时,该方法的栈帧就被销毁了
- 每次new一个对象的时候,就表示在内存中开辟了一块新的存储空间
9.2、数组
9.2.1、什么是数组
具有相同类型的多个常量值有序组织起来的一种数据形式,数组中使用索引来表示元素存放的位置,索引从0开始,步长是1,有点像Excel表格的行号
9.2.2、定义语法
数组元素类型[] 数组名; int [] nums; 复制代码
注意:
- 可以把int[] 看成一种数据类型,int类型的数组类型
- int[]数组表示,这个数组中的元素都是int类型的,同理
9.2.3、数组的初始化
数组在定义后,必须初始化才能使用。所谓初始化,就是在堆内存中给数组分配存储空间,并为每一个 元素赋上初始值,有两种方式:静态初始化和动态初始化,数组的长度是固定的,无论以哪种,一旦初始化完成,数组的长度(元素的个数)就固定了,不能改变,除非重新对该初始化,初始化时,如果我们明确了具体的元素就用静态初始化,如果还不知道具体放哪些元素,只知道个数,用动态初始化
9.2.3.1、静态初始化
我们直接为每一个数组元素设置初始化值,而数组的长度由系统(JVM)决定
语法:数组元素类型[] 数组名 = new 数组元素类型[]{元素1,元素2,元素3,.......};
int[] nums = new int[]{1,3,5,7,9}; //简单写法: int[] nums = {1,3,5,7,9};//简单写法,定义和初始化必须同时写出来 复制代码
9.2.3.2、静态初始化内存分析
public class ArrayDemo1{ public static void main(String[] args) { //定义并初始化数组 int[] nums = new int[] { 1, 3, 5, 7 }; System.out.println("数组长度=" + nums.length); //重新初始化数组 nums = new int[] { 2, 4, 8 }; System.out.println("数组长度=" + nums.length); } } 复制代码
若 num = null,则null表示不再引用堆中的内存空间,那么此时nums就好比是没有初始化的,不能使用
9.2.3.4、动态初始化
程序员只设置数组元素个数,而数组的元素的初始值由系统(JVM)决定
语法:数组元素类型[] 数组名 = new 数组元素类型[length]; int[] nums = new int[5];
注意:不能同时指定元素值和数组长度,int[] nums = new int[5]{1,3,5,7,9}
是错误的
内存图与静态初始化一样,只是拥有默认值
9.2.4、数组中对元素的操作
9.2.4.1、获取元素的个数
int size = 数组名.length;
9.2.4.2、设置元素
nums[1] = 30;
9.2.4.3、获取元素
元素类型 变量名 = 数组名[index];
9.2.5、数组中常见的异常
- NullPointerException:空指针异常(空引用异常)
操作了一个尚未初始化或者没有分配内存空间的数组
- ArrayIndexOutOfBoundsException:数组的索引越界异常
操作的数组的索引不在[0,数组名.length-1]范围内
9.2.6、数组遍历
9.2.6.1、for循环
int[] nums = new int[] { 1, 3, 5, 7 }; for (int index = 0; index < nums.length; index++) { int ele = nums[index];//index依次是 0、1、2、3 System.out.println(ele); } 复制代码
9.2.6.2、for-each(增强for循环)
for(数组元素类型 变量: 数组){ //TODO } 复制代码
int[] nums = new int[] { 1, 3, 5, 7 }; for (int ele : nums) { System.out.println(ele); } 复制代码
使用for-each操作数组更简单,因为可以不关心索引,其底层原理依然是上述的for循环操作数组
9.2.7、二维数组
在之前,数组的每一个元素就是一个个的值,这种数组我们称之为一维数组。二维数组,就是数组中的每一个元素是另一个一维数组
9.2.7.1、二维数组的定义和初始化
静态
public class ArrayInArrayDemo1 { public static void main(String[] args) { //定义三个一维数组 int[] arr1 = { 1, 2, 3 }; int[] arr2 = { 4, 5 }; int[] arr3 = { 6 }; //把三个一维数组存储到另一个数组中,那么该数组就是二维数组 int[][] arr = new int[][] { arr1, arr2, arr3 }; } } 复制代码
二维数组中的元素类型是一维数组,把数组元素类型[]看成一个整体,表示数据类型
动态
数组元素类型[][] 数组名 = new 数组元素类型[x][y]; x表示二维数组中有几个一维数组 y表示每一个一维数组中有几个元素。 int[][] arr = new int[3][5]; 复制代码
9.2.7.2、获取二维数组的元素
for循环
for (int index = 0; index < arr.length; index++) { //取出每一个一维数组 int[] arr2= arr[index]; //迭代一维数组 for (int j = 0; j < arr2.length; j++) { int ele = arr2[j]; System.out.println(ele); } System.out.println("-----"); } 复制代码
for-each
for (int[] arr2 : arr) { //arr2为每次遍历出来的一维数组 for (int ele : arr2) { //ele为从arr2一维数组中遍历出来的元素 System.out.println(ele); } System.out.println("-----"); } 复制代码
十、方法
10.1、方法的定义
方法:为了完成某一特定功能(如:求和,统计数量等)的代码块
语法格式:
修饰符] 返回值类型 方法名称(参数类型 参数名1,参数类型 参数名2,…) { 方法体; [return 返回值;] } 复制代码
格式分析:
- 修饰符:public、static等,static修饰的方法直接使用类名调用即可,用static修饰的方法都属于类的
- 返回值类型:限定返回值的类型,方法在完成一个功能后,是否需要给调用者返回一个结果?
- 如果需要给调用者返回结果,就写上返回数据的类型
- 如果不需要给调用者返回结果,就用关键字
void
,表示没有返回结果
- 方法名称:用于调用方法,遵循标识符规范,首字母小写,采用驼峰命名法,见名知意
- 形式参数:方法中圆括号中的变量,可以有多个形式参数
- 方法体:编写如何完成该功能的代码
- return关键字的作用
- 把返回值给方法的调用者
- 结束该方法,在return后不可以再学任何语句
- 当方法体中没有return时,方法的返回值类型必须为void
- 实际参数:在调用某一个具体方法1时,实际传递的参数值
- 如果一个需要返回值,那么一定要保证在任何条件下都必须得有返回值
注意事项
- 方法必须定义到类中,在java中最小的程序单元是类
- 一个类中可以定义多个方法
- 方法和方法之间是平行的,不能在一个方法中定义另一个方法(相当于不可以在一个房子里面建房子)
- 方法定义没有先后顺序
10.2、方法的调用
如果方法有static修饰,可以直接用方法所在的类的类名调用,如果没有static修饰,那么必须使用实例化对象来调用
10.3、方法的重载
参数列表:参数的类型+参数的个数+参数的顺序
方法签名:方法名称+方法的参数列表
在同一个类中,方法签名是唯一的,否则编译报错
方法的重载:在同一个类中,允许某方法存在一个或者多个同名的方法,但是必须参数列表不同
10.3.1、如何是否是方法重载
方法重载判断的原则:两同一不同
两同:在同一个类中,方法名是相同的
一不同:方法的参数列表不同(参数类型、参数个数、参数顺序),只要参数类型、参数个数、参数顺序其中有一个不同,都成为参数列表不同
方法的重载和返回值类型无关,只是一般都要求返回值类型相同
10.3.2、方法重载的作用
解决了同一功能的方法由于参数不同所造成的方法名称不同而需要重复命名的问题