java基本面试题

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

  1. equals 只能用在引用数据类型中,== 可以用来比较基本类型和引用数据类型
  2. 基本类型用 == 比较其值的大小
  3. 引用类型用 == 比较其地址的大小,引用类型一般会重写equals 方法用来比较值的大小(未重写的equals 方法也是比较地址的大小)

2、java final 关键字的作用 关于 final 的总结

final 表示最终的,结束的。一般可以用来修饰变量、类、方法。
好处:主要用作安全考虑。

  1. 修饰的成员变量只能被赋值一次。
    final 修饰 局部变量必须在使用前赋值
    final 修饰 成员变量如果定义时没有赋值称为(空白final),空白final 必须在构造器或者静态代码块中赋值。
  2. 修饰的类无法被继承,并且类中的方法默认时 final 的
  3. 修饰的方法无法被重写,private类型的方法默认是final的

3、java static 关键字的作用 关于 static 的总结

static 表示静态的,主要用于内存管理。
相当于类纬度的,而不是对象纬度的。通常会用来修饰变量,修饰类,修饰方法,修饰代码块
好处:节省内存,使用方便,增加交互性,常见的时用于工具类。

  1. 修饰成员变量
    外部可以直接通过类名.访问静态成员变量
    当且仅当在类初次加载时会被初始化
    所有类对象共享,内存中值存储一份

  2. 修饰类,只有内部类才能用 static 修饰
    外部可以直接通过类名.类名访问静态内部类
    只能访问外部类的静态成员和方法,不可以访问普通非静态的。

  3. 修饰方法
    外部可以直接通过类名.访问静态方法
    并且静态方法中只能访问静态变量和静态方法,不可以访问普通非静态的。
    静态方法中不可以使用 this 、super 等关于对象的关键字

  4. 修饰代码块
    通常用来初始化静态变量
    只能置于类中,不能置于方法体中(普通代码块可以)

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

【前提,常量数据池中没有 “a”, “b”】
对象一:常量数据池中创建 “a”
对象二:常量数据池中创建 “b”
对象三:堆上开辟空间,copy 对象一,创建第二个 “a”,
对象四:堆上开辟空间,copy 对象二,创建第二个 “b”
对象五:new StringBuilder()
对象六:堆上开辟空间,创建拼接好的"ab"

5、抽象类和接口的区别

抽象类:用 abstract 修饰的类,该类无法被实例化,只有被继承才有意义。抽象类不可以用 final 修饰(final 修饰的类无法被继承),抽象类可以没有抽象方法,但是包含抽象方法的一定时抽象类。抽象类在被继承时,子类是非抽象类时必须重写父类的抽象方法,子类依旧时抽象类时可以不重写。
接口:接口可以理解为一种特殊的类。也可以有成员变量和成员方法。成员变量默认是被 static final 修饰的,只能在声明的时候赋值。接口中的方法默认是抽象方法,如果要实现普通方法必须用 default 标识。接口之间可以是多继承,接口的实现类必须重写接口中的抽象方法。

相同点

  1. 无法被实例化
  2. 抽象类的子类和接口的实现类只有重写了抽象方法才可以被实例化

不同点

  1. 抽象类被继承用的是 extends 关键字,接口被实现用的是 implements 关键字,一个类可以实现多个接口,但只能继承一个类
  2. 抽象类中的抽象方法可以是 public 和 protected 的,接口中的抽象方法只能是 public 的
  3. 抽象类与其子类是 is-a 的关系(子类是父类),接口与其实现类是 has-a 的关系(实现类中有接口的某某功能)
/*
    蝙蝠是一种动物,所以继承了 Animal 类,重写了 showType 方法
    蝙蝠属于哺乳动物,所有具有哺乳动物的特点
    蝙蝠会飞,所以具有飞行动物的特点
 */
public class Bat  extends Animal implements Fly , Mammal{
    
    
    @Override
    public void showType() {
    
    
        type = "Bat";
        System.out.println("the type is " + type);
    }
    @Override
    public void fly() {
    
    
        System.out.println(type + " can fly !");
    }
    @Override
    public void mammal() {
    
    
        System.out.println(type + " is mammal !");
    }
}

6、Java是如何实现跨平台功能的?

  1. java 是一种高级编程语言,能构在不同平台不同机器上运行,这归功于代码执行底层其实是jvm 虚拟机完成的(Java平台的核心组件之一,它是一个基于栈的执行引擎)。
  2. 程序员编写的 java文件经过编译器编译后生成机器可以识别的字节码文件.class文件,然后才能被虚拟机所解释执行(Java字节码是一种中间语言,类似于汇编语言,机器可以识别,人不可识别),由于虚拟机在不同的机器上的实现是相同的。
  3. Java虚拟机有明确的规范 和 Java虚拟机详细实现过程(。。。。。 )

Java字节码文件具有以下优点:

  1. 可移植性:Java字节码文件在任何Java虚拟机上都可以运行,因此它们具有可移植性。
  2. 安全性:由于Java字节码文件只包含中间代码,不包含原始指令或可执行代码,因此它们是安全的。Java虚拟机提供了严格的安全性控制,以确保Java应用程序的安全性。
  3. 高效性:Java字节码文件的解释器被编写为高度优化的本地代码。这使得Java应用程序在各种平台上都可以高效地运行。

反汇编指令解析

7、类加载过程

整个加载过程其实是将编译后的.class 文件加载在 jvm 进行解析使用回收的过程。具体分为 加载,链接(验证、准备、解析),初始化,使用,卸载。

  1. 加载 根据类名读取类的整个二进制流到 jvm 中,并存储在运行时内存区中的方法区;将其转换为一个 java.lang.Class 对象实例,作为方法区中的链接入口。
  2. 链接 整个就是验证加载的正确性
    验证:进行 final 是否合规判断,静态变量是否合法判断
    准备:静态变量赋初始值
    解析:解析类的方法,确保与类之间的相互引用正确性,完成内存结构布局
  3. 初始化 将类中 static 关键字标识的代码统一执行一遍。每个类都有< clinit > 初始化方法,里面存储用户指定的值,覆盖初始值。并且优先初始化父类。
  4. 使用 就是执行方法区的代码
  5. 卸载就是垃圾回收
    参考大佬文章

8、java 反射的了解

java 代码在编译后会将关于类的所有信息存储下来,当程序在运行时可以通过反射的方式获取类的所有信息,(包括成员变量,成员方法,构造器等),并且可以操纵类的字段、方法、构造器等部分,其中也包含 private 私有属性。
主要体现在:

  • 可以通过 class 对象反向解析出类的信息,运行时动态创建对象和编译,体现出灵活性
  • 常见于各种框架中,注解的使用;还有 AOP 切面编程应用到日志输出上

大佬的总结

public class ReflectionTest {
    
    

    public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException, NoSuchFieldException {
    
    
        Person person = new Person();
        person.age = 26;
        person.name = "周芷若";
        test1(person);
    }

    public static void test1(Object object) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
    
    
        Class c = object.getClass();
        Field ageField = c.getDeclaredField("age");
        System.out.println(ageField.getInt(object));
        Method showMethod = c.getMethod("show");

        showMethod.setAccessible(true); // 设置为可达。暴力反射,使得私有方法可以运行
        showMethod.invoke(object);
    }
}

class Person{
    
    
    int age;
    String name;
    private void show(){
    
    
        System.out.println("name:"+ name);
    }
}

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

序列化是将对象转化为字节流,反序列化是将字节流转化为对象。
对象是无法在网络中进行传输的,也无法直接保存到文件中(这两个过程都是通过输入输出流处理的)
java 序列化是通过serializable 接口,这个接口没有需要实现的方法,仅用于标识可序列化的语义。序列化运行时与每个可序列化类关联一个版本编号,称为serialVersionUID,在反序列化期间使用验证的发送方和接收方。
序列化时会用一个输出流FileOutputStream 构造 ObjectOutputStream 对象,然后调用 writeObejct 方法负责写入对象流状态,反序列化时则使用输入流对象。

10、什么是深拷贝,什么是浅拷贝

当我们需要完全复制一个对象时,可以直接使用已有的拷贝克隆方法
其中有深拷贝和浅拷贝两种
浅拷贝:只对基本类型进行拷贝,引用类型只进行引用指向,内部的值还是复用的。此时如果修改引用某一处的引用对象值,其他对象中的引用对象值也随着改变。
深拷贝:也会将引用对象的值进行拷贝,如果有多级引用对象均会进行拷贝。

常见的身拷贝方法:

  1. 实现 Cloneable 接口,并且重写 clone() 方法,在clone() 方法中调用 super.clone()方法进行浅拷贝,对需要深拷贝的地方在递归进行拷贝。
  2. 使用序列化和反序列化,将对象进行序列化为字节流,再将字节流反序列化为新的对象
  3. 第三方工具:Spring Framework 的 ObjectUtils.clone() 。

参考大佬文章

猜你喜欢

转载自blog.csdn.net/Misszhoudandan/article/details/131373094