一、JavaSE部分
1. 面向对象3大特性5大原则
1.1 3大特性
- 继承
继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超类、基类);得到继承信息的类称为子类(派生类)。
java中一个类只能继承一个父类,且只能继承访问权限非private属性和方法。
目的: 代码复用 - 封装
通常认为封装就是把数据和操作数据的方法绑定起来,对数据的访问只能通过已经定义的接口。面向对象的本质就是把现实世界描绘成一系列完全自治、封闭的对象。
我们在类中编写的方法就是对实现细节的一种封装;我们编写一个类就是对数据操作的封装。
目的: 增强数据安全性,不能让其他用户随意访问和修改数据,简化编程,使用者不必在意具体实现细节,而只是通过外部接口即可访问类的成员 - 多态
多态性是指允许不同子类的对象对同一消息做出不同的响应,简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情。
多态分为编译时多态和运行时多态。
方法重载(overload)是指java允许方法名相同而参数不同(返回值可以相同也可以不同),同一个类中允许存在一个或多个以上的同名函数,只要参数类型或参数个数不同即可,实现的是编译时多态(也称为前绑定);
方法重写(override)必须是在继承体系中,子类重写父类方法,JVM运行时根据调用该方法的类型决定调用那个方法,实现的是运行时的多态性(也称为后绑定)。
目的: 增加代码的灵活度 - 抽象(可以不说)
抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两个方面。抽象只关注对象有哪些属性和行为,不关注这些行为的细节是什么。
1.2 5大原则
-
单一职责原则SRP(Single Responsibility Principle)
是指一个类的功能要单一,不能包罗万象。如同一个人一样,分配的工作不能太多,否则一天到晚虽然忙忙碌碌的,但效率却高不起来。 -
开放封闭原则OCP(Open-Close Principle)
一个模块在扩展性方面应该是开放的而在更改性方面应该是封闭的。比如:一个网络模块,原来只服务端功能,而现在要加入客户端功能,那么应当在不用修改服务端功能代码的前提下,就能够增加客户端功能的实现代码,这要求在设计之初,就应当将服务端和客户端分开,公共部分抽象出来。 -
里式替换原则LSP(the Liskov Substitution Principle LSP)
子类应当可以替换父类并出现在父类能够出现的任何地方。比如:公司搞年度晚会,所有员工可以参加抽奖,那么不管是老员工还是新员工,也不管是总部员工还是外派员工,都应当可以参加抽奖,否则这公司就不和谐了。 -
依赖倒置原则DIP(the Dependency Inversion Principle DIP)
具体依赖抽象,上层依赖下层。假设B是较A低的模块,但B需要使用到A的功能,这个时候,B不应当直接使用A中的具体类: 而应当由B定义一抽象接口,并由A来实现这个抽象接口,B只使用这个抽象接口:这样就达到了依赖倒置的目的,B也解除了对A的依赖,反过来是A依赖于B定义的抽象接口。通过上层模块难以避免依赖下层模块,假如B也直接依赖A的实现,那么就可能 造成循环依赖。一个常见的问题就是编译A模块时需要直接包含到B模块的cpp文件,而编译B时同样要直接包含到A的cpp文件。 -
接口分离原则ISP(the Interface Segregation Principle ISP)
模块间要通过抽象接口隔离开,而不是通过具体的类强耦合起来
2. 访问权限修饰符
3. 两个对象值相同(x.equals(y) == true),但可以有不同的hashCode,这句话对不对
不对,如果两对象 x 和 y 满足 x.equals(y) == true,他们的 hashCode 应该相同。Java 对于 equals 和 hashCode 方法规定如下:
1)如果两对象相同(equals()方法返回 true),那么它们的 hashCode 值要相同;
2)如果两个对象的 hashCode 值相同,它们并不一定相同。
当然,你可以不按照要求去做,但如果违背上述原则就会发现在使用容器时,相同的对象可以出现在 Set 集合中,同时增加新元素效率会大大下降。
4. String 类是否可以继承
String 类是 final 类,不可以被继承
5. 当一个对象被当作参数传递到一个方法后,此方法可以改变这个对象的属性,并且返回变化后的结果,那么这里是值传递还是引用传递
是值传递。Java编程语言只有值传递参数。
当一个对象实例作为一个参数被传递到方法中时,参数的值就是该对象的引用的一个副本。指向同一个对象,对象的内容可以在被调用的方法中改变,但对象的引用(不是引用的副本)是永远不会改变的。
6. 重载和重写的区别
方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,后者实现的是运行时的多态性。
重载发生在一个类中,同名的方法有不同的参数列表(参数类型不同、参数个数不同、或者两者都不同)就视为重载;
重写发生在子类与父类之间。
重写的规则:(两同一大一小)
- 参数列表必须完全和被重写的一样
- 返回类型必须和被重写的方法返回类型一样
- 构造方法不能被重写、声明为 final 的方法不能被重写、声明为 static 的方法不能被重写
- 重写访问权限不能比父类中被重写的方法的访问权限更低
- 重写的方法不能抛出比父类更大的异常
7. 为什么方法不可以根据返回类型来区分重载
因为调用时不能指定类型信息,编译器不知道你要调用哪个函数
比如:
float max (int a, int b) ;
int max (int a, int b);
当调用 max (1, 2) 时无法确定要调用哪一个方法,从这一点来说,仅返回值类型不同的重载是不应该允许的。
下面描述来源于这位老哥
在《深入理解Java虚拟机》中,6.3.6章节有这样一段:
Class文件中同方法名、同参数、不同返回值可以,那为什么Java文件中不行呢?
因为Java语言规范的规定,所以编译时会出现错误。
那为什么Class文件可以呢?因为Java虚拟机规范和Java语言规范不同,两者是分开的…
8. char 型变量中可不可以存储一个中文汉字,为什么
char 型可以存储一个汉字,因为 java 中使用的是 Unicode (不选择任何特定的编码,直接使用字符在字符集中的编号,这是统一的唯一方法),一个char 类型占 2 个字节 ,所以存一个中文没问题。
补充: 使用 Unicode 意味着字符在 JVM 内部和外部有不同的表现形式,在 JVM 内部都是 Unicode ,当这个字符被从 JVM 内部转移到外部时(例如存入文件系统),需要进行编码转换。所以 Java 中有字节流和字符流,以及在字符流和字节流之间转换的转换流,如 InputStreamReader 和 OutputStreamReader,这两个类是字节流和字符流之间的适配器类,承担了编码转换的任务。
9. 抽象类和接口有什么异同
参考:https://blog.youkuaiyun.com/gongxiao1993/article/details/82055007
-
抽象类和接口的对比
-
什么时候使用抽象类和接口
如果你拥有一些方法并且想让它们中的一些有默认实现,那么使用抽象类吧。
如果你想实现多重继承,那么你必须使用接口。
10. 抽象的方法是否可以同时是静态的(static),是否可以同时是本地方法,是否可以同时被 synchronized 修饰
都不可以。
抽象方法需要子类重写,静态方法无法被重写,二者矛盾;
本地方法是由本地代码(如C)实现的方法,抽象方法没有实现,二者矛盾;
synchronized 和方法的实现细节有关,抽象方法不涉及实现细节,因此也是矛盾的。
11. 静态变量和实例变量的区别
静态变量 :是被static 修饰的变量,也称为类变量,属于类,不属于任何类的一个对象,一个类不管创建多少个对象,静态变量在内存中仅有一个拷贝。可以实现多个对象共享内存。
实例变量: 必须依存于某一实例,需要先创建对象然后通过对象才能访问到它。
12. == 和 equals 的区别
equals 和 == 最大区别是一个是运算符一个是方法。
==: 如果比较的是基本数据类型,比较的是数值是否相等;如果比较的是引用数据类型,比较的是对象的地址值是否相等;
equals: 用来比较两个对象的内容是否相等。
注意:equals方法不可以用于基本数据类型的变量,如果没有重写equlas方法,那么比较的是引用类型的变量所指向的地址。
13. error 和 exception 的区别
Error 类和 Exception 的父类都是 Throwable 类,区别如下:
Error 类一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢出等。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和预防,遇到这样的错误,建议终止程序。
Exception 类表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行而不应该随意终止异常。
Exception 类又分为编译时异常和运行时异常,运行时异常比如:ArithmaticException, IlleagleArgumentException,编译时可以通过,但运行时就终止了,程序不会处理运行时异常,出现这类异常,程序会终止。而编译时异常,要么用 try…catch 捕获,要么用 throws 关键字抛出,交给它的父类处理,否则编译不通过。
14. 写出5个常见的运行时异常
- java.lang.NullPointerException 空指针异常:出现原因:调用了未经初始化的对象或者是不存在的对象。
- java.lang.ClassNotFoundException 指定的类找不到:类的名称和路径加载错误;通常都是程序试图通过字符串来加载某个类时可能引发异常
- java.lang.NumberFormatException 字符串转化为数字异常:出现原因:字符型数据中包含非数字型字符
- java.lang.IllegalArgumentException 方法参数传递错误
- java.lang.IndexOutOfBoundsException 数组角标越界异常,常见于操作数组对象时发生
- java.lang.ClassCastException 数据类型转换异常
- java.lang.NoClassDefinedException 未找到类定义异常
- SQLException SQL异常,常见于操作数据库时的SQL语句错误
- java.lang.InstantiationException 实例化异常
10.java.lang.NoSuchMethodException 方法不存在异常
15. throw 和 throws 的区别
throw:
- throw语句在方法体内,表示抛出异常,由方法体内的语句处理
- throw是具体向外抛出异常的动作,所以它抛出的是一个异常实例,执行 throw 一定是抛出了某种异常
throws: - throws 语句是用在方法声明后面,表示如果抛出异常,由该方法的调用者来进行异常处理
- throws 主要是声明这个方法会抛出某种类型的异常,让它的使用者要知道需要捕获的异常的类型
- throws 表示出现异常的一种可能性,并不一定会发生这种异常
16. final、finally、finalize的区别
1. final: 用于声明属性、方法、类,分别表示属性不可变,方法不可覆盖,被修饰的类不可继承
2. finally: 异常处理语句结构的一部分,表示总是执行
3. finallize: Object 类的一个方法,在垃圾回收器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收, 例如关闭文件等。该方法更像是一个对象生命周期的临终方法,当该方法被调用则代表该对象即将“死亡”,但是需要注意,我们主动的调用该方法并不会导致该对象死亡,这是一个被动的方法,不需要我门调用。
17. switch 是否能被作用在 byte上,是否能作用在long 上,是否能作用在 String 上
Java5 以前 switch(expr)中,expr 只能是 byte, short, char, int。但从5开始,Java 中引入了枚举类型,expr 也可以是枚举类型。从7开始,还可以是 String类型,但long类型目前还不可以。
18. String、StringBuilder、StringBuffer的区别
- String 是只读字符串,也就意味着String引用的字符串内容是不能改变的
- StringBuffer、StringBuilder 表示的字符串对象可以直接进行修改
- StringBuilder是Java5中引入的,它和StringBuffer的方法完全相同,区别在于它是在单线程环境下使用的,因为它的所有方法都没有被synchronized修饰,因此它的效率理论上比StringBuffer要高
19. 请说出下面程序的输出结果
20. Java 的基本数据类型有哪些
21. short s1 = 1;s1 = s1 + 1;有错吗?short s1 = 1;s1 += 1 有错吗?
前者不正确,后者正确。对于前者,s1 = s1 + 1,1 是int 类型,因此 s1 + 1
运算结果也是 int类型,需要强制转换类型才可以赋值给 short 类型。
22. 下面程序的输出结果为?
public void testInteger(){
Integer f1 = 100, f2 = 100, f3 = 150, f4 = 150;
System.out.println(f1 == f2);//true
System.out.println(f3 == f4);//false
}
简单的说,如果整型字面量的值在-128到127之间,那么不会new新的Integer对象,而是直接引用常量池中的Integer对象
23. String 类的常用方法
24. Java 中有几种类型的流
25. 字节流和字符流的区别
26. 什么是序列化,如何实现Java序列化
27. List 和 Map、Set的区别
https://www.cnblogs.com/jxxblogs/p/11561629.html
28. List a = new ArrayList() 和 ArrayList a = new ArrayList() 的区别
List a = new ArrayList() 这句话创建了一个ArrayList 对象后赋值给了 List,此时它是一个 List 对象了,有些ArrayList 独有的属性和方法就不可以再访问到了。而ArrayList a = new ArrayList() 创建的对象则保留了ArrayList 的所有属性。
29. Collection 和 Map 的继承体系
30. Map 中的 key 和 value 可以为 null 吗
HashMap 对象的 key、value都可以为 null
HashTable 对象的 key、value 值都不可以为 null
两者的 key 都不可以重复,若添加key 相同的键值对,后面的 value 会自动覆盖前面的 value,不会报错。
第二章 多线程
1. 在java 中 wait 和 sleep 方法有何不同
最大的不同就是在等待时,wait会释放锁,sleep会一直持有锁。wait通常被用于线程间交互,sleep通常被用于暂停执行。
2. synchronized 和 volatile 关键字的作用
参考1:https://www.jianshu.com/p/9a0a350cdf2e
参考2:https://www.cnblogs.com/gzhbk/p/14653177.html
1. synchronized : synchronized 则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。也就是说如果分别有两个线程A、B,这个时候在访问同一个变量value,这个时候使用该关键字。如果线程A 拿到锁之后,线程B 就不能获取,只能等待。
2. volatile : 保证了线程A、B对这个变量value 进行操作时的可见性,即A线程修改了某个变量value的值,这新值对线程B来说是 立即可见的。
两者的区别主要是在使用方式、原子性、可见性、线程阻塞、编译优化方面。表格如下
3. 什么是线程池,如何使用
参考:https://www.cnblogs.com/baxianhua/p/9300952.html
1. 什么是线程池: java.util.concurrent.Executors提供了一个 java.util.concurrent.Executor接口的实现用于创建线程池
多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。
假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间。
如果:T1 + T3 远大于 T2,则可以采用线程池,以提高服务器性能。
一个线程池包括以下四个基本组成部分:
- 线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
- 工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
- 任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
- 任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。
线程池技术正是关注如何缩短或调整T1,T3时间的技术,从而提高服务器程序性能的。它把T1,T3分别安排在服务器程序的启动和结束的时间段或者一些空闲的时间段,这样在服务器程序处理客户请求时,不会有T1,T3的开销了。
线程池不仅调整T1,T3产生的时间段,而且它还显著减少了创建线程的数目,看一个例子:
假设一个服务器一天要处理50000个请求,并且每个请求需要一个单独的线程完成。在线程池中,线程数一般是固定的,所以产生线程总数不会超过线程池中线程的数目,而如果服务器不利用线程池来处理这些请求则线程总数为50000。一般线程池大小是远小于50000。所以利用线程池的服务器程序不会为了创建50000而在处理请求时浪费时间,从而提高效率。
2.常见线程池
- newSingleThreadExecutor
单个线程的线程池,即线程池中每次只有一个线程工作,单线程串行执行任务 - newFixedThreadExecutor(n)
固定数量的线程池,没提交一个任务就是一个线程,直到达到线程池的最大数量,然后后面进入等待队列直到前面的任务完成才继续执行 - newCacheThreadExecutor(推荐使用)
可缓存线程池,当线程池大小超过了处理任务所需的线程,那么就会回收部分空闲(一般是60秒无执行)的线程,当有任务来时,又智能的添加新线程来执行。 - newScheduleThreadExecutor
大小无限制的线程池,支持定时和周期性的执行线程
4. 请叙述一下你对线程池的理解
(可以展开说一下线程池如何使用、线程池的好处、线程池的启动策略)
合理利用线程池可以带来三个好处:
- 降低资源消耗,通过重复利用已创建的线程降低线程创建和销毁造成的消耗
- 提高响应速度,当任务到达时,任务可以不需要等到线程创建就可以立即执行
- 提高线程的可管理性,线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
5. 线程池的启动策略
参考:https://www.cnblogs.com/anni-qianqian/p/8329723.html
6. 什么情况下导致线程死锁,遇到线程死锁怎么解决
1. 死锁的定义:
所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。
2. 死锁产生的必要条件:
- 互斥:
线程要求对锁分配的资源(如打印机)进行排他性控制,即在一段时间内某资源只能被一个线程占有。此时若有其他线程请求该资源,则请求的线程只能等待。 - 不可剥夺
线程获得的资源在未使用完毕之前,不能被其他线程夺走,只能由获得该资源的线程自己释放(只能是主动释放) - 请求和保持
线程以及保持了至少一个资源,但又提出了新的资源请求,而该资源已经被其他线程占有,此时请求进程被阻塞,但对自己已经获得的资源保持不放 - 循环等待
存在一种线程资源的循环等待链,链中的每个线程已经获得的资源同时被链中下一个线程所请求。即存在一个处于等待状态的线程集合(p1, p2, … pn),其中pi等待的资源被 p(i + 1) 占有 (i = 0, 1, …, n-1),pn等待的资源被p0占有
package exer;
//产生死锁小测试
public class DeadLockTest implements Runnable {
int flag = 1;
private static Object o1 = new Object(), o2 = new Object();
@Override
public void run() {
System.out.println("flag = " + flag);
if (flag == 1){
synchronized (o1){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2){
System.out.println("1");
}
}
}
if (flag == 2){
synchronized (o2){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1){
System.out.println("1");
}
}
}
}
public static void main(String[] args) {
DeadLockTest td1 = new DeadLockTest();
DeadLockTest td2 = new DeadLockTest();
td1.flag = 1;
td2.flag = 2;
new Thread(td1).start();
new Thread(td2).start();
}
}
7. 线程和进程的区别
进程: 具有一定独立功能的程序关于某个数据集合上的一次运行活动,是操作系统进行资源分配和调度的一个独立单位
线程: 是进程的一个实体,是cpu 调度和分派的基本单位,是比进程更小的可以独立运行的基本单位
特点: 线程的划分尺度小于进程,这使多线程程序拥有高并发性,进程在运行时各自内存单元相互独立,线程之间内存共享,这使得多线程编程可以拥有更好的性能和用户体验
注意: 多线程编程对其他程序是不友好的,占据大量 cpu 资源
8. 说出线程同步及线程调度相关的方法
9. 启动一个线程是调用 run() 方法 还是 start() 方法
第三章 反射
1. 说说你对 Java 中反射的理解
Java 中的反射首先是能够获取到Java 中要反射类的字节码,获取字节码,有3种方法:
package exer;
import org.junit.Test;
//反射获取类的3种方式
public class ClassTest {
@Test
public void test(){
Class<?> name = null;
try {
name = Class.forName("exer.ClassTest");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println("方式一:" + ClassTest.class);
System.out.println("方式二:" + this.getClass());
System.out.println("方式三:" + name);
}
}
2. 动静态代理的区别,什么时候使用
第四章 设计模式
1. 单例模式
package exer;
public class SingleTonTest{
public static void main(String[] args) {
// SingleTon singleTon = SingleTon.getInstance();
// SingleTon singleTon2 = SingleTon.getInstance();
// System.out.println(singleTon == singleTon2);
SingleTon2 singleTon = SingleTon2.getInstance();
SingleTon2 singleTon2 = SingleTon2.getInstance();
System.out.println(singleTon == singleTon2);
}
}
//单例模式 ---> 饿汉式
class SingleTon {
private SingleTon(){} //构造器私有化
private static SingleTon instance = new SingleTon();//创建对象
public static SingleTon getInstance(){//获取对象
return instance;
}
}
//单例模式 ---> 懒汉式
class SingleTon2 {
private SingleTon2(){}//构造器私有化
private static volatile SingleTon2 instance = null;
public static SingleTon2 getInstance(){
if (instance == null) {
synchronized (SingleTon2.class){
if (instance == null){
instance = new SingleTon2();
}
}
}
return instance;
}
}
2. 工厂模式
package exer;
/**
* 工厂方法模式分为3种:
* 普通工厂模式:就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建
* 多个工厂方法模式:是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,就不能
* 正确创建对象,而多个工厂方法模式就是提供多个工厂 方法,分别创建对象。
* 抽象工厂模式:创建多个工厂类,一旦需要新增功能,直接增加新的工厂类即可,不用修改之前的代码
*/
public class SendFactoryTest{
public static void main(String[] args) {
// System.out.println("普通工厂");
// SendFactory factory = new SendFactory();//创建工厂对象
// Send sms = factory.produce("sms");//调用方法产生对象
// sms.send();//掉用对象方法
// System.out.println("工厂方法");
// SendFactory factory = new SendFactory();//创建工厂对象
// Send email = factory.produceEmail();//对象掉方法
// email.send();
// System.out.println("抽象工厂");
// SmsSendFactory factory = new SmsSendFactory();
// Send send = factory.produce();
// send.send();
}
}
/*class SendFactory {//1.普通工厂模式
public Send produce(String type){//工厂方法
if ("sms".equals(type)){
return new SmsSend();
}else if ("email".equals(type)){
return new EmailSend();
}else{
System.out.println("请输入正确的参数");
return null;
}
}
}*/
class SendFactory {//工厂方法模式:提供多个方法,缺点:新增产品时需要修改工厂类,违背了开闭原则
public Send produceSms(){//方法一
return new SmsSend();
}
public Send produceEmail(){//方法二
return new EmailSend();
}
}
interface Send{//接口
void send();
}
class SmsSend implements Send{//接口实现类1
@Override
public void send() {
System.out.println("this is smsSend...");
}
}
class EmailSend implements Send{//接口实现类2
@Override
public void send() {
System.out.println("this is emailSend...");
}
}
//抽象工厂,下次增加新的产品时,只需2步,首先该类实现Send接口,然后新增工厂类实现Provider接口即可
interface Provider{
Send produce();
}
class SmsSendFactory implements Provider {//工厂1
@Override
public Send produce() {
return new SmsSend();
}
}
class EmailSendFactory implements Provider{//工厂2
@Override
public Send produce() {
return new EmailSend();
}
}
3. 建造者模式
package exer;
import java.util.ArrayList;
import java.util.List;
/**
* 建造者模式
* 工厂类模式提供的是单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,
* 所谓复合对象是指某个类具有不同的属性,其实建造者模式就是前面抽象工厂模式和最后的Test结合
*/
public class BuilderTest {
public static void main(String[] args) {
Builder builder = new Builder();
builder.emailSend(5);
}
}
class Builder{
private List<Send> list = new ArrayList<>();
public void emailSend(int count){
for (int i = 0; i < count; i++) {
list.add(new EmailSend());
}
}
public void smsSend(int count) {
for (int i = 0; i < count; i++) {
list.add(new SmsSend());
}
}
}
4. 适配器模式
适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。主要分为3类:类的适配器模式、对象的适配器模式、接口的适配器模式
package exer;
/**
* 适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。主要分为3类:类的适配器模式、对象的适配器模式、接口的适配器模式
*/
public class AdapterTest {
public static void main(String[] args) {
//类的适配器模式
Adapter adapter = new Adapter();
adapter.method1();
adapter.method2();
}
}
class Sourse{//父类
public void method1(){
System.out.println("this is original method");
}
}
interface Target{//接口
void method1();//和原类中相同名字的方法
void method2();//新方法
}
class Adapter extends Sourse implements Target{//适配器类
@Override
public void method2() {
System.out.println("this is new method2");
}
}
package exer;
/**
* 适配器模式
*/
public class WrapperTest {
public static void main(String[] args) {
Wrapper wrapper = new Wrapper(new Sourse());
wrapper.method1();
wrapper.method2();
}
}
//对象的适配器模式,此次不继承父类,而是将父类的实例作为属性,只实现接口
class Wrapper implements Target{
private Sourse sourse;
public Wrapper(Sourse sourse) {//构造器
this.sourse = sourse;
}
@Override
public void method1() {
sourse.method1();//通过属性去调用方法
}
@Override
public void method2() {
System.out.println("this is the target method2");
}
}
5. 装饰模式
装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例
package exer;
/**
* 装饰模式
*/
public class DeproTest {
public static void main(String[] args) {
Decorator decorator = new Decorator(new Soursse());//创建对象
decorator.method();//调用方法
}
}
interface Soursable{//原来的接口
void method();
}
class Soursse implements Soursable{//原来的类
@Override
public void method() {
System.out.println("this is the original method");
}
}
class Decorator implements Soursable{//新增装饰类,实现接口
private Soursse soursse;//原来对象做属性,装饰对象持有被装饰对象的实例
public Decorator(Soursse soursse) {//构造器初始化属性
this.soursse = soursse;
}
@Override
public void method() {
System.out.println("before decorator");//新增的信息
soursse.method();//原来的方法
System.out.println("before decorator");
}
}
第五章 JVM
1. JVM 垃圾回收机制和常见算法
理论上来讲 Sun 公司只定义了垃圾回收机制规则,而不局限于其实现算法,因此不同厂商生产的虚拟机采用的算法也不经相同。
GC(Garbage Collector) 在回收对象前首先必须发现那些无用的对象,如何发现呢?常用搜索算法如下:
通过上面的搜索算法找到垃圾对象后,接下来就是回收过程,回收算法如下:
- 标记-清除(Mark-Sweep)算法
标记清除算法包括两个阶段:标记和清除。在标记阶段,确定所有要回收的对象并做标记。清除阶段紧随标记阶段,将标记阶段确定不可用的对象清除。标记清楚算法是基础的垃圾收集算法,标记和清除阶段的效率不高,而且清除后产生大量的不连续空间,这样当程序需要分配大内存对象时,可能无法找到足够的连续空间 - 复制算法
复制算法是把内存分成大小相等的两块,每次使用其中一块,当垃圾回收的时候,把存活的对象复制到另一块上,然后把这块内存整个清理掉。复制算法实现简单,运行效率高,但是由于每次只能使用其中的一半,造成内存的利用率不高。现在的 JVM 用复制方法收集新生代,由于新生代中大部分对象(98%)都是朝生夕死的,所以两块内存的比例不是1:1 - 标记整理算法
标记整理算法和标记清除算法一样,但是标记整理算法不是把存活对象复制到另一块内存,而是把存活对象往内存的一端移动,然后直接回收边界以外的内存。标记整理算法提高了内存的利用率,并且他适合在收集对象存活时间较长的老年代。
2. 谈谈JVM 的内存结构和内存分配
3. Java 中引用类型有哪些
4. heap 和 stack 有什么区别
参考:https://blog.youkuaiyun.com/u011277123/article/details/90765024
5. Java 的类加载器有哪些
参考:https://blog.youkuaiyun.com/weixin_39609620/article/details/114620232
首先为大家简述什么是类加载器。从它的名称我们就可以看出来,类加载器就是用来加载Class文件到JVM,以供程序使用的。java的类加载,都是通过类加载器完成的。
然后说一下怎么创建类加载器。当java运行class文件时,java运行程序会尝试找到JRE安装的所在目录,然后寻找jvm.dll(默认安装在bin\client目录中),紧接着启动JVM并进行初始化的工作,之后产生Bootstrap Loader,Bootstrap Loader会加载ExtendedLoader和AppClass Loader。Bootstrap Loader是Extended Loader的parent Loader。Extended Loader是AppClassLoader的parent Loader。
再为大家介绍一下java内置的ClassLoader :
第一种是Bootstrap Loader(引导类加载器)。它的实现依赖于底层操作系统,由C编写而成,没有继承于ClassLoader类。根类加载器从系统属性sun.boot.class.path所指定的目录中加载类库。默认为jre目录下的lib目录下的class文件,该加载器没有父加载器。负责加载虚拟机的核心类库,如java.lang.*。Object类就是由根类加载器加载的。
第二种是Extended Loader(标准扩展类加载器)。它的父加载器为根类加载器。由java编写而成,是ClassLoader的子类。它从java.ext.dirs中加载类库,或者从JDK安装目录jre\lib\ext子目录下加载类库。如果把用户创建的jar文件放在该目录下,也会自动由扩展类加载器加载。
第三种是AppClass Loader(应用程序类路径类加载器)。它的父加载器为扩展类加载器。由java编写而成,是ClassLoader的子类,它从环境变量classpath或者系统属性java.class.path所指定的目录中加载类,是用户自定义的类加载器的默认父加载器。
接下来让我们了解一下类加载器工作原理。每个类加载器会先将加载类的任务交给其parent,如果parent找不到,再由自己负责加载。
所以在加载类时,会以Bootstrap Loader→Extended Loader→AppClass Loader的顺序来寻找类,如果找不到,就会丢出NoClassDefFoundError。
我们通过一张图片来展示类加载器树状组织结构:
6.类什么时候被初始化
- 创建类的实例,也就是New 一个对象
- 访问某个类或接口的静态变量,或者对该静态变量赋值
- 调用类的静态方法
- 反射(Class.forName(“com.zjp.Test”))
- 初始化一个类的子类(首先会初始化子类的父类)
- JVM 启动时标明的启动类,即文件名和类名相同的那个类
上面6种情况会导致类的初始化。
类的初始化步骤为:
1)若该类还没有被加载和链接,那先进行加载和链接
2)如该类存在直接父类,且这个类还没有被初始化(在一个类加载器中,类只能被初始化一次),那么初始化直接父类(不适合接口)
3)若类中存在初始化语句(如static变量和 static 块),那就依次执行这些初始化语句。
7. 获得一个类的对象有哪些方式
- 类型.class,例如:String.class
- 对象.getClass(),例如:“hello”.getClass()
- Class.forName(),例如:Class.forName(“java.lang.String”)
8. 既然有GC机制,为什么还会有内存泄漏的情况
转载:https://www.cnblogs.com/panxuejun/p/5888817.html
-
既然 Java 的垃圾回收机制能够自动的回收内存,怎么还会出现内存泄漏的情况呢?这个问题,我们需要知道 GC 在什么时候回收内存对象,什么样的内存对象会被 GC 认为是“不再使用”的。
Java中对内存对象的访问,使用的是引用的方式。在 Java 代码中我们维护一个内存对象的引用变量,通过这个引用变量的值,我们可以访问到对应的内存地址中的内存对象空间。在 Java 程序中,这个引用变量本身既可以存放堆内存中,又可以放在代码栈的内存中(与基本数据类型相同)。 GC 线程会从代码栈中的引用变量开始跟踪,从而判定哪些内存是正在使用的。如果 GC 线程通过这种方式,无法跟踪到某一块堆内存,那么 GC 就认为这块内存将不再使用了(因为代码中已经无法访问这块内存了)。
通过这种有向图的内存管理方式,当一个内存对象失去了所有的引用之后,GC 就可以将其回收。反过来说,如果这个对象还存在引用,那么它将不会被 GC 回收,哪怕是 Java 虚拟机抛出 OutOfMemoryError 。 -
java内存泄漏的根本原因是?
答:内存对象明明已经不需要的时候,还仍然保留着这块内存和它的访问方式(引用)。
-
内存泄漏在哪个领域比较常见?
答:在移动设备对于内存和 CPU都有较严格的限制的情况下, Java 的内存溢出会导致程序效率低下、占用大量不需要的内存等问题。这将导致整个机器性能变差,
严重的也会引起抛出 OutOfMemoryError ,导致程序崩溃。 -
如何避免内存泄漏?
答:明确引用变量的生命周期,是方法内部的局部变量,还是类实例变量,与类实例生命周期相同的要声明为实例变量。
要避免这种情况下的内存泄露,要求我们以C/C++ 的内存管理思维来管理自己分配的内存。第一,是在声明对象引用之前,明确内存对象的有效作用域。在一个函数内有效的内存对象,应该声明为 local 变量,与类实例生命周期相同的要声明为实例变量……以此类推。第二,在内存对象不再需要时,记得手动将其引用置空。
9. Java 中为什么会有GC 机制
10. 对于Java 的 GC 哪些内存需要回收
内存运行时的 JVM 会有一个运行时数据区来管理内存。它包括5大部分:程序计数器、虚拟机栈、本地方法栈、方法区、堆。
其中程序计数器、虚拟机栈、本地方法栈是每个线程私有的内存空间,随线程而生,随线程而亡。例如每个栈帧中分配多少内存基本上在类结构确定是哪个时就已经知道了,因此这三个区域的内存分配和回收都是确定的,无需考虑内存回收问题。
但方法区和堆就不同了,一个接口的多个实现类需要的内存可能不一样,我们只有在程序运行期间才会知道创建了哪些对象,这部风内存的分配和回收都是动态的,GC 主要关注的时这部分内存。
总而言之,GC 主要进行回收的内存是JVM 中的方法区和堆
11. Java 中的GC 什么时候回收垃圾
无论是引用计数器还是可达性分析,判定对象是否存活都与引用有关,那么,如何定义对象的引用勒?
我们希望给出这样一类描述:当内存空间还够时,能够保存在内存中;如果进行了垃圾回收后内存空间仍旧非常紧张,就可以抛弃这些对象。所以根据不同的需求,给出如下4种引用,根据引用类型的不同,GC 回收时也会有不同的操作。
12. 开发中遇到的内存溢出有哪些原因,解决方法
-
常见内存溢出的原因有
-
解决方案:
第六章 Java Web
1. 说一下原生JDBC 操作数据库的流程
- Class.forName()加载数据库连接驱动
- DriverManager.getConnection() 获取数据连接对象
- 根据sql 获取 sql 会话对象,有两种方式 Statement、PreparedStatement
- 执行SQL 处理结果集
- 关闭结果集、关闭会话、关闭连接
import java.sql.*;
public class JdbcTest {
public static void main(String[] args) {
String driver = "com.mysql.cj.jdbc.Driver";//数据库驱动类名的字符串
String url = "jdbc:mysql://localhost:3306/mybatisplus?serverTimezone=GMT%2B8";//数据库连接串
String username = "root";
String password = "root";
Connection conn = null;
Statement stmt = null;
ResultSet res = null;
try {
Class.forName(driver);//1.加载数据库驱动,成功后会将driver类的实例注册到DriverManager类
conn = DriverManager.getConnection(url, username, password);//2.获取数据库链接
stmt = conn.createStatement();//3.获取数据库操作对象
String sql = "select * from user";//4.sql语句
res = stmt.executeQuery(sql);
while (res.next()){//输出结果
System.out.println(res.getString("name") + " " + res.getInt("age"));
}
}catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {//5.关闭链接
if (res != null){
try {
res.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
2. 为什么使用 PreparedStatement?
转载:https://blog.youkuaiyun.com/qq_38080370/article/details/80671145
3. 关系数据库中连接池的机制是什么
4. http 常见的状态码有哪些
- 200 OK //客户端请求成功
- 301 Moved Permanently(永久移除),请求的URL 已经移走。Response 中应该包含一个 Location URL,说明资源现在所处的位置
- 302 found 重定向
- 400 Bad Request //客户端请求有语法错误,不能被服务器所理解
- 401 Unauthorized //请求未经授权,这个状态码必须和 WWW-Authenticate 报头域一起使用
- 403 Forbidden //服务器收到请求,但是拒绝提供服务
- 404 Not Found //请求资源不存在,输入了错误的URL
- 500 Internal Server Error //服务器发生不可预期的错误
- 503 Server Unavailable //服务器当前不能处理客户端的请求,一段时间后可能恢复正常
5. Get 和 Post 的区别
6. http 中请求转发和重定向的区别
7. cookie 和 session 的区别
Cookie 是 web服务器发送给浏览器的一块信息,浏览器会在本地一个文件中给每个web 服务器存储cookie 。以后浏览器再给特定的 web 服务器发送请求时,同时会发送所有为该服务器存储的 cookie。
Session 是存储在web 服务器端的一块信息。session 对象存储特定用户会话所需的属性及配置信息。当用户在应用程序的web 页之间跳转时,存储在 Session 对象中的变量不会丢失,而是在整个用户会话中一值存在下去。
不同点:
1、无论客户端做怎样的设置,session 都可以正常工作。当客户端禁用 cookie 时将无法使用cookie。
2、在存储的数据量方面:session 能够存储任意的java 对象,cookie 只能存储String 类型的对象。
8. 在单点登录中,如果cookie 被禁用了怎么办
单点登录的原理是后端生成一个 session ID,然后设置到cookie,后面的所有请求浏览器都会带上cookie,然后服务端从 cookie 里获取 session ID,再查询到用户信息。所以,保持登录的关键不是cookie,而是通过cookie 保存和传输的 session ID,其本质是能获取用户信息的数据 。除了 cookie,还通常使用HTTP 请求头来传输,但是这个请求头浏览器不会像cookie 一样自动携带,需要手工处理。
9. 什么是 jsp,什么是 Servlet,他们俩有什么区别
10. jsp 有哪些域对象和内置对象以及他们的作用
转载:https://www.cnblogs.com/daiwenxiang/p/12291181.html
4大域对象:
(1)pageContext:page 域指当前页面,在当前jsp 页面有效,跳转到其他页面失效。
(2)request:request 域指依次请求范围内有效,从http 请求到服务器处理结束,返回响应的整个过程。在这个过程中使用 forward(请求转发)方式跳转多个jsp,在这些页面里你都可以使用这个变量
(3)session:session 域指当前会话范围有效,浏览器从打开到关闭过程中,转发、重定向均可以使用
(4)application:context 域指只能在同一个web 中使用,服务器未关闭或者重启,数据就有效
9大内置对象:
1、request对象
request 对象是 javax.servlet.httpServletRequest类型的对象。 该对象代表了客户端的请求信息,主要用于接受通过HTTP协议传送到服务器的数据。(包括头信息、系统信息、请求方式以及请求参数等)。request对象的作用域为一次请求。
2、response对象
response 代表的是对客户端的响应,主要是将JSP容器处理过的对象传回到客户端。response对象也具有作用域,它只在JSP页面内有效。
3、session对象
session 对象是由服务器自动创建的与用户请求相关的对象。服务器为每个用户都生成一个session对象,用于保存该用户的信息,跟踪用户的操作状态。session对象内部使用Map类来保存数据,因此保存数据的格式为 “Key/value”。 session对象的value可以使复杂的对象类型,而不仅仅局限于字符串类型。
4、application对象
application 对象可将信息保存在服务器中,直到服务器关闭,否则application对象中保存的信息会在整个应用中都有效。与session对象相比,application对象生命周期更长,类似于系统的“全局变量”。
5、out 对象
out 对象用于在Web浏览器内输出信息,并且管理应用服务器上的输出缓冲区。在使用 out 对象输出数据时,可以对数据缓冲区进行操作,及时清除缓冲区中的残余数据,为其他的输出让出缓冲空间。待数据输出完毕后,要及时关闭输出流。
6、pageContext 对象
pageContext 对象的作用是取得任何范围的参数,通过它可以获取 JSP页面的out、request、reponse、session、application 等对象。pageContext对象的创建和初始化都是由容器来完成的,在JSP页面中可以直接使用 pageContext对象。
7、config 对象
config 对象的主要作用是取得服务器的配置信息。通过 pageConext对象的 getServletConfig() 方法可以获取一个config对象。当一个Servlet 初始化时,容器把某些信息通过 config对象传递给这个 Servlet。 开发者可以在web.xml 文件中为应用程序环境中的Servlet程序和JSP页面提供初始化参数。
8、page 对象
page 对象代表JSP本身,只有在JSP页面内才是合法的。 page隐含对象本质上包含当前 Servlet接口引用的变量,类似于Java编程中的 this 指针。
9、exception 对象
11. 什么是 xml,使用xml 的优缺点,xml的解析器有哪几种,有什么区别
xml 是一种可扩展性标记语言,支持自定义标签,使用DTD 和XML Schema 标准化 XML 结构。
优点:用于配置文件,格式统一,符合标准;用于在互不兼容的系统间交互数据,共享数据方便;
缺点:xml 文件格式复杂,数据传输站流量,服务端和客户端解析xml 文件占用大量资源且不容易维护。
xml 常用解析器有2 种,分别是 :DOM 和 SAX,主要区别在于它们解析xml 文档的方式不同。使用 DOM 解析xml文档 以DOM 树形结构加载进内存,而SAX 采用的是事件模型。
参考:https://wenku.baidu.com/view/fc3fb5610b1c59eef8c7b410.html
12. 谈谈你对 Ajax 的认识
参考:https://www.jb51.net/article/93258.htm
-
优点:
1、最大的优点是页面无刷新,用户的体验非常好。
2、使用异步方式与服务器通信,具有更加迅速的响应能力。
3、可以把以前一些服务器负担的工作转嫁到客户端,利用客户端闲置的能力来处理,减轻服务器和带宽的负担,节约空间和宽带租用成本。并且减轻服务器的负担,ajax的原则是“按需取数据”,可以最大程度的减少冗余请求,和响应对服务器造成的负担。
4、基于标准化的并被广泛支持的技术,不需要下载插件或者小程序。 -
缺点:
1、ajax不支持浏览器back按钮。
2、安全问题 AJAX暴露了与服务器交互的细节。
3、对搜索引擎的支持比较弱。
4、破坏了程序的异常机制。
5、不容易调试。 -
主要包含的技术
-
XMLhttprequest对象
Ajax的核心是JavaScript对象XmlHttpRequest。该对象在Internet Explorer 5中首次引入,它是一种支持异步请求的技术。简而言之,XmlHttpRequest使您可以使用JavaScript向服务器提出请求并处理响应,而不阻塞用户。通过XMLHttpRequest对象,Web开发人员可以在页面加载以后进行页面的局部更新。 -
AJAX应用和传统Web应用的不同
在传统的Javascript编程中,如果想得到服务器端数据库或文件上的信息,或者发送客户端信息到服务器,需要建立一个HTML form然后GET或者POST数据到服务器端。用户需要点击”Submit”按钮来发送或者接受数据信息,然后等待服务器响应请求,页面重新加载。
因为服务器每次都会返回一个新的页面, 所以传统的web应用有可能很慢而且用户交互不友好。
使用AJAX技术, 就可以使Javascript通过XMLHttpRequest对象直接与服务器进行交互。
通过HTTP Request, 一个web页面可以发送一个请求到web服务器并且接受web服务器返回的信息(不用重新加载页面),展示给用户的还是通一个页面,用户感觉页面刷新,也看不到到Javascript后台进行的发送请求和接受响应。
13. 说说常见的 Linux 命令
- ls [-a -l] 列出文件列表
- mkdir rmdir 创建和删除目录
- tail 显示文件后几行的内容
- tar -xvf:打包
- tar -zcvf:打包并压缩
- grep:查找字符串
- pwd:显示当前所在目录
- touch:创建空文件
- vim vi:编辑器
更多内容查看:https://blog.youkuaiyun.com/qq_23329167/article/details/83856430/
14. 解释一下什么是servlet,说一下servlet的生命周期
15. 过滤器有哪些作用和用法
第七章 数据库
常见sql查询:https://www.cnblogs.com/qixuejia/p/3637735.html
1. sql select 语句的完整执行顺序
2. sql注入问题
3. Mysql 性能优化
4. Mysql 数据库架构图
5. Mysql 架构中各模块的作用
6. Mysql 存储引擎有哪些
7. 事务的4大特性
8. Mysql 4种隔离级别
9. Mysql 中文乱码问题完美解决
解决乱码的核心思想是统一编码。我们在使用Mysql 建数据库和建表时应该尽量使用统一的编码,强烈建议使用 utf8 编码,因为该编码几乎可以兼容世界上所有的字符。
数据库在安装的时候可以设置默认编码,在安装时就一定要设置为utf8编码。设置后再创建的 数据库和表默认就使用utf8,省去了很多麻烦。
10. 如何提高mysql 的安全性
11. 什么是存储过程?使用存储过程的好处?
更多存储过程内容参见:https://www.cnblogs.com/yank/p/4235609.html
12. 在千万级的数据库查询中如何提高效率
第八章 SpringMVC
1.SpringMVC的工作原理
详细过程参考:https://www.cnblogs.com/fengquan-blog/p/11161084.html#top
2. SpringMVC 常用注解
https://www.cnblogs.com/caijh/p/7744604.html
3. 解决get 和post 乱码问题
解决post 乱码问题:可以在web.xml 里面配置一个 CharacterEncodingFilter 过滤器,设置为 utf-8
解决 get 乱码问题:有两种方法。
- 修改tomcat 配置文件添加编码与工程编码一致
- 对参数进行重新编码:String userName = new String( Request.getParameter(“userName”).getBytes(“ISO8859-1”), “utf-8”)。
第九章 Spring
1. 谈谈你对Spring 的理解
spring 是一个开源框架,为简化企业级应用开发而生。Spring 可以是简单的 JavaBean 实现以前只有 EJB 才能实现的功能。Spring 是一个 IOC 和 AOP 容器框架。
spring 容器的核心是:
控制反转 IOC: 传统的java 开发模式中,当需要一个对象的时候,我们会自己使用 new 或者 getInstance 等直接或间接调用构造方法创建一个对象。而在 Spring 开发模式中,spring 容器使用了工厂模式为我们创建了所需要的对象,不需要我们自己创建了,直接调用Spring 提供的对象就行,这是控制反转的思想。
依赖注入 ID: spring 使用 javaBean 对象的set 方法或者带参构造方法为我们在创建所需对象时将其属性自动设置所需值,这就是依赖注入的思想。
面向切面编程 AOP: 在面向对象 OOP 思想中,我们将事物纵向抽象成一个一个的对象。而在面向切面编程中,我们讲一个个的对象某些类似的方面横向抽象成一个切面,对这个切面进行一些如权限控制、事物管理,记录日志等公用操作处理的过程就是面向切面编程的思想。AOP 底层是动态代理,如果是接口采用 JDK 动态代理,如果是类采用 CGLIB 动态代理。
2. Spring 中的设计模式
3. Spring 中的常用注解
4. 简单介绍一下spring bean 的生命周期 
http://www.yq1012.com/myweb/4485.html
5. Spring 结构图
6. Spring 配置文件有什么作用
Spring 配置文件是一个 xml 文件,这个文件包含类信息,描述了如何配置它们,以及如何互相调用。
7. 什么是Spring IOC 容器?它的优点是什么?
IOC:Spring IOC 负责创建对象,管理对象。通过依赖注入,装配对象,配置对象,并且管理这些对象的整个生命周期。
优点:IOC 把应用代码量降到最低。它使应用容易测试,单元测试不再需要单例和JNDI 查找机制。最小的代价和最小的侵入性使松散耦合得以实现。IOC容器支持加载服务时的饿汉式初始化和懒汉式加载。
8. ApplicationContext 的实现类有哪些
9. BeanFactory 和 ApplicationContext 的区别
10. 什么是 Spring 的依赖注入
11. 依赖注入的方式有哪些
- Set 注入
- 构造器注入
- 静态工厂的方法注入
- 实例工厂的方法注入
12. 什么是织入
织入是将切面和其他应用类型或对象连接或创建一个被通知对象的过程。织入可以在编译时,加载时,或运行时完成。
13. 什么是 Spring beans?定义Spring beans 需要包含什么
14. 怎样定义类的作用域
15. Spring 支持的几种 bean的作用域
16. 什么是 Spring 的内部bean
17. 什么是 bean 的自动装配
18. 解释不同的方式的自动装配
19. 什么是基于 Java 的 Spring 注解配置?
20. 什么是基于注解的容器配置?怎样开启注解装配?
21. 简单解释一下 Spring 的AOP
22. 在Spring AOP 中,关注点和横切关注点有什么区别
23. 什么是连接点
24. Spring 有哪些通知类型
25. 什么是切点
切入点是一个或一组连接点,通知将在这些位置执行。可以通过表达式或匹配的方式指明切入点
26. 什么是目标对象
被一个或多个切面所通知的对象。它通常是一个代理对象。也指被通知对象
27. 什么是代理
代理是通知目标对象后创建的对象。从客户端的角度看,代理对象和目标对象是一样的。
第十章 MyBatis
1. MyBatis 中 # 和 $ 的区别
2. Mybatis 编程的步骤
3. JDBC 编程有哪些不足?Mybatis 是如何解决的
4. 使用Mybatis 的mapper 接口调用时有哪些要求
5. Mybatis 中的一级缓存和二级缓存
第十一章 Redis
1. redis 的特点
2. 为什么 redis 需要把所有数据放到内存中
3. redis 常见性能问题,解决方式
4. redis 适用场景
5. redis 有哪几种数据结构
第十二章 Nginx
1. Nginx 反向代理为什么可以提升服务器性能
第十三章
1. 谈谈你对Restful风格的理解
更多内容参考:https://blog.youkuaiyun.com/liuwenbiao1203/article/details/52351129