banner
NEWS LETTER

java基础面试题

Scroll down

1.List和array(数组)区别

1)数组必须声明大小,集合不需要。
2)数组的大小是固定的,集合的大小是可以动态扩展的,空间不足时可以动态扩展。

2.http请求是一个进程还是一个线程

tomcat维护了一个线程池,每一个http请求,会从线程池取出一个空闲线程。

3.讲一下什么是值传递和引用传递?

1)值传递:方法接收的是实参值的拷贝,会创建副本
2)引用传递:方法接收的是实参所引用对象在堆 中的地址,不会创建副本吧,对形参的修改将影响到实参。

java中只有值传递,如果传递的参数是基本类型,传递的就是基本类型的字面量的拷贝,会创建副本。如果是引用类型,传递的就是实参在栈中引用的拷贝,也会拷贝副本。

4.自动拆箱、自动装箱的原理是什么?

自动拆箱实质就是调用包装类的xxxValue()方法啊,自动装箱实质是调用了包装类的valueOf()方法。

4.1包装类的缓存机制了解吗

包装类通过缓存机制来提高性能。Byte、Short、Integer、Long这四种包装类默认创建数值[-128,127]的缓存数据,Character默认缓存[0,127]的数据。

5.面向过程与面向对象的区别

1)面向过程把解决问题的过程拆成一个个方法,然后通过调用这些方法解决问题
2)面向对象先抽象出对象,然后通过调用对象的方法解决问题

5.1面向过程性能比面向对象性能高。

1)加载类和对象的实例化消化资源
2)java是半编译半解释形语言,先编译成字节码文件,在转换成机器代码。无法直接转换为机器代码。

6.接口和抽象类的区别

共同点
1)都不能被实例化
2)都可以包含抽象方法
3)都可以有实现的方法
区别
1)接口是对类行为的约束,抽象类是对代码的复用
2)一个类只能继承一个类,而可以实现多个接口,接口可以多继承
3)接口只能定义public final staitc类型的变量(常量)
4)抽象类可以有构造方法,接口不能有

7.深拷贝、引用拷贝与引用拷贝

1)浅拷贝:在拷贝一个对象时只对这个对象的基本类型的属性进行拷贝,引用类型的成员变量只进行引用的传递。
2)深拷贝:深拷贝除了对基本类型的属性进行拷贝,还会对引用类型进行拷贝。深拷贝的方法:序列化和对其引用类型clone()方法。
3)引用拷贝:使用两个不同引用指向对象。

8.String intern()的作用

尝试将这个字符串对象放入字符串常量池
1)JDK1.6

  • 如果字符串常量池中有,则并不会放入。返回已有的串池中的对象地址
  • 如果没有,则会把对象复制一份,放入串池,并返回串池的引用地址

2)JDK1.7

  • 如果串池中有,则并不会放入。返回已有的的串池中的对象地址
  • 如果没有,则会把对象的引用地址复制一份,放入串池,并返回串池中的引用地址

9.不借助三个变量交换两个值

int x = 1, y = 3;
x = x + y;
y = x - y;
x = x - y;

10.HashMap jdk8与jdk7的区别

1)jdk1.7
底层是数组加链表,通过hash()函数得到key哈希值后根据(n - 1) & hash判断当前元素在数组中存放的索引,遍历以这个元素为头结点的链表,依次和插入的 key 比较,相同就覆盖,不同就采用头插法插入元素;

2)jdk1.8
底层是数组加链表加红黑树。链表有一个阀值,当链表小于这个阀值时就遍历这个链表,依次比较,相同就覆盖,不同就插入到链表尾部。当链表长度大于阀值时会调用treeifyBin()方法,这个方法会在数组长度大于64时将链表转为红黑树。否则就rehash()扩容。

10.1 HashMap扩容流程

jdk1.8当链表长度大于阀值(默认为8),将链表转化为红黑树前会判断数组的长度是否小于64,如果小于则先将数组扩容,数组的默认大小为16,每次扩容容量变为之前的两倍。反之则转化为红黑树。

11.HashMap为什么引入红黑树

由于链表中查询的时间复杂度为O(n),红黑树的平均时间复杂度为O(logn)

12.哈希冲突的解决方法

1)开放定址法
在发生哈希冲突时找个空闲的地址存放元素。

  • 线性探测法:从发生冲突的地址开始,依次探测下一个地址,直到找到一个空闲地址为止
  • 平方探测法:从发生冲突的地址,分别从左边和右边跳跃式的找到空闲的地址

2)再哈希法:提供多个hash函数,发生哈希冲突时使用另一个hash函数
3)建立公共溢出区:将hash表分为基本表和溢出表两部分,凡是发生哈希冲突的元素一律放入溢出表
4)拉链法:数组与链表结合,数组中的每一个单位都是链表,发生哈希冲突则将元素加到链表上。

12.1 HashMap死循环问题

JDK1.7中rehash方法在并发中会形成一个循环列表。如果数组扩容,transfer()方法将原数组中所有的链表重新移到新的位置,并反转链表,假如有两个线程同时在执行这个函数并移动同一个链表,一个获取头结点后阻塞了,另一个线程执行完后,之前阻塞的线程又恢复了,当前线程获取的节点实质是新链表的尾结点,由于是头插法,然后会指向头节点,这样就形成了一个循环链表。调用get()函数获取一个不存在的key,且恰巧hash值与该链表的节点相同,在查找的过程中就会发生死循环。这个问题在JDK1.8解决了。

13.红黑树和平衡二叉树的区别

1)调整平衡的机制不同
二叉平衡树根据左右高度差不超过1的规则和旋转实现
红黑树是根据一系列规则和旋转实现
2)红黑树的插入效率更高
红黑树不追求完全的平衡,是非严格的平衡树,任何不平衡在三次内解决,从而提高了性能。
平衡二叉树是严格的平衡树,必须严格保证左右子树的高度差不超过1,在某些情况下,旋转的次数大于红黑树。
3)红黑树的统计性能比平衡二叉树强

14.BIO,NIO和AIO的区别

1)BIO是同步式阻塞IO,传统的IO。它一个线程只能处理一个连接请求。当线程执write或者read时,除非读完或者写完,在这个过程中不能执行其他的操作。而且BIO传输数据是单向传输的,如果要反向传输,还得创建一个IO流
2)NIO同步非阻塞式IO,NIO是一种可以多路复用的的IO,一个线程可以处理大量连接请求。它的非阻塞体现在当进行write或者read操作时候,如果不能立即执行就返回0,等待下一次操作。而且它的数据传输是双向的。
3)AIO异步式非阻塞式IO,可以认为是NIO的二代版本。

补充:
同步/非同步/阻塞/非阻塞的区别是什么
1)同步与异步与消息的通知机制有关。对于消息处理者,在同步情况下,消息处理者区自己去等消息处理是否返回;在异步的情况下,消息处理者不必区等待消息是否返回,之后通过通知或者回调机制通知消息处理者;同步与异步更多关注的是消息的通知机制,而不是消息的处理;

2)阻塞与非阻塞更多的是关注消息的处理。阻塞是指,函数在返回结果前,线程不会执行其他操作,直到函数返回;非阻塞是指函数不能立刻得到返回结果,便立刻返回,等待下一次操作。

15.HashMap的加载因子为什么是0.75

加载因子 = 填入表中的元素个数 / 散列表的长度,加载因子越大,填满的元素越多,空间利用率越高,但发生冲突的机会变大了。反之…。HashMap的初始容量大小默认是16,为了减少冲突发生的概率,当HashMap的数组长度到达一个临界值的时候,就会触发扩容,把所有元素rehash之后再放在扩容后的容器中,这是一个相当耗时的操作。而这个临界值就是由加载因子*当前容器的容量大小来确定的。
根据泊松分布和测试,0.75,是一种时间和空间上的折中选择·。当桶中元素到达8个的时候,概率已经变得非常小,也就是说用0.75作为加载因子,每个碰撞位置的链表长度超过8个是几乎不可能的。

16.如何访问私有属性

get、set方法
或者反射,不过需要设置访问权限setAccessible(true)

17、创建对象的方法

  1. new关键字
  2. Class.newInstance(无参)
  3. Constructor.newInstance(有参)
  4. Clone方法
  5. 反序列化

18、c++与java的区别

1)java不提供指针直接访问内存,程序更加安全
2)java是单继承的,C++支持多继承;虽然java不可以多继承,但接口可以多实现。
3)java有垃圾回收机制,C++需程序员手动释放无用内存
4)C++支持方法和运算符重载,java只支持方法的重载

18.1 c为什么比java快

1)java需要先编译为.class的二进制文件然后由JVM虚拟机解释执行;C语言先预编译为.obj文件再编译为汇编语言的二进制文件,相比较java省略了很多步骤。
2)java有垃圾回收,c没有

19. equal 和 ==

对于基本数据类型来说,==比较的是两者的值;对于引用类型来说,==比较的两者的内存地址。
对于基本数据类型来说,没有equals方法;对于引用类型来说,equals比较的可能是内存地址,也可能是内存地址和值都比较,从equals的实现上来说,其实用的比较也是==,所以equals的具体比较得看重写后的方法。具体如下:

对于Object类型来说,equals方法比较的是两者的内存地址。但很多类型都重写了equals方法

20.重写equal方法还要重写hashcode方法?

equals()只是判断对象属性是否相同,hashCode()要判断二者地址是否相同。java中如果要判断两个对象是否相等,需要同时满足地址 + 属性都相同!

如果两个对象相同(即:用 equals() 比较返回true),那么它们的 hashCode 值一定要相同;
如果两个对象的 hashCode 相同,它们并不一定相同;

21.string和stringbuffer区别

StringBuffer对象的内容可以修改;而字符串对象一旦产生后就不可以被修改,重新赋值其实是两个对象

StringBuffer线程安全

字符串对象通过“+”的字符串拼接方式,实际上是通过 StringBuilder 调用 append() 方法实现的,拼接完成之后调用 toString() 得到一个 String 对象

string存在字符串常量池 是 JVM 为了提升性能和减少内存消耗针对字符串(String 类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建。

注:如果是 String str = “a” + “b”;实际创建了一个对象,因为编译器优化为String str = “ab”;

场景:
对于三者使用的总结:

  1. 操作少量的数据: 适用 String
  2. 单线程操作字符串缓冲区下操作大量数据: 适用 StringBuilder
  3. 多线程操作字符串缓冲区下操作大量数据: 适用 StringBuffer

22.包装类型的缓存机制了解么

包装类通过缓存机制来提高性能。Byte,Short,Integer,Long 这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,Character 创建了数值在 [0,127] 范围的缓存数据,Boolean 直接返回 True or False。

23.array和arraylist的区别

Array 可以容纳基本类型和对象,而 ArrayList 只能容纳对象。
Array 是指定大小的,而 ArrayList 大小不是固定的,可以动态增长但牺牲效率;。
Array 没有提供 ArrayList 那么多功能,比如 addAll 、 removeAll 和 iterator 等。尽管 ArrayList 明显是更好的选择,但也有些时候 Array 比较好用。
1 、 如果列表的大小已经指定,大部分情况下是存储和遍历它们。
2 、 对于遍历基本数据类型,尽管 Collections 使用自动装箱来减轻编码任务,在指定大小的基本类型的列表上工作也会变得很慢。这时建议使用Array
3 、 如果你要使用多维数组,使用 [][] 比 List<List<>> 更容易

24、快速排序的最坏情况

  • 在分解时每次选取的主元素为最小元素
  • 在分解时每次选取的主元素为最大元素

25、error和exception

Error 和 Exception 都是 Throwable 的子类,用于表示程序出现了不正常的情况。区别在于:

Error 是程序错误,通常为虚拟机相关错误,如系统崩溃,内存不足,堆栈溢出等,编译器不会对这类错误进行检测,JAVA 应用程序也不应对这类错误进行捕获,一旦这类错误发生,通常应用程序会被终止,仅靠应用程序本身无法恢复;
Exception 是程序异常,是可以在应用程序中进行捕获并处理的,是一种设计或实现问题,也就是说,它表示如果程序运行正常,从不会发生的情况。

Checked Exception 和 Unchecked Exception 有什么区别?
Checked Exception 即 受检查异常 ,Java 代码在编译过程中,如果受检查异常没有被 catch或者throws 关键字处理的话,就没办法通过编译。

除了RuntimeException及其子类以外,其他的Exception类及其子类都属于受检查异常 。常见的受检查异常有: IO 相关的异常、ClassNotFoundException 、SQLException…。
Unchecked Exception不受检查异常 ,Java 代码在编译过程中 ,我们即使不处理不受检查异常也可以正常通过编译。
RuntimeException 及其子类都统称为非受检查异常,常见的有(建议记下来,日常开发中会经常用到):

  • NullPointerException(空指针错误)
  • IllegalArgumentException(参数错误比如方法入参类型错误)
  • NumberFormatException(字符串转换为数字格式错误,IllegalArgumentException的子类)
  • ArrayIndexOutOfBoundsException(数组越界错误)
  • ClassCastException(类型转换错误)
  • ArithmeticException(算术错误)
  • SecurityException (安全错误比如权限不够)
  • UnsupportedOperationException(不支持的操作错误比如重复创建同一用户)
  • ……

26、二维数组,行遍历和列遍历哪个更快,从cpu缓存的角度解释

:在计算机系统中,CPU高速缓存是用于减少处理器访问内存所需平均时间的部件。当处理器发出内存访问请求时,会先查看缓存内是否有请求数据。如果存在(命中),则不经访问内存直接返回该数据;如果不存在(失效),则要先把内存中的相应数据载入缓存,再将其返回处理器。
数组在内存中是按行存储的,缓存从内存中抓取一般都是整个数据块,所以它的物理内存是连续的,几乎都是同行不同列的,而如果内循环以列的方式进行遍历的话,将会使整个缓存块无法被利用,而不得不从内存中读取数据,而从内存读取速度是远远小于从缓存中读取数据的。随着数组元素越来越多,按列读取速度也会越来越慢。

27、递归与循环的区别

递归由于是调用自身,而函数调用是有时间和空间的消耗的:每一次函数调用,都需要再内存中分配空间以保存参数、返回地址及临时变量,而且往栈里压入数据和弹出数据都需要时间,更何况,每个进程的栈的容量是有限的。因此,递归实现的效率不如循环,而且,递归还有可能引起严重的调用栈溢出等问题。

28.为什么说java是半编译半解释形语言

  • 编译形语言,把做好的源程序编译为二进制代码的文件,然后就可以直接运行这个程序。执行快,效率高,依赖编译器,跨平台性差。
  • 解释性语言,将源程序翻译一句,执行一句,执行慢,效率低,依靠编译器,但跨平台性强。

为什么说他是解释形语言呢?因为java程序要想运行,必须将.java程序编译成.class(二进制文件)
为什么说他是编译形语言了?因为将.java文件编译成.class后仍然需要JVM编译器解释执行。所以java既有解释形语言的特点也有编译形语言的特点。

29.java的多态性

java的多态分为编译时多态和运行时多态。
1)编译时多态(编译时就能确定调用那个方法)

  • 方法的重载,根据实际参数在编译时能够确定执行重载方法的哪一个
  • 方法的重写,重写也表现出两种多态性,当子类对象引用调用自身方法为编译时多态,其他则为运行时多态。

2)运行时多态(运行时才能确定调用那个方法)

  • 方法的重写,当父类对象引用子类实例方法为运行时多态,需要根据传入的参数确定

30.反射机制了解吗?大概如何操作,有那些方法?

反射就是把java的各种成分(方法、属性)映射成相应的java类

  • 优点:可以让咱们的代码更加灵活,为各种框架提供便利。
  • 缺点:破坏了类的封装性,可以调用私有属性和方法,增加了安全问题

可以通过Class.forName()或者classLoader.load()获取Class类对象 ,然后通过该Class对象调用newInstance()缺省(无参)构造方法方法创建实例,或者得到这个类的Constructor,通过这个Constructor创建实例。然后通过getDeclareMethmod()获取方法或者getDeclareField()方法和获取属性,传入实例调用。

相关方法
image.png

getDeclaredFields()与getFields()的区别是前者可以获取本类的所有属性包括private,后者获取本类以及父类的public成员

31.JDK动态代理如何实现

JDK动态代理本质是创建一个JDK动态代理类实现InvocationHandler接口,然后重写invoke()方法
创建代理独对象通过Proxy.newInstance()创建
image.png
image.png

32.集合体系有哪些,分别有哪些实现类

  • List:ArrayList(数组),LinkedList(双向链表)
  • Map:HashMap(数组+链表),LinkedHashMap(HashMap的基础上加上双向链表),TreeMap(红黑树),HashTable(数组+链表,线程安全的)
  • Set:HashSet(基于HashMap),LinkedHashSet(基于LinkedHashMap),TreeSet(基于TreeMap)
  • queue:PriorityQueue(数组)

33.List和Set的区别

1)list允许有重复的对象,set不能有
2)list可以插入多个null元素,set只能允许插入一个null
3)list是有序的,保持了每个元素的插入顺序。set是无序的

34.Comparator与Comparable的区别

使用Comparator可以创建一个匿名对象来实现排序,不用去修改排序对象的代码;使用Comparable必须重写需要排序的代码

35.ArrayList原理

首先ArrayList有三种方式初始化
1)无参初始化,初始容量默认为10,但只有在第一次添加元素时才会分配容量
2)指定初始容量
3)指定集合,将集合的元素拷贝到新创建的ArrayList中

扩容机制
添加元素时会调用ensureCapacityInternal() 会确保空间是否够用,传参是容器实际长度加1,如果不是第一次添加会判断容器实际长度加1是否大于容器最大长度,如果大于则调用grow()方法啊扩容,扩容规则是扩容到原来的1.5倍,

36.arraycopy()与copyOf()的区别

arrycopy需要传递两个数组参数一个原数组,一个目标数组,而 copyOf只用传参一个原数组,它在方法内部创建了个数组,并调用arrcopy() 并返回这个创建的数组

37.ArrayList是线程安全的吗?如何保证

ArrayList不是线程安全,使用Collections.synchronizedList(),返回一个线程安全的SynchronizedList对象,它本质是对List对象的封装,他有个List的成员变量,它的get(),add()方法都有同步代码块。

38.ArrayLIst与LInkedList的区别

1)ArrayList是通过数组实现的,LinkedList是通过链表实现的
2)ArrayList的get() 效率要比LinkedList快,而LinkedList是基于链表的查找要从头往后遍历。而LinkedList删除或插入的效率要比ArrayList快,ArrayList删除或者插入要移动部分数组。
3)ArrayList支持随机访问,LinkedList不支持随机访问,所谓的随机访问就是通过下标在时间复杂度为O(1)的情况下快速访问元素
4)ArrayList实现了RandomAcess接口,这个接口没有什么实际作用,标记这个类是否支持随机访问,一些方法会根据是否实现RandomAcess选择不同的算法,比如可以通过这来分辨ArrayList和LinkedList,比如binarySearch()方法判断

39.内存排序为什么用红黑树不用B+树

B+树只是查询效率高,修改不占优势。红黑树查找和修改删除都是O(logn)

40.元素的无序性与不可重复性是什么

元素的无序性是指数据存储在底层的并非按照索引添加顺序,而是根据哈希值确定的;反之有序性指的是元素在底层的存储顺序是按照其添加顺序决定的

41.浮点数的计算为什么有丢失风险?如何解决

由于浮点数的存储长度是有限的,无限循环小数会被截断,精度会丢失;用BigDecimal解决,BigDecimal本质通过字符串存储数据

41.1 BigDeciaml为什么不能用equals

因为equals除了比较值是否相等还比较精度是否相等

42.JVM VS JRE VS JDK

JVM是java字节码虚拟机,是用来运行字节码的;JRE是java运行的环境,包括JVM,java类库和java命令等;JDK包含JRE和编译器;

43.String为什么是不可变的

1)他的字符串数组被final修饰,且String类没有提供修改该字符串数组的方法
2)String类被final继承避免了子类破坏String不可变

44.什么是泛型

泛型:就是指在类定义时不会设置类类中的属性或方法参数具体类型,在类创建时才定义。主要有泛型类,泛型接口,泛型方法

其他文章
cover
JVM面试题
  • 22/11/16
  • 13:02
  • java资料
cover
JUC面试题
  • 22/11/16
  • 13:02
  • java资料