【Java面试总结】Java基础(上篇)

时间:2022-07-24
本文章向大家介绍【Java面试总结】Java基础(上篇),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

Java基础

1. 面向对象和面向过程的区别

  • 面向过程:其性能高于面向对象。因为类调用时需要实例化,开销比较大,比较消耗资源,因此在考虑性能的条件下,比如linux、单片机等都采用面向过程开发。但是,面向过程没有面向对象易维护、易复用、易扩展。
  • 面向对象:面向对象易维护、易复用、易扩展。因为其有继承、多态、封装的特性,因此可以设计出低耦合的系统,使系统更加灵活、更加易于维护。但是,面向对象性能比面向过程低。

2. Java语言的特点

  1. 简单易学;
  2. 面向对象(封装、继承、多态);
  3. 平台无关性(Java虚拟机可以实现跨平台);
  4. 可靠性;
  5. 安全性
  6. 支持多线程(C++ 语言没有内置的多线程机制,因此必须调用操作系统的多线程功能来进行多线程程序设计,而 Java 却提供了多线程支持);
  7. 支持网络编程(Java 语言的诞生就是为了简化网络编程设计的,因此Java 不仅支持网络编程而且很方便);
  8. 编译与解释并存。

注:C++11 开始(2011 年的时候),C++就引入了多线程库,在 windows、linux、macos 都可以使用stdÇÉthread和stdÇÉasync来创建线程。参考链接:http://www.cplusplus.com/reference/thread/thread/?kw=thread

3. 关于 JVM JDK 和JRE

JVM

Java虚拟机(JVM)是运行Java字节码的虚拟机。JVM 有针对不同系统的特定实现(windows,Linux,macOS),目的是使用相同的字节码,它们都会给出相同的结果。

什么是字节码?采用字节码的好处是什么?

  • 在Java中,代码就是字节码(扩展名是 .class的文件),它不面向任何的特定的处理器,只面向虚拟机。
  • Java语言通过通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释性语言可移植的特点。所以Java程序运行时比较高效。而且,由于字节码并不是针对特定的机器,因此,Java 程序无需重新编译便可在不同的操作系统的计算机运行。

Java 程序从源代码到运行一般有下面3步:

需要格外注意的是: .class -> 机器码 这一步。在这一步,JVM 类加载器首先会加载字节码文件,然后通过解释器逐行解释执行,这种方式的执行速度会相对比较慢;而且,有些方法和代码块是经常需要被重复调用的(也就是所谓的热点代码),所以后面引进了 JIT 编译器,而 JIT 属于运行时编译。当 JIT 编译器完成第一次编译后,其会将字节码对应的机器码保存下来,下次可以直接使用。我们知道,机器码的运行效率是肯高于 Java 解释器的。这也解释了我们为什么会说 Java 是 编译与解释共存的语言。

HotSpot 采用了惰性评估(Lazy Evaluation)的做法,根据二八定律,消耗大部分系统资源的只有那一小部分的代码(热点代码),而这也就是 JIT 所需要编译的部分。JVM 会根据代码每次被执行的情况收集信息并相应地做出一些优化,因此执行的次数越多,它的速度就越快。JDK 9 引入了一种新的编译模式 AOT(Ahead of Time Compilation),它是直接将字节码编译成机器码,这样就避免了JIT 预热等各方面的开销。JDK 支持分层编译和 AOT 协作使用。但是,AOT 编译器的编译质量是肯定比不上 JIT 编译器的。

总结:

Java虚拟机(JVM)是运行Java字节码的虚拟机,JVM有针对不同系统的特定实现(Windows,Linux,macOS),目的是使用相同的字节码,它们都会给出相同的结果。字节码和不同系统的 JVM 实现是 Java语言 “一次编译,随处可以运行”的关键所在。


JDK 和 JRE

JDK 是 Java Development Kit,它是功能齐全的Java SDK。它拥有 JRE 所拥有的的一切,还有编译器(javac)和 工具(javadoc 和 jdb)。它能够创建和编译程序。

JRE 是 Java 运行时环境。它是运行已编译 Java 程序所需的所有内容的集合,包括Java虚拟机(JVM),Java类库,Java 命令和 其它的一些基础构件。但是,它不能用于创建新程序。

如果你只是为了运行一下 Java 程序的话,那么你只需要安装 JRE 就可以了。如果你需要进行一些Java 编程方面的工作,那么你就需要安装 JDK 了。但是,这不是绝对的。有时,即使您不打算在计算机上进行任何 Java 开发,仍然需要安装 JDK。例如,如果要使用 JSP 部署 Web 应用程序,那么从技术上讲,您只是在应用程序服务器中运行 Java 程序。那你为什么需要 JDK 呢?因为应用程序服务器会将 JSP 转换为 Java servlet,并且需要使用 JDK 来编译 servlet。

4. Oracle JDK 和 OpenJDK 的对比

对于 Java 7,没什么关键的地方。OpenJDK 项目主要基于 Sun 捐赠的 HotSpot 源代码。此外,OpenJDK 被选为 Java 7 的参考实现,由 Oracle 工程师维护。

问:OpenJDK 存储库中的源代码与用于构建 Oracle JDK 的代码之间有什么区别? 答:非常接近 - 我们的 Oracle JDK 版本构建过程基于 OpenJDK 7 构建,只添加了几个部分,例如部署代码,其中包括 Oracle 的 Java 插件和 Java WebStart 的实现,以及一些封闭的源代码派对组件,如图形光栅化器,一些开源的第三方组件,如 Rhino,以及一些零碎的东⻄,如附加文档或第三方字体。展望未来,我们的目的是开源 Oracle JDK 的所有部分,除了我们考虑商业功能的部分。

总结:

  1. Oracle JDK 大概每6个月发一次主要版本,而OpenJDK 版本大概每三个月发布一次。
  2. OpenJDK 是一个参考模型并且是完全开源的,而 Oracle JDK 是 OpenJDK 的一个实现,并不是完全开源的。
  3. Oracle JDK 比 OpenJDK更稳定。两者代码几乎相同,但是Oracle有更多的类额一些错误修复。
  4. 在 响应性 和 JVM 性能方面,Oracle JDK 与 OpenJDK相比提供了更好的性能;
  5. Oracle JDK不会为即将发布的版本提供长期支持,用户每次都必须通过更新到最新版本获得支持来获取最新版本;
  6. Oracle JDK是根据二进制代码许可协议获得许可,而 OpenJDK 根据GPL v2 许可获得许可。

5. Java 和 C ++ 的区别

  • 都是面向对象的语言,都支持 封装、继承、多态
  • Java 不提供指针来直接访问内存,程序内存更加安全
  • Java 的类是单继承的,C ++ 支持多重继承;虽然Java 的类不可以多继承,但是接口可以多继承
  • Java 有自动内存管理机制,不需要程序员手动释放无用内存
  • 在 C 语言中,字符串或字符数组最后都会有一个额外额字符“”来表示结束,但是,Java中没有结束符这一概念。 (具体参考:https://blog.csdn.net/sszgg2006/article/details/49148189

6. 什么是 Java 程序的主类?应用程序和小程序的主类有什么不同?

一个程序中可以有多个类,但只有一个类是主类。在Java应用程序中,这个主类的类是包含在main() 方法的类中。而在Java小程序中,这个主类是一个继承自系统类 JApplet 或 Applet 的子类。应用程序的主类不一定要求是public类,但小程序的主类要求必须是public类。主类是Java程序执行的入口点。

7. Java应用程序和小程序之间有哪些差别?

简单来说应用程序是从主线程启动(也就是main()方法)。applet小程序没有main()方法,主要是嵌在浏览器页面上运行(调用init()或者run()来启动),嵌入浏览器这点跟flash的小游戏类似。

8. 字符型常量和字符串常量的区别?

  1. 形式上:字符常量是单引号引起的一个字符;字符串常量是双引号引起的若干个字符
  2. 含义上:字符常量相当于一个整型值(ASCII 值),可以参加表达式运算;字符串常量代表一个地址值(该字符串在内存中存放的位置)
  3. 占内存大小:字符常量只占2个字节;字符串常量占若干个字节(注意:char在Java中占两个字节)

9. 构造器Constructor 是否可以被 override?

Constructor 不能被 Override(重写),但是可以overload(重载),所以你可以看到一个类中有多个构造函数的情况。

10. 重载和重写的区别

  • 重载就是同样的一个方法能够根据输入数据的不同,做出不同的处理
  • 重写就是当子类继承父类的相同方法,输入数据一样,但是要做出有别于父类的响应时,就要覆盖父类方法

重载

发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同,也可以抛出不同的异常。

下面是《Java 核心技术》对重载这个概念的介绍:

综上所述:重载就是同一个类中多个同名方法根据不同的传参来执行不同的逻辑处理。

重写

重写发生在运行期,是子类对父类的允许访问的方法的实现过程进行重新编写

  1. 参数列表必须完全与被重写的方法相同,否则不能称其为重写而是重载
  2. 返回的类型必须一直与被重写的方法的返回类型相同,否则不能称其为重写而是重载
  3. 访问修饰符的限制一定要大于等于被重写方法的访问修饰符(public>protected>default>private)
  4. 重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常
  5. 如果父类方法访问修饰符为 private/final/static,则子类就不能重写该方法,但是被static修饰的方法能够被再次声明
  6. 构造方法无法被重写

综上:重写就是子类对父类的重新改造,外部样子不能改变,内部逻辑可以改变。

区别

重载

重写

发生范围

同一个类

子类中

参数列表

必须修改

一定不能修改

返回类型

可修改

一定不能修改

异常

可修改

可以减少或删除,一定不能抛出新的异常或更广的异常

访问修饰符

可修改

一定不能做更严格的限制(可以降低限制)

发生阶段

编译期

运行期

11. Java面向对象编程三大特性:封装、继承、多态

封装:

封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。

继承:

继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承,我们能够很方便的复用以前的代码。

注意:

  1. 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类的私有属性和方法,子类是无法访问,只是拥有;
  2. 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展;
  3. 子类可以用自己的方法实现父类的方法。

多态:

多态就是同一个行为具有多个不同表现形式或形态的能力;

多态就是同一个接口,使用不同的实例而执行不同操作,如图所示:

多态的优点:

  1. 消除类型之间的耦合关系;
  2. 可替换性;
  3. 可扩充性;
  4. 接口性;
  5. 灵活性;
  6. 简化性;

多态存在的三个必要条件:

  1. 继承;
  2. 重写;
  3. 父类引用指向子类对象

在Java中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中的同一方法)。

12. String, StringBuffer和StringBuilder的区别是什么?String为什么是不可变的?

可变性:

简单的来说:String 类 中使用 final 关键字修饰字符数组来保存字符串,private final char value[],所以String对象是不可变的。

注:Java 9 之后,String类的实现改用 byte 数组存储字符串 private final byte value[]

而 StringBuffer 和 StringBuilder 都继承自 AbstractStringBuilder 类,在AbstactStringBuilder 中也是使用字符数组保存字符串char value[],但是没有用 final 关键字修饰,所以这两种对象是可变的。

线程安全性:

String 中的对象是不可变的,也就可以理解为常量,线程安全。

AbstactStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义了一些字符串的基本操作,如 append、insert、indexOf 等公共方法。StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以线程时安全的。StringBuilder 并没有对方法进行加同步锁,所以是 非线程安全的。

性能:

每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的String对象。StringBuffer 每次都会对 StringBuffer 本身进行操作,而不是生成新的对象并改变对象引用。相同条件下,使用 StringBuilder 相比 使用 StringBuffer 仅能获得10% ~ 15% 左右的性能提升,但却要冒多线程不安全的风险。

对于三者使用的总结:

  1. 操作少量的数据:适用 String
  2. 单线程操作字符串缓冲区下操作大量数据:适用 StringBuilder
  3. 多线程操作字符串缓冲区下操作大量数据:适用 StringBuffer

13. 自动拆箱和装箱

  • 装箱:将基本类型用它们对应的引用类型包装起来;
  • 拆箱:将包装类型转换为基本数据类型

基本数据类型对应的包装器类型:

int (4字节)

Integer

byte (1字节)

Byte

short (2字节)

Short

long(8字节)

Long

float(4字节)

Float

double(8字节)

Double

char (2字节)

Character

boolean(未定)

Boolean

装箱和拆箱是如何实现的

在装箱的时候自动调用的是Integer的valueOf(int)方法。而在拆箱的时候自动调用的是Integer的intValue方法。其他的也类似,比如Double、Character

因此可以用一句话总结装箱和拆箱的实现过程

装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的 xxxValue方法实现的。(xxx代表对应的基本数据类型)。

关于装箱和拆箱的更多内容以及面试必记,请访问:深入解剖Java中的装箱和拆箱


14. 在一个静态方法内调用一个非静态成员为什么是非法的?

由于静态方法可以不通过对象调用,因此在静态方法里面,不能调用其他非静态变量,也不可以访问非静态变量成员。

15. 在 Java 中定义一个不做事且没有任何参数的构造方法的作用

Java 程序在执行子类的构造方法之前,如果没有用super()来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法”。因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用super()来调用父类中特定的构造方法,则编译时将发生错误,因为Java程序在父类中找不到没有参数的构造方法可供执行。

解决方法是:在父类中加上一个不做事且没有任何参数的构造方法。

16. import java 和 javax 有什么区别?

刚开始的时候 JavaAPI 所必需的包是 java 开头的包,javax 当时只是扩展 API 包来使用。然而随着时间的推移,javax 逐渐地扩展成为 Java API 的组成部分。但是,将扩展从 javax 包移动到 java包确实太麻烦了,最终会破坏一堆现有的代码。因此,最终决定 javax 包将成为标准 API 的一部分。

所以,实际上 java 和 javax 没有区别。这都是一个名字。

17. 接口和抽象类的区别是什么?

  1. 接口的方法默认是 public,所有方法在接口不能有实现(Java 8 开始接口方法可以有默认实现),而抽象类可以有非抽象的方法
  2. 接口中除了 static,final 变量,不能有其他变量,而抽象类中则不一定
  3. 一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过extends 关键字扩展多个接口
  4. 接口方法默认修饰符是 public,抽象方法可以有 public、protected 和 default 这些修饰符(抽象方法就是为了被重写所以不能使用private关键字修饰)
  5. 从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。

注:

  1. 在 JDK 8 中,接口也可以定义静态方法,可以直接用接口名调用。实现类和实现是不可以调用的。如果同时实现两个接口,接口中定义了一样的默认方法,则必须重写,不然会报错。
  2. jdk 9 的接口被允许定义私有方法。

总结一下 jdk7 ~ jdk 9,Java中接口概念的变化:

  1. 在 jdk 7 或更早版本中,接口里面只能有常量变量和抽象方法。这些接口方法必须由选择实现接口的类实现。
  2. jdk8 的时候接口可以有默认方法和静态方法功能。
  3. Jdk 9 在接口中引入了私有方法和私有静态方法。

18. 成员变量与局部变量的区别有哪些?

  1. 定义的位置不一样 局部变量:写在方法内部 成员变量:写在方法外部,直接写在类当中
  2. 作用范围不一样 局部变量:只有方法当中才可以使用,出了方法就不能再用 成员变量:整个类全都可以通用。
  3. 默认值不一样 局部变量:没有默认值,如果想要使用,必须手动进行赋值 成员变量:如果没有赋值,会有默认值,规则和数组一样
  4. 内存的位置不一样 局部变量:位于栈内存 成员变量,位于堆内存。
  5. 生命周期不一样 局部变量:随着方法进栈而诞生,随着方法出栈而死亡。 成员变量:随着对象创建而诞生,随着对象被垃圾回收而消失。

19. 创建一个对象用什么运算符?对象实体和对象引用有何不同?

new 运算符,new 创建对象实例(对象实例在堆内存中),对象引用指向对象实例(对象引用存放在栈内存中)。一个对象引用可以指向 0 个或 1 个对象(一根绳子可以不系气球,也可以系一个气球);一个对象可以有 n 个引用指向它(可以用 n 条绳子系住一个气球)。

20. 什么是方法的返回值?返回值在类的方法里的作用是什么?

方法返回值:是指我们获取到的某个方法体中的代码执行后产生的结果(前提是该方法可能产生结果);

返回值的作用:接收出结果,使得它可以用于其他的操作。

21. 一个类的构造方法的作用是什么?若一个类没有声明构造方法,该程序能正确执行吗?为什么?

主要作用是完成对类对象的初始化工作;

可以执行;

因为一个类即使没有声明构造方法,也会有默认的不带参数的构造方法。

22. 构造方法有哪些特性?

  1. 名字和类名相同;
  2. 没有返回值,但不能用void声明构造函数;
  3. 生成类的对象时自动处理,无需调用。

23. 静态方法和实例方法有何不同?

  1. 在外部调用静态方法时,可以使用“类名.方法名”的方式,也可以使用“对象名.方法名”的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。
  2. 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制。

24. 对象的相等与指向它们的引用相等,两者有什么不同?

对象相等,比的是内存中存放的内容是否相等;

而引用相等,比较的是他们指向的内存地址是否相等。

25. 在调用子类构造方法之前会先调用父类没有参数的构造方法,其目的是?

帮助子类做初始化工作。