JAVA面试八股整理——基础部分

JAVA

基础

JVM JDK JRE

JVM java虚拟机,针对不同的系统,使用相同的字节码会给出相同结果。一次编译,随处可运行

JDK Java SDK 提供给开发者使用,创建和编译Java程序。包含了JRE,同时包含了其它工具(java 源码的编译器 javac 等)

JRE Java运行环境,包含JVM和基础类库

JAVA面试八股整理——基础部分_第1张图片

编译相关
字节码

JVM可以理解的代码叫字节码(.class文件),只面向虚拟机,解决了传统解释性语言效率低的问题,又保留了解释性语言可移植的特点。

JAVA面试八股整理——基础部分_第2张图片

JIT编译器

.class - > 机器码,首先加载字节码文件,然后解释器逐行解释执行,如果代码块经常被调用(热点代码),JIT在第一次编译后将机器码保存下来,下次直接使用。

根据二八定律,消耗大部分资源的只有一小部分代码(热点代码),即JIT需要编译的部分。JIT需要预热,达到一定时间和调用频率才会触发JIT分层

AOT

直接将字节码编译成机器码,避免预热。但不能根据程序运行情况进一步优化。代码经常更新的话每次都需要重新编译。

编译与解释并存

编译:将源代码一次性翻译成可被该平台执行的机器码(C++)开发慢,执行快

解释:一句一句的将代码解释(interpret)为机器代码后再执行(Python)开发快,执行慢

Java程序要先经过编译,生成字节码,再由Java解释器执行

数据类型&变量

基本数据类型、包装类型
基本数据类型

整数型:byte short int long

浮点数:float double

字符:char

布尔:boolean

包装类型(引用类型)

包装类型可用于泛型,基本类型不行

基本数据类型的局部变量存放于虚拟机栈的局部变量表,成员变量放在堆中;包装类型属于对象类型,放在堆

基本数据类型占用空间小

包装类型不赋值时为null,基本数据类型有默认值

基本数据类型 == 比较真值,包装类型 == 比较地址;整型包装类型之间的比较用 equals

包装类型的缓存

整型(-128~127)、布尔、字符默认创建了缓存数据,如 Integer i1 = 40; 直接使用了缓存中的对象

自动装箱、拆箱

装箱:基本类型->引用类型

拆箱:引用类型->基本类型

  • Integer i = 10 等价于 Integer i = Integer.valueOf(10)
  • int n = i 等价于 int n = i.intValue()
浮点数计算 及 BigDecimal

精度丢失:无限循环的小数储存在计算机会被截断,没办法用二进制精确表示

浮点数之间的等值判断,基本数据类型不能用 == 来比较,包装数据类型不能用 equals 来判断

BigDecimal 可以实现对浮点数运算不丢精度

BigInteger

超过long:BigInteger内部用int[]储存任意大小的整型数据

静态变量

static 被类的所有实例共享,所有对象共享同一份静态变量,节省内存。通常静态变量会被final修饰成常量。

字符常量&字符串常量

字符常量相当于一个整型值(ASCII),可以参加运算;字符串常量表示一个地址(字符串在内存中的位置)

char占两个字节

方法

面向对象三大特性:封装、继承、多态

静态方法

静态方法不能调用非静态成员:静态方法属于类,类加载时分配内存,可通过类名直接访问;非静态成员属于对象,实例化后才能存在。

调用可以使用 类名.方法名对象.方法名

只允许访问静态成员

重载&重写

重载:一个方法根据输入不同,处理不同。参数类型、个数、顺序不同,返回值,修饰符可以不同

重写:子类继承父类相同方法,覆盖父类方法。参数列表相同,返回值、抛出异常范围应该小于等于父类,访问修饰符范围大于等于父类(两同两小一大)

可变参数只能作为最后一个参数,重载优先匹配固定参数

面向对象

基础

面向过程:过程拆解成一个个方法

面向对象:抽象出对象,用对象执行方法

对象
实例和引用

new 创建对象实例(对象实例在堆内存),对象引用(在栈内存)指向实例

气球和绳子:一个实例可有多个引用

对象相等

对象相等:内存中内容相等

引用相等:指向的内存地址相等

构造方法

没有声明构造方法也可执行(默认无参构造)

不能重写(overwrite),但能重载(overload)

三大特征
封装

对象的状态信息隐藏在内部,不允许外部直接访问对象内部信息,提供方法给外界。

继承

子类可以增加新的数据或功能,也可以用父类的,但不能选择性继承。

  • 子类能拥有父类对象的私有属性和方法,但无法访问
多态

父类的引用指向子类的实例

Animal animal1 = new Dog();

对象类型和引用类型间有继承(类)/实现(接口)关系

多态不能调用“只存在于子类不存在于父类”的方法

接口&抽象类

共同点

  • 不能被实例化
  • 可以包含抽象方法
  • 都可以使用默认方法(default,实现类没有提供自己的实现,将使用接口中的默认实现方法)

区别

  • 接口主要对类的行为约束;抽象类强调所属关系
  • 一个类只能继承一个类,但可以实现多个接口
  • 接口中的成员变量只能是 public static final 类型的;抽象类成员变量默认default,可以在子类重新定义赋值
深拷贝&浅拷贝

浅拷贝:堆上创建一个新对象,原对象内部是引用对象的话,会直接复制内部对象的地址,即共用一个内部对象

实现Cloneable接口,调用父类Object的clone方法

深拷贝:完全复制整个对象,包括内部对象

序列化和反序列化

public class DeepCopy {
    public static void main(String[] args) {

    }

    // 泛型类型参数必须实现 Serializable 接口
    public static <T extends Serializable> T deepClone(T obj) throws IOException, ClassNotFoundException {
        // 对象写入字节流。序列化
        // 对象写入字节数组
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        // 对象序列化写入outputStream
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
        // 将对象obj写入outputStream
        objectOutputStream.writeObject(obj);
        
        // 从字节流读取对象,反序列化
        ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
        ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
        return (T) objectInputStream.readObject();
    }
}

class User implements Serializable {
    String name;
    Address address;

    public User(String name, Address address) {
        this.name = name;
        this.address = address;
    }
}

class Address {
    String province;
    String city;

    public Address(String province, String city) {
        this.province = province;
        this.city = city;
    }
}

Object

Object常见方法
hashCode: 返回对象的哈希码;
equals: 比较两对象地址是否相等(String重写了该方法)
clone: 浅拷贝
toString: 
wait: 暂停线程
notify、notifyAll: 唤醒线程
== equals

== 对于基本类型比较值,对于引用类型,比较地址(本质也是比较值,但引用类型变量存的地址)

equals 等价于 == ,但一般会重写,来比较属性相等

hashCode

获取哈希码,确定对象在哈希表中的索引位置

与equals

hashcode和equal都是比较对象是否相等,但hashcode效率更高。HashSet对比时,同样的哈希码下再用equals。

两个对象hashcode相等,不一定对象相等(哈希码算法可能重复)

重写equals必须重写hashCode

两对象equals相等时,hashcode一定相等,不然使用hashmap会出现问题(无法正确找到对象)

如果不重写hashcode,equals按属性值比较,hashcode按地址比较

String

String、StringBuffer、StringBuilder

String不可变,故线程安全,每次改变都会生成一个新的String对象。String中使用final修饰的字符数组来保存字符串。

StringBuilderStringBuffer 都继承自 AbstractStringBuilder 类,提供了修改字符串的方法,如 append

StringBuffer 对方法加了同步锁,线程安全;StringBuilder线程不安全

  • StringBuffer StringBuilder类似,代表可变字符序列
  • String不可变,效率低,复用率高(池中存在就不用再创建),需要大量修改的话不用String
  • StringBuffer 效率高,线程安全
  • StringBuilder 效率最高,线程不安全
字符串拼接

字符串 + 实际上是通过StringBuilder调用append方法实现,String的for循环拼接每次循环创建一个StringBuilder,不如直接用StringBuilder拼接效率高。

字符串常量池

JVM为字符串开辟的区域,避免重复创建

// 在堆中创建字符串对象”ab“
// 将字符串对象”ab“的引用保存在字符串常量池中
String aa = "ab";
// 直接返回字符串常量池中字符串对象”ab“的引用
String bb = "ab";
System.out.println(aa==bb);// true
intern

String.intern 将指定字符串对象的引用保存在字符串常量池,若已保存,直接返回该引用

异常

JAVA面试八股整理——基础部分_第3张图片

exception error

exception:程序本身可以处理的异常,可以通过catch捕获

error:程序无法处理的错误,JVM一般会选择线程终止。内存溢出、虚拟机异常等

checked unchecked

checked:没有被catch或throws处理的话没法通过编译,如IO异常

unchecked:不接受检查也可以正常通过编译,由于逻辑错误或环境错误引起,如算术错误、空指针

try

try-catch-finally

finally不一定会运行,如:CPU关闭、线程死亡

泛型

使用方式

泛型类

public class Generic{

泛型接口

public interface Generator<T> {

泛型方法

 public static < E > void printArray( E[] inputArray )
项目用到

自定义一个与CommonResult 接口,接口中的方法 getData() 中使用类型参数 T 来动态指定结果的数据类型。

public interface CommonResult {
    boolean isSuccess();
    String getMessage();
    T getData();
}

工具类

反射

可以在运行时分析类以及执行类中方法,通过反射可以获取任意一个类的所有属性和方法,并调用

原理

反射通过编译后的字节码(class)文件找到其中的信息

应用

在动态代理中,invoke函数中使用反射类Method来调用指定方法

注解也用到了反射,@Value注解就读取到配置文件中的值

优缺点

优点:让代码更灵活,为框架开箱即用的功能提供便利

缺点:安全问题,性能略差

获取class对象

class对象将一个类的信息告诉程序

  1. 知道具体类:
Class alunbarClass = TargetObject.class;
  1. 传入类的全路径 Class.forName
Class alunbarClass1 = Class.forName("cn.javaguide.TargetObject");
  1. 通过对象实例 instance.getClass
TargetObject o = new TargetObject();
Class alunbarClass2 = o.getClass();

注解

常用
@Override
 // 覆盖父类方法
@Deprecated
// 标注内容不再被建议使用
解析方法

注解解析后才会被使用

  • 编译器直接扫描:编译时扫描注解并处理,如override
  • 运行期间通过反射处理:如spring中注解

SPI

服务者提供给框架的使用者的接口,运行时动态加载框架。

将服务的接口与具体实现类解耦。

按照规定将要暴露对外使用的具体实现类在固定文件中声明,框架在运行时扫描并加载实现类。

API&SPI

API:实现方提供接口和实现,我们调用接口

SPI:调用方确定接口规则,厂商根据规则实现

优缺点

优点:接口设计灵活

缺点:需要遍历加载所有实现类,效率低

序列化反序列化

序列化:将数据结构或对象转换成二进制字节流

反序列化:将二进制字节流转成数据结构或对象

目的:通过网络传输对象,或储存

属于计算机网络的应用层

序列化协议
JDK自带

不想被序列化:transient修饰

不推荐原因

  • 不支持跨语言
  • 序列化后体积大,传输性能差
  • 安全问题
Hessian

RPC协议,dubbo2.x默认启用序列化协议

JSON

简单易用,性能低

集合

服务者提供给框架的使用者的接口,运行时动态加载框架。

将服务的接口与具体实现类解耦。

按照规定将要暴露对外使用的具体实现类在固定文件中声明,框架在运行时扫描并加载实现类。

API&SPI

API:实现方提供接口和实现,我们调用接口

SPI:调用方确定接口规则,厂商根据规则实现

优缺点

优点:接口设计灵活

缺点:需要遍历加载所有实现类,效率低

序列化反序列化

序列化:将数据结构或对象转换成二进制字节流

反序列化:将二进制字节流转成数据结构或对象

目的:通过网络传输对象,或储存

属于计算机网络的应用层

序列化协议
JDK自带

不想被序列化:transient修饰

不推荐原因

  • 不支持跨语言
  • 序列化后体积大,传输性能差
  • 安全问题
Hessian

RPC协议,dubbo2.x默认启用序列化协议

JSON

简单易用,性能低

你可能感兴趣的:(java,面试,jvm)