Day44-Java面试系列(八)-牛客网Java高频面试题

本文详细介绍了Java编程中的核心概念,包括抽象类与接口的区别、Java的特点和优势、基本数据类型与引用类型、String相关类的对比、final关键字的使用、==与equals()的差异、static修饰符的作用以及面向对象的三大特性。此外,还讨论了线程创建的不同方式,并阐述了SpringBoot与Spring的关系及其在项目构建中的优势。
摘要由CSDN通过智能技术生成

1、请你说一下抽象类和接口的区别

语法层面上的区别:

  • 抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract方法;
  • 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的;
  • 接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;
  • 一个类只能继承一个抽象类,而一个类却可以实现多个接口。

设计层面上的区别:

  • 抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。
  • 设计层面不同,抽象类作为很多子类的父类,它是一种设计模板式设计。而接口是一种行为规范,它是一种辐射式设计。

2、请说说Java的特点和优点,为什么要选择Java?

  • 面向对象(封装、继承、多态)
  • 平台无关性,平台无关性的具体表现在于,Java是一次编写,到处运行(Write Once,Run AnyWhere)的语言,因此采用Java语言编写具有很好的可移植性,而保证这一点的正是Java的虚拟机机制。在引入虚拟机之后,Java语言在不同的平台上运行不需要重新编译。
  • 可靠性、安全性
  • 支持多线程。C++语言没有内置的多线程机制,因此必须调用操作系统的多线程功能来进行多线程程序设计,而Java语言却提供了多线程支持;
  • 支持网络编程并且很方便,Java语言诞生本身就是为了简化网络编程设计的,因此Java语言不仅支持网络编程而且很方便;
  • 编译与解释并存;

3、请你说说Java基本数据类型和引用类型

  • 基本数据类型(四类八种):整数类型(byte、short、int、long)、浮点型(double、float)、字符类型(char)、布尔类型(boolean)
  • 引用类型(三种):数组、类、接口(null类型)
    • byte:1字节(8位),数据范围是 -2^7 ~ 2^7-1
    • short:2字节(16位),数据范围是 -2^15 ~ 2^15-1
    • int:4字节(32位),数据范围是 -2^31 ~ 2^31-1
    • long:8字节(64位),数据范围是 -2^63 ~ 2^63-1
    • float:4字节(32位),数据范围大约是 -3.4*10^38 ~ 3.4*10^38
    • double:8字节(64位),数据范围大约是 -1.8*10^308 ~ 1.8*10^308
    • char:2字节(16位),数据范围是 \u0000 ~ \uffff
    • boolean:Java规范没有明确的规定,不同的JVM有不同的实现机制。

4、String、StringBuffer、Stringbuilder有什么区别

  1. 可变与不可变。String类中使用字符数组保存字符串,因为有“final”修饰符,所以string对象是不可变的。对于已经存在的String对象的修改都是重新创建一个新的对象,然后把新的值保存进去.
    String类利用了final修饰的char类型数组存储字符,源码如下:
private final char value[];

StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,这两种对象都是可变的。

源码如下:

char[] value;
  1. 是否多线程安全。
  • String中的对象是不可变的,也就可以理解为常量,显然线程安全。
  • StringBuilder是线程不安全的。
  • StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。
    源码如下:
@Override
public synchronized StringBuffer append(String str){
	toStringCache = null;
	super.append(str);
	return this;
}
  1. 性能
    每次对String类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String对象。StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用StringBuilder相比使用StringBuffer仅能获得10%~15%左右的性能提升,但却要冒多线程不安全的风险。

5、请你说一下final关键字

  • final关键字可以用来标志其修饰的类,方法和变量不可变。
  • 当final修饰类时,该类不能被继承,例如java.lang.Math类就是一个final类,它不能被继承。
  • final修饰的方法不能被重写,如果出于某些原因你不希望子类重写父类的某个方法,就可以用final关键字修饰这个方法。
  • 当final用来修饰变量时,代表该变量不可被改变,一旦获得了初始值,该final变量的值就不能被重新赋值。
  • final既可以修饰成员变量(包括类变量和实例变量),也可以修饰局部变量、形参。
  • 加分回答 对于final修饰的成员变量而言,一旦有了初始值就不能被重新赋值,如果既没有在定义成员变量时指定初始值,也没有在初始化块,构造器中为成员变量指定初始值,那么这个成员变量的值将一直是系统默认分配的0、‘\u0000’、false或者是null,那么这个成员变量就失去了存在的意义,所以Java语法规定:final修饰的成员变量必须由程序员显示的指定初始值。
  • final修饰的实例变量,要么在定义该实例变量时指定初始值,要么在普通初始化块或构造器中为该实例变量指定初始值。但要注意的是,如果普通初始化块已经为某个实例变量指定了初始值,则不能再在构造器中为该实例变量指定初始值;
  • final修饰的类变量,要么在定义该变量时指定初始值,要么在静态初始化块中为该类变量指定初始值。 实例变量不能在静态初始化块中指定初始值,因为静态初始化块是静态成员,不可以访问实例变量;
  • 类变量不能在普通初始化块中指定初始值,因为类变量在类初始化阶段已经被初始化了,普通的初始化块不能为其重新赋值。 系统不会为局部变量进行初始化,所以局部变量必须由程序员显示的初始化。
  • 因此使用final修饰局部变量时,既可以在定义时指定默认值,也可以不指定默认值。如果final修饰的局部变量在定义是没有指定默认值,则可以在后面的代码中对该final变量赋初始值,但只能一次,不能重复赋值;如果final修饰的局部变量在定义时已经指定默认值,则后面代码中不能再对该变量赋值。

6、请你说说==与equals()的区别

==常用于相同的基本数据类型之间的比较,也可用于相同类型的对象之间的比较;

  • 如果==比较的是基本数据类型,那么比较的是两个基本数据类型的值是否相等;
  • 如果==是比较的两个对象,那么比较的是两个对象的引用,也就是判断两个对象是否指向了同一块内存区域;

equals方法主要用于两个对象之间,检测一个对象是否等于另一个对象

看一看Object类中equals方法的源码:

public boolean equals(Object obj) {
        return (this == obj);
    }

它的作用也是判断两个对象是否相等,般有两种使用情况:

  • 情况1,类没有覆盖equals()方法。则通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象。
  • 情况2,类覆盖了equals()方法。一般,我们都覆盖equals()方法来两个对象的内容相等;若它们的内容相等,则返回true(即,认为这两个对象相等)。

java语言规范要求equals方法具有以下特性:

  • 自反性。对于任意不为null的引用值x,x.equals(x)一定是true。
  • 对称性)。对于任意不为null的引用值x和y,当且仅当x.equals(y)是true时,y.equals(x)也是true。
  • 传递性。对于任意不为null的引用值x、y和z,如果x.equals(y)是true,同时y.equals(z)是true,那么x.equals(z)一定是true。
  • 一致性。对于任意不为null的引用值x和y,如果用于equals比较的对象信息没有被修改的话,多次调用时x.equals(y)要么一致地返回true要么一致地返回false。
  • 对于任意不为null的引用值x,x.equals(null)返回false。

7、说说static修饰符的用法

  • Java类中包含了成员变量、方法、构造器、初始化块和内部类(包括接口、枚举)5种成员,static关键字可以修饰除了构造器外的其他4种成员。
  • static关键字修饰的成员被称为类成员。类成员属于整个类,不属于单个对象。 static关键字有一条非常重要的规则,即类成员不能访问实例成员,因为类成员属于类的,类成员的作用域比实例成员的作用域更大,很容易出现类成员初始化完成时,但实例成员还没被初始化,这时如果类成员访问实力成员就会引起大量错误。 加分回答 static修饰的部分会和类同时被加载。被static修饰的成员先于对象存在,因此,当一个类加载完毕,即使没有创建对象也可以去访问被static修饰的部分。 静态方法中没有this关键词,因为静态方法是和类同时被加载的,而this是随着对象的创建存在的。静态比对象优先存在。也就是说,静态可以访问静态,但静态不能访问非静态而非静态可以访问静态。

8、说说你对面向对象的理解

面向对象的三大基本特征是:封装、继承、多态。其中,封装是指将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,让外部程序通过该类提供的方法来实现对内部信息的操作和访问,这种做法有助于规范使用者的行为,让使用者只能通过事先预定的方法访问数据,提高了代码的可维护性;继承是面向对象实现代码复用的重要手段,Java通过extends作为关键字实现类的继承,实现继承的类被称为子类,被继承的类称为父类(有的也被称为基类和超类),父类和子类的关系是一种一般和特殊的关系;多态的实现离不开继承,在设计程序时,我们可以将参数的类型定义为父类型。在调用程序时,则可以根据实际情况,传入该父类型的某个子类型的实例,这样就实现了多态。对于父类型,可以有三种形式,即普通的类、抽象类、接口。对于子类型,则要根据它自身的特征,重写父类的某些方法,或实现抽象类/接口的某些抽象方法。 加分回答 通过封装可以实现这些优点: - 隐藏类的成员变量和实现细节,不允许外部直接访问 - 规范使用者的行为,让使用者只能通过事先预定的方法访问数据,通过在这个方法中加入逻辑控制,限制使用者对成员变量的不合理访问 - 可进行数据检查,从而有利于保证对象信息的完整性; - 便于修改,提高代码的可维护性 使用继承的优点包括: - 代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性,提高了代码复用 - 提高代码的可扩展性,很多开源框架的扩展接口都是通过继承父类来完成的 但同时继承也有很多缺点: - 继承是侵入性的。只要继承,就必须拥有父类的所有属性和方法; - 降低代码的灵活性,子类必须拥有父类的属性和方法。 - 增强了耦合性。当父类的常量、变量和方法被修改时,需要考虑子类的修改,而且在缺乏规范的环境下,这种修改可能会导致大段的代码需要重构。 那么多态也有这些优点: - 提高了代码的维护性 - 提高了代码的扩展性 java中实现多态需要三个条件: 1. 需要有继承关系的存在。 2. 需要有方法的重写。 3. 需要有父类的引用指向子类对象。

9、说说线程的创建方式

  • 创建线程有三种方式,分别是继承Thread类、实现Runnable接口、实现Callable接口。
    1. 通过继承Thread类来创建线程的步骤如下 - 定义Thread类的子类,并重写该类的run()方法,该方法将作为线程执行体。 - 创建Thread子类的实例,即创建了线程对象。 - 调用线程对象的start()方法来启动该线程。
    1. 通过实现Runnable接口来创建线程的步骤如下 - 定义Runnable接口的实现类,并实现该接口的run()方法,该方法将作为线程执行体。 - 创建Runnable实现类的实例,并将其作为参数来创建Thread对象,Thread对象为线程对象。 - 调用线程对象的start()方法来启动该线程。
    1. 通过实现Callable接口来创建线程的步骤如下 - 定义Callable接口的实现类,并实现call()方法,该方法将作为线程执行体。
  • 创建Callable实现类的实例,并以该实例作为参数,创建FutureTask对象。
  • 使用FutureTask对象作为参数,创建Thread对象,然后启动线程。
  • 调用FutureTask对象的get()方法,获得子线程执行结束后的返回值。 归纳起来,创建线程的方式实际只有两种:继承父类和实现接口。而使用Runnable接口和Callable接口的方式,区别在于前者不能获得线程执行结束的返回值,后者可以获得线程执行结束的返回值。
  • 而继承父类和实现接口这两种方式的优缺点是:
    - 采用接口的方式创建线程,优点是线程类还可以继承于其他类,并且多个线程可以共享一个线程体,适合多个线程处理同一份资源的情况。缺点是编程稍微麻烦一点点。
  • 采用继承的方式创建线程,优点是编程稍微简单一点点。缺点是因为线程类已经继承了Thread类,所以就不能继承其他的父类了。 所以,通常情况下,更推荐采用接口的方式来创建线程。如果需要返回值,就使用Callable接口,否则使用Runnable接口即可。

10、说说你对Spring Boot的理解,以及它和Spring的区别?

得分点 Spring Boot与Spring 的关系 、Spring Boot的主要功能 、Spring Boot的优点 标准回答 其实从本质上来说,Spring Boot就是Spring,它帮你完成了一些Spring Bean配置。Spring Boot使用“习惯优于配置”的理念让你的项目快速地运行起来,使用Spring Boot能很快的创建一个能独立运行、准生产级别、基于Spring框架的项目。 但Spring Boot本身不提供Spring的核心功能,而是作为Spring的脚手架框架,达到快速构建项目,预设第三方配置,开箱即用的目的。Spring Boot有很多优点,具体如下:

  • 可以快速构建项目
  • 可以对主流开发框架的无配置集成
  • 项目可独立运行,无需外部依赖Servlet容器
  • 提供运行时的应用监控
  • 可以极大地提高开发、部署效率
  • 可以与云计算天然集成 加分回答 Spring Boot 的核心功能: 1. 自动配置 针对很多Spring应用程序常见的应用功能,Spring Boot能自动提供相关配置。 2. 起步依赖 Spring Boot通过起步依赖为项目的依赖管理提供帮助。起步依赖其实就是特殊的Maven依赖和Gradle依赖,利用了传递依赖解析,把常用库聚合在一起,组成了几个为特定功能而定制的依赖。 3. 端点监控 Spring Boot 可以对正在运行的项目提供监控。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿杰杰杰のblog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值