Java面试题整理
Java基础
1.JVM、JDK 和 JRE 的关系?
-
JVM:Java Virtual Machine是Java虚拟机,Java程序需要运行在虚拟机上,不同的平台有自己的虚拟机,因此Java语言可以实现跨平台,Java虚拟机包括:寄存器,堆栈,处理器 。
-
JDK:Java Development Kit是提供给Java开发人员使用的,其中包含了Java的开发工具,也包括了JRE。所以安装了JDK,也就无需再单独安装JRE了。其中的开发工具:编译工具(javac.exe),打包工具(jar.exe)等。
-
JRE:Java Runtime Enviroment包括Java虚拟机和Java程序所需的核心库等。核心类库主要是java.lang包:包含了运行Java程序必不可少的系统类,如基本数据类型、基本数学函数、字符串处理、线程、异常处理类等,系统缺省加载这个包。如果想要运行一个开发好的Java程序,计算机中只需要安装JRE即可。
2.Java 语言面向对象有哪些特性?
- 封装:隐藏对象的属性和实现细节,仅对外提供公共访问方式,将变化隔离,便于使用,提高复用性和安全性。
- 继承:继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承可以提高代码复用性。继承是多态的前提。
- 多态:父类或接口定义的引用变量可以指向子类或具体实现类的实例对象。提高了程序的拓展性。
3.Java 的数据结构有哪些?
- 线性表(ArrayList)
- 链表(LinkedList)
- 栈(Stack)
- 队列(Queue)
- 图(Map)
- 树(Tree)
4.static 关键字有什么作用?
- static 可以修饰内部类、方法、变量、代码块。
- static 修饰的类是静态内部类。
- static 修饰的方法是静态方法,表示该方法属于当前类的,而不属于某个对象的,静态方法也不能被重写,可以直接使用类名来调用。在 static 方法中不能使用 this 或者 super 关键字。
- static 修饰的变量是静态变量或者叫类变量,静态变量被所有实例所共享,不会依赖于对象。静态变量在内存中只有一份拷贝,在JVM加载类的时候,职位静态分配一次内存。
- static 修饰的代码块叫静态代码块,通常用来做程序优化的。静态代码块中的代码在整个类加载的时候只会执行一次。静态代码块可以有多个,如果有多个,按照先后顺序依次执行。
5.String 属于基础的数据类型吗?
- String 不属于基础类型,基础类型有 8 种:byte、boolean、char、short、int、float、long、double,而 String 属于对象。
6.Java 中操作字符串都有哪些类? 它们之间有什么区别?
-
操作字符串的类有:String、StringBuffer、StringBuilder。
-
String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。
-
StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。
7.== 和 equals 的区别是什么?
- 对于基本类型和引用类型 == 的作用效果是不同的;基本类型比较的是值是否相同;引用类型比较的是引用是否相同。
- equals 本质上就是 ==,只不过 String 和 Integer 等重写了 equals 方法,把它变成了值比较。
String x = "string"; String y = "string"; String z = new String("string"); System.out.println(x==y); // true System.out.println(x==z); // false System.out.println(x.equals(y)); // true System.out.println(x.equals(z)); // true /**代码解读:因为 x 和 y 指向的是同一个引用,所以 == 也是 true, 而 new String()方法则重写开辟了内存空间,所以 == 结果为 false, 而 equals 比较的一直是值,所以结果都为 true。**/
8.final、finally 和 finalize 的区别是什么?
-
final:可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表示该变量是一个常量不能被重新赋值。
-
finally:一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码方法finally代码块中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。
-
finalize:是一个方法,属于Object类的一个方法,而Object类是所有类的父类,该方法一般由垃圾回收器来调用,当我们调用System.gc() 方法的时候,由垃圾回收器调用finalize(),回收垃圾,一个对象是否可回收的最后判断。
9.throw 和 throws 的区别是什么?
Java 中的异常处理除了包括捕获异常和处理异常之外,还包括声明异常和拋出异常,可以通过 throws 关键字在方法上声明该方法要拋出的异常,或者在方法内部通过 throw 拋出异常对象。
-
throw:用在方法内部,只能用于抛出一种异常,用来抛出方法或代码块中的异常,受查异常和非受查异常都可以被抛出,一般抛出为一个具体的异常类型。
-
throws:用在方法声明上,可以抛出多个异常,用来标识该方法可能抛出的异常列表。一个方法用 throws 标识了可能抛出的异常列表,调用该方法的方法中必须包含可处理异常的代码,否则也要在方法签名中用 throws 关键字声明相应的异常,重要的是只是声明了异常但并没有进行处理,处理异常的步骤都留给了调用者的实现。
10.Java 反射机制的优缺点?
- 优点:运行期类型的判断,动态加载类,提高代码灵活度。(一般在框架设计,动态实现中使用)。
- 缺点:性能瓶颈,反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的 java 代码要慢很多。
11.Java 获取反射的三种方法?
- 代码示例如下:
//方式一(通过new对象实现反射机制) Student stu = new Student(); Class classobj1 = stu.getClass(); System.out.println(classobj1.getName()); //方式二(通过路径(相对路径)实现反射机制) Class classobj2 = Class.forName("guanyue.Student"); System.out.println(classobj2.getName()); //方式三(通过类名实现反射机制) Class classobj3 = Student.class; System.out.println(classobj3.getName());
12.解释内存中的栈(stack)、堆(heap)和静态存储区的用法?
1)堆区:专门用来保存对象的实例(new 创建的对象和数组),实际上也只是保存对象实例的属性值,属性的类型和对象本身的类型标记等,并不保存对象的方法(方法是指令,保存在 stack 中)。
- 存储的全部是对象,每个对象都包含一个与之对应的 class 的信息(class 的目的是得到操作指令)。
- JVM 只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身。
- 一般由程序员分配释放,若程序员不释放,程序结束时可能又 OS 回收。
2)栈区:对象实例在 heap 中分配好以后,需要在 stack 中保存一个4字节的 heap 内存地址,用来定位该对象实例在 heap 中的位置,便于找到该对象实例。
- 每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中。
- 每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
- 栈分为三个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
- 由编译器自动分配释放,存放函数的参数值,局部变量的值等。
3)静态区/方法区:
- 方法区又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的 class 和 static 变量。
- 方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
- 静态常量存放在方法区的常量中,静态常量初始化后不可更改。
堆和栈是程序的运行关键,应该将其理解清楚。栈是运行时的单位,而堆是存储时的单位。堆中存的是对象。栈中存的是基本数据类型和堆中对象的引用。一个对象的大小是不可估计的,或者说是可以动态变化的,但是在栈中,一个对象只对应了一个4字节的引用(堆栈分离的好处)。
那为什么要把堆和栈区分出来呢?栈中不是也可以存储数据吗?
-
第一,从软件设计的角度看,栈代表了处理逻辑,而堆代表了数据。这样分开,使得处理逻辑更为清晰。分而治之的思想,这种隔离、模块化的思想在软件设计的方方面面都有体现。
-
第二,堆与栈的分离,使得堆中的内容可以被多个栈共享(也可以理解为多个线程访问同一个对象)。这种共享的收益是很多的一方面这种共享提供了一种有效地数据交互方式(如:共享内存),另一方面,堆中的共享常量和缓存可以被所有栈访问,节省了空间。
-
第三,栈因为运行时的需要,比如保存系统运行的上下文,需要进行地址段的划分。由于栈只能向上增长,因此就会限制住栈存储内容的能力。而堆不同,堆中的对象是可以根据需要动态增长的,因此栈和堆的拆分,使得动态增长成为可能,相应栈中只需要记录堆中的一个地址即可。
-
第四,面向对象就是堆和栈的完美结合。其实,面向对象方式的程序与以前结构化的程序在执行上没有任何区别。但是面向对象的引入,使得对待问题的思考方式发生了改变,而更接近于自然方式的思考。当我们把对象拆开,你会发现,对象的属性其实就是数据,存放在堆中;而对象行为(方法),就是运行逻辑,放在栈中。我们在编写对象的时候,其实即编写了数据结构,也编写了处理数据的逻辑。不得不承认,面向对象的设计,确实很美。