面试JavaSE基础(4)

五、JavaSE 常用 API

  1. Math.round(11.5)等于多少?Math.round(- 11.5) 又等于多少?
    Math.round(11.5)的返回值是 12,Math.round(-11.5)的返回值是-11。四舍五入的原理是在参数上加 0.5
    然后进行取整。
    2. switch 是否能作用在 byte 上,是否能作用在 long 上,是否能作用在 String
    上?

    Java5 以前 switch(expr)中,expr 只能是 byte、short、char、int。从 Java 5 开始,Java 中引入了枚举类型,
    expr 也可以是 enum 类型。
    从 Java 7 开始,expr 还可以是字符串(String),但是长整型(long)在目前所有的版本中都是不可以的。
    3. 数组有没有 length() 方法?String 有没有 length() 方法?
    数组没有 length()方法,而是有 length 的属性。String 有 length()方法。JavaScript 中,获得字符串的长度是
    通过 length 属性得到的,这一点容易和 Java 混淆。
    4. String 、StringBuilder 、StringBuffer 的区别?
    Java 平台提供了两种类型的字符串:String 和 StringBuffer/StringBuilder,它们都可以储存和操作字符串,区别
    如下。

1)String 是只读字符串,也就意味着 String 引用的字符串内容是不能被改变的。初学者可能会有这样的误解:
String str = “abc”;
str = “bcd”;
如上,字符串 str 明明是可以改变的呀!其实不然,str 仅仅是一个引用对象,它指向一个字符串对象“abc”。第 二行代码的含义是让 str 重新指向了一个新的字符串“bcd”对象,而“abc”对象并没有任何改变,只不过该对象已 经成为一个不可及对象罢了。
2)StringBuffer/StringBuilder 表示的字符串对象可以直接进行修改。 3)StringBuilder 是 Java5中引入的,它和 StringBuffer 的方法完全相同,区别在于它是在单线程环境下使用的, 因为它的所有方法都没有被synchronized 修饰,因此它的效率理论上也比 StringBuffer 要高。

8. 什么情况下用“+”运算符进行字符串连接比调用 StringBuffer/StringBuilder
对象的 append 方法连接字符串性能更好?

该题来自华为。
字符串是 Java 程序中最常用的数据结构之一。在 Java 中 String 类已经重载了"+"。也就是说,字符串可以直接
使用"+“进行连接,如下面代码所示:
1.String s = “abc” + “ddd”;
但这样做真的好吗?当然,这个问题不能简单地回答 yes or no。要根据具体情况来定。在 Java 中提供了一个StringBuilder 类(这个类只在 J2SE5 及以上版本提供,以前的版本使用 StringBuffer 类),这个类也可以起到”+"的作用。那么我们应该用哪个呢?
下面让我们先看看如下的代码:

9. package string;
2.
10. public class TestSimplePlus
11. {
12. public static void main(String[] args)
13. {
14. String s = "abc";
15. String ss = "ok" + s + "xyz" + 5;
16. System.out.println(ss);
17.10. }
17. }

上面的代码将会输出正确的结果。从表面上看,对字符串和整型使用"+"号并没有什么区别,但事实真的如此吗?
下面让我们来看看这段代码的本质。
我们首先使用反编译工具(如 jdk 带的 javap、或 jad)将 TestSimplePlus 反编译成 Java Byte Code,其中的奥秘就一目了然了。在本文将使用 jad 来反编译,命令如下:
jad -o -a -s d.java TestSimplePlus.class
反编译后的代码如下:

18. package string;
2.
19. import java.io.PrintStream;
4.
20. public class TestSimplePlus
21. {
22. public TestSimplePlus()
23. {
24. // 0 0:aload_0
25. // 1 1:invokespecial #8 <Method void Object()>
26. // 2 4:return
27. }
13.
28. public static void main(String args[])
29. {
30. String s = "abc";
31. // 0 0:ldc1 #16 <String "abc">
32. // 1 2:astore_1
33. String ss = (new StringBuilder("ok")).append(s).append("xyz").append(5).toString();
34. // 2 3:new #18 <Class StringBuilder>
35. // 3 6:dup
36. // 4 7:ldc1 #20 <String "ok">
37. // 5 9:invokespecial #22 <Method void StringBuilder(String)>
38. // 6 12:aload_1
39. // 7 13:invokevirtual #25 <Method StringBuilder StringBuilder.append(String)>
40. // 8 16:ldc1 #29 <String "xyz">
41. // 9 18:invokevirtual #25 <Method StringBuilder StringBuilder.append(String)>
42. // 10 21:iconst_5
43. // 11 22:invokevirtual #31 <Method StringBuilder StringBuilder.append(int)> 
44. 30. // 12 25:invokevirtual #34 <Method String StringBuilder.toString()>
45. // 13 28:astore_2
46. System.out.println(ss);
47. // 14 29:getstatic #38 <Field PrintStream System.out>
48. // 15 32:aload_2
49. // 16 33:invokevirtual #44 <Method void PrintStream.println(String)>
50. // 17 36:return
51. }
52. }

读者可能看到上面的 Java 字节码感到迷糊,不过大家不必担心。本文的目的并不是讲解 Java Byte Code,因此,并不用了解具体的字节码的含义。使用 jad 反编译的好处之一就是可以同时生成字节码和源代码。这样可以进行对照研究。从上面的代码很容易看出,虽然在源程序中使用了"+",但在编译时仍然将"+“转换成 StringBuilder。因此,我们可以得出结论,在 Java 中无论使用何种方式进行字符串连接,实际上都使用的是 StringBuilder。
那么是不是可以根据这个结论推出使用”+“和 StringBuilder 的效果是一样的呢?这个要从两个方面的解释。如果从运行结果来解释,那么”+“和 StringBuilder 是完全等效的。但如果从运行效率和资源消耗方面看,那它们将存在很大的区别。
当然,如果连接字符串行表达式很简单(如上面的顺序结构),那么”+"和 StringBuilder 基本是一样的,但如果结构比较复杂,如使用循环来连接字符串,那么产生的 Java Byte Code 就会有很大的区别。先让我们看看如下的代码:

53. package string;
2.
54. import java.util.*;
4.
55. public class TestComplexPlus
56. {
57. public static void main(String[] args)
58. {
59. String s = "";
60. Random rand = new Random();
61. for (int i = 0; i < 10; i++)
62. {
63. s = s + rand.nextInt(1000) + " ";
64. 14. }
65. System.out.println(s);
66. }
67. }

上面的代码返编译后的 Java Byte Code 如下:

68. package string;
2.
69. import java.io.PrintStream;
70. import java.util.Random;
5.
71. public class TestComplexPlus
72. {
8.
73. public TestComplexPlus()
74. {
75. // 0 0:aload_0
76. // 1 1:invokespecial #8 <Method void Object()>
77. // 2 4:return
78. }
15.
79. public static void main(String args[])
80. {
81. String s = "";
82. // 0 0:ldc1 #16 <String "">
83. // 1 2:astore_1
84. Random rand = new Random();
85. // 2 3:new #18 <Class Random>
86. // 3 6:dup
87. // 4 7:invokespecial #20 <Method void Random()>
88. // 5 10:astore_2
89. for(int i = 0; i < 10; i++)
90. //* 6 11:iconst_0
91. //* 7 12:istore_3
92. //* 8 13:goto 49
93. s = (new StringBuilder(String.valueOf(s))).append(rand.nextInt(1000)).append(" ").t
oString();
94. // 9 16:new #21 <Class StringBuilder>
95. // 10 19:dup
96. // 11 20:aload_1
97. // 12 21:invokestatic #23 <Method String String.valueOf(Object)>
98. // 13 24:invokespecial #29 <Method void StringBuilder(String)>
99. 36. // 14 27:aload_2
100. // 15 28:sipush 1000
101. // 16 31:invokevirtual #32 <Method int Random.nextInt(int)>
102. // 17 34:invokevirtual #36 <Method StringBuilder StringBuilder.append(int)>
103. // 18 37:ldc1 #40 <String " ">
104. // 19 39:invokevirtual #42 <Method StringBuilder StringBuilder.append(String)>
105. // 20 42:invokevirtual #45 <Method String StringBuilder.toString()>
106. // 21 45:astore_1
44.
107. // 22 46:iinc 3 1
108. // 23 49:iload_3
109. // 24 50:bipush 10
110. // 25 52:icmplt 16
111. System.out.println(s);
112. // 26 55:getstatic #49 <Field PrintStream System.out>
113. // 27 58:aload_1
114. // 28 59:invokevirtual #55 <Method void PrintStream.println(String)>
115. // 29 62:return
116. }
117. }

大家可以看到,虽然编译器将"+"转换成了 StringBuilder,但创建 StringBuilder 对象的位置却在 for 语句内
部。这就意味着每执行一次循环,就会创建一个 StringBuilder 对象(对于本例来说,是创建了 10 个 StringBuilder
对象),虽然 Java 有垃圾回收器,但这个回收器的工作时间是不定的。如果不断产生这样的垃圾,那么仍然会占用
大量的资源。解决这个问题的方法就是在程序中直接使用 StringBuilder 来连接字符串,代码如下:

118. package string;
2.
119. import java.util.*;
4.
120. public class TestStringBuilder
121. {
122. public static void main(String[] args)
123. {
124. String s = "";
125. Random rand = new Random();
126. StringBuilder result = new StringBuilder();
127. for (int i = 0; i < 10; i++)
128. {
129. result.append(rand.nextInt(1000));
130.15. result.append(" ");
130. }
131. System.out.println(result.toString());
132. }
133. }

上面代码反编译后的结果如下:

134. 20.package string;
2.
135. import java.io.PrintStream;
136. import java.util.Random;
5.
137. public class TestStringBuilder
138. {
8.
139. public TestStringBuilder()
140. {
141. // 0 0:aload_0
142. // 1 1:invokespecial #8 <Method void Object()>
143. // 2 4:return
144. }
15.
145. public static void main(String args[])
146. {
147. String s = "";
148. // 0 0:ldc1 #16 <String "">
149. // 1 2:astore_1
150. Random rand = new Random();
151. // 2 3:new #18 <Class Random>
152. // 3 6:dup
153. // 4 7:invokespecial #20 <Method void Random()>
154. // 5 10:astore_2
155. StringBuilder result = new StringBuilder();
156. // 6 11:new #21 <Class StringBuilder>
157. // 7 14:dup
158. // 8 15:invokespecial #23 <Method void StringBuilder()>
159. // 9 18:astore_3
160. for(int i = 0; i < 10; i++)
161. //* 10 19:iconst_0
162. //* 11 20:istore 4
163. //* 12 22:goto 47
164. {
165. result.append(rand.nextInt(1000)); 
166.37. // 13 25:aload_3
166. // 14 26:aload_2
167. // 15 27:sipush 1000
168. // 16 30:invokevirtual #24 <Method int Random.nextInt(int)>
169. // 17 33:invokevirtual #28 <Method StringBuilder StringBuilder.append(int)>
170. // 18 36:pop
171. result.append(" ");
172. // 19 37:aload_3
173. // 20 38:ldc1 #32 <String " ">
174. // 21 40:invokevirtual #34 <Method StringBuilder StringBuilder.append(String)>
175. // 22 43:pop
176. }
49.
177. // 23 44:iinc 4 1
178. // 24 47:iload 4
179. // 25 49:bipush 10
180. // 26 51:icmplt 25
181. System.out.println(result.toString());
182. // 27 54:getstatic #37 <Field PrintStream System.out>
183. // 28 57:aload_3
184. // 29 58:invokevirtual #43 <Method String StringBuilder.toString()>
185. // 30 61:invokevirtual #47 <Method void PrintStream.println(String)>
186. // 31 64:return
187. }
188. }

从上面的反编译结果可以看出,创建 StringBuilder 的代码被放在了 for 语句外。虽然这样处理在源程序中看起来复杂,但却换来了更高的效率,同时消耗的资源也更少了。
在使用 StringBuilder 时要注意,尽量不要"+"和 StringBuilder 混着用,否则会创建更多的 StringBuilder 对象,如下面代码所:

for (int i = 0; i < 10; i++)
{
result.append(rand.nextInt(1000));
result.append(" ");
}
改成如下形式:
for (int i = 0; i < 10; i++) 
{
result.append(rand.nextInt(1000) + " ");
}

则反编译后的结果如下:

for(int i = 0; i < 10; i++)
//* 10 19:iconst_0
//* 11 20:istore 4
//* 12 22:goto 65
{
result.append((new StringBuilder(String.valueOf(rand.nextInt(1000)))).append(" ").toString());
// 13 25:aload_3
// 14 26:new #21 <Class StringBuilder>
// 15 29:dup

从上面的代码可以看出,Java 编译器将"+“编译成了 StringBuilder,这样 for 语句每循环一次,又创建了一个StringBuilder 对象。
如果将上面的代码在 JDK1.4 下编译,必须将 StringBuilder 改为 StringBuffer,而 JDK1.4 将”+"转换为StringBuffer(因为 JDK1.4 并没有提供 StringBuilder 类)。StringBuffer 和 StringBuilder 的功能基本一样,只是StringBuffer 是线程安全的,而 StringBuilder 不是线程安全的。因此,StringBuilder 的效率会更高。

  1. 请说出下面程序的输出
  2. class StringEqualTest {
  3. public static void main(String[] args) {
  4. String s1 = “Programming”;
  5. String s2 = new String(“Programming”);
  6. String s3 = “Program”;
  7. String s4 = “ming”;
  8. String s5 = “Program” + “ming”;
  9. String s6 = s3 + s4;
  10. System.out.println(s1 == s2); //false
  11. System.out.println(s1 == s5); //true
  12. System.out.println(s1 == s6); //false
  13. System.out.println(s1 == s6.intern()); //true
  14. System.out.println(s2 == s2.intern()); //false
  15. }
  16. }

补充:解答上面的面试题需要知道如下两个知识点:
String 对象的 intern()方法会得到字符串对象在常量池中对应的版本的引用(如果常量池中有一个字符串与String 对象的 equals 结果是 true),如果常量池中没有对应的字符串,则该字符串将被添加到常量池中,然后返回常量池中字符串的引用; 字符串的+操作其本质是创建了 StringBuilder 对象进行 append 操作,然后将拼接后的 StringBuilder 对象用 toString 方法处理成 String 对象,这一点可以用 javap -c StringEqualTest.class 命令获得 class 文件对应的 JVM 字节码指令就可以看出来。

Java 中的日期和时间

7.1 如何取得年月日、小时分钟秒?
208.

 public class DateTimeTest {
209. public static void main(String[] args) {
210. Calendar cal = Calendar.getInstance();
211. System.out.println(cal.get(Calendar.YEAR));
212. System.out.println(cal.get(Calendar.MONTH)); // 0 - 11
213. System.out.println(cal.get(Calendar.DATE));
214. System.out.println(cal.get(Calendar.HOUR_OF_DAY));
215. System.out.println(cal.get(Calendar.MINUTE));
216. System.out.println(cal.get(Calendar.SECOND));
217. // Java 8
218. LocalDateTime dt = LocalDateTime.now();
219. System.out.println(dt.getYear()); 
220. System.out.println(dt.getMonthValue()); // 1 - 12
221. System.out.println(dt.getDayOfMonth());
222. System.out.println(dt.getHour());
223. System.out.println(dt.getMinute());
224. System.out.println(dt.getSecond());
225. } 

7.2 如何取得从 1970 年 1 月 1 日 0 时 0 分 0 秒到现在的毫秒数?
226.

 Calendar.getInstance().getTimeInMillis(); //第一种方式
227. System.currentTimeMillis(); //第二种方式
228. // Java 8
229. Clock.systemDefaultZone().millis(); 
7.3 如何取得某月的最后一天?(2017-11-19-wl)
230. //获取当前月第一天:
231. Calendar c = Calendar.getInstance();
232. c.add(Calendar.MONTH, 0);
233. c.set(Calendar.DAY_OF_MONTH,1);//设置为 1 号,当前日期既为本月第一天
234. String first = format.format(c.getTime());
235. System.out.println("===============first:"+first);


//获取当前月最后一天
237. Calendar ca = Calendar.getInstance();
238. ca.set(Calendar.DAY_OF_MONTH, ca.getActualMaximum(Calendar.DAY_OF_MONTH));
239. String last = format.format(ca.getTime());
240. System.out.println("===============last:"+last);


//Java 8 
242. LocalDate today = LocalDate.now(); 
243. //本月的第一天
244. LocalDate firstday = LocalDate.of(today.getYear(),today.getMonth(),1); 
245. //本月的最后一天
246. LocalDate lastDay =today.with(TemporalAdjusters.lastDayOfMonth()); 
247. System.out.println("本月的第一天"+firstday); 
248. System.out.println("本月的最后一天"+lastDay);

7.4 如何格式化日期?
1)Java.text.DataFormat 的子类(如 SimpleDateFormat 类)中的 format(Date)方法可将日期格式化。
2)Java 8 中可以用 java.time.format.DateTimeFormatter 来格式化时间日期,代码如下所示:

 import java.text.SimpleDateFormat;
 import java.time.LocalDate;
 import java.time.format.DateTimeFormatter;
 import java.util.Date;
 class DateFormatTest {

 public static void main(String[] args) {
 SimpleDateFormat oldFormatter = new SimpleDateFormat("yyyy/MM/dd");
 Date date1 = new Date();
 System.out.println(oldFormatter.format(date1));
11.
 // Java 8
 DateTimeFormatter newFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
 LocalDate date2 = LocalDate.now();
 System.out.println(date2.format(newFormatter));
	 }
 }

补充:Java 的时间日期 API 一直以来都是被诟病的东西,为了解决这一问题,Java 8 中引入了新的时间日期 API,其中包括 LocalDate、LocalTime、LocalDateTime、Clock、Instant 等类,这些的类的设计都使用了不变模式,因此是线程安全的设计。
7.5 打印昨天的当前时刻?
264.

 import java.util.Calendar;
265. class YesterdayCurrent {
266. public static void main(String[] args){
267. Calendar cal = Calendar.getInstance();
268. cal.add(Calendar.DATE, -1);
269. System.out.println(cal.getTime());
270. }
271. }
9.
10.
272. //java-8
273. import java.time.LocalDateTime;
274. class YesterdayCurrent {
275. public static void main(String[] args) {
276. LocalDateTime today = LocalDateTime.now();
277. LocalDateTime yesterday = today.minusDays(1);
278. System.out.println(yesterday);
279. }
280. }

7.6 Java8 的日期特性?
Java 8 日期/时间特性
Java 8 日期/时间 API 是 JSR-310 的实现,它的实现目标是克服旧的日期时间实现中所有的缺陷,新的日期/时间
API 的一些设计原则是:
 不变性:新的日期/时间 API 中,所有的类都是不可变的,这对多线程环境有好处。
 关注点分离:新的 API 将人可读的日期时间和机器时间(unix timestamp)明确分离,它为日期(Date)、时间(Time)、日期时间(DateTime)、时间戳(unix timestamp)以及时区定义了不同的类。
 清晰:在所有的类中,方法都被明确定义用以完成相同的行为。举个例子,要拿到当前实例我们可以使用 now()方法,在所有的类中都定义了 format()和 parse()方法,而不是像以前那样专门有一个独立的类。为了更好的处理问题,所有的类都使用了工厂模式和策略模式,一旦你使用了其中某个类的方法,与其他类协同工作并不困难。
 实用操作:所有新的日期/时间 API 类都实现了一系列方法用以完成通用的任务,如:加、减、格式化、解析、从日期/时间中提取单独部分,等等。
 可扩展性:新的日期/时间 API 是工作在 ISO-8601 日历系统上的,但我们也可以将其应用在非 ISO 的日历上。
Java 8 日期/时间 API 包解释
 java.time 包:这是新的 Java 日期/时间 API 的基础包,所有的主要基础类都是这个包的一部分,如:LocalDate,
LocalTime, LocalDateTime, Instant, Period, Duration 等等。所有这些类都是不可变的和线程安全的,在绝大多
数情况下,这些类能够有效地处理一些公共的需求。
 java.time.chrono 包:这个包为非 ISO 的日历系统定义了一些泛化的 API,我们可以扩展 AbstractChronology类来创建自己的日历系统。
 java.time.format 包:这个包包含能够格式化和解析日期时间对象的类,在绝大多数情况下,我们不应该直接使用它们,因为 java.time 包中相应的类已经提供了格式化和解析的方法。
 java.time.temporal 包:这个包包含一些时态对象,我们可以用其找出关于日期/时间对象的某个特定日期或时间,比如说,可以找到某月的第一天或最后一天。你可以非常容易地认出这些方法,因为它们都具有“withXXX”的格式。
 java.time.zone 包:这个包包含支持不同时区以及相关规则的类。
Java 8 日期/时间常用 API
1.java.time.LocalDate
LocalDate 是一个不可变的类,它表示默认格式(yyyy-MM-dd)的日期,我们可以使用 now()方法得到当前时间,
也可以提供输入年份、月份和日期的输入参数来创建一个 LocalDate 实例。该类为 now()方法提供了重载方法,我们
可以传入 ZoneId 来获得指定时区的日期。该类提供与 java.sql.Date 相同的功能,对于如何使用该类,我们来看一个
简单的例子。

package com.journaldev.java8.time;
import java.time.LocalDate;
import java.time.Month;
import java.time.ZoneId;
/**
* LocalDate Examples
* @author pankaj
*
*/
public class LocalDateExample {
public static void main(String[] args) {
//Current Date
LocalDate today = LocalDate.now();
System.out.println("Current Date="+today);
//Creating LocalDate by providing input arguments
LocalDate firstDay_2014 = LocalDate.of(2014, Month.JANUARY, 1);
System.out.println("Specific Date="+firstDay_2014);
//Try creating date by providing invalid inputs
//LocalDate feb29_2014 = LocalDate.of(2014, Month.FEBRUARY, 29);
//Exception in thread "main" java.time.DateTimeException:
//Invalid date 'February 29' as '2014' is not a leap year
//Current date in "Asia/Kolkata", you can get it from ZoneId javadoc
LocalDate todayKolkata = LocalDate.now(ZoneId.of("Asia/Kolkata"));
System.out.println("Current Date in IST="+todayKolkata);
//java.time.zone.ZoneRulesException: Unknown time-zone ID: IST
//LocalDate todayIST = LocalDate.now(ZoneId.of("IST"));
//Getting date from the base date i.e 01/01/1970
LocalDate dateFromBase = LocalDate.ofEpochDay(365);
System.out.println("365th day from base date= "+dateFromBase);
LocalDate hundredDay2014 = LocalDate.ofYearDay(2014, 100);
System.out.println("100th day of 2014="+hundredDay2014);
} }
输出:
Current Date=2014-04-28
Specific Date=2014-01-01
Current Date in IST=2014-04-29
365th day from base date= 1971-01-01
100th day of 2014=2014-04-10

2.java.time.LocalTime
LocalTime 是一个不可变的类,它的实例代表一个符合人类可读格式的时间,默认格式是 hh:mm:ss.zzz。像LocalDate 一样,该类也提供了时区支持,同时也可以传入小时、分钟和秒等输入参数创建实例,我们来看一个简单的程序,演示该类的使用方法。

package com.journaldev.java8.time;
import java.time.LocalTime;
import java.time.ZoneId;
/**
* LocalTime Examples
*/
public class LocalTimeExample {
public static void main(String[] args) {
//Current Time
LocalTime time = LocalTime.now();
System.out.println("Current Time="+time);
//Creating LocalTime by providing input arguments
LocalTime specificTime = LocalTime.of(12,20,25,40);
System.out.println("Specific Time of Day="+specificTime);
//Try creating time by providing invalid inputs
//LocalTime invalidTime = LocalTime.of(25,20);
//Exception in thread "main" java.time.DateTimeException:
//Invalid value for HourOfDay (valid values 0 - 23): 25
//Current date in "Asia/Kolkata", you can get it from ZoneId javadoc
LocalTime timeKolkata = LocalTime.now(ZoneId.of("Asia/Kolkata"));
System.out.println("Current Time in IST="+timeKolkata);
//java.time.zone.ZoneRulesException: Unknown time-zone ID: IST
//LocalTime todayIST = LocalTime.now(ZoneId.of("IST"));
//Getting date from the base date i.e 01/01/1970
LocalTime specificSecondTime = LocalTime.ofSecondOfDay(10000);
System.out.println("10000th second time= "+specificSecondTime);
} }
输出:
Current Time=15:51:45.240
Specific Time of Day=12:20:25.000000040
Current Time in IST=04:21:45.276
10000th second time= 02:46:40
 java.time.LocalDateTime

LocalDateTime 是一个不可变的日期-时间对象,它表示一组日期-时间,默认格式是 yyyy-MM-dd-HH-mmss.zzz。它提供了一个工厂方法,接收 LocalDate 和 LocalTime 输入参数,创建 LocalDateTime 实例。我们来看一个简单的例子。

package com.journaldev.java8.time;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.ZoneId;
import java.time.ZoneOffset;
public class LocalDateTimeExample {
public static void main(String[] args) {
//Current Date
LocalDateTime today = LocalDateTime.now();
System.out.println("Current DateTime="+today);
//Current Date using LocalDate and LocalTime
today = LocalDateTime.of(LocalDate.now(), LocalTime.now());
System.out.println("Current DateTime="+today);
//Creating LocalDateTime by providing input arguments
LocalDateTime specificDate = LocalDateTime.of(2014, Month.JANUARY, 1, 10, 10, 30);
System.out.println("Specific Date="+specificDate);
//Try creating date by providing invalid inputs
//LocalDateTime feb29_2014 = LocalDateTime.of(2014, Month.FEBRUARY, 28, 25,1,1);
//Exception in thread "main" java.time.DateTimeException:
//Invalid value for HourOfDay (valid values 0 - 23): 25
//Current date in "Asia/Kolkata", you can get it from ZoneId javadoc
LocalDateTime todayKolkata = LocalDateTime.now(ZoneId.of("Asia/Kolkata"));
System.out.println("Current Date in IST="+todayKolkata);
//java.time.zone.ZoneRulesException: Unknown time-zone ID: IST
//LocalDateTime todayIST = LocalDateTime.now(ZoneId.of("IST"));
//Getting date from the base date i.e 01/01/1970
LocalDateTime dateFromBase = LocalDateTime.ofEpochSecond(10000, 0, ZoneOffset.UTC);
System.out.println("10000th second time from 01/01/1970= "+dateFromBase);
} }
输出:
Current DateTime=2014-04-28T16:00:49.455
Current DateTime=2014-04-28T16:00:49.493
Specific Date=2014-01-01T10:10:30
Current Date in IST=2014-04-29T04:30:49.493
10000th second time from 01/01/1970= 1970-01-01T02:46:40

在所有这三个例子中,我们已经看到如果我们提供了无效的参数去创建日期 /时间,那么系统会抛出java.time.DateTimeException,这是一种运行时异常,我们并不需要显式地捕获它。
同时我们也看到,能够通过传入 ZoneId 得到日期/时间数据,你可以从它的 Javadoc 中得到支持的 Zoneid 的列表,当运行以上类时,可以得到以上输出。
java.time.InstantInstant 类是用在机器可读的时间格式上的,它以 Unix 时间戳的形式存储日期时间,我们来看一个简单的程序

package com.journaldev.java8.time;
import java.time.Duration;
import java.time.Instant;
public class InstantExample {
public static void main(String[] args) {
//Current timestamp
Instant timestamp = Instant.now();
System.out.println("Current Timestamp = "+timestamp);
//Instant from timestamp
Instant specificTime = Instant.ofEpochMilli(timestamp.toEpochMilli());
System.out.println("Specific Time = "+specificTime);
//Duration example
Duration thirtyDay = Duration.ofDays(30);
System.out.println(thirtyDay);
} }
输出:
Current Timestamp = 2014-04-28T23:20:08.489Z
Specific Time = 2014-04-28T23:20:08.489Z
PT720H

日期 API 工具

我们早些时候提到过,大多数日期/时间 API 类都实现了一系列工具方法,如:加/减天数、周数、月份数,等等。
还有其他的工具方法能够使用 TemporalAdjuster 调整日期,并计算两个日期间的周期。

package com.journaldev.java8.time;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.Period;
import java.time.temporal.TemporalAdjusters;
public class DateAPIUtilities {
public static void main(String[] args) {
LocalDate today = LocalDate.now();
//Get the Year, check if it's leap year
System.out.println("Year "+today.getYear()+" is Leap Year? "+today.isLeapYear());
//Compare two LocalDate for before and after
System.out.println("Today is before 01/01/2015? "+today.isBefore(LocalDate.of(2015,1,1)));
//Create LocalDateTime from LocalDate
System.out.println("Current Time="+today.atTime(LocalTime.now()));
//plus and minus operations
System.out.println("10 days after today will be "+today.plusDays(10));
System.out.println("3 weeks after today will be "+today.plusWeeks(3));
System.out.println("20 months after today will be "+today.plusMonths(20));
System.out.println("10 days before today will be "+today.minusDays(10));
System.out.println("3 weeks before today will be "+today.minusWeeks(3));
System.out.println("20 months before today will be "+today.minusMonths(20));
//Temporal adjusters for adjusting the dates
System.out.println("First date of this month= "+today.
with(TemporalAdjusters.firstDayOfMonth()));
LocalDate lastDayOfYear = today.with(TemporalAdjusters.lastDayOfYear());
System.out.println("Last date of this year= "+lastDayOfYear);
Period period = today.until(lastDayOfYear);
System.out.println("Period Format= "+period);
System.out.println("Months remaining in the year= "+period.getMonths());
} }
输出:
Year 2014 is Leap Year? false
Today is before 01/01/2015? true
Current Time=2014-04-28T16:23:53.154
10 days after today will be 2014-05-08
3 weeks after today will be 2014-05-19
20 months after today will be 2015-12-28
10 days before today will be 2014-04-18
3 weeks before today will be 2014-04-07
20 months before today will be 2012-08-28
First date of this month= 2014-04-01
Last date of this year= 2014-12-31
Period Format= P8M3D
Months remaining in the year= 8

** 解析和格式化**
将一个日期格式转换为不同的格式,之后再解析一个字符串,得到日期时间对象,这些都是很常见的。我们来看一
下简单的例子。

package com.journaldev.java8.time;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class DateParseFormatExample {
public static void main(String[] args) {
//Format examples
LocalDate date = LocalDate.now();
//default format
System.out.println("Default format of LocalDate="+date);
//specific format
System.out.println(date.format(DateTimeFormatter.ofPattern("d::MMM::uuuu")));
System.out.println(date.format(DateTimeFormatter.BASIC_ISO_DATE));
LocalDateTime dateTime = LocalDateTime.now();
//default format
System.out.println("Default format of LocalDateTime="+dateTime);
//specific format
System.out.println(dateTime.format(DateTimeFormatter.ofPattern("d::MMM::uuuu HH::mm::ss")));
System.out.println(dateTime.format(DateTimeFormatter.BASIC_ISO_DATE));
Instant timestamp = Instant.now();
//default format
System.out.println("Default format of Instant="+timestamp);
//Parse examples
LocalDateTime dt = LocalDateTime.parse("27::Apr::2014 21::39::48",
DateTimeFormatter.ofPattern("d::MMM::uuuu HH::mm::ss"));
System.out.println("Default format after parsing = "+dt);
} }
输出:
Default format of LocalDate=2014-04-28
28::Apr::2014
20140428
Default format of LocalDateTime=2014-04-28T16:25:49.341
28::Apr::2014 16::25::49
20140428
Default format of Instant=2014-04-28T23:25:49.342Z
Default format after parsing = 2014-04-27T21:39:48

旧的日期时间支持
旧的日期/时间类已经在几乎所有的应用程序中使用,因此做到向下兼容是必须的。这也是为什么会有若干工具方法帮助我们将旧的类转换为新的类,反之亦然。我们来看一下简单的例子。

package com.journaldev.java8.time;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
public class DateAPILegacySupport {
public static void main(String[] args) {
//Date to Instant
Instant timestamp = new Date().toInstant();
//Now we can convert Instant to LocalDateTime or other similar classes
LocalDateTime date = LocalDateTime.ofInstant(timestamp,
ZoneId.of(ZoneId.SHORT_IDS.get("PST")));
System.out.println("Date = "+date);
//Calendar to Instant
Instant time = Calendar.getInstance().toInstant();
System.out.println(time);
//TimeZone to ZoneId
ZoneId defaultZone = TimeZone.getDefault().toZoneId();
System.out.println(defaultZone);
//ZonedDateTime from specific Calendar
ZonedDateTime gregorianCalendarDateTime = new GregorianCalendar().toZonedDateTime();
System.out.println(gregorianCalendarDateTime);
//Date API to Legacy classes
Date dt = Date.from(Instant.now());
System.out.println(dt);
TimeZone tz = TimeZone.getTimeZone(defaultZone);
System.out.println(tz);
GregorianCalendar gc = GregorianCalendar.from(gregorianCalendarDateTime);
System.out.println(gc);
} }
输出:
Date = 2014-04-28T16:28:54.340
2014-04-28T23:28:54.395Z
America/Los_Angeles
2014-04-28T16:28:54.404-07:00[America/Los_Angeles]
Mon Apr 28 16:28:54 PDT 2014
sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-
28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=A
merica/Los_Angeles,offset=-
28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startD
ayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=
7200000,endTimeMode=0]]
java.util.GregorianCalendar[time=1398727734404,areFieldsSet=true,areAllFieldsSet=true,lenient=t
rue,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-
28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=A
merica/Los_Angeles,offset=-
28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startD
ayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=
7200000,endTimeMode=0]],firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2014,MONTH=3,WEEK_OF_Y
EAR=18,WEEK_OF_MONTH=5,DAY_OF_MONTH=28,DAY_OF_YEAR=118,DAY_OF_WEEK=2,DAY_OF_WEEK_IN_MONTH=4,AM_PM=1
,HOUR=4,HOUR_OF_DAY=16,MINUTE=28,SECOND=54,MILLISECOND=404,ZONE_OFFSET=-28800000,DST_OFFSET=3600000]

补充:我们可以看到,旧的 TimeZone 和 GregorianCalendar 类的 toString()方法太啰嗦了,一点都不友好。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值