Java中内存分析:
1.栈(Stack):存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new出来的对象)或者常量池中(字符串常量对象存放在常量池中)。
2.堆(heap):存放所有new出来的对象。
3.常量池(constant pool):在堆中分配出来的一块存储区域,存储显式的String常量和基本类型常量(float、int等)。另外,也可以存储不经常改变的东西(public static final)。常量池中的数据可以共享。
4.静态存储:存放静态成员(static定义的)。
示例分析
1).
String a = "abc";①
String b = "abc";②
分析:
①代码执行后在常量池(constant pool)中创建了一个值为abc的String对象
②执行时,因为常量池中存在"abc"所以就不再创建新的String对象了。
2).
String c = new String("xyz");①
String d = new String("xyz");②
分析:
①Class被加载时,"xyz"被作为常量读入,在常量池(constant pool)里创建了一个共享的值为"xyz"的String对象;然后当调用到new String("xyz")的时候,会先去常量池中查找是否有"xyz"对象,如果没有则在常量池中创建一个此字符串对象,然后再在堆中创建一个此常量池中"xyz"对象的拷贝,最后在堆(heap)里创建这个new String("xyz")对象;
②由于常量池(constant pool)中存在"xyz"对象,所以不用再在常量池中创建"xyz",直接在堆里创建新的new String("xyz")对象。
3).
String s1 = new String("xyz"); //创建二个对象(常量池中"xyz"常量对象和堆中new String("xyz")对象),一个引用(栈中s1引用堆中new String("xyz")对象)
String s2 = new String("xyz"); //创建一个对象(堆中),并且以后每执行一次同样赋值操作均创建一个对象,一个引用
String s3 = "xyz";//创建一个对象(常量池中"xyz"常量对象),一个引用
String s4 = "xyz";//不创建对象(共享上次常量池中的数据),只是创建一个新的引用
4). intern()
java.lang.String的intern()方法"abc".intern()方法的返回值还是字符串"abc",表面上看起来好像这个方法没什么用处。但实际上,它做了个小动作:检查字符串池里是否存在"abc"这么一个字符串,如果存在,就返回池里的字符串;如果不存在,该方法会把"abc"添加到字符串池中,然后再返回它的引用。
String s1 = "Monday";//在常量池中创建"Monday"对象
String s2 = new String("Monday").intern();//在堆中创建new String("Monday")对象后,并未使用该对象,而是继续检查常量池中是否存在"Monday"对象,并返回常量池中的"Monday"对象给s2,此时s1,s2指向同一对象。
5). 而对于基础类型的变量和常量,变量和引用存储在栈中,常量存储在常量池中
int a1 = 1, a2 = 2, a3 = 3;
public static final int INT1 = 1;
public static final int INT2 = 1;
public static final int INT3 = 1;
6). Java中的装箱和拆箱。
在JDK1.5之前,我们要实现基本类型和包装类之间的转换,大多是通过包装类提供的方法,Integer i = Integer.valueOf(5)或者int j = i.intValue()来做互相转换的。JDK1.5之后,编译器会在我们做赋值操作(这里所说的赋值操作不包括构造函数)的时候帮我们自动完成基本类型和包装类之间的相互转换。包装类是类,是对象,而基本类型是有值的“变量”,包装类的实例(对象)创建在堆上,而基本类型创建在栈上。包装类作为类,可以容纳更多的信息。包装类都实现了Comparable接口,可以实现对象之间的比较,所以包装类之间的比较尽量用compareTo,而不是><=这些运算符。上面我们所说的赋值操作,基本上可以分为两种情况,一种是显式赋值,另一种是隐式赋值。
显式赋值,我们可以理解为有赋值符号出现的情况,比如,Integer i = 11;这里编译器会自动的帮你完成把11这个int的基本类型装箱成包装类实例的工作,这样就不用我们再手动的转换了。
隐式赋值,就是没有赋值符号出现的情况,比如方法调用时候的参数传递,比如我们有一个接受Integer类型参数的方法(void method(Integer i)),我们在调用的时候可以直接传递一个基本类型的实参进去(method(5)),这时候编译器会自动的将我们传递进去的实参装箱成包装类,从而完成对方法的调用。在方法调用的时候,在内存中的栈上,首先会为方法的形参创建内存区域,然后将我们传递进去的实参赋值给形参,就是在这时,发生了赋值操作,才会让编译器完成装箱操作。当然,方法执行完后,栈上有关形参的内存区域会被回收。
还有我们要记住一点,就是编译器的装箱/拆箱原则,就是基本类型可以先加宽(比如int转换为long),再转变成宽类型的包装类(Long),但是不能直接转变成宽类型的包装类型。比如我们有个方法 void method(longl),我们通过method(5)是可以调用该方法的,因为int类型的5被加宽成了long类型;但是如果这个方法变成void method(Long
l),我们通过method(5)就不能调用它了,因为int类型的5是不能直接转换成Long包装类的,切记。
最后还有一个值得注意的地方,就是上面我们提到的显示赋值的情况下,比如Integer i = 11的情况,实际上在源码中是调用到了Integer的静态方法valueOf(),在这一块,java的设计者采用了cache pool的设计模式,在Integer这个类里面有一个长度为256的静态Integer数组,里面存储了从-128到127的Integer对象,当我们传递进去的参数在这个范围之内时,这个方法会返回该数组中实际保存的Integer对象实例,只有超过该范围时,才会返回new出来的Integer对象实例。
7).综合示例
分享到:
相关推荐
包装类的主要作用是将基本数据类型转换为对象,以满足Java中只针对对象设计的API和新特性的需求,如泛型、接口等。在面向对象编程中,包装类提供了更多的功能和便利。 二、包装类的使用 1. 自定义包装类:虽然Java...
学习如何创建和使用类及对象,是理解Java面向对象编程的关键。 2. **封装**:封装是面向对象的核心特性之一,它隐藏了对象内部的实现细节,只对外提供公共接口进行交互。通过访问修饰符(public, private, ...
首先,String类的实例在内存中是不可变的,这意味着一旦创建了一个String对象,就不能更改其内容。例如: ```java String A = "koter"; // 创建一个字符串对象A String B = new String("koter"); // 创建一个新的...
一个空的String对象占用28个字节的内存,包含了一个指向字符数组的引用、一个偏移量、一个字符的长度和一个哈希码。当字符串内容非空时,内存占用会增加,因为需要存储字符数据。例如,字符串"ab"会占用28 + 2 * 2 =...
另一方面,所有引用类型,如包装类(Integer、String、Double等),以及自定义的类对象,都存储在堆中。这是因为它们的大小和生命周期可能在程序运行过程中发生变化,堆的动态内存管理特性正好满足这一需求。 #### 字...
Java 中的 String 型是一个特殊的包装类数据,它具有不可变性。什么是不可变性呢?简单来说,就是 String 对象一旦被创建,不能被修改。那么,为什么 String 对象不能被修改呢?这就需要从 Java 的内存分配机制说起...
在Java中,基本数据类型和它们对应的包装类对象之间可以自动转换,这种转换过程被称为自动装箱和拆箱。当进行自动装箱操作时,JVM会为基本数据类型创建相应的包装类对象。 在实际开发中,选择合适的对象创建方式...
Java字符串池和字符对象是Java编程中的重要概念,主要涉及到String类的特性和内存管理。在Java中,String是一个不可变类,这意味着一旦创建了一个String对象,它的值就不能被修改。这种不变性使得String对象在多线程...
#### 三、Java内存分配中的栈与堆 **3.1 栈中的内存分配** 栈主要用来存储局部变量、方法参数等,它的特点是先进后出(FILO)。当方法调用时,局部变量和参数被压入栈中;当方法结束时,它们被弹出栈。 **3.2 堆...
在Java编程中,面向对象的特征主要包括四个核心概念:抽象、继承、封装和多态性。 1. 抽象:抽象是将复杂问题简化的过程,它关注对象的主要特征,忽略次要细节。抽象分为过程抽象(如方法)和数据抽象(如类)。...
Java虚拟机内存分配主要涉及到两种主要的内存区域:堆(Heap)和栈(Stack)。这两种内存区域在程序执行过程中有着不同的角色和管理策略。 栈内存主要用于存储基本类型变量(如int、float等)和对象的引用。在函数...
**包装类**是Java为基本数据类型提供的类封装,它们使得基本类型可以作为对象使用,支持序列化、反射等高级功能。此外,Java还提供了内部类的概念,允许在一个类的内部定义另一个类,增强代码的组织性和封装性。 ##...
5. **基本数据类型包装类**:Java中的每个基本数据类型都有对应的包装类,如Integer、Double等。这些包装类允许将基本类型转换为对象,以便在需要对象的地方使用,如Vector的`add()`方法。在示例中,`args`数组中的...
但包装类的变量则完全可以当成对象使用,它具有面向对象的特征,包装类的变量可以被赋为 null。 此外,本资源还涵盖了 Java 集合框架、注解、泛型、输入/输出、多线程、网络通信、反射、内存管理等相关内容,是 ...
String是一个特殊的包装类数据,可以用两种形式来创建:String str = new String("abc");和String str = "abc";。第一种形式是用new()来新建对象的,它会在堆中创建一个新的对象。第二种形式是先在栈中创建一个对...
包装类(Wrapper Class)是为每个基本数据类型提供对应的类,如`Integer`对应`int`,它们提供了额外的功能,如自动装箱和拆箱。 在面向对象设计中,继承允许子类继承父类的属性和方法,实现代码复用和扩展。多态性...
String类是Java中最常用的类之一,它表示不可变的字符序列。一旦一个String对象被创建,它的值就不能被改变。这种设计带来了很多优点: - **线程安全性**:因为String对象的不变性,多个线程可以安全地共享一个...
3. `Integer`类:作为`int`的包装类,提供了将整数与字符串之间转换的便利方法,以及一些数学运算和比较操作。研究`Integer`源码有助于理解自动装箱拆箱的过程。 4. `ArrayList`类:基于数组实现的动态列表,允许在...
在Java中,对于基本类型的包装类(如`Boolean`和`Integer`),应尽量避免使用`new`关键字创建实例。这是因为Java为`Boolean`和`Integer`提供了缓存机制,用于提高性能和减少内存消耗。 - **对于`Boolean`**:Java为...