java相关面试题

https://blog.csdn.net/qq_36135335/article/details/81535874

ArrayList与LinkedList区别?

ArrayList底层基于数组实现,由于数组在创建时长度(容量)固定,导致每次往数组里面添加时为了避免出现索引越界,会判断添加数据后数组是否需要扩容,而扩容就是创建一个新的容量为原来数组1.5倍数组,将原来数组的数据复制到新数组,这样会消耗性能,又数组有索引,因此ArrayList在进行大量查询的时候效率较高,进行添加时效率较低。

LinkedList底层基于双向链表实现,在进行添加操作时只需要创建新的节点放在链表头或则尾,在进行查找数据时需要遍历所有数据,因此在进行大量操作时LinkedList添加效率较高,查询效率较低。 

 

 

HashSet与TreeSet如何去重?

TreeSet通过是实现Compareable接口重写里面的compareTo方法进行去重,如果compareTo方法返回0,说明是重复的,如果是负数,则往前面排,如果是正数,往后面排; 还可以使用Comparetor定制比较器进行去重,一般使用这个,因为代码耦合度较低

HashSet底层数据结构是哈希表(是一个元素为链表的数组) ,哈希表底层依赖两个方法:hashCode()和equals()

1)首先比较哈希值是否相同 ,如果相同执行2),如果不同执行3)

2)继续执行equals()方法 返回true:元素重复了,不添加 返回false:直接把元素添加到集合

3)就直接把元素添加到集合

 

List与Set的遍历方式有哪些?

List遍历方式有:for循环、foreach、迭代器

Set遍历方式有:foreach、迭代器

 

 

HashMap  HashTable ConcurrentHashMap Properties区别?

hashmap线程不安全,效率高,可以使用null作为键或值,这样为null的键只有一个,可以有多个键对应为null的值;hashmap在多线程环境中,需要手动实现同步机制

hashtable线程安全,效率低,不能使用null作为键或值,hashtable的方法是同步的(synchronized),可以直接用在多线程环境中

ConcurrentHashMap线程安全,效率相对较高,不能使用null作为键或值;

关于锁机制:ConcurrentHashMap提供了和Hashtable以及SynchronizedMap中所不同的锁机制。Hashtable中采用的锁机制是一次锁住整个hash表,从而同一时刻只能由一个线程对其进行操作;
而ConcurrentHashMap中则是一次锁住一个桶。ConcurrentHashMap默认将hash表分为16个桶,诸如get,put,remove等常用操作只锁当前需要用到的桶。
这样,原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的。

Properties是HashTable 的子类 ,主要用于读取配置文件,且只能存String类型,它通过List 方法 Load方法进行读取和写入

 

 

HashMap存值流程是怎样的? 取值过程是怎样的?

 存值(put方法)

1当往一个HashMap增加一个键值对时,会先调用键这个对象的hashcode方法,得到一个hashcode值,然后根据这个值来计算出这个键值对所在的数组下标

2)判断该下标数组存的引用是否为null,如果是,直接把对象存到数组里,存储结束;如果不是,转到3)步

3)判断已存在对象的key的equals方法,跟需要添加的对象的key对比,是否为true,如果是,覆盖这个key所对应的value,存储结束;如果不是,获取到已存在对象的next成员变量的引用,转4)步

4)判断这个next所指向的值是不是为空,如果不为空,重复3)的过程;如果为空,则把next引用指向要增加的对象

取值(get方法)

1)调用key的hashcode方法,根据返回值定位到map里数组对应的下标

2)判断这个数组下标是不是指向了null,如果是,返回null;如果不是,转3)步

3)判断这个引用对应对象的key值的equals方法,跟查询的key值对比,判断是否为true,如果是,返回这个对象的value值;如果不是,转4)步

4)判断这个引用对应对象的next指的是不是null,如果是null,返回null;如果不是,取出这个next对象,重复3)步

 

 

线程与进程区别?

进程是并发执行的程序在执行过程中分配和管理资源的最小单位。

线程是进程的一个执行单元,是进程内科调度实体。比进程更小的独立运行的最小单位。线程也被称为轻量级进程。一个程序至少一个进程,一个进程至少一个线程。

 

 

线程的创建方式有哪些?

1)继承Thread类创建线程

2)实现Runnable接口创建线程

3)使用Callable和Future创建线程

 

 

线程的状态有哪些?常用的线程方法有哪些?

1、新建状态(New):新创建了一个线程对象。

2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于“可运行线程池”中,变得可运行,只等待获取CPU的使用权。即在就绪状态的进程除CPU之外,其它的运行所需资源都已全部获得。

3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。

4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。

5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

其中阻塞状态分为

1)、等待阻塞:运行的线程执行wait()方法,该线程会释放占用的所有资源,JVM会把该线程放入“等待池”中。进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒,

2)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入“锁池”中。

3)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

 

 

 

线程安全问题是什么?如何解决?

线程安全指多个线程访问同一个资源

解决方案:

同步代码块,将操作共享数据的代码放在 synchronized 里面

同步方法,将操作共享数据的代码抽取出来放到一个方法里面

Lock锁

https://blog.csdn.net/RookiexiaoMu_a/article/details/88623375

 

 

 

乐观锁怎么实现?

相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做。可以使用CAS实现

CAS是项乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。CAS是一种非阻塞式的同步方式

CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。(在 CAS 的一些特殊情况下将仅返回 CAS 是否成功,而不提取当前值。)CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。”这其实和乐观锁的冲突检查+数据更新的原理是一样的

https://blog.csdn.net/caisongcheng_good/article/details/79916873

 

 

ThreadLocal如何保证线程安全?

为每个线程创建一个变量副本  达到变量隔离效果

以当前的线程为key,将value值存在一个map中

获取的时候也以当前的线程key去找到对应的map 然后获取到对应的值

 

 

notify与notifyall区别?

notify只会唤醒一个线程,但是它不能保证哪个线程会被唤醒,这取决于线程调度器

notifyAll方法将唤醒所有线程,但是在执行剩余的代码之前,所有被唤醒的线程都将争夺锁定,这就是为什么在循环上调用wait,因为如果多个线程被唤醒,那么线程是将获得锁定将首先执行,它可能会重置等待条件,这将迫使后续线程等待

 

 

sleep 与wait区别?

sleep方法属于Thread类中的,wait方法属于Object类

在调用sleep()方法的过程中,线程不会释放对象锁。而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备

sleep可以在任何地方使用,而wait只能在同步方法或者同步块中使用

 

 

run方法与start方法区别

start方法用来启动一个线程,此时该线程处于就绪状态,而不是运行状态,也就意味着这个线程可以被JVM来调度执行。在调度过程中,JVM通过调用线程类的run方法来完成实际的操作,当run方法结束后,此线程就会终止。

也就是start方法创建线程,run执行线程

 

 

synchronized与lock区别?

Lock是一个接口,而synchronized是Java中的关键字,它们都是悲观锁

1)Lock是显式锁,需要手动开启和关闭,不然容易造成线程死锁,一般会在finally中进行释放lock锁,synchronized是隐式锁,出了作用域自动释放。

2)Lock只有代码块锁,而synchronized既有代码块锁,也有方法锁。

3)Lock锁性能更好,JVM花费少量的时间来调度线程。

 

 

防止表单重复提交

https://www.cnblogs.com/huanghuizhou/p/9153837.html

 

 

线程池和jvm

 

线程池的作用是什么?

线程池是一种多线程任务处理形式,在处理多线程的过程中先将任务添加队列,然后在创建线程后自动启动这些任务,每个线程都使用默认的堆栈大小,以默认的优先级运行,并处在多线程单元中,如果某个线程在托管代码中空闲,则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后辅助线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才能启动

java中的线程池:

1.newCachedThreadPool创建一个可缓存线程池程

2.newFixedThreadPool 创建一个定长线程池

3.newScheduledThreadPool 创建一个定长线程池

4.newSingleThreadExecutor 创建一个单线程化的线程池

 

 

线程池的创建方式?

1.newCachedThreadPool创建一个可缓存线程池程

2.newFixedThreadPool 创建一个定长线程池

3.newScheduledThreadPool 创建一个定长线程池

4.newSingleThreadExecutor 创建一个单线程化的线程池

 

 

线程池的拒绝策略是什么样的?

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 (默认策略)

ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。如果线程队列已满,则后续提交的任务都会被丢弃,且是静默丢弃。

ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务

ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务

 

 

shutdown与shutdownnow 区别?

shutdown只是将线程池的状态设置为SHUTWDOWN状态,正在执行的任务会继续执行下去,没有被执行的任务不再被执行。

shutdownNow则是将线程池的状态设置为STOP,正在执行的任务则被停止,没被执行任务的则返回。

 

 

JVM的组成有哪些?

类加载器,在 JVM 启动时或者类运行时将需要的 class 加载到 JVM 中

执行引擎,负责执行 class 文件中包含的字节码指令,相当于实际机器上的 CPU

内存区,将内存划分成若干个区以模拟实际机器上的存储、记录和调度功能模块,如实际机器上的各种功能的寄存器或者 PC 指针的记录器等

本地方法调用,调用 C 或 C++ 实现的本地方法的代码返回结果

 

 

类加载的过程是怎样的?每一步的作用是什么?

过程:加载(Load),连接(Link)、初始化(Initialize)

加载:程序运行之前jvm会把编译完成的.class二进制文件加载到内存,供程序使用,用到的就是类加载器classLoader ,所以java程序的运行并不是直接依靠底层的操作系统,而是基于jvm虚拟机。如果没有类加载器,java文件就只是磁盘中的一个普通文件。

连接

  验证:确保类加载的正确性。一般情况由javac编译的class文件是不会有问题的,但是可能有人的class文件是自己通过其他方式编译出来的,这就很有可能不符合jvm的编 译规则,这一步就是要过滤掉这部分不合法文件 

  准备:为类的静态变量分配内存,将其初始化为默认值 。我们都知道静态变量是可以不用我们手动赋值的,它自然会有一个初始值 比如int 类型的初始值就是0 ;boolean类型初始值为false,引用类型的初始值为null 。 这里只是为静态变量分配内存,此时是没有对象实例的 

  解析:把类中的符号引用转化为直接引用。比如在方法A中使用方法B,这里的B就是符号引用,需要将其转化成指向B的地址 

初始化:为类的静态变量赋予正确的初始值,上述的准备阶段为静态变量赋予的是虚拟机默认的初始值,此处赋予的才是程序编写者为变量分配的真正的初始值

 

 

 

什么是双亲委派机制?

双亲委派机制是指当一个类加载器收到一个类加载请求时会把请求委派给父类的类加载器,直到父类加载器找不到指定类时,子类加载器才会尝试自己去加载。

 

 

 

运行时数据区有哪些组成?

程序计数器

Java虚拟机栈

本地方法栈

方法区

https://blog.csdn.net/start_lie/article/details/81192962

      

 

jdk1.7和jdk1.8有什么区别?

1.7版本在项目中用的较少
1.1二进制变量的表示,支持将整数类型用二进制来表示,用0b开头。
1.2 Switch语句支持string类型
1.3 Try-with-resource语句 

JDK1.8的新特性
一、接口的默认方法
Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可,这个特征又叫做扩展方法。


二、Lambda 表达式
在Java 8 中你就没必要使用这种传统的匿名对象的方式了,Java 8提供了更简洁的语法,lambda表达式:
Collections.sort(names, (String a, String b) -> {
         return b.compareTo(a);
});


三、函数式接口
Lambda表达式是如何在java的类型系统中表示的呢?每一个lambda表达式都对应一个类型,通常是接口类型。而“函数式接口”是指仅仅只包含一个抽象方法的接口,每一个该类型的lambda表达式都会被匹配到这个抽象方法。因为 默认方法 不算抽象方法,所以你也可以给你的函数式接口添加默认方法。

 
四、方法与构造函数引用
Java 8 允许你使用 :: 关键字来传递方法或者构造函数引用,上面的代码展示了如何引用一个静态方法,我们也可以引用一个对象的方法:
converter = something::startsWith;

String converted = converter.convert("Java");

System.out.println(converted);


五、Lambda 作用域
在lambda表达式中访问外层作用域和老版本的匿名对象中的方式很相似。你可以直接访问标记了final的外层局部变量,或者实例的字段以及静态变量。


六、访问局部变量
可以直接在lambda表达式中访问外层的局部变量:


七、访问对象字段与静态变量 
和本地变量不同的是,lambda内部对于实例的字段以及静态变量是即可读又可写。该行为和匿名对象是一致的:

 

 

 

堆的作用是什么?由哪些组成?

堆是垃圾回收主要的区域,这个区域占jvm内存最大。

堆空间一般分成三部分,这三部分用来存储三类数据:新生代、老年代、永久代。

新生代:存放的是新创建的对象

老年代:存放已经存在有一定时间的对象

永久代是一些静态文件,这些对象的特点是 不需要垃圾回收,永远存活 。形象点描述这块区域为:永久代 。(不过在 Java 8 里已经把 永久代 删除了,把这块内存空间给了 元空间)

 

 

怎么判断对象失去引用?

1.引用计数法
给对象增加一个引用计数器,每当有一个地方引用它时,计数器就+1,当引用失效,计数器就-1,任何时刻计数器为0的对象就表示不能再被使用的,即“对象已死”。

引用计数法实现简单,效率也比较高,但是在主流的JVM中未用选用引用计数法来管理内存,主要原因是因为引用计数法无法解决对象的循环引用问题。

2.可达性分析算法

Java采取了“可达性分析”来判断对象是否存活,同样采用此算法的还有C#

通过一系列称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径,称之为“引用链”,当一个对象到GC Roots没有任何的引用链相连时(从GC Roots到这个对象不可达)时,证明此对象为不可用。


3.生存还是死亡

https://blog.csdn.net/C_U_N_Z/article/details/99450630

https://blog.csdn.net/u012998254/article/details/81428621

 

 

垃圾回收算法有哪些?

1.标记-清除算法

标记-清除算法容易产生内存碎片,导致不连续的空间,碎片太多可能会导致后续过程中需要为大对象分配空间时无法找到足够的空间而提前触发新的一次垃圾收集动作。

2.复制算法

将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题

但是此算法对内存空间的使用做出了高昂的代价,因为能够使用的内存缩减到原来的一半。很显然,Copying算法的效率跟存活对象的数目多少有很大的关系,如果存活对象很多,那么Copying算法的效率将会大大降低。

3.标记整理

内存利用率高 空间连续 但是效率相对较低

4.分代算法

根据堆中新生代和老年代的特点选择不同的算法,新生代一般采用复制算法  老年代采用标记清除和标记整理两种

 

 

 

适合垃圾回收时间有哪些?

安全点:在运行中的线程执行完代码以后回收垃圾

安全区:一段连续的安全点  比如阻塞的线程 , 阻塞的这段时间就是安全的 就可以垃圾回收

 

 

 

垃圾收集器G1是怎样的?

G1垃圾收集器是服务端的垃圾回收器,它的应用目标主要是在多CPU和大内存的应用场景中,旨在大多数情况下,在可预测的垃圾回收时间内,获得更大的吞吐量为目标,G1 的设计场景主要是: 

(1)应用在高并发应用程序中像CMS垃圾收集器一样,快速响应 
(2)在整理内存碎片空间上花费更短的停顿时间 
(3)可预测的GC停顿时间 
(4)更高的吞吐量 
(5)小内存的Java堆也可以用G1 

https://blog.csdn.net/u010454030/article/details/51755071

 

 

常用的jvm优化工具有哪些?默认的堆得大小是多少?

1.jps(jvm process status) 查看当前java虚拟机中运行的进程

2. jstat(jvm statistics monitoring tool)用于监视虚拟机信息

3.jmap(memor for java)查看内存信息

4.jvisualvm、jconsole

默认最小 是256,最大是 1024m

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值