Java名词:对象、引用、堆、栈
一、对象和引用
对象的定义:按照通俗的说法,每个对象都是某个类(class)的一个实例(instance)。那么,实例化的过程怎么描述呢? 来看代码(类是 String)
1 | new String("我是对象A"); |
在 Java 中,实例化指的就是通过关键字“new”来创建对象的过程。以上代码在运行时就会创建两个对象——“我是对象A”和”我是对象B”;现在,该怎么操作他们呢?
需要有个指针指向 创建的对象,进而操作对象。 这就是“引用”
1 | String A, B; //声明了两个变量A,B 他们的类型是String |
上述代码第一行 A
和 B
此时的值为 undefined
,也就是未定义,或者应说未初始化uninitialized
。但不管是 undefined
还是 uninitialized
,都与 null
不同。
虽然说java默认初始化为null
,但是强行使用的话,系统会报错。
1 | System.out.println(A); //系统会报错 |
如果把A,B初始化为null
,编译器是认可的,不会报错。
再来看第二行代码:A = new String("我是对象A");
——创建“我是对象A”的 String 类对象,并将其赋值给 A 这个变量。
此时,A 就是”我是对象A”的引用; “=”操作符赋予了 A 这样的权利。
结论:对象是通过 new 关键字创建的;引用是依赖于对象的;= 操作符把对象赋值给了引用。
在java里,“=”不能被看成是一个赋值语句,它不是在把一个对象赋给另外一个对象,它的执行过程实质上是将右边对象的地址传给了左边的引用,使得左边的引用指向了右边的对象。
java表面上看起来没有指针,但它的引用其实质就是一个指针。
在java里,“=”语句不应该被翻译成赋值语句,因为它所执行的确实不是一个简单的赋值过程,而是一个传地址的过程,被译成赋值语句会造成很多误解,译得不准确。
二、堆,栈, 堆栈
1) 并没有“堆栈”这一东西,这世界上只有“堆”或“栈”
2) 堆是在程序运行时在内存中申请的空间(可理解为动态的过程);切记,不是在编译时;因此,Java 中的对象就放在这里,这样做的好处就是:
当需要一个对象时,只需要通过 new 关键字写一行代码即可,当执行这行代码时,会自动在内存的“堆”区分配空间——这样就很灵活。
另外,需要记住,堆遵循“先进后出”的规则
3) 栈,Java 把对象的引用放在栈里。
因为 Java 在编译程序时,必须明确的知道存储在栈里的东西的生命周期,否则就没法释放旧的内存来开辟新的内存空间存放引用——空间就那么大,前浪要把后浪拍死在沙滩上。
三、基本数据类型
先来看《Java 编程思想》中的一段话:
在程序设计中经常用到一系列类型,他们需要特殊对待。之所以特殊对待,是因为 new 将对象存储于“堆”中,故用 new 创建一个对象──特别小、简单的变量,往往不是很有效。因此,不用new来创建这类变量,而是创建一个并非是引用的变量,这个变量直接存储值,并置于栈中,因此更加高效。
在 Java 中,这些基本类型有:boolean、char、byte、short、int、long、float、double 和 void;还有与之对应的包装器:Boolean、Character、Byte、Short、Integer、Long、Float、Double 和 Void;它们之间涉及到装箱和拆箱。
1 | int a = 3; |
编译器先处理 int a = 3;
编译器在处理时 在栈中创建了一个变量为 a 的内存空间,然后查找有没有字面值为 3 的地址,没找到,就开辟一个存放 3 这个字面值的地址,然后将 a 指向 3 的地址。
编译器忙完了 int a = 3;
,就来接着处理 int b = 3;
在创建完 b 的变量后,由于栈中已经有 3 这个字面值,就将 b 直接指向 3 的地址;就不需要再开辟新的空间了。
假设在定义完 a 与 b 的值后,再令 a = 4;
,此时 b 是等于 3 呢,还是 4 呢?
当编译器遇到a = 4;
时,它会重新搜索栈中是否有 4 的字面值,如果没有,重新开辟地址存放 4 的值;如果已经有了,则直接将 a 指向 4 这个地址;因此 a 值的改变不会影响到 b 的值。
1 | public class Test1 { |
运行结果为:
老鹰
老鹰
birdCopy
里放的是对象的地址 (快捷方式)
java大体上会把内存分为四块区域:堆,栈,静态区,常量区。
堆 : 位于RAM中,用于存放所有的java对象。
栈 : 位于RAM中,引用就存在于栈中。
静态区 : 位于RAM中,被static修饰符修饰的变量会被放在这里。
常量区 :位于ROM中, 很明显,放常量的。