1.异常
1.1异常的简介
1)什么是异常
异常,是对程序在运行过程中,遇到的种种的不正常的情况的描述。异常,在Java中,使用 Exception类来描述。如果程序遇到了未经处理的异常,将会导致程序无法编译或无法继续运行。
2) 异常的继承体系
在Java中,使用类 Throwable 来描述所有的不正常的情况。 Throwable有两个子类,Error 和 Exception 。其中 Error 用来描述发生在JVM级别的错误信息,这些错误,无法被处理。 Exception 用来描述程序在编译或者运行的过程中遇到的异常信息,这些异常一旦处理了,对程序的编译和运行是没有影响的。
继承体系的层次结构,参考下图:
3)异常的分类
普通的异常发生,将会导致程序无法进行正常的编译。这种异常,称为None-Runtime Exception,非运行时异常,或者称为编译时异常,也有人称之为检查性异常,原因在于该种异常,都是javac编译器能直接检查到的异常。
编译器javac.exe 在将源文件(*.java)编译成字节码文件(*.class)时,会主动检查代码的语法格式,如果不正确,就不进行编译工作。如果正确了,才会执行编译工作。
而在Exception的子类RuntimeException中,对异常进行了自动的处理,使其可以正常的编译 运行。但是一旦出现未经处理的异常,依然会导致程序无法继续向下执行。这样的异常称为-- Runtime Exception,运行时异常。 这类异常,通常编译器是不做任何检查的,所以也叫非检查性异常。
总之,异常可以分为 运行时异常 和 编译时异常
1.2异常的处理
异常如果不处理,程序就停止了。但是在实际生产环境中,不能因为异常就终止程序,否则客户体验度会非常不好。
1)异常处理的语法
异常,Exception,父类Throwable。与其平级的是Error(Error错误,程序员无法处理,属于JVM级别的错误)
1.程序员可以处理异常,如果不处理,即JVM帮忙处理,简单粗暴,就是终止程序的运行。
2.所以,程序员应该处理异常,不然的话,客户体验非常差。
3.Exception分类:
-运行时异常RuntimeException,非检查型异常
-非运行时异常 None-RuntimeException,检查性异常,编译时异常。
4.异常处理语法。
try{
//可能出现异常的代码块
}catch(异常类型 变量){
//发生了异常并捕获到异常时,要执行的模块。
}
5.catch模块执行的时机:
try模块里出现了异常情况,catch模块才会执行。 注意,catch里要写处理语句。
6.try-catch 不会终止程序的后续代码的执行。7. catch模块如何处理异常呢? 比如打印异常信息,供程序员查看,然后进行调试,或者继续抛给调用者
8.jvm创建的异常对象如果与catch捕获的异常对象类型匹配(或者向上造型没问题),就会就将地址值赋值给异常类型变量。
try {
// 将可能会出现异常的代码放到这里执行
// 一旦try中的代码出现了异常,则从出现异常的位置开始,到try的大括号的扩回,这一段代码就不执行了。
}catch (需要捕获的异常的类型 标识符) {
// 如果try中的代码出现了异常,并且异常对象的类型和捕获的类型一致
// 这里的代码将会执行
// 一旦一个异常被捕获了,那么这个异常将不再影响程序的编译和执行
}
代码测试:
public class Exception01 {
public static void main(String[] args) {
/**
* 1.在执行try里的代码片段时,如果出现了异常信息,JVM会主动帮助创建异常对象
* 2.catch就会将对象的地址捕获到,赋值给小括号里的变量(类型匹配)
* 3.当catch捕获到异常对象的地址后,就会执行后面{}里的逻辑代码。没有捕获到就不执行{}。
* 4.当执行完catch的{}后,会继续向后执行后续的代码。
*/
try{
int[] nums = {1,2,3,4,5};
for (int i = 0; i <= nums.length; i++) {
int num = nums[i];
System.out.println(num);
}
}catch(Exception e){
// System.out.println("发生了异常");
e.printStackTrace();
}
}
}
- 在执行try里的代码片段时,如果出现了异常信息,JVM会主动帮助创建异常对象
- catch就会将对象的地址捕获到,赋值给小括号里的变量(类型匹配)
- 当catch捕获到异常对象的地址后,就会执行后面{}里的逻辑代码。没有捕获到就不执行{}。
- 当执行完catch的{}后,会继续向后执行后续的代码。
2)多种异常的处理
当代码片段中可能出现多种不同类型的异常时,我们每一个异常都想要处理的情况下,使用多catch处理。
语法:
try {
// 可能出现异常的代码
}catch (异常类型1 标识符) {
// 针对异常类型1的处理
}catch (异常类型2 标识符) {
// 针对异常类型2的处理
}catch (异常类型3 标识符) {
// 针对异常类型3的处理
}
...
注意:一旦有继承关系,必须先写子类型。
案例演示(注意注释部分)
public class Exception02 {
public static void main(String[] args) {
/**
* 多catch情况:
* 1. 多个异常类型没有继承关系,书写顺序无所谓
* 如:ArrayIndexOutOfBoundsException和NullPointerException
* 2.一旦有继承关系,必须先写子类型。
* 如:Exception和ArrayIndexOutOfBoundsException
*/
try {
int[] nums =new int[5];
nums[5] = 100;
String str = null;
int length = str.length();
}catch (ArrayIndexOutOfBoundsException e){
System.out.println("数组下标越界");
}catch (NullPointerException e){
System.out.println("空指针异常");
}catch (Exception e){
System.out.println("异常");
}
//简化版本1: 没有继承关系的异常类型,可以写在一个catch中,使用|分开
//前提条件: 处理逻辑一样
try {
int[] nums =new int[5];
nums[5] = 100;
String str = null;
int length = str.length();
}catch (ArrayIndexOutOfBoundsException|NullPointerException e){
e.printStackTrace();
}
//简化版本2: 使用这些异常的共同父类型即可 前提条件:处理逻辑一样
try {
int[] nums =new int[5];
nums[5] = 100;
String str = null;
int length = str.length();
}catch (Exception e){
e.printStackTrace();
}
}
3) finally异常
finally模块:
1.位于try或者catch模块后,作为异常捕获的结尾。
2.无论try里是否发生了异常,都会执行finally模块
3.应用场景:finally模块一般用于做流的关闭,其他资源释放等操作。
public class Exception03 {
public static void main(String[] args) {
String[] names =new String[3];
try{
String name = names[1];
int length = name.length();
}catch(Exception e){
e.printStackTrace();
}finally {
names[1] = "sad";
}
System.out.println(Arrays.toString(names));
System.out.println("q");
//finally的应用场景:一般用于流的关闭操作。
InputStream is =null;
try{
is = Exception.class.getClassLoader().getResourceAsStream("");
BufferedImage img = ImageIO.read(is);
}catch (Exception e){
e.printStackTrace();
}finally {
try {
is.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
研究一下finally和return的特点(finally代码部分里面有return)
1. 当try里有return关键字,finally模块没有return。
先执行finally模块代码,然后再执行try里的return关键字。并且finally里面的复制操作并不会影响到try里的return返回的值。
2. try和finally里都有return, 一定执行的finally里的return.
测试代码:
public class Exception04 {
public static void main(String[] args){
int result = test4();
System.out.println(result);
}
public static int test4(){
//finally 语句块中有 return 语句
int i = 1;
try {
i++;
System.out.println("try block, i = " + i);
// throw new Exception();
return i;
} catch (Exception e) {
i++;
System.out.println("catch block i = " + i);
return i;
} finally {
i++;
System.out.println("finally block i = " + i);
// return i;
}
}
}
结果:
1.3自定义异常
为什么要自定义异常?
如果系统给我们提供的异常类型,已经不能满足我们的需求了,或者不知道用哪个了。此时就需要进行异常的自定义。
如何自定义异常类型:
1. 继承Exception 或者继承RuntimeException ,定义两个构造器即可。模拟已经存在的子类异常
2. 继承Exception的自定义异常,是编译时异常
3. 继承RuntimeException的自定义异常,是运行时异常
throw:
用在程序方法中,表示抛出异常。一个异常对象实例化完成后,没有任何意义,只有当把这个异常抛出之后,这个异常才会生效,具有阻止程序的编译或者运行的意义。
throws:
1. 书写位置:在方法的定义格式中,也就是形参列表的后面。用于声明异常类型。
修饰词 返回值类型 方法名(形参列表) throws 异常类型名{
}
2. 当我们throw的是RuntimeException类的异常时,不需要throws来声明异常类型, 声明没有意义,JVM运行程序才能知道。
3. 当我们throw的是Exception异常时,必须使用throws来声明异常类型,用来告知 谁调用这个方法,谁处理异常。
throw和throws的特点:
1. throw是用在方法里,用于将一个异常对象抛出,自己不处理,抛给调用者,谁调用这个方法,谁就是调用者。
2. throws是用在方法的定义上。表示告诉调用者需要处理的异常类型。
3. throw的如果是编译时异常,必须throws(必须告诉调用者)
throw的如果是runtimeException, 就没有必要throws了。
案例代码如下:
public class Exception05 {
public static void main(String[] args) {
try {
Person p = new Person("小明",130);
}catch (AgeIllegalException e){
e.printStackTrace();
}
}
}
class Person{
private String name;
private int age;
public Person(String name, int age) throws AgeIllegalException{
this.name = name;
if (age<1 || age>120){
throw new AgeIllegalException("年龄不合理,不应该<1或者大于120");
}
this.age = age;
}
}
class AgeIllegalException extends Exception {
public AgeIllegalException() {
super();
}
public AgeIllegalException(String message) {
super(message);
}
}
2.字符串
2.1 字符串的简介
字符串:是由若干个字符组成的一个有序的序列。在Java中,使用String这个类来描述字符串。
java.lang.String 使用final修饰,不能被继承
字符串底层封装的是字符数组及其针对数组的操作方法
字符串一旦创建,对象永远无法改变,但字符串引用可以重新赋值
字符串在内存中采用Unicode编码方式,任何一个字符对应两个字节的定长编码(一般字母数字占一个字节,都没有超过一个字节的长度)
字符串的索引值从0开始。
2.2 字符串常量池
JVM为了提升性能和减少内存开销,避免重复创建字符串,其维护了一块特殊的内存空间,即字符串常量池 。当需要使用静态字符串(字面量/常量/常量连接的结果)时,先去检查字符串
常量池是否存在该字符串,若存在,则直接返回该字符串的引用地址;若不存在,则在字符串常量池中创建字符串对象,并返回对象的引用地址。
常量池的内存位置:
在 JDK 1.7 之前,运行时常量池(包括字符串常量池)存放在方法区。
在 JDK 1.7 时,字符串常量池被从方法区转移至 Java 堆中,注意并不是运行时常量池,而是字符串常量池被单独转移到堆,运行时常量池剩下的东西还是方法区中。
在 JDK 1.8 时,此时字符串常量池还在堆中。
字符串的拼接以及常量池
字符串常量池:
jvm为了提升性能和减少内存开销,专门为字符串的一些操作,在内存中专门提供了一块区域,用于存储字符串对象。该内存区域就是字符串常量池(字符串缓冲区,缓冲池)
什么时候会用到字符串常量池?
当使用字面量给变量赋值时,会先去常量池中寻找是否存在该字面量的对象,如果有,就将其地址直接给变量。如果没有,就将该字面量对象创建在常量池中,然后将其地址赋值给变量。
代码测试:
public class StringDemo01 {
public static void main(String[] args) {
String s1 ="abc";
String s2 = "abc";
//判断s1和s2的对象地址是不是同一个。
System.out.println(s1 == s2);
String s3 = "abc"+"d";//两个字面量做拼接操作时,编译器在编译期间就做了优化操作。
//直接计算出结果:即s3="abcd"
String s4 = "abcd";
System.out.println(s4 == s3);
String s5 = "d";
String s6 = "abc" + s5;//拼接操作只要有变量参与。那么javac就不会进行优化,只能在运行期间进行计算。
//计算后的结果,存储在堆中,并不是常量池里。
System.out.println(s4 == s6);
String s7 = "ab" + "c" + s5;//优化成了"abc"+s5。但是依然在运行期间与s5做的计算,不会去堆里找,是新对象。
System.out.println(s7 == s6);//s6 s7 都在堆中,属性一样,但是地址不同。
String s8 = new String("d");
String s9 = "abc"+s8;
System.out.println(s7 == s9);//false
String s10 = new String("abc");
String s11 = new String("abc");
//问内存中的堆中有多少个对象 //至少产生了五个。abc,abch,abchd, new(abchd) s13=abchd
String s12 = "h";
String s13 = new String("abc"+s12+"d");
}
}
2.3 构建字符串对象
1.可以直接赋值字面量
如下:
String name ="michael";
2.可以调用构造器
String() : 初始化一个新创建的空字符序列的字符串对象。
String(String str) : 初始化一个新创建的字符串对象,使其字符序列与参数相同。换句话说,新创建的对象是该参数的副本。
String(byte[] bytes): 使用默认编码集解码byte数组,构建一个字符串对象。
String(byte[] bytes,String charsetName): 使用指定编码集charsetName 解码byte数组,构建一个字符串对象。
String(byte[] byte, int offset, int length): 使用默认编码集解码byte数组的从offset开始,length个元素,构建一个字符串对象。
String(char[] value): 初始化一个字符串对象,使其字符序列包含参数的元素。
代码演示如下:
public class StringDemo02 {
public static void main(String[] args) throws UnsupportedEncodingException {
//无参构造器,构造的是一个"" 实际上底层是char[] value = new char[0];
String str = new String();
System.out.println(str.length());
//构造器 String(String str)
String s1 = new String("michael");
System.out.println("s1 = " + s1);
//构造器 String(byte[] bytes) : 传入一个字节序列,按照默认字符集转成对应字符。
byte[] bytes ={-28,-72,-83,-27,-101,-67};
String s2 = new String(bytes);
System.out.println("s2 = " + s2);
//构造器: String(byte[] bytes,String charsetName);
String s3 = new String(bytes,"utf-8");
System.out.println("s3 = " + s3);
//String(byte[] bytes , int offset ,int length)
String s4 = new String(bytes,3,3);
System.out.println("s4 = " + s4);
//String(char[] value)
String s5 = new String(new char[]{'n','n','m','n'});
System.out.println("s5 = " + s5);
}
}
结果:
![](https://i-blog.csdnimg.cn/direct/8d673b4727b64edcb02b1353ed9f5c8f.png)
2.4 字符串的常用方法:
1) length方法
-
length()方法可以获取字符串对象的字符个数。
-
一定要注意,数组的元素个数的获取方式是 引用.length, 不是方法,是属性。
2) 使用indexOf实现检索
-
String类型提供了几个重载的indexOf方法:用于返回指定字符串在此字符串中第一次出现的索引,找不到返回-1。
int indexOf(String str)
int indexOf(String str,int fromIndex)
-
String还定义了lastIndexOf方法:用于返回指定字符串在此字符串中最后一次出现的索引,找不到返回-1。
int lastIndexOf(String str)
int lastIndexOf(String str,int endIndex)
3) 使用substring获取子串
-
作用:substring方法用于返回一个字符串的子字符串
-
常用重载方法。
String substring(int beginIndex)
String substring(int beginIndex,int endIndex)
4) trim截掉空格
- 作用:去掉一个字符串的前与后的空字符,不会去掉中间的空字符
String trim()
5) charAt获取字符
- 作用:返回指定索引处的char值
char charAt(int index)
6) startsWith和endsWith
- 作用:检查一个字符是否以指定的字符串为前缀或后缀
boolean startsWith(String str)
boolean endsWith(String str)
7) 大小写变换
- 作用:对字符串中的字母进行转换成全部大写或全部小写
String toUpperCase()
String toLowerCase()
8) toCharArray
- 作用: 将字符串转变成字符数组
char[] toCharArray()
9) valueOf
- 作用:将其他类型转换成字符串类型
static String valueOf(int value)
static String valueOf(double d)
static String valueOf(char[] ch)
static String valueOf(Object obj)
10) equals与==
-
equals方法用于判断两个字符串的内容是否一样
-
==用于判断两个字符串是不是同一个对象
boolean equals(String str)
测试代码:
public class StringDemo03 {
public static void main(String[] args) {
//length方法
String s1 = "welcome to china";
System.out.println(s1.length());
/**
* 用于检测子字符串所在的位置,如果没有找到,返回-1。
* int indexOf(String str)
* int indexOf(String str,int fromIndex)
*/
int index = s1.indexOf("come");
System.out.println("index = " + index);
//找到第二个o的下标
// index =s1.indexOf("o",5);
index =s1.indexOf("o",s1.indexOf("o")+1);
System.out.println("index = " + index);
/**
* 检索子字符串最后一次出现的索引。
* int lastIndexOf(String str)
* int lastIndexOf(String str,int endIndex)
* 检索子串最后一次出现的位置。从左到右一直检索到指定索引endIndex.
*/
index = s1.lastIndexOf("o");
System.out.println("index = " + index);
index = s1.lastIndexOf("o",8);
System.out.println("index = " + index);
/**
* 用于截取子字符串:
* String substring(int beginIndex)
* String substring(int beginIndex,int endIndex)
*/
String substring = s1.substring(11);
System.out.println("substring = " + substring);
String substring1 = s1.substring(8,10);
System.out.println("substring1 = " + substring1);
System.out.println("substring1 =" + substring1.length());
/**
* trim截掉空格:截掉字符串的前后空格,中间的不截掉
*
*/
String s2 = " michae l";
System.out.println(s2.length());
s2 = s2.trim();
System.out.println(s2.length());
/**
*
* charAt()获取字符:获取指定下标上的字符, 可用于单个字符的字符串,转成字符类型
*
*/
char c = s1.charAt(1);
System.out.println("c = " + c);
/**
* boolean startsWith(String str) :判断字符串是否以指定子串开头。
* boolean endsWith(String str): 判断字符串是否以指定子串结尾。
*
*/
boolean we = s1.startsWith("we");
System.out.println("we = " + we);
boolean an = s1.endsWith("na");
System.out.println("an = " + an);
/**
* String toUpperCase()
* String toLowerCase()
*/
String s3 = "你好welComE+";
System.out.println(s3.toUpperCase());
System.out.println(s3.toLowerCase());
/**
* char[] toCharArray()
*/
char[] charArray = s1.toCharArray();
System.out.println(charArray);
System.out.println(Arrays.toString(charArray));
/**
* 下面的方法是静态的,使用类名调用,作用是将参数类型转成字符申形式.
* static String valueOf(int value)
* static String valueOf(double d)
* static String valueOf(char[] ch)
* static String valueOf(Object obj)
*
*/
String num = String.valueOf(10001);
System.out.println(num);
/**
* equals 与 ==
* equals: 用于比较两个对象的属性是否一样,不是地址值。
* ==: 比较的是地址值
*
*/
String s10 = new String("hello");
String s11 = new String("hello");
System.out.println(s10 == s11);//false
System.out.println(s10.equals(s11));//true
}
}
2.5 StringBuilder和StringBuffer
由于字符串是不可变的,因此对字符串进行操作的时候,比较的麻烦,每次的操作都需要实例化一个 临时的空间来存放结果,效率不高。可以使用其他的某些类,来实现高效率的字符串的操作:StringBuilder和StringBuffer。
StringBuilder和StringBuffer,都是一个可变的字符序列(即可变的字符串),对象创建后可以通过调用方法来实现字符序列的改变,StringBuilder和StringBuffer类中的方法完全相同。 只需要学会其中一个,另一个就会了。下面以StringBuilder来说明
1) 常用构造器
StringBuilder(): 构造一个不带任何字符的字符串生成器,其初始容量为16个字符。
StringBuilder(String str) : 构造一个字符串生成器,其初始化为指定的字符串内容。
2)常用方法
1) append方法
作用:将指定字符串追加到字符串之后。
StringBuilder append(boolean b)
StringBuilder append(String str)
2) insert方法
作用:将指定字符串插入到字符串的指定索引处
StringBuilder insert(int index,String str)
3) delete方法
作用:删除字符串中的一部分
StringBuilder delete(int start , int end)
4) reverse方法
作用:将字符序列进行反转(左右颠倒)
StringBuilder reverse()
5) toString方法
作用:将StringBuilder对象转变成String对象
String toString()
6)replace方法
作用:替换字符
StringBuilder replace()
测试代码:
public class StringDemo04StringBuilder {
public static void main(String[] args) {
/**
* 构造器:
* StringBuilder(): 构建一个空字符串的StringBuilder对象,注意,其内部的属性char[]的长度是16。
* StringBuilder(String str)
*
*/
StringBuilder sb = new StringBuilder();
StringBuilder sb2 = new StringBuilder("");
/**
* 常用方法:
* StringBuilder append(boolean b)
* StringBuilder append(String str)
* StringBuilder insert(int index,String str)
* StringBuilder insert(int index,String str)
* StringBullder replace()
* StringBuilder reverse()
* String toString()
*/
StringBuilder s3 = sb.append("欢迎");
System.out.println(sb==s3);
sb.append("胡蝴蝶").append("asdaad").append(",滴滴答答大");
System.out.println(sb.toString());
sb.insert(4 ,"伟大的");
System.out.println(sb);
//包前不包后
sb.delete(1,2);
// sb.replace("");
System.out.println(sb2);
sb.replace(3,6,"美丽的");
sb.reverse();
System.out.println(sb);
}
}
3)三者的区别
StringBuilder是可变字符串,如果进行字符串的内容计算,建议选择StringBuilder,这样性能更好一些。
java的字符串连接的过程是利用StringBuilder实现的
StringBuilder 也是final修饰的类型,不能被继承
StringBuilder没有重写equals方法和toString()
StringBuilder是非线程安全的,并发处理,性能稍快
StringBuffer是线程安全的,同步处理,性能稍慢
3. 正则表达式
3.1 简介
正则表达式(Regular Expressions),是一个特殊的字符串,可以对普通的字符串进行校验检测等工作。正则表达式不是Java特有的,它是一套独立的语法,可以在java,c++,python等语言中使用。
正则表达式,最基本的使用场景是用来做校验,校验一个字符串是否满足预设的规则。在校验的基础 上,又添加了若干个其他的引用场景,例如: 批量的查找、替换、切割...
3.2 基本语法
1) 字符集合
[] : 表示匹配括号里的任意一个字符。
[abc] :匹配a 或者 b 或者 c
[^abc] : 匹配任意一个字符,只要不是a,或b,或c 就表示匹配成功
[a-z] : 表示匹配所有的小写字母的任意一个。
[A-Za-z] :表示匹配所有的小写字母和大写字母的任意一个。
[a-zA-Z0-9]:表示匹配所有的小写字母和大写字母和数字的任意一个。
[a-z&&[^bc]] :表示匹配所有的小写字母除了b和c, 只要匹配上就是true.
2) 预定义字符集
\d: 用于匹配数字字符中的任意一个
\w: 匹配单词字符中的任意一个 单词字符就是a-zA-Z0-9_
\D: 用于匹配非数字字符中的任意一个
\W: 用于匹配非单词字符中的任意一个
\s: 用于匹配空格,制表符,退格符,换行符等中的任意一个
\S: 用于匹配非空格,制表符,退格符,换行符等中的任意一个
. : 用于匹配任意一个字符
3)数量词
X? :匹配0个或1个
X* :匹配0个或1个以上
x+ :匹配1个以上
X{n} :匹配n个
X{m,}:匹配m个以上
X{m,n}:匹配m~n个
4) 分组:()
在正则表达式上可以使用()来进行对一些字符分组,并可以使用逻辑运算符|来进行选择匹配
reg:
String regex = "(135|137)[\\d]{8}" //匹配手机号是135或者是137开头的
5)^和$
^: 表示严格从头到尾
$: 表示匹配到结尾
测试代码:
/**
* 正则表达式:
* 1.正则表达式是一个特殊的字符串,可以对普通的字符串进行校验、检验等。
* 2.它是一套独立的语法,不是某一个编程语言里独有的,因此大多数编程语言都可以拿过来使用。
*
*/
public class RegexDemo01 {
public static void main(String[] args) {
/**
* 字符集合
* [] : 表示匹配括号里的任意一个字符。
* [abc] :匹配a 或者 b 或者 c
* [^abc] : 匹配任意一个字符,只要不是a,或b,或c 就表示匹配成功
* [a-z] : 表示匹配所有的小写字母的任意一个。
* [A-Za-z] :表示匹配所有的小写字母和大写字母的任意一个。
* [a-zA-Z0-9]:表示匹配所有的小写字母和大写字母和数字的任意一个。
* [a-z&&[^bc]] :表示匹配所有的小写字母除了b和c, 只要匹配上就是true.
*/
System.out.println("a".matches("[abcdefg]"));
String regex ="[^hg]";
String str ="o";
System.out.println(str.matches(regex));//true
System.out.println("+".matches("."));//匹配一下+是不是一个字符串。
System.out.println("-".matches("\\."));//false \\.通配符.变成了普通的点符号
/**
* \d: 用于匹配数字字符中的任意一个
* \w: 匹配单词字符中的任意一个 单词字符就是a-zA-Z0-9_
* \D: 用于匹配非数字字符中的任意一个
* \W: 用于匹配非单词字符中的任意一个
* \s: 用于匹配空格,制表符,退格符,换行符等中的任意一个
* \S: 用于匹配非空格,制表符,退格符,换行符等中的任意一个
* . : 用于匹配任意一个字符
*
*/
System.out.println("a".matches("\\w"));
System.out.println("a".matches("[\\w]"));
/**
* X? :匹配0个或1个
* X* :匹配0个或1个以上
* x+ :匹配1个以上
* X{n} :匹配n个
* X{m,}:匹配m个以上
* X{m,n}:匹配m~n个
*/
//匹配密码:密码必须是8位的数字或者字母组合
System.out.println("assddssd".matches("[a-zA-Z0-9]{8}"));
//匹配用户名:用户名室友字母数字下划线组成,5~8位
System.out.println("sdasdsds".matches("\\w{8}"));
System.out.println("1".matches("[a-z]?"));
System.out.println("".matches("[a-z]?"));
System.out.println("a".matches("[a-z]?"));
}
}
3.3 常用方法
1. boolean matches(String regex)
判断this字符串是否匹配正则表达式regex
2. String[] split(String regex)
对this使用匹配上正则表达式的子串进行切分成字符串数组
3. replaceAll()
/**
* 常用方法:
* 1.boolean matches(String regex)
* 作用:用于检验普通字符串是否符合一个正则表达式。符合,返回true,不符合返回false
* 2.String[] solit(String regex)
* 作用:将一个普通字符串,按照正则表达式切分成一个字符串数组。
* 提示: 符合正则表达式的子串,相当于刀,隔板, 相当于隔板的子串 就没有了。
* 3.replaceAll
*/
public class RegexDemo01 {
public static void main(String[] args) {
String username ="michael123";
String regex = "[a-zA-Z][\\w]{7,9}";
//检查用户名是否匹配正则表达式
if(username.matches(regex)){
System.out.println("用户名可用");
}else{
System.out.println("用户名不可用,必须首字符是字母,总长度是8-10位");
}
//测试split方法
String str ="hello111world222welcome333";
//请使用数字将其切分成字符串数组
String regex1 = "\\d+";
String[] arr = str.split(regex1);
System.out.println(Arrays.toString(arr));
System.out.println("arr.length="+arr.length);
//测试数字在最前面时的情况(,前面会有一个空字符串,所以长度会多一个。)
String str2= "111AAA222CCC333DDD";
String[] arr2 = str2.split(regex1);
System.out.println(Arrays.toString(arr2));
System.out.println("arr2.length="+arr2.length);
//
//
// String replaceAll(String regex,String replacement)
//作用:使用replacement这个串替换掉所有的符合正则表达式的子串
String info = "no zuo no die";
info = info.replaceAll("no","yes");
System.out.println("info="+info);
info ="https://www.baidu123.com.cn.com456";
String regex2 = "[a-zA-Z]+";
info = info.replaceAll(regex2,"#") ;
System.out.println("info="+info);
}
}
3.4 Pattern类和Matcher类
1)简介
其实,Pattern这个类,才是真正来操作正则表达式的类,它位于java.util.regex包下。在String类中的提供的 matches 、split 、replace等方 法,其实都是对这个类中的某些方法的封装。
在我们调用String类中的提供的 matches 、split 、replace等方法时,底层就会创建一个Pattern对象。这样的做法效率是很低的。我们何不主动创建一个pattern,然后每次需要匹配就直接用它而不需要再次创建,大大提高了程序的效率。
正则表达式只能完成简单的校验工作。想要进一步做复杂的工作,需要使用Pattern和Matcher类型。
2)Pattern类
1.Pattern类型的介绍
字符串在真正校验是,底层实际上维护了一个Pattern对象,使用其相关方法进行的校验
因此:我们可以直接使用Pattern进行校验。
Pattern类的构造器私有化了,因此不能直接调用构造器来创建对象,但是其提供了一个工厂方法
来获取对象: static compile(String regex);方法:
String pattern():
作用:返回Pattern里的正则表达式,即特殊的字符串。String[] split(String str)
作用:对字符串进行切分。使用符合正则表达式的子串进行切分,返回字符串数组。
符合正则表达式的子串,就相当于隔板,不要了。
代码
public class PatterDemo01 {
public static void main(String[] args) {
String regex = "[a-zA-Z][_$a-zA-Z0-9]{7,9}]";
//获取一个Patter对象
Pattern p1 = Pattern.compile(regex);
//和普通字符串进行校验,会得到一个Matcher类的对象,该对象里封装的是匹配的结果,并且提供了各种操作。
Matcher matcher = p1.matcher("michael12_");
//想要得到是否匹配成功,需要调用Matcher里的matches()。该方法的作用是从头到尾进行严格匹配。
//匹配成功返回true,匹配失败返回false;
boolean matches = matcher.matches();
System.out.println("matches = " + matches);
}
}
代码(方法):
public class PatterDemo02 {
public static void main(String[] args) {
String regex = "\\d+";
String str ="aaasdad222sd3a333";
Pattern p1 = Pattern.compile(regex);
/*
String pattern():
作用:返回Pattern里的正则表达式,即特殊的字符串。
*/
String str2 = p1.pattern();
System.out.println("str2:" + str2);
/**
* String[] split(String str)
* 作用:对字符串进行切分。使用符合正则表达式的子串进行切分,返回字符串数组。
* 符合正则表达式的子串,就相当于隔板,不要了。
*/
String[] arr = p1.split(str);
System.out.println(Arrays.toString(arr));
}
}
3)Matcher类
小贴士:Pattern对象------>模式对象
Matcher对象------>匹配结果对象。
Matcher类:
1.该类的构造器私有化了,因此不能直接new。而是通过Pattern的matcher()方法获取该类的对象。
2.该类的对象里面,封装的就是正则表达式与普通字符串匹配的结果,
3.该类里提供了多个方法,对封装的结果进行操作。
matches()、lookingAt()、find()、后续方法需要前面方法调用:start()、end()、group()、groupCount()、reset()等。
方法:
研究lookingAt()方法:
1. 用于匹配普通字符串的开头部分是否符合正则表达式,检索到最后。符合返回true
2. 内部含有一个类似于指针的操作。当匹配成功后,指针指向了匹配成功的子串
匹配前位于普通字符串的前面。boolean find()方法
1.用于匹配普通字符串里是否有符合正则表达式的子串,如果有返回true。
2.该方法也有一个指针操作,当匹配成功后,指针就向后移动。
注意:匹配前,指针式在字符串之前
测试代码:
public class PatterDemo03Matcher01 {
public static void main(String[] args) {
/**
* matchers方法参考com.se.day02.bPattern.PatterDemo01
*
* 研究lookingAt()方法:
* 1. 用于匹配普通字符串的开头部分是否符合正则表达式,检索到最后。符合返回true
* 2. 内部含有一个类似于指针的操作。当匹配成功后,指针指向了匹配成功的子串
* 匹配前位于普通字符串的前面。
*
*/
String regex ="\\d+";
// String info = "1234abcd";
String info ="asda123";
Pattern pattern = Pattern.compile(regex);//获取模式对象
Matcher matcher = pattern.matcher(info);//获取匹配结果对象
boolean result = matcher.lookingAt();
System.out.println("result: " + result);
/**
* boolean find()方法
* 1.用于匹配普通字符串里是否有符合正则表达式的子串,如果有返回true。
* 2.该方法也有一个指针操作,当匹配成功后,指针就向后移动。
* 注意:匹配前,指针式在字符串之前
*/
regex = "\\d+";
info ="abc123ddd456aaa777s88s8s";
pattern = Pattern.compile(regex);
matcher = pattern.matcher(info);
//循环的次数,表示了find()找到符合正则表达式要求的个数。
while(matcher.find()){
System.out.println("--匹配成功--");
}
}
}
测试:group()、groupCount()、reset()、start()、end()
注意:上面的方法必须基于matches()、lookingAt()、find()方法,才能使用。
即matches()、lookingAt()、find()用于匹配工作。
剩下的方法用于匹配成功进行的操作。
代码部分:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PatterDemo04Matcher02 {
public static void main(String[] args) {
/**
* String group():
* 作用: 用于返回指针指向的符合正则表达式的那一部分。
*/
Pattern pattern = Pattern.compile("\\d+");
Matcher matcher = pattern.matcher("abc123def456xxx");
while(matcher.find()){
System.out.println(matcher.group());
}
//当使用find()后,指针已经移动到了符合正则表达式的最后一个部分。因此再次调用find(),
// 会继续向后检索,因此检索是否,没有任何符合要求的group().
//如果想要从头检索,需要重置指针。
//重置指针
matcher.reset();
while(matcher.find()){
System.out.println(matcher.group());
}
}
}
研究一下start()和end()
int start():用于返回符合正则表达式子串的开始位置。
int end():用于返回符合正则表达式的子串的最后一个字符的位置+1。注意:异常()
只有当匹配操作成功,才可以使用start(),end(),group()三个方法,否则会抛出java.lang.IllegalStateException,(非法状态异常)也就是当matches(),lookingAt(),find()其中任意一个方法返回true时,才可以使用。
代码部分:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/*
研究一下start()和end()
int start():用于返回符合正则表达式子串的开始位置。
int end():用于返回符合正则表达式的子串的最后一个字符的位置+1。
*/
public class PatterDemo05Matcher03 {
public static void main(String[] args) {
Pattern pattern = Pattern.compile("\\d+");
Matcher matcher = pattern.matcher("abc123def456xxx");
int count = 1;
while (matcher.find()) {
int start = matcher.start();
int end = matcher.end();
System.out.println("第"+count+"部分的开始和结束位置"+start+","+end);
count++;
}
}
}
lookingAt()和find()在一起使用时,可能会影响指针的操作。
代码如下:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* lookingAt()和find()在一起使用时,可能会影响指针的操作。
*/
public class PatterDemo06Matcher04 {
public static void main(String[] args) {
Pattern pattern = Pattern.compile("\\d+");
Matcher matcher = pattern.matcher("123aaa444bbb555");
System.out.println("字符串是否以数字开头:"+matcher.lookingAt());
/**
* 因为在使用lookingAt时,指针向后移动了一次,
* 所以,再使用find()方法时,指针就不是从字符串前开始移动了,而是紧随着lookingAt()指针的位置继续移动。
* 也就是说他们调用的是同一个指针。
* 所以,要使用指针重置的操作
*/
matcher.reset();
while (matcher.find()) {
System.out.println(matcher.group());
}
}
}
groupcount方法的测试:
使用小括号的个数来循环,可以使用group的重置方法
String(group(int group))
注意:想要使用()的个数进行遍历,需要将指针移动到字符串之前,然后还需要调用一下find方法。
group(0):表示的是对组进行拆分括号的整体的样子
group(1):表示第一个括号
group(2):表示第二个括号
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 研究groupcount()
*/
public class PatternDemo07Matcher05 {
public static void main(String[] args) {
Pattern pattern = Pattern.compile("(((ca)(t))s)");
Matcher matcher = pattern.matcher("one cat,two cats in the yard");
/**
* int groupCount():
* 作用:用于获取正则表达式汇总的小括号的个数。()用于分组。
*
*
*/
System.out.println(matcher.groupCount());
while (matcher.find()) {
//打印符合正则表达式的子串。
//group()默认是0,如果分组了,1代表第一组,2代表第二组。0是所有组合起来。
System.out.println(matcher.group(0));
}
/**
* 使用小括号的个数来循环,可以使用group的重置方法
* String(group(int group))
*
* 注意:想要使用()的个数进行遍历,需要将指针移动到字符串之前,然后还需要调用一下find方法。
* group(0):表示的是对组进行拆分括号的整体的样子
* group(1):表示第一个括号
* group(2):表示第二个括号
*
*/
matcher.reset();
matcher.find();
for (int i = 0; i <= matcher.groupCount(); i++) {
System.out.println(matcher.group(i));
}
}
}