Java面试常问问题及答案(非常详细)

您所在的位置:网站首页 后端开发面试常问问题 Java面试常问问题及答案(非常详细)

Java面试常问问题及答案(非常详细)

2023-11-04 12:12| 来源: 网络整理| 查看: 265

一:java基础1.简述string对象,StringBuffer、StringBuilder区分string是final的,内部用一个final类型的char数组存储数据,它的拼接效率比较低,实际上是通过建立一个StringBuffer,让后台调用append(),最后再将StringBuffer toSting(),每次操作Sting 都会重新建立新的对象来保存新的值.这样原来的对象就没用了,就要被垃圾回收.这也是要影响性能的。StringBuffer也是final,线程安全的,中采用一个char数组来保存需要append的字符串,char数组有一个初始大小,当append的字符串长度超过当前char数组容量时,则对char数组进行动态扩展,也即重新申请一段更大的内存空间,然后将当前char数组拷贝到新的位置,因为重新分配内存并拷贝的开销比较大,所以每次重新申请内存空间都是采用申请大于当前需要的内存空间的方式,这里是2倍。StringBuilder,线程不安全。

2.多态的原理多态就是:允许基类的指针或引用指向派生类的对象,而在具体访问时实现方法的动态绑定。原理是java的后期绑定。

3.简要描述面向对象编程的思想抽象:通过特定的实例抽取出共同的特征以后形成的概念的过程,它强调主要特征和忽略次要特征。封装:把对象的属性和方法结合成一个独立的整体,隐藏实现细节,并提供对外访问的接口。继承:从已知的一个类中派生出新的一个类,叫子类。子类实现了父类所有非私有化属性和方法,并能根据自己的实际需求扩展出新的行为。多态:多个不同的对象对同一消息作出响应,同一消息根据不同的对象而采用各种不同的行为方法。

4.反射的原理java虚拟机运行时内存有个叫方法区,主要作用是存储被装载的类的类型信息。每装载一个类的时候,java就会创建一个该类的Class对象实例。我们就可以通过这个实例,来访问这个类的信息。

5.代理的作用和实现代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。 java的静态代理和动态代理(略)

6.hashcode和equals的用法Java对于eqauls方法和hashCode方法是这样规定的:如果两个对象相同,那么它们的hashCode值一定要相同;如果两个对象的hashCode相同,它们并不一定相同(上面说的对象相同指的是用eqauls方法比较。) 一般在覆盖equals()方法的同时也要覆盖hashCode()方法,否则,就会违反Object.hashCode的通用约定,从而导致该类无法与所有基于散列值(hash)集合类(HashMap、HashSet和Hashtable)结合在一起正常运行。

7.set,map,list的区别(尽量详细)map:hashmap:链地址法,大概思路:通过取key的hashCode值、高位运算、取模运算计算位置,插入位置是通过hascode和eques方法判断key是否一致①.判断键值对数组table[i]是否为空或为null,否则执行resize()进行扩容;②.根据键值key计算hash值得到插入的数组索引i,如果table[i]==null,直接新建节点添加,转向⑥,如果table[i]不为空,转向③;③.判断table[i]的首个元素是否和key一样,如果相同直接覆盖value,否则转向④,这里的相同指的是hashCode以及equals;④.判断table[i] 是否为treeNode,即table[i] 是否是红黑树,如果是红黑树,则直接在树中插入键值对,否则转向⑤;⑤.遍历table[i],判断链表长度是否大于8,大于8的话把链表转换为红黑树,在红黑树中执行插入操作,否则进行链表的插入操作;遍历过程中若发现key已经存在直接覆盖value即可;⑥.插入成功后,判断实际存在的键值对数量size是否超多了最大容量threshold,如果超过,进行扩容。treemap:TreeMap实现SortedMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。如果使用排序的映射,建议使用TreeMap。在使用TreeMap时,key必须实现Comparable接口或者在构造TreeMap传入自定义的Comparator,否则会在运行时抛出java.lang.ClassCastException类型的异常。linkedHashMap:保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的,也可以在构造时带参数,按照访问次序排序。HashTable:因为内部是采用synchronized来保证线程安全的CocurrentHashMap:利用锁分段技术增加了锁的数目,从而使争夺同一把锁的线程的数目得到控制。

set:HashSet:内部new了一个hashMap,添加时key放数据,value放一个内部定义的final的Object对象LinkedHashSet:内部new了一个linkHashMap,添加时key放数据,value放一个内部定义的final的Object对象,遍历时有序TreeSet:内部new了一个TreeMap,添加时key放数据,value放一个内部定义的final的Object对象.list:arraylist和linkedList(略)copywriteList:CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里

8.Integer demo1 = 100;int demo2 = 100;

Integer demo3 = 10000;int demo4 = 10000;

Integer demo5 = 100;Integer demo6 = 100;

Integer demo7 = 10000;Integer demo8 = 10000;

int demo10=10000;int demo9=10000;System.out.println(demo1==demo2);System.out.println(demo3==demo4);System.out.println(demo5==demo6);System.out.println(demo7==demo8);System.out.println(demo9==demo10);结果:true 自动拆箱装箱 比大小true 自动拆箱装箱 比大小true java常量池缓存一个引用false 两个引用true 比大小

9.快速失败和安全失败快速失败和安全失败是对迭代器而言的。 快速失败:当在迭代一个集合的时候,如果有另外一个线程在修改这个集合,就会抛出ConcurrentModification异常,java.util下都是快速失败。 安全失败:在迭代时候会在集合二层做一个拷贝,所以在修改集合上层元素不会影响下层。在java.util.concurrent下都是安全失败

二:java多线程1.实现多线程的方式继承Thread类,重写run方法实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread构造函数的target通过Callable和FutureTask创建线程通过线程池创建线程

2.多线程的状态、流程图

3.线程池ThreadPoolExecutor构造方法参数讲解参数名 作用corePoolSize 队列没满时,线程最大并发数maximumPoolSizes 队列满后线程能够达到的最大并发数keepAliveTime 空闲线程过多久被回收的时间限制unit keepAliveTime 的时间单位workQueue 阻塞的队列类型RejectedExecutionHandler 超出 maximumPoolSizes + workQueue 时,任务会交给RejectedExecutionHandler来处理文字描述corePoolSize,maximumPoolSize,workQueue之间关系。当线程池中线程数小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。当线程池中线程数达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行 。当workQueue已满,且maximumPoolSize > corePoolSize时,新提交任务会创建新线程执行任务。当workQueue已满,且提交任务数超过maximumPoolSize,任务由RejectedExecutionHandler处理。当线程池中线程数超过corePoolSize,且超过这部分的空闲时间达到keepAliveTime时,回收这些线程。当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize范围内的线程空闲时间达到keepAliveTime也将回收。

newCachedThreadPool:无限大小线程池。重用之前已构造的可用线程,如果不存在可用线程,那么会重新创建一个新的线程并将其加入到线程池中。如果线程超过 60 秒还未被使用,就会被中止并从缓存中移除。因此,线程池在长时间空闲后不会消耗任何资源。FixedThreadPool:复用固定数量的线程 处理一个 共享的无边界队列 。任何时间点,最多有 nThreads 个线程会处于活动状态执行任务。如果当所有线程都是活动时,有多的任务被提交过来,那么它会一致在队列中等待直到有线程可用。如果任何线程在执行过程中因为错误而中止,新的线程会替代它的位置来执行后续的任务。SingleThreadPool 是通过 java.util.concurrent.Executors 创建的 ThreadPoolExecutor 实例。这个实例只会使用单个工作线程来执行一个无边界的队列。(注意,如果单个线程在执行过程中因为某些错误中止,新的线程会替代它执行后续线程)

4.多线程yield()、sleep()、wait()、join()wait()和notify/notifyAll方法属于object方法。wait():wait方法依赖于同步,wait方法则需要释放锁。yield()、sleep(),join()属于Thread类方法。sleep():可以直接调用。而更深层次的区别在于sleep方法只是暂时让出CPU的执行权,并不释放锁。yield():yield方法的作用是暂停当前线程,以便其他线程有机会执行,不过不能指定暂停的时间,并且也不能保证当前线程马上停止。yield方法只是将Running状态转变为Runnable状态join:join方法就是通过wait方法来将线程的阻塞,如果join的线程还在执行,则将当前线程阻塞起来,直到join的线程执行完成,当前线程才能执行。不过有一点需要注意,这里的join只调用了wait方法,却没有对应的notify方法,原因是Thread的start方法中做了相应的处理,所以当join的线程执行完成以后,会自动唤醒主线程继续往下执行

5.synchronized原理synchronized 依赖于软件层面jvm每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:1、如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。2、如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.3.如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。Java SE1.6为了减少获得锁和释放锁所带来的性能消耗,引入了“偏向锁”和“轻量级锁”,

6.lock原理lock无关JVM实现的,依赖于硬件层面,使用CLH对列(什么前驱,尾部自旋,没记住)实现。原生的CLH队列是用于自旋锁,但Doug Lea把其改造为阻塞锁。

7.synchronized和lock区别使用上:synchronized 在成功完成功能或者抛出异常时,虚拟机会自动释放线程占有的锁;而Lock对象在发生异常时,如果没有主动调用unLock()方法去释放锁,则锁对象会一直持有,因此使用Lock时需要在finally块中释放锁;lock接口锁可以通过多种方法来尝试获取锁包括立即返回是否成功的tryLock(),以及一直尝试获取的lock()方法和尝试等待指定时间长度获取的方法,相对灵活了许多比synchronized;写锁来提高系统的性能,因为读锁是共享锁,即可以同时有多个线程读取共享资源,而写锁则保证了对共享资源的修改只能是单线程的。lock可以实现公平锁

原理上:见上面

8.JUC并发包基本类Atomic原子类分类 :原子更新基本类型(如AtomicInteger ):CAS操作来保证操作的原子性。原子更新数组类型(如AtomicLongArray ):CAS操作来保证操作的原子性。原子更新引用类型(如AtomicReference):

并发容器类:ConcurrentHashMap:分段锁机制保证并发性CopyOnWriteArrayList:CopyOnWriteArrayList的缺点,就是修改代价十分昂贵,每次修改都伴随着一次的数组复制;但同时优点也十分明显,就是在并发下不会产生任何的线程安全问题,也就是绝对的线程安全,类似读写分离和最终一致性(数据并不是最新)

特殊功能类:CountDownLatch:(减计数方式)一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1。当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。CyclicBarrier(加计数方式):计数达到指定值时释放所有等待线程Semaphore:翻译成字面意思为 信号量,Semaphore可以控同时访问的线程个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。ThreadLocal:为每个线程提供一个独立的变量副本

锁类:ReentrantLock(可重入锁):可以有公平锁和非公平锁、尝试获得锁(tryLock())等优点Condition:Lock类可以创建Condition对象,Condition对象用来是线程等待和唤醒线程,需要注意的是Condition对象的唤醒的是用同一个Condition执行await方法的线程,所以也就可以实现唤醒指定类的线程ReadWriteLock:读读共享,写写互斥,读写互斥

其他:BlockQueue:同步实现的队列

线程池类:见上面介绍。

9.blockQueue 阻塞队列

class BoundedBuffer {final Lock lock = new ReentrantLock();final Condition notFull = lock.newCondition(); final Condition notEmpty = lock.newCondition();

final Object[] items = new Object[100];int putptr, takeptr, count;

public void put(Object x) throws InterruptedException {lock.lock();try {while (count == items.length) notFull.await();items[putptr] = x; if (++putptr == items.length) putptr = 0;++count;notEmpty.signal();} finally {lock.unlock();}}

public Object take() throws InterruptedException {lock.lock();try {while (count == 0) notEmpty.await();Object x = items[takeptr]; if (++takeptr == items.length) takeptr = 0;--count;notFull.signal();return x;} finally {lock.unlock();}} }

三:数据库(这里主要看mysql innoDB)1.mysql引擎类别MyISAM:不支持事务,表锁,不支持外键,非聚集索引,数据文件是分离的,适合大量读应用InnoDB:支持事务,支持外键,表锁和行锁、聚集索引,数据文件是和索引绑在一起的,必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。其他:NDB和Memory(表中的数据存放在内存中)、Maria(新开发的引擎,设计目标主要是用来取代原有的MyISAM存储引擎)

2.InnoDB表锁和行列锁的区别表锁:略行锁:大体两种:共享锁和排它锁共享锁:允许事务读取一行数据。排它锁:允许事务删除或者更新一条数据。行锁是通过给索引上的索引项加锁来实现的使用:由于InnoDB预设是Row-Level Lock,所以只有「明确」的指定主键,MySQL才会执行Row lock (只锁住被选取的资料例) ,否则MySQL将会执行Table Lock (将整个资料表单给锁住)。

3.mysql索引对于innodb来说使用上:主键索引、唯一索引、普通索引、全文索引(高版本开始支持)、组合索引原理上:b+树,自适应hash索引,聚集索引(主键默认为辅助索引,如果没有主键,自动选取一个合适的列建立,存储索引和行数据)、辅助索引(存储所在列数据和对应聚合索引位置)使用1:组合索引左匹配原则使用2:mysql EXPLAIN 命令id: SELECT 查询的标识符. 每个 SELECT 都会自动分配一个唯一的标识符.select_type: SELECT 查询的类型.table: 查询的是哪个表partitions: 匹配的分区type: join 类型possible_keys: 此次查询中可能选用的索引key: 此次查询中确切使用到的索引.ref: 哪个字段或常数与 key 一起被使用rows: 显示此查询一共扫描了多少行. 这个是一个估计值.filtered: 表示此查询条件所过滤的数据的百分比extra: 额外的信息

4.索引失效常见情况使用函数、左匹配、like,or(必须所有的or条件都必须是独立索引),is null,等

5.事务四大属性ACID即事务的原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability).。事务隔离级别: 脏读:指一个事务读取了另外一个事务未提交的数据。不可重复读:在一个事务内读取表中的某一行数据,多次读取结果不同幻读:幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。数据库隔离级别:① Serializable (串行化):可避免脏读、不可重复读、幻读的发生。② Repeatable read (可重复读):可避免脏读、不可重复读的发生。③ Read committed (读已提交):可避免脏读的发生。④ Read uncommitted (读未提交):最低级别,任何情况都无法保证。

6.如何优化一条sql第一步:理解sql业务,逻辑上是否可以优化第二部:explain sql执行计划,目测有没有该走的索引没有走第三部:查看有无常见影响效率的错误,如null,列上使用函数,or不走组合索引,join之前减少join列数第四部:增加合适的索引第五步:还不行,尝试限制需求(如只可以统计指定范围数据)或者其他方式实现(如定时任务定时分析一批)第六步:数据量超大,只可以考虑分表分库等手段了。

四:数据结构与算法(这里是部分,思想一样)1.排序(手写部分排序算法)冒泡排序,插入排序,快排。冒泡:两个循环插入:从第二个开始,替换快排:

2.java实现栈和队列栈:链表实现,插入头部插入,删除头部删除队列:链表实现,插入头部插入,出队列尾部去除

3.java遍历二叉树第一个:前序遍历(中后遍历类似):private void qianOut(TwoTreeDemo headNode) {if(headNode!=null){System.out.print(headNode.value+",");if(headNode.leftNode!=null){qianOut(headNode.leftNode);}if(headNode.rightNode!=null){qianOut(headNode.rightNode);}}}

4.链表常见相关算法(提示)第一个:O(1)时间删除指定节点:将该节点的值和next替换为下一节点的,并删除下一节点第二个:查找链表的倒数第k个节点、链表中间值、两个链表重复处:快慢指针法(见底部附注)private NodeDemo getk(NodeDemo headNode,int k) {if(headNode==null || k=k){tweNode = tweNode.nextNode;}i++;}if(i适配器->dst类对象适配器模式适配器拥有src类的实例,实现dst类的接口

桥接模式类似接口和实现类

过滤器模式一个过滤维度写一个过滤类,然后写一个与类,写一个或类 扩展时扩展维度就可以

装饰器模式装饰对象接受所有客户端的请求,并把这些请求转发给真实的对象。这样,就能在真实对象调用前后增加新的功能。

代理模式,代理类和被代理类实现同一个接口被代理类中拥有一个代理类的实例

行为型模式:关注对象之间的通信如:责任链模式,迭代器模式,命令模式责任链模式类似拦截器等

命令模式:将调用者 被调用者 被调用者的行为分开

解释器模式:每个语法为一个解释器类

迭代器模式:将集合类的查看遍历和增删改行为分离

附注:1)transient:防止某个属性被序列化2)CAS算法:有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。这里可能有ABA问题以及如何避免ABA(用版本号)3)快慢指针法:设置两个指针确认接受此offer

转至http://blog.51cto.com/13175699/2108660



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3