2021年——Java核心面试题

java核心面试题

1、喜欢别忘了关注公众号【Java冢狐】

2、java面向对象编程的特征

  • 多态
  • 继承
  • 封装

PS:java不是纯粹的面向对象编程语言,因为java支持基本数据类型,比如int、short、long。尽管它们有自己的包装器类型,但是他们的确不能算是对象

3、 JVM

见往期JVM详情,这一块内容挺多的,就不在这里阐述了

4、JDK、JVM、JRE区别

image

  • JDK:是提供给java开发人员的软件环境,包含JRE和一组开发工具;有几个版本:

    • 标准版
    • 企业版
    • 微型版
  • JRE:是JVM的实现,JRE是由JVM和Java二进制文件以及其他类组成,可以执行任何程序。JRE不包含Java编译器,调试器等任何开发工具

java编译器的任务是将java源代码转换为字节码,可以通过javac命令执行,因此它在JDK中,JRE不需要

5、java为什么不支持多重继承

​ 防止出现菱形问题:ClassA和ClassB都继承了一个有特定方法的父类,该方法会被两个子类重写,当ClassC多重继承ClassA和ClassB时,不知道该调用那个重写方法。所以不支持多重继承

6、java重写(Overrider)和重载(Overload)有什么区别

  • 重写:重写的两个方法名相同,方法参数列表也相同,返回类型也相同。不过是一个在父类中,一个在子类中。

    • 访问限制性比父类弱或者一样
    • 子类的异常不能超过父类
    • 不能继承一个方法,则不能重写这个方法
  • 重载:重载的两个方法名相同,在同一类中,参数列表不同

    • 与修饰符和返回值类型无关
    • 体现的是编译时的多态性

总结:

  • 重载是编译器概念、重写是运行期概念
  • 因为在编译期已经确定调用哪个方法了,所以重载不是多态(静态多态)。而重写就是多态

7、访问权限修饰符

​ 目前一共有四种:public、private、protected、default(缺省)

​ 一个类只能被public或者default修饰

8、抽象类和接口

abstract:抽象类

如果一个类中没有包含足够的信息来描绘一个具体的对象,那么这个类就是抽象类。

除了不能实例化对象外其他功能依然存在。正是由于不能实例化对象,所以必须被继承才能使用。

Java中的抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口

Interface:接口

接口是抽象方法的集合,通常用interface来声明,一个类通过继承接口的方式,从而来继承接口的抽象方法。

接口不是类,类是描述对象的属性和方法,而接口则包含类要实现的方法。

接口无法被实例化只能被实现。

9、static关键字:静态

  • **修饰成员变量和成员方法:**被 static 修饰的成员属于0区。
  • 静态代码块: 静态代码块定义在类中方法外, 静态代码块在非静态代码块之前执行(静态代码块—>非静态代码块—>构造方法)。 该类不管创建多少对象,静态代码块只执行一次.
  • 静态内部类(static修饰类的话只能修饰内部类): 静态内部类与非静态内部类之间存在一个最大的区别: 非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围类,但是静态内部类却没有。没有这个引用就意味着:1. 它的创建是不需要依赖外围类的创建。2. 它不能使用任何外围类的非static成员变量和方法。
  • 静态导包(用来导入类中的静态资源,1.5之后的新特性): 格式为:import static 这两个关键字连用可以指定导入某个类中的指定静态资源,并且不需要使用类名调用类中静态成员,可以直接使用类中静态成员变量和成员方法。

PS:

  • static修饰的变量可以重写赋值
  • static方法中不能使用this和super关键字
  • static方法必须被实现,而不能是抽象的abstract
  • static方法只能被static方法覆盖

10、final、finally、finalize有什么区别

  • final:只赋值一次。表示最终的不可改变

    • 用于修饰类、方法和变量
    • 表示类不能继承,方法不能重写,成员变量必须要初始化
    • private类型的方法默认是final类型的
  • finally通常与try-catch块一起使用,即使try-catch块引发了异常,finally块中的代码也会被执行,用于释放try块中创建的资源

PS:但是不一定会执行:在try前直接返回、抛出异常、系统退出

  • finalize()是Object类的一个特殊方法,当对象正在被垃圾回收时,垃圾收集器将会调用该方法。

final修饰StringBuffer后还可以append吗

可以,final修饰的是一个引用简历,那么这个引用始终指向这个对象,但是这个对象内部的属性是可以改变的。

遇到过OOM吗?

C++程序员重写了finalize()方法,导致了GC回收的时候太慢了,这个时候又有新的对象出现导致了OOM

11、

12、java反射

​ java反射机制是在运行状态中,对于任意一个类,都能知道这个类所有字段和方法;对于任意一个对象,都能调用它的任意字段和方法;这种动态获取信息以及动态调用对象方法的功能称为java反射机制

​ 在常规编程中应该尽可能的避免,因为反射可以通过调用私有的构造方法来破坏设计模式,比如单例模式

​ 但是也正因为反射的存在,出现了Spring之类的框架,甚至Tomcat之类的服务器,他们都通过反射调用适当的方法来对类实例化,省去了很多麻烦

13、java中的组合

​ 通过对象组合可以实现代码的重用,java组合是通过引用其他对象的引用来实现的,使用组合的好处就是我们可以控制其他对象对使用者的可见性,并且可以重用我们需要的对象

14、组合和继承的优缺点

组合关系继承关系
不破坏封装,整体类与局部类之间松耦合,彼此相对独立破坏了封装关系,子类和父类紧密耦合,子类依赖父类的实现,子类缺乏独立性
具有较好的可扩展性支持扩展,但是以增加系统结构的复杂度为代价
支持动态组合,在运行时,整体对象可以选择不同类型的局部对象不支持动态扩展
整体类可以对局部类进行包装,封装局部类的接口,提供新接口子类不能改变父类的接口
创建整体类的对象时,需要创建所有局部类的对象创建子类的对象时,无需创建父类的对象
整体类不能自动获得和局部类同样的接口子类能自动继承父类的接口

15、 java中的类加载器

​ 当我们要访问任何类的时候,都需要通过java Classloader将该类的字节码加载到内存当中,可以通过继承ClassLoader并重写loadClass(String name)方法来创建自定义的类加载器

15.1 类加载器的种类
  • 启动类加载器,用来加载JDK的内部类
  • 扩展类加载器,从JDK扩展目录(JAVA_HOME/lib/ext)中加载类
  • 应用程序加载器,从当前类路径加载类
15.2 自定义类加载器

新建一个类继承java.lang.ClassLoader,重写findClass方法

15.3 类加载器加载过程
  • 加载
  • 验证
  • 准备
  • 解析
  • 初始化
  • 使用
  • 卸载

16、super与this关键字

16.1 super:表示父类的存储空间标识(父类的引用)

​ 当在子类中重写了父类方法时,可以通过super关键字访问父类方法

​ 也可以使用super关键字在子类构造方法中调用父类的构造方法,它必须是构造方法中的第一条语句

16.2 this:代表当前对象的引用(谁调用就代表谁)

​ this关键字提供对当前对象的引用,主要用于确保使用了当前对象的变量,而不是具有相同名称的局部变量

​ 还可以使用this关键字在构造方法中调用其他构造方法

​ 每次创建子类对象时,先初始化父类空间,再创建其子类对象本身。目的在于子类对象中包含了其对应的父类空间,便可以包含其父类的成员,如果父类成员非private修饰,则子类可以随意使用父类成员。代表体现在子类的构造方法调用时,一定先调用父类的构造方法

17、break和continue

​ break主要是用来终止for、while、do-while循环,也可以在switch语句中使用break来退出case条件

​ continue主要是用在for、while、do-while循环跳过当前迭代;甚至可以使用带有标签的continue语句来跳过最外层循环的当前迭代。

18、垃圾回收

​ 垃圾回收,简称GC,会查看堆内存,识别正在使用和未使用的对象,以及会自动删除未使用的对象,用来释放内存

19、序列化和反序列化

​ 我们可以把一个 Java 对象转化成一个数据流,这被称为序列化。一旦对象被转化为数据流后,就可以将其保存到文件或者通过网络套接字发送。

​ 如果一个对象实现了 Serializable 接口,就可以使用 java.io.ObjectOutputStream 将对象写入文件。

​ 将数据流再转化为 Java 对象被称为反序列化。

20、解释内存中栈(stack)、堆(heap)和静态存储区的用法

  • 栈空间:基本数据类型的变量、对象的引用、函数调用的线程保存
  • 堆空间:通过new关键字和构造器创建的对象
  • 静态存储区:程序中的字面量

Sting str = new String(“hello”);

​ str放在栈上,new出来的字符串对象放在堆上,而hello这个字面量放在静态存储区中

​ 但是只创建了两个对象,一个是静态存储区中的“hello”,一个是用new创建在堆上的对象

21、java是值传递的

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

22、静态嵌套类和内部类的差别

  • 静态嵌套类是被声明为静态的内部类,它可以不依赖于外部类实例而被实例化
  • 内部类:需要在外部类实例化后才能实例化

23、==和equals的区别

如果a和b是对象,则a==b是比较两个对象的引用,只有当a和b指向的是堆中的同一个对象才会返回true,而a.equals(b)是进行逻辑比较,所以通常需要重写该方法来提供逻辑一致性的比较。

24、基本类型和包装类型的区别

  • 包装类可以为null而基础类不可以。这使得包装类可以应用于POJO、DTO、VO、PO
  • 包装类型可以用于泛型,而基本类型不可以;因为泛型在编译是会进行类型擦除,最后只保留原始类型
  • 基本类型比包装类型更加高效:基本类型直接在栈内存储,而包装类型则存储的是堆中的引用
  • 两个包装类型的值可以相等,但是值却不相等

25、java八种基本数据类型及其取值范围

  • byte

    • 一个字节存储,其范围-128到127
  • short

    • 两个字节存储,其范围 (-215)到(215-1)
  • char

  • int

  • long

  • float

  • double

  • boolean

26、java字符串类

  • String:不可变,最慢

    • final修饰,String类的方法都是放回新的String
    • 适用于少量的字符串操作

为啥String其对象不可变

Sring类有两个主要的成员变量,其中value指向的是一个字符串数组,字符串中的字符就是用这个value变量存储起来的,并且用final修饰,即value指向的地址不可改变

虽然其指向的数组可变,但是String没有提供相应的方法让我们去进行修改。

  • StringBuilder:可变、快,线程不安全

    • 适用于单线程下在字符串缓冲区进行大量操作
  • StringBuffer:线程安全

    • 适用于多线程下在字符串缓冲器进行大量操作
String为啥不new

多创建一个对象

27、throw和throws的区别

  • throw:(方法内)

    • 表示方法内抛出某种异常对象
    • 如果异常对象是非运行时错误,则需要在方法申明时加上该异常的抛出,即需要加上throws语句,或者在方法体内try-catch异常,否则编译报错
    • 执行到throw语句则后面的语句块不再执行
  • throws:(方法上)

    • 方法在定义上使用throws表示这个方法可能抛出某种一异常
    • 需要由方法的调用者进行异常处理

28、java程序从源代码到运行的步骤

​ .java文件通过JDK中的javac编译,编程.class文件,然后后由JVM变成机器可以执行的二进制机器码

29、对象克隆

  • 需求场景:方法需要return引用类型,但又不希望自己的对象被修改,程序之间方法的调用时参数的传递。有些场景为了保证引用类型的参数不被其他方法修改,可以使用克隆后的值作为参数传递

  • 实现:

    • 实现Cloneable接口,重写clone方法
  • 深拷贝和浅拷贝

    • 浅拷贝:复制基本类型的属性;引用类型的属性复制,复制栈中的变量 和 变量指向堆内存中的对象的指针,不复制堆内存中的对象
    • 深拷贝:复制基本类型的属性;引用类型的属性复制,复制栈中的变量 和 变量指向堆内存中的对象的指针和堆内存中的对象

30、hashcode()和equals()

  • hashcode():作用就是获取哈希码(散列值),实际上是一个int整型。这个哈希码的作用就是用来确定对象在哈希表中的索引位置

  • 相关规定

    • 两个对象相等,则hashcode一定相等,且对两个对象分别调用equals方法都返回true。
    • 反过来两个对象有相同的hashcode值,但是不一定相等
    • equals方法被重写则hashcode也必须被重写
    • hashcode的默认行为是对堆上的对象产生独特值。如果没有重写hashcode,则该class的两个对象无论如何都不会相等
  • 规范

    • 若重写 equals (Object obj) 方法,有必要重写 hashcode () 方法,确保通过 equals (Object obj) 方法判断结果为 true 的两个对象具备相等的 hashcode () 返回值。说得简单点就是:“如果两个对象相同,那么他们的 hashcode 应该 相等”。(注意不是强制)
    • 如果 equals (Object obj) 返回 false,即两个对象 “不相同”,并不要求对这两个对象调用 hashcode () 方法得到两个不相同的数。说的简单点就是:“如果两个对象不相同,他们的 hashcode 可能相同”。
  • 推论

    • 如果两个对象 equals,Java 运行时环境会认为他们的 hashcode 一定相等。
    • 如果两个对象不 equals,他们的 hashcode 有可能相等。
    • 如果两个对象 hashcode 相等,他们不一定 equals。
    • 如果两个对象 hashcode 不相等,他们一定不 equals。

为什么在重写equals方法的时候要重写hashcode的方法

  • 判断的时候先根据hashcode进行判断,相同的情况下再根据equals()方法进行判断,如果没有重写hashcode则会出现equals方法相同但hashcode的值不相等的情况。
  • 这会造成在一些不允许相同对象的java容器中因为hashcode不同导致无法覆盖的问题从而出现相同的两个对象。

31、java创建对象的几种方式

  • 采用new
  • 通过反射
  • 采用clone
  • 通过序列化机制

32、java四种引用

  • 强引用

new对象来建立强引用,GC无法回收,只能通过中断关联来回收

  • 软引用

SoftReference GC无法回收,只有内存不足时才回收 适合做缓存

  • 弱引用

WeakReferce 发生GC就回收 用于ThreadLocal和WeakHashMap

  • 虚引用

PhantoReference get不到 GC 回收的时候会在回收以前把这个虚引用加到与之关联的队列中,然后由队列去监控,当有值时再由队列进行特定的回收。管理堆外内存

33、成员变量和局部变量的区别

  • 定义位置不同

    • 局部变量:在方法内部
    • 成员变量:在方法外部,直接写在类当中
  • 作用范围不一样

    • 局部变量:只有在方法当中才可以使用
    • 成员变量:整个类中通用
  • 默认值不一样

    • 局部变量:没有默认值,想使用必须手动赋值
    • 成员变量:如果没有赋值,就会有默认值
  • 内存位置不一样

    • 局部变量:位于栈内存
    • 成员变量:位于堆内存
  • 生命周期不一样

    • 局部变量:随着方法进栈而诞生,随着方法出栈而消失
    • 成员变量:随着对象创建而诞生,随着对象被垃圾回收而消失

34、JSON序列化

35、java异常处理

36、java集合

37、new与getinstance

  • new:

    • 一定要生成一个新对象,分配内存
    • 创建后只能当次使用
    • 是直接调用构造方法
  • getinstance

    • 是方法
    • 不一定要再次创建,可以把一个已存在的引用给你使用,这个在性能上优于new
    • 可以跨栈区域使用,或者远程跨区域使用。所以getInstance()通常是创建static静态实例方法的
    • 多用于单例模式
  • 对于抽象类要想对其实例化,只能使用getInstance方法,是不能new出来的。

38、new和newInstance

  • new

    • 直接创建一个类、不需要这个类加载过
    • 强类型(GC不会自动回收,只有所有的指向对象的引用被移除是才会被回收,若对象声明周期已经结束,但引用没有被移除,经常会出现内存溢出)
  • newInstance

    • 使用类加载机制创建对象
    • 创建类是这个类必须已经加载过且已经连接
    • 弱类型(GC是回收对象的限制条件很低,容易被回收)、低效率、只能调用无参构造。

实现接口的类加载一般采用newInstance而不会使用new

39、Collection.sort和Arrays.sort的实现原理

总体来说Collection.sort是对list进行排序,Arrays.sort是对数组进行排序

Collection.sort底层实现

Collection.sort方法调用了list.sort方法,而list.sort调用了Arrays.sort的方法

因此Collections.sort方法底层就是调用Array.sort方法

Arrays.sort底层实现
  • legacyMergeSort(a),归并排序

  • ComparableTimSort.sort():即Timsort排序

    • Timsort排序是结合了合并排序和插入排序得出的排序方法

40、HashMap、HashTable、ConcurrentHash

HashMap
  • 底层由链表+数组+红黑树实现
  • 可以存储null键和null值
  • 线性不安全
  • 初始容量为16,扩容每次都是2的n次幂
  • 加载因子为0.75,当Map中元素总数超过Entry数组的0.75,触发扩容操作.
  • 并发情况下,HashMap进行put操作会引起死循环,导致CPU利用率接近100%
  • HashMap是对Map接口的实现
HashTable
  • HashTable的底层也是由链表+数组+红黑树实现。
  • 无论key还是value都不能为null
  • 它是线性安全的,使用了synchronized关键字。
  • HashTable实现了Map接口和Dictionary抽象类
  • Hashtable初始容量为11
ConcurrentHashMap
  • ConcurrentHashMap的底层是数组+链表+红黑树
  • 不能存储null键和值
  • ConcurrentHashMap是线程安全的
  • ConcurrentHashMap使用锁分段技术确保线性安全
  • JDK8为何又放弃分段锁,是因为多个分段锁浪费内存空间,竞争同一个锁的概率非常小,分段锁反而会造成效率低。

41、快速失败和安全失败的区别

快速失败

在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出Concurrent Modification Exception的异常

安全失败

采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历

42、java扩展名

  • .class:编译后的java文件
  • .java:未编译的程序
  • .jsp:页面程序
  • .xml:配置程序
  • .jar:.class的集合

43、a=a+b和a+=b区别

+=操作符会对右边的表达式结果强转匹配左边的数据类型。

44、排查CPU过高

  • 使用top找到占用CPU最高的Java 进程
  • 使用 top -Hp pid命令,找到该pid下线程对应的系统占用情况
  • 使用jstack将该进程的线程栈保存下来
  • 然后解决问题

45、BIO、NIO、AIO

  • BIO:同步阻塞I/O模式,对于低数据量的情况较好
  • NIO:同步非阻塞的I/O模型,适用于高负载、高并发的应用
  • AIO:异步非阻塞的I/O模型

46、JVM1.8为啥用元空间取代了永久代

  • 为了避免OOM一次,因为通常使用PermSize和MaxPermSize设置永久代大小就决定了永久代的上线,但是容易不知道设置多少合适
  • 使用了元空间可以加载多少类的元数据就不再由MaxPermSize控制了,由系统的实际可用空间来控制

47、JVM加载Class文件的原理机制

JVM中类的装载是由类加载器和它的子类来实现的,Java中的类记载器是一个重要的Java运行时系统组件,负责在运行时查找和装入类文件中的类。

类的加载是指把类的.class文件中的数据读入到内存中,通常是创建一个字节数组读入.class文件。

48、transient关键字

  • 一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问
  • 其只能修饰变量,而不能修饰方法和类。另外本地变量也是不能被修饰的。变量如果是用于自定义类变量,则该类需要实现Serializable接口
  • 被其修饰的变量不能被序列化

另外一个静态变量不管是否被transient修饰都不能被序列化

49、问题排查

ssh连接失败,如何定位
  • 先判断是否可以ping通(DNS是否正确)
  • 对端端口是否开启了防火墙
  • 对端服务是否正常
Java程序发生了内存溢出,如何定位

jmap工具查看堆栈信息,看Eden、Old区的变化

杂谈

  • instanceof关键字是用来检查对象是否属于同一个类
  • 静态方法可以用类的名称来调用,而不是用对象引用变量
  • 移位是最有效的计算2次方的操作
  • 构造器不能被继承因此不能被重写,但是可以重载
  • 当一个Java类第一次被真正使用到的时候静态资源被初始化、Java类的加载和初始化过程都是线程安全的,比如枚举类型以及用枚举类创建单例模式。
  • 反射是动态代理的一种实现方式

最后

  • 如果觉得看完有收获,希望能给我点个赞,这将会是我更新的最大动力,感谢各位的支持
  • 欢迎各位关注我的公众号【java冢狐】,专注于java和计算机基础知识,保证让你看完有所收获,不信你打我
  • 如果看完有不同的意见或者建议,欢迎多多评论一起交流。感谢各位的支持以及厚爱。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值