java基础

一.Integer

1.1使用Integer a = int..声明变量

Integer x = 2;     //打上断点
Integer a = 2;
Integer m = 200;
Integer q = 200;
      

进行debug的时候,发现默认调用了,Integer.valueOf(int i)方法,也就是自动装箱的体现

    public static Integer valueOf(int i) {
        //判断i的值是否在缓存池的范围内,如果存在就存缓存池中获取对象
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        //超过了范围则新创建一个新的对象
        return new Integer(i);
    }

二.String

2.1String不同定义方式的内存分析图

String str1 = “abc”;
String str2 = “abc”;
String str3 = “abc”;
String str4 = new String(“abc”);
String str5 = new String(“abc”);
image

2.2String.intern()方法在jdk版本不同的一些区别
2.2.1String.intern() in JDK6
Jdk6中常量池位于PermGen(永久代)中,PermGen是一块主要用于存放已加载的类信息和字符串池的大小固定的区域。执行intern()方法时,若常量池中不存在等值的字符串,JVM就会在常量池中创建一个等值的字符串,然后返回该字符串的引用。除此以外,JVM 会自动在常量池中保存一份之前已使用过的字符串集合。Jdk6中使用intern()方法的主要问题就在于常量池被保存在PermGen中:首先,PermGen是一块大小固定的区域,一般不同的平台PermGen的默认大小也不相同,大致在32M到96M之间。所以不能对不受控制的运行时字符串(如用户输入信息等)使用intern()方法,否则很有可能会引发PermGen内存溢出;其次String对象保存在Java堆区,Java堆区与PermGen是物理隔离的,因此如果对多个不等值的字符串对象执行intern操作,则会导致内存中存在许多重复的字符串,会造成性能损失。

2.2.2String.intern() in JDK7
Jdk7将常量池从PermGen区移到了Java堆区,执行intern操作时,如果常量池已经存在该字符串,则直接返回字符串引用,否则复制该字符串对象的引用到常量池中并返回。堆区的大小一般不受限,所以将常量池从PremGen区移到堆区使得常量池的使用不再受限于固定大小。除此之外,位于堆区的常量池中的对象可以被垃圾回收。当常量池中的字符串不再存在指向它的引用时,JVM就会回收该字符串。可以使用 -XX:StringTableSize 虚拟机参数设置字符串池的map大小。字符串池内部实现为一个HashMap,所以当能够确定程序中需要intern的字符串数目时,可以将该map的size设置为所需数目*2(减少hash冲突),这样就可以使得String.intern()每次都只需要常量时间和相当小的内存就能够将一个String存入字符串池中。

一些特殊的情况:

        String s3 = new String("1") + new String("1");
        String s5 = s3.intern();
        String s4 = "11";
        System.out.println(s5 == s3);  //false
        System.out.println(s5 == s4);  //true
        System.out.println(s3 == s4);  //false

依据上面的说法,String s = new String("1");这行代码做的事情是;JVM首先在字符串池中查找有没有"1"这个字符串对象,可知在字符串常量池中没有“1”,则首先在字符串池中创建一个"1"字符串对象,然后再在堆中创建一个"1"字符串对象,然后将堆中这个"1"字符串对象的地址返回赋给s1引用,这样,s1指向了堆中创建的这个"1"字符串对象;String s3 = s1.intern();先来看一下JDk1.7及其之后的情况,首先会去字符串常量池中检查池是否包含一个等于“1”的字符串,因为在字符串常量池中已经有字符串“1”对象,所以此时s3指向的是方法区中的对象;String s2 = "1";这句代码首先会去字符串的常量池中查否包含一个等于“1”的字符串,因为字符串常量池中已经存在方法区中了,所以s2指向的是字符串常量池中的对象。

答案就非常好理解了s1 == s2为false ,s2 == s3为true ,s1 == s3为false;

【1】
String s0 = "HelloWorld";
String s1 = "World";
String s2 = "Hello" + s1;
System.out.println((s0 == s2));  //结果为false

【2】
String s0 = "HelloWorld";
final String s1 = "World";
String s2 = "Hello" + s1;
System.out.println((s0 == s2)); //结果为true

【3】
String s0 = "HelloWorld";
final String s1 = getWorld();
String s2 = "Hello" + s1;

System.out.println((s0== s2)); //结果为false

private static String getWorld() {
    return "World";
}

分析:

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

【2】和【1】中唯一不同的是bb字符串加了final修饰,对于final修饰的变量,它在编译时被解析为常量值的一个本地拷贝存储到自己的常量池中或嵌入到它的字节码流中。所以此时的”Hello” + s1和”Hello” + “World”效果是一样的。故上面程序的结果为true。

【3】JVM对于字符串引用s1,虽然它为final类型,但是它的值在编译期无法确定,只有在程序运行期调用方法后,将方法的返回值和”Hello”来动态连接并分配地址给s2,故上面程序的结果为false。

对字符串的操作,”abc”+”def”则会三个字符串对象,第三个是”abcdef”。也就是说,在Java中对字符串的一切操作,都会产生一个新的字符串对象。这是正确的,String创建字符串时不可变的,任何对String的改变都会引发新的String对象的生成,而那些不用的未被引用的对象将会作为垃圾回收。

三.java是值传递

3.1基本数据类型的参数

先来看一下基本数据类型的参数按值传递的例子:

public class pass {
    public static void main(String[] args) {
        int i =5;
        System.out.println("调用方法前+ i =" + i);
        change(i);
        System.out.println("调用方法后 i =" + i);
    }

    private static void change(int x){
        x = 2;
    }


}

结果:

调用方法前+ i =5
调用方法后 i =5

分析:当i作为参数传递给change()方法的形参的时候,肉眼可见的觉得是将i整个变量都是传入给形参变量x一样,但是事实是在调用这个方法之后,i的值却没有改变过.原因:由于在传递的时候,传递进来的是变量i的一个副本,也就是说拷贝一份内容而已,当在change方法中不管怎么样修改变量,对i的值是不会改变的

3.2引用类型的参数传递

public class TransferTest2 {

    public static void main(String[] args) {
        Person person = new Person();
        System.out.println("执行方法前的person = "+person);
        change(person);
    }

    public static void change(Person p){
        p = new Person();
    }


}

class Person{

        }

结果是两次输出的地址都一样的

这个原理也和上面那种情况一样,传入的形参是对person的值的一个拷贝,也就是说是一个引用类型的地址,在执行到change()方法中,p 本来值是传入参数的副本,但是现在p有new了一个对象,p的引用地址就改变了,也就是说不再指向传入参数的堆对象,接下来进行的所有操作都不会影响到person对象


内存分析.png

四.对象的复制

clone:

你可能感兴趣的:(java基础)