Java程序设计模拟测试(专用试题)
单选题
Thread类中能运行线程体的方法是B.run( )。start( )方法会启动一个新的线程,init( )方法是构造器的一部分,resume( )方法是恢复一个暂停的线程。
如果要把容器空间分成东、西、南、北、中五个区域,应采用的布局是D.BorderLayout。BorderLayout类可以将容器分为五个区域:EAST,WEST,SOUTH,NORTH和CENTER。每个区域只能放置一个组件,如果放置多个组件,只有最后一个组件可见。
能让线程从阻塞状态恢复到就绪状态的方法是B.resume()。init()方法是构造器的一部分,start()方法会启动一个新的线程,run()方法是线程体的执行部分。
如果lianxi.txt不存在,则A.程序会自动创建该文件。FileOutputStream类的构造方法可以接受一个字符串参数,表示要写入的文件名。如果该文件不存在,会自动创建一个新的文件。如果该文件已存在,会覆盖原有的内容。
关于继承的说法正确的是D.子类将继承父类非私有属性和方法。继承是一种面向对象的特征,它允许子类获得父类的属性和方法。私有属性和方法是父类的内部实现,不会被子类继承。公有和受保护的属性和方法可以被子类继承,但是受保护的属性和方法只能在子类内部访问,不能在外部访问。
下面正确的创建Socket的语句为A.Socket b=new Socket(” 130.3.4.5”,80):。Socket类的构造方法可以接受一个字符串参数和一个整数参数,分别表示要连接的服务器的IP地址和端口号。ServerSocket类是用于创建服务器端的套接字,它不能用来创建客户端的套接字。ServerSocket类的构造方法只接受一个整数参数,表示服务器端的端口号。
为实现线程之间的通信,需要使用下列哪种流合适? C.管道流 (PipedStream)。管道流是一种特殊的流,它可以在两个线程之间传输数据。一个线程可以通过PipedOutputStream向管道中写入数据,另一个线程可以通过PipedInputStream从管道中读取数据。过滤流,缓存流和文件流都不适合用于线程之间的通信,它们都是用于处理其他类型的数据源或目的地的流。
JPanel默认的布局管理器是A. FlowLayout。FlowLayout类是一种简单的布局管理器,它可以将组件按照从左到右,从上到下的顺序排列在容器中。如果容器的大小不足以显示所有的组件,FlowLayout会自动换行。GroupLayout,GridLayout和BorderLayout都不是JPanel的默认布局管理器,但是可以通过setLayout方法来设置。
下面哪些类用于实现TCP/IP客户和服务器? D.Socket, ServerSocket。Socket类和ServerSocket类是用于实现TCP/IP协议的客户端和服务器端的套接字。Socket类可以创建一个客户端的套接字,用于连接到指定的服务器。ServerSocket类可以创建一个服务器端的套接字,用于监听客户端的连接请求。DatagramPacket类和DatagramSocket类是用于实现UDP协议的数据包和套接字。UDP协议是一种无连接的协议,它不保证数据的可靠性和顺序性。
接口MouseMotionListener用于监听组件上的鼠标移动事件,它包括两个方法:mouseMoved(MouseEvent e)和mouseDragged(MouseEvent e)。在以下供选择的方法中,属于接口MouseMotionListener的方法是B. mouseMoved( )。MouseMotionListener接口是用于接收鼠标移动事件的监听器接口,它有两个抽象方法:mouseDragged( MouseEvent e)和mouseMoved( MouseEvent e)。mouseDragged( )方法是当鼠标按下并拖动时触发,mouseMoved( )方法是当鼠标移动但没有按下时触发。 mouseEntered( )方法和mouseExited( )方法是属于MouseListener接口的方法,它们是当鼠标进入或离开组件时触发。mouseReleased( )方法也是属于MouseListener接口的方法,它是当鼠标释放时触发。
mouseEntered和mouseMoved的区别如下:
- mouseEntered是MouseListener接口中的方法,它在鼠标进入某个组件的区域时触发。
- mouseMoved是MouseMotionListener接口中的方法,它在鼠标在某个组件上移动时触发。
- mouseEntered不支持事件冒泡,也就是说,如果鼠标从一个组件的子元素进入到该组件,不会触发mouseEntered事件。
- mouseMoved支持事件冒泡,也就是说,如果鼠标在一个组件的子元素上移动,会触发该组件的mouseMoved事件。
声明并创建一个按钮对象b,应该使用的语句是C.JButton b=new JButton()。JButton类是用于创建按钮的类,它是javax.swing包中的一个类。要声明并创建一个按钮对象,需要使用JButton类的无参构造方法。button类是java.awt包中的一个类,它是一个旧的按钮类,不推荐使用。b.setLabel(“确定”)是一个方法调用,不是一个声明语句。JButton b=new b()是一个错误的语句,因为b不是一个类名。
容器被重新设置大小后,C.FlowLayout布局管理器的容器中的组件大小不随容器大小的变化而改变。FlowLayout类是一种简单的布局管理器,它可以将组件按照从左到右,从上到下的顺序排列在容器中。如果容器的大小不足以显示所有的组件,FlowLayout会自动换行。但是,FlowLayout不会改变组件的大小,只会改变组件的位置。BorderLayout类,CardLayout类和GridLayout类都会根据容器的大小来调整组件的大小和位置。
下面通常用来对对象加锁,并使得对对象的访问是排他的关键字是B. synchronized。synchronized关键字可以用来修饰方法或者代码块,表示该方法或者代码块是同步的,也就是说,同一时刻只能有一个线程执行该方法或者代码块。这样可以保证对对象的访问是互斥的,避免数据的不一致性。transient关键字可以用来修饰属性,表示该属性不会被序列化。static关键字可以用来修饰属性或者方法,表示该属性或者方法是属于类的,而不是属于对象的。serialize不是一个关键字,而是一个接口,表示一个类的对象可以被序列化。
public Color(int r,int g,int b,int alpha): 用指定的红色分量r、绿色分量g、蓝色分量b的值构造一个Color对象,其中r、g、b均为D.0-255之间的一个整数。Color类是用于封装颜色的类,它有多个构造方法,其中一个是接受四个整数参数的构造方法,分别表示红色分量,绿色分量,蓝色分量和透明度分量。这四个参数的取值范围都是0-255,其中0表示最小值,255表示最大值。
假设正在为应用系统设计一个Java图形用户界面 (GUI),要求能输入多行文本信息,下列组件中最能满足这个要求的是D.TextArea。TextArea类是用于创建多行文本输入框的类,它可以让用户输入和编辑多行的文本信息。Label类是用于创建标签的类,它只能显示一行的文本信息,不能让用户输入。Checkbox类是用于创建复选框的类,它可以让用户选择或取消选择一个选项,但不能输入文本。TextField类是用于创建单行文本输入框的类,它只能让用户输入和编辑一行的文本信息。
如果容器组件p的布局是BorderLayout,则在p的下边中添加一个按钮b,应该使用的语句是A. p.add(b,“South”)。BorderLayout类是一种布局管理器,它可以将容器分为五个区域:EAST,WEST,SOUTH,NORTH和CENTER。每个区域只能放置一个组件,如果放置多个组件,只有最后一个组件可见。要在容器的某个区域添加一个组件,需要使用容器的add方法,并指定区域的名称。例如,p.add(b,“South”)表示在p的南边区域添加一个按钮b。B. b.add(p,“North”)是错误的语句,因为不能将容器添加到按钮中。C. p.add(b)是不完整的语句,因为没有指定区域的名称。D.p.add(b,“North”)是正确的语句,但是它表示在p的上边中添加一个按钮b,而不是下边。
线程的哪一个状态已经具备了运行条件:A.就绪状态。
A.就绪状态。就绪状态是指线程已经创建并且获取了除CPU资源之外的所有资源,只要获得CPU资源,就可以立即执行。就绪状态的线程是由调度器根据一定的算法来选择执行的。B.死亡状态是指线程已经结束或者被终止,不再具备运行条件。C.新建状态是指线程刚刚被创建,还没有启动,也没有获取任何资源,不具备运行条件。D.死锁状态是指线程在等待某个资源,而该资源又被其他线程占用,导致线程无法继续执行,也不具备运行条件。
在异常处理中,如释放资源、关闭文件、关闭数据库等由A.finally子句来完成。finally子句是异常处理中的一个可选部分,它用于执行一些必须的清理操作,无论是否发生异常,finally子句都会被执行。try子句是异常处理中的一个必须部分,它用于包含可能发生异常的代码。catch子句是异常处理中的一个可选部分,它用于捕获和处理特定类型的异常。throw子句是用于抛出异常的语句,它不属于异常处理的结构。
在Java中,如果要让正在运行的线程进入睡眠状态,可以调用线程的C.sleep()方法。这个方法是Thread类的一个静态方法,它可以让当前线程暂时放弃CPU的调度权,但不会释放锁资源。sleep()方法有两个重载版本,一个是接受一个long类型的参数,表示睡眠的毫秒数;另一个是接受一个long类型和一个int类型的参数,表示睡眠的毫秒数和纳秒数。
构造函数是一种特殊的成员函数,它的作用是在创建对象时对对象的数据成员进行初始化。构造函数的特点有以下几点:
- 构造函数的名字与类名相同,可以有参数,但不能有返回值(连void也不行)。
- 构造函数是在实例化对象时自动执行的,不需要用户手动调用。
- 构造函数可以有多个,形成重载关系,根据传入的参数类型和个数来调用不同的构造函数。
- 构造函数可以有默认参数,这样可以在实例化对象时不传入参数或只传入部分参数。
- 如果没有定义构造函数,系统会生成一个默认的无参构造函数,不做任何初始化工作。如果定义了构造函数,系统就不再生成默认的无参构造函数。
举个例子,假设有一个类Box,它有三个数据成员:height, width, length。我们可以定义以下几种构造函数:
//无参构造函数 Box() { height = 0; width = 0; length = 0; } //带三个参数的构造函数 Box(int h, int w, int l) { height = h; width = w; length = l; } //带一个参数的构造函数 Box(int x) { height = x; width = x; length = x; } //带默认参数的构造函数 Box(int h = 10, int w = 10, int l = 10) { height = h; width = w; length = l; }
那么我们可以用以下几种方式来实例化对象:
Box b1; //调用无参构造函数或带默认参数的构造函数 Box b2(5); //调用带一个参数的构造函数或带默认参数的构造函数 Box b3(5, 10, 15); //调用带三个参数的构造函数或带默认参数的构造函数 Box b4(5, 10); //调用带默认参数的构造函数
这样就可以根据不同的需求来初始化对象的数据成员了。
程序题
Java分支结构
Java分支结构之多重if (10.0分)
Java分支结构之Switch (10.0分)
第1关:Java分支结构之多重if
任务描述
本小节需要你实现一个星级成绩评定系统,效果如下:
相关知识
若要完成本小节的任务,需要你先了解多重If语句相关知识。
多重If语句
在条件 1 不满足的情况下,才会进行条件 2 的判断;当前面的条件均不成立时,才会执行 else 块内的代码,例如:如果一个人年龄介于 35岁至 60 岁之间,就是“中年”;如果年龄介于 18 岁至 35 岁之间,则是“青年”; 18 岁以下则是“少年”
如下:
- if(<条件1>){
- <条件1成立执行的代码>
- }elseif(<条件2>){
- <条件2成立执行的代码>
- }elseif(<条件3>){
- <条件3成立执行的代码>
- }else{
- <以上条件都不成立执行的代码>
- }
执行过程:
- int age =37;
- if(age <18){
- System.out.println("少年");
- }elseif(age >=18&& age <35){
- System.out.println("青年");
- }elseif(age >=35&& age <60){
- System.out.println("中年");
- }else{
- System.out.println("老年");
- }
编程要求
在start-end区域在Begin-End区域编写一个星级成绩评定系统,规则如下:
- 90分及以上:五星成绩。
- 80-90分(包括80分,不包含90分): 四星成绩。
- 70-80分(包括70分,不包含80分):: 三星成绩。
- 60-70分(包括60分,不包含70分):: 俩星成绩。
- 60分以下(不包含60分)::无星成绩。
效果图如下:
测试说明
我会对你编写的程序测试五次,不要偷懒哦!
开始吧,骚年!
package step3; import java.util.Scanner; public class HelloStep3 { public static void main(String[] args) { System.out.println("星级成绩评定系统"); System.out.println("请输入成绩:"); Scanner sc = new Scanner(System.in); /******start******/ // 读取用户输入的成绩 int score=sc.nextInt(); // 判断成绩是否在90-100之间,如果是,输出五星成绩 if((score>=90)&&(score<=100)){ System.out.println("*****五星成绩"); } // 判断成绩是否在80-90之间,如果是,输出四星成绩 else if((score>=80)&&(score<90)){ System.out.println("****四星成绩"); } // 判断成绩是否在70-80之间,如果是,输出三星成绩 else if((score>=70)&&(score<80)){ System.out.println("***三星成绩"); } // 判断成绩是否在60-70之间,如果是,输出俩星成绩 else if((score>=60)&&(score<70)){ System.out.println("**俩星成绩"); } // 判断成绩是否在0-60之间,如果是,输出无星成绩 else if((score>=0)&&(score<60)){ System.out.println("无星成绩"); } /******end******/ } }
Java高级特性 - 多线程基础(3)线程同步
使用synchronized关键字同步线程 (10.0分)
第1关:使用synchronized关键字同步线程
任务描述
本关任务:使右侧代码中的insert方法在同一时刻只有一个线程能访问。
相关知识
为了完成本关任务,你需要掌握:
1.并发编程什么时候会出现安全问题;
2.怎么解决线程安全问题;
3.synchronized关键字。
并发编程什么时候会出现安全问题
在单线程的时候是不会出现安全问题的,不过在多线程的情况下就很有可能出现,比如说:多个线程同时访问同一个共享资源,多个线程同时向数据库插入数据,这些时候如果我们不做任何处理,就很有可能出现数据实际结果与我们预期的结果不符合的情况。
现在有两个线程同时获取用户输入的数据,然后将数据插入到同一张表中,要求不能出现重复的数据。
我们必然要在插入数据的时候进行如下操作:
- 检查数据库中是否存在该数据;
- 如果存在则不插入,否则插入。
现在有两个线程ThreadA和ThreadB来对数据库进行操作,当某个时刻,线程A和B同时读取到了数据X,这个时候他们都去数据库验证X是否存在,得到的结果都是不存在,然后A、B线程都向数据库插入了X数据,这个时候数据库中出现了两条X数据,还是出现了数据重复。
这个就是线程安全问题,多个线程同时访问一个资源时,会导致程序运行结果并不是想看到的结果。
这里面,这个资源被称为:临界资源(也可以叫共享资源)。
当多个线程同时访问临界资源(一个对象,对象中的属性,一个文件,一个数据库等等)时,就有可能产生线程安全问题。
当多个线程执行一个方法时,方法内部的局部变量并不是临界资源,因为方法是在栈上执行的,而Java栈是线程私有的,因此不会产生线程安全问题。
如何解决线程安全问题
怎么解决线程的安全问题呢?
基本上所有解决线程安全问题的方式都是采用“序列化临界资源访问”的方式,即在同一时刻只有一个线程操作临界资源,操作完了才能让其他线程进行操作,也称作同步互斥访问。
在Java中一般采用synchronized和Lock来实现同步互斥访问。
synchronized关键字
首先我们先来了解一下互斥锁,互斥锁:就是能达到互斥访问目的的锁。
如果对一个变量加上互斥锁,那么在同一时刻,该变量只能有一个线程能访问,即当一个线程访问临界资源时,其他线程只能等待。
在Java中,每一个对象都有一个锁标记(monitor),也被称为监视器,当多个线程访问对象时,只有获取了对象的锁才能访问。
在我们编写代码的时候,可以使用synchronized修饰对象的方法或者代码块,当某个线程访问这个对象synchronized方法或者代码块时,就获取到了这个对象的锁,这个时候其他对象是不能访问的,只能等待获取到锁的这个线程执行完该方法或者代码块之后,才能执行该对象的方法。
我们来看个示例进一步理解synchronized关键字:
- publicclassExample{
- publicstaticvoid main(String[] args) {
- finalInsertData insertData =newInsertData();
- newThread(){
- publicvoid run(){
- insertData.insert(Thread.currentThread());
- };
- }.start();
- newThread(){
- publicvoid run(){
- insertData.insert(Thread.currentThread());
- };
- }.start();
- }
- }
- classInsertData{
- privateArrayList<Integer> arrayList =newArrayList<Integer>();
- publicvoid insert(Thread thread){
- for(int i=0;i<5;i++){
- System.out.println(thread.getName()+"在插入数据"+i);
- arrayList.add(i);
- }
- }
- }
这段代码的执行是随机的(每次结果都不一样):
Thread-0在插入数据0 Thread-1在插入数据0 Thread-1在插入数据1 Thread-1在插入数据2 Thread-1在插入数据3 Thread-1在插入数据4 Thread-0在插入数据1 Thread-0在插入数据2 Thread-0在插入数据3 Thread-0在插入数据4
现在我们加上synchronized关键字来看看执行结果:
- publicsynchronizedvoid insert(Thread thread){
- for(int i=0;i<5;i++){
- System.out.println(thread.getName()+"在插入数据"+i);
- arrayList.add(i);
- }
- }
输出:
Thread-0在插入数据0 Thread-0在插入数据1 Thread-0在插入数据2 Thread-0在插入数据3 Thread-0在插入数据4 Thread-1在插入数据0 Thread-1在插入数据1 Thread-1在插入数据2 Thread-1在插入数据3 Thread-1在插入数据4
可以发现,线程1会等待线程0插入完数据之后再执行,说明线程0和线程1是顺序执行的。
从这两个示例中,我们可以知道synchronized关键字可以实现方法同步互斥访问。
在使用synchronized关键字的时候有几个问题需要我们注意:
- 在线程调用synchronized的方法时,其他synchronized的方法是不能被访问的,道理很简单,一个对象只有一把锁;
- 当一个线程在访问对象的synchronized方法时,其他线程可以访问该对象的非synchronized方法,因为访问非synchronized不需要获取锁,是可以随意访问的;
- 如果一个线程A需要访问对象object1的synchronized方法fun1,另外一个线程B需要访问对象object2的synchronized方法fun1,即使object1和object2是同一类型),也不会产生线程安全问题,因为他们访问的是不同的对象,所以不存在互斥问题。
synchronized代码块
synchronized代码块对于我们优化多线程的代码很有帮助,首先我们来看看它长啥样:
- synchronized(synObject){
- }
当在某个线程中执行该段代码时,该线程会获取到该对象的synObject锁,此时其他线程无法访问这段代码块,synchronized的值可以是this代表当前对象,也可以是对象的属性,用对象的属性时,表示的是对象属性的锁。
有了synchronized代码块,我们可以将上述添加数据的例子修改成如下两种形式:
- classInsertData{
- privateArrayList<Integer> arrayList =newArrayList<Integer>();
- publicvoid insert(Thread thread){
- synchronized(this){
- for(int i=0;i<100;i++){
- System.out.println(thread.getName()+"在插入数据"+i);
- arrayList.add(i);
- }
- }
- }
- }
- classInsertData{
- privateArrayList<Integer> arrayList =newArrayList<Integer>();
- privateObjectobject=newObject();
- publicvoid insert(Thread thread){
- synchronized(object){
- for(int i=0;i<100;i++){
- System.out.println(thread.getName()+"在插入数据"+i);
- arrayList.add(i);
- }
- }
- }
- }
上述代码就是synchronized代码块添加锁的两种方式,可以发现添加synchronized代码块,要比直接在方法上添加synchronized关键字更加灵活。
当我们用sychronized关键字修饰方法时,这个方法只能同时让一个线程访问,但是有时候很可能只有一部分代码需要同步,而这个时候使用sychronized关键字修饰的方法是做不到的,但是使用sychronized代码块就可以实现这个功能。
并且如果一个线程执行一个对象的非static synchronized方法,另外一个线程需要执行这个对象所属类的static synchronized方法,此时不会发生互斥现象,因为访问static synchronized方法占用的是类锁,而访问非static synchronized方法占用的是对象锁,所以不存在互斥现象。
来看一段代码:
- publicclassTest{
- publicstaticvoid main(String[] args) {
- finalInsertData insertData =newInsertData();
- newThread(){
- publicvoid run(){
- insertData.insert();
- }
- }.start();
- newThread(){
- publicvoid run(){
- insertData.insert1();
- }
- }.start();
- }
- }
- classInsertData{
- publicsynchronizedvoid insert(){
- System.out.println("执行insert");
- try{
- Thread.sleep(5000);
- }catch(InterruptedException e){
- e.printStackT\frace();
- }
- System.out.println("执行insert完毕");
- }
- publicsynchronizedstaticvoid insert1(){
- System.out.println("执行insert1");
- System.out.println("执行insert1完毕");
- }
- }
执行结果:
执行insert 执行insert1 执行insert1完毕 执行insert完毕
编程要求
请仔细阅读右侧代码,根据方法内的提示,在Begin - End区域内进行代码补充,具体任务如下:
- 使num变量在同一时刻只能有一个线程可以访问。
测试说明
使程序的输出结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
开始你的任务吧,祝你成功!
package step2; public class Task { public static void main(String[] args) { final insertData insert = new insertData(); for (int i = 0; i < 3; i++) { new Thread(new Runnable() { public void run() { insert.insert(Thread.currentThread()); } }).start(); } } } class insertData{ public static int num =0; /********* Begin *********/ public synchronized void insert(Thread thread){ for (int i = 0; i <= 5; i++) { num++; System.out.println(num); } } /********* End *********/ }
第1关:顺序输出
package step1; public class Task { public static void main(String[] args) throws Exception { /********* Begin *********/ //在这里创建线程, 开启线程 Object a = new Object(); //创建对象 a b c,用作线程间的同步锁 Object b = new Object(); Object c = new Object(); MyThread th1 = new MyThread("AA",a,c); //创建MyThread 对象 th1 th2 th3,传入线程名和两个同步锁对象 MyThread th2 = new MyThread("BB",c,b); MyThread th3 = new MyThread("CC",b,a); th1.start(); //调用 th1 的 start 方法,启动线程 th1 Thread.sleep(10); //让主线程睡眠 10 毫秒,保证 th1 先执行 th2.start(); //调用 th2 的 start 方法,启动线程 th2 Thread.sleep(10); //让主线程睡眠 10 毫秒,保证 th2 先执行 th3.start(); //调用 th3 的 start 方法,启动线程 th3 Thread.sleep(10); //让主线程睡眠 10 毫秒,保证 th3 先执行 System.exit(0); //退出程序 /********* End *********/ } } class MyThread extends Thread { /********* Begin *********/ String threadName; //定义一个字符串变量,用于存储线程名 Object a = null; //定义一个对象变量,用于存储同步锁对象 a和b Object b = null; public MyThread(String threadName,Object a,Object b) { //定义一个构造方法,接收线程名和两个同步锁对象作为参数 super(); //调用父类的构造方法 this.threadName = threadName; //给 threadName 赋值 this.a=a; //分别给 a b 赋值 this.b=b; } public synchronized void run() { //重写 run 方法,实现线程的逻辑 int count = 5; //定义一个整数变量 count,用于记录打印次数 while(count > 0){ //当 count 大于 0 时循环执行以下代码块 synchronized (a){ //获取 a 对象的锁 synchronized (b){ //获取 b 对象的锁 System.out.println("Java Thread" + this.threadName); //输出当前线程名 count--; //count -1 b.notify(); //唤醒等待 b 对象的锁的其他线程(即下一个线程) } try { a.wait(); //释放 a 对象的锁,并让当前线程等待(即进入阻塞状态) }catch (InterruptedException e){ //捕获可能抛出的异常 e.printStackTrace(); //打印异常信息 } } } } /********* End *********/ }
Java面向对象 - Java中的异常
Java 中的异常处理机制 (4.0分)
捕获异常 (6.0分)
第1关:Java 中的异常处理机制
- 1、
在Java中,源文件Test.java中包含如下代码段,则程序编译运行结果是( )
- public class HelloWorld{
- public static void main(String[] args){
- System.out.print(“HelloWorld!”);
- }
- }
A、
输出:HelloWorld!
B、
编译出错,提示“公有类HelloWorld必须在HelloWorld.java文件中定义”
C、
运行正常,但没有输出内容
D、
运行时出现异常
- 2、
关于下列代码,说法正确的是()
- public static void main(String[] args){
- int num1 = 10;
- int num2 = 0;
- System.out.println(num1/num2);
- }
A、
输出0
B、
编译报错,提示除数不能为0
C、
输出无穷大
D、
运行时报错,提示除数不能为0
第2关:捕获异常
任务描述
本关任务:捕获程序的异常,输出异常处理的结果。
相关知识
为了完成本关任务,你需要掌握:1.如何捕获异常。
捕获异常
通过第一关我们知道,有一部分异常是需要程序员提前处理的,这种异常统一称为检测性异常,如果我们不处理,程序是不能编译通过的,在IDE中也会出现一条红线。
这个时候我们就必须处理这段可能出现异常的程序。
如何处理呢?
Java中提供了一个捕获异常的机制:try-catch
通过这两个单词的字面意思我们就能很好的理解了:try:尝试,catch:捕获; 尝试执行代码A和代码B如果这两段代码有一个出现了异常,就会执行catch中的语句,如果代码A、B都不存在异常就不会执行catch代码,最后继续执行代码C。
所以之前报错的代码我们这样写就没错啦:
在这里我们可以发现catch捕获的是FileNotFoundException,这是一个文件未找到异常,所以我们在捕获异常的时候最好要先明确异常的种类是什么。
好奇的同学可能会有疑惑,检测性异常可以用try-catch来处理,那运行时异常可不可以用try-catch来处理呢?
可不可以呢?自己验证一下吧!
编程要求
请仔细阅读右侧代码,根据方法内的提示,在Begin - End区域内进行代码补充,具体任务如下:
- 编辑器中的代码运行时可能会有异常,请利用本关知识处理该异常。
测试说明
补充完代码后,点击测评,平台会对你编写的代码进行测试,当你的结果与预期输出一致时,即为通过。
输入: 4 2
输出: 2
输入: 4 0
输出: 除数不能为0
提示:捕获异常需要用特定的类,下表总结了常用的异常类:
非检测型异常:
异常 |
描述 |
ArithmeticException |
当出现异常的运算条件时,抛出此异常。例如,一个整数"除以零"时,抛出此类的一个实例。 |
ArrayIndexOutOfBoundsException |
用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。 |
ClassCastException |
当试图将对象强制转换为不是实例的子类时,抛出该异常。 |
IllegalArgumentException |
抛出的异常表明向方法传递了一个不合法或不正确的参数。 |
IllegalMonitorStateException |
抛出的异常表明某一线程已经试图等待对象的监视器,或者试图通知其他正在等待对象的监视器而本身没有指定监视器的线程。 |
IllegalStateException |
在非法或不适当的时间调用方法时产生的信号。换句话说,即 Java 环境或 Java 应用程序没有处于请求操作所要求的适当状态下。 |
IllegalThreadStateException |
线程没有处于请求操作所要求的适当状态时抛出的异常。 |
IndexOutOfBoundsException |
指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。 |
NegativeArraySizeException |
如果应用程序试图创建大小为负的数组,则抛出该异常。 |
NullPointerException |
当应用程序试图在需要对象的地方使用 null 时,抛出该异常 |
NumberFormatException |
当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。 |
StringIndexOutOfBoundsException |
此异常由 String 方法抛出,指示索引或者为负,或者超出字符串的大小。 |
... |
... |
检测性异常:
异常 |
描述 |
ClassNotFoundException |
应用程序试图加载类时,找不到相应的类,抛出该异常。 |
CloneNotSupportedException |
当调用 Object 类中的 clone 方法克隆对象,但该对象的类无法实现 Cloneable 接口时,抛出该异常。 |
IllegalAccessException |
拒绝访问一个类的时候,抛出该异常。 |
InstantiationException |
当试图使用 Class 类中的 newInstance 方法创建一个类的实例,而指定的类对象因为是一个接口或是一个抽象类而无法实例化时,抛出该异常。 |
InterruptedException |
一个线程被另一个线程中断,抛出该异常。 |
NoSuchFieldException |
请求的变量不存在 |
NoSuchMethodException |
请求的方法不存在 |
IOException及其子类 |
对文件或流的操作有误时,抛出异常 |
... |
... |
开始你的任务吧,祝你成功!
package step2; import java.util.Scanner; public class Task { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int num1 = sc.nextInt(); int num2 = sc.nextInt(); /********* Begin *********/ try{ System.out.println(num1/num2); } catch(Exception e){ System.out.println("除数不能为0"); } /********* End *********/ } }
Java高级特性 - IO流-2
字符流 - 输入输出 (10.0分)
第1关:字符流 - 输入输出
任务描述
本关任务:使用字符流对文本进行读写操作。
相关知识
为了完成本关任务,你需要掌握:
1.如何使用字符流读数据;
2.如何使用字符流写数据。
Writer
字符流的使用很简单,和字节输入流类似,以FileWriter举例:
执行上述代码即可看到在D盘下创建了一个名为hello.txt的文件,文件的内容为hello。
上面代码fw.flush()和fw.close()也可以省略fw.flush(),只写fw.close()就可以了,但是都省略是不对的,如果都省略你会发现文本没有写入到hello.txt文件。
Reader
Reader的使用也很简单,以FileReader为例:
输出:
hello+ 1019个空格
使用上述代码的会输出hello.txt中的内容,但是会有一个问题:输出hello的同时还输出了1019个空格,这是什么原因呢,如何解决这些问题呢?请你思考。
我们在下一关中揭晓答案。
编程要求
请仔细阅读右侧代码,根据方法内的提示,在Begin - End区域内进行代码补充,具体任务如下:
- 将src/step3/input/目录下的txt文件复制到src/step3/output/目录下;
- 复制的新文件命名为txt;
- txt文件中只有8个字符。
测试说明
补充完代码后,点击测评,平台会对你编写的代码进行测试,当你的结果与预期输出一致时,即为通过。
开始你的任务吧,祝你成功!
package step3; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class Task { public void task() throws IOException{ /********* Begin *********/ String file1 = "src/step3/input/input.txt"; //定义一个字符串变量,存储输入文件的路径 FileReader fr = new FileReader(file1); //创建一个 FileReader 对象,用于读取输入文件的内容 char[] cbuf = new char[8]; //创建一个字符数组,用于存储读取到的字符 fr.read(cbuf); //调用 FileReader 的 read 方法,将输入文件的前 8 个字符读入到数组中 String file2 = "src/step3/output/output.txt"; //定义一个字符串变量,存储输出文件的路径 FileWriter fw = new FileWriter(file2); //创建一个 FileWriter 对象,用于写入输出文件的内容 fw.write(cbuf); //调用 FileWriter 的 write 方法,将字符数组中的内容写入到输出文件中 fr.close(); //关闭 FileReader 对象,释放资源 fw.flush(); //刷新 FileWriter 对象,将缓冲区中的内容写入到文件中 fw.close(); //关闭 FileWriter 对象,释放资源 /********* End *********/ } }
Java面向对象 - 类与对象-2
构造方法 (10.0分)
类与对象练习 (10.0分)
第1关:构造方法
package step2; import java.util.Scanner; public class Test { public static void main(String[] args) { Scanner sc = new Scanner(System.in); String name = sc.next(); String sex = sc.next(); /********** Begin **********/ //分别使用两种构造器来创建Person对象 Person p1 = new Person(); Person p2 = new Person(name,sex); /********** End **********/ } } //创建Person对象,并创建两种构造方法 /********** Begin **********/ class Person{ public Person(){ super(); System.out.println("一个人被创建了"); } public Person(String name,String sex){ super(); System.out.println("姓名:"+name+",性别:"+sex+",被创建了"); } } /********** End **********/
第2关:类与对象练习
package step4; //定义包名为step4 import java.util.Scanner; //导入Scanner类,用于接收用户输入 public class Test { //定义Test类,包含主方法 public static void main(String[] args) { //定义主方法,程序的入口 Scanner sc = new Scanner(System.in); //创建Scanner对象,用于接收用户输入 String theMa = sc.next(); //接收用户输入的面码 int quantity = sc.nextInt(); //接收用户输入的粉的份量 boolean likeSoup = sc.nextBoolean(); //接收用户输入的是否带汤 /********** Begin **********/ //使用三个参数的构造方法创建WuMingFen对象 取名 f1 WuMingFen f1 = new WuMingFen(theMa,quantity,likeSoup); //根据用户输入的参数,创建一个WuMingFen对象f1 //使用两个参数的构造方法创建WuMingFen对象 取名 f2 WuMingFen f2 = new WuMingFen(theMa,quantity); //根据用户输入的面码和份量,创建一个WuMingFen对象f2,likeSoup默认为false //使用无参构造方法创建WuMingFen对象 取名 f3 WuMingFen f3 = new WuMingFen(); //使用无参构造方法创建一个WuMingFen对象f3,所有属性都为默认值 f3.theMa = "酸辣"; //给f3的theMa属性赋值为"酸辣" f3.quantity = 2; //给f3的quantity属性赋值为2 f3.likeSoup = true; //给f3的likeSoup属性赋值为true //分别调用三个类的 check方法 f1.check(); //调用f1的check方法,打印出f1的属性信息 f2.check(); //调用f2的check方法,打印出f2的属性信息 f3.check(); //调用f3的check方法,打印出f3的属性信息 /********** End **********/ } }
/********** Begin **********/ //在这里添加包名 step4 package step4; //定义包名为step4 //创建类 添加属性和方法 public class WuMingFen{ //定义WuMingFen类,表示无名粉这种食物 String theMa; //定义String类型的属性theMa,表示面码 int quantity; //定义int类型的属性quantity,表示粉的份量 boolean likeSoup; //定义boolean类型的属性likeSoup,表示是否带汤 public WuMingFen(){ } //定义无参构造方法,不做任何初始化操作 public WuMingFen(String theMa,int quantity,boolean likeSoup){ //定义带三个参数的构造方法,用于初始化theMa, quantity和likeSoup属性 this.theMa = theMa; this.quantity = quantity; this.likeSoup = likeSoup; } public WuMingFen(String theMa,int quantity){ //定义带两个参数的构造方法,用于初始化theMa和quantity属性,likeSoup默认为false this.theMa = theMa; this.quantity = quantity; } public void check(){ //定义check方法,用于打印出对象的属性信息 System.out.println("面码:"+theMa+",粉的份量:"+quantity+"两,是否带汤:"+likeSoup); } } /********** End **********/
Java高级特性 - 多线程练习题
第1关:顺序输出
package step1; //定义包名为step1 public class Task { //定义Task类,包含主方法 public static void main(String[] args) throws Exception { //定义主方法,程序的入口,可能抛出异常 /********* Begin *********/ //在这里创建线程, 开启线程 Object a = new Object(); //创建一个Object对象a,作为线程间的同步锁 Object b = new Object(); //创建一个Object对象b,作为线程间的同步锁 Object c = new Object(); //创建一个Object对象c,作为线程间的同步锁 MyThread th1 = new MyThread("AA",a,c); //创建一个MyThread对象th1,传入线程名和两个同步锁对象 MyThread th2 = new MyThread("BB",c,b); //创建一个MyThread对象th2,传入线程名和两个同步锁对象 MyThread th3 = new MyThread("CC",b,a); //创建一个MyThread对象th3,传入线程名和两个同步锁对象 th1.start(); //调用th1的start方法,启动线程th1 Thread.sleep(10); //让主线程睡眠10毫秒,让出CPU时间片 th2.start(); //调用th2的start方法,启动线程th2 Thread.sleep(10); //让主线程睡眠10毫秒,让出CPU时间片 th3.start(); //调用th3的start方法,启动线程th3 Thread.sleep(10); //让主线程睡眠10毫秒,让出CPU时间片 System.exit(0); //退出程序 /********* End *********/ } } class MyThread extends Thread { //定义MyThread类,继承Thread类,表示自定义的线程类 /********* Begin *********/ String threadName; //定义String类型的属性threadName,表示线程名 Object a = null; //定义Object类型的属性a,表示同步锁对象 Object b = null; //定义Object类型的属性b,表示同步锁对象 public MyThread(String threadName,Object a,Object b) { //定义带三个参数的构造方法,用于初始化threadName, a和b属性 super(); this.threadName = threadName; this.a=a; this.b=b; } public synchronized void run() { //重写run方法,用于执行线程的任务逻辑,并加上synchronized关键字保证同步性 int count = 5; //定义一个局部变量count,表示打印次数 while(count > 0){ //使用while循环控制打印次数 synchronized (a){ //使用同步代码块锁住a对象,保证只有获得a对象锁的线程才能执行该代码块 synchronized (b){ //使用同步代码块锁住b对象,保证只有获得b对象锁的线程才能执行该代码块 System.out.println("Java Thread" + this.threadName); //打印出当前线程名 count--; //count减一 b.notify(); //唤醒在b对象上等待的其他线程(如果有) } try { a.wait(); //让当前线程在a对象上等待,并释放a对象锁(如果有) }catch (InterruptedException e){ e.printStackTrace(); } } } } /********* End *********/ }