Java面试题一:Java基础

本文深入探讨Java集合类的实现细节,包括HashMap的工作原理、多线程安全问题、ConcurrentHashMap的内部机制,以及各种集合类的特点和应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Java基础

  • 集合类:

1、HashMap的put方法的具体流程?

答:判断空间是否足够->不够则扩容->判断key是否存在->存在则插入链表->链表数如果超过8->转为红黑树。

2、HashMap的扩容操作是怎么实现的?

答:当kv达到扩容的阈值,以2倍扩容,将原表中的对象重新计算位置,并拷贝到新的表。

3、HashMap是怎么解决哈希冲突的?

答:1、使用链表;2、两次hash,jdk1.8中使用高16位参与hash;3、使用红黑树加速链表搜索;

4、HashMap为什么数组长度要保证为2的幂次方呢?

答:length-1的二进制是1111...形式,二进制与操作效率非常快,且不浪费空间,因为0位做与运算还是0,同时也增加了碰撞几率。

5、为什么HashMap中String、Integer这样的包装类适合作为Key?

答:1)都是final类型,不可变;2)内部已经重写了equals()和hashCode();

6、ConcurrentHashMap和Hashtable的区别?

答:HashMap没有考虑同步,HashTable考虑同步,ConcurrentHashMap锁相较HashTable更细粒度。

7、ConcurrentHashMap的具体实现知道吗?

答:结合了HashMap和HashTable的优点,jdk1.7中采用了分段锁,jdk1.8中使用了CAS。

弃用分段锁原因:加入多个分段锁浪费内存空间;生产环境中, map 在放入时竞争同一个锁的概率非常小,分段锁反而会造成更新等操作的长时间等待;为了提高 GC 的效率。

8、Java集合的快速失败机制 “fail-fast”?

答:当多线程同时操作并修改了集合结构,这个时候程序就会抛出ConcurrentModificationException异常,并产生fast-fail。解决方法是:1)在修改的部分加锁;2、使用CopyOnWriteArrayList代替ArrayList,也就是写时复制,将添加的元素写入拷贝的数组,然后用现在的数组替换老的数组。

9、Array 和 ArrayList 有什么区别?

答:1)Array还可以包含基本类型;2)Array是固定大小;3)ArrayList提供了一些方便的操作。

10、HashSet是如何保证数据不可重复的?

答:底层使用HashMap,把数据作为K值,HashMap中K值相同会覆盖,而HashSet则直接返回false。

11、BlockingQueue是什么?

答:基于Reentrantlock,主要用于实现生产者/消费者模式,入队时如果队列满会阻塞或直接返回false,出队如果队列为空会阻塞或直接返回null。

12、HashMap多线程不安全的原因?

答:1)1.8以下会有成环风险(之前是头插,1.8改成尾插);2)会导致插入丢失;

13、ConcurrentHashMap如何保证线程安全?

答:锁住一个node,使用Unsafe类中提供的compareAndSwap比较当前内存中的变量值和你指定的一个变量值是否相等,否则等待并协助扩容。

14、ArrayList和LinkedList有什么区别?

答:ArrayList是基于动态数组的,查询速度快,因为可以通过数组对象的大小计算直接查找到对应的对象,但是在中间插入以及删除效率低,因为要移动数组;LinkedList插入、删除快,因为只需要修改指针的指向,但是查询慢,因为要逐个遍历向后查。

15、有没有有顺序的Map实现类?

答:TreeMap是基于Comparator来实现,LinkedHashmap是基于链表的插入有序。

16、HashMap 排序题。

答:

public static HashMap<Integer, User> sortHashMap(HashMap<Integer, User> map) { 
    // 首先拿到 map 的键值对集合
    Set<Entry<Integer, User>> entrySet = map.entrySet();
    // 将 set 集合转为 List 集合,为什么,为了使用工具类的排序方法
    List<Entry<Integer, User>> list = new ArrayList<Entry<Integer, User>>(entrySet); 
    // 使用 Collections 集合工具类对 list 进行排序,排序规则使用匿名内部类来实现
    Collections.sort(list, new Comparator<Entry<Integer, User>>() {
        @Override
        public int compare(Entry<Integer, User> o1, Entry<Integer, User> o2) {
            //按照要求根据 User 的 age 的倒序进行排
            return o2.getValue().getAge()-o1.getValue().getAge();
        }
    });
    //创建一个新的有序的 HashMap 子类的集合
    LinkedHashMap<Integer, User> linkedHashMap = new LinkedHashMap<Integer, User>(); 
    //将 List 中的数据存储在 LinkedHashMap 中
    for(Entry<Integer, User> entry : list){
        linkedHashMap.put(entry.getKey(), entry.getValue());
    }
    //返回结果
    return linkedHashMap;
}

17、红黑树

红黑树具有以下五个特性:
1)每个节点或者是黑色,或者是红色
2)根节点是黑色
3)每个叶子结点(NIL,这里的叶子结点不是传统的叶子结点,是指为空的叶子结点)是黑色。
4)如果一个结点是红色的,则它的子结点必须是黑色的
5)从一个结点到该结点的子孙结点的所有路径上包含相同数目的黑结点。
红黑树是相对接近平衡的二叉树。因为操作比如插入、删除和查找某个值的最坏情况时间都要求与树的高度成比例,这个在高度上的理论上限允许红黑树在最坏情况下都是高效的,而不同于普通的二叉查找树。

18、B+树

1)有k个子树的中间节点包含有k个元素(B树中是k-1个元素),每个元素不保存数据,只用来索引,所有数据都保存在叶子节点。

2)所有的叶子结点中包含了全部元素的信息,及指向含这些元素记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。

3)所有的中间节点元素都同时存在于子节点,在子节点元素中是最大(或最小)元素。

  • 其它

1、类的实例化顺序?

答:父类>子类,静态代码块>非静态代码块>构造函数。

2、抽象类和接口的区别?

答:1)抽象类可以有构造方法(jdk8接口也可以)、成员变量、静态方法;2)一个类可以实现多个接口,但只能继承一个抽象类;

3、IO模型有哪些?

答:BIO:同步阻塞,对每个请求指派线程阻塞至处理完成,如果是线程池会导致上下文切换开销,如果是新建则会耗尽服务器资源;NIO:同步非阻塞,将多个IO阻塞复用到一个select阻塞上,selector断轮询注册在其上的Channel,如果channel就绪写缓冲区,然后再将缓冲区的数据交给线程;AIO:异步非阻塞,注册读写回调方法,异步调用。

4、反射的原理,反射创建类实例的三种方式是什么?

答:通过.class文件获取包含的java类信息;forName会初始化Class,执行类中静态变量的初始化;loadClass不会,只有在创建实例时才会;getClass()会对类进行静态、非静态的初始化;Class.class不会初始化。

反射机制的价值:1、解耦代码,无需在代码中耦合对象或类信息,可以通过运行时动态传入,进提高代码复用率;2、编译时压根无法得知对象和类信息的情况。例子:

package cn.yonyong.reflection.testdemo;

interface Fruit { //水果接口
  public void eat() ; //吃水果
}

class Apple implements Fruit{ //定义苹果
  public void eat() {
    System.out.println("**吃苹果。"); 
  } 
}

class Orange implements Fruit{
  public void eat() {
    System.out.println("**吃橘子。"); 
  }
}

class Factory{
  public static Fruit getInstance(String className){
    Fruit fruit = null ;
    try{
      fruit = (Fruit) Class.forName(className).newInstance() ;
    }catch(Exception e ){
      e.printStackTrace() ;
    }
    return fruit ;
  }
}

public class FactoryDemo{
  public static void main(String args[]){
  //通过工厂类取得接口实例,传入完整的包.类名称
    Fruit f = Factory.getInstance("cn.yonyong.reflection.testdemo.Apple") ;
    if(f!=null){ //判断是否取得接口实例
      f.eat() ;
    }
  }
}

5、描述动态代理的几种实现方式,分别说出相应的优缺点。

答:动态代理的目的是将非业务逻辑在运行时动态织入代码中,保持解耦。JDK动态代理是基于反射的,生成高效,执行低效,cglib采用底层的字节码技术,为代理类创建了一个子类,生成低效,执行高效。

6、三种单例模式实现 :

为什么枚举单例在 Java 中更好,因为代码简单,Enum 实例的创建在默认情况下是线程安全的,

// 懒汉式 缺点:必须加锁
public class Singleton {
    private static Singleton singleton;
    private Singleton() {}
    public static synchronized Singleton getInstance() {
        if(singleton != null) {
            singleton = new Singleton();
        }
    }
    return singleton;
}


// 饿汉式 缺点:初始化就加载
public class Singleton {
    private static final Singleton SINGLETON = new Singleton(){
    private Singleton() {}
    public static Singleton getInstance() {
        return SINGLETON;
    }
}

// 登记式
public class Singleton {
    private Singleton() {}
    public static Singleton getInstance() {
        return Holder.SINGLETON;
    }
    private static class Holder {
        private static final Singleton SINGLETON = new Singleton();
    }
}

7、error和exception的区别,CheckedException,RuntimeException的区别。

答:Error表示程序无法处理的(如内存资源不足等)的异常,Exception是指程序设计问题引起的异常。RuntimeException通常暗示程序上的错误,不需要做try/catch,需要修改源码;而CheckedException是程序设计者已经知道会受到某些异常,必须被处理。

8、请列出5个运行时异常。

答:空指针(NullPointerException)、数组访问越界(IndexOutOfBoundsException)、缓冲区溢出(BufferOverflowException)、强制类型转换异常(ClassCastException)、数组存储异常(ArrayStoreException)。

9、在自己的代码中,如果创建一个java.lang.String对象,这个对象是否可以被类加载器加载?

答:不可以,双亲委派模式会保证父类加载器先加载类,BootStrap加载器会先加载jdk中的java.lang.String类。

10、泛型的存在是用来解决什么问题?

答:泛型好处是在编译时可以检查类型安全,并且所有强制转换都是隐式、自动的,提高代码复用性。

11、有没可能 2个不相等的对象有同hashcode。

答:没有重载之前使用的是对象在jvm堆上的地址,不会重复,重载由于算法问题,会导致相同。

12、什么是序列化,为什么要定义serialversionUID变量。

答:序列化用来将对象内容流化;原则上序列化后的数据当中的serialVersionUID与当前类当中的serialVersionUID一致,那么该对象才能被反序列化成功,不指定serialVersionUID的后果是,当你添加或修改类中的任何字段时,则已序列化类将无法恢复。

13、进程和线程间通信。

答:线程间通信:1)共享变量;2)wait/notify机制;(java.lang.Object)3)lock/condition机制(java5以后替代2)4)管道。

进程间通信:1)管道;2)命名管道;3)信号量;4)消息队列;5)共享内存(内存映射文件);6)socket。

14、强引用、软引用、弱引用、虚引用。

答:对象引用存放在栈中,对象存放在堆中。强引用:必须将对象置为null才会被回收;软引用:内存不够时才会被回收;弱引用:垃圾回收一旦发现就会回收;虚引用:任何时候都会被回收,必须和引用队列一起用,可以判断引用队列是否加入虚引用了解对象是否要被回收。

15.是否可以在static环境中访问非static变量?

答:当类被Java虚拟机载入的时候,会对static变量进行初始化。如果你的代码尝试不用实例来访问非static的变量,编译器会报错,因为这些变量还没有被创建出来,还没有跟任何实例关联上。

16、Java中的方法覆盖(Overriding)和方法重载(Overloading)是什么意思?

答:Java中的方法重载发生在同一个类里面两个或者是多个方法的方法名相同但是参数不同的情况。与此相对,方法覆盖是说子类重新定义了父类的方法。方法覆盖必须有相同的方法名,参数列表和返回类型。覆盖者可能不会限制它所覆盖的方法的访问。

17、String和StringBuilder、StringBuffer的区别?

答:其中String是只读字符串,也就意味着String引用的字符串内容是不能被改变的。StringBuilder是Java 5中引入的,它和StringBuffer的方法完全相同,区别在于它是在单线程环境下使用的,因为它的所有方面都没有被synchronized修饰,因此它的效率也比StringBuffer要高。

18、Java 中的可序列化接口和可外部接口之间的区别是什么?

答:Externalizable 给我们提供 writeExternal() 和 readExternal() 方法, 这让我们灵活地控制 Java 序列化机制, 而不是依赖于 Java 的默认序列化。

19、可序列化的方法有多少?如果没有方法,那么可序列化接口的用途是什么?

答:它没有任何方法, 在 Java 中也称为标记接口。当类实现 java.io.Serializable 接口时, 它将在 Java 中变得可序列化, 并指示编译器使用 Java 序列化机制序列化此对象。

20、序列化时,你希望某些成员不要序列化?你如何实现它?

答:申明瞬态变量,或者由于静态变量属于类, 而不是对象, 因此它们不是对象状态的一部分, 因此在 Java 序列化过程中不会保存它们。

21、如果一个类是可序列化的,而他的超类没有,那么当进行反序列化的时候,那些从超类继承的实例变量的值是什么?

答:Java中的序列化处理实例变量只会在所有实现了Serializable接口的继承支路上展开。所以当一个类进行反序列化处理的时候,超类没有实现Serializable接口,那么从超类继承的实例变量会通过为实现序列化接口的超类的构造函数进行初始化。

22、是否可以自定义序列化过程, 或者是否可以覆盖 Java 中的默认序列化过程?

答:可以,重写ObjectOutputStream.writeObject(saveThisObject),  ObjectInputStream.readObject()方法,并要声明这些方法为私有方法, 以避免被继承、重写或重载。

23、为什么要为你的对象提供自定义序列化?

答:超类已经序列化,子类不要实现序列化。

24、你能用Java覆盖静态方法吗?如果我在子类中创建相同的方法是编译时错误?

答:不能;不是,这称为隐藏在Java中的方法,静态方法应该使用类名而不是使用对象来调用, 因为静态方法不能在Java中重写。

25、java中实现多态的机制。

答:本质上多态分两种:编译时多态(又称静态多态),运行时多态(又称动态多态);重载(overload)就是编译时多态的一个例子。我们通常所说的多态指的都是运行时多态,也就是编译时不确定究竟调用哪个具体方法,一直延迟到运行时才能确定。这也是为什么有时候多态方法又被称为延迟方法的原因。通常有两种实现方法:子类继承父类(extends)、类实现接口(implements)。

26、字节流和字符流的区别。

答:字节流读取的时候,读到一个字节就返回一个字节; 字符流使用了字节流读到一个或多个字节(中文对应的字节 数是两个,在 UTF-8 码表中是 3 个字节)时,先去查指定的编码表,将查到的字符返回。

27、B+树。

答:1)根节点至少有两个子女;中间节点最多k-1个,k个孩子;叶子节点k-1个元素

2)卫星数据都放在叶子节点节点,叶子节点都在同一层,之间通过指针相连;

3)除了叶子节点,其他节点只放索引数据;

4)父节点存放的是子节点的几个最大/小值;

优势:1)单一节点存放更多数据,树层级低,IO次数少;

2)所有查询都要查询叶子节点,查询更稳定;

3)叶子节点为有序列表,便于范围查询。

28、epoll和poll的区别。

答:epoll的优点:1)没有最大并发连接的限制;2)只有活跃可用的FD才会调用callback函数;3) 内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减少复制开销。poll没有后面两点。

29、HTTP有哪些问题,加密算法有哪些,针对不同加密方式可能产生的问题,及其HTTPS是如何保证安全传输的?

答:对称加密:秘钥以明文方式传给客户,有在传输中被截获的安全问题;非对称加密:非对称加密的风险是截取公钥并伪造公钥发给客户端,客户端用中间人的公钥加密把私钥传给中间人后,中间人自己解密获取私钥,再把秘钥传给服务器;数字证书:数字签名(服务器信息+公钥做hash和CA私钥加密)+服务器信息+公钥,客户端使用CA公钥解密数字签名与原始信息hash后比较是否一致,以此保证安全。

30、字符串在 JVM 中如何存放?

答:使用常量字符串初始化的字符串对象,它的值存放在字符串常量池中;使用字符串构造方法创建的字符串对象,它的值存放在堆内存中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值