百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

整理最近银行部分java面试题及答案(10)

csdh11 2024-11-30 14:15 24 浏览

1、写一个单例模式

spring实现单例模式:


这个单例模式可以按照标准的去做,我弄这个是觉得架构都这么做应该没有问题

2、ArrayList动态扩容原理

ArrayList扩容的本质就是计算出新的扩容数组的size后实例化,并将原有数组内容复制到新数组中去。(不是原数组,而是新数组然后给予数组对象地址)。

默认情况下,新的容量会是原容量的1.5倍。 新容量=旧容量右移一位(相当于除于2)在加上旧容量

ArrayList 的底层是用动态数组来实现的。我们初始化一个ArrayList 集合还没有添加元素时,其实它是个空数组,只有当我们添加第一个元素时,内部会调用扩容方法并返回最小容量10,也就是说ArrayList 初始化容量为10。 当前数组长度小于最小容量的长度时(前期容量是10,当添加第11个元素时就就扩容),便开始可以扩容了,ArrayList 扩容的真正计算是在一个grow()里面,新数组大小是旧数组的1.5倍,如果扩容后的新数组大小还是小于最小容量,那新数组的大小就是最小容量的大小,后面会调用一个Arrays.copyof方法,这个方法是真正实现扩容的步骤。

3、List<Person>A里面有几万条数据,mobile是重复的,怎么处理

才能把mobile不重复的数据放到另外一个List B里面

(参考https://www.cnblogs.com/woshimrf/p/java-list-distinct.html)

用一个空list存放遍历后的数据。

List<Person> result = new LinkedList<>();

for (Person person: A) {

boolean b = result.stream().anyMatch(u -> u.getMObile().equals(person.getMobile()));

if (!b) { result.add(user); }

}

另外一种高效方法:

public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {

Set<Object> seen = ConcurrentHashMap.newKeySet();

return t -> seen.add(keyExtractor.apply(t));

}

A.parallelStream().filter(distinctByKey(Person::getMobile)) .forEach(System.out::println);


4、SQL语句查找两条以上数据在指定多个字段相同的数据(查找重复记录); 查找去重(多个字符同时相同)后的记录 以及sql执行顺序

SELECT p1.name, p1.idnumber,p1.employer,p1.sst,p1.start2enddate,p1.sit,count(*) as reccount
from ssp1,ssp2
where p1.id<> p2.id
and p1.name = p2.name
and p1.idnumber=p2.idnumber
and p1.employer=p2.employer
and p1.sst= p2.sst
and p1.start2enddate = p2.start2enddate
and p1.sit=p2.sit
group by p1.name, p1.idnumber,p1.employer,p1.sst,p1.start2enddate,p1.sit
having count(*) >=3
order by reccount desc;

以上语句执行顺序:from -> where -> group by -> having -> order by -> select 因此,group by中没有的列,select是选不了的

5、ArrayList与Arrays.toList区别

我们经常使用的ArrayList相比Arrays内部的ArrayList来说多实现了一个Cloneable接口,此接口的用法为:

clone方法是在Object中定义的,而且还是protected类型的,只有实现了这个接口,才可以在该类的实力上调用clone方法(集合的方法都要用到clone方法,来复制一个,在新增一个加入,在改变),否则会抛出CloneNotSupportException,Object中默认实现的是一个浅拷贝,也就是表面拷贝,如果需要实现深层次的拷贝的话,必须对类中可变域生成新的实例。

拷贝规则如下:

① 基本类型:拷贝其值

② String字符串:拷贝的是地址,是个引用,但是在修改的时候,他会从字符串池(String pool)中重新生成新的字符串,原有的字符串对象保持不变。

③ 对象:拷贝地址应用,也就是说此时拷贝的对象与原有的对象共享该实例变量,不受访问权限的控制,这在Java中很疯狂,因为它突破了访问权限的定义,一个private修饰的变量,竟然可以被两个实例对象访问。

元素存储区

Arrays.ArrayList的元素存储区的声明:

private final E[] a;

java.util.ArrayList的存储区的声明:

transient Object[] elementData;

从声明上可以看出Arrays.ArrayList中的元素存储数据是一个不可变的数组应用,由于数组的长度本身是不可变的,所以Arrays.ArrayList从元素存储上就不支持长度变化,肯定是不允许add和remove操作了,所以在该类中未对add和remove进行实现,直接调用了父类AbstractList中声明的方法

public void add(int index, E element) {

throw new UnsupportedOperationException();

}

public E remove(int index) {

throw new UnsupportedOperationException();

}

public E set(int index, E element) {

throw new UnsupportedOperationException();

}

但凡使用这三个方法都会抛出UnsupportedOperationException异常。
Java.uti.ArrayList之所以可以变长,是因为使用到了Arrays.copyOf(T[] original, int newLength),具体实现:

/**

* Increases the capacity to ensure that it can hold at least the

* number of elements specified by the minimum capacity argument.

*

* @param minCapacity the desired minimum capacity

*/

private void grow(int minCapacity) {

// overflow-conscious code

int oldCapacity = elementData.length;

int newCapacity = oldCapacity + (oldCapacity >> 1);

if (newCapacity - minCapacity < 0)

newCapacity = minCapacity;

if (newCapacity - MAX_ARRAY_SIZE > 0)

newCapacity = hugeCapacity(minCapacity);

// minCapacity is usually close to size, so this is a win:

elementData = Arrays.copyOf(elementData, newCapacity);

}

扩容方式就是再旧有数组长度的基础上增加1.5倍,然后与需要的最小容量的数组长度进行比较,如果长度超过了最大的长度(int的最大值-8),则返回int的最大值。

而且此类的内部实现了add,remove和set三个方法。

jdk中所有开放的List实现类中没有一个提供以数组为参数的构造方法。从数据结构来看,数组是一种特殊的链表,正因为这个特殊性,造成了Java中的数据无法使用链表(List)的一些特性,使用上与链表对象的使用方式不同,仅支持设值,遍历与排序,再多的功能,如元素的新增,删除以及子链的获取功能均无法直接调用系统接口,只能自行编写代码,本来都是链表,却因为一些特殊性,变成了两种截然不同的对象,为了打通数组与List之间的壁垒,Arrays.ArrayList诞生了,让我们可以使用更短的代码实现数组与List之间的转换。

Arrays.ArrayList仅可用于作为构造List的参数,其他时候均可认为其就是一个数组。

Arrays.ArrayList未实现Cloneable接口,是因为Arrays.ArrayLists的状态本身就不可变,当然其中元素可变,实现之后并无任何意义。

6、静态变量好成员变量的不同

静态变量:由static修饰的变量称为静态变量,其实质上就是一个全局变量。如果某个内容是被所有对象所共享,那么该内容就应该用静态修饰;没有被静态修饰的内容,其实是属于对象的特殊描述。

成员变量:研究一个事物(类),其有: 属性:外在特征;例如人的身高,体重,(对应class成员变量),行为:能够做什么;例如人有说话,打球等行为。(成员函数)

区别:

① 两个变量的生命周期不同

成员变量随着对象的创建而存在,随着对象被回收而释放。

静态变量随着类的加载而存在,随着类的消失而消失。

② 调用方式不同

成员变量只能被对象调用。

静态变量可以被对象调用,还可以被类名调用。

③ 别名不同

成员变量也称为实例变量。

静态变量也称为类变量。

④ 数据存储位置不同

成员变量存储在堆内存的对象中,所以也叫对象的特有数据。

静态变量数据存储在方法区(共享数据区)的静态区,所以也叫对象的共享数据。

7、volatile与automicInteger比较

atomicInteger可以保证变量的原子性

voliate关键字的作用主要有:保证内容可见性、禁止指令重复排序、不保证原子性。

8、CyclicBarrier和CountDownLatch的区别

1)CountDownLatch简单的说就是一个线程等待,直到他所等待的其他线程都执行完成并且调用countDown()方法发出通知后,当前线程才可以继续执行。

2)cyclicBarrier是所有线程都进行等待,直到所有线程都准备好进入await()方法之后,所有线程同时开始执行!

3)CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器可以使用reset() 方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次。

4)CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得CyclicBarrier阻塞的线程数量。isBroken方法用来知道阻塞的线程是否被中断。如果被中断返回true,否则返回false。

9、什么是CAS

CAS是compare and swap的缩写,即我们所说的比较交换。

cas是一种基于锁的操作,而且是乐观锁。在java中锁分为乐观锁和悲观锁。悲观锁是将资源锁住,等一个之前获得锁的线程释放锁之后,下一个线程才可以访问。而乐观锁采取了一种宽泛的态度,通过某种方式不加锁来处理资源,比如通过给记录加version来获取数据,性能较悲观锁有很大的提高。

CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存地址里面的值和A的值是一样的,那么就将内存里面的值更新成B。CAS是通过无限循环来获取数据的,若果在第一轮循环中,a线程获取地址里面的值被b线程修改了,那么a线程需要自旋,到下次循环才有可能机会执行。

java.util.concurrent.atomic

包下的类大多是使用CAS操作来实现的( AtomicInteger,AtomicBoolean,AtomicLong)。

10、高并发优化

并发性上不去是因为当多个线程同时访问一行数据时,产生了事务,因此产生写锁,每当一个获取了事务的线程把锁释放,另一个排队线程才能拿到写锁,QPS(Query Per Second每秒查询率)和事务执行的时间有密切关系,事务执行时间越短,并发性越高,这也是要将费时的I/O操作移出事务的原因。

首先要了解高并发的的瓶颈在哪里?

① 可能是服务器网络带宽不够

② 可能web线程连接数不够

③ 可能数据库连接查询上不去。

根据不同的情况,解决思路也不同。

像第一种情况可以增加网络带宽,DNS域名解析分发多台服务器。

负载均衡,前置代理服务器nginx、apache等等

数据库查询优化,读写分离,分表等等

最后复制一些在高并发下面需要常常需要处理的内容:

尽量使用缓存,包括用户缓存,信息缓存等,多花点内存来做缓存,可以大量减少与数据库的交互,提高性能。

用jprofiler等工具找出性能瓶颈,减少额外的开销。

优化数据库查询语句,减少直接使用工具的直接生成语句(仅耗时较长的查询做优化)。

优化数据库结构,多做索引,提高查询效率。

统计的功能尽量做缓存,或按每天一统计或定时统计相关报表,避免需要时进行统计的功能。

能使用静态页面的地方尽量使用,减少容器的解析(尽量将动态内容生成静态html来显示)。

解决以上问题后,使用服务器集群来解决单台的瓶颈问题。

Redis缓存:

利用Redis可以减轻MySQL服务器的压力,减少了跟数据库服务器的通信次数。高并发的瓶颈就在于跟数据库服务器的通信速度(MySQL本身的主键查询非常快

原本查询数据时是通过主键直接去数据库查询的,选择将数据缓存在Redis,在查询数据时先去Redis缓存中查询,以此降低数据库的压力。如果在缓存中查询不到数据再去数据库中查询,再将查询到的数据放入Redis缓存中,这样下次就可以直接去缓存中直接查询到。


高并发处理数据一致:

首先是在更新操作的时候给行加锁,插入并不会加锁,如果更新操作在前,那么就需要执行完更新和插入以后事务提交或回滚才释放锁。而如果插入在前,更新在后,那么只有在更新时才会加行锁,之后在更新完以后事务提交或回滚释放锁。

在这里,插入是可以并行的,而更新由于会加行级锁是串行的。

也就是说是更新在前加锁和释放锁之间两次的网络延迟和GC,如果插入在前则加锁和释放锁之间只有一次的网络延迟和GC,也就是减少的持有锁的时间。

这里先insert并不是忽略了物品不足的情况,而是因为insert和update是在同一个事务里,光是insert并不一定会提交,只有在update成功才会提交,所以并不会造成过量插入秒杀成功记录。

前边通过调整insert和update的执行顺序来实现简单优化,但依然存在着Java客户端和服务器通信时的网络延迟和GC影响,我们可以将执行秒杀操作时的insert和update放到MySQL服务端的存储过程里,而Java客户端直接调用这个存储过程,这样就可以避免网络延迟和可能发生的GC影响。另外,由于我们使用了存储过程,也就使用不到Spring的事务管理了,因为在存储过程里我们会直接启用一个事务。

相关推荐

探索Java项目中日志系统最佳实践:从入门到精通

探索Java项目中日志系统最佳实践:从入门到精通在现代软件开发中,日志系统如同一位默默无闻却至关重要的管家,它记录了程序运行中的各种事件,为我们排查问题、监控性能和优化系统提供了宝贵的依据。在Java...

用了这么多年的java日志框架,你真的弄懂了吗?

在项目开发过程中,有一个必不可少的环节就是记录日志,相信只要是个程序员都用过,可是咱们自问下,用了这么多年的日志框架,你确定自己真弄懂了日志框架的来龙去脉嘛?下面笔者就详细聊聊java中常用日志框架的...

物理老师教你学Java语言(中篇)(物理专业学编程)

第四章物质的基本结构——类与对象...

一文搞定!Spring Boot3 定时任务操作全攻略

各位互联网大厂的后端开发小伙伴们,在使用SpringBoot3开发项目时,你是否遇到过定时任务实现的难题呢?比如任务调度时间不准确,代码报错却找不到方向,是不是特别头疼?如今,随着互联网业务规模...

你还不懂java的日志系统吗 ?(java的日志类)

一、背景在java的开发中,使用最多也绕不过去的一个话题就是日志,在程序中除了业务代码外,使用最多的就是打印日志。经常听到的这样一句话就是“打个日志调试下”,没错在日常的开发、调试过程中打印日志是常干...

谈谈枚举的新用法--java(java枚举的作用与好处)

问题的由来前段时间改游戏buff功能,干了一件愚蠢的事情,那就是把枚举和运算集合在一起,然后运行一段时间后buff就出现各种问题,我当时懵逼了!事情是这样的,做过游戏的都知道,buff,需要分类型,且...

你还不懂java的日志系统吗(javaw 日志)

一、背景在java的开发中,使用最多也绕不过去的一个话题就是日志,在程序中除了业务代码外,使用最多的就是打印日志。经常听到的这样一句话就是“打个日志调试下”,没错在日常的开发、调试过程中打印日志是常干...

Java 8之后的那些新特性(三):Java System Logger

去年12月份log4j日志框架的一个漏洞,给Java整个行业造成了非常大的影响。这个事情也顺带把log4j这个日志框架推到了争议的最前线。在Java领域,log4j可能相对比较流行。而在log4j之外...

Java开发中的日志管理:让程序“开口说话”

Java开发中的日志管理:让程序“开口说话”日志是程序员的朋友,也是程序的“嘴巴”。它能让程序在运行过程中“开口说话”,告诉我们它的状态、行为以及遇到的问题。在Java开发中,良好的日志管理不仅能帮助...

吊打面试官(十二)--Java语言中ArrayList类一文全掌握

导读...

OS X 效率启动器 Alfred 详解与使用技巧

问:为什么要在Mac上使用效率启动器类应用?答:在非特殊专业用户的环境下,(每天)用户一般可以在系统中进行上百次操作,可以是点击,也可以是拖拽,但这些只是过程,而我们的真正目的是想获得结果,也就是...

Java中 高级的异常处理(java中异常处理的两种方式)

介绍异常处理是软件开发的一个关键方面,尤其是在Java中,这种语言以其稳健性和平台独立性而闻名。正确的异常处理不仅可以防止应用程序崩溃,还有助于调试并向用户提供有意义的反馈。...

【性能调优】全方位教你定位慢SQL,方法介绍下!

1.使用数据库自带工具...

全面了解mysql锁机制(InnoDB)与问题排查

MySQL/InnoDB的加锁,一直是一个常见的话题。例如,数据库如果有高并发请求,如何保证数据完整性?产生死锁问题如何排查并解决?下面是不同锁等级的区别表级锁:开销小,加锁快;不会出现死锁;锁定粒度...

看懂这篇文章,你就懂了数据库死锁产生的场景和解决方法

一、什么是死锁加锁(Locking)是数据库在并发访问时保证数据一致性和完整性的主要机制。任何事务都需要获得相应对象上的锁才能访问数据,读取数据的事务通常只需要获得读锁(共享锁),修改数据的事务需要获...