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 程序⽆须重新编译便可在多种不同操作系统的计算机上运⾏


知道JDK8新特性?

  • Lambda表达式:允许把函数作为一个方法的参数(函数作为参数传递进方法中。
  • Stream流:函数式编程,流式计算
  • 默认方法:默认方法就是一个在接口里面有了一个实现的方法。
  • 方法引用:方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。

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

String 为什么是不可变的?

接口与抽象类的区别?

抽象类:如果一个类里有抽象方法,那么这个类只能是抽象类,抽象类里可以没有抽象方法,抽象方法只能申明,不能实现。

接口:在Java中接口就是一个规范,是一种更加抽象化的抽象类。需要被类实现。

两者区别:

  • ⼀个类可以实现多个接⼝,但只能实现⼀个抽象类。接⼝本身也可以继承接口进行扩展。
  • 接⼝中只能定义公共的静态常量,不能有其他变量,⽅法默认是 public ,所有⽅法在接⼝中不能有实现(Java 8 开始接口⽅法可以有默认实现);抽象类中可以定义普通变量,抽象方法需要被实现,不能是静态的,也不能是私有的,可以使用public 、 protected 和 default 这些修饰符修饰。
  • 抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
  • 从设计层⾯来说,抽象是对类的抽象,是⼀种模板设计(is-a),⽽接⼝是对⾏为的抽象,是⼀种⾏为的规范(like-a)。

相同点:

抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。


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

  1. 从语法形式上看:成员变量是属于类的,⽽局部变量是在⽅法中定义的变量或是⽅法的参数;成员变量可以被 public , private , static 等修饰符所修饰,⽽局部变量不能被访问控制修饰符及 static 所修饰;但是,成员变量和局部变量都能被 final 所修饰。
  2. 从变量在内存中的存储⽅式来看:如果成员变量是使⽤ static 修饰的,那么这个成员变量是属于类的,如果没有使⽤ static 修饰,这个成员变量是属于实例的。对象存于堆内存,如果局部变量类型为基本数据类型,那么存储在栈内存,如果为引⽤数据类型,那存放的是指向堆内存对象的引⽤或者是指向常量池中的地址。
  3. 从变量在内存中的⽣存时间上看:成员变量是对象的⼀部分,它随着对象的创建⽽存在,⽽局部变量随着⽅法的调⽤⽽⾃动消失。
  4. 成员变量如果没有被赋初值:则会⾃动以类型的默认值⽽赋值(⼀种情况例外:被 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。


final、finally、finalize 有什么区别?

final可以修饰类,变量,方法,修饰的类不能被继承,修饰的变量不能重新赋值,修饰的方法不能被重写

finally一般作用在try-catch代码块中,finally代码块内语句无论是否发生异常,都会在执行finally,常用于一些流的关闭。( 可以在try 或 finally 块中使用 System.exit(int) 退出程序,使finally不执行)

finalize:Object类的一个方法,Java 中允许使用 finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。


java 中都有哪些引用类型?

强、软、弱、虚

  • 强引用(StrongReference): 最传统的“引用”的定义,是指在程序代码之中普遍存在的引用赋值,即类似“object obj=new Object()”这种引用关系。无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象。
  • 软引用(SoftReference): 在系统将要发生内存溢出之前,将会把这些对象列入回收范围之中进行第二次回收。如果这次回收后还没有足够的内存,才会抛出内存流出异常。
  • 弱引用(WeakReference): 被弱引用关联的对象只能生存到下一次垃圾收集之前。当垃圾收集器工作时,无论内存空间是否足够,都会回收掉被弱引用关联的对象。
  • 虚引用(PhantomReference): 一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来获得一个对

实例化对象有哪几种方式?

  • new一个对象:最常见的方式User user = new User();
  • 通过反射方式:调用构造器来初始化对象
    Class的newInstance()方法:User user = User.class.newInstance();,因为只能调用空参构造器,权限必须是public。
    Constructor的newInstance(Xxx)方法:Constructor<User> constructor = User.class.getInstance(); User user = constructor.newInstance();可以调用空参的,或者带参的构造器
  • 使用对象的clone()方法返回对象:User user2 = <User>user.clone();不调用任何的构造器,直接复制已有的数据,来初始化新建的对象。要求当前的类需要实现Cloneable接口中的clone接口
  • 使用反序列化方式:ObjectInputStream in = new ObjectInputStream (new FileInputStream(filename)); User user = (User)obj.readObject();,序列化一般用于Socket的网络传输
  • 通过第三方库创建对象,比如Objenesis

什么是反射?

JAVA反射机制是在运行状态中进行自我观察的能力,通过class、constructor、field、method等方法获取一个类中的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性; 这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

反射机制优缺点:

  • 优点: 运行期类型的判断,动态加载类,提高代码灵活度。
  • 缺点: 性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的java代码要慢很多。

Java中的异常体系


在 Java 中,所有的异常都有⼀个共同的祖先 java.lang 包中的 Throwable 类。 Throwable 类有两个重要的⼦类 Exception (异常)和 Error (错误)。

Error 和 Exception 区别是什么?

Exception 类的错误是可以在应用程序中进行捕获并处理的,通常遇到这种错误,应对其进行处理,使应用程序可以继续正常运行。Exception ⼜可以分为受检查异常(必须处理) 和 不受检查异常(可以不处理)。

Error 是⽆法处理的(只能尽量避免)。这种类型的错误通常是虚拟机相关的错误,例如:Java 虚拟机运⾏错误( Virtual MachineError )、虚拟机内存不够错误( OutOfMemoryError )等 。这些异常发⽣时,Java虚拟机(JVM)⼀般会选择线程终⽌,仅靠应用程序本身无法恢复;

受检查异常与不授检查异常

受检查异常:
Java 代码在编译过程中,如果受检查异常没有被 catch / throw 处理的话,会导致程序编译不通过。⽐如下⾯这段 IO 操作的代码。除了 RuntimeException 及其⼦类以外,其他的 Exception 类及其⼦类都属于检查异常 。常⻅的受
检查异常有: IO 相关的异常、 ClassNotFoundException 、 SQLException (数据库相关异常)等。

不受检查异常:
Java 代码在编译过程中 ,我们即使不处理不受检查异常也可以正常通过编译,不受检查异常往往发生在程序运行的过程中,会导致当前线程执行失败。

RuntimeException 及其⼦类都统称为⾮受检查异常,例如:

  • NullPointExecrption (空指针异常)
  • NumberFormatException (字符串转换为数字)
  • ArrayIndexOutOfBoundsException (数组越界)
  • ClassCastException (类型转换错误)
  • ArithmeticException (算术错误)等

我们自定义的异常往往也都继承RuntimeException。

throw 和 throws 的区别是什么?

Java 中的异常处理除了包括捕获异常和处理异常之外,还包括声明异常和拋出异常,可以通过 throws 关键字在方法上声明该方法要拋出的异常(可能出现异常,可以抛出多个异常),或者在方法内部通过 throw 拋出异常对象(一定会抛出一种异常)。


Java 中 IO 流分为几种?

  • 按照流的流向分,可以分为输⼊流和输出流;
  • 按照操作单元划分,可以划分为字节流和字符流;
  • 按照流的⻆⾊划分为节点流和处理流。

Java IO 流共涉及 40 多个类,这些类看上去很杂乱,但实际上很有规则,⽽且彼此之间存在⾮常紧密的联系,Java IO 流的 40 多个类都是从如下 4 个抽象类基类中派⽣出来的。

  • InputStream/Reader: 所有的输⼊流的基类,前者是字节输⼊流,后者是字符输⼊流。
  • OutputStream/Writer:所有输出流的基类,前者是字节输出流,后者是字符输出流。

按操作⽅式分类结构图:

既然有了字节流,为什么还要有字符流?
字符流是由 Java 虚拟机将字节转换得到的,问题就出在这个过程还算是⾮常耗时,并且,如果我们不知道编码类型就很容易出现乱码问题。所以, I/O 流就干脆提供了⼀个直接操作字符的接口,⽅便我们平时对字符进⾏流操作。如果⾳频⽂件、图⽚等媒体⽂件⽤字节流比较好,如果涉及到字符的话使用字符流比较好


什么是序列化?什么是反序列化?

如果我们需要持久化 Java 对象比如将 Java 对象保存在文件中,或者在网络传输 Java 对象,这些场景都需要用到序列化。

  • 序列化: 将数据结构或对象转换成二进制字节流的过程
  • 反序列化:将在序列化过程中所生成的二进制字节流的过程转换成数据结构或者对象的过程

BIO,NIO,AIO 有什么区别?

BIO:同步阻塞,数据的读取写⼊必须阻塞在⼀个线程内等待其完成,它的特点是模式简单使用方便,也不⽤过多考虑系统的过载、限流等问题。对于连接数不是特别高的情况下,这种模型还是比较不错的。但是该模型并发处理能力较低,每当有一个客户端向服务器发起请求时,服务器都要启动一个线程,当⾯对⼗万甚⾄百万级连接的时候就无能为力了。

NIO:同步非阻塞,是B IO 的升级,它⽀持⾯向缓冲的,基于 Channel(通道)的I/O操作,实现了多路复用。NIO⽀持阻塞和⾮阻塞两种模式。阻塞模式使⽤就像传统中的⽀持⼀样,比较简单,但是性能和可靠性都不好;⾮阻塞模式正好与之相反。对于低负载、低并发的应⽤程序,可以使⽤同步阻塞 I/O 来提升开发速率和更好的维护性;对于⾼负载、⾼并发的(⽹络)应⽤,应使⽤ NIO 的⾮阻塞模式来开发

AIO:异步非阻塞,是 NIO 的升级,也叫 NIO2,异步 IO 是基于事件和回调机制实现的,也就是应⽤操作之后会直接返回,不会堵塞在那⾥,当后台处理完成,操作系统会通知相应的线程进⾏后续的操作。客户端发送的请求先交给操作系统处理,OS 处理后再通知线程。


深拷贝 vs 浅拷贝

  • 浅拷贝:对基本数据类型直接复制数据的值,对与引⽤数据类型进⾏则复制对象的引用地址,新旧对象指向同一个内存地址,修改其中一个对象的值,另一个对象的值随之改变。
  • 深拷贝:对基本数据类型直接复制数据的值,对引⽤数据类型,开辟新的内存空间,在新的内存空间里复制一个一模一样的对象,新老对象不共享内存,修改其中一个对象的值,不会影响另一个对象。

深拷贝相比于浅拷贝速度较慢并且花销较大。


参考文章:
https://gitee.com/SnailClimb/JavaGuide
https://www.bilibili.com/video/BV1Eb4y1R7zd
https://csp1999.blog.csdn.net/article/details/117168128

以上是关于Java面试题整理《基础篇》的主要内容,如果未能解决你的问题,请参考以下文章

java面试题及答案整理(基础篇),持续更新中。。。

Java面试题整理《基础篇》

10道不得不会的Java基础面试题

#面试# 阿里大佬Java面试通关要点汇总集基础篇核心篇框架篇

Java面试题整理《集合篇》

Java面试题整理《集合篇》