Java中的Calendar日历API用法完全解析

第一部分 Calendar介绍
Calendar 定义:

public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar> {}

Calendar 可以看作是一个抽象类。
它的实现,采用了设计模式中的工厂方法。表现在:当我们获取Calendar实例时,Calendar会根据传入的参数来返回相应的Calendar对象。获取Calendar实例,有以下两种方式:
(1) 当我们通过 Calendar.getInstance() 获取日历时,默认的是返回的一个GregorianCalendar对象。
     GregorianCalendar是Calendar的一个实现类,它提供了世界上大多数国家/地区使用的标准日历系统。
(2) 当我们通过 Calendar.getInstance(TimeZone timezone, Locale locale) 或 Calendar.getInstance(TimeZone timezone) 或 Calendar.getInstance(Locale locale)获取日历时,是返回“对应时区(zone) 或 地区(local)等所使用的日历”。
     例如,若是日本,则返回JapaneseImperialCalendar对象。
参考如下代码:

public static Calendar getInstance()
{
 // 调用createCalendar()创建日历
 Calendar cal = createCalendar(TimeZone.getDefaultRef(), Locale.getDefault());
 cal.sharedZone = true;
 return cal;
}

public static Calendar getInstance(TimeZone zone)
{
 // 调用createCalendar()创建日历
 return createCalendar(zone, Locale.getDefault());
}

public static Calendar getInstance(Locale aLocale) {
 // 调用createCalendar()创建日历
 Calendar cal = createCalendar(TimeZone.getDefaultRef(), aLocale);
 cal.sharedZone = true;
 return cal;
}

public static Calendar getInstance(TimeZone zone,
   Locale aLocale)
{
 // 调用createCalendar()创建日历
 return createCalendar(zone, aLocale);
}

private static Calendar createCalendar(TimeZone zone,
   Locale aLocale)
{
 // (01) 若地区是“th”,则返回BuddhistCalendar对象
 // (02) 若地区是“JP”,则返回JapaneseImperialCalendar对象
 if ("th".equals(aLocale.getLanguage())
 && ("TH".equals(aLocale.getCountry()))) {
 return new sun.util.BuddhistCalendar(zone, aLocale);
 } else if ("JP".equals(aLocale.getVariant())
 && "JP".equals(aLocale.getCountry())
 && "ja".equals(aLocale.getLanguage())) {
 return new JapaneseImperialCalendar(zone, aLocale);
 } 

 // (03) 否则,返回GregorianCalendar对象
 return new GregorianCalendar(zone, aLocale);
}

当我们获取Calendar实例之后,就可以通过Calendar提供的一些列方法来操作日历。

第二部分 Calendar的原理和思想
我们使用Calendar,无非是操作Calendar的“年、月、日、星期、时、分、秒”这些字段。下面,我们对这些字段的的来源、定义以及计算方法进行学习。
1. Calendar 各个字段值的来源
我们使用Calendar,无非是使用“年、月、日、星期、时、分、秒”等信息。那么它是如何做到的呢? 本质上,Calendar就是保存了一个时间。如下定义:

// time 是当前时间,单位是毫秒。
// 它是当前时间距离“January 1, 1970, 0:00:00 GMT”的差值。
protected long time;

Calendar就是根据 time 计算出 “Calendar的年、月、日、星期、时、分、秒”等等信息。

2. Calendar 各个字段的定义和初始化
Calendar 的“年、月、日、星期、时、分、秒”这些信息,一共是17个字段。
我们使用Calendar,无非是就是使用这17个字段。它们的定义如下:
(字段0) public final static int ERA = 0;
说明:纪元。
取值:只能为0 或 1。0表示BC(“before Christ”,即公元前),1表示AD(拉丁语“Anno Domini”,即公元)。
(字段1) public final static int YEAR = 1;
说明:年。
(字段2) public final static int MONTH = 2;
说明:月
取值:可以为,JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER, UNDECIMBER。
     其中第一个月是 JANUARY,它为 0。
(字段3) public final static int WEEK_OF_YEAR = 3;
说明:当前日期在本年中对应第几个星期。一年中第一个星期的值为 1。
(字段4) public final static int WEEK_OF_MONTH = 4;
说明:当前日期在本月中对应第几个星期。一个月中第一个星期的值为 1。
(字段5) public final static int DATE = 5;
说明:日。一个月中第一天的值为 1。
(字段5) public final static int DAY_OF_MONTH = 5;
说明:同“DATE”,表示“日”。
(字段6) public final static int DAY_OF_YEAR = 6;
说明:当前日期在本年中对应第几天。一年中第一天的值为 1。
(字段7) public final static int DAY_OF_WEEK = 7;
说明:星期几。
取值:可以为,SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY 和 SATURDAY。
     其中,SUNDAY为1,MONDAY为2,依次类推。
(字段8) public final static int DAY_OF_WEEK_IN_MONTH = 8;
说明:当前月中的第几个星期。
取值:DAY_OF_MONTH 1 到 7 总是对应于 DAY_OF_WEEK_IN_MONTH 1;8 到 14 总是对应于 DAY_OF_WEEK_IN_MONTH 2,依此类推。
(字段9) public final static int AM_PM = 9;
说明:上午 还是 下午
取值:可以是AM 或 PM。AM为0,表示上午;PM为1,表示下午。
(字段10) public final static int HOUR = 10;
说明:指示一天中的第几小时。
     HOUR 用于 12 小时制时钟 (0 - 11)。中午和午夜用 0 表示,不用 12 表示。
(字段11) public final static int HOUR_OF_DAY = 11;
说明:指示一天中的第几小时。
     HOUR_OF_DAY 用于 24 小时制时钟。例如,在 10:04:15.250 PM 这一时刻,HOUR_OF_DAY 为 22。
(字段12) public final static int MINUTE = 12;
说明:一小时中的第几分钟。
例如,在 10:04:15.250 PM这一时刻,MINUTE 为 4。
(字段13) public final static int SECOND = 13;
说明:一分钟中的第几秒。
例如,在 10:04:15.250 PM 这一时刻,SECOND 为 15。
(字段14) public final static int MILLISECOND = 14;
说明:一秒中的第几毫秒。
例如,在 10:04:15.250 PM 这一时刻,MILLISECOND 为 250。
(字段15) public final static int ZONE_OFFSET = 15;
说明:毫秒为单位指示距 GMT 的大致偏移量。
(字段16) public final static int DST_OFFSET = 16;
说明:毫秒为单位指示夏令时的偏移量。
public final static int FIELD_COUNT = 17;
这17个字段是保存在int数组中。定义如下:

// 保存这17个字段的数组
protected int  fields[];
// 数组的定义函数
protected Calendar(TimeZone zone, Locale aLocale)
{
 // 初始化“fields数组”
 fields = new int[FIELD_COUNT];
 isSet = new boolean[FIELD_COUNT];
 stamp = new int[FIELD_COUNT];

 this.zone = zone;
 setWeekCountData(aLocale);
}

protected Calendar(TimeZone zone, Locale aLocale) 这是Calendar的构造函数。它会被它的子类的构造函数调用到,从而新建“保存Calendar的17个字段数据”的数组。

3. Calendar 各个字段值的计算
下面以get(int field)为例,简要的说明Calendar的17个字段的计算和操作。 get(int field)是获取“field”字段的值。它的定义如下:

public int get(int field) {
 // 计算各个字段的值
 complete();
 // 返回field字段的值
 return internalGet(field);
}

说明:get(int field)的代码很简单。先通过 complete() 计算各个字段的值,然后在通过 internalGet(field) 返回“field字段的值”。
complete() 的作用就是计算Calendar各个字段的值。它定义在Calendar.java中,代码如下:

protected void complete()
{
 if (!isTimeSet)
 updateTime();
 if (!areFieldsSet || !areAllFieldsSet) {
 computeFields(); // fills in unset fields
 areAllFieldsSet = areFieldsSet = true;
 }
}
private void updateTime() {
 computeTime();
 isTimeSet = true;
}
updateTime() 调用到的 computeTime() 定义在 Calendar.java的实现类中。下面,我列出GregorianCalendar.java中computeTime()的实现:
protected void computeTime() {
 // In non-lenient mode, perform brief checking of calendar
 // fields which have been set externally. Through this
 // checking, the field values are stored in originalFields[]
 // to see if any of them are normalized later.
 if (!isLenient()) {
 if (originalFields == null) {
  originalFields = new int[FIELD_COUNT];
 }
 for (int field = 0; field < FIELD_COUNT; field++) {
  int value = internalGet(field);
  if (isExternallySet(field)) {
  // Quick validation for any out of range values
  if (value < getMinimum(field) || value > getMaximum(field)) {
   throw new IllegalArgumentException(getFieldName(field));
  }
  }
  originalFields[field] = value;
 }
 }

 // Let the super class determine which calendar fields to be
 // used to calculate the time.
 int fieldMask = selectFields();

 // The year defaults to the epoch start. We don't check
 // fieldMask for YEAR because YEAR is a mandatory field to
 // determine the date.
 int year = isSet(YEAR) ? internalGet(YEAR) : EPOCH_YEAR;

 int era = internalGetEra();
 if (era == BCE) {
 year = 1 - year;
 } else if (era != CE) {
 // Even in lenient mode we disallow ERA values other than CE & BCE.
 // (The same normalization rule as add()/roll() could be
 // applied here in lenient mode. But this checking is kept
 // unchanged for compatibility as of 1.5.)
 throw new IllegalArgumentException("Invalid era");
 }

 // If year is 0 or negative, we need to set the ERA value later.
 if (year <= 0 && !isSet(ERA)) {
 fieldMask |= ERA_MASK;
 setFieldsComputed(ERA_MASK);
 }

 // Calculate the time of day. We rely on the convention that
 // an UNSET field has 0.
 long timeOfDay = 0;
 if (isFieldSet(fieldMask, HOUR_OF_DAY)) {
 timeOfDay += (long) internalGet(HOUR_OF_DAY);
 } else {
 timeOfDay += internalGet(HOUR);
 // The default value of AM_PM is 0 which designates AM.
 if (isFieldSet(fieldMask, AM_PM)) {
  timeOfDay += 12 * internalGet(AM_PM);
 }
 }
 timeOfDay *= 60;
 timeOfDay += internalGet(MINUTE);
 timeOfDay *= 60;
 timeOfDay += internalGet(SECOND);
 timeOfDay *= 1000;
 timeOfDay += internalGet(MILLISECOND);

 // Convert the time of day to the number of days and the
 // millisecond offset from midnight.
 long fixedDate = timeOfDay / ONE_DAY;
 timeOfDay %= ONE_DAY;
 while (timeOfDay < 0) {
 timeOfDay += ONE_DAY;
 --fixedDate;
 }

 // Calculate the fixed date since January 1, 1 (Gregorian).
 calculateFixedDate: {
 long gfd, jfd;
 if (year > gregorianCutoverYear && year > gregorianCutoverYearJulian) {
  gfd = fixedDate + getFixedDate(gcal, year, fieldMask);
  if (gfd >= gregorianCutoverDate) {
  fixedDate = gfd;
  break calculateFixedDate;
  }
  jfd = fixedDate + getFixedDate(getJulianCalendarSystem(), year, fieldMask);
 } else if (year < gregorianCutoverYear && year < gregorianCutoverYearJulian) {
  jfd = fixedDate + getFixedDate(getJulianCalendarSystem(), year, fieldMask);
  if (jfd < gregorianCutoverDate) {
  fixedDate = jfd;
  break calculateFixedDate;
  }
  gfd = fixedDate + getFixedDate(gcal, year, fieldMask);
 } else {
  gfd = fixedDate + getFixedDate(gcal, year, fieldMask);
  jfd = fixedDate + getFixedDate(getJulianCalendarSystem(), year, fieldMask);
 }
 // Now we have to determine which calendar date it is.
 if (gfd >= gregorianCutoverDate) {
  if (jfd >= gregorianCutoverDate) {
  fixedDate = gfd;
  } else {
  // The date is in an "overlapping" period. No way
  // to disambiguate it. Determine it using the
  // previous date calculation.
  if (calsys == gcal || calsys == null) {
   fixedDate = gfd;
  } else {
   fixedDate = jfd;
  }
  }
 } else {
  if (jfd < gregorianCutoverDate) {
  fixedDate = jfd;
  } else {
  // The date is in a "missing" period.
  if (!isLenient()) {
   throw new IllegalArgumentException("the specified date doesn't exist");
  }
  // Take the Julian date for compatibility, which
  // will produce a Gregorian date.
  fixedDate = jfd;
  }
 }
 }

 // millis represents local wall-clock time in milliseconds.
 long millis = (fixedDate - EPOCH_OFFSET) * ONE_DAY + timeOfDay;

 // Compute the time zone offset and DST offset. There are two potential
 // ambiguities here. We'll assume a 2:00 am (wall time) switchover time
 // for discussion purposes here.
 // 1. The transition into DST. Here, a designated time of 2:00 am - 2:59 am
 // can be in standard or in DST depending. However, 2:00 am is an invalid
 // representation (the representation jumps from 1:59:59 am Std to 3:00:00 am DST).
 // We assume standard time.
 // 2. The transition out of DST. Here, a designated time of 1:00 am - 1:59 am
 // can be in standard or DST. Both are valid representations (the rep
 // jumps from 1:59:59 DST to 1:00:00 Std).
 // Again, we assume standard time.
 // We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET
 // or DST_OFFSET fields; then we use those fields.
 TimeZone zone = getZone();
 if (zoneOffsets == null) {
 zoneOffsets = new int[2];
 }
 int tzMask = fieldMask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK);
 if (tzMask != (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) {
 if (zone instanceof ZoneInfo) {
  ((ZoneInfo)zone).getOffsetsByWall(millis, zoneOffsets);
 } else {
  int gmtOffset = isFieldSet(fieldMask, ZONE_OFFSET) ?
  internalGet(ZONE_OFFSET) : zone.getRawOffset();
  zone.getOffsets(millis - gmtOffset, zoneOffsets);
 }
 }
 if (tzMask != 0) {
 if (isFieldSet(tzMask, ZONE_OFFSET)) {
  zoneOffsets[0] = internalGet(ZONE_OFFSET);
 }
 if (isFieldSet(tzMask, DST_OFFSET)) {
  zoneOffsets[1] = internalGet(DST_OFFSET);
 }
 }

 // Adjust the time zone offset values to get the UTC time.
 millis -= zoneOffsets[0] + zoneOffsets[1];

 // Set this calendar's time in milliseconds
 time = millis;

 int mask = computeFields(fieldMask | getSetStateFields(), tzMask);

 if (!isLenient()) {
 for (int field = 0; field < FIELD_COUNT; field++) {
  if (!isExternallySet(field)) {
  continue;
  }
  if (originalFields[field] != internalGet(field)) {
  // Restore the original field values
  System.arraycopy(originalFields, 0, fields, 0, fields.length);
  throw new IllegalArgumentException(getFieldName(field));
  }
 }
 }
 setFieldsNormalized(mask);
}

下面,我们看看internalGet(field)的定义。如下:

protected final int internalGet(int field) {
 return fields[field];
}

从中,我们就看出,get(int field) 最终是通过 internalGet(int field)来返回值的。
而 internalGet(int field) ,实际上返回的是field数组中的第field个元素。这就正好和Calendar的17个元素所对应了!
总之,我们需要了解的就是:Calendar就是以一个time(毫秒)为基数,而计算出“年月日时分秒”等,从而方便我们对“年月日时分秒”等进行操作。下面,介绍以下Calendar提供的相关操作函数。

第三部分 Calendar函数接口
1. Calendar的17个字段的公共接口
Calendar的这17个字段,都支持下面的公共函数接口。 这些公共接口的使用示例,请参考CalendarTest.java 示例中的 testAllCalendarSections() 函数。
(1) getMaximum(int field)
作用:获取“字段的最大值”。注意“对比它和 getActualMaximum() 的区别”。 示例:以“MONTH”字段来说。使用方法为:

// 获取Calendar实例
Calendar cal = Calendar.getInstance();
// 获取MONTH的最大值
int max = cal.getMaximum(Calendar.MONTH);

若要获取其它字段的最大值,只需要将示例中的MONTH相应的替换成其它字段名即可。
(2) getActualMaximum(int field)
作用:获取“当前日期下,该字段的最大值”。 示例:以“MONTH”字段来说。使用方法为:

// 获取Calendar实例
Calendar cal = Calendar.getInstance();
// 获取当前MONTH的最大值
int max = cal.getActualMaximum(Calendar.MONTH);

若要获取其它字段的最大值,只需要将示例中的MONTH相应的替换成其它字段名即可。
注意:对比getActualMaximum() 和 getMaximum() 的区别。参考下面的对比示例,
A、 getMaximum() 获取的“字段最大值”,是指在综合所有的日期,在所有这些日期中得出的“字段最大值”。
     例如,getMaximum(Calendar.DATE)的目的是“获取‘日的最大值'”。综合所有的日期,得出一个月最多有31天。因此,getMaximum(Calendar.DATE)的返回值是“31”!
B、 getActualMaximum() 获取的“当前日期时,该字段的最大值”。
     例如,当日期为2013-09-01时,getActualMaximum(Calendar.DATE)是获取“日的最大值”是“30”。当前日期是9月份,而9月只有30天。因此,getActualMaximum(Calendar.DATE)的返回值是“30”!
(3) getMinimum(int field)
作用:获取“字段的最小值”。注意“对比它和 getActualMinimum() 的区别”。 示例:以“MONTH”字段来说。使用方法为:

// 获取Calendar实例
Calendar cal = Calendar.getInstance();
// 获取MONTH的最小值
int min = cal.getMinimum(Calendar.MONTH);

若要获取其它字段的最小值,只需要将示例中的MONTH相应的替换成其它字段名即可。
(4) getActualMinimum(int field)

作用:获取“当前日期下,该字段的最小值”。 示例:以“MONTH”字段来说。使用方法为:
// 获取Calendar实例
Calendar cal = Calendar.getInstance();
// 获取MONTH的最小值
int min = cal.getMinimum(Calendar.MONTH);

若要获取其它字段的最小值,只需要将示例中的MONTH相应的替换成其它字段名即可。
注意:在Java默认的Calendar中,虽然 getMinimum() 和 getActualMinimum() 的含义不同;但是,它们的返回值是一样的。因为Calendar的默认是返回GregorianCalendar对象,而在GregorianCalendar.java中,getMinimum() 和 getActualMinimum() 返回值一样。
(5) get(int field)
作用:获取“字段的当前值”。获取field字段的当前值。 示例:以“MONTH”字段来说。“获取MONTH的当前值”的方法为:

// 获取Calendar实例
Calendar cal = Calendar.getInstance();
// 获取“cal日历”的当前MONTH值
int MONTH = cal.get(Calendar.MONTH);

若要获取其它字段的当前值,只需要将示例中的MONTH相应的替换成其它字段名即可。
(6) set(int field, int value)
作用:设置“字段的当前值”。设置field字段的当前值为value 示例:以“MONTH”字段来说。“设置MONTH的当前值”的方法为:

// 获取Calendar实例
Calendar cal = Calendar.getInstance();
// 设置“cal日历”的当前MONTH值为 1988年
cal.set(Calendar.MONTH, 1988);

说明:
A、1988 是想要设置的MONTH的当前值。这个设置值必须是整数。
B、若要设置其它字段的当前值,只需要将示例中的MONTH相应的替换成其它字段名即可。
(7) add(int field, int value)
作用:给“字段的当前值”添加值。给field字段的当前值添加value。 示例:以“MONTH”字段来说。方法如下:

// 获取Calendar实例,并设置日期为“2013-09-01”
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, 2013);
cal.set(Calendar.MONTH, 8);
cal.set(Calendar.DATE, 1);
// 给“cal日历”的当前MONTH值 “添加-10”
cal.add(Calendar.MONTH, -10);

说明:
A、 -10 是添加值。
     添加值可以为正数,也可以是负数。
     正数表示将日期增加,负数表示将日期减少。
     假设:现在cal的值是“2013-09-01”,现在我们将MONTH字段值增加-10。得到的结果是:“2012-10-01”。
     为什么会这样呢?“2013-09-01”增加-10,也就是将日期向前减少10个月;得到的结果就是“2012-10-01”。
B、 Calendar的17个字段中:除了回滚Calendar.ZONE_OFFSET时,会抛出IllegalArgumentException异常;其它的字段都支持该操作。
C、 若要设置其它字段的当前值,只需要将示例中的MONTH相应的替换成其它字段名即可。
(8) roll(int field, int value)
作用:回滚“字段的当前值” 示例:以“MONTH”字段来说。“回滚MONTH的当前值”的方法为:

// 获取Calendar实例,并设置日期为“2013-09-01”
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, 2013);
cal.set(Calendar.MONTH, 8);
cal.set(Calendar.DATE, 1);
// 将“cal日历”的当前MONTH值 “向前滚动10”
cal.roll(Calendar.MONTH, -10);

说明:
A、 -10 是回滚值。
     当回滚值是负数时,表示将当前字段向前滚;
     当回滚值是正数时,表示将当前字段向后滚。
     回滚Calendar中某一字段时,不更改更大的字段!
     这是roll()与add()的根据区别!add()可能会更改更大字段,比如“使用add()修改‘MONTH'字段,可能会引起‘YEAR'字段的改变”;但是roll()不会更改更大的字段,例如“使用roll()修改‘MONTH'字段,不回引起‘YEAR'字段的改变。”
     假设:现在cal的值是“2013-09-01”,现在我们将MONTH字段值增加-10。得到的结果是:“2013-10-01”。
     为什么会这样呢?这就是因为“回滚”就是“在最小值和最大值之间来回滚动”。本例中,MONTH是9月,前回滚10,得到的值是10月,但是roll()不会改变“比MONTH”更大的字段,所以YEAR字段不会改变。所以结果是“2013-10-01”。
B、 Calendar的17个字段中:除了回滚Calendar.ZONE_OFFSET时,会抛出IllegalArgumentException异常;其它的字段都支持该操作。
C、 若要设置其它字段的当前值,只需要将示例中的MONTH相应的替换成其它字段名即可。
(9) clear(int field)
作用:清空“字段的当前值”。所谓清空,实际上是将“field”的值设置为0;若field最小值为1,则设置为1。 示例:以“MONTH”字段来说。“清空MONTH”的方法为:

// 获取Calendar实例,并设置日期为“9月”
Calendar cal = Calendar.getInstance();
cal.set(Calendar.MONTH, 9);
// 清空MONTH
cal.clear(Calendar.MONTH);

若要清空其它字段,只需要将示例中的MONTH相应的替换成其它字段名即可。
(10) isSet(int field)
作用:判断“字段field”是否被设置。若调用clear()清空之后,则field变为“没有设置状态”。 示例:以“MONTH”字段来说。“判断MONTH是否被设置”的方法为:

// 获取Calendar实例
Calendar cal = Calendar.getInstance();
// 判断MONTH是否被设置
boolean bset = cal.isSet(Calendar.MONTH);

若要判断其它字段,只需要将示例中的MONTH相应的替换成其它字段名即可。

2. Calendar的其它函数
(1) 日期比较函数
Calendar的比较函数,主要有以下几个:

// 比较“当前Calendar对象”和“calendar” 的日期、时区等内容是否相等。
boolean equals(Object object)
// 当前Calendar对象 是否 早于calendar
boolean before(Object calendar)
// 当前Calendar对象 是否 晚于calendar
boolean after(Object calendar)
// 比较“当前Calendar对象”和“calendar”。
// 若 早于 “calendar” 则,返回-1
// 若 相等, 则,返回0
// 若 晚于 “calendar” 则,返回1
int compareTo(Calendar anotherCalendar)

这些函数的使用示例,请参考CalendarTest.java示例中的 testComparatorAPIs() 函数。
示例:假设cal1 和 cal2 都是Calendar的两个对象。

// 它们的使用方法如下
boolean isEqual = cal1.equals(cal2);
boolean isBefore = cal1.before(cal2);
boolean isAfter = cal1.after(cal2);
int icompare = cal1.compareTo(cal2);

(2) “宽容”函数

// 设置“Calendar的宽容度”
void setLenient(boolean value)
// 获取“Calendar的宽容度”
boolean isLenient()

这些函数的使用示例,请参考CalendarTest.java示例中的 testLenientAPIs() 函数。
说明:
Calendar 有两种解释日历字段的模式,即 lenient 和 non-lenient。
A、 当 Calendar 处于 lenient 模式时,它可接受比它所生成的日历字段范围更大范围内的值。当 Calendar 重新计算日历字段值,以便由 get() 返回这些值时,所有日历字段都被标准化。
     例如,lenient 模式下的 GregorianCalendar 将 MONTH == JANUARY、DAY_OF_MONTH == 32 解释为 February 1。
B、 当 Calendar 处于 non-lenient 模式时,如果其日历字段中存在任何不一致性,它都会抛出一个异常。
     例如,GregorianCalendar 总是在 1 与月份的长度之间生成 DAY_OF_MONTH 值。如果已经设置了任何超出范围的字段值,那么在计算时间或日历字段值时,处于 non-lenient 模式下的 GregorianCalendar 会抛出一个异常。
注意:在(02)步骤中的异常,在使用set()时不会抛出,而需要在使用get()、getTimeInMillis()、getTime()、add() 和 roll() 等函数中才抛出。因为set()只是设置了一个修改标志,而get()等方法才会引起时间的重新计算,此时才会抛出异常!
(3) "年月日(时分秒)"、Date、TimeZone、MilliSecond函数

// 设置“年月日”
final void set(int year, int month, int day)
// 设置“年月日时分”
final void set(int year, int month, int day, int hourOfDay, int minute, int second)
// 设置“年月日时分秒”
final void set(int year, int month, int day, int hourOfDay, int minute)
// 获取Calendar对应的日期
final Date getTime()
// 设置Calendar为date
final void setTime(Date date)
// 获取Calendar对应的时区
TimeZone getTimeZone()
// 设置Calendar对应的时区
void setTimeZone(TimeZone timezone)
// 获取Calendar对应的milliscondes值,就是“Calendar当前日期”距离“1970-01-01 0:00:00 GMT”的毫秒数
long getTimeInMillis()
// 设置Calendar对应的milliscondes值
void setTimeInMillis(long milliseconds)

这些函数的使用示例,请参考CalendarTest.java示例中的 testTimeAPIs() 函数。
(4) 其它操作

// 克隆Calendar
Object clone()
// 获取“每周的第一天是星期几”。例如,在美国,这一天是 SUNDAY,而在法国,这一天是 MONDAY。
int getFirstDayOfWeek()
// 设置“每周的第一天是星期几”。例如,在美国,这一天是 SUNDAY,而在法国,这一天是 MONDAY。
void setFirstDayOfWeek(int value)
// 获取一年中第一个星期所需的最少天数,例如,如果定义第一个星期包含一年第一个月的第一天,则此方法将返回 1。如果最少天数必须是一整个星期,则此方法将返回 7。
int getMinimalDaysInFirstWeek()
// 设置一年中第一个星期所需的最少天数,例如,如果定义第一个星期包含一年第一个月的第一天,则使用值 1 调用此方法。如果最少天数必须是一整个星期,则使用值 7 调用此方法。
void setMinimalDaysInFirstWeek(int value)

这些函数的使用示例,请参考CalendarTest.java示例中的 testOtherAPIs() 函数。

第四部分 Calendar使用示例
下面,我们通过示例学习使用Calendar的API。CalendarTest.java的源码如下:

import java.util.Date;
import java.util.Calendar;
import java.util.TimeZone;
import java.util.Random;

public class CalendarTest {

 public static void main(String[] args) {

 // 测试Calendar的“17个字段的公共函数接口”
 testAllCalendarSections() ;

 // 测试Calendar的“比较接口”
 testComparatorAPIs() ;

 // 测试Calendar的“比较接口”
 testLenientAPIs() ;

 // 测试Calendar的Date、TimeZone、MilliSecond等相关函数
 testTimeAPIs() ;

 // 测试Calendar的clone(),getFirstDayOfWeek()等接口
 testOtherAPIs() ;

 }

 /**
 * 测试“Calendar的字段”
 *
 * @param cal -- Calendar对象
 * @param field -- 要测试的“Calendar字段”。可以为以下值:
 * Calendar.YEAR, Calendar.MONTH, Calendar.DATE, ... 等等
 * @param title -- 标题
 */
 private static void testSection(Calendar cal, int field, String title) {
 final Random random = new Random();
 final Date date = cal.getTime();

 final int min = cal.getMinimum(field); // 获取"字段最小值"
 final int max = cal.getMaximum(field); // 获取“字段最大值”

 final int actualMin = cal.getActualMinimum(field); // 获取"当前日期下,该字段最小值"
 final int actualMax = cal.getActualMaximum(field); // 获取“当前日期下,该字段的最大值”

 // 获取“字段的当前值”
 final int ori = cal.get(field);  

 // 设置“字段的当前值”, 并获取“设置之后的值”
 final int r1 = random.nextInt(max);
 cal.set(field, r1);
 final int set = cal.get(field);
 try {
  // 回滚“字段的当前值”:在“字段最小值”和“字段最大值”之间回滚。
  // “回滚值”可以为正,也可以为负。
  cal.roll(field, -max);
 } catch (IllegalArgumentException e) {
  // 当field == Calendar.ZONE_OFFSET时,会抛出该异常!
  e.printStackTrace();
 }
 final int roll = cal.get(field);  

 // 获取一个随机值
 final int sign = ( random.nextInt(2) == 1) ? 1 : -1;
 final int r2 = sign * random.nextInt(max);
 try {
  // 增加“字段的当前值” ,并获取“新的当前字段值”
  // add的“参数值”可以为正,也可以为负。
  cal.add(field, r2);
 } catch (IllegalArgumentException e) {
  // 当field == Calendar.ZONE_OFFSET时,会抛出该异常!
  e.printStackTrace();
 }
 final int add = cal.get(field);

 // 打印字段信息
 System.out.printf("%s:\n\trange is [%d - %d] actualRange is [%d - %d]. original=%d, set(%d)=%d, roll(%d)=%d, add(%d)=%d\n",
  title, min, max, actualMin, actualMax, ori, r1, set, -max, roll, r2, add);
 }

 /**
 * 测试Calendar的“17个字段的公共函数接口”
 */
 private static void testAllCalendarSections() {
 // 00. ERA 字段
 testSection(Calendar.getInstance(), Calendar.ERA, "Calendar.ERA");
 // 01. YEAR 字段
 testSection(Calendar.getInstance(), Calendar.YEAR, "Calendar.YEAR");
 // 02. MONTH 字段
 testSection(Calendar.getInstance(), Calendar.MONTH, "Calendar.MONTH");
 // 03. WEEK_OF_YEAR 字段
 testSection(Calendar.getInstance(), Calendar.WEEK_OF_YEAR, "Calendar.WEEK_OF_YEAR");
 // 04. WEEK_OF_MONTH 字段
 testSection(Calendar.getInstance(), Calendar.WEEK_OF_MONTH, "Calendar.WEEK_OF_MONTH");
 // 05. DATE 字段
 testSection(Calendar.getInstance(), Calendar.DATE, "Calendar.DATE");
 // 06. DAY_OF_MONTH 字段
 testSection(Calendar.getInstance(), Calendar.DAY_OF_MONTH, "Calendar.DAY_OF_MONTH");
 // 07. DAY_OF_YEAR 字段
 testSection(Calendar.getInstance(), Calendar.DAY_OF_YEAR, "Calendar.DAY_OF_YEAR");
 // 08. DAY_OF_WEEK 字段
 testSection(Calendar.getInstance(), Calendar.DAY_OF_WEEK, "Calendar.DAY_OF_WEEK");
 // 09. DAY_OF_WEEK_IN_MONTH 字段
 testSection(Calendar.getInstance(), Calendar.DAY_OF_WEEK_IN_MONTH, "Calendar.DAY_OF_WEEK_IN_MONTH");
 // 10. AM_PM 字段
 testSection(Calendar.getInstance(), Calendar.AM_PM, "Calendar.AM_PM");
 // 11. HOUR 字段
 testSection(Calendar.getInstance(), Calendar.HOUR, "Calendar.HOUR");
 // 12. HOUR_OF_DAY 字段
 testSection(Calendar.getInstance(), Calendar.HOUR_OF_DAY, "Calendar.HOUR_OF_DAY");
 // 13. MINUTE 字段
 testSection(Calendar.getInstance(), Calendar.MINUTE, "Calendar.MINUTE");
 // 14. SECOND 字段
 testSection(Calendar.getInstance(), Calendar.SECOND, "Calendar.SECOND");
 // 15. MILLISECOND 字段
 testSection(Calendar.getInstance(), Calendar.MILLISECOND, "Calendar.MILLISECOND");
 // 16. ZONE_OFFSET 字段
 testSection(Calendar.getInstance(), Calendar.ZONE_OFFSET, "Calendar.ZONE_OFFSET");
 }

 /**
 * 测试Calendar的“比较接口”
 */
 private static void testComparatorAPIs() {
 // 新建cal1 ,且时间为1988年
 Calendar cal1 = Calendar.getInstance();
 cal1.set(Calendar.YEAR, 1988);
 // 新建cal2 ,且时间为2000年
 Calendar cal2 = Calendar.getInstance();
 cal2.set(Calendar.YEAR, 2000);
 // 新建cal3, 为cal1的克隆对象
 Calendar cal3 = (Calendar)cal1.clone();

 // equals 判断 cal1和cal2的“时间、时区等”内容是否相等
 boolean isEqual12 = cal1.equals(cal2);
 // equals 判断 cal1和cal3的“时间、时区等”内容是否相等
 boolean isEqual13 = cal1.equals(cal3);
 // cal1是否比cal2早
 boolean isBefore = cal1.before(cal2);
 // cal1是否比cal2晚
 boolean isAfter = cal1.after(cal2);
 // 比较cal1和cal2
 // (01) 若cal1 早于 cal2,返回-1
 // (02) 若cal1 等于 cal2,返回0
 // (03) 若cal1 晚于 cal2,返回1
 int icompare = cal1.compareTo(cal2);

 System.out.printf("\ntestComparatorAPIs: isEuqal12=%s, isEqual13=%s, isBefore=%s, isAfter=%s, icompare=%s\n",
  isEqual12, isEqual13, isBefore, isAfter, icompare);
 }

 /**
 * 测试Calendar的“比较接口”
 */
 private static void testLenientAPIs() {
  Calendar cal = Calendar.getInstance();

  // 获取默认的“宽容度”。返回true
  boolean oriLenient = cal.isLenient();
  // MONTH值只能是“0-11”,这里越界。但是由于当前cal是宽容的,所以不会抛出异常
  cal.set(Calendar.MONTH, 50); 

  // 设置“宽容度”为false。
  cal.setLenient(false);
  // 获取设置后的“宽容度”
  boolean curLenient = cal.isLenient();
  try {
  // MONTH值只能是“0-11”,这里越界。而且当前cal是不宽容的,所以会产生异常。
  // 但是,异常到下次计算日期时才会抛出。即,set()中不回抛出异常,而要等到get()中才会抛出异常
  cal.set(Calendar.MONTH, 50);
  // 此时,对cal进行读取。读取会导致重新计算cal的值,所以此时抛出异常!
  int m2 = cal.get(Calendar.MONTH);
 } catch (IllegalArgumentException e) {
  e.printStackTrace();
 }

 System.out.printf("\ntestLenientAPIs: oriLenient=%s, curLenient=%s\n",
  oriLenient, curLenient);
 }

 /**
 * 测试Calendar的Date、TimeZone、MilliSecond等相关函数
 */
 private static void testTimeAPIs() {
 Calendar cal = Calendar.getInstance();

 // 设置cal的时区为“GMT+8”
 cal.setTimeZone(TimeZone.getTimeZone("GMT+8"));
 // 获取当前的cal时区
 TimeZone timezone = cal.getTimeZone();

 // 设置 milliseconds
 cal.setTimeInMillis(1279419645742l);
 // 获取 milliseconds
 long millis = cal.getTimeInMillis();
 // 设置 milliseconds之后,时间也改变了。
 // 获取cal对应的日期
 Date date = cal.getTime();

 // 设置时间为“1988-08-08”
 cal.set(1988, 08, 08);
 // 设置时间为“1999-09-09 09:09”
 cal.set(1999, 09, 09, 9, 9);
 // 设置时间为“2000-10-10 10:10:10”
 cal.set(2000, 10, 10, 10, 10, 10);

 System.out.printf("\ntestTimeAPIs: date=%s, timezone=%s, millis=%s\n",
  date, timezone, millis);
 }

 /**
 * 测试Calendar的clone(),getFirstDayOfWeek()等接口
 */
 private static void testOtherAPIs() {
 Calendar cal = Calendar.getInstance();
 // 克隆cal
 Calendar clone = (Calendar)cal.clone();

 // 设置 为 2013-01-10。
 // 注:2013-01-01 为“星期二”,2013-01-06为“星期天”,
 clone.set(Calendar.YEAR, 2013);
 clone.set(Calendar.MONTH, 0);
 clone.set(Calendar.DATE, 10);
 // 设置“本年的第一个星期最少包含1天”。
 // 则2013-01-10属于第2个星期
 clone.setMinimalDaysInFirstWeek(1);
 int m1 = clone.getMinimalDaysInFirstWeek();
 int index1 = clone.get(Calendar.WEEK_OF_YEAR);

 // 设置“本年的第一个星期最少包含7天”。
 // 则2013-01-10属于第1个星期
 clone.setMinimalDaysInFirstWeek(7);
 int m2 = clone.getMinimalDaysInFirstWeek();
 int index2 = clone.get(Calendar.WEEK_OF_YEAR);

 // 设置“每周的第一天是星期几”。
 clone.setFirstDayOfWeek(Calendar.WEDNESDAY);
 // 获取“每周的第一天是星期几”。
 int firstdayOfWeek = clone.getFirstDayOfWeek();

 System.out.printf("\ntestOtherAPIs: firstdayOfWeek=%s, [minimalDay, WeekOfYear]={(%s, %s), (%s, %s)} %s\n",
  firstdayOfWeek, m1, index1, m2, index2, clone.getTime());
 }
}

第五部分 自定义的Calendar接口示例
这些接口在写日历程序时可能会用到。
源代码如下(CalendarSelfDefineTest.java):

import java.util.Calendar;

/**
 * 根据Calendar的API封装的一些常用函数
 */
public class CalendarSelfDefineTest {

 public static void main(String[] args) {
 Calendar cal = Calendar.getInstance();

 // 设置日期为“2013-09-18”
 cal.set(2013, Calendar.SEPTEMBER, 18);

 // 获取“年”
 System.out.printf("year: %s\n", getYear(cal) );
 // 获取“月”
 System.out.printf("month: %s\n", getMonth(cal) );
 // 获取“上月”
 System.out.printf("previcou month: %s\n", getLastMonth(cal) );
 // 获取“下月”
 System.out.printf("next month: %s\n", getNextMonth(cal) );
 // 获取“日”
 System.out.printf("day: %s\n", getDay(cal) );
 // 获取Cal对应星期几
 System.out.printf("weekday: %s\n", getWeekDay(cal) );
 // 本月天数
 System.out.printf("Current Month days: %s\n", getMonthDays(cal) );
 // 上月天数
 System.out.printf("Previcous Month days: %s\n", getPrevMonthDays(cal) );
 // 下月天数
 System.out.printf("Next Month days: %s\n", getNextMonthDays(cal) );
 // 获取当月第一天的星期几
 System.out.printf("First day' weekday : %s\n", getFirstDayWeekday(cal) );
 // 获取当前月最后一天的星期几
 System.out.printf("Last day' weekday : %s\n", getLastDayWeekday(cal) );
 // 获取上月最后一天的星期几
 System.out.printf("PrevMonth Last day' weekday: %s\n", getLastDayWeekdayOfPrevMonth(cal) );
 // 获取下月第一天的星期几
 System.out.printf("NextMonth First day' weekday: %s\n", getFirstDayWeekdayOfNextMonth(cal) );
 }

 /**
 * 获取“年”
 *
 * @return 例如,2013-09-18,则返回2013
 */
 public static int getYear(Calendar cal) {
 return cal.get(Calendar.YEAR);
 } 

 /**
 * 获取“月”
 *
 * @return 返回值可以为以下值:
 * JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER, UNDECIMBER。
 * 其中第一个月是 JANUARY,它为 0。
 *
 * 例如,2013-09-18,则返回8
 */
 public static int getMonth(Calendar cal) {
 return cal.get(Calendar.MONTH);
 } 

 /**
 * 获取“上一个月”
 *
 * @return 返回值可以为以下值:
 * JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER, UNDECIMBER。
 * 其中第一个月是 JANUARY,它为 0。
 *
 * 例如,2012-01-12的上一个月是“11”(即DECEMBER)。
 */
 public static int getLastMonth(Calendar cal) {
 return (cal.get(Calendar.MONTH) + 11) % 12;
 } 

 /**
 * 获取“下一个月”
 *
 * @return 返回值可以为以下值:
 * JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER, UNDECIMBER。
 * 其中第一个月是 JANUARY,它为 0。
 *
 * 例如,2013-12-12的下一个月是“1”(即DECEMBER)。
 */
 public static int getNextMonth(Calendar cal) {
 return (cal.get(Calendar.MONTH) + 13) % 12;
 } 

 /**
 * 获取“日”
 *
 * @return 例如,2013-09-18,则返回18
 *
 */
 public static int getDay(Calendar cal) {
 return cal.get(Calendar.DATE);
 } 

 /**
 * 获取“本月的天数”
 *
 * @return 例如,2013-09-18,则返回30
 *
 */
 public static int getMonthDays(Calendar cal) {
 return cal.getActualMaximum(Calendar.DATE);
 } 

 /**
 * 获取“上一个月的天数”
 *
 * @return 例如,2013-01-18,则返回31 (因为2012-12有31天)
 *
 */
 public static int getPrevMonthDays(Calendar cal) {
 Calendar tmpCal = (Calendar)cal.clone(); // 克隆cal。后面对tmpCal操作,就不会改变cal
 tmpCal.add(Calendar.MONTH, -1);   // 设为“上一个月”
 return tmpCal.getActualMaximum(Calendar.DATE);
 } 

 /**
 * 获取“下一个月的天数”
 *
 * @return 例如,2013-12-18,则返回31 (因为2014-01有31天)
 *
 */
 public static int getNextMonthDays(Calendar cal) {
 Calendar tmpCal = (Calendar)cal.clone(); // 克隆cal。后面对tmpCal操作,就不会改变cal
 tmpCal.add(Calendar.MONTH, 1);   // 设为“下一个月”
 return tmpCal.getActualMaximum(Calendar.DATE);
 } 

 /**
 * 获取Cal对应星期几
 *
 * @return 返回“星期几”,可以为以下值:
 * SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY 和 SATURDAY。
 * SUNDAY为1,MONDAY为2,依次类推。
 * 例如,2013-09-18(星期三),则返回4
 */
 public static int getWeekDay(Calendar cal) {
 return cal.get(Calendar.DAY_OF_WEEK);
 } 

 /**
 * 获取当月第一天对应星期几
 *
 * @return SUNDAY为1,MONDAY为2,依次类推。
 */
 public static int getFirstDayWeekday(Calendar cal) {

 Calendar tmpCal = (Calendar)cal.clone(); // 克隆cal。后面对tmpCal操作,就不会改变cal
 tmpCal.set(Calendar.DATE, 1);   // 把日期设置为当月第一天
 return tmpCal.get(Calendar.DAY_OF_WEEK);
 } 

 /**
 * 获取当前月最后一天对应星期几
 *
 * @return SUNDAY为1,MONDAY为2,依次类推。
 */
 public static int getLastDayWeekday(Calendar cal) {
 Calendar tmpCal = (Calendar)cal.clone(); // 克隆cal。后面对tmpCal操作,就不会改变cal
 tmpCal.set(Calendar.DATE, 1);   // 把日期设置为当月第一天
 tmpCal.roll(Calendar.DATE, -1);   // 把日期设置为当月最后一天
 return tmpCal.get(Calendar.DAY_OF_WEEK);
 } 

 /**
 * 获取上月最后一天的星期几
 *
 * @return SUNDAY为1,MONDAY为2,依次类推。
 */
 public static int getLastDayWeekdayOfPrevMonth(Calendar cal) {

 Calendar tmpCal = (Calendar)cal.clone(); // 克隆cal。后面对tmpCal操作,就不会改变cal
 tmpCal.set(Calendar.DATE, 1);   // 把日期设置为当月第一天
 tmpCal.add(Calendar.DATE, -1);   // 把日期设置为上一个月最后一天
 return tmpCal.get(Calendar.DAY_OF_WEEK);
 } 

 /**
 * 获取下月第一天的星期偏移
 *
 * @return SUNDAY为1,MONDAY为2,依次类推。
 */
 public static int getFirstDayWeekdayOfNextMonth(Calendar cal) {

 Calendar tmpCal = (Calendar)cal.clone(); // 克隆cal。后面对tmpCal操作,就不会改变cal
 tmpCal.add(Calendar.MONTH, 1);   // 设为“下一个月”
 tmpCal.set(Calendar.DATE, 1);   // 设为“第一天”
 return tmpCal.get(Calendar.DAY_OF_WEEK);
 }
}
时间: 2016-05-31

Java8新日期时间API的20个使用示例

除了lambda表达式,stream以及几个小的改进之外,Java 8还引入了一套全新的时间日期API,在本篇教程中我们将通过几个简单的任务示例来学习如何使用Java 8的这套API.Java对日期,日历及时间的处理一直以来都饱受诟病,尤其是它决定将java.util.Date定义为可修改的以及将SimpleDateFormat实现成非线程安全的.看来Java已经意识到需要为时间及日期功能提供更好的支持了,这对已经习惯使用Joda时间日期库的社区而言也是件好事.关于这个新的时间日期库的最大的优点

java使用淘宝API读写json实现手机归属地查询功能代码

一般查询手机归属地内容应该很好用json格式保存,在网上找到了淘宝的归属地API,并下了处理json相关的jar包,做了这个手机归属地查询功能 复制代码 代码如下: package com.think.java; import java.io.BufferedReader;import java.io.InputStream;import java.io.InputStreamReader;import java.net.MalformedURLException;import java.net

java调用百度定位api服务获取地理位置示例

复制代码 代码如下: package test; import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.Reader;import java.net.URL;import java.nio.charset.Charset; import org.json.JSONException;imp

5个Java API使用技巧

本文介绍了一些关于Java API安全和性能方面的简单易用的技巧,其中包括保证API Key安全和开发Web Service方面中在框架方面选择的一些建议. 程序员都喜欢使用API!例如为app应用构建API或作为微服务架构体系的一部分.当然,使用API的前提是能让你的工作变得更轻松.为了简化开发和提高工作效率所作出的努力,有时也意味着需要寻找新的类库或者过程(或者减少过程).对于很多开发团队来说,对于其APP和API进行管理认证和访问控制要耗费很多的时间,因此我们需想分享一些技巧,它们能节约你

java调用中国天气网api获得天气预报信息的方法

本文实例讲述了java调用中国天气网api获得天气预报信息的方法.分享给大家供大家参考.具体实现方法如下: //以冰城哈尔滨为例通过中国天气api调用天气信息 private String getWeatherInfo2(){ StringBuilder info = new StringBuilder(); try { DefaultHttpClient httpclient = new DefaultHttpClient(); HttpGet httget = new HttpGet("ht

JAVA IO API使用详解

一.理论准备流是个抽象的概念,是对输入输出设备的抽象,Java程序中,对于数据的输入/输出操作都是以"流"的方式进行,设备可以是文件.网络.内存等.流具有方向性,至于是输入流还是输出流则是一个相对的概念,一般以程序(小马哥说的是机器)为参考,如果数据的流向是程序至设备,我们成为输出流,反之我们称为输入流,可以将流想象成一个"水流管道"(很多资料都这么讲的),自然就出现了方向的概念.流把I/O设备内部的具体操作给隐藏起来了.所有InputStream和Reader的派

用Java实现全国天气预报的api接口调用示例

step1:选择本文所示例的接口"全国天气预报接口" 聚合数据url:http://www.juhe.cn/docs/api/id/39/aid/87 step2:每个接口都需要传入一个参数key,相当于用户的令牌,所以第一步你需要申请一个key. step3:学过java的同学们都知道,当我们对一个类或者方法不明白其意图和思想时,我们可以去查看文档,这里也不例外,而且对于英文不是特别好的同学来说很幸运的是,聚合网站上的文档都是中文版本的,比起阅读java源码里的英文文档应该轻松很多.

Java通过JsApi方式实现微信支付

要使用JsApi进行微信支付,首先要从微信获得一个prepay_id,然后通过调用微信的jsapi完成支付,JS API的返回结果get_brand_wcpay_request:ok仅在用户成功完成支付时返回.由于前端交互复杂,get_brand_wcpay_request:cancel或者get_brand_wcpay_request:fail可以统一处理为用户遇到错误或者主动放弃,不必细化区分. 示例代码如下: function onBridgeReady(){ WeixinJSBridge

详解Java的JDBC API中事务的提交和回滚

如果JDBC连接是在自动提交模式下,它在默认情况下,那么每个SQL语句都是在其完成时提交到数据库. 这可能是对简单的应用程序,但有三个原因,你可能想关闭自动提交和管理自己的事务: 为了提高性能 为了保持业务流程的完整性 使用分布式事务 若要控制事务,以及何时更改应用到数据库.它把单个SQL语句或一组SQL语句作为一个逻辑单元,而且如果任何语句失败,整个事务失败. 若要启用,而不是JDBC驱动程序默认使用auto-commit模式手动事务支持,使用Connection对象的的setAutoComm

android monkey自动化测试改为java调用monkeyrunner Api

众所周知,一般情况下我们使用android中的monkeyrunner进行自动化测试时,使用的是python语言来写测试脚本.不过,最近发现可以用java调用monkeyrunner Api,用java语言写测试脚本. 于是,就简单研究了一下.这里做一些总结.希望有对在研究的午饭可以有所用处. 开始时,搜素到一些零碎的教程,说使用java调用monkeyrunner时,需要导入android sdk  tools路径下的lib里面的4个包:ddmlib.jar,guavalib.jar,monk

浅谈java调用Restful API接口的方式

摘要:最近有一个需求,为客户提供一些RestfulAPI接口,QA使用postman进行测试,但是postman的测试接口与java调用的相似但并不相同,于是想自己写一个程序去测试RestfulAPI接口,由于使用的是HTTPS,所以还要考虑到对于HTTPS的处理.由于我也是首次使用Java调用restful接口,所以还要研究一番,自然也是查阅了一些资料. 分析:这个问题与模块之间的调用不同,比如我有两个模块frontend和backend,frontend提供前台展示,backend提供数据支

Java 调用Restful API接口的几种方式(HTTPS)

摘要:最近有一个需求,为客户提供一些Restful API 接口,QA使用postman进行测试,但是postman的测试接口与java调用的相似但并不相同,于是想自己写一个程序去测试Restful API接口,由于使用的是HTTPS,所以还要考虑到对于HTTPS的处理.由于我也是首次使用Java调用restful接口,所以还要研究一番,自然也是查阅了一些资料. 分析:这个问题与模块之间的调用不同,比如我有两个模块front end 和back end,front end提供前台展示,back

Android Java调用自己C++类库的实例讲解

Android Java 如何调用自己的 C++ 的类库 下面以 Java 调用 C++ 的加法运算函数为例,做简单说明. (使用 Android Studio 3 编译) 首先编译 c++ 类库 创建独立目录存放 c++ 文件,例如 "app/src/main/cpp/add.cpp",内容如下 #include <jni.h> extern "C" JNIEXPORT jint JNICALL Java_com_example_liyi_demo_U

Java调用微信客服消息实现发货通知的方法详解

本文实例讲述了Java调用微信客服消息实现发货通知的方法.分享给大家供大家参考,具体如下: 微信文档地址:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140547&token=&lang=zh_CN 个人说明:这是一个样例,微信客户消息有很多种,我现在用的是公众号发送消息.样子如下图. 说明:下面开始代码部分了. 1.首先看微信文档.这里才是我们需要的 这里是说发消息要POST请求这个接口:https://a

Android中如何利用AIDL机制调用远程服务

在Android中,每个应用程序都有自己的进程,当需要在不同的进程之间传递对象时,该如何实现呢?显然, Java中是不支持跨进程内存共享的.因此要传递对象,需要把对象解析成操作系统能够理解的数据格式,以达到跨界对象访问的目的.在JavaEE中,采用RMI通过序列化传递对象.在Android中,则采用AIDL(Android Interface DefinitionLanguage:接口描述语言)方式实现. AIDL是一种接口定义语言,用于约束两个进程间的通讯规则,供编译器生成代码,实现Andro

Android Monkey压力测试详细介绍

Monkey 是Android SDK提供的一个命令行工具, 可以简单,方便地运行在任何版本的Android模拟器和实体设备上. Monkey会发送伪随机的用户事件流,适合对app做压力测试 阅读目录 环境搭建 什么是Monkey Monkey 用来做什么 Monkey程序介绍 Monkey 架构 Monkey弱点 Monkey 参数大全 Monkey 命令 基本参数介绍 Monkey 实例 Monkey 查看包名 Monkey  日志分析 必须重视Crash 环境搭建 安装Android SD

java调用dll方法总结

常用的就是jni,jnative(基于jni封装了自己的jar包) 注意:java调用dll,必须dll里做了对应的处理.列如:方法名必须为_java_包名_类名_方法名 使用JNI调用dll步骤 新建一个java项目,将dll文件放入src下 配置改项目环境jre下native目录,指向src 项目jdk一定不要用eclipse自带的,用自己安装的jdk路径 按照dll里的目录顺序新建目录及类 声明跟dll里一样的方法,方法前加native关键字(注意参数类型) 代码静态块里写(不要后缀):

Android AIDL实现与服务相互调用方式

通过AIDL接口在进程间传递数据,记录在开发中遇到的一写问题 AIDL支持数据类型如下: 1. Java 的原生类型 2. String 和CharSequence 3. List 和 Map ,List和Map 对象的元素必须是AIDL支持的数据类型: 以上三种类型都不需要导入(import) 4. AIDL 自动生成的接口 需要导入(import) 5. 实现android.os.Parcelable 接口的类. 需要导入(import). 问题1 在传递非基础数据时 在参数前需加修饰符 v

java调用回调机制详解

调用和回调机制 在一个应用系统中, 无论使用何种语言开发, 必然存在模块之间的调用, 调用的方式分为几种: 1.同步调用 同步调用是最基本并且最简单的一种调用方式, 类A的方法a()调用类B的方法b(), 一直等待b()方法执行完毕, a()方法继续往下走. 这种调用方式适用于方法b()执行时间不长的情况, 因为b()方法执行时间一长或者直接阻塞的话, a()方法的余下代码是无法执行下去的, 这样会造成整个流程的阻塞. 2.异步调用 异步调用是为了解决同步调用可能出现阻塞, 导致整个流程卡住而产