第一天2023/11/21零钱通项目
视频 0334-0341
- 概念:面向过程编程&面向对象编程、变量&属性、过程&封装方法。
- 常见语句1
Scanner
:接收输入
Scanner scanner = new Scanner(System.in);
String key = scanner.next();
- 常见语句2
Data
:日期
Date date = new Date(); //获取当前日期
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm"); //日期时间格式化
System.out.println(sdf.format(date));
- 常见语句3
do{} while(loop)
: 默认显式一次时,loop = false
跳出循环。
常见语句4switch
:多分支条件
switch(key){
case "key_value":
break;
default:
......;
}
- 编程思想-逆向思维:1)特殊用例更容易想到、2)可扩展性,增添条件语句更简单。
项目二 坦克大战
2023/11/27 视频568-571
Java绘图技术
JFrame
画框、 JPannel
画板、 paint(Graphics g)
g画笔
调用paint()
方法的情况:
(1)组件第一次在屏幕中显示的时候;
(2)窗口最小化,然后再显示;
(3)窗口大小发生变化时。
2023/12/1 视频577/578
分析:敌 人坦克数量多,可以放到集合Vector,因为考虑多线程问题
2023/12/2 视频579-598
线程的使用-创建线程的两种方法
1 通过继承Thread
类创建线程
main 主线程,调用(对象化)继承了线程的类创建子线程Thread0x
public static void main(String[] args) throws InteruptedException{
Cat cat = new Cat();
}
注意:实现多线程的不是run,而是Thread中的start0方法,原理见方法2
class cat extends Thread {
@Override
public viod run(){
System.out.println(“miaomiao”);
Thread.sleep(1000);//线程休眠。Command+Alt+T, Surround with: try / catch
}
}
2 通过实现接口Runnable来开发线程
public static void main(String[] args){
Dog dog = new Dog();//Dog没有继承Thread,不能直接调用start();实现了Runnable
Thread thread = new Thread(dog); //1.线程接收了实现Runnable接口的dog对象
thread.start();//2.线程执行start方法...执行dog的run方法
}
class Dog implements Runnable{}
@Override
public void run(){}
因为线程实现了Runnable
public static void main(String[] args){
Tiger tiger = new tiger();
ThreadProxy threadProxy = new ThreadProxy(tiger);//1 线程代理接收了实现了Runnable的对象tiger作为target
threadProxy.start();//2 线程代理执行start方法
}
class ThreadProxy implements Runnable{
private Runnable target = null;
@Override
public void run() {
if(target != null) {
target.run();//5.target动态绑定了tiger对象,是想tiger的run方法
}
public ThreadProxy(Runnable target) {
this.target = target;
}
public void start(){//3.start方法执行start0方法
start0();
}
public void start0(){//4.start0方法执行run方法
run();
}
}
vs
实现Runnable接口方式更加适合多个线程共享一个资源的情况,避免了单继承的限制
T3 t3 = new T3("hello ");
Thread thread01 = new Thread(t3);
Thread thread02 = new Thread(t3);
thread01.start();
thread02.start();
线程常用方法
线程终止
1 自动退出:线程完成任务后
2 通知方式:使用变量控制run方法退出方式停止线程
//设置守护线程
MyDaemonThread myDaemonThread = new MyDaemonThread();
myDaemonThread.setDaemon(true);
myDaemonThread.start
线程的状态 
线程同步机制
Synchronized方法
1 同步代码块
synchronized (对象) { //得到对象的锁,才能操作同步代码
//需要被同步代码;
}
2 方法声明 同步整个方法
public synchronized void m (String name){
}
锁
根据线程同步机制,Java引入对象“互斥锁”,保证任一时刻只有一个一个线程能够访问该对象。同步会导致程序的执行效率降低。
(1)同步方法(非静态的,没有使用static修饰)的锁默认是this,也可以是其他对象(要求是同一个对象);
(2)同步方法(静态的,使用static修饰)的锁为当前类本身,当前类.class
。
步骤:1 分析上锁的代码;2选择同步代码块或同步方法;3 多个线程的所对象为同一个。
备注:
实现接口的对象中的this,多个线程是同一this,new Object是不同对象
继承Thread的对象中的this,多个线程是不同的this
释放锁
1 当前线程的同步方法、同步代码块执行结束
2 当前线程同步代码块、同步方法中遇到break、return
3 当前线程同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束
4 当前线程同步代码块、同步方法中,遇到wait()方法,线程暂停,释放锁
不会释放锁的操作:
1 线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield,暂停线程执行
2 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁。注:不推荐使用suspend()和resume()来控制线程
子弹发射
本方坦克
用户按下J键,本方坦克发射一颗子弹
1 当发射一颗子弹后,就相当于启动一个线程
2 Hero有子弹的对象,当按下J时,就启动一个发射行为(线程),让子弹不停地移动,行程一个射击效果
3 MyPanel不停地重绘子弹
4 当子弹移动到面板地边界时,就应改销毁(线程)
敌人坦克
敌人坦克可以发射子弹(有多颗)
1 在敌人坦克类,使用Vector保存多个Shot
2 当创建敌人坦克时,给敌人坦克初始化一个Shot对象,同时启动Shot
3 在绘制敌人坦克时,需要遍历敌人坦克对象Vector,绘制所有的子弹,当子弹isLive==false时,就从Vector移除
让敌人坦克可以自由移动
思路:
1 因为要求敌人坦克可以自由移动,需要将敌人坦克当作线程使用,启动线程,调用的run方法可以触发循环
2 创建时启动线程,绘制我放子弹时遍历Vector,面板上最懂5颗
功能:我方坦克在发射的子弹消亡后,才能发射新的子弹=>扩展:发射多颗子弹怎么处理
1 按下J键,判断hero的子弹是否销毁,没有销毁就不去触发shotEnemyTank,销毁触发
2 发射多颗子弹,用Vector
功能:敌人坦克可以发射多颗子弹
抽象类不能直接实例化,实例其子对象
sout简写,System.out.println();
避坑
1 视频604
问题:坦克爆炸效果,加入图片后报空指针,找不到url
java版本升级,定位资源方式发生变化,不能用Panel.class.getResource(“url”),而要用MyPanel.class.getResource(“url”),url命名至文件格式 /bomb_1.gif
2 视频605
问题:坦克被击中爆炸后,移除隐形坦克后,rerun,子弹击中坦克即卡住
遍历敌人坦克是否被击中时,循环次数不能使用固定值enemyTankSize,而要使用Vector的size,即enemyTanks.size()
可改善:
1 第一次击中敌人坦克,不会出现爆炸效果
IO流
文件
文件流
流:数据在源文件和程序内存之间的传输
输入流:数据从源文件到程序内存的路径
输出流:数据从程序内存到源文件的路径
常见文件操作
//创建文件对象
new File(String pathname) //根据路径
new File(File parent, String child)//根据父目录文件+子路径构建
new (String parent, String child)//根据父目录+子路径
//获取文件相关信息
getName
getAbsolutePath
getParent
length
exists
isFile
isDirectory
//目录的操作和文件删除
mkdir//创建一级目录
mkdirs//创建多级目录
delete//删除空目录或文件
IO流原理及流的分类
流的分类
按数据单位:字节流(8bit)二进制文件 & 字符流(按字符)文本文件
按流的角色:节点流 & 处理流/包装流
按流的方向:输入流 & 输出流
角色 | 抽象基类 | 字节流 | 字符流 |
---|---|---|---|
节点流 | 输入流 | InpuStream | Reader |
节点流 | 输出流 | OutputStream | Writer |
包装流 | Buffered~ | Buffered~ |
1 字节流
1.1 InputStream
- FileInputStram
1.2 OutputStream - FileOutputStream
- FilterOutputStream
-
- BufferedOutputStram
2 字符流
2.1 Reader
- BufferedOutputStram
- InputStreamReader
-
- FileReader
- BufferedReader
2.2 Writer - OutputStreamWriter
-
- FileWriter
- BufferedWriter
流总结
流常见操作流
- 创建流对象
- 用流对象的方法进行输入或输出
- 关闭流
相关API
(char)readData//将(int)字节转为char
new String(char[])//将char[]转换成String
new String(char[], off, len)//转换指定部分
str.toCharArray()//将String转换成char[]
常见异常处理
Alt+Enter: 抛出到方法外
try-catch-finally
try-catch: finally关闭流
FileInputStream & FileOutputStream
FileInputStream
使用FileInputStream读取hello.txt文件,并将文件内容显示到控制台
String filePath = "e:\\hello.txt";
//【1】字节读取返回int
int readData = 0;
//【2】字节数组
byte[] buf = new byte[8];
int readLen = 0;
//step1. 创建FileInputStream对象,用于读取文件
new FileInputStream(filePath).var;
//step2. 从该输入流读取字节数据
//【1】一次读取一个字节:1)若没有输入,方法将被阻止;2)若返回-1,表示读取完毕
while((readData=fileInputStream.read()) != -1){
sout((char)readData);//int字节转成char显示
}
//【2】一次读取多字节到字节数组(最多bu f.length):1)读取正常,返回实际读取的字节数;2)返回-1表示读取完毕
while ((readLen = fileInputStream.read(buf)) != -1) {
souf(new String(buf, 0, readLen));//字节数组按实际长度展示为string
}
//step3. 关闭流,释放资源
fileInputStream.close();
FileOutputStream
在a.txt文件中写入“hello, world",如果文件不存在,会创建文件(前提目录存在)
//step1. 创建FileOutputStream对象
//【1】默认覆盖方式
new FileOutputStream(filePath)
//【2】设置追加方式
new FileOutputStream(filePath, true)
//step2. 写入
fileOutputStream.write('H');//写入一个字节
fileOutputStream.write(str.getBytes());//写入字符串/多个字节,需将字符串转为字节数组
write(str.getBytes(), int off, int len);
//step3. 关闭流
fileOutputStream.close();
用字节流拷贝二进制文件(图片/音乐):FileInputStream 和 FileOutputStream
step1. 创建文件的输入流和输出流,将文件读入到程序,再将读取到的文件,写入到指定的输出流
step2. 边读边写
while((readLen = fileInputStram.read(buf)) != -1) {
fileOutputStream.write(buf, 0, readLen);
}
step3. 关闭流
FileReader & FileWriter
//FileReader相关方法
new FileReader(File/String)
read//每次读取单个字符,返回该字符,读取完毕返回-1
read(char[])//批量读取多个字符到数组,返回读取到的字符数,读取完毕返回-1
//FileWriter相关方法
new FileWriter(File/String)//覆盖模式
new FileWriter(File/String, true)//追加模式
write(int)//写入单个字符
write(char[])
write(char[], off, len)
write(string)
write(string, off, len)
FileReader
从story.txt读取内容并显示
int data = 0;
byte[] buf = new byte[8];
//step1. 创建FileReader对象
new FileReader(filePath).var
//step2. 循环读取
//【1】使用read单个字符读取
while((data = fileReader.read()) != -1){
souf((char)data);
}
//【2】字符数组读取文件
while((readLen = fileReader.read(buf)) != -1){
sout(new String(buf, 0, readLen));
}
//step3. 关闭流
fileReader.close();
FileWriter
TankGame05
1 防止坦克重叠
EnemyTank类中增加是否碰撞方法
EnemyTank设置setVector,MyPanel调用传入
2 记录玩家成绩(累计击打敌人坦克数量)
记录器Recorder
记录我方击毁地方坦克数
游戏结束,将数据写入文件(IO) myRecord.txt
3