《BAT高频面点》Java基础高频面试题八股文(必看🔥)

小龙coding2023/4/9大约 15 分钟

1、谈谈面向对象的理解

面向过程:一件事该怎么做,注重实现过程,以过程为中心

面向对象:实现对象是谁,只关心怎样使用,不关心具体实现(只关心实现对象是谁,有封装、继承、多态三大特性

封装:可以不用关心内部实现,具体构造,只需知道怎么操作它就是,比如电视,手机,将内部封装起来,直接使用

多态:同一个方法调用,由于对象不同可能会有不同的行为。

比如都是休息,张三是睡觉,李四是爬山等;或则具体场景中,我不知道现在具体传进来的对象是 Student,还是 Teacher,那么可以用 Pepole 去接收它。

多态的存在要有3个必要条件:继承,方法重写,父类引用指向子类对象。3. 父类引用指向子类对象后,用该父类引用调用子类重写的方法,此时多态就出现了

继承:使代码更容易扩展,比如有学生教师,他们都有一些公用方法与属性,可以将其抽取出来定义为父类,再去继承它,复用代码,减少冗余,易于扩展

2、面向对象与面向过程区别

  • 面向过程面向过程性能比面向对象高。 因为类调用时需要实例化,开销比较大,比较消耗资源,所以当性能是最重要的考量因素的时候,比如单片机、嵌入式开发、Linux/Unix 等一般采用面向过程开发。但是,面向过程没有面向对象易维护、易复用、易扩展。
  • 面向对象面向对象易维护、易复用、易扩展。 因为面向对象有封装、继承、多态性的特性,所以可以设计出低耦合的系统,使系统更加灵活、更加易于维护。但是,面向对象性能比面向过程低

3、JDK 与 JRE 区别

简单总结

JDK(Java Developer's Kit)

JDK = JRE + 开发工具集(java javac javadoc jar....)

JRE(Java Runtime Environment) java运行环境

JRE = JVM + 核心类库

只运行,JRE即可,要编译就要JDK

image-20230419230213841

4、值传递与引用传递

Java 语言是 值传递。Java 语言的方法调用只支持参数的值传递。

当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性可以在被调用过程中被改变,但对对象引用的改变是不会影响到调用者的。

JVM 的内存分为堆和栈,其中栈中存储了基本数据类型和引用数据类型实例的地址,也就是对象地址。

而对象所占的空间是在堆中开辟的,所以传递的时候可以理解为把变量存储的对象地址给传递过去,因此引用类型也是值传递。

5、==与equals()区别

== 比较的是地址。对于基本数据类型比较的值,对于引用数据类型比较的是内存地址

equals() 属于 object 类方法,没有重写之前使用效果和 == 一样。可以重写例如 String 类,使其比较内容值是否相等

6、为什么重写 equals 时必须重写 hashCode ⽅法

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

  • 提高效率:使用 hashcode 提前检验,定位,不用每一次都使用 equlas 方法比较,提高效率
  • 保证没有重复对象出现,确保hashmap去重性:假如只重写 equas 方法,不重写 hashcode,相同的对象 hashCode 不同,从而映射到不同下标下,HashMap 无法保证去重

equals 相同,hashcode 相同;hashcode 不同,equals 一定不同;hashcode 相同,equals 不一定相同(哈希冲突)

7、深拷贝与浅拷贝

  • 浅拷贝:仅拷贝被拷贝对象的成员变量的值,也就是基本数据类型变量的值,和引用数据类型变量的地址值,而对于引用类型变量指向的堆中的对象不会拷贝。(拷贝值与引用)
  • 深拷贝:完全拷贝一个对象,拷贝被拷贝对象的成员变量的值,堆中的对象也会拷贝一份。(拷贝整个对象)

例如现在有一个 Student对象,里面有一个 teachers 列表,它的浅拷贝和深拷贝的示意图:

image-20230422172644974

因此深拷贝是安全的,浅拷贝的话如果有引用类型,那么拷贝后对象,引用类型变量修改,会影响原对象。

浅拷贝如何实现呢?

Object 类提供的 clone() 方法可以非常简单地实现对象的浅拷贝。

深拷贝如何实现呢?

  • 重写克隆方法:重写克隆方法,引用类型变量单独克隆,这里可能会涉及多层递归。
  • 序列化:可以先将原对象序列化,再反序列化成拷贝对象。

8、Student s = new Student();在内存中做了哪些事情

  • 载入Student.class文件进内存(方法区)
  • 在栈内存为s开辟空间
  • 在堆内存为学生对象开辟空间
  • 对学生对象的成员变量进行默认初始化
  • 对学生对象的成员变量进行显示初始化
  • 通过构造方法对学生对象的成员变量赋值
  • 学生对象初始化完成,把对象地址赋值给s变量

img

9、重载和重写(Override)的区别

方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。

重载:发生在同一个类中,方法名相同参数列表不同(参数类型不同、个数不同、顺序不同),与方法返回值和访问修饰符无关,即重载的方法不能根据返回类型进行区分

重写:发生在父子类中,方法名、参数列表必须相同,返回值小于等于父类,抛出的异常小于等于父类,访问修饰符大于等于父类(里氏代换原则);如果父类方法访问修饰符为private则子类中就不是重写。

10、多态理解

父类或接口定义的引用变量可以指向子类或具体实现类的实例对象,然后你编译的时候不知道这个指针指向哪里,等到运行时才知道,从而去指向对应的子类或者接口具体实现的类所指向的方法

11、String为何不可变不可变好处

String 类中使⽤ final 关键字修饰字符数组来保存字符串,jdk9后,使用 byte 数组。StringBuilder 与 StringBuffer 都继承⾃ AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使⽤字符数组保存字符串 char[]value 但是没有⽤ final 关键字修饰,所以这两种对象都是可变的。

不可变好处:缓存hash值(使得hash值不变,只进行一次计算)、String pool需要、线程安全、参数安全性(网络连接,参数变了,以为连接主机变了)

12、String有几种编码格式?

13、String,StringBuffer和StringBuilder之间的区别是什么?

可变性:String是不可变对象,任何对String修改都会创建新的String对象,StringBuffer和StringBuilder可变类。

效率:频繁对字符进行操作时,使用String会生成一些临时对象,多一些附加操作,效率低些。

安全性:Stringbuffer方法由synchronized修饰,线程安全。

14、关于String的理解

image-20230422182129223

String str1 = new String("123");//在常量池创一个“123”对象,遇到new在堆内存创建一个对象,并返回堆中的对象引用
String str2 = "123";//因为之前常量池中能找到“123”的对象,所以直接将引用返回,不创建新的
String str3 = str1.intern();//若常量池中包含了str1字符串“123”,则直接返回引用,否则就在池中先创建一个在返回池中的对象引用
System.out.println((str1 == str2) +","+ (str3 == str2))
//输出 false,true
String str4 = new String("234");
String str5 = new String("234");
String str6 = str4.intern();
String str7 = str5.intern();
System.out.println((str4 == str5) +","+ (str6 == str7));
//输出false ture

15、8种基本数据类型与占内存大小

  • byte (1字节, -2的7次方到2的7次方-1)
  • short(2字节, -2的15次方到2的15次方-1)
  • int(4字节)
  • float(4字节)
  • double(8字节)
  • long(8字节)
  • char(2)
  • booean(4—内存对齐),

逻辑上 boolean 型只占1bit,但是虚拟机底层对 boolean 值进行操作实际使用的是 int 型,操作 boolean 数组则使用 byte 型;

16、float 与 double/short与int

被好多次大厂考过

  • float f = 1.1;错

  • float f = 1.1f;对

  • short s = 1;

  • s += 1;s = s+1;错(被强转成了 int )

  • s++; 对(隐式类型转换 相当于 s = (short) (s + 1);)

33、反射原理

  • 什么是反射:动态的获取类的各个属性以及调用它的方法

  • 原理:通过将类对应的字节码文件加载到jvm内存中得到一个Class对象,通过这个Class对象可以反向获取实例的各个属性以及调用它的方法。

  • 获取Class对象的方式

    • Object ——> getClass();(对象.getClass())
    • 任何数据类型(包括基本数据类型)都有一个“静态”的class属性 (类.class)
    • Class.forName("");
  • 使用场景

    • 1、通过反射运行配置文件内容
      • 加载配置文件,并解析配置文件得到相应信息
      • 根据解析的字符串利用反射机制获取某个类的Class实例
      • 动态配置属性
    • 2、JDK动态代理
    • 3、jdbc通过Class.forName()加载数据的驱动程序
    • 4、Spring解析xml装配Bean

Java反射相关类

34、object的方法有哪些?notify和notifyAll的区别?

1、getClass--final方法,获得运行时类型。

2、toString——对象的字符串表示形式(对象所属类的名称+@+转换为十六进制的对象的哈希值组成的字符串

3、equas 方法——如果没有重写用的就是 Object 里的方法,和==一样都是比较两个引用地址是否相等,或则基本数据类型值是否相等

4、Clone 方法——保护方法,实现对象的浅复制,只有实现了 Cloneable 接口才可以调用该方法,否则抛出CloneNotSupportedException 异常。

5、notify 方法——唤醒在该对象上等待的某个线程

6、notifyAll 方法——唤醒在该对象上等待的所有线程

7、Wait 方法——wait() 的作用是让当前线程进入等待状态。同时,wait() 也会让当前线程释放它所持有的锁,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,当前线程被唤醒(进入 "就绪状态")。还有一个 wait(long timeout) 超时时间

补充 sleep不会释放锁

8、Finalize() 方法——可以用于对象的自我拯救

9、Hashcode 方法——该方法用于哈希查找,可以减少在查找中使用equals的次数,重写了equals方法一般都要重写hashCode方法。这个方法在一些具有哈希功能的Collection中用到。

一般必须满足 obj1.equals(obj2)==true。可以推出 obj1.hashCode()==obj2.hashCode(),但是 hashCode 相等不一定就满足equals。不过为了提高效率,应该尽量使上面两个条件接近等价

35、Java中接口和抽象类的区别?

  • 方法:接口只有定义,不能有方法的实现,java 1.8中可以定义default与static方法体,而抽象类可以有定义与实现,方法可在抽象类中实现。
  • 成员变量:接口成员变量只能是public static final的,且必须初始化,抽象类可以和普通类一样任意类型。
  • 继承实现:一个类只能继承一个抽象类(extends),可以实现多个接口(implements)
  • 都不能实例化
  • 接口不能有构造函数,抽象类可以有

总结⼀下 JDK7~JDK9 Java 中接⼝的变化

  1. 在 JDK 7 或更早版本中,接⼝⾥⾯只能有常量变量和抽象⽅法。这些接⼝⽅法必须由选择实现接⼝的类实现。
  2. JDK 8 的时候接⼝可以有默认⽅法和静态⽅法功能。
  3. JDK 9 在接⼝中引⼊了私有⽅法和私有静态⽅法。

24.final 关键字有什么作用?

final 表示不可变的意思,可用于修饰类属性方法

  • 被 final 修饰的类不可以被继承

  • 被 final 修饰的方法不可以被重写

  • 被 final 修饰的变量不可变,被 final 修饰的变量必须被显式第指定初始值,还得注意的是,这里的不可变指的是变量的引用不可变,不是引用指向的内容的不可变。

    例如:

final StringBuilder sb = new StringBuilder("abc");
sb.append("d");
System.out.println(sb);  //abcd

36、既然有了字节流,为什么还要有字符流

Java 虚拟机转字节得到字符流,耗时;不知道编码类型还很容易出现乱码。所以干脆提供字符流。

45.什么是序列化?什么是反序列化?

什么是序列化,序列化就是把 Java 对象转为二进制流,方便存储和传输。

所以反序列化就是把二进制流恢复成对象

image-20230422183426425

37、throw 与 throws区别

throw 关键字用在方法内部,只能用于抛出一种异常;throws 关键字用在方法声明上,可以抛出多个异常,用来标识该方法可能抛出的异常列表

38、红黑树特性?时间复杂度?

二叉查找树(二叉排序,二叉搜索树),相当于二分查找,但是可能出现线性化,相当于o(n),于是出现了红黑树,它是自平衡的二叉搜索树,有红黑两种结点,根节点红色,叶子节点是为空的黑色结点,红黑交替,从任意结点出发到达它可达的叶子结点路径所包含的黑色结点一样,增删查时间复杂度O(logn).通过变色与旋转维持平衡。左旋:逆时针旋转,让父节点右孩子当父亲;右旋:顺时针旋转,让父节点左孩子当父亲。

39、JDK与 cjlib 动态代理

动态代理好处

一个工程如果依赖另一个工程给的接口,但是另一个工程的接口不稳定,经常变更协议,就可以使用一个代理,接口变更时,只需要修改代理,不需要一一修改业务代码

作用:

  • 功能增强:再原有功能加新功能
  • 控制访问:代理类不让你访问目标

JDK动态代理:利用反射机制生成代理类,可以动态指定代理类的目标类,要求实现invovationHandler接口,重写invoke方法进行功能增强,还要求目标类必须实现接口。

Cjlib动态代理:利用ASM开源包,把代理对象的CLass文件加载进来,修改其字节码文件生成子类,子类重写目标类的方法,被final修饰不可以,然后在子类采用方法拦截技术拦截父类方法调用,织入逻辑(定义拦截器实现MethodInterceptor接口)

img

40、异常体系

Throwable的子类为ErrorException

Exception的子类为RuntimeException异常和RuntimeException以外的异常(例如IOException)。

主要分为Error,RuntimeException异常和RuntimeException以外的异常。(错误、运行时异常和编译时异常)

Error就是一些程序处理不了的错误,代表JVM出现了一些错误,应用程序无法处理。例如当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。

常见异常:NullPointerException、ClassNotFoundException、arrayindexoutofboundsexception、ClassCastException(类型强制转换)

img

41、包装类型和基本类型的区别是什么

最主要的区别是包装类型是对象,拥有字段和方法,可以很方便地调用一些基本的方法,初始值是null,而且可以使用null代表空值,而基本数据类型只能使用0来代表初始值

其次是基本数据类型是直接存储在栈中,而包装类型是一个对象,对象的引用变量是存储在栈中,存储了对象在堆中的地址,对象的数据是存在堆中。

42、BIO,NIO,AIO 有什么区别

BIO (Blocking I/O): 同步阻塞 I/O 模式,数据的读取写⼊必须阻塞在⼀个线程内等待其完成。

NIO (Non-blocking/New I/O): NIO 是⼀种同步⾮阻塞的 I/O 模型

AIO (Asynchronous I/O): AIO 也就是 NIO 2。在 Java 7 中引⼊了 NIO 的改进版 NIO 2,它是异步⾮阻塞的 IO 模型。异步 IO 是基于事件和回调机制实现的,也就是应⽤操作之后会直接返回,不会堵塞在那⾥

43、谈谈常量池的理解·

class文件常量池

方法区运行时常量池(Class 文件中的常量池(编译器生成的字面量和符号引用)会在类加载后被放入这个区域),除了在编译期生成的常量,还允许动态生成,例如 String 类的 intern()。)

字符串常量池(jdk6及以前,在方法区,jdk7及以后移到堆)

八种基本类型的包装类的对象池

44、动态链接与静态链接