-
Java中的CompletableFuture是一个实现了Future接口的类,它提供了一种异步编程的方式,可以让你在不阻塞主线程的情况下执行耗时操作。CompletableFuture主要用于处理异步任务的结果,支持链式调用和组合多个异步任务。作用场景:异步执行耗时操作,如文件读写、网络请求等。对多个异步任务进行组合处理,如并行执行、串行执行等。处理异步任务的结果,支持链式调用。示例代码:import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; public class CompletableFutureExample { public static void main(String[] args) { // 创建一个异步任务,模拟耗时操作 Supplier<String> supplier = () -> { try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } return "Hello, CompletableFuture!"; }; // 使用CompletableFuture.supplyAsync()方法创建异步任务 CompletableFuture<String> future = CompletableFuture.supplyAsync(supplier); // 当异步任务完成时,处理结果 future.thenAccept(result -> { System.out.println("异步任务结果:" + result); }); // 阻塞主线程,等待异步任务完成 future.join(); } }在这个示例中,我们创建了一个异步任务,模拟了耗时操作(例如文件读写、网络请求等)。然后,我们使用CompletableFuture.supplyAsync()方法创建了一个异步任务,并通过thenAccept()方法处理任务完成后的结果。最后,我们使用future.join()方法阻塞主线程,等待异步任务完成。
-
ReentrantReadWriteLock是一种Java并发工具类,它提供了一种灵活的读写锁机制。与synchronized关键字不同,ReentrantReadWriteLock允许多个线程同时读取共享资源,但在写入时只允许一个线程进行操作,从而提高了并发性能。ReentrantReadWriteLock的主要特点如下:可重入性:同一个线程可以多次获取读锁或写锁,但每次只能持有一个锁。公平性:当多个线程同时请求锁时,按照等待时间的长短进行排队,而不是随机分配。读写分离:读锁和写锁是分开的,提高了并发性能。下面是一个使用ReentrantReadWriteLock的示例代码:import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class ReentrantReadWriteLockDemo { private ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); private int value; // 读操作 public int read() { readWriteLock.readLock().lock(); // 获取读锁 try { return value; } finally { readWriteLock.readLock().unlock(); // 释放读锁 } } // 写操作 public void write(int value) { readWriteLock.writeLock().lock(); // 获取写锁 try { this.value = value; } finally { readWriteLock.writeLock().unlock(); // 释放写锁 } } }在这个示例中,我们使用了一个ReentrantReadWriteLock对象来实现读写分离。当需要读取value时,我们获取读锁并返回当前值;当需要修改value时,我们获取写锁并更新值。这样可以确保在读取时不会发生数据不一致的问题,同时提高了并发性能。
-
为什么需要方法重载 在使用方法的过程中我们可能会遇到以下如同例子的情形: public class method1 { public static void main(String[] args) { int a1 = 10; int b1 = 20; double ret1 = add(a1, b1); System.out.println("ret1 = " + ret1); double a2 = 10.0; double b2 = 20.0; int ret2 = add(a2, b2); System.out.println("ret2 = " + ret2); } public static int add(int x, int y) { return x + y; } } 在计算ret1时我们可以很容易得到结果,但在计算ret2时由于double转换到参数的int会有损失而报错,这里我们一般是再另外写一个方法,然后给两个方法起不同的名字: public class method2 { public static void main(String[] args) { int a1 = 10; int b1 = 20; int ret1 = addInt(a1, b1); System.out.println("ret1 = " + ret1); double a2 = 10.0; double b2 = 20.0; double ret2 = addDouble(a2, b2); System.out.println("ret2 = " + ret2); } public static int addInt(int x, int y) { return x + y; } public static double addDouble(double x, double y) { return x + y; } } 这样做我们说确实能解决问题,但不友好的地方是:我们需要提供不同的方法名,如果方法一旦变多,给方法起名字显得非常地木乱,那是否能将这里所有表示相加的方法都命名为add呢?可以,这就涉及到了java的方法重载。 方法重载的概念 在自然语言中,一个词语如果有多重含义(就类似文言文的一词多义),那么就是说该词语被重载了,具体代表什么含义需要结合具体的场景。 在Java中如果多个方法的名字相同,参数列表不同,则称该几种方法被重载了。 举个栗子: public class method3 { public static void main(String[] args) { add(2,3); add(2,3,4); add(2.0, 3.0); } public static int add(int x, int y) { return x + y; } public static int add(int x, int y, int z) { return x + y + z; } public static double add(double x, double y) { return x + y; } } 注意:1.方法名必须相同 2.参数列表必须不同(参数的个数不同,参数的类型不同,参数的顺序不同)。 3.与方法的返回类型无关(仅仅因为返回类型不同是无法构成重载的) 4.无需关心怎么选择调用哪个方法,编译器在编译的时候,会对实参进行推演,根据推演的结果来确定调用哪个方法。 方法签名 在同一个作用域中不能定义两个名字相同的变量,但为什么在同一个类中,能够使用相同名字的方法呢?这就用到了方法签名。 法签名即:经过编译器修改过之后得到最终的名字。具体方式:方法路径名+参数列表+返回值类型,构成方法完整的名字。 如何查看呢?还是以上面的代码为例: public class method3 { public static void main(String[] args) { add(2,3); add(2,3,4); add(2.0, 3.0); } public static int add(int x, int y) { return x + y; } public static int add(int x, int y, int z) { return x + y + z; } public static double add(double x, double y) { return x + y; } } 上述代码经过编译之后,然后使用JDK自带的javap反汇编工具查看,具体操作(可用cmd窗口): 1.先对工程进行编译生成.class字节码文件。 2.在控制台中进入到要查看的.class所在的目录 3.输入javap -v 字节码文件名字即可。 ———————————————— 版权声明:本文为CSDN博主「灰灰快醒醒」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/asdssadddd/article/details/131957838
-
前言 在之前的文章中我们介绍了JAVA中String的运用方法,事实上在JAVA字符串操作中仅仅只知道String方法是远远不够的,今天我们要介绍的StringBuffer和StringBuilder在字符串操作中同样占据总要地位。 和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象 StringBuffer StringBuffer[^1]是Java中的一个类,用于表示可以修改的字符序列。它是一个可变对象,这意味着对象的值可以在创建后更改。 StringBuffer类提供了许多处理字符串数据的方法,例如添加或删除字符、将数据插入或附加到现有字符串,以及更改缓冲区的大小。与String类不同,StringBuffer对象可以在不创建新对象或复制现有对象的情况下进行修改。 StringBuffer通常用于需要执行大量字符串操作的情况,例如从较小的字符串构建大型字符串时。它在多线程应用程序中也很有用,因为不同的线程可能同时访问同一个StringBuffer对象并修改其内容。 StringBuffer方法 public StringBuffer append(String s) //将指定的字符串追加到此字符序列。 ```java public StringBuffer reverse() //将此字符序列用其反转形式取代 public delete(int start, int end) //移除此序列的子字符串中的字符。 public insert(int offset, int i) //将 int 参数的字符串表示形式插入此序列中。 insert(int offset, String str) //将 str 参数的字符串插入此序列中。 replace(int start, int end, String str) //使用给定 String 中的字符替换此序列的子字符串中的字符。 1.StringBuffer的直接父类是AbstractStringBuilder 2.StringBuffer实现了Serializable 即StringBuffer的对象可以串行化 3.在父类中 AbstractStringBuilder 有属性 char[] value,不是final 在该value数组存放字符串内容,引出存放在堆中的 4.StringBuffer 是一个final类 不能被继承 5.StringBuffer是存放在 char[] value ,所有的变化 不用每次创建新对象,更换地址所以效率高于String StringBuilder Java中的StringBuilder是一个类,它允许以比简单的串联更有效的方式创建和操作字符串。它提供了在字符串中附加、插入、替换和删除字符的方法。与不可变(不能更改)的String对象不同,StringBuilder对象可以在每次不创建新对象的情况下进行修改。这使得StringBuilder成为需要大量字符串操作的情况下更有效的选择。 StringBuilder方法 //追加字符串 StringBuilder sb = new StringBuilder("x"); sb.append("a"); // 在当前字符串的尾部追加字符串a // xa sb.append("b"); // 在当前字符串的尾部追加字符串a // xab //替换字符串 StringBuilder sb = new StringBuilder("天王盖地虎"); sb.replace(1, 3, "博爱他"); // 替换下标1-3的位置不包含3 sb.replace(0, 1, ""); // 替换下标0-1的位置不包含1 System.out.println(sb); // 替换会产生新内容 //删除字符串 StringBuilder sb = new StringBuilder(); sb.append("巴山楚水凄凉地,responsebility").delete(10, 12) // 按照下标内容删除 .deleteCharAt(0); // 删除指定位置内容 System.out.println(sb); //指定下标新内容 StringBuilder sb = new StringBuilder("abcd"); // insert():在指定下标位置,插入新内容 sb.insert(2, "123") .insert(2, true) .insert(2, Math.PI); //字符串逆序 // 字符串逆序 StringBuilder sb=new StringBuilder("甲乙丙丁"); sb.reverse(); // 逆序反转 System.out.println(sb); //设置分隔符 // StringJoiner joiner = new StringJoiner("#"); // 设置分隔符 StringJoiner joiner = new StringJoiner("#", "@", "@"); // 设置分隔符 String ,StringBuffer 和 StringBuilder的区别 String和StringBuffer互相转换 ———————————————— 版权声明:本文为CSDN博主「许思王」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_73602725/article/details/132069620
-
定义 线性表是一种数据结构,它是由一系列具有相同类型的元素组成的有序集合。线性表中的元素按照线性的顺序 排列,每个元素只有一个前驱元素和一个后继元素,除了第一个元素没有前驱元素,最后一个元素没有后继 元素。 可以表示为 表中的元素序列{x1,x2,...,xn},其中xi是表中的元素,它们具有相同的数据类型,n表示表中元素的个数。 线性表满足以下特性: 元素的有序性:线性表中的元素按照线性的顺序排列,每个元素都有一个确定的位置。 有限性:线性表中的元素个数是有限的。 相同数据类型:线性表中的元素具有相同的数据类型,即它们具有相同的属性和操作。 线性表可以用多种方式来表示和实现,常见的实现方式包括顺序表和链表两种。 顺序表 是一种基于数组的实现方式,元素在内存中连续存储,通过下标访问元素,插入和删除操作需要移动其他元素。 链表 是一种通过节点之间的指针来连接的实现方式,节点包含元素和指向下一个节点的指针,可以实现高效的插入 和删除操作,但访问元素的效率相对较低。 注意 线性表作为数据结构中的基本概念,广泛应用于各个领域的算法和程序设计中。掌握线性表的定义和实现方式, 能够帮助我们更好地理解和应用其他数据结构和算法。 线性表是一种常见的数据结构,它由一系列元素组成,这些元素之间存在着一对一的前后关系。线性表中的元 素可以是任何类型的数据,如整数、字符或对象等。 线性表中的元素排列有序,每个元素都有一个直接前驱元素和一个直接后继元素,除了第一个元素没有前驱元 素,最后一个元素没有后继元素。 线性表的访问方式可以根据元素在表中的位置进行操作,如按索引访问、插入、删除等。其中,按索引访问是 通过元素在表中的位置(索引)来获取对应的元素值。插入和删除可以在指定的位置上插入新的元素或者移除 现有的元素。 线性表有很多种实现方式,常见的包括数组和链表。数组作为一种静态数据结构,需要提前声明一个固定大小 的空间来存储元素,操作灵活性较差。链表则以节点的形式存储元素,每个节点包含了数据及指向下一个节点 的指针,操作相对灵活,但涉及到频繁的内存分配和释放。 线性表常用的算法包括遍历、查找和排序等。遍历操作用于依次访问线性表中的所有元素。查找操作可以根据 某个条件查找满足要求的元素,常见的方法有线性查找和二分查找。排序操作可以将线性表中的元素按照一定 的规则进行排列,常见的排序算法有冒泡排序、插入排序和快速排序等。 总之,线性表是一种简单、常用的数据结构,能够有效地组织和处理大量的数据,广泛应用于各个领域的算法 与程序设计中。 代码实现 在Java中,我们可以使用数组或链表来实现线性表。 使用数组实现线性表: public class ArrayList { private int size; private Object[] array; public ArrayList() { this.size = 0; this.array = new Object[10]; // 初始化数组大小为10 } public void add(Object item) { if (size == array.length) { Object[] newArray = new Object[array.length * 2]; // 当数组容量不足时,扩大数组 System.arraycopy(array, 0, newArray, 0, size); // 将原数组中的元素复制到新数组 array = newArray; } array[size++] = item; } public Object get(int index) { if (index >= 0 && index < size) { return array[index]; } else { throw new IndexOutOfBoundsException(); } } public int size() { return size; } } 在Java语言中,我们可以使用数组来实现线性表的增删改查操作线性表的例子: public class MyArrayList { private Object[] array; private int size; private int capacity; public MyArrayList() { capacity = 10; // 初始化容量为10 array = new Object[capacity]; size = 0; } public int getSize() { return size; } public boolean isEmpty() { return size == 0; } public void add(Object element) { if (size == capacity) { increaseCapacity(); // 扩容 } array[size] = element; size++; } public void remove(int index) { if (index < 0 || index >= size) { throw new IndexOutOfBoundsException("Index out of range"); } for (int i = index; i < size - 1; i++) { array[i] = array[i + 1]; } array[size - 1] = null; size--; } public void set(int index, Object element) { if (index < 0 || index >= size) { throw new IndexOutOfBoundsException("Index out of range"); } array[index] = element; } public Object get(int index) { if (index < 0 || index >= size) { throw new IndexOutOfBoundsException("Index out of range"); } return array[index]; } public boolean contains(Object element) { for (int i = 0; i < size; i++) { if (array[i].equals(element)) { return true; } } return false; } private void increaseCapacity() { capacity *= 2; // 扩大两倍 Object[] newArray = new Object[capacity]; for (int i = 0; i < size; i++) { newArray[i] = array[i]; } array = newArray; } } 使用示例: public class Main { public static void main(String[] args) { MyArrayList list = new MyArrayList(); list.add("A"); list.add("B"); list.add("C"); System.out.println("Size: " + list.getSize()); // 输出:Size: 3 list.remove(1); // 删除索引为1的元素 System.out.println("Size: " + list.getSize()); // 输出:Size: 2 list.set(0, "D"); // 将索引为0的元素设置为"D" System.out.println(list.get(0)); // 输出:D System.out.println(list.contains("C")); // 输出:false } } 使用链表实现线性表: public class LinkedList { private Node head; private int size; private class Node { // 链表节点类 Object data; Node next; public Node(Object data) { this.data = data; this.next = null; } } public LinkedList() { this.head = null; this.size = 0; } public void add(Object item) { Node newNode = new Node(item); if (head == null) { head = newNode; } else { Node current = head; while (current.next != null) { current = current.next; } current.next = newNode; } size++; } public Object get(int index) { if (index >= 0 && index < size) { Node current = head; for (int i = 0; i < index; i++) { current = current.next; } return current.data; } else { throw new IndexOutOfBoundsException(); } } public int size() { return size; } } 在Java语言中,我们用链表来实现线性表的增删改查操作线性表的例子: 代码演示了使用链表实现线性表的增加、删除、修改和查询操作,并使用 MyLinkedList类来实现这些功能。你可以根据需要进一步扩展和优化这个实现。 public class ListNode { Object data; ListNode next; public ListNode(Object data) { this.data = data; this.next = null; } } public class MyLinkedList { private ListNode head; private int size; public MyLinkedList() { head = null; size = 0; } public int getSize() { return size; } public boolean isEmpty() { return size == 0; } public void add(Object element) { ListNode newNode = new ListNode(element); if (head == null) { head = newNode; } else { ListNode current = head; while (current.next != null) { current = current.next; } current.next = newNode; } size++; } public void remove(int index) { if (index < 0 || index >= size) { throw new IndexOutOfBoundsException("Index out of range"); } if (index == 0) { head = head.next; } else { ListNode previous = getNode(index - 1); ListNode current = previous.next; previous.next = current.next; } size--; } public void set(int index, Object element) { if (index < 0 || index >= size) { throw new IndexOutOfBoundsException("Index out of range"); } ListNode node = getNode(index); node.data = element; } public Object get(int index) { if (index < 0 || index >= size) { throw new IndexOutOfBoundsException("Index out of range"); } ListNode node = getNode(index); return node.data; } public boolean contains(Object element) { ListNode current = head; while (current != null) { if (current.data.equals(element)) { return true; } current = current.next; } return false; } private ListNode getNode(int index) { ListNode current = head; for (int i = 0; i < index; i++) { current = current.next; } return current; } } 使用示例: public static void main(String[] args) { MyLinkedList list = new MyLinkedList(); list.add("A"); list.add("B"); list.add("C"); System.out.println("Size: " + list.getSize()); // 输出:Size: 3 list.remove(1); // 删除索引为1的元素 System.out.println("Size: " + list.getSize()); // 输出:Size: 2 list.set(0, "D"); // 将索引为0的元素设置为"D" System.out.println(list.get(0)); // 输出:D System.out.println(list.contains("C")); // 输出:false } ———————————————— 版权声明:本文为CSDN博主「学编程的小程」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_74888502/article/details/131758653
-
前言 上文介绍了网络编程的基础知识,并基于 Java 编写了 BIO 的网络编程。我们知道 BIO 模型是存在巨大问题的,比如 C10K 问题,其本质就是因其阻塞原因,导致如果想要承受更多的请求就必须有足够多的线程,但是足够多的线程会带来内存占用问题、CPU上下文切换带来的性能问题,从而造成服务端崩溃的现象。怎么解决这一问题呢?优化呗,所以后面就有了NIO、AIO、IO多路复用。本文将对这几个模型详细说明并基于 Java 编写 NIO。 基本概念 I/O阻塞是哪里阻塞、怎么阻塞?先简单了解一些基本概念 用户空间:被分配给用户进程的虚拟地址空间,用来存储用户进程的代码、数据和堆栈等。 内核空间:操作系统的基础,负责管理计算机的硬件资源和提供系统调用接口,同时也是用户空间和硬件之间的桥梁。 为了保证操作系统的安全性和稳定性,用户进程和操作系统内核是隔离的,用户进程不能直接访问内核空间,而是需要通过系统调用等方式向内核发起请求,由内核代表用户进程执行操作。 也就是说我们的应用程序在向硬件设备,比如网卡、磁盘等读取或写入数据时需要经过内核。下面对BIO、NIO、IO多路复用模型逐一介绍,详细了解各模型IO过程。 BIO过程 首先明确一下,我们所说的IO阻塞是用户进程也就是用户空间中的程序在向硬件设备读取的这个过程,在还没有数据时给用户的反映是需要一直等待的,这个我们叫阻塞IO。过程如下图: 我们可以看到,在进程向内核发起调用后直到数据返回,整个过程都是阻塞的,结合Java BIO 编程,也就是说在 inputStream.read() 这个过程是阻塞的,就存在几个问题: 由于阻塞会占用当前线程,使之不能进行其他操作,当有新的请求时只能新建线程。在 Linux 系统中,每个线程的默认栈大小为 8MB,在不考虑其他因素的情况下,一个 8G 的服务器最多也就承载1000个请求量。 由于线程数会随着请求量增大而增大,当有大量的线程阻塞唤醒,CPU 频繁的切换上下文会导致性能的下降。 这个问题也就是C10K问题的本质,看上去很直观,使用少的线程就处理多个IO是不是就可以解决呢?继续看NIO过程。 NIO过程 NIO我们说的是非阻塞,通过对BIO的说明,NIO的非阻塞体现在:无论有无数据都直接响应给用户进程,如下图: 我们可以看到,确实是在用户进程调用recvfrom()函数后直接响应,但是在没有拿到数据之前一直在轮询调用,虽然没有因为阻塞造成CPU上下文的切换,但是CPU一直处于空转状态,不能充分发挥CPU的作用。与BIO一样,在单线程的情况下,只能依次处理IO事件,单个线程依然处理不了多个IO事件。 IO多路复用过程 既然NIO与BIO一样并不能解决因阻塞可能会造成的C10K问题,那如何让一个线程可以处理多个IO事件呢?可不可以这样:用一个线程专门监听这些IO,一旦哪个IO有数据了再去接收数据。IO多路复用就是这个原理,如下图: 我们可以看到,多了一个select()函数调用,select()会去监听指定的FD(这里注意一下,在Linux中,一切皆文件,包括socket),内核去监听FD对应的sockets。任意一个或多个socket有数据了就返回给select(),这个时候再去调用recvfrom()接收sockets的数据,从而实现了单个线程处理多个I/O操作,提高系统的效率和性能。 在Linux下,常用的I/O多路复用方式有三种:select、poll和epoll。 select和poll的原理是基于轮询,即不断地查询所有注册的I/O事件,如果有事件发生就立即通知应用程序。这种方式的效率较低,因为每次查询都需要遍历所有的I/O事件。 epoll的原理是基于事件通知,即只有当I/O事件发生时,才会通知应用程序。这种方式的效率更高,因为它避免了无效的查询。 Java NIO编程 相比Java BIO编程,Java NIO编程理解起来没有那么直观,不过在理解多个IO模型(尤其是IO多路复用)后就相对容易理解了,Java NIO实际上就是IO多路复用。 Java NIO 核心概念 在Java NIO编程中,有几个核心的概念(组件)需要了解: 通道(Channel):通道是对原始I/O操作的抽象,可以用于读取和写入数据。它可以与文件、套接字等进行交互。 缓冲区(Buffer):缓冲区是一个容器,用于存储数据。在进行读写操作时,数据会先被读取到缓冲区中,然后从缓冲区中写入或读取。 选择器(Selector):选择器是Java NIO提供的一种多路复用机制,可以通过一个线程管理多个通道的I/O操作。 相对于BIO,开发者不直接与Socket交互,而是通过Selector与多个Channel交互,同时Buffer提供了方法来管理缓冲区的容量、位置和限制,通过设置这些属性,可以控制数据的读写位置和范围。总之NIO在提升IO处理效率和性能的同时支持更丰富的功能, Java NIO 示例 以下是一个简单的Java NIO网络编程示例,用于创建一个基于NIO的服务器和客户端: 服务端代码: import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; public class NIOServer { private Selector selector; public static void main(String[] args) throws IOException { NIOServer server = new NIOServer(); server.startServer(); } public void startServer() throws IOException { // 创建Selector selector = Selector.open(); // 创建ServerSocketChannel,并绑定端口 ServerSocketChannel serverChannel = ServerSocketChannel.open(); serverChannel.configureBlocking(false); serverChannel.socket().bind(new InetSocketAddress(8888)); // 将ServerSocketChannel注册到Selector上,并监听连接事件。当接收到一个客户端连接请求时就绪。该操作只给服务器使用。 serverChannel.register(selector, SelectionKey.OP_ACCEPT); System.out.println("Server started on port 8888"); // 循环等待事件发生 while (true) { // 等待事件触发,阻塞 | selectNow():非阻塞,立刻返回。 selector.select(); Iterator<SelectionKey> keys = selector.selectedKeys().iterator(); while (keys.hasNext()) { SelectionKey key = keys.next(); // 移除当前处理的SelectionKey keys.remove(); if (key.isAcceptable()) { // 处理连接请求 handleAccept(key); } if (key.isReadable()) { // 处理读数据请求 handleRead(key); } } } } private void handleAccept(SelectionKey key) throws IOException { ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel(); // 监听到ServerSocketChannel连接事件,获取到连接的客户端 SocketChannel clientChannel = serverChannel.accept(); clientChannel.configureBlocking(false); // 将clientChannel注册到Selector上,并监听读事件,当操作系统读缓冲区有数据可读时就绪(该客户端的)。 clientChannel.register(selector, SelectionKey.OP_READ); System.out.println("Client connected: " + clientChannel.getRemoteAddress()); } private void handleRead(SelectionKey key) throws IOException { SocketChannel clientChannel = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); int bytesRead = clientChannel.read(buffer); if (bytesRead == -1) { // 客户端断开连接 key.cancel(); clientChannel.close(); System.out.println("Client disconnected "); return; } byte[] data = new byte[bytesRead]; buffer.flip(); buffer.get(data); String message = new String(data).trim(); System.out.println("Received message from client: " + message); // 回复客户端 String response = "Server response: " + message; ByteBuffer responseBuffer = ByteBuffer.wrap(response.getBytes()); clientChannel.write(responseBuffer); } } 客户端代码: import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Scanner; public class NIOClient { private Selector selector; private SocketChannel socketChannel; public static void main(String[] args) { NIOClient client = new NIOClient(); new Thread(() -> client.doConnect("localhost", 8888)).start(); Scanner scanner = new Scanner(System.in); while (true) { String message = scanner.nextLine(); if ("bye".equals(message)) { // 如果发送的消息是"bye",则关闭连接并退出循环 client.doDisConnect(); break; } client.sendMsg(message); } } private void doDisConnect() { try { socketChannel.close(); } catch (IOException e) { e.printStackTrace(); } } private void sendMsg(String message) { // 发送消息到服务器 ByteBuffer buffer = ByteBuffer.wrap(message.getBytes()); try { socketChannel.write(buffer); } catch (IOException e) { e.printStackTrace(); } } private void doConnect(String host, int port) { try { selector = Selector.open(); // 创建SocketChannel并连接服务器 socketChannel = SocketChannel.open(); socketChannel.configureBlocking(false); socketChannel.connect(new InetSocketAddress(host, port)); // 等待连接完成 while (!socketChannel.finishConnect()) { // 连接未完成,可以做一些其他的事情 } socketChannel.register(selector, SelectionKey.OP_READ); System.out.println("连接成功!"); while (true) { // 等待事件触发,阻塞 | selectNow():非阻塞,立刻返回。 selector.select(); Iterator<SelectionKey> keys = selector.selectedKeys().iterator(); while (keys.hasNext()) { SelectionKey key = keys.next(); // 移除当前处理的SelectionKey keys.remove(); if (key.isReadable()) { // 处理读数据请求 handleRead(key); } } } } catch (IOException e) { System.out.println("连接失败!!!"); e.printStackTrace(); } } private void handleRead(SelectionKey key) throws IOException { SocketChannel clientChannel = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); int bytesRead = clientChannel.read(buffer); if (bytesRead == -1) { // 释放资源 key.cancel(); clientChannel.close(); return; } byte[] data = new byte[bytesRead]; buffer.flip(); buffer.get(data); String message = new String(data).trim(); System.out.println("Received message from server: " + message); } } 总结 通过本文介绍,我们可以了解各个IO模型原理,并且对很多概念有了更清晰的认识,比如: 阻塞体现在:用户进程发起系统调用接口后,不论有无数据,是否直接响应结果?如果直接响应就是非阻塞,等待就是阻塞; IO多路复用原理就是单个线程处理多个I/O操作,从而提高系统的效率和性能; 并且通过对IO多路复用的理解快速的入门了Java NIO 编程。 ———————————————— 版权声明:本文为CSDN博主「叫我二蛋」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_28314431/article/details/132047951
-
大家好7月给大家带来codeArts板块的技术干货30篇合集,免去爬楼烦恼,希望可以帮到大家。 1.Nginx反向代理后,web服务器获取真实访问IP的方法【转】 https://bbs.huaweicloud.com/forum/thread-0212126166776222008-1-1.html 2.Eclipse中maven项目报错: org.springframework.web.filter.CharacterEncodingFilter https://bbs.huaweicloud.com/forum/thread-0212126166611645007-1-1.html 3.如何将公司外网数据库部署到客户内网服务器中,以MySQL为例【转】 https://bbs.huaweicloud.com/forum/thread-0218126166402927009-1-1.html 4.Mysql分组查询每组最新的一条数据(三种实现方法)【转】 https://bbs.huaweicloud.com/forum/thread-0212126166137614006-1-1.html 5.服务端回调报错。Cookie 的 domain 传入非法字符 【转】 https://bbs.huaweicloud.com/forum/thread-0284126165918635010-1-1.html 6.Java获取HttpServletRequest request中所有参数的方法【转】 https://bbs.huaweicloud.com/forum/thread-0249126165722109058-1-1.html 7.Java下载文件的几种方式【转】 https://bbs.huaweicloud.com/forum/thread-0284126165496966009-1-1.html 8.Python工程实践之np.loadtxt()读取数据 https://bbs.huaweicloud.com/forum/thread-0284126154810611008-1-1.html 9.python中的extend功能及用法【转】 https://bbs.huaweicloud.com/forum/thread-0218126154771208007-1-1.html 10.Python isalnum()函数的具体使用【转】 https://bbs.huaweicloud.com/forum/thread-0227126154735209007-1-1.html 11.Python endswith()函数的具体使用【转】 https://bbs.huaweicloud.com/forum/thread-0283126154645130002-1-1.html 12.Python Requests使用Cookie的几种方式详解【转】 https://bbs.huaweicloud.com/forum/thread-0205126154329369004-1-1.html 13.Python大批量写入数据(百万级别)的方法【转】 https://bbs.huaweicloud.com/forum/thread-0227126154192353006-1-1.html 14.python自动化神器pyautogui使用步骤【转】 https://bbs.huaweicloud.com/forum/thread-0249126153906646056-1-1.html 15.Python 获取图片GPS等信息锁定图片拍摄地点、拍摄时间(实例代码)【转】 https://bbs.huaweicloud.com/forum/thread-0212126153709428005-1-1.html 16.python绘制ROC曲线的示例代码【转】 https://bbs.huaweicloud.com/forum/thread-0284126153513019006-1-1.html 17.Python中map函数的技巧分享【转】 https://bbs.huaweicloud.com/forum/thread-0227126153401950004-1-1.html 18.Python列表pop()函数使用实例详解【转】 https://bbs.huaweicloud.com/forum/thread-0222126153273615007-1-1.html 19.使用Pandas计算系统客户名称的相似度【转】 https://bbs.huaweicloud.com/forum/thread-0283126152370390001-1-1.html 20.Python字典get()函数使用详解【转】 https://bbs.huaweicloud.com/forum/thread-0212126152189966004-1-1.html 21.Python集合add()函数使用详解【转】 https://bbs.huaweicloud.com/forum/thread-0218126152083399006-1-1.html 22.python中Scikit-learn库的高级特性和实践分享【转】 https://bbs.huaweicloud.com/forum/thread-0218126152004011005-1-1.html 23.MapUtils工具类【转】 https://bbs.huaweicloud.com/forum/thread-0222125918123973044-1-1.html 24.HTTP响应的状态码415解决【转】 https://bbs.huaweicloud.com/forum/thread-0283125917982618029-1-1.html 25.js保留两位小数方法总结【转】 https://bbs.huaweicloud.com/forum/thread-0222125917923319043-1-1.html 26.聊聊springboot项目引用第三平台私有jar踩到的坑【转】 https://bbs.huaweicloud.com/forum/thread-0284125917178124048-1-1.html 27.如何利用mysql5.7提供的虚拟列来提高查询效率【转】 https://bbs.huaweicloud.com/forum/thread-0284125916421713047-1-1.html 28.怎样修改检查约束条件 https://bbs.huaweicloud.com/forum/thread-0284125913342309042-1-1.html 29.【MySQL新手入门系列五】:MySQL的高级特性简介及MySQL的安全简介-转载 https://bbs.huaweicloud.com/forum/thread-0284125377030598012-1-1.html 30.【SQL应知应会】行列转换(二)• MySQL版-转载 https://bbs.huaweicloud.com/forum/thread-0249125376948578009-1-1.html
-
1、报错信息java.lang.IllegalArgumentException: An invalid domain [.javaex.cn] was specified for this cookie at org.apache.tomcat.util.http.Rfc6265CookieProcessor.validateDomain(Rfc6265CookieProcessor.java:203) at org.apache.tomcat.util.http.Rfc6265CookieProcessor.generateHeader(Rfc6265CookieProcessor.java:145) at org.apache.catalina.connector.Response.generateCookieString(Response.java:1019) at org.apache.catalina.connector.Response.addCookie(Response.java:967) at org.apache.catalina.connector.ResponseFacade.addCookie(ResponseFacade.java:386) at com.pactera.sso.client.utils.CookiesUtil.set(CookiesUtil.java:348) at com.pactera.sso.client.utils.CookiesUtil.setFilterCookies(CookiesUtil.java:299)2、报错原因tomcat 升级到 8.5 版本 使用了 Rfc6265CookieProcessor 就出现了 cookie domain 3、解决方案:重写 context.setCookieProcessor(new LegacyCookieProcessor());3.1、springboot 1.x@Configurationpublic class WebServletContextConfig { @Bean public EmbeddedServletContainerCustomizer cookieProcessorCustomizer() { return new EmbeddedServletContainerCustomizer() { @Override public void customize(ConfigurableEmbeddedServletContainer container) { if (container instanceof TomcatEmbeddedServletContainerFactory) { ((TomcatEmbeddedServletContainerFactory) container) .addContextCustomizers(new TomcatContextCustomizer() { @Override public void customize(Context context) { context.setCookieProcessor(new LegacyCookieProcessor()); } }); } } }; } }3.2、springboot 2.x@Configurationpublic class WebServletContextConfig { @Bean public WebServerFactoryCustomizer cookieProcessorCustomizer() { return new WebServerFactoryCustomizer() { @Override public void customize(WebServerFactory container) { if (container instanceof TomcatServletWebServerFactory) { ((TomcatServletWebServerFactory) container) .addContextCustomizers(new TomcatContextCustomizer() { @Override public void customize(Context context) { context.setCookieProcessor(new LegacyCookieProcessor()); } }); } } }; }}转载自https://blog.javaex.cn/article/detail/3856
-
获取request中单个参数String value = request.getParameter(name);获取request中所有的参数方法1Enumeration<String> param = request.getParameterNames();while (param.hasMoreElements()) { String key = param.nextElement(); String value = request.getParameter(key); System.out.println(key + ":" + value);}方法2Map map = request.getParameterMap();Set keSet = map.entrySet();for (Iterator iterator = keSet.iterator(); iterator.hasNext();) { Map.Entry me = (Map.Entry) iterator.next(); Object ok = me.getKey(); Object ov = me.getValue(); String[] value = new String[1]; if (ov instanceof String[]) { value = (String[]) ov; } else { value[0] = ov.toString(); } for (int k=0; k<value.length; k++) { System.out.println(ok + "=" + value[k]); }}转载自https://blog.javaex.cn/article/detail/3866
-
1. 以流的方式下载public HttpServletResponse download(String path, HttpServletResponse response) { try { // path是指欲下载的文件的路径。 File file = new File(path); // 取得文件名。 String filename = file.getName(); // 取得文件的后缀名。 String ext = filename.substring(filename.lastIndexOf(".") + 1).toUpperCase(); // 以流的形式下载文件。 InputStream fis = new BufferedInputStream(new FileInputStream(path)); byte[] buffer = new byte[fis.available()]; fis.read(buffer); fis.close(); // 清空response response.reset(); // 设置response的Header response.addHeader("Content-Disposition", "attachment;filename=" + new String(filename.getBytes())); response.addHeader("Content-Length", "" + file.length()); OutputStream toClient = new BufferedOutputStream(response.getOutputStream()); response.setContentType("application/octet-stream"); toClient.write(buffer); toClient.flush(); toClient.close(); } catch (IOException ex) { ex.printStackTrace(); } return response;}2. 下载本地文件public void downloadLocal(HttpServletResponse response) throws FileNotFoundException { // 下载本地文件 String fileName = "Operator.doc".toString(); // 文件的默认保存名 // 读到流中 InputStream inStream = new FileInputStream("c:/Operator.doc");// 文件的存放路径 // 设置输出的格式 response.reset(); response.setContentType("bin"); response.addHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // 循环取出流中的数据 byte[] b = new byte[100]; int len; try { while ((len = inStream.read(b)) > 0) response.getOutputStream().write(b, 0, len); inStream.close(); } catch (IOException e) { e.printStackTrace(); } }3. 下载网络文件public void downloadNet(HttpServletResponse response) throws MalformedURLException { // 下载网络文件 int bytesum = 0; int byteread = 0; URL url = new URL("windine.blogdriver.com/logo.gif"); try { URLConnection conn = url.openConnection(); InputStream inStream = conn.getInputStream(); FileOutputStream fs = new FileOutputStream("c:/abc.gif"); byte[] buffer = new byte[1204]; int length; while ((byteread = inStream.read(buffer)) != -1) { bytesum += byteread; System.out.println(bytesum); fs.write(buffer, 0, byteread); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }public void downLoadFile(HttpServletResponse response, String httpUrl) { ServletOutputStream out = null; try { //与服务器建立连接 URL url = new URL(httpUrl); URLConnection conn = url.openConnection(); InputStream inputStream = conn.getInputStream(); try { //1.设置文件ContentType类型,这样设置,会自动判断下载文件类型 response.setContentType("multipart/form-data"); } catch (Exception e){ e.printStackTrace(); } out = response.getOutputStream(); // 读取文件流 int len = 0; byte[] buffer = new byte[1024 * 10]; while ((len = inputStream.read(buffer)) != -1) { out.write(buffer, 0, len); } out.flush(); out.close(); inputStream.close(); } catch (Exception e){ e.printStackTrace(); } }4. 在线打开的方式public void downLoad(String filePath, HttpServletResponse response, boolean isOnLine) throws Exception { File f = new File(filePath); if (!f.exists()) { response.sendError(404, "File not found!"); return; } BufferedInputStream br = new BufferedInputStream(new FileInputStream(f)); byte[] buf = new byte[1024]; int len = 0; response.reset(); // 非常重要 if (isOnLine) { // 在线打开方式 URL u = new URL("file:///" + filePath); response.setContentType(u.openConnection().getContentType()); response.setHeader("Content-Disposition", "inline; filename=" + f.getName()); // 文件名应该编码成UTF-8 } else { // 纯下载方式 response.setContentType("application/x-msdownload"); response.setHeader("Content-Disposition", "attachment; filename=" + f.getName()); } OutputStream out = response.getOutputStream(); while ((len = br.read(buf)) > 0) out.write(buf, 0, len); br.close(); out.close(); }5. 将文件转换成base64 /** * 将文件转为base64 */ public static String getBase64FromFile(File file) throws IOException { FileInputStream in = null; ByteArrayOutputStream out = null; try { in = new FileInputStream(file); out = new ByteArrayOutputStream(); int read = 0; byte[] buffer = new byte[1024]; while ((read = in.read(buffer, 0, 1024)) != -1) { out.write(buffer, 0, read); } return Base64.getEncoder().encodeToString(out.toByteArray()); } catch (IOException e) { throw e; } finally { if (in != null) { in.close(); } if (out != null){ out.close(); } } }6. 将MultipartFile转换为File /** * 将MultipartFile转换为File */ public static File MultipartFileToFile(MultipartFile multiFile) throws IOException { String fileName = multiFile.getOriginalFilename(); String prefix = fileName.substring(fileName.lastIndexOf(".")); InputStream in = null; OutputStream out = null; try { File file = File.createTempFile(fileName, prefix); out = new FileOutputStream(file); in = multiFile.getInputStream(); int read = 0; byte[] buffer = new byte[1024]; while ((read = in.read(buffer, 0, 1024)) != -1) { out.write(buffer, 0, read); } return file; } catch (Exception e) { throw e; }finally { if (in != null){ in.close(); } if (out != null){ out.close(); } } }转载自https://blog.javaex.cn/article/detail/542270361309343744
-
Java中对集合的遍历有多种形式迭代器Iterator、游标Cursor。System.out.println("iterator..."); Iterator iterator = redisTemplate.opsForZSet().scan(key, ScanOptions.scanOptions().build()); while (iterator.hasNext()){ ZSetOperations.TypedTuple<String> tuple = (ZSetOperations.TypedTuple<String>) iterator.next(); System.out.println(tuple.getValue()); }Cursor cursor = redisTemplate.opsForZSet().scan(key, ScanOptions.scanOptions().build()); while (cursor.hasNext()){ ZSetOperations.TypedTuple<String> tuple = (ZSetOperations.TypedTuple<String>) cursor.next(); System.out.println(tuple.getValue()); }
-
前言 有时候我们在导入程序之后,系统会给出错误提示:Java:程序包xxxx不存在,现在我这里有一招,就是使用IDEA自动导入Java程序包,这也是IDEA的一大优点。 内容 当出现如题所示的错误时候,不用着急,这是因为配置Java的程序包这块出现了错误,同时可能你还没有设置让IDEA自动加载Jar包,才会报出这种错误的。解决方式如下: File—>Setting—>Build,Execution,Deployment—>Maven—>Importing 然后如下图配置: 如上图配置好之后,点击apply,然后点击OK,这个时候刷新一下IDEA就可以看到,Jar包不足然后报出Java:程序包xxxx不存在的错误就会自动消失,并且在左边栏External Libraries中会出现很多jar包,如下图: 总结 OK,问题解决,就是这么简单,这既是IDEA的魅力所在,方便jar包的下载,也方便大家的编程。 end ———————————————— 原文链接:https://blog.csdn.net/Luojun13Class/article/details/81483151
-
项目中引入的本地的jar包,在自己本地运行也是没有任何问题,但是在打包发布的时候就会报错找不到自己引入的文件 这是本地lib下的jar包 在打包运行后报错NoClassDefFoundError,找不到文件: 本地启动的时候自己本地项目知道根据你导入jar的路径去加载jar包,所以会没问题。但是打包之后,这个jar包是不会被打包进去的,导致报错。所以,我们想要正常运行,就需要把自己的jar包打包到项目中; 1.首先在pom中添加本地jar包的依赖 其中groupId artifactId 和version自己随便写。scope作用于定义为system,systemPath定义为jar包在项目中的路径(${basedir}就是项目的根目录): 2.光做这些还是不够的,这样只能保证我们在本地能够正常运行,打包还是不会被打包进去,所以说我们需要在引入的springboot的maven插件中告诉maven,将我们的刚刚引入的作用域为system的本地jar也打包进来 ———————————————— 原文链接:https://blog.csdn.net/Handsome__c/article/details/121776225
-
在很多地方看见add as library,但就是不知道这是干啥的。上网搜,发现并没有直接介绍这个的,经过一段时间的信息搜集,总结出以下几个知识点,希望给一些和我一样的小小白经验学习,希望每个小白都能成为自己心中向往的大佬。 一. add as library是什么? add as library是IntelliJ IDEA里面的一个功能,当然eclipse里面也有,多用于java开发和Android开发。 二. add as library有什么用? 多用于将将外部jar(lib / * .jar)添加到IntelliJ IDEA项目。 通俗点说,就是你做项目的时候需要一些外部jar包,比如我们用java连接数据库时用到了阿里巴巴公司的数据库连接池Druid,我们就需要导入Druid相关的jar包和配置文件,这些jar包和配置文件就属于外部jar包和配置文件。一般我们都是在项目路径下单独再建一个libs目录来存放这些东西。但直接复制粘贴到这个lib文件夹里面是不够的,我们还要把这些文件和jar包 add as library 来保证各种依赖关系。 三. add as library如何调出? 点击你要add as library的文件或jar包,右键选择add as library即可。 当然还有其他当如jar包的方法,还比较常用的是点File->Project Structure->Moudules->Dependenceies-> 右侧的 + 即可添加jar包 ———————————————— 原文链接:https://blog.csdn.net/doubleguy/article/details/104947149
上滑加载中
推荐直播
-
华为云码道Agent集成与鸿蒙实战2026/08/11 周二 19:00-21:00
王一男-华为云码道产品规划专家;李炎-华为云码道产品专家;彭江敏-华为云鸿蒙端云一体化开发专家
本次直播带你解读华为云码道7月份产品新特性、新功能。更有专家演示码道Agent Space × 钉钉机器集成实战,从0到1打通消息通道;码道鸿蒙端云一体化实战,快速搭建员工签到系统。
回顾中 -
华为云开发者AI素养直播课·第五期2026/09/04 周五 16:00-18:00
林华鼎-华为云AI开发者运营负责人;蒋春阳-华为云AI开发者案例开发专家
本期直播内容: AI工具体验营 · 第5-8课连讲。Agent-Team 多智能体协作完成毕业设计实践
回顾中 -
华为云开发者AI素养ClassRoom·第六期2026/09/08 周二 19:00-20:00
樊渊-2026华为软件挑战赛冠军
高手来了:看软挑高手解析二维排样问题—从工业难题到算法突破
回顾中
热门标签