交通银行面经

【面试官】:你们开发了安徽大学计算智能及应用实验室Web网站是吧,你做了那一块?

实验室网站主要展示本实验室的研究方向、人员、发表的论文、专利、软著以及源码下载。我做的设计并实现论文和专利等学术成果管理展示


【面试官】:你实现的部分后台mysql用了几张表?

因为实验室网站功能比较简单,论文专利的数据也不多,所以我设计了一张表,有关字段包括,以论文为例:论文ID(主键),论文名称,第一作者,第二作者,第三作者,通讯作者,论文发表时间,论文下载地址,论文所投期刊。我相信面试官第一反应肯定是觉得mysql这样设计,数据冗余度太高。我之前按照数据库范式设计规范,我之前的方案是设计多张表:

  • 论文表:论文ID(主键),论文下载地址,论文发表时间

  • 期刊表:期刊ID(主键),期刊名

  • 论文期刊关系表:论文期刊关系ID(主键),论文ID(外键),期刊ID(外键)

  • 第一作者表:作者ID(主键),作者姓名,...,

  • 论文作者关系表:论文作者关系ID(主键),论文ID(外键),作者ID(外键)

  • .............

考虑到实验室的论文成果不是大数据量的,如果表设计多了,进行查询的时候就得进行表之间的笛卡尔积运算,会影响效率,因为数据库设计三范式是理论上的。实践和理论有的时候有偏差。最终的目的都是为了满足实验室成员的需求,有的时候会拿冗余换执行速度。因为在sql当中,表和表之间连接次数越多,效率越低。(笛卡尔积)有的时候可能会存在冗余,但是为了减少表的连接次数,这样做也是合理的,并且对于开发人员来说,sql语句的编写难度也会降低。因此,我考虑设计成一张表。

【补充】

4.2、数据库设计范式共有?

  1. 第一范式:要求任何一张表必须有主键,每一个字段原子性不可再分。

  2. 第二范式:建立在第一范式的基础之上,要求所有非主键字段完全依赖主键,不要产生部分依赖。

  3. 第三范式:建立在第二范式的基础之上,要求所有非主键字段直接依赖主键,不要产生传递依赖。

【面试官】:mysql有几种隔离级别?

  • READ-UNCOMMITTED(读取未提交) : 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。

  • READ-COMMITTED(读取已提交) : 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。

  • REPEATABLE-READ(可重复读) : 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。mysql默认隔离级别

  • SERIALIZABLE(可串行化) : 最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。

【补充】

解决幻读的方法

解决幻读的方式有很多,但是它们的核心思想就是一个事务在操作某张表数据的时候,另外一个事务不允许新增或者删除这张表中的数据了。解决幻读的方式主要有以下几种:

  1. 将事务隔离级别调整为 SERIALIZABLE 。

  2. 在可重复读的事务级别下,给事务操作的这张表添加表锁。

  3. 在可重复读的事务级别下,给事务操作的这张表添加 Next-key Lock(Record Lock+Gap Lock)

【面试官】:项目部属,运行,上传的命令是什么?

  • compile是maven工程的编译命令,作用是将src/main/java下的文件编译为class文件输出到target目录下。查看 target目录,class文件已生成,编译完成。

  • clean是maven工程的清理命令,执行 clean会删除target目录及内容。

  • package是maven工程的打包命令,对于java工程执行package打成jar包,对于web工程打成war包。

  • install是maven工程的安装命令,执行install将maven打成jar包或war包发布到本地仓库。

  • 双击 tomcat7插件下 tomcat7:run 命令直接运行项目


【面试官】:Aop知道么,运用哪些场景?

AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。

【补充】

AspectJ 定义的通知类型有哪些?

  • Before(前置通知):目标对象的方法调用之前触发

  • After (后置通知):目标对象的方法调用之后触发

  • AfterReturning(返回通知):目标对象的方法调用完成,在返回结果值之后触发

  • AfterThrowing(异常通知) :目标对象的方法运行中抛出 / 触发异常后触发。AfterReturning 和 AfterThrowing 两者互斥。如果方法调用成功无异常,则会有返回值;如果方法抛出了异常,则不会有返回值。

  • Around: (环绕通知)编程式控制目标对象的方法调用。环绕通知是所有通知类型中可操作范围最大的一种,因为它可以直接拿到目标对象,以及要执行的方法,所以环绕通知可以任意的在目标对象的方法调用前后搞事,甚至不调用目标对象的方法


【面试官】: String a = new String("abc")创建了几个对象?

正确的为一个或者两个,因为无法判断字符串常量池中是否已经存在“abc”

【面试官】:上题你答很好,你是怎么查看java源码的?

IDEA的debug模式,逐行去追代码


【面试官】:你了解高并发么?在什么场景下运用到?

高并发指的是:在同时或极短时间内,有大量的请求到达服务端,每个请求都需要服务端耗费资源进行处理,并做出相应的反馈。从服务端视角看高并发服务端处理请求需要耗费服务端的资源,比如能同时开启的进程数、能同时运行的线程数、网络连接数、cpu、I/O、内存等等,由于服务端资源是有限的那么服务端能同时处理的请求也是有限的;高并发问题的本质就是:资源的有限性

高并发处理的基本思路

一:从客户端看:

尽量减少请求数量,比如:依靠客户端自身的缓存或处理能力。尽量减少对服务端资源的不必要耗费,比如:重复使用某些资源,如连接池

二:从服务端看:

1:增加资源供给,比如:(1)更大的网络带宽;(2)使用更高配置的服务器;(3)使用高性能的Web服务器;(4)使用高性能的数据库;

2:请求分流,比如:(1)使用集群;(2)分布式的系统架构;

3:应用优化,比如:(1)使用更高效的编程语言;(2)优化处理业务逻辑的算法;(3)优化访问数据库的SQL

数据库层面,常见的手段有:

1:合理选择数据库的引擎,比如Mysql的InnoDB与MyISAM引擎;

2:数据库集群,进行读写分离;

3:合理设计数据库的表结构、索引等;分库、分表,降低单库、单表的数据量;

4:合理使用NoSql;

Web应用层面,常见的手段有:

1:合理高效的利用缓存;

2:合理使用多线程,加快业务处理;

3:尽量避免远程调用、大量I/O等耗时的操作;

4:提供多个能提供相同服务的Web服务器,以实现负载均衡;

5:提供专门的图片、文件、视频等静态资源服务器;
 


【面试官】:线程池,ThreadLocal?

线程池提供了一种限制和管理资源(包括执行一个任务)的方式。 每个线程池还维护一些基本统计信息,例如已完成任务的数量

  • 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

  • 提高响应速度当任务到达时,任务可以不需要等到线程创建就能立即执行。

  • 提高线程的可管理性线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

线程池实现类 ThreadPoolExecutor 是 Executor 框架最核心的类。

ThreadPoolExecutor 3 个最重要的参数:

  • corePoolSize : 核心线程数线程数定义了最小可以同时运行的线程数量。

  • maximumPoolSize : 当队列中存放的任务达到队列容量的时候,当前可以同时运行的线程数量变为最大线程数。

  • workQueue: 当新任务来的时候会先判断当前运行的线程数量是否达到核心线程数,如果达到的话,新任务就会被存放在队列中。

ThreadPoolExecutor其他常见参数 :

  1. keepAliveTime:当线程池中的线程数量大于 corePoolSize 的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了 keepAliveTime才会被回收销毁;

  2. unit : keepAliveTime 参数的时间单位。

  3. threadFactory :executor 创建新线程的时候会用到。

  4. handler :饱和策略。关于饱和策略下面单独介绍一下。

交通银行面经_第1张图片

交通银行面经_第2张图片

  •  ScheduledThreadPoolExecutor 主要用来在给定的延迟后运行任务,或者定期执行任务。

  • FixedThreadPool 被称为可重用固定线程数的线程池。

  • SingleThreadExecutor 是只有一个线程的线程池。

  • CachedThreadPool 是一个会根据需要创建新线程的线程池。

如果我们设置的线程池数量太小的话,如果同一时间有大量任务/请求需要处理,可能会导致大量的请求/任务在任务队列中排队等待执行,甚至会出现任务队列满了之后任务/请求无法处理的情况,或者大量任务堆积在任务队列导致 OOM。这样很明显是有问题的! CPU 根本没有得到充分利用。但是,如果我们设置线程数量太大,大量线程可能会同时在争取 CPU 资源,这样会导致大量的上下文切换,从而增加线程的执行时间,影响了整体执行效率。

应用场景:

1.需要异步处理的时候,需要使用多线程。使用多线程看具体业务,比如说,前台不需要及时回显信息,后台开启线程进行数据处理。博客的浏览量之类。

2.做web端开发的时候,涉及到大量的网络传输,当发生io时候,线程就会停止,等待io结束,数据准备好,线程才会继续执行,所以当io密集时,可以多创建点线程,让线程等待时候,其他线程执行。云盘文件上传下载之类。

3.线程池的作用主要是为了提升系统的性能以及使用率数据库连接池,由于频繁的连接数据库,然而创建连接是一个很消耗性能的事情,所有数据库连接池就出现了。

ThreadLocal线程的变量副本,每个线程隔离。Thread类有一个类型为ThreadLocal.ThreadLocalMap的实例变量threadLocals,也就是说每个线程有一个自己的ThreadLocalMapThreadLocalMap有自己的独立实现,可以简单地将它的key视作ThreadLocalvalue为代码中放入的值(实际上key并不是ThreadLocal本身,而是它的一个弱引用)。ThreadLocalMap有点类似HashMap的结构,只是HashMap是由数组+链表实现的,而ThreadLocalMap中并没有链表结构。如果我们的强引用不存在的话,那么 key 就会被回收,也就是会出现我们 value 没被回收,key 被回收,导致 value 永远存在,出现内存泄漏

 ThreadLocal类主要解决的就是让每个线程绑定自己的值,可以将ThreadLocal类形象的比喻成存放数据的盒子,盒子中可以存储每个线程的私有数据。get() 和 set() 方法来获取默认值或将其值更改为当前线程所存的副本的值,从而避免了线程安全问题。

应用场景:

  • 场景一:在重入方法中替代参数的显式传递假如在我们的业务方法中需要调用其他方法,同时其他方法都需要用到同一个对象时,可以使用ThreadLocal替代参数的传递或者static静态全局变量。这是因为使用参数传递造成代码的耦合度高,使用静态全局变量在多线程环境下不安全。当该对象用ThreadLocal包装过后,就可以保证在该线程中独此一份,同时和其他线程隔离。

  • 场景二:全局存储用户信息。可以尝试使用ThreadLocal替代Session的使用,当用户要访问需要授权的接口的时候,可以现在拦截器中将用户的Token存入ThreadLocal中;之后在本次访问中任何需要用户用户信息的都可以直接冲ThreadLocal中拿取数据。

  • 场景三:解决线程安全问题


【面试官】:java里面还有哪些会导致内存泄漏?

  1. 静态集合类,如HashMap、LinkedList等等。如果这些容器为静态的,那么它们的生命周期与程序一致,则容器中的对象在程序结束之前将不能被释放,从而造成内存泄漏。

  2. ThreadLocal

  3. ​未关闭的资源类:如数据库连接、网络连接和IO连接等。在对数据库进行操作的过程中,首先需要建立与数据库的连接,当不再使用时,需要调用close方法来释放与数据库的连接。只有连接被关闭后,垃圾回收器才会回收对应的对象。否则,如果在访问数据库的过程中,对Connection、Statement或ResultSet不显性地关闭,将会造成大量的对象无法被回收,从而引起内存泄漏。

  4. 非静态内部类持有外部类的引用。如果有地方引用了这个非静态内部类,会导致外部类也被引用。这个内部类对象被长期引用了,即使那个外部类实例对象不再被使用,但由于内部类持有外部类的实例对象,这个外部类对象将不会被垃圾回收,这也会造成内存泄露。

你可能感兴趣的:(java,开发语言)