这两天在网上找了很多篇的Java面试题及答案的文章,整理出面试可能都会问到的问题。同时整理出了一些答案和一些代码步骤,希望对大家都有用。在这里也真心祝愿大家早日拿到自己心仪的offer!
一、JDK 和 JRE 的区别是什么?
JDK:Java Development Kit 的简称,java 开发工具包,提供了 java 的开发环境和运行环境。
JRE:Java Runtime Environment 的简称,java 运行环境,为 java 的运行提供了所需环境。
总结:JDK 包含了 JRE,同时还包含了编译 java 源码的编译器 javac,还包含了很多 java 程序调试和分析的工具。简单来说:如果你需要运行 java 程序,只需安装 JRE 就可以了,如果你需要编写 java 程序,就需要安装 JDK。
二、Java和C++的区别?
我知道很多人没学过C++,但是面试官就是没事喜欢拿咱们Java和C++比呀!没办法!!!就算没学过C++,也要记下来!
·都是面向对象的语言,都支持封装、继承和多态
·Java不提供指针来直接访问内存,程序内存更加安全
·Java的类是单继承的,C++支持多重继承;虽然Java的类不可以多继承,但是接口可以多继承。
·Java有自动内存管理机制,不需要程序员手动释放无用内存
·在C语言中,字符串或字符数组最后都会有一个额外的字符\o'来表示结束。但是,Java语言中没有结束符这一概念。
三、import java 和javax有什么区别?
刚开始的时候JavaAPI所必需的包是java开头的包,javax当时只是扩展API包来使用。然而随着时间的推移,javax逐渐地扩展成为JavaAPI的组成部分。但是,将扩展从javax包移动到java包确实太麻烦了,最终会破坏一堆现有的代码。因此,最终决定javax包将成为标准API的一部分。
所以,实际上java和javax没有区别。这都是一个名字。
四、Java泛型了解么?什么是类型擦除?介绍一下常用的通配符?
Java泛型(generics)是JDK5中引入的一个新特性,泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
Java的泛型是伪泛型,这是因为Java在编译期间,所有的泛型信息都会被擦掉,这也就是通常所说类型擦除。
List<Integer> list=new Arraylist<>();list.add(12);
//这里直接添加会报错
list.add("a");Class<?extends List> clazz=list.getClass();Method add=clazz.getDeclaredMethod("add",Object.class);
/∥但是通过反射添加,是可以的
add.invoke(list,"kl");System.out.println(list)
泛型一般有三种使用方式:泛型类、泛型接口、泛型方法。
三.、== 和 equals 的区别是什么?
5五
基本类型和引用类型比较,== 的作用效果是不同的。
基本类型:比较的是值是否相同
引用类型:比较的是引用是否相同
1、int x = 10;
2、int y = 10;
3、String a = "panda";
4、String b = "panda";
5、String c = new String("panda");
6、// true 基本类型比较值是否相同
7、System.out.println(x == y);
8、// true 引用类型比较引用是否相同,这里引用相同
9、System.out.println(a == b);
10、// false 引用不同
11、System.out.println(a == c);
12、// true 引用不同,String重写了equals,使其用值比较
13、System.out.println(a.equals(c));
equals 本质上就是 ==,Object类中定义的 equals 方法如下
1、public boolean equals(Object obj) {
2、return (this == obj);
3、}
总结:== 对于基本类型比较的是值,对于引用类型比较的是引用;而 equals 默认情况下是引用比较,只是很多类重写了 equals 方法,比如 String、Integer 等把它变成了值比较,所以一般情况下 equals 比较的是值是否相等。
六、Java 中 final 关键字的作用是什么?
final 修饰的类叫最终类,不能被继承
final 修饰的方法叫最终方法,不能被重写,但可以被继承
final 修饰的变量叫常量,必须初始化,初始化之后值不能被修改
七、String 类中常用方法都有哪些?
1、String str = " app le ";
2、// indexOf(): 返回指定字符的索引
3、System.out.println(str.indexOf("a")); // 1
4、// charAt(): 返回指定索引处的字符
5、System.out.println(str.charAt(5)); // l
6、// replace(): 字符串替换
7、System.out.println(str.replace("pp", "cc")); // " acc le "
8、// trim(): 去除字符串两端空白
9、System.out.println(str.trim()); // "app le"
10、// split(): 分割字符串,返回一个分割后的字符串数组
11、String[] arr = str.split(" ");
12、// getBytes(): 返回字符串的 byte 类型数组
13、byte[] bytes = str.getBytes();
14、// length(): 返回字符串长度
15、System.out.println(str.length()); // 8
16、// toLowerCase(): 将字符串转成小写字母
17、System.out.println(str.toLowerCase()); // " app le "
18、// toUpperCase(): 将字符串转成大写字符
19、System.out.println(str.toUpperCase()); // " APP LE "
20、// substring(): 截取字符串
21、System.out.println(str.substring(2)); // "pp le "
22、// equals(): 字符串比较
23、System.out.println("apple".equals(str)); // false
八、抽象类是什么?
拥有抽象方法(指没有方法体的方法,同时抽象方法还必须使用关键字abstract 做修饰)的类就是抽象类,抽象类要使用 abstract 关键字声明。
九、 抽象类必须要有抽象方法吗?
不需要,抽象类不一定非要有抽象方法,如下代码可以正常运行
1、public abstract class elephant {
2、String str = "apple";
3、public void test01(){
4、System.out.println("aaaa");
5、}
6、}
十、普通类和抽象类的区别是什么?
普通类不能包含抽象方法,抽象类可以有抽象方法
普通类可以直接实例化,抽象类不能直接实例化
十一、Files 的常用方法都有哪些?
1、// Files.exists():检测文件路径是否存在
2、Path path1 = Paths.get("C:\\Users\\e-yangfangchao\\Desktop\\test");
3、System.out.println(Files.exists(path1, new LinkOption[]{LinkOption.NOFOLLOW_LINKS})); // true
4、// Files.createFile():创建文件
5、Path path2 = Paths.get("C:\\Users\\e-yangfangchao\\Desktop\\test\\a.txt");
6、try {
7、Path newFilw = Files.createFile(path2);
8、} catch (
FileAlreadyExistsException e){
9、System.out.println("exists");
10、} catch (IOException e) {
11、System.out.println("other wrong");
12、}
13、// Files.createDirectory():创建文件夹
14、Path path3 = Paths.get("C:\\Users\\e-yangfangchao\\Desktop\\test\\newDirectory");
15、try {
16、Path newDirectory = Files.createDirectory(path3);
17、} catch (
FileAlreadyExistsException e) {
18、System.out.println("exists");
19、} catch (IOException e){
20、System.out.println("other wrong");
21、}
22、// Files.delete():删除一个文件或目录
23、try {
24、Files.delete(path2);
25、} catch (IOException e) {
26、e.printStackTrace();
27、}
28、// Files.copy():复制文件
29、Path source = Paths.get("C:\\Users\\e-yangfangchao\\Desktop\\test\\b.txt");
30、Path target = Paths.get("C:\\Users\\e-yangfangchao\\Desktop\\test\\newb.txt");
31、try {
32、Files.copy(source,target);
33、} catch (
FileAlreadyExistsException e) {
34、System.out.println("targetFile already exists");
35、} catch (IOException e){
36、System.out.println("other wrong");
37、}
38、// Files.move():移动文件
39、Path source1 = Paths.get("C:\\Users\\e-yangfangchao\\Desktop\\test\\b.txt");
40、Path target1 = Paths.get("C:\\Users\\e-yangfangchao\\Desktop\\test\\newDirectory\\a.txt");
41、try {
42、Files.move(source1,target1);
43、} catch (
FileAlreadyExistsException e) {
44、System.out.println("targetFile1 already exists");
45、} catch (IOException e){
46、System.out.println("other wrong");
47、}
48、// Files.size():查看文件个数
49、// Files.read():读取文件
50、// Files.write():写入文件
十二、Java 中都有哪些容器?
十三、如何实现数组和List之间的转换?
数组转 List ,使用 JDK 中 java.util.Arrays 工具类的 asList 方法
public static void testArray2List() {
String[] strs = new String[] {"aaa", "bbb", "ccc"};
List<String> list = Arrays.asList(strs);
for (String s : list) {
System.out.println(s);
}
}
List 转数组,使用 List 的 toArray 方法。无参 toArray 方法返回 Object 数组,传入初始化长度的数组对象,返回该对象数组
public static void testList2Array() {
List<String> list = Arrays.asList("aaa", "bbb", "ccc");
String[] array = list.toArray(new String[list.size()]);
for (String s : array) {
System.out.println(s);
}
}
十四、Queue的add()和offer()方法有什么区别?
- Queue 中 add() 和 offer() 都是用来向队列添加一个元素。
- 在容量已满的情况下,add() 方法会抛出IllegalStateException异常,offer() 方法只会返回 false 。
JDK1.8 源码中的解释
/**
* Inserts the specified element into this queue if it is possible to do so
* immediately without violating capacity restrictions, returning
* {@code true} upon success and throwing an {@code IllegalStateException}
* if no space is currently available.
*
* @param e the element to add
* @return {@code true} (as specified by {@link Collection#add})
* @throws IllegalStateException if the element cannot be added at this
* time due to capacity restrictions
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this queue
* @throws NullPointerException if the specified element is null and
* this queue does not permit null elements
* @throws IllegalArgumentException if some property of this element
* prevents it from being added to this queue
*/
boolean add(E e);
/**
* Inserts the specified element into this queue if it is possible to do
* so immediately without violating capacity restrictions.
* When using a capacity-restricted queue, this method is generally
* preferable to {@link #add}, which can fail to insert an element only
* by throwing an exception.
*
* @param e the element to add
* @return {@code true} if the element was added to this queue, else
* {@code false}
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this queue
* @throws NullPointerException if the specified element is null and
* this queue does not permit null elements
* @throws IllegalArgumentException if some property of this element
* prevents it from being added to this queue
*/
boolean offer(E e);
十五、创建线程有哪几种方式?
① 继承 Thread 类创建线程
1、public class MyThread extends Thread{
2、@Override
3、public void run() {
4、System.out.println("run task");
5、}
6、}
7、public class Demo {
8、public static void main(String[] args) {
9、MyThread myThread = new MyThread();
10、myThread.start();
11、}
12、}
② 实现 Runnable 接口创建线程
1、public class MyThread implements Runnable{
2、@Override
3、public void run() {
4、System.out.println("run task");
5、}
6、}
7、public class Demo {
8、public static void main(String[] args) {
9、MyThread myThread = new MyThread();
10、Thread thread = new Thread(myThread);
11、thread.start();
12、}
13、}
③ 通过 Callable 和 Future 创建线程
1、public class MyThread implements Callable {
2、@Override
3、public Object call() {
4、System.out.println("run!");
5、return "run task success";
6、}
7、}
8、public class Demo {
9、public static void main(String[] args) throws ExecutionException, InterruptedException {/10、MyThread myThread = new MyThread();
11、FutureTask futureTask = new FutureTask<String>(myThread);
12、Thread thread = new Thread(futureTask);
13、thread.start();
14、// 获得子线程执行结束后的返回值
15、System.out.println(futureTask.get()); // run task success
16、}
17、}
十六、线程有哪些状态?
线程通常都有五种状态,创建、就绪、运行、阻塞和死亡。
创建状态:在生成线程对象,并没有调用该对象的start方法,这时线程处于创建状态。
就绪状态:当调用了线程对象的 start 方法之后,该线程就进入了就绪状态,但是此时线程调度程序还没有把该线程设置为当前线程,此时处于就绪状态。在线程运行之后,从等待或者睡眠中回来之后,也会处于就绪状态。
运行状态:线程调度程序将处于就绪状态的线程设置为当前线程,此时线程就进入了运行状态,开始运行 run 函数当中的代码。
阻塞状态:线程正在运行的时候,被暂停,通常是为了等待某个事件的发生(比如说某项资源就绪)之后再继续运行。sleep,suspend,wait 等方法都可以导致线程阻塞。
死亡状态:如果一个线程的 run 方法执行结束或者调用 stop 方法后,该线程就会死亡。对于已经死亡的线程,无法再使用 start 方法令其进入就绪。
十七、sleep() 和 wait() 有什么区别?
sleep():是线程类(Thread)的静态方法,让调用线程进入睡眠状态,让出执行机会给其他线程,等到休眠时间结束后,线程进入就绪状态和其他线程一起竞争cpu的执行时间。因为 sleep() 是 static 静态的方法,他不能改变对象的锁,当一个 synchronized 块中调用了 sleep() 方法,线程虽然进入休眠,但是对象的锁没有被释放,其他线程依然无法访问这个对象。
wait():是 Object 类的方法,当一个线程执行到 wait 方法时,它就进入到一个和该对象相关的等待池,同时释放对象的锁,使其他线程能够访问,可以通过 notify,notifyAll 方法来唤醒等待的线程。
十八、线程池都有哪些状态?
线程池有5种状态:Running、ShutDown、Stop、Tidying、Terminated。
Running:这是最正常的状态,接受新的任务,处理等待队列中的任务。线程池的初始化状态是Running。线程池被一旦被创建,就处于 Running 状态,并且线程池中的任务数为 0
ShutDown:不接受新的任务提交,但是会继续处理等待队列中的任务。调用线程池的 shutdown() 方法时,线程池由Running -> ShutDown。
Stop:不接受新的任务提交,不再处理等待队列中的任务,中断正在执行任务的线程。调用线程池的shutdownNow() 方法时,线程池由 (Running or ShutDown) -> Stop。
Tidying:所有的任务都销毁了,workCount 为 0,线程池的状态在转换为 Tidying 状态时,会执行钩子方法 terminated()。因为 terminated() 在 ThreadPoolExecutor 类中是空的,所以用户想在线程池变为 Tidying 时进行相应的处理;可以通过重载 terminated() 函数来实现。当线程池在 ShutDown 状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 ShutDown -> Tidying。当线程池在Stop 状态下,线程池中执行的任务为空时,就会由Stop -> Tidying。
Terminated:线程池处在 Tidying 状态时,执行完 terminated() 之后,就会由 Tidying -> Terminated。
十九、线程池中 submit() 和 execute() 方法有什么区别?
接收的参数不一样
1、submit(Runnable task)
2、submit(Runnable task,T result)
3、submit(Callable task)
4、execute(Runnable command)
submit() 有返回值,而 execute() 没有。用到返回值的例子,比如说我有很多个做 validation 的task,我希望所有的 task 执行完,然后每个 task 告诉我它的执行结果,是成功还是失败,如果是失败,原因是什么。
submit()方便 Exception 处理,如果你在你的 task 里会抛出 checked 或者 unchecked exception,而你又希望外面的调用者能够感知这些 exception 并做出及时的处理,那么就需要用到submit,通过捕获 Future.get 抛出的异常。
二十、synchronized 和 volatile 的区别是什么?
volatile 本质是在告诉 jvm 当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;synchronized 则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
volatile 仅能使用在变量级别;synchronized 则可以使用在变量、方法、和类级别的。
volatile 仅能实现变量的修改可见性,不能保证原子性;而 synchronized 则可以保证变量的修改可见性和原子性。
volatile 不会造成线程的阻塞;synchronized 可能会造成线程的阻塞。
volatile 标记的变量不会被编译器优化;synchronized 标记的变量可以被编译器优化。
今天关于Java面试的内容就分享到这里了,需要获取PDF版本的小伙伴点击下方名片免费获取,欢迎留言~