java内存简单总结

来源:互联网 发布:2017年大数据市场规模 编辑:程序博客网 时间:2024/06/06 10:03

栈中用来存放一些原始数据类型的局部变量数据和对象的引用(String,数组.对象等等)但不存放对象内容

堆中存放使用new关键字创建的对象.


public class Cat {
    private double weight;
    private int age;

    public Cat(double _weight, int _age) {
        this.weight = _weight;
        this.age = _age;
    }

    public double getWeight() {
        return weight;
    }

    public int getAge() {
        return age;
    }

    public void setWeight(double weight) {
        this.weight = weight;
    }

    public void setAge(int age) {
        this.age = age;
    }

}

 

当new对象时:
 Cat kitty = new Cat(3.0, 2);

 Cat构造函数定义了两个形参,也就是局部变量,在我们试图通过new向它传实参值时, 在栈内存中定义了两个引用变量,_weight,_age并赋值,即两个引用变量所在栈内存中内容分别为3.0,2,同时在堆中开辟了一块新空间, 空间中有两个内存区域,而内存中内容为系统初始化内容。 然后通过构造函数将栈内存中值赋给堆内存中值,就这样堆内存中新空间就被赋值了,接下来就像大家所想的那样将引用kitty指向这块新空间。 做完上面的事情后,局部变量将从栈中弹出,这个过程是自发的。

 

Cat kitty = new Cat(3.0, 2);

        new Seg().change(kitty);
        System.out.println(kitty.getAge());

    public void change(Cat _cat) {

        _cat.setAge(5);
  }
new出kitty时,在栈内存中定义了一个引用,指向堆中分配的内存,堆内存存放kitty的信息。

然后,我们开始调用change方法了,在我们把kitty传给该方法时,在栈内存中就定义了局部变量_cat,该引用变量将被指向kitty所指向的堆内存区域,
  然后_cat对它指向的堆内存区域内容进行了更改,这不就是kitty所指堆内存区域被更改了吗?因为它们指向同一块堆内存。就这样,调用kitty.getAge()时,将得到5这样的结果。最后,分配出来的_cat将从栈中弹出。

 

String是一个特殊的引用类型的数据类型,可以用:

  String str = new String("abc");

  String str = "abc";

两种的形式来创建,第一种是用new()来新建对象的,它会在存放于堆中。每调用一次就会创建一个新的对象。而第二种是先在栈中创建

一个对String类的对象引用变量str,然后通过符号引用去字符串常量池 里找有没有"abc",如果没有,则将"abc"存放进字符串常量池 ,

并令str指向”abc”,如果已经有”abc” 则直接令str指向“abc”。

 

关于equals()和==:

  这个对于String简单来说就是比较两字符串的Unicode序列是否相当,如果相等返回true;而==是 比较两字符串的地址是否相同,也

就是是否是同一个字符串的引用。

 

        String s4 = "abc";
        String s5 = "abc";
        System.out.print(s4.equals(s5)); // true
        System.out.print(s4 == s5); //true

 对“abc”的值,实际上是在class文件被JVM装载到内存当中就已经为“abc”这个字符串在常量池的CONSTANT_String_info表中分配了空间来存储“abc”这个值。既然“abc”
         这个字符串常量存储在常量池中,常量池是属于类型信息的一部分,类型信息也就是每一个被转载的类型,
         这个类型反映到JVM内存模型中是对应存在于JVM内存模型的方法区中,也就是这个类型信息中的常量池是存在于在方法区中,方法区可以在一个堆中自由分配。
         这也就说明了为什么s4==s5,因为它们俩都是指向常量池中“abc”串的引用,
         而像new出来的String在新分配的堆内存中内容“abc”,只是常量池中“abc”串的拷贝。所以,请大家不要用new方法来初始化String类型,直接赋值就可以了。

 

        String s6 = "kvill";
        String s7 = "kvill";
        String s8 = "kv" + "ill";
        System.out.println(s6 == s7); // true
        System.out.println(s6 == s8);// true

 首先,我们要知结果为道Java 会确保一个字符串常量只有一个拷贝。 因为例子中的 s6和s7中的”kvill”都是字符串常量,它们在编译期就被确定了,所以s6==s7为true;而”kv”和”ill”也都是字符串常量,当一个字 符串由多个字符串常量连接而成时, 它自己肯定也是字符串常量,所以s2也同样在编译期就被解析为一个字符串常量,所以s2也是常量池中”kvill”的一个引用。所以我们得出s6==s7==s8;用new String() 创建的字符串不是常量,不能在编译期就确定,所以new String() 创建的字符串不放入常量池中,它们有自己的地址空间。

 

        String s9 = "kvill";
        String s10 = new String("kvill");
        String s11 = "kv" + new String("ill");
        System.out.println(s9 == s10);
        System.out.println(s9 == s11);
        System.out.println(s10 == s11);

 s9还是常量池 中"kvill”的应用,s10因为无法在编译期确定,所以是运行时创建的新对象”kvill”的引用, s11因为有后半部分newString(”ill”)所以也无法在编译期确定,所以也是一个新创建对象”kvill”的引用;明白了这些也就知道为何得出此结果了。

 

        final StringBuffer a = new StringBuffer("111");

        final StringBuffer b = new StringBuffer("222");

        //   a=b;//此句编译不通过

        a.append("222");// 编译通过
final只对引用的"值"(即内存地址)有效,它迫使引用只能指向初始指向的那个对象,改变它的指向会导致编译期错误。至于它所指向的对象 的变化,final是不负责的。

 

        String a1 = "a1";
        String b1 = "a" + 1;
        System.out.println((a1 == b1));

        String c1 = "atrue";
        String d1 = "a" + "true";
        System.out.println((c1 == d1));

        String e1 = "a3.4";
        String f1 = "a" + 3.4;
        System.out.println((e1 == f1));
JVM对于字符串常量的"+"号连接,将程序编译期,JVM就将常量字符串的"+"连接优化为连接后的值,拿"a" +
1来说,经编译器优化后在class中就已经是a1。在编译期其字符串常量的值就确定下来,故上面程序最终的结果都为true。

 

        String str1 = "ab";
        String str2 = "b";
        String str = "a" + str2;
        System.out.println((str1 == str));

JVM对于字符串引用,由于在字符串的"+"连接中,有字符串引用存在,而引用的值在程序编译期是无法确定的,即"a"+bb无法被编译器优化,只有在程序运行期来动态分配并将连接后的新地址赋给str2。所以上面程序的结果也就为false。

 

 

        String t1 = "ab";
        final String t2 = "b";
        String t3 = "a" + t2;
        System.out.println((t1 == t3)); // result = true

 对于final修饰的变量,它在编译时被解析为常量值的一个本地拷贝存储到自己的常量 池中或嵌入到它的字节码流中。所以此时的"a" +bb和"a" +"b"效果是一样的。故上面程序的结果为true。

 

    public static String getBB() {
        return "b";
    }

        String aa = "ab";
        final String bbb = getBB();
        String bb = "a" + bbb;
        System.out.println((aa == bb));


JVM对于字符串引用bb,它的值在编译期无法确定,只有在程序运行期调用方法后,将方法的返回值和"a"来动态连接并分配地址为str2,故上面 程序的结果为false。

 

  字符串是一个特殊包装类,其引用是存放在栈里的,而对象内容必须根据创建方式不同定(常量池和堆).有的是编译期就已经创建好,存放在字符串常 量池中,而有的是运行时才被创建.使用new关键字,存放在堆中。

 


 

原创粉丝点击