基于Java的万年历
资源链接:基于Java的万年历(课设)
摘 要
Java编程语言自诞生十几年来,已经成功地运用在网络计算及移动等各个领域。对于开发者来说,它具有简单、面向对象、健壮、安全、结构中立、可移植和高效能等众多优点。此次我们用JAVA来设计一个万年历程序,该程序以网页形式运行,并且支持图形化界面,能以日历的形式显示日期与星期,用户能自己输入日期,支持查找日期,并且能够更新日期,是一个简单易用的小应用。
关键词:JAVA,图形化界面,万年历
1 绪论
Java的前身是Oak,它一开始只是被应用于消费性电子产品中。后来它的开发者们发现它还可以被用于更大范围的Internet上。l995年,Java语言的名字从0ak编程了Java。1997年J2SE1.1发布。1998年J2SE1.2发布,标志Java2的诞生。十多年来,Java编程语言及平台成功地运用在网络计算及移动等各个领域。Java的体系结构由Java语言、Java class、Java API、Java虚拟机组成。它具有简单、面向对象、健壮、安全、结构中立、可移植和高效能等众多优点。Java支持多线程编程,Java运行时系统在多线程同步方面具有成熟的解决方案。Java的平台标准有Java ME,Java SE和Java EE。Java发展到今天,它的卓越成就及在业界的地位毋庸置疑。目前在众多的支持Java的开发工具中主要的7JavaDevelopment Kit,NetBeans,Jcreator,JBuilder,JDeveloper和Eclipse等。其中Java Development Kit简称JDK是大多开发工具的基础。以上的每种开发工具都有优缺点,对于开发者来说,重要的是要根据自己的开发规模、开发内容和软硬件环境等因素来选择一种合适的开发工具。
2 需求分析
对于日期,1、3、5、7、8、10、12月有31天;4、6、9、11月有30天;闰年的2月有29天,平年有28天。每4年一个闰年,每100年一个闰年,每400年一个闰年。根据以上条件,给定任意的年份和月份,便可计算出当月的天数。
对于时间,将表盘分为12等份,每一份对应一小时;将表盘分为60等份,每一份对应一分钟;将表盘分为3600等份,每一份对应一秒钟。
每秒钟分针转过的角度:minute_angle=(minute+second/60)*360/60;
每秒钟时针转过的角度:hour_angle=(hour-12+minute/60)*360/12;
根据以上条件,便可以计算出任意时刻时分秒针在表盘上的具体位置,利用Graphics2D类在面板上绘制即可。
将标签和日期进行关联这样就可以将日期的具体信息显示出来,比如节假日、每日运势、黄道吉日等。
通过调用系统时间,制作一个计时装置,结合窗口和标签实现闹钟的功能。
通过设置时钟、日历、标签等的背景和背景图片可以实现更改界面风格,从而实现换肤功能。
再结合菜单、面板、标签、按钮,做出一个简单实用的可视化图形用户界面。
万年历是一个非常实用的小工具。然而在当代,万年历的功能已经不仅仅局限于看时间、查日期的功能了。为此,本系统加入了更加人性化的换肤功能,以及设置闹钟的功能。让小小的万年历全面接管时间!
3 概要设计
3.1 类间组合框架
为了实现代码复用、降低源程序各个模块内的耦合性以及方便修改源代码,将各个类按照层次自顶向下分层设计。利用对象组合的设计方法,让上层类组合下层类的对象,尽量将功能下沉。
各个类之间的组合关系如图所示:
图3-1 类之间的组合关系
3.2 布局结构示意
万年历的主框架在一个NiceFrame里,通过继承JFrame而来。包含NiceMenubar、NicePanel、Panel_Left三大模块。
NiceMenuba包含Start、Function、Help三个菜单;当用户点击菜单项的时候,对应的部分会做出反馈并显示在面板上。
Panel_Left上边放NIceClock,下边放NiceLabels;NiceLabels包含三个标签,通过监听日历的点击事件显示内容。
NicePanel使用BorderLayout布局,分为Panel_North、Panel_Center、Panel_South三个部分;Panel_Center负责通过调用NiceBase获得计算出的日历格式,并显示在面板上。
各模块的窗口布局如图所示:
图3-2 布局示意图
3.3 对各个类的概述
图3-3 程序UML图
NiceCalendar类,包含程序的入口,对窗口的基本设置,如窗口大小、图标、名称等。
public class NiceCalendar{
NiceFrame NF;
}
NiceFrame类,继承JFrame类,是程序的主框架负责窗口的整体布局。
public class NiceFrame extends JFrame{
NiceClock NC;
NiceMenubar NM;
JPanel Panel_Left;
}
NiceClock类,继承JPanel,实际上是一个面板,通过重载paintComponent方法,利用Graphics2D将时钟绘制在面板上。
class NiceClock extends JPanel{
ImageIcon I;
NiceClock(ImageIcon I){
}
public void paintComponent(GraphicsG){
}
}
NiceMenuBar类,继承了JMenuBar;组合了NiceClock,实现对时钟的换肤功能;实现ActionListener接口,对菜单项进行事件监听。定义的changeSkin方法对全局更改背景颜色。
public class NiceMenubar extends JMenuBar implements ActionListener{
NicePanel NP;
NiceClock NC;
NiceMenubar(NiceClock NC){
}
public void actionPerformed(ActionEvent e){
}
public void changeSkin(Color C){
}
}
NiceSkip类,继承JFrame,引用NicePanel,主要功能是在Skip按钮按下后,调用NicePanel的refresh方法刷新日历。
public class NiceSkip extends JFrame implements ActionListener{
NicePanel NP;
void Skip(NicePanel NP){
}
public void actionPerformed(ActionEvent e){
}
}
NicePanel类,组合了NiceBase和NiceLabels,负责日历的绘制与刷新,在标签上显示日期、节日、占卜。
pJAublic class NicePanel extends JPanel implements ActionListener {
NiceBase NB;
NiceLabels NL;
void refresh(){
}
public void actionPerformed(ActionEvent e) {
}
}
NiceBase类,提供日历的计算方法,接收指定的年份和月份,并返回一个当月日历的数组。
public class NiceBase{
//String day[];
pricate int year;
private int month;
public void setYear(int year){
}
public int getYear(){
}
public void setMonth(int month){
}
public int getMonth(){
}
public String[] getCalendar(){
}
}
NiceLabels类,包含三个标签,用来显示节日、占卜、闹钟。
public class NiceLabels extends JPanel{
Color C;
Random R;
String getTips(){
}
void getRemind(int num){
}
}
NiceAlarm类,继承JFramen类,是一个弹出窗口,接收TextField输入的时间,并调用NiceTimer类计时。
public class NiceAlarm extends JFrame implements ActionListener{
NiceLabels NL;
}
NiceTimer类,接收时间,根据系统当前时间进行倒计时。
Public class NiceTimer{
NiceLabels NL;
int goalHour;
int goalMinute;
public void run() {
}
}
根据需求分析,能够得出需要哪些功能、哪些类。定义这些类,构建类之间的组合关系,为之后的详细设计奠定基础。系统中各个类都是一个整体,使用组合的方法,当需要使用某个标签、按钮的时候仅仅去调用即可,降低代码的耦合性。
在使用Java的Calendar类获取当前日期的时候,需要进行换算。因为国外的星期的第一天是Sunday,而中国一周的第一天是Monday,这就是文化差异。为了规避差异,需要将Calendar.get(Calendar.DAY_OF_WEEK)-1;还有时差的问题:使用System.currentTimeMillis(); 获取的系统时间解析后得到的时间与我国的时间不一致,这是因为我国所处的时区是东八区,因此需要加上八个时区才可以,这在换算的时候应该考虑到。
4 运行环境
Java Development Kit 8
Windows 10
Intel(R) Core(TM) i5-10300H
2.50GHz CPU主频
8GB RAM
256GB SSD
5 开发工具和编辑语言
IntelliJ IDEA 2022.1.3
Java
6 详细设计
6.1 NiceCaelendar类
图6-1 NiceClendar UML图
设置窗口属性,提供程序的入口方法。
public class NiceCalendar{
public static void main(String[] args){
//main方法 //程序入口
NiceFrame NF = new NiceFrame(); //Window//组合
NF.setBounds(200,200,1200,600); //设置窗口大小 //1200X600
Image image = // 将图片转换为Image对象 Toolkit.getDefaultToolkit().createImage("logo.jpg");
NF.setIconImage(image); //设置窗口图标
NF.setTitle("Nice Calendar"); //title //窗口标题
NF.setVisible(true); //let the window visible //窗口可见
}; // main结束
} // NiceCalendar类的结束
6.2 NiceFrame类
图6-2 NiceFrame UML图
JFrame类型的窗口,将窗口分为菜单、左上、左下、右四个部分。菜单放NiceMenuBar;左上放NiceClock;左下放NiceLabels;右放NicePanel。
NiceClock NC = new NiceClock(new ImageIcon("WHITE.jpg")); // 背景
NiceMenubar NM = new NiceMenubar(NC); // 组合NiceMenu
JPanel Panel_Left = new JPanel(new GridLayout(2, 1)); // 设置布局
/*set layout //设置布局 将面板分成上下两等*/
public NiceFrame(){
// 构造器
setVisible(true); // 可见
setDefaultCloseOperation(DISPOSE_ON_CLOSE); // 处理
Panel_Left.add(NC); // 添加NiceClock
Panel_Left.add(NM.NP.NL); // 添加NiceLabels
setLayout(new GridLayout(1, 2)); // 设置布局
setJMenuBar(NM); // 添加菜单栏
add(Panel_Left); // 添加Panel_Left
add(NM.NP); // 添加NicePanel
NM.NP.refresh(); // 显示默认日历
} // 构造函数结束
图6-3 NiceClock UML图
6.3 NiceClock类
NiceClock类,继承JPanel类,通过重写父类的printComponent方法将表盘绘制在面板上。绘制过程包括:打开抗锯齿、放入背景图片、计算表盘半径、绘制数字、绘制刻度、绘制时针、绘制分针。
public void paintComponent(Graphics G){
//draw components
Graphics2D G2D = (Graphics2D)G;
G2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); //抗锯齿 使指针显示逼真
G2D.setColor(Color.RED); //前景颜色
G2D.drawImage(I.getImage(),0,0,getWidth(),getHeight(),
I.getImageObserver()); //set background
int x_Width = this.getWidth() / 2; //get the width of panel
int y_Width = this.getHeight() / 2; //get the height of panel
int radius = (int) (Math.min(this.getWidth(), this.getHeight()) * 0.8 * 0.5); //calculate the radius of clock
for(int i = 0; i < 12; i++){
double angle = Math.PI / 180 * i * (360 / 12);
int x = (x_Width - 4) + (int)((radius - 12) * Math.cos(angle));
int y = (y_Width + 4) + (int)((radius - 12) * Math.sin(angle));
int j = i + 3;
if (j > 12)
j = j - 12;
G2D.drawString(Integer.toString(j), x, y); //add numbers to clock
}
AffineTransform old = G2D.getTransform();
/*transform 2D coordinates to another*/
for (int i = 0; i < 60; i++){
int w = i % 5 == 0 ? 6 : 3; //判断刻度的大小
G2D.fillRect(x_Width - 2, 28, w, 3); //绘制刻度
G2D.rotate(Math.toRadians(6), x_Width, y_Width);
} // add marks to clock
G2D.setTransform(old);
Calendar C = Calendar.getInstance();
int hour = C.get(Calendar.HOUR_OF_DAY);
int minute = C.get(Calendar.MINUTE);
int second = C.get(Calendar.SECOND);
double hour_angle = (hour - 12 + minute / 60d) * 360d / 12d;
G2D.rotate(Math.toRadians(hour_angle), x_Width, y_Width);
int x_hour_array[] = {
x_Width, x_Width + 9, x_Width, x_Width - 9 };
int y_hour_array[] = {
65, y_Width, y_Width + 10, y_Width };
G2D.drawPolygon(x_hour_array, y_hour_array, x_hour_array.length);//绘制时针
G2D.setTransform(old);
double minute_angle = (minute + second / 60d) * 360d / 60d;
G2D.rotate(Math.toRadians(minute_angle), x_Width, y_Width);
int x_minute_array[] = {
x_Width, x_Width + 6, x_Width, x_Width - 6 };//分针长度
int y_minute_array[] = {
45, y_Width, y_Width + 12, y_Width };
G2D.drawPolygon(x_minute_array, y_minute_array, x_minute_array.length);
//绘制分针
G2D.setTransform(old);
}
NiceClock(ImageIcon I){
this.I = I; //接收背景图片
}
6.4 NiceMenubar类
图6-4 NiceMenubar UML图
NiceMenubar类,负责控制万年历所有的功能。起到承上启下的关键作用。changeSkin方法通过设置各个按钮、标签的背景颜色完成换肤功能。其中,日历中的42个按钮用for循环遍历设置。
public void changeSkin(Color C){
// 设置各种背景
NP.NL.setBackground(C);
NP.Button_Current.setBackground(C);
NP.Button_Current.setBackground(C);
NP.Button_lastYear.setBackground(C);
NP.Button_nextYear.setBackground(C);
NP.Button_lastMonth.setBackground(C);
NP.Button_nextMonth.setBackground(C);
NP.Panel_South.setBackground(C);
NP.Panel_North.setBackground(C);
NP.Panel_Center.setBackground(C);
NP.Label_Year.setBackground(C);
NP.Label_Month.setBackground(C);
NP.Label_Calendar.setBackground(C);
for(int i = 0; i < 42; i ++){
NP.Button_Day[i].setBackground(C);
}
} // 换肤结束
} // NiceMenubar类结束
6.5 NicePanel类
图6-5 NicePanel UML图
NicePanel类,用for循环将按钮加入到面板里并设置按钮颜色和字体。refresh方法用来重新获取日历并显示。
NicePanel(){
setLayout(new BorderLayout()); // 边界布局
Label_Calendar.setFont(new Font("Arove", 1, 30));
Label_Calendar.setForeground(Color.RED);
Panel_Center.setLayout(new GridLayout(7, 7)); // 布局:7X7网格
for(int i = 0; i < 7; i++){
// 将week[]添加到面板
Label_Week[i] = new JLabel(Week[i], JLabel.CENTER);
Label_Week[i].setFont(new Font("Arvo", 1, 20));
Label_Week[i].setForeground(Color.GREEN);
Label_Week[i].setBackground(NL.C);
Panel_Center.add(Label_Week[i]);
}
for(int i = 0; i < 42; i++){
//add day[]添加到面板
Button_Day[i] = new JButton("None");
Button_Day[i].setBorder(BorderFactory.createLineBorder(Color.WHITE));
Panel_Center.add(Button_Day[i]);
}
}
void refresh(){
String day[] = NB.getCalendar();
String month;
for(int i = 0; i<42; i++){
Button_Day[i].setForeground(Color.CYAN);
Button_Day[i].setFont(new Font("Arove", 1, 22));
Button_Day[i].setText(day[i]);
}
switch(NB.getMonth()){
case 1:month = " 一月 ";break;
case 2:month = " 二月 ";break;
case 3:month = " 三月 ";break;
case 4:month = " 四月 ";break;
case 5:month = " 五月 ";break;
case 6:month = " 六月 ";break;
case 7:month = " 七月 ";break;
case 8:month = " 八月 ";break;
case 9:month = " 九月";break;
case 10:month = " 十月 ";break;
case 11:month = " 十一月 ";break;
case 12:month = " 十二月 ";break;
default:month = " NULL ";
}
Label_Year.setText(" "+ NB.getYear() +" ");
Label_Month.setText(month);
}
6.6 NiceBase类
图6-6 NiceBase UML图
NiceBase类,包括设置年月的方法和获取年月的方法以及获取日历按钮数字的方法。
public void setYear(int year){
//设置年份
if(year < 2119 && year > 1919)
this.year = year;
}
public int getYear(){
//返回年份
return year;
}
public void setMonth(int month){
//设置月份
if(month < 0)
this.month = month + 12;
else if(month > 12)
this.month = month -12;
else
this.month = month;
}
public int getMonth(){
//返回月份
return month;
}
public String[] getCalendar(){
//绘制日历
String a[] = new String[42];
Calendar C = Calendar.getInstance();
C.set(year, month - 1, 1); //月份以0为基数
int D = C.get(Calendar.DAY_OF_WEEK)-1;
int day = 0;
if(month == 1||month == 3||month == 5||month == 7||month == 8||month == 10||month == 12)
day = 31; // 1 3 5 7 8 10 12他们每月有31天
else if(month == 4||month == 6||month == 9||month == 11)
day = 30; // 4 6 9 11他们一个月有30天
else if(month == 2){
if(((year % 4 == 0)&&(year % 100!= 0))||(year % 400 == 0))
day = 29; // 有时29,否则28
else
day = 28;
}
for(int i = D, n = 1; i < D + day; i++){
a[i] = String.valueOf(n);
n++;
}
return a;
}
}
6.7 NiceAlarm类
图6-7 NiceAlarm UML图
NiceAlarm类,当set按钮被按下时,转到NiceTImer类去执行。
public void actionPerformed(ActionEvent e){
if(e.getSource() == Textfield_H){
NL.Label_Alarm.setText("Alarm: "+
Textfield_H.getText() +":"+ Textfield_M.getText());
}
if(e.getSource() == Textfield_M){
NL.Label_Alarm.setText("Alarm: "+
Textfield_H.getText() +":"+ Textfield_M.getText());
}
if(e.getSource() == Button_Set){
this.dispose();
new NiceTimer(Integer.valueOf(Textfield_H.getText()),
Integer.valueOf(Textfield_M.getText()), NL).run();
}
}
6.8 NiceTimer类
图6-8 NiceTimer UML图
NiceTimer类,run方法每秒获取一次当前时间,并与目标时间计算后得出差值显示到终端。
public void run() {
int currentSecond;
int currentMinute;
int currentHour;
do{
long currentTime = System.currentTimeMillis(); // 获取当前时间
currentTime = currentTime / 1000;
currentSecond = (int) (currentTime % 60); // 获得第二
currentTime = currentTime / 60;
currentMinute = (int) (currentTime % 60); // 获取分钟
currentTime = currentTime / 60;
currentHour = (int) (currentTime % 24); // 获取小时数
if (goalMinute-currentMinute > 0)
System.out.println("闹钟: "+ (goalHour-currentHour) +" : "+
(goalMinute-currentMinute-1) +" : "+ (59-currentSecond));
try {
Thread.sleep(1000); // 系统暂停一秒钟
}
catch (InterruptedException e){
e.toString();
}
}while (goalHour*100+goalMinute > currentHour*100+currentMinute);
NL.Label_Alarm.setText("闹钟:时间到!"); // 时间到
}
}
6.9 NiceLabels类
图6-9 NiceLabels UML图
NiceLabels类,主要功能是设置标签内容,getTips()方法和getRemind()方法可以返回对应日期的占卜和节日。
String getTips(){
int i = R.nextInt(10);
String S[] = new String[10];
S[0] = "小贴士:既不好也不坏";
S[1] = "小贴士:不喝酒/不旅行/不参加葬礼";
S[2] = "小贴士:参观/贸易/理发/旅行";
S[3] = "小贴士:禁止砍伐/结婚/洗澡";
S[4] = "小贴士:不要祈祷/建造/种植";
S[5] = "小贴士:无所事事/倒霉的一天";
S[6] = "小贴士:祈祷/结婚/洗澡/旅行";
S[7] = "小贴士:不结婚/不游泳/装饰";
S[8] = "小贴士:禁止建造/禁止交易/砍伐树木";
S[9] = "小贴士:一切顺利";
return S[i];
}
void getRemind(int num){
switch(num){
case 11: Label_Remind.setText("提醒:1月1日元旦");break;
case 214: Label_Remind.setText("提醒:2月14日情人节");break;
case 38: Label_Remind.setText("提醒:3月8日妇女节");break;
case 41: Label_Remind.setText("提醒:4月1日愚人节");break;
case 422: Label_Remind.setText("提醒:4月22日地球日");break;
case 51: Label_Remind.setText("提醒:五一劳动节");break;
case 54: Label_Remind.setText("提醒:五四青年节");break;
case 57: Label_Remind.setText("提醒:五七回华诞");break;
case 61: Label_Remind.setText("提醒:6月1日儿童节");break;
case 71: Label_Remind.setText("提醒:七一建党纪念日");break;
case 81: Label_Remind.setText("提醒:8月1日建军节");break;
case 96: Label_Remind.setText("提醒:9月6日我的生日");break;
case 910: Label_Remind.setText("提醒:9月10日教师节");break;
case 101: Label_Remind.setText("提醒:10月1日国庆节");break;
case 1128: Label_Remind.setText("提醒:11月28日感恩节");break;
case 1224: Label_Remind.setText("提醒:12月24日平安夜");break;
case 1225: Label_Remind.setText("提醒:12月25日圣诞节");break;
default: Label_Remind.setText("提醒:NULL");
}
}
}
7 调试分析
7.1 0:-1:59问题
在计时器功能里,倒数时间显示在terminal终端,但是在计时结束的时候显示的不是0 : 0 : 0,而是0 : -1 : 59。后来找其中的原因,发现是因为:
计时函数使用的是do-while循环,即先输出计时数字,再判断是否计时结束。这样会导致每次计时都会多输出一秒,也就是0 : 0 : 0的下一秒,0 : -1 : 59。
解决办法是避免最后一轮额外循环的输出,用一个判断语句进行限定:if(goalMinute-currentMinute >= 0){}
图6-1 0:-1:59
问题
图6-2 修复后的效果
7.2 菜单条的颜色问题
在换肤功能中,功能的实现原理是改变背景颜色。对于标签、按钮来说,使用setBackground()方法即可实现,但是菜单条却不行。虽然菜单条的颜色不是很影响皮肤效果,但是若能该变,显示效果会更好。于是我在网上寻找解决办法,有的说必须重载void paintComponent(Graphics G)方法、有的说可以使用setBackground()但是都不合适,最终有一位网友提到了这个方法,特别有效getComponent().setBackground()于是这个问题就被顺利解决了。
7.3 时差问题
在计时器功能里,为使时间同步,需要调用System.currentTimeMillis()方法获取当前毫秒数,然后对其进行解析,方法如下:
currentTime = currentTime / 1000;
currentSecond = (int) (currentTime % 60); // 获得第二
currentTime = currentTime / 60;
currentMinute = (int) (currentTime % 60); // 获取分钟
currentTime = currentTime / 60;
currentHour = (int) (currentTime % 24); // 获取小时数
但是,据此得到的小时数却有问题,经过几轮调试后发现,总是与当前时间相差8个小时。后来听过思考,应该是因为北京时间处于东八区,而获取的时间是标准时间。解决办法是将用户输入的时间减去8,换算成标准时间再使用。
7.4 功能补充
如果有时间的话,应当再添加一个可以让用户自定义日期提醒的功能,这样用户就可以添加生日、会议等自定义事项,人机交互更加自由。
还要有登录功能,搭配数据库,可以实时保存用户自定义提醒事项和闹钟的数据。
8 测试结果
8.1 跳转功能
Fuction菜单下有闹钟、跳转、上一年、下一年、上个月、下个月等功能。测试Function菜单下的跳转功能,跳转到2023年10月1日。
图8-1 跳转前,窗口输入
图8-2 跳转后,日期改变
8.2 换肤功能
测试菜单下的皮肤功能。
有安静的白色、柔和黄色、纯黑色、黄昏的深色四种皮肤可选,选择黄昏的深色和纯黑色进行测试。
图8-3 黄昏的深色
图8-4 纯黑色
8.3 定时功能
测试功能菜单下的闹钟功能。当前时间20:53, 定一个闹钟,20:53提醒。
图8-5 定时前,窗口输入
图8-6 运行时,终端显示倒计时
图8-7 时间到,显示时间到!
8.4 显示关于
帮助菜单提供关于开发者的一些信息。其中自述文件显示软件的使用说明、关于显示开发者的个人信息。
测试帮助菜单下的关于提示
图8-8 关于开发者
9 总结与展望
通过这次课程设计,加深了我们对面向对象程序设计的理论、方法和基础知识的理解,同时我们也掌握使用Java语言进行面向对象设计的基本方法,并且提高运用面向对象知识分析实际问题、解决实际问题的能力。更进一步地认识到在Java语言中不可以任意实现内部类对外部类中的变量的访问,除非为final型,另外,在编写程序的过程中,对java编译器的使用也是必不可少的,他可以帮助我们更迅速更直观的知道很多的类与对应的方法,如Canlender中的getInstance方法,该方法返回一个Calendar对象,其日历字段已由当前日期和时间初始化,以及大量的add和set方法的使用。在以后的时间里我们会利用课余时间来继续学习Java语言,争取能够较好地掌握它并且能够达到运用自如的境界。这次课程设计,使我对本门课程有了更深一步的理解,《JAVA程序设计》并不是简单的计算机语言,它能够延伸到更宽更广的领域。
资源链接:基于Java的万年历(课设)