201871010116-祁英红《面向对象程序设计(java)》第十六周学习总结
博文正文开头格式:(2分)
项目 |
内容 |
《面向对象程序设计(java)》 |
https://www.cnblogs.com/nwnu-daizh/ |
这个作业的要求在哪里 |
https://www.cnblogs.com/nwnu-daizh/p/12031970.html |
作业学习目标 |
(1) 掌握Java应用程序的打包操作; (2) 掌握线程概念; (3) 掌握线程创建的两种技术。 (4) 学习设计应用程序的GUI。
|
随笔博文正文内容包括:
第一部分:总结教材14.1-14.3知识内容(20分)
一、程序与进程的概念
1、程序是一段静态的代码,它是应用程序执行的蓝 本。
2、进程是程序的一次动态执行,它对应了从代码加载、执行至执行完毕的一个完整过程。
3、操作系统为每个进程分配一段独立的内存空间和系统资源,包括:代码数据以及堆栈等资源。每一个进程的内部数据和状态都是完全独立的。
4、多任务操作系统中,进程切换对CPU资源消耗较大。
5、一个程序至少有一个进程,一个进程至少有一个线程。
6、线程的划分尺度小于进程,使得多进程程序的并发性高。
7、进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
8、线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
9、从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
二、多线程的概念
1、多线程是进程执行过程中产生的多条执行线索。
2、线程是比进程执行更小的单位。
3、线程不能独立存在,必须存在于进程中,同一进 程的各线程间共享进程空间的数据。
4、每个线程有它自身的产生、存在和消亡的过程, 是一个动态的概念。
5、多线程意味着一个程序的多行语句可以看上去几 乎在同一时间内同时运行。
6、线程创建、销毁和切换的负荷远小于进程,又称为轻量级进程(lightweight process)。
7、Java实现多线程有两种途径:
1)创建Thread类的子类
2)在程序中定义实现Runnable接口的类
三、Java中的线程
在Java中,“线程”指两件不同的事情:
1、java.lang.Thread类的一个实例;
2、线程的执行。
在 Java程序中,有两种方法创建线程:
一是对 Thread 类进行派生并覆盖 run方法;
二是通过实现Runnable接口创建。
使用java.lang.Thread类或者java.lang.Runnable接口编写代码来定义、实例化和启动新线程。
一个Thread类实例只是一个对象,像Java中的任何其他对象一样,具有变量和方法,生死于堆上。
Java中,每个线程都有一个调用栈,即使不在程序中创建任何新的线程,线程也在后台运行着。
一个Java应用总是从main()方法开始运行,main()方法运行在一个线程内,他被称为主线程。
一旦创建一个新的线程,就产生一个新的调用栈。
线程总体分两类:用户线程和守候线程。
当所有用户线程执行完毕的时候,JVM自动关闭。但是守候线程却不独立于JVM,守候线程一般是由操作系统或者用户自己创建的。
Java线程:创建与启动
1、定义线程
扩展java.lang.Thread类。
此类中有个run()方法,应该注意其用法:public void run()
如果该线程是使用独立的Runnable运行对象构造的,则调用该Runnable对象的run方法;否则,该方法不执行任何操作并返回。
Thread的子类应该重写该方法。
2、实现java.lang.Runnable接口。
void run()
使用实现接口Runnable的对象创建一个线程时,启动该线程将导致在独立执行的线程中调用对象的run方法。
方法run的常规协定是,它可能执行任何所需的操作。
3、实例化线程
1、如果是扩展java.lang.Thread类的线程,则直接new即可。
2、如果是实现了java.lang.Runnable接口的类,则用Thread的构造方法:
[java] view plain copy
Thread(Runnabletarget)
Thread(Runnabletarget, String name)
Thread(ThreadGroupgroup, Runnable target)
Thread(ThreadGroupgroup, Runnable target, String name)
Thread(ThreadGroupgroup, Runnable target, String name, long stackSize)
其中:
Runnable target:实现了Runnable接口的类的实例。
Thread类也实现了Runnable接口,因此,从Thread类继承的类的实例也可以作为target传入这个构造方法。
四、线程状态
线程的状态转换是线程控制的基础。线程状态总的可以分为五大状态。用一个图来描述如下:
1、新状态:线程对象已经创建,还没有在其上调用start()方法。
2、可运行状态:当线程有资格运行,但调度程序还没有把它选定为运行线程时线程所处的状态。当start()方法调用时,线程首先进入可运行状态。在线程运行之后或者从阻塞、等待或睡眠状态回来后,也返回到可运行状态。
3、运行状态:线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式。
4、等待/阻塞/睡眠状态:这是线程有资格运行时它所处的状态。实际上这个三状态组合为一种,其共同点是:线程仍旧是活的,但是当前没有条件运行。换句话说,它是可运行的,但是如果某件事件出现,他可能返回到可运行状态。
5、死亡态:当线程的run()方法完成时就认为它死去。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦死亡,就不能复生。如果在一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。
五、启动线程
1、在线程的Thread对象上调用start()方法,而不是run()或者别的方法。
2、在调用start()方法之前:线程处于新状态中,新状态指有一个Thread对象,但还没有一个真正的线程。
3、在调用start()方法之后:发生了一系列复杂的事情——
4、启动新的执行线程(具有新的调用栈);
5、该线程从新状态转移到可运行状态;
6、当该线程获得机会执行时,其目标run()方法将运行。
7、注意:对Java来说,run()方法没有任何特别之处。像main()方法一样,它只是新线程知道调用的方法名称(和签名)。因此,在Runnable上或者Thread上调用run方法是合法的。但并不启动新的线程。
六、线程的终止
1、当线程的run方法执行方法体中最后一条语句后, 或者出现了在run方法中没有捕获的异常时,线 程将终止,让出CPU使用权。
2、调用interrupt()方法也可终止线程。 void interrupt() –
1)向一个线程发送一个中断请求,同时把这个线 程的“interrupted”状态置为true。
2)若该线程处于blocked 状态, 会抛出 InterruptedException。
六、测试线程是否被中断的方法
Java提供了几个用于测试线程是否被中断的方法。
* static boolean interrupted() – 检测当前线程是否已被中断, 并重置状态 “interrupted”值为false。
* boolean isInterrupted() – 检测当前线程是否已被中断, 不改变状态 “interrupted”值 。
六、守护线程
* 守护线程的惟一用途是为其他线程提供服务。例 如计时线程。
* 若JVM的运行任务只剩下守护线程时,JVM就退 出了。
* 在一个线程启动之前,调用setDaemon方法可 将线程转换为守护线程(daemon thread)。
例如: setDaemon(true);
第二部分、实验部分
实验1: 导入第13章示例程序,测试程序并进行代码注释。
测试程序1
l 在elipse IDE中调试运行教材585页程序13-1,结合程序运行结果理解程序;
l 将所生成的JAR文件移到另外一个不同的目录中,再运行该归档文件,以便确认程序是从JAR文件中,而不是从当前目录中读取的资源。
l 掌握创建JAR文件的方法;
程序代码如下:
package resource; import java.awt.*; import java.io.*; import java.net.*; import java.util.*; import javax.swing.*; /** * @version 1.41 2015-06-12 * @author Cay Horstmann */ public class ResourceTest { public static void main(String[] args) { EventQueue.invokeLater(() -> { JFrame frame = new ResourceTestFrame(); frame.setTitle("ResourceTest"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); }); } } /** * A frame that loads image and text resources. */ class ResourceTestFrame extends JFrame { private static final int DEFAULT_WIDTH = 300; private static final int DEFAULT_HEIGHT = 300;//设置组件大小 public ResourceTestFrame() { setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); //利用about.gif图像文件制作图标 //在找到ResourceTest类的地方查找about.gif文件 URL aboutURL = getClass().getResource("about.gif"); Image img = new ImageIcon(aboutURL).getImage(); setIconImage(img); JTextArea textArea = new JTextArea(); //读取about.txt文件 InputStream stream = getClass().getResourceAsStream("about.txt"); try (Scanner in = new Scanner(stream, "UTF-8")) { while (in.hasNext()) textArea.append(in.nextLine() + "\n"); } add(textArea); } }
Main-Class: resource.ResourceTest
Core Java: Fundamentals 10th Edition Cay Horstmann and Gary Cornell Copyright 漏 2016 Prentice-Hall
运行结果如下:
归档及之后运行截图:
双击打开:
创建JAR文件的方法:
一、制作只含有字节码文件的jar包
1、最简单的jar包——直接输出内容
2、含有两个类的jar包——通过调用输出内容
3、有目录结构的jar包——通过引包并调用输出内容
二、制作含有jar文件的jar包
1、两个jar包间相互调用——调用jar外的jar输出内容
2、jar包中含有jar包——调用jar内的jar输出内容
三、制作含有资源文件的jar包
1、资源文件在jar包内部——读取jar内的文件
2、资源文件在另一个jar包内部——读取另一个jar内的文件
3、资源文件在jar包外部——读取jar外的文件
测试程序2:
l 在elipse IDE中调试运行ThreadTest,结合程序运行结果理解程序;
l 掌握线程概念;
l 掌握用Thread的扩展类实现线程的方法;
l 利用Runnable接口改造程序,掌握用Runnable接口创建线程的方法。
class Lefthand extends Thread { public void run() { for(int i=0;i<=5;i++) { System.out.println("You are Students!"); try{ sleep(500); }//输出第一次后,出现休眠时间为500ms catch(InterruptedException e) { System.out.println("Lefthand error.");} } //for循环执行六次,输出6次You are Students! } } class Righthand extends Thread { public void run() { for(int i=0;i<=5;i++) { System.out.println("I am a Teacher!"); try{ sleep(300); }//输出第一次后,出现休眠时间为600ms catch(InterruptedException e) { System.out.println("Righthand error.");} }//for循环执行六次,输出6次I am a Teacher! } } public class ThreadTest { static Lefthand left; static Righthand right; public static void main(String[] args) { left=new Lefthand(); right=new Righthand(); left.start(); right.start(); } }
改后的程序代码:
class Lefthand implements Runnable { public void run() { for(int i=0;i<=5;i++) { System.out.println("You are Students!"); try{ Thread.sleep(500); } catch(InterruptedException e) { System.out.println("Lefthand error.");} } } } class Righthand implements Runnable { public void run() { for(int i=0;i<=5;i++) { System.out.println("I am a Teacher!"); try{ Thread.sleep(300); } catch(InterruptedException e) { System.out.println("Righthand error.");} } } } public class fufj { static Thread left; static Thread right; public static void main(String[] args) { Runnable rleft = new Lefthand(); Runnable rright = new Righthand(); left = new Thread(rleft); right = new Thread(rright); left.start(); right.start(); } }
运行结果如下:
一、定义线程
1、扩展java.lang.Thread类。
此类中有个run()方法,应该注意其用法:public void run()
如果该线程是使用独立的Runnable运行对象构造的,则调用该Runnable对象的run方法;否则,该方法不执行任何操作并返回。
Thread的子类应该重写该方法。
二、实现java.lang.Runnable接口。
void run()
使用实现接口Runnable的对象创建一个线程时,启动该线程将导致在独立执行的线程中调用对象的run方法。
方法run的常规协定是,它可能执行任何所需的操作。
三、实例化线程
1、如果是扩展java.lang.Thread类的线程,则直接new即可。
2、如果是实现了java.lang.Runnable接口的类,则用Thread的构造方法。
[java] view plain copy
Thread(Runnabletarget)
Thread(Runnabletarget, String name)
Thread(ThreadGroupgroup, Runnable target)
Thread(ThreadGroupgroup, Runnable target, String name)
Thread(ThreadGroupgroup, Runnable target, String name, long stackSize)
其中:
Runnable target:实现了Runnable接口的类的实例。
Thread类也实现了Runnable接口,因此,从Thread类继承的类的实例也可以作为target传入这个构造方法。
测试程序3:
l 在Elipse环境下调试教材625页程序14-1、14-2 、14-3,结合程序运行结果理解程序;
l 在Elipse环境下调试教材631页程序14-4,结合程序运行结果理解程序;
l 对比两个程序,理解线程的概念和用途;
掌握线程创建的两种技术。
程序代码如下:
package bounce; import java.awt.geom.*; /** * 弹球从矩形的边缘上移动和弹出的球 * @version 1.33 2007-05-17 * @author Cay Horstmann */ public class Ball { private static final int XSIZE = 15; private static final int YSIZE = 15; private double x = 0; private double y = 0; private double dx = 1; private double dy = 1; /** * 将球移动到下一个位置,如果球击中一个边缘,则向相反的方向移动。 */ public void move(Rectangle2D bounds) { x += dx; y += dy; if (x < bounds.getMinX()) { x = bounds.getMinX(); dx = -dx; } if (x + XSIZE >= bounds.getMaxX()) { x = bounds.getMaxX() - XSIZE; dx = -dx; } if (y < bounds.getMinY()) { y = bounds.getMinY(); dy = -dy; } if (y + YSIZE >= bounds.getMaxY()) { y = bounds.getMaxY() - YSIZE; dy = -dy; } } /** * 获取当前位置的球的形状。 */ public Ellipse2D getShape() { return new Ellipse2D.Double(x, y, XSIZE, YSIZE); } }
package bounce; import java.awt.*; import java.util.*; import javax.swing.*; /** * 拉球的部件。 * @version 1.34 2012-01-26 * @author Cay Horstmann */ public class BallComponent extends JPanel { private static final int DEFAULT_WIDTH = 450; private static final int DEFAULT_HEIGHT = 350; private java.util.List<Ball> balls = new ArrayList<>(); /** * 向组件中添加一个球。 * @param b要添加的球 */ public void add(Ball b) { balls.add(b); } public void paintComponent(Graphics g) { super.paintComponent(g); // 擦除背景 Graphics2D g2 = (Graphics2D) g; for (Ball b : balls) { g2.fill(b.getShape()); } } public Dimension getPreferredSize() { return new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT); } }
package bounce; import java.awt.*; import java.awt.event.*; import javax.swing.*; /** * 显示一个动画弹跳球。 * @version 1.34 2015-06-21 * @author Cay Horstmann */ public class Bounce { public static void main(String[] args) { EventQueue.invokeLater(() -> { JFrame frame = new BounceFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); }); } } /** * 框架与球组件和按钮。 */ class BounceFrame extends JFrame { private BallComponent comp; public static final int STEPS = 1000; public static final int DELAY = 3; /** * 用显示弹跳球的组件构造框架,以及开始和关闭按钮 */ public BounceFrame() { setTitle("Bounce"); comp = new BallComponent(); add(comp, BorderLayout.CENTER); JPanel buttonPanel = new JPanel(); addButton(buttonPanel, "Start", event -> addBall()); addButton(buttonPanel, "Close", event -> System.exit(0)); add(buttonPanel, BorderLayout.SOUTH); pack(); } /** * 向容器添加按钮。 * @param c容器 * @param title 按钮标题 * @param 监听按钮的操作监听器 */ public void addButton(Container c, String title, ActionListener listener) { JButton button = new JButton(title); c.add(button); button.addActionListener(listener); } /** * 在面板上添加一个弹跳球,使其弹跳1000次。 */ public void addBall() { try { Ball ball = new Ball(); comp.add(ball); for (int i = 1; i <= STEPS; i++) { ball.move(comp.getBounds()); comp.paint(comp.getGraphics()); Thread.sleep(DELAY); } } catch (InterruptedException e) { } } }
运行结果如下:
package bounceThread; import java.awt.geom.*; /** 弹球从矩形的边缘上移动和弹出的球 * @version 1.33 2007-05-17 * @author Cay Horstmann */ public class Ball { private static final int XSIZE = 15; private static final int YSIZE = 15; private double x = 0; private double y = 0; private double dx = 1; private double dy = 1; /** 将球移动到下一个位置,如果球击中一个边缘,则向相反的方向移动。 */ public void move(Rectangle2D bounds) { x += dx; y += dy; if (x < bounds.getMinX()) { x = bounds.getMinX(); dx = -dx; } if (x + XSIZE >= bounds.getMaxX()) { x = bounds.getMaxX() - XSIZE; dx = -dx; } if (y < bounds.getMinY()) { y = bounds.getMinY(); dy = -dy; } if (y + YSIZE >= bounds.getMaxY()) { y = bounds.getMaxY() - YSIZE; dy = -dy; } } /** 获取当前位置的球的形状。 */ public Ellipse2D getShape() { return new Ellipse2D.Double(x, y, XSIZE, YSIZE); } }
package bounceThread; import java.awt.*; import java.util.*; import javax.swing.*; /** * 拉球的部件。 * @version 1.34 2012-01-26 * @author Cay Horstmann */ public class BallComponent extends JComponent { private static final int DEFAULT_WIDTH = 450; private static final int DEFAULT_HEIGHT = 350; private java.util.List<Ball> balls = new ArrayList<>(); /** * 在面板上添加一个球。 * @param b要添加的球 */ public void add(Ball b) { balls.add(b); } public void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D) g; for (Ball b : balls) { g2.fill(b.getShape()); } } public Dimension getPreferredSize() { return new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT); } }
package bounceThread; import java.awt.*; import java.awt.event.*; import javax.swing.*; /** * 显示动画弹跳球。 * @version 1.34 2015-06-21 * @author Cay Horstmann */ public class BounceThread { public static void main(String[] args) { EventQueue.invokeLater(() -> { JFrame frame = new BounceFrame(); frame.setTitle("BounceThread"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); }); } } /** * 带有面板和按钮的框架。 */ class BounceFrame extends JFrame { private BallComponent comp; public static final int STEPS = 1000; public static final int DELAY = 5; /** * 用显示弹跳球以及开始和关闭按钮的组件构建框架 */ public BounceFrame() { comp = new BallComponent(); add(comp, BorderLayout.CENTER); JPanel buttonPanel = new JPanel(); addButton(buttonPanel, "Start", event -> addBall()); addButton(buttonPanel, "Close", event -> System.exit(0)); add(buttonPanel, BorderLayout.SOUTH); pack(); } /** * 向容器添加按钮。 * @param c 容器 * @param title 按钮标题 * @param listener 按钮的操作监听器 */ public void addButton(Container c, String title, ActionListener listener) { JButton button = new JButton(title); c.add(button); button.addActionListener(listener); } /** * 在画布上添加一个弹跳球,并启动一个线程使其弹跳 */ public void addBall() { Ball ball = new Ball(); comp.add(ball); Runnable r = () -> { try { for (int i = 1; i <= STEPS; i++) { ball.move(comp.getBounds()); comp.repaint(); Thread.sleep(DELAY); } } catch (InterruptedException e) { } }; Thread t = new Thread(r); t.start(); } }
运行结果如下:
Java提供了线程类Thread来创建多线程的程序。其实,创建线程与创建普通的类的对象的操作是一样的,而线程就是Thread类或其子类的实例对象。每个Thread对象描述了一个单独的线程。要产生一个线程,有两种方法:
1、需要从Java.lang.Thread类派生一个新的线程类,重载它的run()方法;
2、实现Runnalbe接口,重载Runnalbe接口中的run()方法。
实验2:结对编程练习:采用GUI界面设计以下程序,并创建程序归档文件。
设计一个100以内整数小学生四则运算练习程序,由计算机随机产生10道加减乘除练习题,学生输入答案,由程序检查答案是否正确,每道题正确计10分,错误不计分,10道题测试结束后给出测试总分;
将程序中测试练习题及学生答题结果输出到文件,文件名为test.txt。
编程伙伴:李华
程序代码如下:
package Test; import java.awt.*; import javax.swing.*; public class CeshiTest { public static void main(String[] args) { EventQueue.invokeLater(() -> { JFrame frame = new Ceshi(); frame.setTitle("四则运算器"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); }); } }
package Test; import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.*; import javax.swing.*; public class Ceshi extends JFrame { int i = 0, m = 0; int n = 0, sum = 0; private PrintWriter out = null; private JTextArea display; private JScrollPane text; private String[] exam = new String[50]; private String[] answer = new String[10]; public Ceshi() { JPanel panel = new JPanel(); panel.setLayout(new GridLayout(1, 3)); JTextField timu = new JTextField(); JTextField daan = new JTextField(20); JTextField panduan = new JTextField(); panel.add(timu); panel.add(daan); panel.add(panduan); add(panel, BorderLayout.NORTH); JPanel panel1 = new JPanel(); panel1.setLayout(new GridLayout(1, 3)); Button button1 = new Button("生成题目"); Button button2 = new Button("判断正误"); Button button3 = new Button("生成文件"); panel1.add(button1); panel1.add(button2); panel1.add(button3); add(panel1, BorderLayout.SOUTH); setSize(500, 400); button1.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { daan.setText(null); panduan.setText(null); if (i < 10) { int a = (int) (Math.random() * 100); int b = (int) (Math.random() * 100); //随机产生100以内整数 int m = (int) Math.round(Math.random() * 3); switch (m) { case 0: while (b == 0 || a % b != 0) { b = (int) Math.round(Math.random() * 100); a = (int) Math.round(Math.random() * 100); //100以内整数 } timu.setText("第" +(i+1)+"道" + ": " + a + "/" + b + "="); exam[i] = timu.getText(); n = a / b; i++; break; case 1: timu.setText("第" +(i+1)+"道" + ": " + a + "*" + b + "="); exam[i] = timu.getText(); n = a * b; i++; break; case 2: timu.setText("第" +(i+1)+"道" + ": " + a + "+" + b + "="); exam[i] = timu.getText(); n = a + b; i++; break; case 3: while (a < b) { int x = a; a = b; b = x; } timu.setText("第" +(i+1)+"道" + ": " + a + "-" + b + "="); exam[i] = timu.getText(); n = a - b; i++; break; } } } }); button2.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (i < 11) { int input = Integer.parseInt(daan.getText());//用户输入计算答案 String d =daan.getText().toString().trim();//输入答案 if (daan.getText() != " ") { if (input == n) { sum += 10; display.append("当前成绩为" + sum+"\n"); panduan.setText("答案正确"); } else display.append("没有成绩"+ "\n"); panduan.setText("答案错误"); } answer[m] = d; m++; } } }); button3.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { try { out = new PrintWriter("C:\\Users\\祁英红\\Desktop\\text.txt"); } catch (FileNotFoundException e1) { // TODO Auto-generated catch block e1.printStackTrace();//输出路径 } for (int counter = 0; counter < 10; counter++) { out.write(exam[counter] + answer[counter]+"\n"); } out.println("最终运算成绩为" + sum); out.close(); } }); display=new JTextArea("运算结果显示区:",60,10); text= new JScrollPane(display); display.setText(""); display.append("当前运算成绩为" + sum+ "\n"); System.out.println(sum); add(text,BorderLayout.CENTER); } }
运行结果如下:
创建程序归档文件:
打开后:
实验总结:(15分)
通过本周的学习,我掌握了线程概念;在学习过程中,了解了线程创建的两种技术:用Thread类的子类创建线程以及用Runnable()接口实现线程。理解和掌握了基础线程的优先级属性及调度方法;学会了Java GUI 编程技术的基础。本章知识是Java的重点同时也为以后操作系统的学习做了一个铺垫,因此需要更加重视,在以后的学习中会继续巩固学习。