• [技术干货] 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 
  • [技术干货] 三路排序算法(Java 实例代码)-转载
     三路排序算法 一、概念及其介绍 三路快速排序是双路快速排序的进一步改进版本,三路排序算法把排序的数据分为三部分,分别为小于 v,等于 v,大于 v,v 为标定值,这样三部分的数据中,等于 v 的数据在下次递归中不再需要排序,小于 v 和大于 v 的数据也不会出现某一个特别多的情况),通过此方式三路快速排序算法的性能更优。 二、适用说明 时间和空间复杂度同随机化快速排序。  三路快速排序算法是使用三路划分策略对数组进行划分,对处理大量重复元素的数组非常有效提高快速排序的过程。它添加处理等于划分元素值的逻辑,将所有等于划分元素的值集中在一起。  三、过程图示  图片看转载链接哈~我们分三种情况进行讨论 partiton 过程,i 表示遍历的当前索引位置: (1)当前处理的元素 e=V,元素 e 直接纳入蓝色区间,同时i向后移一位。 (2)当前处理元素 e<v,e 和等于 V 区间的第一个位置数值进行交换,同时索引 lt 和 i 都向后移动一位(3)当前处理元素 e>v,e 和 gt-1 索引位置的数值进行交换,同时 gt 索引向前移动一位。 最后当 i=gt 时,结束遍历,同时需要把 v 和索引 lt 指向的数值进行交换,这样这个排序过程就完成了,然后对 <V 和 >V 的数组部分用同样的方法再进行递归排序。  四、Java 实例代码 源码包下载: Download https://www.runoob.com/wp-content/uploads/2020/09/runoob-algorithm-QuickSort3Ways.zip  QuickSort3Ways.java 文件代码: package runoob;  /**  * 三路快速排序  */ public class QuickSort3Ways {     //核心代码---开始     // 递归使用快速排序,对arr[l...r]的范围进行排序     private static void sort(Comparable[] arr, int l, int r){         if (l >= r) {             return;         }         // 随机在arr[l...r]的范围中, 选择一个数值作为标定点pivot         swap( arr, l, (int)(Math.random()*(r-l+1)) + l );         Comparable v = arr[l];         int lt = l;     // arr[l+1...lt] < v         int gt = r + 1; // arr[gt...r] > v         int i = l+1;    // arr[lt+1...i) == v         while( i < gt ){             if( arr[i].compareTo(v) < 0 ){                 swap( arr, i, lt+1);                 i ++;                 lt ++;             }             else if( arr[i].compareTo(v) > 0 ){                 swap( arr, i, gt-1);                 gt --;             }             else{ // arr[i] == v                 i ++;             }         }         swap( arr, l, lt );         sort(arr, l, lt-1);         sort(arr, gt, r);     }     //核心代码---结束      public static void sort(Comparable[] arr){          int n = arr.length;         sort(arr, 0, n-1);     }      private static void swap(Object[] arr, int i, int j) {         Object t = arr[i];         arr[i] = arr[j];         arr[j] = t;     }      // 测试 QuickSort3Ways     public static void main(String[] args) {          // 三路快速排序算法也是一个O(nlogn)复杂度的算法         // 可以在1秒之内轻松处理100万数量级的数据         int N = 1000000;         Integer[] arr = SortTestHelper.generateRandomArray(N, 0, 100000);         sort(arr);         SortTestHelper.printArray(arr);     } } ———————————————— 版权声明:本文为CSDN博主「彼岸的菜鸟」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/2301_78835635/article/details/132144215 
  • [技术干货] 【Java 基础篇】Java 迭代器详解-转载
     导言 在 Java 中,迭代器是一种常用的设计模式,用于遍历集合中的元素。它提供了一种统一的方式来访问集合中的元素,而不必暴露集合的内部实现细节。本文将介绍 Java 迭代器的概念、使用方法和常见技巧,并提供一些示例代码。 一、迭代器的概念 迭代器是一种对象,它允许按顺序访问集合中的元素,而不需要知道集合的底层结构。通过使用迭代器,我们可以遍历集合并访问其中的元素,而无需关心集合的具体实现方式。 Java 提供了 Iterator 接口作为迭代器的基础接口。该接口定义了一组用于访问集合元素的方法,包括 hasNext、next 和 remove 等。  二、使用迭代器 要使用迭代器遍历集合,我们需要进行以下步骤:  获取集合的迭代器对象:通过调用集合的 iterator 方法获取迭代器对象。例如,对于 ArrayList 集合,可以使用 iterator() 方法获取迭代器对象。 List<String> list = new ArrayList<>(); // 添加元素到集合中  Iterator<String> iterator = list.iterator(); 遍历集合元素:通过使用迭代器的 hasNext 和 next 方法来遍历集合中的元素。hasNext 方法用于检查是否还有下一个元素,next 方法用于获取下一个元素的值。 while (iterator.hasNext()) {     String element = iterator.next();     // 处理元素 } 可选操作:迭代器还提供了 remove 方法,用于从集合中删除当前迭代的元素。需要注意的是,该方法只能在调用 next 方法后才能调用,且每次只能调用一次。 iterator.remove(); 三、迭代器的优势 使用迭代器遍历集合具有以下优势:  抽象集合的实现:通过使用迭代器,我们可以在不了解集合内部实现的情况下遍历和访问集合元素。这样,集合的实现细节对外部代码是透明的。  安全性:迭代器提供了一种安全的方式来遍历集合。它通过维护迭代器的状态来保证在遍历过程中不会出现并发修改的问题。  通用性:迭代器是一种通用的设计模式,在 Java 中被广泛应用于各种集合类型。无论是数组、列表、集合还是映射,我们都可以使用迭代器来遍历和访问元素。  四、迭代器的常见技巧 除了基本的使用方法外,还有一些常见的技巧可以帮助我们更好地使用迭代器。  1. 使用增强的 for 循环 Java 提供了增强的 for 循环(foreach 循环),可以简化迭代器的使用。它可以直接遍历集合中的元素,而不需要显式地使用迭代器。  for (String element : list) {     // 处理元素 } 2. 遍历过程中的修改 在使用迭代器遍历集合时,如果需要在遍历过程中修改集合,应使用迭代器的 remove 方法,而不是直接操作集合。直接操作集合可能会导致并发修改异常。  Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) {     String element = iterator.next();     if (shouldRemove(element)) {         iterator.remove(); // 使用迭代器删除元素     } } 3. 避免重复创建迭代器 在迭代器的使用过程中,应避免在每次迭代时都创建新的迭代器对象。如果需要多次遍历集合,可以在第一次遍历时创建迭代器,并在后续的遍历中重复使用该迭代器。  Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) {     String element = iterator.next();     // 处理元素 }  // 再次使用同一个迭代器进行遍历 while (iterator.hasNext()) {     String element = iterator.next();     // 处理元素 } 4. 使用迭代器的限制功能 迭代器提供了一些限制功能,如只读迭代器和单向迭代器。如果在遍历过程中不需要修改集合或只需要向前遍历,可以使用只读或单向迭代器,以提高性能和安全性。  List<String> list = new ArrayList<>(); // 添加元素到集合中  Iterator<String> readOnlyIterator = list.iterator(); Iterator<String> forwardIterator = list.listIterator(); 五、示例代码 下面是一个使用迭代器遍历集合并打印元素的示例代码:  List<String> list = new ArrayList<>(); list.add("Apple"); list.add("Banana"); list.add("Orange");  Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) {     String element = iterator.next();     System.out.println(element); } 在上面的示例中,我们创建了一个 ArrayList 集合,并使用迭代器遍历集合中的元素,然后打印每个元素的值。  总结 迭代器是一种在 Java 中常用的设计模式,用于遍历集合中的元素。通过使用迭代器,我们可以统一访问集合元素,而不需要了解集合的具体实现。本文介绍了迭代器的概念、使用方法和常见技巧,并提供了示例代码。  希望本文对你理解和使用 Java 迭代器提供了帮助。如果你有任何问题或建议,请随时留言。 ———————————————— 版权声明:本文为CSDN博主「繁依Fanyi」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_21484461/article/details/131424468 
  • [技术干货] 【JAVA】多态的概念与实际利用-转载
     前言 在面向对象(OOP)的程序设计语言中,多态与封装、继承合称为OOP的三大特性。在今天,我们就来学习一下JAVA中的多态是什么样子的。、 多态 指一个对象在不同情况下可以表现出不同的行为。Java多态性分为两种:编译时多态性(静态多态性)和运行时多态性(动态多态性)。 编译时多态性:也称为静态多态性,是指在编译期间就能确定方法的调用方式、参数类型及返回值类型等,主要通过方法重载实现。 运行时多态性:也称为动态多态性,是指在运行期间才能确定方法的调用方式,主要通过方法重写实现。Java中实现运行时多态性的关键是继承和方法重写。 具体来说,当一个类的子类重新定义了一个或多个已在父类中定义的方法时,那么子类的对象将可以调用重新定义的方法,而不是调用父类中的方法。这种现象称为方法重写。通过父类的引用变量来引用一个子类的对象时,父类引用变量不能直接调用子类中重新定义的方法,而是要调用子类中的方法,这种多态性称为动态多态性。 多态实现的重要条件 Java多态实现的重要条件包括: 继承:必须有继承关系,子类必须继承父类。 覆盖:子类必须重写父类的方法。 向上转型:可以声明一个父类引用类型的变量,将它指向一个子类对象。通过这种方式调用方法,就可以实现多态。 动态绑定:在运行时而不是编译时进行方法调用。这样就能够根据实际调用的对象类型来决定调用哪个方法,实现多态。 虚函数 在Java中,所有方法都是虚函数,因为它们都是在运行时动态绑定的。当子类继承父类时,它可以覆盖父类的方法,但具体调用哪个方法取决于对象的实际类型而不是变量的声明类型。因此,可以在运行时动态处理对象的多态性。 使用关键字“override”来覆盖父类的方法。子类中的方法必须与父类中被覆盖的方法具有相同的名称、参数列表和返回类型。这样,当调用子类中的方法时,根据对象的类型来确定应该调用哪个方法。 实例 假设我们有一个Animal类和一个Cat类,Cat类是Animal类的子类。 Animal类中定义了一个虚方法makeSound(),它会输出动物发出的声音: public class Animal {     public void makeSound() {         System.out.println("The animal makes a sound");     } } Cat类继承了Animal类并覆盖了makeSound()方法: public class Cat extends Animal {     public void makeSound() {         System.out.println("The cat meows");     } } 现在,我们可以实例化一个Animal对象和一个Cat对象,并调用它们makeSound()方法: Animal animal = new Animal(); Cat cat = new Cat(); animal.makeSound(); // 输出 "The animal makes a sound" cat.makeSound(); // 输出 "The cat meows" 注意,由于Cat类继承了Animal类,因此可以使用Cat对象来代替Animal对象,因为它们都是Animal类的实例。这就是面向对象编程中的多态性。 多态的实现方式 方式一:重写: 这个内容已经在上一篇文章中我有详细的讲过Java 重写(Override)与重载(Overload) 方式二:接口 生活中的接口最具代表性的就是插座,例如一个三接头的插头都能接在三孔插座中,因为这个是每个国家都有各自规定的接口规则,有可能到国外就不行,那是因为国外自己定义的接口类型。 java中的接口类似于生活中的接口,就是一些方法特征的集合,但没有方法的实现。具体可以看 java接口 这一章节的内容。 方式三:抽象类和抽象方法 ———————————————— 版权声明:本文为CSDN博主「许思王」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_73602725/article/details/133038845 
  • [技术干货] Java:不支持发行版本5-转载
     错误 Java:不支持发行版本5 详细错误 同学在github上找到一个微服务项目(基于maven进行构建),进行二开,导入项目运行控制台报错 Java:不支持发行版本5,笔者修改项目结构(F i l e FileFile→ \rightarrow→P r o j e c t S t r u c t u r e Project StructureProjectStructure)以及设置(F i l e FileFile→ \rightarrow→S e t t i n g s SettingsSettings)后,依旧报错 解决方案 对于pom.xml依赖文件加入如下配置 <properties>     <maven.compiler.source>1.8</maven.compiler.source>     <maven.compiler.target>1.8</maven.compiler.target> </properties> 报错原因 报错提示 “Java:不支持发行版本5” 表明项目使用了 Java 5 的发行版本,但当前环境不支持该版本。 解决原因 通过将项目的编译器源码和目标版本设置为 1.8,确保项目在 Java 8 环境下进行编译和运行。这样可以与当前环境的 Java 版本保持一致,避免报错。 笔者在此对pom.xml依赖文件加入如下配置进行详细解释 <properties>     <maven.compiler.source>1.8</maven.compiler.source>     <maven.compiler.target>1.8</maven.compiler.target> </properties> <properties> </properties> 标签用于定义项目的属性。在上述解决方案中,我们向 标签添加了两个属性: <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.source>:指定项目源代码的 Java 版本。在此处,我们将其设置为 1.8,表示使用 Java 8 的语法和特性编写源代码。 <maven.compiler.target>1.8</maven.compiler.target> <maven.compiler.target>:指定编译生成的字节码的 Java 版本。同样地,我们将其设置为 1.8,以确保编译后的代码与 Java 8 兼容。 通过将这两个属性设置为 1.8,我们告诉 Maven 使用 Java 8 的编译器进行编译,并生成与 Java 8 兼容的字节码文件。这样可以解决报错问题,确保项目在 Java 8 环境下正确编译和运行。 参考文献 解决方案参考错误“ Java:不支持发行版本5”的正确解决方案 解决原因参考chatgpt 原创不易 转载请标明出处 如果对你有所帮助 别忘啦点赞支持哈 ———————————————— 版权声明:本文为CSDN博主「飞滕人生TYF」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/T_Y_F_/article/details/131317847 
总条数:737 到第
上滑加载中