2021.Java面试题目.1(精选)

1、Java中垃圾回收有什么目的?什么时候进行垃圾回收?

垃圾回收的目的是识别并且丢弃应用不再使用的对象来释放和重用资源。
    
当对象没有被程序使用的时候会进行垃圾回收

2、HashSet和TreeSet的区别

共同点:无序、无索引、不可重复
不同点:TreeSet可以对元素进行排序

3、创建线程有哪几种不同的方式?你喜欢哪一种?为什么?

1、继承Thread类
   --写一个Thread类的子类
   --重写run()方法
   --创建Thread类的子类对象
   --调用start()方法,开启线程
2、实现Runnable接口
   --写一个Runnable接口的子类
   --重写run()方法
   --创建Thread对象,把Runnable接口的子类对象作为参数传递
   --调用start()方法,开启线程
3、实现Callable接口
   --写一个Callable接口的子类
   --重写call()方法.有返回值
   --创建FutureTask的对象,将Callable的子类对象作为参数传递
   --创建Thread对象,把FutureTask对象作为参数传递
   --调用start()方法,开启线程
4、使用Executor框架来创建线程池
    
创建线程方式的不同点:
1、继承Thread类:
	由于类的单继承性,继承Thread类后不可以再继承其他类
2、实现Runnable接口:
	扩展性较强,可以继承其他类,同时还可以实现多个接口
3、实现Callable接口:
	线程执行完之后有返回值
4、线程池:
	我们自己频繁地去创建和销毁线程比较消耗系统资源,同时也比较浪费时间.
    当创建一个线程池,其实就是创建了一个能够存储线程的容器,需要执行线程任务时,就从线程池中拿一个线程出来用,用完之后再还给线程池.

4、== 和 equals 的区别

1)对于==,比较的是值是否相等
  如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等;
  如果作用于引用类型的变量,则比较的是所指向的对象的地址(堆内存地址)2)对于equals方法,注意:equals方法不能作用于基本数据类型的变量,equals继承Object类,比较的是是否是同一个对象
  如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;
  诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。

5、String、StringBuffer、StringBuilder,它们之间的区别

1.创建对象: 
    String声明的是不可变的对象(被final修饰),每次对String类型的改变时都会生成一个新的对象,
    StringBuffer和StringBuilder是可以改变对象的。
2.对于操作效率:
    StringBuilder > StringBuffer > String
3.对于线程安全:
    StringBuffer 是线程安全,可用于多线程;
    StringBuilder 是非线程安全,用于单线程
   不频繁的字符串操作使用 String。反之,StringBuffer 和 StringBuilder 都优于String
	所以,如果在项目中需要拼接字符串最好是采用StringBuffer 而非String.

6、Java中的HashMap的工作原理

1.存储方式:  
    Java中的HashMap是以键值对(key-value)的形式存储元素的。

2.调用原理: 
    HashSet数据结构:哈希表结构(数组+链表+红⿊树),HashMap需要一个hash函数,它使用hashCode()equals()方法来向集合/从集合添加和检索元素。当调用put()方法的时候,HashMap会计算key的hash值,然后把键值对存储在集合中合适的索引上。如果该索引上没有元素(null),把元素直接存储在这个位置 如果有元素,继续通过equals⽅法判断元素的属性只是否⼀样 如果equals⽐较为true,就说明元素重复 如果equals⽐较为false,以链表的形式存储在数组的同⼀个索引位置 如果链表的⻓度超过8,就把链表转化为红⿊树(提⾼查询的效率)

3.其他热性:
    HashMap的一些重要的特性是它的容量(capacity),负载因子(load factor)和扩容极限(threshold resizing)。
	HashMap实现了Map接口,该接口的作用主要是为客户提供三种方式的数据显示:只查看keys列表;只查看values列表,或以key-value形式成对查看。Map接口并没有定义数据要如何存储,也没有指定如何判定key是一样,因此并不是所有的Map实现都会与hashCode方法扯上关系,如TreeMap便是要求对象实现Comparator接口,通过其compare方法来比对两者是否一致,而非hashCode及equals。同理,如果我们自己实现Map接口,我们也可以直接使用数组进行数据存储使用==判定key值是否一致,依然可以完全满足Map接口的定义。

7、java 中 IO 流分为几种

按功能来分:
    输入流(input)、输出流(output)。
按类型来分:
    字节流和字符流。
字节流和字符流的区别是:
    字节流按 8 位传输以字节为单位输入输出数据,字符流按 16 位传输以字符为单位输入输出数据。字符流只能对纯文本文件进行读写.

8、什么是多线程

说起多线程,那么就不得不说什么是线程,而说起线程,又不得不说什么是进程。
  进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
  进程可以简单的理解为一个可以独立运行的程序单位。它是线程的集合,进程就是有一个或多个线程构成的,每一个线程都是进程中的一条执行路径。
  那么多线程就很容易理解:多线程就是指一个进程中同时有多个执行路径(线程)正在执行。
1.进程:正在执行的程序
2.线程:线程是进程中的一个执行单元(路径),如果一个进程中包含多个线程,这个程序就是多线程程序。

9、线程有哪些状态

NEW(新建状态): 
    至今尚未启动的线程处于这种状态。
RUNNABLE(就绪状态):
    正在 Java 虚拟机中执行的线程处于这种状态。
BLOCKED(阻塞状态):受阻塞并等待某个监视器锁的线程处于这种状态。
WAITING(等待状态):无限期地等待另一个线程来执行某一特定操作的线程处于这种状态。
TIMED_WAITING(计时等待):调用了sleep方法的状态。
TERMINATED(结束状态):已退出的线程处于这种状态。
    
1.新建状态
	当用new操作符创建一个线程时。此时程序还没有开始运行线程中的代码。
2.就绪状态
	一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。
	处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序来调度的。
3.运行状态(running)
	当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法。
4.阻塞状态(blocked)
	线程运行过程中,可能由于各种原因进入阻塞状态:
	①线程通过调用sleep方法进入睡眠状态;
	②线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
	③线程试图得到一个锁,而该锁正被其他线程持有;
	④线程在等待某个触发条件;
	所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。
5.死亡状态(dead)
	有两个原因会导致线程死亡:
	①run方法正常退出而自然死亡;
	②一个未捕获的异常终止了run方法而使线程猝死;
	为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法,如果是可运行或被阻塞,这个方法返回true;如果线程仍旧是new状态且不是可运行的,或者线程死亡了,则返回false

10、请谈一下对于TCP三次握手和四次挥手的理解

三次握手:
	(1)客户端向服务器发出连接请求等待服务器确认
	(2)服务器向客户端返回一个响应告诉客户端收到了请求
	(3)客户端向服务器再次发出确认信息,此时连接建立
四次挥手:
	(1)客户端向服务器发出取消连接请求
	(2)服务器向客户端返回一个响应,表示收到客户端取消请求
	(3)服务器向客户端发出确认取消信息(向客户端表明可以取消连接了)
	(4)客户端再次发送确认消息,此时连接取消

11、Executors和Semaphore都能控制线程的执行,那么这两者的区别是什么,请简要说明

(1)使用线程池,实际工作线程由线程池创建,是可复用的;使用Seamphore,实际工作的线程由自己创建,是不可复用的
(2)线程池自动进行限流,Seamphore手动通过acquire和release方法进行限流

12、请说一下你对类,抽象类,接口的理解,它们之间的区别是什么(从继承结构,对象实例化,继承/实现中方法的特点,成员方法方面进行阐述)

类是具有相同属性和行为的对象的集合,抽象类是对于事物的抽象,接口是对于行为的抽象
区别:
	(1)类和抽象类都只能进行单继承,不能进行多继承,但可以多层继承;而接口可以多实现
	(2)类可以进行对象实例化;抽象类和接口均不能进行对象实例化,但可以间接通过多态的方式进行实现
	(3)继承一个类可以选择性的重写其中的方法;继承一个抽象类必须重写其中所有的抽象方法,其他方法可以选择性进行重写;实现一个接口必须重写其中所有的抽象方法
	(4)一个类中如果有抽象方法那么该类必须为抽象类,抽象类中可以没有抽象方法;接口中只能存在抽象方法,在jdk8中允许定义默认和静态方法,jdk9中允许定义私有方法

13、HashMap、HashTable、ConCurrentHashMap的区别

HashMap、HashTable、ConCurrentHashMap底层都是哈希表。
	HashMap首先会在底层创建一个长度为16,加载因子为0.75的Entry数组,根据Entry对象的哈希值确定存入数组的索引位置,当存入位置有元素时,比较存入位置元素的哈希值,不相等时以链表结构存入数组索引位置,相等则不存入。Jdk1.8版本及以后,当链表长度超过8时,链表会转换为红黑树。当超过加载因子时,会自动扩容为原来的两倍。可以存储键值都为空的Entry对象,由于没有采用锁机制,所以线程是不安全的。
	HashTable与ConCurrentHashMap底层与HashMap类似,区别在于它们无论key还是value都不能为NULL,底层都采用了锁机制,它们都是线程安全的。
	HashTable底层数组初始长度为11,加载因子为0.75,当超过加载因子时,会自动扩容为原来的两倍加1;
    采用了synchronized悲观锁,在修改数据时锁住整个HashTable,效率低。
    ConCurrentHashMap把整个Map分为N个Segment,N的默认值为16,加载因子也是0.75,段内元素超过该段对应Entry数组长度的75%触发扩容,不会对整个Map进行扩容,插入前检测需不需要扩容,有效避免无效扩容;			ConCurrentHashMap与HashTable最大的区别就在于ConCurrentHashMap采用synchronized悲观锁锁住单个Segment对象,使用了锁分离技术;同时ConCurrentHashMap也采用了CAS算法(乐观锁),使用 容量大小-1 & 哈希地址 计算出待插入键值的下标,如果该下标上的标记为null,则直接调用CAS算法将元素插入到数组中,如果不为空,则根据volatile获取当前位置最新的节点地址值,挂在最新节点下面,变成链表,当链表长度大于等于8时,链表自动转成红黑树,效率较高。
	在不考虑线程安全时,优先使用HashMap;在考虑线程安全时,优先使用ConCurrentHashMap。

14、什么是值传递和引用传递?

值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量。
引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身 。
一般认为,java内的基础类型数据传递都是值传递;java中实例对象的传递是引用传递。

15、final、finalize、finally的区别

1.简单区别:
	final用于声明属性,方法和类,分别表示属性不可交变,方法不可覆盖,类不可继承。
	finally是异常处理语句结构的一部分,表示总是执行。
	finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,供垃圾收集时的其他资源回收,例如关闭文件等。
    
2.中等区别:
	虽然这个单词在Java中都存在,但是并没太多关联:
	final:java中的关键字,修饰符。
	A).如果一个类被声明为final,就意味着它不能再派生出新的子类,不能作为父类被继承。因此,一个类不能同时被声明为abstract抽象类的和final的类。
	B).如果将变量或者方法声明为final,可以保证它们在使用中不被改变.
  1)被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。
  2)被声明final的方法只能使用,不能重载。
    	
	finally:java的一种异常处理机制。
  finally是对Java异常处理模型的最佳补充。finally结构使代码总会执行,而不管无异常发生。使用finally可以维护对象的内部状态,并可以清理非内存资源。特别是在关闭数据库连接这方面,如果程序员把数据库连接的close()方法放到finally中,就会大大降低程序出错的几率。
    
	finalize:Java中的一个方法名。
Java技术使用finalize()方法在垃圾收集器将对象从内存中清除出去前,做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没被引用时对这个对象调用的。它是在Object类中定义的,因此所的类都继承了它。子类覆盖finalize()方法以整理系统资源或者执行其他清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。
    
3.详细区别:
	这是一道再经典不过的面试题了,我们在各个公司的面试题中几乎都能看到它的身影。finalfinally和finalize虽然长得像孪生兄弟一样,但是它们的含义和用法却是大相径庭。
final关键字我们首先来说说final。它可以用于以下四个地方:
	1).定义变量,包括静态的和非静态的。
	2).定义方法的参数。
	3).定义方法。
	4).定义类。
        
定义变量,包括静态的和非静态的。定义方法的参数
第一种情况:
  如果final修饰的是一个基本类型,就表示这个变量被赋予的值是不可变的,即它是个常量;
  如果final修饰的是一个对象,就表示这个变量被赋予的引用是不可变的
这里需要提醒大家注意的是,不可改变的只是这个变量所保存的引用,并不是这个引用所指向的对象。
第二种情况:final的含义与第一种情况相同。
实际上对于前两种情况,一种更贴切的表述final的含义的描述,那就是,如果一个变量或方法参数被final修饰,就表示它只能被赋值一次,但是JAVA虚拟机为变量设定的默认值不记作一次赋值。被final修饰的变量必须被初始化。初始化的方式以下几种:    
1.在定义的时候初始化。    
2.final变量可以在初始化块中初始化,不可以在静态初始化块中初始化。
3.静态final变量可以在定义时初始化,也可以在静态初始化块中初始化,不可以在初始化块中初始化。    
4.final变量还可以在类的构造器中初始化,但是静态final变量不可以。

16、重载(Overload)和重写(Override)的区别

重载:发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同,发生在编译时。
    
重写:发生在父子类中,方法名、参数列表必须相同,返回值小于等于父类,抛出的异常小于等于父类,访问修饰符大于等于父类;如果父类方法访问修饰符为private则子类中就不是重写。

方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。
	重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;
    重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的参数列表,有兼容的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。重载对返回类型没有特殊的要求,不能根据返回类型进行区分。

17、简述synchronized 和Lock的异同

Lock是Java 5以后引入的新的API,和关键字synchronized相比主要相同点:Lock 能完成synchronized所实现的所有功能;
主要不同点:Lock有比synchronized更精确的线程语义和更好的性能,而且不强制性的要求一定要获得锁。synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且最好在finally 块中释放(这是释放外部资源的最好的地方)。

18、简述一下Volatile关键字和AtomicInteger类的作用和实现原理以及原子性的含义

Volatile:
	当多个线程在访问共享数据时,不是直接访问的主内存的数据,而是在线程本地内存创建了一个和主内存一样的变量副本,对副本进行赋值,然后在重新赋值给主内存。
	可能会出现多个线程临时存储的变量副本的值不一样,使用volatile修饰这个变量,就可以解决问题,强制让线程每次使用变量时,都从主内存中获取最新的值。
        
	一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
	1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,	这新值对其他线程来说是立即可见的。
   2)禁止进行指令重排序。
	
AtomicInteger:
	AtomicInteger类就是一个能解决原子性问题的原子类,它提供了一些方法可以对变量进行自增、获取、修改等操作,但是这些方法可以保证原子性,也能保证线程安全。

	原子性:
	原子性指意思是多个操作是不可分割的原子项,他们要么同时成功,要么同时失败。 例如:当多线程在执行自增操作时,就不是原子性的。

19、面向对象的特征

	抽象:就是把现实生活中的某一类东西提取出来,用程序代码表示,我们通常叫做类或者接口。抽象包括两个方面:一个是数据抽象,一个是过程抽象。数据抽象也就是对象的属性。过程抽象是对象的行为特征。
	封装:把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行封装隐藏。封装分为属性的封装和方法的封装。核心思想就是“隐藏细节”、“数据安全”:将对象不需要让外界访问的成员变量和方法私有化,只提供符合开发者意愿的公有方法来访问这些数据和逻辑,保证了数据的安全和程序的稳定。好处:良好的封装能减少耦合,对成员变量更精确的控制。
	继承:子类可以继承父类的非私有方法和属性(默认属性和方法也不行),达到复用代码的效果。Java是单继承,一个类只能继承一个父类。
	多态: 不同类的对象对同一消息作出不同的响应叫做多态。同一消息可以根据发送对象的不同而采用多种不同的行为方式。可以用于消除类型之间的耦合关系,Spring 的核心就是多态和面向接口编程。多态的分类,编译时多态,方法的重载,运行时多态,方法的覆盖。多态存在的条件:存在继承关系,子类重写父类的方法,父类引用指向子类。

20、简述一下 就下面有哪些接口和实现类

Collection接口
  	--List接口:有索引、存取顺序一致、元素可以重复
  	   -- ArrayList类:数组结构,查询快增删慢
  	   -- LinkedList类:链表结构,查询慢增删快
  
    -- Set接口:没有索引、存取顺序不一致、元素不可以重复
  	   -- TreeSet类:红黑树结构,可以对元素进行排序
  	   -- HashSet类:哈希表结构,可以保证元素唯一

21、 HashMap中put(K,V)方法实现的原理

	第一步:先将K,V封装到Node对象中
	第二步:底层会调用K的hashCode()方法得出hash值,然后通过哈希算法,将hash值转换成数组的下标,下标位置上如果没有任何元素,就将Node添加到此位置上,如果下标对应的位置上有链表,此时会拿着K和链表中,每一个节点中的K进行equals比较,如果所有的equals方法返回的都是false,name这个新节点将会被添加到链表的末尾,如果其中有一个equal()返回了true,name这个节点的value将会被覆盖。

22、线程sleep()和wait()的区别

	sleep的作用是让线程休眠指定的时间,在时间到达时恢复。	 
    wait()的作用是是线程就进入到一个和该对象相关的等待池中(进入等待队列,也就是阻塞的一种,叫等待阻塞),同时释放对象锁,并让出CPU资源,待指定时间结束后返还得到对象锁。等待的线程只是被激活,但是必须得再次获得锁才能继续往下执行,也就是说只要锁没被释放,原等待线程因为为获取锁仍然无法继续执行。
    
   补充:
1.属于不同的两个类,sleep()方法是线程类(Thread)的静态方法,wait()方法是Object类里的方法。
2.sleep()方法不会释放锁,wait()方法释放对象锁。
3.sleep()方法可以在任何地方使用,wait()方法则只能在同步方法或同步块中使用。
4.sleep()必须捕获异常,wait()方法、notify()方法和notiftAll()方法不需要捕获异常。
5.sleep()使线程进入阻塞状态(线程睡眠),wait()方法使线程进入等待队列(线程挂起),也就是阻塞类别不同。
6.它们都可以被interrupted方法中断。

23、hashcode和equals的作用

hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode() 函数。

equals它的作用也是判断两个对象是否相等,如果对象重写了equals()方法,比较两个对象的内容是否相等;如果没有重写,比较两个对象的地址是否相同,等价于“==”。同样的,equals()定义在JDK的Object.java中,这就意味着Java中的任何类都包含有equals()函数。

24、增强for循环底层是怎么实现的,使用的时候有什么需要注意的

增强for循环底层是迭代器。
1.在迭代器迭代元素的过程中,不允许使用集合对象改变集合中的元素的个数(也就是添加或者删除),可以进行修改,如果需要添加或者删除只能使用迭代器的方法进行操作
2.如果使用集合对象在迭代的过程中改变集合的元素个数,会出现异常java.util.ConcurrentModificationException
3.迭代器迭代的过程:就是迭代器从创建开始,到最后一次使用的过程中。

增强for循环内容补充:
(1)JDK1.5新特性,collection接口继承了Iterable接口,该接口有一个方法即:增强for循环方法。底层原理凡是支持迭代器的都支持增强for循环 增强for循环,只用于集合和数组的遍历,集合只能用Collection不能用Map集合,只能把Map集合转化成Set集合,才能用for循环。 
(2)格式 for(数据类型 变量名:被遍历的集合(Collection)或者数组) { } 
(3)局限性: 必须要有遍历的目标 对集合或者数组进行遍历时,只能获取集合元素,不能对集合元素进行操作 迭代器除了遍历,还可以进行remove操作集合中的元素 列表迭代器还可以在遍历过程中进行增删改查的操作 
(4)传统for循环和增强for循环的区别 增强for循环有一个局限性,就是必须要有遍历的目标(集合或者数组) 遍历数组时建议使用传统for循环,因为可以定义角标,比如打印100次helloworld时用传统for循环方便
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值