本文已参与「新人创作礼」活动,一起开启掘金创作之路。
import lombok.EqualsAndHashCode;
/**
* 存在继承关系的子类——加上注解:@EqualsAndHashCode(callSuper = true)
* 作用: 将其父类属性也进行比较
* */
@EqualsAndHashCode(callSuper = true)
小工具
List<String> 的快速初始化
注:这两种初始化无法对 list 对象进行元素的增删
List<String> list = Arrays.asList("txt", 12, true);
// 单个元素的 list
List<String> oneList = Collections.singletonList("dd");
此方法可进行元素控制
import com.google.common.collect.Lists;
Lists.newArrayList(12, 2);
List 打印元素(Java8)
list.forEach(System.out::println);
List 去重
public static Object duplicateRemove(List<Object> list) {
Set<Object> set = new HashSet<>(list);
return new ArrayList<>(set);
}
List a 去除 List b 中元素
aList.removeAll(bList);
List 两种遍历
public void itRest() {
List<String> strList = new ArrayList<>();
// iterator 遍历方式
Iterator it = strList.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
// for-in 遍历方式
for (String str : strList) {
System.out.println(str);
}
}
StringList 转 List
// idStr = "1,2,3,4";
Arrays.asList(idStr.split(","))
将 list 按照指定数量分成小 list
import com.google.common.collect.Lists;
List<List<String>> subSets = Lists.partition(list, size);
List 与 JSONArray 互相转换(fastJson)
// List 转 JSONArray
List<T> list = new ArrayList<T>();
JSONArray array= JSONArray.parseArray(JSON.toJSONString(list));
// JSONArray转List
JSONArray array = new JSONArray();
List<EventColAttr> list = JSONObject.parseArray(array.toJSONString(), EventColAttr.class);
// 字符串转List
String str = "";
List<T> list = JSONObject.parseArray(str,T.class);
三目运算符
if(a<b)
min=a;
else
min=b;
// 三目
min = (a<b)?a:b;
获取 Map 的 Key or Value
/**
* 获取 Map 的 key
*
* @param map
* @param isKey true/false
* @return true 返回 keys,false 返回 values
*/
public static List<Object> mapKeys(Map<Object, Object> map, boolean isKey) {
List<Object> keys = new ArrayList<>();
List<Object> values = new ArrayList<>();
for (Map.Entry<Object, Object> entry : map.entrySet()) {
keys.add(entry.getKey());
values.add(entry.getValue());
}
return isKey ? keys : values;
}
JSON & JavaBean
// Java 对象转 JSONObject
JSONObject jsonObj = (JSONObject) JSON.toJSON(javaBean);
// JSONObject 转 Java 对象
AlarmAddition addition = JSON.parseObject(detail.getAlarmDetail(), AlarmAddition.class);
数据库事务机制
@Transactional(rollbackFor = Exception.class)
深/浅拷贝
BeanUtils.copyProperties(dst, src);
注:List/Map 不能使用 BeanUtils
// 首先引入 spring 的工具包,使用工具 BeanUtils
import org.apache.commons.beanutils.BeanUtils;
@SneakyThrows
@Test
public void useDeepCopy2() {
WorkDay six = new WorkDay();
six.setLocation(6);
six.setName("Saturday");
six.setWork(false);
WorkDay five = new WorkDay();
System.out.println(six);
System.out.println(five);
// arg1 目标 arg2 本源
BeanUtils.copyProperties(five, six);
System.out.println(five);
}
WorkDay(location=6, name=Saturday, isWork=false)
WorkDay(location=0, name=null, isWork=false)
WorkDay(location=6, name=Saturday, isWork=false)
StringBuffer 和 StringBuilder
结论:使用 StringBuilder
原文地址 www.runoob.com/java/java-s…
String 输出 1 格式为 01
System.out.println(String.format("%02d", one));
这里使用了 String.format() 方法,作用就是格式化输出参数。%02d 是指定输出格式,%作先导标记,0 表示自动补 0, 2 的意思是最小长度为 2(如果用 4,则 1 输出 0001),d 表示整数。
HashMap 静态声明
private static Map<String, String> paramMap;
static {
paramMap = new HashMap<>();
paramMap.put("Device.Time.CurrentLocalTime", "dataTime");
}
数据类型转换
// String 转 Boolean
String a = "true";
Boolean.getBoolean(a);
// Boolean 转 String
boolean b = true;
String s = String.valueOf(b);
int 和 Integer
1,无论如何,Integer 与 new Integer 不会相等。不会经历拆箱过程,new 出来的对象存放在堆,而非 new 的 Integer 常量则在常量池(在方法区),他们的内存地址不一样,所以为 false。
2,两个都是非 new 出来的 Integer,如果数在-128 到 127 之间,则是 true, 否则为 false。因为 java 在编译 Integer i2 = 128 的时候,被翻译成:Integer i2 = Integer.valueOf(128); 而 valueOf() 函数会对-128 到 127 之间的数进行缓存。
3,两个都是 new 出来的,都为 false。还是内存地址不一样。
4,int 和 Integer(无论 new 否)比,都为 true,因为会把 Integer 自动拆箱为 int 再去比。
————————————————
版权声明:本文为 CSDN 博主「login_sonata」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。 原文地址 blog.csdn.net/login_sonat…
转义
import org.apache.commons.lang3.StringEscapeUtils;
StringEscapeUtils.unescapeJava(taskRs.getImgFeature().replace("\"",""));
编码技巧
SQL 中不要使用 where 1=1
原因:
- 使用 where 1=1 虽可以很方便的解决问题,但是会造成非常大的性能损失。
- 添加了 where 1=1 就无法使用索引等查询优化策略,数据库会被迫全表扫描,当表中数据量大的话,查询速度会非常慢。
Mybatis 中的正确使用:使用 标签
<!-- 根据条件查询产品 -->
<select id="selectByInfo" resultType="com.zte.vops.tmc.pojo.Product" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from product
where 1=1
<if test="productName != null">
and product_name = #{productName,jdbcType=VARCHAR},
</if>
<if test="bussType != null">
and buss_type = #{bussType,jdbcType=INTEGER},
</if>
order by product_name
</select>
<!-- 根据条件查询产品 -->
<select id="selectByInfo" resultType="com.zte.vops.tmc.pojo.Product" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from product
<!-- where标签可以自动添加where,同时处理sql语句中第一个and关键字 -->
<where>
<if test="productName != null">
and product_name = #{productName,jdbcType=VARCHAR},
</if>
<if test="bussType != null">
and buss_type = #{bussType,jdbcType=INTEGER},
</if>
</where>
order by product_name
</select>
注解使用
@RestController
使用 @RestController 可以取代 @Controller + @RequestBody 注解,确保请求返回 json 格式
反例
@Controller
public class ReadController {
@Resource
private ReadService readService;
@GetMapping("/pojo")
@RequestBody
public Ingredient readPojo() {
return readService.readPojo();
}
}
正例
@RestController
public class ReadController {
@Resource
private ReadService readService;
@GetMapping("/pojo")
public Ingredient readPojo() {
return readService.readPojo();
}
}
框架使用
Feign
配置特定接口(客户端)超时时间
在 yml 文件中修改配置。
feign:
client:
config:
default:
connectTimeout: 3000
readTimeout: 40000
terminal-service:
connectTimeout: 3000
readTimeout: 100000
如上 default 表示默认所有接口,terminal-service 指 @FeignClient 注解中 name 为 terminal-service 的 api(客户端)下的接口。
若从 cpe 模块调用 protocol 模块,则应在 cpe 模块的配置文件中修改上述配置,即 在调用方配置。
对抽象理解
在继承关系较高的类中,有一些不适合写具体方法,但是大家都会用到的方法声明
使用 abstract 关键词将该方法声明为抽象方法,注意: 包含一个或多个抽象方法的类本身必须被声明为抽象的,即 - 一个类如果有抽象方法,则这个类一定是抽象的 同时,一个类即使没有抽象方法也可以被声明为抽象类。
抽象类中可以包含抽象方法/具体方法/具体数据,极致的抽象类(即不包含具体方法的类)我们一般管他叫接口
一个类只能继承一个抽象类,而一个类却可以实现多个接口
抽象类不能被实例化,只能通过其具体的子类进行实例化。如 List
public void abTest(){
List<String> list = new ArrayList<>();
}
Java 设计模式
单例模式
import java.util.HashMap;
import java.util.Map;
/**
* 设计模式之单例模式
* 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
* 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,
* 不需要实例化该类的对象。
*
* 1. 单例模式只能有一个实例
* 2. 单例必须自己创建自己的唯一实例
* 3. 单例必须给其他所有对象提供这一实例
*
* 主要解决:一个全局使用的类频繁地创建与销毁。
*
* 何时使用:当您想控制实例数目,节省系统资源的时候。
*/
public class DesignPatternSingleton {
public static void main(String[] args) {
SlackerSingletonObject slackerSingletonObject = SlackerSingletonObject.getInstance();
HungrySingletonObject hungrySingletonObject = HungrySingletonObject.getInstance();
EnumSingletonObject enumSingletonObject = EnumSingletonObject.INSTANCE;
}
}
/**
* 懒汉模式-单例模式
*/
class SlackerSingletonObject{
private static SlackerSingletonObject instance;
private SlackerSingletonObject() {
System.out.println("加载 SlackerSingletonObject");
}
public static synchronized SlackerSingletonObject getInstance() {
if(instance==null) {
instance = new SlackerSingletonObject();
}
return instance;
}
public static Map<String,Object> getProFile(){
Map<String,Object> map = new HashMap<String,Object>();
map.put("jdbcDriver", "oracle");
return map;
}
}
/**
* 饿汉模式-单例模式
*/
class HungrySingletonObject{
private static HungrySingletonObject instance = new HungrySingletonObject();
private String name ;
private HungrySingletonObject() {
System.out.println("加载 HungrySingletonObject");
}
public static HungrySingletonObject getInstance() {
return instance;
}
public static Map<String,Object> getProFile(){
Map<String,Object> map = new HashMap<String,Object>();
map.put("jdbcDriver", "oracle");
return map;
}
}
/**
* 枚举--单例模式
*/
enum EnumSingletonObject{
INSTANCE;
public static Map<String,Object> getProFile(){
Map<String,Object> map = new HashMap<String,Object>();
map.put("jdbcDriver", "oracle");
return map;
}
}
工厂模式
/**
* 设计模式之工厂模式
* 工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
* 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
*
* 主要解决:主要解决接口选择的问题。
* 何时使用:我们明确地计划不同条件下创建不同实例时。
* 如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品。
* 关键代码:创建过程在其子类执行。
*/
public class DesignPatternFactory {
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();
//获取 Circle 的对象,并调用它的 draw 方法
Shape shape1 = shapeFactory.getShape("CIRCLE");
//调用 Circle 的 draw 方法
shape1.draw();
//获取 Rectangle 的对象,并调用它的 draw 方法
Shape shape2 = shapeFactory.getShape("RECTANGLE");
//调用 Rectangle 的 draw 方法
shape2.draw();
//获取 Square 的对象,并调用它的 draw 方法
Shape shape3 = shapeFactory.getShape("SQUARE");
//调用 Square 的 draw 方法
shape3.draw();
}
}
interface Shape{
public void draw();
}
class Rectangle implements Shape{
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
class Circle implements Shape{
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
class Square implements Shape {
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
class ShapeFactory {
public Shape getShape(String shapeType) {
if(shapeType==null) {
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")) {
return new Circle();
}else if(shapeType.equalsIgnoreCase("RECTANGLE")) {
return new Rectangle();
}else {
return new Square();
}
}
}
Java Interface 接口
作者:刘扬俊 链接:blog.csdn.net/qq_19782019… 来源:CSDN
接口的概念
官方解释:Java 接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。
我的解释:接口可以理解为一种特殊的类,里面全部是由全局常量和公共的抽象方法所组成。接口是解决 Java 无法使用多继承的一种手段,但是接口在实际中更多的作用是制定标准的。或者我们可以直接把接口理解为 100%的抽象类,即接口中的方法必须全部是抽象方法。(JDK1.8 之前可以这样理解)
接口的特点
就像一个类一样,一个接口也能够拥有方法和属性,但是在接口中声明的方法默认是抽象的。(即只有方法标识符,而没有方法体)。 接口指明了一个类必须要做什么和不能做什么,相当于类的蓝图。 一个接口就是描述一种能力,比如“运动员”也可以作为一个接口,并且任何实现“运动员”接口的类都必须有能力实现奔跑这个动作(或者 implement move() 方法),所以接口的作用就是告诉类,你要实现我这种接口代表的功能,你就必须实现某些方法,我才能承认你确实拥有该接口代表的某种能力。 如果一个类实现了一个接口中要求的所有的方法,然而没有提供方法体而仅仅只有方法标识,那么这个类一定是一个抽象类。(必须记住:抽象方法只能存在于抽象类或者接口中,但抽象类中却能存在非抽象方法,即有方法体的方法。接口是百分之百的抽象类) 一个 JAVA 库中接口的例子是:Comparator 接口,这个接口代表了“能够进行比较”这种能力,任何类只要实现了这个 Comparator 接口的话,这个类也具备了“比较”这种能力,那么就可以用来进行排序操作了。
为什么要用接口
接口被用来描述一种抽象。 因为 Java 不像 C++一样支持多继承,所以 Java 可以通过实现接口来弥补这个局限。 接口也被用来实现解耦。 接口被用来实现抽象,而抽象类也被用来实现抽象,为什么一定要用接口呢?接口和抽象类之间又有什么区别呢?原因是抽象类内部可能包含非 final 的变量,但是在接口中存在的变量一定是 final,public,static 的。
接口的进一步理解
我们知道,如果某个设备需要向电脑中读取或者写入某些东西,这些设备一般都是采用 USB 方式与电脑连接的,我们发现,只要带有 USB 功能的设备就可以插入电脑中使用了,那么我们可以认为 USB 就是一种功能,这种功能能够做出很多的事情(实现很多的方法),其实 USB 就可以看做是一种标准,一种接口,只要实现了 USB 标准的设备我就认为你已经拥有了 USB 这种功能。(因为你实现了我 USB 标准中规定的方法),下面是具体的例子:
先声明 USB 接口:其中规定了要实现 USB 接口就必须实现接口规定实现的 read( ) 和 write( ) 这两个方法。
interface USB(){
void read();
void write();
}
然后写一个 U 盘类和一个键盘类,这两个类都去实现 USB 接口。(实现其中的方法)
class UDisk implements USB{
@Override
public void read(){
System.out.println("U 盘通过 USB 读取");
}
@Override
public void write(){
System.out.println("U 盘通过 USB 写入");
}
}
class KeyBoard implements USB{
@Override
public void read(){
System.out.println("键盘通过 USB 读取");
}
@Override
public void write(){
System.out.println("键盘通过 USB 写入");
}
}
那么,现在 U 盘和键盘都实现了 USB 功能,也就是说 U 盘和键盘都能够调用 USB 接口中规定的方法,并且他们实现的方式都不一样。
我们在写一个测试,来看看具体的实现:
public class Main{
public static void main(String[] args){
// generate a UDisk Object
UDisk uDisk = new UDisk();
uDisk.read();
uDisk.write();
KeyBoard keyBoard = new KeyBoard();
keyBoard.read();
keyBoard.write();
}
}
关于接口的几个重点
- 我们不能直接去实例化一个接口,因为接口中的方法都是抽象的,是没有方法体的。 这样怎么可能产生具体的实例呢? 我们可以使用接口类型的引用指向一个实现了该接口的对象,并且可以调用这个接口中的方法。因此,根据 Java 中多态的特性,前文的方法调用可以如下方式写:
public class Main{
public static void main(String[] args){
// 类似 List 和 Map
USB keyBoard = new KeyBoard();
keyBoard.read();
keyBoard.write();
}
}
- 一个类可以实现不止一个接口。
- 一个接口可以继承于另一个接口,或者另一些接口,接口也可以继承,并且可以多继承。
- 一个类如果要实现某个接口的话,那么它必须要实现这个接口中的所有方法。
- 接口中所有的方法都是抽象的和 public 的,所有的属性都是 public,static,final 的。
- 接口用来弥补类无法实现多继承的局限。
- 接口也可以用来实现解耦。
接口的通俗理解
前面我们讲多态的时候用“空调”——“遥控器”的方式去理解多态,实际上在上面的的几个重点中的第一条讲的也是多态的实现,比如,我们可以把“节能”作为一种标准,或者说节能就是一个“接口”,这个接口中有一个方法,叫做变频方法,任何空调,如果要称得上叫做节能空调的话,那么必须实现“节能”这个接口,实现“节能”这个接口,也就必须实现“节能”接口中规定实现的“变频”方法,这样才算是真正的实现了“节能”这个接口,实现了“节能”这个功能。
当某个空调实现了“节能”接口后,这个空调就具备了节能的功能,那么我们也可以不用空调类的引用指向空调对象,我们可以直接使用一个“节能”接口类型引用的“遥控器”去指向“空调”,虽然这个“遥控器”上面只有一个按键,只有一个“变频”的方法,但是“遥控器”所指向的空调是实现了“节能”这个接口的,是有“变频”方法的实现的,我们用这个只有一个“变频”方法的遥控器去命令空调调用“变频”方法,也是行得通的,实在不清楚的同学可以去看我的另一篇文章:JAVA 之对象的多态性。
接口的标识用法
虽然接口内部定义了一些抽象方法,但是并不是所有的接口内部都必须要有方法,比如 Seriallizable 接口,Seriallizable 接口的作用是使对象能够“序列化”,但是 Seriallizable 接口中却没有任何内容,也就是说,如果有一个类需要实现“序列化”的功能,则这个类必须去实现 Seriallizable 接口,但是却并不用实现方法(因为接口中没有方法),此时,这个 Serilizable 接口就仅仅是一个“标识”接口,是用来标志一个类的,标志这个类具有这个“序列化”功能。具体的实现请参考我的另一篇文章:JAVA 之 IO 流。
Java 超时机制
public class InterfaceTimeOut{
public static void main(String args[]){
final ExecutorService exec = Executors.newSingleThreadExecutor();
Callable<String> call = new Callable<String>(){
@Ovrride
public String call() throws Exception{
Thread.sleep(1000 * 5);
return "线程执行完";
}
};
Future<String> future = exec.submit(call);
String obj = null;
try{
obj = future.get(1000 * 6,TimeUnit.MILLISECONDS);
System.out.print("任务成功返回"+obj);
}catch(InterruptedException e){
e.printStackTrace();
}catch(ExecutionException e){
e.printStackTrace();
}catch(TimeoutException e){
e.printStackTrace();
}
exec.shutdown();
}
}
数组初始化
// 静态初始化
array = new int[]{1,2,3,4,5};
int[] array = {1,2,3,4,5};
// 动态初始化
int[] array = new int[10];
public static native void arraycopy(Object src,int srcPos,Object dest, int destPos,int length);
// src: 源数组
// srcPos: 源数组要复制的起始位置
// dest: 目的数组
// destPos: 目的数组放置的起始位置
// length: 要复制的长度
为什么不建议使用 Date,而是使用 Java8 新的时间和日期 API
作者:武培轩 链接:www.jianshu.com/p/dbe781216… 来源:简书
Date api 存在的线程问题
java.util.Date 和 java.util.Calendar 类易用性差,不支持时区,而且都不是线程安全的。
Date 如果不格式化,打印出的日期可读性差。
Thu Sep 12 13:47:34 CST 2019
可以使用 SimpleDateFormat 对时间进行格式化,但 SimpleDateFormat 是线程不安全的,SimpleDateFormat 的 format 方法源码如下:
private StringBuffer format(Date date, StringBuffer toAppendTo,
FieldDelegate delegate) {
// Convert input date to time field list
calendar.setTime(date);
boolean useDateFormatSymbols = useDateFormatSymbols();
for (int i = 0; i < compiledPattern.length; ) {
int tag = compiledPattern[i] >>> 8;
int count = compiledPattern[i++] & 0xff;
if (count == 255) {
count = compiledPattern[i++] << 16;
count |= compiledPattern[i++];
}
switch (tag) {
case TAG_QUOTE_ASCII_CHAR:
toAppendTo.append((char)count);
break;
case TAG_QUOTE_CHARS:
toAppendTo.append(compiledPattern, i, count);
i += count;
break;
default:
subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols);
break;
}
}
return toAppendTo;
}
其中 calendar 是共享变量,并且这个共享变量没有做线程安全控制。当多个线程同时使用相同的 SimpleDateFormat 对象【如用 static 修饰的 SimpleDateFormat 】调用 format 方法时,多个线程会同时调用 calendar.setTime 方法,可能一个线程刚设置好 time 值另外的一个线程马上把设置的 time 值给修改了导致返回的格式化时间可能是错误的。
在多并发情况下使用 SimpleDateFormat 需注意。
SimpleDateFormat 除了 format 是线程不安全以外,parse 方法也是线程不安全的。parse 方法实际调用 alb.establish(calendar).getTime() 方法来解析,alb.establish(calendar) 方法里主要完成了
- 重置日期对象 cal 的属性值
- 使用 calb 中中属性设置 cal
- 返回设置好的 cal 对象
但是这三步不是原子操作,导致解析出来的时间可能是错误的。
Date 对时间处理比较麻烦,比如想获取某年、某月、某星期,以及 n 天以后的时间,如果用 Date 来处理的话真是太难了,并且 Date 类的 getYear、getMonth 这些方法都被弃用了。
多线程并发如何保证线程安全
避免线程之间共享一个 SimpleDateFormat 对象,每个线程使用时都创建一次 SimpleDateFormat 对象 => 创建和销毁对象的开销大
对使用 format 和 parse 方法的地方进行加锁 => 线程阻塞性能差
使用 ThreadLocal 保证每个线程最多只创建一次 SimpleDateFormat 对象 => 较好的方法
Java 8 新的时间和日期 API
Java 8 的日期和时间类都在 java.time 包中,包括:
- LocalDate
- LocalTime、Instant
- Duration
- Period
常用使用方法:
// LocalDate 只会获取年月日
// 创建 LocalDate,获取当前年月日
LocalDate localDate = LocalDate.now();
// 构造指定的年月日
LocalDate localDate1 = LocalDate.of(2019, 9, 12);
// 获取年、月、日、星期几
int year = localDate.getYear();
int year1 = localDate.get(ChronoField.YEAR);
Month month = localDate.getMonth();
int month1 = localDate.get(ChronoField.MONTH_OF_YEAR);
int day = localDate.getDayOfMonth();
int day1 = localDate.get(ChronoField.DAY_OF_MONTH);
DayOfWeek dayOfWeek = localDate.getDayOfWeek();
int dayOfWeek1 = localDate.get(ChronoField.DAY_OF_WEEK);
// LocalTime 只会获取时分秒
// 创建 LocalTime
LocalTime localTime = LocalTime.of(14, 14, 14);
LocalTime localTime1 = LocalTime.now();
// 获取小时
int hour = localTime.getHour();
int hour1 = localTime.get(ChronoField.HOUR_OF_DAY);
// 获取分
int minute = localTime.getMinute();
int minute1 = localTime.get(ChronoField.MINUTE_OF_HOUR);
// 获取秒
int second = localTime.getMinute();
int second1 = localTime.get(ChronoField.SECOND_OF_MINUTE);
// 获取毫秒
int nanoscd = localTime.getNano();
// LocalDateTime 获取年月日时分秒,相当于 LocalDate + LocalTime
// 创建 LocalDateTime
LocalDateTime localDateTime = LocalDateTime.now();
LocalDateTime localDateTime1 = LocalDateTime.of(2019, Month.SEPTEMBER, 10, 14, 46, 56);
LocalDateTime localDateTime2 = LocalDateTime.of(localDate, localTime);
LocalDateTime localDateTime3 = localDate.atTime(localTime);
LocalDateTime localDateTime4 = localTime.atDate(localDate);
// 获取 LocalDate
LocalDate localDate2 = localDateTime.toLocalDate();
// 获取 LocalTime
LocalTime localTime2 = localDateTime.toLocalTime();
// Instant 获取秒数,用于表示一个时间戳(精确到纳秒)
// 创建 Instant 对象
Instant instant = Instant.now();
// 获取秒数
long currentSecond = instant.getEpochSecond();
// 获取毫秒数
long currentMilli = instant.toEpochMilli();
// 直接获取系统时间戳
System.currentTimeMillis()
// Duration.between() 方法创建 Duration 对象
LocalDateTime from = LocalDateTime.of(2017, Month.JANUARY, 1, 00, 0, 0); // 2017-01-01 00:00:00
LocalDateTime to = LocalDateTime.of(2019, Month.SEPTEMBER, 12, 14, 28, 0); // 2019-09-15 14:28:00
Duration duration = Duration.between(from, to); // 表示从 from 到 to 这段时间
long days = duration.toDays(); // 这段时间的总天数
long hours = duration.toHours(); // 这段时间的小时数
long minutes = duration.toMinutes(); // 这段时间的分钟数
long seconds = duration.getSeconds(); // 这段时间的秒数
long milliSeconds = duration.toMillis(); // 这段时间的毫秒数
long nanoSeconds = duration.toNanos(); // 这段时间的纳秒数
// 修改 LocalDate、LocalTime、LocalDateTime、Instant
// LocalDate、LocalTime、LocalDateTime、Instant 为不可变对象,修改这些对象对象会返回一个副本
LocalDateTime localDateTime = LocalDateTime.of(2019, Month.SEPTEMBER, 12, 14, 32, 0);
// 增加一年
localDateTime = localDateTime.plusYears(1);
localDateTime = localDateTime.plus(1, ChronoUnit.YEARS);
// 减少一个月
localDateTime = localDateTime.minusMonths(1);
localDateTime = localDateTime.minus(1, ChronoUnit.MONTHS);
// 通过 with 修改某些值
// 修改年为 2020
localDateTime = localDateTime.withYear(2020);
localDateTime = localDateTime.with(ChronoField.YEAR, 2020);
// 时间计算
// 获取该年的第一天
LocalDate localDate = LocalDate.now();
LocalDate localDate1 = localDate.with(firstDayOfYear());
TemporalAdjusters 包含许多静态方法,可以直接调用,以下列举一些:
方法名 描述 dayOfWeekInMonth 返回同一个月中每周的第几天 firstDayOfMonth 返回当月的第一天 firstDayOfNextMonth 返回下月的第一天 firstDayOfNextYear 返回下一年的第一天 firstDayOfYear 返回本年的第一天 firstInMonth 返回同一个月中第一个星期几 lastDayOfMonth 返回当月的最后一天 lastDayOfNextMonth 返回下月的最后一天 lastDayOfNextYear 返回下一年的最后一天 lastDayOfYear 返回本年的最后一天 lastInMonth 返回同一个月中最后一个星期几 next / previous 返回后一个/前一个给定的星期几 nextOrSame / previousOrSame 返回后一个/前一个给定的星期几,如果这个值满足条件,直接返回
格式化时间
LocalDate localDate = LocalDate.of(2019, 9, 12);
String s1 = localDate.format(DateTimeFormatter.BASIC_ISO_DATE);
String s2 = localDate.format(DateTimeFormatter.ISO_LOCAL_DATE);
// 自定义格式化
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String s3 = localDate.format(dateTimeFormatter);
// 注:
// localDate.format 与 sdf.format 并不是同一个 format 方法
// 解析时间
LocalDate localDate1 = LocalDate.parse("20190912", DateTimeFormatter.BASIC_ISO_DATE);
LocalDate localDate2 = LocalDate.parse("2019-09-12", DateTimeFormatter.ISO_LOCAL_DATE);
LocalDate localDate2 = LocalDate.parse("2019-09-12", DateTimeFormatter.ofPattern("yyyy-MM-dd");
总结
和 SimpleDateFormat 相比,DateTimeFormatter 是线程安全的。
Instant 的精确度更高,可以精确到纳秒级。
Duration 可以便捷得到时间段内的天数、小时数等。
LocalDateTime 能够快速地获取年、月、日、下一月等。
TemporalAdjusters 类中包含许多常用的静态方法,避免自己编写工具类。
Java8 时间 API 使用
LocalTime(本地时间)
LocalTime 表示一天中的某个时间,例如 18:00:00。LocaTime 与 LocalDate 类似,他们也有相似的 API。
需要注意的是:LocalTime 本身不关心是 AM 还是 PM,而是格式化程序来负责这个事情。
LocalDateTime(本地日期时间)
LocalDateTime 表示一个日期和时间,它适合用来存储确定时区的某个时间点。不适合跨时区的问题。
若需要处理跨时区的时间,需要使用 ZonedDateTime.
ZonedDateTime(带时区的时间)
时区(Time Zone) 是地球上的区域使用同一个时间定义。1884 年在华盛顿召开国际经度会议时,为了克服时间上的混乱,规定将全球划分为 24 个时区。
由于实用上常常 1 个国家,或 1 个省份同时跨着 2 个或更多时区,为了照顾到行政上的方便,常将 1 个国家或 1 个省份划在一起。所以时区并不严格按南北直线来划分,而是按自然条件来划分。