//2023.06.28 Java期末复习课程 1.写出程序运行结果 class A{ public A(){ System.out.println("Hello!"); } public A(String s){ this(); System.out.println("My name is"+s); } } class B extends A{ public static void main(String[] args){ B b=new B("Amy"); } public B(){ System.out.println("I am Amy."); } public B(String s){ super(s); System.out.println("How are you!"); } } 程序运行结果如下: Hello! My name is Amy How are you! 首先,程序从B类的主方法开始执行。在主方法中,创建了一个B类的对象b,并调用了带有一个String类型参数的构造方法,参数为"Amy"。由于B类继承自A类,因此构造B类对象时会先调用父类A的构造方法。 在A类的构造方法中,首先打印输出"Hello!"。接着,B类的构造方法调用了父类A的带有一个String类型参数的构造方法,这里传入了"Amy"作为参数。在A类的带有一个String类型参数的构造方法中,先调用了A类的无参构造方法(this()),所以又会打印输出"Hello!"。然后,打印输出"My name is Amy"。 接着,回到B类的构造方法,继续执行后续的代码。在B类的构造方法中,打印输出"I am Amy."。最后,打印输出"How are you!"。 因此,程序运行结果为: Hello! My name is Amy How are you! ******************************************************************************************************* 2.下列try catch语句块中的第二个语句S1将引起一个异常,试回答下列问题。 try{ S1; S2; }catch(ExceptionType1 e){ }catch(ExceptionType2 e){ }finally{ S3; } S4 (1)S2会执行吗? (2)如果异常未被捕获,S3会被执行吗?S4会被执行吗? (3)如果catch子句捕获了异常,S3会被执行吗?S4会被执行吗? (1) S2不会执行。由于S1引发了异常,程序将会跳转到catch子句,而不会继续执行后面的语句。 (2) 如果异常未被捕获,S3会被执行。无论是否发生异常,finally块中的代码都会被执行。但是,S4不会被执行,因为异常发生后程序会跳出try-catch-finally语句块。 (3) 如果catch子句捕获了异常,S3会被执行。无论是否发生异常,finally块中的代码都会被执行。S4也会被执行,因为异常已经被处理并且程序会继续执行后面的语句。 如果异常未被捕获,程序会跳转到catch子句之前的位置,不会执行后面的语句,但finally块中的代码仍会被执行。 如果catch子句捕获了异常,程序会继续执行catch子句之后的语句,包括finally块中的代码。 ******************************************************************************************************* 3.下列代码简单模拟了多个窗口购买火车票,会出现下图问题,请问代码如何修改?(提示:synchronized) class BuyTicket implements Runnable{ int ticketnum=10;//共有十张宁波到北京的火车票 public void run{ for(int i=1;i<=20;i++){//每个窗口有20人在排队买票 if(ticketnum>0)//票数大于0,买票 System.out.println(Thread.currentThread().getName()+"买到从宁波到北京的第"+ticketnum--+"张车票"); } } } public class Test{ public static void main(String[] args){ BuyTicket t=new BuyTicket(); Thread t1=new Thread(t,"1号窗口"); t1.start(); Thread t2=new Thread(t,"2号窗口"); t2.start(); Thread t3=new Thread(t,"3号窗口"); t3.start(); } } 问题出在多个线程同时访问ticketnum变量时可能会出现竞争条件,导致数据不一致。为了解决这个问题,可以使用synchronized关键字来保证多个线程访问ticketnum变量时的同步性。 修改后的代码如下: class BuyTicket implements Runnable{ private int ticketnum=10;//共有十张宁波到北京的火车票 public synchronized void run(){ for(int i=1;i<=20;i++){//每个窗口有20人在排队买票 if(ticketnum>0){//票数大于0,买票 System.out.println(Thread.currentThread().getName()+"买到从宁波到北京的第"+ticketnum--+"张车票"); } } } } public class Test{ public static void main(String[] args){ BuyTicket t=new BuyTicket(); Thread t1=new Thread(t,"1号窗口"); t1.start(); Thread t2=new Thread(t,"2号窗口"); t2.start(); Thread t3=new Thread(t,"3号窗口"); t3.start(); } } 在BuyTicket类的run方法上加上synchronized关键字,这样就可以保证在多线程环境下只有一个线程能够访问run方法,从而避免了多个线程同时修改ticketnum的问题。这样可以保证每个窗口买票时的操作是互斥的,不会出现问题。 在BuyTicket类的run方法上添加了synchronized关键字,这样每个窗口在执行run方法时都会获得对象锁,保证了同一时间只有一个窗口在买票。这样就避免了多个窗口同时买同一张票的问题。 ******************************************************************************************************* 4.阅读以下代码片段,回答问题 import java.awt.event.*; import javax.swing.*; import java.awt.*; class EventTest extends JFrame implements ActionListener/*a*/{ JButton btn1,btn2;Container ctpn; EventTest(){ ctpn=this.getContentPane();//b btn1=new JButton("Blue"); btn2=new JButton("Red"); btn1.addActionListener(this);//c btn2.addActionListener(this); this.setTitle("Action Event");setSize(200,150); this.setLayout(new FlowLayout(FlowLayout.CENTER)); //d this.ctpn.add(btn1);ctpn.add(btn2); this.setVisible(true); } public void actionPerformed(ActionEvent e){ JButton btn=(JButton)e.getSource();//e if(btn==btn1) ctpn.setBackground(Color.blue); if(btn==btn2) ctpn.setBackground(Color.red); } public static void main(String args[]){ EventTest frm=new EventTest(); } } (1)在"//"后添加注释 (2)画出程序的运行效果,并说程序完成的功能 (1) 在"//"后添加注释: import java.awt.event.*; // 导入事件相关的包 import javax.swing.*; // 导入Swing相关的包 import java.awt.*; // 导入AWT相关的包 class EventTest extends JFrame implements ActionListener{ //a 继承JFrame类,并实现ActionListener接口,ActionListener接口是一个事件监听器接口,用于处理按钮点击事件 JButton btn1,btn2; // 创建两个按钮对象 Container ctpn; // 创建一个容器对象 EventTest(){ ctpn=this.getContentPane();//b getContentPane()方法用于获取容器的内容面板。 btn1=new JButton("Blue"); // 创建名为"Blue"的按钮 btn2=new JButton("Red"); // 创建名为"Red"的按钮 btn1.addActionListener(this); //c 为按钮添加事件监听器,addActionListener(this)方法将当前类实例作为按钮的事件监听器 btn2.addActionListener(this); //c 为按钮添加事件监听器 this.setTitle("Action Event"); // 设置窗口标题为"Action Event" setSize(200,150); // 设置窗口大小为200x150像素 this.setLayout(new FlowLayout(FlowLayout.CENTER)); //d 设置布局为居中对齐的流式布局 this.ctpn.add(btn1); // 将"Blue"按钮添加到容器中 ctpn.add(btn2); // 将"Red"按钮添加到容器中 this.setVisible(true); // 设置窗口可见 } public void actionPerformed(ActionEvent e){ JButton btn=(JButton)e.getSource(); //e 获取触发事件的按钮对象 if(btn==btn1) ctpn.setBackground(Color.blue); // 如果是"Blue"按钮触发的事件,将容器背景设置为蓝色 if(btn==btn2) ctpn.setBackground(Color.red); // 如果是"Red"按钮触发的事件,将容器背景设置为红色 } public static void main(String args[]){ EventTest frm=new EventTest(); // 创建EventTest对象 } } (2) 程序的运行效果: 程序创建了一个窗口,窗口标题为"Action Event",大小为200x150像素。窗口中有两个按钮,一个是"Blue"按钮,另一个是"Red"按钮。当点击"Blue"按钮时,窗口的背景颜色会变为蓝色;当点击"Red"按钮时,窗口的背景颜色会变为红色。 程序完成的功能是:该程序演示了如何使用Java的事件处理机制,通过按钮的事件监听器,实现了当点击不同的按钮时,改变窗口的背景颜色。 5.下列代码完成了邮箱系统的登陆功能,请在注释位置编写相应代码,写出运行结果。相关信息和Java帮助如下: User表:id username password 1 zhangsan 123 2 lisi 234 数据库操作类已实现,大纲视图如下: JDBCUtils url:String user:String password:String driver:String {...} getConnection():Connection close(Statement,Connection):void close(ResultSet,Statement,Connection):void 接口Connection下的方法: Statement createStatement() 创建一个Statement对象来将SQL语句发送到数据库 接口Statement下的方法: ResultSet executeQuery(String sql) 执行给定的SQL语句,该语句返回单个ResultSet对象。 接口ResultSet下的方法: boolean next() 将光标从当前位置先前移一行。 import java.sql.*; public class LoginTest{ public static void main(String[] args){ String username="lisi"; String pasasword="123"; boolean flag=new LoginTest().login(username,password); if(flag) System.out.println("登陆成功"); else System.out.println("用户名或密码错误"); } public boolean login(String username,String password){ if(username==null||password==null){ return false; } Connection conn=null;PreparedStatement pstmt=null;ResultSet rs=null; try{ conn=JDBCUtils.getConnection(); //添加数据库操作代码 }catch(SQLException e){ e.printStackTrace(); }finally{ JDBCUtils.close(rs,pstmt,conn); } return false; } } answer: pstmt = conn.prepareStatement("SELECT * FROM User WHERE username = ? AND password = ?"); pstmt.setString(1, username); pstmt.setString(2, password); rs = pstmt.executeQuery(); if(rs.next()){ return true; // 用户名和密码匹配,登录成功 } import java.sql.*; public class LoginTest{ public static void main(String[] args){ String username="lisi"; String password="123"; boolean flag=new LoginTest().login(username,password); if(flag) System.out.println("登陆成功"); else System.out.println("用户名或密码错误"); } public boolean login(String username,String password){ if(username==null||password==null){ return false; } Connection conn=null;PreparedStatement pstmt=null;ResultSet rs=null; try{ conn=JDBCUtils.getConnection(); pstmt = conn.prepareStatement("SELECT * FROM User WHERE username = ? AND password = ?"); pstmt.setString(1, username); pstmt.setString(2, password); rs = pstmt.executeQuery(); if(rs.next()){ return true; } }catch(SQLException e){ e.printStackTrace(); }finally{ JDBCUtils.close(rs,pstmt,conn); } return false; } } 运行结果: 用户名或密码错误 程序通过调用login方法进行登录验证,传入用户名和密码参数。在login方法中,首先判断用户名和密码是否为空,如果为空则直接返回false。然后获取数据库连接,通过PreparedStatement对象执行SQL语句查询用户表,使用占位符设置用户名和密码参数。如果查询结果存在,则返回true表示登录成功,否则返回false表示用户名或密码错误。 在main方法中,调用login方法并根据返回结果输出相应的提示信息。在本例中,用户名为"lisi",密码为"123",所以输出结果为"用户名或密码错误"。 6.下面有两个Java源文件(Lute.java和Music.java),阅读程序,回答问题。 a:Lute.java package mypack; interface Instrument{//乐器 int NUM=5; void play(); String what(); void adjust(); } class Wind implements Instrument{//管乐器 public void play(){ System.out.println("Wind.play()"); } public String what() {return "Wind";} public void adjust() {} } class Stringed{//有弦乐器 int StringNum; Stringed(int num){ this.StringNum=num; } } public class Lute extends Stringed implements Instruments{//古琵琶 public Lute(int num){ super(num); } public void play(){ System.out.println("lute.play()"); } public String what() {return "lute"}; public void adjust() {} } b.Music.java import mypack.* public class Music{ public static void main(String[] args){ Lute test=new Lute(6); test.play(); } } (1)以上代码分别有哪些接口、类,在哪些包中,有公共类吗? (2)类Wind中的adjust()方法是抽象方法吗?能省略吗?为什么? (3)类Lute和类Stringed是什么关系?Lute类中有成员变量吗? (4)类Lute中的方法是public修饰符能省略吗?为什么? (5)请写出程序的运行结果。 (1)以上代码接口有"Instrument",类有'Wind','Stringed','Lute','Music',都在'mypack'包中。 在'Lute.java'中有一个公共类'Lute'。在'Music.java'文件中有一个公共类'Music'。 (2)类Wind中的adjust()方法不是抽象方法。它不能省略。如果省略adjust()方法的实现,则Wind类必须声明为抽象类。 (3)类Lute是类Stringed的子类。它们之间是继承关系。Lute类中有一个名为StringNum的成员变量。 (4) 类Lute中的方法的修饰符是public,可以省略。这是因为Lute类实现了Instrument接口,在接口中的所有方法都是公共的,因此在实现接口方法时,默认的访问修饰符是public。 (5) 程序的运行结果是:lute.play() 二.程序设计题 1.“具有报警功能的移门”,针对上述描述,请用类、接口等面向对象知识进行设计实现,方法中,不必写出具体实现代码,用System.out.println()。 根据描述,我们可以设计以下类和接口来实现具有报警功能的移门: 1. 接口:Alarm - 方法:void alarm(),用于触发报警功能。 2. 类:Door - 属性:isOpen(表示门的状态,true表示门开着,false表示门关着) - 方法:void open(),用于打开门 - 方法:void close(),用于关闭门 - 方法:void alarm(),实现Alarm接口中的方法,用于触发报警功能 - 方法:void move(),用于移动门的位置 - 方法:void displayStatus(),用于显示门的当前状态 设计实现如下: interface Alarm { void alarm(); } class Door implements Alarm { private boolean isOpen; public void open() { isOpen = true; System.out.println("门已打开"); } public void close() { isOpen = false; System.out.println("门已关闭"); } public void alarm() { System.out.println("门正在报警"); } public void move() { System.out.println("门正在移动"); } public void displayStatus() { System.out.println("门的状态:" + (isOpen ? "开着" : "关着")); } } 这样设计的类和接口可以实现具有报警功能的移门。Door类实现了Alarm接口,并实现了alarm()方法,用于触发报警功能。同时,Door类还具有打开门、关闭门、移动门、显示门状态的功能。 2.数据可视化在统计报表有重要应用,柱状图以及折线图等图形是以不同视角展示数据。假设对于同一份二维数据,系统要求根据不同情景,展示不同图形(柱形图或折线图),且数据变化,图形自动变化(刷新),而且,需要预留接口,以方便需求升级,如同一份数据还支持饼图。为了满足以上需求,请设计类系,并写一个测试类,进行模拟。具体要求: (a) 设计一个数据类,且有一份订阅者列表,可具备对订阅者(图形)列表的管理功能(删除与增加),当数据变化时,自动触发订阅者更新图形。 (b) 设计一个图形的抽象类,具备根据数据绘制图形的功能,并具备订阅数据和取消订阅的功能。 (c) 写一个测试类,模拟上述过程。 备注:方法中,不必写出具体实现代码,用System.out.println()说明即可。 根据需求,我们可以设计以下类来满足要求: 1. 数据类:Data - 属性:subscribers(订阅者列表) - 方法:addSubscriber(Subscriber subscriber),用于添加订阅者 - 方法:removeSubscriber(Subscriber subscriber),用于移除订阅者 - 方法:updateData(),用于更新数据并通知订阅者 2. 抽象类:Graph - 属性:data(数据对象) - 方法:subscribe(),用于订阅数据 - 方法:unsubscribe(),用于取消订阅数据 - 抽象方法:draw(),用于根据数据绘制图形 3. 具体图形类:BarGraph(柱状图)和LineGraph(折线图) - 方法:draw(),实现Graph抽象类中的draw()方法,根据数据绘制柱状图或折线图 设计实现如下: import java.util.ArrayList; import java.util.List; // 数据类 class Data { private List subscribers; public Data() { subscribers = new ArrayList<>(); } public void addSubscriber(Graph subscriber) { subscribers.add(subscriber); } public void removeSubscriber(Graph subscriber) { subscribers.remove(subscriber); } public void updateData() { // 更新数据 System.out.println("数据更新"); // 通知订阅者更新图形 for (Graph subscriber : subscribers) { subscriber.draw(); } } } // 抽象图形类 abstract class Graph { protected Data data; public Graph(Data data) { this.data = data; } public void subscribe() { data.addSubscriber(this); } public void unsubscribe() { data.removeSubscriber(this); } public abstract void draw(); } // 具体图形类:柱状图 class BarGraph extends Graph { public BarGraph(Data data) { super(data); } public void draw() { System.out.println("绘制柱状图"); } } // 具体图形类:折线图 class LineGraph extends Graph { public LineGraph(Data data) { super(data); } public void draw() { System.out.println("绘制折线图"); } } // 测试类 public class Test { public static void main(String[] args) { // 创建数据对象 Data data = new Data(); // 创建柱状图和折线图 Graph barGraph = new BarGraph(data); Graph lineGraph = new LineGraph(data); // 订阅数据 barGraph.subscribe(); lineGraph.subscribe(); // 更新数据,图形自动变化 data.updateData(); // 取消订阅折线图 lineGraph.unsubscribe(); // 更新数据,只有柱状图会变化 data.updateData(); } } 根据需求设计了数据类(Data)、抽象图形类(Graph)以及具体图形类(BarGraph和LineGraph)。测试类(Test)中模拟了订阅数据、更新数据以及取消订阅的过程。 测试类中先创建了数据对象(Data),然后创建了柱状图(BarGraph)和折线图(LineGraph)。接着订阅了数据,然后更新数据,可以看到柱状图和折线图都会自动绘制图 形。最后取消了对折线图的订阅,再次更新数据时,只有柱状图会变化。 3.当前,在我国,不同的企业类型有不同的税收计算方法,假设有高新企业、外资企业、普通企业。现有税收系统,可计算每一类企业的收税,也可自动计算某个地区(如鄞州区)的税收,一般地,某个地区的企业都有不同类型的企业存在。为了满足以上需求,请设计类系,并写一个测试类,进行模拟。具体要求: (a) 设计一抽象类:企业,具备计算税收的功能。 (b) 高新企业、外资企业、普通企业应该是企业的子类,都具有计算税收的功能。 (c) 设计一个地区类,它是由不同类型的企业构成,具备计算地区税收的功能。 (d) 写一个测试类,进行模拟。 备注:方法中,不必写出具体实现代码,用System.out.println(),或者直接返回一个值进行说明即可。提示:抽象类、方法重写、泛型等的应用。 根据需求,我们可以设计以下类来满足要求: 1. 抽象类:Enterprise(企业) - 方法:calculateTax(),用于计算税收 2. 子类:HighTechEnterprise(高新企业)、ForeignEnterprise(外资企业)、NormalEnterprise(普通企业) - 方法:calculateTax(),重写父类的计算税收方法 3. 类:Region(地区) - 属性:enterprises(不同类型的企业列表) - 方法:addEnterprise(Enterprise enterprise),用于添加企业 - 方法:removeEnterprise(Enterprise enterprise),用于移除企业 - 方法:calculateRegionTax(),用于计算地区税收 设计实现如下: abstract class Enterprise { public abstract double calculateTax(); } class HighTechEnterprise extends Enterprise { public double calculateTax() { System.out.println("计算高新企业的税收"); return 0.0; } } class ForeignEnterprise extends Enterprise { public double calculateTax() { System.out.println("计算外资企业的税收"); return 0.0; } } class NormalEnterprise extends Enterprise { public double calculateTax() { System.out.println("计算普通企业的税收"); return 0.0; } } class Region { private List enterprises; public Region() { enterprises = new ArrayList<>(); } public void addEnterprise(Enterprise enterprise) { enterprises.add(enterprise); } public void removeEnterprise(Enterprise enterprise) { enterprises.remove(enterprise); } public double calculateRegionTax() { double regionTax = 0.0; for (Enterprise enterprise : enterprises) { regionTax += enterprise.calculateTax(); } return regionTax; } } public class Test { public static void main(String[] args) { // 创建地区对象 Region region = new Region(); // 添加不同类型的企业 region.addEnterprise(new HighTechEnterprise()); region.addEnterprise(new ForeignEnterprise()); region.addEnterprise(new NormalEnterprise()); // 计算地区税收 double regionTax = region.calculateRegionTax(); System.out.println("地区税收:" + regionTax); } } 根据需求设计了抽象类Enterprise、子类HighTechEnterprise、ForeignEnterprise和NormalEnterprise,以及类Region。测试类(Test)中模拟了创建地区对象、添加不同类型的企业、计算地区税收的过程。 测试类中先创建了地区对象(Region),然后添加了高新企业(HighTechEnterprise)、外资企业(ForeignEnterprise)和普通企业(NormalEnterprise)。接着计算地区税收,可以看到会调用各个企业子类的计算税收方法,并计算出地区的总税收。最后将地区税收输出。