1. 数组
数组创建及初始化
总结:
三种写法包括了动态初始化和静态初始化,其中省略格式不能再省略(拆分);没有初始化时(默认值为基类类型对应的默认值)其中引用类型的默认值为null
三种写法
1. 动态初始化:在创建数组时,直接指定数组中元素的个数
int[] array = new int[10];
2. 静态初始化:在创建数组时不直接指定数据元素个数,而直接将具体的数据内容
进行指定。静态初始化时, {}中数据类型必须与[]前数据类型一致。
double[] array2 = new double[]{1.0, 2.0, 3.0, 4.0, 5.0};
3.静态初始化可以简写,省去后面的new T[]
String[] array3 = {"hell", "Java", "!!!"};
静态和动态初始化也可以分为两步,但是省略格式不可以
int[] array1;
array1 = new int[10];
int[] array2;
array2 = new int[]{10, 20, 30};
注意省略格式不可以拆分, 否则编译失败
int[] array3;
array3 = {1, 2, 3};
数组遍历
在数组中可以通过 数组对象.length 来获取数组的长度
1.for循环方式
int[]array = new int[]{10, 20, 30, 40, 50};
for(int i = 0; i < array.length; i++){
System.out.println(array[i]);
}
2.使用 for-each 遍历数组
int[] array = {1, 2, 3};
for (int x : array) {//这里的x可以只是个标识,和下面对应即可
System.out.println(x);
}
作用:for-each 是 for 循环的另外一种使用方式.
能够更方便的完成对数组的遍历. 可以避免循环条件和更新语句写错
3.借助Java的操作数组的工具类 Arrays
import java.util.Arrays;//工具类 Arrays
public class UseOfArrays {
public static void main(String[] args) {
int[] array = {1,3,2,5,4};
Array.sort(array);//工具类 Arrays 排序
String str = Arrays.toString(array);
// 创建一个字符串变量来接收它的返回值
System.out.println(str);
// 因为 Arrays.toString 是有返回值(必须有),所以可以直接输出
System.out.println(Arrays.toString(array));
}
}
数组空间(重点)
局部变量在栈上,new的对象在堆上
**引用很像指针,最大的区别就是不能对 引用 解引用,可以对指针解引用,**因为在Java没有传址的概念的,其他功能类似,但时两个东西,不是同一个东西,不能混淆在一起。
有个问题就诞生了,指针有空指针,那引用有空引用(引用里面存的地址是空的)吗?
答案是有的,只不过跟C语言不同,C是大写NULL,Java是小写null
但是请注意,举这个例子只是为了让你理解引用是一个什么东西,顺便区别两者。
C的东西只是辅助你理解Java,尽量学Java的时候,抛开C,两者(引用和指针)有很多不同地方。
public class UseOfArrays {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
int[] array1 = null;
//当一个引用被赋予null,就代表这该引用不指向任何对象
// 既然该引用不指向任何对象,那么我这样写代码会怎么样?
System.out.println(array1.length);//error
// 求一个空引用所指向的对象的长度,但是空引用不会指向任何一个对象的
System.out.println(array1[0]);
// 这个写法毫无疑问也是错的,没对象,你怎么访问对象里的东西?
}
}
数组作为参数(重点)
案例一:
import java.util.Arrays;
public class UseOfArrays {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
func1(array);
System.out.println(Arrays.toString(array));//1,2,3,4,5
}
public static void func1(int[] array){
array = new int[]{11,2,13,4,51};
}
}
案例二:
import java.util.Arrays;
public class UseOfArrays {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
func2(array);
System.out.println(Arrays.toString(array));//99,2,3,4,5
}
public static void func2(int[] array){
array[0] = 99;
}
}
解析:案例一的结果还是array原来的值,并没有发生改变;fun1()只是创建了一个新的空间,也没有返回值。只是将array的实参地址重新赋值(地址0x99—>0x66)
案例二的结果发生了改变,借助array存储的地址,访问并修改了成员变量(array[0])
总结:通过其中任何一个引用修改这个对象的值,另一个引用去访问的时候,也是被改变的。
还有一个问题值得讨论:一个引用 是否 能同时 指向 多个对象?
答案是不能, 一个引用只能指向一个对象(一个引用只能保存一个对象的地址)
public static void main(String[] args) {
int[] array1 = new int[]{1,2,3,4,5};
array1 = new int[10];
array1 = new int[2];
array1 = new int[4];
}
此时的array1,存储的地址,已经被改变(array是一个局部变量,意味着存的值是可以被改变的),
现在存的是 new int[4] 的这个对象的地址, 而不是说,存几个对象的地址。
数组作为方法的返回值
(代码示例: 写一个方法, 将数组中的每个元素都 * 2)
先写没有返回值的
import java.util.Arrays;
public class UseOfArrays {
public static void main(String[] args) {
int[] array={1,2,3,4,5};
func(array);
System.out.println(Arrays.toString(array));
}
public static void func(int[] array){
for (int i = 0; i < array.length; i++) {
array[i]=2*array[i];
}
}
}
写有返回值的
不破坏原有数组:另外由于数组是引用类型, 返回的时候只是将这个数组的首地址返回给函数调用者, 没有拷贝数组内容, 从而比较高效.有的人可能会说,方法调用完的时候,不是会销毁栈上的空间,我们这样将地址带回来,不会存在一些问题吗?请注意 new 的对象,都是存在堆上的,而不是栈上。这里的返回的是将堆上对象的地址(注意返回类型int[]),而不是局部变量ret,所以不会出现问题。
import java.util.Arrays;
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array={1,2,3,4,5};
int[] ret = func(array);//建立数组类型(对象)变量来接收
System.out.println(Arrays.toString(ret));
System.out.println(Arrays.toString(array));
}
public static int[] func(int[] array){//新建对象(数组)
int[] ret = new int[array.length];
for (int i = 0; i < array.length; i++) {
ret[i]=2*array[i];
}
return ret;//这里返回是 新建对象(数组),在堆上的地址
}
}
深拷贝和浅拷贝
有面试官问你某个程序实现了某种拷贝?
回答:想怎么拷贝?拷贝值就是深拷贝;拷贝地址就是浅拷贝。
数组拷贝
方法1 循环拷贝
方法2: 利用Java提供的函数: Arrays.copyOf()
方法3:Arrays.copyOfRange()左闭右开区间
方法4: System.arraycopy(array,0,array2,0,array.length)
方法5:克隆:数组名.clone();
方法1 循环拷贝
import java.util.Arrays;
public class UseOfArrays {
public static void main(String[] args) {
int[] array={1,2,3,4,5};
int[] array2 = copyshuzu(array);// 是方法中 引用ret存储值的一份拷贝,能够访问 ret 指向的堆上的对象
System.out.println(Arrays.toString(array));
System.out.println(Arrays.toString(array2));
}
public static int[] copyshuzu(int[] array){
int[] ret = new int[array.length];//创一个相同类型,元素个数相同的 数组变量array2来接收 array 的元素
for (int i = 0; i < array.length; i++) {
ret[i] = array[i];//通过 对应的下标进行复制拷贝
}
return ret;// 将对象的地址转过去
}
}
方法2: 利用Java提供的函数: Arrays.copyOf()
import java.util.Arrays;
public class UseOfArrays {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
int[] array2 = Arrays.copyOf(array,array.length);
System.out.println(Arrays.toString(array2));//
// 既然能规定 拷贝数量,我输入拷贝数量超过,源数组array的元素个数呢?
int[] array3 = Arrays.copyOf(array,array.length*2);
System.out.println(Arrays.toString(array3));
}
//但是注意一点,本质是copy明白嘛? 这扩容出来的数组,跟元素组,不是同一个数组
//如果我们想"扩容",输入的倍数必须整数,因为它的长度类型规定为整形
}
方法3:Arrays.copyOfRange()左闭右开区间
import java.util.Arrays;
public class UseOfArrays {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
int[] ret = Arrays.copyOfRange(array,0,4);
System.out.println(Arrays.toString(ret));//1,2,3,4
}
}
方法4:对法二的改进
import java.util.Arrays;
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
int[] array2 = new int[array.length];
System.arraycopy(array,0,array2,0,array.length);
// 跟用for循环进行拷贝时一个道理,根据对应的下标进行赋值拷贝
System.out.println(Arrays.toString(array2));
}
}
方法5:克隆
import java.util.Arrays;
public class UseOfArrays {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
int[] array2 = array.clone();// 类似 副本: 可以理解为是一份资料的备份
System.out.println(Arrays.toString(array2));
}
}
2.二维数组
创建
- 数据类型[][] 数组名 = { 初始化数据 }
- 数据类型[][] 数组名 = new 数据类型[][]{ 初始化数据 }
- 数据类型[][] 数组名 = new 数据类型[行][列数]
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
// 创建一个 2行3列的二维数组
int[][] array = {{1,2,3},{4,5,6}};
int[][] array2 = new int[][]{{1,2,3},{4,5,6}};
int[][] array3 = new int[2][3];
}
}
遍历
方法1: array[下标].length,就能得到相对应的一维数组(对象)的元素个数(长度)。
public class UseOfArrays {
public static void main(String[] args) {
int[][] array = {{1,2,3},{4,5,6}};
print(array);
// 这里我们在用实例证明一下
// array.length 是否能的得到 行数 2
System.out.println(array.length);
// array[下标].length 是否能得到 列数3
System.out.println(array[0].length);
System.out.println(array[1].length);
}
public static void print(int[][] array){
// 按照我们以前对C的理解,二维数组的存储模式应该为
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
//打印一行数据
System.out.print(array[i][j] + " ");
}
System.out.println()
}
}
}
方法2:二维数组也有对应的 方法: Arrays.deepToString(数组名)
import java.util.Arrays;
public class TheDefinitionAndUseOfArrays {
public static void main(String[] args) {
int[][] array = {{1,2,3},{4,5,6}};
System.out.println(Arrays.deepToString(array));
}
}
特别的二维数组
int[][] array = {{1,2},{1,2,3}};
意义在于我们可以改,去赋予这种二维数组,我们将其称为 不规则的二维数组
在C语言中,二维数组,是不能省略列的,行可以省略
而Java与之相反,行不能省略,列可以
int[][] array = new int[2][];
array[0]=new int[3];// 意味着第一个数组长度为3
array[1]=new int[2];// 意味着第二个数组长度为2