文章目录
- 一、一维数组
- 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、数组在内存方面存储的时候,数组中的元素内存地址是连续的。(将数组当中首元素的内存地址作为整个元素的内存地址)
1、数组的优缺点
数组的优缺点:
- 优点:
- 1、每个元素的内存地址在空间存储上是连续的。
- 2、每一个元素类型相同,所以占用空间大小是一样的。
- 3、知道第一个元素的内存地址,就可以计算出某个下标上元素的内存地址。
- 4、数组的检索效率最高。
- 缺点:
- 1、由于为了保证数组中每个元素的内存地址连续,所以在数组上随机删除或者增加元素的时候,效率较低。
- 2、数组无法存储大数据量,因为很难在内存空间找到一块特别大的连续的空间。
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]);
3、一维数组的遍历
for(int i=0;i< arr1.length;i++){
System.out.println(arr1[i]);
}
如果下标越界的话,会出现如下异常:
即数组下标越界异常。
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类型的。
//存储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]);
}
什么时候使用静态初始化,什么时候使用动态初始化?
- 当确定具体有哪些值的时候,采用静态初始化
- 当不确定有哪些值的时候,采用动态初始化
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会自动将字符串当中的空格作为切分,切分成一个个的字符串。
在Edit Configuration里面修改参数之后,就能够返回正确的登录消息了:
- 改良:上面的代码当中判断用户名和密码的时候容易出现空指针异常,因此改成如下代码:
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中对数组的扩容:先新建一个大容量的数组,然后将小容量数组中的数据一个一个拷贝到大数组当中。
- 数组扩容的效率较低,因为涉及到拷贝的问题。所以在开发当中要尽可能少的进行数组的拷贝。
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();
}
}
}
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();
}
}