20145210 《Java程序设计》第07周学习总结

教材学习内容总结

第十二章 Lambda

12.1 认识Lambda语法

•Lambda 教材的引入循序渐近、深入浅出

•如果使用JDK8的话,可以使用Lambda特性去除重复的信息,例:

Comparator<String>byLength = 
      (String name1,String name2)->name1.length() - name2.length();

•JDK8提供了方法参考的特性,可以重用byLength方法

•方法参考的特性,在重用现有API上扮演了重要角色,重用现有方法操作,可避免到处写下Lambda表达式

•方法参考不仅避免了重复撰写Lambda表达式,也可以让程序代码更清楚

•Arrays.sort

•lambda表达式中,即使不接受任何参数,也必须写下括号。例如:

() -> "Justin"    //不接受参数,返回字符串
() -> System.out.println()   //不接受参数,没有返回值

•在只有Lambda表达式的情况下,参数的类型必须写出来,如果有目标类型的话,在编译程序可推断出类型的情况下,就可以不写出Lambda表达式的参数类型。例:

Comparator<String>byLength = (name1,name2) -> name1.length() - name2.length();
//可从Comparator<String>中推断出name1与name2的类型,实际上是String,因而不用写出参数类型

•Lambda表达式本身是中性的,不代表任何类型的实例,同样的Lambda表达式,可用来表示不同目标类型的对象操作

•JDK8的Lambda并没有导入新类型来作为Lambda表达式的类型,而是就现有的interface语法来定义函数接口,作为Lambda表达式的目标类型

•Lambda表达式只关心方法签署上的参数与返回定义,但忽略方法名称

•有个新标注@FunctionalInterface在JDK8中被引入,它可以这么使用:

@FunctionalInterface
public iterface Func<P,R>{
    R apply(P p);
}

•如果接口使用了@FunctionalInterface来标注,而本身并非函数接口的话,就会引发编译错误

•Lambda遇上this与final

•Lambda表达式中this的参考对象以及toString()的接受者,是来自Lambda的周围环境,也就是看Lambda表达式是在哪个名称范畴,就能参考该范畴内的名称,像是变量或方法

•在JDK中,如果变量本身等效于final局部变量,也就是说,如果变量不会再匿名类中有重新指定的动作,就可以不用加上final关键词

•如果Lambda表达式中捕获的局部变量本身等效于final局部变量,可以不用在局部变量上加上fianl

•在lambda表达式中不可以改变被捕获的局部变量值

•方法与构造函数参考

•Lambda表达式只是定义函数接口操作的一种方式,除此之外,只要静态方法的方法签署中,参数与返回值定义相同,也可以使用静态方法来定义函数接口操作,例:可用第二段代码代替第一段代码:

String[] names = {"Justin","caterpillar","Bush"}'
Arrays.sort(names,(name1,name2) ->name1.length() - name2.length());

第十三章 时间与日期

13.1 认识时间与日期

•时间的度量

•格林威治标准时间:简称GMT,已不作为标准时间使用

•世界时:在1972年引入UTC之前,GMT与UT是相同的

•国际原子时:简称TAI

•世界协调时间:简称UTC

•Unix时间:不考虑闰秒修正,用以表达时间轴上某一瞬间

•epoch:某个特定时代的开始,用以表达时间轴上某一瞬间

•几个重点:

  •就目前来说,即使标注为GMT,实际上谈到时间指的是UTC时间

  •秒的单位定义是基于TAI,也就是铯原子辐射振动次数

  •UTC考虑了地球自转越来越慢而有闰秒修正,确保UTC与UT相差不会超过0.9秒,最近一次的闰秒修正为2012年6月30日,当时TAI实际上已超前UTC有35秒之长

  •Unix时间是1970年1月1日00:00:00为起点而经过的秒数,不考虑闰秒,不少来自于Unix的系统、平台、软件等,也都选择这个时间作为时间表示法的起算点

•年历简介:

  •儒略历:修正了罗马历隔三年设置一闰年的错误,改采四年一闰

  •格里高利历:改革了儒略历

  •ISO 8601标准:并非年历系统,而是时间日期表示方法的标准,用以统一时间日期的数据交换格式

•时区

  •每个地区的标准时间各不相同

13.2 认识Date与Calendar

•时间轴上瞬间的Date

•System.currentTimeMillis():如果想要取得系统时间,方法之一是使用System.currentTimeMillis()方法,返回的是long类型整数,代表1970年1月1日0时0分0秒0毫秒至今经过的毫秒数

•有人会使用Date实例来取得系统时间描述,不过Date也是偏向机器的时间观点,例如:

import java.util.*;
import static java.lang.System.*;

public class DateDemo {
    public static void main(String[] args) {
        Date date1 = new Date(currentTimeMillis());
        Date date2 = new Date();

        out.println(date1.getTime());
        out.println(date2.getTime());
    }
}

运行结果如下:

•Date有两个构造函数可以使用,一个可使用epoch毫秒数构建,另一个为无自变量构造函数,内部亦是使用System.currentTimeMillis()取得epoch毫秒数,调用getTime()可取得内部保存的epoch毫秒数值

•Date实例基本上建议只用来当作时间轴上的某一瞬间

•因为Date实例的时区无法变换,也不建议使用toString()来得知年月日等字段信息,有关字符串时间格式的处理,不再是Date的职责

•格式化时间日期的DateFormat

•java.text.DateFormat:字符串时间格式处理

•取得SimpleDateFormat实例:

  •直接构建SimpleDateFormat实例

  •使用DateFormat的getDateInstance()、getTimeInstance()、getDateTimeInstance()等静态方法

•通过DateFormat的各种静态方法进行格式化:

import java.util.*;
import static java.lang.System.out;
import static java.text.DateFormat.*;

public class DateFormatDemo {
    public static void main(String[] args) {
        Date date = new Date();
        dateInstanceDemo(date);
        timeInstanceDemo(date);
        dateTimeInstanceDemo(date);
    }
    
    static void dateInstanceDemo(Date date) {
        out.println("getDateInstance() demo");
        out.printf("\tSHORT: %s%n", getDateInstance(LONG).format(date));
        out.printf("\tSHORT: %s%n", getDateInstance(SHORT).format(date));
    }

    static void timeInstanceDemo(Date date) {
        out.println("getTimeInstance() demo");
        out.printf("\tLONG: %s%n", getTimeInstance(LONG).format(date));
        out.printf("\tMEDIUM: %s%n", getTimeInstance(MEDIUM).format(date));
        out.printf("\tSHORT: %s%n",getTimeInstance(SHORT).format(date));
    }
    
    static void dateTimeInstanceDemo(Date date) {
        out.println("getDateTimeInstance() demo");
        out.printf("\tLONG: %s%n", 
           getDateTimeInstance(LONG, LONG).format(date));
        out.printf("\tMEDIUM: %s%n",
           getDateTimeInstance(SHORT, MEDIUM).format(date));
        out.printf("\tSHORT: %s%n",
           getDateTimeInstance(SHORT, SHORT).format(date));
    }
}

运行结果如下:

•直接构建SimpleDateFormat的好处是,可使用模式字符串自定义格式,例:

import java.text.*;
import java.util.*;

public class CurrentTime {
    public static void main(String[] args) {
        DateFormat dateFormat = new SimpleDateFormat(
                args.length == 0 ? "EE-MM-dd-yyyy" : args[0]);  
        System.out.println(dateFormat.format(new Date())); 
    }
}

运行结果如下:

•SimpleDateFormat有一个parse()方法,可以按构建SimpleDateFormat时指定的格式,将指定的字符串剖析为Date实例,例:

import java.util.*;
import java.text.*;

public class HowOld {
    public static void main(String[] args) throws Exception {
        System.out.print("输入出生年月日(yyyy-mm-dd):");
        DateFormat dateFormat = new SimpleDateFormat("yyyy-mm-dd");
        Date birthDate = dateFormat.parse(new Scanner(System.in).nextLine());
        Date currentDate = new Date();
        long life = currentDate.getTime() - birthDate.getTime();
        System.out.println("你今年的岁数为:" +
                (life / (365 * 24 * 60 * 60 * 1000L)));
    }
}


运行结果如下:

•处理时间日期的Calendar

•Date现在建议作为时间轴上的瞬时代表

•DateFormat:格式化时间日期

•Calendar:取得某个时间日期信息,或者对时间日期进行操作

•通过Calendar的getInstance()取得的Calendar实例,默认就是取得GregorianCalendar实例,例如:

Calendar calendar = Calendar.getInstance();

•取得Calendar实例后,可以使用getTime()取得Date实例,如果想要取得年月日等日期时间字段,可以使用get()方法指定Calender上的字段枚举常数:

out.println(calendar.get(Calendar.YEAR));
out.println(calendar.get(Calendar.MOUNTH));
out.println(calendar.get(Calendar.DATE));

•如果你要设定时间日期等字段,不要对Date设定,应该使用out.println(calendar.get(Calendar.YEAR));,同样地,月份的部分请使用枚举常数设定:

Calendar calendar = Calendar.getInstace();
calendar.set(2014,Calendar.MAY,26);    //2015/5/26
out.println(calendar.get(Calendar.YEAR));     //2014
out.println(calendar.get(Calendar.MOUNTH));   //Calendar.MAY的值4
out.println(calendar.get(Calendar.DATE));     //26

•在取得一个Calendar的实例后,可以使用add()方法,来改变Calendar的时间:

calendar.add(Calendar.MONTH,1);  //Calendar时间加一个月
calendar.add(Calendar.HOUR,3);   //Calendar时间加三小时
calendar.add(Calendar.YEAR,-2);  //Calendar时间减两年
calendar.add(Calendar.DATE,3);   //Calendar时间加三天

•如果打算只针对日期中某个字段加减,则可以使用roll()方法:

calendar.roll(Calendar.DATE,1);  //只对日字段加1

•想比较两个Calendar的时间日期先后,可以使用after()或before()方法

•设定TimeZone

•使用Calendar时若没有使用时区信息,则会使用默认时区

•取得默认时区信息:使用java.util.TimeZone的getDefault(),例:

import static java.lang.System.out;
import java.util.TimeZone;

public class TimeZoneDemo {
    public static void main(String[] args) {
        TimeZone timeZone = TimeZone.getDefault();
        out.println(timeZone.getDisplayName());
        out.println("\t时区ID:" + timeZone.getID());
        out.println("\t日光节约时数:" + timeZone.getDSTSavings());
        out.println("\tUTC 偏移毫秒数:" + timeZone.getRawOffset());
    }
}

运行结果为:

•可使用ID字符串取得指定时区的TimeZone实例:

TimeZone taipeiTz = TimeZone.getTimeZone("Asia/Taipei");
TimeZone copenhagenTz = TimeZone.getTimeZone("Europe/Copenhagen");

•可使用的ID可用TimeZone.getAvailableIDs()来取得,它会返回String[]

•Calendar在调用getInstance()时,可以指定TimeZone,如果已经取得Calendar实例,也可以通过setImeZone()方法设定TimeZone

13.3 JDK8新时间日期API•

•机器时间观点的API

•如果你取得Date实例,下一步该获取时间信息应该是通过Date的getTime()取得epoch毫秒数,这样就不会混淆

•now():取得代表Java epoch毫秒数的Instance实例

•ofEpochMill():指定Java epoch毫秒数

•ofEpochSecond():指定秒数,在取得Instance实例后,可以使用plusScends()、plusMillis()、plusNanos()、minusSeconds()、minusMillis()、minusNanos()来做时间轴上的运算

•toInstance():想要改用Instance,可以调用Date实例的toInstance()方法来取得,如果有个Instance实例,可以使用Date的静态方法from()转为Date

•人类时间观点的API

•LocalDateTime、LocalDate、LocalTime

  •LocalDateTime:包括日期与时间

  •LocalDate:只有日期,如果设定不存在的日期,会抛出DateTimeException

  •LocalTime:只有时间

  •这些类是不具时区的时间与日期定义

  •LocalDateTime.of:由于没有时区信息,程序无从判断这个时间是否不存在,就不会抛出DateTimeException

•ZonedDateTime、OffsetdateTime

  •如果你的时间日期需要带有时区,可以基于LocalDateTime、LocalDate、LocalTime等来补充缺少的信息:
import static java.lang.System.out;
import java.time.*;

public class ZonedDateTimeDemo {
    public static void main(String[] args) {
        LocalTime localTime = LocalTime.of(0, 0, 0);
        LocalDate localDate = LocalDate.of(1975, 4, 1);
        ZonedDateTime zonedDateTime = ZonedDateTime.of(
                localDate, localTime, ZoneId.of("Asia/Taipei"));
        
        out.println(zonedDateTime);
        out.println(zonedDateTime.toEpochSecond());
        out.println(zonedDateTime.toInstant().toEpochMilli());
    }
}

运行结果如下:

•Year、YearMonth、Month、MonthDay

  •如果想要取得代表月份的数字,不要使用oridinal()方法,因为oridinal()是enum在定义时的顺序,从0开始,想要取得代表月份的数要通过getValue()方法,例:
import static java.lang.System.out;
import java.time.Month;

public class MonthDemo {
    public static void main(String[] args) {
        for(Month month : Month.values()) {
            out.printf("original: %d\tvalue: %d\t%s%n", 
                    month.ordinal(), month.getValue(), month);
        }
    }
}

运行结果为:

•对时间的运算

•TemporalAmount

  •ofPattern():是java.time.format.DateTimeFormatter的静态方法

  •Duration:对于时间计量,新时间与日期API以类Duration来定义,可用于计量天、时、分、秒的时间差,精度调整可以达纳秒等级,而秒的最大值可以是long类型可保存值

  •Period:对于年、月、星期、日的日期差,使用Period类定义

  •plus()方法可以接受Duration实例来计算

•TemporalUnit

  •plus()方法另一重载版本,接受java.time.temporal.TemporalUnit实例,java.time.temporal.ChronoUnit是TemporalUnit实作类,使用enum实作

  •TemporalUnit定义了between()等方法

•Temporal

  •两个重载版本的plus():

     •plus(TemporalAmount amount)

     •plus(long amountToAdd,TemporalUnit unit)

  •两个重载版本的minus():

     •minus(TemporalAmount amount)

     •minus(long amountToSubtract,TemporalUnit unit)

•TemporalAccessor

  •TemporalAccessor定义了只读的时间对象读取操作,实际上Temporal是TemporalAccessor子接口,增加了对时间的处理操作,像是plus()、minus()、with()等方法

•年历系统设计

•java.time套件中的类在需要实行年历系统时都是采用单一的ISO8601年历系统

•需要其他年历系统:需要明确实行java.time.chrono中等操作了java.time.chrono.Chronology接口的类

教材学习中的问题和解决过程

问题

如何计算一个程序的运行时间

解决

(1)以毫秒为单位计算:

  long startTime=System.currentTimeMillis();   //获取开始时间

  doSomeThing();  //测试的代码段

  long endTime=System.currentTimeMillis(); //获取结束时间

  System.out.println("程序运行时间: "+(end-start)+"ms");

(2)以纳秒为单位计算:

  long startTime=System.nanoTime();   //获取开始时间

  doSomeThing();  //测试的代码段

  long endTime=System.nanoTime(); //获取结束时间

  System.out.println("程序运行时间: "+(end-start)+"ns");

代码调试中的问题和解决过程

问题

书上435页的代码为什么要调用clone()?

import static java.lang.System.out;
import java.util.Calendar;

public class CalendarUtil {
    public static void main(String[] args) {
        Calendar birth = Calendar.getInstance();
        birth.set(1975, Calendar.MAY, 26);
        Calendar now = Calendar.getInstance();
        out.printf("岁数:%d%n", yearsBetween(birth, now));
        out.printf("天数:%d%n", daysBetween(birth, now));
    }

    public static long yearsBetween(Calendar begin, Calendar end) {
        Calendar calendar = (Calendar) begin.clone();
        long years = 0;
        while (calendar.before(end)) {
            calendar.add(Calendar.YEAR, 1);
            years++;
        }
        return years - 1;
    }

    public static long daysBetween(Calendar begin, Calendar end) {
        Calendar calendar = (Calendar) begin.clone();
        long days = 0;
        while (calendar.before(end)) {
            calendar.add(Calendar.DATE, 1);
            days++;
        }
        return days - 1;
    }
}

解决

避免调用yearsBetween()、datsBetween()之后传入的Calendar自变量被修改,两个方法都对第一个自变量进行了clone()复制对象的动作。执行结果如下:

本周代码托管截图

其他(感悟、思考等,可选)

开学七周,java课程已经学习了大半,这几周我对于java的学习越来越深入,了解的内容也越爱越多。本周的学习任务只有一章,相较于之前每周两章的学习进度,感觉自己更适应本周的对于一章的学习,让我能够更加深入地进行学习与思考,学的内容也更扎实。

学习进度条

代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
目标 5000行 30篇 400小时
第一周 200/200 2/2 20/20
第二周 300/500 1/3 18/38
第三周 500/1000 1/4 22/60
第四周 300/1300 1/5 30/90
第五周 800/2100 1/6 30/120
第六周 600/2700 1/7 40/160
第七周 350/13050 1/8 30/190

参考资料

posted on 2016-04-15 22:24  20145210姚思羽  阅读(246)  评论(2编辑  收藏  举报

导航