JAVA堆 栈和常量池详解
- 格式:pdf
- 大小:163.79 KB
- 文档页数:12
堆、栈及静态数据区详解五大内存分区在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。
栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。
里面的变量通常是局部变量、函数参数等。
堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。
如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
自由存储区,就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free 来结束自己的生命的。
全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。
常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改,而且方法很多)明确区分堆与栈在bbs上,堆与栈的区分问题,似乎是一个永恒的话题,由此可见,初学者对此往往是混淆不清的,所以我决定拿他第一个开刀。
首先,我们举一个例子:void f() { int* p=new int[5]; }这条短短的一句话就包含了堆与栈,看到new,我们首先就应该想到,我们分配了一块堆内存,那么指针p呢?他分配的是一块栈内存,所以这句话的意思就是:在栈内存中存放了一个指向一块堆内存的指针p。
在程序会先确定在堆中分配内存的大小,然后调用operator new分配内存,然后返回这块内存的首地址,放入栈中,他在VC6下的汇编代码如下:00401028 push 14h0040102A call operator new (00401060)0040102F add esp,400401032 mov dword ptr [ebp-8],eax00401035 mov eax,dword ptr [ebp-8]00401038 mov dword ptr [ebp-4],eax这里,我们为了简单并没有释放内存,那么该怎么去释放呢?是delete p么?澳,错了,应该是delete []p,这是为了告诉编译器:我删除的是一个数组,VC6就会根据相应的Cookie 信息去进行释放内存的工作。
堆与栈,静态变量和全局变量的区别堆与栈,静态变量和全局变量的区别对和栈的主要的区别由以下几点:1、管理方式不同;2、空间大小不同;3、能否产生碎片不同;4、生长方向不同;5、分配方式不同;6、分配效率不同;管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。
空间大小:一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。
但是对于栈来讲,一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小是1M(好像是,记不清楚了)。
当然,我们可以修改:打开工程,依次操作菜单如下:Project->Setting->Link,在Category 中选中Output,然后在Reserve中设定堆栈的最大值和commit。
注意:reserve最小值为4Byte;commit是保留在虚拟内存的页文件里面,它设置的较大会使栈开辟较大的值,可能增加内存的开销和启动时间。
碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。
对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出,详细的可以参考数据结构,这里我们就不再一一讨论了。
生长方向:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。
分配方式:堆都是动态分配的,没有静态分配的堆。
栈有2种分配方式:静态分配和动态分配。
静态分配是编译器完成的,比如局部变量的分配。
动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。
分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。
java内存使用情况的命令Java是一种面向对象的编程语言,它在开发应用程序时需要使用内存来存储数据和执行代码。
因此,了解Java的内存使用情况对于开发人员来说是非常重要的。
Java虚拟机(JVM)负责管理Java应用程序的内存,它使用垃圾回收机制来自动管理内存的分配和释放。
JVM的内存可以分为以下几个部分:1. 堆(Heap):堆是Java程序运行时动态分配的内存区域,用于存储对象实例。
堆的大小可以通过命令行参数-Xmx和-Xms来设置。
-Xms表示JVM启动时初始分配的堆内存大小,-Xmx表示堆能够达到的最大内存大小。
2. 方法区(Method Area):方法区用于存储已加载的类信息、常量、静态变量等数据。
方法区的大小可以通过命令行参数-XX:PermSize和-XX:MaxPermSize来设置。
-XX:PermSize表示JVM启动时初始分配的方法区大小,-XX:MaxPermSize表示方法区能够达到的最大大小。
3. 栈(Stack):栈用于存储Java方法中的局部变量以及方法调用时的状态信息。
每个Java线程都有一个独立的栈,栈的大小是固定的,并且在线程创建时被分配。
栈的大小可以通过命令行参数-Xss来设置。
除了上述部分,JVM还会使用一些额外的内存空间,如直接内存(DirectMemory)和本地方法栈(Native Method Stack),用于存储一些特殊的数据和执行本地方法。
了解Java的内存使用情况对于定位内存泄漏和优化程序性能非常有帮助。
下面是几个常用的命令,可以用于监控和调整Java程序的内存使用情况:1. jps:该命令用于列出当前运行的Java进程,以及对应的进程ID。
2. jstat:该命令用于监控Java虚拟机的各种运行状态,包括堆的使用情况、类加载数量、垃圾回收情况等。
常用的参数包括-jstat -gcutil <pid>和-jstat-gccapacity <pid>。
jvm堆的基本结构
Java虚拟机(JVM)堆是一种重要的内存分配结构,被用来存储Java 类实例和数组,是Java内存管理的重要组成部分。
JVM堆由以下三部分组成:
1.堆栈:堆栈是一种先进后出(LIFO)的内存结构,用于存储Java对象的本地变量。
堆栈空间占用资源比较小,但容量有限,一般比较小(只支持少计数的变量)。
2.程序计数器:程序计数器是一个小巧且独立的内存结构,用于保存执行过程中当前活动线程正在执行的字节码行号。
jvm通过程序计数器控制程序运行,它不会存储任何对象。
3.垃圾回收堆:垃圾回收堆是一种用于存储对象的内存结构,一般由堆顶(Young generation),年老代(Old Generation )和永久代(Permanent Generation)组成。
堆顶是一个存储新生成的对象的内存区域,当堆顶达到容量上限时,部分对象会被转移至年老代;而永久代则用于存放永久数据,如Java类,字段和方法。
总的来说,JVM堆是一个内存结构,用于管理Java对象。
它主要由堆栈、程序计数器和垃圾回收堆组成,通过这三个基本构建块构成JVM
堆,兼顾性能和可维护性。
JVM堆是Java内存管理的重要组成部分,其利用了可伸缩性和性能可控性,是运行Java程序的重要基础。
栈和堆的区别 (转) 终于知道区别了(2007-09-12 08:50:49)转载标签:IT/科技一个由 c/C++ 编译的程序占用的内存分为以下几个部分:1 、栈区( stack )—由编译器自动分配释放,存放函数的参数值,局部变量的值等。
其操作方式类似于数据结构中的栈。
2 、堆区( heap )—一般由程序员分配释放,若程序员不释放,程序结束时可能由 OS 回收。
注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3 、全局区(静态区)( static )—全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。
程序结束后由有系统释放。
4 、文字常量区—常量字符串就是放在这里的。
程序结束后由系统释放。
5 、程序代码区—存放函数体的二进制代码。
例子程序:这是一个前辈写的,非常详细//main.cppint a = 0; //全局初始化区char *p1; //全局未初始化区main(){int b; 栈char s[] = "abc"; //栈char *p2; //栈char *p3 = "123456"; //123456在常量区,p3在栈上。
static int c =0; //全局(静态)初始化区p1 = (char *)malloc(10);p2 = (char *)malloc(20); //分配得来得10和20字节的区域就在堆区。
strcpy(p1, "123456"); //123456放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
}栈:在 Windows 下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。
这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS 下,栈的大小是 2M (也有的说是 1M ,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示 overflow 。
jvm内存模型面试题JVM(Java Virtual Machine)是Java编程语言的运行环境,它负责将Java字节码解释成特定平台的机器指令并执行。
JVM内存模型是JVM运行时的核心组成部分,它定义了Java程序在JVM中如何分配和使用内存。
本文将围绕JVM内存模型展开讨论,并回答一些与之相关的面试题。
一、Java内存区域的划分JVM内存模型将所有的内存划分为以下几个区域:1. 程序计数器(Program Counter Register):用于标记当前线程所执行的字节码指令的地址。
2. Java虚拟机栈(Java Virtual Machine Stacks):每个线程在运行期间都会创建一个栈用于存储局部变量、操作数栈、动态链接、方法出口等信息。
3. 本地方法栈(Native Method Stack):与Java虚拟机栈类似,用于支持本地方法的调用。
4. Java堆(Java Heap):被所有线程共享的内存区域,用于存储对象实例和数组。
5. 方法区(Method Area):也称为永久代(Permanent Generation),用于存储已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
6. 运行时常量池(Runtime Constant Pool):方法区的一部分,用于存放编译期生成的各种字面量和符号引用。
7. 直接内存(Direct Memory):不是JVM运行时数据区的一部分,但是被频繁使用,并且可以通过设置-Xmx和-Xms控制其大小。
二、面试题1. 什么是Java虚拟机栈?它与堆有什么区别?Java虚拟机栈是线程私有的,用于存储线程执行方法的信息。
每个方法在执行时都会创建一个栈帧,栈帧包含了局部变量表、操作数栈、动态链接、方法出口等信息。
而Java堆是所有线程共享的,用于存储对象实例和数组。
2. 什么是方法区?它与堆有什么区别?方法区是JVM的一个逻辑区域,用于存储已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
Java⾥的堆(heap)栈(stack)和⽅法区(method)基础数据类型直接在栈空间分配,⽅法的形式参数,直接在栈空间分配,当⽅法调⽤完成后从栈空间回收。
引⽤数据类型,需要⽤new来创建,既在栈空间分配⼀个地址空间,⼜在堆空间分配对象的类变量。
⽅法的引⽤参数,在栈空间分配⼀个地址空间,并指向堆空间的对象区,当⽅法调⽤完成后从栈空间回收。
局部变量 new 出来时,在栈空间和堆空间中分配空间,当局部变量⽣命周期结束后,栈空间⽴刻被回收,堆空间区域等待GC回收。
⽅法调⽤时传⼊的 literal 参数,先在栈空间分配,在⽅法调⽤完成后从栈空间分配。
字符串常量在DATA 区域分配,this 在堆空间分配。
数组既在栈空间分配数组名称,⼜在堆空间分配数组实际的⼤⼩!哦对了,补充⼀下static在DATA区域分配。
从Java的这种分配机制来看,堆栈⼜可以这样理解:堆栈(Stack)是操作系统在建⽴某个进程时或者线程(在⽀持多线程的操作系统中是线程)为这个线程建⽴的存储区域,该区域具有先进后出的特性。
每⼀个Java应⽤都唯⼀对应⼀个JVM实例,每⼀个实例唯⼀对应⼀个堆。
应⽤程序在运⾏中所创建的所有类实例或数组都放在这个堆中,并由应⽤所有的线程共享.跟C/C++不同,Java中分配堆内存是⾃动初始化的。
Java中所有对象的存储空间都是在堆中分配的,但是这个对象的引⽤却是在堆栈中分配,也就是说在建⽴⼀个对象时从两个地⽅都分配内存,在堆中分配的内存实际建⽴这个对象,⽽在堆栈中分配的内存只是⼀个指向这个堆对象的指针(引⽤)⽽已。
<⼆>这两天看了⼀下深⼊浅出JVM这本书,推荐给⾼级的java程序员去看,对你了解JAVA的底层和运⾏机制有⽐较⼤的帮助。
废话不想讲了.⼊主题:先了解具体的概念:JAVA的JVM的内存可分为3个区:堆(heap)、栈(stack)和⽅法区(method)堆区:1.存储的全部是对象,每个对象都包含⼀个与之对应的class的信息。
栈内存和堆内存的理解
栈内存和堆内存是操作系统管理内存的典型结构,是用于与运行程序有关的内存管理中的基本概念,它们共同构成了操作系统管理内存的整体框架,提供了程序员运行程序的基本手段,也是计算机的核心技术之一.
栈内存是一种内存组织形式,主要存放函数及函数内所有变量的值,也用来存储函数调用时的位置指针和函数参数等信息,一般认为其操作迅速而浪费空间,是一种受限的通用缓存,主要适用于处理中断和递归调用等情况.
堆内存是由操作系统自动维护管理的一种大型内存,它被用于动态分配和管理大规模的内存,主要用于存放局部变量和全局变量,而这些变量的大小或者位置随着程序运行的不同而变化。
堆内存对外观不可见,它可以灵活地添加和释放。
因此,堆内存大小不受限制,是一种更有效率的内存管理。
总结一下,栈内存特性是快速,有限,专用内存,主要用于存放函数及函数内所有变量的值,而堆内存特性是浪费资源,灵活动态分配,可以存放局部变量和全局变量,而大小和位置由程序运行而变化。
如果要根据特性选择内存,则栈内存适用并发处理、处理函数调用等特殊应用,而堆内存适合动态分配大量内存的情况。
Java常量池详解⽬录(1)class常量池(2)运⾏时常量池(3)基本类型包装类常量池(4)字符串常量池总结java中有⼏种不同的常量池,以下的内容是对java中⼏种常量池的介绍,其中最常见的就是字符串常量池。
(1)class常量池在Java中,Java类被编译后就会形成⼀份class⽂件;class⽂件中除了包含类的版本、字段、⽅法、接⼝等描述信息外,还有⼀项信息就是常量池,⽤于存放编译器⽣成的各种字⾯量和符号引⽤,每个class⽂件都有⼀个class常量池。
其中字⾯量包括:1.⽂本字符串 2.⼋种基本类型的值 3.被声明为final的常量等;符号引⽤包括:1.类和⽅法的全限定名 2.字段的名称和描述符 3.⽅法的名称和描述符。
(2)运⾏时常量池运⾏时常量池存在于内存中,也就是class常量池被加载到内存之后的版本,是⽅法区的⼀部分(JDK1.8 运⾏时常量池在元空间,元空间也是⽅法区的⼀种实现)。
不同之处是:它的字⾯量可以动态的添加(String类的intern()),符号引⽤可以被解析为直接引⽤。
JVM在执⾏某个类的时候,必须经过加载、连接、初始化,⽽连接⼜包括验证、准备、解析三个阶段。
⽽当类加载到内存中后,jvm就会将class常量池中的内容存放到运⾏时常量池中,这⾥所说的常量包括:基本类型包装类(包装类不管理浮点型,整形只会管理-128到127)和字符串类型(即通过String.intern()⽅法可以强制将String放⼊常量池),运⾏时常量池是每个类私有的。
在解析阶段,会把符号引⽤替换为直接引⽤。
(3)基本类型包装类常量池Java 基本类型的包装类的⼤部分都实现了常量池技术。
Byte,Short,Integer,Long这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,Character创建了数值在[0,127]范围的缓存数据,Boolean直接返回True或False,如果超出对应范围就会去创建新的对象。
java栈的用法Java栈的用法Java栈是一种非常重要的数据结构,它在Java语言中广泛应用于各种场景,例如方法调用、异常处理、表达式求值等。
本文将介绍Java栈的基本概念、常见操作以及实现方式等内容。
一、基本概念1. 栈的定义栈是一种线性数据结构,它具有后进先出(Last In First Out,LIFO)的特点。
栈可以看作是一个容器,只能在容器的一端进行插入和删除操作。
插入操作称为“进栈”或“压栈”,删除操作称为“出栈”。
2. 栈的实现方式Java中可以使用数组或链表来实现栈。
使用数组实现时,需要定义一个固定大小的数组,并记录当前栈顶元素位置;使用链表实现时,则需要定义一个头节点和一个指向当前节点的指针。
3. 栈的应用场景Java栈在很多场景下都有着重要的应用,例如:- 方法调用:每当调用一个方法时,都会创建一个新的栈帧并压入当前线程对应的虚拟机栈中。
- 异常处理:当抛出异常时,JVM会创建一个异常对象,并将其压入当前线程对应的虚拟机栈中。
- 表达式求值:通过使用两个栈,一个存放操作数,一个存放运算符,可以实现表达式的求值。
二、常见操作1. 压栈(push)将一个元素压入栈顶。
Java代码示例:```public void push(E item) {ensureCapacity(size + 1);elements[size++] = item;}```2. 出栈(pop)弹出栈顶元素,并返回该元素。
Java代码示例:```public E pop() {if (size == 0)throw new EmptyStackException();E result = elements[--size];elements[size] = null; // 避免内存泄漏 return result;}```3. 查看栈顶元素(peek)返回当前栈顶元素,但不弹出该元素。
Java代码示例:```public E peek() {if (size == 0)throw new EmptyStackException(); return elements[size - 1];}```4. 判断是否为空(isEmpty)判断当前栈是否为空。
Java堆、栈和常量池详解转载于他人,出处未知。
讲解很详细,你一定有所收获!一、在JAVA中,有六个不同的地方可以存储数据:1.寄存器(register)。
这是最快的存储区,因为它位于不同于其他存储区的地方——处理器内部。
但是寄存器的数量极其有限,所以寄存器由编译器根据需求进行分配。
你不能直接控制,也不能在程序中感觉到寄存器存在的任何迹象。
------最快的存储区,由编译器根据需求进行分配,我们在程序中无法控制.2.栈(stack)。
位于通用RAM中,但通过它的“栈指针”可以从处理器哪里获得支持。
栈指针若向下移动,则分配新的内存;若向上移动,则释放那些内存。
这是一种快速有效的分配存储方法,仅次于寄存器。
创建程序时候,JAVA编译器必须知道存储在栈内所有数据的确切大小和生命周期,因为它必须生成相应的代码,以便上下移动栈指针。
这一约束限制了程序的灵活性,所以虽然某些JAVA 数据存储在栈中——特别是对象引用,但是JAVA对象不存储其中。
------存放基本类型的变量数据和对象、数组的引用,但对象本身不存放在栈中,而是存放在堆(new出来的对象)或者常量池中(字符串常量对象存放在常量池中)3.堆(heap)。
一种通用性的内存池(也存在于RAM中),用于存放所以的JAVA 对象。
堆不同于栈的好处是:编译器不需要知道要从堆里分配多少存储区域,也不必知道存储的数据在堆里存活多长时间。
因此,在堆里分配存储有很大的灵活性。
当你需要创建一个对象的时候,只需要new写一行简单的代码,当执行这行代码时,会自动在堆里进行存储分配。
当然,为这种灵活性必须要付出相应的代码。
用堆进行存储分配比用栈进行存储存储需要更多的时间。
------存放所有new出来的对象。
4.静态存储(static storage)。
这里的“静态”是指“在固定的位置”。
静态存储里存放程序运行时一直存在的数据。
你可用关键字static来标识一个对象的特定元素是静态的,但JAVA对象本身从来不会存放在静态存储空间里。
------存放静态成员(static定义的)5.常量存储(constant storage)。
常量值通常直接存放在程序代码内部,这样做是安全的,因为它们永远不会被改变。
有时,在嵌入式系统中,常量本身会和其他部分分割离开,所以在这种情况下,可以选择将其放在ROM中------存放字符串常量和基本类型常量(public static final,String类型常量,String s=“abc”直接赋值型)6.非RAM存储。
如果数据完全存活于程序之外,那么它可以不受程序的任何控制,在程序没有运行时也可以存在。
------硬盘等永久存储空间速度来说,有如下关系:寄存器>栈>堆>其他二、栈,堆和常量池1.对于栈和常量池中的对象可以共享,对于堆中的对象不可以共享。
栈中的数据大小和生命周期是可以确定的,当没有引用指向数据时,这个数据就会消失。
堆中的对象的由垃圾回收器负责回收,因此大小和生命周期不需要确定,具有很大的灵活性。
2.对于字符串:其对象的引用都是存储在栈中的,如果是编译期已经创建好(直接用双引号定义的)的就存储在常量池中,如果是运行期(new出来的)才能确定的就存储在堆中。
对于equals相等的字符串,在常量池中永远只有一份,在堆中有多份。
如以下代码:Java代码String s1="china";String s2="china";String s3="china";String ss1=new String("china");String ss2=new String("china");String ss3=new String("china");这里解释一下黄色这3个箭头,对于通过new产生一个字符串(假设为”china”)时,会先去常量池中查找是否已经有了”china”对象,如果没有则在常量池中创建一个此字符串对象,然后堆中再创建一个常量池中此”china”对象的拷贝对象。
这也就是有道面试题:String s=new String(“xyz”);产生几个对象?一个或两个,如果常量池中原来没有”xyz”,就是两个。
3.对于基础类型的变量和常量:变量和引用存储在栈中,常量存储在常量池中。
如以下代码:Java代码int i1=9;int i2=9;int i3=9;public static final int INT1=9;public static final int INT2=9;public static final int INT3=9;4.对于成员变量和局部变量:成员变量就是方法外部,类的内部定义的变量;局部变量就是方法或语句块内部定义的变量。
局部变量必须初始化。
形式参数是局部变量,局部变量的数据存在于栈内存中。
栈内存中的局部变量随着方法的消失而消失。
成员变量存储在堆中的对象里面,由垃圾回收器负责回收。
如以下代码:Java代码class BirthDate{private int day;private int month;private int year;public BirthDate(int d,int m,int y){day=d;month=m;year=y;}省略get,set方法………}public class Test{public static void main(String args[]){int date=9;Test test=new Test();test.change(date);BirthDate d1=new BirthDate(7,7,1970);}public void change1(int i){i=1234;}对于以上这段代码,date为局部变量,i,d,m,y都是形参为局部变量,day,month,year为成员变量。
下面分析一下代码执行时候的变化:1.main方法开始执行:int date=9;date局部变量,基础类型,引用和值都存在栈中。
2.Test test=new Test();test为对象引用,存在栈中,对象(new Test())存在堆中。
3.test.change(date);i为局部变量,引用和值存在栈中。
当方法change执行完成后,i就会从栈中消失。
4.BirthDate d1=new BirthDate(7,7,1970);d1为对象引用,存在栈中,对象(new BirthDate())存在堆中,其中d,m,y为局部变量存储在栈中,且它们的类型为基础类型,因此它们的数据也存储在栈中。
day,month,year为成员变量,它们存储在堆中(new BirthDate()里面)。
当BirthDate 构造方法执行完之后,d,m,y将从栈中消失。
5.main方法执行完之后,date变量,test,d1引用将从栈中消失,new Test(),new BirthDate()将等待垃圾回收。
三、Java堆、栈和常量池详解(二)1.栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方。
与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。
2.栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。
但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。
另外,栈数据可以共享,详见第3点。
堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,Java的垃圾收集器会自动收走这些不再使用的数据。
但缺点是,由于要在运行时动态分配内存,存取速度较慢。
3.Java中的数据类型有两种。
一种是基本类型(primitive types),共有8种,即int,short,long,byte,float, double,boolean,char(注意,并没有string的基本类型)。
这种类型的定义是通过诸如int a=3;long b=255L;的形式来定义的,称为自动变量。
值得注意的是,自动变量存的是字面值,不是类的实例,即不是类的引用,这里并没有类的存在。
如int a=3;这里的a是一个指向int类型的引用,指向3这个字面值。
这些字面值的数据,由于大小可知,生存期可知(这些字面值固定定义在某个程序块里面,程序块退出后,字段值就消失了),出于追求速度的原因,就存在于栈中。
另外,栈有一个很重要的特殊性,就是存在栈中的数据可以共享。
假设我们同时定义int a=3;int b=3;编译器先处理int a=3;首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为3的地址,没找到,就开辟一个存放3这个字面值的地址,然后将a指向3的地址。
接着处理int b=3;在创建完b的引用变量后,由于在栈中已经有3这个字面值,便将b直接指向3的地址。
这样,就出现了a与b同时均指向3的情况。
特别注意的是,这种字面值的引用与类对象的引用不同。
假定两个类对象的引用同时指向一个对象,如果一个对象引用变量修改了这个对象的内部状态,那么另一个对象引用变量也即刻反映出这个变化。
相反,通过字面值的引用来修改其值,不会导致另一个指向此字面值的引用的值也跟着改变的情况。
如上例,我们定义完a与b的值后,再令a=4;那么,b不会等于4,还是等于3。
在编译器内部,遇到a=4;时,它就会重新搜索栈中是否有4的字面值,如果没有,重新开辟地址存放4的值;如果已经有了,则直接将a指向这个地址。
因此a值的改变不会影响到b的值。
另一种是包装类数据,如Integer,String,Double等将相应的基本数据类型包装起来的类。
这些类数据全部存在于堆中,Java用new()语句来显示地告诉编译器,在运行时才根据需要动态创建,因此比较灵活,但缺点是要占用更多的时间。
举例如下:Java代码public class Test{public static void main(String[]args){int a1=1;int b1=1;int c1=2;int d1=a1+b1;Integer a=1;Integer b=2;Integer c=3;Integer d=3;Integer e=321;Integer f=321;Long g=3L;System.out.println(a1==b1);//true结果1System.out.println(c1==d1);//true结果2System.out.println(c==d);//true结果3System.out.println(e==f);//false结果4}}分析:结果1:a1==b1如上面所述,会在栈中开辟存储空间存放数据。