30道Java经典面试题总结

1、JDK 和 JRE 有什么区别?

JDK(Java Development Kit),Java 开发工具包

JRE(Java Runtime Environment),Java 运行环境

JDK 中包含 JRE,JDK 中有一个名为 jre 的目录,里面包含两个文件夹 bin 和 lib,bin 就是 JVM,lib 就是 JVM 工作所需要的类库。

2、== 和 equals 的区别是什么?

  1. 对于基本类型,== 比较的是值;
  2. 对于引用类型,== 比较的是地址;
  3. equals 不能用于基本类型的比较;
  4. 如果没有重写 equals,equals 就相当于 ==;
  5. 如果重写了 equals 方法,equals 比较的是对象的内容;

3、final 在 java 中有什么作用?

(1)用来修饰一个引用

  1. 如果引用为基本数据类型,则该引用为常量,该值无法修改;
  2. 如果引用为引用数据类型,比如对象、数组,则该对象、数组本身可以修改,但指向该对象或数组的地址的引用不能修改。
  3. 如果引用时类的成员变量,则必须当场赋值,否则编译会报错。

(2)用来修饰一个方法

当使用 final 修饰方法时,这个方法将成为最终方法,无法被子类重写。但是,该方法仍然可以被继承。

(3)用来修饰类

当用 final 修改类时,该类成为最终类,无法被继承。

比如常用的 String 类就是最终类。

4、java 中的 Math.round(-1.5) 等于多少?

Math 提供了三个与取整有关的方法:ceil、floor、round

(1)ceil:向上取整;

Math.ceil(11.3) = 12;

Math.ceil(-11.3) = 11;

(2)floor:向下取整;

Math.floor(11.3) = 11;

Math.floor(-11.3) = -12;

(3)round:四舍五入;

加 0.5 然后向下取整。

Math.round(11.3) = 11;

Math.round(11.8) = 12;

Math.round(-11.3) = -11;

Math.round(-11.8) = -12;

5、String 属于基础的数据类型吗?

不属于。

八种基本数据类型:byte、short、char、int、long、double、float、boolean。

6、String str=“i” 与 String str=new String(“i”) 一样吗?

String str=“i” 会将起分配到常量池中,常量池中没有重复的元素,如果常量池中存中 i,就将 i 的地址赋给变量,如果没有就创建一个再赋给变量。

String str=new String(“i”) 会将对象分配到堆中,即使内存一样,还是会重新创建一个新的对象。

7、如何将字符串反转?

将对象封装到 stringBuilder 中,调用 reverse 方法反转。

8、String 类的常用方法都有那些?

(1)常见 String 类的获取功能

length:获取字符串长度;
charAt(int index):获取指定索引位置的字符;
indexOf(int ch):返回指定字符在此字符串中第一次出现处的索引;
substring(int start):从指定位置开始截取字符串, 默认到末尾;
substring(int start,int end):从指定位置开始到指定位置结束截取字符串;

(2)常见 String 类的判断功能

equals(Object obj): 比较字符串的内容是否相同, 区分大小写;
contains(String str): 判断字符串中是否包含传递进来的字符串;
startsWith(String str): 判断字符串是否以传递进来的字符串开头;
endsWith(String str): 判断字符串是否以传递进来的字符串结尾;
isEmpty(): 判断字符串的内容是否为空串 “”;

(3)常见 String 类的转换功能

byte[] getBytes(): 把字符串转换为字节数组;
char[] toCharArray(): 把字符串转换为字符数组;
String valueOf(char[] chs): 把字符数组转成字符串。valueOf 可以将任意类型转为字符串;
toLowerCase(): 把字符串转成小写;
toUpperCase(): 把字符串转成大写;
concat(String str): 把字符串拼接;

(4)常见 String 类的其他常用功能

replace(char old,char new) 将指定字符进行互换
replace(String old,String new) 将指定字符串进行互换
trim() 去除两端空格
int compareTo(String str) 会对照 ASCII 码表 从第一个字母进行减法运算 返回的就是这个减法的结果,如果前面几个字母一样会根据两个字符串的长度进行减法运算返回的就是这个减法的结果,如果连个字符串一摸一样 返回的就是 0。

9、new String(“a”) + new String(“b”) 会创建几个对象?

对象 1:new StringBuilder()

对象 2:new String(“a”)

对象 3:常量池中的 “a”

对象 4:new String(“b”)

对象 5:常量池中的 “b”

深入剖析:StringBuilder 中的 toString():

对象 6:new String(“ab”)

强调一下,toString() 的调用,在字符串常量池中,没有生成 “ab”

附加题

String s1 = new String(“1”) + new String(“1”);//s1 变量记录的地址为:new String
s1.intern();// 在字符串常量池中生成 “11”。如何理解:jdk6:创建了一个新的对象 “11”,也就有新的地址;jdk7:此时常量池中并没有创建 “11”,而是创建了一个指向堆空间中 new String(“11”) 的地址;
String s2 = “11”;
System.out.println(s1 == s2);//jdk6:false;jdk7:true

10、如何将字符串反转?

添加到 StringBuilder 中,然后调用 reverse()。

11、String 类的常用方法都有那些?

equals、length、contains、replace、split、hashcode、indexof、substring、trim、toUpperCase、toLowerCase、isEmpty 等等。

12、普通类和抽象类有哪些区别?

抽象类不能被实例化;
抽象类可以有抽象方法,只需申明,无须实现;
有抽象方法的类一定是抽象类;
抽象类的子类必须实现抽象类中的所有抽象方法,否则子类仍然是抽象类;
抽象方法不能声明为静态、不能被 static、final 修饰。

13、接口和抽象类有什么区别?

(1)接口

接口使用 interface 修饰;
接口不能实例化;
类可以实现多个接口;

①java8 之前,接口中的方法都是抽象方法,省略了 public abstract。②java8 之后;接口中可以定义静态方法,静态方法必须有方法体,普通方法没有方法体,需要被实现;

(2)抽象类

抽象类使用 abstract 修饰;
抽象类不能被实例化;
抽象类只能单继承;
抽象类中可以包含抽象方法和非抽象方法,非抽象方法需要有方法体;
如果一个类继承了抽象类,①如果实现了所有的抽象方法,子类可以不是抽象类;②如果没有实现所有的抽象方法,子类仍然是抽象类。

14、java 中 IO 流分为几种?

(1)按流划分,可以分为输入流和输出流;

(2)按单位划分,可以分为字节流和字符流;

字节流:inputStream、outputStream;

字符流:reader、writer;

15、BIO、NIO、AIO 有什么区别?

(1)同步阻塞 BIO

一个连接一个线程。

JDK1.4 之前,建立网络连接的时候采用 BIO 模式,先在启动服务端 socket,然后启动客户端 socket,对服务端通信,客户端发送请求后,先判断服务端是否有线程响应,如果没有则会一直等待或者遭到拒绝请求,如果有的话会等待请求结束后才继续执行。

(2)同步非阻塞 NIO

NIO 主要是想解决 BIO 的大并发问题,BIO 是每一个请求分配一个线程,当请求过多时,每个线程占用一定的内存空间,服务器瘫痪了。

JDK1.4 开始支持 NIO,适用于连接数目多且连接比较短的架构,比如聊天服务器,并发局限于应用中。

一个请求一个线程。

(3)异步非阻塞 AIO

一个有效请求一个线程。

JDK1.7 开始支持 AIO,适用于连接数目多且连接比较长的结构,比如相册服务器,充分调用 OS 参与并发操作。

16、Files 的常用方法都有哪些?

exist
createFile
createDirectory
write
read
copy
size
delete
move

17、什么是反射?

所谓反射,是 java 在运行时进行自我观察的能力,通过 class、constructor、field、method 四个方法获取一个类的各个组成部分。

在 Java 运行时环境中,对任意一个类,可以知道类有哪些属性和方法。这种动态获取类的信息以及动态调用对象的方法的功能来自于反射机制。

18、什么是 java 序列化?什么情况下需要序列化?

序列化就是一种用来处理对象流的机制。将对象的内容流化,将流化后的对象传输于网络之间。

序列化是通过实现 serializable 接口,该接口没有需要实现的方法,implement Serializable 只是为了标注该对象是可被序列化的,使用一个输出流(FileOutputStream)来构造一个 ObjectOutputStream 对象,接着使用 ObjectOutputStream 对象的 writeObejct(Object object)方法就可以将参数的 obj 对象到磁盘,需要恢复的时候使用输入流。

序列化是将对象转换为容易传输的格式的过程。

例如,可以序列化一个对象,然后通过 HTTP 通过 Internet 在客户端和服务器之间传输该对象。在另一端,反序列化将从流中心构造成对象。

一般程序在运行时,产生对象,这些对象随着程序的停止而消失,但我们想将某些对象保存下来,这时,我们就可以通过序列化将对象保存在磁盘,需要使用的时候通过反序列化获取到。

对象序列化的最主要目的就是传递和保存对象,保存对象的完整性和可传递性。

譬如通过网络传输或者把一个对象保存成本地一个文件的时候,需要使用序列化。

19、为什么要使用克隆?如何实现对象克隆?深拷贝和浅拷贝区别是什么?

(1)什么要使用克隆?

想对一个对象进行复制,又想保留原有的对象进行接下来的操作,这个时候就需要克隆了。

(2)如何实现对象克隆?

实现 Cloneable 接口,重写 clone 方法;
实现 Serializable 接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深克隆。
BeanUtils,apache 和 Spring 都提供了 bean 工具,只是这都是浅克隆。

(3)深拷贝和浅拷贝区别是什么?

浅拷贝:仅仅克隆基本类型变量,不克隆引用类型变量;
深克隆:既克隆基本类型变量,又克隆引用类型变量;

(4)代码实例

20、throw 和 throws 的区别?

(1)throw

作用在方法内,表示抛出具体异常,由方法体内的语句处理;
一定抛出了异常;

(2)throws

作用在方法的声明上,表示抛出异常,由调用者来进行异常处理;
可能出现异常,不一定会发生异常;

21、final、finally、finalize 有什么区别?

final 可以修饰类,变量,方法,修饰的类不能被继承,修饰的变量不能重新赋值,修饰的方法不能被重写

finally 用于抛异常,finally 代码块内语句无论是否发生异常,都会在执行 finally,常用于一些流的关闭。

finalize 方法用于垃圾回收。

一般情况下不需要我们实现 finalize,当对象被回收的时候需要释放一些资源,比如 socket 链接,在对象初始化时创建,整个生命周期内有效,那么需要实现 finalize 方法,关闭这个链接。

但是当调用 finalize 方法后,并不意味着 gc 会立即回收该对象,所以有可能真正调用的时候,对象又不需要回收了,然后到了真正要回收的时候,因为之前调用过一次,这次又不会调用了,产生问题。所以,不推荐使用 finalize 方法。

22、try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?

23、常见的异常类有哪些?

  1. NullPointerException:空指针异常;
  2. SQLException:数据库相关的异常;
  3. IndexOutOfBoundsException:数组下角标越界异常;
  4. FileNotFoundException:打开文件失败时抛出;
  5. IOException:当发生某种 IO 异常时抛出;
  6. ClassCastException:当试图将对象强制转换为不是实例的子类时,抛出此异常;
  7. NoSuchMethodException:无法找到某一方法时,抛出;
  8. ArrayStoreException:试图将错误类型的对象存储到一个对象数组时抛出的异常;
  9. NumberFormatException:当试图将字符串转换成数字时,失败了,抛出;
  10. IllegalArgumentException 抛出的异常表明向方法传递了一个不合法或不正确的参数。
  11. ArithmeticException 当出现异常的运算条件时,抛出此异常。例如,一个整数 “除以零” 时,抛出此类的一个实例。

24、hashcode 是什么?有什么作用?

Java 中 Object 有一个方法:

public native int hashcode();

(1)hashcode() 方法的作用

hashcode() 方法主要配合基于散列的集合一起使用,比如 HashSet、HashMap、HashTable。

当集合需要添加新的对象时,先调用这个对象的 hashcode() 方法,得到对应的 hashcode 值,实际上 hashmap 中会有一个 table 保存已经存进去的对象的 hashcode 值,如果 table 中没有改 hashcode 值,则直接存入,如果有,就调用 equals 方法与新元素进行比较,相同就不存了,不同就存入。

(2)equals 和 hashcode 的关系

如果 equals 为 true,hashcode 一定相等;

如果 equals 为 false,hashcode 不一定不相等;

如果 hashcode 值相等,equals 不一定相等;

如果 hashcode 值不等,equals 一定不等;

(3)重写 equals 方法时,一定要重写 hashcode 方法

(4)百度百科

hashcode 方法返回该对象的哈希码值。支持该方法是为哈希表提供一些优点,例如,java.util.Hashtable 提供的哈希表。

hashCode 的常规协定是: 
在 Java 应用程序执行期间,在同一对象上多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上 equals 比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。 
如果根据 equals(Object) 方法,两个对象是相等的,那么在两个对象中的每个对象上调用 hashCode 方法都必须生成相同的整数结果。 
以下情况不 是必需的:如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么在两个对象中的任一对象上调用 hashCode 方法必定会生成不同的整数结果。但是,程序员应该知道,为不相等的对象生成不同整数结果可以提高哈希表的性能。 
实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。)

当 equals 方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。

(5)小白解释

1.hashcode 是用来查找的,如果你学过数据结构就应该知道,在查找和排序这一章有
例如内存中有这样的位置
0  1  2  3  4  5  6  7  
而我有个类,这个类有个字段叫 ID, 我要把这个类存放在以上 8 个位置之一,如果不用 hashcode 而任意存放,那么当查找时就需要到这八个位置里挨个去找,或者用二分法一类的算法。
但如果用 hashcode 那就会使效率提高很多。
我们这个类中有个字段叫 ID, 那么我们就定义我们的 hashcode 为 ID%8,然后把我们的类存放在取得得余数那个位置。比如我们的 ID 为 9,9 除 8 的余数为 1,那么我们就把该类存在 1 这个位置,如果 ID 是 13,求得的余数是 5,那么我们就把该类放在 5 这个位置。这样,以后在查找该类时就可以通过 ID 除 8 求余数直接找到存放的位置了。

  1. 但是如果两个类有相同的 hashcode 怎么办那(我们假设上面的类的 ID 不是唯一的),例如 9 除以 8 和 17 除以 8 的余数都是 1,那么这是不是合法的,回答是:可以这样。那么如何判断呢?在这个时候就需要定义 equals 了。
    也就是说,我们先通过 hashcode 来判断两个类是否存放某个桶里,但这个桶里可能有很多类,那么我们就需要再通过 equals 来在这个桶里找到我们要的类。
    那么。重写了 equals(),为什么还要重写 hashCode() 呢?
    想想,你要在一个桶里找东西,你必须先要找到这个桶啊,你不通过重写 hashcode() 来找到桶,光重写 equals() 有什么用啊。

25、java 中操作字符串都有哪些类?它们之间有什么区别?

(1)String

String 是不可变对象,每次对 String 类型的改变时都会生成一个新的对象。

(2)StringBuilder

线程不安全,效率高,多用于单线程。

(3)StringBuffer

线程安全,由于加锁的原因,效率不如 StringBuilder,多用于多线程。

不频繁的字符串操作使用 String,操作频繁的情况不建议使用 String。

StringBuilder > StringBuffer > String。

26、java 中都有哪些引用类型?

(1)强引用

Java 中默认声明的就是强引用,比如:

Object obj = new Object();
obj = null;
  • 1
  • 2

只要强引用存在,垃圾回收器将永远不会回收被引用的对象。如果想被回收,可以将对象置为 null;

(2)软引用(SoftReference)

在内存足够的时候,软引用不会被回收,只有在内存不足时,系统才会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会跑出内存溢出异常。

byte[] buff = new byte[1024 * 1024];
SoftReference<byte[]> sr = new SoftReference<>(buff);

(3)弱引用(WeakReference)

进行垃圾回收时,弱引用就会被回收。

(4)虚引用(PhantomReference)

(5)引用队列(ReferenceQueue)

引用队列可以与软引用、弱引用、虚引用一起配合使用。

当垃圾回收器准备回收一个对象时,如果发现它还有引用,就会在回收对象之前,把这个引用加入到引用队列中。

程序可以通过判断引用队列中是否加入了引用,来判断被引用的对象是否将要被垃圾回收,这样可以在对象被回收之前采取一些必要的措施。

27、在 Java 中,为什么不允许从静态方法中访问非静态变量?

  1. 静态变量属于类本身,在类加载的时候就会分配内存,可以通过类名直接访问;
  2. 非静态变量属于类的对象,只有在类的对象产生时,才会分配内存,通过类的实例去访问;
  3. 静态方法也属于类本身,但是此时没有类的实例,内存中没有非静态变量,所以无法调用。

28、说说 Java Bean 的命名规范

  1. JavaBean 类必须是一个公共类,并将其访问属性设置为 public
  2. JavaBean 类必须有一个空的构造函数:类中必须有一个不带参数的公用构造器,此构造器也应该通过调用各个特性的设置方法来设置特性的缺省值。
  3. 一个 javaBean 类不应有公共实例变量,类变量都为 private
  4. 持有值应该通过一组存取方法(getXxx 和 setXxx)来访问:对于每个特性,应该有一个带匹配公用 getter 和 setter 方法的专用实例变量。

属性为布尔类型,可以使用 isXxx() 方法代替 getXxx() 方法。

通常属性名是要和 包名、类名、方法名、字段名、常量名作出区别的:

首先: 必须用英文,不要用汉语拼音

(1)包 (package)

用于将完成不同功能的类分门别类,放在不同的目录 (包) 下,包的命名规则:将公司域名反转作为包名。比如 www.sohu.com 对于包名:每个字母都需要小写。比如:com.sohu.test; 该包下的 Test 类的全名是:com.sohu.Test.Java 。

如果定义类的时候没有使用 package, 那么 java 就认为我们所定义的类位于默认包里面 (default package)。

(2)类

首字母大写,如果一个类由多个单词构成,那么每个单词的首字母都大写,而且中间不使用任何的连接符。尽量使用英文。如 ConnectionFactory

(3)方法

首单词全部小写,如果一个方法由多个单词构成,那么从第二个单词开始首字母大写,不使用连接符。addPerson

(4)字段

与方法相同。如 ageOfPerson

(5)常量

所有单词的字母都是大写,如果有多个单词,那么使用下划线链接即可。

如:public static final int AGE_OF_PERSON = 20; // 通常加上 static

29、Java Bean 属性命名规范问题分析

public class User {
	private String busName;
	private String pCount;
	private Boolean isRunning;
	//正确的命名方式,驼峰式的
	public String getBusName() {
		return busName;
	}
	public void setBusName(String busName) {
		this.busName = busName;
	}
    //这是什么?
	public String getpCount() {
		return pCount;
	}
	public void setpCount(String pCount) {
		this.pCount = pCount;
	}
    //这个也是不允许的
	public Boolean getIsRunning() {
		return isRunning;
	}
	public void setIsRunning(Boolean isRunning) {
		this.isRunning = isRunning;
	}
}
  1. javabean 属性命名尽量使用常规的驼峰式命名规则
  2. 属性名第一个单词尽量避免使用一个字母:如 eBook, eMail。
  3. boolean 属性名避免使用 “is” 开头的名称
  4. 随着 jdk, eclipse, spring 等软件版本的不断提高, 底版本的出现的问题可能在高版本中解决了, 低版本原来正常的代码可能在高版本环境下不再支持。

30、什么是 Java 的内存模型?

在了解什么是 Java 内存模型之前,先了解一下为什么要提出 Java 内存模型。

之前提到过并发编程有三大问题

CPU 缓存,在多核 CPU 的情况下,带来了可见性问题
操作系统对当前执行线程的切换,带来了原子性问题
译器指令重排优化,带来了有序性问题
为了解决并发编程的三大问题,提出了 JSR-133,新的 Java 内存模型,JDK 5 开始使用。

简单总结下

Java 内存模型是 JVM 的一种规范
定义了共享内存在多线程程序中读写操作行为的规范
屏蔽了各种硬件和操作系统的访问差异,保证了 Java 程序在各种平台下对内存的访问效果一致
解决并发问题采用的方式:限制处理器优化和使用内存屏障
增强了三个同步原语(synchronized、volatile、final)的内存语义
定义了 happens-before 规则

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值