• 数组


    1、Java 中数组是对象吗

    • 什么是对象:
      语言层面:对象是根据某个类创建出来的一个实例,表示某类事物中一个具体的个体.对象具有各种属性,并且具有一些特定的行为计算机层面:对象就是内存中的一个内存块,在这个内存块封装了一些数据,也就是类中定义的各个属性
    • 数组:
      语言层面上,数组不是某类事物中的一个具体的个体,而是多个个体的集合,那么数组应该不是对象而在计算机的角度,数组也是一个内存块,也封装了一些数据,这样的话也可以称之为对象
    int[] a = new int[4];
    //a.length;  //对属性的引用不能当成语句
    int len = a.length;  //数组中保存一个字段, 表示数组的长度  
    //以下方法说明数组可以调用方法,java中的数组是对象.
    //这些方法是Object中的方法,所以可以肯定,数组的最顶层父类也是Object
    a.clone();
    a.toString();

    这基本上可以认定,java中的数组也是对象,它具有java中其他对象的一些基本特点:封装了一些数据,可以访问属性,也可以调用方法.所以:Java数组是对象
    而在C++中,数组虽然封装了数据,但数组名只是一个指针,指向数组中的首个元素,既没有属性,也没有方法可以调用;所以 C++中的数组不是对象,只是一个数据的集合,而不能当做对象来使用

    2、Java中数组的类型

    2.1、虚拟机自动创建了数组类型,可以把数组类型和8种基本数据类型一样, 当做java的内建类型:

    • 每一维度用一个"[“表示;开头两个”[",就代表是二维数组.
    • "["后面是数组中元素的类型(包括基本数据类型和引用数据类型).

    2.2、String[] s = new String[4];

    • 在java语言层面上,s是数组,也是一个对象,那么他的类型应该是 String[]
    • 在JVM中,他的类型为 [java.lang.String

    3、Java中数组的继承关系

    3.1、数组的顶层父类也必须是 Object
    这就说明数组对象可以向上直接转型到 Object,也可以向下强制类型转换,也可以使用 instanceof 关键字做类型判定

    //1  在test1()中已经测试得到以下结论: 数组也是对象, 数组的顶层父类是Object, 所以可以向上转型
    int[] a = new int[8];
    Object obj = a ; //数组的父类也是Object,可以将a向上转型到Object  
    //2  那么能向下转型吗?
    int[] b = (int[])obj;  //可以进行向下转型  
    //3  能使用instanceof关键字判定吗?
    if(obj instanceof int[]){  //可以用instanceof关键字进行类型判定
     System.out.println("obj的真实类型是int[]");
    }

    3.2、Java中数组的另一种"继承"关系

    String[] s = new String[5];
    Object[] obja = s;   //成立,说明可以用Object[]的引用来接收String[]的对象
    // s的直接父类是?
    //5那么String[] 的直接父类是Object[] 还是 Object? 
    System.out.println(s.getClass().getSuperclass().getName()); 
    //打印结果为java.lang.Object,说明String[] 的直接父类是 Object而不是Object[] 
    • 数组类直接继承了 Object,关于 Object[]类型的引用能够指向
      String[]类型的对象,这并不是严格意义上的继承,String[] 不继承自Object[],但是可以允许 String[]向上转型到
      Object[];可以理解为:其实这种关系可以这样表述:如果有两个类A和B,如果B继承(extends)了A,那么A[]类型的引用就可以指向B[]类型的对象
    • 数组的这种用法不能作用于基本类型数据
    int[] aa = new int[4];  
    Object[] objaa = aa;  //错误的,不能通过编译  
    因为 int 不是引用类型,Object 不是 int 的父类,在这里自动装箱不起作用
    Object 数组中可以存放任何值,包括基本数据类型
    public class ArrayTest {
     public static void main(String[] args) {
      test1();
      test2();
      test3();
     }
     /**
      * 数组具有这种特性:
      * 如果有两个类A和B,如果B继承(extends)了A,那么A[]类型的引用就可以指向B[]类型的对象
      * 测试数组的特殊特性对参数传递的便利性
      */
     private static void test3() {
      String[] a = new String[3];
      doArray(a);
     }
     private static void doArray(Object[] objs){
     }
     private static void doArray1(Object obj){
      //不能用Object接收数组,因为这样无法对数组的元素进行访问
      // obj[1]  //错误
      //如果在方法内部对obj转型到数组,存在类型转换异常的风险
      // Object[] objs = (Object[]) obj;
     }
     private static void doArray2(String[] strs){
      //如果适用特定类型的数组,就限制了类型,失去灵活性和通用性
     }
     private static void doArray3(String name, int age, String id, float account){
      //如果不适用数组而是依次传递参数,会使参数列表变得冗长,难以阅读
     }
     /**
      * 测试数组的集成关系, 并且他的继承关系是否和数组中元素的类型有关
      */
     private static void test2() {
      //1  在test1()中已经测试得到以下结论: 数组也是对象, 数组的顶层父类是Object, 所以可以向上转型
      int[] a = new int[8];
      Object obj = a ; //数组的父类也是Object,可以将a向上转型到Object
      //2  那么能向下转型吗?
      int[] b = (int[])obj;  //可以进行向下转型
      //3  能使用instanceof关键字判定吗?
      if(obj instanceof int[]){  //可以用instanceof关键字进行类型判定
       System.out.println("obj的真实类型是int[]");
      }
      //4   下面代码成立吗?
      String[] s = new String[5];
      Object[] obja = s;   //成立,说明可以用Object[]的引用来接收String[]的对象
      //5  那么String[] 的直接父类是Object[] 还是 Object?
      System.out.println(s.getClass().getSuperclass().getName());
      //打印结果为java.lang.Object,说明String[] 的直接父类是 Object而不是Object[]
      //6   下面成立吗?  Father是Son的直接父类
      Son[] sons = new Son[3];
      Father[] fa = sons;  //成立
      //7  那么Son[] 的直接父类是Father[] 还是  Object[] 或者是Object?
      System.out.println(sons.getClass().getSuperclass().getName());
      //打印结果为java.lang.Object,说明Son[]的直接父类是Object
      /**
       * 做一下总结, 如果A是B的父类, 那么A[] 类型的引用可以指向 B[]类型的变量
       * 但是B[]的直接父类是Object, 所有数组的父类都是Object
       */
      //8  上面的结论可以扩展到二维数组
      Son[][] sonss = new Son[2][4];
      Father[][] fathers = sonss;
      //将Father[][]数组看成是一维数组, 这是个数组中的元素为Father[]
      //将Son[][]数组看成是一维数组, 这是个数组中的元素为Son[]
      //因为Father[]类型的引用可以指向Son[]类型的对象
      //所以,根据上面的结论,Father[][]的引用可以指向Son[][]类型的对象
      /**
      * 扩展结论:
      * 因为Object是所有引用类型的父类
      * 所以Object[]的引用可以指向任何引用数据类型的数组的对象. 如:
      * Object[] objs = new String[1];
      * Object[] objs = new Son[1];
      *
      */ 
      //9  下面的代码成立吗?
      int[] aa = new int[4];
      //Object[] objaa = aa;  //错误的,不能通过编译
      //这是错误的, 因为Object不是int的父类,在这里自动装箱不起作用
      //10  这样可以吗?
      Object[] objss = {"aaa"12.5};//成立
     }
     /**
      * 测试在java语言中,数组是不是对象
      * 如果是对象, 那么他的类型是什么?
      */
     private static void test1() {
      int[] a = new int[4];
      //a.length;  //对属性的引用不能当成语句
      int len = a.length;  //数组中保存一个字段, 表示数组的长度
      //以下方法说明数组可以调用方法,java中的数组是对象.这些方法是Object中的方法,所以可以肯定,数组的最顶层父类也是Object
      a.clone();
      a.toString();
      /**
       * java是强类型的语言,一个对象总会有一个特定的类型,例如 Person p = new Person();
       * 对象p(确切的说是引用)的类型是Person类, 这个Person类是我们自己编写的
       * 那么数组的类型是什么呢? 下面使用反射的方式进行验证
       */
      int[] a1 = {1234};
      System.out.println(a1.getClass().getName());
      //打印出的数组类的名字为[I
      String[] s = new String[2];
      System.out.println(s.getClass().getName());
      //打印出的数组类的名字为  [Ljava.lang.String;
      String[][] ss = new String[2][3];
      System.out.println(ss.getClass().getName());
      //打印出的数组类的名字为    [[Ljava.lang.String;
      /**
       * 所以,数组也是有类型的,只不过这个类型不是有程序员自己定义的类, 也不是jdk里面
       * 的类, 而是虚拟机在运行时专门创建的类
       * 类型的命名规则是:
       *   每一维度用一个[表示;
       *   [后面是数组中元素的类型(包括基本数据类型和引用数据类型)
       *
       * 在java语言层面上,s是数组,也是一个对象,那么他的类型应该是String[],
       * 但是在JVM中,他的类型为[java.lang.String
       *
       * 顺便说一句普通的类在JVM里的类型为 包名+类名, 也就是全限定名
       */
     }    
     public static class Father {
     }    
     public static class Son extends Father {
     }
    }

    4、Java 数组初始化

    Java 数组是静态的,即当数组被初始化之后,该数组的长度是不可变的;

    5、数组扩容

    可以参照利用 List 集合中的add方法模拟实现

    // datas 原始数组    newLen 扩容大小
    public static <T> T[] expandCapacity(T[] datas,int newLen){
     newLen = newLen < 0 ? datas.length :datas.length + newLen;   
     //生成一个新的数组
     return Arrays.copyOf(datas, newLen);
    }
    // datas  原始数组
    public static <T> T[] expandCapacity(T[] datas){
     int newLen = (datas.length * 3) / 2;      //扩容原始数组的1.5倍
     //生成一个新的数组
     return Arrays.copyOf(datas, newLen);
    }
    // datas 原始数组    mulitiple 扩容的倍数
    public static <T> T[] expandCapacityMul(T[] datas,int mulitiple){
     mulitiple = mulitiple < 0 ? 1 : mulitiple;
     int newLen = datas.length * mulitiple;
     return Arrays.copyOf(datas,newLen );
    }

    6、数组复制问题

    所以通过 Arrays.copyOf() 方法产生的数组是一个浅拷贝。同时数组的 clone() 方法也是,集合的 clone() 方法也是,所以我们在使用拷贝方法的同时一定要注意浅拷贝这问题

    7、数组转换为 List

    asList 返回的是一个长度不可变的列表。数组是多长,转换成的列表就是多长,我们是无法通过 add、remove 来增加或者减少其长度的

    public static void main(String[] args) {
     int[] datas = new int[]{12345};
     List list = Arrays.asList(datas);
     System.out.println(list.size()); // 1
    }

    为什么上述结果输出为 1?
    首先看 asList的源码:

    public static <T> List<T> asList(T... a) {
     return new ArrayList<T>(a);
    }
    • 注意这个参数:T…a,这个参数是一个泛型的变长参数,我们知道基本数据类型是不可能泛型化的,也是就说 8个基本数据类型是不可作为泛型参数的, 但是为什么编译器没有报错呢?这是因为在 Java 中,数组会当做一个对象来处理,它是可以泛型的,所以我们的程序是把一个 int 型的数组作为了 T 的类型,所以在转换之后 List中就只会存在一个类型为 int 数组的元素了;
    • 这里是直接返回一个 ArrayList 对象返回,但是注意这个 ArrayList 并不是 java.util.ArrayList,而是
      Arrays 工具类的一个内之类,这个内部类并没有提供 add() 方法,那么查看父类 AbstractList仅仅只是提供了方法,方法的具体实现却没有,所以具体的实现需要子类自己来提供,但是非常遗憾这个内部类ArrayList 并没有提供 add 的实现方法

    size:元素数量、toArray:转换为数组,实现了数组的浅拷贝、get:获得指定元素、contains:是否包含某元素

    8、Java中length和length()的区别

    • 1、获取数组的长度是使用属性 length,获取字符串长度是使用方法 length()
    • 2、为什么数组有length属性?
      数组是一个容器对象,其中包含固定数量的同一类型的值.一旦数组被创建,那么数组的长度就是固定的了。数组的长度可以作为final实例变量的长度。因此,长度可以被视为一个数组的属性
    • 有两种创建数组的方法:1、通过数组表达式创建数组。2、通过初始化值创建数组。无论使用哪种方式,一旦数组被创建,其大小就固定了
    • 3、Java 中为什么没有定义一个类似 String 一样 Array 类:数组包含所有从 Object继承下来方法,为什么没有一个array类呢?一个简单的解释是它被隐藏起来了
    • 4、为什么 String 有length()方法?
      背后的数据结构是一个 char 数组,所以没有必要来定义一个不必要的属性(因为该属性在 char 数值中已经提供了)
    别废话,拿你代码给我看。
  • 相关阅读:
    大数加法、乘法实现的简单版本
    hdu 4027 Can you answer these queries?
    zoj 1610 Count the Colors
    2018 徐州赛区网赛 G. Trace
    1495 中国好区间 尺取法
    LA 3938 动态最大连续区间 线段树
    51nod 1275 连续子段的差异
    caioj 1172 poj 2823 单调队列过渡题
    数据结构和算法题
    一个通用分页类
  • 原文地址:https://www.cnblogs.com/lvxueyang/p/13707533.html
Copyright © 2020-2023  润新知