编辑
2023-07-07
实用工具
00

DateUtil

java
package com.lhw.utils; import com.lhw.common.ResultCodeEnum; import com.lhw.exception.BusinessException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; public class DateUtils { /** * 字符串转换为java.util.Date<br> * 支持格式为 yyyy.MM.dd G 'at' hh:mm:ss z 如 '2017-12-12 AD at 22:10:59 PSD'<br> * yy/MM/dd HH:mm:ss 如 '2017/12/12 17:55:00'<br> * yy/MM/dd HH:mm:ss pm 如 '2017/12/12 17:55:00 pm'<br> * yy-MM-dd HH:mm:ss 如 '2017-12-12 17:55:00' <br> * yy-MM-dd HH:mm:ss am 如 '2017-12-12 17:55:00 am' <br> * @param time String 字符串<br> * @return Date 日期<br> */ public static Date stringToDate(String time){ SimpleDateFormat formatter =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); try { int len = time.length(); time=time.replace(":","") .replace("-","") .replace("/","") .replace(" ",""); ArrayList<String> formatterList=new ArrayList<>(); formatterList.add("yyyyMMdd"); formatterList.add("yyyyMMddHHmm"); formatterList.add("yyyyMMddHHmm"); formatterList.add("yyyyMMddHHmmss"); for (String strFormatter:formatterList ) { if(strFormatter.length()==time.length()) formatter=new SimpleDateFormat(strFormatter); } return formatter.parse(time); } catch (Exception e) { throw new BusinessException(ResultCodeEnum.ERR_0x1000.getCode(), e.getMessage()); } } public static boolean afterDate(Date d1, Date d2) { DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); try { String strFirstDate = dateFormat.format(d1); d1 = dateFormat.parse(strFirstDate); String strSecondDate = dateFormat.format(d2); d2 = dateFormat.parse(strSecondDate); } catch (Exception e) { // 日期型字符串格式错误 throw new BusinessException(ResultCodeEnum.ERR_0x1000.getCode(), "日期型字符串格式错误。"+e.getMessage()); } int betweenDays = 0; Calendar c1 = Calendar.getInstance(); Calendar c2 = Calendar.getInstance(); c1.setTime(d1); c2.setTime(d2); if(c1.after(c2)){ return true; }else return false; } public static boolean beforeDate(Date d1, Date d2) { DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); try { String strFirstDate = dateFormat.format(d1); d1 = dateFormat.parse(strFirstDate); String strSecondDate = dateFormat.format(d2); d2 = dateFormat.parse(strSecondDate); } catch (Exception e) { // 日期型字符串格式错误 throw new BusinessException(ResultCodeEnum.ERR_0x1000.getCode(), "日期型字符串格式错误。"+e.getMessage()); } int betweenDays = 0; Calendar c1 = Calendar.getInstance(); Calendar c2 = Calendar.getInstance(); c1.setTime(d1); c2.setTime(d2); if(c1.before(c2)){ return true; } else return false; } public static int getBetweenDays(Date d1, Date d2) { DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); try { String strFirstDate = dateFormat.format(d1); d1 = dateFormat.parse(strFirstDate); String strSecondDate = dateFormat.format(d2); d2 = dateFormat.parse(strSecondDate); } catch (Exception e) { // 日期型字符串格式错误 throw new BusinessException(ResultCodeEnum.ERR_0x1000.getCode(), "日期型字符串格式错误。"+e.getMessage()); } int betweenDays = 0; Calendar c1 = Calendar.getInstance(); Calendar c2 = Calendar.getInstance(); c1.setTime(d1); c2.setTime(d2); // 保证第二个时间一定大于第一个时间 if(c1.after(c2)){ c1.setTime(d2); c2.setTime(d1); } int betweenYears = c2.get(Calendar.YEAR)-c1.get(Calendar.YEAR); betweenDays = c2.get(Calendar.DAY_OF_YEAR)-c1.get(Calendar.DAY_OF_YEAR); for(int i=0;i<betweenYears;i++){ int tmp=countDays(c1.get(Calendar.YEAR)); betweenDays+=countDays(c1.get(Calendar.YEAR)); c1.set(Calendar.YEAR,(c1.get(Calendar.YEAR)+1)); } return betweenDays; } public static int countDays(int year){ int n=0; for (int i = 1; i <= 12; i++) { n += countDays(i,year); } return n; } public static int countDays(int month, int year){ int count = -1; switch(month){ case 1: case 3: case 5: case 7: case 8: case 10: case 12: count = 31; break; case 4: case 6: case 9: case 11: count = 30; break; case 2: if(year % 4 == 0) count = 29; else count = 28; if((year % 100 ==0) & (year % 400 != 0)) count = 28; } return count; } /** * * @Description: 将日期转换为字符串 * @author yehui * @version 2016年12月27日 下午1:19:15 * @param date * @param format * 转化格式,如:yyyy-MM-dd HH:mm:ss * @return * */ public static String formatDateToString(Date date, String format) { SimpleDateFormat sdf = new SimpleDateFormat(format); String str = sdf.format(date); return str; } public static Date addDays(Date date,int days){ Calendar calendar=Calendar.getInstance(); calendar.setTime(date); calendar.add(Calendar.DAY_OF_YEAR,days); return calendar.getTime(); } }
编辑
2023-07-06
学习记录
00

汇总

23种设计模式被分为三大类:创建型模式、结构型模式、行为型模式

创建型结构型行为型
单例模式适配器模式观察者模式
工厂方法模式装饰器模式模板方法模式
抽象工厂模式代理模式策略模式
建造者模式外观模式命令模式
原型模式桥接模式职责链模式
-组合模式状态模式
-享元模式访问者模式

七大原则:

  1. 开放封闭原则
  2. 单一职责原则
  3. 依赖倒置原则
  4. 接口隔离原则
  5. 里氏替换原则
  6. 迪米特原则
  7. 合成复用原则

创建型

单例模式

单例模式(Singleton Pattern)是一种常用的软件设计模式,它保证一个类只有一个实例,并提供了一个全局访问点。分为饿汉式和懒汉式,饿汉式是直接初始化类对象,而懒汉式是需要使用到对象再初始化

以下是单例模式的示例代码:

java
public class Singleton { private static Singleton instance; private Singleton() { // 私有构造函数,防止其他类从外部创建实例 } public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }

在这个示例中,Singleton 类是一个单例类,它只有一个实例。通过 getInstance() 方法获取单例实例。如果实例不存在,则创建一个新的实例。

单例模式在软件开发中有很多用途。以下是一些常见的应用场景:

数据库连接池:在一个系统中,数据库连接池的实例只需要一个,通过单例模式可以确保每个线程都使用相同的数据库连接池。

日志记录器:日志记录器通常只需要一个实例,通过单例模式可以确保所有的日志信息都写入同一个日志文件中。

配置管理器:在一个系统中,配置管理器只需要一个实例,通过单例模式可以确保所有的配置信息都来自于同一个实例。

线程池:在一个系统中,线程池只需要一个实例,通过单例模式可以确保所有的任务都分配到同一个线程池中执行。

需要注意的是,单例模式并不是适用于所有的场景。在多线程环境下,需要考虑到线程安全问题,可以使用线程安全的单例实现方式。同时,在某些情况下,可能需要根据不同的需求创建多个实例,因此需要根据具体的需求来决定是否使用单例模式。

编辑
2023-07-05
学习记录
00

汇总

序号锁名称应用
1乐观锁CAS
2悲观锁synchronized、vector、hashtable
3自旋锁 CAS
4可重入锁synchronized、Reentrantlock、Lock
5读写锁ReentrantReadWriteLock,CopyOnWriteArrayList、CopyOnWriteArraySet
6公平锁Reentrantlock(true)
7非公平锁synchronized、reentrantlock(false)
8共享锁ReentrantReadWriteLock中读锁
9独占锁synchronized、vector、hashtable、ReentrantReadWriteLock中写锁
10重量级锁synchronized
11轻量级锁锁优化技术
12偏向锁锁优化技术
13分段锁concurrentHashMap
14互斥锁 synchronized
15同步锁synchronized
16死锁相互请求对方的资源
17锁粗化锁优化技术
18锁消除锁优化技术

1、乐观锁

乐观锁

乐观锁是一种乐观思想,假定当前环境是读多写少,遇到并发写的概率比较低,读数据时认为别的线程不会正在进行修改(所以没有上锁)。写数据时,判断当前 与期望值是否相同,如果相同则进行更新(更新期间加锁,保证是原子性的)。

Java中的乐观锁: CAS,比较并替换,比较当前值(主内存中的值),与预期值(当前线程中的值,主内存中值的一份拷贝)是否一样,一样则更新,否则继续进行CAS操作。

如上图所示,可以同时进行读操作,读的时候其他线程不能进行写操作。

2、悲观锁

悲观锁

悲观锁是一种悲观思想,即认为写多读少,遇到并发写的可能性高,每次去拿数据的时候都认为其他线程会修改,所以每次读写数据都会认为其他线程会修改,所以每次读写数据时都会上锁。其他线程想要读写这个数据时,会被这个线程block,直到这个线程释放锁然后其他线程获取到锁。

Java中的悲观锁: synchronized修饰的方法和方法块、ReentrantLock。

如上图所示,只能有一个线程进行读操作或者写操作,其他线程的读写操作均不能进行。

编辑
2023-07-01
前端
00

安装yarn

查询当前镜像

npm get registry

全局安装yarn

npm install -g yarn

看一下yarn版本,运行以下命令

yarn -v

改为淘宝镜像

yarn config set registry https://registry.npm.taobao.org 最新地址

npm config set registry https://registry.npmmirror.com

安装项目依赖

yarn install

运行项目

yarn run dev

打包

yarn run build
编辑
2023-06-29
学习记录
00

简述

Deque是一个双端队列接口,继承自Queue接口,Deque的实现类是LinkedList、ArrayDeque、LinkedBlockingDeque,其中LinkedList是最常用的。

Deque有三种用途:

普通队列(一端进另一端出):

Queue queue = new LinkedList()或Deque deque = new LinkedList()

双端队列(两端都可进出)

Deque deque = new LinkedList()

堆栈

Deque deque = new LinkedList()

注意:Java堆栈Stack类已经过时,Java官方推荐使用Deque替代Stack使用。Deque堆栈操作方法:push()、pop()、peek()。

Deque是一个线性collection,支持在两端插入和移除元素。名称 deque 是“double ended queue(双端队列)”的缩写,通常读为“deck”。大多数 Deque 实现对于它们能够包含的元素数没有固定限制,但此接口既支持有容量限制的双端队列,也支持没有固定大小限制的双端队列。

此接口定义在双端队列两端访问元素的方法。提供插入、移除和检查元素的方法。每种方法都存在两种形式:一种形式在操作失败时抛出异常,另一种形式返回一个特殊值(null 或 false,具体取决于操作)。插入操作的后一种形式是专为使用有容量限制的 Deque 实现设计的;在大多数实现中,插入操作不能失败。

下表总结了上述 12 种方法:

第一个元素 (头部)最后一个元素 (尾部)
抛出异常特殊值抛出异常特殊值
插入addFirst(e)offerFirst(e)addLast(e)
删除removeFirst()pollFirst()removeLast()
检查getFirst()peekFirst()getLast()

Deque接口扩展(继承)了 Queue 接口。在将双端队列用作队列时,将得到 FIFO(先进先出)行为。将元素添加到双端队列的末尾,从双端队列的开头移除元素。从 Queue 接口继承的方法完全等效于 Deque 方法,如下表所示:

Queue方法等效Deque方法
add(e)addLast(e)
offer(e)offerLast(e)
remove()removeFirst()
poll()pollFirst()
element()getFirst()
peek()peekFirst()

双端队列也可用作 LIFO(后进先出)堆栈。应优先使用此接口而不是遗留 Stack 类。在将双端队列用作堆栈时,元素被推入双端队列的开头并从双端队列开头弹出。堆栈方法完全等效于 Deque 方法,如下表所示:

堆栈方法等效Deque方法
push(e)addFirst(e)
pop()removeFirst()
peek()peekFirst()

方法汇总

方法说明
add方法往队列尾部加入元素
addFirst方法往队列首部加入元素
addLast方法往队列尾部加入元素
contains方法判断队列中是否包含某个元素
descendingIterator方法以相反的顺序返回deque
element方法检索但不删除返回队列的头部
getFirst方法与element相同,检索但不删除返回第一个元素,具体不知道为什么会有重复的方法,应该是版本更新兼容吧,就语义来说这个好一点
getLast方法检索并返回最后一个元素
iterator方法以正确的顺序返回此元素的迭代器
offer方法将指定元素插入该元素的尾部,返回true或者false,与add的区别是add当没有可用空间时会抛异常,而offer返回false
offerFirst方法在双端队列头部插入元素
offerLast方法在双端队列尾部插入元素,同offer一样,为了Queue和Deque兼容
peek()方法和peekFirst/peekLast方法返回但不删除双端队列的首元素/尾元素
poll方法和pollFirst/pollLast方法poll检索并删除队列的首元素,pollFirst也是,pollLast检索并删除队列的尾元素
pop方法pop从这个deque表示的堆栈中弹出一个元素,与poll不同的是pop当为空时会报异常,而poll会返回null
push方法在此deque的首部添加元素
remove方法以及removeFirst/removeLast方法remove和removeFirst检索并删除队列中第一个元素,removeLast检索并删除队列中最后一个元素(同poll相同,就不演示了)
size方法返回此的确中的元素数