Java常考面试题(更新中!)

**

**声明:试题来源于牛客网,网上较好的答案我会直接复制过来。其余的我会按照自己的理解去总结答案。

**
附上最新的文档格式,便于按目录查看,(ctrl+鼠标左键会直接定位到问题,倒序排列最新的在最上面)
https://yunpan.cn/cMdPARwTYMCqQ 访问密码 dfcf
目录

14、什么是迭代器(Iterator)? 2
13、Java集合类框架的基本接口有哪些? 3
12、什么是死锁(deadlock)? 3
11、在监视器(Monitor)内部,是如何做线程同步的?程序应该做哪种级别的同步? 4
10、概括的解释下线程的几种可用状态。 4
9、创建线程有几种不同的方式?你喜欢哪一种?为什么? 5
8、进程和线程的区别是什么? 6
7、什么是值传递和引用传递? 7
6、接口和抽象类的区别是什么? 7
5、Java中的方法覆盖(Overriding)和方法重载(Overloading)是什么意思? 7
4、同步方法和同步代码块的区别是什么? 8
3、Java支持的数据类型有哪些?什么是自动拆装箱? 9
2、”static”关键字是什么意思?Java中是否可以覆盖(override)一个private或者是static的方法? 9
1、什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”? 10

分割线
++++
++++
++++
++++
++++
++++
++++
++++
++++
++++
++++
++++
14、什么是迭代器(Iterator)?
迭代器(iterator)有时又称游标(cursor)是程式设计的软件设计模式,可在容器物件(container,例如链表或阵列)上遍访的接口,设计人员无需关心容器物件的内容。
Iterator提供了统一遍历操作集合元素的统一接口, Collection接口实现Iterable接口,
每个集合都通过实现Iterable接口中iterator()方法返回Iterator接口的实例, 然后对集合的元素进行迭代操作.

有一点需要注意的是:在迭代元素的时候不能通过集合的方法删除元素, 否则会抛出ConcurrentModificationException异常. 但是可以通过Iterator接口中的remove()方法进行删除.(详细见云笔记)也就是: Iterator 在工作的时候是不允许被迭代的对象被改变的。但你可以使用 Iterator 本身的方法 remove() 来删除对象, Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性

迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示。
当你需要访问一个聚合对象,而且不管这些对象是什么都需要遍历的时候,就应该考虑使用迭代器模式。另外,当需要对聚集有多种方式遍历时,可以考虑去使用迭代器模式。迭代器模式为遍历不同的聚集结构提供如开始、下一个、是否结束、当前哪一项等统一的接口。

补充:
迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。

Java中的Iterator功能比较简单,并且只能单向移动:
  (1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。
 (2) 使用next()获得序列中的下一个元素。
 (3) 使用hasNext()检查序列中是否还有元素。
 (4) 使用remove()将迭代器新返回的元素删除。
 Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素

13、Java集合类框架的基本接口有哪些?
集合类接口指定了一组叫做元素的对象。集合类接口的每一种具体的实现类都可以选择以它自己的方式对元素进行保存和排序。有的集合类允许重复的键,有些不允许。
Java集合类提供了一套设计良好的支持对一组对象进行操作的接口和类。Java集合类里面最基本的接口有:
Collection:代表一组对象,每一个对象都是它的子元素。
Set:不包含重复元素的Collection。
List:有顺序的collection,并且可以包含重复元素。
Map:可以把键(key)映射到值(value)的对象,键不能重复。
补充:
Java集合类里最基本的接口有:
Collection:单列集合的根接口
List:元素有序 可重复
ArrayList:类似一个长度可变的数组 。适合查询,不适合增删
LinkedList:底层是双向循环链表。适合增删,不适合查询。
Set:元素无序,不可重复
HashSet:根据对象的哈希值确定元素在集合中的位置
TreeSet: 以二叉树的方式存储元素,实现了对集合中的元素排序
Map:双列集合的根接口,用于存储具有键(key)、值(value)映射关系的元素。
HashMap:用于存储键值映射关系,不能出现重复的键key
TreeMap:用来存储键值映射关系,不能出现重复的键key,所有的键按照二叉树的方式列

TreeSet和TreeMap底层是红黑树

为什么集合类没有实现Cloneable和Serializable接口?
克隆(cloning)或者是序列化(serialization)的语义和含义是跟具体的实现相关的。因此,应该由集合类的具体实现来决定如何被克隆或者是序列化。

12、什么是死锁(deadlock)?
死锁 :是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去

(1) 因为系统资源不足。
(2) 进程运行推进顺序不合适。
(3) 资源分配不当等。
如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则
就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。
1.互斥 2.保持锁并请求锁 3.不可抢夺 4.循环等待
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
死锁的解除与预防:
理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和
解除死锁。所以,在系统设计、进程调度等方面注意如何不让这四个必要条件成立,如何确定资源的合理分配算法,避免进程永久占据系统资源。此外,也要防止进程在处于等待状态的情况下占用资源。因此,对资源的分配要给予合理的规划。

如何确保N个线程可以访问N个资源同时又不导致死锁?
使用多线程的时候,一种非常简单的避免死锁的方式就是:指定获取锁的顺序,并强制线程按照指定的顺序获取锁。因此,如果所有的线程都是以同样的顺序加锁和释放锁,就不会出现死锁了。

11、在监视器(Monitor)内部,是如何做线程同步的?程序应该做哪种级别的同步?
以下为复制:这里不怎么了解
在 java 虚拟机中, 每个对象( Object 和 class )通过某种逻辑关联监视器,每个监视器和一个对象引用相关联, 为了实现监视器的互斥功能, 每个对象都关联着一把锁.

一旦方法或者代码块被 synchronized 修饰, 那么这个部分就放入了监视器的监视区域, 确保一次只能有一个线程执行该部分的代码, 线程在获取锁之前不允许执行该部分的代码

另外 java 还提供了显式监视器( Lock )和隐式监视器( synchronized )两种锁方案

10、概括的解释下线程的几种可用状态。
1. 新建( new ):新创建了一个线程对象。
2. 可运行( runnable ):线程对象创建后,其他线程(比如 main 线程)调用了该对象 的 start ()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获 取 cpu 的使用权 。
3. 运行( running ):可运行状态( runnable )的线程获得了 cpu 时间片( timeslice ) ,执行程序代码。
4. 阻塞( block ):阻塞状态是指线程因为某种原因放弃了 cpu 使用权,也即让出了 cpu timeslice ,暂时停止运行。直到线程进入可运行( runnable )状态,才有 机会再次获得 cpu timeslice 转到运行( running )状态。阻塞的情况分三种:
(一). 等待阻塞:运行( running )的线程执行 o . wait ()方法, JVM 会把该线程放 入等待队列( waitting queue )中。
(二). 同步阻塞:运行( running )的线程在获取对象的同步锁时,若该同步锁 被别的线程占用,则 JVM 会把该线程放入锁池( lock pool )中。
(三). 其他阻塞: 运行( running )的线程执行 Thread . sleep ( long ms )或 t . join ()方法,或者发出了 I / O 请求时, JVM 会把该线程置为阻塞状态。 当 sleep ()状态超时、 join ()等待线程终止或者超时、或者 I / O 处理完毕时,线程重新转入可运行( runnable )状态。
5. 死亡( dead ):线程 run ()、 main () 方法执行结束,或者因异常退出了 run ()方法,则该线程结束生命周期。死亡的线程不可再次复生。

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

①继承Thread类(真正意义上的线程类),是Runnable接口的实现。
②实现Runnable接口,并重写里面的run方法。
③使用Executor 框架创建线程池。Executor 框架是 juc 里提供的线程池的实现。
调用线程的start():启动此线程;调用相应的run()方法
继承于Thread类的线程类,可以直接调用start方法启动线程(使用static也可以实现资源共享).一个线程(对象)只能够执行一次start(),而且不能通过Thread实现类对象的run()去启动一个线程。
实现Runnable接口的类需要再次用Thread类包装后才能调用start方法。(三个Thread对象包装一个类对象,就实现了资源共享)。
线程的使用的话,注意锁和同步的使用。(多线程访问共享资源容易出现线程安全问题)
线程的同步见第四点。

一般情况下,常见的是第二种。
* Runnable接口有如下好处:
* ①避免点继承的局限,一个类可以继承多个接口。
* ②适合于资源的共享

/*
* Thread的常用方法:
* 1.start():启动线程并执行相应的run()方法
* 2.run():子线程要执行的代码放入run()方法中
* 3.currentThread():静态的,调取当前的线程
* 4.getName():获取此线程的名字
* 5.setName():设置此线程的名字
* 6.yield():调用此方法的线程释放当前CPU的执行权(很可能自己再次抢到资源)
* 7.join():在A线程中调用B线程的join()方法,表示:当执行到此方法,A线程停止执行,直至B线程执行完毕,
* A线程再接着join()之后的代码执行
* 8.isAlive():判断当前线程是否还存活
* 9.sleep(long l):显式的让当前线程睡眠l毫秒 (只能捕获异常,因为父类run方法没有抛异常)
* 10.线程通信(方法在Object类中):wait() notify() notifyAll()
*
* 设置线程的优先级(非绝对,只是相对几率大些)
* getPriority() :返回线程优先值
* setPriority(int newPriority) :改变线程的优先级
*/

8、进程和线程的区别是什么?
首先程序是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。
而进程是程序的一次执行过程,或是正在运行的一个程序。动态过程:有它自身的产生、存在和消亡的过程,是运行中的指令集合。

进程可进一步细化为线程,线程是一个程序内部的一条执行路径。
每个Java程序都有一个隐含的主线程
进程是分配资源的基本单位,而线程是独立运行和调度的基本单位。
任意时刻,一个CPU只能运行一个进程,进程获得资源后进行分配,由不同的线程来执行和协作。
1. 进程:程序的一次执行
2. 线程:CPU的基本调度单位
一个进程的内存空间是可以被线程共享的。
进程的内存空间一般是独立的,而线程的内存空间一般是共享的,线程间的同步是为了防止竞争(因同时修改导致数据的不一致),所以要使用互斥锁,防止多个线程同时读写某一块内存区域。
还有的内存区域只允许固定个数的线程进入,就要使用信号量,防止线程之间产生冲突。
互斥锁止允许一个线程进入临界区,而信号量允许多个线程同时进入临界区。

在系统编程中,进程通信和线程同步内容以后有时间再查资料。
线程与进程的区别归纳:
a.地址空间和其它资源:进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。
b.通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。(线程的通信参见同步代码块和同步方法那块)
c.调度和切换:线程上下文切换比进程上下文切换要快得多。
d.在多线程OS中,进程不是一个可执行的实体。

为什么要引入多线程:
①程序需要同时执行两个或多个任务
②程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。
③需要一些后台运行的程序时
Java垃圾回收中的守护线程也是必须的。

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

①值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量.
②引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身,所以对引用对象进行操作会同时改变原对象.
③一般认为,java内的传递都是值传递.
另外补充一点:
java中全部可以看成值传递,一个是原值,一个是地址值。但是注意一点,String类是一个特殊点,
因为new对象在栈中String str1 = new String(“xjp”); 值存在栈中
赋值对象在常量池中String str2=”xjp”; 值存在常量池中
String类可以当成值来传递,但是引用类型,比如String数组,按照引用类型来。
本来是值可以改变的。但是如果重新new了对象,那么原值也不变。

6、接口和抽象类的区别是什么?

①接口中所有的方法隐含的都是抽象的。而抽象类则可以同时包含抽象和非抽象的方法。
②类可以实现很多个接口,但是只能继承一个抽象类。但是接口间可以多继承。
③将类声明为抽象的,就可以不用实现接口或者抽象类的所有方法。
④Java接口中声明的变量默认都是final的。抽象类可以包含非final的变量。
Java接口中的成员函数默认是public的。抽象类的成员函数可以是private,protected或者是public。
⑤接口是绝对抽象的,不可以被实例化。抽象类也不可以被实例化,但是他们都可以new 对象,即便两者中都没有方法,匿名内部类的使用。

⑥如果抽象类中包含main方法的话是可以被调用的(通过类名的方式)。

5、Java中的方法覆盖(Overriding)和方法重载(Overloading)是什么意思?

overload 多个加载 所以在一个类中进行加载,方法名相同,参数个数或类型不同,与返回值无关。
override 覆盖、优先执行,指的是子类重写父类的方法,方法名、返回类型和参数必须相同。除了访问权限>=父类,抛出异常必须<=父类(或者不抛异常)。(特殊点,如果子类重写的方法所抛异常小于父类,那么就不能调用父类的方法,因为子类的异常小,捕获不了),private修饰的父类方法不能被重写。

4、同步方法和同步代码块的区别是什么?

①首先是为什么要使用同步这个概念:
java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查),将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,从而保证了该变量的唯一性和准确性。

线程的同步的弊端:由于同一个时间只能有一个线程访问共享数据,效率变低了。

②同步方法:(粗粒度锁):
1. 修饰一般方法: public synchronized void method (){…}, 获取的是当前调用 对象 this 上的锁
2. 修饰静态方法: public static synchronized void method (){…}, 获取当前类的 字节码对象上的锁

内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。

③同步代码块(细粒度锁):
synchronized ( obj-同步监视器) {…},
同步代码块可以指定获取哪个对象上的锁。
需要被同步的代码块(即为操作共享数据的代码)

同步监视器:由任意一个类的对象来充当。哪个线程获取此监视器,谁就执行大括号里被同步的代码。俗称:锁。要求:所有的线程必须共用同一把锁!

java 中每个对象都有一把锁, 线程可以通过 synchronized 关键字来获取对象上的锁。

注意:不能用synchronized修饰方法外面的语句块(类语句块),synchronized锁住的是对象,当初始化对象的时候,JVM在对象初始化完成之前会调用方法外面的语句块,这个时候对象还不存在,所以就不存在锁了。(可以使用类.class的形式代替,因为返回的是Class 的对象。)

还有就是同步代码块中的对象锁必须唯一:
第一种:如果是在实现了Runnable接口的类中,可以使用this充当
synchronized ( this) {…},因为实现类一般只new一个。
在实现类中new一个对象也可以,也就是用全部变量对象
(注意不能在方法中,局部变量对象会有很多,相当于多个锁独立,也不安全);

第二种:如果是继承了Thread类的线程类。谨慎使用this,因为会创建多次
同样,不能在方法里面去new 对象,在类中进行new 对象的时候还要注意:
必须声明为static的,因为线程类对象会new 多次,锁必须是同一个。

关于线程的通信:
线程通信。如下的三个关键字使用的话,都得在同步代码块或同步方法中。
* wait():一旦一个线程执行到wait(),就释放当前的锁。
* notify()/notifyAll():唤醒wait的一个(优先级或者随机)或所有的线程
* 使用两个线程打印 1-100. 线程1, 线程2 交替打印
*
* java.lang.IllegalMonitorStateException
* 因为以上的方法都在Object类里面声明的。而且必须在同步代码块或者同步方法里面使用才行。

④至于代码层面的解释理解之类的总结,以后遇到再详细学习。

3、Java支持的数据类型有哪些?什么是自动拆装箱?
基本数据类型:
整数值型:byte,short,int,long,
字符型:char
浮点类型:float,double
布尔型:boolean
整数默认int型,小数默认是double型。Float和long类型的必须加后缀。

首先知道String是引用类型不是基本类型,引用类型声明的变量是指该变量在内存中实际存储的是一个引用地址,实体在堆中。引用类型包括类、接口、数组等。 而包装类就属于引用类型。String类还是final修饰的。
自动装箱和拆箱就是基本类型和引用类型之间的转换,至于为什么要转换,因为基本类型转换为引用类型后,就可以new对象,从而调用包装类中封装好的方法进行基本类型之间的转换或者toString(当然用类名直接调用也可以,便于一眼看出该方法是静态的),还有就是如果集合中想存放基本类型,泛型的限定类型只能是对应的包装类型。

2、”static”关键字是什么意思?Java中是否可以覆盖(override)一个private或者是static的方法?

首先是static关键字,static原意是“静态的”。
①static可以修饰内部类,但是不能修饰普通类。静态内部类的话可以直接调用静态构造器(不用对象)。

②static修饰方法,static方法就是没有this的方法。在static方法内部不能调用非静态方法,反过来是可以的。而且可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法。这实际上正是static方法的主要用途。方便在没有创建对象的情况下来进行调用(方法/变量)。

最常见的static方法就是main,因为所有对象都是在该方法里面实例化的,而main是程序入口,所以要通过类名来调用。还有就是main中需要经常访问随类加载的成员变量。

③static修饰变量,就变成了静态变量,随类加载一次,可以被多个对象共享。

④static修饰代码块,形成静态代码块,用来优化程序性能,将需要加载一次的代码设置成随类加载,静态代码块可以有多个。
Java中static方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而static方法是编译时静态绑定的。
还有私有的方法不能被继承,子类就没有访问权限,肯定也是不能别覆盖的。

1、什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”?
虚拟机是一种抽象化的计算机,通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机有自己完善的硬体架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。JVM屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码( 字节码 ),就可以在多种平台上不加修改地运行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值