Java基础面试题整理
Posted 龙源lll
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java基础面试题整理相关的知识,希望对你有一定的参考价值。
什么是面向对象?
面向对象: 生活中的一切事物都可以被称之为对象,生活中随处可见的事物就是一个对象,我们可以将这些事物的状态特征(属性)以及行为特征(方法)提取并出来,并以固定的形式表示。
面向对象VS面向过程:
面向过程更加注重每一个步骤以及执行顺序,而面向对象则会考虑事情有哪些参与者,能够做什么。
面向过程会将任务拆分为一系列步骤:(以洗衣服为例)
- 1、打开洗衣机
- 2、放衣服
- 3、放洗衣粉
- 4、清洗
- 5、烘干
面向对象则会拆分人和洗衣机两个对象:
- 人:打开洗衣机、放衣服、放洗衣粉
- 洗衣机:清洗、烘干
就结果而言:⾯向过程性能⽐⾯向对象⾼。 因为类调⽤时需要实例化,开销比较⼤,比较消耗资源,所以当性能是最重要的考量因素的时候,⼀般采⽤⾯向过程开发。但是,⾯向过程没有⾯向对象易维护、易复⽤、易扩展。因为⾯向对象有封装、继承、多态性的特性,所以可以设计出低耦合的系统,使系统更加灵活、更加易于维护。但是,⾯向对象性能⽐⾯向过程低。
封装、继承与多态
封装:
封装指隐藏对象的状态信息(属性),不允许外部对象直接访问对象的内部信息。但是可以提供一些可以被外界访问的方法来操作属性。通过封装,明确标识出允许外部使用的所有成员函数项,而其内部细节则对外部调用透明,外部调用者无需去关心其具体内部实现。
继承:
- 当多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承单独的那个类即可。多个类可以称为子类,单独这个类称为父类或者超类。
- 继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的属性或方法(对父类进行扩展),也可以拥有父类的属性和方法,并且通过自己的方法再次实现父类的方法(重写)。通过使用继承,可以快速地创建新的类,可以提高代码的重用,程序的可维护性,节省大量创建新类的时间 ,提高我们的开发效率。
- Java只支持单继承,不支持多继承。一个类只能有一个父类,不可以有多个父类。Java支持多层继承(继承体系)。Java继承了父类非私有的成员变量和成员方法,但是请注意:子类是无法继承父类的构造方法的。
多态:
- 多态:一种事物的多种形态(多态的前提:继承、重写、向上转型),比如说所属类不同的对象,在调用其共同父类的方法时,会产生不同的执行结果。
- 通过多态可以提高代码的可重用性,降低模块之间的耦合度。(在Java中方法的重载和重写是实现多态的2种方式。)
重载和重写的区别
- 重载:同一个类中定义了多个方法名相同而参数不同的方法,这些⽅法能够根据输⼊数据的不同,做出不同的处理。
- 重写:子类从父类继承的某个实例方法无法满足子类的功能需要时,需要在子类中对该实例方法进行重新实现,这样的过程称为重写,也叫做覆写、覆盖。
重载在同一个类中,方法名相同,参数不同(参数的个数、顺序、类型不同),⽅法返回值和访问修饰符可以不同。
方法重写的前提:继承,要求方法名、参数列表、返回值类型必须相同,子类的修饰符大于等于父类,抛出的异常范围⼩于等于⽗类。
重写的返回值类型如果⽅法的返回类型是void和基本数据类型,则返回值重写时不可修改。但是如果⽅法的返回值是引⽤类型,重写时是可以返回该引⽤类型的⼦类的。
构造⽅法⽆法被重写,但是可以重载,一个类中可以出现多个构造函数
Java 语言有哪些特点?
1.面向对象
面向对象(OOP)就是Java语言的基础,也是Java语言的重要特性。面向对象的概念:生活中的一切事物都可以被称之为对象,生活中随处可见的事物就是一个对象,我们可以将这些事物的状态特征(属性)以及行为特征(方法)提取并出来,并以固定的形式表示。
2.简单好用
Java语言是由C和C++演变而来的,它省略了C语言中所有的难以理解、容易混淆的特性(比如指针),变得更加严谨、简洁、易使用。
3.健壮性
Java的安全检查机制,将许多程序中的错误扼杀在摇蓝之中。 另外,在Java语言中还具备了许多保证程序稳定、健壮的特性(强类型机制、异常处理、垃圾的自动收集等),有效地减少了错误,使得Java应用程序更加健壮。
4.安全性
Java通常被用在网络环境中,为此,Java提供了一个安全机制以防恶意代码的攻击,从而可以提高系统的安全性。
5.平台无关性
Java平台无关性由Java 虚拟机实现,Java软件可以不受计算机硬件和操作系统的约束而在任意计算机环境下正常运行。
6.支持多线程
在C++ 语言没有内置的多线程机制,因此必须调用操作系统的多线程功能来进行多线程程序设计,而 Java 语言却提供了多线程支持。多线程机制使应用程序在同一时间并行执行多项任务,该机制使得程序能够具有更好的交互性、实时性。
7.分布式(支持网络编程)
Java语言具有强大的、易于使用的网络能力,非常适合开发分布式计算的程序。java中提供了网络应用编程接口(java.net),使得我们可以通过URL、Socket等远程访问对象。
8.编译与解释共存
Java 是编译与解释共存的语言
JDK 、JRE和JVM
JDK:Java Development Kit 的简称,java 开发工具包,是Sun Microsystems针对Java开发员的产品,提供了 java 的开发环境和运行环境,JDK中包含JRE,在JDK的安装目录中可以找到一个jre的目录。
JRE:Java Runtime Environment 的简称,是运行基于Java语言编写的程序所不可缺少的运行环境。通过它,Java的开发者才得以将自己开发的程序发布到用户手中,让用户使用。在jre文件中有bin和lib文件夹,我们可以认为bin文件夹对应的就是JVM,而lib则对应JVM工作所需要的类库。
JVM:java virtual machine简称,是我们常说的java虚拟机,它是整个java实现跨平台的最核心的部分,所有的java程序会首先被编译为 .class 的类文件(字节码文件),这种类文件可以在虚拟机上执行。JVM屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。
需要运行 java 程序,只需安装 JRE 就可以了
需要编写 java 程序,需要安装 JDK
字节码文件:
字节码文件是一种可以JVM 上运行的文件(文件扩展名为 .class ),它不⾯向任何特定的处理器,只⾯向虚拟机。Java 语⾔通过字节码的⽅式,在⼀定程度上解决了传统解释型语⾔执⾏效率低的问题,同时⼜保留了解释型语⾔可移植的特点。所以 Java 程序运⾏时比较⾼效,⽽且,由于字节码并不针对⼀种特定的机器,因此,Java 程序⽆须重新编译便可在多种不同操作系统的计算机上运⾏
String、StringBuffer 和 StringBuilder 的区别?
可变性:
String 类中使⽤ final 关键字修饰字符数组来保存字符串,所以 String 对象是不可变的,每次操作都会产生新对象。⽽ StringBuilder 与 StringBuffer 都继承⾃ AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使⽤字符数组保存字符串 char[]value 但是没有⽤ final 关键字修饰,所以这两种对象都是可变的。
线程安全:
String 中的对象是不可变的,也就可以理解为常量,线程安全。StringBuilder 与 StringBuffer 的构造⽅法都是调⽤⽗类构造⽅法也就是 AbstractStringBuilder(其中定义了一些字符串的基本操作,本身是线程安全的) 实现的。StringBuffer 对⽅法加了同步锁或者对调⽤的⽅法加了同步锁(synchronized),所以是线程安全的。StringBuilder 并没有对⽅法进⾏加同步锁,所以是⾮线程安全的。
性能:
每次对 String 类型进⾏改变的时候,都会⽣成⼀个新的 String 对象,然后将指针指向新的 String对象。StringBuffer 每次都会对 StringBuffer 对象本身进⾏操作,⽽不是⽣成新的对象并改变对象引⽤。相同情况下使⽤ StringBuilder 相⽐使⽤ StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的⻛险。
对于三者使⽤的总结:
1. 操作少量的数据: 适⽤ String
2. 单线程操作字符串缓冲区下操作⼤量数据: 适⽤ StringBuilder
3. 多线程操作字符串缓冲区下操作⼤量数据: 适⽤ StringBuffer
接口与抽象类的区别?
抽象类:如果一个类里有抽象方法,那么这个类只能是抽象类,抽象类里可以没有抽象方法,抽象方法只能申明,不能实现。
接口:在Java中接口就是一个规范,是一种更加抽象化的抽象类。需要被类实现。
两者区别:
- ⼀个类可以实现多个接⼝,但只能实现⼀个抽象类。接⼝本身也可以继承接口进行扩展。
- 接⼝中只能定义公共的静态常量,不能有其他变量,⽅法默认是 public ,所有⽅法在接⼝中不能有实现(Java 8 开始接口⽅法可以有默认实现);抽象类中可以定义普通变量,抽象方法需要被实现,不能是静态的,也不能是私有的,可以使用public 、 protected 和 default 这些修饰符修饰。
- 抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
- 从设计层⾯来说,抽象是对类的抽象,是⼀种模板设计(is-a),⽽接⼝是对⾏为的抽象,是⼀种⾏为的规范(like-a)。
相同点:
抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
成员变量与局部变量的区别有哪些?
- 从语法形式上看:成员变量是属于类的,⽽局部变量是在⽅法中定义的变量或是⽅法的参数;成员变量可以被 public , private , static 等修饰符所修饰,⽽局部变量不能被访问控制修饰符及 static 所修饰;但是,成员变量和局部变量都能被 final 所修饰。
- 从变量在内存中的存储⽅式来看:如果成员变量是使⽤ static 修饰的,那么这个成员变量是属于类的,如果没有使⽤ static 修饰,这个成员变量是属于实例的。对象存于堆内存,如果局部变量类型为基本数据类型,那么存储在栈内存,如果为引⽤数据类型,那存放的是指向堆内存对象的引⽤或者是指向常量池中的地址。
- 从变量在内存中的⽣存时间上看:成员变量是对象的⼀部分,它随着对象的创建⽽存在,⽽局部变量随着⽅法的调⽤⽽⾃动消失。
- 成员变量如果没有被赋初值:则会⾃动以类型的默认值⽽赋值(⼀种情况例外:被 final 修饰的成员变量也必须显式地赋值),⽽局部变量则不会⾃动赋值。
==与equals
==
: 比较两个对象是否相等,本质是比较两者的值是否相等,对于基本数据类型来说,==
比较的是值。对于引用数据类型来说,==
比较的是对象的内存地址(因为引用类型变量存的值是对象的地址)。- equals() : 它的作⽤不能用于判断基本数据类型的变量,只能用来判断两个对象是否相等。但它⼀般有两种使⽤情况:
- 情况 1:类没有覆盖 equals() ⽅法。则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象。
- 情况 2:类覆盖了 equals() ⽅法。⼀般,我们都覆盖 equals() ⽅法来比较两个对象的内容是否相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。
以String为例:
String 中的 equals ⽅法是被重写过的,因为 object 的 equals ⽅法是比较的对象的内存地址,⽽ String 的 equals ⽅法比较的是对象的值。当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引⽤。如果没有就在常量池中重新创建⼀个 String 对象。
例题:
hashCode 与 equals
hashCode()介绍:
- hashCode() 的作⽤是获取哈希码,也称为散列码;它实际上是返回⼀个 int 整数。这个哈希码的作⽤是确定该对象在哈希表中的索引位置。 hashCode() 定义在 JDK 的 Object.java中,这就意味着 Java 中的任何类都包含有 hashCode() 函数。Object 的 hashcode ⽅法是本地⽅法,也就是⽤ c 语⾔或 c++ 实现的,该⽅法通常⽤来将对象的 内存地址 转换为整数之后返回。
- 散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利⽤到了散列码!(可以快速找到所需要的对象)
为什么要有 hashCode?
- 以“ HashSet 如何检查重复”为例⼦来说明为什么要有 hashCode?
当对象加⼊ HashSet 时, HashSet 会先计算对象的 hashcode 值来判断对象加⼊的位置,同时也会与其他已经加⼊的对象的 hashcode 值作比较,如果没有相符的 hashcode, HashSet会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调⽤ equals() ⽅法来检查 hashcode 相等的对象是否真的相同。如果两者相同, HashSet 就不会让其加⼊操作成功。如果不同的话,就会重新散列到其他位置。这样我们就⼤⼤减少了 equals 的次数,相应就⼤⼤提⾼了执⾏速度。
为什么重写 equals 时必须重写 hashCode ⽅法?
- 如果两个对象相等,则 hashcode ⼀定也是相同的。
- 两个对象相等,对两个对象分别调⽤ equals⽅法都返回 true。
- 但是,两个对象有相同的 hashcode 值,它们也不⼀定是相等的 。
- 因此,equals ⽅法被覆盖过,则 hashCode ⽅法也必须被覆盖。
- hashCode() 的默认⾏为是对堆上的对象产⽣独特值。如果没有重写 hashCode() ,则该class 的两个对象⽆论如何都不会相等(即使这两个对象指向相同的数据)
为什么两个对象有相同的 hashcode 值,它们也不⼀定是相等的?
- 因为 hashCode() 所使⽤的杂凑算法也许刚好会让多个对象传回相同的杂凑值。越糟糕的杂凑算法越容易碰撞,但这也与数据值域分布的特性有关(所谓碰撞也就是指的是不同的对象得到相同的 hashCode) 。
- 我们刚刚也提到了 HashSet ,如果 HashSet 在对⽐的时候,同样的 hashcode 有多个对象,它会使⽤ equals() 来判断是否真的相同。也就是说 hashcode 只是⽤来缩⼩查找成本。
final关键字
final 关键字,意思是最终的、不可修改的,最见不得变化 ,用来修饰类、方法和变量,具有以下特点:
- 修饰类:类不能继承,final 类中的所有成员方法都会被隐式的指定为 final 方法;
- 修饰符变量:该变量为常量,,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能让其指向另一个对象。
- 修饰符方法:方法不能重写
说明:使用 final 方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的 Java 实现版本中,会将 final 方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升(现在的 Java 版本已经不需要使用 final 方法进行这些优化了)。类中所有的 private 方法都隐式地指定为 final。
参考文章:
https://gitee.com/SnailClimb/JavaGuide
https://www.bilibili.com/video/BV1Eb4y1R7zd
https://csp1999.blog.csdn.net/article/details/117168128
以上是关于Java基础面试题整理的主要内容,如果未能解决你的问题,请参考以下文章