Java重学
1.面向对象思想
面向对象–组织者
面向过程–执行者
三特性
封装-
继承-为复用,抽取公用东西作为父类,子类只需扩展特殊特性
多态-父类引用指向子类对象。首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
2.基本数据类型
boolean,char,byte,short,int,long,float,double
注意:String是引用类型
3.== 和 equals的区别
==
比较基本类型时,比较的是值
比较引用类型时,比较的是引用指向的值(地址)
equals
默认比较地址
对应自定义类,若需要比较值,则重写equals
4.final
修饰类,则类不可继承
修饰方法,则方法不可重写
修饰变量,则变量成为常量
注意:
修饰基本数据类型,则值本身不可修改。
修饰引用类型,则引用的指向不可修改。
1 | final Student student = new Student(1, "Name"); |
5.String、StringBuffer、StringBuilder
String不可变,因是final类型,每次操作产生新的String对象,然后将指针指向新对象。StringBuffer、StringBuilder可变,操作均在原对象上进行。
StringBuffer线程安全;StringBuilder线程不安全,性能高,实际开发中优先选择StringBuilder。
StringBuffer中所有方法都默认加了synchronized修饰,因此线程安全。
开发中使用StringBuilder解决什么问题?
一般用于字符串拼接,开发中每个线程访问一个StringBuilder,可使用。(HashMap同理)
1 | StringBuilder s = new StringBuilder(): |
6.接口和抽象类
分JDK讨论
JDK1.8之前:
语法上
抽象类:有构造器,方法可有抽象的,也可有非抽象的
接口:方法都是抽象的,属性都是常量,默认public static final修饰
设计上
抽象类:同一类事物的抽取
接口:标准制定,定制对接标准
JDK1.8之后:
接口里可有实现的方法,通过在方法声明上加default或者static(一般为空实现)
7.转型
1 | 向上转型 |
8.缓存、装箱和拆箱
Integer和int的比较
比较的是数值,Integer自动拆箱,和其他无关。
- Integer和Integer的比较
- 使用new,就是开辟一块新内存,是新对象
- 不使用new,通过赋值,需要看范围。Integer做了缓存,-128至127范围内的数采用缓存的对象;不在范围内的,内部创建新对象。
9.重载、重写
重载:发生在一个类中,方法名相同,参数列表不同。(和返回类型无关)
重写:发生在父子类之间,方法名相同,参数列表相同。
10.ArrayList和LinkedList
底层数据结构差异
ArrayList:数组实现,连续内存
LinkedList:双向链表,不连续内存
基础结论
ArrayList,查找快,插入、删除慢
LinkedList,查找慢,插入、删除快
ArrayList实现
1.增加
正常直接添加到末尾,数组空间不足,需要扩容。数组初始化容量为10,建议直接设置初始化大小。
1 | // 扩容:容量不足进行扩容,按原有容量1.5倍进行扩容。 |
- LinkedList实现
11.HashSet存储原理
HashSet底层使用HashMap实现,HashSet的值作为HashMap的key。
1 | public boolean add(E e) { |
- Hash算法
hash算法计算存储对象的hashcode,再跟array.length-1做位运算,得到数组下标。若对应下标处无其他元素,直接存储。若有,则不同对象计算的hash值相等,需要用equals方法进行比较。equals相等则不插入,不等则插入。
equals用来比较的是两个对象的内容是否相等,基类Object中进行定义的equals方法源码(此时equals方法返回的是==的判断):
1 | public boolean equals(Object obj) { |
自定义类不重写equals,调用的仍是基类Object的equals。
- 哈希表
本质是一个数组,数组元素是链表。数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的
JDK1.8优化,链表长度超过8,变红黑树。
12.ArrayList和Vector
ArrayList:线程不安全,效率高,常用
Vector:Vector 继承AbstractList,实现了List,线程安全(使用synchronized修饰),效率低,不常用
1 | // 确定Vector的容量,synchronized上锁 |
13.Hash Table、HashMap、ConcurrentHashMap
HashTable线程安全对象,内部有上锁控制synchronized,效率低。
HashMap线程不安全对象,效率高。
了解:Collections.synchronizedMap()工具类提供同步包装器方法,返回具有线程安全的集合对象,内部单纯加锁。
ConcurrentHashMap(重点)
兼顾线程安全和效率,引入分段锁(锁粒度变小)。
分析:HashTable锁住整段数据,ConcurrentHashMap分段锁数据。
实现
数据结构
JDK1.7采用链表方式,JDK1.8采用链表+红黑树
hash碰撞后处理方式
1.7中,碰撞之后,用拉链法解决
1.8中,碰撞之后,默认使用拉链法;链表长度超过8,且数组容量超过64,将拉链表转换为红黑树存储。
保证并发安全的手段
1.7采用分段锁synchronized,1.8使用CAS和synchronized的组合
查询复杂度
1.7使用链表时时间复杂度为O(n),1.8使用红黑树时O(long(n))
开发中选择:1.局部变量优先选择HashMap
2.全局变量选择ConcurrentHashMap
14.HashMap、LinkedHashMap(重点)
HashMap 采用一种“Hash 算法”来决定每个元素的存储位置。当程序执行 map.put(String,Obect)方法 时,系统将调用String的 hashCode() 方法得到其 hashCode 值,系统会根据该 hashCode 值来决定该元素的存储位置。
HashMap中key和value都允许为null。key为null的键值对永远都放在以table[0]为头结点的链表中。可以有一个或多个键所对应的值为null。
每个 Java 对象都有 hashCode() 方法,都可通过该方法获得它的 hashCode 值。
源码如下:
1 | public V put(K key, V value) { |
HashMap内部维护了一个存储数据的Entry数组,HashMap采用链表解决冲突,每一个Entry本质上是一个单向链表。当准备添加一个key-value对时,首先通过hash(key)方法计算hash值,然后通过indexFor(hash,length)求该key-value对的存储位置,计算方法是先用hash&0x7FFFFFFF后,再对length取模,这就保证每一个key-value对都能存入HashMap中,当计算出的位置相同时,由于存入位置是一个链表,则把这个key-value对插入链表头。
HashMap
初始化大小为16
最大装填因此为0.75,HashMap中元素个数达到容量的0.75,则扩容,扩容后容量变成之前的2倍。
HashMap底层采用链表法(拉链法)解决冲突。
JDK1.8引入红黑树,链表长度>8,且数组容量>64,链表转换为红黑树。
红黑树中节点数<6,红黑树转换为链表。
HashTable
Hashtable中,key和value都不允许出现null值。
15.IO流
16.serialVersionUID的作用
执行序列化时,写对象到磁盘会根据类结构生成一个版本号ID。
反序列化,程序会比较根据当前类结构生成的版本号ID和磁盘中ID是否一致,一致则反序列化成功,否则反序列化失败。
17.Java异常
Error是虚拟机内部错误
- 栈内存溢出错误:StackOverflowError(递归,递归层次太多或递归没有结束)
- 堆内存溢出错误:OutOfMemoryError(堆创建了很多对象)
Exception是程序错误
RuntimeException,也称LogicException
通过完善我们的代码编程逻辑,来解决问题
非RuntimeException
通过使用try catch或者throws处理
5个运行时异常
1 | 算数异常, |
5个非运行时异常
1 | IOException, |
18.throw和throws的区别
throws作用于方法声明
throw作用于方法内
19.创建线程的三种方式
1 | 继承Thread |
案例:继承Thread
1 | class MyThread extends Thread { |
案例:实现runnable接口只是创建一个可执行任务,并没有新建线程
1 | package com.apache.examples; |
案例:实现Callable接口
1 | package com.apache.examples; |
20.线程的生命周期
21.Sleep和wait的区别
所属类不同
sleep()定义在Thread类
wait()定义在Object类
对于锁资源处理方式不同
sleep()不释放锁
wait()释放锁
22.线程安全的理解
- 理解
多线程访问同一个对象,如果不用进行额外的同步控制,调用对象的方法都可以获得正确结果,则该对象是线程安全的。
- 如何保证线程安全
在源码中最常见的方式是,使用synchronized关键字给代码块或方法加锁。
比如StringBuffer源码:
1 |
|
23.ThreadLocal理解
作用
为每个线程创建一个副本,实现在线程上下文传递同一个对象,如connection
实验一:证明ThreadLocal为每个线程创建一个变量副本
1 | 作者:风清扬 |
输出结果不同,证实是两个线程
- 底层如何实现为每个线程保存一个副本
1 | Long result = threadLocal.get(); |
调用ThreadLocal.get方法时,实际上是从当前线程中获取ThreadLocalMap<**ThreadLocal**, **Object**>,然后根据当前ThreadLocal获取当前线程共享变量Object。
24.ThreadLocal使用场景
前置知识:不管是什么框架,最本质的操作都是基于JDBC,当我们需要跟数据库打交道的时候,都需要有一个connection。
mybatis管理sqlsession,其内部都是采用ThreadLocal来实现的。
两个dao操作不同的connection不能保证service的事务控制。
所以,我们保证是同个connection即可
1 | //事务的边界放在业务层 |
更优雅的写法:
1 | public class ConnectionUtils { |
25.类加载机制
25.1 什么是类的加载机制?
Java源文件->编译->.class文件
类加载器ClassLoader读取.class文件,并将其转化为java.lang.Class的实例。
类加载器ClassLoader如何加载class?–类加载机制
25.2 class文件有哪些来源
- Java内部的核心类,位于$JAVA_HOME/jre/lib
- Java的扩展类,位于$JAVA_HOME/jre/lib/ext目录下
- 编写的类或第三方jar包
25.3 JDK分工加载三种class
1 | Java核心类 -- BootstrapClassLoader加载器(根加载器或引导加载器) |
所谓双亲委托机制,就是加载一个类,会先获取到一个系统类加载器AppClassLoader的实例,然后往上层层请求,先由BootstarpClassLoader去加载,如果BootStrapClassLoader发现没有,再下发给ExtClassLoader去加载,还是没有,才由AppClassLoader去加载。如果还是没有,则报错。
26.Servlet
- 存储位置不同
Session存储在服务器;Cookie存储在客户端
- 存储的数据格式不同
Session:value为对象
Cookie:value为字符串
- 存储数据的大小限制不同
Session:受服务器内存影响
Cookie:受浏览器影响,最大为4k
- 生命周期不同
Session:默认为30分钟
Cookie:由客户端控制,实际为客户端的一个文件,分两种情况:
- 默认是会话级cookie,随浏览器关闭而消失,比如保存session的cookie
- 非会话级cookie,可设置有效期,setMaxAge
联系:http协议是一种无状态协议,Session机制背后的原理是,服务器会自动生成会话级的cookie来保存session的标识(sessionId)。
28.业务分层
Web交互层
业务逻辑层
数据存储层
29.MVC理解
M-模型,代表一个存储数据的对象
V-视图,代表模型包含数据的可视化,如HTML
C-控制器,作用于模型和视图上,控制数据流向模型对象,并在数据变化时更新视图,代表技术是Controller
DispatchServlet前端控制器,接受客户端请求,根据客户端请求的URL,分发到对应的业务控制器(如UserController)
30.序列化
保持对象在内存中的状态,并可把保存的对象状态进行恢复。
java序列化场景:
- 需将内存中的对象保存到文件
- 通过socket通信传输对象
- 系统拆分为多个服务后,服务之间传输对象,需要序列化
31.数据库设计的三大范式、反范式
1.数据库的三大范式
第一范式
列不可分
第二范式
有主键
第三范式
不存在传递依赖
2.反范式
反范式优点:
提高查询效率
为提高查询效率,通过冗余一个商品名称字段,可将表关联查询转换为单表查询。
保存历史快照信息
比如订单表,收货人各种信息都属于历史快照,需要冗余保存。不能只通过用户地址ID去关联查询,因为收货人信息可能在后期变更。
32.常用聚合函数
count(* | 列)
sum(列)
avg(列)
max(列)
min(列)
33.左连接、右连接、内连接
左连接:以左表为主
1 | select a.*,b.* from a left join b on a.id = b.id |
右连接:以右表为主
1 | select a.*,b.* from a right join b on a.id = b.id; |
内连接:只列出两张表关联查询符合条件的记录
1 | select a.*, b.* from a inner join b on a.id=b.id; |
34.JDBC如何实现事务控制
JDBC对事务的操作是基于Connection来控制的
1 | try { |
事务边界在业务层进行控制,因为业务层通常包含多个dao操作。
35.事务的特点
ACID
1 | 原子性( Atomicity )、一致性( Consistency )、隔离性( Isolation )和持久性( Durability ) |
日志包括回滚日志(undo)和重做日志(redo)。
重做日志:当我们通过事务修改数据时,首先会将数据库变化的信息记录到重做日志中,然后再对数据库中的数据进行修改。这样即使数据库系统发生奔溃,我们还可以通过重做日志进行数据恢复。
回滚日志:保存事务发生之前的数据版本
36.事务的隔离级别
4级别
1 | 1. READ UNCOMMITTED 读未提交,脏读、不可重复读、幻读有可能发生。 |
37.synchronized和volatile的区别
作用位置不同
synchronized修饰方法、代码块
volatile修饰变量
作用不同
synchronized变量修改可见,原子性,可能造成线程阻塞
volatile仅变量修改可见,无原子性,不造成线程阻塞
38.synchronized和lock的区别
作用位置不同
synchronized可给方法、代码块加锁
lock只能给代码块加锁
锁机制不同
synchronized无需手动获取、释放锁,发生异常自动解锁,不出现死锁
lock需程序员获取和释放锁,如lock.lock()、lock.unlock(),未手动释放锁可能出现死锁。
**提示:unlock()可放在finally中**
名称 | 修饰对象 | 效果 |
---|---|---|
synchronized | 成员方法 | 默认锁当前对象 |
synchronized | 静态方法 | 默认锁当前类的class对象,如User.class |
synchronized | 代码块 | 可手动设置锁对象,如synchronized(this) |
39.深拷贝、浅拷贝
浅拷贝:只拷贝值类型的成员变量,不复制引用类型成员。
深拷贝:除了复制对象本身之外,也会复制对象包含的所有成员变量,包括引用类型成员对象。
40.什么是死锁,怎么防止死锁
- 死锁定义
线程A和线程B相互持有对方需要的锁,从而发生阻塞,最终变为死锁。
死锁示例:
1 |
|
输出:
1 | Thread-0-> 获取到a资源 |
- 防止死锁
- 减少同步代码嵌套
- 降低锁的使用粒度
- 采用tryLock方法,可设置超市时间,超时后自动释放锁,可防止死锁。
41.反射
反射是一种能力,指在程序运行时动态获取当前类的所有属性和方法,可动态执行方法,给属性赋值等操作。
1 | package com.apache.examples.reflact; |
42.Spring认识
IOC,重点是解耦
AOP,非核心业务抽离
方便融合主流框架
43.Spring的bean的作用域
- 默认singleton,即单例模式
- prototype,每次调用bean时都创建一个新对象
- request,一个http请求创建一个对象
- session
- global-session
44.Spring的bean是线程安全的吗
是线程安全。
回顾,造成线程不安全的三个必备要素:
- 多线程环境
- 访问同一个资源
- 资源具有状态性
bean满足1、2条件,bean是无状态的,因此线程安全
无状态指的是,没有存储数据,即没有通过数据的状态来作为下一步操作的条件
45.悲观锁、乐观锁
46.MyBatis缓存机制
缓存,作用提高查询
发布时间: 2020-09-21
最后更新: 2020-10-06
本文链接: https://juoyo.github.io/posts/c00a178c.html
版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!