Java学习-05 常用类库学习
1、泛型
概念:
泛型,即“参数化类型”。就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定
义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
这个感觉就是类似抽象类,是不确定时的选择。LinkList<>之类的,用的就是这个,因为这里面可以传各种类型。
然后是上界限定和下界限定,用来帮助我们选择想要的类,当然也可以不限制。
作用:
1、 提高代码复用率
2、 泛型中的类型在使用时指定,不需要强制类型转换(类型安全,编译器会检查类型)
注意:
在编译之后程序会采取去泛型化的措施。
也就是说Java中的泛型,只在编译阶段有效。
在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。
2、Object类
需要注意它的equals 和 toString,另外就是这里面的空指针异常。
3、Math类
常用的数学方法都有,然后需要注意两个:
- 四舍五入 round
- 向下取整 floor
- 向上取整 ceil
注意负数,-1.5 四舍五入是 -1,大小记清楚就行了,就是按照大小操作的。
4、Arrays类
提供了很多方法:
- toString
- sort
- search
- copy of
等等。
5、BigDecimal类
主要用于解决浮点数的计算误差。
6、Date类
大部分都过时了,需要注意的是 getTime方法。1s = 1000ms。计时分时区。
7、DateFormat类
需要注意format和parse两个常用方法。
8、Calender类
为了改进Date,出现的类,实现了大部分的时间方面的方法。需要注意的是时间的计数是从0开始,所以月份是0-11,秒数是0-59等等。
9、String类
重要类,经常需要使用,方法很多。
需要注意的是:
- 为什么说String一经创建就无法更改?因为实际存储是按照Char数组存储的,存放在常量池,更改的时候,实际上是复制操作。
- 堆:新生代,老年代,永久代(元空间),它们的Gc机制不同,新生代Gc访问的多,15轮之后,变成老年代,老年代也会变成新生代。
- 常量池在永久代中,不被Gc。
- 常量池的性质决定我们,能不用 + 号拼接字符串就不用,因为会产生大量不被Gc的垃圾,占用内存。如果需要大量拼接字符串,使用StringBuffer或者StringBuilder,两者区别是线程安全与否,这样节省内存,操作方式是 append添加字符串,toString转变为字符串。
10、案例过程学习
我们做一个找到休息日的案例:
小明的作息规律为上三天班,休息一天,经常不确定休息日是否周末,为此,请你开发一个程序,当小明输入年及月,以日历方式显示对应月份的休息日,用中括号进行标记.同时,统计出本月有几天休息,轮到周末休息有几天.(注:首次休息日是 2020 年 2 月 2 日)
代码:
FindRestDays
import java.text.ParseException;
/**
* @Author: deemoHui
* @Description: 主要逻辑细节的调度
* @Date Created in 2020-08-03 4:40
* @Modified By:
*/
public class FindRestDays {
View view;
MyDate date;
// 起始休息时间
String originalRestDay = "2020-02-02";
/**
* 初始化视图
*/
public void initial() {
view = new View();
view.welcome();
}
/**
* 主要计算细节调度
*
* @throws ParseException 时间换算异常
*/
public void findRestDays() throws ParseException {
w:
while (true) {
// 选择功能
int command = view.userMenu();
switch (command) {
// 退出
case 0: {
break w;
}
// 计算休息日
case 1: {
// 获取输入
date = view.getUserDateView();
int status = calculateRestDays(view, date);
// 状态为-1 表示再次调用了findRestDays,为了不重复输入界面,return结束
if (status == -1) return;
break;
}
// 更改起始休息日 此功能还在开发中,计算休息日会出错
case 2: {
originalRestDay = view.getOriginalRestDayView();
break;
}
}
}
view.bye();
}
/**
* 计算休息日逻辑
*
* @param view:视图
* @param date:日期类,存取相关数据
* @return 状态码,表示是否调用findRestDays
* @throws ParseException 时间换算异常
*/
public int calculateRestDays(View view, MyDate date) throws ParseException {
// 定义一个状态。如果进入differenceDay < -1的findRestDays(),需要提前终止一个findRestDays()
int status = -1;
// 获取初始时间 ms
long originalTime = date.parseFormatDate(originalRestDay);
// 获取起始计算时间 ms
long startTime = date.parseFormatDate(date.getFormatDate());
// 计算相差天数
long differenceDay = date.calculateDifferenceDay(originalTime, startTime);
// 判断是否是和起始时间同一个月份,这里的 -1 是需要修正的,根据起始时间不同而不同
if (differenceDay < -1) {
// 说明输入时间在2020年 2 月之前,因此报错要求重新输入
view.wrongTimeView();
findRestDays();
return status;
} else {
// 计算休息日
date.calculateRestDays(differenceDay);
// 显示休息日
view.printCalendar(date);
return 0;
}
}
}
Main
import java.text.ParseException;
/**
* @Author: deemoHui
* @Description: 主程序入口,负责整体调度
* @Date Created in 2020-08-04 9:13
* @Modified By:
*/
public class Main {
public static void main(String[] args) throws ParseException {
// 创建计算休息日实例
FindRestDays findRestDays = new FindRestDays();
// 初始化
findRestDays.initial();
// 计算休息日
findRestDays.findRestDays();
}
}
MyDate
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
/**
* @Author: deemoHui
* @Description: 日期类,负责主要数据的存取
* @Date Created in 2020-08-03 4:30
* @Modified By:
*/
public class MyDate {
private int year;
private int month;
private int totalDays;
private int startDay;
SimpleDateFormat format;
private String formatDate;
// 初始化休息日数组保存休息的天数,长度为30/3 = 10
private final long[] restDay = new long[10];
GregorianCalendar calendar;
public MyDate() {
format = new SimpleDateFormat("yyyy-MM-dd");
}
/**
* 双参构造
*
* @param year:年份
* @param month:月份
*/
public MyDate(int year, int month) {
this.year = year;
this.month = month;
//对年份,月份,以及第一天来创建对象
this.calendar = new GregorianCalendar(year, month - 1, 1);
format = new SimpleDateFormat("yyyy-MM-dd");
//获取该月的第一天是星期几
this.startDay = calendar.get(Calendar.DAY_OF_WEEK) - 1;
//获取该月份的天数
this.totalDays = this.calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
// 格式化日期为 2020-02-01 用于后续计算
if (month < 10) {
formatDate = year + "-0" + month + "-" + "01";
} else {
formatDate = year + "-" + month + "-" + "01";
}
}
/**
* 计算date ms值,用于计算
*
* @param DateText :格式化的日期
* @return ms值
* @throws ParseException 转换异常
*/
public long parseFormatDate(String DateText) throws ParseException {
Date date = format.parse(DateText);
return date.getTime();
}
/**
* 计算日期相差多少天
*
* @param originalTime 原始休息时间 ms
* @param startTime 输入查询的年月份起始时间 ms
* @return 差值 Day
*/
public long calculateDifferenceDay(long originalTime, long startTime) {
return (startTime - originalTime) / (1000 * 60 * 60 * 24);
}
/**
* 计算休息日
*
* @param differenceDay:输入时间与起始休息时间相差天数
*/
public void calculateRestDays(long differenceDay) {
/*
每一个休息日 都等于上个休息日 +4,所以重点在于怎么把这个月第一次休息日计算出来
那么要输出一个月,所以先计算这个月第一天是不是休息日,不是的话,还有几天到休息日。
*/
long restStartDay;
// 等于 -1 说明输入的时间是2020-2月
if (differenceDay == -1) {
restStartDay = 2;
} else {
restStartDay = differenceDay % 4 + 1;
}
restDayLoop(restStartDay);
}
/**
* 计算restDay的循环
*
* @param restStartDay:每个月的起始休息日是那一天
*/
public void restDayLoop(long restStartDay) {
long nextRestDay;
restDay[0] = restStartDay;
nextRestDay = restStartDay;
for (int i = 1; i < 10; i++) {
nextRestDay += 4;
if (nextRestDay < this.totalDays) {
restDay[i] = nextRestDay;
} else {
break;
}
}
}
public String getFormatDate() {
return formatDate;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getMonth() {
return month;
}
public int getTotalDays() {
return totalDays;
}
public int getStartDay() {
return startDay;
}
public long[] getRestDay() {
return restDay;
}
}
View
import java.text.ParseException;
import java.util.Scanner;
/**
* @Author: deemoHui
* @Description: 视图,负责显示
* @Date Created in 2020-08-03 4:10
* @Modified By:
*/
public class View {
// 接收器
Scanner input = new Scanner(System.in);
/**
* 欢迎界面
*/
public void welcome() {
System.out.println("---------------------------------------------------------------------");
System.out.println("欢迎使用小娜休息帮手,即将进入用户界面,请稍等。");
}
/**
* 拜拜界面
*/
public void bye() {
System.out.println("---------------------------------------------------------------------");
System.out.println("欢迎下次使用,再见。小娜会想你的。");
}
/**
* 用户菜单
*
* @return 命令码
*/
public int userMenu() {
System.out.println("---------------------------------------------------------------------");
System.out.println("请选择要执行的操作(输入 0/1/2):");
System.out.println("1: 计算休息日");
System.out.println("2: 更改起始休息日(默认为 2020-02-02):");
System.out.println("0: 退出");
int command;
try {
command = Integer.parseInt(input.nextLine());
} catch (RuntimeException e) {
System.out.println("输入格式有误,请重新输入。");
return userMenu();
}
if (command < 0 || command > 2) {
System.out.println("命令不存在,请重新输入。");
return userMenu();
}
return command;
}
/**
* 获取用户输入的年月信息视图,并返回一个MyDate对象
*
* @return MyDate对象,包含年月
*/
public MyDate getUserDateView() {
MyDate date;
int year = getYearView();
int month = getMonthView();
date = new MyDate(year, month);
return date;
}
/**
* 获取用户输入的年份视图,返回得到的年份
*
* @return 得到的年份
*/
public int getYearView() {
int year;
System.out.println("---------------------------------------------------------------------");
System.out.println("请输入年份:");
try {
year = Integer.parseInt(input.nextLine());
} catch (RuntimeException e) {
System.out.println("您输入的格式有误,请重新输入");
return getYearView();
}
// 判断年份大小是否合适
if (year < 1990 || year > 9999) {
System.out.println("请输入正确的年份。");
return getYearView();
}
return year;
}
/**
* 获取用户输入的月份视图,返回得到的月份
*
* @return 得到的月份
*/
public int getMonthView() {
int month;
System.out.println("---------------------------------------------------------------------");
System.out.println("请输入月份:");
try {
month = Integer.parseInt(input.nextLine());
} catch (RuntimeException e) {
System.out.println("您输入的格式有误,请重新输入");
return getMonthView();
}
// 判断年份大小是否合适
if (month < 1 || month > 12) {
System.out.println("请输入正确的月份。");
return getMonthView();
}
return month;
}
/**
* 打印休息的日历
*
* @param date:日期类,包含输入信息的日期相关属性
*/
public void printCalendar(MyDate date) {
String[] week = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
// 定义换行计数器
int dayCount;
// 定义休息日计数器
int restDayCount = 0;
// 定义周末休息日计数器
int weekendRestDayCount = 0;
// 得到年份
int year = date.getYear();
// 得到月份
int month = date.getMonth();
//获取该月份的天数
int totalDays = date.getTotalDays();
//获取该月的第一天是星期几
int startDay = date.getStartDay();
// 得到计算出来的休息日
long[] restDay = date.getRestDay();
System.out.println(year + "年" + month + "月的休息日日历如下:\n");
System.out.println("---------------------------------------------------------------------");
//输出星期
for (String s : week) System.out.print(s + "\t");
System.out.println();
//输出第一天前的空格
for (dayCount = 0; dayCount < startDay; dayCount++)
System.out.print("\t");
//依次输出每一天
for (int day = 1; day <= totalDays; day++) {
// 如果当前的天数在 restDay中,说明,这一天休息。
if (day == restDay[restDayCount]) {
System.out.print("[" + day + "]" + "\t");
// dayCount == 6 和 0 时 ,是周末的打印次序,所以在这里判断,如果是就说明在周末休息了
if (dayCount == 6 || dayCount == 0) weekendRestDayCount++;
restDayCount++;
} else {
System.out.print(day + "\t");
}
dayCount++;
//每个星期输完换行
if (dayCount == 7) {
System.out.println();
dayCount = 0;
}
}
System.out.println("\n");
System.out.println("本月休息总天数:" + restDayCount);
System.out.println("本月轮到周末休息天数是" + weekendRestDayCount + "天");
}
/**
* 错误的查询时间界面
*/
public void wrongTimeView() {
System.out.println("请输入正确的查询时间, 或者更改起始休息时间。");
}
/**
* 获取修改起始休息时间界面
*
* @return 起始休息时间
*/
public String getOriginalRestDayView() {
System.out.println("请输入起始休息时间(格式样例:2020-02-02):");
String newOriginalRestDay;
// 初始化一个date对象,用来检测输入的休息时间格式是否正确
MyDate date = new MyDate();
try {
newOriginalRestDay = input.nextLine();
// 检测,能parse成功,就正确
date.format.parse(newOriginalRestDay);
// 捕捉parse异常,如果异常说明输入格式有误
} catch (ParseException e) {
System.out.println("您输入的格式有误,请重新输入");
return getOriginalRestDayView();
}
return newOriginalRestDay;
}
}
本次做休息日日历案例,主要有以下几点学习:
- 1、慢慢学习自己分析案例,分析需要的主要模块,分析需要设计哪些类,现在还是有点不会,很多分析不清楚,慢慢练习,估计后边需要练习使用抽象类和接口之类的东西,但是确实不会用,希望后边能有案例带领练习。
- 2、打印日历的难点是第一行,也就是每个月第一天怎么打印,通过百度学习了如何操作。
- 3、怎么计算任意年份月份的休息日呢?大概练习了Calendar类的方法,但是很生疏,API不熟悉,是百度的,之后需要仔细学习API。
- 4、因为getTime得到的ms,可能很大,所以用了long类型,但是天数又很小,想通过类型强转为int,但是出问题了,这一块需要注意,可能是因为我之前用的Long?类型强转的时候一定要注意,有时候bug就在这。
- 5、控制台怎么都不齐,然后百度发现,这个可能是显示的问题,所以不再纠结这一块,浪费了大量时间。