在java中给java 数组内存结构分配堆内存没错,可是为什么给对象也分配的是堆内存,java 数组内存结构元素很多可对象只有一个啊,

java中创建数组时怎么分配内存?_百度知道
java中创建数组时怎么分配内存?
int[] arr=new int[3];
上面创建int数组时,怎么分配内存的?arr是一个引用变量,是通过指针指向new int[3](存在堆里面)对吧,那么,栈里面为arr分配几个指针,是3个还是1个?
&&&&&&&&&&&&&&&&&&&&
疑问1****:
其实我就是想知道jav...
我有更好的答案
分配指针只有一个
不是arr[0]
也不是arr[1] 就是arr疑问2啊
java中数组指针指向头部元素
原来你知道指向第一个....
既然指向第一个了
后面方括号内的数字 就告诉你要跳过几个数据
比如arr[0] 就一个也不跳 直接从头读取一个元素
arr[1]就会跳过第一个再读取 至于跳多少 你定义的时候已经规定了类型疑问4啊 原来你都知道?问题5 int[]arr 就一个指针 arr我提个问题好么
下次提这么多问题
多弄点分行么.
我想你就是这么一个问题吧 其实不是基本类型的时候
变量变为引用变量
C++叫指针数组
也是和你的疑问3一样
通过索引到元素后
读取内部地址后再访问实例 也就是堆了关于括号内的
楼主所谓的内存变 其实不过是&引用变量的内存&变 分配的空间是没变的 说白就是改变一个引用的地址 也就是他的值
改变一个变量的值
有啥好奇怪的?
针对疑问4,我想问一下。也就是说当元素为对象时,栈里存储的指针不是指向第一个元素,而是指向一个“数”,这个“数”才是指向数组元素的指针(或则就是元素在堆中的存储地址)对吧?当数组元素有变动时,修改对应的“数”就行了,对吧??其实我也是这样认为的。只是放上来看看其他人的意见。
啊对对 就是修改地址 那个数
采纳率:71%
来自团队:
arr在这里这是个对象引用,里面存储的是指向堆里的对象的一个元素的地址这个不变,根据第一个地址和元素的索引计算获得堆里对应的元素的地址。arr只分配一个指针,那就是数组的第一个元素的地址
为您推荐:
其他类似问题
内存的相关知识
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。Java数组及其内存分配_Linux编程_Linux公社-Linux系统门户网站
你好,游客
Java数组及其内存分配
来源:Linux社区&
作者:xiaoxuetu
几乎所有的程序设计语言都支持数组。Java也不例外。当我们需要多个类型相同的变量的时候,就考虑定义一个数组。在Java中,数组变量是引用类型的变量,同时因为Java是典型的静态语言,因此它的数组也是静态的,所以想要使用就必须先初始化(为数组对象的元素分配空间)。
1.数组的初始化方式及其内存分配
对于Java数组的初始化,有以下两种方式,这也是面试中经常考到的经典题目:
静态初始化:初始化时由程序员显式指定每个数组元素的初始值,由系统决定数组长度,如:
1 //只是指定初始值,并没有指定数组的长度,但是系统为自动决定该数组的长度为4
2 String[] computers = {"Dell", "Lenovo", "Apple", "Acer"};  //①
3 //只是指定初始值,并没有指定数组的长度,但是系统为自动决定该数组的长度为3
4 String[] names = new String[]{"多啦A梦", "大雄", "静香"};  //②
动态初始化:初始化时由程序员显示的指定数组的长度,由系统为数据每个元素分配初始值,如:
1 //只是指定了数组的长度,并没有显示的为数组指定初始值,但是系统会默认给数组数组元素分配初始值为null
2 String[] cars = new String[4];  //③
前面提到,因为Java数组变量是引用类型的变量,所以上述几行初始化语句执行后,三个数组在内存中的分配情况如下图所示:
由上图可知,静态初始化方式,程序员虽然没有指定数组长度,但是系统已经自动帮我们给分配了,而动态初始化方式,程序员虽然没有显示的指定初始化值,但是因为Java数组是引用类型的变量,所以系统也为每个元素分配了初始化值null,当然不同类型的初始化值也是不一样的,假设是基本类型int类型,那么为系统分配的初始化值也是对应的默认值0。
对于多维数组,假设有这么一段代码:
1 int[][] nums = new int[2][2];2 nums[0][1] = 2;
那么他在内存中的分配情况如下:
由上图可知,并没有真正的多维数组,它的本质其实是一维数组。
相关资讯 & & &
& (09月20日)
& (04月04日)
& (12月03日)
& (06月17日)
& (03月01日)
   同意评论声明
   发表
尊重网上道德,遵守中华人民共和国的各项有关法律法规
承担一切因您的行为而直接或间接导致的民事或刑事法律责任
本站管理人员有权保留或删除其管辖留言中的任意内容
本站有权在网站内转载或引用您的评论
参与本评论即表明您已经阅读并接受上述条款下次自动登录
现在的位置:
& 综合 & 正文
Java的数组是静态的,即当数组被初始化之后,该数组的长度是不可变的,所谓初始化,就是为数组对象的元素分配内存空间,并为每个数组元素指定初始值。
静态初始化:初始化时由程序员显示指定每个数组元素的初始值,由系统决定数组长度。
动态初始化:初始化时程序员只指定数组长度,由系统为数组元素分配初始值
一旦初始化完成,数组的长度就不可改变。Java语言允许通过数组的length属性来访问数组的长度
执行动态初始化时,程序员只需指定数组的长度,即为每个数组元素指定所需的内存空间,系统将负责为这些数组元素分配初始值。
Java的数组变量是一种引用类型的变量,数组变量并不是数组本身,它只是指向堆内存中的数组对象。
Java的数组变量是引用类型的变量,它不是数组对象本身,只要让数组变量指向有效的数组对象,程序中即可使用该数组变量
数组变量只是一个引用变量,通常存放在栈内存中(也可以放入堆内存中);而数组对象就是保存在堆内存中的连续内存空间,对数组执行初始化,其实并不是对数组变量执行初始化,而是要对数组对象执行初始化—也就是为该数组对象分配一块连续的内存空间,这块连续内存空间的长度就是数组的长度
所有局部变量都是放在栈内存里保存的,不管其是基本类型的变量,还是引用类型的变量,都是存储在各自的方法栈区中,但引用类型变量所引用的对象(包括数组、普通Java对象)则总是存储在堆内存中
堆内存中的对象(不管是数组对象,还是普通的Java对象)通常不允许直接访问,为了访问堆内存中的对象,通常只能通过引用变量
NullPointerException.当通过引用变量来访问实例属性,或者调用非静态方法时,如果该引用变量还未引用一个有效的对象,程序就会引发NullPointerException。
引用类型数组的数组元素依然是引用类型的,因此数组元素里存储的还是引用,它指向另一块内存,这块内存里存储了该引用变量所引用的对象(包括数组和Java对象)
数组元素就是变量
Main方法声明的变量都属于局部变量,因此它们都被保存在main方法栈中,但数组元素则作为数组对象的一部分,总是保存在堆内存中,不管它们是基本类型的数组元素,还是引用类型的数组元素。
一个Object[]类型的数组,每个数组元素都相当于一个Object类型的引用变量,因此可指向任何对象(包括数组对象和普通Java对象)
Java内存管理分为两个方面:内存分配和内存回收。内存分配特指创建Java对象时JVM为该对象在堆内存中所分配的内存空间。内存回收指的是当该Java对象失去引用,变成垃圾时,JVM的垃圾回收机制自动清理该对象,并回收该对象所占用的内存。
局部变量分为三类:形参:在方法签名中定义的局部变量,由方法调用者为其赋值,随方法的结束而消亡。方法内的局部变量:在方法内定义的局部变量,必须在方法内对其进行显式初始化。这种类型的局部变量在初始化完成后开始生效,随方法的结束而消亡。代码块内的局部变量:在代码块内定义的局部变量,必须在代码块内对其进行显示初始化。这种类型的局部变量从初始化完成后开始生效,随代码块的结束而消亡。
局部变量都被存储在方法的栈内存中。
使用static修饰的成员变量是类变量,属于该类本身;没有使用static修饰的成员变量是实例变量,属于该类的实例。在同一个JVM中,每个类只对应一个Class对象,但每个类可以创建多个Java对象。同一个JVM内的一个类的类变量只需一块内存空间;但对于实例变量而言,该类每创建一次实例,就需要为实例变量分配一块内存空间。即程序中有几个实例,实例变量就需要几块内存空间。
类也是对象,所有类都是Class的实例。每个类初始化完成之后,系统都会为该类创建一个对应的Class实例,程序可以通过反射来获取某个类所对应的Class实例。Class.forName(“Person”)
对于实例变量而言,它属于Java对象本身,每次创建Java对象时都需要为实例变量分配内存空间,并执行初始化。
实例变量属于Java类本身,只有当程序初始化该Java类是才会该类的类变量分配内存空间,并执行初始化。
当创建任何Java对象时,程序总会先依次调用每个父类非静态初始化块、父类构造器(总是从Object开始)执行初始化,最后才调用本类的非静态初始化块、构造器执行初始化。
Super调用用于显式调用父类的构造器,this调用用于显式调用本类中另一个重载的构造器。Super和this调用都只能在构造器中使用,而且super调用和this调用都必须作为构造器的第一行代码。
子类的方法可以访问父类的实例变量,这是因为子类继承父类就会获得父类的成员变量和方法,但父类的方法不能访问子类的实例变量,因为父类根本无从知道它将被哪个子类继承,它的子类将会增加怎样的成员变量。
构造器只是负责对Java对象实例变量执行初始化,在执行构造器代码之前,该对象所占的内存以及被分配下来,这些内存里值都默认是空值----对于基本类型的变量,默认的空值就是0或false;对于引用类型的变量,默认的空值就是null。
This在构造器中时,this代表正在初始化的Java对象
当子类方法重写了父类方法之后,父类表面上只是调用属于自己的、被子类重写的方法,但随着执行context的改变,将会变成父类实际调用子类的方法。
Java程序允许某个方法通过return this;返回调用该方法的Java对象,但不允许直接return super;甚至不允许直接将super当成一个引用变量使用。
类变量属于类本身,而实例变量则属于Java对象;类变量在类初始化阶段完成初始化,而实例变量则在对象初始化阶段完成初始化
Final可以修饰变量,被final修饰的变量被赋初始值之后,不能重新对它重新赋值;final可以修饰方法,被final修饰的方法不能被重写;final可以修饰类,被final修饰的类不能派生子类
被final修饰的实例变量必须显式指定初始值,而且只能在三个位置指定初始值:1.定义final实例变量时指定初始值;在非静态初始化块中为final实例变量指定初始值;在构造器中为final实例变量指定初始值
Java会缓存所用曾经用过的字符串直接量。例如执行String a = “java”;语句之后,系统的字符串池中就会缓存一个字符串”java”;如果程序再次执行String b = “java”;系统将会让b直接指向字符串池中的”java”字符串。因此a==b将会返回true。
Set代表一种集合元素无序、集合元素不可重复的集合,Map则代表一种由多个key—value对组成的集合。Map集合是Set集合的扩展。
Map集合的key具有一个特征,所有key不能重复,key之间没有顺序。Set(K) keyset()
Map集合的所有key将具有Set集合的特征。对于Map而言,相当于key-value的Set集合。
HashSet,系统采用Hash决定集合元素的存储位置,这样可以保证快速存取集合元素;HashMap,系统将value当成key的附属,系统根据Hash算法来决定key的存储位置,这样可以保证快速存取集合key。而value总是紧随key存储。
Java集合实际上是多个引用变量所组成的集合,这些引用变量指向实际的Java对象。
HashSet判断两个对象相等的标准除了要求通过equals()方法返回true之外,还要求两个对象的hashCode()返回值相等。
TreeSet底层采用以后NavigableMap来保存TreeSet集合的元素。
对于TreeMap而言,采用一种被称为”红黑树”的排序二叉树来保存Map中每个Entry----每个Entry都被当成”红黑树”的一个节点对待。以后每向TreeMap中放入一个key-value对,系统都需哟啊将该Entry当成一个新节点,添加到已有红黑树中,通过这种方式就可保证TreeMap中所有key总是由小到大地排列。
TreeMap添加元素、取出元素的性能都比HashMap低。TreeMap中的所有Entry总是按key根据指定排序规则保持有序状态,TreeSet中所有的元素总是根据指定排序规则保持有序状态。
Map集合是一个关联数组,它包含两组值:一组是所有key组成的集合,因为Map集合的key不允许重复,而且Map不会保存key加入的顺序,因此这些key可以组成一个Set集合;另外一组是value组成的集合,因为Map集合的value完全可以重复,而且Map可以根据key来获取对应的value,所以这些value可以组成一个List集合。
Map接口提供了get(K key)方法允许Map对象根据key来取得value;List接口提供了get(int index)方法允许List对象根据元素索引来取得value
ArrayList使用了transient修饰了elementData数组。这保证系统序列化ArrayList对象时不会直接序列化elementData数组,而是通过ArrayList提供的writeObject、readObject方法来实现定制序列化;但对于Vector而言,它没有使用transient修饰elementData数组,而且Vector只提供了一个writeObject方法,并未完全实现定制序列化。从序列化机制的角度来看,ArrayList的实现比Vector的实现更安全。
List代表一种线性表的数据结构,ArrayList则是一种顺序存储的线性表。ArrayList底层采用数组来保存每个集合元素,LinkedList则是一种链式存储的线性表。
ArrayList性能总体优于LinkedList
Iterator是一个迭代器接口,它专门用于迭代各种Collection集合,包括Set集合和List集合
对于JVM的垃圾回收机制来说,是否回收一个对象的标准在于:是否还有引用变量引用该对象?只要有引用变量引用该对象,垃圾回收机制就不会回收它。
程序创建一个对象,并把这个对象赋给一个引用变量,这个引用变量就是强引用。
程序在运行过程中会不断地分配内存空间,那些不再使用的内存空间应该即时回收它们,从而保证系统可以再次使用这些内存,如果存在无用的内存没有被回收回来,那就是内存泄露。
垃圾回收机制主要完成:跟踪并监控每个Java对象,当某个对象处于不可达状态时,回收该对象所占用的内存;清理内存分配、回收过程中产生的内存碎片。
当需要使用字符串,还有Byte、Short、Integer、Long、Float、Double、Boolean、Character包装类的实例时,程序不应该采用new的方式来创建对象,而应该直接采用直接量来创建它们。
使用StringBuilder和StringBuffer进行字符串连接,如果程序使用多个String对象进行字符串连接操作,在允许时将生产大量临时字符串。
尽量少用静态变量,如果某个对象呗static变量所引用,那么垃圾回收机制通常是不会回收这个对象所占的内存。
String java
= new String(“crazyjava”);创建了两个字符串对象,一个是”crazyjava”直接量对应的字符串对象,一个是new String()构造器返回的字符串对象。
Java程序整创建对象的常规方式:1.通过new调用构造器创建Java对象;2.通过Class对象的newInstance()方法调用构造器创建Java对象;3.通过Java的反序列化机制从IO流中恢复Java对象;4. 通过Java对象提供的clone()方法复制一个新的Java对象。
对于字符串以及Byte、Short、Integer、Long、Float、Double、Boolean、Character这些基本类型的包装类,Java还允许以直接量的方式来创建Java对象。
对于Java程序中的字符直接量,JVM会使用一个字符串来保存它们;当第一次使用某个字符串直接量时,JVM会将它们放入字符串池进行缓存。在一般情况下,字符串池中字符串对象不会被垃圾回收,当程序再次需要使用该字符串时,无需重新创建一个新的字符串,而是直接让引用变量指向字符串池中已有的字符串。
如果程序需要比较两个字符串是否相同,用==进行判断就可以了;但如果要判断两个字符串所包含的字符序列是否相同,则应该使用String重写过的equals()方法进行比较。
当一个算术表达式中包含多个基本类型的值时,整个算术表达式的数据类型将发生自动提升:所有byte型、short型和char型将被提升到int型。
Java多线程:1.继承Thread类来创建线程类,重写run()方法作为线程执行体;2.实现Runnable接口来创建线程类,重写run()方法作为线程执行体;3.实现Callable接口来创建线程类,重写call()方法作为线程执行体
任何线程进入同步方法、同步代码块之前,必须先获取同步方法、同步代码块对于的同步监视器
Switch语句的表达式而言, 只能人如下5种数据类型:byte、short、int、char、enum。绝不能是String类型。
构造器是Java每个类都会提供的一个”特殊方法”。构造器负责对Java对象执行初始化操作,不管是定义实例变量时指定的初始值,还是在非静态初始化块中所做的操作,实际都会被提取到构造器中被执行。
构造器并不创建Java对象,构造器只是负责执行初始化,在构造器执行之前,Java对象所需要的内存空间,应该说是由new关键字申请出来的。
对于使用private修饰符修饰的方法,只能在当前类中访问到该方法,子类无法访问父类定义的private方法。所以子类无法重写父类的该方法。如果子类中定义了一个与父类private方法具有相同方法名、相同形参列表、相同返回值类型的方法,依然不是重写,只是在子类中重新定义了一个新方法。
非静态内部类不能拥有静态成员
被static修饰的成员(Field、方法、内部类、初始化块、内部枚举类)属于类本身,而不是单个的Java对象。静态方法属于类,而不是属于Java对象。
静态内部类不能访问外部类的非静态成员。
当系统执行到程序中catch块代码里的return语句时,依然会去执行对应的finally快。
不论try块是正常结束,还是中途非正常地退出,finally块确实都会执行。但是如果System.exit(0)将停止当前线程和所有其他当场死亡的线程。Finally块不能让已经停止的线程继续执行。
只要Java虚拟机不退出,不管try块是否正常结束,还是遇到异常非正常退出,finally块总会获得执行的机会。
当Java程序执行完try块,catch块时遇到了return语句,return语句会导致该方法立即结束。系统执行完return语句之后,并不会立即结束该方法,而是去寻找该异常处理流程中是否包含finally块,如果没有finally块,方法终止,返回相应的返回值。如果有finally块,系统立即开始执行finally块----只有当finally块执行完成后,系统才会再次跳回来根据return语句结束方法。如果finally快里使用了return语句来导致方法结束,则finally块已经结束了方法,系统将不会跳回去执行try块,catch块里的任何代码。
在try块后使用catch块来捕捉多个异常时,程序应该小心多个catch块之间的顺序:捕捉父类异常的catch块都应该在捕捉子类异常的catch块之后。
子类重写父类方法时,子类方法只能声明跑出父类方法所声明抛出的异常的子类。
线性表是由n(n&=0)个数据元素(节点)a1, a2, a3….an组成的有限序列
遍历二叉树指的是按某种规律依次访问二叉树的每个节点,对二叉树的遍历过程就是讲非线性结构的二叉树中的节点排列在一个线性序列上的过程。
排序是对一组任意的数据元素(或记录)经过排序操作后,就可以把它们变成一组按关键字排序的有序序列。
【上篇】【下篇】java中数据在内存中的状态
时间: 00:07:23
&&&& 阅读:275
&&&& 评论:
&&&& 收藏:0
标签:&&&&&&&&&&&&&&&&&&&&&&&&&&&此文转自csdn,看完瞬间就明白了
首先,我们知道,Java中的数据类型分为两种,基本数据类型和引用数据类型。而基本数据类型,为什么不直接使用他们的包装类呢,例如Integer、Long等等呢?下面是Thinking in Java 中的解释:
有 一系列类需特别对待;可将它们想象成&基本&、&主要&或者&主&(Primitive)类型,进行程序设计时要频繁用到它们。之所以要特别对待,是由于 用new创建对象(特别是小的、简单的变量)并不是非常有效,因为new将对象置于&堆&里。对于这些类型,Java采纳了与C和C++相同的方法。也就 是说,不是用new创建变量,而是创建一个并非句柄的&自动&变量。这个变量容纳了具体的值,并置于堆栈中,能够更高效地存取。
Java决定了每种主要类型的大小。就象在大多数语言里那样,这些大小并不随着机器结构的变化而变化。这种大小的不可更改正是Java程序具有很强移植能力的原因之一。
可以看到,Sun是为了系统的优化,才采用基本的数据类型。
那么,基本数据类型和引用数据类型到底有什么区别呢 ?
下面依然是Thinking in Java中的话:
程序运行时,我们最好对数据保存到什么地方做到心中有数。特别要注意的是内存的分配。有六个地方都可以保存数据:(1) 寄存器。这是最快的保存区域,因为它位于和其他所有保存方式不同的地方:处理器内部。然而,寄存器的数量十分有限,所以寄存器是根据需要由编译器分配。我们对此没有直接的控制权,也不可能在自己的程序里找到寄存器存在的任何踪迹。(2) 堆栈。驻留于常规RAM(随机访问存储器)区域,但可通过它的&堆栈指针&获得处理的直接支持。堆栈指针若向下移,会创建新的内存;若向上移,则会释放那些内存。这是一种特别快、特别有效的数据保存方式,仅次于寄存器。创建程序时,Java编译器必须准确地知道堆栈内保存的所有数据的&长度&以及&存在时间&。这是由于它必须生成相应的代码,以便向上和向下移动指针。这一限制无疑影响了程序的灵活性,所以尽管有些Java数据要保存在堆栈里&&特别是对象句柄,但Java对象并不放到其中。(3) 堆。一种常规用途的内存池(也在RAM区域),其中保存了Java对象。和堆栈不同,&内存堆&或&堆&(Heap)最吸引人的地方在于编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要在堆里停留多长的时间。因此,用堆保存数据时会得到更大的灵活性。要求创建一个对象时,只需用new命令编制相关的代码即可。执行这些代码时,会在堆里自动进行数据的保存。当然,为达到这种灵活性,必然会付出一定的代价:在堆里分配存储空间时会花掉更长的时间!(4) 静态存储。这儿的&静态&(Static)是指&位于固定位置&(尽管也在RAM里)。程序运行期间,静态存储的数据将随时等候调用。可用static关键字指出一个对象的特定元素是静态的。但Java对象本身永远都不会置入静态存储空间。
(5) 常数存储。常数值通常直接置于程序代码内部。这样做是安全的,因为它们永远都不会改变。有的常数需要严格
地保护,所以可考虑将它们置入只读存储器(ROM)。
从上面我们就可以看到,基本数据类型的变量,是存放在堆栈中的,我更喜欢直接称之为栈(stack)。而引用数据类型的引用,也就是Thinking in Java所说的句柄,也是直接放在栈里面,而引用数据类型引用的对象,则是放在堆(heap)中的
明白了基本数据类型和引用数据类型在内存中的区别,那么理解下面的问题就和简单了。
一、String对象的创建问题ss
这本是一个简单的问题,无关轻重,大家用天天使用字符串也用的很happy,你们真的了解字符串吗?
大家知道,String的创建有两种形式,一种是直接放在双引号("")中,一种是直接用new的方式创建,那么这两种方式有什么区别呢?
1、String a="abc";
2、String b=new String("abcdef")
上面的两行代码,分别创建了几个对象呢?
还是先说说JVM对字符串是怎么处理的吧。字符串是一个引用数据类型,根据我们上面对java中基本数据类型和引用数据类型的
分析,String对象肯定是存放在堆中的。关键在于,String对象是一个final的常量,JVM为了管理String和高效性,在堆中,会用一个
特殊的字符串池来保存字符串直接量。
所谓的字符串直接量,就是指字符串由字符串、数值常量直接构成,没有变量,也没有方法。
String a="abc";
String b="abc"+10;
String c="abc"+"def";
这样的字符串,JVM在编译的时候就能完全确定这个字符串,就可以在字符串池中创建该字符串,如果字符串池中已经有这样的
String,就让该变量直接指向在字符串池中的该字符串。
而另外一些不是字符串直接量的字符串
String a="abc"+"abc".lenght();
String b=a+"asc";
JVM无法在编译的时候就确定该字符串,就只能在程序运行期间,在堆中非字符串池中的地方开辟内存,存放这样的字符串。
好了,明白JVM对字符串的处理,在来谈谈String对象创建的问题:
(这两行代码是无关的)
1、String a="abc";
2、String b=new String("abcdef");
上面的两个创建字符串的方法到底分别创建了几个对象?
第1行,创建了一个对象,它是一个字符串直接量,直接被创建存放在字符串池中。
第2行,创建了2个对象,"abcdef"本身就是一个字符串直接量,被创建后存放在字符串池中,然后,它又作为一个参数,
传递给了构造器new String(),这样又在堆中非字符串池的地方开辟了块内存,创建了一个字符串对象。
那么,就在问去几个问题,下面的几段代码分别创建了几个对象
1、String a="ab";
2、String b="ab";
3、String d="abcd";
4、String c=a+"cd";
5、String e=new String("abcd")
6、String f="aaa"+"bbb";
第1行,1个、因为字符串池还是空的,不存在字符串"ab",所以在字符串池创建了一个对象"ab"。
第2行,0个、因为字符串池中"ab"已经存在,所以没有创建,直接将b指向"ab";
第3行,1个、因为也是在字符串池中创建了一个"abcd"对象
第4行,2个、先在字符串池中创建"cd",因为采用了变量,所以要在堆中非字符串池中在创建一个对象a+"cd";
第5行,1个、因为字符串池中已经存在"abcd",直接将他作为一个参数传给构造器new一个新的对象。
第6行,1个,因为在编译的时候就能确定f的值,所以只创建一个"aaabbb"到字符串池中;(很多人误解为3个)
在看下面的
final String a="aaa";
final String b="bbb";
String c=a+b;
字符串c对被创建在哪里,字符串池,还是堆中的其他地方,答案是字符串池中,因为a、b是常量,不是变量。
二、String和StringBuffer的区别在Java中,String和StringBuffer,StringBuilder都能表示一个字符串。String的定义是:表示一个字符串常量。那么,看下面的一段代码:String str="abc";str="def";常量,基本数据类型的常量是不可变的,可是我们可以看到,这里所谓的字符串常量str是可变的。这是为什么呢?引用数据类型的常量,指的是引用的对象不可变,而引用,也即句柄是可变的。也就是多,当一个String类型的字符串改变时,实际上是它指向的内存单元发生了改变,原来的内存单元的内容并没有发生改变。 StringBuffer的定义是:表示一个字符串变量。StringBuffer strbuf="abc";这样的初始化是错误的,要想初始化一个StringBuffer类型的字符串必须使用new来初始化,如
由此可见,StringBuffer字符串变量,指的是不仅引用可变,引用的对象也是可变的。下面的两个例子可以很好的说明这个问题。1、
String str1="abc";
String str2=str1;
StringBuffer str3=new StringBuffer("abc");
StringBuffer str4=str3;
str1=str1+str2;
str3=str3.append(str4);
System.out.println(str1);
System.out.println(str2);
System.out.println(str3);
System.out.println(str4);输出的结果:abcabcabcabcabcabcabc2、public class Test5{
public static void main(String[] args)
String str1="java";
StringBuffer str2=new StringBuffer("java");
test(str1);
test(str2);
System.out.println(str1);
System.out.println(str2);
public static void test(String str)
str=str.replace("j", "i");
public static void test(StringBuffer str)
str.append("c");
}}程序的输出结果为javajavac(String对象的处理确实与其他的对象有所区别,在深度克隆中也有体现,对它的处理更像是对一个基本数据类型的处理。这主要就是因为它是一个常量的原因。
三、==和equals的问题==的含义,如果是比较基本数据类型,那么就是比较数据类型字面值的大小。如果是比较引用数据类型,就是比较它们在内存地址上是否是相同的。而equals方法,是Object的方法之一,所有的java类都有这个方法,区别只是自己有没有重写的问题。如果没有重写,那么也是直接比较内存地址是否相同。重写了,那就要看它们是怎么重写的。看下面的例子
String str1 = "abc";
String str2 = "abc";
String str3=new String("abc");
String str4=new String("abc");
StringBuffer str5=new StringBuffer("abc");
StringBuffer str6=new StringBuffer("abc");
System.out.println("1:"+(str1==str2));
System.out.println("2:"+(str1.equals(str2)));
System.out.println("3:"+(str2==str3));
System.out.println("4:"+(str2.equals(str3)));
System.out.println("5:"+(str3==str4));
System.out.println("6:"+(str3.equals(str4)));
//System.out.println("7:"+(str4==str5));
System.out.println("8:"+(str4.equals(str5)));
System.out.println("9:"+(str5==str6));
System.out.println("10:"+(str5.equals(str6)));输出结果是1:true2:true3:false4:true5:false6:true8:false9:false10:false解释:String对象的初始化有两种方式,前面已经解释了。可见,str1和str2表示的"abc"都是存放在字符串池中,而在字符串池中,这两个"abc"其实是一个内存中的数据,所以str1==str2是true。str1.equals(str2)是true。str3和str4采用的new方式,那么它们对用的字符串"abc"都是在堆中非字符串池中,分别存放在堆中不同的地方,所以str2==str3是false。str3==str4是false。而String和Strinbuffer除了都是直接继承Object之外,并没有其他的直接联系,两者完全是不相干的类。所以才有str4==str5是false、str4.equals(str5)是false。而StringBuffer也根本没有重写从父类继承的equals方法,所以str5==str6是false。str5.equals(str6)是false。
四、数组的问题数组也是一个引用数据类型,Java中的数组是静态的,也就是说,一旦数组初始化完成指定了它的长度,就不能在去改变它的长度。可是,数组也是和String一样的,引用的对象不可变,但是引用是可变的int[] a=new int[5];a代表的是引用,new int[5]才代表引用的对象。也就是说new int[5]这个真正代表数组的对象在内存里面是不能改变所占内存的大小,但是,却可以去改变数组变量a的引用,指向别的数组对象。在来说下数组在Java中的内存分配问题。数组的引用是放在栈中的,而引用的对象是放在堆中的,不管这个数组中的元素是基本数据类型,还是引用数据类型。只要记住一点:在Java中,所有局部变量都是放在栈里面保存的,不管是基本数据类型的变量,还是引用数据类型的变量,都是存储在各自的方法栈区;但引用类型变量所引用的对象,都是放在堆内存中的。值得一说的是,如果是一个引用类型的数组,例如String[] str=new String[6];引用str存放在栈里面,而str指向的堆内存中也是引用,它说指向的引用才真正的指向堆内存中的字符串。
标签:&&&&&&&&&&&&&&&&&&&&&&&&&&&原文:http://www.cnblogs.com/ccharp/p/4617415.html
教程昨日排行
&&国之画&&&& &&&&&&
&& &&&&&&&&&&&&&&
鲁ICP备号-4
打开技术之扣,分享程序人生!}

我要回帖

更多关于 java数组占用内存大小 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信