深蓝XX面试,无情拷打,怪我太菜了

5 篇文章 0 订阅
3 篇文章 0 订阅

虽然只是一面,虽然看似简单,每一个字都被无情拷打,每一个八股背后都是场景… 还有几个SQL和Redis相关的场景,被拷打的已经混乱了,实在是记不起来了。。。。就下面这六个问题,扩展扩展再扩展。。。。突然想起还有一个问题简单,真没扩展。。。

一、ArrayList和LinkedList的区别

ArrayList和LinkedList都是java.util包下集合框架的部分,都实现了List接口,用于存储动态数据,二者的区别主要体现在以下几个方面:底层实现:ArrayList是基于动态数组实现的;LinkedList是基于双向链表实现的。性能方面:ArrayList查询元素的时间复杂度是O(1)常数级的,因为ArrayList支持索引查找的;删除和插入元素操作时间复杂度可能是O(n),因为可能需要移动大量的元素;LinkedList获取元素需要从头开始遍历,不支持随机访问元素,所以时间复杂度是O(n);删除和插入元素操作只需要修改指针,而不需要移动大量元素,所以该操作的时间复杂度是O(1);内存开销方面:ArrayList只存数据,而LinkedList还需要额外的空间存储指针,所以它的开销会更大;容量调整方面:ArrayList中添加的元素超过其初始容量后,会进行扩容,重新分配数组大小,一般是当前大小的1.5倍;LinkedList是基于节点的,不需要扩容,直接在后面添加就可以了;使用场景:ArrayList适合查询操作多插入删除操作少的一些场景吧,当需要频繁插入或删除元素时,特别是在列表的开头或中间,使用 LinkedList 更有优势。

二、int 和 Integer(基本数据类型和包装类)

int是基本数据类型用于表示32位的整数,Integer是一个类,封装了一个int值,以及一些方法parseInt()\valueOf()\toString()等;int类型的数据一般存储在栈上,Integer对象存储在堆上;一般来说对int的操作会比Integer快,因为int存储的就是实际的值而不是引用,它不涉及对象的一个额外开销,Integer操作的时候可能会涉及到拆箱和装箱额外的开销。

为什么有int了还要有Integer?—对象的性质,在有一些场景我们需要使用对象,就比如说集合中就只能存对象而不能存基本数据类型;实用的方法,对象类中封装了一些实用方法,在某些场景下方便我们直接调用。

int和Integer之间进行比较?—如果用" == “比较int和Integer,此时Integer会自动拆箱,二者进行值比较;如果用” == “比较两个Integer,那么二者进行引用比较,而不是值比较;但是因为Java会缓存-128 -到127之间的Integer值,所以对于该范围内的值,用” == “比较两个Integer可能会返回true,即使它们是两个不相同的对象;对于不在该范围内的值,用” == “比较两个Integer也可能会返回false,即使两个Integer对象的值相同。—如果用” equals() "方法比较两个Integer对象,那么比较的是二者的值。

总结:当使用" == “比较int和Integer时,总是进行值比较;当使用” == "比较两个Integer时,总是进行引用比较,不是数值比较,除非值在-128到127的范围;如果想要比较两个Integer对象的数值,最好使用"equals()"方法。

三、在Spring中某些时候你可能会发现某些方法上的注解不起作用

以下是一些原因和场景:

1、自调用问题:当一个Spring Bean的内部方法调用了另一个使用了注解的方法,那么该注解可能不起作用;—原因是这种内部方法调用绕过了Spring的代理机制;因为内部自调用就是一个普通的Java方法调用,不会经过代理对象,而Java注解刚好又是通过代理增强来实现的,直接的内部调用就绕过了这个代理,因为相关的增强不会被触发,也就是注解不起作用。

2、非Spring容器管理的对象:当一个对象不是由Spring容器创建的,是我们直接通过new关键字创建的对象,那么该对象上的任何Spring注解都不会起作用;—原因首先要知道通过Spring容器创建的对象会经历一个特定的生命周期,在这个生命周期中,Spring容器会对该对象进行一系列的操作,例如依赖注入、初始化、应用各种增强等,所以如果是我们直接通过new关键字创建的对象,它完全避开了Spring容器的上下文,该对象自然就不会经历Spring定义的生命周期,甚至对于Spring容器来说根本就不知道该对象的存在,也就不能对其通过相关注解进行增强等。

3、注解冲突:用在类上的注解和用在该类方法上的注解存在冲突;

4、未正确使用注解:使用的注解需要在特定的场景;

四、集合中为什么使用迭代器设计模式?

我们知道当我们需要遍历ArrayList、LinkedList这样的集合时,最简单的方式就是使用for循环,不是很简单直观吗?那为什么还要使用迭代器这一设计模式呢?

1、为多样性的集合结构提供统一的遍历接口:Java集合框架包含多种数据结构,每一种数据结构都有特定的内部实现,迭代器为这些不同的数据结构提供了统一的遍历接口,使得我们在编码的时候不必太过于关心它们内部结构的差异,依然可以使用统一的方式去遍历;

2、增加安全性:迭代器模式提供了一种安全机制,就是在使用迭代器遍历集合时候,如果有外部线程修改了集合,迭代器就会立即抛出ConcurrentModifycationException异常,这为集合的并发修改提供了一层安全保障;

3、迭代器独立的遍历状态:对于每一个迭代器它都各自维护自己的遍历状态,如果多个迭代器同时对同一集合进行遍历,迭代器之间相互独立不会相互影响;

4、扩展性增强:基于迭代器我们可以轻松为集合添加新的遍历方式,而不需要去修改集合的内部结构实现;

5、为复杂的数据结构提供简化的遍历:考虑像TreeSet、LinkedHashMap、图、树,这样的复杂数据结构,如果使用for循环进行遍历可能会比较复杂,使用迭代器可能会简便的多;

6、隐藏遍历的细节:对于复杂的遍历细节,迭代器模式允许隐藏这些细节,使得客户端代码更简单、干净。

五、对于超卖问题,怎么在Java代码层面来控制

首先我们要明确超卖问题一般是出现在并发环境中,当多个线程或进程同时试图购买库存有限的商品时。

1、使用synchronized关键字:确保一次只会有一个线程可以执行购买操作或修改库存的代码块;

2、使用Lockjava.util.concurrent.locks包提供了更加灵活的锁机制,例如ReentrantLock

六、对于超卖问题,其它的解决方式

1、使用数据库锁:悲观锁和乐观锁;—悲观锁是悲观的认为多个线程在并发访问共享数据时都会造成数据安全问题,所以每次对数据的操作都要加锁;主要是依赖数据库的锁机制来进行实现的,例如,关系型数据库中的 SELECT ... FOR UPDATE 语句。—乐观锁就是乐观的认为大部分情况下访问数据都不会发生冲突,因此不会事先对数据进行加锁,而是在实际访问的时候检查是否存在冲突;主要是通过时间戳、数据版本机制来实现的,当访问的数据需要更新时,会检查版本号是否发生变化来确定是否在此期间有其它线程已经访问修改过了,如果是则需要重新获取数据;

2、使用原子操作:使用java.util.concurrent.atomic包中的原子类,来保证库存修改的原子性(原子类对象.compareAndSet())。

3、限流和队列、分布式锁

七、MyBatis中#号和$号占位符

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值