八股文_常规_java

1,098 阅读6分钟

基础篇

基本类型

  • byte、short、int、long、float、double、char、boolean
  • int, float占用4个字节,long, double占用8个字节
  • String不是基本数据类型

java对象头

  • 对象实例分为三个部分:对象头、实例数据和对齐填充
  • 对象头由Mark Word、指向类的指针以及数组长度 组成

hashCode

根据对象的内存地址生成一个哈希值,两个对象可能产生同一hashcode

transient

被该变量修饰的关键字不会被序列化,当对象被反序列化时也不会被恢复

equals

  • ==:基本类型比较的是变量值,引用类型比较的是内存地址

  • 一没有重写equals方法,默认采用的是 ==,重写了equals方法按重写的规则来

    public class test1 {
     public static void main(String[] args) {
         String a = new String("ab"); // a 为⼀个引⽤
         String b = new String("ab"); // b 为另⼀个引⽤
         String aa = "ab"; // 放在常量池中
         String bb = "ab"; // 从常量池中查找
         
         if (a == b) // false,⾮同⼀对象
         System.out.println("a==b");
         
         if (aa == bb) // true
         System.out.println("aa==b");
       }
    }
    

字符串常量池

  • String s1 = "hello"会直接将字符串创建在常量池中

  • String s2 = new String("hello")会在堆上创建一个对象,如果常量池中没有hello,也会在常量池中一个hello对象

  • 地址比较

    String s1 = "11";
    String s2 = "11";
    System.out.println(s1==s2); // true
    
    String s3 = new String("11");
    System.out.println(s1==s3); // false
    

StringBuffer & StringBuilder

  • String是final修饰的,每次操作都会产生新的String对象
  • StringBuffer是线程安全的,每个方法都加了synchronized
  • StringBuilder线程不安全

jdk8新特性

  • 元空间替代了永久代
  • 引入lambda、Stream、CompletetableFuture

final

  • 修饰类:表示类不可被继承
  • 修饰方法:表示方法不可被子类覆盖,但是可以重载
  • 修饰变量:如果是基本数据类型的变量,则其数值⼀旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另⼀个对象

final、finally、finalize

  • final:修饰的类不能被继承,修饰的方法不能被重写,修饰的变量不可被重新赋值
  • finally:用于try-catch中释放资源
  • finalize:对象被jvm回收前会执行这个方法

重载和重写

  • 重写:子类重写父类方法

  • 重载:只和方法名、参数有关,方法名和参数加在一起计算方法签名,只要签名不同就不会报错

自动拆箱和装箱

装箱:调用valueOf()将基本数据类型转换成对象,如 Integer.valueOf(int)

拆箱:将包装类型转换为基本数据类型;如:Integer.intValue()

深拷贝和浅拷贝

深拷贝会重新创建一个对象然后指向它;浅拷贝传递的是对象的地址,两个变量会指向同一对象

反射

动态的获取类的结构信息,如字段、方法、构造函数,动态创建对象,调用对象的属性、方法

泛型

  • 在编译时进行类型检查,确保不会出现类型不匹配问题,比如一个集合类型是List<String>,如果向集合里面添加Integer类型的数据就会编译时报错

  • 泛型擦除:只有编译的时候泛型有效,运行时泛型的类型信息会被擦除,变为Object

抽象类

  • 抽象类被abstract修饰,不能被new,其他都和正常类一样
  • 抽象方法不能有方法体,有抽象方法的类必须是抽象类
  • 抽象方法要么在子类中被重写,要么在子类也是抽象类

接口和抽象类区别

  • 接口中不能有构造函数,抽象类中可以有
  • 一个类可以implements多个接口,但只能extends一个抽象类;接口本身可以通过extends扩展多个接口

静态方法和实例方法

静态方法访问本类的成员时,只允许访问静态成员变量、方法,而不允许访问非静态的成员变量和方法

对象实例化过程

  • 父类的静态成员变量和静态代码块
  • 子类的静态成员变量和静态代码块
  • 父类成员变量和构造块
  • 父类的构造函数
  • 子类成员变量和构造块
  • 子类的构造函数

异常类层次结构

  • 异常:空指针、数组索引越界、类找不到
  • Error:内存溢出、栈溢出

Throwable的方法:

  • public string getMessage():返回简要描述
  • public string toString() :返回详细信息
  • public void printStackTrace() :在控制台上打印

集合篇

集合.jpg

Collection

  • ArrayList 扩容机制: 初始容量是10,当ArrayList中元素数量超过当前容量时,新建一个数组,容量是原来的1.5倍,然后将原来数组中的元素复制到新数组中
  • Arraylist & Vector: 两者结构一样,但 Vector是线程安全的,所有方法都加了synchorized

Map

  • TreeMap:基于红黑树实现的Map集合,key可以按照自定义的规则进行排序

  • HashMap

    • HashMap的结构是数组+链表+红黑树,HashMap使用key的hashcode()计算哈希值,当发生哈希冲突时,节点被添加到链表中;
    • 默认容量是16,负载因子是0.75,当存储的元素超过16 * 0.75 = 12个时会促发扩容操作,每次扩容容量x2,并重新计算所有元素的哈希值,并移动到新的数组中
    • 当链表长度超过8,链表会转换为红黑树;如果树中元素数量低于6,红黑树会转换回链表,红黑树能够在最坏情况下将查找的复杂度从O(n)降低到O(log n)
    • LinkedHashMapHashMap的基础上给每个节点增加了前后指针,节点间形成了一个双向链表,可以记录元素的插入顺序
  • HashSet

    • 将要放入的对象作为key,调用HashMap方法,判断两个对象key是否一样,如果一样对象重复,添加失败;如果key不一样,添加成功
    • HashMap判断key重复:判断hashcode()和equals()
  • HashMapHashSet

    • HashMap允许null键和null值,HashSet也允许存null

    • HashSet底层使用HashMap实现的,在构造器中是new 了 HashMap,大部分方法都是调用的HashMap中的方法

      private static final Object PRESENT = new Object();
      
      public HashSet()  { map = new HashMap<>();}
      
      public boolean add(E e) {
          return map.put(e, PRESENT)==null;
      }
      
  • HashMapHashTable

    • HashMap允许存null的键或值,HashTable不允许
    • HashMap线程不安全,HashTable线程安全(大部分方法加了synchronized修饰)
  • ConcurrentHashMapHashTable

    • HashTable与ConcurrentHashMap都能保证线程安全,但HashTable性能较低
    • HashTable是在方法上进行加锁,所有的操作都用一个锁,效率低;ConcurrentHashMap采用的是CAS+synchorized,如果某个节点为空则通过CAS插入数据,如果不为空则使用synchorized锁定链表头节点