Java数组学习
数组概述
Java的数组要求所有的数组元素具有相同的数据类型。因此,在一个数组中,数组元素的类型是唯一的,即一个数组里只能存储一种数据类型的数据,而不能存储多种数据类型的数据。
PS:因为Java语言是面向对象的语言,而类与类之间可以支持继承关系,这样可能产生一个数组里可以存放多种数据类型的假象。例如有一个水果数组,要求每个数组元素都是水果,实际上数组元素既可以是苹果,也可以是香蕉,但这个数组的数组元素的类型还是唯一的,只能是水果类型。
数组也是一种数据类型,它本身是一种引用类型。例如int是一个基本类型,但int[](这是定义数组的一种方式)就是一种引用类型了。
数组定义
type [] arrayName;
type arraryName[];
数组是一种引用类型的变量,因此使用它定义一个变量时,仅仅表示定义了一个引用变量(也就是定义了一个指针),这个引用变量还未指向任何有效的内存,因此定义数组时不能指定数组的长度。而且由于定义数组只是定义了一个引用变量,并未指向任何有效的内存空间,所以还没有内存空间来存储数组元素,因此这个数组也不能使用,只有对数组进行初始化后才可以使用。
数组初始化
初始化,就是为数组的数组元素分配内存空间,并为每个数组元素赋初始值。
数组的初始化有如下两种方式。
静态初始化:初始化时由程序员显式指定每个数组元素的初始值,由系统决定数组长度。
arratName =new type[] {element1,element2...}
简化写法:arrayName={element1,element2...};
int [] array =new int [] {5,6,8,0}
int [] array={5,6,7,8}
动态初始化:初始化时程序员只指定数组长度,由系统为数组元素分配初始值。
arrayName =new type[length];
int [] prices =new int[5];
系统按如下规则分配初始值。
数组元素的类型是基本类型中的整数类型(byte、short、int和long),则数组元素的值是0。
数组元素的类型是基本类型中的浮点类型(float、double),则数组元素的值是0.0.
数组元素的类型是基本类型中的字符类型(char),则数组元素的值是'u0000'。
数组元素的类型是基本类型中的布尔类型(boolean),则数组元素的值是false。
数组元素的类型是引用类型(类、接口和数组),则数组元素的值是null。
数组使用
System.out.println(arr[1]);
arr[1]="aaaa";
如果访问数组元素时指定的索引值小于0,或者大于等于数组的长度,编译程序不会出现任何错误,但运行时出现异常:java.lang.ArrayIndexOutOfBoundsException:N(数组索引越界异常),异常信息后的N就是程序员试图访问的数组索引。
ForEach循环
for( type variableName : attay | collention)
{//variableName 自动迭代访问每个元素
}
eg:
for(String book :books ){
book="aaa";
System.out.println(book);
}
内存中的数组
数组引用变量只是一个引用,这个引用变量可以指向任何有效的内存,只有当该引用指向有效内存后,才可通过该数组变量来访问数组元素。
与所有引用变量相同的是,引用变量是访问真实对象的根本方式。也就是说,如果我们希望在程序中访问数组对象本身,则只能通过这个数组的引用变量来访问它
实际的数组对象被存储在堆(heap)内存中;如果引用该数组对象的数组引用变量是一个局部变量,那么它被存储在栈(stack)内存中。数组在内存中的存储示意图如下图所示。PS(图片来源于传播智客)
栈内存与堆内存区别:
当一个方法执行时,每个方法都会建立自己的内存栈,在这个方法内定义的变量将会逐个放入这块栈内存里,随着方法的执行结束,这个方法的内存栈也将自然销毁。因此,所有在方法中定义的局部变量都是放在栈内存中的;当我们在程序中创建一个对象时,这个对象将被保存到运行时数据区中,以便反复利用(因为对象的创建成本通常较大),这个运行时数据区就是堆内存。堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用(在方法的参数传递时很常见),则这个对象依然不会被销毁。只有当一个对象没有任何引用变量引用它时,系统的垃圾回收器才会在合适的时候回收它。
当我们看一个数组时,一定要把数组看成两个部分:一部分是数组引用,也就是在代码中定义的数组引用变量;还有一部分是实际的数组对象,这部分是在堆内存里运行的,通常无法直接访问它,只能通过数组引用变量来访问。
基本类型数组的初始化
int [] arr;
arr=new int [5];
for(int i=0;i<arr.length;i++){
arr[i]=i+100;
}
执行第一行内存如下
执行第二行
执行for循环
引用类型数组的初始化
class person{
public int age;
public double height;
public void info(){
sout;
}
}
//main中
Person []stu;
stu=new Person[2];
person zhang=new Person();
zhang.age=18;
zhang.height=120;
person li=new Person();
li.age=14;
li.height=130;
stu[0]=zhang;
stu[1]=li;
li.info();
stu[1].info();
执行Person[]stu;代码时,这行代码仅仅在栈内存中定义了一个引用变量,也就是一个指针,这个指针并未指向任何有效的内存区.
初始化后内存变化
声明两个Person变量,zhang和li,此时在栈内存中分配两块内存用于存储变量zhang和li,在堆内存中分配两块内存用于存储zhang和li的数据,如下图
给stu数组赋值后堆内存变化如下:
多维数组
二维数组定义
type [][] arrName;
Java语言采用上面的语法格式来定义二维数组,但它的实质还是一维数组,只是其数组元素也是引用,数组元素里保存的引用指向一维数组。
接着对这个“二维数组”执行初始化,同样可以把这个数组当成一维数组来初始化,把这个“二维数组”当成一个一维数组,其元素的类型是type[]类型,则可以采用如下语法进行初始化:
arrName=new type [length][]
工具类
Java提供的Arrays类里包含的一些static修饰的方法可以直接操作数组,这个Arrays类里包含了如下几个static修饰的方法(static修饰的方法可以直接通过类名调用)。
int binarySearch(type[]a,type key):使用二分法查询key元素值在a数组中出现的索引;如果a数组不包含key元素值,则返回负数。调用该方法时要求数组中元素已经按升序排列,这样才能得到正确结果。
int binarySearch(type[]a,int fromIndex,int toIndex,type key):这个方法与前一个方法类似,但它只搜索a数组中fromIndex到toIndex索引的元素。
type[]copyOf(type[]original,int newLength):这个方法将会把original数组复制成一个新数组,其中length是新数组的长度。如果length小于original数组的长度,则新数组就是原数组的前面length个元素;如果length大于original数组的长度,则新数组的前面元素就是原数组的所有元素,后面补充0(数值类型)、false(布尔类型)或者null(引用类型)。
type[]copyOfRange(type[]original,int from,int to):这个方法与前面方法相似,但这个方法只复制original数组的from索引到to索引的元素。
boolean equals(type[]a,type[]a2):如果a数组和a2数组的长度相等,而且a数组和a2数组的数组元素也一 一相同,该方法将返回true。
void fill(type[]a,type val):该方法将会把a数组的所有元素都赋值为val。
void fill(type[]a,int fromIndex,int toIndex,type val):该方法与前一个方法的作用相同,区别只是该方法仅仅将a数组的fromIndex到toIndex索引的数组元素赋值为val。
void sort(type[]a):该方法对a数组的数组元素进行排序。
void sort(type[]a,int fromIndex,int toIndex):该方法与前一个方法相似,区别是该方法仅仅对fromIndex到toIndex索引的元素进行排序。
String toString(type[] a):该方法将一个数组转换成一个字符串。该方法按顺序把多个数组元素连缀在一起,多个数组元素使用英文逗号(,)和空格隔开。