Java基础 面试题

1.常用的集合类有哪些?具有哪些特性?是否有序?是否可以为null?是否排序?是否重复? 每个集合类底层的数据存储结构?

常用的三大类集合:Set、List、Map。其中Set和List继承自Collection。Collection是一组对象的集合,而Map存储的方式不一样,他是以键值对的形式存放多个对象的。
在这里插入图片描述
ArrayList 数组列表,有序,可重复,内部是通过 Array 实现。ArrayList线程是非安全的,一般使用在单线程的情况下,如果在多线程的情况下可以使用collections.synchronizedList(List 1)函数返回一个线程安全的ArrayList类。而且ArrayList也可以有null值。
在这里插入图片描述
LinkedList 是双向链表,也即每个元素都有指向前后元素的指针。既然是链表那么顺序读取的效率非常高,而随机读取的效率较低。对比 ArrayList 如果随机读取数据较多时使用 ArrayList 性能高,插入删除较多时使用 LinkedList 性能高。
LinkedList 是非线程安全的,集合中的元素允许为空,保存的元素为有序的,实现了List接口,则允许集合中的元素是可以重复的。
在这里插入图片描述
Vector:底层数据结构是数组,查询快,增删慢,线程安全,效率低,可以存储重复元素
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
Set:
(1)HashSet底层数据结构采用哈希表实现,元素无序且唯一,线程不安全,效率高,可以存储null元素,元素的唯一性是靠所存储元素类型是否重写hashCode()和equals()方法来保证的,如果没有重写这两个方法,则无法保证元素的唯一性。
(2)LinkedHashSet底层数据结构采用链表和哈希表共同实现,链表保证了元素的顺序与存储顺序一致,哈希表保证了元素的唯一性。线程不安全,效率高。
(3)TreeSet底层数据结构采用二叉树来实现,元素唯一且已经排好序;唯一性同样需要重写hashCode和equals()方法,二叉树结构保证了元素的有序性。

Map用于保存具有映射关系的数据,Map里保存着两组数据:key和value,它们都可以使任何引用类型的数据,但key不能重复。所以通过指定的key就可以取出对应的value。
在这里插入图片描述
在这里插入图片描述
ArrayXxx:底层数据结构是数组,查询快,增删慢
LinkedXxx:底层数据结构是链表,查询慢,增删快
HashXxx:底层数据结构是哈希表。依赖两个方法:hashCode()和equals()
TreeXxx:底层数据结构是二叉树。两种方式排序:自然排序和比较器排序

2.2、Comparator和Comparable接口的作用和异同点?

Comparable和Comparator都是用来实现集合中元素的比较、排序的。
Comparable是在集合内部定义的方法实现的排序,位于java.util下。
Comparator是在集合外部实现的排序,位于java.lang下。

Comparable是一个对象本身就已经支持自比较所需要实现的接口,如String、Integer自己就实现了Comparable接口,可完成比较大小操作。自定义类要在加入list容器中后能够排序,也可以实现Comparable接口,在用Collections类的sort方法排序时若不指定Comparator,那就以自然顺序排序。所谓自然顺序就是实现Comparable接口设定的排序方式。

Comparator是一个专用的比较器,当这个对象不支持自比较或者自比较函数不能满足要求时,可写一个比较器来完成两个对象之间大小的比较。Comparator体现了一种策略模式(strategy design pattern),就是不改变对象自身,而用一个策略对象(strategy object)来改变它的行为。

总而言之Comparable是自已完成比较,Comparator是外部程序实现比较。

3.什么是泛型?泛型的好处?泛型和方法重载有什么联系?泛型可以使用基本类型么?

泛型是程序设计语言的一种特性。允许程序员在强类型程序设计语言中编写代码时定义一些可变部分,那些部分在使用前必须作出指明。各种程序设计语言和其编译器、运行环境对泛型的支持均不一样。将类型参数化以达到代码复用提高软件开发工作效率的一种数据类型。泛型类是引用类型,是堆对象,主要是引入了类型参数这个概念。
泛型的好处:
// 1. 省略了强转的代码。
// 2. 可以把运行时的问题提前到编译时期。
泛型和方法重载有什么联系?
在这里插入图片描述
泛型可以使用基本类型么?
1.泛型的定义:在程序中我们将一个对象放入集合中,但是集合不会记住对象的类型,当我们在次使用对象的时候,对象变为Object类型,而程序中还是原来的类型,我们必须要自己转换其类型,为了解决这个问题,则提出泛型。

2.泛型要求包容的是对象类型,而基本数据类型在Java中不属于对象。但是基本数据类型有其封装类,且为对象类型。

3.想放int类型,要放Integer类型不能直接放int(基本数据类型)。

4.HashMap的底层实现的数据结构?1.7- 数组+链表;1.8+ 数组+链表+红黑树

在这里插入图片描述
从上图我们可以看出HashMap底层实现还是数组,只是数组的每一项都是一条链。
Hash算法可以将一个数据转换为一个标志,这个标志和源数据的每一个字节都有十分紧密的关系。

散列一般指Hash(散列函数)
Hash,一般翻译做散列、杂凑,或音译为哈希,是把任意长度的输入(又叫做预映射pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值

5.什么是红黑树? 在HashMap中链表是如何转换为红黑树的? 为什么使用红黑树?

红黑树其实就是一种自平衡的二叉查找树。他这个自平衡的特性就是对HashMap中链表可能会很长做出的优化。

红黑树是每个节点都带有颜色属性的二叉查找树,颜色或红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:
性质1. 节点是红色或黑色。
性质2. 根节点是黑色。
性质3. 每个叶节点(NIL节点,空节点)是黑色的。
性质4. 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
性质5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
在这里插入图片描述
在这里插入图片描述

6.HashMap的负载因子是(0.75);HashMap的初始长度是多少(16)?HashMap是如何扩容的?(按照16的整数倍进行扩容。)

Map<String,Object> map = new HashMap<>(30);内存中初始化的map长度为多少?32
负载因子 loadFactor表示一个散列表的空间的使用程度,有这样一个公式:initailCapacity*loadFactor=HashMap的容量。
所以负载因子越大则散列表的装填程度越高,也就是能容纳更多的元素,元素多了,链表大了,所以此时索引效率就会降低。
反之,负载因子越小则链表中的数据量就越稀疏,此时会对空间造成烂费,但是此时索引效率高。

7.什么是对象流?是将内存中的某个时间节点的状体信息以字节流的形式持久化到硬盘文件中

对象流有的时候,我们可能需要将内存中的对象持久化到硬盘上,或者将硬盘中的对象信息读到内存中,这个时候我们需要使用对象输入输出流。

8.什么是序列化?什么是反序列化? Serarizable接口的作用?

序列化 (Serialization)是将对象的状态信息转换为二进制的过程
实现序列化需要实现java.io.Serializable接口。
反序列化:就是通过序列化后的字段还原成这个对象本身。
 实现serializable接口的作用是就是可以把对象存到字节流,然后可以恢复。所以你想如果你的对象没实现序列化怎么才能进行网络传输呢,要网络传输就得转为字节流,所以在分布式应用中,你就得实现序列化,如果你不需要分布式应用,那就没那个必要实现序列化。

9.Transient关键字作用?是否可以和final、static搭配使用?以及他们之间的区别?

首先介绍一下序列化Serializable

通常一个类实现序列化方式是实现序列化接口: class XXX implements Serializable

序列化的作用:把数据长久的保存在磁盘中,磁盘和内存是不同的,内存一般在程序运行时占用,数据保存周期短,随程序结束而结束,磁盘可以长久保存数据

transient关键字的作用,在已实现序列化的类中,有的变量不需要保存在磁盘中,就要transient关键字修饰,如银行卡密码等,就这个作用------在已序列化的类中使变量不序列化

10.什么是进程?什么是线程?进程和线程之间的异同点?

进程: 具有独立功能程序在某个数据集合上的一次执行过程。

线程: 进程内的一个执行实体或执行单元。

进程和线程的区别:

(a) 不同进程的地址空间是独立的,而同一进程内的线程共享同一地址空间。一个进程的线程在另一个进程内是不可见的。

(b) 在引入线程的操作系统中,进程是资源分配和调度的单位,线程是处理机调度和分配的单位,资源是分配给进程的,线程只拥有很少资源,因而切换代价比进程切换低。

11.线程的状态以及转换方式?进程三态、五态的状态转换图?

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

12.java多线程编程的实现方式?常用的线程操作的方法?

在Java中,有三种方式来实现多线程,分别是:

继承Thread类
从源码可以看出,Thread类本质上是实现了Runnable接口的一个实例,启动线程的唯一方式就是通过Thread类的start()方法,start()方法是个native方法,它会启动一个新的线程,并执行run()方法。用这种方式启动线程,直接用自己的类继承Thread类,并重写他的run()方法就可以启动线程并执行自己定义的run()方法了。
实现Runnable接口
由于Java不支持多继承,如果自己的类已经继承了其他类,那么启动线程就要实现Runnable接口并重写它的run()方法。
从源码中可以看出,Runnable接口从Java1.0就已经有了,它内部只有一个抽象方法run()。
使用ExecutorService,Callable,Future实现带返回结果的多线程。

13.多线程中可以直接调用run方法,执行线程么

首先Thread类中run()和start()方法的区别如下:
run()方法:在本线程内调用该Runnable对象的run()方法,可以重复多次调用;
start()方法:启动一个线程,调用该Runnable对象的run()方法,不能多次启动一个线程;
run方法可以创建一个线程,但是相当于同步的方式,没有多线程的存在。
只有调用start方法才是交给jvm管理,才是多线程。

14.线程类实现之后,调用run方法和start方法有什么区别?

线程对象调用run方法不开启线程。仅是对象调用方法。线程对象调用start开启线程,并让jvm调用run方法在开启的线程中执行。

15.产生死锁的四个必要条件?

(1)互斥条件:进程对所分配到的资源不允许其他进程进行访问,若其他进程访问该资源,只能等待,直至占有该资源的进程使用完成后释放该资源

(2)请求和保持条件:进程获得一定的资源之后,又对其他资源发出请求,但是该资源可能被其他进程占有,此事请求阻塞,但又对自己获得的资源保持不放

(3)不可剥夺条件:是指进程已获得的资源,在未完成使用之前,不可被剥夺,只能在使用完后自己释放

(4)环路等待条件:是指进程发生死锁后,必然存在一个进程–资源之间的环形链

16.如何避免线程产生死锁?Synchronize关键字的用法?

三种用于避免死锁的技术
1.加锁顺序(线程按照一定的顺序加锁)
**2.加锁时限(**线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
3. 死锁检测

Synchronize关键字的用法:
1、对象锁
包括方法锁(默认锁对象为this当前实例对象)和同步代码块锁(自己指定锁对象)
2、类锁
指定synchronize修饰静态的方法或指定锁为class对象

17.线程同步?

当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作

18.线程的通信机制(wait-notify机制)

java实现线程通信机制主要通过以下几种方式

1 while()轮询

2 等待/通知机制

wait()、notify()、notifyAll()结合synchronized关键字

3 条件对象:Condition

Condition和ReentrantLock重入锁

等待/通知机制

synchronized是JVM提供的内置锁,我们通过java中的生产者消费者模式来看看wait/notify是如何实现线程间通信的

wait()/notify()/notifyAll()是Object类定义的方法,必须与synchronized一起使用,更确切的来说是在synchronized临界区内使用,

也就是说调用wait()方法的前提是该线程已经获取锁,调用notify()/notifyAll()的前提是也是该线程已经获取了锁,

wait() : 使当前线程进入阻塞状态,退出临界区,释放CPU,暂时放弃所获取的锁,当其他线程调用notify()/notidyAll()方法的时候,会唤醒一个或者多个处于等待该线程可能会被唤醒然后重新取竞争获取锁,如果重新获得了锁,被唤醒的线程会继续从上次阻塞的地方执行。

notify()/notifyAll():当前线程调用此方法,会唤醒之前阻塞的线程,但是notify()/notifyAll()方法不会释放锁,而是会继续往下执行,直到遇到wait()方法才会释放锁,所以一般线程调用完成notify()/notifyAll()方法后,都会调用wait()释放锁,退出临界区,从而让其他线程有机会获取锁

19.ConcurrentHashMap如何实现多线程并发的? CAS无锁化解决多线程并发;【乐观锁】;Synchronize关键字解决多线程并发【悲观锁】

20.什么是乐观锁?什么是悲观锁?

悲观锁是一个事务锁定了一些数据之后,只有当当前锁提交了事务,释放了锁,其他事务才能获得锁并执行操作。

乐观锁是首先假设数据冲突很少,只有在数据提交修改的时候才进行校验,如果冲突了则不会进行更新。

21.OIS7层模型和Tcp/Ip4层模型?

在这里插入图片描述

22.OSI7层模型中各层模型的主要协议、运行的设备、端口号有哪些?

在这里插入图片描述

23.常用的网络通信协议:TCP、UDP、Http、ftp、pop3、smtp

24.TCP和UDP的区别?以及网络编程实现方式 TCP和UDP的区别

TCP与UDP区别总结:
1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
3、TCP面向
字节流
,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文
UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5、TCP首部开销20字节;UDP的首部开销小,只有8个字节
6、TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道

25.如何获取一个类的描述类Class类的实例对象?(至少有3中方式)

1.通过class的方式创建Class实例:类名.class

2.通过对象的getClass()方法获得。

3.通过Class类的forName()方法来获得。

26.什么是反射?反射的作用和意义?什么是Class类,作用是什么?

反射的概念和思想
JAVA反射机制是在运行状态中,对于任意一个实体类,都能够获取这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;
这种动态获取类的构造信息以及动态调用实例对象的行为方法的功能称为java语言的反射机制。

27.使用java在内存中创建一个新对象的方式有哪几种?

new创建;
对象流创建;
反射创建;
克隆创建;

28.什么是注解?注解和注释的区别?
注解
注解是java程序中使用元数据对功能代码进行补充说明的一种编程方式;
注解是在jdk1.5以后才支持的;
注解使用使用在类、属性、方法、构造方法、局部变量、参数、枚举类型上,之前用的注解有 @override @Test
注解与注释的区别:
注释:
是对程序的解释说明的内容,是给编程人员阅读理解代码使用;
注释不参与程序的编译运行;
注解:
是对程序的补充说明,是给jvm在编译运行程序时使用的“注释说明”
注解是参与程序的编译运行的;

注释是给编程人员看的,不参与程序的编译运行;
注解是给jvm看的,参与程序的编译运行;

29.常用的元注解有哪些?各有什么作用?

四个元注解分别是:@Target,@Retention,@Documented,@Inherited ,再次强调下元注解是java API提供,是专门用来定义注解的注解,其作用分别如下:

@Target 表示该注解用于什么地方,可能的值在枚举类 ElemenetType 中,包括:
ElemenetType.CONSTRUCTOR----------------------------构造器声明
ElemenetType.FIELD --------------------------------------域声明(包括 enum 实例)
ElemenetType.LOCAL_VARIABLE------------------------- 局部变量声明
ElemenetType.METHOD ----------------------------------方法声明
ElemenetType.PACKAGE --------------------------------- 包声明
ElemenetType.PARAMETER ------------------------------参数声明
ElemenetType.TYPE--------------------------------------- 类,接口(包括注解类型)或enum声明

30.常用的排序算法以及稳定性?

稳定算法:冒泡排序、插入排序、归并排序、基数排序
不稳定算法 :选择排序、快速排序、希尔排序、堆排序
什么是算法稳定性

考察排序算法的时候有一个很重要的特性,就是算法的稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的。

一、什么是算法稳定性

考察排序算法的时候有一个很重要的特性,就是算法的稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的。

二、算法稳定性的重要性

算法稳定性为什么这么重要呢?

1)在实际的应用中,我们交换的不一定只是一个整数,而可能是一个很大的对象,交换元素存在一定的开销;

2)参照基数排序(后面会讲),不稳定排序是无法完成基数排序的,讲述完基数排序后,还会补充这里的原因。

八大算法的稳定性

1)直接插入排序@排序算法之插入排序(Insertion Sort)

其大致原理是:将数组分为无序区和有序区两个区,然后不断将无序区的第一个元素按大小顺序插入到有序区中去,最终将所有无序区元素都移动到有序区完成排序。

我们假设一个数组,元素已经排序为{1,5A,7,5B,9},其中前面三个已经排序完成,后面没有排序,即前面三个是有序区,后面两个是无序区,现在要将无序区的5B插入到有序区,则如果我们将元素插入到5A之前,我们需要往后移动两个元素,如果插入到5A之后,则需要移动一个元素,因此我们选择移动一个元素,而5A和5B也保持原来的顺序,因而直接插入排序是稳定的。

2)希尔排序@排序算法之希尔排序(Shell Sort)

其大致原理是:又称Gap缩小排序。先将序列按Gap划分为元素个数相同的若干组,使用直接插入排序法进行排序,然后不断缩小Gap直至为1,最后使用直接插入排序完成排序。希尔排序其实是直接插入排序的增强版。

我们来证明它是不稳定的,假设有一个数组{3,2A,2B,4},我们要升序排列,按照算法,第一次Gap=2,即可以分为{3,2B}和{2A,4}两组,然后对每一组进行插入排序,可以排序成{2B,2A,3,4},第二次Gap=1,由于插入排序是稳定的,所以2A和2B不会交换顺序了。由此可以看到,希尔排序是不稳定的。

3)冒泡排序@排序算法之冒泡排序(Bubble Sort)

其大致原理是:将序列划分为无序和有序区,不断通过交换较大元素至无序区尾完成排序。

熟悉冒泡排序的人一定知道,冒泡排序通过不断的交换元素,将无序区的最大(最小)元素往无序区搬运,因而和插入排序一样,为了减少其交换次数,冒泡排序是稳定的。

4)快速排序@排序算法之快速排序(Quick Sort)

其大致原理是:不断寻找一个序列的中点,将小于该中点的元素搬移到中点左边,大于该中点的元素搬移到中点右边,或者反过来。然后对中点左右的序列递归的进行排序,直至全部序列排序完成,使用了分治的思想。

关于算法的稳定性有一点本来是打算后面再讲的,但是讲到快速排序就一定要说了。读者肯定注意到了,前面的插入排序和冒泡排序完全可以实现为不稳定算法,只是在比较元素决定是否交换的时候,是否加上等于号而已。快速排序更加显示了这一点,解释如下:

在算法导论里面,快速排序选择都是元素序列的最后一个元素,假设元素序列如下{3,9,5A,6,8,5B},这种情况下,和上面的情况一下,稳不稳定还是看判断的时候是否出现等号,但是如果选择不是这样的,我们假设一种特殊状况:{3,9,5A,5B,6,8,5C},算法的实现是选择中间的5B作为中点,则不论等号与否,都是不稳定的。实际上,算法导论的选择是非常有意义的,了解其算法过程的人可以看到,这样的选择极大的降低了交换元素的复杂度和移动元素的次数。算法导论中是加了等号的,即≤最后一个元素的值被移到了左边,因而快速排序是稳定的。

5)直接选择排序@排序算法之选择排序(Selection Sort)

其大致原理是:将序列划分为无序和有序区,寻找无序区中的最小值和无序区的首元素交换,有序区扩大一个,循环最终完成全部排序。

我们还是假设一个序列{1,3,5,10A,10B,7},看这个数列,假设前面三个是有序区,后面三个是无序区,则无序区中最小的元素是7,和无序区的首元素交换10A交换,则可以看到序列变成了{1,3,5,7,10B,10A},然后继续,无序区就剩下{10B,10A},我们又可以看到,这里又是一个等号问题,同样,前面的交换是必然的,而后面的交换(如果等于也要交换)则不是必然的,为了减少元素交换,直接选择排序是不稳定的。

6)堆排序

其大致原理是:利用大根堆或小根堆思想,首先建立堆,然后将堆首与堆尾交换,堆尾之后为有序区。

考虑序列{9,5A,7,5B},按照堆排序的算法走一遍(算法导论中用的是最大堆,这个序列也是用最大堆来设计的),很快就可以发现,输出序列为{5B,5A,7,9},而且与等号无关,因此堆排序是不稳定的。

7)归并排序@排序算法之归并排序(Merge Sort)

其大致原理是:将原序列划分为有序的两个序列,然后利用归并算法进行合并,合并之后即为有序序列。

归并排序一样是稳定的,但是归并排序的稳定性并不是为了减少元素交换次数,因为它的算法实现中没有元素交换这一概念。

8)基数排序

其大致原理是:将数字按位数划分出n个关键字,每次针对一个关键字进行排序,然后针对排序后的序列进行下一个关键字的排序,循环至所有关键字都使用过则排序完成。具体请参见:算法总结系列之五: 基数排序(Radix Sort)

基数排序对多个关键字进行排序,并且这些关键字还是有优先级别的,对于整数来说,位数越高的数字优先级越高,而基数排序则是对优先级低的先排序,因此,基数排序对于整数是从个十百千万一个个去排序的。注意,这里必须使用稳定排序,否则,就会让原先的地位排序成果毁于一旦,最终的不到正确的排序结果。

基数排序不过是一种思想,其每一位的排序都需要稳定算法,否则无法得到正确的结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值