p569-591
坦克大战!(绘图+监听事件+线程+文件处理)
绘图
绘图原理
Component类提供了两个和绘图相关最重要的方法:
1. paint(Graphics g)绘制组件的外观
2. repaint()刷新组件的外观。
当组件第一次在屏幕显示的时候,程序会自动的调用paint()方法来绘制组件。
在以下情况paint0将会被调用:
1.窗口最小化再最大化
2窗口的大小发生变化
3. repaint方法被调用
绘图方法
Graphics类
Graphics类你可以理解就是画笔,为我们提供了各种绘制图形的方法:[参考jdk帮助文档]
1.画直线drawLine(int x1,int y1,int x2.int y2)
2.画矩形边框drawRect(int x, int y, int width, int height)
3.画椭圆边框drawOval(int x, int y, int width, int height)
4.填充矩形 fillRect(int x, int y, int width, int height)
5.填充椭圆fillOval(int x, int y, int width, int height)
6.画图片drawlmage(lmage img, int x, int y., ..)
7.画字符串drawString(String str, int x, int y)
8.设置画笔的字体setFont(Font font)
9设置画笔的颜色setColor(Color c)
使用代码:
class MyPanel extends JPanel{//panel是画板@Overridepublic void paint(Graphics g) {//Graphics的g是画笔super.paint(g);//调用父类方法完成初始化。?初始化了谁?// g.drawOval(10,10,150,125);//画圆(椭圆// g.drawLine(10,10,100,100);//直线//g.drawRect(10,10,100,100);//矩形// g.setColor(Color.BLUE);//设置画笔颜色//g.fillRect(10,10,100,100);//填充矩形//g.setColor(Color.RED);//设置为红色//g.fillOval(10,10,100,100);//填充圆// Image im1 = Toolkit.getDefaultToolkit().getImage("D:\\madake\\02.jpg");//加载图片//g.drawImage(im1,10,10,175,221,this );//卧槽成功了!//g.setColor(Color.RED);//画字符串,设置字体等//g.setFont(new Font("隶书",Font.BOLD,50));//字体和颜色//g.drawString("四斋蒸鹅心",100,100);//小细节,这个100,100是字符串的左下角} }
画坦克
package com.day28.TankGame; import javax.swing.JPanel; import java.awt.*;public class MyPanel extends JPanel {Mytank hero = null;public MyPanel(){hero = new Mytank(100,100);//初始化自己坦克的位置}@Overridepublic void paint(Graphics g) {super.paint(g);g.fillRect(0,0,1000,750);//专门写一个方法来画坦克drawTank(hero.getX(),hero.getY(),g,0,0);drawTank(hero.getX()+60,hero.getY(),g,0,1);}/**** @param x 坦克的左上角坐标* @param y 坦克右上角坐标* @param g 画笔* @param direct 方向* @param type 坦克类型(敌人的还是我的?*/public void drawTank(int x,int y,Graphics g,int direct,int type){switch (type){case 0://我的坦克g.setColor(Color.cyan);break;case 1://敌人的坦克g.setColor(Color.yellow);break;}//根据坦克的方向来绘制switch (direct){case 0:{Graphics2D g2d = (Graphics2D) g;g2d.setStroke(new BasicStroke(5));g.fill3DRect(x,y,10,60,false);//左轮子g.fill3DRect(x+30,y,10,60,false);//右轮子g.fill3DRect(x+10,y+10,20,40,false);//身体g.fillOval(x+10,y+20,20,20);//炮台g.drawLine(x+20,y,x+20,y+30);//炮break;}default:System.out.println("其他情况暂未处理!");}} }
事件处理机制:移动控制
监听键盘KeyListener
java事件处理是采取"委派事件模型"。当事件发生时,产生事件的对象,会把此"信息”传递·给"事件的监听者"处理,这里所说的“信息"实际上就是java.awt.event 事件类库里某个类所创建的对象,把它称为“事件的对象"。
1.前面我们提到几个重要的概念事件源,事件,事件监听器我们下面来全面的介绍它们。
2事件源:事件源是一个产生事件的对象,比如按钮,窗口等。
3.事件:事件就是承载事件源状态改变时的对象,比如当键盘事件、鼠标事件、窗口事件等等,会生成一个事件对象,该对象保存着当前事件很多信息,比如KeyEvent对象有含有被按下键的Cod值。java.awt.event包和javax.swing.event包中定义了各种事件类型
5.事件监听器接口:
⑴当事件源产生一个事件,可以传送给事件监听者处理
(2)事件监听者实际上就是一个类,该类实现了某个事件监听器接口比如前面我们案例中的MyPanle就是一个类,它实现了KeyListener接口,它就可以作为一个事件监听者,对接受到的事件进行处理
(3)事件监听器接口有多种,不同的事件监听器接口可以监听不同的事件,一个类可以实现多个监听接口
(4)这些接口在java.awt.event包和javax.swing.event包中定义。列出常用的事件监听器接口,查看jdk文档聚集了.
让坦克动起来
动了
请大家在HspTankGame02.java基础上画出三辆敌人的坦克,注意颜色。如图所示·分析:
1.因为敌人的坦克,是在MyPanel上,所以我们的代码在MyPanel
2.因为敌人的坦克,后面有自己特殊的属性和方法,可以单开一个EnemyTank
3.敌人坦克数量多,可以放入到集合Vector ,因为考虑多线程问题
线程基础
进程
1.进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为
该进程分配内存空间。当我们使用迅雷,又启动了一个进程,操作系统将为迅雷分配新的内存空间。
2.进程是程序的一次执行过程,或是正在运行的一个程序。是动态过程:有它自身的
产生、存在和消亡的过程
惊吓
什么是线程
1.线程由进程创建的,是进程的一个实体
2一个进程可以拥有多个线程,如“我使用迅雷的时候,可以同时下载多个文件,每一个下载任务就对应一个线程”。
3.单线程:同一个时刻,只允许执行一个线程
4.多线程:同一个时刻,可以执行多个线程,比如:一个qq进程,可以同时打开多个聊天窗口,一个迅雷进程,可以同时下载多个文件
5.并发:同一个时刻,多个任务交替执行,造成一种“貌似同时”的错觉,简单的说,单核cpu实现的多任务就是并发。
4.并行:同一个时刻,多个任务同时执行。多核cpu可以实现并行。
比如这个电脑就是8核cpu
Thread类
1.当一个类继承了Thread类,该类就可以当做一个线程。
2.我们通常会重写run方法,写上自己的业务代码。
3. run Thread类实现了Runnable接口的run方法
线程源码追踪
1.当我们点run的时候就进入一个进程
2.进程启动后就开启一个线程
System.out.println("喵喵喵~"+i+"次"+Thread.currentThread().getName());//这是线程名
3.当Cat.start()开始时,主线程不会阻塞,会继续执行。(间隔1000ms)
所以你们单核cpu就并发,多核就并行?
4.JConsole可以监控线程执行的情况
public class Cup {public static void main(String[] args) {Cat cat = new Cat();cat.start();//启动线程,我调用这个的时候就会调用run,为啥不直接run?//因为run只是个普通的方法,并不会真正启动线程。这样就会把run执行完毕才向下。//相当于它是main的一部分,在main线程里。for (int i = 0; i < 60; i++) {System.out.println("主线程!"+i+Thread.currentThread().getName());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}class Cat extends Thread{@Overridepublic void run() {int i =0;while(i++<65){System.out.println("喵喵喵~"+i+"次"+Thread.currentThread().getName());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
最后都涉及到操作系统了。
线程的基本使用
.1. java是单继承的,在某些情况下一个类可能已经继承了某个父类,这时在用继承Thread类方法来创建线程显然不可能了。
2. java设计者们提供了另外一个方式创建线程,就是通过实现Runnable接口来创建线程
public class Xianche {public static void main(String[] args) {Dog dog = new Dog();Thread t = new Thread(dog);//把狗放进去,静态代理。t.start();}
}class Dog implements Runnable{int count =0;@Overridepublic void run() {while(count++<10){System.out.println("狗叫!");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
继承Thread vs 实现Runnable的区别
1.从java的设计来看,通过继承Thread或者实现Runnasle接口来创建线程本质上没有区别,从jdk帮助文档我们可以看到Thread类本身就实现了Runnable接口
2.实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制
线程终止
基本说明
1.当线程完成任务后,会自动退出。
2.还可以通过使用变量来控制run方法退出的方式停止线程,即通知方式
说人话就是,再main线程里写个控制 小线程的loop的语句
同时,小线程的loop默认为true,放在while里
线程常用方法
常用方法第一组
1.setName //设置线程名称,使之与参数name相同
2. getName //返回该线程的名称
3.start//使该线程开始执行;Java虚拟机底层调用该线程的startO 方法
4.run//调用线程对象 run方法;
5.setPriority //更改线程的优先级
6.getPriority //获取线程的优先级
7.sleep1/在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
8. interrupt //中断线程
注意事项和细节
1.start底层会创建新的线程,调用run,run 就是一个简单的方法调用,不会启动新线程
2.线程优先级的范围:max,min,nom
3. interrupt,中断线程,但并没有真正的结束线程。所以一般用于中断正在休眠线程4.sleep:线程的静态方法,使当前线程休眠。
常用方法第二组
1. yield:线程的礼让。让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功
2. join:线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务
案例:创建一个子线程,每隔1s输出hello,输出20次,主线程每隔1秒,输出hi,输出20次.要求:两个线程同时执行,当主线程输出5次后,就让子线程运行完毕,主线程再继续