一、理论概述:
Array
1、Java语言中的数组是一种引用数据类型。不属于基本数据类型。数组的父类是object。
2、数组实际上是一个容器,可以同时容纳多个元素。(数组是一个数据的集合)
数组:字面意思是“一组数据”
3、数组当中可以存储“基本数据类型”的数据,也可以存储“引用数据类型”的数据。
4、数组因为是引用类型,所以数组对象是堆内存当中。(数组是存储在堆当中的)
5、数组当中如果存储的是“java对象”的话,实际上存储的是对象的“引用(内存地址)”,数组中不能直接存储java对象
6、数组一旦创建,在java中规定,长度不可变。(数组长度不可变)
7、数组的分类:一维数组、二维数组、三维数组、多维数组... ( -维数组较多,二维数组偶尔使用! )
8、所有的数组对象都有length属性(java自带的),用来获取数组中元素的个数。
9、java中的数组要求数组中元素的类型统一。比如int类型数组只能存储int类型, Person类型数组只能存储Person类型。
例如:超市购物,购物袋中只能装苹果,不能同时装苹果和橘子。( 数组中存储的元素类型统)
10、数组在内存方面存储的时候,数组中的元素内存地址(存储的每一个元素都是有规则的挨着排列的)是连续的。内存地址连续
这是数组存储元素的特点(特色)。数组实际上是一种简单的数据结构。
11、所有的数组都是拿“第一个小方框的内存地出”作为整个数组对象的内存地址。(数组中首元素的内存地址作为整个数组对象)
12、数组中每一个元素都是有下标的,下标从0开始,以1递增。最后一个元素的下标是: length一1
下标非常重要,因为我们对数组中元素进行“存取”的时候,都需要通过下标来进行。
13.数组这种数据结构的优点和缺点是什么?
优点:查询/查找/检索某个下标上的元素时效率极高。可以说是查询效率最高的一个数据结构。
为什么检索效率高?
第一:每一个元素的内存地址在空间存储上是连续的。
第二:每一个元素类型相同,所以占用空间大小一样。
第三:知道第一个元素内存地址,知道每一个元素占用空间的大小,又知道下标,所以
通过一个数学表达式就可以计算出某个下标上元素的内存地址。直接通过内存地址定位
元素,所以数组的检索效率是最高的。
数组中存储100个元素,或者存储100万个元素,在元素查询/检索方面,效率是相同的,
因为数组中元素查找的时候不会一个一个找,是通过数学表达式计算出来的。
内存地址,直接定位的。)
缺点:
第一:由于为了保证数组中每个元素的内存地址连续,所以在数组上随机删除或者增加元素的时候,
效率较低,因为随机增删元素会涉及到后面元素统一向前或者向后位移的操作。
第二:数组不能存储大数据量,为什么?
因为很难在内存空间上找到一块特别大的连续的内存空间。注意:对于数组中最后一个元素的增删,是没有效率影响的。
二、怎么初始化一维数组
14、怎么初始化一个一维数组呢?
包括两种方式:静态初始化一维数组,动态初始化一维数组
静态初始化语法格式:
int[] array ={100,60,90,50};动态初始化语法格式:
int[] array =new int[5]; // 这里5表示数组的元素个数
// 初始化一个5个长度的int类型的数组,每个元素默认值为:0
String[] array =new String[6]; //初始化6个长度的String类型的数组,每个元素默认值为:null
【静态一维数组】
代码演示:
public class ArrayTest01 {
public static void main(String[] args) {
// 声明一个int类型的数组,使用静态初始化方式
int[] a ={50,6,10,90,96};
// 所有的数组对象都有length属性(数组元素个数)
System.out.println("数组元素的个数:"+a.length);
//数组中每一个元素都有下标
// 通过下标对数组中的元素进行存和取
// 取(读)
System.out.println("第一个元素:"+a[0]);
System.out.println("最后一个元素:"+a[a.length-1]);
// 存(改)
// 把第一个元素改成8
a[0] =8;
System.out.println(a[0]);
// 一组数组怎么遍历呢?
for (int i=0;i<a.length;i++){ // i是从0~4 而a.length为5
System.out.println(a[i]); // i是从0~4,是下标
}
for (int i=a.length-1;i>=0;i--){
System.out.println("颠倒顺序输出:"+a[i]);
}
}
}
输出结果:
三、动态初始化一维数组
思考什么时候采用静态初始化方式,什么时候采用动态初始化方式?
当你创建数组的时候,确定数组中存储哪些具体的元素时,采用静态初始化方式。
当你创建数组的时候,不确定将来数组中存储哪些数据,你可以采用动态初始化的方式,预先分配内存空间。
代码演示:
public class ArrayTest01 {
public static void main(String[] args) {
// 声明一个数组,采用动态初始化方式创建
int[] a =new int[4]; // 创建元素长度为4的int数组,数组中每个元素的默认值为0
// 遍历数组
for (int i=0;i<a.length;i++){
System.out.println("元素下标为"+i+"的元素是:"+a[i]);
}
// 当我们不确定将数组里面写什么数据的时候可以先把数组写成动态的 然后后期添加修改数据
// 后期修改数据
a[0]=9;
System.out.println("动态初始化方式后期修改的数据:"+a[0]);
// 初始化一个Object类型的数组,采用动态初始化方式
Object[] objs =new Object[4]; // 4个长度,动态初始化,所以每个元素默认值为null
for (int i=0;i<objs.length;i++){
System.out.println(objs[i]);
}
System.out.println("=================================");
// 初始化一个String类型
String[] str =new String[4]; // 4个长度,每个元素的默认值也为null
for (int i=0;i<str.length;i++){
System.out.println(str[i]);
}
// 采用静态初始化方式
String[] strings ={"abc","sdf","wer","gik"};
for (int i=0;i<strings.length;i++){
System.out.println(strings[i]);
}
// 储存Object,采用静态初始化怎么搞(对象)
// 三个对象如下: 怎么用数组静态储存呢
Object o1 =new Object();
Object o2 =new Object();
Object o3 =new Object();
Object[] objects ={o1,o2,o3};
for (int i=0;i<objects.length;i++){
System.out.println(objects[i]);
// 输出的为java.lang.Object@1b6d3586格式 当println输出的时候Object自动调用Object的toString方法
}
}
}
输出结果:
内存图演示:
四、当一个方法的参数是数组时
代码演示:
package com.bjpowernode.javase.array;
public class ArrayTest02 {
public static void main(String[] args) { //String[] args 相当于数组 也可以写成String args[]
// 静态初始化方式
// 创建int数组
int[] a1 ={1,2,3};
// 调用方法时传一个数组
ArrayTest02.printArray(a1);
// 动态初始化方式
int[] a2 =new int[4]; // 创建元素长度为4的int数组 默认值为0
ArrayTest02.printArray(a2);
// 创建String数组
String[] a3 ={"asdf","qwer","zxcv","hjkl"};
ArrayTest02.print1Array(a3);
String[] a4 =new String[4]; // 动态初始化方式 默认值为null
ArrayTest02.print1Array(a4);
// 还可以写成
// ArrayTest02.print1Array(new String[4]);
}
// 创建一个数组参数方法[int类型]
public static void printArray(int[] array) {
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]); // 1 2 3
}
}
// 创建一个数组参数方法[String类型]
public static void print1Array(String[] args){
for (int i=0;i<args.length;i++){
System.out.println(args[i]); // asdf qwer zxcv hjkl
}
}
}
注意:当一个方法的参数为数组时:静态初始化方法还可以怎么写?
代码演示:
package com.bjpowernode.javase.array;
// 当一个方法的参数是一个数组的时候,还可以采用这种方式传
public class ArrayTest03 {
public static void main(String[] args) {
// 静态初始化
int[] a ={1,2,3};
// 调用数组方法
ArrayTest03.printArray(a);
// 思考:能不能直接把{1,2,3}进行传值? ArrayTest03.printArray({1,2,3});
// 经测试:不能够这样传值 那么可以怎么写呢?
// 测试结果为下: new int[] 不能省略,而且[]里面不能写任何东西
System.out.println("测试结果如下:");
ArrayTest03.printArray(new int[]{1,2,3});
System.out.println("===============================");
// 动态初始化
int[] a1 =new int[4]; // 4个长度 初始值为0
ArrayTest03.printArray(a1);
}
// 创建一个数组方法[int类型]
public static void printArray(int[] array){
for (int i=0;i<array.length;i++){
System.out.println(array[i]);
}
}
}
五、main方法的String数组
1、main方法上面的"String[] args"有什么用? // 相当于一个String数组
分析:谁负责调用main方法(JVM负责调用)
JVM调用main方法的时候,会自动传一个String数组过来
代码演示如下:
public class ArrayTest04 {
public static void main(String[] args) { // String[] args 相等于一个数组
System.out.println("JVM给传递过来的String数组参数,它这个数组的长度为:"+args.length); //输出结果:0 // 长度为0了 说明数组里面没有任何元素
// 通过下面的演示:证明了JVM机传过来的数组里面为空,没有任何元素
// JVM机的初始化方式相当于 String[] strings ={}; 里面是空的 但是有这个数组对象
// 上面的调用相当于下面的调用【演示如下】
// String[] strings ={}; // 和下面的new String[0];意义相同 证明了数组里面没有任何元素
String[] strings=new String[0]; // 相当于JVM机当中的String数组
// 0不是String类型 在这里面表示长度为0,也就是说有这个对象但是里面没有元素
ArrayTest04.printArray(strings); // 相当于JVM传的数组
}
// 创建一个数组参数方法
public static void printArray(String[] args){ // 相当于上面的main方法
System.out.println(args.length);
}
}
输出结果:【结论:证明了JVM机当中的String数组里面的元素为空,即没有任何元素,但是有这个数组对象】
思考:这个main方法当中的String[] args 数组什么时候里面会有值呢?【重点】
package com.bjpowernode.javase.array;
/*
1、main方法上面的"String[] args"有什么用?
分析:谁负责调用main方法(JVM)
JVM调用main方法的时候,会自动传一个String数组过来
*/
public class ArrayTest04 {
public static void main(String[] args) { // String[] args 相等于一个数组
System.out.println("JVM给传递过来的String数组参数,它这个数组的长度为:"+args.length); //输出结果:0
// 通过下面的演示:证明了JVM机传过来的数组里面为空,没有任何元素
// JVM机的初始化方式相当于 String[] strings ={}; 里面是空的 但是有这个数组对象
// 上面的调用相当于下面的调用【演示如下】
// String[] strings ={}; // 和下面的new String[0];意义相同 证明了数组里面没有任何元素
String[] strings=new String[0]; // 相当于JVM机当中的String数组
// 0不是String类型 在这里面表示长度为0,也就是说有这个对象但是里面没有元素
ArrayTest04.printArray(strings); // 相当于JVM传的数组
// 这个数组什么时候里面会有值呢?
// 其实这个数组是留给用户的,用户可以在控制台上输入参数,这个参数自动会被转换为"String[] args" (数组)
// 假如这样运行程序:java ArrayTest04 abc def xyz
// 那么这个时候JVM会自动将abc def xyz通过空格的方式进行分离,分离完成之后,自动放到"String[] args"数组当中
// 把abc def xyz转换成字符串数组:{"abc","def","xyz"};
// 所以main方法上面的String[] args数组主要是用来接收用户的输入参数的
// 遍历数组【接收用户输入的参数然后进行遍历输出】
for (int i=0;i<args.length;i++){
System.out.println(args[i]); // 当用户传一个abc时 这里就会输出一个abc .....
}
}
// 创建一个数组参数方法
public static void printArray(String[] args){ // 相当于上面的main方法
System.out.println(args.length);
}
}
用户输入后的输出结果如下:
六、main方法String参数的案例
package com.bjpowernode.javase.array;
// 模拟一个系统,假设这个系统要使用,必须输入用户名和密码
public class ArrayTest05 {
public static void main(String[] args) {
if (args.length != 2) {
System.out.println("使用该系统时输入程序参数,参数当中包括用户名和密码信息,例如:junker 123");
return;
}
// 执行到这里可以说明用户提供了用户名和密码
// 取出用户名
String username = args[0]; // String类型
// 取出用户名密码
String password = args[1];
// 假设用户名是admin,密码是123时表示登录成功,其他一律失败
// 判断两个字符串是否相等,需要使用equals方法
//if (username.equals("admin")&&password.equals("123"))// 这样判断的缺点:当username为null时调用equals容易空指针异常
// 为了避免空指针异常通常这样写
// "admin"为String字符串类型,String也是个类默认重写了Object的equals方法
if ("admin".equals(username) && "123".equals(password)) {
System.out.println("用户:" + username + "登录成功");
} else {
System.out.println("验证失败,用户名或者密码错误,请重新输入");
}
}
}
七、数组中储存引用数据类型
代码演示:
package com.bjpowernode.javase.array;
public class ArrayTest06 {
public static void main(String[] args) {
// 定义一个Animal类数组
// Animal[] 是数组
// animal[0]..... 不是数组 是一个取出的元素
Animal[] animals ={new Animal(),new Animal()};
for (int i=0;i<animals.length;i++){
Animal an =animals[i]; //接收数组中的元素 animal[i]拿到的相当于对象
an.move(); //对象调用move方法
}
}
}
// 定义一个动物类
class Animal{
// move方法
public void move(){
System.out.println("move方法.....");
}
}
运行结果:
eg:一个数据类型的数组只能储存同一个数据类型的对象 演示如下:
代码演示:
package com.bjpowernode.javase.array;
public class ArrayTest06 {
public static void main(String[] args) {
// 动态初始化定义一个Animal类型的数组
Animal[] animals =new Animal[2]; // 能储存两个元素
// 创建一个Animal对象,放到数组的第一个盒子中
animals[0] =new Animal(); // 运行成功,能够储存
// 创建一个Product对象,放到数组的第二个盒子中
// animals[1] =new Product(); 报错:不兼容的类型
// 报错原因:因为数组为Animal类型 只能存放Aniamal对象 不能存放Product其他对象
}
}
// 定义一个动物类
class Animal{
// move方法
public void move(){
System.out.println("move方法.....");
}
}
// 定义一个商品类
class Product{
}
思考:什么时候一个类型数组可以存放不同类型的对象呢?[继承的时候]
代码演示如下:
package com.bjpowernode.javase.array;
public class ArrayTest06 {
public static void main(String[] args) {
// 动态初始化定义一个Animal类型的数组
Animal[] animals =new Animal[2];
// 创建一个Cat对象 放到Animal类数组当中
animals[0] =new Cat();
// 创建一个Dog对象 放到Animal类数组当中
animals[1]=new Dog();
// 或者 Animal[] animal ={new Cat();new Dog};
for (int i=0;i<animals.length;i++){
// 如果调用父类的方法,取出来的方法可能是Cat的、也可能是Dog
animals[i].move(); // Animal[] animal ={new Cat();new Dog};对里面进行下标提取元素然后调用方法
}
}
}
// 定义一个动物类
class Animal{
// move方法
public void move(){
System.out.println("move方法.....");
}
}
// 定义一个猫类
class Cat extends Animal{
// 重写move方法
public void move(){
System.out.println("猫在走猫步");
}
}
// 定义一个狗类
class Dog extends Animal{
// 重写move方法
public void move(){
System.out.println("小狗真可爱");
}
}
输出结果:
再思考:如果子类Cat或者Dog当中有自己独有的方法,那么这个独有的方法该怎么调用呢?[向下转型]
代码演示:
package com.bjpowernode.javase.array;
public class ArrayTest06 {
public static void main(String[] args) {
// 动态初始化定义一个Animal类型的数组
Animal[] animals =new Animal[2];
// 创建一个Cat对象 放到Animal类数组当中
animals[0] =new Cat();
// 创建一个Dog对象 放到Animal类数组当中
animals[1]=new Dog();
// 或者 Animal[] animal ={new Cat();new Dog};
for (int i=0;i<animals.length;i++){
// 如果调用父类的方法,取出来的方法可能是Cat的、也可能是Dog
// animals[i].move(); // 调用的有Cat子类move方法 也有Dog子类move方法
// 那么怎么拿到子类猫当中独有的方法呢?
// 需要用到向下转型
if (animals[i] instanceof Cat){ //animal[i]才能拿到子类元素 animal是数组 不是元素子类对象
Cat cat =(Cat)animals[i];
cat.catchMouse(); // 拿到独有的抓老鼠方法
}else if (animals[i] instanceof Dog){
Dog dog =(Dog)animals[i];
dog.move();
}
}
}
}
// 定义一个动物类
class Animal{
// move方法
public void move(){
System.out.println("move方法.....");
}
}
// 定义一个猫类
class Cat extends Animal{
// 重写move方法
public void move(){
System.out.println("猫在走猫步");
}
// 独有的方法,抓老鼠
public void catchMouse(){
System.out.println("小猫喜欢抓老鼠");
}
}
// 定义一个狗类
class Dog extends Animal{
// 重写move方法
public void move() {
System.out.println("小狗真可爱");
}
// 独有的方法,跑得快
public void runNing(){
System.out.println("Dog跑到真快");
}
}
输出结果: