文章目录

  • 一、Calendar 日历类
    • 1.1 内部属性
    • 1.2 设置时间属性值
    • 1.3 获取时间属性
    • 1.4 使用 Calander 计算时间
  • 二、Calender 类中的设计模式
    • 2.1 简单工厂模式
    • 2.2 建造者模式

一、Calendar 日历类


java.util.Calendar 是从 JDK1.1 就推出的支持日期处理的工具类,它本身是一个抽象类,提供了许多可以进行日期操作的变量与方法。

源码中的介绍【译】:

Calendar类是一个抽象类,它提供了一些方法,用于在特定的时间瞬间和一组日历字段(例如年、月、月的第几天、小时等)之间进行转换,并用于操作日历字段(例如获取下周的日期)。时间上的一个瞬间可以用毫秒值表示,毫秒值是从1970年1月1日00:00:00.000 GMT(公历)开始的偏移量。

该类还提供了额外的字段和方法,用于在包外实现具体的日历系统。这些字段和方法被定义为受保护。

与其他区域设置敏感类一样,Calendar提供了一个类方法getInstance,用于获取这种类型的常用对象。Calendar的getInstance方法返回一个日历对象,其日历字段已用当前日期和时间初始化:

Calendar中定义的表示时间的常量 private final static int(部分):

属性 描述
YEAR 1 年,1970~至今
MONTH 2 月,1~12
DAY_OF_MONTH 5 本月的日,从0开始,0~30
DAY_OF_YEAR 6 本年的天,从1月1开始,1~366
DAY_OF_WEEK 7 本星期的日,从星期天为1开始,1~7
HOUR 10 当前小时(12小时制)0~11
HOUR_OF_DAY 11 当前小时(24小时制)0~23
MINUTE 12 当前的分钟 0~59
SECOND 13 当前的秒 0~59
MILLISECOND 14 当前的毫秒 0~999

更多表示时间的常量属性可通过 JDK 源码 java.util.Calendar 查看

1.1 内部属性

private long instant;        // 当前时间戳private int[] fields;       // Calendar.stamp[] (下半部分) 和 Calendar.fields[] (上半部分) 组合
// 从最小用户戳 MINIMUM_USER_STAMP 开始的伪时间戳。 private int nextStamp;      // (计算用于指示已设置瞬间。)private int maxFieldIndex;  // 保留已设置字段的最大索引(不包括week和year)。
private String type;        // 日历类型, 支持 gregory、iso8601、buddhist、japanese 四种类型
private TimeZone zone;      // 时区
private boolean lenient = true;    // 表示日历是否宽松
private Locale locale;      // 表示日历语言, 默认为 en
private int firstDayOfWeek, minimalDaysInFirstWeek;
// firstDayOfWeek 设置星期中第几天是第一天, 默认为0,即星期天
// minimalDaysInFirstWeek 设置一年中第一周所需的最少天数, 默认是1
// 1 -->  第一周定义为包含一年中第一个月的第一天
// 7 --> 如果必须是一整周

1.2 设置时间属性值

Calendar 类中一共提供了16个set方法,用于设置类的一些属性,这里列举几个常见的:

public void set(int field, int value){}public final void set(int year, int month, int date){}public final void set(int year, int month, int date, int hourOfDay, int minute){}public final void set(int year, int month, int date, int hourOfDay, int minute,int second){}
public void setTimeZone(TimeZone value){}public void setFirstDayOfWeek(int value){}

【例】修改当前的星期

import java.util.Calendar;
public class Demo {public static void println(Object o){ System.out.println(o); }public static void main(String[] args) {Calendar calendar = Calendar.getInstance();println("当前时间:" + calendar.getTime());println("当前星期: " + calendar.get(Calendar.DAY_OF_WEEK));println("修改星期为星期日(1)");calendar.set(Calendar.DAY_OF_WEEK, 1);println("修改后的时间: " + calendar.getTime());println("修改后的星期: " + calendar.get(Calendar.DAY_OF_WEEK));}
}

运行结果
当前时间:Wed Jul 20 09:26:48 CST 2022
当前星期: 4
修改星期为星期日(1)
修改后的时间: Sun Jul 17 09:26:48 CST 2022
修改后的星期: 1

可以看到,如果只修改其中一个属性,那么就会修改为最接近的时间属性,而其他属性不会发生变化。

1.3 获取时间属性

public int get(int field){}

【例】获取当前时间的一些属性

import java.util.Calendar;
public class Demo {public static void println(Object o){ System.out.println(o); }public static void main(String[] args) {Calendar calendar = Calendar.getInstance();println("默认时区: " + calendar.getTimeZone());println("当前时间:" + calendar.getTime());println("日历类型: " + calendar.getCalendarType());println("当前年份: " + calendar.get(Calendar.YEAR));println("当前月份: " + calendar.get(Calendar.MONTH));println("当前日期: " + calendar.get(Calendar.DATE));println("当天是今年的第几个星期: " + calendar.get(Calendar.WEEK_OF_YEAR));println("当天是本月的第几个星期: " + calendar.get(Calendar.WEEK_OF_MONTH));println("当天是今年中的第几天: " + calendar.get(Calendar.DAY_OF_YEAR));println("当天是本月的第几天: " + calendar.get(Calendar.DAY_OF_MONTH));println("当前是当天的第几个小时: " + calendar.get(Calendar.HOUR_OF_DAY));println("当前是一小时的第几分钟: " + calendar.get(Calendar.MINUTE));println("当前是一分钟的第几秒: " + calendar.get(Calendar.SECOND));println("当前是一秒中的第几毫秒: " + calendar.get(Calendar.MILLISECOND));}
}

1.4 使用 Calander 计算时间

【例1】 计算指定天数后的日期

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;public class Demo {public static void println(Object o){ System.out.println(o); }public static Date after(int day){Calendar calendar = Calendar.getInstance();calendar.add(Calendar.DAY_OF_WEEK, day);return calendar.getTime();}public static void main(String[] args) {SimpleDateFormat sdf = new SimpleDateFormat("yyyy 年 MM 月 dd 日 HH:mm:ss");println("今天是: " + sdf.format(new Date()));println("25天后是: " + sdf.format(after(25)));println("100天后是: " + sdf.format(after(100)));}
}

运行结果
今天是: 2022 年 07 月 20 日 09:43:15
25天后是: 2022 年 08 月 14 日 09:43:15
100天后是: 2022 年 10 月 28 日 09:43:15

【例2】 计算指定月份过某天的日期

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;public class Demo {public static void println(Object o){ System.out.println(o); }public static Date after(int month, int day){Calendar calendar = Calendar.getInstance();calendar.add(Calendar.MONTH, month);calendar.add(Calendar.DAY_OF_YEAR, day);return calendar.getTime();}public static void main(String[] args) {SimpleDateFormat sdf = new SimpleDateFormat("yyyy 年 MM 月 dd 日 HH:mm:ss");println("今天是: " + sdf.format(new Date()));println("1月再过2天后是: " + sdf.format(after(1, 2)));println("3月再过10天后是: " + sdf.format(after(3, 10)));}
}

运行结果
今天是: 2022 年 07 月 20 日 09:45:27
1月再过2天后是: 2022 年 08 月 22 日 09:45:27
3月再过10天后是: 2022 年 10 月 30 日 09:45:27

【例3】 计算两个日期相差多少年、多少月、多少天
参考资料:https://www.sojson.com/blog/260.html

static DayCompare dayCompare(String format, String fromDate, String toDate){SimpleDateFormat sdf = new SimpleDateFormat(format);Calendar from = Calendar.getInstance();Calendar to = Calendar.getInstance();try {from.setTime(sdf.parse(fromDate));to.setTime(sdf.parse(toDate));} catch (ParseException e) {e.printStackTrace();}return dayCompare(from.getTime(), to.getTime());
}static DayCompare dayCompare(Date fromDate, Date toDate){assert toDate.compareTo(fromDate) >= 0 : "【ERROR】<fromDate> must be earlier than <toDate>.";Calendar from = Calendar.getInstance();Calendar to = Calendar.getInstance();from.setTime(fromDate);to.setTime(toDate);// 计算年月日int fromYear = from.get(Calendar.YEAR);int fromMonth = from.get(Calendar.MONTH);int fromDay = from.get(Calendar.DAY_OF_MONTH);int toYear = to.get(Calendar.YEAR);int toMonth = to.get(Calendar.MONTH);int toDay = to.get(Calendar.DAY_OF_MONTH);// 计算差值int year = toYear - fromYear;int month = toMonth - fromMonth;int day = toDay - fromDay;// 处理负值if(day < 0){to.add(Calendar.MONTH, -1);day += to.getActualMaximum(Calendar.DAY_OF_MONTH);month--;}if(month < 0){to.add(Calendar.YEAR, -1);month += to.getActualMaximum(Calendar.MONTH);year--;}return DayCompare.builder().day(day).month(month).year(year).build();
}

测试:

public static void main(String[] args){String FORMAT = "yyyy-MM-dd";SimpleDateFormat sdf = new SimpleDateFormat(FORMAT);Date fromDate = new Date();String fromStr = "2022-07-20";String[] to = {"2022-08-20", "2022-09-10", "2023-05-30", "2024-01-01"};for (String toStr : to) {DayCompare dayCompare = dayCompare(FORMAT, fromStr, toStr);System.out.printf("从[%s]到[%s], 相差: [%s] 年 [%s] 月 [%s] 日%n", fromStr, toStr, dayCompare.getYear(), dayCompare.getMonth(), dayCompare.getDay());}
}

测试结果
从[2022-07-20]到[2022-08-20], 相差: [0] 年 [1] 月 [0] 日
从[2022-07-20]到[2022-09-10], 相差: [0] 年 [1] 月 [21] 日
从[2022-07-20]到[2023-05-30], 相差: [0] 年 [9] 月 [10] 日
从[2022-07-20]到[2024-01-01], 相差: [1] 年 [4] 月 [12] 日

【例4】 计算两个日期各自相差多少年、多少月、多少天
参考资料:https://www.sojson.com/blog/260.html

static DayCompare dayCompare(String format, String fromDate, String toDate){assert toDate.compareTo(fromDate) >= 0 : "【ERROR】<fromDate> must be earlier than <toDate>.";SimpleDateFormat sdf = new SimpleDateFormat(format);Calendar from = Calendar.getInstance();Calendar to = Calendar.getInstance();try {from.setTime(sdf.parse(fromDate));to.setTime(sdf.parse(toDate));} catch (ParseException e) {e.printStackTrace();}return dayCompare(from.getTime(), to.getTime());
}
public static DayCompare dayCompare(Date fromDate, Date toDate){Calendar from = Calendar.getInstance();Calendar to = Calendar.getInstance();from.setTime(fromDate);to.setTime(toDate);int fromYear = from.get(Calendar.YEAR);int fromMonth = from.get(Calendar.MONTH);int fromDay = from.get(Calendar.DAY_OF_MONTH);int toYear = to.get(Calendar.YEAR);int toMonth = to.get(Calendar.MONTH);int toDay = to.get(Calendar.DAY_OF_MONTH);int year = toYear - fromYear;// 相差月份 = 目标的总月份 - 当前的总月份int month = (toYear * 12 + toMonth) - (fromYear * 12 + fromMonth);// 相差天数 = 时间戳 / 1000 / 3600 / 24int day = (int) ((to.getTimeInMillis() - from.getTimeInMillis()) / (1000 * 3600 * 24));return DayCompare.builder().day(day).month(month).year(year).build();
}

测试

public static void main(String[] args) {String FORMAT = "yyyy-MM-dd";String fromStr = "2022-07-20";String[] to = {"2022-08-20", "2022-09-10", "2023-05-30", "2024-01-01"};for (String toStr : to) {DayCompare dayCompare = dayCompare(FORMAT, fromStr, toStr);System.out.printf("从[%s]到[%s], 相差了: [%s] 年, 相差了 [%s] 月 相差了 [%s] 日%n", fromStr, toStr, dayCompare.getYear(), dayCompare.getMonth(), dayCompare.getDay());}
}

运行结果
从[2022-07-20]到[2022-08-20], 相差了: [0] 年, 相差了 [1] 月 相差了 [31] 日
从[2022-07-20]到[2022-09-10], 相差了: [0] 年, 相差了 [2] 月 相差了 [52] 日
从[2022-07-20]到[2023-05-30], 相差了: [1] 年, 相差了 [10] 月 相差了 [314] 日
从[2022-07-20]到[2024-01-01], 相差了: [2] 年, 相差了 [18] 月 相差了 [530] 日

二、Calender 类中的设计模式


首先根据 Calender 类定义可以看出,它是一个实现了可序列化、可克隆、可比较的抽象类,这意味这它不能直接通过 new 来创建对象,一般是通过其继承子类来创建对象,它的实现类有三个,而在 java.util 包里的有两个,分别是 GregorianCalendar.java 和 JapaneseImperialCalendar,这里是两种不同日历类型的实现,前者 Gregorian 是 格力高日历,是公历的全称,后者是 JapaneseImperial 日本用的日历,我们通过 getInstance() 获取的Calender的对象默认是公历,即 Gregorian 。

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

2.1 简单工厂模式

我们创建 Calendar时,可以通过具体的实现类,除此之外,Calendar 提供了getInstace()直接获取实例。

public static Calendar getInstance()
{return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
}

如果是默认配置,那么则将调用 createCalendar() 方法,并传入默认的时区和地区

private static Calendar createCalendar(TimeZone zone,Locale aLocale)
{// 适配器模式 CalendarProvider provider =LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale).getCalendarProvider();// 如果适配器提供了当前 TimeZone 和 Locale的 Calendar 实现,那么直接调用提供者的实例化方法if (provider != null) {try {return provider.getInstance(zone, aLocale);} catch (IllegalArgumentException iae) {// 返回默认的实例化}}Calendar cal = null;// 判断地区是否具有扩展名if (aLocale.hasExtensions()) {// 获取地区所在的日历类型String caltype = aLocale.getUnicodeLocaleType("ca");if (caltype != null) {switch (caltype) {case "buddhist": // 佛教日历cal = new BuddhistCalendar(zone, aLocale);break;case "japanese": // 日本日历cal = new JapaneseImperialCalendar(zone, aLocale);break;case "gregory": // 公历cal = new GregorianCalendar(zone, aLocale);break;}}}if (cal == null) {// 如果没有指定之前的三个日历类型,则根据地区的语言和国家来判断日历类型if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {cal = new BuddhistCalendar(zone, aLocale);} else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"&& aLocale.getCountry() == "JP") {cal = new JapaneseImperialCalendar(zone, aLocale);} else {// 其他情况一律返回公历cal = new GregorianCalendar(zone, aLocale);}}return cal;
}

通过解读 Calendar 类的源码,我们可以发现,它getInstance() 创建实例的方法,本质上就是调用了内部定义的 createCalendar()方法,创建实例时主要根据时区和地区来判断,共有三种日历类型,佛教日历、日本日历和公历。

比较有趣的是,在 createCalendar() 方法里用到了适配器模式,即 CalendarProvider,这里就不继续深入了,有个印象即可,在创建 Canlendar实例时,底层源码先从这个适配器提供者去获取实例,如果不为空就直接返回,否则就根据时区和地区来判断,返回相应的日历,默认则返回 GregorianCalendar 日历,即公历。

为什么说这是一种简单工厂模式呢?因为 Calendar 抽象类里的createCalendar() 创建实例时,直接根据该方法传入的参数来返回对应的 Calendar 实现类,符合工厂模式的思想,此时,我们可以把 Calendar就看成一个工厂,它能根据时区和地区返回对应的实例,这似乎有点未卜先知的意思,在抽象类里直接调用了该抽象类自身的实现类。

【使用案例】 简单工厂模式创建 Calendar 实例对象,并将年月日改为2008年的1月1日

public static void main(String[] args) {Calendar calendar = Calendar.getInstance();calendar.set(Calendar.YEAR, 2008);calendar.set(Calendar.MONTH, 1);calendar.set(Calendar.DAY_OF_MONTH, 1);System.out.println(calendar.getTime());
}

2.2 建造者模式

java.util.Calendar类是从 JDK1.1 开始出现的,在后来的版本中也升级过,如果不查看官方文档,我们可以看看其源码实现,比如在之前 createCalendar()方法里,调用了适配器类的一个方法,返回了 CalendarProvider 类型的对象,而这个类正是 JDK1.8 才出现的,这表示 Calendar 目前的这种简单工厂模式的创建实例方式,在 JDK1.8 时进行过升级。
通过源码结构图,我们可以看到 Calendar类里只有三个内部类,而且两个还是私有的,另一个唯一公开的则是 Builder

部分源码:

  /*** ...省略了很多注释内容* @since 1.8* @see Calendar#getInstance(TimeZone, Locale)* @see Calendar#fields*/
public static class Builder {private static final int NFIELDS = FIELD_COUNT + 1; // +1 for WEEK_YEARprivate static final int WEEK_YEAR = FIELD_COUNT;private long instant;private int[] fields;private int nextStamp;private int maxFieldIndex;private String type;private TimeZone zone;private boolean lenient = true;private Locale locale;private int firstDayOfWeek, minimalDaysInFirstWeek;}

这个Builder 内部类 同样是在 JDK8 开始出现的,它和getInstance() 一样可以创建 Calendar 的实例对象,不过在创建时可以链式的设置一些属性。在设置属性时,其内部进行了一系列的初始化操作。要想了解它是如何初始化的,查看 build() 方法的源码即可:

public Calendar build() {// 如果为空则初始化为默认if (locale == null) {locale = Locale.getDefault();}if (zone == null) {zone = TimeZone.getDefault();}Calendar cal;if (type == null) {type = locale.getUnicodeLocaleType("ca");}if (type == null) {if (locale.getCountry() == "TH"&& locale.getLanguage() == "th") {type = "buddhist";} else {  // 其他情况直接使用 gregory 公历type = "gregory";}}// 判断类型switch (type) {case "gregory":cal = new GregorianCalendar(zone, locale, true);break;case "iso8601":GregorianCalendar gcal = new GregorianCalendar(zone, locale, true);// 使gcal成为序言公历gcal.setGregorianChange(new Date(Long.MIN_VALUE));// 周定义与ISO 8601兼容setWeekDefinition(MONDAY, 4);cal = gcal;break;case "buddhist":cal = new BuddhistCalendar(zone, locale);cal.clear();break;case "japanese":cal = new JapaneseImperialCalendar(zone, locale, true);break;default:throw new IllegalArgumentException("unknown calendar type: " + type);}cal.setLenient(lenient);if (firstDayOfWeek != 0) {cal.setFirstDayOfWeek(firstDayOfWeek);cal.setMinimalDaysInFirstWeek(minimalDaysInFirstWeek);}// 如果只是初始化,直接返回计算后的实例if (isInstantSet()) {cal.setTimeInMillis(instant);cal.complete();return cal;}if (fields != null) {boolean weekDate = isSet(WEEK_YEAR)&& fields[WEEK_YEAR] > fields[YEAR];if (weekDate && !cal.isWeekDateSupported()) {// 不支持当前设置的星期日期, 直接抛异常throw new IllegalArgumentException("week date is unsupported by " + type);}//将字段从min stamp设置为 max stamp,以便字段解析在日历中起作用。for (int stamp = MINIMUM_USER_STAMP; stamp < nextStamp; stamp++) {for (int index = 0; index <= maxFieldIndex; index++) {...}}if (weekDate) {...}cal.complete();}return cal;
}

代码看起来比较长,但是逻辑并不难理解,相比 createInstance(),这种方式 Builder 的build()方式似乎更复杂, 主要是因为需要处理一些默认的没有设置的属性。

【应用案例】 使用建造者模式创建 Calendar 实例对象,并将年月日改为2008年1月1日

public static void main(String[] args) {Calendar calendar = new Calendar.Builder().set(Calendar.YEAR, 2008).set(Calendar.MONTH, 1).set(Calendar.DAY_OF_MONTH, 1).build();System.out.println(calendar.getTime());
}

设计模式 笔记4 | 简单工厂模式 在源码中的应用 | Calendar 日历 | 源码浅析 | 使用总结 | 建造者模式相关推荐

  1. 23种设计模式 -----Day01:简单工厂模式

    目录 前言 1.设计模式(Design pattern)定义 2.为什么要使用设计模式(使用设计模式的意义) 3.设计原则 a.单一职责原则 b.开放封闭原则 c.里氏代换原则 d.迪米特法则 e.依 ...

  2. Head First设计模式读书笔记四 简单工厂 工厂模式 抽象工厂模式

    本文示例代码材料源自Head First设计模式 以前整理自己整理的链接: 工厂模式 https://blog.csdn.net/u011109881/article/details/56541580 ...

  3. 大话设计模式Python实现-简单工厂模式

    简单工厂模式(Simple Factory Pattern):是通过专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类. 下面使用简单工厂模式实现一个简单的四则运算 1 #!/us ...

  4. 手撕设计模式之「简单工厂模式」(Java描述)

    前言 利用简单工厂模式可以实现对象创建和业务逻辑处理的分离,但存在工厂类职责过重,增添新产品违背开闭原则等问题.它不属于GoF 23种设计模式之一,但是它可以作为学习工厂方法模式前的一个很好的引导. ...

  5. 设计模式----2(简单工厂模式的概念,简单工厂模式的实现,简单工厂模式的优缺点)

    简单工厂模式 简单工厂模式的概念 简单工厂模式属于类的创建型模式,又叫做静态工厂方法模式.通过专门定义一个类来负 责创建其他类的实例,被创建的实例通常都具有共同的父类. 具体分类 工厂(Creator ...

  6. 设计模式(1):简单工厂模式(Simple Factory Pattern)

    1. 从一个简单的例子开始 在众多的设计原则中,有一条原则是这么说的:要针对接口编程,不要针对实现编程. 针对接口编程的话,可以使用不同的实现类来创建这个对象.比如需要一个List: List< ...

  7. Java描述设计模式(02):简单工厂模式

    一.生活场景简介 1.引入场景 订餐流程简单描述 1).食品抽象类,规定食品的基础属性操作 2).鱼类,鸡肉类食品类扩展 3).订餐流程类,根据食品名称,加工指定类型食品 4).模拟客户端预定操作 2 ...

  8. 设计模式学习-简单工厂模式

    模式概述: 专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类,简单工厂(Simple Factory)模式又称为静态工厂方法(Static Factory Method)模式,属 ...

  9. 从王者荣耀看设计模式(四.简单工厂模式)

    从王者荣耀看设计模式(简单工厂模式) 一.简介 游戏开始前,玩家可从英雄池自由挑选将要出战的英雄 二.简单工厂模式 简单工厂模式(Simple Factory Pattern)属于类的创新型模式,又叫 ...

最新文章

  1. linux检查邮件命令,Linux:mail的邮件收发及查看
  2. 寻找区域中有几个点 叉乘+二分 poj 2318
  3. 利用Android Studio快速搭建App
  4. 计算机命令秒退,Win10专业版下ping命令闪退自动关闭的解决办法
  5. delphi listview失去焦点后的颜色_阴阳师姑获鸟和惠比寿建模更新对比 爷爷帅了 觉醒后鸟姐颜值提升...
  6. 变量:2021数字科技前沿应用趋势
  7. 怎么获取codeforces的数据_飞瓜数据5大功能盘点,帮你抓住2019抖音新一波涨粉红利期!...
  8. selectprovider 分页_修改EFOracleProvider——解决分页排序问题
  9. pytorch——nn.BatchNorm2d()函数
  10. 二进制、十进制和16进制对照表以及对应的字符
  11. 2021爱分析・中国采购数字化趋势报告
  12. ---常见的计算几何问题的快速计算机算法(计算机图形学+游戏渲染碰撞检测)
  13. python语言实例-Python代码样例列表
  14. 12306对抢票软件“下手”了 1
  15. 机器学习任务的一般流程及必要步骤
  16. vue3中进行vuex的分包管理(typescript)
  17. Unity3D性能优化——工具篇
  18. 软件经验|使用消费级无人机干测绘(四)ContexCapture软件介绍
  19. 【转】systemd环境变量的小坑
  20. 计算机械结构变形,机械结构设计-力学原理设计准则

热门文章

  1. 关键springmvc过滤器配置失败的解决办法。
  2. 基于”PLUS模型+“生态系统服务多情景模拟预测
  3. NEGF程序计算态密度,电子密度,透射率
  4. OpenLayers实现交互绘制点线面
  5. 视频创作者必看,手机如何给视频插入任意背景音乐?
  6. Python+selenium+firefox登录微博并爬取数据(2)
  7. PS脚本篇--1.代码是什么,写代码干嘛?
  8. 优炫软件人口普查案例入选《创新100—2022数智化场景实践》
  9. 用手机+电脑GPRS无线上网
  10. spa,ssr对seo的影响和区别