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


    JAVA

    基础

    JVM JDK JRE

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

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

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

    在这里插入图片描述

    编译相关
    字节码

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

    在这里插入图片描述

    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 来判断
    
    • 1

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

    BigInteger

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

    静态变量

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

    字符常量&字符串常量

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

    char占两个字节

    方法

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

    静态方法

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

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

    只允许访问静态成员

    重载&重写

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

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

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

    面向对象

    基础

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

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

    对象
    实例和引用

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

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

    对象相等

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

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

    构造方法

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

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

    三大特征
    封装

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

    继承

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

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

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

    Animal animal1 = new Dog();
    
    • 1

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

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

    接口&抽象类

    共同点

    • 不能被实例化
    • 可以包含抽象方法
    • 都可以使用默认方法(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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    Object

    Object常见方法
    hashCode: 返回对象的哈希码;
    equals: 比较两对象地址是否相等(String重写了该方法)
    clone: 浅拷贝
    toString: 
    wait: 暂停线程
    notify、notifyAll: 唤醒线程
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    == 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    intern

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

    异常

    在这里插入图片描述

    exception error

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

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

    checked unchecked

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

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

    try

    try-catch-finally

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

    泛型

    使用方式

    泛型类

    public class Generic{
    
    • 1

    泛型接口

    public interface Generator<T> {
    
    • 1

    泛型方法

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

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

    public interface CommonResult {
        boolean isSuccess();
        String getMessage();
        T getData();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    工具类

    反射

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

    原理

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

    应用

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

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

    优缺点

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

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

    获取class对象

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

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

    注解

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

    注解解析后才会被使用

    • 编译器直接扫描:编译时扫描注解并处理,如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

    简单易用,性能低

  • 相关阅读:
    视联网赋能数字政府建设,推进基层公共服务系统驶入新通道
    js对象和原型、原型链的关系
    Jetpack compose比xml优秀的地方
    在HTML单页面中,使用Bootstrap框架的多选框如何提交数据
    每天一道算法题——动态规划
    Linux硬盘垃圾清理心得
    回忆旅途的过往
    学习笔记(1)
    PyCharm 2022最新版详细图文安装教程(安装+运行测试+汉化+背景图设置)
    互联网摸鱼日报(2023-11-20)
  • 原文地址:https://blog.csdn.net/zhr1030635594/article/details/130896505