JAVA时钟日历报告.doc_第1页
JAVA时钟日历报告.doc_第2页
JAVA时钟日历报告.doc_第3页
JAVA时钟日历报告.doc_第4页
JAVA时钟日历报告.doc_第5页
已阅读5页,还剩18页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Java语言课程期末作业Java语言课程期末作业题 目 第三题 时间日历 学 院 计算机学院 专 业 XXX 班 别 学 号 姓 名 2011年11月一、 课程题目设计一个时间日历,交互性要求:能够设置时间。例如:Windows7系统自带日历:其它功能可自行扩展。二、 题目分析与设计 1. 使用的开发环境:Eclipse+JDK1.6+windows7(64bit) sp12.题目的需求由题目的要求可以分析得出以下基本需求:(1) 系统有三个模块,左边显示的是时间,右边显示的日历。日期部分是一台历形式显示,时间部分包括图形钟和数字钟。(2) 有指针时钟和数字时钟模块,可以同步系统时间,动态准确走时(3) 日历初始态是根据部分是本地系统时间为准,在日历内用标记显示当前月的今天。(4) 在日历模块内,日历可以通过点击选择下拉框查看过去和未来某一年月的日期。(5) 有设置模块,允许用户输入正确格式的时间而改变程序的当前日期。系统的日期不受影响,在同步系统时间后,更改此程序的时间不再与系统同步。3.软件功能架构图。时钟功能本程序主要有三个功能:设置(1) 设置当前日期设置功能主程序(2) 显示时钟时间(时分秒设置(3) 显示日历(年月日)日历功能 恶搞时钟功能的实现:类名为Clock,首先在构造时钟面板,并对时钟面板的参数设定(标题,大小,背景,布局)。然后重写方法void paint(Graphics g)在此方法内将绘制时钟的一帧,此方法中先获取时钟面板容器的一些参数(边界大小,面板高度),然后用得到的参数确定所要绘制的时钟的圆心位置,再根据几何知识计算出表盘12个刻度的位置计算出,并将12个数字绘制在对应的位置,然后获取系统的或者用户设置的时间(时分秒),将该时间转化为字符串表示并根据设定的字体和颜色绘制到时钟面板内的指定位置作为数字时钟。再根据几何知识计算出时、分、秒针的位置,然后根据设置线宽和颜色绘制到面板中。创建时钟面板设置面板参数获取面板参数绘制表盘获取当前时间绘制数字时钟绘制指针时钟日历功能的实现:类名为MyCalendar,首先创建日历面板,并对日历面板的参数设定(标题,大小,布局)。然后创建年和月的选择下拉列表,添加年月标识并布局到主面板的BorderLayout.NORTH位置,创建方法show_Day()用于显示所选择的月内的天数和各个日期所处的位置,此方法首先获取当前的年月或者从下拉框选择的年月。然后通过该选择的或者设置的年月创建新的日历,穿件新的日期后,得到所选月份的天数以及这一年是否为闰年,通过get (Calendar.DAY_OF_WEEK)获取当前月的第一天的位置,然后以此得出该月各天的位置,最后加入到主面板中,布局到BorderLayout. CENTER位置。创建日历面板设置面板参数创建年月下拉列表标识加入面板获取当前时间或者下拉选择 创建新日历获取该月天数和该月第一天的位置绘制每一天的位置加入主面板设置功能的实现:类名为Test和DateTest;因为设计设置功能通过点击主界面的设置-更改时间按钮实现,所以将设置功能放于主界面所在的Test类中,而类DateTest主要实现将获取的字符串格式的日期转变为一个日历实例。首先在主界面创建菜单栏“设置”和“帮助”。在“设置”下有“更改时间”这个菜单项,“帮助”下有“关于”其中有本程序的相关信息介绍。在“更改时间”这个菜单项上增加监听,当鼠标单击时触发。触发操作后将弹出对话框,用户可在对话框内输入更改为的时间,获得设置的时间后,将实例化的Clock和Calendar的相关时间变量改变,并刷新界面,完成时间日期的设置。创建主界面添加设置菜单点击更改时间弹出对话框输入对应格式的日期,时间 调用实例化的Clock和Calendar的时间变量,更改为设置的时间自动刷新界面,完成时间日期设置4界面设计过程,设计上的创意及组件的布局策略。界面的设计:整体设计仿照windows7系统自带的时间日历设计,分为左右两大块的BorderLayout方式布局,但主色调采用暗色调为主,呈现夜光效果,在界面的上方设计有菜单,方便对时间进行设置,还有设计一个帮助菜单,是程序的关于信息。在日历的主体部分日历置于整个界面的左边,时钟置于整个界面的右边,其实也可以将时钟设置与左边,但为了与windows7 的日历排列方式相似就放置于右边了。在日历模块上,对整个模块设计了一个边框以标识其为独立模块,在左上角添加有“日历”字样,在日历子面板内有两个下拉按钮,用于鼠标或键盘方向键快速选择年月,这点较windows的点击翻页更为快速,而在日历子面板的下方大部分区域用于显示所选年月的日历情况。在显示日期的部分上方为星期标识用“一”“二”.等,日期面板中没有日期的地方不显示,为背景色,凡是处于周末的日期均标记为红色,“今天”标记为蓝色。在时间面板内,整个面板也设计了一个边框并在边框上的左上角添加“时钟”字样,面板底色为深灰色,而表盘设计和刻度为青色,呈现出夜光效果,在时钟内部的下方参考汽车仪表盘的设计,内嵌数字时钟。表盘的指针选择用于交通的红黄绿色,十分醒目。整体1:1效果如下:5.程序逻辑的实现,包括类的设计、对象的协作过程等,必要时加以各种图形和表格辅助论述。主界面的逻辑实现:程序运行后,首先构造一个新的主类Test的实例:在主类继承JFrame,在主类内,引用主类的的构造方法,设置窗体的标题为“Java时钟日历”,然后创建菜单栏mb,菜单m,n、菜单项m2,n2;然后将菜单项加入菜单,菜单加入菜单栏;再实例化Clock和Calendar 。最后将菜单,clock和Calendar的实例加入主界面。设置主界面的大小和位置,设置主界面大小不可调整和可见。再设置用户在此窗体上发起 close 时默认执行的操作。整个主界面的逻辑到此实现。main()开始标题:时间日历菜单:设置菜单项目:更改时间实例化主类Test主面板菜单项目:关于菜单:帮助菜单栏实例化clock clo程序结束实例化MyCalendar cal设置主界面大小,位置,不可见,close操作下面所有图中:实线为逻辑关系而虚线为实际运行流程图菜单设置功能的逻辑实现:用户点击弹出时间输入框串菜单更改时间对“更改时间”加入ActionListener获取输入的字符串格式为yyyy-MM-dd HH:mm:ss创建日历set_c;设置set_c.setTime(set);实例化DateTest调用add_t方法获取设置日期set获取设置时间与系统时间差(秒)diff调用cal的下拉框变量改为所设置的年月所在项setSelectedIndex();得出与系统的时间差(时分秒),设置实例clo的add_h,ad_m,add_s,当刷新clo的panel的时候将显示新时间调用cal的当前日期变量cal.now_ year,now_month,now_date改为所设置的日期设置cal的日期选择为false调用cal的日期绘制方法结束虚线为实际运行流程设置panel的背景为深灰色。标题为“时钟”大小为300,300Clock的逻辑实现:重写Paint()方法定时器触发repaint()Clock开始继承Jpanel类调用getInsert.Left getinsets.topgetSize().height 计算表盘的位置和半径以及圆心坐标,绘制表盘由圆心和半径绘制表盘的十二个刻度对Calendar now使用now.get()获取当前时间;将获取的要绘制的时间转化为string串,在panel指定位置上绘制上string显示为数字时钟获取要绘制的时间:now.get()+add_*未设置时钟时add_*默认为0设置后,为更改时钟时所得到的值虚线为实际运行流程结束利用获取的时间和几何知识计算时分秒针的位置然后由设定的格式画出时分秒针Calendar的逻辑实现MyCalendar开始继承Jpanel类取当前系统日期的年now_year月now_month日now_date设置panel的背景.大小.布局创建2个年月的JComboBox和两个Jlabel“年”“月”Show_Day方法由当前的年份得出前后五十年,加入到JcomboBox设置JcomboBox选中当前年份,和当前月份用户点击设置对JcomboBox监听获取当前年月或者选择的年月修改日期后主类调用获取选取月的天数并考虑对闰年的检测构造新日历结束将每一天的位置加入panel内获取该月第一天的位置星期标识加入panel主要的类和关键方法的说明:本程序有四个类:分别为Test主类 MyCalendar类和clock类还有一个用于设置主类TEST类public class Test extends JFrame implements ActionListener主要是对窗口的外观设计,和对clock和Calendar的实例化,另外包含设置菜单的监听和时钟绘制的刷新,全部内容如下1构造函数创建一个窗口2添加菜单栏 3实例化时钟 日历4添加菜单,时间,日历到主面板5设置窗体参数6监听菜单7设置日历新日期8更改日历的下拉选择9更改日历的日期显示10更改时钟显示日期11创建一个监听事件12创建一个时间计数器13实现ActionListener接口必须实现的方法14主函数Clock类public class Clock extends JPanel 里面的主要用于绘制时钟面板:其中的paint方法为绘制时钟的关键;MyCalendar类public class MyCalendar extends JPanel implements ActionListener 主要用于显示日历面板,其中的Show_Day()为显示日期的关键:class DateTest用于设置时间的时候,将输入的字符串转为日期关键方法说明:Paint()方法的具体内容;其中涉及相关几何知识的地方不做阐述方法主要通过获取的面板大小信息确定表盘的位置,然后使用Graphics2D 进行表盘的绘制,而对指针的绘制依不同的时间位置不同;而时间可以是系统的时间也可以是设置的时间;通过求时间差的办法将二者统一到一起;在没有设置时钟的情况下,默认时间差为0;而设置了之后则求出这个时间差;每次绘图的时候都会在系统时间上加上这个差值便得到了设置的时钟;另外时钟的设置可能导致,某部分超出或者低于:比如当前为01:50:20设置为02:10:20 计算得出相差为0时20分0秒;但是在panit里时分秒是分开绘制的;此时若自己用时间差简单加减,则会得到01:10:20;而不是02:10:20;因为分钟数相加超过了或者等于60应该使时钟加一;所以paint内加入相关语句判别时分秒各个部分是否需要向前加一或者减一。另外的绘制部分则特别设置了颜色,字体和大小增强显示效果;Show_Day()方法的具体内容;此方法为显示日历日期的方法;在方法体内在默认情况下设置有一个bool变量,若bool为false则读取显示系统的时间,若选择了下拉列表的年月,则令bool的值变为true,则读取由下拉列表获得的日期;下拉列表的月份可以直接由下拉项的位置确定;如五月就在第五个选项:但年份不能直接由位置得出,如系统年为2011年处于第50项而不是2011项;于是直接读取下拉项目为字符串,再将其转换为int表示的年份,最后通过年月构造出需要显示的该月日历;然后调用方法获取该月的天使和该月的第一天的在一周内位置依次便可以确定该月的每一天的位置从未绘制显示出该月的所有日期。三、 测试分析典型测试数据的构建: 因为整个时钟日历的交互主要在于下拉列表的选择和时钟日期的更改:我在这两个地方选择多组数据进行测试;当前系统时间:2011年11月28日 星期一1. 程序默认情况测试:编号1操作:点击运行程序:预计结果:时钟能显示当前系统时间无滞后,超前;日历显示为当前年月的日历;日期处于正确的星期下,周末显示为红色,当天显示为蓝色,其余为黑色;2下拉菜单测试编号2操作:选择年下拉列表的第一项(1961);月下拉列表的第一项(1),查看极限情况的显示结果 预计结果:显示正确编号3操作:选择年下拉列表的最后一项(2061);月下拉列表的最后一项(12),查看极限情况的显示结果 预计结果:显示正确编号4操作:选择年下拉列表的早于当前年份的一项(1990);月下拉列表的后于当前月的项(12),查看显示结果 预计结果:显示正确3时间修改测试(程序支持五十年内的日历)编号5操作:输入一个大于下拉列表能显示的时间(2062-1-1 12:59:50)。预计结果:时间不发生改变编号6操作:输入一个小于下拉列表能显示的时间(1960-1-1 12:59:50)。预计结果:时间不发生改变编号7操作:输入一个错误格式的时间(1990:2:20 12:20:20 )。预计结果:不发生改变编号8操作:输入一个早于当前系统时间的正确格式时间(2010-5-2 12:50:20 )预计结果:正确改变编号9操作:输入一个晚于当前系统时间的正确格式时间(2050-10-30 22:10:2 )预计结果:正确改变本程序的测试情况,与预计结果作对比 将windows自带日历作为参考标准编号1结果:编号2结果:编号3结果:编号4结果:编号5测试结果: 没有改变编号6测试结果:年份下拉框内的当前内容选择为无。其他未发生该百年。与预期不符分析:当前年份与1960年的差为51年,在下拉框的变量里该值会变为50-51=-1,即下拉选择的第-1个选项对应为1960年,而该选项默认为空因为程序仅设定了前后50年的日历所以该不符合期望结果并不影响程序的正常使用编号7测试结果:没有改变编号8测试结果:正确改变编号9测试结果:正确改变在其他非纳入统计的测试中,有时候会出现设置时间一秒内的误差比如设置为12:20:10秒可能初始状态为12:20:9或者12:20:11,这种原因主要是因为设计时的时间差值精度在秒的尺度上计算。所以会出现这样的系统误差,但这不影响实际使用。测试统计:编号预计结果实际结果符合情况1显示正确显示正确符合2显示正确显示正确符合3显示正确显示正确符合4显示正确显示正确符合5没有改变没有改变符合6没有改变年份改变为空白不符合7没有改变没有改变符合8正确改变正确改变符合9正确改变正确改变符合从整个测试的情况看出,在正常使用下,时钟日历能非常稳定运行。但不能保证在非正常设置的日期值下的结果。结论;整个程序完全完成了要求的内容,满足了题目的所列的需求。附录:源代码清单1.Test.java2.Clock,java3.MyCalendar.javaTest.javaimport javax.swing.*;import java.awt.event.ActionListener;import java.awt.event.ActionEvent;import java.awt.*;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Calendar;import java.util.Date;public class Test extends JFrame implements ActionListener /* * */private static final long serialVersionUID = 1L;long diff=0; Date set;/构造函数创建一个窗体 public Test()super(Java时钟日历); /添加菜单栏MenuBar mb=new MenuBar();/菜单项Menu m=new Menu(设置,true); Menu n=new Menu(帮助,true);/二级菜单MenuItem m2= new MenuItem(更改时间);MenuItem n2=new MenuItem(关于);/添加菜单m.add(m2); n.add(n2);mb.add(m); mb.add(n); /实例化时钟 日历final MyCalendar cal=new MyCalendar(); final Clock clo=new Clock();/添加菜单,时间,日历到主面板this.setMenuBar(mb);this.add(clo);this.add(cal,BorderLayout.WEST);this.setSize(500,300);this.setLocation(400,300);this.setResizable(false);this.setVisible(true);this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);/监听菜单n2.addActionListener(new ActionListener() /菜单帮助-关于public void actionPerformed (ActionEvent e) JOptionPane.showMessageDialog(null, n 此程序为XX工业大学XX级XX专业的JAVA课程作业n 夜光效果时钟n 自动获取系统时间n 支持设置时钟和日历 n 支持当前系统日期前后五十年日历的查看nnVersion 1.0.0 作者:XX E-mail:552397723, 关于时钟-日历, JOptionPane.INFORMATION_MESSAGE);); m2.addActionListener(new ActionListener() /菜单设置-更改时间public void actionPerformed (ActionEvent e) /弹出的窗体 String str1=JOptionPane.showInputDialog(null,请输入时间格式(yyyy-MM-dd HH:mm:ss)n示例(2011-11-25 17:45:30); /实例化DateTestDateTest test=new DateTest(); try /获取设置的时间set=test.add_t(str1); catch (ParseException e1) e1.printStackTrace();/创建新日期日历Date now=new Date();Calendar set_c=Calendar.getInstance();/设置日历新日期set_c.setTime(set);/更改日历的下拉选择int select=set_c.get(Calendar.YEAR)-Calendar.getInstance().get(Calendar.YEAR)+50;cal.Year.setSelectedIndex(select);cal.Month.setSelectedIndex(set_c.get(Calendar.MONTH); /更改日历的日期显示cal.now_year=set_c.get(Calendar.YEAR);cal.now_month=set_c.get(Calendar.MONTH);cal.now_date=set_c;/日历的刷新cal.bool=false;cal.Show_Day();/获取设置时间与系统时间差 单位秒diff=(set.getTime()-now.getTime()/1000;/更改时钟显示日期clo.add_h=diff%(24*3600)/3600; clo.add_m=diff%3600/60; clo.add_s=diff%60; ); int delay = 500;/创建一个监听事件ActionListener drawTestClock = new ActionListener()public void actionPerformed(ActionEvent evt)repaint(); ;/创建一个时间计数器new Timer(delay,drawTestClock).start();/test/实现ActionListener接口必须实现的方法public void actionPerformed(ActionEvent e)public static void main(String args)new Test();/main class DateTest /获取设置的时间public Date add_t( String date) throws ParseException /格式化时间SimpleDateFormat dfs = new SimpleDateFormat(yyyy-MM-dd HH:mm:ss);/将特定格式的时间字符串转化为日期Date begin=dfs.parse(date); System.out.println(begin);return begin; Clock,javaimport java.awt.BasicStroke;import java.awt.BorderLayout;import java.awt.Color; import java.awt.Font;import java.awt.Graphics; import java.awt.Graphics2D;import java.awt.Insets;import java.util.Calendar;import java.util.GregorianCalendar;import javax.swing.JPanel;import javax.swing.border.TitledBorder;public class Clock extends JPanel /* * */private static final long serialVersionUID = 1L;int x,y,x0,y0,r,h,ss,mm,hh,ang; long add_h=0,add_m=0,add_s=0; JPanel pane= new JPanel(); final double RAD=Math.PI/180; /构造函数创建一个窗体 public Clock() pane.setBackground(Color.DARK_GRAY);this.setLayout(new BorderLayout(); this.add(pane); setBorder(new TitledBorder(时钟);setSize(300,300); /绘制图形 public void paint(Graphics g) super.paint(g); Graphics2D g2D = (Graphics2D)g; Insets insets = getInsets(); int L = insets.left/2,T = insets.top/2; h = getSize().height; g.setColor(Color.cyan); /画圆 g2D.setStroke(new BasicStroke(5.0f); g.drawOval(L+40,T+40,h-80,h-80); r=h/2-40; /半径 x0=40+r-8+L; y0=40+r-5-T; ang=60; /绘制时钟上的12个刻度 for(int i=1;i=12;i+) x=(int)(r+12)*Math.cos(RAD*ang)+x0); y=(int)(r+12)*Math.sin(RAD*ang)+y0); g.setFont(new Font(Tahoma, Font.BOLD, 14); g.drawString(+i,x,h-y); ang-=30; /设置精确时间到秒 Calendar now= new GregorianCalendar(); int nowh,nowm,nows; nows=(int)(now.get(Calendar.SECOND)+add_s); if(nows=60) nowm=(int)(now.get(Calendar.MINUTE)+add_m+1); else nowm=(int)(now.get(Calendar.MINUTE)+add_m); if(nowm=60) nowh=(int)(now.get(Calendar.HOUR_OF_DAY)+add_h+1); else nowh= (int) (now.get(Calendar.HOUR_OF_DAY)+add_h); nows=(nows+60)%60; nowm=(nowm+60)%60; nowh=(nowh+24)%24; /获取格式化时间串 String st; if(nowh10) st=0+nowh;else st=+nowh; if(nowm10) st+=:0+nowm;else st+=:+nowm; if(nows10) st+=:0+nows;else st+=:+nows; /计算时间与度数的关系 ss=90-nows*6; /指针度数 mm=90-nowm*6; hh=90-nowh*30-nowm/2; x0=r+40+L; /圆心 y0=r+40+T; /绘制窗体在窗体上显示时间 g.setColor(Color.lightGray); g.fillRect(x0-50,y0+30,100,30); g.setFont(new Font(Tahoma, Font.BOLD, 15); g.setColor(Color.blue); g.drawString(st,x0-35,y0+50); /绘制时针 x=(int)(r*0.6*Math.cos(RAD*hh)+x0; y=(int)(r*0.6*Math.sin(RAD*hh)+y0-2*T; g2D.setStroke(new BasicStroke(4.2f); g.setColor(Color.red); g.drawLine(x0,y0,x,h-y); /绘制分针 x=(int)(r*0.8*Math.cos(RAD*mm)+x0; y=(int)(r*0.8*Math.sin(RAD*mm)+y0-2*T; g2D.setStroke(new BasicStroke(3.0f); g.setColor(Color.green); g.drawLine(x0,y0,x,h-y); /绘制秒针 x=(int)(r*0.9*Math.cos(RAD*ss)+x0; y=(int)(r*0.9*Math.sin(RAD*ss)+y0-2*T; g2D.setStroke(new BasicStroke(1.0f); g.setColor(Color.yellow); g.drawLine(x0,y0,x,h-y); MyCalendar.javaimport java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.border.TitledBorder;import java.util.*; import java.awt.BorderLayout; public class MyCalendar extends JPanel implements ActionListener /* * */private static final long serialVersionUID = 1L;/定义JComboBox Month = new JComboBox();JComboBox Year = new JComboBox(); JLabel Year_l = new JLabel(年); JLabel Month_l = new JLabel(月); Calendar now_date =Calendar.getInstance(); JLabel Label_day = new JLabel49; /7X7 jLable0-6显示星期几,一个月最多跨越6周X7天int now_year = now_date.get(Calendar.YEAR);int now_month = now_date.get(Calendar.MONTH);boolean bool = false;String year_str =null; int month_int; JPanel pane_ym = new JPanel(); JPanel pane_day = new JPanel();public MyCalendar() super();/设定年月 for (int i = now_year -50; i = now_year +50; i+) Year.addItem(i + ); /五十年内的日历 for (int i = 1; i 13; i+) Month.addItem(i + ); /十二个月 Year.setSelectedIndex(50); /初始化选中第50项,即当前系统的年份pane_ym.add(Year);pane_ym.add(Year_l);Month.setSelectedIndex(now_month);pane_ym.add(Month); pane_ym.add(Month_l); Month.addActionListener(this); /监听Year.addActionListener(this); /初始化日期并绘制 日历显示格pane_day.setLayout(new GridLayout(7,7, 15, 15); for (int i = 0; i 49; i+) Label_dayi = new JLabel( ); pane_day.add(Label_dayi); this.Show_Day(); /绘制对应的日历日期pane_ym.setBackground(Color.LIGHT_GRAY);pane_day.setBackground(Color.LIGHT_GRAY);this.setLayout(new BorderLayout(); this.add(pane_day, BorderLayout.CENTER); this.add(pane_ym, BorderLayout.NORTH); this.setBorder(new TitledBorder(日历);setSize(300,300); void Show_Day() if (!bool) year_str = now_year+; month_int = now_month; else year_str = Year.getSelectedItem().toString(); /获取选取的年份stringmonth_int = Month.getSelectedIndex(); /获取选取的月份 int year_int = Integer.parseInt(year_str); /获得年份值int Calendar cal=Calendar.getInstance(); /创建一个Calendar实例 cal.set(year_int, month_int, 1); /构造一个日期 St

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论