java学习之数组(一)【内存】
在java语言当中,为了更方便多个数据的管理,这里提供数组。
比如说,现在我们有一组数据,7,8,9,9,为了保存这四个数据,我们分别要定义变量来保存,少了还好说。但是假如,有100多个数据呢,我们一个一个定义起来,岂不是很麻烦。这个时候我们就引入了数组这个概念,来方便我们对多个数组的管理。
数组的定义:同一种类型数据的集合,通俗来讲,数组就是一个容器,容纳同类型的数据。
数组的书写格式:元素类型[] 数组名 = new 元素类型[元素的个数或者叫做数组的格式]。
class ArrDemo{ public static void main(String[] args) { /** *数组的定义格式:元素类型[] = new 元素类型[数组的长度或者叫做元素的个数]; *我们知道数组是存放许多同类型的元素的实体,之所以叫做实体是因为他是实实在在存放数据的地方。那么我们为了能区别于其他的变量声明,这里我们 *用一个关键字来创建他,这个关键字就是new。那么之后的int[]是表示建立一个int类型的数组,其中的数字7就是这个数组的长度 *数组的一个特点就是,一旦你定义了一个数组,你就必须明确他的长度。 */ int[] arr = new int[7]; /** *在定义完数组之后,我们就开始访问他,这个时候数组提供给我们的就是一个索引,表示我们要去的数据的位置。 *数组的索引的特点就是都是从0开始的,并且在你没有初始值的时候,他们都有各自类型的默认值。 * *byte,short,int类型的默认值是0 *float类型的默认值是0.0f *double类型的默认值是0.0 *char类型的默认值比较特殊,他的默认类型是'\u0000';这里可能有一个疑问,char当中不是只能容纳一个字符么,为什么这里可以容纳五个。因为这里容纳的 *并不是字符,而是字符的unicode编码,以\u 开头的就是unicode编码,表示的意思是空字符。 * */ System.out.println(arr[0]);//这里打印出来的就是int类型的默认值0; } }
这里我们也可以通过索引对数组中元素进行赋值,比如;arr[0] = 89;这样就把索引为0位置的数字,改为了89这个整数。
为了更好的理解数组,我们来看一下数组在内存当中的表现。
当我们运行一个程序的时候,我们需要在内存当中单独开辟一个内存空间,同时这个被分配的内存又有一个进一步的更细致的内存划分。因为这个内存划分就跟一个操场的划分差不多,有足球场,篮球场,田径区等等,这样划分的目的是为了让各个区域独立开,更高效的利用内存。如果一个操场,什么都有,一个是太乱不容易管理,一个是不安全容易出事。那么内存到底划分为哪些部分呢?
内存的划分:
1、寄存器,这个我们用不着,这个都是cpu直接操作的。
2、本地方法区。//这个是跟所处的系统相关的,因操作系统不一样
3、方法去。//内加载机制
4、栈存储区。
5、堆存储区域。
栈内存:
里边存储的是什么?
存储的都是局部变量。所谓的局部变量就是凡是定义在方法当中的变量都是局部变量。在类当中直接存储的就不是局部变量。
该局部变量所属的作用域一旦结束,那么这个局部变量也随之被内存自动释放。
特点:栈内存更新速度很块,声明周期很短。
class MemDemo{ int a = 20;//这个没有定义在方法当中,这个就不是局部变量
{
int c = 8777;//在局部代码块当中定义的变量也是局部变量,限定局部变量的声明周期。当我们使用一大部分变量的时候,有些变量用完之后,就释放了,这部分可以利用这种结构。
} public static void main(String[] args) { int b = 89;//这个就是定义在方法当中的,所以他是局部变量 //byte[] bt = new byte[9]; //short[] sr= new short[9]; //System.out.println(bt[0]); //System.out.println(sr[0]); for(int x=5;x<9;x++)//这里面的x也是局部变量 } }
堆内存:
堆内存存储的是什么呢?
堆内存当中存储的是数组和对象(其实数组就是对象的意思)。凡是new关键字建立的都在堆当中。数组和对象都是实体,实体的就是实实在在存储的个数。实体用于封装数据,而且都封装数字。实体当中存储这个一堆数据,其中一个数据消亡了,那么其他的数据都还可以用的。那么此时这个实体肯定是不能随时释放的。
堆不会随时释放,
特点:
1、每一个实体都有一个唯一的地址值。
2.堆内存中的每一个变量都有默认初始化的值,根据的类型的不同而不同。
3、垃圾回收机制。
方法先进栈,然后变量才进栈。当我们把数组实体赋值给一个栈内的变量的时候,其实就是在堆内存开辟了一个内存空间,并且给她分配了一个内存地址,此时地址虽然是二进制,但是为了表示方便采用大进制的来存储,即十六进制。这个时候数组就有一个唯一的地址。在我们定义数组的时候,我们定义了元素的个数,那么数组实体当中就为这些个元素分配了一个唯一的索引。当我们在堆内存当中定义一个数组实体之后,他会进行一次默认的初始化。这个就是一个规定。堆内存的变量有个特点,就会有一个默认初始化。
那么当把堆内存当中的实体付给栈内存当中的变量时,实际上是把堆内存当中的变量地址,赋值给栈内存的变量。栈内存当中的变量通过操作堆内存的地址,来操作数据。这个种方式就叫做引用。
当我们给数组赋值的时候,一定要考虑到类型是否相同。
class MemDemo{ public static void main(String[] args) { int[] a = new int[9]; a[0] = 's';//非法的数据类型不一样 a[0] = null;//非法的数据类型不一样 a = null;//null 的一个作用在与 解索引,即栈内存当中变量与堆内存当中的变量分开。 } }
当堆内存当中没有被栈内存当中的变量引用时,此时就变成了垃圾。会被GC不定时回收掉,以免内存占满,系统崩溃掉。
内存图:
数组操作的时候,出现的小错误:
1、不能超出索引范围。
class MemDemo{ public static void main(String[] args) { int[] a = new int[3]; System.out.println(a[3]); } }
此时我们编译的时候,没有报错,但是我们明明知道他是错误的。我们编译的时候,并没有分配内存。 比如说假如我们编译完了就分配内存了,那么如果我们编译完了放在那里,并没有运行,此时如果分配的话,显示不合理,也不合逻辑。
编译的过程是检查是否有语法的错误,但是在这里我们的语法并没有错误。因为a[3]的这个索引只有程序运行的时候,才会去找。此时我们再用JVM来运行,此时它创建完数组之后,来找这个内存的时候发现并没有找到,所以报错:连位置都详细告诉我们了。
假如我们这样:
class MemDemo{ public static void main(String[] args) { int[] a = new int[3]; a = null; System.out.println(a[0]); } }
当引用型变量即数组或者对象,在没有任何实体指向的情况下被调用,还在用他操作实体,此时就会报空指针错误。
如果我们直接打印数组,会出现什么呢?
class MemDemo{ public static void main(String[] args) { int[] a = new int[3]; //a = null; System.out.println(a); } }
运行结果:
这个字符什么意思呢?
我们分开看:
1、"["这个符号代表是数组。
2、"I"这个代表是int类型
3、@分界线,左边是需要看懂的。
4、15bdc50这个是数组实体的哈希值,是一个算法(如果冲突就继续往下算,这个哈希算法是采用windows的,根据系统自己定义的),用这个算法计算,数组实体在内存当中的存储位置。
未完待续。