疯狂Java讲义(七)----第二部分

 1.Math类

        Java提供了基本的+、-、*、/、%等基本算术运算的运算符,但对于更复杂的数学运算,例如.
三角函数、对数运算、指数运算等则无能为力。Java 提供了Math工具类来完成这些复杂的运算,Math类是一个工具类,它的构造器被定义成private的,因此无法创建Math类的对象; Math 类中的所有方法都是类方法,可以直接通过类名来调用它们。Math类除提供了大量静态方法之外,还提供了两个类变量: PI和E,正如它们名字所暗示的,它们的值分别等于π和e。
        Math类的所有方法名都明确标识了该方法的作用,读者可自行查阅API来了解Math类各方法的说明。下面程序示范了Math类的用法。

        上面程序中关于Math类的用法几乎覆盖了Math类的所有数学计算功能,读者可参考上面程序来学习Math类的用法。

2.Java 7 的ThreadLocalRandom与Random

        Random类专门用于生成一个伪随机数,它有两个构造器:一个构造器使用默认的种子(以当前时间作为种子),另一个构造器需要程序员显式传入一个long型整数的种子
        ThreadLocalRandom类是Java 7新增的一个类,它是Random的增强版。在并发访问的环境下,使用ThreadLocalRandom来代替Random可以减少多线程资源竞争,最终保证系统具有更好的线程安全性

        ThreadLocalRandom类的用法与Random类的用法基本相似,它提供了一个静态的current()方法来获取ThreadI ocalRandom对象,获取该对象之后即可调用各种nextXxx()方法来获取伪随机数了。
        ThreadLocalRandom与Random都比Math的random()方法提供了更多的方式来生成各种伪随机数,可以生成浮点类型的伪随机数,也可以生成整数类型的伪随机数,还可以指定生成随机数的范围。关于Random类的用法如下程序所示。

从上面程序中可以看出,Random 可以提供很多选项来生成伪随机数。
Random使用一个48 位的种子,如果这个类的两个实例是用同一个种子创建的,对它们以同样的顺序调用方法,则它们会产生相同的数字序列

        下面就对上面的介绍做一个实验,可以看到当两个Random对象种子相同时,它们会产生相同的数字序列。值得指出的,当使用默认的种子构造Random对象时,它们属于同一个种子

扫描二维码关注公众号,回复: 13295338 查看本文章

        从上面运行结果来看,只要两个Random对象的种子相同,而且方法的调用顺序也相同,它们就会产生相同的数字序列。也就是说,Random 产生的数字并不是真正随机的,而是一种伪随机。
        为了避免两个Random对象产生相同的数字序列,通常推荐使用当前时间作为Random对象的种子,如下代码所示。

        在多线程环境下使用ThreadLocalRandom 的方式与使用Random基本类似,如下程序片段示范了ThreadLocalRandom的用法。

3.  BigDecimal 类

        前面在介绍float、double两种基本浮点类型时已经指出,这两个基本类型的浮点数容易引起精度丢失。先看如下程序。

        上面程序运行结果表明,Java 的double类型会发生精度丢失,尤其在进行算术运算时更容易发生这种情况(duoble在保存数据时,可能小数保存的是无限循环的,所以会丢失,运算就更丢失了)。不仅是Java,很多编程语言也存在这样的问题。
        为了能精确表示、计算浮点数,Java 提供了BigDecimal 类,该类提供了大量的构造器用于创建BigDecimal对象,包括把所有的基本数值型变量转换成一个BigDecimal 对象,也包括利用数字字符串、数字字符数组来创建BigDecimal对象
        查看BigDecimal类的BigDecimal(double val)构造器的详细说明时,可以看到不推荐使用该构造器的说明,主要是因为使用该构造器时有一定的不可预知性当程序使用new BigDecimal(0. 1)来创建一个BigDecimal对象时,它的值并不是0.1,它实际上等于一个近似0.1的数。这是因为0.1无法准确地表示为double浮点数,所以传入BigDecimal构造器的值不会正好等于0.1 ( 虽然表面上等于该值)。
        如果使用BigDecimal(String val)构造器的结果是可预知的一写入 new BigDecimal("0.1")将创建一个BigDecimal,它正好等于预期的0.1。因此通常建议优先使用基于String的构造器
        如果必须使用double浮点数作为BigDecimal构造器的参数时,不要直接将该double浮点数作为构造器参数创建BigDecimal 对象,而是应该通过BigDecimal.valueOf(double value)静态方法来创建BigDecimal对象
        BigDecimal类提供了add()、subtract()、 mutiply()、 divide()、 pow()等方法对精确浮点数进行常规算术运算。下面程序示范了BigDecimal 的基本运算。

        上面程序中f1和f3都是基于0.05创建的BigDecimal对象,其中f1是基于"0.05"字符串,但f3是基于0.05的double浮点数。运行,上面程序,看到如下运行结果:

         从上面运行结果可以看出BigDecimal进行算术运算的效果,而且可以看出创建BigDecimal对象时,一定要使用String对象作为构造器参数,而不是直接使用double数字。

        如果程序中要求对double浮点数进行加、减、乘、除基本运算,则需要先将double类型数值包装成BigDecimal对象,调用BigDecimal对象的方法执行运算后再将结果转换成double型变量。这是比较烦琐的过程,可以考虑以BigDecimal为基础定义一个Arith工具类,该工具类代码如下。

4. Date类

        Java提供了Date 类来处理日期、时间(此处的Date是指java.util包下的Date类,而不是java.sql包下的Date类),Date 对象既包含日期,也包含时间。Date 类从JDK 1.0 起就开始存在了,但正因为它历史悠久,所以它的大部分构造器、方法都已经过时,不再推荐使用了。

        Date类提供了6个构造器,其中4个已经Deprecated(英文意思是不赞成) (Java 不再推荐使用,使用不再推荐的构造器时编译器会提出警告信息,并导致程序性能、安全性等方面的问题),剩下的两个构造器如下。

➢Date():生成一一个代表当前日期时间的Date对象。 该构造器在底层调用System.currentTimeMillis()获得long整数作为日期参数。
➢Date(longdate):根据指定的long型整数来生成一个Date对象。该构造器的参数表示创建的Date
对象和GMT1970年1月1日00:00:00之间的时间差
,以毫秒作为计时单位。
 

与Date构造器相同的是,Date 对象的大部分方法也Deprecated了,剩下为数不多的几个方法。
➢boolean after(Date when):测试该日期是否在指定日期when之后。
➢boolean before(Date when):测试该日期是否在指定日期when之前。
➢long getTime():返回该时间对应的long型整数,即从GMT 1970-01-01 00:00:00到该Date对象之间的时间差,以毫秒作为计时 单位。
➢void setTime(long time):设置该Date对象的时间。
下面程序示范了Date 类的用法。

        总体来说,Date是一个设计相当糟糕的类,因此Java官方推荐尽量少用Date的构造器和方法。如果需要对日期、时间进行加减运算,或获取指定时间的年、月、日、时、分、秒信息,可使用Calendar工具类

5. Calendar类

        因为Date 类在设计上存在一些缺陷,所以Java提供了Calendar 类来更好地处理日期和时间。Calendar是一个抽象类,它用于表示日历。
        历史上有着许多种纪年方法,它们的差异实在太大了,比如说一个人的生日是“七月七日”,那么一种可能是阳(公)历的七月七日,但也可以是阴(农)历的日期。为了统一计时,全世界通常选择最普及、最通用的日历: Gregorian Calendar,也就是日常介绍年份时常用的“公元几几年”
        Calendar类本身是一个抽象类,它是所有日历类的模板,并提供了一些所有日历通用的方法;但它本身不能直接实例化,程序只能创建Calendar子类的实例,Java本身提供了一个GregorianCalendar类,一个代表格里高利日历的子类,它代表了通常所说的公历
        当然,也可以创建自己的Calendar子类,然后将它作为Calendar对象使用(这就是多态)。在IBM的alphaWorks站点( htp//wwalphaworks.ibm.com/tech/calendars)上,IBM的开发人员实现了多种日历。在Internet上,也有对中国农历的实现。因为篇幅关系,本章不会详细介绍如何扩展Calendar子类,读者可以查看.上述Calendar的源码来学习。
        Calendar类是一个抽象类,所以不能使用构造器来创建Calendar 对象。但它提供了几个静态getInstance()方法来获取Calendar对象,这些方法根据TimeZone,Locale 类来获取特定的Calendar,如果不指定TimeZone、Locale, 则使用默认的TimeZone、Locale 来创建Calendar.

 Calendar类提供了大量访问、修改日期时间的方法,常用方法如下。
➢void add(int field, int amount):根据日历的规则,为给定的日历字段添加或减去指定的时间量
➢int get(int field);返回指定日历字段的值
➢int getActualMaximum(int field):返回指定日历字段可能拥有的最大值。例如月,最大值为11。
➢int getActualMinimum(int field):返回指定日历字段可能拥有的最小值。例如月,最小值为0。
➢void roll(int field, int amount):与add()方法类似,区别在于加上amount后超过了该字段所能表示的最大范围时,也不会向上一个字段进位
➢void set(int field, int value):将给定的日历字段设置为给定值。
➢void set(int year, int month, int date):设置Calendar对象的年、月、日三个字段的值。
➢void set(int year, int month, int date, int hourOfDay, int minute, int second):设置Calendar对象的年、月、日、时、分、秒6个字段的值。


上面的很多方法都需要一个int类型的field参数,field是Calendar类的类变量,如Calendar.YEAR、Calendar.MONTH等分别代表了年、月、日、小时、分钟、秒等时间字段。需要指出的是, Calendar.MONTH字段代表月份,月份的起始值不是1,而是0,所以要设置8月时,用7而不是8。如下程序示范了Calendar类的常规用法。

  (1) add 与 roll 的区别

add(int field, int amount)的功能非常强大,add 主要用于改变Calendar的特定字段的值。如果需要增加某字段的值,则让amount为正数;如果需要减少某字段的值,则让amount为负数即可。
add(int field, int amount)有如下两条规则。
➢当被修改的字段超出它允许的范围时,会发生进位,即上一级字段也会增大。例如: 
➢如果下一级字段也需要改变,那么该字段会修正到变化最小的值。例如:

        对于上面的例子,8-31 就会变成2-29。因为MONTH的下一级字段是DATE,从31到29改变最小。所以上面2003-8-31的MONTH字段增加6后,不是变成2004-3-2,而是变成2004-2-29。
        roll()的规则与add()的处理规则不同:当被修改的字段超出它允许的范围时,上一级字段不会增大

  (2) 设置Calendar的容错性

        调用Calendar 对象的set()方法来改变指定时间字段的值时,有可能传入一个不合法的参数,例如为MONTH字段设置13,这将会导致怎样的后果呢?看如下程序。

        上面程序①②两处的代码完全相似,但它们运行的结果不一样: ①处代码可以正常运行,因为设置MONTH字段的值为13,将会导致YEAR字段加1;②处代码将会导致运行时异常,因为设置的MONTH字段值超出了MONTH字段允许的范围关键在于程序中粗体字代码行,Calendar提供了一个setLenient()用于设置它的容错性,Calendar 默认支持较好的容错性通过setLenient(false)可以关闭Calendar的容错性,让它进行严格的参数检查
        Calendar有两种解释日历字段的模式: lenient 模式non-lenient模式。当Calendar处于lenient 模式时,每个时间字段可接受超出它允许范围的值;当Calendar处于non-lenient模式时,如果为某个时间字段设置的值超出了它允许的取值范围,程序将会抛出异常。

  (3) set()方法延迟修改

        set(f, value)方法将日历字段f更改为value, 此外它还设置了一个内部成员变量,以指示日历字段f已经被更改。尽管日历字段f是立即更改的,但该Calendar所代表的时间却不会立即修改,直到下次调用get()、 getTime()、 getTimeInMillis()、 add() 或roll()时才会重新计算日历的时间。这被称为set()方法的延迟修改,采用延迟修改的优势是多次调用set()不会触发多次不必要的计算(需要计算出一个代表实际时间的long型整数)。
        下面程序演示了set()方法延迟修改的效果。.

        上面程序中创建了代表2003-8-31的Calendar对象,当把这个对象的MONTH字段加1后应该得到2003-10-1(因为9月没有31日),如果程序在①号代码处输出当前Calendar里的日期,也会看到输出2003-10-1,③号代码处将输出2003-10-5。
        如果程序将①处代码注释起来,因为Calendar的set()方法具有延迟修改的特性,即调用set(方法后Calendar实际上并未计算真实的日期,它只是使用内部成员变量表记录MONTH字段被修改为8,接着程序设置DATE字段值为5,程序内部再次记录DATE字段为5——就是9月 5日,因此看到③处输出2003-9-5

6. Java 8 新增的日期与时间包

Java 8开始专门新增了一个java.time包,该包下包含了如下常用的类。


Clock: 该类用于获取指定时区的当前日期、时间。该类可取代System类的currentTimeMillis()方法,而且提供了更多方法来获取当前日期、时间。该类提供了大量静态方法来获取Clock对象


Duration: 该类代表持续时间。该类可以非常方便地获取一段时间。


Instant: 代表一个具体的时刻,可以精确到纳秒。该类提供了静态的now()方法获取当前时刻,也提供了静态的now(Clock clock)方法来获取clock对应的时刻。除此之外,它还提供了一系列minusXxx()方法在当前时刻基础上减去一段时间,也提供了plusXxx()方法 在当前时刻基础上加上一段时间


LocalDate: 该类代表不带时区的日期,例如2007-12-03。 该类提供了静态的now()方法来获取当前日期,也提供了静态的now(Clock clock)方法来获取clock对应的日期。除此之外,它还提供了minusXxx()方法在当 前年份基础.上减去几年、几月、几周或几日等,也提供了plusXxx()方法在当前年份基础上加上几年、几月、几周或几日等。


LocalTime: 该类代表不带时区的时间,例如10:15:30。 该类提供了静态的now()方法来获取当前时间,也提供了静态的now(Clock clock)方法来获取clock对应的时间。除此之外,它还提供了minusxXxx()方法在当前年份基础上减去几小时、几分、几秒等,也提供了plusXxx()方法在当前年份基础上加上几小时、几分、几秒等。


LocalDateTime: 该类代表不带时区的日期、时间,例如2007-12-03T10:15:30。该类提供了静态的now()方法来获取当前日期、时间,也提供了静态的now(Clock clock)方 法来获取clock 对应的日期、时间。除此之外,它还提供了minusXxx()方 法在当前年份基础上减去几年、几月、几日、几小时、几分、几秒等,也提供了plusXxx()方法在当前年份基础上加上几年、几月、几日、几小时、几分、几秒等。


MonthDay: 该类仅代表月日,例如--04-12。 该类提供了静态的now()方法来获取当前月日,也提供了静态的now(Clock clock)方法来获取clock 对应的月日。


Year: 该类仅代表年,例如2014。 该类提供了静态的now()方法来获取当前年份,也提供了静态的now(Clock clock)方法来 获取clock对应的年份。除此之外,它还提供了minusYears()方法在当前年份基础.上减去几年,也提供了plusYears()方法在当前年份基础上加上几年。


YearMonth: 该类仅代表年月,例如2014-04。 该类提供了静态的now()方法来获取当前年月,也提供了静态的now(Clock clock)方法来 获取clock 对应的年月。除此之外,它还提供了minusXxx()方法在当前年月基础上减去几年、几月,也提供了plusXxx()方法在当前年月基础上加上几年、几月。


ZonedDateTime: 该类代表一个时区化的日期、时间


Zoneld: 该类代表一个时区

DayOfWeek: 这是一个枚举类,定义了周日到周六的枚举值


Month: 这也是一个枚举类,定义了一月到十二月的枚举值

7.  正则表达式(regex)

        正则表达式是一个强大的字符串处理工具,可以对字符串进行查找、提取、分割、替换等操作。String类里也提供了如下几个特殊的方法。
        ➢boolean matches(String regex):判断该字符串是否匹配指定的正则表达式
        ➢String replaceAll(String regex, String replacement): 将该字符串中所有匹配regex的子串替换成replacement
        ➢String replaceFirst(String regex, String replacement):将该字符串中第一个匹配regex的子串替换成replacement
        ➢String[] split(tring regex):以regex作为分隔符,把该字符串分割成多个子串
        上面这些特殊的方法都依赖于Java 提供的正则表达式支持,除此之外,Java 还提供了Pattern 和Matcher两个类专门用于提供正则表达式支持
        很多读者都会觉得正则表达式是一个非常神奇、高级的知识,其实正则表达式是一种非常简单而且非常实用的工具。正则表达式是一个用于匹配字符串的模板。实际上,任意字符串都可以当成正则表达式使用,例如"abc", 它也是一个正则表达式,只是它只能匹配"abc"字符串。
        如果正则表达式仅能匹配"abc"这样的字符串,那么正则表达式也就不值得学习了。下面开始学习如何创建正则表达式。

  (1)创建正则表达式

        除此之外,正则表达式中有一些特殊字符,这些特殊字符在正则表达式中有其特殊的用途,比如前面介绍的反斜线(\)。如果需要匹配这些特殊字符,就必须首先将这些字符转义,也就是在前面添加一个反斜线(\)。 正则表达式中的特殊字符如表7.2所示。

//System.out.println("\");  //这个会出错
System.out.println("\\");   //输出\
System.out.println("\\\\");  //输出\\

       上面的正则表达式依然只能匹配单个字符,这是因为还未在正则表达式中使用“通配符”,“通配符”是可以匹配多个字符的特殊字符。正则表达式中的“通配符”远远超出了普通通配符的功能,它被称为预定义字符,正则表达式支持如表7.3所示的预定义字符。

注意: 上面的代码要用双引号引起了!!!! (这里错了)

public class HelloWorld {
    public static void main(String []args) {
       System.out.println("\\d\\d\\d-\\d\\d\\d-\\d\\d\\d\\d");  // 输出\d\d\d-\d\d\d-\d\d\d\d
	   System.out.println("c\\wt");  //输出c\wt
		
    }
}

        在一些特殊情况下,例如,若只想匹配a~f的字母,或者匹配除ab之外的所有小写字母,或者匹配中文字符,上面这些预定义字符就无能为力了,此时就需要使用方括号表达式,方括号表达式有如表7.4所示的几种形式。

 注意:上面的 [\\u0041-\\u0056] 改成 [\u0041-\u0056]

[\\u0041-\\u0056] 返回的是什么呢?

我们把它拆解一下  [\\u0041-\\u0056]

  • \\匹配的是\
  • u004匹配的是 u 0 4
  • u0056 匹配的是 u 0 5 6
  • 1-\\匹配的是 1到\ ASCII码值的所有字符

 见如下运行过程:

        正则表示还支持圆括号表达式,用于将多个表达式组成一个子表达式,圆括号中可以使用或运算符(|)。例如,正则表达式"((public)|(protected)|(private))"用于匹配Java的三个访问控制符其中之一。
        除此之外,Java正则表达式还支持如表7.5所示的几个边界匹配符。

        实际上,正则表达式还提供了数量标识符,正则表达式支持的数量标识符有如下几种模式。

  • Greedy(贪婪模式):数量表示符默认采用贪婪模式,除非另有表示。贪婪模式的表达式会一直匹配下去,直到无法匹配为止。如果你发现表达式匹配的结果与预期的不符,很有可能是因为——你以为表达式只会匹配前面几个字符,而实际上它是贪婪模式,所以会一直匹配下去。
  • Reluctant(勉强模式):用问号后缀(?)表示,它只会匹配最少的字符。也称为最小匹配模式
  • Possessive(占有模式):用加号后缀(+)表示,目前只有Java支持占有模式,通常比较少用。

三种模式的数量表示符如表7.6所示。

 关于贪婪模式和勉强模式的对比,看如下代码:

        当从"hello , java!"字符串中查找匹配"\\w*"子串时,因为"\\w*"使用了贪婪模式,数量表示符(*)会一直匹配下去,所以该字符串前面的所有单词字符都被它匹配到,直到遇到空格,所以替换后的效果是“, java!",如果使用勉强模式,数量表示符(*)会尽量匹配最少字符,即匹配0个字符,所以替换后的结果是“hello , java!”。

 三者的原理分析:

假设待处理的字符串是 :xfooxxxxxxfoo

1. 贪婪模式

模式分为子模式p1(.*)和子模式p2(foo)两个部分. 其中p1中的量词匹配方式使用默认方式(贪婪型)。 

匹配开始时,吃入所有字符xfooxxxxxx去匹配子模式p1。匹配成功,但这样以来就没有了字符串去匹配子模式p2。本轮匹配失败;第二轮:减少p1部分的匹配量,吐出最后一个字符, 把字符串分割成xfooxxxxxxfo和o两个子字符串s1和s2。 s1匹配p1, 但s2不匹配p2。本轮匹配失败;第三轮,再次减少p1部分匹配量,吐出两个字符, 字符串被分割成xfooxxxxxxfo和oo两部分。结果同上。第四轮,再次减少p1匹配量, 字符串分割成xfooxxxxxx和foo两个部分, 这次s1/s2分别和p1/p2匹配。停止尝试,返回匹配成功。(倒着补位,满足所有子模块)

2. 勉强模式(非贪婪模式)

第一次尝试匹配, p1由于是0或任意次,因此被忽略,用字符串去匹配p2,失败;第二次,读入第一个字符x, 尝试和p1匹配, 匹配成功; 字符串剩余部分fooxxxxxxfoo中前三个字符和p2也是匹配的. 因此, 停止尝试, 返回匹配成功。在这种模式下,如果对剩余字符串继续去寻找和模式相匹配的子字符串,还会找到字符串末尾的另一个xfoo,而在贪婪模式下,由于第一次匹配成功的子串就已经是所有字符,因此不存在第二个匹配子串。(能不要就不要的满足子模块)

3. 占有模式

匹配开始时读入所有字符串, 和p1匹配成功, 但没有剩余字符串去和p2匹配。因此, 匹配失败。返回。(子模式都要匹配东西才行,否者原项返回)

public class HelloWorld {
    public static void main(String []args) {
        String test1 = "a<tr>aava</tr>abb ";

        String reg1 = "<.+>";
        System.out.println("贪婪匹配结果:" + test1.replaceAll(reg1,"###"));
 
        String reg2 = "<.+?>";
        System.out.println("勉强匹配结果:" + test1.replaceAll(reg2,"###"));
 
 
    }
}

返回结果:

public class HelloWorld {
    public static void main(String []args) {	
		String test2 = "<atraavaabb>";
 		String test3 = "<trrrrrr>";
		
		String reg3 = "<.++";
		String reg4 = "<t++r++>";
        String reg5= "<.++>";
		
		System.out.println("占有匹配结果1:" + test2.replaceAll(reg3, "###"));
		System.out.println("占有匹配结果2:" + test3.replaceAll(reg4, "###"));
		System.out.println("占有匹配结果3:" + test2.replaceAll(reg5, "###"));
 
    }
}

返回结果:

 结果3没有任何改变是因为">"这个子模块没有匹配到东西,test2中的">"被".++"给抢去了,而没有分配到东西

  (2)使用正则表达式

        一旦在程序中定义了正则表达式,就可以使用Pattern和 Matcher来使用正则表达式
        Pattern对象是正则表达式编译后在内存中的表示形式,因此,正则表达式字符串必须先被编译为Pattern对象,然后再利用该Pattern对象创建对应的Matcher对象。执行匹配所涉及的状态保留在Matcher对象中,多个Matcher 对象可共享同一个Pattern对象。
        因此,典型的调用顺序如下:

        上面定义的 Pattern对象可以多次重复使用。如果某个正则表达式仅需一次使用,则可直接使用Pattern类的静态 matches()方法,此方法自动把指定字符串编译成匿名的Pattern对象,并执行匹配,如下所示。

        上面语句等效于前面的三条语句。但采用这种语句每次都需要重新编译新的 Pattern对象,不能重复利用已编译的Pattern对象,所以效率不高。
        Pattern是不可变类,可供多个并发线程安全使用

        Matcher类提供了如下几个常用方法。

  • find():返回目标字符串中是否包含与Pattern 匹配的子串
  • group():返回上一次与Pattern 匹配的子串
  • start():返回上一次与Pattern 匹配的子串在目标字符串中的开始位置
  • end():返回上一次与Pattern 匹配的子串在目标字符串中的结束位置加1
  • lookingAt():返回目标字符串前面部分与Pattern是否匹配
  • matches():返回整个目标字符串与Pattern是否匹配
  • reset():将现有的Matcher对象应用于一个新的字符序列

        通过Matcher类的find()和 group()方法可以从目标字符串中依次取出特定子串(匹配正则表达式的子串),例如互联网的网络爬虫,它们可以自动从网页中识别出所有的电话号码。下面程序示范了如何从大段的字符串中找出电话号码。

        从上面运行结果可以看出,find()方法依次查找字符串中与Pattern匹配的子串,一旦找到对应的子串,下次调用find()方法时将接着向下查找。

        find()方法还可以传入一个int类型的参数,带int参数的find()方法将从该int索引处向下搜索。start()和 end()方法主要用于确定子串在目标字符串中的位置,如下程序所示。

        上面程序使用find()、group()方法逐项取出目标字符串中与指定正则表达式匹配的子串,并使用start()、end()方法返回子串在目标字符串中的位置。运行上面程序,看到如下运行结果:

        matches()和 lookingAt()方法有点相似,只是matches()方法要求整个字符串和 Pattern 完全匹配时才返回true,而lookingAt()只要字符串以Pattern开头就会返回truereset()方法可将现有的Matcher对象应用于新的字符序列。看如下例子程序。

        上面程序创建了一个邮件地址的Pattern,接着用这个Patternm 与多个邮件地址进行匹配。当程序中的Matcher为null时,程序调用matcher()方法来创建一个Matcher对象,一旦Matcher对象被创建,程序就调用Matcher 的reset()方法将该Matcher应用于新的字符序列
        从某个角度来看,Matcher 的matches()、lookingAt()和 String 类的 equals()、startsWith()有点相似。区别是String类的 equals()和 startsWith()都是与字符串进行比较,而Matcher的 matches()和 lookingAt()则是与正则表达式进行匹配
        事实上, String类里也提供了matches()方法,该方法返回该字符串是否匹配指定的正则表达式。例如:

 除此之外,还可以利用正则表达式对目标字符串进行分割、查找、替换等操作,看如下例子程序。

        上面程序使用了Matcher类提供的replaceAll()把字符串中所有与正则表达式匹配的子串替换成"哈哈:)",实际上,Matcher类还提供了一个replaceFirst(),该方法只替换第一个匹配的子串。运行上面程序,会看到字符串中所有以“re”开头的单词都会被替换成“哈哈:)”。
        实际上,String类中也提供了replaceAll()、replaceFirst()、split()等方法。下面的例子程序直接使用String 类提供的正则表达式功能来进行替换和分割。

 上面程序只使用String类的replaceFirst()split()方法对目标字符串进行了一次替换分割。运行上面程序,会看到如图7.6所示的运行效果。


正则表达式是一个功能非常灵活的文本处理工具,增加了正则表达式支持后的Java,可以不再使用StringTokenizer类(也是一个处理字符串的工具,但功能远不如正则表达式强大)即可进行复杂的字符串处理。

散称知识点:

  • Java原本提供了Date和Calendar用于处理日期、时间的类,包括创建日期、时间对象,获取系统当前日期、时间等操作。但Date不仅无法实现国际化,而且它对不同属性也使用了前后矛盾的偏移量,比如月份与小时都是从0开始的,月份中的天数则是从1开始的,年又是从1900 开始的,而java.util.Calendar则显得过于复杂,从下面介绍中会看到传统Java对日期、时间处理的不足。Java 8吸取了Joda-Time库(一个被广泛使用的日期、时间库)的经验,提供了一套全新的日期时间库。
     

猜你喜欢

转载自blog.csdn.net/indeedes/article/details/120958984