首页 第7章 异常、事件和多线程机制

第7章 异常、事件和多线程机制

举报
开通vip

第7章 异常、事件和多线程机制第7章异常、事件和多线程机制7.1异常7.2事件7.3多线程机制习题7.1异常7.1.1定义异常是指当程序中某些地方出错时创建的一种特殊的运行时错误对象。Java创建异常对象后,就发送给Java程序,即抛出异常(throwinganexception)。程序捕捉到这个异常后,可以编写相应的异常处理代码进行处理。使用异常处理可以使得程序更加健壮,有助于调试和后期维护。7.1.2异常类由继承类图可以看出,Throwable类派生了两个类:Exception类和Error类,其中Error类系统保留,而Exception...

第7章 异常、事件和多线程机制
第7章异常、事件和多线程机制7.1异常7.2事件7.3多线程机制习题7.1异常7.1.1定义异常是指当程序中某些地方出错时创建的一种特殊的运行时错误对象。Java创建异常对象后,就发送给Java程序,即抛出异常(throwinganexception)。程序捕捉到这个异常后,可以编写相应的异常处理代码进行处理。使用异常处理可以使得程序更加健壮,有助于调试和后期维护。7.1.2异常类由继承类图可以看出,Throwable类派生了两个类:Exception类和Error类,其中Error类系统保留,而Exception类供应用程序使用,它下面又派生出几个具体的异常类,都对应着一项具体的运行错误,如图7.1所示。图7.1异常类图Exception类中常用的方法为:publicStringtoString()返回异常的简短描述。publicStringgetMessage()返回异常的详细信息描述。异常类分为系统定义的异常和用户自定义的异常。1.系统定义的异常图7.1中Exception派生的这些子类都是系统事先定义好并包含在Java类库中的,系统定义的异常对应着一些系统错误,如中断、文件没找到等错误。表7.1列举了一些常见的系统异常类。表7.1常见的系统异常类2.用户自定义异常用户自定义异常用来处理用户应用程序中的特定逻辑的运行错误,用户自定义的异常类通常继承自Exception类,例如:publicclassUserDefineExceptionextendsException{publicUserDefineException(Stringmsg){super(msg);}};7.1.3异常处理Java异常通常在调用某些方法不一定完全成功时抛出,针对抛出的异常程序需要给出相应的处理,这称为异常处理。异常处理分为三个部分:捕捉异常、程序 流程 快递问题件怎么处理流程河南自建厂房流程下载关于规范招聘需求审批流程制作流程表下载邮件下载流程设计 的跳转和异常处理语句块。当一个异常被抛出时,程序中有专门的语句来接收这个被抛出的异常对象,这个过程就是捕捉异常;当一个异常类的对象被捕捉或接收后,用户程序就会发生流程跳转,系统中止程序运行,跳转到异常处理语句的执行,或者直接跳出系统,回到操作系统状态下。在Java语言中,try语句用来启动Java的异常处理机制,通常是可能抛出异常的语句的调用;而catch语句进行捕捉和处理异常,有时添加finally语句块,finally中的语句是正常执行或者处理异常之后必须执行的语句。语句格式如下:try{语句块;}catch(异常类异常类参数名){异常处理语句块;}finally{try或者catch语句完毕后必须执行的语句(通常用于关闭文件流对象或者数据库对象等);}1.直接抛出异常例如:publicvoidmyMethod(){try{urlName=newURL("http://www.sina.com.cn");getAppletContext().showDocument(urlName,"right");}catch(MalformedURLExceptione){System.out.println(e.getMessage());}}try部分试图打开一个网址http://www.sina.com.cn,如果不成功,比如网址输入错误,那么引发异常程序忽略try中的下一行代码,直接跳到catch块中执行语句System.out.println(e.getMessage()),打印出引发的异常的错误信息描述。其中MalformedURLException说明引发的异常可能是URL网址输入错误类。2.间接抛出异常例如:publicvoidmyMethod()throwsMalformedURLException{urlName=newURL("http://www.sina.com.cn");getAppletContext().showDocument(urlName,"right");}在方法后面直接抛出。3.综合方法例如:publicvoidmyMethod()throwsMalformedURLException{try{urlName=newURL("http://www.sina.com.cn");getAppletContext().showDocument(urlName,"right");}catch(MalformedURLExceptione){System.out.println(e.getMessage());}}7.1.4多异常的处理多异常的处理使用多个catch来捕捉不同类的异常,Java中对catch块的数量没有限制。格式如下:Try{...}catch(异常类1标识符){...}catch(异常类2标识符){...}finally{...}【例7.1】测试多异常处理和自定义异常。源程序代码如下://程序文件名UseMultiException.javapublicclassUseMultiExceptionextendsException{staticStringstr1;staticStringstr2;publicstaticvoidmain(String[]args){inti1=0;inti2=0;str1=newString(args[0]);str2=newString(args[1]);StringstrResult=newString();try{read(str1,str2);i1=Integer.parseInt(str1);i2=Integer.parseInt(str2);intresult=i1/i2;strResult=String.valueOf(result);}catch(NumberFormatExceptione){strResult="错误的数字:"+e.getMessage();}catch(ArithmeticExceptione){strResult="被0除错误:"+e.getMessage();}str1=s1.trim();str2=s2.trim();if(str1.equals("0")){throw(newUserDefineException("本系统0不能为被除数"));}}};classUserDefineExceptionextendsException{publicUserDefineException(Stringmsg){super(msg);}};具体运行测试如下,结果如图7.2所示。javaUseMultiException2gf5数字格式出错。javaUseMultiException2gf0数字格式出错,下面的程序代码不再执行,无法抛出第二个异常。javaUseMultiException20被0除出错。javaUseMultiException03引发用户自定义错误。javaUseMultiException939/3的 答案 八年级地理上册填图题岩土工程勘察试题省略号的作用及举例应急救援安全知识车间5s试题及答案 为3。图7.2异常测试输出7.2事件事件用于描述程序、系统和程序使用者之间的各种活动。这些事件由系统事先定义好,当用户在图形界面上单击控件或双击鼠标时就可能引发某个事件,而用户程序中需要编制相应的代码来对这些事件做出处理。1.事件源图形用户界面上每个可能产生事件的组件称为事件源。2.事件监听者Java系统中注册的用于接收特殊事件的类。不同的事件对应着不同的监听者,要想事件被监听者监听并处理,则需先将事件源注册到监听者。3.事件处理流程事件源触发事件并将事件作为一个参数传递给监听者,监听者实现某个接口中的抽象方法,从而实现对事件的处理。Java的事件处理机制是一个委托事件模型,如图7.3所示。图7.3Java事件处理机制事件源注册的方法如下:publicvoidaddActionListener(ActionListenerl)添加特定的动作,监听接收来自事件源的动作事件,如果l为空,不会产生任何动作。监听者实现的接口为ActionListener接口,接口ActionListener来自包java.awt.event。在此接口中只有一个方法:publicvoidactionPerformed(ActionEvente)当事件对象e发生时,调用此方法。监听者就需要实现这个方法。Java中常用的事件和相应的事件监听者如表7.2所示。表7.2常用事件及其监听者7.2.1动作事件(ActionEvent)ActionEvent包含一个事件,该事件为执行动作事件ACTION_PERFORMED。触发这个事件的动作为:(1)点击按钮。(2)双击列表中的选项。(3)选择菜单项。(4)在文本框中输入回车。常用方法如下:publicStringgetActionCommand()返回引发某个事件的命令按钮的名字,如果名字为空,那么返回标签值。publicvoidsetActionCommand(Stringcommand)设置引发事件的按钮的名字,默认设置为按钮的标签。【例7.2】测试动作事件。源程序代码如下://程序文件名UseButton.javaimportjava.awt.*;importjava.awt.event.*;importjava.applet.*;importjava.applet.Applet;publicclassUseButtonextendsAppletimplementsActionListener{Stringstr1=newString();Buttonb1;//声明按钮对象;Buttonb2;Colorc;publicvoidinit(){b1=newButton();b2=newButton("按钮对象2");//添加事件监听者b1.addActionListener(this);b2.addActionListener(this);this.add(b1);this.add(b2);}publicvoidstart(){b1.setLabel("按钮对象1");str1=b2.getLabel();repaint();}publicvoidpaint(Graphicsg){g.setColor(c);g.drawString("引发事件的对象的标签:"+str1,40,60);}//实现接口中的方法,响应动作事件publicvoidactionPerformed(ActionEvente){Stringarg=e.getActionCommand();if(arg=="按钮对象1"){c=Color.red;str1="按钮对象1";}elseif(arg=="按钮对象2"){c=Color.blue;str1="按钮对象2";}repaint();}}单击“按钮对象1”,引发对象1的单击事件,输出结果如图7.4所示;单击“按钮对象2”,引发对象2的单击事件,输出结果如图7.5所示。图7.4单击“按钮对象1”的输出图7.5单击“按钮对象2”的输出7.2.2文本事件(TextEvent)文本事件即代表文本区域中文本变化的事件TEXT_VALUE_CHANGED,在文本区域中改变文本内容。publicvoidaddTextListener(TextListenerl)添加特定的文本事件,监听者接收来自文本对象的文本事件。如果l为空,那么不会抛出任何异常,而且也不会完成任何动作。publicinterfaceTextListenerextendsEventListener用于接收文本事件的监听者接口。当对象的文本发生变化时,调用监听者对象的方法。接口中的方法为:publicvoidtextValueChanged(TextEvente)当文本发生改变时调用。publicObjectgetSource()发生事件的对象,从EventObject继承来的方法。【例7.3】测试文本事件。源程序代码如下://程序文件名UseTextEvent.javaimportjava.awt.*;importjava.awt.event.*;importjava.applet.*;importjava.applet.Applet;publicclassUseTextEventextendsAppletimplementsActionListener,TextListener{TextFieldtOld;TextAreatNew;Panelp;publicvoidinit(){tOld=newTextField(25);tNew=newTextArea(8,25);//添加事件监听者tOld.addActionListener(this);tOld.addTextListener(this);//设置界面p=newPanel(newBorderLayout());p.add(tOld,BorderLayout.NORTH);p.add(tNew,BorderLayout.SOUTH);add(p);}//响应文本事件publicvoidtextValueChanged(TextEvente){if(e.getSource()==tOld)tNew.setText(tOld.getText());}//响应动作事件publicvoidactionPerformed(ActionEvente){if(e.getSource()==tOld)tNew.setText("");}};在文本框中键入“你好,这是文本事件同步”字符串,可以看见文本区域中的字符跟随变化,如图7.6所示,这是响应文本事件。键入字符串后按回车键,则发生响应动作事件,将文本区域清空,如图7.7所示。图7.6响应文本事件图7.7响应动作事件7.2.3选择事件(ItemEvent)选择事件中包含以事件为代表的选择项,选中状态发生变化的事件ITEM_STATE_CHANGED。引发的动作为:(1)改变列表类List对象选项的选中或不选中状态。(2)改变下拉列表类Choice对象选项的选中或不选中状态。(3)改变复选按钮类Checkbox对象的选中或不选中状态。事件源对象注册的方法如下:publicvoidaddItemListener(ItemListenerl)添加特定的项监听者,接收对象的选择项发生变化的事件。publicItemSelectablegetItemSelectable()ItemEvent事件的方法,返回产生事件的事件源对象。publicinterfaceItemListenerextendsEventListener接收选项事件的监听者接口。当选项中事件发生时,调用监听对象的itemStateChanged方法。publicvoiditemStateChanged(ItemEvente)当用户选中一项或未选中一项时,调用这个方法。【例7.4】测试选择事件。分别对设置颜色的复选框和有三种字号10、12和14的组合框进行选择时,标签的颜色和字体发生变化。源程序代码如下://程序文件名UseItemEvent.javaimportjava.awt.*;importjava.awt.event.*;importjava.applet.*;importjava.applet.Applet;publicclassUseItemEventextendsAppletimplementsItemListener{CheckboxcDisp;ButtonbtnDisp;ChoicecFont;publicvoidinit(){cDisp=newCheckbox("红色");btnDisp=newButton("颜色显示");cFont=newChoice();cFont.add("10");cFont.add("12");cFont.add("14");//添加事件cDisp.addItemListener(this);cFont.addItemListener(this);add(cDisp);add(cFont);add(btnDisp);}//接口事件publicvoiditemStateChanged(ItemEvente){Checkboxtemp;Choicetemp2;FontoldF;//复选框if(e.getItemSelectable()instanceofCheckbox){temp=(Checkbox)(e.getItemSelectable());//选中为红色,否则为蓝色if(temp.getState())btnDisp.setBackground(Color.red);elsebtnDisp.setBackground(Color.blue);}//组合框if(e.getItemSelectable()instanceofChoice){oldF=btnDisp.getFont();temp2=(Choice)(e.getItemSelectable());Strings=temp2.getSelectedItem();//设置字体btnDisp.setFont(newFont(oldF.getName(),oldF.getStyle(),Integer.parseInt(s)));}}}当选中红色复选框和组合框中的字号为14时,显示界面如图7.8所示;当未选中红色复选框和选中组合框中的字号为10时,显示界面如图7.9所示。图7.8选中红色和字号为14的界面图7.9未选中红色和字号为10的界面7.2.4调整事件(AdjustmentEvent)调整事件包含一个事件,即ADJUSTMENT_VALUE_CHANGED事件,当操纵滚动条改变其滑块位置时引发动作。AjustEvent的方法如下:publicAdjustablegetAdjustable()返回引发事件的对象。publicintgetValue()返回调整事件中的当前值。publicvoidaddAdjustmentListener(AdjustmentListenerl)添加调整监听者来接收来自对象的AdjustmentEvent实例。publicinterfaceAdjustmentListenerextendsEventListener接收调整事件的监听接口,有一个方法:publicvoidadjustmentValueChanged(AdjustmentEvente)可在调整改变时调用这个值。【例7.5】测试调整事件。设置一个水平滚动条,取值为1~36,随着滑块的变化,滚动条的值将显示在文本区域中,并且字体大小也会跟随变化。源程序代码如下://程序文件名UseAdjustmentEvent.javaimportjava.awt.*;importjava.awt.event.*;importjava.applet.*;importjava.applet.Applet;publicclassUseAdjustmentEventextendsAppletimplementsAdjustmentListener{Scrollbars;TextAreatxtValue;Panelp;publicvoidinit(){s=newScrollbar(Scrollbar.HORIZONTAL,0,1,10,36);//添加监听者s.addAdjustmentListener(this);txtValue=newTextArea(5,25);//界面布局p=newPanel(newBorderLayout());p.add(s,BorderLayout.NORTH);p.add(txtValue,BorderLayout.SOUTH);add(p);}publicvoidstart(){}publicvoidadjustmentValueChanged(AdjustmentEvente){intvalue;FontoldF;if(e.getAdjustable()==s){//得到滚动条的值value=e.getValue();//将值写入文本区域txtValue.setText(newInteger(value).toString());//按照滚动条的值设置字体oldF=txtValue.getFont();txtValue.setFont(newFont(oldF.getName(),oldF.getStyle(),value));}}}随着滚动条的变化,可以看见文本区域中的取值以及大小都会跟随变化,取值为10和取值为35时的效果各不相同,如图7.10和图7.11所示。图7.10字号为10的界面图7.11字号为35的界面7.2.5鼠标事件(MouseEvent)表明画布或界面组件中发生的鼠标事件,包含按下鼠标、释放鼠标、单击鼠标、进入部件的地理位置的鼠标事件和退出部件的地理位置的鼠标事件,以及鼠标移动事件(鼠标移动和鼠标拖动)。鼠标使用addMouseListener方法注册,通过MouseListener接收鼠标事件;鼠标还可以使用addMouseMotionListener方法注册,通过MouseMotionListener监听者监听鼠标移动事件。监听者中有具体的方法分别针对上述具体的鼠标事件,系统能够自动分辨鼠标事件的类型并调用相应的方法,所以只需编码实现相应的代码就可以了。publicintgetButton()返回哪个按钮发生变化。publicintgetClickCount()返回与这个事件相关的鼠标单击的次数。publicPointgetPoint()返回同源部件相对的事件发生的x、y位置。publicintgetX()返回同源部件相对的事件发生的x位置。publicintgetY()返回同源部件相对的事件发生的y位置。【例7.6】测试按钮和画布的鼠标事件,包括单击、按下、进入和退出等。鼠标事件的演示如图7.12和7.13所示。源程序代码如下://程序文件名UseMouseEvent.javaimportjava.awt.*;importjava.awt.event.*;importjava.applet.*;importjava.applet.Applet;publicclassUseMouseEventextendsAppletimplementsMouseListener,MouseMotionListener{Buttonbtn;publicvoidinit(){btn=newButton("演示鼠标事件");add(btn);//给按钮添加鼠标事件和鼠标移动事件btn.addMouseListener(this);btn.addMouseMotionListener(this);//给画布添加鼠标事件和鼠标移动事件this.addMouseListener(this);this.addMouseMotionListener(this);}//单击事件publicvoidmouseClicked(MouseEvente){Pointp=newPoint();if(e.getSource()==btn){if(e.getClickCount()==1){btn.setLabel("单击鼠标");}elseif(e.getClickCount()==2){btn.setLabel("双击鼠标");}}else{if(e.getClickCount()==1){p=e.getPoint();showStatus(p.x+","+p.y+"单击鼠标");}elseif(e.getClickCount()==2){p=e.getPoint();showStatus(p.x+","+p.y+"双击鼠标");}}}//进入事件publicvoidmouseEntered(MouseEvente){if(e.getSource()==btn)btn.setLabel("进入Button");elseshowStatus("进入Applet");}publicvoidmouseExited(MouseEvente){if(e.getSource()==btn)btn.setLabel("退出Button");elseshowStatus("退出Applet");}//按下事件publicvoidmousePressed(MouseEvente){if(e.getSource()==btn)btn.setLabel("按下鼠标");elseshowStatus("按下鼠标");}//释放事件publicvoidmouseReleased(MouseEvente){if(e.getSource()==btn)btn.setLabel("松开鼠标");elseshowStatus("松开鼠标");}//移动事件publicvoidmouseMoved(MouseEvente){if(e.getSource()==btn)btn.setLabel("移动鼠标");elseshowStatus("移动鼠标,新位置"+e.getX()+","+e.getY());}//拖动事件publicvoidmouseDragged(MouseEvente){if(e.getSource()==btn)btn.setLabel("拖动鼠标");elseshowStatus("拖动鼠标");}};图7.12状态行提示鼠标移动位置图7.13提示“按下鼠标”7.2.6键盘事件(KeyEvent)键盘事件有三个:键盘按键按下,按键释放,按键被敲击。常用方法如下:publicchargetKeyChar()返回事件中键的字符。publicintgetKeyCode()返回整数键码。publicstaticStringgetKeyText(intkeyCode)返回描述这个键码的字符串,例如“HOME”、“F1”或者“A”等。publicinterfaceKeyListenerextendsEventListener用来接收键盘事件。使用方法addKeyListener注册。针对键盘的三个事件接口提供相应的方法进行处理,具体方法如下:publicvoidkeyPressed(KeyEvente)按键时引发事件处理。publicvoidkeyReleased(KeyEvente)释放键时引发事件处理。publicvoidkeyTyped(KeyEvente)键入键时引发事件处理。例如,按键处理事件如下:publicvoidkeyPressed(KeyEvente){charch=e.getKeyChar();if(ch=='Y'||ch=='y')txt.setText("同意");elseifch=='N'||ch=='n'()txt.setText("反对");elsetxt.setText("无效");}7.3多线程机制7.3.1线程简介线程(thread)就是进程中的一个执行线索。Java虚拟机允许进程中同时执行多个线程。每个线程都有一个优先级。具有较高优先级的线程先执行。  线程是操作系统分配CPU时间的基本实体。每一个应用程序至少有一个线程,也可以拥有多个线程。线程是程序中的代码流。多个线程可以同时运行并能共享资源。  线程与进程不同,每个进程都需要操作系统为其分配独立的地址空间,而同一进程中的各个线程是在同一块地址空间中工作。在Java程序中,一些动态效果(如动画的实现、动态的字幕等)常利用多线程技术来实现。线程存在一个生命周期,由以下方法体现:  (1)start()方法:启动一个线程。  (2)run()方法:定义该线程的动作。  (3)sleep()方法:使线程睡眠一段时间,单位为ms。  (4)suspend()方法:使线程挂起。  (5)resume()方法:恢复挂起的线程。  (6)yield()方法:把线程移到队列的尾部。  (7)stop()方法:结束线程生命周期并执行清理工作。  (8)destroy()方法:结束线程生命周期但不做清理工作。其中最常用的是方法start()、run()、sleep()、stop()。7.3.2线程类和Runnable接口 1.建立Thread类的子类classmyThreadextendsThread{...publicvoidstart()//启动线程{...}publicvoidrun()//运行线程{...}}【例7.7】多线程实例,主函数给予调用,调用情况如图7.14所示。源程序代码如下:publicclassMThread{publicstaticvoidmain(String[]args){System.out.println("HelloWorld!");thread2t1=newthread2("线程实例1");//创建线程实例t1.start();//调用thread2t2=newthread2("线程实例2");t2.start();thread2t3=newthread2("线程实例3");t3.start();}}//自定义线程类thread2classthread2extendsThread{Threadthread;//定义线程实例Stringstr;//构造函数publicthread2(Stringstr){this.str=str;}//启动线程publicvoidstart(){thread=newThread(this);thread.start();}publicvoidrun(){inti=0;while(thread!=null){try{//计数到5时睡眠10秒if(i==5)sleep(10000);}catch(Exceptione){System.out.println(e.getMessage());}System.out.println(str);i++;}}};图7.14多线程操作界面2.实现接口RunnablepublicinterfaceRunnableRunnable接口可以由任意试图实现线程机制的类来实现。接口包含一个run方法。publicvoidrun()对象实现Runnable接口时,创建一个线程,启动线程导致对象run方法的调用。实现接口Runnable进行多线程设计的方法较为常用。下面给出一个例子。【例7.8】编写Applet,实现Runnable接口进行简单的动画演示:三幅图片依次上移,如图7.15所示。源程序代码如下://程序文件名为UseRunnable.javaimportjava.awt.*;importjava.applet.Applet;publicclassUseRunnableextendsAppletimplementsRunnable{Threadt;Imageimgs[];inthigh,h1,h2,h3;publicvoidinit(){high=getHeight()/3;h1=high;h2=high*2;h3=high*3;imgs=newImage[3];for(inti=0;i<3;i++){imgs[i]=getImage(getDocumentBase(),"image"+(i+1)+".gif");}}publicvoidstart(){t=newThread(this);t.start();}publicvoidstop(){t=null;}//实现接口的run方法,获得动画效果publicvoidrun(){while(t!=null){try{Thread.sleep(100);repaint();h1--;//上移,到顶点时睡眠if(h1==0){Thread.sleep(3000);h2=high;}//上移,到顶点时睡眠h2--;if(h2==0){Thread.sleep(3000);h3=high;}//上移,到顶点时睡眠h3--;if(h3==0){Thread.sleep(3000);h1=high;}}catch(InterruptedExceptione){System.out.println(e.getMessage());}}}publicvoidpaint(Graphicsg){//三幅图片依次显示g.drawImage(imgs[0],0,h1,this);g.drawImage(imgs[1],0,h2,this);g.drawImage(imgs[2],0,h3,this);}publicvoidupdate(Graphicsg){paint(g);}};图7.15动画界面习题1.自定义一个用户异常,用于数值运算时在输入的字符中检测非数字字符,并编写抛出异常时的提示“使用非法字符”,测试异常。2.为第6章习题1中图6.12所示的“提交”按钮添加动作事件,用来收集界面上输入的个人信息,并在原有界面上添加一个文本区域,将数据输出到文本区域中。3.为第6章习题2添加调整事件,使得三个滚动条进行滑动时,标签的颜色相应地发生变化。4.编写Applet,实现Runnable接口,使得界面上的一图片持续做水平运动。
本文档为【第7章 异常、事件和多线程机制】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
个人认证用户
正方体
暂无简介~
格式:ppt
大小:635KB
软件:PowerPoint
页数:89
分类:
上传时间:2022-05-10
浏览量:0