1.List接口有哪些实现类?
答:1. ArrayList:基于数组实现的动态数组,支持随机访问和快速插入/删除操作,适用于频繁访问和修改元素的场景。 2. LinkedList:基于链表实现的双向链表,支持快速插入/删除操作,适用于频繁插入/删除元素的场景。 3. Vector:与ArrayList类似,但是是线程安全的,支持同步操作,适用于多线程环境。 4. Stack:基于Vector实现的堆栈数据结构,支持入栈和出栈操作。 5. CopyOnWriteArrayList:与ArrayList类似,但是是线程安全的,通过在修改操作时创建一个新的副本来实现线程安全。 6. ImmutableList:不可变列表,一旦创建就不能修改,可以通过Collections工具类的静态方法创建。 7. Arrays.asList():将数组转换为List的固定大小的列表。
2.ArrayList类有几个构造函数?
ArrayList类有以下几个构造函数:
ArrayList(): 创建一个初始容量为10的空列表。ArrayList(Collection<? extends E> c): 创建一个包含指定集合中的元素的列表,按照集合的迭代器返回的顺序。ArrayList(int initialCapacity): 创建一个具有指定初始容量的空列表。ArrayList(E[] array): 创建一个包含指定数组中的元素的列表,按照数组的顺序。 这些构造函数提供了不同的方式来创建ArrayList对象。你可以根据具体的需求选择合适的构造函数来初始化ArrayList对象。
3.ArrayList的本质是?
ArrayList的本质是一个动态数组,通过数组来存储元素,具有随机访问、动态调整大小和有序存储的特点。
4.ArrayList每次扩容多少?从哪个方法看出来的
ArrayList的扩容策略是每次扩容当前容量的一半。具体来说,当ArrayList需要扩容时,会创建一个新的数组,并将原有元素复制到新数组中。新数组的容量是原数组容量的1.5倍(即当前容量的一半)。从源码的 ensureCapacityInternal 方法可以看出ArrayList的扩容策略。
5.>>符号的作用是什么
Java中的右移位操作符。它的作用是将一个数的二进制表示向右移动指定的位数,并根据移动方式填充空位。
具体来说, >> 的作用如下:
- 对于正数,右移操作会将数值向右移动指定的位数,高位补0。
- 对于负数,右移操作会将数值向右移动指定的位数,高位补1。
6.ArrayList的常用的公有方法是
-
add(E element): 将元素添加到ArrayList的末尾。
-
add(int index, E element): 在指定位置插入元素。
-
remove(int index): 移除指定位置的元素。
-
remove(Object obj): 移除第一个匹配的指定元素。
-
get(int index): 获取指定位置的元素。
-
set(int index, E element): 替换指定位置的元素。
-
size(): 返回ArrayList中元素的个数。
-
isEmpty(): 判断ArrayList是否为空。
-
contains(Object obj): 判断ArrayList是否包含指定元素。
-
indexOf(Object obj): 返回指定元素第一次出现的索引。
-
lastIndexOf(Object obj): 返回指定元素最后一次出现的索引。
-
clear(): 清空ArrayList中的所有元素。
-
toArray(): 将ArrayList转换为数组。
-
iterator(): 返回一个迭代器,用于遍历ArrayList中的元素。
7.序列化接口标识
当一个类实现了Serializable接口,表示该类的对象可以被序列化为字节流,以便在网络传输或持久化存储时使用。反之,如果一个类没有实现Serializable接口,则它的对象不能被序列化。
要实现序列化,只需在类的声明中添加implements Serializable关键字即可如下所示:
public class MyClass implements Serializable {
// 类的成员和方法
}
8.对象输入流和输出流的类名是
对象输入流和输出流的类名分别是ObjectInputStream和ObjectOutputStream
-
ObjectInputStream类用于从输入流中读取对象的数据,并将其反序列化为Java对象。它提供了readObject()方法用于读取对象,以及其他一些读取基本数据类型的方法。可以使用它来读取通过ObjectOutputStream序列化的对象。
-
ObjectOutputStream类用于将对象序列化为字节流,并将其写入输出流。它提供了writeObject()方法用于写入对象,以及其他一些写入基本数据类型的方法。可以使用它来将Java对象序列化并写入到文件、网络流或其他输出流中。
9.使用对象输入流中的什么方法可以读取磁盘对象
要读取磁盘上的对象,可以使用对象输入流中的readObject()方法。该方法会从输入流中读取字节数据,并将其反序列化为Java对象。
10.如何保证一个类中只实例一次Scanner对象
-
静态变量:要保证一个类中只实例化一次Scanner对象,可以使用静态变量和静态初始化块的结合来实现。静态变量在类加载时只会初始化一次,而静态初始化块在类加载时会执行一次。
-
反射:使用反射结合单例模式来实现
import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.Scanner; public class SingletonScanner { private static Scanner scanner; private SingletonScanner() { // 私有构造方法 就无法创建对象,只能通过静态变量读取,保证只有一个对象 } public static Scanner getInstance() { if (scanner == null) { synchronized (SingletonScanner.class) { if (scanner == null) { try { Constructor<Scanner> constructor = Scanner.class.getDeclaredConstructor(); constructor.setAccessible(true); scanner = constructor.newInstance(); } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) { e.printStackTrace(); } } } } return scanner; } }
将Scanner对象封装在SingletonScanner类中,并使用双重检查锁定来确保只实例化一次。通过getInstance()方法获取Scanner对象,如果对象为空,则使用反射创建一个新的Scanner对象。
11.构造函数的作用是
构造函数的主要作用是创建和初始化对象。它负责为对象分配内存空间,并执行必要的初始化操作,以确保对象在创建时处于正确的状态。构造函数还可以接受参数,用于在创建对象时传递初始化数据。通过限制构造函数的访问权限,可以控制对象的创建方式。
简答的设计模式可以了解:(构造函数可以设置为私有或受保护的,以限制对象的创建。这种方式常用于实现单例模式或工厂模式,确保只能通过特定的方式创建对象。 )
12.什么是网络编程的三要素
IP地址和端口号和协议
13.简介tcp协议
tcp是一种面向连接的、可靠的、基于字节流的传输层协议
- 面向连接:在进行数据传输之前,发送方和接收方需要先建立一个连接。连接的建立需要经过三次握手,确保双方都能够正常通信。连接建立后,双方可以进行数据的传输。
- 可靠性:TCP协议提供可靠的数据传输机制。它通过序列号、确认应答、超时重传、流量控制和拥塞控制等机制来确保数据的可靠性,保证数据不丢失、不重复和按序到达。
- 字节流传输:TCP协议将数据视为连续的字节流进行传输,没有数据边界。发送方将数据拆分成合适的大小的数据段,接收方按照顺序重新组装成完整的数据。
- 全双工通信:TCP协议支持全双工通信,即发送方和接收方可以同时进行数据的发送和接收。每个方向的数据流都是独立的,互不干扰。
- 拥塞控制:TCP协议通过拥塞控制机制来避免网络拥塞。它根据网络的拥塞程度和带宽情况调整发送速率,以保证网络的稳定性和公平性。
14.简介upd协议
UDP是一种无连接的、不可靠的、基于数据报的传输层协议。它是互联网协议栈中的一部分,用于在网络中快速传输数据。
- 无连接:UDP协议不需要在数据传输之前建立连接。发送方将数据封装成数据报,直接发送给接收方,不需要进行连接的建立和拆除。
- 不可靠性:UDP协议提供的是一种尽力而为的传输机制,不保证数据的可靠性。它不提供确认应答、重传机制和流量控制,数据报可能会丢失、重复或者乱序到达。
- 数据报传输:UDP协议将数据封装成数据报进行传输,每个数据报都是独立的,有自己的标识和长度。数据报的大小限制在64KB以内。
- 高效性:由于UDP协议没有连接建立和拆除的开销,以及不需要维护连接状态的信息,因此传输效率较高。它适用于实时性要求较高的应用场景,如音视频传输、实时游戏等。
- 广播和多播:UDP协议支持广播和多播功能。广播是将数据报发送给同一网络中的所有主机,而多播是将数据报发送给特定的一组主机。 UDP协议适用于那些对实时性要求较高、数据传输可靠性要求相对较低的应用场景。它常用于音视频传输、实时游戏、DNS查询、实时监控等。然而,由于UDP协议的不可靠性,应用层需要自行处理数据的完整性和可靠性,如使用应答机制、重传机制或者冗余校验等
15.简介端口
端口是计算机网络中用于标识不同应用程序或服务的数字
在TCP/IP协议中,端口号是一个16位的无符号整数,范围从0到65535
0到1023的端口号被称为"知名端口" HTTP(端口号80)、FTP(端口号21)、SSH(端口号22)等。
16.创建TCP服务器的类名是
ServerSocket
简单的应用代码如下:
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer {
public static void main(String[] args) {
try {
// 创建ServerSocket对象,指定监听的端口号
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("服务器已启动,等待客户端连接...");
// 循环等待客户端连接
while (true) {
// 监听客户端连接请求,accept()方法会阻塞直到有客户端连接
Socket clientSocket = serverSocket.accept();
// 创建一个新的线程处理客户端请求
Thread thread = new Thread(() -> {
// 处理客户端请求的代码
// ...
System.out.println("客户端连接成功,处理请求...");
// 关闭客户端连接
try {
clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
});
// 启动线程处理客户端请求
thread.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
17.如果程序端口被其他程序占用,无法启动,java的异常名称是
如果程序端口被其他程序占用,无法启动服务器,Java中会抛出 java.net.BindException 异常。
18.tcp网络信息通讯落实在什么技术
-
Socket编程:TCP通信可以通过Socket编程实现。Socket是一种抽象概念,用于表示网络中的一个端点,它可以用于建立连接、发送和接收数据等操作。
-
三次握手:在TCP连接建立过程中,客户端和服务器之间需要进行三次握手,确保双方都能够可靠地发送和接收数据。
-
数据分段:TCP将数据分成多个小的数据段进行传输,每个数据段都有序号和校验和,以便在传输过程中进行错误检测和恢复。
-
流控制:TCP使用滑动窗口机制来控制发送方和接收方之间的数据流量,以避免发送方发送速度过快而导致接收方无法处理的问题。
-
拥塞控制:TCP通过拥塞窗口和拥塞避免算法来控制网络中的拥塞情况,以保证网络的稳定性和公平性。
-
错误恢复:当数据在传输过程中发生错误时,TCP使用确认和重传机制来保证数据的可靠性,确保数据能够正确地被接收方接收。
19.tcp和udp谁更加安全,为什么
TCP的安全性:
- 可靠性:TCP是一种面向连接的协议,提供可靠的数据传输。它使用三次握手和确认机制来确保数据的完整性和可靠性。
- 数据校验:TCP使用校验和机制来检测数据在传输过程中的错误,以确保数据的准确性。
- 有序性:TCP保证数据按照发送的顺序进行传输,确保数据的有序性。
- 加密和认证:TCP本身并不提供加密和认证机制,但可以在应用层上使用SSL/TLS等协议来加密和认证数据。
UDP的安全性:
- 速度和效率:UDP是一种无连接的协议,没有TCP的握手和确认机制,因此速度更快。但这也意味着UDP在数据传输过程中可能会丢失数据或者顺序错乱。
- 灵活性:UDP允许发送方发送任意大小的数据包,更加灵活。但这也使得UDP容易受到拒绝服务(DoS)攻击和其他网络攻击。
- 安全性依赖应用层:UDP本身不提供数据的加密和认证机制,安全性完全依赖于应用层的实现。
综上所述,TCP在可靠性和数据完整性方面更强,适用于对数据传输可靠性要求较高的场景。而UDP在速度和灵活性方面更好,适用于实时性要求高、数据传输要求不那么严格的场景。在安全性方面,TCP和UDP本身并没有太大的差别,安全性主要依赖于应用层的实现和使用的加密和认证机制。因此,选择TCP还是UDP主要取决于具体的应用需求和安全策略。
20.创建线程对象的三种方法
- 继承Thread类:
创建线程的一种方法是通过继承Thread类,并重写其run()方法来定义线程的执行逻辑。然后,通过创建Thread的子类对象来创建线程对象。例如
public class MyThread extends Thread { @Override public void run() { // 线程执行的逻辑 } } // 创建线程对象 MyThread thread = new MyThread();
- 实现Runnable接口:
另一种方法是通过实现Runnable接口来创建线程对象。Runnable接口代表了一个可运行的任务,需要实现run()方法来定义线程的执行逻辑。然后,通过创建Thread对象,并将Runnable对象作为参数传递给Thread的构造方法来创建线程对象。例如:
public class MyRunnable implements Runnable { @Override public void run() { // 线程执行的逻辑 } } // 创建线程对象 MyRunnable runnable = new MyRunnable(); Thread thread = new Thread(runnable);
- 使用匿名内部类:
可以使用匿名内部类的方式来创建线程对象,这种方式可以简化代码。例如:
Thread thread = new Thread(new Runnable() { @Override public void run() { // 线程执行的逻辑 } });
21.开启线程的方法名
开启线程的方法名是 start(),实例如下
public class MyThread extends Thread {
@Override
public void run() {
// 线程执行的逻辑
System.out.println("线程执行中");
}
}
public class Main {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start(); // 开启线程
}
}
22.为什么要使用多线程
-
提高程序的并发性:多线程允许多个任务同时执行,从而提高程序的并发性。这对于需要同时处理多个请求或任务的应用程序特别有用,可以减少等待时间,提高系统的吞吐量。
-
提高程序的响应能力:通过将耗时的操作放在后台线程中执行,可以避免阻塞主线程,使程序更加响应。这对于需要处理用户界面、网络请求或其他需要即时响应的场景非常重要。
-
充分利用多核处理器:多线程可以充分利用多核处理器的优势,将任务分配给不同的核心并行执行,从而提高程序的计算能力和效率。
-
实现异步编程:多线程可以实现异步编程,允许程序在等待某些操作完成时继续执行其他任务,而不必阻塞线程。这对于需要处理IO操作、定时任务或事件驱动的应用程序非常有用。
-
提高任务的分解和组合能力:多线程可以将复杂的任务分解为多个子任务并行执行,然后将结果合并,从而提高任务的处理效率和灵活性。
总之,使用多线程可以提高程序的性能、响应能力和并发性,充分利用多核处理器,并实现异步编程和任务的分解与组合。然而,多线程编程也需要注意线程安全性和资源竞争等问题,需要合理设计和管理线程,以确保程序的正确性和稳定性。
23.为什么要使用反射
使用反射的主要目的是在运行时动态地获取和操作类的信息,以及调用类的方法和访问类的属性。以下是使用反射的一些常见原因:
-
动态加载类:通过使用反射,可以在运行时动态地加载类,而不是在编译时确定类的依赖关系。这对于需要根据配置文件或用户输入来加载不同的类或插件的应用程序非常有用。
-
获取类的信息:使用反射可以获取类的各种信息,如类的名称、修饰符、父类、接口、方法、字段等。这对于编写通用的代码、框架或工具类,以及进行调试和日志记录非常有用。
-
调用类的方法:通过反射,可以动态地调用类的方法,包括公共方法、私有方法和静态方法。这对于编写通用的代码、实现插件化架构或实现动态代理非常有用。
-
访问类的属性:反射还可以访问类的字段和属性,包括公共字段、私有字段和静态字段。这对于进行对象的序列化和反序列化、动态修改对象的属性或进行对象的复制非常有用。
-
动态创建对象:通过反射,可以动态地创建对象实例,而不需要在编译时明确知道类的类型。这对于根据配置信息或运行时条件创建对象非常有用。
总之,使用反射可以在运行时动态地获取和操作类的信息,调用类的方法和访问类的属性。它提供了更大的灵活性和动态性,使得代码可以更加通用、灵活和可扩展。然而,反射的使用也需要谨慎,因为它可能会影响性能,并且可能会绕过编译时的类型检查,导致运行时的错误。
24.获取Class对象的三种方式
-
使用类名.class语法获取Class对象,通过 MyClass.class 来获取。
-
使用Class.forName()方法获取Class对象,通过提供类的全限定名来获取。
-
使用对象的getClass()方法获取Class对象,通过创建一个MyClass对象并调用其 getClass() 方法来获取。
实例如下:
public class MyClass { public static void main(String[] args) { // 1.使用类名.class语法获取Class对象 Class<?> clazz1 = MyClass.class; System.out.println("Class对象1:" + clazz1.getName()); try { // 2.使用Class.forName()方法获取Class对象 Class<?> clazz2 = Class.forName("com.example.MyClass"); System.out.println("Class对象2:" + clazz2.getName()); } catch (ClassNotFoundException e) { e.printStackTrace(); } // 3.使用对象的getClass()方法获取Class对象 MyClass myObject = new MyClass(); Class<?> clazz3 = myObject.getClass(); System.out.println("Class对象3:" + clazz3.getName()); }
25.线程池的作用是什么
线程池的作用是管理和复用线程,以提高线程的利用率和系统性能。具体来说,线程池的作用包括:
- 降低线程创建和销毁的开销:线程的创建和销毁是一项开销较大的操作。使用线程池可以避免频繁地创建和销毁线程,而是通过重复使用已有的线程,减少了创建和销毁线程的开销。
- 提高线程的执行效率:线程池可以根据系统的负载情况,自动调整线程的数量。当任务较多时,线程池可以创建更多的线程来处理任务,提高任务的并发执行效率。当任务较少时,线程池可以缩减线程的数量,避免资源的浪费。
- 控制并发线程的数量:线程池可以限制并发执行的线程数量,避免系统资源被过度占用。通过设置线程池的核心线程数、最大线程数和任务队列等参数,可以控制并发线程的数量,避免系统资源耗尽。
- 提供线程的管理和监控功能:线程池可以提供线程的管理和监控功能,如线程的状态监控、线程池的状态监控、线程的异常处理等。通过这些功能,可以更好地管理和监控线程的运行情况,提高系统的稳定性和可靠性。 总的来说,线程池的作用是优化线程的创建和销毁,提高线程的利用率和系统性能,以及提供线程的管理和监控功能。在多线程编程中,合理使用线程池可以帮助我们更好地管理和控制线程的执行。
26.lambda表达式应用的前提
jdk版本大于或等于8
函数式接口:Lambda只能用于接口,接口内部有且只有一个抽象方法,可以用多个方法但是必须保证其他方法有默认实现,必须留一个抽象方法出来
简单的实例如下:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.forEach(n -> System.out.println(n)); // 遍历打印每个元素
List<Integer> evenNumbers = numbers.stream().filter(n -> n % 2 == 0).collect(Collectors.toList()); // 筛选偶数
List<String> stringNumbers = numbers.stream().map(n -> String.valueOf(n)).collect(Collectors.toList()); // 映射为字符串
27.Set集合有什么特点
-
无序性:Set集合中的元素是无序的,即元素的存储顺序与添加顺序不一致。这是因为Set集合使用哈希表或树等数据结构来存储元素,而这些数据结构不保持元素的特定顺序。
-
不允许重复元素:Set集合不允许包含重复的元素。如果试图向Set集合中添加重复的元素,添加操作将会被忽略,不会改变Set集合的内容。
-
基于元素的唯一性:Set集合中的元素是唯一的,它们的唯一性是根据元素的 equals() 和 hashCode() 方法来确定的。因此,如果要将自定义对象添加到Set集合中,需要正确实现这两个方法。
-
高效的查找操作:由于Set集合使用哈希表或树等数据结构来存储元素,它具有高效的查找操作。对于大量数据的查找,Set集合比List集合更加高效。
-
不保证顺序:虽然Set集合中的元素是无序的,但是具体的实现类可能会提供一种特定的顺序。例如, LinkedHashSet 类可以保持元素的插入顺序,而 TreeSet 类可以按照元素的自然顺序或自定义顺序进行排序。
-
常见实现类:Java提供了多个Set集合的实现类,常见的有 HashSet 、 LinkedHashSet 和 TreeSet 。 HashSet 是最常用的实现类,它基于哈希表实现,具有较快的插入和查找速度; LinkedHashSet 是HashSet的子类,它通过链表维护元素的插入顺序; TreeSet 是基于红黑树实现的,可以按照元素的自然顺序或自定义顺序进行排序。
下面是一个set集合的简单应用实例
import java.util.HashSet;
import java.util.Set;
public class SetExample {
public static void main(String[] args) {
// 创建一个Set集合
Set<Integer> numberSet = new HashSet<>();
// 添加元素到Set集合
numberSet.add(5);
numberSet.add(2);
numberSet.add(8);
numberSet.add(2); // 重复元素,将被忽略
// 打印Set集合中的元素
System.out.println("Set集合中的元素:");
for (int number : numberSet) {
System.out.println(number);
}
// 判断Set集合中是否包含某个元素
int target = 8;
if (numberSet.contains(target)) {
System.out.println("Set集合中包含元素 " + target);
} else {
System.out.println("Set集合中不包含元素 " + target);
}
// 删除Set集合中的元素
numberSet.remove(2);
// 打印更新后的Set集合
System.out.println("更新后的Set集合中的元素:");
for (int number : numberSet) {
System.out.println(number);
}
}
}
28.Map映射有什么特点
-
键值对:Map映射是由键和值组成的集合。每个键都是唯一的,而值可以重复。
-
无序性:Map映射中的键值对是无序的,即它们的存储顺序与插入顺序无关。
-
键的唯一性:Map映射中的键是唯一的,不能重复。如果插入具有相同键的多个键值对,则后面的键值对会覆盖前面的键值对。
-
值的重复性:Map映射中的值可以重复,允许多个键关联同一个值。
-
可以包含空键和空值:Map映射可以包含空键和空值,但是只能有一个空键和多个空值。
-
动态增长:Map映射的大小可以动态增长,根据需要自动调整内部容量。
-
高效的查找操作:通过键可以快速查找对应的值,具有高效的查找性能。
-
提供多种实现类:Java提供了多种实现Map接口的类,如HashMap、TreeMap、LinkedHashMap等,每种实现类都有不同的特点和适用场景。
29.简述迭代
迭代是指通过重复执行一系列操作来逐步逼近目标或解决问题的过程。在编程中,迭代通常用于遍历数据集合或执行重复的操作
常用的迭代方式:
- for循环:
for (int i = 0; i < 5; i++) { System.out.println(i); } - while循环:
int i = 0;
while (i < 5) {
System.out.println(i);
i++;
}
-
增强for循环(foreach循环):
String[] names = {"Alice", "Bob", "Charlie"}; for (String name : names) { System.out.println(name); } -
迭代器(Iterator):
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); Iterator<Integer> iterator = numbers.iterator(); //通过调用 hasNext() 方法判断是否还有下一个元素,如果有, //则通过 next() 方法获取下一个元素,并执行相应的操作。 while (iterator.hasNext()) { Integer number = iterator.next(); System.out.println(number); }
30.简述多线程,同步锁
多线程: 指在一个程序中同时执行多个线程,每个线程都是独立的执行路径。多线程可以提高程序的性能和响应能力,尤其适用于处理并发任务、异步操作和提高系统吞吐量等场景。
同步锁:是一种机制,用于控制多个线程对共享资源的访问,以保证线程安全性。通过同步锁,我们可以确保在同一时间只有一个线程可以访问共享资源,避免多个线程同时修改共享资源导致的竞争问题和数据不一致性。
多线程是同时执行多个线程的机制,可以提高程序性能和响应能力。同步锁是一种机制,用于控制多个线程对共享资源的访问,以保证线程安全性。通过合适的同步锁机制,我们可以避免竞争条件和数据不一致性的问题。