目录
一、Java 基础
1.JDK 和 JRE 有什么区别?
- JRE:Java Runtime Environment,是Java运行环境,如果你不需要开发只需要运行Java程序,那么你可以安装JRE。例如程序员开发出的程序最终卖给了用户,用户不用开发,只需要运行程序,所以用户在电脑上安装JRE即可。
- JDK:Java Development Kit,它是Java开发运行环境,在程序员的电脑上当然要安装JDK;JDK包含了JRE。
2.== 和 equals 的区别是什么?
- ==:比较的对象引用,也就是内存地址是否相等
- equals:比较的是值
2.Java重写equals()
- 保证具有对称性、传递性、一致性(自反性和非空性自动满足)
public class ColorPoint{
private Point point;
private Color color;
public ColorPoint(int x, int y, Color color){
point = new Point(x, y);
this.color = color;
}
//返回一个与该有色点在同一位置上的普通Point对象
public Point asPoint(){
return point;
}
public boolean equals(Object o){
if(o == this)
return true;
if(!(o instanceof ColorPoint))
return false;
ColorPoint cp = (ColorPoint)o;
return cp.point.equals(point)&&
cp.color.equals(color);
}
}
3.hashCode()方法的作用是什么?
- Java Set中的元素是不可重复的,可两个元素是否重复应该依据什么来判断呢?这就是Object.equals方法了。但是,如果每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。如果集合中现在已经有1000个元素,那么第1001个元素加入集合时,它就要调用1000次equals方法。这显然会大大降低效率。 于是,Java采用了哈希表的原理。哈希算法将数据依特定算法直接指定到一个地址上。如果详细讲解哈希算法,那需要更多的文章篇幅,我在这里就不介绍了。初学者可以这样理解,hashCode方法实际上返回的就是对象存储的物理地址(实际可能并不是)。 这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。所以这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。
3.两个对象的 hashCode()相同,则 equals()也一定为 tr ue,对吗?
- 不对,存在hash冲突
4.final 在 java 中有什么作用?
- 终态,修饰类不能被继承、修饰函数不能被重写、修饰变量则变量只能读不能写;
5.java 中的 Math.round(-1.5) 等于多少?
- Math.round()是四舍五入函数,返回值为int型
6.String 属于基础的数据类型吗?
- 不是,是封装类型
7.java 中操作字符串都有哪些类?它们之间有什么区别?
- String:不可变对象,不能修改
- StringBuffer:长度可变,线程安全
- StringBulider:长度可变,线程不安全
8.String str1="i"、String str2="i"与 String str3=new String(“i”)一样吗?
- str1与str2一样,str1与str3不一样。
- String str="i"; 因为String 是final类型的,所以“i”应该是在常量池。
- new String("i");则是新建对象放到堆内存中。
9.如何将字符串反转?
- StringBuilder(str).reverse().toString();
10.String 类的常用方法都有那些?
- length()、toCharArray()、split(String)、contains(String)、startsWith(String)、endsWith(String)、toUpperCase()、replace(String,String)、substring(int,int)、trim()、charAt(int)、indexOf(String)
11.抽象类必须要有抽象方法吗?
- 抽象类不一定有抽象方法;但是包含一个抽象方法的类一定是抽象类。(有抽象方法就是抽象类)
- 在abstract class 中可以有自己的数据成员,也可以有非abstarct的成员方法,而在interface中,只能够有静态的不能被修改的数据成员(也就是必须是static final的,不过在 interface中一般不定义数据成员),所有的成员方法都是abstract的。
12.普通类和抽象类有哪些区别?
- 抽象类不能被实例化
13.抽象类能使用 final 修饰吗?
- 抽象方法不能用final修饰,因为子类需要重写抽象方法,如果被final修饰则无法重写
14.接口和抽象类有什么区别?
相同点:
- 都不能实例化对象
- 都可以包含抽象方法,而且抽象方法必须被继承的类全部实现。
不同点:
- 抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
- 接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现
- 接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量
- 抽象方法只能申明,不能实现,接口是设计的结果 ,抽象类是重构的结果
- 抽象类里可以没有抽象方法
- 抽象方法要被实现,所以不能是静态的,也不能是私有的。
- 接口可继承接口,并可多继承接口,但类只能单根继承。
15.java 中 IO 流分为几种?
- BIO:同步阻塞I/O模式
- NIO:同步非阻塞的I/O模型
- AIO:异步非阻塞的IO模型。异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。
二、容器
18.java 容器都有哪些?
- ArrayList、LinkedList、Set、TreeSet、HashMap、TreeMap、Deque、PriorityQueue
19.Collection 和 Collections 有什么区别?
- java.util.Collection 是一个集合接口;
- java.util.Collections 是一个包装类。它包含有各种有关集合操作的静态方法(对集合的搜索、排序、线程安全化等),此类不能实例化,就像一个工具类,服务于Java的Collection框架。
20.List、Set、Map 之间的区别是什么?
- List:有序集合
- Set:不重复集合,LinkedHashSet按照插入排序,SortedSet可排序,HashSet无序
- Map:键值对集合
21.HashMap 和 Hashtable 有什么区别?
- Hashtable:底层数组+链表实现,无论key还是value都不能为null,线程安全
- HashMap :底层数组+链表或数组加红黑树实现,可以存储null键和null值,线程不安全
22.如何决定使用 HashMap 还是 TreeMap?
- 不需要排序使用HashMap,需要排序使用TreeMap
23.说一下 HashMap 的实现原理?
- 用于存储Key-value集合的数据结构;底层用数组加链表或数组加红黑树实现,当链表长度超过8时转为红黑树;初识容量是16,扩容是2倍容量扩容;当数组大小使用率75%时触发扩容
24.说一下 HashSet 的实现原理?
- HashSet是一个HashMap实例,都是一个存放链表的数组。HashSet中不允许有重复元素,这是因为HashSet是基于HashMap实现的,HashSet中的元素都存放在HashMap的key上面,而value中的值都是统一的一个固定对象private static final Object PRESENT = new Object();
- HashSet中add方法调用的是底层HashMap中的put()方法,而如果是在HashMap中调用put,首先会判断key是否存在,如果key存在则修改value值,如果key不存在这插入这个key-value。而在set中,因为value值没有用,也就不存在修改value值的说法,因此往HashSet中添加元素,首先判断元素(也就是key)是否存在,如果不存在这插入,如果存在着不插入,这样HashSet中就不存在重复值。
- 所以判断key是否存在就要重写元素的类的equals()和hashCode()方法,当向Set中添加对象时,首先调用此对象所在类的hashCode()方法,计算次对象的哈希值,此哈希值决定了此对象在Set中存放的位置;若此位置没有被存储对象则直接存储,若已有对象则通过对象所在类的equals()比较两个对象是否相同,相同则不能被添加。
25.ArrayList 和 LinkedList 的区别是什么?
- ArrayList:底层是数组,查找快,增加删除慢
- LinkedList:底层是链表,查找慢,增加删除快
26.如何实现数组和 List 之间的转换?
- 数组转 List ,使用 JDK 中 java.util.Arrays 工具类的 asList 方法
- List 转数组,使用 List 的toArray方法
27.ArrayList 和 Vector 的区别是什么?
- Vector:线程安全的动态数组,虽然是线程安全的,但性能较差,一般情况下使用ArrayList,除非特殊需求;
- ArrayList:线程不安全
28.Array 和 ArrayList 有何区别?
- Array:可以包含基本类型和对象类型,大小是固定的
- ArrayList:只能包含对象类型,大小是动态变化的。
- ArrayList提供了更多的方法和特性,比如:addAll(),removeAll(),iterator()等等。
29.在 Queue 中 poll()和 remove()有什么区别?
- 异常处理方式不一样,offer()/peek()/poll()异常时返回null或false;add()/element()/remove()抛出异常30.哪些集合类是线程安全的?
31.迭代器 Iterator 是什么?
- 用于顺序访问集合对象的元素,无需知道集合对象的底层实现。
32.Iterator 怎么使用?有什么特点?
- 使用方法iterator()要求容器返回一个Iterator。Iterator将准备好返回序列的第一个元素。
- 使用next()获得序列中的下一个元素
- 使用hasNext()检查序列中是否还有元素。
- 使用remove()将迭代器新近返回的元素删除。
- 使用迭代器的remove()方法
- 使用for正循环遍历,每次删除时修改下标i=i-1
public static void main(String[] args) {
List<String> platformList = new ArrayList<>();
platformList.add("博客园");
platformList.add("CSDN");
platformList.add("掘金");
Iterator<String> iterator = platformList.iterator();
while (iterator.hasNext()) {
String platform = iterator.next();
if (platform.equals("博客园")) {
iterator.remove();
}
}
System.out.println(platformList);
}
33.Iterator 和 ListIterator 有什么区别?
- ListIterator 继承 Iterator
- ListIterator 比 Iterator多方法
- 使用范围不同,Iterator可以迭代所有集合;ListIterator 只能用于List及其子类
34.怎么确保一个集合不能被修改?
- 首先我们要清楚,集合(map,set,list…)都是引用类型,所以我们如果用final修饰的话,集合里面的内容还是可以修改的。可以采用Collections包下的unmodifiableMap方法,通过这个方法返回的map,是不可以修改的
35.unmodifiableMap不能修改的原理:
- 当调用其读相关方法(size、isEmpty、get)时,间接调用的是成员属性m的对应方法。
- 当调用其修改相关方法(put、remove、clear)时,直接抛出
UnsupportedOperationException
异常,不让修改。
三、多线程
35.并行和并发有什么区别?
- 并行:进程并行执行,多核cpu并行执行多个进程
- 并发:一个进程的多个线程并发,并发只是看起来像同时执行,其实是轮流使用cpu的
36.线程和进程的区别?
1)功能不同
- 进程就是运行中的程序,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
- 线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
2)工作原理不同
- 进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
- 线程是独立调度和分派的基本单位。线程可以为操作系统内核调度的内核线程,如Win32线程;由用户进程自行调度的用户线程,如Linux平台的POSIX Thread;或者由内核与用户进程,如Windows 7的线程,进行混合调度。
3)作用不同
- 进程是操作系统中最基本、重要的概念。是多道程序系统出现后,为了刻画系统内部出现的动态情况,描述系统内部各道程序的活动规律引进的一个概念,所有多道程序设计操作系统都建立在进程的基础上。
- 通常在一个进程中可以包含若干个线程,它们可以利用进程所拥有的资源。在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。
36.python协程是什么?与线程有什么区别?
- 进程上的一把锁,python程序运行时,只有一个线程可以获取这把锁,这也是python为什么是伪并发的原因
37.守护线程是什么?
- java里线程分2种,守护线程、用户线程。守护线程是专门用于服务其他的线程,比如垃圾回收线程,就是最典型的守护线程。
- 如果其他的线程(即用户自定义线程)都执行完毕,连main线程也执行完毕,那么jvm就会退出(即停止运行)——此时,连jvm都停止运行了,守护线程当然也就停止执行了。
- 当线程只剩下守护线程的时候,JVM就会退出;补充一点如果还有其他的任意一个用户线程还在,JVM就不会退出。
37.使用守护线程需要注意什么?
- thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。
- 在Daemon线程中产生的新线程也是Daemon的。
- 守护线程不能用于去访问固有资源,比如读写操作或者计算逻辑。因为它会在任何时候甚至在一个操作的中间发生中断。
- Java自带的多线程框架,比如ExecutorService,会将守护线程转换为用户线程,所以如果要使用后台线程就不能用Java的线程池。
38.创建线程有哪几种方式?
- 两种,继承Thread类或者实现Runnable接口,因为Java不允许多继承,但允许实现多个接口,所以推荐使用实现Runnable接口的方式
39.说一下 runnable 和 callable 有什么区别?
- 两者最大的不同点是:实现Callable接口的任务线程能返回执行结果;而实现Runnable接口的任务线程不能返回结果;
- Callable接口的call()方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛;
39.callable创建线程的demo
public class CallableImpl implements Callable<String> {
private String acceptStr;
public CallableImpl(String acceptStr) {
this.acceptStr = acceptStr;
}
@Override
public String call() throws Exception {
// 任务阻塞 1 秒
Thread.sleep(1000);
return this.acceptStr + " append some chars and return it!";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<String> callable = new CallableImpl("my callable test!");
FutureTask<String> task = new FutureTask<>(callable);
long beginTime = System.currentTimeMillis();
// 创建线程
new Thread(task).start();
// 调用get()阻塞主线程,反之,main线程不会阻塞
String result = task.get();
long endTime = System.currentTimeMillis();
System.out.println("hello : " + result);
System.out.println("cast : " + (endTime - beginTime) / 1000 + " second!");
}
}
40.线程有哪些状态?
- 创建(new)、就绪(runnable)、运行(running)、阻塞(blocked)、time waiting、waiting、消亡(dead)
41.sleep() 和 wait() 有什么区别?
- wait、notify/notifyAll是Object的本地final方法,wait使当前线程阻塞,前提是必须先获得锁,所以只能在synchronized锁范围内里使用wait、notify/notifyAll方法,
- sleep方法是Thead类的静态方法,sleep可以在任何地方使用,
- sleep() 和 wait() 都会释放cpu资源,但sleep()不会释放锁,wait()会释放锁
42.notify()和 notifyAll()有什么区别?
- notify():唤醒等待线程
- notifyAll():唤醒所有等待线程,但最终也只有一个线程能够获得锁
42.选择使用notify还是notifyAll()?
- notify方法和notifyAll()方法两者非常相似,到底该用哪一个,老实说,这个选择有点困难。选择notify的话,因为要唤醒的线程比较少(only one),程序的处理速度当然比notifyAll略胜一筹。但是选择notify时,若这部分处理不好,可能会出现程序挂掉的危险。一般说来,选择notifyAll所写出来的程序代码会比notify可靠。除非你能确定程序员对程序代码的意义和能力限度一清二楚,否则选择notifyAll应该是比较稳扎稳打。
43.线程的 run()和 start()有什么区别?
- start() 方法是用来启动线程的,轮到该线程执行时,会自动调用 run();
- 直接调用 run() 方法,无法达到启动多线程的目的,相当于主线程线性执行 Thread 对象的 run() 方法。
- 一个线程对线的 start() 方法只能调用一次,多次调用会抛出 java.lang.IllegalThreadStateException 异常;run() 方法没有限制。
44.创建线程池有哪几种方式?
- 通过Executors工厂方法创建
- 通过new
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)自定义创建
推荐:使用ThreadPoolExecutor方式创建线程池(阿里不允许使用
Executors,使用ThreadPoolExecutor可以避免资源耗尽的风险)
//创建使用单个线程的线程池
ExecutorService es1 = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
es1.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "正在执行任务");
}
});
}
//创建使用固定线程数的线程池
ExecutorService es2 = Executors.newFixedThreadPool(3);
//创建一个会根据需要创建新线程的线程池
ExecutorService es3 = Executors.newCachedThreadPool();
//创建拥有固定线程数量的定时线程任务的线程池
ScheduledExecutorService es4 = Executors.newScheduledThreadPool(2);
//创建只有一个线程的定时线程任务的线程池
ScheduledExecutorService es5 = Executors.newSingleThreadScheduledExecutor();
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(5, 20, 2, TimeUnit.SECONDS, new LinkedBlockingQueue<>(5));
for (int i = 0; i < size; i++) {
poolExecutor.execute(new DemoTask(i));
Console.log("poolSize:" + poolExecutor.getPoolSize());
}
46.线程池中 submit()和 execute()方法有什么区别?
- 参数不同:execute() 参数 Runnable ;submit() 参数 (Runnable) 或 (Runnable 和 结果 T) 或 (Callable)
- 返回值不同:execute() 没有返回值;而 submit() 有返回值,submit() 的返回值 Future 调用get方法时,可以捕获处理异常
- 总结:推荐使用submit()
45.线程池都有哪些状态?
- RUNNING
- SHUTDOWN:不接受新的任务提交,但是会继续处理等待队列中的任务。调用shutdown()方法时变为这个状态
- STOP:不接受新的任务提交,不再处理等待队列中的任务,中断正在执行任务的线程。调用shutdownNow()方法时变为这个状态
- TIDYING:所有的任务都销毁了,workCount 为 0,线程池的状态在转换为 TIDYING 状态时,会执行钩子方法 terminated()。因为terminated()在ThreadPoolExecutor类中是空的,所以用户想在线程池变为TIDYING时进行相应的处理;可以通过重载terminated()函数来实现。
- TERMINATED
47.在 java 程序中怎么保证多线程的运行安全?
- JDK Atomic开头的原子类、synchronized、LOCK,可以解决原子性问题
- synchronized、volatile、LOCK,可以解决可见性问题
- Happens-Before 规则可以解决有序性问题
48.多线程锁的升级原理是什么?
- 资源竞争加剧是会进行锁升级,升级方向是无锁-偏向锁-轻量级锁-重量级锁
49.什么是死锁?
- 两个线程互相等待对方的资源
50.怎么防止死锁?
- 破坏产生的死锁的4个条件
- 超时退出,释放资深资源
- 降低锁的使用粒度,尽量不要几个功能用同一把锁
- 尽量减少同步的代码块。
51.ThreadLocal 是什么?有哪些使用场景?
52.说一下 synchronized 底层实现原理?
- 通过调用底层的monitor锁互斥访问共享资源。一个进程只有一把monitor锁,所以当一个线程获得进入synchronized代码块时,其他线程进不去其他被synchronized修饰的代码块
53.synchronized 和 volatile 的区别是什么?
- synchronized:加锁,底层通过monitor锁互斥访问共享资源
- volatile:无锁,通过CAS操作互斥访问共享
54.synchronized 和 Lock(ReentrantLock )有什么区别?
- 最关键的区别是synchronized同一时间只允许一个线程访问互斥代码块(即使这些互斥代码块不是同一个代码块);Lock允许多个线程同时访问不同加了Lock的代码块
56.说一下 atomic 的原理?
- cas机制:每次修改变量值的时候,都会比较工作内存中的旧值与主存的旧值是否相等,相等说明没有其他线程修改过,可以修改;弊端是cas自璇操作耗费cpu资源,和ABA问题
四、反射
57.什么是反射?
- 定义:运行时获取类的信息以及运行时根据类名创建实例等。
- 应用场景:Spring AOP
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
public class Test01 {
public static void main(String[] args) throws ClassNotFoundException {
String a = "java.lang.String";
// 根据一类的全名字符串来获得一个类的类对象
Class<?> clazz = Class.forName(a);
// 获得传递过来的类的所有方法
Method[] methods = clazz.getDeclaredMethods();
for (Method m: methods) {
System.out.println(m);
}
// 获得类的所有属性
Field[] declaredFields = clazz.getDeclaredFields();
for(Field d : declaredFields) {
System.out.println(d);
}
// 获得类的所有构造器
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
for (Constructor c : constructors) {
System.out.println(c);
}
}
}
58.什么是 java 序列化?什么情况下需要序列化?
- 序列化是将 Java 对象转换成字节流的过程。反序列化是将字节流转换成 Java 对象的过程。
- 当 Java 对象需要在网络上传输 或者 持久化存储到文件中时,就需要对 Java 对象进行序列化处理。深拷贝也可以借助序列化实现
- 序列化的实现:类实现 Serializable 接口,这个接口没有需要实现的方法。实现 Serializable 接口是为了告诉 jvm 这个类的对象可以被序列化。
@SuppressWarnings("resource")
public static void main(String[] args) throws IOException,ClassNotFoundException {
//序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("TestSerializable.obj"));
oos.writeObject("测试序列化");
oos.writeObject(618);
TestSerializable test = new TestSerializable(1, "ConstXiong");
oos.writeObject(test);
//反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("TestSerializable.obj"));
System.out.println((String)ois.readObject());
System.out.println((Integer)ois.readObject());
System.out.println((TestSerializable)ois.readObject());
}
59.动态代理是什么?有哪些应用?
- 当想要给实现了某个接口的类中的方法,加一些额外的处理,比如说加日志,加事务等。可以给这个类创建一个代理,故名思议就是创建一个新的类,这个类不仅包含原来类方法的功能,而且还在原来的基础上添加了额外处理的新类。这个代理类并不是定义好的,是动态生成的。具有解耦意义,灵活,扩展性强。
- 动态代理的应用:Spring的AOP,加事务,加权限,加日志。
60.怎么实现动态代理?
- jdk动态代理:jdk动态代理是由Java内部的反射机制来实现的,目标类基于统一的接口InvocationHandler
- cglib动态代理,cglib动态代理底层则是借助asm来实现的,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。
package com.example.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyInvocationHandler implements InvocationHandler {
private Object target;
/**
* 绑定委托对象并返回一个代理类
*
* @param target
* @return
*/
public Object bind(Object target) {
this.target = target;
//取得代理对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this); //要绑定接口(这是一个缺陷,cglib弥补了这一缺陷)
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("getName".equals(method.getName())) {
System.out.println("------before " + method.getName() + "------");
Object result = method.invoke(target, args);
System.out.println("------after " + method.getName() + "------");
return result;
} else {
Object result = method.invoke(target, args);
return result;
}
}
}
package com.example.jdk;
import com.example.service.UserService;
import com.example.service.impl.UserServiceImpl;
/**
* 测试类
*/
public class RunJDK {
public static void main(String[] args) {
MyInvocationHandler proxy = new MyInvocationHandler();
UserService userServiceProxy = (UserService) proxy.bind(new UserServiceImpl());
System.out.println(userServiceProxy.getName(1));
System.out.println(userServiceProxy.getAge(1));
}
}
五、对象拷贝
61.为什么要使用克隆?
- 想对一个对象进行处理,又想保留原有的数据进行接下来的操作,就需要克隆了。
62.如何实现对象克隆?
- 通过拷贝构造方法实现浅拷贝
class Person{
private Age age;
private String name;
public Person(Age age,String name) {
this.age=age;
this.name=name;
}
//拷贝构造方法
public Person(Person p) {
this.name=p.name;
this.age=p.age;
}
- 通过重写clone()方法进行浅拷贝
class Student implements Cloneable{
//学生类的成员变量(属性),其中一个属性为类的对象
private String name;
private Age aage;
private int length;
//重写Object类的clone方法
public Object clone() {
Object obj=null;
//调用Object类的clone方法,返回一个Object实例
try {
obj= super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return obj;
}
}
63.深拷贝和浅拷贝区别是什么?
- 浅克隆后的对象中非基本对象和原对象指向同一块内存,因此对这些非基本对象的修改会同时更改克隆前后的对象
- 深克隆可以实现完全的克隆,可以用重写clone()方法或序列化的方式实现。
六、Java Web
64.jsp 和 servlet 有什么区别?
65.jsp 有哪些内置对象?作用分别是什么?
66.说一下 jsp 的 4 种作用域?
67.session 和 cookie 有什么区别?
68.说一下 session 的工作原理?
69.如果客户端禁止 cookie 能实现 session 还能用吗?
70.spring mvc 和 struts 的区别是什么?
71.什么是sql 注入,如何避免?
SQL注入是一种注入攻击,可以执行恶意SQL语句。
- 防止措施:不要使用动态SQL:避免将用户提供的输入直接放入SQL语句中;最好使用准备好的语句和参数化查询,这样更安全。
- 补救措施:数据库中敏感数据加密存储
- 降低损失:限制数据库权限和特权:将数据库用户的功能设置为最低要求;这将限制攻击者在设法获取访问权限时可以执行的操作。
- 避免直接向用户显示数据库错误:攻击者可以使用这些错误消息来获取有关数据库的信息。
- 定期测试与数据库交互的Web应用程序:这样做可以帮助捕获可能允许SQL注入的新错误或回归
72.什么是 XSS 攻击,如何避免?
攻击者往 web 页面里插入恶意的 HTML 代码(Javascript、css、html 标签等),当用户浏览该页面时,嵌入其中的 HTML 代码会被执行,从而达到恶意攻击用户的目的。如盗取用户 cookie 执行一系列操作,破坏页面结构、重定向到其他网站等。
- 预防思路:web 页面中可由用户输入的地方,如果对输入的数据转义、过滤处理;后台输出页面的时候,也需要对输出内容进行转义、过滤处理(因为攻击者可能通过其他方式把恶意脚本写入数据库);前端对 html 标签属性、css 属性赋值的地方进行校验
73.什么是 CSRF 攻击,如何避免?
简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并运行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。
- 预防思路:检查HTTP头的Referer字段;添加校验token
七、异常
74.throw 和 throws 的区别?
throw:
- 表示方法内抛出某种异常对象
- 如果异常对象是非 RuntimeException 则需要在方法申明时加上throws 语句向上抛出异常
- 执行到 throw 语句则后面的语句块不再执行
throws:
- 方法的定义上使用 throws 表示这个方法可能抛出某种异常
- 需要由方法的调用者进行异常处理
75.final、finally、finalize 有什么区别?
- final可以用来修饰类、方法、变量,分别有不同的意义所在,final修饰的class代表不可继续扩展,final修饰的变量代表不可修改,final修饰的方法代表不可重写。
- finally则是java保证某一段重点代码一定要被执行的修饰符,例如:我们需要用try块让JDBC保证连接,保证unlock锁等动作
- finalize()是Object中的方法,当垃圾回收器将要回收对象所占内存之前被调用,即当一个对象被虚拟机宣告死亡时会先调用它finalize()方法,让此对象处理它生前的最后事情
76.try-catch-finally 中哪个部分可以省略?
- catch 和 finally 语句块可以省略其中一个。
77.try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?
会执行
- 不管有没有异常,finally中的代码都会执行
- 当try、catch中有return时,finally中的代码依然会继续执行
- finally是在return后面的表达式运算之后执行的,此时并没有返回运算之后的值,而是把值保存起来,不管finally对该值做任何的改变,返回的值都不会改变,依然返回保存起来的值。也就是说方法的返回值是在finally运算之前就确定了的。
- 如果return的数据是引用数据类型,而在finally中对该引用数据类型的属性值的改变起作用,try中的return语句返回的就是在finally中改变后的该属性的值。
- finally代码中最好不要包含return,程序会提前退出,也就是说返回的值不是try或catch中的值
78.常见的异常类有哪些?
- NullPointerException、SQLException、IndexOutOfBoundsException、NumberFormatException、FileNotFoundException、IOException
- IllegalArgumentException、NoSuchMethodException、SecurityException、UnsupportedOperationException、RuntimeExceptionRuntimeException
八、网络
79.http 响应码 301 和 302 代表的是什么?有什么区别?
1xx: 信息
消息: | 描述: |
---|---|
100 Continue | 服务器仅接收到部分请求,但是一旦服务器并没有拒绝该请求,客户端应该继续发送其余的请求。 |
101 Switching Protocols | 服务器转换协议:服务器将遵从客户的请求转换到另外一种协议。 |
103 Checkpoint | 用于 PUT 或者 POST 请求恢复失败时的恢复请求建议。 |
2xx: 成功
消息: | 描述: |
---|---|
200 OK | 请求成功(这是对HTTP请求成功的标准应答。) |
201 Created | 请求被创建完成,同时新的资源被创建。 |
202 Accepted | 供处理的请求已被接受,但是处理未完成。 |
203 Non-Authoritative Information | 请求已经被成功处理,但是一些应答头可能不正确,因为使用的是其他文档的拷贝。 |
204 No Content | 请求已经被成功处理,但是没有返回新文档。浏览器应该继续显示原来的文档。如果用户定期地刷新页面,而Servlet可以确定用户文档足够新,这个状态代码是很有用的。 |
205 Reset Content | 请求已经被成功处理,但是没有返回新文档。但浏览器应该重置它所显示的内容。用来强制浏览器清除表单输入内容。 |
206 Partial Content | 客户发送了一个带有Range头的GET请求,服务器完成了它。 |
3xx: 重定向
消息: | 描述: |
---|---|
300 Multiple Choices | 多重选择。链接列表。用户可以选择某链接到达目的地。最多允许五个地址。 |
301 Moved Permanently | 所请求的页面已经转移至新的 URL 。 |
302 Found | 所请求的页面已经临时转移至新的 URL 。 |
303 See Other | 所请求的页面可在别的 URL 下被找到。 |
304 Not Modified | 未按预期修改文档。客户端有缓冲的文档并发出了一个条件性的请求(一般是提供If-Modified-Since头表示客户只想比指定日期更新的文档)。服务器告诉客户,原来缓冲的文档还可以继续使用。 |
305 Use Proxy | 客户请求的文档应该通过Location头所指明的代理服务器提取。 |
306 Switch Proxy | 目前已不再使用,但是代码依然被保留。 |
307 Temporary Redirect | 被请求的页面已经临时移至新的 URL 。 |
308 Resume Incomplete | 用于 PUT 或者 POST 请求恢复失败时的恢复请求建议。 |
4xx: 客户端错误
消息: | 描述: |
---|---|
400 Bad Request | 因为语法错误,服务器未能理解请求。 |
401 Unauthorized | 合法请求,但对被请求页面的访问被禁止。因为被请求的页面需要身份验证,客户端没有提供或者身份验证失败。 |
402 Payment Required | 此代码尚无法使用。 |
403 Forbidden | 合法请求,但对被请求页面的访问被禁止。 |
404 Not Found | 服务器无法找到被请求的页面。 |
405 Method Not Allowed | 请求中指定的方法不被允许。 |
406 Not Acceptable | 服务器生成的响应无法被客户端所接受。 |
407 Proxy Authentication Required | 用户必须首先使用代理服务器进行验证,这样请求才会被处理。 |
408 Request Timeout | 请求超出了服务器的等待时间。 |
409 Conflict | 由于冲突,请求无法被完成。 |
410 Gone | 被请求的页面不可用。 |
411 Length Required | "Content-Length" 未被定义。如果无此内容,服务器不会接受请求。 |
412 Precondition Failed | 请求中的前提条件被服务器评估为失败。 |
413 Request Entity Too Large | 由于所请求的实体太大,服务器不会接受请求。 |
414 Request-URI Too Long | 由于 URL 太长,服务器不会接受请求。当 POST 请求被转换为带有很长的查询信息的 GET 请求时,就会发生这种情况。 |
415 Unsupported Media Type | 由于媒介类型不被支持,服务器不会接受请求。 |
416 Requested Range Not Satisfiable | 客户端请求部分文档,但是服务器不能提供被请求的部分。 |
417 Expectation Failed | 服务器不能满足客户在请求中指定的请求头。 |
消息: | 描述: |
---|---|
500 Internal Server Error | 请求未完成。服务器遇到不可预知的情况。 |
501 Not Implemented | 请求未完成。服务器不支持所请求的功能,或者服务器无法完成请求。 |
502 Bad Gateway | 请求未完成。服务器充当网关或者代理的角色时,从上游服务器收到一个无效的响应。 |
503 Service Unavailable | 服务器当前不可用(过载或者当机)。 |
504 Gateway Timeout | 网关超时。服务器充当网关或者代理的角色时,未能从上游服务器收到一个及时的响应。 |
505 HTTP Version Not Supported | 服务器不支持请求中指明的HTTP协议版本。 |
511 Network Authentication Required | 用户需要提供身份验证来获取网络访问入口。 |
80.forward 和 redirect 的区别?
- 直接转发方式(Forward):客户端和浏览器只发出一次请求,Servlet、HTML、JSP或其它信息资源,由第二个信息资源响应该请求,在请求对象request中,保存的对象对于每个信息资源是共享的。对应RequestDispatcher类的forward()方法
- 间接转发方式(Redirect):实际是两次HTTP请求,服务器端在响应第一次请求的时候,让浏览器再向另外一个URL发出请求,从而达到转发的目的。HttpServletRequest类的sendRedirect()方法。
80.get 和 post 请求有哪些区别?
- 短的说:GET产生一个TCP数据包;POST产生两个TCP数据包。
- 长的说:
- 对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
- 而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
80.浏览器输入url按回车背后经历了哪些?
- 解析url,检测url地址是否合法
- 浏览器先查看浏览器缓存-系统缓存-路由器缓存,如果缓存中有,会直接在屏幕中显示页面内容。若没有,则跳到第三步操作。
- 在发送http请求前,需要域名解析(DNS解析),解析获取相应的IP地址。
- 浏览器向服务器发起tcp连接,与浏览器建立tcp三次握手。
- 握手成功后,浏览器向服务器发送http请求,请求数据包。
- 服务器处理收到的请求,将数据返回至浏览器
- 浏览器收到HTTP响应
- 浏览器解码响应,如果响应可以缓存,则存入缓存。
- 浏览器发送请求获取嵌入在HTML中的资源(html,css,javascript,图片,音乐······),对于未知类型,会弹出对话框。
- 浏览器发送异步请求。
- 页面全部渲染结束。
80.cookies机制和session机制的区别
- cookies数据保存在客户端,session数据保存在服务器端;
- cookies可以减轻服务器压力,但是不安全,容易进行cookies欺骗;
- session较安全,但占用服务器资源
80.http协议请求方式
- GET, POST 和 HEAD、OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。
80.http和https区别?
HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。简单来说,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。总的来说: HTTPS=SSL+HTTP。HTTPS和HTTP的区别主要如下:
- https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
- http和https使用的是完全不同的连接方式,默认端口也不一样,前者是80,后者是443。(这个只是默认端口不一样,实际上端口是可以改的)
- http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
80.http报文
请求报文包含三部分:
- 请求行:包含请求方法、URI、HTTP版本信息
- 请求头部(headers)字段
- 请求内容实体(body)
响应报文包含三部分:
- 状态行:包含HTTP版本、状态码、状态码的原因短语
- 响应头部(headers)字段
- 响应内容(body)实体
81.简述 tcp 和 udp的区别?
TCP | UDP | |
---|---|---|
是否需要连接 | 基于连接 | 无连接 |
对系统资源的要求 | 较多 | 少 |
流模式 | 数据报模式 | |
保证数据正确性、数据顺序 | 可能丢包、不保证顺序 |
82.tcp 为什么要三次握手,两次不行吗?为什么?
83.说一下tcp 粘包是什么?
- 发送方发送的多个数据包,到接收方缓冲区首尾相连,粘成一包,被接收。
83. tcp 粘包是怎么产生的?
- TCP 协议默认使用 Nagle 算法可能会把多个数据包一次发送到接收方。
- 应用程读取缓存中的数据包的速度小于接收数据包的速度,缓存中的多个数据包会被应用程序当成一个包一次读取。
83. tcp 粘包怎么解决?
- 发送方使用 TCP_NODELAY 选项来关闭 Nagle 算法
- 数据包增加开始符和结束符,应用程序读取、区分数据包。
- 在数据包的头部定义整个数据包的长度,应用程序先读取数据包的长度,然后读取整个长度的包字节数据,保证读取的是单个包且完整。
84.OSI 的七层模型都有哪些?
84.如果已经建立了连接,但是客户端突然出现故障了怎么办?
- TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75分钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。
84.TCP协议如何保证可靠传输
- 确认应答机制、超时重传机制、流量控制机制、拥塞控制机制
86.什么是跨域?
- 域名跨越,比如A工程的前端访问B工程的后端接口
86.如何实现跨域?
- jsonp方式:JS设计模式中的一种代理模式。在html页面中通过相应的标签从不同域名下加载静态资源文件是被浏览器允许的(从js中调用不行),所以我们可以通过这个“漏洞”来进行跨域。局限是只能get请求。
- CORS:实现CORS通信的关键是服务器。基本只需要对服务器动手脚,前端代码跟同源时一样,也就是不跨域时一样。
- nginx代理跨域: 同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨越问题。通过nginx配置一个代理服务器做跳板机,反向代理访问domain2接口实现跨域登录。
- 注:反向代理:正常我们可以通过内网可以通过代理来访问外网,这个算是正向代理;反向代理就相当于通过代理让外网可以访问到我们内网
87.说一下 JSONP 实现原理?
- 参考86题
九、设计模式
88.说一下你熟悉的设计模式?
- 单例模式、责任链模式
- 单例模式优点:内存里只有一个实例减少了内存的开销、可以避免对资源的多重占用、单例模式设置全局访问点,可以优化和共享资源的访问。
- 单例模式的缺点:没有接口扩展困难、在并发测试中,单例模式不利于代码调试、
88.单例模式?
- 懒汉模式
public class LazySingleton {
private static volatile LazySingleton instance = null; //保证 instance 在所有线程中同步
private LazySingleton() {
} //private 避免类在外部被实例化
public static synchronized LazySingleton getInstance() {
//getInstance 方法前加同步
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
- 饿汉模式
public class HungrySingleton {
private static final HungrySingleton instance = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return instance;
}
}
单例模式代码重点:
- 构造函数需要用private修饰
- 懒汉模式需要 volatile修饰,饿汉模式需要final模式修饰
88.原型模式
class Prototype implements Cloneable {
public Prototype clone(){
Prototype prototype = null;
try{
prototype = (Prototype)super.clone();
}catch(CloneNotSupportedException e){
e.printStackTrace();
}
return prototype;
}
}
class ConcretePrototype extends Prototype{
public void show(){
System.out.println("原型模式实现类");
}
}
public class Client {
public static void main(String[] args){
ConcretePrototype cp = new ConcretePrototype();
for(int i=0; i< 10; i++){
ConcretePrototype clonecp = (ConcretePrototype)cp.clone();
clonecp.show();
}
}
}
原型模式主要用于对象的复制,它的核心是就是类图中的原型类Prototype。Prototype类需要具备以下两个条件:
- 实现Cloneable接口。在java语言有一个Cloneable接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException异常。
- 重写Object类中的clone方法。Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此,Prototype类需要将clone方法的作用域修改为public类型。
- 原型模式是一种比较简单的模式,也非常容易理解,实现一个接口,重写一个方法即完成了原型模式。在实际应用中,原型模式很少单独出现。经常与其他模式混用,他的原型类Prototype也常用抽象类来替代。
88.代理模式?
1)代理模式的好处
- 中介隔离作用:在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。
- 开闭原则,增加功能:代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。
2)静态代理
//第一步:创建服务类接口
public interface BuyHouse {
void buyHosue();
}
//第二步:实现服务接口
import main.java.proxy.BuyHouse;
public class BuyHouseImpl implements BuyHouse {
@Override
public void buyHosue() {
System.out.println("我要买房");
}
}
//第三步:创建代理类
package main.java.proxy.impl;
import main.java.proxy.BuyHouse;
public class BuyHouseProxy implements BuyHouse {
private BuyHouse buyHouse;
public BuyHouseProxy(final BuyHouse buyHouse) {
this.buyHouse = buyHouse;
}
@Override
public void buyHosue() {
System.out.println("买房前准备");
buyHouse.buyHosue();
System.out.println("买房后装修");
}
}
//第四步:编写测试类
import main.java.proxy.impl.BuyHouseImpl;
import main.java.proxy.impl.BuyHouseProxy;
public class ProxyTest {
public static void main(String[] args) {
BuyHouse buyHouse = new BuyHouseImpl();
buyHouse.buyHosue();
BuyHouseProxy buyHouseProxy = new BuyHouseProxy(buyHouse);
buyHouseProxy.buyHosue();
}
}
静态代理总结:
- 优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。
- 缺点:我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改
3)动态代理
在动态代理中我们不再需要再手动的创建代理类,我们只需要编写一个动态处理器就可以了。真正的代理对象由JDK再运行时为我们动态的来创建。
//第一步:编写动态处理器
package main.java.proxy.impl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicProxyHandler implements InvocationHandler {
private Object object;
public DynamicProxyHandler(final Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("买房前准备");
Object result = method.invoke(object, args);
System.out.println("买房后装修");
return result;
}
}
//第二步:编写测试类
package main.java.proxy.test;
import main.java.proxy.BuyHouse;
import main.java.proxy.impl.BuyHouseImpl;
import main.java.proxy.impl.DynamicProxyHandler;
import java.lang.reflect.Proxy;
public class DynamicProxyTest {
public static void main(String[] args) {
BuyHouse buyHouse = new BuyHouseImpl();
BuyHouse proxyBuyHouse = (BuyHouse) Proxy.newProxyInstance(BuyHouse.class.getClassLoader(), new
Class[]{BuyHouse.class}, new DynamicProxyHandler(buyHouse));
proxyBuyHouse.buyHosue();
}
}
88.工厂模式?
- 工厂模式是什么是创建模式的一种,共分为简单工厂,工厂方法和抽象工厂三种
1)使用工厂模式的好处是什么?
- 解耦:把对象的创建和使用的过程分开。就是Class A 想调用 Class B ,那么A只是调用B的方法,而至于B的实例化,就交给工厂类。
- 降低代码重复:如果创建对象B的过程都很复杂,需要一定的代码量,而且很多地方都要用到,那么就会有很多的重复代码。我们可以这些创建对象B的代码放到工厂里统一管理。既减少了重复代码,也方便以后对B的创建过程的修改维护。(当然,我个人觉得也可以把这些创建过程的代码放到类的构造函数里,同样可以降低重复率,而且构造函数本身的作用也是初始化对象。不过,这样也会导致构造函数过于复杂,做的事太多,不符合java 的设计原则。)
- 减少修改量:由于创建过程都由工厂统一管理,所以发生业务逻辑变化,不需要找到所有需要创建B的地方去逐个修正,只需要在工厂里修改即可,降低维护成本。同理,想把所有调用B的地方改成B的子类B1,只需要在对应生产B的工厂中或者工厂的方法中修改其生产的对象为B1即可,而不需要找到所有的new B()改为new B1()。
- 减少使用复杂度:另外,因为工厂管理了对象的创建逻辑,使用者并不需要知道具体的创建过程,只管使用即可,减少了使用者因为创建逻辑导致的错误。
2)工厂模式的具体分类及实现代码
- 产品类代码如下:
public interface Shape {
void draw();
}
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("画一个圆");
}
}
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("画一个矩形");
}
}
- 简单工厂
public class ShapeFactory {
public Shape getShape(String shapeType){
if(shapeType.equalsIgnoreCase("circle")){
return new Circle();
}else if(shapeType.equalsIgnoreCase("rectangle")){
return new Rectangle();
}else if(shapeType.equalsIgnoreCase("squere")){
return new Squere();
}
return null;
}
public Shape getShape(Class clazz) throws Exception{
return (Shape) clazz.newInstance();
}
}
- 工厂方法
public abstract class ShapeFactory {
public abstract Shape getShape();
}
public class CircleFactory extends ShapeFactory {
@Override
public Shape getShape() {
return new Circle();
}
}
public class RectangleFactroy extends ShapeFactory {
@Override
public Shape getShape() {
return new Rectangle();
}
}
3)简单工厂、工厂方法、抽象工厂的区别是什么?
- 简单工厂所有产品由同一个工厂创建
- 工厂方法每一个产品对应一个工厂
89.简单工厂和抽象工厂有什么区别?
十、Spring/Spring MVC
90.为什么要使用 spring?
spring 是一个开源的轻量级 JavaBean 容器框架。
- 轻量:非入侵性的、所依赖的东西少、资源占用少、部署简单,不同功能选择不同的 jar 组合
- 容器:工厂模式实现对 JavaBean 进行管理,通过控制反转(IOC)将应用程序的配置和依赖性与应用代码分开
- 松耦合:通过 xml 配置或注解即可完成 bean 的依赖注入
- AOP:通过 xml 配置 或注解即可加入面向切面编程的能力,完成切面功能,如:日志,事务...的统一处理
- 方便集成:通过配置和简单的对象注入即可集成其他框架,如 Mybatis、Hibernate、Shiro...
- 丰富的功能:JDBC 层抽象、事务管理、MVC、Java Mail、任务调度、JMX、JMS、JNDI、EJB、动态语言、远程访问、Web Service...
91.解释一下什么是 aop?
- 面向切面编程技术:这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。
- 使用AOP技术,可以将一些系统性相关的编程工作,独立提取出来,独立实现,然后通过切面切入进系统。从而避免了在业务逻辑的代码中混入很多的系统相关的逻辑——比如权限管理,事物管理,日志记录等等。
92.解释一下什么是 ioc?
- 控制反转,是 spring 的核心,对于 spring 框架来说,就是由 spring 来负责控制对象的生命周期和对象间的关系。 简单来说,控制指的是当前对象对内部成员的控制权;控制反转指的是,这种控制权不由当前对象管理了,由其他(类,第三方容器)来管理。
93.spring 有哪些主要模块?
Spring框架的七大模块
- 1. Spring Core:框架的最基础部分,提供 IoC 容器,对 bean 进行管理。
- 2.Spring Context:基于 bean,提供上下文信息,扩展出JNDI、EJB、电子邮件、国际化、校验和调度等功能。
- 3.Spring DAO:提供了JDBC的抽象层,它可消除冗长的JDBC编码和解析数据库厂商特有的错误代码,还提供了声明性事务管理方法。
- 4.Spring ORM:提供了常用的“对象/关系”映射APIs的集成层。 其中包括JPA、JDO、Hibernate、MyBatis 等。
- 5.Spring AOP:提供了符合AOP Alliance规范的面向方面的编程实现。
- 6.Spring Web:提供了基础的 Web 开发的上下文信息,可与其他 web 进行集成。
- 7.Spring Web MVC:提供了 Web 应用的 Model-View-Controller 全功能实现。
94.spring 常用的注入方式有哪些?
- 构造方法注入,setter注入,基于注解的注入。
95.spring 中的 bean 是线程安全的吗?
结论是非线程安全的。Spring容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体还是要结合具体scope的Bean去研究。Spring 的 bean 作用域(scope)类型
- singleton:单例,默认作用域。
- prototype:原型,每次创建一个新对象。
- request:请求,每次Http请求创建一个新对象,适用于WebApplicationContext环境下。
- session:会话,同一个会话共享一个实例,不同会话使用不用的实例。
- global-session:全局会话,所有会话共享一个实例。
线程安全这个问题,要从单例与原型Bean分别进行说明。对于原型Bean,每次创建一个新对象,也就是线程之间并不存在Bean共享,自然是不会有线程安全的问题。对于单例Bean,所有线程都共享一个单例实例Bean,因此是存在资源的竞争。如果单例Bean,是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例Bean是线程安全的。比如Spring mvc 的 Controller、Service、Dao等,这些Bean大多是无状态的,只关注于方法本身。
96.spring 支持几种 bean 的作用域?
- 5种,参考94题
97.spring 自动装配 bean 有哪些方式?
98.spring 事务实现方式有哪些?
99.说一下 spring 的事务隔离?
100.说一下 spring mvc 运行流程?
101.spring mvc 有哪些组件?
102.@RequestMapping 的作用是什么?
- 是一个注解,用来标识 http 请求地址与 Controller 类的方法之间的映射。
103.@Autowired 的作用是什么?与@Resource的区别?
- @Autowired 是一个注解,它可以对类成员变量、方法及构造函数进行标注,让 spring 完成 bean 自动装配的工作。
- @Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入,而@Resource默认按 byName自动注入罢了。
十一、Spring Boot/Spring Cloud
104.什么是 spring boot?
- Spring Boot是Spring组件一站式解决方案,主要是简化了使用Spring的难度,简省了繁重的配置,提供了各种启动器,开发者能快速上手。
105.为什么要用 spring boot?
- 参考104题
106.spring boot 核心配置文件是什么?
- application.yml:用于 Spring Boot 项目的自动化配置,包括数据库连接信息、redis连接信息等
- bootstrap:使用 Spring Cloud Config 配置中心时,这时需要在 bootstrap 配置文件中添加连接到配置中心的配置属性来加载外部配置中心的配置信息;
107.spring boot 配置文件有哪几种类型?它们有什么区别?
- .properties 和 .yml,它们的区别主要是书写格式不同。 另外,.yml 格式不支持 @PropertySource 注解导入配置。
.properties | app.user.name = javastack |
.yml | app: user: |
108.spring boot 有哪些方式可以实现热部署?
109.jpa 和 hibernate 有什么区别?
- JPA本身是一种规范,它的本质是一种ORM规范(不是ORM框架,因为JPA并未提供ORM实现,只是制定了规范)。
- JPA是标准接口,Hibernate是实现,并不是对标关系,借用下图可以看清楚他们之间的关系,Hibernate属于遵循JPA规范的一种实现,但是JPA是Hibernate遵循的规范之一,Hibernate还有其他实现的规范
110.什么是 spring cloud?
- 基于Spring Boot用于快速构建分布式系统的通用模式的工具集.使用Spring Cloud开发的应用程序非常适合在Docker或者Pass上部署,所以又叫云原生应用(Cloud Native Application). 云原生可以简单的理解为面向云环境的软件架构;
111.spring cloud 断路器的作用是什么?
- 在分布式架构中,断路器模式的作用也是类似的,当某个服务单元发生故障(类似用电器发生短路)之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个错误响应,而不是长时间的等待。这样就不会使得线程因调用故障服务被长时间占用不释放,避免了故障在分布式系统中的蔓延。
112.spring cloud 的核心组件有哪些?
- 服务发现——Netflix Eureka
- 客服端负载均衡——Netflix Ribbon
- 断路器——Netflix Hystrix
- 服务网关——Netflix Zuul
- 分布式配置——Spring Cloud Config
十二、Hibernate
113.为什么要使用 hibernate?
114.什么是 ORM 框架?
115.hibernate 中如何在控制台查看打印的 sql 语句?
116.hibernate 有几种查询方式?
117.hibernate 实体类可以被定义为 final 吗?
118.在 hibernate 中使用 Integer 和 int 做映射有什么区别?
119.hibernate 是如何工作的?
120.get()和 load()的区别?
121.说一下 hibernate 的缓存机制?
122.hibernate 对象有哪些状态?
123.在 hibernate 中 getCurrentSession 和 openSession 的区别是什么?
124.hibernate 实体类必须要有无参构造函数吗?为什么?
十三、Mybatis
125.mybatis 中 #{}和 ${}的区别是什么?
126.mybatis 有几种分页方式?
127.RowBounds 是一次性查询全部结果吗?为什么?
128.mybatis 逻辑分页和物理分页的区别是什么?
129.mybatis 是否支持延迟加载?延迟加载的原理是什么?
130.说一下 mybatis 的一级缓存和二级缓存?
131.mybatis 和 hibernate 的区别有哪些?
132.mybatis 有哪些执行器(Executor)?
133.mybatis 分页插件的实现原理是什么?
134.mybatis 如何编写一个自定义插件?
十四、RabbitMQ
135.rabbitmq 的使用场景有哪些?
136.rabbitmq 有哪些重要的角色?
- 生产者:消息的创建者,负责创建和推送数据到消息服务器
- 消费者:消息的接收方,用于处理数据和确认消息
- 代理:rabbitmq 本身不生产消息,只是储存和传递消息
137.rabbitmq 有哪些重要的组件?
- ConnectionFactory(连接管理器):应用程序与 rabbit 之间建立连接的管理器,程序代码中使用
- Channel(信道):消息推送使用的通道
- Exchange(交换器):用于接收、分配消息
- Queue(队列):用于存储生产者的消息
- RoutingKey(路由键):用于把生产者的数据分配到交换器上
- BindingKey(绑定键):用于把交换器的消息绑定到队列上
138.rabbitmq 中 vhost 的作用是什么?
- vhost 可以理解为虚拟 broker,即 mini-RabbitMQ server。其内部均含有独立的 queue、exchange 和 binding 等,但最最重要的是,其拥有的独立的权限系统,可以做到vhost范围的用户控制。从rabbitmq的全局角度,vhost可以作为不同权限隔离的手段,不同的应用可以泡在不同的vhost中
139.rabbitmq 的消息是怎么发送的?
- 首先客户端必须连接到 RabbitMQ 服务器才能发布和消费消息,客户端和 rabbit server 之间会创建一个 tcp 连接,一旦 tcp 打开并通过了认证(认证就是你发送给 rabbit 服务器的用户名和密码),你的客户端和 RabbitMQ 就创建了一条 amqp 信道(channel),信道是创建在“真实” tcp 上的虚拟连接,amqp 命令都是通过信道发送出去的,每个信道都会有一个唯一的 id,不论是发布消息,订阅队列都是通过这个信道完成的。
140.rabbitmq 怎么保证消息的稳定性?
- 提供事务功能
- 将 channel 设置为 comfirm(确认)模式
141.rabbitmq 怎么避免消息丢失?
- 消息持久化
- ack 确认机制
- 设置集群镜像模式
- 消息补偿机制
142.要保证消息持久化成功的条件有哪些?
- 声明队列必须设置持久化,将 durable 设为 true
- 消息推送投递模式必须设置持久化,deliveryMode 设置为2(持久)
- 消息已经到达持久化交换器
- 消息已经到达持久化队列
143.rabbitmq 持久化有什么缺点?
- 持久化会降低服务器的吞吐量,因为使用磁盘而非内存存储,尽可能使用ssd硬盘来缓解吞吐量问题
144.rabbitmq 有几种广播类型?
- fanout:所有 bind 到此 exchange 的 queue 都可以接受消息
- direct:通过 routingKey 和 exchange 决定那个唯一的 queue 可以接收消息
- topic:符合表达式的 routing 所绑定的 queue 可以接收消息,* 匹配一个词,# 匹配一个或多个词
145.rabbitmq 怎么实现延迟消息队列?
- 通过消息过期后进入死信交换器,再由交换器转发到延迟消费队列,实现延迟功能
- 使用 RabbitMQ-delayed-message-exchange 插件实现延迟功能
146.rabbitmq 集群有什么用?
- 高可用:某个服务器出现问题,整个 rabbitmq 还可以继续使用
- 高容量:集群可以承载更多的消息量
147.rabbitmq 节点的类型有哪些?
- 磁盘节点:消息会存储到磁盘
- 内存节点:消息存储到内存中,重启服务器消息丢失,性能高于磁盘节点
148.rabbitmq 集群搭建需要注意哪些问题?
- 个节点之间使用“--link”连接,该属性不能忽略
- 个节点使用的 erlang cookie 值必须相同,该值相当于密钥,用于各节点的认证
- 整个集群中必须包含一个磁盘节点
149.rabbitmq 每个节点是其他节点的完整拷贝吗?为什么?
不是完整拷贝,原因:
- 存储空间的考虑:如果每个节点都拥有所有队列的完全拷贝,这样新增节点不但没有新增存储空间,反而增加了更多冗余数据
- 性能考虑:如果每条消息都需要完整拷贝到每一个集群节点,那新增节点并没有提升处理消息的能力,最多是保持和单节点相同的性能甚至更糟
150.rabbitmq 集群中唯一一个磁盘节点崩溃了会发生什么情况?
- 不能创建队列
- 不能创建交换器
- 不能创建绑定
- 不能创建用户
- 不能更改权限
- 不能添加和删除集群节点
唯一磁盘节点奔溃,集群可以保持运行,但不能更改任何东西
151.rabbitmq 对集群节点停止顺序有要求吗?
- rabbitmq 对集群定制顺序有要求,先关闭内存节点,最后关闭磁盘节点。如果顺序相反,会造成消息丢失
十五、Kafka
152.kafka 可以脱离 zookeeper 单独使用吗?为什么?
- kafka 不能脱离 zookeeper 单独使用,因为 kafka 使用 zookeeper 管理和协调 kafka 的节点服务器。
153.kafka 有几种数据保留的策略?
kafka 有两种数据保存策略:
- 按照过期时间保留。
- 按照存储的消息大小保留。
154.kafka 同时设置了 7 天和 10G 清除数据,到第五天的时候消息达到了 10G,这个时候 kafka 将如何处理?
- 这个时候 kafka 会执行数据清除工作,时间和大小不论那个满足条件,都会清空数据。
155.什么情况会导致 kafka 运行变慢?
- cpu 性能瓶颈
- 磁盘读写瓶颈
- 网络瓶颈
156.使用 kafka 集群需要注意什么?
- 集群的数量不是越多越好,最好不要超过 7 个,因为节点越多,消息复制需要的时间就越长,整个群组的吞吐量就越低。
- 集群数量最好是单数,因为超过一半故障集群就不能用了,设置为单数容错率更高。
156.Kafka性能强于RabbitMQ的原因
- 页缓存技术+磁盘顺序写(写数据):页缓存Page Cache—操作系统自己管理的内存缓存(os cache),由CPU决定了什么时候将数据写入磁盘
- 零拷贝技术(读数据): Kafka的设计是直接将操作系统OS Cache中的数据发送到网卡,跳过了两次拷贝数据的步骤,Socket缓存中仅仅会拷贝一个文件描述符过去,不会拷贝数据到Socket缓存,大大提升了数据读取性能。
十六、Zookeeper
157.zookeeper 是什么?
- 经典的分布式数据一致性解决方案,致力于为分布式应用提供一个高性能、高可用,且具有严格顺序访问控制能力的分布式协调服务。
158.zookeeper 都有哪些功能?
- 分布式应用程序可以基于ZooKeeper实现数据发布与订阅、负载均衡、命名服务、分布式协调与通知、集群管理、Leader选举、分布式锁、分布式队列等功能。
159.zookeeper 有几种部署模式?
- 单机部署:一般用来检验Zookeeper基础功能,熟悉ZK各种基础操作及特性;
- 伪集群部署:在单台机器上部署集群,方便在本地验证集群模式下的各种功能;
集群部署:一般在生产环境使用,具备一致性、分区容错性;
159.zookeeper 有几种角色?
- 领导者(leader):负责进行投票的发起和决议,更新系统状态
- 学习者(learner):包括跟随者(follower)和观察者(observer),follower用于接受客户端请求并想客户端返回结果,在选主过程中参与投票;Observer可以接受客户端连接,将写请求转发给leader,但observer不参加投票过程,只同步leader的状态,observer的目的是为了扩展系统,提高读取速度
- 客户端(client),请求发起方
159.zookeeper为什么需要Observer?
- Zookeeper需保证高可用和强一致性;为了支持更多的客户端,需要增加更多Server;Server增多,投票阶段延迟增大,影响性能;
- 权衡伸缩性和高吞吐率,引入Observer。Observer不参与投票;Observers接受客户端的连接,并将写请求转发给leader节点;
- 加入更多Observer节点,提高伸缩性,同时不影响吞吐率
- 总结:server有投票权,observer没有投票权,其他功能一样
160.zookeeper 怎么保证主从节点的状态同步?
-
Zookeeper的核心是原子广播,这个状态同步机制保证了leader和server具有相同的系统状态。实现这个机制的协议叫做Zab协议。Zab协议有两种模式,它们分别是恢复模式和广播模式。
160.恢复模式是什么?什么时候进入恢复模式?
- 当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数server的完成了和leader的状态同步以后,恢复模式就结束了。
160.广播模式是什么?什么时候进入广播模式?
- 一旦leader已经和多数的follower进行了状态同步后,leader就可以开始广播消息了,即进入广播状态。这时候当一个server加入zookeeper服务中,它会在恢复模式下启动,发现leader,并和leader进行状态同步。待到同步结束,它也参与消息广播。
- Zookeeper服务一直维持在Broadcast状态,直到leader崩溃了或者leader失去了大部分的followers支持。
160.zookeeper选举?
半数通过(大于,不是大于等于),比如3台机器挂一台,2>3/2;4台机器挂2台 2!>4/2
- A提案说,我要选自己,B你同意吗?C你同意吗?B说,我同意选A;C说,我同意选A。(注意,这里超过半数了,其实在现实世界选举已经成功了。但是计算机世界是很严格,另外要理解算法,要继续模拟下去。)
- 接着B提案说,我要选自己,A你同意吗;A说,我已经超半数同意当选,你的提案无效;C说,A已经超半数同意当选,B提案无效。
- 接着C提案说,我要选自己,A你同意吗;A说,我已经超半数同意当选,你的提案无效;B说,A已经超半数同意当选,C的提案无效。
- 选举已经产生了Leader,后面的都是follower,只能服从Leader的命令。
而且这里还有个小细节,就是其实谁先启动谁当头。
160.为什么zookeeper集群的数目一般为奇数个?
- Leader选举算法采用了Paxos协议;
- Paxos核心思想:当多数Server写成功,则任务数据写成功。如果有3个Server,则两个写成功即可;如果有4或5个Server,则三个写成功即可。Server数目一般为奇数(3、5、7)如果有3个Server,则最多允许1个Server挂掉;如果有4个Server,则同样最多允许1个Server挂掉由此,
- 我们看出3台服务器和4台服务器的的容灾能力是一样的,所以为了节省服务器资源,一般我们采用奇数个数,作为服务器部署个数。
162.集群中有 3 台服务器,其中一个节点宕机,这个时候 zookeeper 还可以使用吗?
- 可以
160.zookeeper的读写机制
- Zookeeper是一个由多个server组成的集群,一个leader,多个follower
- 每个server保存一份数据副本
- 全局数据一致
- 分布式读写
- 更新请求转发,由leader实施
160.数据操作流程
- 1.在Client向Follwer发出一个写的请求
- 2.Follwer把请求发送给Leader
- 3.Leader接收到以后开始发起投票并通知Follwer进行投票
- 4.Follwer把投票结果发送给Leader
- 5.Leader将结果汇总,如果需要写入,则开始写入同时把写入操作通知给Follwer,然后commit;
- 6.Follwer把请求结果返回给Client
160.Follower主要有四个功能:
- 向Leader发送请求(PING消息、REQUEST消息、ACK消息、REVALIDATE消息);
- 接收Leader消息并进行处理;
- 接收Client的请求,如果为写请求,发送给Leader进行投票;
- 返回Client结果。
160.zookeeper保证:
- 更新请求顺序进行,来自同一个client的更新请求按其发送顺序依次执行
- 数据更新原子性,一次数据更新要么成功,要么失败
- 全局唯一数据视图,client无论连接到哪个server,数据视图都是一致的
- 实时性,在一定事件范围内,client能读到最新数据
160.zookeeper如何保证写请求顺序执行?(数据一致性与Paxos算法)
- 保持数据的一致性有个原则就是:在一个分布式数据库系统中,如果各节点的初始状态一致,每个节点都执行相同的操作序列,那么他们最后能得到一个一致的状态。
- Paxos算法解决的就是保证每个节点执行相同的操作序列。好吧,这还不简单,master维护一个全局写队列,所有写操作都必须放入这个队列编号,那么无论我们写多少个节点,只要写操作是按编号来的,就能保证一致性。没错,就是这样,可是如果master挂了呢。
- Paxos算法通过投票来对写操作进行全局编号,同一时刻,只有一个写操作被批准,同时并发的写操作要去争取选票,只有获得过半数选票的写操作才会被批准(所以永远只会有一个写操作得到批准),其他的写操作竞争失败只好再发起一轮投票,就这样,在日复一日年复一年的投票中,所有写操作都被严格编号排序。编号严格递增,当一个节点接受了一个编号为100的写操作,之后又接受到编号为99的写操作(因为网络延迟等很多不可预见原因),它马上能意识到自己数据不一致了,自动停止对外服务并重启同步过程。任何一个节点挂掉都不会影响整个集群的数据一致性(总2n+1台,除非挂掉大于n台)。
161.集群中为什么要有主节点?
- 在分布式环境中,有些业务逻辑只需要集群中的某一台机器进行,其他机器可以共享这个结果,这样可以大大减少重复计算,提高性能,所以就需要主节点
163.说一下 zookeeper 的通知机制?
- 客户端注册监听它关心的目录节点,当节点发生变化时(数据增删改、子目录节点增删改)时,zookeeper就会通知客户端。通知机制是观察者(watch)的模式,异步回调的触发机制。zookeeper支持watch的概念,客户端可在每个znode节点上设置一个观察。如被观察服务端的znode节点有变更,那么watch就会被触发,这watch所属的客户端就会收到一个通知包,被告知节点已经发生变化,把对应的事件通知给设置过的Watcher的Client端。
- zookeeper所有读操作:getData(),getChildren()和exists()都有设置watch的选项,watch只会通知一次!
十七、MySql
164.数据库的三范式是什么?
165.一张自增表里面总共有 7 条数据,删除了最后 2 条数据,重启 mysql 数据库,又插入了一条数据,此时 id 是几?
- 一般情况下,我们创建的表的类型是InnoDB,如果新增一条记录(不重启mysql的情况下),这条记录的id是8;但是如果重启(文中提到的)MySQL的话,这条记录的ID是6。因为InnoDB表只把自增主键的最大ID记录到内存中,所以重启数据库或者对表OPTIMIZE操作,都会使最大ID丢失。
- 但是,如果我们使用表的类型是MylSAM,那么这条记录的ID就是8。因为MylSAM表会把自增主键的最大ID记录到数据文件里面,重启MYSQL后,自增主键的最大ID也不会丢失。
166.如何获取当前数据库版本?
- 在cmd里面输入 mysql -V 来获取mysql版本号如图
167.说一下 ACID 是什么?
- Atomicity 原子性:整个事务是一个独立的单元,要么操作成功,要么操作不成功
- Consistency 一致性:事务必须要保持和系统处于一致的状态(如果不一致会导致系统其它的方出现bug)
- Isolation 隔离性:事务是并发控制机制,他们的交错也需要一致性,隔离隐藏,一般通过悲观或者乐观锁实现
- Durability 耐久性:一个成功的事务将永久性地改变系统的状态,所以在它结束之前,所有导致状态的变化都记录在一个持久的事务日志中
168.char 和 varchar 的区别是什么?
- char类型的长度是固定的,varchar的长度是可变的。这就表示,存储字符串'abc',
- 使用char(10),表示存储的字符将占10个字节(包括7个空字符)
- 使用varchar2(10),,则表示只占3个字节,10是最大值,当存储的字符小于10时,按照实际的长度存储。
169.float 和 double 的区别是什么?
- 01.在内存中占有的字节数不同:单精度浮点数在机内存占4个字节;双精度浮点数在机内存占8个字节
- 02.有效数字位数不同:单精度浮点数有效数字8位;双精度浮点数有效数字16位
- 03.数值取值范围:单精度浮点数的表示范围:-3.40E+38~3.40E+38;双精度浮点数的表示范围:-1.79E+308~-1.79E+308
- 04.在程序中处理速度不同:一般来说,CPU处理单精度浮点数的速度比处理双精度浮点数快
170.mysql 的内连接、左连接、右连接有什么区别?
171.mysql 索引是怎么实现的?
- Mysql的索引使用B+树实现,B+树可以快速搜索到想要的数据。(哈希表、平衡二叉树、B树、B+树都是索引数据结构)
172.怎么验证 mysql 的索引是否满足需求?
- explain可以检查你的sql索引命中情况,实际上线后,最好配合监控来看下接口的RT
173.说一下数据库的事务隔离?
数据库事务的隔离级别有4种,由低到高分别为Read uncommitted 、Read committed 、Repeatable read 、Serializable
- Read uncommitted:一个事务可以读取另一个未提交事务的数据。会产生脏读
- Read committed :一个事务要等另一个事务提交后才能读取数据。读提交就是若有事务对数据进行更新(UPDATE)操作时,读操作事务要等待这个更新操作事务提交后才能读取数据,可以解决脏读问题。但在这个事例中,出现了一个事务范围内两个相同的查询却返回了不同数据,这就是不可重复读。
- Repeatable read:就是在开始读取数据(事务开启)时,不再允许修改操作。重复读可以解决不可重复读问题。写到这里,应该明白的一点就是,不可重复读对应的是修改,即UPDATE操作。但是可能还会有幻读问题。因为幻读问题对应的是插入INSERT操作,而不是UPDATE操作。
- Serializable :Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。
173.四种隔离级别可能导致的问题:
- Serializable (串行化):最严格的级别,事务串行执行,资源消耗最大;
- REPEATABLE READ(重复读) :保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但不能避免“幻读”,但是带来了更多的性能损失。
- READ COMMITTED (提交读):大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”,但不能避免“幻读”和“不可重复读取”。该级别适用于大多数系统。
- Read Uncommitted(未提交读) :事务中的修改,即使没有提交,其他事务也可以看得到,会导致“脏读”、“幻读”和“不可重复读取”。
174.说一下 mysql 常用的引擎?
- MyISAM:B+树结构,叶子结点存的数据的引用。支持全文索引;不支持事务;它是表级锁;会保存表的具体行数.
- InnoDB:B+树结构,叶子结点存的数据。5.6以后才有全文索引;支持事务;它是行级锁;不会保存表的具体行数.
175.说一下 mysql 的行锁和表锁?
- MyISAM 只支持表锁,InnoDB 支持表锁和行锁,默认为行锁。
- 表级锁:开销小,加锁快,不会出现死锁。锁定粒度大,发生锁冲突的概率最高,并发量最低。
- 行级锁:开销大,加锁慢,会出现死锁。锁力度小,发生锁冲突的概率小,并发度最高。
176.说一下乐观锁和悲观锁?
- 乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在提交更新的时候会判断一下在此期间别人有没有去更新这个数据。
- 悲观锁:每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻止,直到这个锁被释放。
- 数据库的乐观锁需要自己实现,在表里面添加一个 version 字段,每次修改成功值加 1,这样每次修改的时候先对比一下,自己拥有的 version 和数据库现在的 version 是否一致,如果不一致就不修改,这样就实现了乐观锁。
177.mysql 问题排查都有哪些手段?
- 使用 show processlist 命令查看当前所有连接信息。
- 使用 explain 命令查询 SQL 语句执行计划。
- 开启慢查询日志,查看慢查询的 SQL。
178.如何做 mysql 的性能优化?
- 为搜索字段创建索引。
- 避免使用 select *,列出需要查询的字段。
- 避免使用where +<> 和where +null
- 选择正确的存储引擎。
- 分库分表
- 读写分离
十八、Redis
179.redis 是什么?都有哪些使用场景?
180.redis 有哪些功能?
181.redis 和 memecache 有什么区别?
182.redis 为什么是单线程的?
183.什么是缓存穿透?怎么解决?
184.redis 支持的数据类型有哪些?
185.redis 支持的 java 客户端都有哪些?
186.jedis 和 redisson 有哪些区别?
187.怎么保证缓存和数据库数据的一致性?
188.redis 持久化有几种方式?
189.redis 怎么实现分布式锁?
190.redis 分布式锁有什么缺陷?
191.redis 如何做内存优化?
192.redis 淘汰策略有哪些?
193.redis 常见的性能问题有哪些?该如何解决?
十九、JVM
194.说一下 jvm 的主要组成部分?及其作用?
- 类加载器(Class Loader):加载类文件到内存。Class loader只管加载,只要符合文件结构就加载,至于能否运行,它不负责,那是有Exectution Engine 负责的。
- 执行引擎(Execution Engine):也叫解释器,负责解释命令,交由操作系统执行。
- 本地库接口(Native Interface):本地接口的作用是融合不同的语言为java所用
- 运行时数据区(Runtime Data Area)
195.说一下 jvm 运行时数据区?
- 堆:堆是java对象的存储区域,任何用new字段分配的java对象实例和数组,都被分配在堆上;jdk1.7以后,运行时常量池从方法区移到了堆上。
- 方法区:用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。
- 虚拟机栈:虚拟机栈中执行每个方法的时候,都会创建一个栈桢用于存储局部变量表,操作数栈,动态链接,方法出口等信息。
- 本地方法栈:与虚拟机发挥的作用相似,相比于虚拟机栈为Java方法服务,本地方法栈为虚拟机使用的Native方法服务,执行每个本地方法的时候,都会创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信息。
- 程序计数器:指示Java虚拟机下一条需要执行的字节码指令。
196.说一下堆栈的区别?
- 堆存放的是对象的实例和数组。因此该区更关注的是数据的存储
- 栈存放:局部变量,操作数栈,返回结果。该区更关注的是程序方法的执行。
197.java中类加载器的种类
- 启动类加载器(Bootstrap ClassLoader):C++实现,它是属于虚拟机自身的一部分,主要负责加载<JAVA_HOME>\lib目录
- 扩展类加载器(Extension ClassLoader):Java实现的,独立于虚拟机,主要负责加载<JAVA_HOME>\lib\ext目录中或被java.ext.dirs系统变量所指定的路径的类库。
- 应用程序类加载器(Application ClassLoader):Java实现的,独立于虚拟机。主要负责加载用户类路径(classPath)上的类库,如果我们没有实现自定义的类加载器那这玩意就是我们程序中的默认加载器。
198.什么是双亲委派模型?说一下类加载的执行过程?
- 双亲委派的意思是如果一个类加载器需要加载类,那么首先它会把这个类请求委派给父类加载器去完成,每一层都是如此。一直递归到顶层,当父加载器无法完成这个请求时,子类才会尝试去加载。这里的双亲其实就指的是父类,没有mother。父类也不是我们平日所说的那种继承关系,只是调用逻辑是这样。
200.怎么判断对象是否可以被回收?
- 引用计数器:实现简单、判定效率也挺高的。给对象头中添加一个引用计数器,当有一个地方引用该对象的时候,引用计数器加一;引用失效的时候减一。当引用计数器为零的时候,该对象便不可能在被使用。当下非常火热的Python就是使用的这个判断方式。
- 可达性分析:
201.java 中都有哪些引用类型?
- 强引用: java 默认的引用类型,如果不特意使用 java.lang.ref 下的类,那么程序中的所有引用都是强引用。有强引用存在的对象永远都不会被 gc 收集,所以在内存不够用时,JVM 宁愿抛出 OutOfMemoryError 这样的错误,也不愿意将强引用对象进行回收。
- 软引用:软引用不会保证对象一定不会被回收,只能最大可能保证。如果内存有剩余,那么软引用对象不会被回收,如果内存不足,那么 gc 会回收软引用对象。所以这种特性可以用来实现缓存技术。软引用要用 java.lang.ref.SoftReference 来实现。
- 弱引用:弱引用一定会被 gc 回收,不管内存是否不足。通过 java.lang.ref.WeakReference 来使用弱引用,WeakHashMap 同样也利用了弱引用。
- 虚引用:
202.说一下 jvm 有哪些垃圾回收算法?
- 标记-清除:直接将标记为垃圾的内存清除,优点是快,缺点是产生内存碎片
- 复制:将内存分为两块,每次将还存活移动到空闲的一块
- 标记-整理:标记后将存活对象移向内存的一端。然后清除端边界外的对象
- 分代收集:核心思想是根据对象存活的不同生命周期将内存划分为不同的域,新生代与复制算法、老年代与标记整理算法
203.说一下 jvm 有哪些垃圾回收器?
收集器 | 解释 | |
---|---|---|
新生代收集器 | Serial(英文连续)
|
|
ParNew(Parallel New) |
| |
Parallel Scavenge |
| |
老年代收集器 | Serial Old |
|
Parallel Old |
| |
最重要: CMS(Concurrent Mark Sweep)
|
| |
堆内存垃圾收集器 | G1(Garbage First ) |
|
![](https://img-blog.csdnimg.cn/20210210204902215.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3Nhbm1pODI3Ng==,size_16,color_FFFFFF,t_70)
![](https://img-blog.csdnimg.cn/20210210205058584.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3Nhbm1pODI3Ng==,size_16,color_FFFFFF,t_70)
![](https://img-blog.csdnimg.cn/20210210205514210.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3Nhbm1pODI3Ng==,size_16,color_FFFFFF,t_70)
![](https://img-blog.csdnimg.cn/20210210222816663.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3Nhbm1pODI3Ng==,size_16,color_FFFFFF,t_70)
204.详细介绍一下 CMS 垃圾回收器?
- 参考204题
205.新生代垃圾回收器和老生代垃圾回收器都有哪些?有什么区别?
- 参考204题
206.简述分代垃圾回收器是怎么工作的?
- 参考204题
207.说一下 jvm 调优的工具?
208.常用的 jvm 调优的参数都有哪些?