• [互动交流] CodeArts IDE for java 安装完成异常
    不能登录不能创建项目java 文件不能执行
  • [技术干货] Java Stream流详解-转载
     学习目标 看玩这篇将会: 1.了解stream流 2.学会使用stream流 3.掌握stream流的使用场景 每个方法通过举例子的形式学习!  Stream API主要提供了两种类型的操作:中间操作 和 终止操作。  中间操作 中间操作是返回一个新的流,并在返回的流中包含所有之前的操作结果。它们总是延迟计算,这意味着它们只会在终止操作时执行,这样可以最大限度地优化资源使用。  Filter(过滤)  filter()方法接受一个谓词(一个返回boolean值的函数),并返回一个流,其中仅包含通过该谓词的元素。 建一个数组,帅选出长度大于4的元素 eg: public class Main {     public static void main(String[] args) {         List<String> names = Arrays.asList("Alex", "Brian", "Charles", "David");         List<String> collect = names.stream().filter(item -> item.length() > 4).collect(Collectors.toList());         System.out.println(collect);     } } 这段代码创建了一个包含4个字符串的List集合,然后使用Stream()方法将其转化为一个Stream流。接下来使用filter()方法筛选出长度大于4的字符串,返回一个新的包含符合条件元素的Stream流collect。最后使用collect()方法将筛选后的结果转换成一个List集合。 使用Stream流中的filter()方法可以对流中的元素进行筛选过滤。在这段代码中,lambda表达式item -> item.length() > 4指定了筛选判断条件,即只保留长度大于4的字符串。collect(Collectors.toList())则将筛选后的结果转换成一个List集合返回。 通过这段代码,开发人员可以对包含字符串的数据进行快速的筛选和过滤,并且返回结果是一个新的可操作的集合,方便后续进行处理或展示。  Map(转换)  map()方法可将一个流的元素转换为另一个流。它接受一个函数,该函数映射流中的每个元素到另一个元素。 public class Main {     public static void main(String[] args) {         List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);         List<Integer> collect = numbers.stream().map(n ->         {             n = n * 2;             return n;         }).collect(Collectors.toList());         for (Integer integer : collect) {             System.out.println("integer = " + integer);         }     } } 这段代码使用了 Java 8 中的 Stream API 实现了一种对数字列表中的每个元素进行乘以 2 的操作,并将操作后的结果保存到新的列表中。  首先创建了一个包含数字 1~5 的列表。 然后利用 stream() 方法将列表转换成 Stream 对象。 接下来调用 map() 方法对每个元素进行操作,这里使用了 lambda 表达式对每个元素进行了乘以 2 的操作。 最后调用 collect() 方法将结果收集起来,并转换成 List。   转换为Map public class Main {     public static void main(String[] args) {         List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);         Map<Byte, Integer> collect = numbers.stream().collect(Collectors.toMap(Integer::byteValue, item -> item*2, (val1, val2) -> val2));         for (Map.Entry<Byte, Integer> byteIntegerEntry : collect.entrySet()) {             Byte key = byteIntegerEntry.getKey();             System.out.println("key = " + key);             System.out.println("Value = " + byteIntegerEntry.getValue());         }     } }  这段代码使用了 Java 8 中的 Stream API 实现了一种将数字列表转换成字节-整数键值对的方式。具体来说,代码中: 首先创建了一个包含数字 1~5 的列表。 然后利用 stream() 方法将列表转换成 Stream 对象。 接下来调用 collect(Collectors.toMap(…)) 方法将 Stream 转换成 Map<Byte, Integer>。 在 toMap 方法中,我们以每个整数的字节值为键,该整数乘以 2 为值,当遇到重复的键时取最后一个值。(这里实际上可以用任何能区分不同键的方式作为第一个参数,而不一定是 Integer::byteValue) 最后,在 for 循环中遍历了这个 Map 并打印出每个键值对的内容。 总的来说,通过 Stream API 可以方便地实现对集合数据进行筛选、映射、分组、统计等各种操作,相对于传统的循环遍历方式更为简洁、可读性更高,可以提高开发效率。  Sorted(排序)  sorted()方法可对流进行排序。它可以接受一个Comparator参数,也可以使用自然排序Ordering.natural()。默认排序是按升序排序。 public class Main {     public static void main(String[] args) {         int[] numbers = { 5, 2, 8, 3, 7 };         int[] sortedNumbers = Arrays.stream(numbers).sorted().toArray();         System.out.println(Arrays.toString(sortedNumbers));     } } 这段代码创建了一个包含整数的数组numbers,然后使用Arrays.stream()方法将其转化为一个IntStream流。接下来使用sorted()方法对流中的元素进行排序操作,返回一个新的排序后的IntStream流。最后,使用toArray()方法将排序后的结果转换为一个新的int类型数组sortedNumbers,并使用Arrays.toString()方法将结果输出到控制台。 使用Stream流可以简化代码,提高效率和可读性,方便开发人员对数据进行快速处理和排序。  Distinct(去重)  distinct()方法从流中返回所有不同的元素。在内部,它使用equals()方法来比较元素是否相同。因此,我们需要确保equals()方法已正确实现。 public class Main {     public static void main(String[] args) {         List<Integer> numbers = Arrays.asList(1, 2, 3, 2, 1);         List<Integer> collect = numbers.stream().distinct().collect(Collectors.toList());         System.out.println(collect);     } } 这段代码创建了一个包含整数的List集合numbers,其中包含了若干个重复的整数。接下来使用Stream()方法将其转化为一个Stream流。使用distinct()方法对流中的元素进行去重操作,返回一个新的不包含重复元素的Stream流collect。最后使用collect()方法将去重后的结果转换成一个List集合,并使用System.out.println()方法输出到控制台。 使用Stream流中的distinct()方法可以快速地对集合中的重复元素进行去重处理。在这段代码中,集合中的元素都是整数,使用distinct()方法去除了所有重复的整数,返回一个新的元素不重复且顺序不变的List集合。 运行该示例代码,输出结果为:[1, 2, 3],即去重后的不包含重复元素的整数List集合。  Limit(限制)  limit()方法可以将流限制为指定的元素数。 public class Main {     public static void main(String[] args) {         List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);         List<Integer> collect = numbers.stream().limit(3).collect(Collectors.toList());         System.out.println(collect);     } } 这段代码创建了一个包含整数的List集合numbers,其中包含了5个整数。接下来使用Stream()方法将其转化为一个Stream流。使用limit()方法对流中的元素进行限制操作,仅保留前3个元素,返回一个新的只包含前3个元素的Stream流collect。最后使用collect()方法将限制操作后的结果转化为一个新的List集合,并使用System.out.println()方法输出到控制台。 使用Stream流中的limit()方法可以快速地对集合中的元素进行截取操作,仅保留前N个元素。在这段代码中,集合中包含了5个整数,使用limit(3)方法仅保留了前3个整数,返回一个新的只包含前3个元素的List集合。 运行该示例代码,输出结果为:[1, 2, 3],即仅包含前3个元素的整数List集合。  Skip(跳过) public class Main {     public static void main(String[] args) {         List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);         List<Integer> collect = numbers.stream().skip(2).collect(Collectors.toList());         System.out.println(collect);     } }  skip()方法可跳过前N个元素。  这段代码创建了一个包含整数的List集合numbers,其中包含了5个整数。接下来使用Stream()方法将其转化为一个Stream流。使用skip()方法对流中的元素进行跳过操作,跳过前2个元素,返回一个新的不包含前2个元素的Stream流collect。最后使用collect()方法将跳过操作后的结果转化为一个新的List集合,并使用System.out.println()方法输出到控制台。 使用Stream流中的skip()方法可以快速地对集合中的元素进行跳过操作,跳过前N个元素。在这段代码中,集合中包含了5个整数,使用skip(2)方法跳过前2个元素,返回一个新的不包含前2个元素的List集合。 运行该示例代码,输出结果为:[3, 4, 5],即不包含前2个元素的整数List集合。  Peek(展示)  peek()方法可以用于在Stream流中获取元素同时执行一些操作,如打印、调试、观察等。通常会与其他的方法联合使用。 public class Main {     public static void main(String[] args) {         List<String> names = Arrays.asList("Alex", "Brian", "Charles", "David");         List<String> filteredNames = names.stream()                 .peek(System.out::println)                 .filter(name -> name.startsWith("C"))                 .peek(name -> System.out.println("Filtered value: " + name))                 .collect(Collectors.toList());         System.out.println("-----------------------------------------------------------------");         System.out.println(filteredNames);     } } 代码中创建了一个列表 names 包含四个字符串元素,然后使用流式操作处理这个列表。首先使用 peek() 方法将每个元素打印到控制台,然后使用 filter() 方法过滤掉不符合条件的元素,即不以字母 C 开头的字符串。接下来再次使用 peek() 方法将符合条件的字符串打印到控制台,以便验证过滤操作的效果。最后使用 collect() 方法将符合条件的字符串收集到一个新的列表 filteredNames 中,并输出该列表。 注意到,控制台上先输出了列表中的四个字符串,但只有以字母 C 开头的字符串 Charles 才符合筛选条件,因此仅仅 Charles 被保存在了 filteredNames 列表中。第二个 peek() 方法也被用来打印筛选出的元素 Charles。  终止操作 终止操作返回一个结果或副作用(例如:显示控制台输出),并将流关闭。  forEach(循环)  forEach()方法可将给定的方法应用于流中的每个元素。该方法是一种消费流的方式,不会返回值。 public class Main {     public static void main(String[] args) {         List<String> names = Arrays.asList("Alex", "Brian", "Charles", "David");         names.stream().forEach(System.out::println);     } } 这段代码创建了一个包含四个字符串元素的列表 names,使用流式操作将每个元素打印到控制台。具体来说,它使用 forEach() 方法遍历列表中的所有元素,并对每个元素执行打印操作。 其中,四个字符串元素按顺序打印到了控制台上。注意到,使用 forEach() 方法时并没有指定任何条件或谓词,因此它会对列表中的所有元素进行操作,以达到遍历、打印等目的。  Collect(收集)  collect()方法可以将流中的元素收集到一个集合中。一般与其他方法配合使用。 public class Main {     public static void main(String[] args) {         List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);         List<Integer> evenNumbers = numbers.stream().filter(n -> n % 2 == 0).collect(Collectors.toList());         System.out.println(evenNumbers);     } } 这段代码创建了一个包含整数的列表 numbers,使用流式操作筛选出所有偶数,然后将它们收集到一个新的列表 evenNumbers 中,并打印输出。具体来说,它使用了 filter() 方法过滤掉所有奇数元素,只保留所有偶数元素,并使用 collect() 方法将它们收集到一个新的列表 evenNumbers 中。 注意到,只有偶数元素被保留在了新列表 evenNumbers 中,而奇数元素全部被过滤掉了。而且,在筛选偶数元素时,使用了 lambda 表达式 n -> n % 2 == 0,其中 % 表示取模操作,判断当前数是否为偶数。如果 n % 2 的结果是 0,就把 n 这个数保留下来,否则就过滤掉。  Count(计数)  count()方法可以返回流中的元素数。 public class Main {     public static void main(String[] args) {         List<String> names = Arrays.asList("Alex", "Brian", "Charles", "David");         long count = names.stream().count();         System.out.println(count);     } } 这段代码创建了一个包含四个字符串元素的列表 names,使用流式操作计算出它包含的元素数量(即列表大小),并将该数量打印到控制台。具体来说,它使用了 count() 方法统计列表中元素的个数。 注意到,count() 方法返回的是一个 long 类型的值,表示列表中元素的个数。因为列表 names 包含了四个元素,所以 count() 方法返回值为 4,最终被打印输出到了控制台。  Reduce(聚合)  reduce()方法可以将流元素聚合为单个结果。它接受一个BinaryOperator参数作为累加器。 public class Main {     public static void main(String[] args) {         List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);         Optional<Integer> sum = numbers.stream().reduce((a, b) -> a + b);         System.out.println(sum);     } } 这段代码创建了一个包含整数的列表 numbers,使用流式操作将它们累加起来得到总和,并将结果打印输出。具体来说,它使用了 reduce() 方法对列表中的所有元素进行累加操作。reduce() 方法接收一个 BinaryOperator 函数作为参数,用于指定如何处理相邻的两个元素并返回一个新的结果值。 注意到,reduce() 方法返回的是一个 Optional 类型的值,表示结果可能存在也可能不存在(例如当列表为空时)。由于列表 numbers 包含 1 到 5 共五个元素,因此 reduce() 方法的操作过程如下: 1 + 2 = 3 3 + 3 = 6 6 + 4 = 10 10 + 5 = 15 最终得到的结果 15 被包装成 Optional 类型的对象并打印输出到控制台。  AnyMatch(任意匹配)  anyMatch()方法如果至少有一个元素与给定的谓词匹配,则返回true。 public class Main {     public static void main(String[] args) {         List<String> names = Arrays.asList("Alex", "Brian", "Charles", "David");         boolean anyStartsWithB = names.stream().anyMatch(name -> name.startsWith("B"));         System.out.println(anyStartsWithB);     } } 这段代码创建了一个包含四个字符串元素的列表 names,使用流式操作检查其中是否有任意一个元素以字母 “B” 开头,并将检查结果(布尔值)打印输出。具体来说,它使用了 anyMatch() 方法匹配列表中的所有元素,并依次对每个元素执行指定的谓词操作(这里是以 “B” 开头),只要有一个元素符合条件,就返回 true,否则返回 false。 注意到,列表 names 中包含了一个以字母 “B” 开头的元素 “Brian”,因此 anyMatch() 方法返回 true,最终被打印输出到了控制台。  AllMatch(全部匹配)  allMatch()方法如果所有元素都与给定谓词匹配,则返回true。 public class Main {     public static void main(String[] args) {         List<String> names = Arrays.asList("Alex", "Brian", "Charles", "David");         boolean allStartsWithB = names.stream().allMatch(name -> name.startsWith("B"));         System.out.println(allStartsWithB);     } } 这段代码创建了一个包含四个字符串元素的列表 names,使用流式操作检查其中是否所有元素都以字母 “B” 开头,并将检查结果(布尔值)打印输出。具体来说,它使用了 allMatch() 方法匹配列表中的所有元素,并依次对每个元素执行指定的谓词操作(这里是以 “B” 开头),只有当所有元素都符合条件时,才返回 true,否则返回 false。 注意到,虽然列表 names 中包含了一个以字母 “B” 开头的元素 “Brian”,但是它不是所有元素都以 “B” 开头,因此 allMatch() 方法返回 false,最终被打印输出到了控制台。  NoneMatch(无匹配)  noneMatch()方法,如果没有任何元素与给定谓词匹配,则返回true。 public class Main {     public static void main(String[] args) {         List<String> names = Arrays.asList("Alex", "Brian", "Charles", "David");         boolean noneStartsWithB = names.stream().noneMatch(name -> name.startsWith("E"));         System.out.println(noneStartsWithB);     } } 这段代码创建了一个包含四个字符串元素的列表 names,使用流式操作检查其中是否没有任意一个元素以字母 “E” 开头,并将检查结果(布尔值)打印输出。具体来说,它使用了 noneMatch() 方法匹配列表中的所有元素,并依次对每个元素执行指定的谓词操作(这里是以 “E” 开头),只有当所有元素都不符合条件时,才返回 true,否则返回 false。 注意到,列表 names 中不包含任何一个以字母 “E” 开头的元素,因此 noneMatch() 方法返回 true,最终被打印输出到了控制台。  以上就是Java Stream流的基础知识和操作方式。Stream API可以使Java程序员编写出高效,干净,紧凑的代码,使得代码易于阅读和维护。建议初学者多加练习,掌握基本操作。中级和高级程序员则需要深入研究Stream API的实现原理和运作机制,进一步提高代码的质量和效率。  使用Stream流的优缺点: 优点: Stream流可以帮助简化代码,减少样板代码,从而提高代码质量和可读性。 Stream流充分利用了现代多核处理器的优势,在多线程场景下可以获得更好的性能表现。 Stream流提供了丰富的操作方法,可以轻松地处理各种集合和数组的数据,从而降低程序员的编码难度和心理负担。 Stream流可以帮助开发人员更容易地写出函数式风格的代码,使代码更加健壮可维护。  缺点: Stream流有时候会让代码变得复杂,反而降低了可读性,因此在某些简单的情况下可能不需要使用Stream流。 Stream流可能会对程序的性能产生一定影响,尤其是在大型数据集或者复杂的业务逻辑的情况下,程序员需要根据具体的情况进行测试和分析,选择最优解。 Stream流可能会造成资源浪费,例如创建中间操作的临时对象,这些对象将占用存储空间,导致效率降低。 在实际开发中,应该根据具体情况来决定是否使用Stream流。一般建议在数据集较大或者需要进行复杂的数据处理操作时使用Stream流,而在一些简单的操作中则可以直接使用循环和传统的集合操作方法。此外,如果代码可读性受到影响,也可以考虑使用传统的集合操作方法来实现代码。 ———————————————— 版权声明:本文为CSDN博主「小尘要自信」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_54796785/article/details/131122376 
  • [技术干货] 【JavaScript】JS能力测试题:数组扁平化 | 判断质数 | 获取字符串的长度-转载
     数组扁平化 题目描述 请补全JavaScript代码,要求将数组参数中的多维数组扩展为一维数组并返回该数组。 注意:  数组参数中仅包含数组类型和数字类型 题解 递归  遍历数组 并 判断遍历的当前元素的数据类型,分为以下两种情况:  类型为数值:添加该元素到输出数组中。 类型为数组:重复上诉操作。   const _flatten = arr => {          return arr.reduce((target, item) => {     return target.concat(Array.isArray(item) ? _flatten(item) : item);  }, []) } 其中参数 target为输出数组,item为遍历的当前元素  字符串法  a. 利用toString()方法,将数组转为字符串,在使用split()方法,以","为分割变为数组。  b. 利用join()方法将数组转为字符串且用","分割,在利用上诉split()方法以","为分割变为数组。  const _flatten = arr => {     return arr.toString().split(",") } const _flatten = arr => {     return arr.join(",").split(",") } 简单粗暴法  思路:  使用while循环检测数组,只要数组中存在元素的数据类型为数组,就利用扩展运算符将该元素的可遍历属性追加到输出数组中展开直到数组元素的数据类型均为数值为止。  const _flatten = arr => {     while(arr.some(item=>{Array.isArray(item)})){         arr = [].concat(...arr)     }     return arr } some() :测试数组中是否至少有一个元素通过了由提供的函数实现的测试,在本题中用于测试数组中是否存在元素的数据类型为数组。  扩展运算符(…):取出参数对象中的所有可遍历属性,拷贝到当前对象之中。在本题中扩展运算符(…)用于取出被检测到元素类型为数组的对象内的所有可遍历属性,并使用concat方法拷贝到输出数组中。  判断质数 题目描述 请补全JavaScript代码,要求在Number对象的原型对象上添加"_isPrime"函数,该函数判断调用的对象是否为一个质数,是则返回true,否则返回false。  题解 质数的定义,不能被除了1和它本身的数字因式分解的数字,对应到数学的概念就是取余为0  双指针  Number.prototype._isPrime = function(){     let j=this-1;     for(let i=2; i<j;i++){                  if(i>=j)break         if(!(this%i) || !(this%j)){             return false         }         j--            }     return true } 平方根  假设Number数值为c, 若a * b = c 且(a<=b)则a,b需满足 a<=Math.sqrt©<=b,因此只用依次让[2,Math.sqrt©]范围内的数值对number进行取余数检验。  Number.prototype._isPrime = function(){          for(let i=2; i <= Math.sqrt(this); i++){         if(!(this%i)){             return false         }     }     return true } 获取字符串的长度 题目描述 如果第二个参数 bUnicode255For1 === true,则所有字符长度为 1 否则如果字符 Unicode 编码 > 255 则长度为 2  示例:  输入:  'hello world, 牛客', false 1 输出:  17 1 题解 function strLength(s, bUnicode255For1) {     if(bUnicode255For1){        return s.length      }else{         let length = 0         for(let i=0; i<s.length; i++){             if(s[i].codePointAt() > 255){                 length += 2             }else{                 length += 1             }         }         return length     } } codePointAt(index)  作用:返回一个非负整数,该整数是从给定索引开始的字符的 Unicode 码位值。  参数:index ———————————————— 版权声明:本文为CSDN博主「阿选不出来」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/m0_63300737/article/details/133319227 
  • [技术干货] 【JAVA】为什么要使用封装以及如何封装-转载
     前言 Java的封装指的是在一个类中将数据和方法进行封装,使其可以保护起来,只能在该类内部访问,而不允许外部直接访问和修改。这是Java面向对象编程的三个基本特性之一,另外两个是继承和多态。在此之前我们已经学习过关于继承的概念,今天我们来学习有关封装的内容。  封装的含义 封装是面向对象编程的一种重要概念,是将数据和对数据的操作封装在一个类中,使得数据对外部的访问受到限制,只能通过类中的公共方法来访问或操作。封装的目的是隐藏类的实现细节,并且保护数据不被随意修改,从而增强了代码的安全性和可维护性。另外,封装也使得代码的扩展和修改更加方便,只需在类内部进行修改而不需要修改其他代码。  通过封装,可以提高类的安全性和可维护性,使得类的实现细节被隐藏,只暴露出有限的接口和功能给外部使用,从而减少了类之间的耦合性。同时,封装还可以实现数据隐藏和数据保护,提高程序的可靠性和可扩展性。  四种访问控制符 Java中,采用访问控制符来控制类中数据成员和方法的访问权限,主要有四种访问控制符:public、private、protected和默认访问控制符(即不写访问控制符)。  public:表示该数据成员或方法可以被其他任何类访问。 private:表示该数据成员或方法只能在当前类内部访问。 protected:表示该数据成员或方法可以在当前类和其子类中访问。 默认访问控制符:表示该数据成员或方法只能在同一个包内的其他类中访问。 封装的两种经典实例 银行账户类 该类的主要属性包括账户名、账号、账户余额等。其中账户余额是一个私有属性,外部无法直接访问。类中提供了一系列操作账户的方法,例如存款、取款、查询余额等。这些方法都能够在保证账户余额正确的情况下,修改账户余额。  public class BankAccount {     private String accountNumber;     private double balance;      public BankAccount(String accountNumber, double balance) {         this.accountNumber = accountNumber;         this.balance = balance;     }      public String getAccountNumber() {         return accountNumber;     }      public double getBalance() {         return balance;     }      public void deposit(double amount) {         balance += amount;     }      public void withdraw(double amount) {         if (balance >= amount) {             balance -= amount;         } else {             System.out.println("Insufficient balance.");         }     } } accountNumber: 表示账户号码的字符串。 balance: 表示账户余额的双精度浮点数。 BankAccount(String accountNumber, double balance): 构造函数,用于创建一个新的银行账户对象。 getAccountNumber(): 返回账户号码的方法。 getBalance(): 返回账户余额的方法。 deposit(double amount): 存款方法,用于向账户中添加资金。 withdraw(double amount): 取款方法,用于从账户中扣除资金。如果账户余额不足,将输出一条错误信息。 学生类 public class Student {     // 属性     private String name;     private int age;     private String gender;     private String id;      // 构造方法     public Student(String name, int age, String gender, String id) {         this.name = name;         this.age = age;         this.gender = gender;         this.id = id;     }      // 方法     public void study() {         System.out.println("学生正在学习");     }      public void showInfo() {         System.out.println("姓名:" + name);         System.out.println("年龄:" + age);         System.out.println("性别:" + gender);         System.out.println("学号:" + id);     }      // Getter和Setter方法     public String getName() {         return name;     }      public void setName(String name) {         this.name = name;     }      public int getAge() {         return age;     }      public void setAge(int age) {         this.age = age;     }      public String getGender() {         return gender;     }      public void setGender(String gender) {         this.gender = gender;     }      public String getId() {         return id;     }      public void setId(String id) {         this.id = id;     } } 属性:学生类具有姓名、年龄、性别和学号这些属性。 构造方法:学生类具有一个带四个参数的构造方法,用于初始化学生对象。 方法:学生类具有学习方法和展示学生信息的方法。 Getter和Setter方法:学生类具有获取和设置属性值的Getter和Setter方法,用于保护属性的私有性。 ———————————————— 版权声明:本文为CSDN博主「许思王」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_73602725/article/details/133498865 
  • [技术干货] Java 超高频常见字符操作【建议收藏】-转载
     前言 为了巩固所学的知识,作者尝试着开始发布一些学习笔记类的博客,方便日后回顾。当然,如果能帮到一些萌新进行新技术的学习那也是极好的。作者菜菜一枚,文章中如果有记录错误,欢迎读者朋友们批评指正。 (博客的参考源码可以在我主页的资源里找到,如果在学习的过程中有什么疑问欢迎大家在评论区向我提出)  1. 字符串拼接 1. 使用 + 操作符:  String str1 = "Hello"; String str2 = ", "; String str3 = "World";  String result = str1 + str2 + str3; 这种方法是最简单的,通过使用 + 操作符可以将多个字符串连接在一起。Java会自动处理字符串拼接并创建一个新的字符串对象。  2. 使用 concat() 方法:  String str1 = "Hello"; String str2 = ", "; String str3 = "World";  String result = str1.concat(str2).concat(str3); concat() 方法用于将一个字符串连接到另一个字符串的末尾。可以连续调用 concat() 方法来连接多个字符串。  3. 使用 StringBuilder 类:  StringBuilder 是一个可变的字符串类,适用于需要频繁修改字符串的情况,因为它不会创建多个中间字符串对象,从而提高了性能。  StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("Hello"); stringBuilder.append(", "); stringBuilder.append("World");  String result = stringBuilder.toString(); 在这种情况下,我们首先创建一个 StringBuilder 对象,然后使用 append() 方法来添加需要拼接的字符串,最后通过 toString() 方法将 StringBuilder 转换为不可变的 String。  4. 使用 StringBuffer 类:  StringBuffer 类与 StringBuilder 类 类似,但是是线程安全的,适用于多线程环境。  StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append("Hello"); stringBuffer.append(", "); stringBuffer.append("World");  String result = stringBuffer.toString(); 与 StringBuilder 一样,我们可以使用 append() 方法来构建最终的字符串,然后通过 toString() 方法获取不可变的 String。  5. 使用 String.join() 方法(Java 8+):  这种方法更加简洁,特别适用于将多个字符串与指定的分隔符连接起来的情况。  String.join()  String str1 = "Hello"; String str2 = ", "; String str3 = "World";  String result = String.join(str2, str1, str3); 2. 字符串查找 1.使用 indexOf() 方法查找子字符串的位置:  indexOf() 方法用于查找一个字符串是否包含另一个子字符串,并返回第一次出现的位置索引。如果未找到,它将返回 -1。  String mainString = "Hello, World!"; String subString = "World"; int position = mainString.indexOf(subString);  if (position != -1) {    System.out.println("子字符串在主字符串中的位置是:" + position); } else {     System.out.println("子字符串未找到!"); } 2.使用 lastIndexOf() 方法查找最后一次出现的位置:  lastIndexOf() 方法与 indexOf() 方法类似,但它返回最后一次出现的位置索引。  String mainString = "Java is a powerful programming language. Java is also fun!"; String subString = "Java"; int position = mainString.lastIndexOf(subString);  if (position != -1) {     System.out.println("最后一次出现的位置是:" + position); } else {     System.out.println("子字符串未找到!"); } 3.使用 contains() 方法检查字符串是否包含子字符串:  contains() 方法用于检查一个字符串是否包含另一个子字符串,它返回一个布尔值。  String mainString = "This is a Java example."; String subString = "Java"; boolean contains = mainString.contains(subString);  if (contains) {     System.out.println("主字符串包含子字符串!"); } else {     System.out.println("主字符串不包含子字符串!"); } 4.使用正则表达式进行高级搜索:  Java 也支持使用正则表达式来进行复杂的字符串搜索。你可以使用 Pattern 和 Matcher 类来实现这一点。这允许你执行更复杂的模式匹配操作。  import java.util.regex.*;  String text = "The quick brown fox jumps over the lazy dog."; String pattern = "fox";  Pattern compiledPattern = Pattern.compile(pattern); Matcher matcher = compiledPattern.matcher(text);  while (matcher.find()) {     System.out.println("找到匹配字符串: " + matcher.group()); } 3. 字符串截取 1.使用 substring() 方法:  substring() 方法用于从一个字符串中截取子串,你可以指定截取的起始位置和结束位置。这个方法有两种形式:一种只传入起始位置,另一种传入起始位置和结束位置。  String mainString = "Hello, World!";  // 截取从索引2到索引5之间的子串(包括索引2,但不包括索引5) String subString1 = mainString.substring(2, 5); System.out.println("截取子串1: " + subString1); // 输出 "llo"  // 从索引7开始截取到字符串末尾 String subString2 = mainString.substring(7); System.out.println("截取子串2: " + subString2); // 输出 "World!" 2.使用 split() 方法分割字符串:  split() 方法允许你根据某个分隔符将字符串拆分为子串,然后选择需要的子串。  String text = "apple,banana,grape";  // 使用逗号作为分隔符拆分字符串 String[] fruits = text.split(",");  for (String fruit : fruits) {     System.out.println("水果: " + fruit); } 3.使用正则表达式进行高级截取:  Java 的正则表达式库允许你根据复杂的模式来截取字符串。  import java.util.regex.*;  String text = "The quick brown fox jumps over the lazy dog."; String pattern = "\\b\\w+\\b"; // 匹配单词  Pattern compiledPattern = Pattern.compile(pattern); Matcher matcher = compiledPattern.matcher(text);  while (matcher.find()) {     System.out.println("匹配到的单词: " + matcher.group()); } 4. 字符串替換 1.使用 replace() 方法:  replace() 方法用于将指定的字符或子字符串替换为新的字符或子字符串。  String originalString = "Hello, World!"; String searchString = "World"; String replacementString = "Java";  String modifiedString = originalString.replace(searchString, replacementString); System.out.println("替换后的字符串: " + modifiedString); 输出:  替换后的字符串: Hello, Java! 1 2.使用 replaceAll() 方法进行正则表达式替换:  replaceAll() 方法允许你使用正则表达式进行更灵活的替换操作。  String originalString = "The quick brown fox jumps over the lazy dog."; String pattern = "fox"; String replacementString = "cat";  String modifiedString = originalString.replaceAll(pattern, replacementString); System.out.println("替换后的字符串: " + modifiedString); 输出:    替换后的字符串: The quick brown cat jumps over the lazy dog. 1 3.使用 StringBuilder 进行替换:  如果需要进行多次替换或性能要求较高,可以使用 StringBuilder 类。  StringBuilder stringBuilder = new StringBuilder("Java is easy. Java is fun."); String searchString = "Java"; String replacementString = "Python"; int index = stringBuilder.indexOf(searchString); while (index != -1) {     stringBuilder.replace(index, index + searchString.length(), replacementString);     index = stringBuilder.indexOf(searchString); }  String modifiedString = stringBuilder.toString(); System.out.println("替换后的字符串: " + modifiedString); 输出:    替换后的字符串: Python is easy. Python is fun. 1 5. 字符串分割 1. 使用 split() 方法:  split() 方法是Java中最常用的字符串分割方法。它使用指定的正则表达式作为分隔符,将字符串分割成一个字符串数组。  String inputString = "apple,orange,banana,grape"; String[] fruits = inputString.split(",");  System.out.println("分割后的水果:"); for (String fruit : fruits) {      System.out.println(fruit); } 输出:  分割后的水果: apple orange banana grape 2. 使用 StringTokenizer 类:  StringTokenizer 类是Java中另一种进行字符串分割的方式,它使用指定的分隔符将字符串分割成标记。  String inputString = "Java is a powerful programming language"; StringTokenizer tokenizer = new StringTokenizer(inputString);  System.out.println("分割后的单词:"); while (tokenizer.hasMoreTokens()) {      System.out.println(tokenizer.nextToken()); } 输出: 分割后的单词: Java is a powerful programming language 3. 使用正则表达式: 你也可以使用正则表达式作为分隔符,以实现更灵活的字符串分割。 String inputString = "Java123is456a789powerful"; String[] parts = inputString.split("\\d+"); System.out.println("分割后的部分:"); for (String part : parts) {      System.out.println(part); } 输出: 分割后的部分: Java is a 4. 使用 Apache Commons Lang 库:  Apache Commons Lang 库提供了 StringUtils 类,其中有一个方便的 split() 方法,可以更容易地处理字符串分割。  import org.apache.commons.lang3.StringUtils;  String inputString = "Java;C;C++;Python"; String[] languages = StringUtils.split(inputString, ';');  System.out.println("分割后的编程语言:"); for (String language : languages) {      System.out.println(language); 输出: 分割后的编程语言: Java C C++ 字符串比较 1. 使用 equals() 方法进行内容比较: equals() 方法用于比较两个字符串的内容是否相同。它比较字符串的每个字符,而不仅仅是比较引用是否相等。 String str1 = "Hello"; String str2 = "World"; String str3 = "Hello"; boolean isEqual1 = str1.equals(str2); // 返回 false boolean isEqual2 = str1.equals(str3); // 返回 true System.out.println("str1 和 str2 是否相等:" + isEqual1); 2. 使用 equalsIgnoreCase() 方法进行忽略大小写的内容比较: equalsIgnoreCase() 方法与 equals() 方法类似,但它会忽略字符串的大小写。 String str1 = "Hello"; String str2 = "hello"; boolean isEqualIgnoreCase = str1.equalsIgnoreCase(str2); // 返回 true 3. 使用 compareTo() 方法进行字典顺序比较: compareTo() 方法用于比较两个字符串的字典顺序。它返回一个整数,表示两个字符串之间的比较结果。 String str1 = "apple"; String str2 = "banana"; String str3 = "cherry";  int result1 = str1.compareTo(str2); // 返回负数 int result2 = str2.compareTo(str1); // 返回正数 int result3 = str1.compareTo(str3); // 返回负数  System.out.println("str1 和 str2 的比较结果:" + result1); System.out.println("str2 和 str1 的比较结果:" + result2); System.out.println("str1 和 str3 的比较结果:" + result3); 4. 使用 startsWith() 和 endsWith() 方法检查前缀和后缀: startsWith() 方法用于检查字符串是否以指定的前缀开头,而 endsWith() 方法用于检查字符串是否以指定的后缀结尾。 Strng str = "Hello, World"; boolean startsWithHello = str.startsWith("Hello"); // 返回 true boolean endsWithWorld = str.endsWith("World");     // 返回 false System.out.println("字符串是否以 Hello 开头:" + startsWithHello); System.out.println("字符串是否以 World 结尾:" + endsWithWorld); 5. 使用 compareToIgnoreCase() 方法进行忽略大小写的字典顺序比较: compareToIgnoreCase() 方法与 compareTo() 方法类似,但它会忽略字符串的大小写。 String str1 = "apple"; String str2 = "Banana"; int result = str1.compareToIgnoreCase(str2); // 返回正数 System.out.println("str1 和 str2 的比较结果(忽略大小写):" + result); 7. 字符串格式化 1. 使用 String.format() String.format() 方法允许您创建格式化的字符串,类似于C语言中的 printf() 函数。它使用占位符来指定要插入的数据以及它们的格式。占位符由百分号 (%) 后跟一个字符组成,该字符表示插入数据的类型。以下是一些常见的占位符及其用法: %s :字符串。 %d :整数。 %f :浮点数。 %n :换行符。 示例: String name = "Alice"; int age = 30; double salary = 50000.50; String formattedString = String.format("Name: %s, Age: %d, Salary: %.2f", name, age, salary); System.out.println(formattedString); 输出: Name: Alice, Age: 30, Salary: 50000.50 在上面的示例中,我们使用了 %s 、 %d 和 %.2f 占位符来插入字符串、整数和浮点数,并指定了浮点数保留两位小数。 2. 使用 printf() printf() 方法是 System.out 对象的一个方法,它用于将格式化的字符串输出到控制台。与 String.format() 类似,它使用相同的占位符来格式化输出。 示例: String name = "Bob"; int age = 25; double height = 1.75; 输出: Name: Bob, Age: 25, Height: 1.75 8. 字符串空格处理 1. 删除空格:  .使用 String 类的 trim() 方法删除字符串前后的空格。 trim() 返回一个新的字符串,其中删除了前导和尾随的空格。 String text = "  This is a text with spaces  "; String trimmedText = text.trim(); System.out.println(trimmedText); // 输出: "This is a text with spaces" 2. 替换空格:  使用 String 类的 replace() 方法替换字符串中的空格。 你可以将空格替换为其他字符或字符串。 String text = "Hello, World!"; String replacedText = text.replace(" ", "_"); System.out.println(replacedText); // 输出: "Hello,_World!" 3. 分割字符串:  使用 split() 方法将字符串拆分成字符串数组。 默认情况下,split() 使用空格作为分隔符,但你可以指定自定义分隔符。 String sentence = "This is a sample sentence"; String[] words = sentence.split(" "); // 使用空格分割成单词数组 4. 检查空格:  使用 contains() 方法检查字符串是否包含空格。 String text = "This has spaces"; boolean hasSpaces = text.contains(" "); // 返回 true 5. 统计空格的数量:  使用循环遍历字符串并计算空格的数量。 String text = "Count the spaces in this text"; int spaceCount = 0; for (char c : text.toCharArray()) {     if (c == ' ') {         spaceCount++;     } } System.out.println("空格数量:" + spaceCount); // 输出: "空格数量:5" 6. 替换多个连续空格:  使用正则表达式来替换连续的多个空格为单个空格。 String text = "Replace  multiple   spaces  with one."; String replacedText = text.replaceAll("\\s+", " "); System.out.println(replacedText); // 输出: "Replace multiple spaces with one." 7. 处理制表符和换行符:  空白字符不仅包括空格,还包括制表符 (\t) 和换行符 (\n) 等。 你可以使用 replaceAll() 来处理它们,就像处理空格一样。 String textWithTabs = "This\tis\ta\ttab\tseparated\ttext"; String textWithNewlines = "This\nis\na\nnewline\nseparated\ntext"; 总结 欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下。 (博客的参考源码可以在我主页的资源里找到,如果在学习的过程中有什么疑问欢迎大家在评论区向我提出) ———————————————— 版权声明:本文为CSDN博主「东离与糖宝」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/HHX_01/article/details/132499313 
  • [其他问题] .so动态库出现undefined symbol
    使用了一个第三方的动态库libgmssl.so来编写生成了一个新的动态库tlcp.so,但是使用时出现undefined symbol后面使用ldd -r tlcp.so查看,显示如下内容undefined symbol: tls_send    (./tlcp.so) undefined symbol: tls_ctx_cleanup    (./tlcp.so) undefined symbol: tls_socket_accept    (./tlcp.so) undefined symbol: tls_do_handshake    (./tlcp.so) undefined symbol: tls_ctx_set_certificate_and_key    (./tlcp.so) undefined symbol: tls_socket_connect    (./tlcp.so) 这些函数的实现在libgmssl.so内已经实现,且使用nm libgmssl.so显示
  • [技术干货] Java生成UUID的常用方式-转载
     java.util.UUID类来生成UUID import java.util.UUID;  public class UUIDGenerator {     public static void main(String[] args) {                  //随机生成一个UUID对象         UUID uuid = UUID.randomUUID();         System.out.println("生成的UUID为:" + uuid.toString());                  //通过给定的字符串名称和命名空间生成UUID对象         UUID uuid2 = UUID.nameUUIDFromBytes("example_name".getBytes());         System.out.println("生成的UUID2为:" + uuid2.toString());     } } /*优点: Java自带,无需引入额外的库和依赖; 简单易用,一行代码就可以生成UUID。  缺点: 生成的UUID可能会重复,虽然重复的概率较小,但是在高并发的情况下还是有可能发生; 无法控制生成的UUID的格式,只能生成标准的UUID*/  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Apache Commons IO库中的UUIDUtils类 import org.apache.commons.io.UUIDUtils;  public class UUIDGenerator {     public static void main(String[] args) {                  //随机生成一个UUID字符串         String uuid = UUIDUtils.randomUUID().toString();         System.out.println("生成的UUID为:" + uuid);     } } /* 三方库优缺点 优点: 可以生成唯一的UUID; 很多开源库和框架都提供了UUID生成的支持。  缺点: 会增加项目的依赖和复杂度; 不同的库实现方式不同,可能会影响生成的UUID的格式和唯一性。 */  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 使用Google Guice库中的UUIDGenerator类生成UUID import com.google.inject.Inject; import com.google.inject.name.Named; import java.util.UUID;  public class UUIDGenerator {     private final UUID uuid;      @Inject     public UUIDGenerator(@Named("randomUUID") UUID uuid) {         this.uuid = uuid;     }      public UUID getUUID() {         return uuid;     }          public static void main(String[] args) {         UUIDGenerator generator = new UUIDGenerator(UUID.randomUUID());         System.out.println("生成的UUID为:" + generator.getUUID().toString());     } }  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 使用JDK的MessageDigest类和SecureRandom类:可以通过Hash算法和随机数生成UUID 写法一: import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.UUID;  public class UUIDGenerator {     public static void main(String[] args) throws NoSuchAlgorithmException {         SecureRandom secureRandom = new SecureRandom();         byte[] seed = secureRandom.generateSeed(16);         MessageDigest md5 = MessageDigest.getInstance("MD5");         md5.update(seed);         UUID uuid = UUID.nameUUIDFromBytes(md5.digest());         System.out.println("生成的UUID为:" + uuid.toString());     } } 写法二: import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Random;  public class UUIDGenerator {      public static String generateUUID() {         String result = "";         try {             MessageDigest md = MessageDigest.getInstance("MD5");             byte[] messageDigest = md.digest((System.currentTimeMillis() + new Random().nextInt(99999999) + "").getBytes());             StringBuilder sb = new StringBuilder();             for (byte b : messageDigest) {                 sb.append(String.format("%02x", b));             }             result = sb.toString();         } catch (NoSuchAlgorithmException e) {             e.printStackTrace();         }         return result;     }  }  /* 优点: 可以通过Hash算法和随机数生成唯一的UUID,具有较高的唯一性; 实现简单,无需引入额外的库和依赖。  缺点: 重复的概率比较难以预测,取决于生成的Hash值的分布情况; 无法控制生成的UUID的格式,只能生成基于MD5或SHA-1的UUID。 */  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 使用Snowflake算法生成UUID Snowflake算法是Twitter开源的分布式ID生成算法,可以在多个节点上生成唯一的ID  import com.github.f4b6a3.uuid.UuidCreator; import com.github.f4b6a3.uuid.enums.UuidVariant; import com.github.f4b6a3.uuid.enums.UuidVersion; import com.github.f4b6a3.uuid.impl.TimeBasedUuidCreator;  import java.time.Instant;  public class UUIDGenerator {     public static void main(String[] args) {         UuidCreator creator = TimeBasedUuidCreator.withRandomNodeId();         Instant now = Instant.now();         long timestamp = now.getEpochSecond() * 1000 + now.getNano() / 1000000;         String uuid = creator.create(UuidVersion.VERSION_TIME_BASED, timestamp).toString();         System.out.println("生成的UUID为:" + uuid);     } } /* 优点: 可以在分布式系统中生成唯一的ID,具有较高的唯一性和可读性; 可以控制生成的ID的格式和信息。  缺点: 实现相对复杂,需要实现一个全局唯一的时钟服务; 只适用于分布式系统,不适用于独立的单机系统。 */ Snowflake算法第二种:  public class UUIDGenerator {      /** 开始时间截 (2017-01-01) */     private final long twepoch = 1483200000000L;     /** 机器id所占的位数 */     private final long workerIdBits = 5L;     /** 数据标识id所占的位数 */     private final long datacenterIdBits = 5L;     /** 支持的最大机器id,结果是31 */     private final long maxWorkerId = -1L ^ (-1L << workerIdBits);     /** 支持的最大数据标识id,结果是31 */     private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);     /** 序列在id中占的位数 */     private final long sequenceBits = 12L;     /** 机器ID向左移12位 */     private final long workerIdShift = sequenceBits;     /** 数据标识id向左移17位(12+5) */     private final long datacenterIdShift = sequenceBits + workerIdBits;     /** 时间截向左移22位(5+5+12) */     private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;     /** 生成序列的掩码,这里为4095 */     private final long sequenceMask = -1L ^ (-1L << sequenceBits);     /** 工作机器id(0~31) */     private long workerId = 0L;     /** 数据中心id(0~31) */     private long datacenterId = 0L;     /** 毫秒内序列(0~4095) */     private long sequence = 0L;     /** 上次生成ID的时间截 */     private long lastTimestamp = -1L;      /**      * 构造函数      *      * @param workerId     工作ID (0~31)      * @param datacenterId 数据中心ID (0~31)      */     public UUIDGenerator(long workerId, long datacenterId) {         if (workerId > maxWorkerId || workerId < 0) {             throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));         }         if (datacenterId > maxDatacenterId || datacenterId < 0) {             throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));         }         this.workerId = workerId;         this.datacenterId = datacenterId;     }      /**      * 获得下一个ID (该方法是线程安全的)      *      * @return SnowflakeId      */     public synchronized long nextId() {         long timestamp = timeGen();         // 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过,此时应当抛出异常         if (timestamp < lastTimestamp) {             throw new RuntimeException(                     String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));         }          // 如果是同一时间生成的,则进行毫秒内序列         if (lastTimestamp == timestamp) {             sequence = (sequence + 1) & sequenceMask;             // 毫秒内序列溢出             if (sequence == 0) {                 // 阻塞到下一个毫秒,获得新的时间戳                 timestamp = tilNextMillis(lastTimestamp);             }         }         // 时间戳改变,毫秒内序列重置         else {             sequence = 0L;         }          // 上次生成ID的时间截         lastTimestamp = timestamp;          // 移位并通过或运算拼到一起组成64位的ID         return ((timestamp - twepoch) << timestampLeftShift) //                 | (datacenterId << datacenterIdShift) //                 | (workerId << workerIdShift) //                 | sequence;     }      /**      * 阻塞到下一个毫秒,直到获得新的时间戳      *      * @param lastTimestamp 上次生成ID的时间截      * @return 当前时间戳      */     protected long tilNextMillis(long lastTimestamp) {         long timestamp = timeGen();         while (timestamp <= lastTimestamp) {             timestamp = timeGen();         }         return timestamp;     }      /**      * 返回以毫秒为单位的当前时间      *      * @return 当前时间(毫秒)      */     protected long timeGen() {         return System.currentTimeMillis();     }  } 将时间戳和随机数作为种子生成UUID import java.util.UUID;  public class UUIDGenerator {     public static void main(String[] args) {         long time = System.currentTimeMillis();         int random = (int) (Math.random() * Integer.MAX_VALUE);         UUID uuid = new UUID(time, random);         System.out.println("生成的UUID为:" + uuid.toString());     } 使用Redis集群的redisson框架提供的RUID类生成UUID import org.redisson.api.RUID;  public class UUIDGenerator {     public static void main(String[] args) {         RUID ruid = RUID.randomUID();         System.out.println("生成的UUID为:" + ruid.toString());     } } 利用SecureRandom类生成 import java.security.SecureRandom; import java.util.UUID;  public class UUIDGenerator {      public static String generateUUID() {         return UUID.randomUUID().toString();     }      public static String generateSecureUUID() {         SecureRandom random = new SecureRandom();         byte[] bytes = new byte[16];         random.nextBytes(bytes);         return UUID.nameUUIDFromBytes(bytes).toString();     }  } 三方库详细版 Apache Commons: 引入以下Maven依赖 <dependency>     <groupId>commons-lang</groupId>     <artifactId>commons-lang</artifactId>     <version>2.6</version> </dependency> java示例代码:  import org.apache.commons.lang3.StringUtils; import java.util.UUID;  public class GenerateUUID {     public static void main(String[] args) {         UUID uuid = UUID.randomUUID();         String uuidStr = StringUtils.remove(uuid.toString(), '-');         System.out.println("UUID:" + uuidStr);     } } Google Guava: Google Guava库可以使用它的UUID类来生成UUID。需要引入以下Maven依赖:  <dependency>     <groupId>com.google.guava</groupId>     <artifactId>guava</artifactId>     <version>30.0-jre</version> </dependency> java示例:  import com.google.common.base.CharMatcher; import java.util.UUID;  public class GenerateUUID {     public static void main(String[] args) {         UUID uuid = UUID.randomUUID();         String uuidStr = CharMatcher.is('-').removeFrom(uuid.toString());         System.out.println("UUID:" + uuidStr);     } } 注意事项 之前提到了 Apache Commons 的 UUIDUtils 工具类,但是这个工具类实际上是用于字符串格式与 UUID 转化的,而不是生成 UUID。 如果你想要使用 Apache Commons 中的工具类来生成 UUID ,可以使用 RandomStringUtils 类中的 randomUUID() 方法。下面是一个简单示例:  import org.apache.commons.lang3.RandomStringUtils;  public class GenerateUUID {     public static void main(String[] args) {         String uuid = RandomStringUtils.randomNumeric(8) + "-" +                       RandomStringUtils.randomNumeric(4) + "-" +                       RandomStringUtils.randomNumeric(4) + "-" +                       RandomStringUtils.randomNumeric(4) + "-" +                       RandomStringUtils.randomNumeric(12);         System.out.println("UUID:" + uuid);     } } /* 上述代码中,RandomStringUtils的randomNumeric  方法用于生成指定长度的数字字符串,然后通过字符串拼接的方式生成UUID。 需要注意的是,这种方式所生成的UUID并不是符合UUID标准规范的。 */  ———————————————— 版权声明:本文为CSDN博主「潮流coder」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_45699990/article/details/129814826 
  • [技术干货] Post 和 Get 两种方式实现数据导出Excel文件并下载 -转载
     Post 和 Get 两种方式实现数据导出Excel文件并下载  前端使用Vue,后端使用Springboot。  一般而言,使用post方式是比较方便的。但有时候,需要下载的数据在后端进行查询的时候很比较复杂的查询条件,而这个查询条件是前端进行下载请求的时候传递的参数,如果参数比较复杂或参数量比较大,超出了get方式的参数限制,就需要使用post方式进行下载请求。  Post方式: 后端将查询到的数据作一些处理之后,将数据写入到请求的响应体中,前端在请求接口之后,在回调函数中,将响应体中的二进制流转化为Blob对象,然后创建一个下载链接进行下载。  后端代码:      @PostMapping("/export")     public void export(HttpServletResponse response, @RequestParam("className") String className) {         try {             // 根据班级名获取该班级的所有学生的信息             List<Student> list = StudentService.lists(className);             // 为了导出的表格中有序号,在定义实体类的时候加了一个serialNo属性             for(int i = 0; i < list.size(); i++) {                 list.get(i).setSerialNo(i + 1);             }             // 设置excel文件的名字,这边可以不用,在前端重新设置了文件名             String name = "学生信息列表.xlsx";             String fileName = new String(name.getBytes("gb2312"), "ISO8859-1");             response.setContentType("application/octet-stream");             response.setHeader("Content-Disposition","attachment;filename=" + fileName);                          // 这里注册的 CustomCellWriteHandler工具类是用来在excel中适应内容自动调整表格宽度的             ExcelWriter writer = EasyExcel.write(response.getOutputStream()).registerWriteHandler(new CustomCellWriteHandler()).build();             WriteSheet sheet = EasyExcel.writerSheet(0,"sheet").head(BillList.class).build();             writer.write(list,sheet);             writer.finish();         } catch (Exception e) {             log.info("jjj", e);         }     }  Student 实体类:  @Data public class Student{     // ExcelIgnore 注册表示该属性不导出到excel     @ExcelIgnore     private Integer id;     @ExcelProperty(value = "班级", index = 1)     private String className;     @ExcelProperty(value = "学号", index = 2)     private String studentNumber;     @ExcelProperty(value = "姓名", index = 3)     private String name;     @ExcelProperty(value = "性别", index = 4)     private String gender;     @       // 导出excel的序号列, index值表示在导出的excel中的第几列,从0开始     @ExcelProperty(value = "序号", index = 0)     private Integer serialNo; }  CustomCellWriteHandler工具类:  import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy; import org.apache.commons.collections4.CollectionUtils; import org.apache.poi.ss.usermodel.Cell;   import java.util.HashMap; import java.util.List; import java.util.Map;   /**  * Excel 导出列宽度自适应  */ public class CustomCellWriteHandler extends AbstractColumnWidthStyleStrategy {       private Map<Integer, Map<Integer, Integer>> CACHE = new HashMap<>();       @Override     protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<CellData> cellDataList, Cell cell, Head head, Integer integer, Boolean isHead) {         boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);         if (needSetWidth) {             Map<Integer, Integer> maxColumnWidthMap = CACHE.get(writeSheetHolder.getSheetNo());             if (maxColumnWidthMap == null) {                 maxColumnWidthMap = new HashMap<>();                 CACHE.put(writeSheetHolder.getSheetNo(), maxColumnWidthMap);             }               Integer columnWidth = this.dataLength(cellDataList, cell, isHead);             if (columnWidth >= 0) {                 if (columnWidth > 255) {                     columnWidth = 255;                 }                   Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex());                 if (maxColumnWidth == null || columnWidth > maxColumnWidth) {                     maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth);                     writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256);                 }               }         }     }       private Integer dataLength(List<CellData> cellDataList, Cell cell, Boolean isHead) {         if (isHead) {             return cell.getStringCellValue().getBytes().length;         } else {             CellData cellData = cellDataList.get(0);             CellDataTypeEnum type = cellData.getType();             if (type == null) {                 return -1;             } else {                 switch (type) {                     case STRING:                         return cellData.getStringValue().getBytes().length;                     case BOOLEAN:                         return cellData.getBooleanValue().toString().getBytes().length;                     case NUMBER:                         return cellData.getNumberValue().toString().getBytes().length;                     default:                         return -1;                 }             }         }     } }  前端代码:  // 导入接口文件,一个controller的接口都配置在一个js/ts文件中 import studentApi from '@/api/studentAPI'   // exportExcel 绑定了下载按钮的点击事件 function exportExcel() {   studentApi.exportExcelUrl(className).then(res=>{     const url = window.URL.createObjectURL(new Blob([res]))     let link = document.createElement('a')     link.style.display = 'none'     link.href = url     link.setAttribute('download', '学生信息列表.xlsx')     document.body.appendChild(link)     link.click()     URL.revokeObjectURL(link.href) // 释放URL 对象     document.body.removeChild(link)     link = null   }) }  studentAPI.ts:  import AxiosInstance from "@/plugins/axiosInstance";   const exportExcelUrl = (className:string) => {     return AxiosInstance.post('/rent/export', {responseType:'arraybuffer', params: {className: className}}) }   export default {     exportExcelUrl } axiosInstance.ts:  import axios from 'axios'   // 配置后端接口地址前缀 const ConfigBaseURL = 'http://localhost:8080/'     // 本地环境 const Axios = axios.create({     timeout: 5000,      baseURL: ConfigBaseURL,     headers: {         'Content-Type': 'application/json;charset=UTF-8'     } })   Axios.interceptors.request.use(config => {     return config }, error => {     return Promise.reject(error) })   Axios.interceptors.response.use(response => {     var data = response.data     return response.data }, error => {       return Promise.reject(error) })   export default Axios ;  Get方式: 实现方式和post基本一致,后端只需要修改一下接口的请求方式和response的contentType;前端就只需要提供一个链接即可。  后端代码:      @GetMapping("/export")     public void export(HttpServletResponse response, @Param("className") String className) {         try {             // 根据班级名获取该班级的所有学生的信息             List<Student> list = StudentService.lists(className);             // 为了导出的表格中有序号,在定义实体类的时候加了一个serialNo属性             for(int i = 0; i < list.size(); i++) {                 list.get(i).setSerialNo(i + 1);             }             // 设置Excel文件名             String name = "学生信息列表.xlsx";             String fileName = new String(name.getBytes("gb2312"), "ISO8859-1");             response.setContentType("application/vnd.ms-excel;chartset=utf-8");             response.setHeader("Content-Disposition","attachment;filename=" + fileName);               // 这里注册的 CustomCellWriteHandler工具类是用来在excel中适应内容自动调整表格宽度的             ExcelWriter writer = EasyExcel.write(response.getOutputStream()).registerWriteHandler(new CustomCellWriteHandler()).build();             WriteSheet sheet = EasyExcel.writerSheet(0,"sheet").head(BillList.class).build();             writer.write(list,sheet);             writer.finish();         } catch (Exception e) {             log.info("jjj", e);         }     }  除了请求方式从Post改为了Get,还有一个变化之处就是:  response.setContentType("application/vnd.ms-excel;chartset=utf-8"); 前端代码:  // exportExcel 绑定了下载按钮的点击事件 function exportExcel() {   // 本来后面的接口地址也是配置在接口文件中的,这边为了方便阅读,直接放在了此处。   var className = 2205   window.location.href = "http://localhost:8080/student/export?className=" + className } 参考:  使用VUE+SpringBoot+EasyExcel 整合导入导出demo_娜乌西卡lxm的博客-CSDN博客  通过get或post请求下载excel表格的解决办法_get请求下载excel_一键写代码的博客-CSDN博客  Vue2 导出Excel + 解决乱码问题 —— axios (下载后台传过来的流文件(excel)后乱码问题)_Shimeng_1989的博客-CSDN博客 ———————————————— 版权声明:本文为CSDN博主「CHEN HANGJUN」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/chen1030416518/article/details/129973905 
  • [技术干货] JAVA编程中常用到的线程和注意点总结
    一般的创建线程的方式有两种:继承 Thread 类(extends Thread)或者实现Runnable 接口(implements Runnable)1)  继承 Thread 类    实现步骤:  继承 Thread 类, 覆盖run()方法, 提供并发运程的过程  创建这个类的实例  使用 start() 方法启动线程2)  实现 Runnable 接口  实现步骤:  实现 Runnable 接口, 实现run()方法, 提供并发运程的过程  创建这个类的实例, 用这个实例作为Thread 构造器参数,创建Thread 类  使用 start() 方法启动线程      线程都会出现最基本的五种状态:1)  New      新建状态    当程序使用new关键字创建了一个线程后,该线程就处于新建状态,此时线程还未启动,  当线程对象调用start()方法时,线程启动,进入Runnable 状态2)  Runnable    可运行(就绪)状态  当线程处于Runnable 状态时,表示线程准备就绪,等待获取CPU3)  Running    运行(正在运行)状态  假如该线程获取了CPU,则进入Running 状态,开始执行线程体,即run()方法中的内容  注意:   (1)如果系统叧有1个CPU,那么在仸意时间点则叧有1条线程处于Running 状态;   (2)如果是双核系统,那么同一时间点会有2条线程处于Running 状态但是,当线程数大于处理器数时,依然会是多条线程在同一个CPU 上轮换执行   (3)当一条线程开始运行时,如果它不是一瞬间完成,那么它不可能一直处于Running 状态, 线程在执行过程中会被中断,目的是让其它线程获得执行的机会,像这样线程调度的策略取决于底层平台。对于抢占式策略的平台而言,系统系统会给每个可执行的线程一小段时间来处理仸务,当该时间段(时间片)用完,系统会剥夺该线程所占资源(CPU), 让其他线程获得运行机会。  (4) 调用yield()方法,可以使线程由Running 状态进入Runnable 状态4)  Block      阻塞(挂起)状态  当如下情冴下,线程会进入阻塞状态:  线程调用了sleep()方法主动放弃所占CPU 资源  线程调用了一个阻塞式IO 方法(比如控制台输入方法),在该方法返回前,该线程被阻塞  当正在执行的线程被阻塞时,其它线程就获得执行机会了。需要注意的是,当阻塞结束时,该线程将进入Runnable 状态,而非直接进入Running 状态5)  Dead      死亡状态    当线程的run()方法执行结束,线程进入Dead 状态  需要注意的是,不要试图对一个已经死亡的线程调用start()方法,线程死亡后将不能再次作为线程执行,系统会抛出IllegalThreadStateException 异常注:1)  new运算创建线程后,线程进入New状态(初始状态)2)  调用  start()方法后,线程从New状态进入Runnable 状态(就绪状态)          start()方法是在main()方法(Running 状态)中调用的3)  线程结束后,进入Dead 状态(死亡状态),被对象垃圾回收4)  线程进入Dead 状态后,叧能被垃圾回收,不能再开始5)  如果线程在运行过程中,自己调用了yield()方法,则主动由 Running 状态进入Runnable 状态
  • [技术干货] Spring MVC请求处理流程和九大组件-转载
    需求:前端浏览器请求url: http://localhost:8080/demo/handle01,前端⻚⾯显示后台服务器的时间 开发过程 配置DispatcherServlet前端控制器 开发处理具体业务逻辑的Handler(@Controller、@RequestMapping) xml配置⽂件配置controller扫描,配置springmvc三⼤件 将xml⽂件路径告诉springmvc(DispatcherServlet)  流程说明 第⼀步:⽤户发送请求⾄前端控制器DispatcherServlet 第⼆步:DispatcherServlet收到请求调⽤HandlerMapping处理器映射器 第三步:处理器映射器根据请求Url找到具体的Handler(后端控制器),⽣成处理器对象及处理器拦截器(如果 有则⽣成)⼀并返回DispatcherServlet 第四步:DispatcherServlet调⽤HandlerAdapter处理器适配器去调⽤Handler 第五步:处理器适配器执⾏Handler 第六步:Handler执⾏完成给处理器适配器返回ModelAndView 第七步:处理器适配器向前端控制器返回 ModelAndView,ModelAndView 是SpringMVC 框架的⼀个底层对象,包括 Model 和 View 第⼋步:前端控制器请求视图解析器去进⾏视图解析,根据逻辑视图名来解析真正的视图。 第九步:视图解析器向前端控制器返回View 第⼗步:前端控制器进⾏视图渲染,就是将模型数据(在 ModelAndView 对象中)填充到 request 域 第⼗⼀步:前端控制器向⽤户响应结果。  Spring MVC 九⼤组件 HandlerMapping(处理器映射器) HandlerMapping 是⽤来查找 Handler 的,也就是处理器,具体的表现形式可以是类,也可以是⽅法。⽐如,标注了@RequestMapping的每个⽅法都可以看成是⼀个Handler。Handler负责具体实际的请求处理,在请求到达后,HandlerMapping 的作⽤便是找到请求相应的处理器Handler 和 Interceptor。  HandlerAdapter(处理器适配器) HandlerAdapter 是⼀个适配器。因为 Spring MVC 中 Handler 可以是任意形式的,只要能处理请求即可。但是把请求交给 Servlet 的时候,由于 Servlet 的⽅法结构都是doService(HttpServletRequest req,HttpServletResponse resp)形式的,要让固定的 Servlet 处理⽅法调⽤ Handler 来进⾏处理,便是HandlerAdapter 的职责。  HandlerExceptionResolver(Handler异常解析器) HandlerExceptionResolver ⽤于处理 Handler 产⽣的异常情况。它的作⽤是根据异常设置ModelAndView,之后交给渲染⽅法进⾏渲染,渲染⽅法会将 ModelAndView 渲染成⻚⾯。  ViewResolver(视图解析器) ViewResolver即视图解析器,⽤于将String类型的视图名和Locale解析为View类型的视图,只有⼀个resolveViewName()⽅法。从⽅法的定义可以看出,Controller层返回的String类型视图名viewName 最终会在这⾥被解析成为View。View是⽤来渲染⻚⾯的,也就是说,它会将程序返回的参数和数据填⼊模板中,⽣成html⽂件。ViewResolver 在这个过程主要完成两件事情: ViewResolver 找到渲染所⽤的模板(第⼀件⼤事)和所⽤的技术(第⼆件⼤事,其实也就是找到视图的类型,如JSP)并填⼊参数。默认情况下,Spring MVC会⾃动为我们配置⼀个InternalResourceViewResolver,是针对 JSP 类型视图的。  RequestToViewNameTranslator(ViewName翻译器) RequestToViewNameTranslator 组件的作⽤是从请求中获取 ViewName。因为 ViewResolver 根据ViewName 查找 View,但有的 Handler 处理完成之后,没有设置 View,也没有设置 ViewName,便要通过这个组件从请求中查找 ViewName。  LocaleResolver(区域设置解析器) ViewResolver 组件的 resolveViewName ⽅法需要两个参数,⼀个是视图名,⼀个是 Locale。LocaleResolver ⽤于从请求中解析出 Locale,⽐如中国 Locale 是 zh-CN,⽤来表示⼀个区域。这个组件也是 i18n 的基础。  ThemeResolver(主题解析器) ThemeResolver 组件是⽤来解析主题的。主题是样式、图⽚及它们所形成的显示效果的集合。Spring MVC 中⼀套主题对应⼀个 properties⽂件,⾥⾯存放着与当前主题相关的所有资源,如图⽚、CSS样式等。创建主题⾮常简单,只需准备好资源,然后新建⼀个“主题名.properties”并将资源设置进去,放在classpath下,之后便可以在⻚⾯中使⽤了。SpringMVC中与主题相关的类有ThemeResolver、ThemeSource和Theme。ThemeResolver负责从请求中解析出主题名,ThemeSource根据主题名找到具体的主题,其抽象也就是Theme,可以通过Theme来获取主题和具体的资源。  MultipartResolver(上传处理器) MultipartResolver ⽤于上传请求,通过将普通的请求包装成 MultipartHttpServletRequest 来实现。MultipartHttpServletRequest 可以通过 getFile() ⽅法 直接获得⽂件。如果上传多个⽂件,还可以调⽤ getFileMap()⽅法得到Map<FileName,File>这样的结构,MultipartResolver 的作⽤就是封装普通的请求,使其拥有⽂件上传的功能。  FlashMapManager(传输参数管理器) FlashMap ⽤于重定向时的参数传递,⽐如在处理⽤户订单时候,为了避免重复提交,可以处理完post请求之后重定向到⼀个get请求,这个get请求可以⽤来显示订单详情之类的信息。这样做虽然可以规避⽤户重新提交订单的问题,但是在这个⻚⾯上要显示订单的信息,这些数据从哪⾥来获得呢?因为重定向时么有传递参数这⼀功能的,如果不想把参数写进URL(不推荐),那么就可以通过FlashMap来传递。只需要在重定向之前将要传递的数据写⼊请求(可以通过ServletRequestAttributes.getRequest()⽅法获得)的属性OUTPUT_FLASH_MAP_ATTRIBUTE中,这样在重定向之后的Handler中Spring就会⾃动将其设置到Model中,在显示订单信息的⻚⾯上就可以直接从Model中获取数据。FlashMapManager 就是⽤来管理 FalshMap 的。 ———————————————— 版权声明:本文为CSDN博主「共饮一杯无」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_35427589/article/details/132873827 
  • [技术干货] SpringMVC 域对象共享数据-转载
     在Spring MVC中,可以使用域对象来共享数据。域对象是一个Map类型的对象,可以在请求处理方法之间共享数据。 数据共享可以实现数据在不同组件、请求或模块之间的传递和共享,以方便数据的处理和展示,提高应用程序的性能和灵活性。 SpringMVC主要提供了一下几种方式来达到数据共享: 1、使用ServletAPI向request域对象共享数据 在处理请求的方法中将数据存储到了HttpServletRequest对象的request域中,使用setAttribute方法。"myData"是数据的键,"data"是数据的值。  示例代码: // 获取要共享的数据 // 将数据存储在request域对象中 // 转发请求至其他Servlet或JSP页面  protected void doGet(HttpServletRequest request, HttpServletResponse response) {         String data = "Hello, World!";          request.setAttribute("myData", c);          RequestDispatcher dispatcher =     request.getRequestDispatcher("/path/to/your/servlet-or-jsp");     dispatcher.forward(request, response); } 2、使用ModelAndView向request域对象共享数据 ModelAndView对象是一个包含数据模型和视图信息的容器,在控制器方法中可以将需要共享的数据存储到ModelAndView对象中,然后将该对象返回给Spring MVC框架。  创建一个ModelAndView对象,并使用addObject方法将数据存储到该对象的模型中。"key"是数据的键,“value"是数据的值。然后,使用setViewName方法设置视图信息,指定要渲染的视图名为"example-view”。  然后,Spring MVC框架会将ModelAndView对象中的数据传递给视图,并在视图渲染时将数据存储到request域对象中。在视图中可以使用相应的表达式语言(如JSTL或Thymeleaf的EL表达式)来获取并展示存储在request域中的数据。  @RequestMapping("/testModelAndView") public ModelAndView testModelAndView(){     /**      * ModelAndView有Model和View的功能      * Model主要用于向请求域共享数据      * View主要用于设置视图,实现页面跳转      */     ModelAndView mav = new ModelAndView();     //向请求域共享数据     mav.addObject("testScope", "hello,ModelAndView");     //设置视图,实现页面跳转     mav.setViewName("success");     return mav; } 建议尽量在实际开发中使用更现代和推荐的方式,例如使用@ModelAttribute注解或在方法参数中使用Model对象,以更加简洁和便捷地向request域对象共享数据。  3、使用Model向request域对象共享数据 Model是一个接口,它可以在控制器方法的参数中声明,并使用它的方法将数据添加到其中,然后这些数据将自动传递到request域中。  @RequestMapping("/testModel") public String testModel(Model model){     model.addAttribute("testScope", "hello,Model");     return "success"; } 4、使用map向request域对象共享数据 在控制器方法的参数中声明一个Map参数,Spring MVC会自动将一个Map对象注入到该参数中。您可以向这个Map对象添加键值对,然后它们将自动传递到request域中。  @RequestMapping("/testMap") public String testMap(Map<String, Object> map){     map.put("testScope", "hello,Map");     return "success"; } 当控制器方法返回视图名称时,Spring MVC框架会自动将Map对象中的数据传递到request域中,并在渲染视图时可以使用这些数据。例如,如果您使用JSP作为视图技术,您可以在JSP文件中使用EL表达式${key}来获取存储在request域中的数据  5、使用ModelMap向request域对象共享数据 ModelMap是一个具体实现了Model接口的类,它提供了更多的便捷方法来添加和访问数据。  @RequestMapping("/testModelMap") public String testModelMap(ModelMap modelMap){     modelMap.addAttribute("testScope", "hello,ModelMap");     return "success"; } 当控制器方法返回视图名称时,Spring MVC框架会自动将ModelMap对象中的数据传递到request域中,并在渲染视图时可以使用这些数据。例如,如果您使用JSP作为视图技术,您可以在JSP文件中使用EL表达式${key}来获取存储在request域中的数据。  6、Model、ModelMap、Map的关系 Model、ModelMap、Map类型的参数其实本质上都是 BindingAwareModelMap 类型的  public interface Model{} public class ModelMap extends LinkedHashMap<String, Object> {} public class ExtendedModelMap extends ModelMap implements Model {} public class BindingAwareModelMap extends ExtendedModelMap {} 7、向session域共享数据 在控制器类上使用@SessionAttributes注解并指定了"myData"作为需要存储在Session域中的模型属性。在example方法中,我们使用model.addAttribute方法将数据添加到"myData"模型属性中。  @RequestMapping("/testSession") public String testSession(HttpSession session){     session.setAttribute("myData", "hello,session");     return "success"; } 8、向application域共享数据 控制器方法的参数中声明了一个ServletContext对象,并将其命名为"servletContext"。然后,我们使用setAttribute方法将数据添加到ServletContext对象中,"key"是数据的键,"value"是数据的值。  数据将被存储在Application域中,可以在整个应用程序中访问和共享。  @RequestMapping("/testApplication") public String testApplication(HttpSession session){     ServletContext application = session.getServletContext();     application.setAttribute("testApplicationScope", "hello,application");     return "success"; } 9、总结 本文主要讲解了以下几种域对象可以使用: request域对象:通过使用HttpServletRequest对象的setAttribute方法向request域中存储数据,使用getAttribute方法从request域中获取数据。 session域对象:通过使用HttpSession对象的setAttribute方法向session域中存储数据,使用getAttribute方法从session域中获取数据。 application域对象:通过使用ServletContext对象的setAttribute方法向application域中存储数据,使用getAttribute方法从application域中获取数据。 这些域对象都可以在控制器中使用,可以在不同的请求处理方法之间共享数据。例如,一个请求处理方法可以将数据存储到request域中,然后另一个请求处理方法可以从request域中获取这些数据。 ———————————————— 版权声明:本文为CSDN博主「我有一颗五叶草」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/m0_60915009/article/details/132835848 
  • [技术干货] JAVA中的泛型-转载
    如下是定义了一个类,这个类中可以存放一组 int 类型的数据。 class Number{     int[] arr;       public Number(int a) {         this.arr = new int[10];         this.arr[0] = a;     }       public void setArr(int n, int data) {         this.arr[n] = data;     }       public int getArr(int n) {         return arr[n];     } } 可是虽然这样写没有问题可是它的应用范围实在是太小了,它只能存储 int 类型的数据。如果我们现在想让这个类中可以存放任何类型的数据应该怎么做呢? 在 JAVA 中利用泛型就可以实现这点。 泛型 泛型:就是适用于许多许多类型。从代码上讲,就是对类型实现了参数化。  泛型的语法: class 泛型类名称<类型形参列表> { }  class ClassName<T1, T2, ..., Tn> { } 类名后的 <T> 代表占位符,表示当前类是一个泛型类。 类型形参一般使用一个大写字母表示,常用的名称有: E 表示 Element K 表示 Key V 表示 Value N 表示 Number T 表示 Type S, U, V 等等 - 第二、第三、第四个类型 泛型类   我们将开始的代码进行改进:  class Number<T>{     Object[] arr;//这里可以暂时忽略Object,下文会说       public Number(T a) {         this.arr = new Object[10];         this.arr[0] = a;     }       public void setArr(int n, T data) {         this.arr[n] = data;     }       public T getArr(int n) {         return (T)arr[n];     } }  在 JAVA 中由于数组比较特殊,不能new泛型类型的数组,数组在new的时候必须要指定其类型所以如果定义成这样就会报错  。 为了不让它报错就需要进行强制类型转换因为 Object类 是所有类的父类所以也可以这样定义: 虽然这样写编译器没有报错可是还是报了警告,也就是说我们这样写会存在安全隐患。 这里推荐将数组定义成这样: 调用的时候需要注意: 如果是简单类型(如:int , char……)必须要传对应的包装类。 基本数据类型和对应的包装类  基本数据类型    包装类 byte    Byte short    Short int    Integer long    Long float    Float double    Double char    Character boolean    Boolean 这个表看起来多其实除了 Integer 和 Character, 其余基本类型的包装类都是首字母大写。 如图,此时我们的类泛用性就非常高了,我们可以使用 main 方法测试一下:     public static void main(String[] args) {         Number<Integer> a = new Number<>(3);         Number<Double> b = new Number<>(3.14);         Number<String> c = new Number<>("hello");         System.out.println(a.getArr(0));         System.out.println(b.getArr(0));         System.out.println(c.getArr(0));              } 泛型方法:  泛型除了可以应用于类之外也可以应用于方法。  语法: 方法限定符 <类型形参列表> 返回值类型 方法名称(形参列表) { ... }      public static <T> void swap(T[] array, int i, int j) {         T t = array[i];         array[i] = array[j];         array[j] = t;     }  泛型方法也和类一样不能使用基本类型,需要使用对应的包装类。 我们可以测试一下:      public static void main(String[] args) {         Integer[] b = {1,2,3,4};         swap(b, 0, 1);         System.out.println(Arrays.toString(b));     } 小结: 泛型类<类型实参> 变量名; // 定义一个泛型类引用 new 泛型类<类型实参>(构造方法实参); // 实例化一个泛型类对象,此时可以省略类型实参  Number<Integer> a = new Number<>(3); Number<Integer> list = new Number<Integer>(3); 泛型是将数据类型参数化,进行传递 使用 <T> 表示当前类是一个泛型类。 泛型目前为止的优点:数据类型参数化,编译时自动进行类型检查和转换  ———————————————— 版权声明:本文为CSDN博主「休息一下…」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/2302_76339343/article/details/132867383 
  • [技术干货] MySQL常见面试题(2023年最新)-转载
     前言 java最新面试题(java基础、集合、多线程、jvm、锁、算法、CAS、Redis、数据库、mybatis、spring、springMVC、springBoot、微服务)  1.char和varchar的区别 ①char设置多少长度就是多少长度,varchar可以改变长度,所以char的空间利用率不如varchar的空间利用率高。 ②因为长度固定,所以存取速度要比varchar快。 ③char适用于固定长度的字符串,比如身份证号、手机号等,varchar适用于不固定的字符串。  2.数据库的三大范式 第一范式(1NF): 保证字段不可再分,保证原子性。 第二范式(2NF): 满足1NF前提下,表的每一列都必须和主键有关系。消除部分依赖关系。 第三范式(3NF): 满足2NF前提下,表的每一列比必须和主键有直接关系,不能是间接关系。消除传递依赖  3.你了解sql的执行顺序吗? ⑧select ⑨distinct(去重) ⑥聚合函数  ①from 表1  ③[inner join | left join | right join](连接) 表2  ②on(连接条件) 表1.字段 = 表2.字段  ④where 查询条件  ⑤group by(分组) 字段  ⑦having 分组过滤条件  ⑩order by(排序) 字段  ⑪limit(分页) 0,10  4.索引是什么 是一种高效获取数据的数据结构,相当于目录,更快的找到数据,是一个文件,占用物理空间。  5.索引的优点和缺点 优点: ①提高检索的速度。 ②索引列对数据排序,降低排序成本。 ③mysql 8之后引入了,隐藏索引,当一个索引被隐藏就不会被优化器所使用,就可以看出来索引对数据库的影响,有利于调优。 缺点: ①索引也是一个文件,所以会占用空间。 ②降低更新的速度,因为不光要更新数据,还要更新索引。  6.索引的类型 ①普通索引: 基本索引类型,允许定义索引的字段为空值和重复值。 ②唯一索引: 索引的值必须唯一,允许定义索引的字段为空值。 ③主键索引: 索引的值必须唯一,不可以为空。 ④复合索引: 多个字段加索引,遵守最左匹配规则。 ⑤全局索引: 只有在 MyISAM 引擎上才能使用。  7.索引怎么设计(优化) ①选择唯一性索引:值是唯一的,查询的更快。 ②经常作为查询条件的字段加索引。 ③为经常需要排序、分组和联合操作的字段建立索引:order by、group by、union(联合)、distinct(去重)等。 ④限制索引个数:索引数量多,需要的磁盘空间就越多,更新表时,对索引的重构和更新就很费劲。 ⑤表数据少的不建议使用索引(百万级以内):数据过少,有可能查询的速度,比遍历索引的速度都快。 ⑥删除不常用和不再使用的索引。 ⑦用类型小的类型做索引:比如:int和BIGINT能用int就使用int。因为类型小,查询速度快和索引占用的空间更少。 ⑧使用前缀索引,要是字符串越长,那么索引占的空间越大,并且比较起来就时间就越长。  8.怎么避免索引失效(也属于sql优化的一种) ①某列使用范围查询(>、<、like、between and)时, 右边的所有列索引也会失效。 ②不要对索引字段进行运算。 ③在where子句中不要使用 OR、!=、<>和对值null的判断。 ④避免使用’%'开头的like的模糊查询。 ⑤字符串不加单引号,造成索引失效。  9.索引的数据类型 Hash: 查询时调用Hash函数获得地址,回表查询实际数据。(InnoDB和MylSAM不支持,Memory支持)。 B+树: 每次从根节点出发去查询,然后得到地址,回表查询实际数据。  10.索引为什么使用树结构 因为可以加快查询效率,而且可以保持有序。  11.二叉查找树、B树、B+树 二叉查找树(二叉排序树、二叉搜索树): 一个节点最多两个子节点(左小右大),查询次数和比较次数都是最小的,但是索引是存在磁盘的,当数据量过大的时候,不能直接把整个索引文件加载到内存,需要分多次IO,最坏的情况IO的次数就是树的高度,为了减少IO,需要把树从竖向变成横向。 B树( B- ): 是一种多路查询树,每个节点包含K个子节点,节点都存储索引值和数据,K是B树的阶(树高被称为树的阶)。虽然比较的次数比较多,但是是在内存的比较,可以忽略不计,但是B树IO的次数要比二叉查找树要少,因为B树的高度可以更低。 B+树: B树的升级版,只有叶子节点储存的是索引值指向的数据库的数据。  12.为什么使用B+树不用B树 ①B树只适合随机检索,而B+树同时支持随机检索和顺序检索(因为叶子节点相当于链表,保存索引值都是有序的)。 顺序检索: 按照序列顺序遍历比较找到给定值。 随机检索: 不断从序列中随机抽取数据进行比较,最终找到结果。  ②减少了磁盘IO,提高空间利用率: 因为B+树非叶子节点不会存放数据,只有索引值,所以非叶子节点可以保存更多的索引值,这样B+树就可以更矮,减少IO次数。  ③B+树适合范围查找: 这才是关键,因为数据库大部分都是范围查找,B+树的叶子节点是有序链表,直接遍历就行,而B树的范围查找可能两个节点距离很远,只能通过中序遍历去查找,所以使用B+树更合适。 中序遍历: (根在中,从左往右,一棵树的左子树永远在根前面,根永远在右子树前面)  13.最左匹配原则 最左优先,以最左边为起点任何连续的索引都能匹配上。同时遇到范围查询(>、<、between and、like)就会停止匹配。 例如:Z表建立联合索引 (a,b,c) //这样索引abc列都起效,因为符合最左匹配原则,where子句几个搜索条件顺序调换不影响查询结果,因为Mysql中有查询优化器,会自动优化查询顺序 select  *  from Z where a = 1 and b = 2 and c = 3   //因为a列是起点,没有a列匹配不上,所以索引失效 select * from table_name where  b = 2 and c = 3   //因为连续不到b,所以只有a列索引生效 select * from table_name where  a = 1 and c = 3  14.Mysql怎么查看是否使用到索引或怎么查看sql执行计划 使用explain  例如:explain select * from 表名 where 条件 结果:会查出key,key就是你使用的索引。还有type这个字段,可以看到索引是全表扫描还是索引扫描等等。 type字段内容性能对比:ALL < index < range ~ index_merge < ref < eq_ref < const < system  15.一条sql查询非常慢,我们怎么去排查和优化? 排查: (1) 开启慢查询。 (2) 查看慢查询日志(定位低效率sql,命令:show processlist)。 (3) 使用explain查看sql的执行计划(看看索引是否失效或者性能低)  优化: sql优化 + 索引 + 数据库结构优化 + 优化器优化  16.MylSAM和InnoDB、Memory的区别 MylSAM: mysql5.5之前的存储引擎,是表锁(悲观锁)级别的,不支持事务和外键。 InnoDB: mysql5.5之后的存储引擎,是行锁(乐观锁)级别的,支持事务和外键。 Memory: 内存数据库引擎,因为在内存操作,所以读写很快,但是Mysql服务重启,会丢失数据,不支持事务和外键。  17.什么是事务 事务和隔离级别详解及实际应用  事务是对数据库中一系列操作进行统一的回滚或者提交的操作,主要用来保证数据的完整性和一致性。  18.事务的四大特性(ACID) 原子性(Atomicity): 要么全部成功要么全部失败。 一致性(Consistency): 事务执行前和事务执行后,原本和数据库一致的数据仍然一致。 隔离性(Isolation): 事务与事务之间互不干扰。 持久性(Durability): 事务一旦被提交了,那么对数据库中的数据的改变就是永久的。  19.脏读、不可重复读、幻读 脏读: 也叫"读未提交",顾名思义,就是某一事务A读取到了事务B未提交的数据。 不可重复读: 在一个事务内,多次读取同一个数据,却返回了不同的结果。实际上,这是因为在该事务间隔读取数据的期间,有其他事务对这段数据进行了修改,并且已经提交,就会发生不可重复读事故。 幻读: 在同一个事务中,第一次读取到结果集和第二次读取到的结果集不同。像幻觉一样所以叫幻读。 从上面可以看出脏读和不可重复读是基于数据值的错误,幻读是基于条数增加或者减少的错误  20.事务的隔离级别? ① read uncommited(读取未提交内容): 在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。读取未提交的数据,也被称之为脏读(Dirty Read) ② read committed(读取提交内容): 这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。一个事务只能看见已经提交事务所做的改变。可解决脏读 ③ repeatable read(可重读): 这是MySQL的默认事务隔离级别,同一事务的多个实例在并发读取数据时,会看到同样的数据。不过理论上,这会导致另一个棘手的问题:幻读(Phantom Read)。可解决脏读、不可重复读 ④ serializable(可串行化) : 这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。可解决脏读、不可重复读、幻读。  21.怎么优化数据库 ①SQL优化 ②加缓存 ③分表 ④读写分离  22.SQL优化 ①不要用select *,要使用具体字段。 ②使用数值代替字符串,比如:0=唱,1=跳,2=rap。 ③避免返回大量数据,采用分页最好。 ④使用索引,提升查询速度,不宜建太多索引,不能建在重复数据比较多的字段上。 ⑤批量插入比单条插入要快,因为事务只需要开启一次,数据量太小体现不了。 ⑥避免子查询,优化为多表连接查询。 ⑦尽量使用union all替代union,因为union会自动去重。  23.常用的聚合函数 ①sum(列名) 求和      ②max(列名) 最大值      ③min(列名) 最小值      ④avg(列名) 平均值      ⑤first(列名) 第一条记录 ⑥last(列名) 最后一条记录 ⑦count(列名) 统计记录数不包含null值 count(*)包含null值。  24.几种关联查询 内连接(inner join): 查询两个表匹配数据。 左连接(left join): 查询左表全部行以及右表匹配的行。 右连接(right join): 查询右表全部行以及左表匹配的行。  25.in和exists的区别 in(): 适合子表(子查询)比主表数据小的情况。 exists(): 适合子表(子查询)比主表数据大的情况。  26.drop、truncate、delete的区别 速度: drop > truncate > delete。 回滚: delete支持,truncate和drop不支持。 删除内容: delete表结构还在,删除部分或全部数据,不释放空间。truncate表结构还在,删除全部数据,释放空间。drop表结构和数据不在,包括索引和权限,释放空间。 ———————————————— 版权声明:本文为CSDN博主「爱穿背带裤的馫」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/twotwo22222/article/details/129115194 
  • [技术干货] 【JAVA】Object类与抽象类-转载
     Object类 Object类是java中默认提供的一个类。java里面除了Object类,其他全部的类都是会默认继承Object类的。所以所有的类的对象都可以用Object类的引用来接收。 举个栗子: class Person{    } class Student{     } public class Test {     public static void function(Object obj) {     System.out.println(obj); }     public static void main(String[] args) {         function(new Person());         function(new Student());     }      } 在java开发的过程中,Objec类是参数的最高统一类型。但是Object类也存在有定义好的一些方法。  这些都是Object类定义好的方法: 这几个方法对于java学习来说是必须掌握,不可缺少的。 获取对象信息toString()方法 如果要打印对象中的内容,直接重写Object类的toSting方法即可: // Object类中的toString()方法实现:     public String toString() {         return getClass().getName() + "@" + Integer.toHexString(hashCode());     } 其中的道理就是:object类是所有类的父类,重写了tostring方法后,在使用print方法时就会调用我们重写的toString方法以达到效果。 对象比较equals方法 在java中,==进行比较时: 要是两个比较对象是基本类型时,比较的是变量值是否相等 要是两个比较对象是引用类型变量时,比较的是引用地址是否相等 如果要比较对象中的内容,就必须重写Object类的equals方法,equals方法默认是按地址来比较的 // Object类中的equals方法     public boolean equals(Object obj) {         return (this == obj); // 使用引用中的地址直接来进行比较     } class Person{     private String name ;     private int age ;     public Person(String name, int age) {         this.age = age ;         this.name = name ;     } } public class Test {     public static void main(String[] args) {         Person p1 = new Person("qeo", 20) ;         Person p2 = new Person("qeo", 20) ;         int a = 10;         int b = 10;         System.out.println(a == b); // 输出true         System.out.println(p1 == p2); // 输出false         System.out.println(p1.equals(p2)); // 输出false     } } 上面比较的是地址,想要比较他们的内容就要重写equals方法:  class Person{ ...     @Override     public boolean equals(Object obj) {         if (obj == null) {             return false ;         }          if(this == obj) {             return true ;         } // 不是Person类对象         if (!(obj instanceof Person)) {             return false ;         } Person person = (Person) obj ; // 向下转型,比较属性值         return this.name.equals(person.name) && this.age==person.age ;     } } 所以要比较对象的内容是不是相同的时候,就一定要重写equals方法   hashcode方法 通过观看toString方法的源码我们发现了里面有一个hashcode方法:      public String toString() {         return getClass().getName() + "@" + Integer.toHexString(hashCode());     } 它的作用就是帮助我们算一个具体的对象位置,它的结果是一个内存地址然后会调用toHexString方法,将这个地址以16进制来打印输出。  hashcode方法源码:   public native int hashCode(); 它是一个native方法,底层是用c/c++代码写的,在java中我们是看不到的。  在我们的认知中,两个名字相同,年龄相同的对象存储在同一个位置,但是不重写hashcode方法的话,结果是不同的:  class Person {     public String name;     public int age;     public Person(String name, int age) {         this.name = name;         this.age = age;     } } public class TestDemo4 {     public static void main(String[] args) {         Person per1 = new Person("go", 20) ;         Person per2 = new Person("go", 20) ;         System.out.println(per1.hashCode());         System.out.println(per2.hashCode());     } } //执行结果         460141958         1163157884 这时我们再重写hashcode方法:  class Person {     public String name;     public int age;     public Person(String name, int age) {         this.name = name;         this.age = age;     } @Override     public int hashCode() {         return Objects.hash(name, age);     } } public class TestDemo4 {     public static void main(String[] args) {         Person per1 = new Person("go", 20) ;         Person per2 = new Person("go", 20) ;         System.out.println(per1.hashCode());         System.out.println(per2.hashCode());     } } //执行结果         460141958         460141958 这时发现他们的哈希值就是一样的了。  结论:  hashcode方法用来确定对象在内存中储存的位置相不相同。  hashcode在散列表中才有用,在散列表中hashcode的作用是获取对象的散列码,来确定对象在散列表中的位置。  内部类 当一个事物的内部,还有一部分需要一个完整的结构进行描述,而这个内部的完整的结构只要为外部的事物提供服务,那这个内部结构使用内部类。  在Java中,可以将一个类定义在另一个类或者方法的内部,前者称为内部类,后者称为外部类。  public class OutClass {     class InnerClass{     } } // OutClass是外部类 // InnerClass是内部类 注意:  定义在class类名{}的外部的,即便在一个文件夹内,都不是内部类  public class A{ } class B{ } // A 和 B是两个独立的类,彼此之前没有关系 内部类和外部类共用同一个Java源文件,但是经过编译后,内部类会形成单独的字节码文件  内部类的分类 根据内部类在类中位置不同,可以分为以下几种:  成员内部类:未被static修饰的实例内部类和被static修饰的静态内部类  局部内部类(不谈修饰符)  匿名内部类  public class OutClass {     // 成员位置定义:未被static修饰 --->实例内部类     public class InnerClass1{     } // 成员位置定义:被static修饰 ---> 静态内部类     static class InnerClass2{     }     public void method(){ // 方法中也可以定义内部类 ---> 局部内部类:几乎不用         class InnerClass5{         }     } } 实例内部类 未被static修饰的内部类:  public class OutClass {     private int a;     static int b;     int c;       public void methodA() {         a = 10;         System.out.println(a);     }       public static void methodB() {         System.out.println(b);     } //实例内部类:未被static修饰       class InnerClass {         int c;           public void methodInner() { // 在实例内部类中可以直接访问外部类中:任意访问限定符修饰的成员             a = 100;             b = 200;             methodA();             methodB(); // 如果外部类和实例内部类中具有相同名称成员时,优先访问的是内部类自己的             c = 300;             System.out.println(c); // 如果要访问外部类同名成员时候,必须:外部类名称.this.同名成员名字             OutClass.this.c = 400;             System.out.println(OutClass.this.c);         }     }       public static void main(String[] args) { // 外部类:对象创建 以及 成员访问         OutClass outClass = new OutClass();         System.out.println(outClass.a);         System.out.println(OutClass.b);         System.out.println(outClass.c);         outClass.methodA();         outClass.methodB();         System.out.println("=============实例内部类的访问============="); // 要访问实例内部类中成员,必须要创建实例内部类的对象 // 而普通内部类定义与外部类成员定义位置相同,因此创建实例内部类对象时必须借助外部类 // 创建实例内部类对象         OutClass.InnerClass innerClass1 = new OutClass().new InnerClass(); // 上述语法比较怪异,也可以先将外部类对象先创建出来,然后再创建实例内部类对象         OutClass.InnerClass innerClass2 = outClass.new InnerClass();         innerClass2.methodInner();     } } 注意:  1. 外部类中的任何成员都可以在实力内部类中直接访问    2. 实例内部类所处的位置与外部类成员位置相同,因此也受public、private等访问限定符的约束  3. 在实例内部类方法中访问同名的成员时,优先访问自己的,如果要访问外部类同名的成员,必须:外部类名称.this.同名成员 来访问  4. 实例内部类对象必须在先有外部类对象前提下才能创建  5. 实例内部类的非静态方法中包含了一个指向外部类对象的引用   6. 外部类中,不能直接访问实例内部类中的成员,如果要访问必须先要创建内部类的对象。   静态内部类 被static修饰的内部类就称为静态内部类  public class OutClass {     private int a;     static int b;     public void methodA(){         a = 10;         System.out.println(a);     }     public static void methodB(){         System.out.println(b);     } // 静态内部类:被static修饰的成员内部类     static class InnerClass{         public void methodInner(){ // 在内部类中只能访问外部类的静态成员 // a = 100; // 编译失败,因为a不是类成员变量             b =200; // methodA(); // 编译失败,因为methodA()不是类成员方法             methodB();         }     }     public static void main(String[] args) { // 静态内部类对象创建 & 成员访问         OutClass.InnerClass innerClass = new OutClass.InnerClass();         innerClass.methodInner();     } } 注意:  在静态内部类中只能访问外部类中的静态成员   创建静态内部类对象时,不需要先创建外部类对象   局部内部类  定义在外部类的方法体或者{}中,这种内部类只能在其定义的位置使用,一般使用的非常少:  public class OutClass {     int a = 10;     public void method(){         int b = 10; // 局部内部类:定义在方法体内部 // 不能被public、static等访问限定符修饰         class InnerClass{             public void methodInnerClass(){                 System.out.println(a);                 System.out.println(b);             }         } // 只能在该方法体内部使用,其他位置都不能用         InnerClass innerClass = new InnerClass();         innerClass.methodInnerClass();     }     public static void main(String[] args) { // OutClass.InnerClass innerClass = null; 编译失败     } } 注意:  1. 局部内部类只能在所定义的方法体内部使用  2. 不能被public、static等修饰符修饰  3. 编译器也有自己独立的字节码文件,命名格式:外部类名字$数字内部类名字.class  4. 几乎不会使用   匿名内部类  这个内部类是没有名字的:  interface IA {     void test(); }     public class Test {     public static void main(String[] args) {         IA a = new IA() {             @Override             public void test() {                 System.out.println("重写的方法");             }         };         a.test();     }  ———————————————— 版权声明:本文为CSDN博主「paper jie」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/paperjie/article/details/132684042 
  • [技术干货] 使用HttpURLConnection发送POST请求并携带请求参数-转载
     1、先创建URL对象,指定请求的URL地址。 URL url = new URL("http://example.com/api"); 2、调用URL对象的openConnection()方法创建HttpURLConnection对象。 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 3、设置请求方法为POST。 connection.setRequestMethod("POST"); 4、设置请求头,包括Content-Type、Content-Length等。其中Content-Type表示请求体的格式,Content-Length表示请求体的长度。 connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); connection.setRequestProperty("Content-Length", String.valueOf(param.getBytes().length)); 5、设置连接超时和读取超时时间。 connection.setConnectTimeout(5000); connection.setReadTimeout(5000); 6、允许向服务器写入写出数据。 connection.setDoOutput(true);   connection.setDoInput(true); 7、获取输出流,向服务器写入数据。 OutputStream outputStream = connection.getOutputStream(); outputStream.write(param.getBytes()); outputStream.flush(); outputStream.close();  这里的param是请求参数,需要将其转换为字节数组后写入输出流。  8、获取响应码,判断请求是否成功。 int statusCode = connection.getResponseCode(); 9、读取响应数据。 InputStream inputStream = statusCode == 200 ? connection.getInputStream() : connection.getErrorStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); StringBuilder response = new StringBuilder(); String line; while ((line = reader.readLine()) != null) {     response.append(line); } reader.close(); inputStream.close();  这里的response是响应数据,需要将其读取为字符串后使用。 完整的示例代码如下所示:  String param = "name=张三&age=18"; URL url = new URL("http://example.com/api"); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("POST"); connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); connection.setRequestProperty("Content-Length", String.valueOf(param.getBytes().length)); connection.setConnectTimeout(5000); connection.setReadTimeout(5000); connection.setDoOutput(true); OutputStream outputStream = connection.getOutputStream(); outputStream.write(param.getBytes()); outputStream.flush(); outputStream.close(); int statusCode = connection.getResponseCode();   InputStream inputStream = statusCode == 200 ? connection.getInputStream() : connection.getErrorStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); StringBuilder response = new StringBuilder(); String line; while ((line = reader.readLine()) != null) {    response.append(line); } reader.close(); inputStream.close(); connection.disconnect(); System.out.println(response.toString());  需要注意的是,以上示例代码中的请求参数是以字符串形式传递的,如果需要传递复杂的请求参数,可以考虑使用JSON等格式。同时,如果请求的URL需要携带查询参数,可以在URL中添加查询参数。  下面使用HttpURLConnection 发送POST 请求 参数类型是json  下面是使用HttpURLConnection微信小程序发送订阅消息的一个例子  POST请求  json组装成了一个JSONObject  json类似是这样的  {   "touser": "OPENID",   "template_id": "TEMPLATE_ID",   "page": "index",   "data": {       "name01": {           "value": "某某"       },       "amount01": {           "value": "¥100"       },       "thing01": {           "value": "广州至北京"       } ,       "date01": {           "value": "2018-01-01"       }   } }    try {               URL url = new URL(" https://api.weixin.qq.com/cgi-bin/message/subscribe/send?" +                     "access_token=" +                     "自己的小程序token");               HttpURLConnection connection = (HttpURLConnection) url.openConnection();             connection.setRequestMethod("POST");             connection.setRequestProperty("Content-Type", "application/json");               connection.setDoOutput(true);             connection.setDoInput(true);   //构造发送给用户的订阅消息内容             Map messageContent = new HashMap<String, Object>();             messageContent.put("character_string1", new HashMap<String, Object>() {{                 put("value", "a123456789");             }});             messageContent.put("amount2", new HashMap<String, Object>() {{                 put("value", "1元");             }});             messageContent.put("thing3", new HashMap<String, Object>() {{                 put("value", "西安大学长安学区");             }});             messageContent.put("time4", new HashMap<String, Object>() {{                 put("value", "2021年10月20日");             }});             messageContent.put("thing5", new HashMap<String, Object>() {{                 put("value", "这是备注");             }});             JSONObject messageContentJson = new JSONObject(messageContent);               //构造订阅消息             Map subscribeMessage = new HashMap<String, Object>();             subscribeMessage.put("touser", " 。。。");//填写你的接收者openid             subscribeMessage.put("template_id", " 填写你的模板ID");//填写你的模板ID             subscribeMessage.put("data", messageContentJson);             JSONObject subscribeMessageJson = new JSONObject(subscribeMessage); /*             String s = subscribeMessageJson.toJSONString();             System.out.println("JSONString:" + s); */             String s1 = subscribeMessageJson.toString();             System.out.println("String:" + s1);             byte[] bytes = s1.getBytes();               DataOutputStream wr = new DataOutputStream(connection.getOutputStream());             wr.write(bytes);             wr.close();               int statusCode = connection.getResponseCode();               InputStream inputStream = statusCode == 200 ? connection.getInputStream() : connection.getErrorStream();             BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));             StringBuilder response = new StringBuilder();             String line;             while ((line = reader.readLine()) != null) {                 response.append(line);             }             reader.close();             inputStream.close();             connection.disconnect();             System.out.println(response.toString());               connection.disconnect();           } catch (Exception e) {             e.printStackTrace();         } ———————————————— 版权声明:本文为CSDN博主「ImisLi」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/ImisLi/article/details/129946651 
总条数:1643 到第
上滑加载中