前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Java面试总结】Java基础(下篇)

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

原创
作者头像
Rochester
修改2020-09-07 17:44:48
5790
修改2020-09-07 17:44:48
举报
文章被收录于专栏:牛人NR牛人NR

26. == 与 equals(重要)

== :它的作用 是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象(基本数据类型 == 比较的是值,引用数据类型 == 比较的是 内存地址)

equals() :它的作用也是判断两个对象是否相等。但它一般有两种使用情况:

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

说明:

  • String 中的 equals 方法是被重写过的,因为Object 的 equals 方法是比较的对象的内存地址,而String 的equals 方法比较的是对象的值;
  • 当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有,就把它赋给当前引用。如果没有,就在常量池中重新创建一个 String 对象。

27. hashCode 与 equals(重要)

“重写 equals 时 必须重写 hashCode 方法”

hashCode() 介绍:

hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK 的 Object.java 中,这就意味着Java 中任何类都包含hashCode() 函数。

散列表存储的是键值对(key-value),它的特点是:根据”键“快速的检索出对应的”值“,这其中就用到了散列码。(可以快速找到所需要的对象)

为什么要有 hashCode

我们先以“HashSet 如何检查重复”为例子来说明为什么要有 hashCode:当你把对象加入 HashSet时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与该位置其他已经加入的对象的 hashcode 值作比较,如果没有相符的 hashcode,HashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。(摘自我的 Java 启蒙书《Head first java》第二版)。这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。

通过我们可以看出:hashCode()的作用就是获取哈希码,也称为散列码;它实际上是返回一个 int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置hashCode()在散列表中才有用,在其它情况下没用。在散列表中 hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。

hashCode()与 equals()的相关规定

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

更多阅读:Java hashCode() 和 equals()的若干问题解答

28. 为什么Java中只有值传递?

在程序设计语言中有关将参数传递给方法(或函数)的一些专业术语。按值调用(call by value)表示方法接收的是调用者提供的值,而按引用调用(call by reference)表示方法接收的是调用者提供的变量地址。一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值。 它用来描述各种程序设计语言(不只是 Java)中方法参数传递方式。

Java 程序设计语言采用按值调用。也就是说,方法得到的是所有参数的一个拷贝,也就是说,方法不能修改传递给它的任何参数变量的内容;

下面通过3个例子说明:

例1:

代码语言:javascript
复制
 /**
  * @author silentCow
  * @Date 2020/8/30 9:01
  * 值传递
  */
 public class ZCD {
     public static void main(String[] args) {
         int num1 = 100;
         int num2 = 200;
 ​
         swap(num1,num2);
 ​
         System.out.println("num1=" + num1);
         System.out.println("num2=" + num2);
     }
 ​
     public static void swap(int a,int b) {
         int temp = a;
         a = b;
         b = temp;
 ​
         System.out.println("a=" + a);
         System.out.println("b=" + b);
     }
 }

输出结果:

代码语言:javascript
复制
 a=200
 b=100
 num1=100
 num2=200

解析:

image-20200830091836424
image-20200830091836424

在 swap 方法中,a、b的值进行交换,并不会影响到num1、num2,因为,a、b中的值,只是从num1、num2中复制过来的,也就是说,a、b 相当于 num1、num2 的副本,副本的内容无论怎么修改,都不会影响到原件本身。

通过上面的例子,我们知道了一个方法不能修改一个基本数据类型的参数,而对象引用作为参数就不一样,请看例2。

例2:

代码语言:javascript
复制
 /**
  * @author silentCow
  * @Date 2020/8/30 9:23
  */
 public class ZCD2 {
     public static void main(String[] args) {
         int[] array = {1,2,3,4,5};
         System.out.println(array[0]);
         change(array);
         System.out.println(array[0]);
     }
 ​
     public static void change(int[] arr) {
         // 将数组的第一个元素变为0
         arr[0] = 0;
     }
 }

输出结果:

代码语言:javascript
复制
 1
 0

解析:

image-20200830093607698
image-20200830093607698

arr 被初始化 array 的拷贝也就是一个对象的引用,也就是说 arr 和 array 指向的是同一个数组对象。 因此,外部对引用对象的改变会反映到所对应的对象上。

通过 example2 我们已经看到,实现一个改变对象参数状态的方法并不是一件难事。理由很简单,方法得到的是对象引用的拷贝,对象引用及其他的拷贝同时引用同一个对象。

很多程序设计语言(特别是,C++和 Pascal)提供了两种参数传递的方式:值调用和引用调用。有些程序员认为 Java 程序设计语言对对象采用的是引用调用,实际上,这种理解是不对的。由于这种误解具有一定的普遍性,所以下面给出一个反例来详细地阐述一下这个问题。

例3

代码语言:javascript
复制
 /**
  * @author silentCow
  * @Date 2020/8/30 9:42
  */
 public class ZCD3 {
     public static void main(String[] args) {
         Student s1 = new Student("小王");
         Student s2 = new Student("小红");
 ​
         swap(s1, s2);
 ​
         System.out.println("s1:" + s1.getName());
         System.out.println("s2:" + s2.getName());
     }
 ​
     public static void swap(Student a, Student b) {
         Student temp = a;
         a = b;
         b = temp;
 ​
         System.out.println("a:" + a.getName());
         System.out.println("b:" + b.getName());
     }
 }

输出结果:

代码语言:javascript
复制
 a:小红
 b:小王
 s1:小王
 s2:小红

解析:

交换之前:

image-20200830095556786
image-20200830095556786

交换之后:

image-20200830095556785
image-20200830095556785

通过上面两张图可以很清晰的看出: 方法并没有改变存储在变量 s1 和 s2 中的对象引用。swap 方法的参数 a和 b 被初始化为两个对象引用的拷贝,这个方法交换的是这两个拷贝

总结

Java 程序设计语言对对象采用的不是引用调用。实际上,对象引用是按值调用的。

Java中方法参数的使用情况:

  • 一个方法不能修改一个基本数据类型的参数(即数值型或布尔值)
  • 一个方法可以改变一个对象参数的状态
  • 一个方法不能让对象参数引用一个新的对象

29. 简述线程、程序、进程的基本概念以及它们之间的关系

线程:与进程相似,但线程是 一个比进程更小的执行单位。一个进程在执行的过程中可以产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间做切换工作的时候,负担要比进程小的多。也正因如此,线程被称之为轻量级进程。

程序:是含有指令和数据的文件,被存储在磁盘或其它的数据存储设备中,也就是说程序是静态的代码。

进程:是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建、运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接一个指令的执行着,同时,每个进程还占用某些系统资源,如CPU、内存空间、文件、输入输出设备的使用权等。换句话说,当程序 在执行中,将会被操作系统载入内存中。

线程是进程划分成的更小的运行单位。线程和进程最大的不同在于基本上各进程是相互独立的,而各线程不一定,因为同一进程中的线程极有可能相互影响。从另一个角度来看,进程属于操作系统的范畴,主要是同一时间段内,可以执行一个以上的程序,而线程则是在同一程序内几乎同时执行一个以上的程序段。

30. 线程有哪些基本状态?

Java 线程在运行的生命周期中的指定时刻只可能处于下面 6 种不同状态的其中一个状态(图源《Java并发编程艺术》4.1.4 节)。

image-20200830103352629
image-20200830103352629

线程在生命周期中并不是固定处于某一个状态而是随着代码的执行在不同状态之间切换。Java 线程状态变迁如下图所示(图源《Java 并发编程艺术》4.1.4 节):

image-20200830095827042
image-20200830095827042

由上图可以看出:

线程创建之后它将处于NEW(新建)状态,调用start()方法后开始运行,线程这时候处于READY(可运行)状态。可运行状态的线程获得了 cpu 时间片(timeslice)后就处于RUNNING(运行)状态

操作系统隐藏 Java 虚拟机(JVM)中的 READY 和 RUNNING 状态,它只能看到 RUNNABLE 状态,所以 Java 系统一般将这两个状态统称为RUNNABLE(运行中)状态。

当线程执行wait()方法之后,线程进入WAITING(等待)状态。进入等待状态的线程需要依靠其他线程的通知才能够返回到运行状态,而TIME_WAITING(超时等待)状态相当于在等待状态的基础上增加了超时限制,比如通过sleep(long millis)方法或wait(long millis)方法可以将 Java线程置于 TIMED WAITING 状态。当超时时间到达后 Java 线程将会返回到 RUNNABLE 状态。当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到BLOCKED(阻塞)状态。线程在执行Runnable 的run()方法之后将会进入到TERMINATED(终止)状态。

31. 关于 final 关键字的总结:

final 关键字主要用在三个地方:变量、方法、类

  1. 对于一个 final 变量,如果是基本数据类型的变量,则其一旦被初始化之后便不能再被修改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
  2. 当用 final 修饰一个类时,表名这个类不能被继承。final 类中所有的成员方法都会被隐式地指定为 final 方法。
  3. 使用 final 方法的原因有两点:第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早起的 Java 实现版本中,会将 final 方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升(现在的 Java版本已经不需要使用 final 方法进行这些优化了)。类中所有的 private 方法都会隐式地指定为 final。

32. Java 中的异常处理

Java异常类层次结构图

image-20200830103320305
image-20200830103320305

在 Java 中,所有的异常都有一个共同的祖先 java.lang 包含的 Throwable 类。

Throwable:有两个重要的子类:Exception(异常)和 Error(错误),二者都是 Java 异常处理的重要子类,各自都包含大量子类。

Error(错误)是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。例如,Java 虚拟机运行错误(Virtual MachineError),当 JVM 不再有继续执行操作所需的内存资源时,将出现OutOfMemoryError。这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。

这些错误表示故障发生于虚拟机自身、或者发生在虚拟机试图执行应用时,如 Java 虚拟机运行错误(Virtual MachineError)、类定义错误(NoClassDefFoundError)等。这些错误是不可查的,因为它们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况。对于设计合理的应用程序来说,即使确实发生了错误,本质上也不应该试图去处理它所引起的异常状况。在 Java中,错误通过 Error 的子类描述。

Exception(异常):是程序本身可以处理的异常。Exception 类有一个重要的子类 RuntimeException。RuntimeException 异常由 Java虚拟机抛出。NullPointerException(要访问的变量没有引用任何对象时,抛出该异常)、ArithmeticException(算术运算异常,一个整数除以 0时,抛出该异常)和ArrayIndexOutOfBoundsException(下标越界异常)。

注意:异常和错误的区别:异常能被程序本身处理,错误是无法处理。

Throwable 类常用方法

  • public string getMessage():返回异常发生时的简要描述
  • public string toString():返回异常发生时的详细信息
  • public string getLocalizedMessage():返回异常对象的本地化信息。使用 Throwable 的子类覆盖这个方法,可以生成本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与getMessage()返回的结果相同
  • public void printStackTrace():在控制台上打印 Throwable 对象封装的异常信息

异常处理总结

  • try 块:用于捕获异常。其后可接零个或多个 catch 块,如果没有 catch 块,则必须跟一个finally 块。
  • catch 块:用于处理 try 捕获到的异常。
  • finally 块:无论是否捕获或处理异常,finally 块里的语句都会被执行。当在 try 块或catch 块中遇到 return语句时,finally 语句块将在方法返回之前被执行。

在以下 4 种特殊情况下,finally 块不会被执行:

  1. 在 finally 语句块第一行发生了异常。因为在其他行,finally 块还是会得到执行
  2. 在前面的代码中用了 System.exit(int)已退出程序。 exit 是带参函数;若该语句在异常语句之后,finally 会执行
  3. 程序所在的线程死亡。
  4. 关闭 CPU。

注意:当 try 语句和 finally 语句中都有 return 语句时,在方法返回之前,finally 语句的内容将被执行,并且 finally 语句的返回值将会覆盖原始的返回值。如下:

代码语言:javascript
复制
  public static int f(intvalue) {
         try {
             returnvalue * value;
         } finally {
             if (valueWX2) {
                 return0;
             }
         }
     }

如果调用f(2),返回值将是 0,因为 finally 语句的返回值覆盖了 try 语句块的返回值。

33. Java序列化如果有些字段不想进行序列化,怎么办?

对于不想进行序列化的变量,使用 transient 关键字修饰。

transient 关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被 transient 修饰的变量值不会被持久化和恢复。transient 只能修饰变量,不能修饰类和方法。

34. 获取用键盘输入常用的两种方法

方法1:通过 Scanner

代码语言:javascript
复制
 Scanner input = new Scanner(System.in);
 String s = input.nextLine();
 input.close();

方法2:通过 BufferedReader

代码语言:javascript
复制
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String s=input.readLine();

35. Java 中 IO 流

Java 中的IO 流分为几种?

  • 按照流的流向分,可以分为输入流和输出流;
  • 按照操作单元划分,可以划分为字节流和字符流;
  • 按照流的角色划分,可以分为字节流和处理流。

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

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

按操作方式分类结构图:

image-20200830181249235
image-20200830181249235

按操作对象分类结构图:

image-20200830164503352
image-20200830164503352

既然有了字节流,为什么还要有字符流?

问题:不管是文件读写还是网络发送接收,信息的最小存储单元都是字节,那为什么 I/O 流操作要分为字节流操作和字符流操作呢?

答:字符流是由 Java 虚拟机将字节转换得到的,问题就出在这个过程还算是非常耗时的,并且,如果我们不知道编码类型就很容易出现乱码问题。所以,I/O 流就干脆提供了一个直接操作字符的接口,方便我们平时对字符流进行操作。如果音频文件、图片等媒体文件用字节流比较好,如果涉及到字符的话则使用字符流比较好。

BIO,NIO,AIO 有什么区别?

  • BIO (Blocking I/O):同步阻塞 I/O 模式,数据的读取写入必须阻塞在一个线程内等待其完成。在活动连接数不是特别高(小于单机 1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的 I/O 并且编程模型简单,也不用过多考虑系统的过载、限流等问题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。但是,当面对十万甚至百万级连接的时候,传统的 BIO 模型是无能为力的。因此,我们需要一种更高效的 I/O 处理模型来应对更高的并发量。
  • NIO (Non-blocking/New I/O): NIO 是一种同步非阻塞的 I/O 模型,在 Java 1.4 中引入了NIO 框架,对应 java.nio 包,提供了 Channel , Selector,Buffer 等抽象。NIO 中的 N 可以理解为 Non-blocking,不单纯是 New。它支持面向缓冲的,基于通道的 I/O 操作方法。 NIO提供了与传统 BIO 模型中的Socket和ServerSocket相对应的SocketChannel和ServerSocketChannel两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞 I/O 来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发
  • AIO (Asynchronous I/O): AIO 也就是 NIO 2。在 Java 7 中引入了 NIO 的改进版 NIO 2,它是异步非阻塞的 IO 模型。异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。AIO 是异步 IO 的缩写,虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO 的 IO 行为还是同步的。对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程自行进行 IO 操作,IO 操作本身是同步的。查阅网上相关资料,我发现就目前来说 AIO 的应用还不是很广泛,Netty 之前也尝试使用过 AIO,不过又放弃了

36. 常见关键字总结:static,final,this,super

点击阅读此篇文章

final 关键字

final 关键字,意思是最终的、不可修改的,最见不得变化,用来修饰类、方法和变量,具有以下特点:

  1. final 修饰的类不能被继承,final类 中的所有成员方法都会被隐式的指定为final方法;
  2. final修饰的方法不能被重写;
  3. final修饰的变量是常量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能让其指向另一个对象

说明:使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升(现在的Java版本已经不需要使用final方法进行这些优化了)。类中所有的private方法都隐式地指定为final。

static 关键字

static 关键字主要有以下四种使用场景:

  1. 修饰成员变量和成员方法: 被 static 修饰的成员属于类,不属于单个这个类的某个对象,被类中所有对象共享,可以并且建议通过类名调用。被static 声明的成员变量属于静态成员变量,静态变量 存放在 Java 内存区域的方法区。调用格式:类名.静态变量名 类名.静态方法名()
  2. 静态代码块: 静态代码块定义在类中方法外, 静态代码块在非静态代码块之前执行(静态代码块—>非静态代码块—>构造方法)。 该类不管创建多少对象,静态代码块只执行一次.
  3. 静态内部类(static修饰类的话只能修饰内部类): 静态内部类与非静态内部类之间存在一个最大的区别: 非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围类,但是静态内部类却没有。没有这个引用就意味着:1. 它的创建是不需要依赖外围类的创建。2. 它不能使用任何外围类的非static成员变量和方法。
  4. 静态导包(用来导入类中的静态资源,1.5之后的新特性): 格式为:import static 这两个关键字连用可以指定导入某个类中的指定静态资源,并且不需要使用类名调用类中静态成员,可以直接使用类中静态成员变量和成员方法。

this 关键字

this关键字用于引用类的当前实例。 例如:

代码语言:javascript
复制
class Manager {
    Employees[] employees;
     
    void manageEmployees() {
        int totalEmp = this.employees.length;
        System.out.println("Total employees: " + totalEmp);
        this.report();
    }
     
    void report() { }
}

在上面的示例中,this关键字用于两个地方:

  • this.employees.length:访问类Manager的当前实例的变量。
  • this.report():调用类Manager的当前实例的方法。

此关键字是可选的,这意味着如果上面的示例在不使用此关键字的情况下表现相同。 但是,使用此关键字可能会使代码更易读或易懂。

super 关键字

super关键字用于从子类访问父类的变量和方法。 例如:

代码语言:javascript
复制
public class Super {
    protected int number;
     
    protected showNumber() {
        System.out.println("number = " + number);
    }
}
 
public class Sub extends Super {
    void bar() {
        super.number = 10;
        super.showNumber();
    }
}

在上面的例子中,Sub 类访问父类成员变量 number 并调用其其父类 Super 的 showNumber() 方法。

使用 this 和 super 要注意的问题:

  • 在构造器中使用 super() 调用父类中的其他构造方法时,该语句必须处于构造器的首行,否则编译器会报错。另外,this 调用本类中的其他构造方法时,也要放在首行。
  • this、super不能用在static方法中。

简单解释一下:

被 static 修饰的成员属于类,不属于单个这个类的某个对象,被类中所有对象共享。而 this 代表对本类对象的引用,指向本类对象;而 super 代表对父类对象的引用,指向父类对象;所以, this和super是属于对象范畴的东西,而静态方法是属于类范畴的东西

详细请参考:https://gitee.com/SnailClimb/JavaGuide/blob/master/docs/java/basic/final,static,this,super.md#final-%E5%85%B3%E9%94%AE%E5%AD%97

37. Collections 工具类和 Arrays 工具类常⻅方法总结

更多详细内容请参考:Collections 工具类和 Arrays 工具类常见方法

Collections

Collections 工具类常用方法:

  1. 排序
  2. 查找,替换操作
  3. 同步控制(不推荐,需要线程安全的集合类型时请考虑使用 JUC 包下的并发集合)
排序操作
代码语言:javascript
复制
void reverse(List list)//反转
void shuffle(List list)//随机排序
void sort(List list)//按自然排序的升序排序
void sort(List list, Comparator c)//定制排序,由Comparator控制排序逻辑
void swap(List list, int i , int j)//交换两个索引位置的元素
void rotate(List list, int distance)//旋转。当distance为正数时,将list后distance个元素整体移到前面。当distance为负数时,将 list的前distance个元素整体移到后面。
查找,替换操作
代码语言:javascript
复制
int binarySearch(List list, Object key)//对List进行二分查找,返回索引,注意List必须是有序的
int max(Collection coll)//根据元素的自然顺序,返回最大的元素。 类比int min(Collection coll)
int max(Collection coll, Comparator c)//根据定制排序,返回最大元素,排序规则由Comparatator类控制。类比int min(Collection coll, Comparator c)
void fill(List list, Object obj)//用指定的元素代替指定list中的所有元素。
int frequency(Collection c, Object o)//统计元素出现次数
int indexOfSubList(List list, List target)//统计target在list中第一次出现的索引,找不到则返回-1,类比int lastIndexOfSubList(List source, list target).
boolean replaceAll(List list, Object oldVal, Object newVal), 用新元素替换旧元素
同步控制

Collections提供了多个synchronizedXxx()方法·,该方法可以将指定集合包装成线程同步的集合,从而解决多线程并发访问集合时的线程安全问题。

我们知道 HashSet,TreeSet,ArrayList,LinkedList,HashMap,TreeMap 都是线程不安全的。Collections提供了多个静态方法可以把他们包装成线程同步的集合。

最好不要用下面这些方法,效率非常低,需要线程安全的集合类型时请考虑使用 JUC 包下的并发集合。

方法如下:

代码语言:javascript
复制
synchronizedCollection(Collection<T>  c) //返回指定 collection 支持的同步(线程安全的)collection。
synchronizedList(List<T> list)//返回指定列表支持的同步(线程安全的)List。
synchronizedMap(Map<K,V> m) //返回由指定映射支持的同步(线程安全的)Map。
synchronizedSet(Set<T> s) //返回指定 set 支持的同步(线程安全的)set。
Collections还可以设置不可变集合,提供了如下三类方法:
代码语言:javascript
复制
emptyXxx(): 返回一个空的、不可变的集合对象,此处的集合既可以是List,也可以是Set,还可以是Map。
singletonXxx(): 返回一个只包含指定对象(只有一个或一个元素)的不可变的集合对象,此处的集合可以是:List,Set,Map。
unmodifiableXxx(): 返回指定集合对象的不可变视图,此处的集合可以是:List,Set,Map。
上面三类方法的参数是原有的集合对象,返回值是该集合的”只读“版本。

Arrays类的常见操作

  1. 排序 : sort()
  2. 查找 : binarySearch()
  3. 比较: equals()
  4. 填充 : fill()
  5. 转列表: asList()
  6. 转字符串 : toString()
  7. 复制: copyOf()

38. 深拷⻉ vs 浅拷⻉

  1. 浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝;
  2. 深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 27. hashCode 与 equals(重要)
  • 28. 为什么Java中只有值传递?
  • 29. 简述线程、程序、进程的基本概念以及它们之间的关系
  • 30. 线程有哪些基本状态?
  • 31. 关于 final 关键字的总结:
  • 32. Java 中的异常处理
    • Java异常类层次结构图
      • Throwable 类常用方法
        • 异常处理总结
        • 33. Java序列化如果有些字段不想进行序列化,怎么办?
        • 34. 获取用键盘输入常用的两种方法
        • 35. Java 中 IO 流
          • Java 中的IO 流分为几种?
            • 既然有了字节流,为什么还要有字符流?
              • BIO,NIO,AIO 有什么区别?
              • 36. 常见关键字总结:static,final,this,super
                • final 关键字
                  • static 关键字
                    • this 关键字
                    • super 关键字
                    • 37. Collections 工具类和 Arrays 工具类常⻅方法总结
                      • Collections
                        • 排序操作
                        • 查找,替换操作
                        • 同步控制
                        • Collections还可以设置不可变集合,提供了如下三类方法:
                      • Arrays类的常见操作
                      • 38. 深拷⻉ vs 浅拷⻉
                      相关产品与服务
                      数据保险箱
                      数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档