第7章 java基础类库(下)

本文介绍Java8中改进的日期时间API,包括新的日期时间类、正则表达式的使用及国际化支持。针对Date和Calendar类的不足,Java8引入了更为直观且易于使用的日期时间类。

Java8的日期、时间类

Date类无法实现国际化,而且对不同属性也使用了前后矛盾的偏移量

Calendar类过于复杂

Java8吸收Joda-Time的经验,提供了一套全新的日期时间库

  • Date类

Java官方推荐尽量少用Date类(java.util.Date)的构造器和方法,若有需要可使用Calendar工具类

  • Calendar类

是抽象类,所以不能通过构造器创建对象,而是提供了几个静态getInstance()方法,根据TimeZone、Locale来获取指定的Calendar

Calendar与Date类相互转换

public class Test {
    public static void main(String[] args) {
        Calendar cal  = Calendar.getInstance();
        Date date = cal.getTime();

        Calendar cal2 = Calendar.getInstance();
        cal2.setTime(date);
    }
}

Calendar类用法

package chapter7.four;

import java.util.Calendar;

import static java.util.Calendar.*;

/**
 * Created by dongyu.liu on 2016/10/27.
 */
public class CalendarTest {
    public static void main(String[] args) {
        Calendar c = Calendar.getInstance();
        // 年
        System.out.println(c.get(YEAR));// 2016  c.get(int field)
        c.add(1, 10);
//        System.out.println(c.get(YEAR));// 2026
        // 月
        System.out.println(c.get(MONTH));// 9  月份的起始月是0不是1,所以9代表10月
        // 日
        System.out.println(c.get(DATE));// 27
        c.set(2003,10,23,12,32,23);
        c.add(YEAR,10);// 2013
        System.out.println(c.getTime());// 2013-11-23 12:32:23
        // 月向前推8个月
        c.roll(MONTH,-8);// // 2013-3-23 12:32:23
        System.out.println(c.getTime());
        // 指定日历字段可能的最大值
        System.out.println(c.getActualMaximum(MONTH));//  11
        // 最小值
        System.out.println(c.getActualMinimum(MONTH));// 0
    }
}

add(int field,int amount)和roll(int field ,int amount)的区别

// add  与 roll的区别
        Calendar c1 = Calendar.getInstance();
        c1.set(2003,7,23,0,0,0);// 2003-8-23
        Calendar c2 = Calendar.getInstance();
        c2.set(2003,7,23,0,0,0);// 2003-8-23
        // 如果上级字段需要改变  add的上级字段会改变,roll的上级字段不会改变
        c1.add(MONTH,6);
        System.out.println(c1.getTime());// 2004-2-23  上级字段会增大
        c2.roll(MONTH,6);
        System.out.println(c2.getTime());// 2003-2-23  上级字段不会增大
        // 如果下级字段需要改变,二者处理方式相同,下级字段会修正到变化最小的值
        Calendar c3 = Calendar.getInstance();
        c3.set(2003,7,31,0,0,0);// 2003-8-23
        c3.add(MONTH,6);
        System.out.println(c1.getTime());// 2004-2-29
        Calendar c4 = Calendar.getInstance();
        c4.set(2003,7,31,0,0,0);// 2003-8-23
        c4.roll(MONTH,6);
        System.out.println(c4.getTime());// 2003-2-29

calendar可设置容错性,若容错性关闭,则set传入的field只能在指定的范围内(如MONTH是0-11),否则报错

        c4.setLenient(false);

set(f,value)方法具有延迟修改的特性,即多次调用set()方法时,calendar所代表的时间不会立即被修改,但系统会有记录(多次set(f,value)是叠加的效果),
当调用get(),getTime(),getTimeInMillis(),add(),或roll()时才重新计算calendar所代表的时间
* Java8新增的日期、时间包

package chapter7.four;

import java.time.*;

/**
 * Created by dongyu.liu on 2016/10/29.
 */
public class NewDatePackageTest {
    public static void main(String[] args) {
        //-------------Clock类--------------------
        // 该类用于获取指定时区的当前日期、时间。该类可取代System.currentTimeMillis()方法
        Clock clock = Clock.systemUTC();
        System.out.println("当前时刻为"+clock.instant());// 当前时刻为2016-10-29T06:55:19.777Z
        // 获取clock对应的毫秒数
        System.out.println(clock.millis()==System.currentTimeMillis());// true
        //-------------Duration类--------------------
        // 代表持续时间,可方便的获取一段时间
        Duration d = Duration.ofSeconds(6000);
        System.out.println(d.toMinutes());//  100
        System.out.println(d.toHours());// 1
        System.out.println(d.toDays());// 0
        // 在Clock基础上增加6000秒,返回新的Clock
        Clock clock2 = Clock.offset(clock,d);
        System.out.println(clock2.instant());//2016-10-29T08:35:20.430Z   与clock时间相差1小时40分
        //-------------Instant类--------------------
        // 代表一个具体的时刻,可以精确到纳秒
        Instant instant = Instant.now();
        // 获取当前时间
        System.out.println(instant);//2016-10-29T06:57:52.516Z
        Instant instant2 = instant.plusSeconds(6000);
        System.out.println(instant2);//  增加6000秒
        // 根据字符串解析instant对象
        Instant instant3 = Instant.parse("2016-10-29T06:57:52.516Z");
        System.out.println(instant3);
        // 在instant3的基础上添加5小时4分钟
        Instant instant4 = instant3.plus(Duration.ofHours(5).plusMinutes(4));
        System.out.println(instant4);
        // 获取instant4五天以前的实例
        System.out.println(instant4.minus(Duration.ofDays(5)));
        //-------------LocalDate类--------------------
        // 该类代表不带时区的日期  例如: 2007-12-03
        LocalDate localDate = LocalDate.now();
        System.out.println(localDate);
        // 获得2014年的第146天
        localDate = LocalDate.ofYearDay(2014, 146);
        System.out.println(localDate);
        // 设置为2014521日
        localDate = LocalDate.of(2014, Month.MAY, 21);
        System.out.println(localDate);
        //-------------LocalTime类--------------------
        // 代表不带时区的时间,例如101530
        LocalTime localTime = LocalTime.now();
        localTime = LocalTime.of(22,33);
        System.out.println(localTime);// 22:33
        // 返回一天中的第5503秒
        localTime = LocalTime.ofSecondOfDay(5503);
        System.out.println(localTime);// 01:31:43
        //-------------LocalDateTime类--------------------
        // 代表不带时区的日期、时间 例如: 2007-12-03T10:15:30
        LocalDateTime localDateTime = LocalDateTime.now();
        // 当前日期加上25小时3分钟
        LocalDateTime future = localDateTime.plusHours(25).plusMinutes(3);
        System.out.println(future);// 2016-10-30T16:26:23.811
        //-------------Year   YearMonth     MonthDay类--------------------
        // MonthDay代表月日 --04-12
        // Year代表年  2014
        // YearMonth  代表年月  2014-04
        Year year = Year.now();
        System.out.println(year);// 2016
        year = year.plusYears(5);
        System.out.println(year);// 2021
        // 根据指定月份获取YearMonth
        YearMonth ym = year.atMonth(10);
        System.out.println(ym);// 2021-10
        // 当前年月再加5年减3个月
        System.out.println(YearMonth.now().plusYears(5).minusMonths(3));// 2021-07
        // 月日
        System.out.println(MonthDay.now());//   --10-29
        // 设置为523日
        MonthDay md2 = MonthDay.now();
        System.out.println(MonthDay.now().with(Month.MAY).withDayOfMonth(23));//  --05-23

    }
}

4.正则表达式

正则表达式是一种强大的字符串处理工具(用于匹配字符串的模板),可以对字符串进行查找、提取、分割、替换等操作。

String类中提供了几个特殊的方法:matches()、replaceAll()、replaceFirst()、split()()

Pattern和Matcher两个类专门用于提供正则表达式支持

  • 创建正则表达式
    1. 合法字符
    2. 特殊字符
    3. 预定义字符
    4. 方括号表达式
    5. 圆括号表达式
    6. 边界匹配符
    7. 数量标识符(贪婪模式(默认)、勉强模式、占有模式)
String str = "hello , java";
// 贪婪模式的正则表达式
System.out.println(str.replaceFirst("\\w*","@"));//  @ , java
// 勉强模式的正则表达式
System.out.println(str.replaceFirst("\\w*?","@"));//  @hello , java
"\u0041\\\\"  //  匹配A\
"\u0061\t"    // 匹配a<制表符>
"\\?\\["      // 匹配?[

预定义字符(通配符)

. 匹配任何字符

\d 匹配0-9的所有数字(digit)

\D 匹配非数字

\s 匹配空白字符、包括空格、制表符、回车符、换页符、换行符等(space)

\S 匹配非空白字符

\w 匹配所有的单词字符,包括0-9所有数字、26个英文字符和下划线(word)

\W 匹配所有的非单词字符

c\\w  //可以匹配cat、cbt、cct   c0t  c9t等一批字符串
\\d\\d\\d-\\d\\d\\d-\\d\\d\\d\\d  //匹配如  000-000-0000形式的电话号码

((private)|(protected)|(public))  // 匹配java的三个访问控制符之一
  • 使用正则表达式

一旦在程序中定义了正则表达式,就可以使用Pattern和Matcher来使用正则表达式。

Pattern对象是正则表达式编译后在内存中的表示形式,因此,正则表达式字符串必须被编译为pattern对象,
然后再利用该Pattern对象创建对应的Matcher对象。多个Matcher对象可共享同一个Pattern对象。

 // 将一个正则表达式便以为Pattern对象
        Pattern p  = Pattern.compile("a*b");
        // 使用Pattern对象创建Matcher对象
        Matcher m  = p.matcher("aaaaab");
        boolean b = m.matches();
        System.out.println(b);// true
// 如果某个正则表达式仅需一次使用   但下面的方式要每次重新编译Pattern对象,所以效率不高
        boolean c = Pattern.matches("a*b", "aaaaab");

Patter类是不可变类,可供多个并发线程安全使用

Matcher类中的常用方法:

find():返回目标字符串是否包含与Pattern匹配的子串

group():返回上一次与Pattern匹配的子串

start(): 上次匹配的字符串在目标字符串中的开始位置

end(): 上次匹配的字符串在目标字符串中的结束位置加1

lookingAt():返回目标字符串前面部分与Pattern是否匹配

matches():返回是否匹配

reset():将现有的Macher对象应用于一个新的字符序列

CharSequence接口,CharBuffer String StringBuffer StringBuilder 都是它的实现类,它代表一个各种表示形式的字符串

模拟互联网网络爬虫

public class FindGroup {
    public static void main(String[] args) {
        String s = "lalsdfidsjdsfd13588886666fdsonsdcdso" +
                "dovdskfkmods15822223333dfjidsoc" +
                "158222jofsdf135dsfodsff";
        Pattern p = Pattern.compile("((13\\d)|(15\\d))\\d{8}");
        Matcher m = p.matcher(s);
        while(m.find()){
            System.out.println(m.group());//  13588886666   15822223333
        }
    }
}

start() end()

public class StartEnd {
    public static void main(String[] args) {
        String regStr = "java is very easy!";
        Matcher m  = Pattern.compile("\\w+").matcher(regStr);
        while(m.find()){
            System.out.println(m.group()+"子串的起始位置是:"+m.start()+"结束位置是:"+m.end());
//            java子串的起始位置是:0结束位置是:4
//            is子串的起始位置是:5结束位置是:7
//            very子串的起始位置是:8结束位置是:12
//            easy子串的起始位置是:13结束位置是:17
        }
    }
}

LookingAt():只要以Pattern开头就返回true

matches(): 要求完全匹配才可以

reset():将现有的Macther对象用于新的字符串序列

public class MatchesTest {
    public static void main(String[] args) {
        String[] mails = {
                "dfisdof@fds.com",
                "sdfods@dsfd.com",
                "dsfojs@jdsojf.org",
                "dsfodsj@dsf.xx"
        };
        String mailRegex = "\\w{3,20}@\\w+\\.(com|org|cn)";
        Pattern p = Pattern.compile(mailRegex);
        Matcher m = null;
        for (String mail:mails){
            System.out.println(mail.matches(mailRegex));//  利用字符串的api  true  true true false
        }

        for (String mail : mails) {
            if (m == null) {
                m = p.matcher(mail);
            } else {
                m.reset(mail);
            }
            System.out.println(mail + (m.matches() ? "是" : "不是") + "一个有效地址");
        }
        for (String mail : mails) {
            m = p.matcher(mail);
            System.out.println(mail + (m.matches() ? "是" : "不是") + "一个有效地址");
//            dfisdof@fds.com是一个有效地址
//            sdfods@dsfd.com是一个有效地址
//            dsfojs@jdsojf.org是一个有效地址
//            dsfodsj@dsf.xx不是一个有效地址
        }
    }
}

分割、查找、替换

可以使用Matcher类提供的replaceAll() replaceFirst()方法

也可以使用String类提供的replaceFirst() split()等方法

增加了正则表达式支持的Java StringTokenizer类可不使用(没有正则表达式强大)

public class StringRegTest {
    public static void main(String[] args) {
        String[] msgs = {"regd ref ds","re re","dfd re "};
        for(String msg:msgs) {
            System.out.println(msg.replaceFirst("re\\w*", "哈哈哈"));
            System.out.println(Arrays.toString(msg.split(" ")));
        }
    }
}

国际化与格式化

国际化 I18N Internationalization

本地化 L10N Localization

Java8的国际化支持升级到了Unicode6.2.0字符集

  • Java的国际化思路

java程序的国际化思路时将程序中的标签、提示等信息放在资源文件中,程序需要支持哪些国家、
语言环境,就对应提供相应的资源文件。

主要通过下面三个类完成:
1. java.util.ResourceBundle:用于加载国家、语言资源包
2. java.util.Locale:用于封装特定的国家/区域、语言环境
3. java.text.MessageFormat:用于格式化带占位符的字符串

资源文件名有三种命名方式:

  1. baseName.language_country.properties
  2. baseName.language.properties
  3. baseName.properties

baseName可随意指定,language和country必须是java支持的语言

  • Java支持的国家和语言
public class LocalList {
    public static void main(String[] args) {
        // 返回Java所支持的全部国家和语言的数组
        Locale[] localList = Locale.getAvailableLocales();
        // 遍历数组的每个元素,依次获取所支持的国家和语言
        for (Locale l : localList) {
            System.out.println(l.getDisplayCountry() + "=" + l.getCountry() + "  " + l.getDisplayLanguage() + "=" + l.getLanguage());
        }
//        =  中文=zh
//        马其顿王国=MK  马其顿文=mk
//        白俄罗斯=BY  白俄罗斯文=be
//        斯洛文尼亚=SI  斯洛文尼亚文=sl
//        秘鲁=PE  西班牙文=es
//        印度尼西亚=ID  印度尼西亚文=in
//        英国=GB  英文=en
        // ......
    }
}
  • 完成程序格式化

    1. 添加配置文件
      mess_en_US.properties

mess_zh_CN.properties hello=你好!
2. java代码

public class HelloI18NTest {
    public static void main(String[] args) {
        Locale locale = Locale.getDefault(Locale.Category.FORMAT);
        System.out.println(locale.getDisplayCountry()+locale.getCountry());// 中国CN
        System.out.println(locale.getDisplayLanguage()+locale.getLanguage());// 中文zh
        // 根据local判断要加载哪个配置文件     mess_en_US.properties    mess_zh_CN.properties
        ResourceBundle bundle = ResourceBundle.getBundle("chapter7.six.mess",locale);
        System.out.println(bundle.getString("hello"));
    }
}
  • 使用MessageFormat处理包含占位符的字符串

    1. 配置文件

mess_zh_CN.properties

hello=你好,{0}!今天是{1}

public class HelloArg {
    public static void main(String[] args) {
        Locale currentLocale = Locale.getDefault(Locale.Category.FORMAT);
        ResourceBundle bundle = ResourceBundle.getBundle("chapter7.six.mess", currentLocale);
        String msg = bundle.getString("hello");
        System.out.println(MessageFormat.format(msg,"yeeku",new Date()));
//  你好,yeeku!今天是16-10-30 下午2:08
    }
}
  • 使用类文件代替资源文件

java允许使用类文件代替资源文件,必须满足以下条件:
1. 类名必须是baseName.language_country.properties
2. 必须继承ListResourceBundle,并重写getContents()方法,该方法返回Object数组

类文件

public class mess_zh_CN extends ListResourceBundle {
    private final Object[][] myData = {
            {"hello","{0},你好!今天的日期时{1}"},
            {"hello2","{0},你好!我是类文件代替资源文件哦!今天的日期时{1}"},
    };
    @Override
    protected Object[][] getContents() {
        return myData;
    }
}

调用方法

public class HelloArg {
    public static void main(String[] args) {
        Locale currentLocale = Locale.getDefault(Locale.Category.FORMAT);
        ResourceBundle bundle = ResourceBundle.getBundle("chapter7.six.mess", currentLocale);
        String msg = bundle.getString("hello2");
//        yeeku,你好!我是类文件代替资源文件哦!今天的日期时16-10-30 下午2:29
        System.out.println(MessageFormat.format(msg,"yeeku",new Date()));
    }
}

ResouceBundle搜索资源文件的顺序是:

  1. baseName_zh_CN.class
  2. baseName_zh_CN.properties
  3. baseName_zh.class
  4. baseName_zh.properties
  5. baseName.class
  6. baseName.properties

    • 使用NumberFormat格式化数字

用于实现数字格式化,是抽象类Format的子类,它也是一个抽象基类

NumberFormat有国际化的作用

public static void main(String[] args) {
        double db = 213.3243354;
        Locale[] locales = {Locale.CHINA, Locale.JAPAN, Locale.GERMAN, Locale.US};
        NumberFormat[] nf = new NumberFormat[12];
        for (int i = 0; i <locales.length ; i++) {
            nf[i * 3] = NumberFormat.getNumberInstance(locales[i]);
            nf[i * 3 + 1] = NumberFormat.getPercentInstance(locales[i]);
            nf[i * 3 + 2] = NumberFormat.getCurrencyInstance(locales[i]);
        }
        for (int i = 0; i <locales.length ; i++) {
            String tip = i == 0 ? "-----中国的格式------" : i == 1 ? "-日本格式--" : i == 2 ? "---德国格式-----" : "--美国格式----";
            System.out.println(tip);
            System.out.println("通用数值格式:"+nf[i*3].format(db));
            System.out.println("百分比数值格式:"+nf[i*3+1].format(db));
            System.out.println("货币数值格式:"+nf[i*3+2].format(db));
//            -----中国的格式------
//            通用数值格式:213.324
//            百分比数值格式:21,332%
//                    货币数值格式:¥213.32
//                    -日本格式--
//            通用数值格式:213.324
//            百分比数值格式:21,332%
//                    货币数值格式:¥213
//                    ---德国格式-----
//                    通用数值格式:213,324
//            百分比数值格式:21.332%
//                    货币数值格式:¤ 213,32
//                    --美国格式----
//            通用数值格式:213.324
//            百分比数值格式:21,332%
//                    货币数值格式:$213.32
        }
    }
  • 使用DateFormat格式化日期、时间

用于实现日期格式化,是抽象类Format的子类,它也是一个抽象类

获得DateFormat后 可调用setLenient(boolean lenient)来设置是否使用严格语法

DateFormat的parse可以将字符串解析成Date对象,但要求被解析的字符串必须符合要求

String str1 = "2014-12-12";
        String str2 = "2014年12月10日";
        System.out.println(DateFormat.getDateInstance().parse(str1));// Fri Dec 12 00:00:00 CST 2014
        System.out.println(DateFormat.getDateInstance().parse(str2));// 报错
public class DateFormatTest {
    public static void main(String[] args) {
        Date dt = new Date();
        Locale[] locales = {Locale.CHINA, Locale.US};
        DateFormat[] df = new DateFormat[16];
        //  为上面两个Locale创建16个DateFormate对象
        for (int i = 0; i <locales.length ; i++) {
            df[i * 8] = DateFormat.getDateInstance(SHORT,locales[i]);
            df[i * 8+1] = DateFormat.getDateInstance(MEDIUM,locales[i]);
            df[i * 8+2] = DateFormat.getDateInstance(LONG,locales[i]);
            df[i * 8+3] = DateFormat.getDateInstance(FULL,locales[i]);
            df[i * 8+4] = DateFormat.getTimeInstance(SHORT, locales[i]);
            df[i * 8+5] = DateFormat.getTimeInstance(MEDIUM, locales[i]);
            df[i * 8+6] = DateFormat.getTimeInstance(LONG, locales[i]);
            df[i * 8+7] = DateFormat.getTimeInstance(FULL, locales[i]);

        }
        for (int i = 0; i <locales.length; i++) {
            System.out.println(i==0?"----中国的时间格式----":"---美国日期格式----");
            System.out.println(df[i*8].format(dt));
            System.out.println(df[i*8+1].format(dt));
            System.out.println(df[i*8+2].format(dt));
            System.out.println(df[i*8+3].format(dt));
            System.out.println(df[i*8+4].format(dt));
            System.out.println(df[i*8+5].format(dt));
            System.out.println(df[i*8+6].format(dt));
            System.out.println(df[i*8+7].format(dt));
        }
    }
}
  • 使用SimpleDateFormat格式化日期

解决DateFormat不够灵活的问题 比如parse()方法要求字符串必须是特定格式

public class SimpleDateFormatTest {
    public static void main(String[] args) throws ParseException {
        Date d = new Date();
        SimpleDateFormat sdf1 = new SimpleDateFormat("Gyyyy年中第D天");
        String dateStr = sdf1.format(d);
        System.out.println(dateStr);// 公元2016年中第304天

        // 一个非常特殊的日期字符串
        String str = "14###三月##21";
        SimpleDateFormat sdf2 = new SimpleDateFormat("y###MMM##d");
        System.out.println(sdf2.parse(str));// Fri Mar 21 00:00:00 CST 2014
    }
}

Java8新增的日期、时间格式器

java.time.format包下提供了一个DateTimeFormatter格式器类,
相当于DateFormat和SimpleDateFormat的合体,功能非常强大

  • 使用DateTimeFormatter完成格式化
public class NewFormatterTest {
    public static void main(String[] args) {
        DateTimeFormatter[] formatters = new DateTimeFormatter[]{
                // 直接使用常量创建DateTimeFormatter格式器
                DateTimeFormatter.ISO_LOCAL_DATE,
                DateTimeFormatter.ISO_LOCAL_TIME,
                DateTimeFormatter.ISO_LOCAL_DATE_TIME,
                //使用本地化的不同风格来创建DateTimeFormatter格式器
                DateTimeFormatter.ofLocalizedDateTime(FULL, MEDIUM),
                DateTimeFormatter.ofLocalizedDate(LONG),
                // 根据模式字符串来创建DateTimeFormatter格式器
                DateTimeFormatter.ofPattern("Gyyy%%MMMdd HH:mm:ss")};
        LocalDateTime date = LocalDateTime.now();
// 依次使用不同的格式器对LocalDateTime进行格式化
        for (int i = 0; i < formatters.length; i++) {
            // 下面两行代码作用相同
            System.out.println(date.format(formatters[i]));
            System.out.println(formatters[i].format(date));
//            2016-10-30
//            2016-10-30
//            15:47:01.667
//            15:47:01.667
//            2016-10-30T15:47:01.667
//            2016-10-30T15:47:01.667
//            2016年10月30日 星期日 15:47:01
//            2016年10月30日 星期日 15:47:01
//            2016年10月30日
//            2016年10月30日
//            公元2016%%十月30 15:47:01
//            公元2016%%十月30 15:47:01
        }
    }
}
  • 使用DateTimeFormatter解析字符串
public class NewFormatterParse {
    public static void main(String[] args) {
        String str1 = "2014==04==12 01时06分09秒";
        DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyy==MM==dd HH时mm分ss秒");
        // 执行解析
        LocalDateTime dt1 = LocalDateTime.parse(str1, formatter1);
        System.out.println(dt1);//  2014-04-12T01:06:09
        System.out.println(dt1.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));// 2014-04-12 01:06:09
    }
}

本章小结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值