admin 管理员组文章数量: 893695
正则,异常、Collection、List集合
一,正则表达式
1,概述
正则表达式就是由一些特定的字符组成,代表的是一个规则。
2,书写规则
public boolean matches(String regex) | 判断字符串是否匹配正则表达式,匹配返回true,不匹配返回false。 |
---|---|
正则表达式的书写的规则
符号 | 含义 | 举例 |
---|---|---|
? | 0次或1次 | \d? |
* | 0次或多次 | \d* (abc)* |
+ | 1次或多次 | \d+ (abc)+ |
{} | 具体次数 | a{7} \d{7,19} |
(?i) | 忽略后面字符的大小写 | (?i)abc |
a((?i)b)c | 只忽略b的大小写 | a((?i)b)c |
符号 | 含义 | 举例 |
---|---|---|
[] | 里面的内容出现一次 | [abc] |
^ | 取反 | [^abc] |
&& | 交集,不能写单个的& | [a-z&&m-p] |
. | 任意字符 | \n 回车符号不匹配 |
\ | 转义字符 | \d |
\d | 0-9 | \d+ |
\D | 非0-9 | \D+ |
\s | 空白字符 | |
\S | 非空白字符 | [^\s] |
\w | 单词字符 | [a-zA-Z_0-9] |
\W | 非单词字符 | [^\w] |
() | 分组 | a(bc)+ |
| | 写在方括号外面表示并集 | ab|AB |
3,应用案例
需求: 校验用户输入的电话,邮箱,时间是否合法
通过往字符串的matches()方法中传入一个正则表达式来返回一个布尔值判断符不符合要求。
这里大致的了解就行,知道方法的使用,用到正则的时候上网上搜索
// 1: 创建一个邮箱正则String yreg = "[a-zA-Z0-9]+@[a-zA-Z0-9]+\\.[a-zA-Z0-9]+";String y1 = "110@qq.com";System.out.println(y1.matches(yreg));// 2: 创建一个时间正则String sreg = "([1-9]\\d{3}-)(([0]{0,1}[1-9]-)|([1][0-2]-))(([0-3]{0,1}[0-9]))";String t1 = "2023-3-03";System.out.println(t1.matches(sreg));// 3: 创建一个中文正则String zreg = "[\u4E00-\u9FA5]+";String z1 = "你好";System.out.println(z1.matches(zreg));
4,用于查找信息
正则的另一个作用,就是爬取一个文本中想要的数据;
需求如下:
请把下面文本中的电话,邮箱,座机号码,热线都爬取出来。
电话:18512516758,18512508907
或者联系邮箱: boniu@itcast.cn
座机电话:01036517895,010-98951256
邮箱:bozai@itcast.cn,
邮箱2:dlei0009@163.com,
热线电话:400-618-9090 ,400-618-4000,
4006184000,4006189090
// 1.定义爬取规则(正则表达式)String regex = "(\\w{1,}@\\w{2,10}(\\.\\w{2,10}){1,2})|(1[3-9]\\d{9})|(0\\d{2,5}-?\\d{5,15})|400-?\\d{3,8}-?\\d{3,8}";
// 编译正则Pattern pattern = Pattern.compile(regex);Matcher matcher = pattern.matcher(data);while (matcher.find()) {String group = matcher.group();System.out.println(group);}
18699997777boniu@itcast.cn01036517895010-98951256bozai@itcast.cndlei0009@163.com400-618-9090400-618-400040061840004006189090
5,用于搜索替换,分割内容
方法名 | 说明 |
---|---|
public String replaceAll(String regex , String newStr) | 按照正则表达式匹配的内容进行替换 |
public String[] split(String regex): | 按照正则表达式匹配的内容进行分割字符串,反回一个字符串数组。 |
String str = "abc5234d678efg345hij245klm5234no534pq23rst543uvw534xy4z";
// 需求去掉字符串中的数字,知道是符合正则的条件,都给替换为空,结果如下String s = str.replaceAll("[0-9]*", "");System.out.println(s); // abcdefghijklmnopqrstuvwxyz
String str = "abc2d3e4f5g6h5i3j5k6lmnop3qrstuvwxyz";
// 按照数字切分String[] split = str.split("\\d");for (String s1 : split) {System.out.print(s1 +"\t");}
// abc d e f g h i j k lmnop qrstuvwxyz
最后还需要了解正则中的贪婪匹配和非贪婪匹配;
需求: 我们只想要姓名
public class Regex2 {public static void main(String[] args) {String data ="欢迎张全蛋光临本系统! 他删库并跑路,欢迎李二狗子光临本系统!欢迎马六子光临本系统! " +"它浏览了很多好看的照片!欢迎夏统光临!他在六点钟购买了一台拖拉机!";String regex1 = "欢迎(.+)光临"; // .+是贪婪匹配,尽量多的匹配,从后往前,还个字符匹配String regex2 = "欢迎(.+?)光临"; // .+?是非贪婪匹配(懒模式) 尽量小的匹配,从前往后,还个字符匹配Pattern compile = Pattern.compile(regex2);Matcher matcher = compile.matcher(data);while (matcher.find()) {
// 这里一个小括号是一组String group = matcher.group(1);System.out.println(group);}
什么意思呢,贪婪也就是整个的去匹配,直接看结果,如果这里compile()中使用的是regex1,结果如下:
// 张全蛋光临本系统! 他删库并跑路,欢迎李二狗子光临本系统!欢迎马六子光临本系统! 它浏览了很多好看的照片!欢迎夏统
比较贪婪嘛,返回的是第一个光临开始到最后一个光临结束的字符串。
而如果是非贪婪匹配就是离自己最近的,使用regex2 结果如下:
张全蛋
李二狗子
马六子
夏统
最后还有一点,先看需求:有重复的数据的时候,怎么去重呢?在replaceAll中传如下正则,这里我不理解。但是老师讲到了,老师说是固定的,所以这里就先记着有这样的一个方法,等到用的时候能够想起来就行
// 有重复的数据的时候,怎么去重呢?String str = "我我我我爱爱爱爱java";String res = str.replaceAll("(.)\\1+","$1");System.out.println(res); // 我爱java
二,异常
1,认识异常
通过前面的学习,会有这样的一个问题,调用一个方法时,经常一部小心就出异常了,然后在控制台打印一些异常信息。其实打印的这些异常信息,就叫做异常。
因为写代码时经常会出现问题,Java的设计者们早就为我们写好了很多个异常类,来描述不同场景下的问题。而有些类是有共性的所以就有了异常的继承体系
抛出异常(throws)
在方法上使用throws关键字,可以将方法内部出现的异常抛出去给调用者处理。
方法 throws 异常1 ,异常2 ,异常3 ..{…}
捕获异常(try…catch)
直接捕获程序出现的异常。
try{
// 监视可能出现异常的代码!
}catch(异常类型1 变量){
// 处理异常
}catch(异常类型2 变量){
// 处理异常
}...
先来演示一个运行时异常产生
int[] arr = {11,22,33};
//5是一个不存在的索引,所以此时产生ArrayIndexOutOfBoundsExcpetion
System.out.println(arr[5]);
对于程序中的异常我们通常有两种解决方案。
- 第一种:使用throws在方法上声明,意思就是告诉下一个调用者,这里面可能有异常啊,你调用时注意一下。
// throws ParseException
public static void main(String[] args) throws ParseException{SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date d = sdf.parse("2028-11-11 10:24");System.out.println(d);
}
- 第二种:使用try…catch语句块异常进行处理。
public static void main(String[] args) throws ParseException{try {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date d = sdf.parse("2028-11-11 10:24");System.out.println(d);} catch (ParseException e) {e.printStackTrace();}}
2,自定义异常
尽管java 的编写这个提供了很多的异常类,但是无法为这个世界上的全部问题都提供异常类,如果企业自己的某种问题,想通过异常来表示,那就需要自己来定义异常类了。通过一个需求,来自定义异常
需求:写一个saveAge(int age)方法,在方法中对参数age进行判断,如果age<0 或者 >=130就认为年龄不合法,如果年龄不合法,就给调用者抛出一个年龄非法异常。
Java的API中是没有年龄非常这个异常的,所以我们可以自定义一个异常类,用来表示年龄非法异常,然后再方法中抛出自定义异常即可。
// 1、必须让这个类继承自Exception,才能成为一个编译时异常类。
public class AgeIllegalException extends Exception{public AgeIllegalException() {}public AgeIllegalException(String message) {super(message);}
}
- 写一个测试类,在测试类中定义一个saveAge(int age)方法,对age判断如果年龄不在0~130之间,就抛出一个AgeIllegalException异常对象给调用者。
public class ExceptionTest2 {public static void main(String[] args) {// 需求:保存一个合法的年龄try {saveAge2(225);System.out.println("saveAge2底层执行是成功的!");} catch (AgeIllegalException e) {e.printStackTrace();System.out.println("saveAge2底层执行是出现bug的!");}}//2、在方法中对age进行判断,不合法则抛出AgeIllegalExceptionpublic static void saveAge(int age){if(age > 0 && age < 150){System.out.println("年龄被成功保存: " + age);}else {// 用一个异常对象封装这个问题// throw 抛出去这个异常对象throw new AgeIllegalRuntimeException("/age is illegal, your age is " + age);}}
}
这个特殊说明一下: 自定义异常可能是编译时异常,也可以是运行时异常
1.如果自定义异常类继承Excpetion,则是编译时异常。特点:方法中抛出的是编译时异常,必须在方法上使用throws声明,强制调用者处理。2.如果自定义异常类继承RuntimeException,则运行时异常。特点:方法中抛出的是运行时异常,不需要在方法上用throws声明。
3,异常的处理
有如下的场景:A调用B,B调用C;C中有异常产生抛给B,B中有异常产生又抛给A;异常到了A这里就不建议再抛出了,因为最终抛出被JVM处理程序就会异常终止,并且给用户看异常信息,用户也看不懂,体验很不好。
此时比较好的做法就是:1.将异常捕获,将比较友好的信息显示给用户看;2.尝试重新执行,看是是否能修复这个问题。
- 第一种处理方式是,在main方法中对异常进行try…catch捕获处理了,给出友好提示。(只看过程)
public static void main(String[] args) {try {test1();} catch (FileNotFoundException e) {System.out.println("您要找的文件不存在!!");e.printStackTrace(); // 打印出这个异常对象的信息。记录下来。} catch (ParseException e) {System.out.println("您要解析的时间有问题了!");e.printStackTrace(); // 打印出这个异常对象的信息。记录下来。}}public static void test1() throws FileNotFoundException, ParseException {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date d = sdf.parse("2028-11-11 10:24:11");System.out.println(d);test2();}public static void test2() throws FileNotFoundException {// 读取文件的。InputStream is = new FileInputStream("D:/meinv.png");}
- 第二种处理方式是:在main方法中对异常进行捕获,并尝试修复
public static void main(String[] args) {// 需求:调用一个方法,让用户输入一个合适的价格返回为止。// 尝试修复while (true) {try {System.out.println(getMoney());break;} catch (Exception e) {System.out.println("请您输入合法的数字!!");}}}public static double getMoney(){Scanner sc = new Scanner(System.in);while (true) {System.out.println("请您输入合适的价格:");double money = sc.nextDouble();if(money >= 0){return money;}else {System.out.println("您输入的价格是不合适的!");}}}
三,集合
1,集合的分类
集合就像是一个容器,用来存储我们想存储的数据。根据集合中元素存储的特点进行分类学习,如下图所示:一类是单列集合元素是一个一个的,另一类是双列集合元素是一对一对的。
下面的笔记先记录Collection集合中的方法。
通过这个图我们可以看到,有两个接口实现了Collection接口,分别为Set接口和List接口;既然Collection是他们两个的父接口,所以定义的抽象方法,肯定是,他们两个能够通用的。下面先对这两个实现接口主要的特点做一个简单的介绍。
3,Collection集合中常用的方法
List集合: 集合中的元素有序<指存入时的顺序和取出的顺序一致>,2,集合中的元素可以重复,3,集合中的元素可以通过索引获取
Set集合(相反):集合中的元素无序,元素不可重复,不可通过索引获取
所以作为他们的父接口的抽象方法,必然是兼顾二者的方法;如下:
方法名 | 说明 |
---|---|
public boolean add(E e) | 把给定的对象添加到当前集合中 |
public void clear() | 清空集合中所有的元素 |
public boolean remove(E e) | 把给定的对象在当前集合中删除 |
public boolean contains(Object obj) | 判断当前集合中是否包含给定的对象 |
public boolean isEmpty() | 判断当前集合是否为空 |
public int size() | 返回集合中元素的个数。 |
public Object[] toArray() | 把集合中的元素,存储到数组中 |
Collection<String> c = new ArrayList<>();
//1.public boolean add(E e): 添加元素到集合
c.add("java1");
c.add("java1");
c.add("java2");
c.add("java2");
c.add("java3");
System.out.println(c); //打印: [java1, java1, java2, java2, java3]//2.public int size(): 获取集合的大小
System.out.println(c.size()); //5//3.public boolean contains(Object obj): 判断集合中是否包含某个元素
System.out.println(c.contains("java1")); //true
System.out.println(c.contains("Java1")); //false//4.pubilc boolean remove(E e): 删除某个元素,如果有多个重复元素只能删除第一个
System.out.println(c.remove("java1")); //true
System.out.println(c); //打印: [java1,java2, java2, java3]//5.public void clear(): 清空集合的元素
c.clear();
System.out.println(c); //打印:[]//6.public boolean isEmpty(): 判断集合是否为空 是空返回true 反之返回false
System.out.println(c.isEmpty()); //true//7.public Object[] toArray(): 把集合转换为数组
Object[] array = c.toArray();
System.out.println(Arrays.toString(array)); //[java1,java2, java2, java3]//8.如果想把集合转换为指定类型的数组,可以使用下面的代码
String[] array1 = c.toArray(new String[c.size()]);
System.out.println(Arrays.toString(array1)); //[java1,java2, java2, java3]//9.还可以把一个集合中的元素,添加到另一个集合中
Collection<String> c1 = new ArrayList<>();
c1.add("java1");
c1.add("java2");
Collection<String> c2 = new ArrayList<>();
c2.add("java3");
c2.add("java4");
c1.addAll(c2); //把c2集合中的全部元素,添加到c1集合中去
System.out.println(c1); //[java1, java2, java3, java4]
即ArrayList、LinkedList、vector、HashSet、LinkedHashSet、TreeSet集合都可以调用下面的方法。
四,Collection的遍历
有这样的一个动物类;用于编号id,姓名name这两个属性,包括对应的toString ,setter,getter方法,有参和无参构造,主要代码如下:
class Animal{private String id;private String name;// toString ,setter,getter方法,有参和无参构造,,,,省略
}
有这样一个测试类,在主方法中创建Animal中的元素:通过下面的几个方法来学习遍历集合
public static void main(String[] args) {Collection<Animal> animals = new ArrayList<>();animals.add(new Animal("1001","薛定谔的猫"));animals.add(new Animal("1002","芝诺的乌龟"));animals.add(new Animal("1003","麦克斯韦妖"));animals.add(new Animal("1004","拉普拉斯兽"));}
可能会想到最原始的遍历,for循环,这样就错了,因为Collection是Set的父类,所以必须要做到兼容,就不可能做到通过索引的方式来获取,只能通过特殊的方法,也就是下面的迭代器。
1,迭代器遍历集合
通过刚才的结构图中我们可以看到,Collection是Iterator的子类,所以就拥有了父类的迭代器的功能,在遍历的时候就可以使用。
先介绍Collection接口中获取迭代器的方法:
方法名称 | 说明 |
---|---|
Iterator iterator() | 返回集合中的迭代器对象,该迭代器对象默认指向当前集合的第一个元素 |
再介绍迭代器中的两个主要的方法:
方法名称 | 说明 |
---|---|
boolean hasNext() | 询问当前位置是否有元素存在,存在返回true ,不存在返回false |
E next() | 获取当前位置的元素,并同时将迭代器对象指向下一个元素处。 |
那么我们就可以使用了,
// 通过集合对象调用Iterator方法来获取一个迭代器,然后通过迭代中的两个方法,来实现遍历
Iterator<Animal> iterator = animals.iterator();
while (iterator.hasNext()) {System.out.println(iterator.next());
}
注意:在使用迭代器的时候,不要去增加或者删除元素,这样很容易造成异常。也不要在一次循环中,通过next方法获取两次对象,否则也可能出问题,因为next调用一次就会向下一个数据挪动。
比如:如果说是最后一个hasNext判断为true,能够进去,第一次使用next获取数据后,指针会向后移动,但是后面已经没有元素了,所以在下一次再去执行next的时候,就会发生异常。
2,增强for遍历
底层使用的还是迭代器
public Iterator<E> iterator() {return new Itr();}
格式:
for (元素的数据类型 变量名 : 数组或者集合) {}
增强for可以用来遍历集合或者数组。
增强for遍历集合,本质就是迭代器遍历集合的简化写法
for (Animal animal : animals) {System.out.println(animal);
}
3,forEach遍历集合
得益于JDK 8开始的新技术Lambda表达式,提供了一种更简单、更直接的方式来遍历集合。
方法名称 | 说明 |
---|---|
default void forEach(Consumer<? super T> action) | 结合lambda遍历集合 |
这个参数是一个函数式接口: 使用lambda方式如下: 网上对其的具体分析:【Java 8 新特性】Java Consumer示例_功能型接口consumer_猫巳的博客-CSDN博客
animals.forEach(animal -> System.out.println(animal));
五,List集合
1,特点,特有的方法
List系列集合特点: 有序,可重复,有索引
ArrayList:有序,可重复,有索引。
LinkedList:有序,可重复,有索引。
List集合因为支持索引,所以多了很多与索引相关的方法,当然,Collection的功能List也都继承了。
方法名称 | 说明 |
---|---|
void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
E get(int index) | 返回指定索引处的元素 |
2, List集合的遍历方式
List集合相比于前面的Collection多了一种可以通过索引遍历的方式,所以List集合遍历方式一共有四种:
- 普通for循环(只因为List有索引)
- 迭代器
- 增强for
- Lambda表达式
List<String> list = new ArrayList<>();
list.add("java");
list.add("PHP");
list.add("Python");//1.普通for循环
for(int i = 0; i< list.size(); i++){//i = 0, 1, 2String e = list.get(i);System.out.println(e);
}//2.增强for遍历
for(String s : list){System.out.println(s);
}//3.迭代器遍历
Iterator<String> it = list.iterator();
while(it.hasNext()){String s = it.next();System.out.println(s);
}//4.lambda表达式遍历
list.forEach(s->System.out.println(s));
3,ArrayList集合的底层原理
ArrayList集合底层是基于数组结构实现的,也就是说往集合容器中存储元素时,底层本质上是往数组中存储元素。 特点如下:
记住他的特点,可以根据具体的环境选择不同的集合以提高程序的效率
我们知道数组的长度是固定的,但是集合的长度是可变的,这是怎么做到的。原理如下:
数组扩容,并不是在原数组上扩容(原数组是不可以扩容的),底层是创建一个新数组,然后把原数组中的元素全部复制到新数组中去。
4,LinkedList底层原理
LinkedList底层是链表结构,链表结构是由一个一个的节点组成,一个节点由数据值、下一个元素的地址组成。如下图所示
现在要在B节点和D节点中间插入一个元素,只需要把B节点指向D节点的地址断掉,重新指向新的节点地址就可以了。如下图所示:
现在想要把D节点删除,只需要让C节点指向E节点的地址,然后把D节点指向E节点的地址断掉。此时D节点就会变成垃圾,会把垃圾回收器清理掉。
上面的链表是单向链表,它的方向是从头节点指向尾节点的,只能从左往右查找元素,这样查询效率比较慢;还有一种链表叫做双向链表,不光可以从做往右找,还可以从右往左找。如下图所示:
LinkedList集合是基于双向链表实现的,所以相对于ArrayList新增了一些可以针对头尾进行操作的方法,如下图示所示:
方法名称 | 说明 |
---|---|
public void addFirst(E e) | 在该列表开头插入指定的元素 |
public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
public E getFirst() | 返回此列表中的第一个元素 |
public E getLast() | 返回此列表中的最后一个元素 |
public E removeFirst() | 从此列表中删除并返回第一个元素 |
public E removeLast() | 从此列表中删除并返回最后一个元素 |
因为LinkedList底层是双向链表,所以下面的特点就很好理解了
特点:查询慢,增删相对较快,但对首尾元素进行增删改查的速度是极快的。
应用场景: 队列,栈 实例代码如下:
//1.创建一个队列:先进先出、后进后出
LinkedList<String> queue = new LinkedList<>();
//入对列
queue.addLast("第1号人");
queue.addLast("第2号人");
queue.addLast("第3号人");
queue.addLast("第4号人");
System.out.println(queue);//出队列
System.out.println(queue.removeFirst()); //第4号人
System.out.println(queue.removeFirst()); //第3号人
System.out.println(queue.removeFirst()); //第2号人
System.out.println(queue.removeFirst()); //第1号人
//1.创建一个栈对象
LinkedList<String> stack = new ArrayList<>();
//压栈(push) 等价于 addFirst()
stack.push("第1颗子弹");
stack.push("第2颗子弹");
stack.push("第3颗子弹");
stack.push("第4颗子弹");
System.out.println(stack); //[第4颗子弹, 第3颗子弹, 第2颗子弹,第1颗子弹]//弹栈(pop) 等价于 removeFirst()
System.out.println(statck.pop()); //第4颗子弹
System.out.println(statck.pop()); //第3颗子弹
System.out.println(statck.pop()); //第2颗子弹
System.out.println(statck.pop()); //第1颗子弹//弹栈完了,集合中就没有元素了
System.out.println(list); //[]
5,Vector
Vector底层和ArrayLlist底层一样也是一个对象数组,
protected Object[] elementData;
Vector是线程同步的,即线程安全的 操作方法带有 synchronized 关键字(先了解)
在开发中需要线程同步安全的时候,考虑使用 Vector
底层结构 | 版本 | 线程安全(同步)效率 | 扩容倍数 | |
---|---|---|---|---|
ArrayList | 可变数组 | jdk1.2 | 不安全,效率高 | 如果有参构造1.5倍 如果无参 1,第一次是10 2,第二次是1.5倍扩容 |
Vector | 可变数组 | 1.0 | 安全,效率不高 | 如果无参默认是10,满后,就按照两倍扩容 如果指定大小,则每次直接按两倍扩 |
本文标签: 正则,异常CollectionList集合
版权声明:本文标题:正则,异常、Collection、List集合 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/jishu/1687328118h90062.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论