
Java常见面试题
文章平均质量分 81
Java常见面试题,包括Java基础、集合、多线程、JVM等。
倚-天-照-海
这个作者很懒,什么都没留下…
展开
-
Java常见面试题
本篇文章讲述了Java开发常见面试题,主要包括Java 基础、容器(集合)、多线程、反射、对象拷贝、异常、网络IO、设计模式、Spring MVC、Spring/Spring Boot/Spring Cloud、mybatis、MySQL数据库、zookeeper、kafka等。原创 2024-11-02 16:42:31 · 267 阅读 · 0 评论 -
CompletableFuture的简单用法
Java8中的CompletableFuture是对Future的扩展实现, 主要是为了弥补Future没有相应的回调机制的缺陷。CompletableFuture实现了Future和CompletionStage两个接口。原创 2024-11-14 23:35:46 · 721 阅读 · 0 评论 -
ThreadLocal原理与简单使用
什么是ThreadLocal?ThreadLocal类顾名思义可以理解为线程本地变量,也就是说,如果定义了一个ThreadLocal,每个线程往这个ThreadLocal中读写数据是线程隔离的,互相之间不会影响。它提供了一种将可变数据通过每个线程有自己的独立副本从而实现线程封闭的机制。它大致的实现思路是怎样的?Thread类有一个类型为ThreadLocal.ThreadLocalMap的实例变量threadLocals,也就是说每个线程有一个自己的ThreadLocalMap。原创 2024-11-11 00:02:27 · 1293 阅读 · 0 评论 -
线程池ThreadPoolExecutor
每当使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。在Java中通过线程池使线程可以复用,就是线程执行完一个任务,并不被销毁,而是可以继续执行其他的任务。线程池的种类及使用场景。原创 2024-11-10 22:38:32 · 870 阅读 · 0 评论 -
线程池ForkJoinPool
工作队列中保存的是等待执行的任务ForkJoinTask,是保存在数组中的,每个工作线程会优先完成自己队列中的任务,当自己队列中的任务为空时,才会通过工作窃取work-stealing算法从其他任务队列中获取任务。3.每个工作线程在处理自己的工作队列同时,会尝试窃取一个任务(或是来自于刚刚提交到 pool 的任务,或是来自于其他工作线程的工作队列),窃取的任务位于其他线程的工作队列的队首,也就是说工作线程在窃取其他工作线程的任务时,使用的是 FIFO 方式。例子:求1-100的和。原创 2024-11-10 21:39:23 · 204 阅读 · 0 评论 -
CountDownLatch和CyclicBarrier的区别
CountDownLatch是一个同步的辅助类,允许一个或多个线程,等待其他一组线程完成操作,再继续执行。CyclicBarrier是一个同步的辅助类,允许一组线程之间相互等待,达到一个共同点,再继续执行。CountDownLatch的计数器无法被重置;CyclicBarrier的计数器可以被重置后使用,因此它被称为是循环的barrier。他们都是:Synchronization aid,把它翻译成同步辅助器,既然是辅助工具,怎么使用?哪些场景使用?原创 2024-11-10 18:30:44 · 638 阅读 · 0 评论 -
乐观锁和悲观锁的实现方式
当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值与当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。:总是认为不会产生并发问题,每次读取数据的时候总认为不会有其他线程对数据进行修改,因此不会上锁,但是在更新数据时,会判断其他线程在这之前有没有对数据进行修改,如果其他线程对数据进行了修改,因为并发冲突而导致更新失败,该线程就会反复重试更新数据,直到成功为止。为例,看一下在不使用锁的情况下是如何保证线程安全的。原创 2024-11-10 18:05:59 · 818 阅读 · 0 评论 -
AbstractQueuedSynchronizer(AQS)的原理与源码分析
头节点最开始是不封装线程的,头节点中thread字段的值被默认初始化成null,只有第一个获取锁的线程释放锁之后,唤醒头节点的下一个节点中的线程去获取锁,这时头节点的下一个节点成为新的头节点,此时头节点中才有线程,而且此时头节点中的线程被设置成了null,因为当前线程已经获取到了锁,没必要再保存在同步队列中了。1.当前线程尝试获取锁,对于公平锁,首先判断同步队列中是否有线程在等待,如果有,则将当前线程添加到同步队列中,如果没有,则通过CAS操作尝试修改同步状态state的值。如果获取失败,则阻塞当前线程。原创 2024-11-06 23:05:51 · 610 阅读 · 0 评论 -
java同步的实现方式,以及synchronized与Lock的异同
C、tryLock(long timeout, TimeUnit timeUnit)方法:如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false。java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查),相互之间产生冲突,将会导致数据不准确,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,从而保证了该变量的唯一性和准确性。原创 2024-11-06 21:59:29 · 928 阅读 · 0 评论 -
synchronized加锁原理以及锁升级过程
由于synchronized中用到了CAS技术,此处先对CAS做个简单介绍。是乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败。CAS(V,A,B)操作中至少包含三个操作数——需要读写的内存位置(V)、进行比较的预期原值(A)和拟写入的新值(B)。如果内存位置V的值与预期原值A相匹配,那么处理器会自动将该位置值更新为新值B。否则处理器不做任何操作。无论哪种情况,它都会在CAS指令之前返回该位置的值。在。原创 2024-11-03 22:33:25 · 1355 阅读 · 0 评论 -
并发和并行,守护线程和用户线程,以及线程的六种状态
创建线程之后调用线程的start()方法,或该线程调用的Thread.sleep()方法结束,或在该线程中其他线程调用的join()结束,或其他线程调用notify或notifyAll方法唤醒该线程,或该线程获取到了同步方法的锁对象,或该线程的CPU时间片用完了,或在该线程中调用Thread.yield()方法。因此,当所有的非守护线程(用户线程,就是应用程序里的自定义线程)结束时,程序也就终止了,同时会杀死进程中的所有守护线程。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。原创 2024-11-03 22:04:49 · 1097 阅读 · 0 评论 -
多线程之间的通讯
由于线程A和线程B持有同一个MyObject类的对象object,尽管这两个线程需要调用不同的方法,但是它们是同步执行的,假如线程A先执行methodA方法,那么线程B需要等待线程A执行完了methodA()方法之后,它才能执行methodB()方法。当条件满足时,线程B调用 notify()通知线程A,所谓通知线程A,就是唤醒线程A,让它尝试获取锁,若获取成功,则进入可运行状态,否则进入阻塞状态。因为,线程B已经发了通知了,以后不再发通知了。=5),线程A调用wait() 放弃CPU,并进入等待状态。原创 2024-11-03 21:32:18 · 853 阅读 · 0 评论 -
notify和notifyAll的区别,以及sleep、wait和join的区别
对象的wait(0)方法的作用是让线程一直等待,直到其他线程针对该线程调用同一个对象的notify或notifyAll方法。这是因为线程t1和线程t2的run方法中同步代码块中的锁对象都是t1对象,在线程t2的run中调用了t1.join()方法,由上面分析可知,在join方法内部,线程t2会再次获取t1对象作为锁对象进入到join方法内部的同步方法中,并在执行join方法内部的wait方法时释放掉t1对象锁,这时线程t2就不再拥有任何锁,线程t1就可以获取到锁对象t1从而执行自己的run方法。原创 2024-11-03 21:21:14 · 1017 阅读 · 0 评论 -
interrupt、interrupted、isInterrupted方法详解
thread线程调用了interrupt方法,并没有使thread线程立即中断,只是将thread线程的中断标志设置为true(线程的中断状态被设置),通过thread.isInterrupted方法判断线程的中断状态是否被设置,若被设置了,则返回true,否则返回false。当线程调用interrupt方法时,会进入到同步代码块中,由于blocker==null,所以不执行if语句中的代码,而是调用interrupt0()方法,将线程的中断标志位设置为true。方法不能中断线程,只是设置线程的中断状态。原创 2024-11-03 20:30:08 · 699 阅读 · 0 评论 -
进程与线程的区别,以及创建线程的几种方式
(此处说的是线程,而不是普通对象)的wait方法,此时线程A会释放掉持有的线程B对象的锁,并等待线程B执行run方法。当线程B运行结束时,会自动调用自身的notifyAll方法,唤醒所有等待线程B对象锁的线程尝试获取锁,即线程A会被唤醒,尝试获取线程B对象的锁,如果获取成功,则接着wait方法之后的代码继续执行,如果获取失败,则进入线程B对象的锁池阻塞等待,并尝试下一次获取B对象的锁。(1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。原创 2024-11-03 20:20:18 · 850 阅读 · 0 评论 -
Java内存模型以及原子性、可见性与有序性
线程安全高频面试题:高并发如何保证线程安全,谈谈你对高并发的理解。其实java的多线程并发问题最终都会反映在java的内存模型上,高并发情况下需要保证线程安全,所谓线程安全无非是要控制多个线程对某个公共资源的有序访问或修改。在回答高并发的问题时,主要从java内存模型与线程同步两方面回答即可。线程安全就是说多线程访问同一代码,不会产生不确定的结果。如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。原创 2024-11-03 19:50:50 · 648 阅读 · 0 评论 -
迭代器 Iterator的原理以及与ListIterator的区别
3.ListIterator实现了Iterator接口,并新增了其他功能,比如:add()方法向集合中增加元素,set()方法修改集合中的元素,previousIndex()方法获取前一个元素的索引,nextIndex()方法获取后一个元素的索引等。由于每一个容器都有取出或删除元素的功能,这些功能定义都一样,只不过实现的具体方式不同(因为每一个容器的数据结构不一样),所以,在java中对共性的功能进行抽取并封装在Iterator接口中,从而出现了迭代器。一个方法,用于获取迭代器。原创 2024-11-03 10:48:00 · 482 阅读 · 0 评论 -
Queue中add/offer、element/peek、remove/poll区别
1. add和offer方法都是向队列中增加元素,区别在于:在队列满的情况下,add方法将选择抛异常来表示队列已经满了,而offer方法通过返回false表示队列已经满了;3. element和peek方法都是返回队列的头元素,但是不删除头元素,区别在于:在队列为空的情况下,element方法将抛异常,而peek方法将返回null。2. remove和poll方法都是删除并返回队列的头元素,区别在于:在队列为空的情况下,remove方法将抛异常,而poll方法将返回null;原创 2024-11-03 10:35:52 · 315 阅读 · 0 评论 -
数组和List之间的区别及转换
附上arraylist扩充机制:newCapacity=oldCapacity+(oldCapacity>>1)(注:>>1:右移1位,相当于除以2),但由于源码里传过来的minCapcatiy的值是size+1,能够实现grow方法调用就肯定是(size+1)>elementData.length的情况,所以size就是初始最大容量或上一次扩容后达到的最大容量,才会进行扩容。而且,每次添加新的元素的时候都会检查内部数组的空间是否足够,效率比数组低(这是比较麻烦的地方)。原创 2024-11-03 10:33:10 · 443 阅读 · 0 评论 -
ConcurrentHashMap的实现原理
在jdk1.7及之前,ConcurrentHashMap采用数组+单向链表的数据结构,在jdk1.8变更为数组+单向链表+红黑树的结构。由于HashMap是线程不安全的,如何在多线程高并发环境下安全地使用HashMap呢?那就需要考虑线程安全的Map。Hashtable、SynchronizedMap和ConcurrentHashMap都是线程安全的,他们之间有什么区别呢?原创 2024-11-03 10:27:51 · 491 阅读 · 0 评论 -
java8有哪些新特性
(concatStr方法)原创 2024-11-03 10:00:03 · 1019 阅读 · 0 评论 -
File类的常用方法
renameTo(File dest)如果目标文件与源文件是在同一个路径下,那么renameTo的作用是重命名, 如果目标文件与源文件不是在同一个路径下,那么renameTo的作用就是剪切,而且还不能操作文件夹。listFiles() 返回目录下的文件或者目录对象(File类实例),包含隐藏文件。delete() 删除文件或者一个空文件夹,不能删除非空文件夹,马上删除文件,返回一个布尔值。length() 获取文件的大小(字节数),如果文件不存在则返回0L,如果是文件夹也返回0L。原创 2024-11-03 09:59:30 · 319 阅读 · 0 评论 -
HashMap的实现原理
HashMap是基于哈希表的Map接口的实现。此实现提供所有可选的映射操作,并允许使用null值和null键。(除了不同步和允许使用null之外,HashMap类与Hashtable大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。值得注意的是HashMap不是线程安全的,如果想要线程安全的HashMap,可以通过Collections类的静态方法synchronizedMap获得线程安全的HashMap。在JDK1.7中,HashMap的底层是基于数组和链表来实现的原创 2024-11-03 10:00:00 · 962 阅读 · 0 评论 -
java中IO流
流的概念和作用流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。IO流的分类根据处理数据类型的不同分为:字符流和字节流根据数据流向不同分为:输入流和输出流字符流的由来:因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。非纯文本文件,不能用字符流,会导致文件格式破坏,不能正常执行字节流和字符流的区别。原创 2024-11-02 19:43:25 · 866 阅读 · 0 评论 -
Java中常见的异常类型
NullPointerException是未检查异常,即使使用throw抛出了未检查异常,若没有使用try-catch进行捕获处理,或者没有使用throws声明异常,也不会有错误提示。throw语句用在方法体内,表示抛出异常(已检查异常),由方法体内的语句处理,处理的方式有两种:要么使用try-catch捕获异常,在方法体内部自己处理,要么使用throws声明抛出这个异常,交给调用者处理(也可以将try-catch与throws结合使用)。其中的Exception又分为已检查异常和未检查异常。原创 2024-11-02 18:46:13 · 783 阅读 · 0 评论 -
创建对象的方式
3、Class类的newInstance需要其构造方法是共有的或者对调用方法可见的,而构造器类的newInstance可以在特定环境下调用私有构造方法来创建对象。4、Class类的newInstance抛出类构造函数的异常,而构造器类的newInstance包装了一个InvocationTargetException异常。2、Class类的newInstance只能触发无参数的构造方法创建对象,而构造器类的newInstance能触发有参数或者任意参数的构造方法来创建对象。原创 2024-11-02 16:39:09 · 609 阅读 · 0 评论 -
对象拷贝(克隆)
由测试结果可以发现原始对象stu和克隆对象stu2不是同一对象,但是两个对象的name属性和classes属性是同一个对象,并且原始对象stu和克隆对象stu2的属性值是相等的,这也验证了“Java的克隆机制是对类的实例的属性逐一复制”。name属性和classes同为引用类型的实例,克隆后原始对象stu和克隆对象stu2的name属性和classes属性是同一对象,说明没有实现Cloneable接口的类(如此处的String和Classes)的实例克隆是通过传引用实现的,即内层对象没有克隆。原创 2024-11-02 16:32:50 · 623 阅读 · 0 评论 -
序列化和反序列化
举个不是很恰当的例子:比如在淘宝上买桌子,桌子这种很不规则的东西,该怎么从一个城市运输到另一个城市,这时候一般都会把它拆掉成板子,再装到箱子里面,就可以快递寄出去了,这个过程就类似我们的序列化的过程(把数据转化为可以存储或者传输的形式)。比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。原创 2024-11-02 16:24:54 · 708 阅读 · 0 评论 -
反射简介及简单使用
在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取类的信息以及动态调用对象的方法的功能就称为java语言的反射机制。例如,User类中的。想要使用反射机制,就必须要先获取到该类的字节码文件对象,通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(方法,属性,类名,父类名,实现的所有接口等等),每一个类对应着一个字节码文件,也就对应着一个Class类型的对象,也就是字节码文件对象。原创 2024-11-02 16:16:17 · 745 阅读 · 0 评论 -
接口和抽象类有什么区别
当多个对象都拥有相同的行为,但是行为的具体实现方式不一样时,可以将这些共同行为的定义向上抽取放在接口中,让多个对象实现接口,行为的具体实现方式在不同的对象中可以具有不同的实现。但是,在jdk 1.8中,在接口中增加了一些新的特性,在接口中允许有静态方法和默认方法,即接口中可以有方法的具体实现,接口与抽象类之间的区别越来越小。当多个对象都拥有相同的行为,且行为的实现方式都一样时,可以将这些共同行为的实现方式向上抽取成模板方法放在抽象类中,让多个对象继承抽象类,复用模板方法。原创 2024-11-02 15:46:11 · 156 阅读 · 0 评论 -
int的取值范围
21474836477(-(2^31-1))减1就是-21474836478(-(2^31)),而-21474836477的补码减1就是10000000 00000000 00000000 00000000,因此-21474836478的补码是10000000 00000000 00000000 00000000。整型数字在计算机中用补码表示,取值范围是-(2^31)~(2^31-1),即-21474836478~2147483647。1+(-1)按照反码就是-0(32个1,最高位的1表示负号)。原创 2024-11-02 15:44:58 · 330 阅读 · 0 评论 -
String、StringBuffer和StringBuilder的区别
由于String是不可变类,每次在修改String类型的变量时,都会在运行时常量池或堆内存中创建新的对象。如下所示,在栈内存中创建了字符串类型的引用变量str,在常量池中创建了"abc"字符串对象,并将str指向了"abc"对象。所以,在修改字符串对象时,需要创建新的对象,同时可能销毁不用的对象,所以效率比较低。对StringBuilder和StringBuffer对象的修改是在原来对象上修改的,不会创建新的对象,所以速度要比String快很多。因为String是不可变类,所有不可变类都是线程安全的。原创 2024-11-02 15:41:11 · 317 阅读 · 0 评论 -
final、finally、finalize的作用和区别
是方法名,在object类中定义的,因此所有的类都继承了它。finalize()方法是在垃圾收集器清除对象之前对这个对象调用的,即在GC准备释放对象所占用的内存空间之前,它将首先调用finalize()方法,进行一些清理工作。finalize()调用的时机:在Java中,由于GC的自动回收机制,因而并不能保证finalize方法会被及时地执行(垃圾对象的回收时机具有不确定性),也不能保证它们会被执行(程序由始至终都未触发垃圾回收)。(3)被final修饰的类被称为最终类,该类不能被其他类继承。原创 2024-11-02 15:39:07 · 228 阅读 · 0 评论 -
==和equals、hashCode方法
此处对HashMap的知识点做一个简单的介绍:在jdk1.7及以前版本,HashMap底层是用数组和链表的形式实现的,数组中存放的是桶,桶中存放的是链表的头结点,链表是用来解决哈希冲突的。有的面试官会问到“equals方法与hashCode方法有什么区别”,由上面分析可知,equals方法默认是比较两个对象在内存中的地址值是否相等,即两个对象是不是同一个对象,而hashCode方法默认是获取对象在内存中的地址值,根本不是对两个对象进行比较,因此,这两个方法的功能都不一样,无可比性。原创 2024-11-02 15:37:44 · 686 阅读 · 0 评论 -
JVM、JRE、JDK区别和联系
JVM的主要作用是将class文件中的二进制数据加载到运行时数据区的方法区,在堆区生成相应的java.lang.Class对象,并结合本地接口,通过执行引擎执行java程序。编写的java源程序(.java格式的文件)需要被编译为类文件(.class格式的文件),才能被java虚拟机识别(JVM只能识别class文件),JVM识别class文件中的字节码指令并调用操作系统向上的API完成动作。JVM负责运行字节码:JVM把每一条要执行的字节码交给解释器,翻译成对应的机器码,然后由解释器执行。原创 2024-11-02 15:32:46 · 339 阅读 · 0 评论