从零开始学习Java——基础知识之数组(第五天)

一、数组的定义

数组(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;// 画图内存图

image_1b6mfdfus12fh10ur1c0clus67n9.png-16.7kB
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};

image_1b6mfq7hf1n0ueme1l8i1k2m12g0m.png-19.9kB
当执行’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类型的深度解析
>>“不将就·能创新·耐得住——网络人”

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值