Java面试自用

抽象类和接口的区别

JDK1.8以前

语法:

​ 抽象类:抽象类的方法可以抽象也可以非抽象,可以有构造方法

​ 接口:方法都是抽象方法,属性都是常量默认public static final 修饰,不能有构造方法

设计:

​ 抽象类:是同一类事物的抽取。例如针对dao层的操作的封装

​ 接口:一种操作的抽取,更像是标准的制定,定制系统之间对接的标准。

​ 例子:单体项目,分层开发,interface作为各层之间的纽带在controller中注入service

JDK1.8以后

接口中可以有实现的方法,但是方法声明加上default或static

abstract

修饰类:抽象类

修饰方法:抽象方法

不能修饰变量

Intager int 拆箱装箱

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wqhnCtvg-1603516172801)(C:\Users\KoreyChan\AppData\Roaming\Typora\typora-user-images\image-20201010173903857.png)]

Intager x = a ; 若a在-128~127之间,则java会对其进行缓存,再次出现 Intager x = a 时直接从缓存中取,并非new了,所以上图中i3==i4 ;

否则相当于 new Intager(a);所以i6!=i7

Intager可以为null,且默认值为null

int 默认值为0

重写重载

重写:父子类之间 方法名相同,参数列表相同

重载:一个类里面 方法名相同,参数列表不同(跟返回类型无关)

public double add(int a,int b)

public int add(int a,int b)不构成重载

集合

集合的有序无序是指在插入的时候,保持插入的顺序性,先插入的放在集合前面,后插入的放在集合后面。

List(有序,可重复)

ArrayList/LinkedList

Set(无序,不可重复)

HashSet/TreeSet

Collection:集合接口

Collections:集合工具类

ArrayList/LinkedList

ArrayList

数组,一块连续的内存空间

查找快,连续的内存空间方便寻址,删除,插入慢,因为发生了数据迁移

LinkedList

双向链表(每个元素都有两个指针一个指向pre(上一个),一个指向next(下一个)),不是连续的内存空间

查找慢。需要通过指针一个个寻找,插入删除快,只需要改变前后节点的指针指向即可。

此外,LinkedList中有两个指针一个指向fist(第一个元素)一个指向last(最后一个元素)

ArrayList扩容

ArrayList数组长度不够时

1.创建一个新数组 ,新数组长度是原数组的1.5倍(位运算)

2.原数组数据迁移到新数组

HashSet

HashSet的底层是用HashMap实现的,HashSet的值作为Map的key(键)(不可重复),Map的value都为PRESENT

Hash算法

集合中使用Hash算法是为了保持值的唯一性,如果插入数据时一个个比较,效率太低,用HashCode和数组长度的位运算结果来确定值存储的位置,如果出现两个元素的HashCode相同,则用equals方法比较两个元素,不同的话,形成一个链表结构,新进来的元素放在表头。

HashCode是为了计算元素在集合中的位置。两个元素的HashCode相同只能说明这两个元素在同一个链表中。

JDK1.8以后,随着数据的增加,链表会越来越长,此时可能会优化成一个红黑树

Hash表

本质是一个数组,数组的元素是链表

HashMap/HashTable

HashMap线程不安全,性能高 key和value都可以为空

HashTable线程安全,性能低 key和value都不能为空

ConcurrentHashMap(分段锁机制)线程安全,性能高

将Hash表分段,每段分别上锁。-

如何选择?

非多线程访问同一个资源,优先选择HashMap

全局变量,多线程访问同一个资源,优先选择ConcurrentHashMap

栈和双向链表

array[index++]= object;新元素加在栈顶

array[array.length-1];出栈从栈顶开始出

双向链表

Public class Node{

​ Node pre;//指向上一个结点

​ Node next;//指向下一个结点

​ T data;//泛型保存数据

}

IO流

分类

输入流 输出流(相对于程序而言)

InputStream OutputStream

Reader Writer

字节流 字符流(字节8位,字符16位)

字节流一般用于二级制文件

字符流一般用于文本文件

当需要读取文件中的内容时,一般使用字符流。

序列化

Java 提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。

将序列化对象写入文件之后,可以从文件中读取出来,并且对它进行反序列化,也就是说,对象的类型信息、对象的数据,还有对象中的数据类型可以用来在内存中新建对象。

版本号用于反序列化时比较当前类版本号和字节流传输的版本号是否相同。相同则可以进行反序列化。

异常体系

throwable

error exception

​ runtimeexception 非运行时异常

运行时异常: ,数组越界异常,类型转化异常,算数异常,数字格式异常

非运行时异常:IOException; SQLExpection FileNotFoundException; NoSuchFileException NoSuchMethodException

创建线程的方式

继承Thread类

实现Runnable接口

实现callable接口(可以获取线程执行以后的返回值)

但是实际上后两种都是创建了一个可执行的任务,并不是一个线程,还是要采用多线程的方式执行

例:new Thread(new Runnable(){}).start

new 一个Runnable的实现类 在用new的Thread类执行这个实现类

线程的启动

thread.start()启动一个新线程并执行run方法

thread.run()不回启动新线程,只调用run方法

线程的生命周期

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8nQ8j5f3-1603516172804)(C:\Users\KoreyChan\AppData\Roaming\Typora\typora-user-images\image-20201011162043958.png)]

线程的六种状态:

new; runnable; bolcked; waiting; timed wating ;terminated

线程进入同步代码块,且未获取到锁,进入blocked状态,锁被释放后进入runnable状态

线程调用wait 或者join 进入waiting状态 调用notify或者notifyAll()或者join线程结束后,进入runnable状态

线程调用sleep(time),或者wait(time)后进入timed waiting状态,休眠时间结束,调用notify或者notifyAll进入runnable状态

程序执行结束 线程进入terminated状态

线程安全

当多个线程访问同一个对象时,如果不用进行额外的同步控制或者其他的协调,调用这个对象都可以获得正确的结果,那么说这个对象是线程安全的

如何实现线程安全

synchroized关键字加锁

何时要实现线程安全

当多个线程访问同一个资源,需要实现线程安全

如果多个线程访问各自资源,不用考虑线程安全

sleep和wait

wait定义在Object,必须在同步方法或同步代码块中使用

sleep定义在Thread,可以在任何代码块使用

Java的锁是对象级别的,而不是线程级别的。所以wait定义在Object中。所有对象都能被作为监视器,即拥有一个独占锁,一个等待队列和一个入口队列的实体。

wait必须要写在同步代码块中,因为,避免了当前线程还没进入wait,别的线程先notify了,这会导致,wait的线程永远无法被唤醒。

wait,notify都写在同步代码块中,就避免了两个线程出现wait,notify执行错乱的情况。

ThreadLocal

**作用:**为每一个线程创建一个副本

​ 实现线程上下文传递对象

每个线程都有对应的一个map,map保存键值对

map的key为threadlocal

map的value为具体存放的东西

用threadlocal.get()获取当前线程对应的map的value值

用threadlocal.set()设置当前线程对应的map的value值

解决的问题

mybatis管理sqlsession,管理connection

类加载器和双亲委派机制

类加载机制:

Java文件——编译——>class文件

类加载器读取class文件,转化为实例,JVM用这个实例创建对象,调用方法

Java内部核心类:BootstrapClassLoader(启动类加载器)加载器负责加载

Java扩展类:ExtClassLoader(标准扩展类加载器)加载器负责加载

用户开发用到的第三方jar包:AppClassLoader(系统类加载器)加载器负责加载

除此之外还有一个CustormClassLoader(用户自定义类加载器)

双亲委派机制:

当需要加载一个类时,先实例化一个AppClassLoader加载器,在一层层向上推进,先用最顶层的加载器加载这个类,若这个类不存在,再用下一层加载器加载,依此类推,若都加载不到这个类则保存。

JavaScript

JavaScript原型关键作用是拓展原有类的特性

JSP和Servlet

Jsp本质就是一个Servlet

Jsp页面 先翻译成 Servlet 再编译成 .class文件

JSP=html+java

Servlet=java+html

侧重方向不同

servlet生命周期

创建对象,初始化,service(),根据条件选择执行doGet或doPost方法,销毁

servlet是单例的。即对象的创建只有一次。

初始化一次,销毁一次,

service和doXXX可以执行多次。

session和cookie

session和cookie存储的都是键值对

session存储在服务器上,value保存的是对象,是Object类型

cookie存储在客户端上,value保存的是字符串

Session大小受服务器内存控制

Cookie一般来说最大为4k

session生命周期由服务器端控制,默认30分钟

cookie客户端控制,本质是客户端的一个文件,默认随着浏览器关闭消失,可以用setMaxAge设置有效期

cookie设置httpOnly=true 可以防止客户端xxs攻击

两者的关系

每一个客户端在服务器上都有一个session用来保存客户端的一些信息,但是由于http协议是无状态的,客户端连接时,无法辨别是哪个客户端连接,所以在session中保存了一个sessionId,当客户端第一次连接服务器时,创建session时,在客户端的cookie中保存了(JsessionId,sessionId)这样一个键值对。下一次访问的时候根据cookie中的JsessionId来查找匹配的session。

转发重定向

转发:发生在服务器内部的跳转,可以用request传递数据,客户端发送请求给服务器,服务器内部转发这个请求,然后响应客户端。

转发只发送一次请求;

重定向:发生在客户端的跳转,必须用session传递数据。客户端发送请求,服务端响应给客户端一个url,客户端向这个url重新发送请求。

重定向发送多次请求。

JavaEE三层架构/MVC

三个层次

Web层:负责与用户交互并对外提供服务接口

业务逻辑层:实现业务逻辑模块

数据存取层:将业务逻辑层处理的结果持久化,方便后续查询

其中Web层又分为:MVC三层

MVC

M:model模型:代表一个存取数据的对象(pojo)

V:view视图:模型包含数据的可视化html,jsp

C:controller控制器:作用与模型和视图,控制数据流向模型对象,并在数据改变时,更新视图。servlet,Controller

SpringMVC有两个控制器:

DispatchServlet:前端控制器,接受客户端请求,根据请求的URL转发到不同的业务控制器,例如:UserController

Jsp四大作用域

ServletContext context域:在同一个web应用中使用(全局的)

HttpSession session域:在同一个会话中使用(私有的,多请求响应之间)

HttpServletRequest request域:在一个请求中使用(转发有效,重定向无效)

PageContext page域:在当前jsp页面使用

并发并行

并发:同一个CPU执行多个任务,按照细分时间片交替执行

并行:在多个CPU上同时处理多个任务

Java中的垃圾回收机制

System.gc()调用垃圾收集器

如何确定回收的对象

1.引用计数法,给对象添加一个引用计数器,每当这个对象被引用时,计数器的值加1,若一个对象的引用计数器为0,说明这是一个失效的垃圾对象,会被gc进行回收。但是此方法无法解决对象循环引用的问题

2.可达性分析法,把所有的引用关系看作是一张图,通过一系列名为gc roots的对象为起始点,向下搜索,搜索走过的路径为引用链,当一个对象到任何一个gc roots都没有引用链相连,说明此对象可以回收。

什么时候回收

  1. cpu空闲,
  2. 内存存储满了,
  3. 主动调用System.gc

如何回收

1.标记清除法,标记所有要回收的对象,标记完统一回收。但是可能会产生大量不连续的内存碎片。

2.复制算法,将内存按照容量分为大小相等的两块,每次使用其中一块,这一块用完了将还存活的对象移到另一块,再把已经使用过的这块清理掉。缺点是使用的内存变小了。

3.标记整理法,标记所有要回收的对象,统一回收,并对存活的对象进行整理。不会产生内存碎片

4.分代收集法,根据不同的场景分别选择以上的三种方法进行垃圾回收

jvm把内存分为三个区域:新生代,老年代,永久代

新生代:保存生命周期短的对象,一般所有的新生成的对象都是保存在新生代。发生MinorGC回收对象。

老年代:存放生命周期长的对象,在新生代中经历了多次垃圾回收后依然存活的对象会保存到老年代。满了发生FullGC回收对象

永生代:存放静态文件:java类,方法等。JDK8舍弃了永久代,提供了元空间技术。

新生代每次都有只有少量的对象存活,所以用复制算法。

老年代对象存活率高,用标记清除或者标记整理法

Mysql

四种索引方式

主键索引:

​ 主键是一种唯一性索引,加速查询列值唯一。

唯一索引:

​ 索引列所有值都只能出现一次,即必须唯一,值可以为空,可以加速 查询

普通索引:

​ 没有什么限制,查询速度可以提高

全文索引:

​ 只用于myisam引擎数据表,用作char varchar text数据类型的列

组合索引:

​ 将几个列作为一条索引进行检索,使用最左匹配原则

mysql锁

mysql锁分为共享锁和排他锁,也叫做读锁和写锁。

读锁是共享的,只能读不能写。

写锁是排他的,会阻塞其他的写锁和读锁。

写锁又可以分为表锁和行锁。

表锁锁定整张表,阻塞其他用户的读写操作。

行锁分为乐观锁和悲观锁。

悲观锁:悲观的认为,每次拿数据别人都会修改数据。所以每次拿数据都会上锁。别人想到拿到这个数据必须等待锁释放。

乐观锁:乐观的认为别人拿数据不会修改。所以拿数据时候不会上锁,只是在提交时候判断数据是否被修改。

数据版本记录机制,表中加一个数字类型version,拿数据时将version一起读出。数据更新一次version加一。提交时对比表中数据版本号和第一次读取的版本号是否相同,相等就更新,否则视为过期数据。

时间戳。与上面类似。

事务基本特性和隔离级别

基本特性

A:原子性,一个事务中的操作要么全部成功,要么全部失败。

C:一致性,数据库总是从一个一致性的状态,转移到另一个一致性的状态

D:隔离性,一个事务的修改在提交前,对其他事务时不可见的

I:持久性,一旦事务提交,做的修改会永久的保存到数据库中。(回滚日志,重做日志)

隔离级别

  1. 读未提交:脏读:读到其他事务未提交的数据。
  2. 读已提交:不可重复读:读已提交的事务,即有事务在修改数据时,等修改完提交完再读,两次读取的结果不一致。
  3. 可重复读:读取数据时,不再允许修改操作。mysql的默认级别,每次读取的结果都一致。可能产生幻读。
  4. 串行化:给每一行读取的数据加锁,导致 超时,锁竞争问题。

三大范式和反范式

第一范式:保证数据表中每列的原子性。每一字段都不可分割。

第二范式:确保表中的每一列都与主键相关

第三范式:确保表中的每一列都与主键直接相关

反范式:允许适当的数据冗(rong)余和重复提高数据库读 性能

​ 优点:查询时减少表的关联,更好的索引优化

​ 缺点:修改数据成本高

左连接,右连接,内连接

左连接:左表为主,显示左表的所有数据以及右表符合查询条件的语句

右连接:右表为主,显示右表的所有数据以及左表符合查询条件的语句

内连接:查询两表中符合筛选条件的语句

sql注入

通过字符串拼接构成特殊的查询语句

解决:采用预处理对象PreparedStatement。

关键字

group by

根据group by后面的分组字段,对数据表中的数据分组。

order by

根据order by后面的字段,对数据表中的数据排序

having

可以使用聚合函数,来判断 having sum(grade)>100 ;总分大于100的

UDP/TCP

TCP提供可靠的传输连接,传输前需要建立连接,面向字节流,传输效率低。

UDP无法保证传输的可靠性,无需创建连接,以报文的方式传输,效率高。

三次握手

第一次握手:建立连接,客户端发送一个SYN包,包含一个随机数x,等待服务器确认。

第二次握手:服务器接受到SYN包,向客户端发送一个确认的ACK包,包含数值x+1,同时发送一个SYN包,包含一个随机数y。

第三次握手:客户端接受到服务器的SYN和ACK包,向服务器发送确认的ACK包,包含数值y+1,完成三次握手。

为什么要三次握手

A向B发送信息(第一次握手),B收到A的信息(第二次握手ACK)。

B向A发送信息(第二次握手SYN),A也要收到并回复(第三次握手)。

Spring

bean作用域:

singleton单例模式

prototype:调用bean都会产生一个新对象

request:每次http请求会产生一个新对象

session:每一个session共享同一个对象

Spring线程安全问题

一般Spring中的bean是无状态的,我们都直到无状态的bean不存在线程安全的问题。如果bean是有状态的可以用ThreadLocal,为每一个线程创建保存变量的线程副本。或者设置bean的属性为prototype,即每次调用bean都会创建一个对象。

SpringIOC

1.配置文件的形势

2.注解方式

@Component:标准一个普通的spring Bean类。
@Controller:标注一个控制器组件类。
@Service:标注一个业务逻辑组件类。
@Repository:标注一个DAO组件类。

@Autowrie:自动填充

控制反转:本来由程序自己创建所要依赖的对象,现在由IOC容器创建并且注入,降低了对象间的耦合关系。

SpringAOP

面向切面编程:将一些非核心业务逻辑抽离,封装成一个可重用模块。实现核心业务和非核心业务的解耦。非核心业务例如:事务管理,日志,性能检测,读写分离。

SpringMVC

请描述Spring MVC的工作流程?描述一下 DispatcherServlet 的工作流程?

(1)用户发送请求至前端控制器DispatcherServlet;
(2) DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handle;
(3)处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet;
(4)DispatcherServlet 调用 HandlerAdapter处理器适配器;
(5)HandlerAdapter 经过适配调用 具体处理器(Handler,也叫后端控制器);
(6)Handler执行完成返回ModelAndView;
(7)HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet;
(8)DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析;
(9)ViewResolver解析后返回具体View;
(10)DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
(11)DispatcherServlet响应用户。

synchroized和lock

synchroized可以给方法代码块加锁

lock只能给代码块加锁

synchroized自动获取释放锁

lock手动获取释放锁,lock()和unlock()

synchroized和volatile

synchroized修饰方法和变量

保证变量修改的可见性和原子性,可能造成线程阻塞

volatile修饰变量

保证变量修改的可见性,无法保证原子性,不会造成线程阻塞

死锁

多个线程互相持有对方所需要的资源,产生阻塞,最终变为死锁

避免死锁

用trylock方法,设置超时时间,超时以后主动退出,避免死锁。

减少同步代码块的嵌套。

死锁的四个条件

  1. 互斥条件:进程对所持有的资源,不允许其他进程访问,其他进程要访问,只能等占有资源的进程使用完,并释放资源
  2. 请求和保持条件:进程获得资源后,对其他资源发出请求,其他资源已经被其他进程占有,但是本线程对自己已经获得的资源不释放。
  3. 不可剥夺条件:进程已获得的资源,在未完成使用前,不能被剥夺,只能自己释放。
  4. 环路等待条件:进程发生死锁后,若干进程形成头尾相连的循环等待资源的关系。

设计模式

单例模式

实例化某个对象时,只有一个实例对象存在。

饿汉模式:在类加载时就将实例对象初始化,因为无论哪一个线程获取这个对象,都是同一个对象,所以是线程安全的。但是在类加载时初始化,浪费内存。

懒汉模式

使用时才会创建对象,但是存在线程安全问题,一个线程执行到判断语句之后,还没有new对象时,另一个线程判断为对象没创建,此时可能创建多个实例。

懒汉模式双重锁机制

两个判断语句中加锁,锁类的.class对象。

观察者模式

对象间一对多的关系,一个对象的状态改变,所有依赖他的对象都被通知,并自动更新。

分页

PageHelp插件

数据结构与算法

遍历

**先序:**考察到一个节点后,即刻输出该节点的值,并继续遍历其左右子树。(根左右)

**中序:**考察到一个节点后,将其暂存,遍历完左子树后,再输出该节点的值,然后遍历右子树。(左根右)

**后序:**考察到一个节点后,将其暂存,遍历完左右子树后,再输出该节点的值。(左右根)

哈夫曼树

一组权值中最小的两个作为左右子树,权值相加为根,在取包含根在内的最小的两个数值分别做左右子树,依次类推,直到所有权值都用过。剩下的树就是哈夫曼树。

求带权外部路径长度:权值*权重(根为0,往下一层+1)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JuVfDwLy-1603516172806)(file:///D:\Program Files (x86)]\QQ\聊天记录\1320033674\Image\C2C\65AE5AE17EC2909A5A9E50B60C54E0C2.jpg)

二叉树

满二叉树

所有叶子节点都在最底层

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ff3yf8i6-1603516172808)(C:\Users\KoreyChan\AppData\Roaming\Typora\typora-user-images\image-20201019113324130.png)]

完全二叉树

所有节点的编号与满二叉树相同

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LyqwbMUj-1603516172809)(C:\Users\KoreyChan\AppData\Roaming\Typora\typora-user-images\image-20201019113340589.png)]

二叉查找树

每个节点的左子树所有项的值,小于该节点的值。

每个节点的右子树所有项的值,大于该节点的值。

平衡二叉树

中加锁,锁类的.class对象。

观察者模式

对象间一对多的关系,一个对象的状态改变,所有依赖他的对象都被通知,并自动更新。

分页

PageHelp插件

数据结构与算法

遍历

**先序:**考察到一个节点后,即刻输出该节点的值,并继续遍历其左右子树。(根左右)

**中序:**考察到一个节点后,将其暂存,遍历完左子树后,再输出该节点的值,然后遍历右子树。(左根右)

**后序:**考察到一个节点后,将其暂存,遍历完左右子树后,再输出该节点的值。(左右根)

哈夫曼树

一组权值中最小的两个作为左右子树,权值相加为根,在取包含根在内的最小的两个数值分别做左右子树,依次类推,直到所有权值都用过。剩下的树就是哈夫曼树。

求带权外部路径长度:权值*权重(根为0,往下一层+1)

[外链图片转存中…(img-JuVfDwLy-1603516172806)]\QQ\聊天记录\1320033674\Image\C2C\65AE5AE17EC2909A5A9E50B60C54E0C2.jpg)

二叉树

满二叉树

所有叶子节点都在最底层

[外链图片转存中…(img-ff3yf8i6-1603516172808)]

完全二叉树

所有节点的编号与满二叉树相同

[外链图片转存中…(img-LyqwbMUj-1603516172809)]

二叉查找树

每个节点的左子树所有项的值,小于该节点的值。

每个节点的右子树所有项的值,大于该节点的值。

平衡二叉树

它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值