java7-异常、字符串、正则表达式

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这个类来描述字符串。

  1. java.lang.String 使用final修饰,不能被继承

  2. 字符串底层封装的是字符数组及其针对数组的操作方法

  3. 字符串一旦创建,对象永远无法改变,但字符串引用可以重新赋值

  4. 字符串在内存中采用Unicode编码方式,任何一个字符对应两个字节的定长编码(一般字母数字占一个字节,都没有超过一个字节的长度)

  5. 字符串的索引值从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);
    }
}

结果:

michael是上面直接赋值的代码结果

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));

        }
    }
}

  • 21
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值