文章目录
- 一、Java 基础
- 1.JDK 和 JRE 有什么区别?
- 2.== 和 equals 的区别是什么?
- 3.两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?
- 4.final 在 java 中有什么作用?
- 5.构造器 Constructor 是否可被 override?
- 6.重载和重写的区别?
- 7.在 Java 中定义⼀个不做事且没有参数的构造⽅法的作⽤?
- 8.java 中的 Math.round(-1.5) 等于多少?
- 9.String 属于基础的数据类型吗?
- 10.java 中操作字符串都有哪些类?它们之间有什么区别?
- 11.String str="i"与 String str=new String(“i”)一样吗?
- 12.如何将字符串反转?
- 13.String 类的常用方法都有那些?
- 14.普通类和抽象类有哪些区别?
- 15.抽象类能使用 final 修饰吗?
- 16.接口和抽象类有什么区别?
- 17.java 中 IO 流分为几种?
- 18.BIO、NIO、AIO 有什么区别?
- 二、集合
- 1.java 集合都有哪些?
- 2.Collection 和 Collections 有什么区别?
- 3.List、Set、Map 之间的区别是什么?
- 4.HashMap 和 Hashtable 有什么区别?
- 5.如何决定使用 HashMap 还是 TreeMap?
- 6.说一下 HashMap 的实现原理?
- 7.说一下 HashSet 的实现原理?
- 8.HashSet如何检查重复?
- 9.ArrayList 和 LinkedList 的区别是什么?
- 10.如何实现数组和 List 之间的转换?
- 11.ArrayList 和 Vector 的区别是什么?
- 12.Array(数组) 和 ArrayList 有何区别?
- 13.在 Queue 中 poll()和 remove()有什么区别?
- 14.哪些集合类是线程安全的?
- 15.Iterator 怎么使用?有什么特点?
- 16.Iterator 和 ListIterator 有什么区别?
- 17.怎么确保一个集合不能被修改?
- 三、多线程
- 1.并行和并发有什么区别?
- 2.线程和进程的区别?
- 3.守护线程是什么?
- 4.创建线程有哪几种方式?
- 5.说一下 runnable 和 callable 有什么区别?
- 6.线程有哪些状态?
- 7.sleep() 和 wait() 有什么区别?
- 8.notify()和 notifyAll()有什么区别?
- 9.线程的 run()和 start()有什么区别?
- 10.创建线程池有哪几种方式?
- 11.线程池都有哪些状态?
- 12.线程池中 submit()和 execute()方法有什么区别?
- 13.在 java 程序中怎么保证多线程的运行安全?
- 14.什么是死锁?
- 15.怎么防止死锁?
- 16.ThreadLocal 是什么?有哪些使用场景?
- 17.说一下 synchronized 的使用?
- 18.synchronized 和 volatile 的区别是什么?
- 19.synchronized 和 Lock 有什么区别?
- 20.synchronized 和 ReentrantLock 区别是什么?
- 四、反射
- 五、对象拷贝
- 六、Java Web
- 七、异常
- 八、网络
- 九、Spring/Spring MVC
- 十、Spring Boot/Spring Cloud
- 十一、Mybatis
- 十二、MySql
- 1.什么是sql注入?如何避免 sql 注入?
- 2.数据库的三范式是什么?
- 3.一张自增表里面总共有 7 条数据,删除了最后 2 条数据,重启 mysql 数据库,又插入了一条数据,此时 id 是几?
- 4.如何获取当前数据库版本?
- 5.说一下 ACID 是什么?
- 6.char 和 varchar 的区别是什么?
- 7.mysql 的内连接、左连接、右连接有什么区别?
- 8.怎么验证 mysql 的索引是否满足需求?
- 9.说一下数据库的事务隔离?
- 10.说一下 mysql 常用的引擎?
- 11.说一下 mysql 的行锁和表锁?
- 12.说一下乐观锁和悲观锁?
- 13.MySQL 问题排查都有哪些手段?
- 14.如何做 mysql 的性能优化?
- 十三、Redis
- 十四、JVM
一、Java 基础
1.JDK 和 JRE 有什么区别?
JRE(Java Runtime Environment,Java运行环境):JRE中包含Java虚拟机与Java程序所需的核心类库等,如果要运行已经开发好的Java程序,计算机中只需要安装JRE即可。
JDK(Java Development Kit,Java开发工具包):JDK是提供给Java开发人员使用的,其中包含了Java的开发工具包,也包含了JRE。
用JDK开发完成的Java程序,交给JRE运行,由JVM保证跨平台。
2.== 和 equals 的区别是什么?
==:如果比较的是基本数据类型,则会比较其值是否相等;如果比较的是引用数据类型,则会比较其内存首地址是否相同。
equals:只能比较引用数据类型。如果类没有重写equals()方法,等价于"=="比较这两个对象(因为Object类中的equals()方法默认是使用 == 比较的,而Object是顶层基类),如果重写了equals()方法,一般都会用来比较这两个对象的内容是否相等。
3.两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?
不对。 类的hashCode方法和equals方法都可以重写,返回的值可以自己定义。
4.final 在 java 中有什么作用?
如果final修饰的类,则该类不能被继承;
如果final修饰的是方法,则该方法不能被重写;
如果final修饰的是变量,则该变量为常量,初始化之后不能再次修改。
5.构造器 Constructor 是否可被 override?
Constructor 不能被 override(重写),但是可以 overload(重载),所以你可以看到⼀个类中有多个 构造函数的情况。
6.重载和重写的区别?
重载:在同一个类中,有多个方法,⽅法名相同,但参数列表不相同,便产生了重载,重载与方法返回值及权限修饰符无关。
区别:
重载是一个类中的同名方法能够根据输⼊数据的不同,做出不同的处理;
重写是当⼦类继承⾃⽗类的相同⽅法,输⼊数据⼀样,但要做出不同的响应时,你就要覆盖⽗类⽅法。
7.在 Java 中定义⼀个不做事且没有参数的构造⽅法的作⽤?
Java 程序在执⾏⼦类的构造⽅法之前,如果没有⽤ super() 来调⽤⽗类特定的构造⽅法,则会调⽤⽗类中“没有参数的构造⽅法”。因此,如果⽗类中只定义了有参数的构造⽅法,⽽在⼦类的构造⽅法中⼜没有⽤ super() 来调⽤⽗类中特定的构造⽅法,则编译时将发⽣错误,因为 Java 程序在⽗类中找不到没有参数的构造⽅法可供执⾏。解决办法是在⽗类⾥加上⼀个不做事且没有参数的构造⽅法。
8.java 中的 Math.round(-1.5) 等于多少?
-1。
Math.round()方法会返回最接近参数的整数(四舍五入),其等价于(int)Math.floor(a + 0.5f)。
9.String 属于基础的数据类型吗?
不属于。
Java中的基本数据类型只有八种:short、byte、int、long、float、double、char、boolean
10.java 中操作字符串都有哪些类?它们之间有什么区别?
Java中操作字符串的类有String、StringBuffer、StringBuilder。
String声明的是不可变字符串对象,每次操作字符串时,都会创建一个新的String对象,而后将引用指向它,所以String只适合用在字符串内容不常改变的情况下。
StringBuffer与StringBuilder声明的是字符串缓冲区对象,在操作字符串时,都是在原有字符串对象的基础上操作的。
而StringBuffer与StringBuilder的最大区别是:
StringBuffer是线程安全的,速度较慢,适合在多线程条件下用于字符串内容经常改变的情况;
StringBuilder是线程不安全的,速度较快,适合在单线程条件下用于字符串经常改变的情况。
11.String str="i"与 String str=new String(“i”)一样吗?
不一样。
在String str = "i"中,把i值存在常量池,再把地址赋给str。假设再写一个String s = “i”,则会再次把i的地址赋给str2,但是i对象不会重新创建,他们引用的是同一个地址值,共享同一个i的内存。
而String str = new String(“i”),则是直接创建一个新的i对象,然后将新对象的地址值赋给str引用。
12.如何将字符串反转?
可以调用StringBuilder/StringBuffer类的reverse()方法。
13.String 类的常用方法都有那些?
indexOf()、length()、getBytes()、split()、equals()、charAt()、subString()。
14.普通类和抽象类有哪些区别?
抽象类中可以有普通方法,也可以有抽象方法,普通类中只能有普通方法,不能有抽象方法。
抽象类不能直接实例化,普通类可以直接实例化。
15.抽象类能使用 final 修饰吗?
不能。
抽象类不能被实例化,其只关心子类具有的功能,而不是功能的具体实现,如果被final修饰,则其不能被继承,抽象类中的方法就失去了意义。
16.接口和抽象类有什么区别?
接口中只能有抽象方法,抽象类中可以有抽象方法,也可以有普通方法;
接口中的变量必须被 static final 修饰,抽象类中的变量可以被任意修饰符修饰;
接口中的方法默认被 public 修饰,抽象类中的方法可以被任意权限修饰符修饰;
抽象类中可以有构造方法,接口中不能有构造方法;
抽象类只能被单继承,接口可以被多实现(接口本身可以通过extends关键字扩展多个接口)。
17.java 中 IO 流分为几种?
按功能来分:输入流、输出流
按类型来分:字符流、字节流
18.BIO、NIO、AIO 有什么区别?
BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。
NIO:New IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。
AIO:Asynchronous IO 是 NIO 的升级,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。
二、集合
1.java 集合都有哪些?
List:ArrayList、LinkedList、Vector、Stack
Set:HashSet、TreeSet、LinkedHashSet
Map:HashMap、LinkedHashMap、TreeMap、Hashtable、ConcurrentHashMap
2.Collection 和 Collections 有什么区别?
Collection 是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法,所有单列集合都是它的子类,比如 List、Set 等。
Collections 是一个工具类,包含了很多静态方法,不能被实例化。
3.List、Set、Map 之间的区别是什么?
List是有序、可重复的单列集合;
Set是无序、不可重复的单列集合;
Map是键不可重复,值可重复的双列集合。
4.HashMap 和 Hashtable 有什么区别?
HashMap中去掉了Hashtable中的contains()方法,但是加入了containsKey()与containsValue()方法;
HashMap是线程不安全的,速度较快;Hashtable是线程安全的,速度较慢;
Hashtable中不能有null键和null值;HashMap中可以有null键和null值,但是null键只能有一个。
5.如何决定使用 HashMap 还是 TreeMap?
如果需要得到得到一个有序的结果就使用TreeMap(HashMap得到的结果排列顺序是不确定的),因为TreeMap的key值是要求实现Comparable接口,所以迭代的时候是会按照key值排序的,但如果对排序没有要求的话,就使用HashMap,HashMap的效率会比较高。
6.说一下 HashMap 的实现原理?
HashMap 基于 Hash 算法实现的,我们通过 put(key,value)存储,get(key)来获取。当传入 key 时,HashMap 会根据 key. hashCode() 计算出 hash 值,根据 hash 值将 value 保存在 bucket 里。当计算出的 hash 值相同时,我们称之为 hash 冲突,HashMap 的做法是用链表和红黑树存储相同 hash 值的 value。当 hash 冲突的个数比较少时,使用链表否则使用红黑树。
7.说一下 HashSet 的实现原理?
HashSet其实底层就是HashMap,其值存储在HashMap的key上,HashMap的value则全部用PRESENT代替。
8.HashSet如何检查重复?
当你把对象加⼊ HashSet 时,HashSet会先计算对象的 hashcode 值来判断对象加⼊的位置,同时也会与其他加⼊的对象的hashcode值作⽐,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同hashcode值的对象,这时会调⽤ equals() ⽅法来检查hashcode相等的对象是否真的相同。如果两者相同,HashSet就不会让加⼊操作成功。(HashSet添加元素实际是调用HashMap的put()方法,如果某一个键已经有值了,则再次调用put()方法传入新值的同时会返回上一个值,再HashSet中会进行判断,如果返回的是null则添加,如果不是null则不添加)
hashCode()与equals()的相关规定:
1.如果两个对象相等,则hashcode⼀定也是相同的
2.两个对象相等,对两个equals⽅法返回true
3.两个对象有相同的hashcode值,它们也不⼀定是相等的
4.综上,equals⽅法被覆盖过,则hashCode⽅法也必须被覆盖
5.hashCode()的默认⾏为是对堆上的对象产⽣独特值。如果没有重写hashCode(),则该class的两个对象⽆论如何都不会相等(即使这两个对象指向相同的数据)。
9.ArrayList 和 LinkedList 的区别是什么?
数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。
随机访问效率:ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。
增加和删除效率:增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList 增删操作可能要移动数组内的其他数据。
10.如何实现数组和 List 之间的转换?
数组转换成List:调用Arrays工具类的asList()方法;
List转换成数组:调用对应List子类(List本身没有该方法)的toArray()方法。
11.ArrayList 和 Vector 的区别是什么?
ArrayList线程不安全,效率高;Vector线程安全,效率较低。
ArrayList和Vector都会根据需求动态地调整容量,但是ArrayList扩容每次只增加50%,而Vector扩容每次增加100%。
12.Array(数组) 和 ArrayList 有何区别?
Array可以存储基本类型和引用类型的数据;ArrayList只能存储引用类型的数据,无法存储基本类型;
Array大小是固定的,ArrayList的大小是动态变化的;
ArrayList提供了更多的方法和特性,如:addAll(),removeAll(),iterator()等。
13.在 Queue 中 poll()和 remove()有什么区别?
poll()方法和remove()方法都是获取并移除队首元素,但是poll()方法在队首元素为空的时候会取出null,而remove()方法在队首元素为空的时候会抛出异常。
14.哪些集合类是线程安全的?
Vector、HashTable、ConcurrentHashMap、Stack。
15.Iterator 怎么使用?有什么特点?
集合类通过调用iterator()方法返回一个Iterator对象,该对象可通过next()方法获取序列的下一个元素(第一次调用时,返回序列的第一个元素),通过hasNext()方法检查序列中是否还有元素,通过remove()方法将新返回的元素删除。
特点:
1.Iterator遍历集合元素的过程中不允许线程对集合元素进行修改,否则会抛出ConcurrentModificationEception的异常。
2.Iterator遍历集合元素的过程中可以通过remove()方法来移除集合中的元素,删除的是上一次next()方法返回的内容。
3.Iterator必须依附于一个集合类对象而存在,Iterator本身不具有装载数据对象的功能。
16.Iterator 和 ListIterator 有什么区别?
Iterator 可以遍历 Set 和 List 集合,而 ListIterator 只能遍历 List。
Iterator 只能单向遍历,而 ListIterator 可以双向遍历(向前/后遍历)。
ListIterator 从 Iterator 接口继承,然后添加了一些额外的功能,比如添加一个元素、替换一个元素、获取前面或后面元素的索引位置。
17.怎么确保一个集合不能被修改?
可以使用 Collections工具类的unmodifiableCollection() 方法来创建一个只读集合,这样改变集合的任何操作都会抛出 UnsupportedOperationException 异常。
三、多线程
1.并行和并发有什么区别?
并发,指的是多个事情,在同一时间段内同时发生了;并行,指的是多个事情,在同一时间点上同时发生了;
并发的多个任务之间是互相抢占资源的;并行的多个任务之间是不互相抢占资源的;
只有在多CPU的情况中,才会发生并行;否则,看似同时发生的事情,其实都是并发执行的。
2.线程和进程的区别?
进程是操作系统分配资源的最小单元;线程是操作系统调度的最小单元;
一个程序中至少有一个进程;一个进程中至少有一个线程;
进程间相互独立,资源不共享;同一进程的不同线程共享整个进程的资源;
线程的创建和切换开销比进程小。
3.守护线程是什么?
守护线程,是指在程序运行时在后台提供一种通用服务的线程,比如垃圾回收线程就是一个守护线程。守护线程并不属于程序中不可或缺的部分,因此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止;
将线程转换为守护线程可以通过调用Thread对象的setDaemon(true)方法来实现。
4.创建线程有哪几种方式?
继承Thread类,并重写类中的run()方法;创建该类的对象,通过对象调用start()方法启动线程;
实现Runable接口,并重写接口中的run()方法;创建该类对象作为参数创建Thread类的对象,通过Thread对象调用start()方法启动线程;
实现Callable接口,重写接口中的call()方法;创建该类对象作为参数创建FutureTask类的对象,再将FutureTask对象作为参数创建Thread类对象,通过Thread对象调用start()方法启动线程。这种创建方式可以通过FutureTask对象调用get()方法获取子线程执行结束后的返回值。
5.说一下 runnable 和 callable 有什么区别?
两者最大的不同点是:实现Callable接口的线程执行结束后能返回执行结果;而实现Runnable接口的线程不能返回结果;
Callable接口的call()方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部处理,不能继续上抛;
6.线程有哪些状态?
创建、就绪、运行、阻塞、死亡。
创建状态:生成线程对象,但是没有调用start()方法,此时线程处于创建状态;
就绪状态:调用start()方法后,线程就进入了就绪状态,在线程调度程序没有将该线程设置为当前线程之前,都是处于就绪状态;线程从等待或者睡眠状态回来之后,也会处于就绪状态;
运行状态:线程调度程序将该线程设置为当前线程,线程进入运行状态,开始运行run()方法中的代码;
阻塞状态:当一个线程在运行状态的时候,被暂停,就会进入阻塞状态,sleep()、suspend()、wait()等方法都可以导致线程阻塞;
死亡状态:当run()方法运行结束或者调用stop()方法后,线程进入死亡状态,对于已经死亡的线程,无法再使用start()方法令其进入就绪状态。
7.sleep() 和 wait() 有什么区别?
最主要的区别是:sleep()方法没有释放锁(其他线程不能访问),而wait()方法释放了锁(其他线程可以访问);
sleep()是Thread类中的静态方法,wait()是Object类中的成员方法;
sleep()到时间自动回复,wait()需要调用notify()/notyfiAll()唤醒;
sleep()可以在任意地方使用,wait()只能在同步控制方法或者同步代码块中使用。
8.notify()和 notifyAll()有什么区别?
notifyAll()会唤醒所有的线程,notify()之后唤醒一个线程。notifyAll() 调用后,会将全部线程由等待池移到锁池,然后参与锁的竞争,竞争成功则继续执行,如果不成功则留在锁池等待锁被释放后再次参与竞争。而 notify()只会唤醒一个线程,具体唤醒哪一个线程由虚拟机控制。
9.线程的 run()和 start()有什么区别?
start() 方法用于启动线程,run() 是线程运行时会自动执行的方法。
run() 可以重复调用,而 start() 只能调用一次。
10.创建线程池有哪几种方式?
new FixedThreadPool(int nThreads):创建一个固定长度的线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程规模将不再变化,当线程发生未预期的错误而结束时,线程池会补充一个新的线程。
new CachedThreadPool():创建一个可缓存的线程池,如果线程池的规模超过了处理需求,将自动回收空闲线程,而当需求增加时,则可以自动添加新线程,线程池的规模不存在任何限制。
new SingleThreadExecutor():这是一个单线程的Executor,它创建单个工作线程来执行任务,如果这个线程异常结束,会创建一个新的来替代它;它的特点是能确保依照任务在队列中的顺序来串行执行。
new ScheduledThreadPool(int corePoolSize):创建了一个固定长度的线程池,而且以延迟或定时的方式来执行任务,类似于Timer。
11.线程池都有哪些状态?
Running、ShutDown、Stop、Tidying、Terminated。
RUNNING:这是最正常的状态,接受新的任务,处理等待队列中的任务。线程池的初始化状态是RUNNING。线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0。
SHUTDOWN:不接受新的任务提交,但是会继续处理等待队列中的任务。调用线程池的shutdown()方法时,线程池由RUNNING -> SHUTDOWN。
STOP:不接受新的任务提交,不再处理等待队列中的任务,中断正在执行任务的线程。调用线程池的shutdownNow()方法时,线程池由(RUNNING or SHUTDOWN ) -> STOP。
TIDYING:所有的任务都销毁了,workCount 为 0,线程池的状态在转换为 TIDYING 状态时,会执行钩子方terminated()。因为terminated()在ThreadPoolExecutor类中是空的,所以用户想在线程池变为TIDYING时进行相应的处理;可以通过重载terminated()函数来实现。
当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。
当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。
TERMINATED:线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。
12.线程池中 submit()和 execute()方法有什么区别?
exucute()只能执行实现Runnable接口的线程,submit()可以执行实现Runnable接口或Callable接口的线程;
submit()有返回值,而execute()没有;
submit()的返回值Future调用get()方法时,可以捕获处理异常。
13.在 java 程序中怎么保证多线程的运行安全?
使用synchronized关键字,可以用于代码块,方法(静态方法,同步锁是当前字节码对象;实例方法,同步锁是实例对象);
使用volatile 关键字,被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量;
lock锁机制。
使用线程安全的类,比如Vector、ConcurrentHashMap、StringBuffer。
14.什么是死锁?
多个线程同时被阻塞,它们中的⼀个或者全部都在等待某个资源被释放。线程被⽆限期地阻塞,程序不能正常终⽌。
15.怎么防止死锁?
死锁的四个必要条件:
互斥条件: 该资源任意⼀个时刻只由⼀个线程占⽤;
请求与保持条件:⼀个线程因请求资源⽽阻塞时,对已获得的资源保持不放;
不剥夺条件:线程已获得的资源在未使⽤完之前不能被其他线程强⾏剥夺,只有⾃⼰使⽤完毕后才释放资源;
循环等待条件:若⼲线程之间形成⼀种头尾相接的循环等待资源关系。
要防止死锁只需破坏四个必要条件中的任意一个即可:
破坏互斥条件:这个条件时无法破坏的,因为我们使用锁本来就是想要线程之间互斥的(临界资源需要互斥访问);
破坏请求与保持条件:一次性申请所有资源;
破坏不剥夺条件:占⽤部分资源的线程进⼀步申请其他资源时,如果申请不到,可以主动释放它占有的资源;
破坏循环等待条件 :靠按序申请资源来预防。按某⼀顺序申请资源,释放资源则反序释放。
16.ThreadLocal 是什么?有哪些使用场景?
ThreadLocal 为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
ThreadLocal 的使用场景有数据库连接和 session 管理等。
17.说一下 synchronized 的使用?
修饰实例⽅法: 作⽤于当前对象实例加锁,进⼊同步代码前要获得当前对象实例的锁;
修饰静态⽅法: 也就是给当前类加锁,会作⽤于类的所有对象实例,因为静态成员不属于任何⼀个实例对象,是类成员(static 表明这是该类的⼀个静态资源,不管new了多少个对象,只有⼀份)。所以如果⼀个线程A调⽤⼀个实例对象的⾮静态synchronized⽅法,⽽线程B需要调⽤这个实例对象所属类的静态synchronized⽅法,是允许的,不会发⽣互斥现象,因为访问静态synchronized⽅法占⽤的锁是当前类的锁,⽽访问⾮静态synchronized⽅法占⽤的锁是当前实例对象锁;
修饰代码块: 指定加锁对象,对给定对象加锁,进⼊同步代码库前要获得给定对象的锁。
18.synchronized 和 volatile 的区别是什么?
volatile 只能修饰变量;synchronized 则可以修饰类、方法、代码块。
volatile 仅能实现变量的修改可见性,不能保证原子性;而 synchronized 则可以保证变量的修改可见性和原子性。
volatile 不会造成线程的阻塞;synchronized 可能会造成线程的阻塞。
19.synchronized 和 Lock 有什么区别?
synchronized 可以给类、方法、代码块加锁;而 lock 只能给代码块加锁。
synchronized 不需要手动获取锁和释放锁,使用简单,发生异常会自动释放锁,不会造成死锁;而 lock 需要自己加锁和释放锁,如果使用不当没有 unLock()去释放锁就会造成死锁。
通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到。
20.synchronized 和 ReentrantLock 区别是什么?
ReentrantLock 使用起来比较灵活,但是必须有释放锁的配合动作;
ReentrantLock 必须手动获取与释放锁,而 synchronized 不需要手动释放和开启锁;
ReentrantLock 只适用于代码块锁,而 synchronized 可用于修饰方法、代码块等。
四、反射
1.什么是反射?
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
2.什么是 java 序列化?什么情况下需要序列化?
Java 序列化是为了保存各种对象在内存中的状态,并且可以把保存的对象状态再读出来。
以下情况需要使用 Java 序列化:
把内存中的对象状态保存到一个文件中或者数据库中;
用套接字在网络上传送对象;
通过RMI(远程方法调用)传输对象。
3.Java 序列化中如果有些字段不想进行序列化,怎么办?
对于不想进⾏序列化的变量,使⽤ transient 关键字修饰。
transient 关键字的作⽤是:阻⽌实例中那些⽤该关键字修饰的的变量序列化;当对象进行反序列化时,被 transient 修饰的变量值不会被持久化和恢复。transient 只能修饰变量,不能修饰类和⽅法。
4.动态代理是什么?有哪些应用?
当想要给实现了某个接口(实现接口是必要条件)的类中的方法,加一些额外的处理。比如说加日志,加事务等。则可以给这个类创建一个代理,其实就是创建一个新的类,这个类不仅包含原来类方法的功能,而且还在原来的基础上添加了额外处理的新功能。这个代理类并不是定义好的,是动态生成的。具有解耦意义,灵活,扩展性强。
应用:
Spring AOP的动态代理实现;
RPC框架中的应用。
5.怎么实现动态代理?
常用的动态代理实现方式有两种,一种是利用JDK反射机制生成代理,另外一种是使用CGLIB代理。
JDK代理必须要提供接口,而CGLIB则不需要,可以直接代理类。
JDK代理方式:
首先必须定义一个接口,还要有一个InvocationHandler(将实现接口的类的对象传递给它)处理类。再有一个工具类Proxy(习惯性将其称为代理类,因为调用他的newInstance()可以产生代理对象,其实他只是一个产生代理对象的工具类)。利用InvocationHandler,拼接代理类源码,将其编译生成代理类的二进制码,利用加载器加载,并将其实例化产生代理对象,最后返回。
cglib代理方式:
导入cglib所需的jar包;
实现MethodIntercetor类,实现代理类对象的具体逻辑。
五、对象拷贝
1.为什么要使用克隆?
想对一个对象进行处理,又想保留原有数据进行后续操作,就需要克隆。在Java语言中,克隆针对的是类的实例。
2.如何实现对象克隆?
实现Cloneable接口(如果没实现则会报CloneNotSupportedException异常)并重写Object类中的clone()方法;
实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现深度克隆。
3.深拷贝和浅拷贝区别是什么?
浅拷⻉:对基本数据类型进⾏值传递,对引⽤数据类型进⾏引⽤传递,两个对象指向同一个内存地址,其中一个改变,另一个会随之改变。
深拷⻉:对基本数据类型进⾏值传递,对引⽤数据类型,创建⼀个新的对象,并复制其内容,其中一个修改,另一个不会变化。
六、Java Web
1.get 和 post 请求有哪些区别?
在浏览器进行回退操作时,get请求是无害的,而post请求则会重新请求一次;
get请求参数是连接在url后面的,而post请求参数是存放在requestbody内的;
因为get请求参数暴露在url上,所以安全方面post比get更加安全;
get请求因为浏览器对url长度有限制,对传参数量有限制,而post请求因为参数存放在requestbody内所以参数数量没有限制;
get请求产生1个tcp数据包,post请求产生2个tcp数据包。
2.jsp 和 servlet 有什么区别?
jsp经编译后就会变成Servlet。(JSP的本质就是Servlet,JVM只能识别java的类,不能识别JSP的代码,Web容器将JSP的代码编译成JVM能够识别的java类)
jsp更适用于页面显示,servlet更适用于逻辑控制。
Servlet中没有内置对象,Jsp中的内置对象都必须通过HttpServletRequest对象,HttpServletResponse对象以及HttpServlet对象获取。
Jsp是Servlet的一种简化,开发人员使用Jsp只需要完成输出到客户端的内容,Jsp中的Java脚本如何嵌入到一个类中,由Jsp容器完成。而Servlet则是个完整的Java类,这个类的service()方法用于生成对客户端的响应。
3.jsp 有哪些内置对象?作用分别是什么?
pageContext:页面间共享数据,可以获取其他八个内置对象;
request:封装客户端的请求,其中包含来自get/post请求的参数;
response:封装服务器对客户端的响应;
application:所有用户间共享数据;
session:一次会话的多个请求间共享数据;
page:JSP页面本身(相当于Java程序中的this);
out:输出对象,将数据输出到页面上;
config:Web 应用的配置对象;
exception:封装页面抛出异常的对象。
4.说一下 jsp 的 4 种作用域?
page:代表与一个页面相关的对象和属性。
request:代表客户端发出的一个请求相关的对象和属性。一个请求可能跨越多个页面,涉及多个Web组件;需要在页面显示的临时数据可以置于此作用域。
session:代表某个用户与服务器建立的一次会话相关的对象和属性。
application:代表与整个Web程序相关的对象和属性,它实质上是跨越整个Web程序,包括多个页面、请求和会话的一个全局作用域。
5.forward 和 redirect 的区别?
forward地址栏不会发生变化,redirect地址栏会发生变化;
forward是客户端行为,redirect是服务器行为;
forward是一次请求,可以使用request对象来共享资源;redirect是两次请求,不能使用request对象来共享资源;
因为forward比redirect少一次请求,所以效率会比较高。
6.session 和 cookie 有什么区别?
存储位置不同:session 存储在服务器端;cookie 存储在浏览器端。
安全性不同:cookie 安全性一般,在浏览器存储,可以被伪造和修改。
容量和个数限制:cookie 有容量限制,每个站点下的 cookie 也有个数限制。
存储的多样性:session 可以存储在 Redis 、数据库以及应用程序中;而 cookie 只能存储在浏览器中。
7.说一下 session 的工作原理?
客户端登录完成之后,服务器会创建对应的 session,session 创建完之后,会把 session 的 id 发送给客户端,客户端再存储到浏览器中。这样客户端每次访问服务器时,都会带着 sessionid,服务器拿到 sessionid 之后,在内存找到与之对应的 session 就可以正常工作了。
8.如果客户端禁止 cookie 能实现 session 还能用吗?
一般默认情况下,在会话中,session 的 sessionid 是通过 cookie 存储在客户端。
如果客户端禁用 cookie,客户端请求服务器无法携带 sessionid,服务器无法识别请求中的用户身份,session失效。
可以通过其他方法在禁用 cookie 的情况下,继续使用session:
1.把 sessionid 作为参数追加到原 url 中,后续的浏览器与服务器交互中携带 sessionid 参数。
2.服务器的返回数据中包含 sessionid,浏览器发送请求时,携带 sessionid 参数。
3.通过 Http 协议其他 header 字段,服务器每次返回时设置该 header 字段信息,浏览器中 js 读取该 header 字段,请求服务器时,js设置携带该 header 字段。
9.什么是 XSS 攻击,如何避免?
XSS 攻击:
即跨站脚本攻击,它是 Web 程序中常见的漏洞。原理是攻击者往 Web 页面里插入恶意的脚本代码(css 代码、Javascript 代码等),当用户浏览该页面时,嵌入其中的脚本代码会被执行,从而达到恶意攻击用户的目的,如盗取用户 cookie、破坏页面结构、重定向到其他网站等。
避免方法:
web 页面中可由用户输入的地方,需要对输入的数据转义、过滤处理;
后台输出页面的时候,也需要对输出内容进行转义、过滤处理(因为攻击者可能通过其他方式把恶意脚本写入数据库);
前端对 html 标签属性、css 属性赋值的地方进行校验。
10.什么是 CSRF 攻击,如何避免?
CSRF:
Cross-Site Request Forgery(中文:跨站请求伪造),可以理解为攻击者盗用了你的身份,以你的名义发送恶意请求,比如:以你名义发送邮件、发消息、购买商品,虚拟货币转账等。
避免方法:
验证请求来源地址;
添加并验证 token;
敏感操作添加验证码;
使用 post 请求。
七、异常
1.throw 和 throws 的区别?
throw:
表示方法内抛出某种异常对象;如果异常对象是非运行时异常,则需要在方法申明时加上该异常的抛出,即需要加上 throws 语句,或者 在方法体内 try catch 处理该异常,否则编译报错;执行到 throw 语句则后面的语句块不再执行。
throws:
方法的定义上使用 throws 表示这个方法可能抛出某种异常;需要由方法的调用者进行异常处理。
2.final、finally、finalize 有什么区别?
final可以修饰类、变量、方法,修饰类则该类不能被继承,修饰方法则该方法不能被重写,修饰变量则该变量是一个常量且不能被二次赋值。
finally一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码方法finally代码块中,不管是否出现异常,该代码块都会执行,一般用来进行一些关闭资源的操作。
finalize()是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法。
3.try-catch-finally 中哪个部分可以省略?
try-catch-finally 其中 catch 和 finally 都可以被省略,但是不能同时省略,也就是说有 try 的时候,必须后面跟一个 catch 或者 finally。
4.try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?
finally 一定会执行,即使是 catch 中 return 了,catch 中的 return 会等 finally 中的代码执行完之后,才会执行。
5.常见的异常类有哪些?
NullPointException、ClassCastException、IndexOutOfBoundsException、NumberFormatException、IllegalArgumentException、FileNotFoundxception
八、网络
1.http 响应码 301 和 302 代表的是什么?有什么区别?
官方的比较简洁的说明:
301 redirect: 301 代表永久性转移(Permanently Moved)
302 redirect: 302 代表暂时性转移(Temporarily Moved)
详细来说,301和302状态码都表示重定向,就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址,这个地址可以从响应的Location首部中获取(用户看到的效果就是他输入的地址A瞬间变成了另一个地址B)——这是它们的共同点。他们的不同在于。301表示旧地址A的资源已经被永久地移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址;302表示旧地址A的资源还在(仍然可以访问),这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址。
2.简述 tcp 和 udp的区别?
UDP 是面向无连接的通讯协议,UDP 数据包括目的端口号和源端信息。
优点:UDP 速度快、操作简单、要求系统资源较少,由于通讯不需要连接,可以实现广播发送;
缺点:UDP 传送数据前并不与对方建立连接,对接收到的数据也不发送确认信号,发送端不知道数据是否会正确接收,也不重复发送,不可靠。
TCP 是面向连接的通讯协议,通过三次握手建立连接,通讯完成时四次挥手;
优点:TCP 在数据传递时,有确认、窗口、重传、阻塞等控制机制,能保证数据正确性,较为可靠;
缺点:TCP 相对于 UDP 速度较慢,要求系统资源较多。
3.tcp 为什么要三次握手,两次不行吗?为什么?
三次握⼿的⽬的是建⽴可靠的通信信道,通讯简单来说就是数据的发送与接收,而三次握⼿最主
要的⽬的就是双⽅确认自己与对方的发送与接收是正常的。
第⼀次握⼿:Client 什么都不能确认;Server 确认了对方发送正常,⾃⼰接收正常
第⼆次握⼿:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:对方发送正常,自己接收正常
第三次握⼿:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己发送、接收正常,对方发送、接收正常
如果只有两次握手的话,只有客户端能确定自己和对方是否都发送接收正常,服务器端无法确定。
4.OSI 的七层模型都有哪些?
应用层:网络服务与最终用户的一个接口。
表示层:数据的表示、安全、压缩。
会话层:建立、管理、终止会话。
传输层:定义传输数据的协议端口号,以及流控和差错校验。
网络层:进行逻辑地址寻址,实现不同网络之间的路径选择。
数据链路层:建立逻辑连接、进行硬件地址寻址、差错校验等功能。
物理层:建立、维护、断开物理连接。
5.如何实现跨域?
1、jsonp
利用了 script 不受同源策略的限制
缺点:只能 get 方式,易受到 XSS攻击
2、CORS(Cross-Origin Resource Sharing),跨域资源共享
当使用XMLHttpRequest发送请求时,如果浏览器发现违反了同源策略就会自动加上一个请求头 origin;
后端在接受到请求后确定响应后会在后端在接受到请求后确定响应后会在 Response Headers 中加入一个属性 Access-Control-Allow-Origin;
浏览器判断响应中的 Access-Control-Allow-Origin 值是否和当前的地址相同,匹配成功后才继续响应处理,否则报错
缺点:忽略 cookie,浏览器版本有一定要求
3、代理跨域请求
前端向发送请求,经过代理,请求需要的服务器资源
缺点:需要额外的代理服务器
4、Html5 postMessage 方法
允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本、多窗口、跨域消息传递
缺点:浏览器版本要求,部分浏览器要配置放开跨域限制
5、修改 document.domain 跨子域
相同主域名下的不同子域名资源,设置 document.domain 为 相同的一级域名
缺点:同一一级域名;相同协议;相同端口
6、基于 Html5 websocket 协议
websocket 是 Html5 一种新的协议,基于该协议可以做到浏览器与服务器全双工通信,允许跨域请求
缺点:浏览器一定版本要求,服务器需要支持 websocket 协议
6.说一下 JSONP 实现原理?
jsonp 即 json+padding,动态创建script标签,利用script标签的src属性可以获取任何域下的js脚本,通过这个特性,服务器端不再返回json格式,而是返回一段调用某个函数的js代码,在src中进行了调用,以此实现跨域。
九、Spring/Spring MVC
1.为什么要使用 spring?
spring 提供 ioc 技术,容器会帮你管理依赖的对象,而不需要自己创建和管理依赖对象,更轻松的实现了程序的解耦。
spring 提供了面向切片编程,可以更方便的处理某一类问题,比如日志输出,事务控制,异常的处理等。
spring 提供了事务支持,使得事务操作变的更加方便。
更方便的框架集成,spring 可以很方便的集成其他框架,比如 MyBatis、hibernate 等。
2.解释一下什么是 aop?
在运行时,动态地将代码切入到类的指定方法指定位置上的编程思想就是AOP(Aspect Oriented Programming,面向切面编程);
AOP是Spring提供的关键特性之一。AOP即面向切面编程,是OOP(Object Oriented Programming,面向对象编程)编程的有效补充;
使用AOP技术,可以将一些系统性相关的编程工作,独立提取出来,独立实现,然后通过切面切入进系统;
可以避免在业务逻辑的代码中混入很多系统相关的逻辑——比如权限管理,事务管理,日志记录等等;
这些系统性的编程工作都可以独立编码实现,然后通过AOP技术切入系统,从而达到将不同的关注点分离出来的效果。
3.解释一下什么是 ioc?
IoC(Inverse of Control:控制反转)是⼀种设计思想,就是 将原本在程序中⼿动创建对象的控制权,交由Spring框架来管理。在 Spring 中是用容器来实现 IoC ,IoC 容器实际上就是个Map(key,value),Map 中存放的是各种对象。IoC 容器就像是⼀个⼯⼚⼀样,当我们需要创建⼀个对象的时候,只需要配置好配置⽂件/注解即可,不⽤去考虑对象是如何被创建出来的。
4.spring 有哪些主要模块?
spring core:框架的最基础部分,提供 ioc 和依赖注入特性。
spring context:构建于 core 封装包基础上的 context 封装包,提供了一种框架式的对象访问方法。
spring dao:提供了JDBC的抽象层。
spring aop:提供了面向切面的编程实现,让你可以自定义拦截器、切点等。
spring Web:提供了针对 Web 开发的集成特性,例如文件上传,利用 servlet listeners 进行 ioc 容器初始化和针对 Web 的 ApplicationContext。
spring Web mvc:spring 中的 mvc 封装包提供了 Web 应用的 Model-View-Controller(MVC)的实现。
5.spring 常用的注入方式有哪些?
Spring通过DI(依赖注入)实现IOC(控制反转),常用的注入方式主要有三种:构造方法注入、setter注入、基于注解的注入。
6.spring 支持几种 bean 的作用域?
singleton : 唯⼀bean实例,Spring中的bean默认都是单例的。
prototype : 每次请求都会创建⼀个新的bean 实例。
request : 每⼀次HTTP请求都会产⽣⼀个新的bean,该bean仅在当前HTTP request内有效。
session : 每⼀次HTTP请求都会产⽣⼀个新的bean,该bean仅在当前HTTP session内有效。
global-session: 全局session作⽤域,仅仅在基于portlet的web应⽤中才有意义,Spring5已经没有了。Portlet是能够⽣成语义代码(例如:HTML)⽚段的⼩型Java Web插件。它们基于portlet容器,可以像servlet⼀样处理HTTP请求。但是,与 servlet 不同,每个portlet都有不同的会话。
7.spring 自动装配 bean 有哪些方式?
spring 配置文件中 标签的 autowire 参数可以控制 bean 自动装配的方式
default - 默认的方式和 “no” 方式一样
no - 不自动装配,需要使用 标签或参数
byName - 根据名称进行装配
byType - 根据类型进行装配
constructor - 根据构造函数进行装配
8.spring 事务实现方式有哪些?
编程式事务管理,在代码中调用 commit()、rollback()等事务管理相关的方法;
基于 TransactionProxyFactoryBean 的声明式事务管理;
基于注解 @Transactional 的声明式事务管理;
基于 Aspectj AOP 配置(注解)事务。
9.说一下 spring 的事务隔离?
spring 有五大隔离级别,默认值为 ISOLATION_DEFAULT(使用数据库的设置),其他四个有:
READUNCOMMITTED:读未提交,最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读);
READCOMMITTED:读已提交,一个事务提交后才能被其他事务读取到(会造成幻读、不可重复读),Oracle 的默认级别;
REPEATABLEREAD:可重复读,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(会造成幻读),MySQL 的默认级别;
SERIALIZABLE:串行化,代价最高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读、幻读。
10.说一下 spring mvc 运行流程?
1.用户向服务器发送请求,请求被前端控制器DispatcherServlet捕获;
2.DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;
3.DispatcherServlet根据获得的Handler,选择一个合适的HandlerAdapter,提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller);
5.Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象;
6.根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet;
7.ViewResolver结合Model和View,来渲染视图;
8.将渲染结果返回给客户端。
11.spring mvc 有哪些组件?
前端控制器组件(DispatcherServlet)
处理器映射器组件(HandlerMapping)
处理器适配器组件(HandlerAdapter)
视图解析器组件(ViewResolver)
异常处理组件(HandlerExceptionResolver)
国际化组件(LocaleResolver)
主题解析组件(ThemeResolver)
请求视图名称转换组件(RequestToViewNameTranslator)
FlashMap管理组件(FlashMapManager)
12.@RequestMapping 的作用是什么?
用于处理请求 url 映射的注解,可用于类或方法上。用于类上,则表示类中的所有响应请求的方法都是以该地址作为父路径。
13.@Autowired 的作用是什么?
@Autowired 是一个注释,它可以对类成员变量、方法及构造函数进行标注,让 spring 完成 bean 自动装配的工作。
@Autowired 默认是按照类去匹配,配合 @Qualifier 指定按照名称去装配 bean。
十、Spring Boot/Spring Cloud
1.什么是 spring boot?
SpringBoot是一个框架,一种全新的编程规范,他的产生简化了框架的使用,所谓简化是指简化了Spring众多框架中所需的大量且繁琐的配置文件,可以说SpringBoot是一个服务于框架的框架,服务范围是简化配置文件。
2.为什么要用 spring boot?
配置简单、独立运行、自动装配、无代码生成和 xml 配置、提供应用监控、易上手、提升开发效率。
3.spring boot 核心配置文件是什么?
bootstrap (. yml 或者 . properties):bootstrap 由父 ApplicationContext 加载的,比 applicaton 优先加载,且 boostrap 里面的属性不能被覆盖;
application (. yml 或者 . properties):用于 spring boot 项目的自动化配置。
4.spring boot 配置文件有哪几种类型?它们有什么区别?
类型:
.properties 和 .yml
区别:
1.内容格式
.properties文件,通过.来连接,通过=来赋值,结构上,没有分层的感觉,但比较直接。
.yml文件,通过:来分层,结构上,有比较明显的层次感,最后key赋值的:后需要留一个空格。
2.执行顺序
如果工程中同时存在application.properties文件和 application.yml文件,yml文件会先加载,而后加载的properties文件会覆盖yml文件。
5.spring boot 有哪些方式可以实现热部署?
Spring Loaded;
spring-boot-devtools。
6.什么是 spring cloud?
Spring Cloud是一系列框架的有序集合。 它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。
十一、Mybatis
1.mybatis 中 #{}和 ${}的区别是什么?
#{}:传入的内容会被作为字符串,被加上引号,以预编译的方式传入,安全性高,可以防止sql注入。
${}:传入的内容会直接拼接,不会加上引号,可能存在sql注入的安全隐患。
2.mybatis 有几种分页方式?
数组分页
sql语句limit分页
拦截器分页
mybatis自带的RowBounds分页
3.RowBounds 是一次性查询全部结果吗?为什么?
RowBounds 表面是在“所有”数据中检索数据,其实并非是一次性查询出所有数据,因为 MyBatis 是对 jdbc 的封装,在 jdbc 驱动中有一个 Fetch Size 的配置,它规定了每次最多从数据库查询多少条数据,假如你要查询更多数据,它会在你执行 next() 的时候,去查询更多的数据。就好比你去自动取款机取 10000 元,但取款机每次最多能取 2500 元,所以你要取 4 次才能把钱取完。只是对于 jdbc 来说,当你调用 next() 的时候会自动帮你完成查询工作,这样做的好处可以有效的防止内存溢出。
4.mybatis 逻辑分页和物理分页的区别是什么?
逻辑分页: 使用 MyBatis 自带的 RowBounds 进行分页,它是一次性查询很多数据,然后在数据中再进行检索。
物理分页: 自己手写 SQL 分页或使用分页插件 PageHelper,去数据库查询指定条数的分页数据的形式。
区别:
逻辑分页是一次性查询很多数据,然后再在结果中检索分页的数据。这样做弊端是需要消耗大量的内存、有内存溢出的风险、对数据库压力较大。
物理分页是从数据库查询指定条数的数据,弥补了一次性全部查出的所有数据的种种缺点,比如需要大量的内存,对数据库查询压力较大等问题。
5.mybatis 是否支持延迟加载?延迟加载的原理是什么?
MyBatis 支持延迟加载,设置 lazyLoadingEnabled=true 即可。
延迟加载的原理的是调用的时候触发加载,而不是在初始化的时候就加载信息。比如调用 a. getB(). getName(),这个时候发现 a. getB() 的值为 null,此时会单独触发事先保存好的关联 B 对象的 SQL,先查询出来 B,然后再调用 a. setB(b),而这时候再调用 a. getB(). getName() 就有值了,这就是延迟加载的基本原理。
6.说一下 mybatis 的一级缓存和二级缓存?
一级缓存:基于 PerpetualCache 的 HashMap 本地缓存,它的生命周期是和 SQLSession 一致的,有多个 SQLSession 或者分布式的环境中数据库操作,可能会出现脏数据。当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空,默认一级缓存是开启的。
二级缓存:也是基于 PerpetualCache 的 HashMap 本地缓存,不同在于其存储作用域为 Mapper 级别的,如果多个SQLSession之间需要共享缓存,则需要使用到二级缓存,并且二级缓存可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现 Serializable 序列化接口(可用来保存对象的状态)。
开启二级缓存数据查询流程:二级缓存 -> 一级缓存 -> 数据库。
缓存更新机制:当某一个作用域(一级缓存 Session/二级缓存 Mapper)进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被清除。
7.mybatis 和 hibernate 的区别有哪些?
灵活性:MyBatis 更加灵活,自己可以写 SQL 语句,使用起来比较方便。
可移植性:MyBatis 有很多自己写的 SQL,因为每个数据库的 SQL 可以不相同,所以可移植性比较差。
学习和使用门槛:MyBatis 入门比较简单,使用门槛也更低。
二级缓存:hibernate 拥有更好的二级缓存,它的二级缓存可以自行更换为第三方的二级缓存。
8.mybatis 有哪些执行器(Executor)?
SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置在Map中,重复使用Statement对象。
BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理,与JDBC批处理相同。
9.mybatis 分页插件的实现原理是什么?
分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据不同的数据库方言,添加对应的物理分页语句和物理分页参数。
十二、MySql
1.什么是sql注入?如何避免 sql 注入?
SQL注入是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的SQL语句的结尾上添加额外的SQL语句,以此来实现欺骗数据库服务器执行非授权的查询数据、读写文件以及执行命令等操作。
避免SQL注入:采用预编译方式处理SQL语句
2.数据库的三范式是什么?
第一范式(1NF):每一列都是不可分割的原子项;
第二范式(2NF):在满足1NF的基础上,消除部分依赖,即一张数据表中的任何非主键字段都完全依赖于主键字段。
第三范式(3NF):在满足2NF的基础上,消除传递依赖,即非主键字段之间没有依赖关系,都直接依赖于主键字段。
3.一张自增表里面总共有 7 条数据,删除了最后 2 条数据,重启 mysql 数据库,又插入了一条数据,此时 id 是几?
MylSAM和InnoDB区别:
MyISAM是MySQL的默认数据库引擎(5.5版之前)。虽然性能极佳,⽽且提供了⼤量的特性,包括全⽂索引、压缩、空间函数等,但MyISAM不⽀持事务和⾏级锁,⽽且最⼤的缺陷就是崩溃后⽆法安全恢复。不过,5.5版本之后,MySQL引⼊了InnoDB(事务性数据库引擎),MySQL 5.5版本后默认的存储引擎为InnoDB。
结果:
表类型如果是MylSAM,则id为8;如果为InnoDB,则id为6。
因为InnoDB表只会把自增主键的最大id记录在内存中,重启后会导致最大id丢失。
4.如何获取当前数据库版本?
select version();
5.说一下 ACID 是什么?
原⼦性(Atomicity): 事务是最⼩的执⾏单位,不允许分割。事务的原⼦性确保操作要么全部成功,要么全部失败;
⼀致性(Consistency): 执⾏事务前后,数据保持⼀致,多个事务对同⼀个数据读取的结果是相同的;
隔离性(Isolation): 并发访问数据库时,⼀个⽤户的事务不被其他事务所⼲扰;
持久性(Durability): ⼀个事务被提交之后,它对数据库中数据的改变是持久的,即使数据库发⽣故障也不应该对其有任何影响。
6.char 和 varchar 的区别是什么?
char是长度固定的,其长度取值范围为0~255;varchar是可变长的,其取值范围为0~65535;
char存储时,如果字符数没有达到定义的位数,会在后面用空格补全,在存入数据库;varchar存储时,只保存需要保存的字符,另加一个字节来记录长度(如果长度超过255则使用2个字节),varchar值保存时不进行填充;
数据库取char值时,尾部的空格会被删除;数据库取varchar值时,尾部的空格仍然保留;
char值存储时,英文字符占一个字节,汉字字符占两个字节;varchar存储时,英文和汉字都是占用两个字节;
char的效率比varchar高。
7.mysql 的内连接、左连接、右连接有什么区别?
内连接是把匹配的关联数据全部查询出来;左连接是左边的表全部查询出来,右边的表查询出符合条件的数据;右连接正好相反。
8.怎么验证 mysql 的索引是否满足需求?
在 select 语句前使用 explain 查看 SQL 是如何执行查询语句的,从而分析你的索引是否满足需求。
9.说一下数据库的事务隔离?
可能产生的问题:
脏读(Dirty read): 当⼀个事务正在访问数据并且对数据进⾏了修改,⽽这种修改还没有提交到数据库中,这时另外⼀个事务也访问了这个数据,然后使⽤了这个数据。因为这个数据是还没有提交的数据,那么另外⼀个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
不可重复读(Unrepeatableread): 指在⼀个事务内多次读同⼀数据。在这个事务还没有结束时,另⼀个事务也访问该数据。那么,在第⼀个事务中的两次读数据之间,由于第⼆个事务的修改导致第⼀个事务两次读取的数据可能不⼀样。
幻读(Phantom read): 幻读与不可重复读类似。它发⽣在⼀个事务(T1)读取了⼏⾏数据,接着另⼀个并发事务(T2)插⼊了⼀些数据时。在随后的查询中,第⼀个事务(T1)就会发现多了⼀些原本不存在的记录,就好像发⽣了幻觉⼀样,所以称为幻读。
不可重复读和幻读区别:
不可重复读的重点是修改⽐如多次读取⼀条记录发现其中某些列的值被修改,幻读的重点在于新增或者删除⽐如多次读取⼀条记录发现记录增多或减少了。
隔离级别:
读未提交(READ-UNCOMMITTED):允许读取尚未提交的数据变更(会造成脏读、不可重复读、幻读)
读已提交(READ-COMMITTED):允许读取并发事务已经提交的数据(会造成不可重复读、幻读)
可重复读(REPEATABLE-READ):对同⼀字段的多次读取结果都是⼀致的,除⾮数据是被当前事务⾃⼰所修改(MySql默认级别,可能出现幻读)
串行化(SERIALIZABLE):完全服从ACID的隔离级别。所有的事务依次逐个执⾏,这样事务之间就完全不可能产⽣⼲扰(解决所有问题,但是会极大降低效率)
10.说一下 mysql 常用的引擎?
MyISAM是MySQL的默认数据库引擎(5.5版之前)。虽然性能极佳,⽽且提供了⼤量的特性,包括全⽂索引、压缩、空间函数等,但MyISAM不⽀持事务和⾏级锁,⽽且最⼤的缺陷就是崩溃后⽆法安全恢复。5.5版本之后,MySQL引⼊了InnoDB(事务性数据库引擎),MySQL 5.5版本后默认的存储引擎为InnoDB。
⼤多数时候我们使⽤的都是 InnoDB 存储引擎,但是在某些情况下使⽤ MyISAM 也是合适的⽐如读密集的情况下。(如果你不介意 MyISAM 崩溃恢复问题的话)。
11.说一下 mysql 的行锁和表锁?
MyISAM 只支持表锁,InnoDB 支持表锁和行锁,默认为行锁。
表锁:开销小,加锁快,不会出现死锁。发生锁冲突的概率最高,并发低。
行锁:开销大,加锁慢,会出现死锁。发生锁冲突的概率小,并发高。
12.说一下乐观锁和悲观锁?
乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在提交更新的时候会判断一下在此期间别人有没有去更新这个数据。
悲观锁:每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻止,直到这个锁被释放。
数据库的乐观锁需要自己实现,可以使用版本号机制,在表里面添加一个 version 字段,每次修改成功值加 1,这样每次修改的时候先对比一下,自己拥有的 version 和数据库现在的 version 是否一致,如果不一致就不修改,这样就实现了乐观锁。
13.MySQL 问题排查都有哪些手段?
使用 show processlist 命令查看当前所有连接信息。
使用 explain 命令查询 SQL 语句执行计划。
开启慢查询日志,查看慢查询的 SQL。
14.如何做 mysql 的性能优化?
为搜索字段创建索引;
避免使用 select * ,列出需要查询的字段;
垂直分割分表;
选择正确的存储引擎。
十三、Redis
1.redis 是什么?redis 有哪些功能?都有哪些使用场景?
简单来说 redis 就是⼀个数据库,不过与传统数据库不同的是 redis 的数据是存在内存中的,所以读写速度⾮常快,因此 redis 被⼴泛应⽤于缓存⽅向。另外,redis 也经常⽤来做分布式锁。redis 提供了多种数据类型来⽀持不同的业务场景。除此之外,redis ⽀持事务 、持久化、LUA脚本、LRU驱动事件、多种集群⽅案。
2.redis 和 memecache 有什么区别?
1.redis⽀持更丰富的数据类型(⽀持更复杂的应⽤场景):Redis不仅仅⽀持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
2.Redis⽀持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进⾏使⽤,⽽Memecache把数据全部存在内存之中。
3.Memcached是多线程,⾮阻塞IO复⽤的⽹络模型;Redis使⽤单线程的多路 IO 复⽤模型。
3.redis 为什么是单线程的?
因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。因为单线程容易实现,而且CPU不会成为瓶颈,所以采用的单线程方案。
4.什么是缓存穿透?怎么解决?
缓存穿透:
指查询一个一定不存在的数据,由于缓存不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。
解决方案:
如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们就把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
5.redis 支持的数据类型有哪些?
string、list、hash、set、zset。
6.redis 持久化有几种方式?
redis提供两种方式进行持久化,一种是RDB(Redis Database)持久化(原理是将Reids在内存中的数据库记录定时存储到磁盘上),另外一种是AOF(append only file)持久化(原理是将Reids的操作日志以追加的方式写入文件)。
十四、JVM
1.说一下 jvm 的主要组成部分?及其作用?
组成部分:
类加载器(Class Loader)
执行引擎(Execution Engine)
本地库接口(Native Interface)
运行时数据区(Runtime Data Area)
作用:
首先类加载器(ClassLoader)把 Java 代码转换成字节码,运行时数据区(Runtime Data Area)再把字节码加载到内存中,而字节码文件只是 JVM 的一套指令集规范,并不能直接交给底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由 CPU 去执行,而这个过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能。
2.说一下 jvm 运行时数据区?
堆:java对象的存储区域,任何用new字段分配的java对象实例和数组,都被分配在堆上;
方法区:用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据;
虚拟机栈:虚拟机栈中执行每个方法的时候,都会创建一个栈桢用于存储局部变量表,操作数栈,动态链接,方法出口等信息;
本地方法栈:与虚拟机栈发挥的作用相似,虚拟机栈为Java方法服务,本地方法栈则为虚拟机使用的本地方法服务,执行每个本地方法的时候,都会创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信息;
程序计数器:指示Java虚拟机下一条需要执行的字节码指令。
3.说一下堆栈的区别?
栈内存存储的是局部变量而堆内存存储的是实体;
栈内存的更新速度要大于堆内存,因为局部变量的生命周期很短。
4.队列和栈是什么?有什么区别?
栈(Stack)是限定只能在表的一端进行插入和删除操作的线性表。 队列(Queue)是限定只能在表的一端进行插入和在另一端进行删除操作的线性表。
栈必须按"后进先出"的规则进行操作,而队列必须按"先进先出"的规则进行操作。
5.什么是双亲委派模型?
每⼀个类都有⼀个对应它的类加载器。系统中的 ClassLoder 在协同⼯作的时候会默认使⽤ 双亲委派模型 。即在类加载的时候,系统会⾸先判断当前类是否被加载过。已经被加载的类会直接返回,否则才会尝试加载。加载的时候,⾸先会把该请求委派该⽗类加载器的 loadClass() 处理,因此所有的请求最终都应该传送到顶层的启动类加载器 BootstrapClassLoader 中。当⽗类加载器⽆法处理时,才由⾃⼰来处理。当⽗类加载器为null时,会使⽤启动类加载器 BootstrapClassLoader 作为⽗类加载器。
6.说一下类加载的执行过程?
加载:查找和导入Class文件;
连接:把类的二进制数据合并到JRE中;
(1)校验:检查载入Class文件数据的正确性;
(2)准备:给类的静态变量分配存储空间;
(3)解析:将符号引用转成直接引用;
初始化:对类的静态变量,静态代码块执行初始化操作
7.怎么判断对象是否可以被回收?
引⽤计数法:
给对象中添加⼀个引⽤计数器,每当有⼀个地⽅引⽤它,计数器就加1;当引⽤失效,计数器就减1;任何时候计数器为0的对象就是不可能再被使⽤的。
可达性分析算法:
这个算法的基本思想就是通过⼀系列的称为 “GC Roots” 的对象作为起点,从这些节点开始向下搜索,节点所⾛过的路径称为引⽤链,当⼀个对象到 GC Roots 没有任何引⽤链相连的话,则证明此对象是不可⽤的。
8.java 中都有哪些引用类型?
强引⽤(StrongReference)
软引⽤(SoftReference)
弱引⽤(WeakReference)
虚引⽤(PhantomReference)
9.说一下 jvm 有哪些垃圾回收算法?
标记-清除算法
标记-整理算法
复制算法
分代收集算法
10.说一下 jvm 有哪些垃圾回收器?
Serial收集器:最早的单线程串行垃圾回收器。
Serial Old收集器:Serial 垃圾回收器的老版本,同样也是单线程的,可以作为 CMS 垃圾回收器的备选预案。
ParNew收集器:Serial收集器的多线程版本。
Parallel Scavenge收集器:吞吐量优先的多线程收集器,可以牺牲等待时间换取系统的吞吐量;其使用的是复制的内存回收算法。
Parallel Old收集器:Parallel Old 是 Parallel 老生代版本; 使用的是标记-整理的内存回收算法。
CMS收集器:一种以获得最短停顿时间为目标的收集器,非常适用 B/S 系统。
G1收集器:一种兼顾吞吐量和停顿时间的 GC 实现,是 JDK 9 以后的默认 GC 选项。
11.详细介绍一下 CMS 垃圾回收器?
CMS(Concurrent Mark Sweep)收集器是⼀种以获取最短回收停顿时间为⽬标(以牺牲吞吐量为代价)的收集器,⾮常适合在注重⽤户体验的应⽤上使⽤。
CMS(Concurrent Mark Sweep)收集器是HotSpot(红点)虚拟机第⼀款真正意义上的并发收集器,它第⼀次实现了让垃圾收集线程与⽤户线程(基本上)同时⼯作。
12.新生代垃圾回收器和老生代垃圾回收器都有哪些?有什么区别?
新生代回收器:Serial、ParNew、Parallel Scavenge
老年代回收器:Serial Old、Parallel Old、CMS
整堆回收器:G1
新生代垃圾回收器一般采用的是复制算法,复制算法的优点是效率高,缺点是内存利用率低;老年代回收器一般采用的是标记-整理的算法进行垃圾回收。