java基础面试题

系列文章目录

提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:第一章 java基础



1、面向对象

面向对象的三个基本特征是:封装、继承和多态

  • 封装可以隐藏实现细节,使得代码模块化
  • 继承可以扩展已存在的代码模块。
  • 多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性时的正确调用

封装和继承的目的都是代码重用,而多态则是为了实现接口重用

封装

  1. 什么是封装
    封装:就是隐藏对象的属性和实现细节,仅对外提供公共访问方式
  2. 封装的好处
    • 良好的封装能够减少耦合
    • 类内部的结构可以自由修改
    • 可以对成员进行更精确的控制
    • 隐藏信息,实现细节。

继承

  1. 什么是继承
    所谓继承:是指可以让某个类型的对象获得另一个类型的对象的属性的方法
    兔子和羊属于食草动物类,狮子和豹属于食肉动物类。食草动物和食肉动物又是属于动物类。所以继承需要符合的关系是:is-a,父类更通用,子类更具体。虽然食草动物和食肉动物都是属于动物,但是两者的属性和行为上有差别,所以子类会具有父类的一般特性也会具有自身的特性
  2. 继承的特性
    • 子类拥有父类非private的属性和方法
    • 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展
    • 子类可以用自己的方式实现父类的方法
    • 提高了类之间的耦合性
    • 在Java中只允许单继承,而不允许多重继承,也就是说一个子类只能有一个父类,但是Java中却允许多层继承,多层继承就是,例如类C继承类B,类B继承类A,所以按照关系就是类A是类B的父类,类B是类C的父类,这是Java继承区别于C++继承的一个特性
  3. 继承条件下构造方法调用规则
    • 如果子类的构造方法中没有通过super显示调用父类的有参构造方法,也没有通过this显示调用自身的其他构造方法,则系统会默认先调用父类的无参构造方法。在这种情况下写不写super()语句效果都是一样
    • 如果子类的构造方法中通过super显示调用父类的有参构造方法,那将执行父类相应构造方法,而不执行父类无参构造方法
    • 如果子类的构造方法中通过this显示调用自身的其他构造方法,在相应构造方法中应用以上两条规则
    • 特别注意的是,如果存在多级继承关系,在创建一个子类对象时,以上规则会多次向更高一级父类应用,一直到执行顶级父类Object类的无参构造方法为止。

super和this关键字

  1. this
    • 表示当前对象
    • 调用本类中的方法或属性
    • 调用本类中的构造方法时,需要放在程序首行
  2. super
    • 子类调用上级父类的方法或属性
    • 调用父类中的构造方法时,需要放在程序首行

方法的重写与重载

  1. 方法的重写
    子类从父类中继承方法,有时子类需要修改父类中定义的方法的实现,这称做方法的重写。当一个子类继承一父类,而子类中的方法与父类中的方法的名称、参数个数和类型都完全一致时,就称子类中的这个方法重写了父类中的方法。
  2. 重写的规则
    • 重写的方法不能比被重写的方法有更严格的访问权限
    • 重写的方法不能比被重写的方法产生更多的异常
  3. 方法的重载
    方法重载是指多个方法可以享有相同的名字,但是参数的数量或类型不能完全相同。调用方法时,编译器根据参数的个数和类型来决定当前所使用的方法。方法重载为程序的编写带来方便,是OOP多态性的具体表现。在Java系统的类库中,对许多重要的方法进行重载,为用户使用这些方法提供了方便。
  4. 重载的规则
    • 被重载的方法必须改变参数列表(参数个数或类型不一样)
    • 被重载的方法可以改变返回类型
    • 被重载的方法可以改变访问修饰符
    • 被重载的方法可以声明新的或更广的检查异常
    • 方法能够在同一个类中或者在一个子类中被重载
    • 无法以返回值类型作为重载函数的区分标准。

抽象类

抽象类定义的规则

  • 抽象类和抽象方法都必须用abstract关键字来修饰
  • 抽象类不能被实例化,也就是说不能用new关键字去创建对象
  • 抽象方法只需声明,而不需实现
  • 含有抽象方法的类必须被声明为抽象类,抽象类的子类必须复写所有的抽象方法后才能被实例化,否则这个子类还是个抽象类。

注意

  • 在抽象类定义的语法中,方法的定义可分为两种:一种是一般的方法;另一种是“抽象方法”,它是以abstract关键字为开头的方法,此方法只声明了返回值的数据类型、方法名称与所需的参数,但没有定义方法体。
  • 抽象类也可以像普通类一样,有构造方法、一般方法、属性,更重要的是还可以有一些抽象方法,留给子类去实现,而且在抽象类中声明构造方法后,在子类中必须明确调用。

final关键字

在Java中声明类、属性和方法时,可使用关键字final来修饰
修饰类:表示该类不能被继承
修饰方法:表示该方法不能被子类重写,但是可以重载
修饰变量:表示该变量一旦被赋值就不可以更改

修饰成员变量

  • 如果final修饰的是类变量,只能在静态初始化块中指定初始值或者声明该类变量时指定初始值。
  • 如果final修饰的是成员变量,可以在非静态初始化块、声明该变量或者构造器中执行初始值

修饰局部变量
系统不会为局部变量进行初始化,局部变量必须由程序员显示初始化。因此使用final修饰局部变量时,即可以在定义时指定默认值(后面的代码不能对变量再赋值),也可以不指定默认值,而在后面的代码中对final变量赋初值(仅一次)

修饰基本数据类型和引用数据类型

  • 如果是基本数据类型,则其数值在初始化后便不可修改
  • 如果是引用数据类型,则在对其初始化后便不能再让其指向另一个对象,但是对象内的属性可以修改

为什么局部内部类或匿名内部类只能访问局部final变量
因为内部类和外部类是处于同一个级别的,内部类不会因为定义在方法中就会随着方法的执行完毕就被销毁。这里就会导致:当外部类的方法结束时,局部变量就会被销毁了,但是内部类对象可能还存在(只有没有人再引用它时,才会死亡)。这里就出现了一个矛盾:内部类对象访问了一个不存在的变量。为了解决这个问题,就将局部变量复制了一份作为内部类的成员变量,这样当局部变量死亡后,内部类仍可以访问它,实际访问的是局部变量的"copy"。这样就好像延长了局部变量的生命周期
将局部变量复制为内部类的成员变量时,必须保证这两个变量是一样的,所以就将局部变量设置为final,对它初始化后,我就不让你再去修改这个变量,就保证了内部类的成员变量 和方法的局部变量的⼀致性。这实际上也是⼀种妥协。使得局部变量与内部类内建立的拷贝保持⼀致。

接口

  1. 接口的定义
    接口(interface)是Java所提供的另一种重要技术,它的结构和抽象类非常相似,也具有数据成员与抽象方法,但它与抽象类又有以下两点不同

    • 接口里的数据成员必须初始化,且数据成员均为常量
    • 接口里的方法必须全部声明为abstract,也就是说,接口不能像抽象类一样保有一般的方法,而必须全部是“抽象方法”。
  2. 接口的实现
    在Java中接口是用于实现多继承的一种机制,也是Java设计中最重要的一个环节,每一个由接口实现的类必须在类内部复写接口中的抽象方法,且可自由地使用接口中的常量。 既然接口里只有抽象方法,它只要声明而不用定义处理方式,于是自然可以联想到接口也没有办法像一般类一样,再用它来创建对象。利用接口打造新的类的过程,称之为接口的实现(implementation)。

  3. 接口的扩展
    接口是Java实现多继承的一种机制,一个类只能继承一个父类,但如果需要一个类继承多个抽象方法的话,就明显无法实现,所以就出现了接口的概念。一个类只可以继承一个父类,但却可以实现多个接口。

    接口与一般类一样,均可通过扩展的技术来派生出新的接口。原来的接口称为基本接口或父接口,派生出的接口称为派生接口或子接口。通过这种机制,派生接口不仅可以保留父接口的成员,同时也可加入新的成员以满足实际的需要。

    同样的,接口的扩展(或继承)也是通过关键字extends来实现的。有趣的是,一个接口可以继承多个接口,这点与类的继承有所不同。

接口和抽象类的区别

1、定义上的区别

  • 抽象类可以存在普通成员函数,而接口只能存在public abstract 方法。
  • 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的。
  • 抽象类只能继承⼀个,接口可以实现多个

2、设计目的上的区别

  • 接口的设计目的,是对类的行为进行约束(更准确的说是一种“有”约束,因为接口不能规定类不可以有什么行为),也就是提供一种机制,可以强制要求不同的类具有相同的行为。它只约束了行为的有无,但不对如何实现行为进行限制。

    抽象类是对类本质的抽象,表达的是 is a的关系,比如:BMW is a Car。抽象类包含并实现子类的通用特性,将子类存在差异化的特性进行抽象,交由子类去实现。

  • 而抽象类的设计目的,是代码复用。当不同的类具有某些相同的行为(记为行为集合A),且其中一部分行为的实现方式一致时(A的非真子集,记为B),可以让这些类都派生于一个抽象类。在这个抽象类中实现了B,避免让所有的子类来实现B,这就达到了代码复用的目的。而A减B的部分,留给各个子类自己实现。正是因为A-B在这里没有实现,所以抽象类不允许实例化出来(否则当调用到A-B时,无法执行)。

    接口是对行为的抽象,表达的是like a的关系。比如:Bird like a Aircraft(像飞行器一样可以飞),但其本质上is a Bird。接口的核心是定义行为,即实现类可以做什么,至于实现类主体是谁、是如何实现的,接口并不关心。

  1. 使用场景上的区别
    当你关注一个事物的本质的时候,用抽象类;当你关注一个操作的时候,用接口。

抽象类的功能要远超过接口,但是,定义抽象类的代价高。因为高级语言来说(从实际设计上来说也是)每个类只能继承一个类。在这个类中,你必须继承或编写出其所有子类的所有共性。虽然接口在功能上会弱化许多,但是它只是针对一个动作的描述。而且你可以在一个类中同时实现多个接口。在设计阶段会降低难度

多态

  1. 什么是多态
    指一个类实例的相同方法在不同情形有不同表现形式。多态机制使具有不同内部结构的对象可以共享相同的外部接口。这意味着,虽然针对不同对象的具体操作不同,但通过一个公共的类,它们(那些操作)可以通过相同的方式予以调用。 多态就是同一个接口,使用不同的实例而执行不同操作
    2.多态的实现条件
    • 继承的存在(继承是多态的基础,没有继承就没有多态)
    • 子类重写父类的方法(多态下调用子类重写的方法)
    • 父类引用变量指向子类对象(子类到父类的类型转换)

2、JRE、JDK、JVM的区别

  1. 什么是 JDK?
    JDK 的英文全称是 Java Development Kit。JDK是用于制作程序和Java应用程序的软件开发环境。Java 开发人员可以在 Windows、macOS、Solaris 和 Linux 上使用,是一个跨平台编程语言。JDK 帮助他们编写和运行 Java 程序。可以在同一台计算机上安装多个 JDK 版本。

  2. 什么是 JRE?
    JRE 的英文全称是 Java Runtime Environment。JRE 是一个旨在运行其他软件的软件(有点绕口)。它包含类库、加载器类和 JVM。简单来说,如果你想运行 Java 程序,你需要 JRE。如果您不是程序员,则无需安装 JDK,只需安装 JRE 即可运行 Java 程序。不过,所有 JDK 版本都与 Java Runtime Environment 捆绑在一起,因此无需在 PC 单独下载和安装 JRE。JRE 的完整形式是 Java 运行时环境。

  3. 什么是JVM?
    JVM 的英文全称是Java Virtual Machine。JVM 是一个引擎,它提供运行时环境驱动 Java 代码或应用程序。它将 Java 字节码转换为机器语言。JVM 是 Java 运行环境 (JRE) 的一部分。它不能单独下载和安装。要安装 JVM,您需要安装 JRE。JVM的就是Java虚拟机。

    在许多其他编程语言中,编译器为特定系统生成机器代码。但是,Java 编译器则称为 JVM 虚拟机生成代码。

  4. 主要区别
    JDK是一个软件开发工具包,而JRE是一个允许Java程序运行的软件包,JVM则是一个执行字节码的环境。

    JDK的全称是Java Development Kit,JRE的全称是Java Runtime Environment,而JVM的全称是Java Virtual Machine。

    JDK 是平台相关的,JRE 也是平台相关的,但是 JVM 不是平台相关的。

    JDK 包含开发、调试等工具。JRE 包含类库和其他支持文件,而软件开发工具不包含在 JVM 中。

    JDK 附带安装程序,另一方面,JRE 仅包含执行源代码的环境,而 JVM 捆绑在软件 JDK 和 JRE 中。

3、==和equals方法之前的区别

  • ==:对比的是栈中的值,基本数据类型是变量值、引用类型是堆中内存对象的地址
  • equals:object中默认也是采⽤==⽐较,通常会重写

4、以“HashSet如何检查重复”为例子来说明为什么要有hashCode

当对象加入HashSet时,HashSet会先计算对象的hashcode值来判断对象加入的位置,查看该位置是否有值,如果没有,HashSet会假设对象没有重复出现。但是如果发现有值,这时会调用equals()方法来检查两个对象是否真的相同。如果两者相同,HashSet就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。这样就大大减少了equals的次数,相应就大大提高了执行速度。

  • 如果两个对象相等,则hashcode一定也是相同的
  • 两个对象相等,对两个对象分别调用equals方法都返回true
  • 两个对象有相同的hashcode值,它们也不一定是相等
  • hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)

5、String、StringBuffer和StringBuilder

  1. String是不可变的,如果尝试修改,会产生一个新的字符串对象。改变的只是引用而已。而StringBuffer和StringBuilder对象是可以进行改变的
  2. String和StringBuffer是线程安全的。其中String因为不可变性,所以是安全的。而StringBuffer对象中使用synchronized关键字,所以也是安全的
  3. 所以在使用的时候推荐使用StringBuffer避免出现线程不安全问题,当然如果能保证是单线程调用的话,使用StringBuilder是最好的,因为他没有添加synchronized,效率比较高

6、List和Set的区别

  • List: 有序,按对象进入的顺序保存对象,可重复,允许多个Null元素对象,可以使用lterator取出所有元素,在逐一遍历,还可以使用get(int index)获取指定下标的元素
  • Set:无序,不可重复,最多允许有一个Null元素对象,取元素时只能用lterator接口取得所有元素,在逐一遍历各个元素

7、ArrayList和LinkedList的区别

  1. 首先从底层数据结构上来说:ArrayList基于数组实现的,而LinkedList是基于链表来实现的
  2. 由于底层数据结构不同,所以使用场景也不同。ArrayList更适合于随机查找,LinkedList更适合于删除和添加操作。当然这个并不是绝对的。
  3. 另外ArrayList和LinkedList都实现了List接口,但LinkedList还额外实现了Deque接口,所以LinkedList还可以当作队列还使用(双端队列)

8、hashCode和equals之间的关系

  • 如果两个对象相等,则hashCode⼀定也是相同的
  • 两个对象相等,对两个对象分别调⽤equals⽅法都返回true
  • 两个对象有相同的hashCode值,它们也不⼀定是相等的
  • 重写equals方法,则hashCode方法也需要进行重写,例如在HashMap中会优先进行hashCode判断
  • hashCode是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论论如何都不会相等,即使这两个对象指向相同的数据

9、Jdk1.7到Jdk1.8 HashMap 发生了什么变化?

底层结构不同

  • jdk7中采用数组+链表的结构
  • jdk8中采用数组+链表/红黑树的结构

链表插入元素的方式不同

  • jdk7采用头插法
  • jdk8采用尾插法。因为1.8中插入key和value时需要判断链表元素个数,所以需要遍历链表统计链表元素个数,所以正好就直接使用尾插法

10、深拷贝和浅拷贝

深拷贝和浅拷贝就是指对象的拷贝,一个对象中存在两种类型的属性,一种是基本数据类型,一种是实例对象的引用。

  • 浅拷贝是指:只会拷贝基本数据类型的值,以及实例对象的引用地址,并不会复制一份引用地址所指向的对象,也就是浅拷贝出来的对象,内部的类属性指向的是同一个对象
  • 深拷贝是指:既会拷贝基本数据类型的值,也会针对实例对象的引用地址所指向的对象进行复制,深拷贝出来的对象,内部的属性指向的不是同一个对象

11、java中的异常体系

  • Java中的所有异常都来自顶级父类Throwable。
  • Throwable下有两个子类Exception和Error。
  • Error是程序无法处理的错误,一旦出现这个错误,则程序将被迫停止运行。
  • Exception不会导致程序停止,又分为RunTimeException运行时异常和CheckedException检查异常。
  • RunTimeException常常发生在程序运行过程中,会导致程序当前线程执行失败。如:NullPointerException、ClassCastException、IllegalArgumentException、IndexOutOfBoundsException
  • CheckedException常常发生在程序编译过程中,会导致程序编译不通过。需要使用try catch进行处理
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值