面试题整理(未完待续)

java 基础

1.为什么重写equals一定要重写hashcode?

补充: 想要弄明白hashCode的作用,必须要先知道Java中的集合。  
总的来说,Java中的集合(Collection)有两类,一类是List,再有一类是Set。前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。这里就引出一个问题:要想保证元素不重复,可两个元素是否重复应该依据什么来判断呢?
这就是Object.equals方法了。但是,如果每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。也就是说,如果集合中现在已经有1000个元素,那么第1001个元素加入集合时,它就要调用1000次equals方法。这显然会大大降低效率。
于是,Java采用了哈希表的原理。哈希(Hash)实际上是个人名,由于他提出一哈希算法的概念,所以就以他的名字命名了。哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上,初学者可以简单理解,hashCode方法实际上返回的就是对象存储的物理地址(实际可能并不是)。
这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。所以这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。

2.map常见分类区别

3.java8新特性

4.说说lamda表达式的

lambda运算符:所有的lambda表达式都是用新的lambda运算符 " => ",可以叫他,“转到”或者 “成为”。运算符将表达式分为两部分,左边指定输入参数,右边是lambda的主体。

    lambda表达式:

           1.一个参数:param=>expr

           2.多个参数:(param-list)=>expr

5.十进制的数在内存中是怎么存的?

以二进制补码形式存储,最高位是符号位,正数的补码是它的原码,负数的补码是它的反码加1,在求反码时符号位不变,符号位为1,其他位取反

6. ==比较的是什么

根据变量类型决定 。
基本类型 byte char short int long float double以及他们的包装类
比较方式 ==比较的是值是否相等

引用类型
比较方式 ==比较的是他们内存地址是否相等

7.若对一个类不重写,它的equals()方法是如何比较的?

如果不重写的话与==是一样的 比较的是对象的内存地址,如果像String 和date这些对象重写了equals方法 比较是值是否相等

8.java 对象创建过程

对象的创建
下图便是 Java 对象的创建过程,我建议最好是能默写出来,并且要掌握每一步在做什么。

在这里插入图片描述

1.1new指令
​ 虚拟机遇到一条new指令时,首先去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,那么须先执行相应的类加载过程。

1.2分配内存
​ 接下来虚拟机将为新生代对象分配内存。对象所需的内存的大小在类加载完成后便可完全确定。分配方式有“指针碰撞(Bump the Pointer)”和“空闲列表(Free List)”两种方式,具体由所采用的垃圾收集器是否带有压缩整理功能决定。

1.3初始化
​ 内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),这一步操作保证了对象的实例字段在Java代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。

1.4对象的初始设置
​ 接下来虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息。这些信息存放在对象的对象头(Object Header)之中。根据虚拟机当前的运行状态的不同,如对否启用偏向锁等,对象头会有不同的设置方式。

1.5 init方法
​ 在上面的工作都完成了之后,从虚拟机的角度看,一个新的对象已经产生了,但是从Java程序的角度看,对象创建才刚刚开始—方法还没有执行,所有的字段都还为零。所以,一般来说,执行new指令后悔接着执行init方法,把对象按照程序员的意愿进行初始化(应该是将构造函数中的参数赋值给对象的字段),这样一个真正可用的对象才算完全产生出来。

9servlet的生命周期

Servlet的生命周期分为5个阶段:加载、创建、初始化、处理客户请求、卸载。

(1)加载:容器通过类加载器使用servlet类对应的文件加载servlet

(2)创建:通过调用servlet构造函数创建一个servlet对象

(3)初始化:调用init方法初始化

(4)处理客户请求:每当有一个客户请求,容器会创建一个线程来处理客户请求

(5)卸载:调用destroy方法让servlet自己释放其占用的资源

10 JVM垃圾回收机制

常见垃圾回收算法 标记清楚算法 标记整理算法 复制算法
分代收集算法: 分代收集算法是目前大部分JVM的垃圾收集器采用的算法
将堆分为 年轻代 年老代 堆区之外还有一个代就是永久代
年轻代(Young Generation)的回收算法 目标回收生命周期短的
年老代(Old Generation)的回收算法 在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。
永久代(Permanent Generation)的回收算法 用于存放静态文件,如Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate 等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代也称方法区,

11.replace和replaceAll区别

replace方法:支持字符和字符串的替换
replaceAll方法:基于正则表达式的字符串替换。

12.什么是守护线程

Java线程分为用户线程守护线程。

守护线程是程序运行的时候在后台提供一种通用服务的线程。所有用户线程停止**,进程会停掉所有守护线程**,退出程序。

Java中把线程设置为守护线程的方法:在 start 线程之前调用线程的 setDaemon(true) 方法。

13 thread 和runnable的区别

Runnable更容易实现资源共享,能多个线程同时处理一个资源。
Thread实现售票功能的时候,创建了两个对象;而Runnable实现售票功能的时候,使用的是同一个Runnable对象。
总结
(1)由于Java不允许多继承,因此实现了Runnable接口可以再继承其他类,但是Thread明显不可以;
(2)Runnable可以实现多个相同的程序代码的线程去共享同一个资源,而Thread并不是不可以,而是相比于Runnable来说,不太适合。

14sleep() 和 wait() 有什么区别

1.sleep() 是 Thread 类的静态本地方法;wait() 是Object类的成员本地方法
2.sleep() 方法可以在任何地方使用;wait() 方法则只能在同步方法或同步代码块中使用,否则抛出异常Exception in thread “Thread-0” java.lang.IllegalMonitorStateException
3.sleep() 会休眠当前线程指定时间,释放 CPU 资源,不释放对象锁,休眠时间到自动苏醒继续执行;wait() 方法放弃持有的对象锁,进入等待队列,当该对象被调用 notify() / notifyAll() 方法后才有机会竞争获取对象锁,进入运行状态
4.JDK1.8 sleep() wait() 均需要捕获 InterruptedException 异常

15 线程的状态

  1. 新建状态 建线程对象,未调用 start 方法
  2. 可运行状态 :线程对象创建后,被调用 start 方法。此状态的线程位于可运行线程池中,等待获取 CPU 的使用权
  3. 运行中(RUNNING) 线程获取了 CPU 的使用权,执行程序代码
  4. 阻塞(BLOCKED):线程因为某种原因放弃了 CPU 的使用权,暂时停止运行,直到线程进入可运行状态,才有机会再次获取 CPU 的使用权进入运行状态
  5. 消亡(DEAD):线程已经执行完毕。主线程 main 方法结束或因异常退出;子线程 run 方法结束或因异常退出

16 java 程序中怎么保证多线程的运行安全?

一、线程安全在三个方面体现

1.原子性:提供互斥访问,同一时刻只能有一个线程对数据进行操作,(atomic,synchronized);

2.可见性:一个线程对主内存的修改可以及时地被其他线程看到,(synchronized,volatile);

3.有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序,该观察结果一般杂乱无序,(happens-before原则)。

synchronized、volatile、LOCK

17 hashCode()返回该对象的哈希码值;equals()返回两个对象是否相等。

关于hashCode和equal是方法是有一些 常规协定 :

1、两个对象用equals()比较返回true,那么两个对象的hashCode()方法必须返回相同的结果。

2、两个对象用equals()比较返回false,不要求hashCode()方法也一定返回不同的值,但是最好返回不同值,亿提搞哈希表性能。

3。重写equals()方法,必须重写hashCode()方法,以保证equals方法相等时两个对象hashcode返回相同的值*。

shiro 框架

在这里插入图片描述提供了 认证授权加密 会话管理等功能

工作流程 三个组件

subject 主体 与当前应用交互的东西都是subject 所用的subject 都委托securitymanager 安全管理器 ,可以把subject 当成一个门面 securitymanager 才是实际执行者

securitymanager 安全管理器

是shiro 的核心 类似于springMVC的dispatchservlet 前端控制器 所有有关的操作都会有安全管理器交互 负责与后面组件交互

realm 数据源 是shiro获取安全数据的地方 securityManager 要验证用户身份就需要冲realm这里获取想对应的数据 以确定用户是否合法

securitymanager 安全管理器 又包括组件

Authenticator:负责执行认证操作。 //哦then提卡特

Authorizer:负责执行授权操作。 //哦rai则

SessionManager:负责对session进行管理;

Session DAO: 负责管理session数据。一般来说,如果需要针对个性化的Session数据存储,才需要使用Session DAO;

CacheManager:负责对Session和授权数据进行缓存;

Shiro是以过滤器的方式对访问规则进行控制,并内置一组过滤器。其中比较常用的是:

anon:不认证也可以访问;

authc:必须认证后才允许访问;

perms:指定资源需要有指定权限才允许访问;

SSM

springmvc是什么,优点

Spring Web MVC是一种基于Java的实现了MVC设计模式的、请求驱动类型的、轻量级Web框架。

springmvc执行原理

1。用户发起请求

2.前端控制器dispatchservlet接受请求后 请求 handlerMapping 返回一个handler

3.dispatchservlet请求handleradapter

4.handlerAdapter 调用 处理器 返回一个modelandview

  1. dispatchservlet请求视图解析器 viewResolver进行渲染
  2. 响应请求

Springmvc哪些组件,每个组件的作用。

dispatchservlet 前端控制器 负责接收并处理所有的web请求

HandlerMapping 负责处理web请求和具体的Controller之间的映射关系匹配。

Controller:DispatherServlet controller的处理方法处理当前的业务请求,处理完成后返回ModelAndView对象。

ViewResolver 用来处理视图名与具体的view实例之间的映射对应关系。根据ModelAndView中的视图名查找相应的View实现类,然后将查找的结果返回给DispatcherServlet,DispatcherServlet最终会将ModelAndView中的模型数据交给返回的View处理最终的视图渲染工作。

.View:为支持多种视图技术

SpringMVC 注解

@Controller @service @repository @component

@requestMapping @getmapping @postmapping

@responseBody @restcontroller @requestParm @pathvariable @cookvalues

@sessionAttributes @ModelAttributes

spring Spring是Java Web三大经典框架(Struts、Spring、Hibernate ,SSH)中主要用于降低模块之间耦合度的框架,实际上Spring除了能够通过IoC降低模块之间的耦合度外

ioc 和di

2.什么是控制反转(IoC):

(1).IoC是Inversion of Control的缩写,有的翻译成“控制反转”,还有翻译成为“控制反向”或者“控制倒置”。

(2).1996年,Michael Mattson在一篇有关探讨面向对象框架的文章中,首先提出了IoC 这个概念。简单来说就是把复杂系统分解成相互合作的对象,这些对象类通过封装以后,内部实现对外部是透明的,从而降低了解决问题的复杂度,而且可以灵活地被重用和扩展。IoC理论提出的观点大体是这样的:借助于“第三方”实现具有依赖关系的对象之间的解耦,如下图所示:

即把各个对象类封装之后,通过IoC容器来关联这些对象类。这样对象与对象之间就通过IoC容器进行联系,但对象与对象之间并没有什么直接联系。

如果去掉IoC容器后系统中的对象A与对象B就有了直接关系,如下图所示:

比如好多的对象类要关联起来的话,就会变得很复杂,如下图所示:

所以提出IoC控制反转是很有必要的。

(3).为什么要把这种方式叫做控制反转呢?

— 软件系统在没有引入IoC容器之前,对象A依赖对象B,那么A对象在实例化或者运行到某一点的时候,自己必须主动创建对象B或者使用已经创建好的对象B,其中不管是创建还是使用已创建的对象B,控制权都在我们自己手上。

— 如果软件系统引入了Ioc容器之后,对象A和对象B之间失去了直接联系,所以,当对象A实例化和运行时,如果需要对象B的话,IoC容器会主动创建一个对象B注入到对象A所需要的地方。

— 通过前面的对比,可以看到对象A获得依赖对象B的过程,由主动行为变成了被动行为,即把创建对象交给了IoC容器处理,控制权颠倒过来了,这就是控制反转的由来!

3.IoC的别名:依赖注入(DI)

(1).2004年,Martin Fowler探讨了同一个问题,既然IoC是控制反转,那么到底是“哪些方面的控制被反转了呢?”,经过详细地分析和论证后,他得出了答案:“获得依赖对象的过程被反转了”。控制被反转之后,获得依赖对象的过程由自身管理对象变为由IoC容器主动注入。于是,他给“控制反转”取了一个更合适的名字叫做“依赖注入(Dependency Injection,DI)”。他的这个答案,实际上给出了实现IoC的方法:注入。

(2).所谓依赖注入,就是由IoC容器在运行期间,动态地将某种依赖关系注入到对象之中。

(3).所以,依赖注入(DI)和控制反转(IoC)是从不同的角度描述的同一件事情,就是指通过引入IoC容器,利用依赖关系注入的方式,实现对象之间的解耦。

AOP

什么是AOP?

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP(面向对象编程)的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码。如下图所示:

经典应用:事务管理、性能监视、安全检查、缓存 、日志等。

AOP通知类型 advice

@before 前置通知

@after 后置通知

@around 环绕通知

@after returning正常返回通知

@afterThrowing 异常返回通知

spring bean配置单例和多例

bean的作用域

singletion单例

protoype 每次调用getbean都会调用一个新的实例

request 每次HTTP请求都会一个新的bean

session

bean 的注入方式

1.属性注入(要求属性提供呢setXxx方法 )

1 <bean id="dept" class="com.proc.bean.Dept">
2     <property name="id" value="2"/>
3     <property name="name" value="信息部"></property>
4 </bean>

2.构造器注入 (构造函数注入·)

spring bean 默认是单例默认,在对应.xml文件中的配置是:

singleton就是配置这个bean是否是单例的,如果不写,就是默认值true。

//xml修改bean作用域
<bean id="user" class="..." scope="singleton"/>  

//注解修改bean的作用域 @scope


@Component("lifeCycleBean")

//@Scope(value=ConfigurableBeanFactory.SCOPE_PROTOTYPE)

@Scope("prototype")//默认是单例(singleton),更改为多例(prototype)

public class LifeCycleBean {

 

}

bean的生命周期

1)实例化 Bean 对象

调用实例化 Bean 对象之前的 InstantiationAwareBeanPostProcessorAdapter 接口的 postProcessBeforeInstantiation 方法

Bean 的创建,由 BeanFactory 读取 Bean 定义文件,并生成各个实例

调用实例化Bean对象之后的 InstantiationAwareBeanPostProcessorAdapter 接口的 postProcessAfterInstantiation 方法

(2)设置 Bean 对象属性

执行在 Bean 设置属性时的 InstantiationAwareBeanPostProcessorAdapter 接口的 postProcessPropertyValues 方法,设置pvs值

Setter 注入,执行 Bean 的属性依赖注入

(3)将容器和 bean 本身的信息暴露出来便于使用过程 (aware接口)

实现 BeanNameAware 接口,重写并执行 setBeanName()。

实现 BeanFactoryAware接口,重写并执行 setBeanFactory()。

(4)初始化 Bean 对象过程

调用初始化 Bean 对象之前的InstantiationAwareBeanPostProcessorAdapter 接口的 postProcessBeforeInitialization 方法

继承 InitializingBean 类,重写 afterPropertiesSet(),完成初始化。

在 xml 文件中的 Bean 标签中使用 init-method 可以设置自定义初始化方法。

调用初始化 Bean 对象之后的 InstantiationAwareBeanPostProcessorAdapter 接口的 postProcessAfterInitialization 方法。

(5)销毁 Bean 对象过程

实现 DisposableBean 接口,重写 destroy(),在容器关闭时,如果 Bean 类实现了该接口,则执行它的 destroy() 方法

在 xml 文件中的 Bean 标签中定义 destroy-method,在容器关闭时,可以在 xml 文件中的 Bean 标签中使用“destory-method”设置自定义的对象销毁方法的方法

mybatis

mybatis的一级缓存和二级缓存

一级缓存( SqlSession级別)

Mybatis的一级缓存是 SqlSession级别的缓存。在操作数据库时需要构造 SqlSession对象,在对象中有一个 HashMap用于存储缓存数据。不同的 SqlSession之间的缓存数据区域( Hashmap)是互相不影响的。

一级缓存的作用域是 SqlSession范围的,当在同一个 SqlSession中执行两次相同的sql语句时,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次査询时会从缓存中获取数据,不再去底层数据库查询,从而提高查询效率。需要注意的是,如果 SqlSession执行了DML操作( Insert、update和delete),并提交到数据库, MyBatis 则会清空SqlSession中的一级缓存,这样做的目的是为了保证缓存中存储的是最新的信息,避免出现脏读现象。

当一个 SqlSession结束后该 SqlSession中的一级缓存也就不存在了。 Mybatis默认开启一级缓存,不需要进行任何配置。

二级缓存(mapper级别)

二级缓存是mapper级别的缓存。使用二级缓存时,多个 SqlSession使用同一个 Mapper的sql语句去操作数据库,得到的数据会存在二级缓存区域,它同样是使用 HashMap进行数据存储。相比一级缓存 SqlSession,二级缓存的范围更大,多个 SqlSession可以共用二级缓存,二级缓存是跨 SqlSession的。

二级缓存是多个 SqlSession共享的,其作用域是 mapper的同一个 namespace。不同的SqlSession两次执行相同的 namespace下的sql语句,且向sql中传递的参数也相同,即最终执行相同的sql语句,则第一次执行完毕会将数据库中査询的数据写到缓存(内存),第二次查询时会从缓存中获取数据,不再去底层数据库查询,从而提高查询效率。

MyBatis默认没有开启二级缓存,需要在 setting全局参数中配置开启二级缓存。

  1. 如何清空一级缓存

sqlSession.flush()

sqlSession.close()

执行dml操作

开启二级缓存casheEnabled

<setting name="cacheEnabled"value="true"/>

在需要缓存的mapper中设置:

<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>

以上配置创建了一个LRU缓存,并每隔60秒刷新,最大存储512个对象,而且返回的对象被认为是只读的。

cache元素用来开启当前 mapper的 namespace下的二级缓存。该元素的属性设置如下:

flushInterval:刷新间隔。可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式的时间段。默认情况下是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。

size:缓存数目。可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的可用内存资源数目。默认值是1024。

readOnly:只读。属性可以被设置为true或false。只读的缓存会给所有调用者返回缓存对象的相同实例,因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false。

eviction:收回策略,默认为LRU。有如下几种:

LRU。最近最少使用的策略,移除最长时间不被使用的对象

FIFO。先进先出策略,按对象进入缓存的顺序来移除它们

SOFT。软引用策略,移除基于垃圾回收器状态和软引用规则的对象。

WEAK。弱引用策略,更积极地移除基于垃圾收集器状态和弱引用规则的对象。

事务:

事务说明

在Spring中,事务有两种实现方式,分别是编程式事务管理和声明式事务管理两种方式。

编程式事务管理: 编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。

声明式事务管理: 建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

声明式事务管理不需要入侵代码,通过@Transactional就可以进行事务操作,更快捷而且简单。推荐使用

@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义

关于负载均衡的理解

把大量的并发访问或数据流量分担到多台节点设备上分别处理,减少用户等待响应的时间

发布了78 篇原创文章 · 获赞 5 · 访问量 7385

猜你喜欢

转载自blog.csdn.net/weixin_41930050/article/details/101056475