文章目录
- 前言
- Redis 5种 【基本数据类型】
- Java 4种【修饰权限】
- cookie / session / token
- Java【反射】
- MyBatis 的【# 与 $】
- StringBuilder 和 StringBuffer
- 【抽象类 和 接口】
- 【final】关键字
- JVM【内存模型】
- “源代码 → 可执行文件” 的过程
- 【JDK / JRE / JVM】
- 【Linux】常用命令
- Java【IO流】
- Java【多线程】开启方法
- Java 常见的【线程安全类】
- Java 常见的【加密】方法
- Java【泛型】
- 【static】关键字
- Java 中常用的【锁】
- Java 常用的【设计模式】
- 【HTTP 与 HTTPS】的区别
- 五层【网络协议】
- TCP【三次握手】
- TCP【四次挥手】
前言
本文将会涉及到Java面试中的高频考题,覆盖面非常广,但是并不会讲解的特别详细,推荐当作面试前的 复习提纲 使用。
文章会不断更新,也可能会另开新篇。
祝各位面试顺利,收获理想offer!
Redis 5种 【基本数据类型】
- String:字符串。
- Hash:哈希,由键和值组成的map,键 / 值 皆为 String 类型。
- List:列表,按照 输入的顺序 存储 String 类型数据。
- Set:集合,唯一、无序 的存储 String 类型数据。
- ZSet:有序集合,唯一、有序 的存储 String 类型数据。(Sorted Set)
Java 4种【修饰权限】
- private:类内
- protected:类内、子类、包内
- 默认(友好型):包内
- public:无限制
cookie / session / token
Cookie | Session | |
---|---|---|
数据大小 | 小文本数据,限制4KB | 多样数据类型,稍大 |
存储位置 | 客户端 | 服务器(压力大),并会在cookie中设置session ID |
安全性 | 低 | 较高 |
使用场景 | 每次HTTP请求,浏览器自动携带cookie给服务器,校验登录状态… | 通过唯一 session ID 识别用户 |
Token:“令牌技术”。
服务端生成 的一串 加密的字符串(jwt:header + payload + signature),客户端 在每次请求时发送给服务器做校验。
不依赖于cookie,因此可以用于 移动应用 或 跨域 请求 ❗
可以包含用户的身份信息和权限数据,但是 敏感数据 ❌ 不推荐写入token,token是可以被轻松破译的。
Java【反射】
- 获取Class对象:String.class / str.getClass()
- 创建对象:getConstructor() + newInstanse()
- 获取方法与变量:getMethod() + getField()
- 使用方法:method.invoke()
MyBatis 的【# 与 $】
#会 预编译,对于字符串参数会加上引号,防止SQL注入;
$只是作简单的 字符串替换,不会有预编译效果,所以可能会被注入恶意SQL,例如delete…
StringBuilder 和 StringBuffer
StringBuilder 线程不安全,但是效率高;
StringBuffer 线程安全,内置锁,但是效率低。
【抽象类 和 接口】
相同点:都不能直接使用,都是抽象概念。
【抽象类】强调所属关系,可以有构造方法,可以有方法实现;【接口】强调功能的抽象。
【抽象类】用 extends 继承;【接口】用 implements 实现。
【final】关键字
- 变量:只能被赋值一次
- 方法:不可被重写
- 类:不可继承
JVM【内存模型】
- 线程 共有:方法区 + 堆;
- 线程 私有:本地方法栈 + 虚拟机栈 + 程序计数器。
“源代码 → 可执行文件” 的过程
注意:Java编译器和Java解释器都是JVM的一部分。
【JDK / JRE / JVM】
- JDK:Java Development Kit,Java开发工具包,包含Java的开发工具 和 JRE;开发工具包括:编译工具(javac.exe)打包工具(jar.exe)等。
- JRE:Java Runtime Environment,Java运行环境,包括Java虚拟机 (JVM) 和 核心类库等;如果想要运行一个开发好的Java程序,计算机中只需要安装JRE即可。
- JVM:Java Virtual Machine,java虚拟机,它只认识.class为后缀的文件,它能将class文件中的 字节码指令 进行识别,并调用操作系统向上的API完成动作。JVM是java能够跨平台的核心机制。通俗的说就是跨平台用的,就是把我们写的代码,转换成class文件用的。
【Linux】常用命令
- ls:当前目录 内容;
- pwd:当前目录 路径;
- mkdir:创建目录;
- cd:打开目录;
- vim:创建或打开文件(使用vim);
- rm:删除文件或目录;
- mv:修改文件或目录名称;
- tar:解压缩包;
- ps:显示运行的进程;
- grep:在文件中搜索指定文本;
- (nohup) java -jar xxx.jar:启动Java项目;
- ps -ef | grep xxx:查看Java项目进程;
- kill:终止进程;
- shutdown:关机;
- sudo:以超级用户权限运行命令(sudo useradd 创建用户…)。
1 ~ 2:当前目录 的内容与位置;
3 ~ 7:目录与文件 增删改查;
9 ~ 13:Java项目 与 进程的查阅 / 终止。
此处并不全面,有更多需求可以查阅 👉 Linux常用命令60条
Java【IO流】
- 字节 流(InputStream / OutputStream):万能 的IO流,但是其内容无法查阅(是字节码);
- 字符 流(Reader / Writer):主要处理 文本,其内容是可查阅的。
Java【多线程】开启方法
- 继承 Thread 类;
- 实现 Runnable 接口;
- FutureTask 封装 Callable 接口实例…
Java是 【单继承、多实现】 的,所以通常使用Runnable较多,可以避免单继承的局限性。
Java 常见的【线程安全类】
- 单列集合:Vector
- 多列集合:HashTable、ConcurrentHashMap
- StringBuffer…
Java 常见的【加密】方法
- 单向加密算法(不可破解):MD5、SHA、HMAC…
- 非单项加密算法(可解密):BASE64、对称加密算法、非对称加密算法、数字签名算法、数字证书…
Java【泛型】
泛型的【本质】是 参数化类型,也就是说所操作的 数据类型 被指定为一个 参数。
如果没有泛型,程序会在 运行时 报错;而使用泛型后,我们在编译时就可以检查错误。
泛型可以避免因类型不同而重复书写类、接口和方法的情况,提高 代码复用。
常见的 泛型标识:
T :代表一般的任何类。
E :代表 Element 元素的意思,或者 Exception 异常的意思。
K :代表 Key 的意思。
V :代表 Value 的意思,通常与 K 一起配合使用。
S :代表 Subtype 的意思,文章后面部分会讲解示意。
1. 泛型类
最典型的泛型类就是Java的各种集合类,例如:ArrayList、HashMap…
// ArrayList
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {}
// 自定义泛型类
public class Generic<T> {
private T key;
public Generic(T key) {
this.key = key;
}
public T getKey(){
return key;
}
}
2. 泛型接口
Java类库中的常见接口也不在少数:List、Map、Set…
// Map
public interface Map<K,V> {}
// 自定义泛型接口
public interface Inter<T> {
public abstract void show(T t) ;
}
3. 泛型方法
public <类型参数> 返回类型 方法名(类型参数 变量名) {
...
}
public class Test<T> { // 泛型类
public void testMethod(T t) { // 泛型类中的【普通方法】
System.out.println(t);
}
public <T> T testMethod1(T t) { // ⭐ 泛型类中的【泛型方法】
return t;
}
}
4. 泛型通配符
通配符通常在 规定泛型的 上界、下界 时使用:
类型通配符:< ? >
有上界的类型通配符:< ? extends T > 【?不能高于 T类型】
有下界的类型通配符:< ? super T > 【?不能低于 T类型】
List<?> list1 = new ArrayList<Object>();
List<?> list2 = new ArrayList<Number>();
List<?> list3 = new ArrayList<Integer>();
// List<? extends Number> list4 = new ArrayList<Object>(); -> 报错
List<? extends Number> list5 = new ArrayList<Number>();
List<? extends Number> list6 = new ArrayList<Integer>();
List<? super Number> list7 = new ArrayList<Object>();
List<? super Number> list8 = new ArrayList<Number>();
// List<? super Number> list9 = new ArrayList<Integer>(); -> 报错
(推荐学习文章 👉 Java中的泛型)
【static】关键字
被static修饰的 成员变量和方法 并不属于某个对象,而是属于类,不需要创建对象就可以调用。
⭐ 在类的内部:
- 静态方法 只能使用 静态成员变量,只能调用 静态方法;
- 非静态方法 可以使用 所有成员变量,可以调用 所有方法;
- 静态成员变量 可以被 所有方法 使用。
- 非静态成员变量 只能被 非静态方法 使用。
其实很好记忆:
① 静态就是一直都有的,非静态就是原本没有的;
② 第一条举例:静态方法一直存在,为了不出错,他也只能使用一直存在的部分。
③ 第二条举例:非静态方法创建出来的时候,对象必然创建了,所有部分都已经可用了。
Java 中常用的【锁】
Synchronized | ReentrantLock | |
---|---|---|
所属位置 | Java内置锁 | util下的类 |
加锁方式 | 自动 | 手动 |
修饰范围 | 代码块、方法 | 代码块 |
响应中断 | 不可响应线程中断 | 可中断线程,解决死锁 |
实现原理 | JVM层面通过监视器 | 基于AQS |
ReentrantLock 开启锁的方式:(释放锁 unlock)
- lock():① 锁空闲:立刻获取;② 当前线程持有:持有数 +1;③ 其他线程持有:当前线程 一直休眠,直到获取锁。
- lockInterruptibly():逻辑同lock,但可 响应线程中断。
- tryLock():① 锁空闲:立刻获取,返回true;② 当前线程持有:持有数 +1,返回true;③ 其他线程持有:返回false。
- tryLock(long timeout, TimeUnit unit):逻辑同tryLock,但其他线程持有时会 等待一段时间。
不难看出,ReentrantLock 的 2、3、4 加锁方式都可以有效 避免死锁 问题。
死锁检测工具:jstack、jconsole…
Java 常用的【设计模式】
挑两个非常常见的介绍:单例模式、工厂模式。推荐学习 👉 Java中常用的设计模式
1. 单例模式
单例模式 是一种创建型设计模式,它确保一个类 只有一个实例,并 提供 一个 全局访问点 来访问该实例。
【优点】:
- 在内存里只有一个实例,减少 内存开销,尤其是 频繁的创建和销毁 实例;
- 避免对 资源的多重占用(比如写文件操作)。
【缺点】:
- 职责过重,违背 单一职责原则;
- 没有抽象层,不能拓展。
2. 工厂模式
工厂模式 属于创建型模式,它在 创建对象时 提供了一种 封装机制,将实际 创建对象的代码 与 使用代码 分离。
您需要一辆汽车,可以直接 从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。
【优点】:
- 大量的产品 需要创建,并且具有共同的接口时,可以通过工厂方法模式进行创建。
- 扩展性高,如果想增加一个产品,只要 扩展一个工厂类 就可以;
- 屏蔽产品的具体实现,调用者只关心 产品的接口;
【缺点】:
工厂类负责创建所有实例;如果有 新的类加入,需要 不断的修改 工厂类,不利于后期维护。
【HTTP 与 HTTPS】的区别
- 安全:HTTP 明文传输,安全性较差;HTTPS(SSL+HTTP) 数据传输过程是 加密的,安全性较好。
- 费用:使用 HTTPS 协议需要到 CA 申请证书,一般免费证书较少,因而需要一定费用。
- 响应速度:HTTP 页面 响应速度 比 HTTPS 快: HTTP 使用 TCP 三次握手建立连接,而 HTTPS除了 TCP 的三个包,还要加上 ssl 握手需要的 9 个包,所以一共是 12 个包。
- 连接方式:HTTP 和 HTTPS 连接方式 不同,用的 端口 也不一样,前者是 80,后者是 443。
- 资源消耗:HTTPS 是建构在 SSL/TLS 之上的 HTTP 协议,所以 HTTPS 更耗费 服务器资源。
推荐学习 👉 HTTP 与 HTTPS
五层【网络协议】
OSI 参考模型 通常只做理论层面的研究,实际应用都会采用 TCP/IP 五层协议 的体系结构。
在 TCP/IP 模型 中,每一层都有着特定的协议选择:
- 应用层:HTTP(超文本传输协议)、FTP(文件传输协议)、DNS(域名解析协议)…
- 传输层:TCP、UDP;
- 网络层:IP;
注意:应用层 和 传输层 是 【端对端】 传输,它们不需要知道底层是如何传输的,是一条 逻辑链路;而下面三层是 【点对点】 传输,严格的从一台设备传输到另一台设备。
TCP【三次握手】
- 客户端向服务端发送一个连接请求报文段SYN;【客户端发送】
- 服务端收到请求后,回复一个确认报文段SYN-ACK,表示接受连接请求;【服务端接收 + 发送】
- 客户端再次回复一个确认报文段ACK,表示确认服务器的接受。【客户端接收】
在通过三次握手之后,客户端 和 服务端 都互相了解一个事实:彼此都拥有良好的【接收与发送】能力! 所以连接建立,可以通信。
TCP【四次挥手】
- 客户端向服务端发送一个连接释放请求报文段FIN;
- 服务端收到请求后,回复一个确认报文段ACK,表示接受释放请求;
- 服务端再向客户端发送一个连接释放请求报文段FIN;
- 客户端收到请求后,回复一个确认报文段ACK,表示接受释放请求。
首先弄清楚SYN、FIN、ACK代表什么意思:
- SYN:我可以发送,我们连接好吗?
- FIN:我不再发送,但是仍可以接收。
- ACK:收到请求,发送同意回复。
所以,在【三次握手】时,“SYN-ACK” 一起发送并不影响逻辑;但是【四次挥手】的 “FIN 和 ACK” 不能一起发送,否则逻辑上形成 “我不再发送 + 我发送了同意回复” ❌;正确的应该是先ACK,再FIN,这就是为什么会多出一个步骤。