最近辞职在家复习准备面试,顺便整理一份发出来分享给大家
01. 你对面向对象思想的理解?
面向对象编程简称 OOP,是开发程序的一种方法、思想。
面向过程编程中常常会导致所有的代码都在一起,难以阅读和维护,牵一动百。而 OOP,使用许多代码模块,每个模块都只提供特定的功能,彼此独立,可以增加代码重用几率,更加有利于软件的开发、维护和升级。
另外 OOP 的三大核心特性:继承、封装、多态 的特性,使得程序员能够设计出 高内聚、低耦合 的系统结构,使得系统更灵活、易扩展,成本较低
02. 很多程序员都知道多态,但大都知其然不知其所以然,说说你对多态的理解
一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,在编程时并不确定,而是在程序运行期间才确定。
因为在程序运行时才确定具体的类,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性
多态的优点:解耦、灵活、可扩展性强
多态存在的三个必要条件:继承、重写、父类引用指向子类对象
03. Collection 集合有什么子类
List
1. 可以允许重复的对象。
2. 可以插入多个 null 元素。
3. 有序,保持元素的插入顺序
4. 常用的实现类有 ArrayList、LinkedList 和 Vector。ArrayList 最为流行,它提供了使用索引的随意访问,而 LinkedList 则对于经常需要从 List 中添加或删除元素的场合更为合适。
Set
1. 不允许重复对象
2. 无序
3. 只允许一个 null 元素
4.Set 接口最流行的几个实现类是 HashSet、LinkedHashSet 以及 TreeSet。最流行的是基于 HashMap 实现的 HashSet;TreeSet 还实现了 SortedSet 接口。
Map
1.Map 不是 collection 的子接口或者实现类。Map 是一个接口。
2.Map 可能会持有相同的值对象但键对象必须是唯一的。
3.TreeMap 也通过 Comparator 或者 Comparable 维护了一个排序顺序。
4.Map 里你可以拥有随意个 null 值但最多只能有一个 null 键。
Map 接口最流行的几个实现类是 HashMap、LinkedHashMap、Hashtable 和 TreeMap。(HashMap、TreeMap 最常用)
04. list,set,map 的使用场景
1. 如果你经常会使用索引来对容器中的元素进行访问,那么 List 是你的正确的选择。如果你已经知道索引了的话,那么 List 的实现类比如 ArrayList 可以提供更快速的访问, 如果经常添加删除元素的,那么肯定要选择 LinkedList。
2. 如果你想容器中的元素能够按照它们插入的次序进行有序存储,那么还是 List,因为 List 是一个有序容器,它按照插入顺序进行存储。
3. 如果你想保证插入元素的唯一性,也就是你不想有重复值的出现,那么可以选择一个 Set 的实现类,比如 HashSet、LinkedHashSet 或者 TreeSet。
4. 如果你以键和值的形式进行数据存储那么 Map 是你正确的选择。你可以根据你的后续需要从 Hashtable、HashMap、TreeMap 中进行选择。
05. ArrayList 和 HashMap 的比较
使用场景:如果需要快速随机访问元素,应该使用 ArrayList。需要键值对形式的数据时,应该使用 HashMap
相同点:
1)都是线程不安全,不同步
2)都可以储存 null 值
3)获取元素个数方法一样,都用 size()方法获取
区别:
1)实现的接口
ArrayList 实现了 List 接口(Collection(接口)->List(接口)->ArrayList(类)),底层使用的是数组;而 HashMap 现了 Map 接口(Map(接口)->HashMap(类)),底层使用的是 Hash 算法存储数据。2)存储元素
ArrayList 以数组的方式存储数据,里面的元素是有顺序,可以重复的;而 HashMap 将数据以键值对的方式存储,键的哈希码(hashCode)不可以相同,相同后面的值会将前面的值覆盖,值可以重复,里面的元素无序。3)添加元素的方法
ArrayList 用 add(Object object)方法添加元素,而 HashMap 用 put(Object key, Object value)添加元素。4)默认的大小和扩容
在 Java 7 中,ArrayList 的默认大小是 10 个元素,HashMap 的默认大小是 16 个元素(必须是 2 的幂)。
- 多线程创建方式有哪几种(具体创建过程是什么)
1. 继承 thread 类,重写 run 方法,然后 new Thread().start 启动线程,也可以直接匿名内部类的方式创建
new Thread(
public void run(){};
).start;
在里边写 run 方法的实现
2. 实现 runnable 接口,重写 run 方法
3. 实现 callable 接口(一般很少用)
07. 多线程的常见应用场景:
1、后台定时任务,例如:定时向大量(100w 以上)的用户发送邮件;
2、异步处理,例如:发微博、记录日志等;
3、分布式计算消息队列
08. 线程有哪几种状态
1. 创建状态 刚被 new Thread()
2. 就绪状态 准备好了 run 方法,等待 cpu
3. 运行状态 running
4. 阻塞状态(包括 sleep、wait(notify)、suspend(resume)、以及没抢到锁)
5. 死亡状态
09. 线程池初始化时有哪些参数可以设置
一般 tomcat 线程池不用自己配,自己写线程池的话是需要自己配置参数的
那 线程初始化时设置参数的格式是:
private static final int corePoolSize = xxx;
创建线程池对象时,将配置的各种参数用逗号连接传参进去。
ThreadPoolExecutor mExecute = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
workQueue,
threadFactory,
rejectHandler
);
各个参数代表的意义:
corePoolSize
线程池的核心线程数。在没有设置 allowCoreThreadTimeOut 为 true 的情况下,核心线程会在线程池中一直存活,即使处于闲置状态。maximumPoolSize
线程池所能容纳的最大线程数。大于这个数调用任务拒绝 handler
keepAliveTime
非核心线程闲置时的超时时长。超过该时长,非核心线程就会被回收。unit
keepAliveTime 时长对应的单位。workQueue
线程池中的任务队列,当有任务没有足够的线程去执行的时候,就会进入任务队列等待
RejectedExecutionHandler
如果线程池中线程数大于 maximumPoolSize 则使用 RejectedExecutionHandler 来进行任务拒绝处理。
线程池的种类 (4 种) 区别和使用场景
首先,不同线程池之间的区别就是他们新建时传入的参数值不一样(新建方法是一样的)
1.newFixedThreadPool,线程数量固定的线程池,线程池的 corePoolSize 和 maximumPoolSize 大小一样,并且 keepAliveTime 为 0,传入的队列 LinkedBlockingQueue 为无界队列
2.newSingleThreadExecutor,单线程池,corePoolSize 和 maximumPoolSize 都是 1,keepAliveTime 是 0
3.newCachedThreadPool,可缓存线程池,说到缓存一般离不开过期时间,该线程池也是,corePoolSize 设置为 0,maximumPoolSize 设置为 int 最大值,不同的是,线程池传入的队列是 SynchronousQueue,一个同步队列,该队列没有任何容量,每次插入新数据,必须等待消费完成。当有新任务到达时,线程池没有线程则创建线程处理,处理完成后该线程缓存 60 秒,过期后回收,线程过期前有新任务到达时,则使用缓存的线程来处理。
4.newScheduledThreadPool,这个线程池使用了 ScheduledThreadPoolExecutor,该线程池继承自 ThreadPoolExecutor, 执行任务的时候可以指定延迟多少时间执行,或者周期性执行。
Thread 类中的 start()和 run()方法有什么区别
start 启动线程,run 方法只是 thread 的一个普通方法,就算调用了也还是在主线程里执行
同步有几种实现方法?
同步的实现方面有两种,使用 synchronized 同步锁、volatile 关键字、lock 锁对象的 lock()和 unlock()方法
volatile 关键字
Java 语言中的 volatile 变量可以被看作是一种“程度较轻的 synchronized”
锁提供了两种主要特性:互斥(mutual exclusion)和可见性(visibility)。
互斥指一个线程拿到了锁,其他线程就必须等待完成。
可见性要表示一个线程改完了一个公共数据(实际上是先改自己的缓存数据,然后同步到公共数据上,然后另外的线程看到公共数据变了,将变换的值同步到自己的缓存,这个过程有好几部,所以会有线程安全问题),必须要保证其他线程知道数据改变了
使用 volatile 变量的主要原因用法简单,在公共数据上加一个 volatile 关键字就行,其次是性能优于 sychronize 同步锁
线程产生死锁的原因和解决办法:
什么是死锁:打个比方,假设有 P1 和 P2 两个进程,都需要 A 和 B 两个资源,现在 P1 持有 A 等待 B 资源,而 P2 持有 B 等待 A 资源,两个都等待另一个资源而不肯释放资源,就这样无限等待中,这就形成死锁
定义:如果一组进程中每一个进程都在等待仅由该组进程中的其他进程才能引发的事件,那么该组进程是死锁的。
产生死锁原因:一种原因是系统提供的资源太少了,远不能满足并发进程对资源的需求。这种竞争资源引起的死锁是我们要讨论的核心;
另一种原因是由于进程推进顺序不合适引发的死锁。资源少也未必一定产生死锁。就如同两个人过独木桥,如果两个人都要先过,在独木桥上僵持不肯后退,必然会应竞争资源产生死锁;但是,如果两个人上桥前先看一看有无对方的人在桥上,当无对方的人在桥上时自己才上桥,那麽问题就解决了。所以,如果程序设计得不合理,造成进程推进的顺序不当,也会出现死锁。
解决死锁问题的三种方法:预防死锁、检测死锁及避免死锁。
解决办法:预防死锁的发生往往需要很大的系统开销,而且不能充分利用资源,为此,一种简便的方法是系统为进程分配资源时,不采取任何限制性措施,但是提供了检测和解脱死锁的手段
核心思想:打破线程间的相互等待状态,比如
1. 找到相互等待的线程的 spid,kill 掉
2. 使用 sql server 内部的锁监视器线程执行死锁检查,当检测到死锁时,回滚事务以及该事务持有的锁,使得其他线程得以正常运行
多线程有几种实现方法?
多线程有三种实现方法,分别是继承 Thread 类、实现 Runnable 接口、实现 Callable 接口
Callable 和 Runnable 的区别是什么
(1)Callable 规定的方法是 call(),Runnable 规定的方法是 run().
(2)Callable 的任务执行后可返回值,而 Runnable 的任务是不能返回值得
(3)call 方法可以抛出异常,run 方法不可以
(4) 运行 Callable 任务可以拿到一个 Future 对象,Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,如果线程没有执行完,Future.get()方法可能会阻塞当前线程的执行;如果线程出现异常,Future.get()会 throws InterruptedException 或者 ExecutionException;如果线程已经取消,会跑出 CancellationException。取消由 cancel 方法来执行。isDone 确定任务是正常完成还是被取消了。一旦计算完成,就不能再取消计算。如果为了可取消性而使用 Future 但又不提供可用的结果,则可以声明 Future<?> 形式类型、并返回 null 作为底层任务的结果。
IO 流,了解常见的几个流对象以及基本的流操作即可
数组集合
什么是连接池,为什么使用连接池
什么是接口隔离?
接口跟抽象类的区别?
hashcode 和 equal 的区别
hashmap 和 hashtable 的区别是什么?
hashmap 实现原理,扩容因子过大过小的缺点,扩容过程
ArrayList 和 linkenList 的区别?
java 地址和值传递的例子
值传递:只传递值,各是各的
引用传递:传递地址,共同操作这个值
Linux 常用命令列举 10 个
ls/ll 显示文件夹下所有文件 / 详细显示
pwd 查看当前目录的绝对路径
cd 切换目录
ps 查看进程
kill 杀死进程
mv 移动文件
mkdir 创建文件夹
rmdir 删除文件夹
touch 创建文件
tar zxvf 解压
date 显示日期
cal 显示日历
vi 用 vim 编辑文件
clear 清屏
synchronized 实现原理
synchronizated 和 lock 差别?
java Nio
是否可以继承 String 类?
String 类是 final 类故不可以继承。
几种线程暂停
a. wait(): 使一个线程处于等待状态,并且释放持有的锁
b. sleep(): 使一个正在运行的线程处于睡眠状态,时间过了自动会醒,所以不释放锁
c. notify(): 唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确
切的唤醒某一个等待状态的线程,而是由 JVM 确定唤醒哪个线程,而且不是按优先级。
d. allnotity(): 唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。
启动一个线程是用 run()还是 start()?
启动一个线程是调用 start()方法,使线程就绪状态,以后可以被调度为运行状态,一个线程必须关联一些具体的执行代码,run()方法是该线程所关联的执行代码。
SOCKET 套接字中有几中连接方式,各有什么区别?
Sockets 有两种主要的操作方式: 面向连接 (TCP/IP) 的和无连接 (UDP) 的。无连接的操作使用数据报协议,无连接的操作是快速的和高效的, 但是数据安全性不佳. 面向连接的操作使用 TCP 协议. 面向连接的操作比无连接的操作效率更低, 但是数据的安全性更高
sleep()和 wait()区别
sleep() 方法:线程主动放弃 CPU,使得线程在指定的时间内进入阻塞状态,不能得到 CPU 时间,指定的时间一过,线程重新进入可执行状态。典型地,sleep() 被用在等待某个资源就绪的情形:测试发现条件不满足后,让线程阻塞一段时间后重新测试,直到条件满足为止。
wait():与 notify() 配套使用,wait()使得线程进入阻塞状态,它有两种形式,一种允许指定以毫秒为单位的一段时间作为参数,另一种没有参数,当指定时间参数时对应的 notify() 被调用或者超出指定时间时线程重新进入可执行状态,后者则必须对应的 notify() 被调用
hashCode 方法的作用?
hashcode 这个方法是用来鉴定 2 个对象是否相等的。hashcode 方法一般用户不会去调用,比如在 hashmap 中,由于 key 是不可以重复的,他在判断 key 是不是重复的时候就判断了 hashcode 这个方法,而且也用到了 equals 方法。这里不可以重复是说 equals 和 hashcode 只要有一个不等就可以了!所以简单来讲,hashcode 相当于是一个对象的编码。我们一般在覆盖 equals 的同时也要覆盖 hashcode,让他们的逻辑一致。
简述 synchronized 和 java.util.concurrent.locks.Lock 的异同?
主要相同点:Lock 能完成 synchronized 所实现的所有功能
主要不同点:Lock 有比 synchronized 更精确的线程语义和更好的性能。synchronized 会自动释放锁,而 Lock 一定要求程序员手工释放,并且必须在 finally 从句中释放。Lock 还有更强大的功能,例如,它的 tryLock 方法可以非阻塞方式去拿锁。
Java 字节码的执行有两种方式:
1)即时编译方式:解释器先将字节编译成机器码,然后再执行该机器码。2)解释执行方式:解释器通过每次解释并执行一小段代码来完成 java 字节 码程序的所有操作。
Java 四种引用包括强引用,软引用,弱引用,虚引用
2. 获取 Class 的方式有哪些
方式一:对象.getClass()
Student student = new Student();
student.getClass();
方式二:类名.Class
Student.Class
方式三:Class.forname(“ 完整的类路径. 包名. 类名 ”)
3.int 和 Integer 的区别
基础答案:Ingeter 是 int 的包装类,int 的初值为 0,Ingeter 的初值为 null。
进阶答案:Ingeter 将 -128-127 进行缓存,因此
4.ArrayList 和 LinkedList 的区别
1.ArrayList 是实现了基于动态数组的数据结构,LinkedList 基于链表的数据结构。
2. 对于随机访问 get 和 set,ArrayList 觉得优于 LinkedList,因为 LinkedList 要移动指针。
3. 对于新增和删除操作 add 和 remove,LinedList 比较占优势,因为 ArrayList 要移动数据。
5.== 和 equals 的区别
1)对于 ==,比较的是值是否相等
如果作用于基本数据类型的变量,则直接比较其存储的“值”是否相等;
如果作用于引用类型的变量,则比较的是所指向的对象的地址
2)对于 equals 方法,注意:equals 方法不能作用于基本数据类型的变量
如果没有对 equals 方法进行重写,则比较的是引用类型的变量所指向的对象的地址;
诸如 String、Date 等类对 equals 方法进行了重写的话,比较的是所指向的对象的内容。
6.override 和 overload 的比较
重载 Overload:在同一个类中,允许存在一个以上的同名函数,只要他们的参数个数或者参数类型不同即可。
重载的特点:与返回值类型无关,只看参数列表。
重写 Override 表示子类中的方法可以与父类中的某个方法的名称和参数完全相同,通过子类创建的实例对象调用这个方法时,将调用子类中定义的方法,这相当于把父类中定义的那个完全相同的方法给覆盖掉了,这也是面向对象编程的多态的一种表现。子类覆盖父类方法时只能抛出父类的异常或者异常的子类或者父类异常的子集,因为子类可以解决父类的一些问题,但不能比父类有更多的问题。还有,子类方法的访问权限只能比父类的更大,不能更小。如果父类的方法是 private 类型,则子类中根本不存在覆盖,即子类中和父类的 private 的同名的方法没有覆盖的关系,因为 private 的访问权限只限于同一类中,而子类就不会访问到 private 的方法,所以是子类中增加的一个全新的方法
7. 简述 servlet 生命周期
(1)加载和实例化
当 Servlet 容器启动或客户端发送一个请求时,Servlet 容器会查找内存中是否存在该 Servlet 实例,若存在,则直接读取该实例响应请求;如果不存在,就创建一个 Servlet 实例。
(2)初始化
实例化后,Servlet 容器将调用 Servlet 的 init()方法进行初始化(一些准备工作或资源预加载工作)。
(3)服务
初始化后,Servlet 处于能响应请求的就绪状态。当接收到客户端请求时,调用 service()的方法处理客户端请求,HttpServlet 的 service()方法会根据不同的请求 转调不同的 doXxx()方法。
(4)销毁
当 Servlet 容器关闭时,Servlet 实例也随时销毁。其间,Servlet 容器会调用 Servlet 的 destroy()方法去判断该 Servlet 是否应当被释放(或回收资源)。
9.farward 和 redirect 区别
转发(Forward),是一次请求,只有一个 request,因此 request 域内的数据可以共享。
重定向(Redirect)实际是两次 HTTP 请求,服务器端在响应第一次请求的时候,让浏览器再向另外一个 URL 发出请求,从而达到转发的目的。
10.final finally finalize 区别
Java 提供 finalize() 方法,垃圾回收器准备释放内存的时候,会先调用 finalize()。
11. 乐观锁与悲观锁
悲观锁
总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java 中 synchronized 等独占锁就是悲观锁思想的实现。
乐观锁
总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和 CAS 算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于 write_condition 机制,其实都是提供的乐观锁。在 Java 中 java.util.concurrent.atomic 包下面的原子变量类就是使用了乐观锁的一种实现方式 CAS 实现的。
两种锁的使用场景
冲突真的很少发生的时候,用乐观锁就比较合适。
经常产生冲突,用悲观锁就比较合适。
12.String、StringBuffer 和 StringBuilder 的区别
在线程安全:StringBuilder 是线程不安全的,而 StringBuffer 是线程安全的
运行速度:StringBuilder > StringBuffer > String
13.char 可以存储汉字吗为什么
char 是按照字符存储的,不管英文还是中文 占用占用 2 个字节,用来储存 Unicode 字符 unicode 编码字符集中包含了汉字,所以,char 型变量中当然可以存储汉字啦。
15. 简述 HashMap 的实现原理
16.HashMap 和 HashTable 的区别
线程不安全和线程不安全
键值可为 null 键值不可为 null
前者快后者慢
我们能否让 HashMap 同步?
HashMap 可以通过下面的语句进行同步:
Map m = Collections.synchronizeMap(hashMap);
18.a=a+ b 与 a += b 的区别
举例:a 为 int,b 为 float 则 a = a + b 需要强制类型转换,也就是我们常写的 a = (int) (a+b);
而我们的 a += b 被我们的编译器在编译期做了一些小手脚。也就是编译器帮我们进行了强制类型转化。
& 和 && 的区别
& 和 && 都可以用作逻辑与的运算符
&& 还具有短路的功能
& 还可以用作位运算符
深拷贝和浅拷贝的区别
打个不太恰当的比喻:一个是复制一份新的 一个是弄了一个快捷方式
Java 的八大基本数据类型是什么
byte short int long float double boolean char
进程、线程和协程的区别
1) 一个线程可以多个协程,一个进程也可以单独拥有多个协程
2) 线程进程都是同步机制,而协程则是异步
3) 协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态
创建两种线程的方式 他们有什么区别
1. 继承 Thread 类
2. 实现 Runnable 接口
Array(数组)和 ArrayList(列表)有什么区别
1)精辟阐述:
可以将 ArrayList 想象成一种“会自动扩增容量的 Array”。
2)Array([]):数组,最高效;但是其容量固定且无法动态改变;
ArrayList:动态数组,容量可动态增长;但牺牲效率;
Runnable 和 Callable 的区别
编写多线程程序一般有三种方法,Thread,Runnable,Callable.
Runnable 和 Callable 的区别是,
(1)Callable 规定的方法是 call(),Runnable 规定的方法是 run().
(2)Callable 的任务执行后可返回值,而 Runnable 的任务是不能返回值得
(3)call 方法可以抛出异常,run 方法不可以
wait(),notify()和 suspend(),resume()之间的区别
wait(),notify() 有两种形式一种是传入一个时间参数,自动恢复,一种是不传参,不会自动恢复,必须使用 notify()方法
suspend(),resume():suspend() 方法很容易引起死锁问题,已经不推荐使用了。
default,public,private,protected 区别
tomcat 调优
声明式事务
接口隔离
代码块、静态代码块执行顺序
Java 子父类间静态代码块、非静态代码块、构造方法的执行顺序
子类 A 继承父类 B,A a=new A();
正确的执行顺序是: 父类B静态代码块 -> 子类A静态代码块 -> 父类B非静态代码块 -> 父类B构造函数 -> 子类A非静态代码块 -> 子类A构造函数
也就是说非静态初始化块的执行顺序要在构造函数之前。
如何捕获子线程异常