java面试准备2
迪丽瓦拉
2024-05-31 16:25:55
0

值传递和引用传递

值传递:是指在调用函数时将实际参数复制一份传递到函数中去,这样在函数中如果对参数进行修改,将不会影响到实际参数,。
引用传递:是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递

是值传递。Java语言的方法调用只支持参数的值传递。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性可以再被调用的过程中被改变,但是对该对象引用的改变是不会影响到调用者的。

Java中方法参数的使用情况

(1)一个方法不能修改一个基本数据类型的参数(即数值型或者布尔型)
(2)一个方法可以改变一个对象参数的状态
(3)一个方法不能让对象参数引用一个新的对象

BIO,NIO,AIO有什么区别

(1)BIO(Blocking I/O):同步阻塞I/O模式,数据的读取写入必须在一个线程内等待其完成,在活动连接数不是特别高(小于单机1000)的情况下,这种模型是不错的,可以让每一个连接专注于自己的I/O并且编程模型简单,也不用过多考虑系统的过载、限流等问题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或者请求。但是,当面对十万甚至百万级别连接的时候,传统的BIO模型是无能为力的。因为,我们需要一种更加高效的I/O处理模型来应对更高的并发量。
(2)NIO(NEW I/O):NIO是一种同步非阻塞的I/O模型,NIO可以被理解为Non-blocking,它支持面向缓冲,基于通道的I/O操作方法。对于高负载、高并发的应用,应该使用NIO的非阻塞模式来开发。
(3)AIO(Asynchronous I/O):它是异步非阻塞的IO模型。异步IO是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会阻塞在那里,当后台处理完成,操作系统会通知队形的线程进行后续的操作。

什么是反射机制

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性。这种动态获取的信息以及动态调用对象的方法称为反射。

静态编译和动态编译

静态编译:在编译时确定类型,绑定对象
动态编译:在运行时确定类型,绑定对象

反射机制的应用场景有哪些

(1)我们在使用JDBC连接数据库时使用Class.forName()通过反射加载数据库的驱动程序;
(2)Spring框架使用很多反射机制,经典的就是xml配置模式装载Bean的过程。将程序内的所有xml或者properties配置文件加载进入内存中。java类里面解析xml或者properties里面的内容得到队形实体类的字节码字符串以及相关的属性信息。使用反射机制,根据这个字符串获取某个类的Class实例。

什么是字符串常量池

字符串常量池位于堆内存中,专门用来存储字符串常量,可以提高内存的使用率,避免开辟多块空间存储相同的字符串,在创建字符串的的时候jvm会首先检查字符串常量池,如果该字符串已经存在于池中,则返回它的引用,如果不存在,则实例化一个字符串放到池中,并返回其引用。

String有哪些特性

不变性:String是只读字符串,对它进行任何操作,其实都是创建一个新的对象,再把引用指向该对象。不变模式的主要作用在于当一个对象需要被多线程共享频繁访问时,可以保证数据的一致性。
常量池优化:String对象创建之后,会在字符串常量池中进行缓存,如果下次创建相同的对象时,会直接返回缓存的引用。
final:使用final来定义String类,表示String不能被继承,提高了系统的安全性。

在使用HashMap的时候,用String做key有什么好处?

HashMap内部实现是通过key的hashcode来确定value的存储位置,因为字符串是不可变的,所有创建字符串的时候,它的hashcode被缓存下来,不需要再次计算,所以相比于其它对象更快。

String和StringBuffer、StringBuilder的区别是什么?

可变性:String中使用字符数组来保存字符串,private、final、char value[],所以String对象是不可变的。StringBuilder与StringBuffer都继承自AbstractStringBuilder类。这两种对象都是可变的。String对象是不可变的,也就可以理解为常量,线程安全。StringBuffer对方法加了同步锁或者对调用的方法加入了同步锁,所以是线程安全的。StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
性能:StringBuilder > StringBuffer > String

如果操作少量数据时使用String,单线程操作字符串缓冲区下大量数据使用StringBuilder,多线程操作字符串缓冲区下大量数据使用StringBuilder

Integer a = 127和Integer b = 127相等吗

对于对象引用类型: == 比较的对象的内存地址
对于基本数据类型:==比较的是值。如果整型字面量的值在-128到127之间,那么自动装箱不会new新的Integer对象,超过范围时a1 == b1返回的就是false;

Java集合的快速失败机制

是java集合的一种错误检测机制,当多个线程对集合进行结构上的改变操作时,有可能会产生fail-fase机制。
例如:假设存在两个线程(线程1、线程2),线程1通过Iterator在遍历集合A中的元素,在某个时间线程2修改了集合A的结构,那么这个时候程序就会抛出ConcurrentModificationException异常,从而产生fail-fast机制。
原因:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个modCount变量。集合在被遍历期间如果内容发生变化,就会改变modCount值。每当迭代器使用hasNext()遍历下一个元素之前,都会检测modCount变量是否为exceptedmodCount值,是的话就返回遍历;否则抛出异常,终止遍历。

解决办法:
1.在遍历过程中,所有涉及到改变modCount值的地方全部加上synchronized。
2.使用CopyOnWriteArrayList来替换ArrayList。

怎样确保一个集合不能被修改

可以使用Collections.unmodificableCollection(Collection c)方法来创建一个只读集合,这样任何改变集合的操作都会抛出Java.lang.UnsupporedOperationException异常。

集合是数组的区别

数组是固定长度的;集合是可变长度的。
数组可以存储基本数据类型,也可以存储引用数据类型;集合只能存储引用数据类型。
数组存储的元素必须是同一个数据类型,集合存储的对象可以是不同的数据类型。

如何边遍历边删除Collection中的元素

唯一正确方式是使用Iterator.remove()方法

遍历一个List有哪些不同的方式?每种方式的实现原理是什么?

(1)for循环遍历,基于计数器。在集合外部维护一个计数器,然后一次读取每一个元素的位置。
(2)迭代器遍历,Iterator。Iterator是面向对象的一个设计模式,目的是屏蔽不同数据集合的特点,统一遍历集合的几口。
(3)foreach循环遍历。foreach内部也是采用了Iterator的方式实现,使用时不需要显示声明Iterator或计数器。优点是代码简洁,缺点是只能做简单的遍历,不能边遍历边操作集合,例如删除、替换。

ArrayList的优缺点

ArrayList的优点如下:
ArrayList底层以数组实现,是一种随机访问模式。ArrayList实现RandomAccess接口,因此查找速度很快。
ArrayList在顺序添加一个元素的时候很快。
缺点:
删除元素时,需要做一个元素复制操作,如果要复制的元素很多,就会比较耗性能。
插入元素的时候,也需要做一个元素复制操作,缺点同上。

说一下HashMap的实现原理

HashMap概述:HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选地映射操作,并且允许使用null值和null键。此类并不保证映射的顺序,特别是它不保证顺序恒久不变。
HashMap的数据结构:在java编程语言中,基本的数据结构就是两种,一个是数组,另一个是模拟指针(引用),所有数据结构都可以用这两种基本结构来进行构造,Hashmap也不例外。HashMap实际上是一个链表散列的数据结构,即数组和链表的集合。
HashMAP是基于Hash算法实现的:
(1)当我们向Hashmap中put元素的时候,利用key的hashcode重新hash出当前对象的元素在数组中的下标。
(2)存储时,如果出现hash值相同的key,此时有两种情况。如果key相同,此时覆盖原值。如果key不相同,也就是出现hash冲突,则将key-value放入链表中。
(3)获取时,之际找到hash值对应的下标,再进一步判断key是否相同,从而找到对象的值。
(4) 理解了以上过程就不难明白HashMap是如何解决hash冲突的问题,核心就是使用了数组的存储方式,然后将冲突的key的对象放入链表中,一旦发现冲突就在链表中做进一步的对比。

jdk1.8中对HashMap实现了优化,当链表中的节点超过8个之后,该联保转化为红黑树来提高查询效率,由曾经的O(N)转化为O(logn)

简单总结下HashMap是使用了哪些方法来有效解决哈希冲突的:

(1)使用链地址法(散列表)来链接具有相同哈希值的数据。
(2)使用二次扰动函数(hash函数)来降低哈希冲突的概率,使得数据分布更加均匀。
(3)当链表长度超过八个节点时,使用红黑树来降低时间复杂度,使得遍历更快。

HashMap与HashTable的区别

(1)线程安全:HashMap是非线程安全的,HashTable是线程安全的。
(2)效率: 因为线程安全的问题,HashMap 要比 HashTable 效率高一点。另外,HashTable 基本被淘汰,不要在代码中使用它;
(3) 对Null key 和Null value的支持: HashMap 中,null 可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为 null。但是在HashTable 中 put 进的键值只要有一个 null,直接抛NullPointerException。
(4)4. 初始容量大小和每次扩充容量大小的不同 : ①创建时如果不指定容量初始值,Hashtable 默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap 默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。②创建时如果给定了容量初始值,那么 Hashtable 会直接使用你
给定的大小,而 HashMap 会将其扩充为2的幂次方大小。也就是说 HashMap 总是使用2的幂作为哈希表的大小。
5. 底层数据结构: JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。Hashtable 没有这样的机制。

相关内容