精华内容
下载资源
问答
  • 珍惜当下数据集处理是每种语言必备的功能,Java更甚之,数据集可以允许重复,也可以不允许重复,可以允许 null 存在,也可以不允许 null 存在,可以自动排序,也可以不自动排序,可以是阻塞式的,也可以是非阻塞式的...

    珍惜当下05e4eb58bee0ae28a165fbcf7343c4a7.png

    4bb87c489e7af05966edddf15c7c7d28.png


    数据集处理是每种语言必备的功能,Java更甚之,数据集可以允许重复,也可以不允许重复,可以允许 null 存在,也可以不允许 null 存在,可以自动排序,也可以不自动排序,可以是阻塞式的,也可以是非阻塞式的......前面我们分别详细介绍了Java常用两大数据集:集合和数组,这篇文章我们将对比介绍两者在开发中的使用场景及相关性能问题。还是比较实用的,总结一下分析给小伙伴们~~数组和集合如何选?

    数组在实际的系统开发中用得越来越少了,我们通常只有在阅读一些开源项目时才会看到它们的身影,在 Java 中它确实没有List、Set、Map这些集合类用起来方便,但是在基本类型处理方面数组还是占优势的,而且集合类的底层也都是通过数组实现的,比如对一个数据集求和,数组实现如下:

    /**   * 数组求和   * @param nums   * @return   */public static long getArraySum(int[] nums){    long sum = 0;    for (int i = 0; i < nums.length; i++) {        sum += i;    }    return sum;}public static void main(String[] args) {    int[] nums = new int[10000000];    for (int i = 0; i < nums.length; i++) {        nums[i] = i;    }    //数组求和    long star = System.currentTimeMillis();    long sum = getArraySum(nums);    System.out.println("数组和为:"+sum);    long end = System.currentTimeMillis();    System.out.println("数组求和耗时:"+(end-star)+"ms"); }运行结果如下:数组和结果为:49999995000000数组求和耗时:10ms

    对一个 int 类型的数组求和,取出所有的数组元素然后相加,此时如果是基本类型则使用数组效率是最高的,使用集合则效率次之。再看使用List求和如下:

     /**   * 集合求和   * @param nums   * @return   */  public static long getListSum(List nums){      long sum = 0;      for (int i = 0; i < nums.size(); i++) {          sum += nums.get(i);      }      return sum;  }public static void main(String[] args) {     //集合求和  Integer[] nums = new Integer[10000000];  for (int i = 0; i < nums.length; i++) {      nums[i] = i;  }  List numList = Arrays.asList(nums);  long starTime = System.currentTimeMillis();  long sum = getListSum(numList);  System.out.println("集合求和结果:"+sum);  long endTime = System.currentTimeMillis();  System.out.println("集合求和耗时:"+(endTime - starTime)+"ms");}运行结果:集合求和结果:49999995000000集合求和耗时:21ms

    我们可以看到对于一个1000W数据求和运算,使用集合求和耗时大约是数组求和耗时的2倍。对于集合求和:

    sum += nums.get(i)

    这里其实已经做了一个拆箱动作,Integer对象通过intValue()方法自动拆箱转换成了一个int基本类型,对于性能濒于临界的系统来说该方案是比较危险的,特别是大数量的时候,首先,在初始化 List 数组时要进行装箱动作,把一个 int 类型包装成一个 Integer 对象,虽然有整型池(面试踩过的坑:Java Integer 缓存),但不在整型池范围内的都会产生一个新的 Integer 对象,而且众所周知,基本类型是在栈内存中操作的,而对象则是在堆内存中操作的,栈内存的特点是速度快,容量小,堆内存的特点是速度慢,容量大,从性能上来讲,基本类型的处理占优势。其次,在进行求和计算(或者其他遍历计算)时要做拆箱动作,因此无谓的性能消耗也就产生了。

    int 和 long 型数据存储范围如下:

     @Test    public void test(){        System.out.println(String.format("int型数据范围:%d ~ %d",Integer.MIN_VALUE,Integer.MAX_VALUE));        System.out.println(String.format("long型数据范围:%d ~ %d",Long.MIN_VALUE,Long.MAX_VALUE));    }运行结果:int型数据范围:-2147483648 ~ 2147483647long型数据范围:-9223372036854775808 ~ 9223372036854775807
    有一个问题:数组一旦经过初始化长度不可变,这样不便使用,在实际开发中若有必要,可以使用变长数组。比如要对班级学生的信息进行统计,因为我们不知道一个班级会有多少学生(随时都可能会有学生入学、退学或转学),所以需要有一个足够大的数组来容纳所有的学生,但问题是多大才算足够大?这个足够大是相对于当时的场景而言的。随着环境的变化,“足够大”也可能会转变成“足够小”,然后就会出现超出数组最大容量的情况,那该如何解决呢?我们可以通过对数组扩容“婉转”地解决该问题,代码如下:
     /**     * 数组扩容     * @param data     * @param newLengh     * @param      * @return     */    public static T[]  expandCapacity(T[] data,int newLen){        newLen = newLen         return Arrays.copyOf(data,newLen);    }

    上述代码中采用的是Arrays数组工具类的copyof方法,产生了一个newLen 长度的新数组,并把原有的值拷贝了进去,之后就可以对超长的元素进行赋值了(依据类型的不同分别斌值为 0、 false 或 null ) 。其实,集合的长度自动维护功能的原理与此类似。在实际开发中,如果确实需要变长的数据集,数组也是在考虑范围之内的,不能因固定长度而将其否定之。多种最值算法,适时选择

    对一批数据进行排序,然后找出其中的最大值或最小值,这是基本的数据结构知识。在 Java 中我们可以通过编写算法的方式,也可以通过数组先排序再取值的方式来实现。下面以求最大值为例:

    1)自行实现:

    快速查找最大值先来看用快速查找法取最大值的算法,其代码如下:

     /**   * 获取数组最大值   * @param data   * @return   */  public static int getMax(int[] data){      int max = data[0];      for (int i = 0; i < data.length; i++) {          max = max > data[i] ? max : data[0];      }      return max;  }

    这是我们经常使用的最大值算法,也是速度最快的算法。它不要求排序,只要遍历一遍数组即可找出最大值。

    2、先排序,后取值:

    对于求最大值,也可以采用先排序后取值的方式,同样比较简单,代码如下:

      /**   * 获取数组最大值   *   * @param data   * @return   */  public static int getMax(int[] data) {      //先排序      Arrays.sort(data.clone());      //在取值      return data[data.length - 1];  }

    从效率上来讲,当然是自己写快速查找法更快一些了,只用遍历一遍就可以计算出最大值。但在实际测试中我们发现,如果数组数量少于1万,两者基本上没有差别,在同一个毫秒级别里,此时就可以不用自己写算法了,直接使用数组先排序后取值的方式。如果数组元素超过1万,就需要依据实际情况来考虑:自己实现,可以提升性能:先排序后取值,简单,通俗易懂。排除性能上的差异,两者都可以选择,甚至后者更方便一些,也更容易想到。

    现在问题来了,在代码中为什么要先使用clone拷贝再排序呢?那是因为数组也是一个对象,不拷贝就改变了原有数组元素的顺序,除非数组元素的顺序无关紧要。

    接着往下思考,如果要查找仅次于最大值的元素(也就是老二),该如何处理呢?要注意,数组的元素是可以重复的,最大值可能是多个,所以单单一个排序然后取倒数第二个元素是解决不了问题的。此时,就需要一个特殊的排序算法了,先要剔除重复数据,然后再排序。当然,自己写算法也可以实现,但是集合类已经提供了非常好的方法,要是再使用数组自己写算法就显得有点过时了。数组不能剔除重复数据,但 Set 集合却是可以的,而且 Set 的子类 Treeset 还能自动排序。代码如下:

      /**   * 获取比最大值小一个的最大值   * @param data   * @return   */  public static int getSecond(Integer[] data){      List list = Arrays.asList(data);      TreeSet treeList = new TreeSet<>(list);      //取得比最大值小一个的最大值      return treeList.lower(treeList.last());  }

    剔除重复元素并升序排列,这都由 Treeset 类实现的,然后可再使用 lower 方法寻找小于最大值的值。大家看,上面的程序非常简单吧?那如果是我们自己编写代码会怎么样?那至少要遍历数组两遍才能计算出老二的值,代码的复杂度将大大提升。也许你会说,这个要求有点变态,怎么会有这样的需求?不,有这样的需求很正常,比如在学校按成绩排名时,如果一个年级有 1200 人,只要找出最高的三个分数 〔 可不一定就是 3 个人,也可能是多人),是不是就是这种情况呢?因此在实际应用中求最值,包括最大值、最小值、第二大值、倒数第二小值等,使用集合是最简单的方式,当然若从性能方面来考虑,数组是最好的选择。注意最值计算时使用集合最简单,使用数组性能最优。

    不同列表选择不同的遍历方式

    我们思考这样一个案例:统计一个省的各科高考成绩,比如数学平均分是多少,语文平均分是多少等,这是每年招生办都会公布的数据,我们来想想看该算法应如何实现。当然使用数据库中的一个 SQL 语句就能求出平均值,不过这不再我们的考虑之列,这里还是使用纯 Java 的算法来解决之,看代码:

      public static void main(String[] args) {        int stuNum = 100 * 10000;        //100万学生成绩        List stuList = new ArrayList<>(stuNum);        for (int i = 0; i < stuNum; i++) {            stuList.add(new Random().nextInt(150));        }        //求平均值        long startTime = System.currentTimeMillis();        System.out.println("平均成绩是:"+average(stuList)+"分");        long endTime = System.currentTimeMillis();        System.out.println("所花时间是:"+(endTime-startTime)+"ms");    }    /**     * 求平均成绩     * @return     */    public static int average(List scores){        int sum = 0;        for (int score:scores) {            sum += score;        }        return sum/scores.size();    }运行结果:平均成绩是:74分所花时间是:38ms

    把 100 万名学生的成绩放到一个 ArrayList 数组中,然后通过 foreach 方式遍历求和,再计算平均值,程序非常简单,输出的结果是:平均分是:74,执行,间是:38ms 仅仅求一个算术平均值就花费了38毫秒,不要说考虑其他诸如加权平均值、补充平均值等算法,那花的时间肯定更长.我们仔细分析一下 arverage 方法,加号操作是最基本操作,没有什么可以优化的,剩下的就是一个遍历了,问题是 List 的遍历可以优化吗?我们可以尝试一下,List 的遍历还有另外一种方式,即通过下标方式来访问,代码如下:

     public static void main(String[] args) {        int stuNum = 100 * 10000;        //100万学生成绩        List stuList = new ArrayList<>(stuNum);        for (int i = 0; i < stuNum; i++) {            stuList.add(new Random().nextInt(150));        }        //求平均值        long startTime = System.currentTimeMillis();        System.out.println("平均成绩是:"+average(stuList)+"分");        long endTime = System.currentTimeMillis();        System.out.println("所花时间是:"+(endTime-startTime)+"ms");    }     /**     * 求平均成绩     * @return     */    public static int average(List scores){        int sum = 0;        for (int i = 0; i < scores.size(); i++) {            sum += scores.get(i);        }        return sum/scores.size();    }运行结果:平均成绩是:74分所花时间是:13ms

    执行时间已经大幅度下降,性能提升了65% ,这是一个飞速提升!那为什么我们使用下标方式遍历数组会有这么高的性能提升呢?这是因为 ArrayList 数组实现了 RandolllAccess 接口(随机存取接口),这也就标志着 ArrayList 是一个可以随机存取的列表。在 Java 中, RandomAccess 和 Cloneable、 Serializable 一样,都是标志性接口,不需要任何实现,只是用来表明其实现类具有某种特质的,实现了Cloneable 表明可以被拷贝,实现Serializable 接口表明被序列化 ,实现了 RandomAccess 则表明这个类可以随机存取,对我们的ArrayList来说也就标志着其数据元素之间没有关联,即两个位置相邻的元素之间没有相互依赖和索引关系,可以随机访问和存储。我们知道,Java 中的 foreach 语法是 iterator (迭代器)的变形用法,也就是说上面的 foreach 与下面的代码等价:

    for (Iterator iterator = scores.iterator(); iterator.hasNext();){   sum += iterator.next();

    那我们再想想什么是迭代器,迭代器是23个设计模式中的一种,“提供一种方法访问一个容器对象中的各个元素,同时又无须暴露该对象的内部细节”,也就是说对于ArrayList , 需要先创建一个迭代器容器,然后屏蔽内部遍历细节,对外提供 hasNext、next 等方法。问题是 ArrayList 实现了 RandomAccess 接口,已表明元素之间本来没有关系,可是,为了使用迭代器就需要强制建立一种互相“知晓”的关系,比如上一个元素可以判断是否有下一个元素,以及下一个元素是什么关系等,这也是通过foreach遍历耗时的原因。Java为ArrayList类加上RandomAccess 接口,就是在告诉我们,“嘿,ArrayList是随机存取的,采用下标方式遍历列表速度会更快”,接着又有一个问题了:为什么不把 RandomAccess 加到所有的 List 实现类上呢?那是因为有些List实现类不是随机存取的,而是有序存取的,比如 LinkedList 类, LinkedList 也是一个列表,但它实现了双向链表,每个数据结点中都有三个数据项:前节点的引用(PreviousNode)、本节点元素(Node Element )、后继节点的引用(Next Node),这是数据结构的基本知识,不多讲了,也就是说在LinkedList 中的两个元素本来就是有关联的,我知道你的存在,你也知道我的存在。那大家想想看,元素之间已经有关联关系了,使用foreach 也就是迭代器方式是不是效率更高呢?我们修改一下例子,代码如下:

    public static void main(String[] args) {      int stuNum = 100 * 10000;      //100万学生成绩      List stuList = new LinkedList();      for (int i = 0; i < stuNum; i++) {          stuList.add(new Random().nextInt(150));      }      //求平均值      long startTime = System.currentTimeMillis();      System.out.println("平均成绩是:"+average(stuList)+"分");      long endTime = System.currentTimeMillis();      System.out.println("所花时间是:"+(endTime-startTime)+"ms");  }    /**     * 求平均成绩     * @return     */    public static int average(List scores){        int sum = 0;        for (int score:scores) {            sum += score;        }        return sum/scores.size();    }平均成绩是:74分所花时间是:18ms

    确实如此,效率也提高了,18ms,可能大家还想要测试一下下标方式(也就是采用 get 方法访问元素)遍历LinkedList 元素的情况,其实不用测试,效率真的非常低,前面我们也介绍过LinkedList的get方法源码,这里就不再赘述了,感兴趣小伙伴可以翻看。

    明白了随机存取列表和有序存取列表的区别,我们优化average方法如下,实现不同的列表采用不同的遍历方法:

    /** * 求平均成绩 * @return */public static int average(List scores){    int sum = 0;   if (scores instanceof RandomAccess){       for (int i = 0; i < scores.size(); i++) {           sum += scores.get(i);       }   }else {       for (int score :scores) {           sum += score;       }   }    return sum/scores.size();}

    这样列表的遍历可以实现以不变应万变,随机存取列表(eg:ArrayList)采用下标遍历,有序列表(eg:LinkedList)采用foreach遍历。

    今天总结了一些数组和列表的一些优化小技巧,感兴趣小伙伴欢迎留言一起交流哦。323f701ae1cff3846b61ad2233f3905f.png

    另外对于冠状病毒,希望小伙伴们调整好心态8cc907ef14227320d96fa30c3aa26218.png,客观对待,不信谣不传谣,少出门,做好自我防护,爱己爱他人,平平安安度过难关。1d495e92e46da18e16bf6e228ba105ce.png


    欢迎关注ITSK,每天进步一点点,我们追求在交流中收获成长和快乐4cbea8c83867fc9ac9fb704aeb2bf48f.png5e146281428838971ef44ac5f15cb77f.png49084b5b3ec56291c74151eb9fd60ba2.png
    展开全文
  • 第一章 数组假设班里有30个同学,每个同学都有一个总成绩,如果要使用变量来记录这些些同学的成绩,我们需要定义30个变量,如果...当数据过多的时候,如果要统一管理,保存,就可以使用Java中提供的【数组】功能。1...

    第一章 数组

    假设班里有30个同学,每个同学都有一个总成绩,如果要使用变量来记录这些些同学的成绩,我们需要定义30个变量,

    如果我们求出我们班所有人的总成绩,得一个一个的加。这样的操作,是非常麻烦的。

    如果要把这30个成绩数据进行保存,之后要提取某个同学的成绩时,你得知道,这个同学他对应的变量名。

    这也是一个非常麻烦的地方。

    当数据过多的时候,如果要统一管理,保存,就可以使用Java中提供的【数组】功能。

    1.1 什么是数组

    数组是编程语言中最常见的数据结构。可以存储多个数据,一个数组只能存储同一种类型的数据。

    简单来说,数组就是一个装同一种类型数据的容器.

    这个容器是开辟在内存中的一个连续的空间,从而可以保证数据存储的顺序。这个顺序是从0开始排序,这些排序的序号叫索引,也叫角标

    可以理解为索引就是数据在数组中的位置.

    数组,是引用数据类型中的一员。

    生活中的小粟子:

    ded6fe9a87242e9d0b83967189dde03f.png

    在超市的储物柜,有很多的小柜子,每一个小柜子都有一个编号,这些小柜子是连续排列的。我们的数组,就类似于这么一个储物柜。

    数组是存在于内存中的,是看不见,但是存在的。

    1.2 创建数组的格式

    数组的应用,有种方式。

    1.2.1 动态创建

    数组创建的格式:

    数据类型 变量名

    数据类型[] 数组名 = new 数据类型[数组的长度];

    指定创建的数据,装的是什么样的数据。

    在数组的定义在,这个符号[]表示一维数组。

    数据类型[] :表示定义的是数组变量 ,也可以理解为数组数据类型,也是数据类型中的一种,归类于引用数据类型。

    数组名 == 变量名

    new :这个关键字,意思是在内存中去创建出存放数据的位置、空间

    数据类型[数组的长度]:表示在内存中存放数据容器中数组。

    数组的长度:表示存放数据的个数。

    存放在数组中的数据,又叫做数组元素,简称元素。

    示例:

    我们要创建一个数组,装30个同学的成绩:

    int[] chengJi = new int[30];

    1.2.2 静态创建

    创建格式:

    数据类型[] 数组名 = new 数据类型[]{元素...};

    所谓的静态,是在创建数据的同时,给出具体的数据。

    一创建就直接给数据。

    对于静态的数组创建的方式,还可以简写与:

    数据类型[] 数组名 = {元素...};

    这种格式,在开发中应用的最多。

    1.3 数组的初始化&操作

    初始化:数组在创建好的时候,会按数据的类型给每个元素一个【默认值】。

    数组操作:就是为每个数组的元素赋值,是往这个容器中添加或修改数据。

    数组的操作,最终操作的是数组中的数据。

    在数组创建时,不同的数据类型在堆内存中会有不同的默认值:

    u byte、short、int、long-->默认初始化值都是0整数

    u float、double-->默认初始化值都是0.0 小数

    u char --> 默认初始化值都是' ' (空格)

    u boolean --> 默认初始化值都是false

    u 引用数据类型 --> 默认初始化值都是null:

    无效的,无价值的;等于零的,

    在JAVA中意思是不存在的,空的

    1.3.1 数组的操作

    数组,是创建在内存中,连续的空间。

    我们把数据装到数组中对应位置,这个位置,使用的是当前数据所在的数组中的索引来指定。

    简单的一句话,索引记录、指定了数据所在的位置。

    int[] chengJi = new int[30];

    //把同学的成绩装到数组中

    //装第1个成绩

    使用数组的时候,要指定所使用的、要查找的索引位置,通过[索引]来实现,索引是从0开始计算,第1个就是0

    chengJi[0] = 99;

    //第2个成绩

    chengJi[1] = 98;

    ……

    chengJi[29] = ...

    这种方式,不管是静态的创建还是动态的创建,只要是往数组中放数据,都一样。

    但是,需要注意的是,如果数组中,指定的索引上已经有数据,再装,就是覆盖。原来的数据就没了

    如果要取出数组中的数据:

    假设要取了第13位同学的成绩,

    int t13 = chengJi[12];

    System.out.println(chengJi[12]);

    chengJi[12]; //这样也可以,但是没有意义。

    以上,是常规的数组的操作。

    如果我要把所有数组中元素,都拿来出进行计算,要怎么做??

    要把数组中的元素都拿出来,最快最有效的方法,是使用for循环。使用for循环来对数据进行的操作,叫做遍历。

    四个术语:索引、角标、元素、遍历

    角标:上角标(最大索引)、下角标(最小索引)

    1.3.2 示例

    1.3.2.1 设置值和取值

    示例1:设置值和取值

    ec9865a30503ea483c8db82a3361ef19.png

    索引的操作:[索引值]

    要操作的是哪一个数组中的哪一个索引值

    操作数据的格式:数组名[索引值];

    1.3.2.2 修改数据

    示例2:

    修改数组中的数据

    824b0bc4761f3252ec44b23ada1a6437.png

    修改之后,原数据将会消失。

    展开全文
  • 对象内存分析Java在内存分配时涉及区域寄存器(1)运行Java程序就会启动对应的线程,每一个线程,都有一个寄存器,用来记录程序在当前线程执行的位置。当线程阻塞后然后再重新运行,就可以在寄存器记录的位置继续执行...
    02de19d80e199acddd8ac6bcaa9b8731.png

    IT技术研习社,专注互联网技术研究与分享,喜欢的朋友可以点击【关注】;把经验传递给有梦想的人;

    对象内存分析

    Java在内存分配时涉及区域

    寄存器

    (1)运行Java程序就会启动对应的线程,每一个线程,都有一个寄存器,用来记录程序在当前线程执行的位置。当线程阻塞后然后再重新运行,就可以在寄存器记录的位置继续执行了。

    (2)线程之间的寄存器互不影响,所以称为线程私有的。同时,寄存器是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。在程序中无法通过代码控制寄存器的。

    (1) 每个线程包含一个栈区,栈中只保存基本数据类型的对象和对象的引用(不是对象),对象都存放在堆区中

    (2)每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。

    (1)存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令)

    (2)jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身

    (3)存放用new产生的数据

    方法区

    方法区是系统分配的一个内存逻辑区域,是JVM在装载类文件时,用于存储类型信息的(类的描述信息)。

    (1)Class的基本信息

    a.每个类的全限定名

    b.该类是类还是接口

    c.该类型的访问修饰符等

    (2)已装载的Class的详细信息

    a. 运行时常量池:在方法区中,每个类型都对应一个常量池,存放该类型所用到的所有常量,常量池中存储了诸如文字字符串、final变量值、类名和方法名常量。它们以数组形式通过索引被访问,是外部调用与类联系及类型对象化的桥梁。(存的可能是个普通的字符串,然后经过常量池解析,则变成指向某个类的引用)

    b.字段信息:字段信息存放类中声明的每一个字段的信息,包括字段的名、类型、修饰符。字段名称指的是类或接口的实例变量或类变量,字段的描述符是一个指示字段的类型的字符串,如private A a=null;则a为字段名,A为描述符,private为修饰符

    c.方法信息:类中声明的每一个方法的信息,包括方法名、返回值类型、参数类型、修饰符、异常、方法的字节码。(在编译的时候,就已经将方法的局部变量、操作数栈大小等确定并存放在字节码中,在装载的时候,随着类一起装入方法区。)

    静态变量(静态区):

    类变量,类的所有实例都共享,我们只需知道,在方法区有个静态区,静态区专门存放静态变量和静态块。

    非RAM存储

    硬盘等永久存储空间

    堆与栈详解

    (1)函数中定义的一些基本类型的数据变量和对象的引用变量都在函数的栈内存中分配。 栈的优势是存取速度比堆要快,仅次于直接位于CPU 的寄存器,而且数据可以共享。 存在栈中的数据大小与生存周期必须是确定的。因此里面的变量通常是局部变量、函数参数等。当在一段代码块定义一个变量时(局部变量),Java就在栈中为这个变量分配内存空间,当该变量所在的作用域结束后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。

    (2)堆内存用来存放由new创建的对象和数组。在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。

    【注意】

    在堆中产生了一个数组或对象后,还可以在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。引用变量是普通的变量,定义时在栈中分配,引用变量在程序运行到其作用域之外后被释放。而数组和对象本身在堆中分配,即使程序运行到使用new 产生数组或者对象的语句所在的作用域之外,数组和对象本身占据的内存不会被释放,数组和对象在没有引用变量指向它的时候,才变为垃圾,不可能在被使用,但仍然占据内存空间不放,在随后的一个不确定的时间被(GC)垃圾回收器收走(释放掉)。

    栈:就是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区。里面的变量通常是局部变量、函数参数等。

    堆:就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序没有释放掉,那么在程序结束后,操作系统会自动回收。

    深层次认识Java内存分配

    (1)在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配。

    (2)当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。

    (3)堆内存用来存放由new创建的对象和数组。在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。

    (4)在堆中产生了一个数组或对象后,还可以在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。

    (5)引用变量就相当于是为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象。

    (6)栈与堆都是Java用来在RAM(random-access-memory)[随机存取存储器]中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。

    (7)Java的堆是一个运行时数据区,类的对象从中分配空间。这些对象通过new来建立,它们不需要程序代码来显式的释放。

    (8)堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。

    (9)但缺点是,由于要在运行时动态分配内存,存取速度较慢。

    (10)栈的优势是,存取速度比堆要快,仅次于寄存器,栈中的数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。

    (11) 栈中主要存放一些基本类型的变量(int, short, long, byte, float, double, boolean, char)和对象引用。

    对象的实例化内存分析

    Person p; p = new Person();

    内存分析

    (1) java 中所说的地址值是 java虚拟机计算出来的值,而并非真正的内存地址,这点跟C语句不一样,C语句是可以指向内存地址的。

    (2)对象的声明发生在栈空间之中,此时只是声明了一个引用类型的变量p,(注意类类型的变量都属于引用型变量,类似String S ),而 p变量此时尚未保存任何数值,或者说 p 为空对象即 null,通过 new 运算符和类的构造方法为声明的对象分配成员变量。(详情参考如下)

    a.成员变量在堆的GC(garbage collection 垃圾回收)区分配空间,执行构造方法语句。

    b.new运算符为成员变量分配内存空间后将返回一个引用赋值给变量 p,确保这些成员变量能够通过 p 引用指向的地址可以访问。当然对象的声明和分配成员变量可以一个步骤完成。例如

    c. Person p = new Person ();

    对象分配成员变量的内存过程

    (1)在堆的永久区检查类模板信息,如果没有则执行第二步加载类的模板信息,如内存中有类信息则不执行第二步。

    (2)若没有,则加载类模板信息

    (3)在堆的GC区开辟合适的空间,保存类的成员变量。

    (4)为类对象的属性赋值此时只是赋缺省的初始化值

    (5)在栈内存中调用其构造方法,并将构造方法执行的结果值,赋值给类的成员变量。(类的方法调用发生在栈空间中,如果执行默认的构造方法没有任何结果返回,此时类的成员变量依然是第4步的缺省的初始化值)

    (6)在栈空间执行构造方法并将执行的结果传递给堆空间的成员变量(将构造方法执行的结果传递给堆空间的成员变量赋值操作)【this.name = name;】

    (7)将堆空间的类成员变量的内存首地址传递给栈的引用变量p,p 指向了类的这个实例化的对象,此时 p 可以称作 Person类的一个对象,并且可以使用 p.属性或者p.方法的形式访问对象的属性和方法。

    IT技术研习社,专注互联网技术研究与分享,喜欢的朋友可以点击【关注】;把经验传递给有梦想的人;

    展开全文
  • 我们定义的内容,比如类名、方法名、变量名等等命名规则:是有硬性要求的标识符可以包含英文字母(区分大小写)、0-9数字、$和_(下划线)标识符不能以数字开头标识符不能是关键字关键字:是指Java已经定义好的单词...

    a1ddb452eae7973f1320ae2731263859.png

    标识符

    • 标识符:是指在程序中自己定义的内容,如类名、方法名、变量名等等。
    • 标识符,是指在程序中,我们定义的内容,比如类名、方法名、变量名等等
    • 命名规则:是有硬性要求的
      • 标识符可以包含英文字母(区分大小写)、0-9数字、$和_(下划线)
      • 标识符不能以数字开头
      • 标识符不能是关键字

    关键字:是指Java已经定义好的单词,具有特殊含义,比如public、static、class、void等等

    • 命名规范:见名知意
      • 类名规范:首字母大写,后边的每个单词首字母大写(大驼峰式)
      • 方法名规范:首字母小写,后边的每个首字母大写(小驼峰式)
      • 变量名规范:一个单词的全部小写,如果多个单词组成的使用小驼峰

    常量

    常量:Java程序中固定不变的数据。

    常量分六种:

    663d638c13d7ede1c82a39b4feb8d35e.png

    变量

    常量是固定不变的数据,那么程序中可以改变的称为变量。

    例如数学中,可以使用字母代替数字运算:

    x = 1+1
    y = 1.1 + 1
    ...

    程序中,可以使用字母来保存数字的方式继续运算,可以提高计算能力,可以解决更多的问题,比如x可以保存5,也可以保存6,这样保存的数据是可以改变的,不过要注意变量的数据类型。

    Java中要求一个变量每次只能保存一个数据,必须要明确保存的数据类型。

    这里先介绍一下数据类型在继续说变量。

    数据类型

    Java的数据类型分为两大类:

    • 基本数据类型:整数、浮点数、字符、布尔
    • 引用数据类型:类、数组、接口、字符串

    其中基本数据类型又分四类八种:

    31821dce0700152179c7722efeefc8e9.png

    默认类型:整数类型是int、浮点类型是double

    变量的定义

    变量的三要素:数据类型变量名数据值

    格式一:定义变量并赋值

    数据类型 变量名 = 数据值;
    int a = 10;
    注意:给变量赋值不能超过数据类型的取值范围

    格式二:先定义后赋值

    数据类型 变量名;//先定义
    变量名 = 数据值;//再赋值
    ​
    int b;
    b = 20;
    long类型:建议数据后加L表示
    float类型:数据后加F表示

    注意事项:

    变量名:在一个大括号范围内,变量名不能重复

    变量赋值:定义的变量只有先赋值才能使用,且赋值要符合数据类型的取值范围。

    展开全文
  • 今天做报表统计遇到这样的问题: JSP页面中的checkbox值在数据库中存放的是类似这样的"... * 根据一个数组中的值向另个一个数组中取其对应的值,具体如下 * strVal1、strVal2两个数组分别...
  • Java里面,两个整数相除得到是个整数,比如int a = 28;int b = 10;a/b == 2; //true在Flex中,上面...如果你不小心把返回的结果作为数组的索引去取值的话,那么什么结果都没有。var a:Number = 28;var result:Nu...
  • java中Integer数组取值

    2020-12-30 14:23:13
    java中Integer数组取值 先看效果: 话不多说,直接上代码: public static void main(String[] args) { Integer[] array = new Integer[5]; array[0] = 0; array[1] = 1; array[2] = 2; array[3] = 3; array...
  • } &0xff 相当与小0的数 加256 ,主要因为java byte 取值 -128-127 与 usign char 0- 255不同,所以要这样作 主要是大小端的问题,根据实际情况改 public class Utils{ public static byte[] byteMerger(byte[] byte...
  • golang和java中byte取值范围不一样 o(╯□╰)o,这里是个坑golang定义如下// byte is an alias for uint8 and is equivalent to uint8 in all ways. It is// used,by convention,to distinguish byte values from 8-...
  • java:数组取值的过程

    千次阅读 2018-08-06 15:49:03
    1.动态初始化数组怎么运行取值? 1.1:主方法main进栈 1.2:在方法内的声明的局部变量int[] 1.3:在堆中开辟3块内存空间 1.4:把内存地址给予声明的局部变量 1.5:取数组值时,根据内存地址取值。 2.静态初始化取值...
  • importjava.sql.*;importjava.util.ArrayList;publicclassUntitled1{publicUntitled1(){}publicstaticvoidmain(String[]args){ArrayLista=newArrayList();try...package yhtest.checksql;imp...
  • java数组如何取值

    2015-01-12 21:48:58
    定义了一个String类型的数组 String srg[]={"1","2","3","4","5","6"}; for循环 for(int i=0;i<3;i++) { System.our.println("测试"); System.our.println(srg[0]); System.our.println(srg[1]); } ...
  • 取值 int[0] 遍历数组 JVM储存数据分析 1.堆:可以理解为凡是new 出来的东西都放在这里面 int [] a = new int[9] a --->int类型的数组放在堆里,长度是9 2.栈:主要存放一些变量容器名。大小,周期都已确定 a ---->...
  • 一、引入的jar包 ...二、取值循环 String str= " [{"sort":"1","item":"单选1","value":"答案一"},{"sort":2,"item":"选项2","value":"答案二"}]"; List<HashMap> list =JSON.parseArray(str, Hash...
  • "SPOT", "elementID": 1331794603.39, "name": "上海" } ] } ], } 想将pathID为1,elementID为1331794584.439的name设置为"南京",如何使用java客户端语句?多谢!现在方法是用笨方法:先获取这个DBObject,修改...
  • 字符串格式是这样的[[x1,y1,v1],[x2,y2,v2],[x3,y3,v3] ....[xn,yn,vn]]。想按顺序取出里面的v1...vn放到一个数组或者集合里,怎样最简洁?
  • Integer [] items = new Integer[]{-2,-3,-4,-5,-1,1,2,3,4,5}; int a= (int) Math.floor(Math.random()*items.length); Integer i = items[a]; System.out.println("kdjkjdjkj"+i);
  • 数组取值问题

    千次阅读 2018-07-07 17:30:12
    package test;...import java.util.Arrays; public class ValuePick { public static void main(String[] args) { int[] indexValue = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17...
  • } java获取json数组格式中的值 String str = "{'array':[{'id':5,'name':'张三'},{'id':6,'name':'李四'}]}"; JSONObject jsonObject = JSONObject.parseObject(str); JSONArray jsonArray = jsonobj.getJSONArray...
  • 在使用Java进行程序设计的时候,当为一个long型变量赋值一个整数常量时,如果这个值超过int型数据的取值范围,程序就会出现编译错误,但是有一种情况程序不会出错的,就是将多个int型数据进行算数运算的结果赋值给...
  • 一旦java数组定义了数组的长度,就不能做修改。但是python 没有这个限制。数组的取值可以用数组的下标进行取值,改值需要直接arr[i]=x形式。定义方式:数据类型[] 变量名= new(非内部数据类型) 数据类型[...
  • java 数组

    2017-09-26 11:24:00
    一旦java数组定义了数组的长度,就不能做修改。 但是python 没有这个限制。数组的取值可以用数组的下标进行取值,改值需要直接arr[i]=x形式。 定义方式:数据类型[] 变量名= new(非内部数据类型)...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 941
精华内容 376
关键字:

java数组取值

java 订阅