day15——Java常用API(二):常见算法、正则表达式与异常处理详解
文章目录
- 一、常见算法详解
- 1.1 排序算法
- 冒泡排序
- 选择排序
- 1.2 查找算法
- 二分查找
- 二、正则表达式深入解析
- 2.1 正则表达式基础
- 2.2 正则表达式应用案例
- 测试
- 数据校验
- 信息爬取
- 文本处理
- 2.3 高级技巧
- 三、异常处理机制
- 3.1 异常体系结构
- 3.2 异常处理方式
- 抛出异常(throws)
- 捕获异常(try-catch)
- 3.3 自定义异常
- 3.4 异常处理最佳实践
- 四、总结与应用场景
- 4.1 算法应用场景
- 4.2 正则表达式应用场景
- 4.3 异常处理应用场景
本文将深入探讨Java中三大核心技术:常见算法、正则表达式和异常处理,帮助开发者掌握这些必备技能。
一、常见算法详解
1.1 排序算法
冒泡排序
原理:通过相邻元素比较和交换,每一轮将最大元素"冒泡"到数组末尾
关键步骤:
- 确定轮数:数组长度-1
- 每轮比较次数:数组长度-当前轮数-1
- 相邻元素比较,顺序错误则交换
// 冒泡排序实现
public static void bubbleSort(int[] arr) {for (int i = 0; i < arr.length - 1; i++) {for (int j = 0; j < arr.length - i - 1; j++) {if (arr[j] > arr[j + 1]) {int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;}}}
}
选择排序
原理:每轮选择一个固定位置元素,与后面元素比较找到最小元素并交换
关键步骤:
- 确定轮数:数组长度-1
- 每轮从当前位置开始向后比较
- 找到最小元素索引并交换
// 选择排序实现
public static void selectionSort(int[] arr) {for (int i = 0; i < arr.length - 1; i++) {int minIndex = i;for (int j = i + 1; j < arr.length; j++) {if (arr[j] < arr[minIndex]) {minIndex = j;}}int temp = arr[i];arr[i] = arr[minIndex];arr[minIndex] = temp;}
}
1.2 查找算法
二分查找
前提:数组必须有序
原理:每次将查找范围缩小一半
步骤:
- 定义左右边界(left, right)
- 计算中间位置 mid = (left + right) / 2
- 比较中间元素与目标值:
- 目标值大:left = mid + 1
- 目标值小:right = mid - 1
- 相等:返回mid
- 循环直到left > right,未找到返回-1
// 二分查找实现
public static int binarySearch(int[] arr, int target) {int left = 0;int right = arr.length - 1;while (left <= right) {int mid = left + (right - left) / 2;if (arr[mid] == target) {return mid;} else if (arr[mid] < target) {left = mid + 1;} else {right = mid - 1;}}return -1;
}
二、正则表达式深入解析
2.1 正则表达式基础
核心作用:
- 数据格式校验(如手机号、邮箱验证)
- 文本内容提取(爬取特定信息)
- 文本替换与分割
基础语法:
类别 | 表达式 | 说明 |
---|---|---|
字符类 | [abc] | 匹配a、b或c |
[^abc] | 匹配除a、b、c外的字符 | |
[a-zA-Z] | 匹配任意字母 | |
预定义字符 | . | 匹配任意单个字符 |
\d | 匹配数字[0-9] | |
\D | 匹配非数字 | |
\s | 匹配空白字符 | |
\S | 匹配非空白字符 | |
数量词 | X? | 0或1次 |
X* | 0或多次 | |
X+ | 1或多次 | |
X{n} | 恰好n次 | |
X{n,} | 至少n次 | |
X{n,m} | n到m次 | |
边界匹配 | ^ | 行首 |
$ | 行尾 | |
\b | 单词边界 |
2.2 正则表达式应用案例
测试
public static void main(String[] args) {// 1、字符类(只能匹配单个字符)System.out.println("a".matches("[abc]")); // [abc]只能匹配a、b、cSystem.out.println("e".matches("[abcd]")); // falseSystem.out.println("d".matches("[^abc]")); // [^abc] 不能是abcSystem.out.println("a".matches("[^abc]")); // falseSystem.out.println("b".matches("[a-zA-Z]")); // [a-zA-Z] 只能是a-z A-Z的字符System.out.println("2".matches("[a-zA-Z]")); // falseSystem.out.println("k".matches("[a-z&&[^bc]]")); // [a-z&&[^bc]] a到z,除了b和cSystem.out.println("b".matches("[a-z&&[^bc]]")); // falseSystem.out.println("ab".matches("[a-zA-Z0-9]")); // false 注意:以上带 [内容] 的规则都只能用于匹配单个字符// 2、预定义字符(只能匹配单个字符) . \d \D \s \S \w \WSystem.out.println("徐".matches(".")); // .可以匹配任意字符System.out.println("徐徐".matches(".")); // false// \转义System.out.println("\"");// \n \tSystem.out.println("3".matches("\\d")); // \d: 0-9System.out.println("a".matches("\\d")); //falseSystem.out.println(" ".matches("\\s")); // \s: 代表一个空白字符System.out.println("a".matches("\s")); // falseSystem.out.println("a".matches("\\S")); // \S: 代表一个非空白字符System.out.println(" ".matches("\\S")); // falseSystem.out.println("a".matches("\\w")); // \w: [a-zA-Z_0-9]System.out.println("_".matches("\\w")); // trueSystem.out.println("徐".matches("\\w")); // falseSystem.out.println("徐".matches("\\W")); // [^\w]不能是a-zA-Z_0-9System.out.println("a".matches("\\W")); // falseSystem.out.println("23232".matches("\\d")); // false 注意:以上预定义字符都只能匹配单个字符。// 3、数量词: ? * + {n} {n, } {n, m}System.out.println("a".matches("\\w?")); // ? 代表0次或1次System.out.println("".matches("\\w?")); // trueSystem.out.println("abc".matches("\\w?")); // falseSystem.out.println("abc12".matches("\\w*")); // * 代表0次或多次System.out.println("".matches("\\w*")); // trueSystem.out.println("abc12张".matches("\\w*")); // falseSystem.out.println("abc12".matches("\\w+")); // + 代表1次或多次System.out.println("".matches("\\w+")); // falseSystem.out.println("abc12张".matches("\\w+")); // falseSystem.out.println("a3c".matches("\\w{3}")); // {3} 代表要正好是n次System.out.println("abcd".matches("\\w{3}")); // falseSystem.out.println("abcd".matches("\\w{3,}")); // {3,} 代表是>=3次System.out.println("ab".matches("\\w{3,}")); // falseSystem.out.println("abcde徐".matches("\\w{3,}")); // falseSystem.out.println("abc232d".matches("\\w{3,9}")); // {3, 9} 代表是 大于等于3次,小于等于9次// 4、其他几个常用的符号:(?i)忽略大小写 、 或:| 、 分组:()System.out.println("abc".matches("(?i)abc")); // trueSystem.out.println("ABC".matches("(?i)abc")); // trueSystem.out.println("aBc".matches("a((?i)b)c")); // trueSystem.out.println("ABc".matches("a((?i)b)c")); // false// 需求1:要求要么是3个小写字母,要么是3个数字。System.out.println("abc".matches("[a-z]{3}|\\d{3}")); // trueSystem.out.println("ABC".matches("[a-z]{3}|\\d{3}")); // falseSystem.out.println("123".matches("[a-z]{3}|\\d{3}")); // trueSystem.out.println("A12".matches("[a-z]{3}|\\d{3}")); // false// 需求2:必须是”我爱“开头,中间可以是至少一个”编程“,最后至少是1个”666“System.out.println("我爱编程编程666666".matches("我爱(编程)+(666)+"));System.out.println("我爱编程编程66666".matches("我爱(编程)+(666)+"));}
数据校验
// 手机号校验(支持手机和座机)
public static boolean validatePhone(String phone) {return phone.matches("(1[3-9]\\d{9})|(0\\d{2,7}-?[1-9]\\d{4,19})");//表示手机号 1开头,第二位是3-9,后面是9位的数字 //或 座机号:以0开头,后面区号长度2-7位,-? -至多出现1次,开头1-9,后面4-19位的数字
}// 邮箱校验
public static boolean validateEmail(String email) {/*** dlei0009@163.com* 25143242@qq.com* itheima@itcast.com.cn*/return email.matches("\\w{2,}@\\w{2,20}(\\.\\w{2,10}){1,2}");// \\w 可能是英文字符或数字,{2,} 2位以上,有一个@,2-20位的字符或数字,//有一个. \\. 第一个\ 告诉第二个\:你就是个\,第二个\告诉后面的.:你就是个.//\\w{2,10} 2-10位的字符或数字// (\\.\\w{2,10}){1,2} 表示(\\.\\w{2,10}) 这个表达式可能出现1-2次
}
信息爬取
// 从文本中提取所有电话号码
public static List<String> extractPhones(String text) {List<String> phones = new ArrayList<>();String regex = "(1[3-9]\\d{9})|(0\\d{2,7}-?[1-9]\\d{4,19})";Pattern pattern = Pattern.compile(regex);Matcher matcher = pattern.matcher(text);while (matcher.find()) {phones.add(matcher.group());}return phones;
}// 需求:从以下内容中爬取出,手机,邮箱,座机、400电话等信息。public static void method1(){String data = " 来学习Java,电话:1111111111,18699997777" +" 或者联系邮箱:boniufe@com.cn,\n" +" 热线电话:400-618-90960 40061897090";// 1、定义爬取规则//(1[3-9]\\d{9})|(0\\d{2,7}-?[1-9]\\d{4,19}) 手机或座机的正则表达式//(\\w{2,}@\\w{2,20}(\\.\\w{2,10}){1,2}) 邮箱的表达式//(400-?\\d{3,7}-?\\d{3,7}) 400开头,-可有可无,3-7位数字String regex = "(1[3-9]\\d{9})|(0\\d{2,7}-?[1-9]\\d{4,19})|(\\w{2,}@\\w{2,20}(\\.\\w{2,10}){1,2})"+ "|(400-?\\d{3,7}-?\\d{3,7})";// 2、把正则表达式封装成一个Pattern对象Pattern pattern = Pattern.compile(regex);// 3、通过pattern对象去获取查找内容的匹配器对象。Matcher matcher = pattern.matcher(data);// 4、定义一个循环开始爬取信息while (matcher.find()){String rs = matcher.group(); // 获取到了找到的内容了。System.out.println(rs);}}
文本处理
正则表达式用于搜索替换、分割内容,需要结合String提供的如下方法完成:
// 需求1:请把 古力娜扎ai8888迪丽热巴999aa5566马尔扎哈fbbfsfs42425卡尔扎巴,中间的非中文字符替换成 “-”String s1 = "古力娜扎ai8888迪丽热巴999aa5566马尔扎哈fbbfsfs42425卡尔扎巴";System.out.println(s1.replaceAll("\\w+", "-")); //数字或字符替换成-
// 需求2(拓展):某语音系统,收到一个口吃的人说的“我我我喜欢编编编编编编编编编编编编程程程!”,需要优化成“我喜欢编程!”。String s2 = "我我我喜欢编编编编编编编编编编编编程程程";/*** (.)一组:.匹配任意字符的。* \\1 :为这个组声明一个组号:1号* +:声明必须是重复的字* $1可以去取到第1组代表的那个重复的字*/System.out.println(s2.replaceAll("(.)\\1+", "$1")); // 替换所有非汉字字符
String result = text.replaceAll("[^\\u4e00-\\u9fa5]", "-");// 按数字分割字符串
String[] parts = text.split("\\d+");
2.3 高级技巧
- 非贪婪匹配:在量词后加?(如.*?)
- 分组引用:使用()分组,$n引用分组
- 前瞻后顾:(?=exp)正向前瞻,(?!exp)负向前瞻
// 提取用户名(非贪婪匹配)
public static List<String> extractUsernames(String text) {List<String> usernames = new ArrayList<>();Pattern pattern = Pattern.compile("欢迎(.+?)光临");Matcher matcher = pattern.matcher(text);while (matcher.find()) {usernames.add(matcher.group(1)); // 获取第一个分组}return usernames;
}
三、异常处理机制
3.1 异常体系结构
Java异常体系:
异常分类:
- 编译时异常:必须显式处理(如IOException)
- 运行时异常:可处理也可不处理(如NullPointerException)
3.2 异常处理方式
抛出异常(throws)
public void readFile() throws IOException {// 可能抛出IOException的代码FileReader reader = new FileReader("file.txt");// ...
}
alt+回车 跑出异常
捕获异常(try-catch)
try {// 可能出错的代码int result = 10 / 0;
} catch (ArithmeticException e) {// 处理算术异常System.out.println("除数不能为零");e.printStackTrace();
} catch (Exception e) {// 处理其他异常System.out.println("发生未知错误");
} finally {// 无论是否异常都会执行System.out.println("清理资源");
}
3.3 自定义异常
自定义编译时异常:
public class InvalidAgeException extends Exception {public InvalidAgeException() {super("年龄无效");}public InvalidAgeException(String message) {super(message);}
}
自定义运行时异常:
public class BusinessException extends RuntimeException {public BusinessException(String message) {super(message);}
}
使用自定义异常:
public void setAge(int age) throws InvalidAgeException {if (age < 0 || age > 150) {throw new InvalidAgeException("年龄必须在0-150之间");}this.age = age;
}
3.4 异常处理最佳实践
上图表示A调用B,B调用C,处理异常时,C抛出异常给B,B抛出异常给A,A是最上层,不能再抛, 需要处理,处理方法有两种:(1)记录异常反馈给用户,(2)尝试重新修复
- 优先处理具体异常:先捕获具体异常,再捕获通用异常
- 避免空catch块:至少记录异常信息
- 使用finally释放资源:确保资源正确关闭
- 异常信息清晰:提供足够的问题描述
- 合理使用自定义异常:封装业务相关异常
- 异常与返回码结合:根据场景选择合适方式
// 标准异常处理模板
public void processFile(String path) {FileInputStream fis = null;try {fis = new FileInputStream(path);// 处理文件} catch (FileNotFoundException e) {log.error("文件不存在: {}", path);throw new BusinessException("文件不存在");} catch (IOException e) {log.error("文件读取错误", e);throw new BusinessException("文件处理失败");} finally {if (fis != null) {try {fis.close();} catch (IOException e) {log.warn("关闭文件流失败", e);}}}
}
四、总结与应用场景
4.1 算法应用场景
- 冒泡/选择排序:小型数据集排序
- 二分查找:有序数据集快速查找
- 复杂算法:大型系统性能优化
4.2 正则表达式应用场景
- 表单验证:手机号、邮箱、身份证号等
- 日志分析:提取关键信息
- 文本处理:批量替换、格式转换
- 数据清洗:去除无效字符
4.3 异常处理应用场景
- 资源管理:文件、网络连接等
- 业务校验:参数检查、状态验证
- 系统边界:第三方服务调用
- 错误恢复:事务回滚、重试机制
综合最佳实践:
- 对用户输入使用正则验证
- 核心业务逻辑使用自定义异常
- 数据处理结合合适算法
- 资源操作使用try-with-resources
// 综合应用示例:用户注册
public void registerUser(String username, String phone, String email) throws InvalidInputException {// 正则验证输入if (!validatePhone(phone)) {throw new InvalidInputException("手机号格式错误");}if (!validateEmail(email)) {throw new InvalidInputException("邮箱格式错误");}// 使用高效算法检查用户名是否已存在if (binarySearch(existingUsers, username) >= 0) {throw new BusinessException("用户名已存在");}// 保存用户(数据库操作)try {userDao.save(new User(username, phone, email));} catch (SQLException e) {throw new PersistenceException("用户保存失败", e);}
}
掌握这些核心技术,能够显著提升代码质量和开发效率。在实际项目中,合理组合使用算法、正则和异常处理,可以构建出健壮高效的Java应用程序。