Java复习面试题

这篇博客整理了Java面试中常见的知识点,包括Java基础、Spring、SpringBoot、监听器、Mybatis、Mybatis-plus、Rabbitmq、Springcloud、Zookeeper、数据库MySQL、Redis以及SQL优化等方面,覆盖了面向对象特性、并发编程、数据结构、框架原理、中间件使用等多个方面,是Java开发者面试的必备参考资料。
摘要由CSDN通过智能技术生成

Java基础

1. 面向对象的三个基本特征?
面向对象的三个基本特征是:封装、继承、多态

继承:让某个类型的对象获得另一个类型的对象的属性的方法;继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例和方法,或子类从父类继承方法,使得子类具有父类相同的行为

封装:隐藏部分对象的属性和实现细节,对数据的访问只能通过对外公开的接口,通过这种方式,对象对内部数据提供了不同级别的保护,以防止程序中无关的部分意外的改变或错误的使用了对象的私有部分

多态:对于同一个行为,不同的子类对象具有不同的表现形式,多态存在的3个条件:1.继承,2.重写,3.父类引用指向子类对象
同一个事件发生不同的对象上会产生不同的结果
2. &和&&的区别?
&&:逻辑与运算符。当运算符左右两边的表达式都为true,才返回true。同时具有短路性,如果第一个表达式为false,则直接返回false。
&:逻辑与运算符、按位与运算符。
按位与运算符:用于二进制的计算,只有对应的两个二进位均为1时,结果位才为1,否则为0。
逻辑与运算符:&在用于逻辑与时,和&&的区别是不具有短路性。所在通常使用逻辑与运算符都使用&&,而&更多的适用于位运算。
3. String是Java基本数据类型吗?
不是。
java中的基本数据类型有8个:byte、short、int、long、float、double、char、boolean;除了基本数据类型,剩下的都是引用类型。
基本数据类型:数据直接存储在栈上。
引用数据类型区别:数据存储在堆上,栈上只存储引用地址。
4. String类可以继承吗?
不能
String类使用final修饰,无法被继承
5. final关键字的作用?
final关键字可以用于三个地方,用于修饰类、类属性和类方法。
被final关键字修饰的类不能被继承
被final关键字修饰的类属性和类方法不能被覆盖(重写);重写:只要子类方法与父类方法同名参数就是重写
对于被final关键字修饰的类属性而言,子类就不能给他重新赋值了,如果重新赋值,会报错
Java中的final关键字有哪些用法?
修饰类:该类不能再派生出新的子类,不能作为父类被继承。因此,一个类不能同时被声明为abstractfinal。
修饰方法:该方法不能被子类重写。
修饰变量:该变量必须在声明时给定初值,而在以后只能读取,不可修改,如果变量是对象,则指的是引用不可修改,但是对象的属性还是可以修改的。

public class FinalDemo {
   
    // 不可再修改该变量的值
    public static final int FINAL_VARIABLE = 0;
    // 不可再修改该变量的引用,但是可以直接修改属性值
    public static final User USER = new User();
    public static void main(String[] args) {
   
        // 输出:User(id=0, name=null, age=0)
        System.out.println(USER);
        // 直接修改属性值
        USER.setName("test");
        // 输出:User(id=0, name=test, age=0)
        System.out.println(USER);
    }
}
6. String和StringBuilder、StringBuffer的区别?
String:String的值被创建后不能修改,任何对String的修改都会引发新的String对象的生成。
StringBuffer:跟String类似,但是值可以被修改,使用synchronized来保证线程安全。 (线程安全)
StringBuilder:没有使用synchronized,具有更高的性能。 (线程不安全)
7. String s = new String (“xyz”)创建了几个字符串对象?
一个或两个。如果字符串常量池已经有"xyz",则是一个,否则,两个。
当字符串常量池也没有"xyz",此时会创建如下两个对象:
一个是字符串字面量"xyz"所对应、驻留在一个全局共享的字符串常量池中的实例,此时该实例也是在堆中,字符串常量池只放引用。
另一个是通过new String()创建并初始化的,内容与"xyz"相同的实例,也是在堆中。
8. String s = “xyz” 和String s = new String(“xyz”)区别?
两个语句都会先去字符串常量池中检查是否已经存在"xyz",如果有则直接使用,如果没有则会在常量池中创建"xyz"对象。
另外,String s = new String("xyz")还会通过new String()在堆里创建一个内容与"xyz"相同的对象实例。
所以前者其实理解为被后者所包含
9. == 和equals的区别是什么?
==:运算符,用于比较基础类型变量和引用类型变量。
对于基础类型变量,比较的变量保存的值是否相同,类型不一定相同。
对于引用类型变量,比较的是两个对象的地址是否相同。

equals:Object类中定义的方法,通常用于比较两个对象的值是否相等。
equals在Object方法中其实等同于==,但是在实际的使用中,equals通常被重写用于比较两个对象的值是否相同。
10. 两个对象的hashCode()相同,则equals()也一定为true,对吗?
不对。 hashCode()和equals()之间的关系如下:
当有a.equals(b) == true时,则a.hashCode() == b.hashCode()必然成立,
反过来,当a.hashCode() == b.hashCode()时,a.equals(b)不一定为true。
11. 什么是反射?
反射是指在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法,并且对于任意一个对象,都能够调用它的任意一个方法;这种动态获取信息以及动态调用对象方法的功能称为反射机制。
12. 深拷贝和浅拷贝的区别?
数据分为基本数据类型和引用数据类型。基本数据类型:数据直接存储在栈中;引用数据类型:存储在栈中的是对象的引用地址,真实的对象数据存放在堆内存里。

浅拷贝:对于基础数据类型:直接复制数据值;对于引用数据类型:只是复制了对象的引用地址,新旧对象指向同一个内存地址,修改其中一个对象的值,另一个对象的值随之改变。

深拷贝:对于基础数据类型:直接复制数据值;对于引用数据类型:开辟新的内存空间,在新的内存空间里复制一个一模一样的对象,新老对象不共享内存,修改其中一个对象的值,不会影响另一个对象。

深拷贝相比于浅拷贝速度较慢并且花销较大。

13. 并发个并行有什么区别?
并发:两个或多个事件在同一时间间隔发生。

并行:两个或者多个事件在同一时刻发生。

并行是真正意义上,同一时刻做多件事情,而并发在同一时刻只会做一件事件,只是可以将时间切碎,交替做多件事情。

网上有个例子挺形象的:
你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持并发也不支持并行。

你吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发。

你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行。
14. Java静态变量和成员变量的区别?
public class Demo {
   
    /**
     * 静态变量:又称类变量,static修饰
     */
    public static String STATIC_VARIABLE = "静态变量";
    /**
     * 实例变量:又称成员变量,没有static修饰
     */
    public String INSTANCE_VARIABLE = "实例变量";
}
成员变量存在于堆内存中,静态变量存在于方法区中。
成员变量与对象共存亡,随着对象创建而存在,随着对象被回收而释放。静态变量与类共存亡,随着类的加载而存在,随着类的消失而消失。
成员变量所属于对象,所以也称为实例变量。静态变量所属于类,所以也称为类变量。
成员变量只能被对象所调用 。静态变量可以被对象调用,也可以被类名调用。

15. 重载(Overload)和重写(Override)的区别?
方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。

重载:一个类中有多个同名的方法,但是具有有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)。

重写:发生在子类与父类之间,子类对父类的方法进行重写,参数都不能改变,返回值类型可以不相同,但是必须是父类返回值的派生类。即外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定于自己的行为。
16. 为什么不能根据返回类型来区分重载?
如果我们有两个方法如下,当我们调用:test(1)时,编译器无法确认要调用的是哪一个。
方法的返回值是作为方法运行之后的一个"状态",但是并不是所有调用都关注返回值,所以不能将返回值作为重载的唯一区分条件。
17. 抽象类 (abstract class) 和接口(interface)有什么区别?
抽象类只能单继承,接口可以多实现。
抽象类可以有构造方法,接口中不能有构造方法。
抽象类中可以有成员变量,接口中没有成员变量,只能有常量(默认就是public static final)

抽象类中可以包含非抽象的方法,在java7之前接口中的所有方法都是抽象的,在java8之后,接口支持非抽象方法:default方法、静态方法等。java9支持私有方法、私有静态方法。

抽象类中的抽象方法类型可以是任意修饰符,java8之前接口中的方法只能是public类型,java9支持 private类型。



设计思想的区别:
接口是自上而下的抽象过程,接口规范了某些行为,是对某一行为的抽象。我需要这个行为,我就去实现某个接口,但是具体这个行为怎么实现,完全由自己决定。

抽象类是自下而上的抽象过程,抽象类提供了通用实现,是对某一类事物的抽象。我们在写实现类的时候,发现某些实现类具有几乎相同的实现,因此我们将这些相同的实现抽取出来成为抽象类,然后如果有一些差异点,则可以提供抽象方法来支持自定义实现。

我在网上看到有个说法,挺形象的:

普通类像亲爹 ,他有啥都是你的。
抽象类像叔伯,有一部分会给你,还能指导你做事的方法。
接口像干爹,可以给你指引方法,但是做成啥样得你自己努力实现。

18. Error 和 Exception 有什么区别?
Error和Exception都是Throwable的子类,用于表示程序出现了不正常的情况。区别在于:

Error表示系统级的错误和程序不必处理的异常,是恢复不是不可能但很困难的情况下的一种严重问题,比如内存溢出,不可能指望程序能处理这样的情况。

Exception表示需要捕捉或者需要程序进行处理的异常,是一种设计或实现问题,也就是说,它表示如果程序运行正常,从不会发生的情况。
19. 阐述final、finally、finalize的区别
其实是三个完全不相关的东西,只是长的有点像。
final如上所示。

finally:finally是对Java异常处理机制的最佳补充,通常配合try、catch使用,用于存放那些无论是否出现异常都一定会执行的代码。在实际使用中,通常用于释放锁、数据库连接资源,把资源释放方法放到finally中,可以大大降低程序出错的几率。

finalize:Object中的方法,在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。finalize()方法仅作为了解即可,在java9中该方法已经被标记位废弃,并添加新的java.lang.ref.Cleaner,提供更灵活和有效的方法来释放资源。
20. try、catch、finally考察,请指出下面程序的运行结果
public class TryDemo {
   
    public static void main(String[] args) {
   
        System.out.println(test());
    }
    public static int test() {
   
        try {
   
            return 1;
        } catch (Exception e) {
   
            return 2;
        } finally {
   
            System.out.print("3");
        }
    }
}

执行结果: 31return前会先执行finally语句块,所以是先输出finally里的3,在输出return1
21. JDK 1.8之后有哪些新特性?
接口默认方法:Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可

Lambda 表达式和函数式接口:Lambda 表达式本质上是一段匿名内部类,也可以是一段可以传递的代码。Lambda 允许把函数作为一个方法的参数(函数作为参数传递到方法中),使用 Lambda 表达式使代码更加简洁,但是也不要滥用,否则会有可读性等问题,《Effective Java》作者 Josh Bloch 建议使用 Lambda 表达式最好不要超过3行。

Stream API:用函数式编程方式在集合类上进行复杂操作的工具,配合Lambda表达式可以方便的对集合进行处理。Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

方法引用:方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。

日期时间API:Java 8 引入了新的日期时间API改进了日期时间的管理。

Optional 类:著名的 NullPointerException 是引起系统失败最常见的原因。很久以前 Google Guava 项目引入了 Optional 作为解决空指针异常的一种方式,不赞成代码被 null 检查的代码污染,期望程序员写整洁的代码。受Google Guava的鼓励,Optional 现在是Java 8库的一部分。

新工具:新的编译工具,如:Nashorn引擎 jjs、 类依赖分析器 jdeps。

22. wait()和sleep()方法的区别?
来源不同:sleep()来自Thread类,wait()来自Object类。
对于同步锁的影响不同:sleep()不会该表同步锁的行为,如果当前线程持有同步锁,那么sleep是不会让线程释放同步锁的。wait()会释放同步锁,让其他线程进入synchronized代码块执行。

使用范围不同:sleep()可以在任何地方使用,wait()只能在同步控制方法或者同步控制块里面使用,否则会抛出lllegalMonitorStateException。(违法的监控状态异常)

恢复方式不同:两者会暂停当前线程,但是在恢复上不太一样。sleep()在时间到了之后会重新恢复,wait()则需要其他线程调用同一对象的notify()/nofityAll()才能重新恢复。
23. 多线程程序有几种实现方式?
通常来说,可以认为有三种方式:
1.继承Thread类
2.实现Runnable接口
3.实现Callable接口
其中,Thread其实也是实现了Runnable接口,Runnable和Callable的主要区别在于是否有返回值
24. Thread调用start()方法和调用run()方法的区别?
run():普通的方法调用,在主线程中执行,不会新建一个线程来执行。
start():新启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到CPU时间片,就开始执行run()方法。
25. synchronized和Lock的区别
1)Lock 是一个接口;synchronized 是 Java 中的关键字,synchronized 是内置的语言实现;

2)Lock 在发生异常时,如果没有主动通过 unLock() 去释放锁,很可能会造成死锁现象,因此使用 Lock 时需要在 finally 块中释放锁;synchronized 不需要手动获取锁和释放锁,在发生异常时,会自动释放锁,因此不会导致死锁现象发生;

3)Lock 的使用更加灵活,可以有响应中断、有超时时间等;而 synchronized 却不行,使用 synchronized 时,等待的线程会一直等待下去,直到获取到锁;

4)在性能上,随着近些年 synchronized 的不断优化,Lock 和 synchronized 在性能上已经没有很明显的差距了,所以性能不应该成为我们选择两者的主要原因。官方推荐尽量使用 synchronized,除非 synchronized 无法满足需求时,则可以使用 Lock。

26. 如何检测死锁?
死锁的四个必要条件:

1)互斥条件:进程对所分配到的资源进行排他性控制,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。

2)请求和保持条件:进程已经获得了至少一个资源,但又对其他资源发出请求,而该资源已被其他进程占有,此时该进程的请求被阻塞,但又对自己获得的资源保持不放。

3)不可剥夺条件:进程已获得的资源在未使用完毕之前,不可被其他进程强行剥夺,只能由自己释放。

4)环路等待条件:存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被 链中下一个进程所请求

27. 怎么预防死锁?
预防死锁的方式就是打破四个必要条件中的任意一个即可。

1)打破互斥条件:在系统里取消互斥。若资源不被一个进程独占使用,那么死锁是肯定不会发生的。但一般来说在所列的四个条件中,“互斥”条件是无法破坏的。因此,在死锁预防里主要是破坏其他几个必要条件,而不去涉及破坏“互斥”条件。。

2)打破请求和保持条件:1)采用资源预先分配策略,即进程运行前申请全部资源,满足则运行,不然就等待。 2)每个进程提出新的资源申请前,必须先释放它先前所占有的资源。

3)打破不可剥夺条件:当进程占有某些资源后又进一步申请其他资源而无法满足,则该进程必须释放它原来占有的资源。

4)打破环路等待条件:实现资源有序分配策略,将系统的所有资源统一编号,所有进程只能采用按序号递增的形式申请资源。

28. 为什么要使用线程池?直接new个线程不是很舒服?
如果我们在方法中直接new一个线程来处理,当这个方法被调用频繁时就会创建很多线程,不仅会消耗系统资源,还会降低系统的稳定性,一不小心把系统搞崩了,就可以直接去财务那结帐了。

如果我们合理的使用线程池,则可以避免把系统搞崩的窘境。总得来说,使用线程池可以带来以下几个好处:

降低资源消耗。通过重复利用已创建的线程,降低线程创建和销毁造成的消耗。
提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
增加线程的可管理型。线程是稀缺资源,使用线程池可以进行统一分配,调优和监控。

29. 线程池的核心属性有哪些?
hreadFactory(线程工厂):用于创建工作线程的工厂。

corePoolSize(核心线程数):当线程池运行的线程少于 corePoolSize 时,将创建一个新线程来处理请求,即使其他工作线程处于空闲状态。

workQueue(队列):用于保留任务并移交给工作线程的阻塞队列。

maximumPoolSize(最大线程数):线程池允许开启的最大线程数。

handler(拒绝策略):往线程池添加任务时,将在下面两种情况触发拒绝策略:1)线程池运行状态不是 RUNNING;2)线程池已经达到最大线程数,并且阻塞队列已满时。

keepAliveTime(保持存活时间):如果线程池当前线程数超过 corePoolSize,则多余的线程空闲时间超过 keepAliveTime 时会被终止。

30. List、Set、Map三者的区别?
List(对付顺序的好帮手): List 接口存储一组不唯一(可以有多个元素引用相同的对象)、有序的对象。

Set(注重独一无二的性质):不允许重复的集合,不会有多个元素引用相同的对象。

Map(用Key来搜索的专业户): 使用键值对存储。Map 会维护与 Key 有关联的值。两个 Key可以引用相同的对象,但 Key 不能重复,典型的 Key 是String类型,但也可以是任何对象。

31. ArrayList和LinkedList的区别?
ArrayList 底层基于动态数组实现,LinkedList 底层基于链表实现。

对于按 index 索引数据(get/set方法):ArrayList 通过 index 直接定位到数组对应位置的节点,而 LinkedList需要从头结点或尾节点开始遍历,直到寻找到目标节点,因此在效率上 ArrayList 优于 LinkedList。

对于随机插入和删除:ArrayList 需要移动目标节点后面的节点(使用System.arraycopy 方法移动节点),而 LinkedList 只需修改目标节点前后节点的 next 或 prev 属性即可,因此在效率上 LinkedList 优于 ArrayList。

对于顺序插入和删除:由于 ArrayList 不需要移动节点,因此在效率上比 LinkedList 更好。这也是为什么在实际使用中 ArrayList 更多,因为大部分情况下我们的使用都是顺序插入。

32. 介绍下HashMap的底层数据结构
JDK1.8,底层是由"数组+链表+红黑树"组成,而在JJDK1.8之前是由"数组+链表"组成


为什么要改成"数组+链表+红黑树"?
主要是为了提升hash冲突严重时(链表过长)的查找性能,使用链表的查找性能是0(n),而使用红黑树是0(logn)


那在什么时候用链表?什么时候用红黑树?
对于插入,默认情况下是使用链表节点。当同一个索引位置的节点在新增后超过8个(阈值8):如果此时数组长度大于等于 64,则会触发链表节点转红黑树节点(treeifyBin);而如果数组长度小于64,则不会触发链表转红黑树,而是会进行扩容,因为此时的数据量还比较小。

对于移除,当同一个索引位置的节点在移除后达到 6 个,并且该索引位置的节点为红黑树节点,会触发红黑树节点转链表节点(untreeify)。

33. HashMap和Hashtable的区别?
HashMap 允
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值