Java 面试题 001 - 010

001、基本数据类型有几种?

  • byte:长 1 字节,8 比特,表示范围 -2^7 ~ 2^7-1
  • char:长 2 字节,16 比特
  • short:长 2 字节,16 比特,表示范围 -2^15 ~ 2^15-1
  • int:长 4 字节,32 比特,表示范围 -2^31 ~ 2^31-1
  • float:长 4 字节,32 比特
  • long:长 8 字节,64 比特,表示范围 -2^63 ~ 2^63-1
  • double:长 8 字节,64 比特
  • boolean:boolean 只有两个值 true 和 false,可以使用 1 bit 来存储,但没明确规定具体大小。JVM 会在编译时将 boolean 类型的数据转换为 int 类型,使用 1 来表示 true,0 表示 false,此时占 4 个字节;但对于 boolean 类型的数组 JVM 会将其编译成 byte 数组,每个 boolean 类型元素占 1 个字节。

002、基本数据类型的包装类?

每种基本类型都有对应的包装类型,基本类型与其对应的包装类型之间的赋值使用自动装箱与拆箱完成。

所有包装类都被声明为 final,都不可被继承。

Integer a = 66; // 自动装箱 调用了 Integer.valueOf(66)
int b = a; // 自动拆箱 调用了 a.intValue()

003、包装类的缓存池?

new Integer(123) 与 Integer.valueOf(123) 的区别在于:

  • new Integer(123) 每次都会新建一个对象
  • Integer.valueOf(123) 会使用缓存池中的对象,多次调用会取得同一个对象的引用
Integer a1 = new Integer(123);
Integer a2 = new Integer(123);
System.out.println(a1 == a2); // false
System.out.println(a1.equals(a2)); // true
Integer a3 = 123; // 相当于调用 Integer.valueOf(123)
Integer a4 = 123; // 相当于调用 Integer.valueOf(123)
System.out.println(a3 == a4); // true
System.out.println(a3.equals(a4)); // true

valueOf() 方法的实现比较简单,就是先判断值是否在缓存池中,如果在的话就直接返回缓存池的内容。

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

在 Java 8 中,Integer 缓存池的大小默认为 -128 ~ 127。

Integer a5 = 1239;
Integer a6 = 1239;
System.out.println(a5 == a6); // false
System.out.println(a5.equals(a6)); // true

基本类型对应的缓冲池如下:

包装类范围
Booleantrue 和 false
Byte-128 ~ 127,byte 类型所有数据
Short-128 ~ 127
Integer-128 ~ 127
Long-128 ~ 127
Character\u0000 ~ \u007F,所有字符
Float
Double

004、谈谈你对 String 字符串的理解?

String 类被声明为 final,因此不可被继承。

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
    private final char value[];
}    

在 Java8 中,String 内部使用被 final 修饰的 char 类型数组 value 来存储数据,这意味着 value 数组初始化之后不能再引用其他数组,同时 String 内部没有提供改变 value 数组的方法,因此可以保证 String 不可变。

005、String 类型变量不可变的好处?

  1. 可以缓存 hash 值。因为 String 的 hash 值经常被使用,例如 String 用做 HashMap 的 key。不可变的特性可以使得 hash 值也不可变,因此只需要进行一次计算。

  2. String Pool 的需要。如果一个 String 对象已经被创建过了,那么就会从字符串常量池中取得引用。如果字符串是可变的,则使用一个引用更改字符串将导致其他引用的值错误,只有字符串是不可变的,才可能使用 String Pool。

    java-string-pool

  3. 线程安全。String 的不可变性使其天生就是线程安全的,可以在多个线程中安全地使用。

006、String, StringBuffer and StringBuilder 对比?

1. 可变性

  • String 不可变
  • StringBuffer 和 StringBuilder 可变

2. 线程安全

  • String 不可变,因此是线程安全的
  • StringBuilder 不是线程安全的
  • StringBuffer 内部使用 synchronized 进行同步,是线程安全的

3. 使用场景

  • 在读多写少的场景下,推荐使用 String
  • 在读少写多且不需要考虑线程安全的情况下,推荐使用 StringBuilder
  • 在读少写多且需要考虑线程安全的情况下,必须使用 StringBuffer

007、字符串常量池?

设计思想

  • 字符串的分配,和其他的对象分配一样,耗费高昂的时间与空间代价,作为最基础的数据类型,大量频繁的创建字符串,极大程度地影响程序的性能
  • JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化
    • 为字符串开辟一个字符串常量池,类似于缓存区
    • 创建字符串常量时,首先坚持字符串常量池是否存在该字符串
    • 存在该字符串,返回引用实例,不存在,实例化该字符串并放入池中
  • 实现的基础
    • 实现该优化的基础是因为字符串是不可变的,可以不用担心数据冲突进行共享
    • 运行时实例创建的全局字符串常量池中有一个表,总是为池中每个唯一的字符串对象维护一个引用,这就意味着它们一直引用着字符串常量池中的对象,所以,在常量池中的这些字符串不会被垃圾收集器回收

具体解析

  • 直接使用双引号声明出来的 String 对象会直接存储在字符串常量池中。
  • 如果使用 new String("abc") 方式创建字符串对象,首先会判断字符串常量池中是否存在该字符串,若不存在就会先在字符串常量池中创建当前字符串对象,然后在堆中 new 一个 String(“abc”) 对象。
  • intern() 会直接返回该字符串在字符串常量池中对应字符串对象的引用。
String str1 = "abc";
String str2 = "abc";
String str3 = "abc";
System.out.println(str1 == str2); // true
String str4 = new String("abc");
String str5 = new String("abc");
String str6 = new String("abc");
System.out.println(str4 == str5); // false
System.out.println(str1 == str4); // false
str4 = str4.intern();
System.out.println(str1 == str4); // true

367903008-5951291f92d71_articlex

在 Java7 之前,String Pool 被放在运行时常量池中,它属于永久代(方法区的一种实现);而在 Java7 之后,String Pool 被移到堆中。这是因为永久代的空间有限,在大量使用字符串的场景下会导致 OutOfMemoryError 错误。

String str = new String(“abc”);这句话创建了几个字符串对象?

创建 1 或 2 个字符串对象。如果字符串常量池中已存在字符串常量“abc”,则只会在堆空间创建一个字符串常量“abc”。如果字符串常量池中没有字符串常量“abc”,那么它将首先在字符串常量池中创建,然后再在堆空间中创建,因此将创建总共 2 个字符串对象。

在堆中创建的对象到底有没有引用常量池中的常量?

不确定,我认为是引用了的。

008、final 的作用?

  1. 修饰变量

    如果变量是基本类型,final 使其数值不可变;如果变量是引用类型,final 使其引用不可变,也就是说该引用不能再指向其他对象,但是该应用所指向的对象本身是可以改变的。

  2. 修饰方法

    声明该方法不可以被子类重写。

    private 方法隐式地被指定为 final,如果在子类中定义的方法和基类中的一个 private 方法名相同,此时子类的方法不是重写父类方法,而是在子类中定义了一个新的方法。

  3. 修饰类

    声明当前类不可以被继承。

009、static 的作用?

  1. 修饰变量

    被 static 修饰的变量叫做静态变量,又称为类变量,也就是说这个变量属于类,类所有的实例都共享静态变量,可以直接通过类名来访问它。静态变量在内存中只存在一份。

  2. 修饰方法

    被 static 修饰的方法叫做静态方法,和静态变量一样,静态方法也属于类,可以通过类名直接访问,但静态方法不能是抽象的,并且只能访问所属类的静态字段和静态方法,静态方法中不能有 this 和 super 两个关键字(因为这两个关键字都与具体对象关联)。

  3. 修饰代码块

    被 static 修饰的代码块叫做静态代码块,静态代码块只在类初始化时执行一次。

  4. 修饰内部类

    被 static 修饰的内部类叫做静态内部类,静态内部类与非静态内部类之间最大的区别是:非静态内部类在编译完成之后会隐含地保存着一个引用,该引用指向创建它的外围类;而静态内部类却没有,没有这个引用就意味着它的创建不需要依赖外围类的创建,并且它不能使用任何外围类的非 static 成员变量和方法

    public class OuterClass {
        class InnerClass {
        }
        static class StaticInnerClass {
        }
        public static void main(String[] args) {
            // InnerClass innerClass = new InnerClass(); // 'OuterClass.this' cannot be referenced from a static context
            OuterClass outerClass = new OuterClass();
            InnerClass innerClass = outerClass.new InnerClass();
            StaticInnerClass staticInnerClass = new StaticInnerClass();
        }
    }
    
  5. 静态导包

    静态导包格式为:import static 。这两个关键字连用可以指定导入某个类中的指定静态资源,并且不需要使用类名调用类中静态成员,可以直接使用类中静态成员变量和成员方法。

    举个例子,没使用静态导包前代码如下:

    public class Demo {
        public static void main(String[] args) {
            int max = Math.max(6, 9);
            System.out.println(max);
        }
    }
    

    使用静态导包后代码如下:

    import static java.lang.Math.*;
    public class Demo {
        public static void main(String[] args) {
            int max = max(6, 9);
            System.out.println(max);
        }
    }
    

010、静态代码块、普通代码块、构造方法初始化顺序?

静态代码块和静态变量优先于普通代码块和实例变量初始化,静态代码块和静态变量的初始化顺序取决于它们在代码中的顺序,最后才是构造方法的初始化,初始化顺序如下:

  • 父类(静态代码块、静态变量)
  • 子类(静态代码块、静态变量)
  • 父类(普通代码块、实例变量)
  • 父类(构造方法)
  • 子类(普通代码块、实例变量)
  • 子类(构造方法)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值