一、数组的定义
数组(Array)是一种数据存储结构,是一组相同类型的数据的集合,可以分为一维数组和多维数组,本篇文章主要是介绍一维数组。
1、数组的声明
数组使用前必须指定数组中存放数据的类型,也就是需要先声明;
格式为:
- 数据类型 [] 数组名;
- 数据类型 数组名 [];
第一种写法更符合Java的数据结构,所以推荐使用第一种书写方式;
int[] i;
char[] c;
double [] d;
folat f [];//不推荐使用这种形式,一般是C语言的写法
2、数组的创建
- 数组名 = new 数据类型 [长度];
i = new int[10];
c = new char[2];// new关键字是在内存开辟一块空间
d = new double[32];// 指定数组的长度为32
**注意:**new关键字是在内存开辟一块空间
int[] ages = new int[48];
过程分析:首先在栈内存里存储ages变量,然后通过new关键字在堆内存中开辟一块32*48大小的空间,返回一个表示该块空间的地址(hashCode码)然后赋值给ages,最终ages里存储的是指向那块空间的地址(hashCode码)
- 当然声明和创建可以一起写:数据类型 [] 数组名 = new 数据类型 [长度];
int[] i = new int[10];
3、数组的初始化
在引用数组中的使用是通过:数组名 [下标] 的方式引用
(1)、静态初始化
直接在创建数组的时候就赋初值;
int[] ages = {12,13,32,42};// 推荐使用这种形式
int ages[] = {12,13,32,42};
int[] ages = new int[]{12,13,32,42};
注意:大括号中的内容必须为定义的数据类型,数组的长度会默认为大括号中的内容的长度。获取数组的长度“数组名.length”
(2)、动态初始化
int[] ages = new int[48];
ages[0] = 21;
ages[1] = 22;
动态的为数组中的每个值赋值。
4、数组的默认值
如果不给数组初始化,那么对应类型的数据类型数组会有相应的初始值;
/**
@author sfbaobao
测试数组的初始值
*/
public class TestArrayDefaultValue{
public static void main(String[] args){
byte[] b = new byte[12];
short[] s = new short[12];
int[] i = new int[12];
long[] l = new long[12];
char[] c = new char[12];
float[] f = new float[12];
double[] d = new double[12];
boolean[] bool = new boolean[12];
System.out.println(b[0]);// 0
System.out.println(s[0]);// 0
System.out.println(i[0]);// 0
System.out.println(l[0]);// 0
System.out.println("-" + c[0] + "-");// - - 默认为空格
System.out.println(f[0]);// 0.0
System.out.println(d[0]);// 0.0
System.out.println(bool[0]);// false
}
}
- boolean类型的默认值为false;
- 整型类型的默认为 0;
- 小数类型的默认为 0.0;
- 字符类型的默认为 ‘\u0000’空格;
5、遍历数组
(1)、使用一般for循环遍历
public class Test{
public static void main(String[] args){
int [] nums = {1,2,3,4,5,6,7,8,9};
for(int i=0; i<nums.length; i++){
System.out.println(nums[i] + " ");
}
}
}// out>>>1 2 3 4 5 6 7 8 9
通过nums.length
获取数组长度,再作为判断条件即可得到遍历数组中的每个元素。数组第一个元素下标是0即nums[0]为第一个元素。
注意:
数组下标越界异常:java.lang.ArrayIndexOutOfBoundsException
是在设计数组时容易出现的异常,需要检查程序中的数组下标是否超过了数组本身的长度或者为负数。
(2)、增强型for循环
for(数据类型 变量名 : 数组名){
功能代码;
}
int [] nums = {1,2,3,4,5,6,7,8,9};
for(int n : nums){// 使用增强型for循环
System.out.println(n + " ");
}
}// out>>>1 2 3 4 5 6 7 8 9
在不考虑使用循环下标的情况下,使用增强型for循环会更方便
(3)、数组排序(调用Arrays的sort()):
-
Arrays.sort(数组名)升序
返回void类型即没有返回值
/**
@author sfbaobao
测试Arrays.sort()数组排序方法
*/
import java.util.Arrays;
public class TestArraySort{
public static void main(String[] args){
int[] nums = {1,22,13,231,34,45,23,25,45,3322,432};
Arrays.sort(nums);// 调用数组排序方法
for(int n : nums){
System.out.print(n + " ");
}
}
}// out>>>1 13 22 23 25 34 45 45 231 432 3322
Arrays.sort(nums);
会将nums指向的内存空间存储的数据进行排序,虽然没有返回值,但是nums指向的内存空间的数据已经被排序了,所以输出nums指向的内存中存储的数据已经是拍好序的了。
二、通过数组赋值看看内存如何变化
在计算机内存中有两块存储空间,一个是栈内存,一个是堆内存;一般基本类型数据是存储在栈内存中,引用数据类型是存储在堆内存中。
假设有两个数组:
int[] a = {1,2,3,4,5};
int[] b = a;// 画图内存图
当int[] b = a
语句执行时,b的值就等于a中存储的地址,所以b指向的空间也是a执行的那块内存。
int[] a = {1,2,3,4,5};
int[] b = a;// 画图内存图
a = {1,2,3,4,5};
b = {1,2,3,4,5};
当执行’a = {1,2,3,4,5};’这条语句时,a首先会断开与堆内存1的联系,然后存储指向堆内存2的地址。此时堆内存1还有b指向它,所以堆内存1不会被Java垃圾回收机制回收。当执行完’b = {1,2,3,4,5};’时,b也断开了与堆内存1的联系,那这时堆内存1将变成垃圾,等待Java垃圾回收机制回收。
三、思考题
1、String类型数据存储在什么位置?
String s1 = new String("myString");// 第一种定义方式
String s2 = "myString";// 第二种定义方式
String s3 = "myString";
第一种方式通过关键字new定义过程:在程序编译期,编译程序先去字符串常量池检查,是否存在“myString”,如果不存在,则在常量池中开辟一个内存空间存放“myString”;如果存在的话,则不用重新开辟空间,保证常量池中只有一个“myString”常量,节省内存空间。然后在内存堆中开辟一块空间存放new出来的String实例,在栈中开辟一块空间,命名为“s1”,存放的值为堆中String实例的内存地址,这个过程就是将引用s1指向new出来的String实例。
第二种方式直接定义过程:在程序编译期,编译程序先去字符串常量池检查,是否存在“myString”,如果不存在,则在常量池中开辟一个内存空间存放“myString”;如果存在的话,则不用重新开辟空间。然后在栈中开辟一块空间,命名为“s1”,存放的值为常量池中“myString”的内存地址。
常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量。常量池还具备动态性,运行期间可以将新的常量放入池中,String类的intern()方法是这一特性的典型应用。不懂吗?后面会介绍intern方法的。虚拟机为每个被装载的类型维护一个常量池,池中为该类型所用常量的一个有序集合,包括直接常量(string、integer和float常量)和对其他类型、字段和方法的符号引用(与对象引用的区别?读者可以自己去了解)。
/**
测试String类型数据的存储位置
*/
public class TestString{
public static void main(String[] args){
String s1 = new String("myString");
String s2 = "myString";
String s3 = "myString";
System.out.println(s1 == s2);// false
System.out.println(s2 == s3);// true
}
}// out>>>false; true
本段解析摘自:java内存分配和String类型的深度解析
>>“不将就·能创新·耐得住——网络人”