2020Java笔试/面试题(持续收集整理更新)

说明:

本篇用于收集知识点方便随时巩固,持续更新与纠错。

关于JDK版本,若无特殊说明,默认为JDK 1.8,。

关于JVM版本,若无特殊说明,默认为 HotSpot。

目录

一、Java 基础

1.1 Java中的基本数据类型有哪几种?各占用多少字节?

1.2 String 在Java中是个特殊的存在,谈谈?

1.3 谈谈 String、StringBuffer、StringBuilder 的异同。

1.4 ArrayList、Vector、LinkedList 的异同?

1.5 讲讲类的实例化过程中静态变量、成员变量等的加载顺序

1.6  HashMap 你用过吗?说说你对它的认识?

未完,待续。。。

最后


一、Java 基础

1.1 Java中的基本数据类型有哪几种?各占用多少字节?

答:Java 中的基本数据类型有8种。其中:

  • 数值型
    • 整数类型
      • byte:1字节8位有符号整数
      • short:2字节16位有符号整数
      • int:4字节32位有符号整数
      • long:8字节64位有符号整数
    • 浮点类型
      • float:4字节32位浮点数
      • double:8字节64位浮点数
  • 字符型
    • char:2字节16位Unicode字符
  • 布尔型
    • boolean:1字节8位

1.2 String 在Java中是个特殊的存在,谈谈?

答:

Sting类可以序列化。String实现了序列化接口,所以是可以被序列化与反序列化的。

String对象之间可以进行比较。实现了Comparable接口中的compareTo()方法,所以字符串之间是可以进行比较的(是根据字符串中每个字符的ASCII码进行比较),如下图源码。

String类不可以被继承。因为String 是一个被final所修饰的类。

String是不可变的。String类底层实际存储数据的是:private final char value[]; 也就是说 String类底层维护的是一个字符序列。这个 char 类型数组被 final 所修饰,所以一经创建,就不可修改。

String类重写了equals() 方法。先比较两个字符串的地址是否相同,是则直接返回true;如果地址不相同,再看两个字符串内容是否相同,是的话也返回true。

String可以用字面量方式创建。String 是一个类,与其他类不同的是,String可以使用字面量的形式创建或赋值。

String str = "123";
/*
1 先到常量池去通过String的equals()方法去找是否存在字符串"123",
    如果存在,直接将地址返回(这也保证了常量池中的字符串常量都是唯一的)。
2 如果常量池中不存在"123",则先在常量池中创建一个"123"的字符序列,然后再将其地址返回。
*/


String str = new String("123");
/*
1 先到常量池去通过String的equals()方法去找是否存在字符串"123",
    如果不存在,就在常量池中创建一个。
    (所以说在new字符串的时候,如果常量池中不存在,实际上会创建两次。)
2 在堆中开辟空间,创建一个Sting 对象,并返回其地址。
*/

String常量存储在方法区中的常量池,new出来的对象存储在堆内存中

字符串之间可以使用 + 号拼接。拼接产生的结果是第三个字符串,进行拼接的字符串本身是没有发生任何变化的。

String 的本地方法 intern() 可以从常量池中获取字符串。如果常量池中没有,就先创建,再将其地址返回。

1.3 谈谈 String、StringBuffer、StringBuilder 的异同。

这三个类 都被 final 锁修饰,不允许被继承。

从线程安全的角度讲:

  • String 实例对象是不可变的字符串常量,不存在线程安全问题,即String是线程安全的。
  • StringBuilder 没有使用锁机制,在多线程并发的情况下,可能会出现线程安全问题。
  • StringBuffer 中涉及到修改底层数据的方法,都加了 synchronized 关键字,所以StringBuffer 是线程安全的。

从对象可变性角度讲

  • String 底层维护的是一个被final 锁修饰的字符数组,不可变。
  • StringBuffer 和 StringBuilder 底层维护的是一个可变的字符数组,所以其对象实例是可变的。

从频繁修改的性能角度

  • String对象本身是不可变的,对字符串修改,实际上是在产生新的对象,因此修改效率低下。
  • StringBuilder 底层维护的是一个可变的字符数组,可以通过扩容等机制,实现对字符序列的修改。因此修改效率较高。
  • StringBuffer 底层维护的也是一个可变的字符数组,但与StringBuilder不同的是,StringBuffer中的修改操作都被加了锁,获取锁、释放锁、阻塞等因素导致了StringBuffer的性能方面可能会比StringBuilder低。
    在 JDK 1.6 之后,对synchronized 进行了一些锁优化,其中的“锁消除”优化,会使得StringBuffer在某种绝对安全的情况下忽略方法上加的锁,从而其性能会有所提升。

其他方面

  • StringBuffer 的 toString() 方法会对字符序列进行缓存,以减少元素复制的开销,而 StringBuilder 则是直接复制。从而从某种程度上说,推荐使用StringBuffer。
  • StringBuffer 与 StringBuilder 的初始容量、扩容机制等
    • 初始容量:16
    • 扩容:
      • 扩容至当前的两倍加2 :int newCapacity = value.length * 2 + 2;
      • 如果长度还是不够,那么实际需要多长,就扩容至多长:
        if (newCapacity - minimumCapacity < 0)
                    newCapacity = minimumCapacity;

1.4 ArrayList、Vector、LinkedList 的异同?

Vector

  • 底层存储数据的是Object类型的数组。
  • new Vector() 时,初始大小为10
  • 每次扩容至原来的两倍(可以设定增长因子capacityIncrement)
  • 是线程安全的
  • 优点:底层数据结构是数组,查找效率高。
  • 缺点:插入、删除元素等操作效率低,且由于是线程安全的,所以较ArrayList来说整体效率较低。

ArrayList

  • 底层数据存储是Object类型的数组
  • new ArrayList() 时,底层数据指针指向一个空数组;
  • 第一次添加元素时,将数据指针指向长度为10的数组。
  • 每次扩容至原来的1.5倍
  • 是线程不安全的,在多线程操作下,可能抛出:ConcurrentModificationException(并发修改异常)
  • 优点:效率略高于 Vector ,可以在单线程下使用。
  • 缺点:线程不安全,数组的缺点它都有。

LinkedList

  • 双向链表,底层存储的是 Node 类型的节点链表
  • 线程不安全,可能会抛出:ConcurrentModificationException
  • 优点:插入、删除节点很容易
  • 缺点:查找元素效率较前面两个低

1.5 讲讲类的实例化过程中静态变量、成员变量等的加载顺序

1、父类静态变量

2、父类静态代码块

3、子类静态变量

4、子类静态代码块

5、父类成员变量

6、父类构造代码块

7、父类构造方法

8、子类成员变量

9、子类构造代码块

10、子类构造方法

代码验证

public class Main {
    public static void main(String[] args) {
        Son son = new Son();
        System.out.println("====================");
        Son son2 = new Son();
    }
}

class Parent{
    public static int a = 1;
    private int aa = 10;

    static {
        System.out.println("父类的静态代码块加载了,在此之前静态变量a已经加载了,a = " + a);
    }

    {
        System.out.println("父类的构造代码块加载了,在此之前成员变量aa已经加载了: aa = " + aa);
    }
    public Parent() {
        System.out.println("父类的构造方法加载了");
    }
}

class Son extends Parent{
    public static int b = 2;
    private int bb = 20;

    public Son() {
        System.out.println("子类的构造方法加载了");
    }

    static {
        System.out.println("子类的静态代码块加载了,在此之前静态变量b已经加载了,b = " + b);
    }

    {
        System.out.println("子类的构造代码块加载了(此处将【构造块】放在【构造函数】下面,但是仍然是【构造块】先加载),在此之前成员变量bb已经加载了: bb = " + bb);
    }

}

打印结果:

 

 

1.6  HashMap 你用过吗?说说你对它的认识?

HashMap空参构造,只初始化了负载因子(0.75),其他成员变量均为默认值。

常用的有参构造方法 HashMap(int initialCapacity),是可以设置初始化大小的,在大概知道需要多大的map时,可以考虑使用这个构造方法。

HashMap 扩容:每次扩容至原来的2倍。

使用空参构造创建的对象,在第一次添加元素的时候,才会初始化一个长度为16的Node类型的数组。

链表转红黑树的时机:链表长度大于8 , 数组长度大于64

红黑树转链表的时机:链表程度小于 6

HashMap 允许空值作为键和值

HashMap 是无序,且键不重复的

HashMap 线程不安全,多线程操作下可能会抛出 ConcurrentModificationException

参考链接:Java 1.8-API源码学习之java.util.HashMap

 

未完,待续。。。

 

最后

本文是我自己复习并积累的过程,文中难免会有遗漏或不准确的地方

若有大佬路过发现我的错误还请指正,可以发送到我的邮箱:yangxinhufox@foxmail.com

嫌麻烦就请在下方直接评论,万分感谢!!!

  • 9
    点赞
  • 111
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值