文章目录

  • 一、一维数组
  • 1、数组的优缺点
  • 2、初始化一维数组
  • 3、一维数组的遍历
  • 4、动态初始化数组
  • 5、方法的参数是数组
  • 6、main方法当中的String数组
  • 7、数组当中存储引用数据类型
  • 8、数组扩容
  • 二、二维数组
  • 1、二维数组遍历
  • 2、二维数组做参数
  • 三、例题
  • 1、数组模拟栈


一、一维数组

1、java语言当中的数组是一种引用数据类型。不属于基本数据类型。数组的父类是Object。

2、数组实际上是一个容器,可以同时容纳多个元素。(数组是一个数据的集合)

3、数组当中可以存储基本数据类型,也可以存储引用数据类型。

4、数组因为是引用类型,所以数组对象是在堆内存当中。

5、数组当中如果存储的是java对象的话,实际上存储的是对象的引用地址。

6、数组一旦创建,在java中规定长度不可变。

7、数组的分类:一维数组,二维数组,三维数组…

8、所有数组对象都有length属性,用来获取数组中的元素。

9、java中数组要求数组元素的类型统一。

10、数组在内存方面存储的时候,数组中的元素内存地址是连续的。(将数组当中首元素的内存地址作为整个元素的内存地址)

java 一个数组对象存放在栈上还是堆中 数组对象在java中存储在()中_算法

1、数组的优缺点

数组的优缺点:

  • 优点:
  • 1、每个元素的内存地址在空间存储上是连续的。
  • 2、每一个元素类型相同,所以占用空间大小是一样的。
  • 3、知道第一个元素的内存地址,就可以计算出某个下标上元素的内存地址。
  • 4、数组的检索效率最高。
  • 缺点:
  • 1、由于为了保证数组中每个元素的内存地址连续,所以在数组上随机删除或者增加元素的时候,效率较低。
  • 2、数组无法存储大数据量,因为很难在内存空间找到一块特别大的连续的空间。

java 一个数组对象存放在栈上还是堆中 数组对象在java中存储在()中_System_02

2、初始化一维数组

1、声明一维数组:

int[] array1;
    double[] array2;
    boolean[] array3;
    String[] array4;

2、初始化一维数组:

  • 静态初始化
//静态初始化一维数组
    int[] arr1={10,11,12};
  • 动态初始化
//动态初始化一维数组
    int[] arr2=new int[5];//这里的5表示数组的元素个数
        //静态初始化一维数组
    String[] arr3=new String[5];

如果是C++的话,还可以把中括号放到数组名字的后面,在java当中同样可以运行。

int arr1[]={10,11,12};

使用方法length:

int[] arr1={10,11,12};
        System.out.println("数组元素的个数是:"+arr1.length);
        System.out.println("数组当中的第一个元素:"+arr1[0]);
        System.out.println("数组当中的最后一个元素:"+arr1[arr1.length-1]);

java 一个数组对象存放在栈上还是堆中 数组对象在java中存储在()中_数组_03

3、一维数组的遍历

for(int i=0;i< arr1.length;i++){
            System.out.println(arr1[i]);
        }

如果下标越界的话,会出现如下异常:

即数组下标越界异常。

java 一个数组对象存放在栈上还是堆中 数组对象在java中存储在()中_数据结构_04

4、动态初始化数组

//声明一个数组,采用动态初始化的方式创建
        int[] a=new int[4];//创建长度为4的int数组,数组当中每个元素默认值是0
        for(int i=0;i<a.length;i++){
            System.out.println(a[i]);
        }
//初始化一个Object类型的数组,采用动态初始化的方式
        Object[] o=new Object[5];
        for(int i=0;i<o.length-1;i++){
            System.out.println(o[i]);
        }

Object的元素默认是null类型的。

java 一个数组对象存放在栈上还是堆中 数组对象在java中存储在()中_算法_05

//存储Object,采用静态初始化
        Object o1=new Object();
        Object o2=new Object();
        Object o3=new Object();
        Object[] o4={o1,o2,o3};
        for(int i=0;i<o4.length;i++){
            System.out.println(o4[i]);
        }

java 一个数组对象存放在栈上还是堆中 数组对象在java中存储在()中_java_06

什么时候使用静态初始化,什么时候使用动态初始化?

  • 当确定具体有哪些值的时候,采用静态初始化
  • 当不确定有哪些值的时候,采用动态初始化

5、方法的参数是数组

public class Test02 {
    public static void main(String[] args) {
        int[] x = {1, 2, 3, 4};
        printArray(x);
        String[] s=new String[4];
        printArray(s);
    }

    public static void printArray(Object[] array) {
        for (int i = 0; i < array.length; i++) {
            System.out.println(array[i]);
        }
    }
    public static void printArray(int[] array) {
        for (int i = 0; i < array.length; i++) {
            System.out.println(array[i]);
        }
    }
}

如果直接传递一个静态数组的话,语法应该这样写:

//新的写法:
        printArray(new int[]{1,2,3,4});

6、main方法当中的String数组

public class Test03 {
    public static void main(String[] args) {
        
    }
}
  • JVM负责调用main方法
  • JVM调用main方法的时候,会自动传一个String数组过来

有下面的代码测试得知:JVM传递过来的数组参数String[] args,其长度是0。

public class Test03 {
    public static void main(String[] args) {
        //JVM默认传递过来的这个数组对象的长度是:0
        //通过测试得出:args不是null
        System.out.println("JVM传递过来的数组参数,数组的长度是:"+args.length);
        String[] str=new String[0];
        String[] str1={};//静态初始化数组,里面没有东西
    }
}
  • 这个数组是留给用户的,用户可以在控制台上面输入参数,这个参数会被自动转换成为String[] args
String[] str=new String[0];
        String[] str1={};//静态初始化数组,里面没有东西

关于main当中数组的案例:

  • 模拟一个系统,假设这个系统要使用,必须输入用户名和密码:
public class Test03 {
    //用户名和密码输入到String[] args当中
    public static void main(String[] args) {
        if(args.length !=2){
            System.out.println("请使用该系统输入程序参数,参数中包括用户名和密码信息:");
            return;
        }
        //程序执行到此处,说明用户确实提供了用户名和密码
        //接下来应该判断用户名和密码是否正确
        //取出用户名
        String username=args[0];
        String password=args[1];
        //假设用户名是admin,密码是123的时候表示登录成功,其他的一律失败
        //判断两个字符串是否相等,需要使用equals方法
        if(username.equals("admin") && (password.equals("123"))){
            System.out.println("登录成功,欢迎【"+username+"】回来");
            System.out.println("您可以继续使用该系统!");
        }else{
            System.out.println("验证失败!请重新尝试!");
        }
    }
}
  • args会自动将字符串当中的空格作为切分,切分成一个个的字符串。

java 一个数组对象存放在栈上还是堆中 数组对象在java中存储在()中_System_07


在Edit Configuration里面修改参数之后,就能够返回正确的登录消息了:

java 一个数组对象存放在栈上还是堆中 数组对象在java中存储在()中_数组_08

  • 改良:上面的代码当中判断用户名和密码的时候容易出现空指针异常,因此改成如下代码:

java 一个数组对象存放在栈上还是堆中 数组对象在java中存储在()中_System_09

7、数组当中存储引用数据类型

public class Test04 {
    public static void main(String[] args) {
        Animal a1 =new Animal();
        Animal a2 =new Animal();
        Animal[] animals={a1,a2};

        //对Animal数组进行遍历
        for(int i=0;i< animals.length;i++){
            /*Animal a=animals[i];
            a.move();*/
            //将上面的两行代码进行合并
            animals[i].move();
            //这个move方法是Animal对象当中的move()方法
        }

        //动态初始化一个长度为2的Animal类型数组
        Animal[] aa=new Animal[4];
        //创建一个Animal对象,放到数组的第一个盒子当中
        aa[0]=new Animal();
        //Animal数组中只能存放Animal类型,不能存放Product类型
        //但由于Cat类是Animal的子类,因此可以存放Cat类型
        aa[1]=new Cat();
        Animal[] bb={new Cat(),new Bird(),new Animal()};
        //该数组当中存储了两个对象的内存地址
        //如果调用的方法是父类中存在的方法,则不需要向下转型,直接使用父类型引用即可
        for(int i=0;i<bb.length;i++){
            Animal an=bb[i];
            an.move();

            //如果调用子对象特有的方法,则需要向下转型
            if(bb[i] instanceof Cat){
                Cat cat=(Cat) bb[i];
                cat.catchMouse();
            }

            if(bb[i] instanceof Bird){
                Bird bird=(Bird) bb[i];
                bird.sing();
            }


        }
    }
}
class Animal{
    public void move(){
        System.out.println("Aniaml move....");
    }
}

class Cat extends Animal{
    public void move(){
        System.out.println("走猫步!");
    }
    public void catchMouse(){
        System.out.println("抓老鼠!");
    }
}

8、数组扩容

  • 关于一维数组的扩容:
  • java中对数组的扩容:先新建一个大容量的数组,然后将小容量数组中的数据一个一个拷贝到大数组当中。
  • 数组扩容的效率较低,因为涉及到拷贝的问题。所以在开发当中要尽可能少的进行数组的拷贝。

java 一个数组对象存放在栈上还是堆中 数组对象在java中存储在()中_java_10

public class Test05 {
    public static void main(String[] args) {
        //拷贝源(从这个数组中拷贝)
        int[] src={1,11,22,34};
        //拷贝目标(拷贝到这个目标数组上)
        int[] dest=new int[20];//动态初始化一个长度为20的数组,每一个元素的默认值是0
        //调用JDK当中System类中的arraycopy方法,来完成数组的拷贝
        System.arraycopy(src,1,dest,3,2);
        //遍历目标数组
        for(int i=0;i<dest.length;i++){
            System.out.print(dest[i]);
        }
    }
}

引用数据类型的拷贝:

String[] str={"hello","world","study","java"};
        String[] newstr=new String[20];
        System.arraycopy(str,0,newstr,0,4);
        for(int i=0;i<newstr.length;i++){
            System.out.print(newstr[i]+" ");
        }

这里拷贝的是对象的地址,而不是对象:

Object[] o={new Object(),new Object(),new Object()};
        Object[] newObjs=new Object[10];
        //这里拷贝的是对象的地址,而不是对象
        System.arraycopy(o,0,newObjs,0,o.length);

二、二维数组

  • 二维数组其实是一个特殊的一维数组,特殊在这个一维数组当中的每一个元素是一个一维数组。
public class Test06 {
    public static void main(String[] args) {
        //一维数组
        int[] array={100,200,300};
        //二维数组
        int[][] a={
                {100,200,300},{300,200,100},{0}
        };
    }
}

1、二维数组遍历

public class Test06 {
    public static void main(String[] args) {
        //二维数组
        int[][] a={
                {100,200,300},
                {300,200,100},
                {0},
                {11,0,99,1}
        };

        //二维数组遍历
        String[][] array={
                {"java","oracle","python","c#"},
                {"张三","李四","王五"},
                {"lucky","rose"}
        };
        for(int i=0;i<array.length;i++){
            String[] b=array[i];
            for(int j=0;j<b.length;j++){
                System.out.print(b[j]+" ");
            }
            System.out.println();
        }
    }
}

java 一个数组对象存放在栈上还是堆中 数组对象在java中存储在()中_数组_11

2、二维数组做参数

public class Test07 {
    public static void main(String[] args) {
        int[][] array=new int[3][4];
        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();
        }
        int[][] a={
                {1,2,3,4},
                {9,8,0,7,},
                {5,8,1}
        };
        printArray(a);
        printArray(new int[][]{{1,2,3},{54,0,9}});
    }

    public static void printArray(int[][] array) {
        for (int i = 0; i < array.length; i++) {
            for(int j=0;j<array[i].length;j++){
            System.out.println(array[i][j]+" ");
            }
        }
    }
}

三、例题

1、数组模拟栈

  • 编写程序,使用一维数组,模拟栈的数据结构
  • 1、这个栈可以存储java中的任何引用类型的数据
  • 2、在栈中提供push方法模拟压栈(栈满了,要有提示信息)
  • 3、在栈中提供pop方法模拟弹栈(栈空了,要有提示信息)
  • 4、编写测试程序,new栈对象,调用push pop方法来模拟压栈弹栈的动作
  • 5、假设栈的默认初始化容量是10
public class Stack {//栈类
    //提供一个数组来存储栈的元素
    private Object[] elements;//使用Object类型,因为可以存储任何类型的数据
    //由于属性私有化了,因此封装并且提供set和get方法
    public Object[] getElements() {
        return elements;
    }

    public void setElements(Object[] elements) {
        elements = elements;
    }
    //栈帧:永远指向栈顶部的元素
    //每加一个元素,栈帧+1
    //每减一个元素,栈帧-1
    private int index=-1;//此时栈帧指向了空栈的顶部元素
    //构造方法
    public Stack(){
        this.elements=new Object[10];//默认初始化容量是10
    }
    //push方法(push压栈)

    /**
     * 压栈的方法
     * @param obj 被压栈的元素
     */
    public void push(Object obj){
        if(this.index>=this.elements.length-1){
            System.out.println("栈已经满了!压栈失败!");
            return;
        }
        //程序到达这里,说明栈没有满
        elements[++index]=obj;
        //所有的输出方法在执行的时候,如果输出的是引用的话,自动调用的是toString方法
        System.out.println("压栈成功,栈帧指向:"+index);
    }
    //压栈表示栈中多一个元素
    //如果栈满了,则压栈失败
    //pop方法(弹栈)

    /**
     * 弹栈的方法,从数组当中往外取元素,每次取出一个元素,栈帧向下移动一位
     * @param obj
     * @return
     */
    public Object pop(){
        if(this.index<=-1){
            System.out.println("栈已经空了!弹栈失败!");
            return null;
        }
        //程序到达这里,说明栈没有空

        System.out.println("弹栈"+elements[index]+"成功,"+"栈帧指向:"+index);
        index--;
        return elements[index+1];
    }
    //栈中少一个元素
    //栈如果空,则弹栈失败
    //

    public static void main(String[] args) {

    }
}

测试类:

public class MyStackTest {
    public static void main(String[] args) {
        //创建一个栈对象,初始化容量是10个元素
        Stack s=new Stack();
        //调用方法压栈
        s.push(10);
        s.push(new Object());
        s.push(10);
        s.push(new Object());
        s.push(10);
        s.push(new Object());
        s.push(10);
        s.push(new Object());
        s.push(10);
        s.push(new Object());
        s.push(new Object());
        s.pop();
        s.pop();
        s.pop();
        s.pop();
        s.pop();
        s.pop();
        s.pop();
        s.pop();
        s.pop();
        s.pop();
        s.pop();
        s.pop();
    }
}