java后端面试题大全
- 1.java基础
- 1.1 java中==和equals的区别
- 1.2 String、StringBuffer、StringBuilder的区别
- 1.3 intern方法的作用及原理
- 1.4 String不可变的含义
- 1.5 static用法、使用位置、实例
- 1.6 为什么静态方法不能调用非静态方法和变量
- 1.7 异常/Exception
- 1.7 try/catch/finally的return顺序
- 1.8 字符流和字节流
1.java基础
1.1 java中==和equals的区别
- 等等于(==)是运算符, 如果是基本数据类型, 则比较存储的值; 如果是引用数据类型, 则比较所指向对象的地址值 (是否同一个对象)
- equals是Object的方法, 如果类未重写equals方法, 则相当于==, 一般情况下, 类会重写equals方法来比较两个对象的内容是否相等
public boolean equals(Object obj) {return (this == obj);
}
面试题:
一、下面的代码将创建几个字符串对象
String s1 = new String(“Hello”);
String s2 = new String(“Hello”);
答案: 3个
new这个关键词, 毫无以为会在堆中分配内存, 创建一个String类对象, 因此s1在栈中的引用指向堆中的这个String对象, 因为"Hello"是一个常量, 所以会去常量池中找有没有这个常量存在, 没有的话会在常量池中分配一个空间, 存储这个常量, 并将这个常量对象的空间地址给到堆中的String对象; 如果常量池中已经有了这个常量,就直接用那个常量池中的常量对象的引用呗,就只需要创建一个堆中的String对象。
注意: JDK1.7之后, 方法区的常量池被移动到堆中
二、字面量+字面量
创建了一个对象
String s = "abc" + "def"
当+号两端都是编译期确定的字符串常量时, 编译器会进行相应的优化, 直接将两个字符串常量拼接好, 放到常量池中, 所以只会创建一个"abcdef"对象
二、字面量+对象
创建了三个对象
String s1="abc";
String s2 ="abc"+s1+"def";
s1="abc"一个
s2中:"abc"已经存在,不再创建,"def"一个,"abcdef"一个
三、new String(“xx”) + new String(“xx”)
创建4个String对象
String s = new String("abc") + new String("abc");
第一个new String(“abc”)创建了两个
第二个new String(“abc”)创建了一个(常量池中有"abc"不再创建)
new String(“abc”) + new String(“abc”)一个
ps: 两个字符串相加会在堆上创建1个String对象”abcabc”(因为没有显式使用双引号指定,也没有调用intern,所以常量池里边目前没有“abcabc”对象)
String s = new String("abc") + new String("def");
这种情况下, 则会创建5个对象
1.2 String、StringBuffer、StringBuilder的区别
String | StringBuffer | StringBuilder | |
---|---|---|---|
可变性 | 不可变 | 可变 | 可变 |
线程安全性 | 线程安全 | 线程安全 | 线程不安全 |
一、可变性
String是不可变的对象(final修饰), 所以每次修改String类型变量, 实质上都等同于生成一个新的String对象, 然后将指针指向新的String对象
String对象的字符内容是存储在一个字符数组value[]中, 而这个value[]是被final修饰
这样不仅效率低下, 而且大量浪费有限的内存空间,所以经常改变内容的字符串最好不要用 String. 因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,那速度是一定会相当慢
StringBuffer、StringBuilder的父类AbstractStringBuilder的value数组不是final类型
二、线程安全性
String的线程是安全的, 因为value数组是final
StringBuffer是线程安全的, 因为他的方法使用了synchronized(单线程时没必要使用, 因为加锁了, 速度慢)
StringBuilder是线程不安全的(单线程时建议使用,因为没加锁,速度快)
1.3 intern方法的作用及原理
在java中存在8种基本类型以及一种特殊的类型String, 这些类型为了使它们在运行过程中速度更快, 更节省内存, 都提供了一种常量池的概念(在方法区), 常量池相当于java系统提供的缓存
String类型的常量池比较特殊, 主要使用方式有两种
- 直接使用双引号声明出来的String对象会直接存储在常量池中
- 如果不是用双引号声明的String对象, 可以使用intern方法将其缓存到常量池
1.4 String不可变的含义
String不可变的含义: 将一个已有字符串"123"重新赋值为"456", 不是在原内存地址上修改数据, 而是重新指向一个新对象、新地址
也就是说, 不可变的含义是内部数据不可变, 而非引用不可变
String str= "123";
str = "456";
System.out.println(str);
1.5 static用法、使用位置、实例
- 修饰成员属性(静态变量)
- 修饰成员方法(静态方法)
- 修饰代码块(静态代码块)
- 修饰内部类(静态内部类)
- 静态导包
1.6 为什么静态方法不能调用非静态方法和变量
与类的加载顺序有关, 加载静态方法时, 非静态的还未初始化
1.7 异常/Exception
Throwable有两个直接子类
- Error
- JVM内部的严重问题, 比如资源不足等, 无法恢复
- 处理方式: 程序员不用处理
- Exception
- JVM通过处理还可回到正常执行流程, 即可恢复
- 分RuntimeException和其他Exception
- RuntimeException(unchecked exception)
处理或者不处理都可以(不需try…catch或在方法声明时throws) - 其他Exception(checked exception)
java编译器要求程序必须捕获(try…catch) 或者声明抛出(throws)这种异常
- RuntimeException(unchecked exception)
为什么要对unchecked异常和checked异常进行区分?
编译器将检查你是否为所有的checked异常提供了异常处理机制, 比如使用Class.forName()来查找给定的字符串的class对象的时候, 如果没有为这个方法提供异常处理, 编译将无法通过
1.7 try/catch/finally的return顺序
finally语句与return语句详解
1.8 字符流和字节流
字节流、字符流、缓冲流
字节流和字符流的使用非常相似