java 基础知识面试题(持续更新)

1.权限修饰符 public ,protected ,默认, private  的区别是?

      当前类  相同包下  子类  项目中其他包下

public      √     √     √      √ 

protected   √     √     √      ×

默认     √     √     ×      ×

private    √        ×     ×        × 

2.java中的基本数据类型有哪些? String 属于基本类型吗?

 答:java中基本类型有8种 , byte short,int ,long ,float , double ,char ,boolean  ,String 属于引用类型,不属于基本类型

      整数类型: byte, short ,int ,log          

      byte (1字节,8位 取值范围:-128 ~ 127) ,short(2字节,16 位,很少用) ,int(4字节,32位最常用)  ,long(8字节,64位)            

数值类型

       小数类型:  float(4字节 32位) , double (8字节,64位)

字符类型  : char   占2个字节,16位 ; 单引号引起的字符,可以为中文

布尔类型  : boolean   值为true 或者 false 

java中默认的整数类型是 int , 默认的小数类型是double

类型的自动提升:

例如:

double d = 1L;//类型小转大,可以自动提升

long l = 1.0; //类型大转小,只能强转

3. int 与 Integer 的区别是什么?

答:1、Integer是int的包装类,int则是java的一种基本数据类型 
2、Integer变量必须实例化后才能使用,而int变量不需要 
3、Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值 
4、Integer的默认值是null,int的默认值是0

拓展: int 与 Integer 的比较 , Integer 与 Integer 的比较

//情形1 Integer 与 int类型比较
Integer i   =   1;
int     j   =   1;
System.out.println(i == j);


//情形2 Integer 与 Integer 比较(不通过new 的形式)
Integer i1 =   100;
Integer i2 =   100;
System.out.println(i1 == i2);


//情形3 Integer 与 Integer 比较(不通过new 的形式)
Integer i3 =   128;
Integer i4 =   128;
System.out.println(i3 == i4);


//情形4 Integer 与 Integer 通过new
Integer i5 =   new Integer(110);
Integer i6 =   110;
System.out.println(i5 == i6);


//情形5 Integer 与 Integer 通过new
Integer i7 =   new Integer(120);
Integer i8 =   new Integer(120);
System.out.println(i7 == i8);

最终执行结果:

true
true
false
false
false

分析:

//写在前面 ,对于 == ,int 类型之间的比较,比较的是值, Integer对象之间比较的是内存地址

/*
情形一:
Integer 与 int 的比较 , 会将Integer 拆箱成 int 类型的值再做比较,所以最终是两个int类型的比较. 而两个int类型比较的是字面值,所以结果为true
*/

/*
情形二:
Integer i1 = 100;这句最终执行的代码是 Integer i1 = Integer.valueOf(100);
此时查看Integer.valueOf(int)的源码:
*/
public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
}

//继续查看IntegerCache的源码:
 private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;
            //即相当于 new Integer[256];
            cache = new Integer[(high - low) + 1];
            int j = low;
            //最终cache[0] ~ cache[255] 等价于 -128 ~ 127
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }


/*
也就是说, i1 与 i2 都是从IntegerCache 数据中取出,且内存地址一致.所以结果为true;
*/

/*
情形三:
同上,仍然会调用Integer.valueOf()方法,由于不满足判断条件:
*/
if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
}
/*
所以 两次i3 ,i4 相当于分别执行了 new Integer(128); 而Integer 对象之间的比较,比较的是内存地址;
由于new 了两次,所以内存地址不同,故结果为false;
*/

/*
情形四:
参考情形二, i5,i6 相当于重新new了两次,内存地址不一致,故比较结果为false


情形五:        
同上,每次new 都会新开辟内存空间,故结果为false;

*/

4.String s = "Hello", s = "World" 此时s的值已经改变了, 为什么还说String 是不可变的?

答: 第一次 s = ''Hello'',相当于创建了 1个 String对象 ''Hello'' , 同时将该对象在内存中的引用地址赋给了变量s

而当执行 s = ''Hello''时,同样也会创建一个String对象 "World", 将此对线的地址赋给 变量s ,并打破原来的引用关系

也就是说,每次更改 s 的值都会新开辟一块内存空间,而不是修改原有内存空间中的值 ,所以说 String 是不可变的;

如何验证?

可以通过System.identityHashCode(obj)验证,该方法可以根据变量的内存地址计算出相应的hashCode值(官方API,并没有这么说):

String s = "Hello";
System.out.println(System.identityHashCode(s));
//s = "World";
System.out.println(System.identityHashCode(s));

此时执行的结果为:

1068824137
1068824137

不难发现,两次执行的结果是一致的,因此 s 引用的内存地址没有改变,  将上面的注释打开再执行,结果为:

1068824137
864237698

可以发现,两次的值不一致,可以直观的说明.s的内存地址已经发生了改变

除此之外,查看String 代码可以发现 String 是有 char[] 组成;

private final char value[];//等价于  private final char[] value; 只是写法不同而已

而char[] value 使用了private 与 final修饰 所以一旦初始化就不可以更改;

通过 内存地址 与 String中的成员变量 足以说明String是不可变的;

4.  String ,StringBuilder,StringBuffer 之间的区别是什么?

从拼接速度上来说:StringBuilder > StringBuffer > String

测试案例:

@Test
    public void testAppend() {
        long start_time = 0;
        long end_time = 0;


        start_time = System.currentTimeMillis();
        StringBuffer sf = new StringBuffer("str");
        for (int j = 0; j < 100000; j++) {
            sf = sf.append("str");
        }
        end_time = System.currentTimeMillis();
        System.out.println("StringBuffer 拼接总耗时:"+(end_time - start_time)+"ms");



        /*start_time = System.currentTimeMillis();
        StringBuilder sb = new StringBuilder("str");
        for (int j = 0; j < 100000; j++) {
            sb = sb.append("str");
        }
        end_time = System.currentTimeMillis();
        System.out.println("StringBuilder 拼接总耗时:"+(end_time - start_time)+"ms");*/


        /*start_time = System.currentTimeMillis();
        String s = "str";
        for (int j = 0; j < 100000; j++) {
            s += "str";
        }
        end_time = System.currentTimeMillis();
        System.out.println("String 拼接总耗时:"+(end_time - start_time)+"ms");*/
    }

同样执行10W次拼接str字符串,5次执行取平均耗时结果:

StringBuffer :4.4ms

StringBuilder:3.4ms

String:16.4ms

从结果来看 StringBuffer 与 StringBuilder的拼接速度远远大于String的拼接, 这是因为 StringBuffer与StringBuilder 的拼接是对同一对象进行操作,而String的拼接则是不停的开辟新的内存

StringBuilder 与 StringBuffer 的差异会随着拼接次数的增大而增大,有兴趣的伙伴可以动手试一下

从线程安全上来看:StringBuffer > StringBuilder

原因:StringBuffer 的 append方法 都加上了 synchronized关键字,实现了方法的同步,多线程下是安全的,而StringBuilder中在多线程中可能出现数据不一致的情况

猜你喜欢

转载自www.cnblogs.com/lzzRye/p/9452670.html