笔记
- 生产者消费者代码案
- 线程的生命周期
- File类
多线程&IO
- 今日内容
- 生产者与消费者案例
- 线程通信方法
- 生产者消费者案例优化
- 线程生命周期
- File类构造器
- File类常用方法
- File类目录遍历
第一章 生产者与消费者
1.1 案例介绍
生产者与消费者案例:角色一个生产者(线程),一个消费者(线程),针对于同样的一个资源数据,进行不同方向的操作。最终要达到的目的:生产一个消费一个,不能一次生产多个,也不能一次消费多个。实现的效果:生产1个,消费1个,生产2个,消费2个…
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GQBxtCsw-1682346063120)(imgs\05131.png)]
1.2 案例实现
- 生产和消费共用一个对象
/**
* 资源对象,定义产品的计数器
*/
public class Resource {
int count;//定义产品的计数器 default = 0
}
/**
* 生产者线程,负责对资源对象中计数器++操作
*/
public class Produce implements Runnable{
//创建资源对象
private Resource r ;
public Produce(Resource r) {
this.r = r;
}
@Override
public void run() {
while (true){
r.count++;
System.out.println("生产第"+r.count+"个");
}
}
}
/**
* 消费者线程,负责将资源对象中的计数器输出打印
*/
public class Customer implements Runnable{
//创建资源对象
private Resource r ;
public Customer(Resource r) {
this.r = r;
}
@Override
public void run() {
while (true){
//输出资源对象中的计数器
System.out.println("消费第"+r.count+"个");
}
}
}
public static void main(String[] args) {
//创建资源对象
Resource r = new Resource();
//开启生产者线程和消费者线程
Runnable product = new Produce(r);
Runnable customer = new Customer(r);
new Thread(product).start();//开启生产者线程
new Thread(customer).start();//开启消费者线程
}
- 数据安全加入同步锁,保证同步对象锁唯一的
/**
* 生产者线程,负责对资源对象中计数器++操作
*/
public class Produce implements Runnable{
//创建资源对象
private Resource r ;
public Produce(Resource r) {
this.r = r;
}
@Override
public void run() {
while (true){
synchronized (r) {
r.count++;
System.out.println("生产第" + r.count + "个");
}
}
}
}
/**
* 消费者线程,负责将资源对象中的计数器输出打印
*/
public class Customer implements Runnable{
//创建资源对象
private Resource r ;
public Customer(Resource r) {
this.r = r;
}
@Override
public void run() {
while (true){
synchronized (r) {
//输出资源对象中的计数器
System.out.println("消费第" + r.count + "个");
}
}
}
}
1.3 线程通信的方法
线程通信的方法:线程的等待和唤醒,定义在Object类中:
- void wait() 线程无限等待
- void notify() 唤醒等待的单个线程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l6FBrSGx-1682346063125)(imgs\05132.png)]
- 线程等待与唤醒中的异常问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QD55AVEw-1682346063126)(D:/Atguigu/day22源码笔记/笔记/images/3.png)]
IllegalMonitorStateException:无效的监视器状态异常
程序中出现这个异常:线程wait() 和 notify() 造成的异常:监视器就是对象锁
wait(),notify() 必须要出现在同步中,方法的调用者必须是锁对象!!
/**
* 资源对象,定义产品的计数器
*/
public class Resource {
int count;//定义产品的计数器 default = 0
/**
* 定义标志位 default false
* flag = false,可以生产,不能消费
* flag = true ,可以消费,不能生产
*/
boolean flag ;
}
/**
* 生产者线程,负责对资源对象中计数器++操作
*/
public class Produce implements Runnable{
//创建资源对象
private Resource r ;
public Produce(Resource r) {
this.r = r;
}
@Override
public void run() {
while (true){
synchronized (r) {
//判断变量flag =true ,无限等待
if (r.flag)
try{
r.wait();
}catch (Exception ex){}
r.count++;
System.out.println("生产第..." + r.count + "个");
//改标志位,可消费
r.flag = true;
//唤醒对方线程
r.notify();
}
}
}
}
/**
* 消费者线程,负责将资源对象中的计数器输出打印
*/
public class Customer implements Runnable{
//创建资源对象
private Resource r ;
public Customer(Resource r) {
this.r = r;
}
@Override
public void run() {
while (true){
synchronized (r) {
//判断标记 flag=false,不能消费,无限等
if (!r.flag)
try{
r.wait();
}catch (Exception ex){}
//输出资源对象中的计数器
System.out.println("消费第" + r.count + "个");
//消费完毕改标志位 flag = false 可以生产
r.flag = false;
//唤醒对方线程
r.notify();
}
}
}
}
1.4 wait()和sleep()区别
-
wait()是Object类的,非静态
-
sleep()是Thread类的,静态的
-
wait()等待,需要被唤醒
-
sleep()休眠,时间到自己就醒了
-
wait()方法调用者必须是锁对象
-
sleep()方法出现哪里都可以
-
sleep()方法不会释放同步锁
-
wait()方法会释放同步锁,被唤醒后要重新获取同步锁才能执行
1.5 生产者与消费者案例优化
/**
* 资源对象,定义产品的计数器
* 成员私有修饰,提供get set
*/
public class Resource {
private int count;//定义产品的计数器 default = 0
/**
* 定义标志位 default false
* flag = false,可以生产,不能消费
* flag = true ,可以消费,不能生产
*/
private boolean flag ;
/**
* 定义get方法,被消费者线程调用
* 完成消费者的工作
*/
public synchronized void get(){
//判断标志位
if (!flag)
try{this.wait();}catch (Exception ex){}
//消费
System.out.println("消费第"+count+"个");
//改标志位
flag = false;
this.notify();
}
/**
* 定义set方法,被生产者线程调用
* 完成生产者的工作
*/
public synchronized void set(){
//判断标志位
if (flag)
//等待
try{this.wait();}catch (Exception ex){}
//生产
count++;
System.out.println("生产第..."+count+"个");
//修改标志位
flag = true;
this.notify();
}
}
/**
* 生产者线程,负责对资源对象中计数器++操作
*/
public class Produce implements Runnable{
//创建资源对象
private Resource r ;
public Produce(Resource r) {
this.r = r;
}
@Override
public void run() {
while (true)
//调用资源对象的set方法
r.set();
}
}
/**
* 消费者线程,负责将资源对象中的计数器输出打印
*/
public class Customer implements Runnable{
//创建资源对象
private Resource r ;
public Customer(Resource r) {
this.r = r;
}
@Override
public void run() {
while (true)
//调用资源对象的方法get
r.get();
}
}
第二章 线程生命周期
所谓线程的声明周期:线程的状态,反映出线程从创建到结束的生命历程。API已经给出了线程的生命周期。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sXJKKuuw-1682346063126)(imgs\05134.png)]
java.util.concurrent.ConcurrentHashMap
类:JDK1.5版本- Map接口的实现类,键值对的集合
- 底层的数据结构和HashMap一致
- ConcurrentHashMap类是一半线程安全,一半线程不安全
- 如果不修改里面的键值对,线程不安全的
- 如果动了里面的键值对,线程是安全
第三章 File类
3.1 File类介绍
-
java.io.File
类- 计算机存在路径,目录,文件
- File类将计算机中的路径,目录,文件做成对象
- 提供很多的方法,操作路径,目录和文件
-
计算机操作系统
- 目录就是文件夹(Directory),不能存储数据,是文件的容器
- 文件(File),可以存储数据
- 路径(Path),一个文件或者目录,在计算机中的存储位置
File类,是平台无关性的类
3.2 File类构造方法 (非常重要)
- File(String pathname)传递字符串的路径,可以表示目录也可以表示文件
/**
* File(String pathname)传递字符串的路径,可以表示目录也可以表示文件
* 路径里面,IDEA自动添加转义字符
*/
public static void testConstructor1(){
File file = new File("E:\\soft\\apache-maven-3.3.9\\bin\\m2.conf");
System.out.println("file = " + file);
}
- File(String parent,String child) 传递字符串的父路径,字符串的子路径
/**
* File(String parent,String child) 传递字符串的父路径,字符串的子路径
* E:\soft\apache-maven-3.3.9\bin 为参考点
* E:\soft\apache-maven-3.3.9\ 是父路径
* E:\soft\apache-maven-3.3.9\bin\ 里面的都是子路径
*/
public static void testConstructor2(){
File file = new File("e:\\soft\\apache-maven-3.3.9","bin");
System.out.println("file = " + file);
}
- File(File parent,String child) 传递File类型父类经,字符串的子路径
/**
* File(File parent,String child) 传递File类型父类经,字符串的子路径
* 构造方法的第一个参数是File类型,可以调用File类的方法
*/
public static void testConstructor3(){
File parent = new File("E:\\soft\\apache-maven-3.3.9");
File file = new File(parent,"bin");
System.out.println("file = " + file);
}
Windows操作系统:路径,目录,文件的名字,都是不区分大小写。Linux操作系统区分
路径的写法:Windows \,Linux / Java中路径,写 \ 或者 / 都可以
3.3 File类的创建和删除的方法(非常重要)
- boolean createNewFile() 创建文件的方法,文件的路径和名字定义在File类构造方法中
/**
* boolean createNewFile() 创建文件的方法,文件的路径和名字定义在File类构造方法中
* 创建成功返回true
* createNewFile() 无论名字怎么写,创建出现来都是文件
*/
public static void testCreateNewFile() throws IOException {
//创建File对象,路径和文件名写在构造方法中
File file = new File("e:/a.txt");
//创建文件
boolean b = file.createNewFile();
System.out.println("b = " + b);
}
- boolean mkdirs() 创建目录,目录的名字和路径写在File类构造方法中
/**
* boolean mkdirs() 创建目录,目录的名字和路径写在File类构造方法中
* 创建成功返回true
*/
public static void testMkdirs(){
//创建File对象,目录路径写在构造方法中
File file = new File("e:/a/b/c/d");
//调用方法创建文件夹
boolean b = file.mkdirs();
System.out.println("b = " + b);
}
- boolean delete() 删除,要删除的文件或者是目录写在File类构造方法中
/**
* boolean delete() 删除,要删除的文件或者是目录写在File类构造方法中
* 删除不走回收站,在硬盘中就没有了
* 删除有风险,执行需谨慎
* 如果文件夹不是空的,就删除不掉
*/
public static void testDelete(){
File file = new File("e:/a");
//boolean b = file.delete();
//System.out.println("b = " + b);
}
3.4 File类的判断方法(非常重要)
- boolean exists() 判断File构造方法中路径,是不是存在的,存在返回true
/**
* boolean exists() 判断File构造方法中路径,是不是存在的,存在返回true
*/
public static void testExists(){
//建立File对象,构造方法中,写路径
File file = new File("E:\\SOFT\\APACHE-MAVEN-3.3.9\\BIN");
//判断路径是否存在
boolean b = file.exists();
System.out.println("b = " + b);
}
- boolean isFile() 判断构造方法中的路径,是不是文件,是文件返回true
- boolean isDirectory() 判断构造方法中的路径,是不是目录,是目录返回true
/**
* boolean isFile() 判断构造方法中的路径,是不是文件,是文件返回true
* boolean isDirectory() 判断构造方法中的路径,是不是目录,是目录返回true
*/
public static void testIsFileDirectory(){
File file = new File("e:\\soft\\apache-maven-3.3.9\\bin\\mvn.cmd");
if (file.exists()) {
//判断路径是不是目录
boolean directory = file.isDirectory();
System.out.println("directory = " + directory);
//判断是不是文件
boolean isFile = file.isFile();
System.out.println("isFile = " + isFile);
}
}
3.5 File类获取方法(非常重要)
- String getName()获取名字
/**
* String getName()获取名字
* 返回的名字,File构造方法中,路径最后部分的名字
*/
public static void testGetName(){
File file = new File("E:\\soft\\apache-maven-3.3.9\\bin\\m2.conf");
//getName()方法获取名字
String name = file.getName();
System.out.println("name = " + name);
}
- File getParentFile() 返回File构造方法中,路径的父路径(上一级文件夹)
/**
* File getParentFile() 返回File构造方法中,路径的父路径(上一级文件夹)
* getParentFile()方法的返回值是File对象
* 当一个方法的返回值是一个对象的时候,继续使用对象调用方法
* 链式编程,方法调用链
*/
public static void testGetParentFile(){
File file = new File("E:\\soft\\apache-maven-3.3.9\\bin");
//获取构造方法中,路径的父路径
File parent = file.getParentFile();
System.out.println("parent = " + parent);
}
- File getAbsoluteFile() 返回File构造方法中个,路径的绝对路径
/**
* File getAbsoluteFile() 返回File构造方法中个,路径的绝对路径
* 绝对路径,在计算机系统中,具有唯一性
* 例子:E:\soft\apache-maven-3.3.9\bin
*
* File file = new File("bin");
* 构造方法中,路径不是绝对路径
* 返回的绝对路径:是IDEA创建的工程根目录 E:\0411JavaSe\bin
*/
public static void testGetAbsoluteFile(){
File file = new File("bin");
//获取构造方法中路径的绝对路径
File absoluteFile = file.getAbsoluteFile();
System.out.println("absoluteFile = " + absoluteFile);
}
3.6 ListFiles (非常重要)
- File[] listFiles() 获取File构造方法中,路径下的所有文件和文件夹 (目录遍历)
/**
* File[] listFiles() 获取File构造方法中,路径下的所有文件和文件夹 (目录遍历)
*/
public static void testListFiles(){
File file = new File("c:/abc");
//调用方法listFiles 目录的遍历
File[] fileArray = file.listFiles();
System.out.println(fileArray.length);
for (File f : fileArray){
System.out.println("f = " + f);
}
}
- 文件过滤器接口
java.io.FileFiter
接口:- 接口的实现类,就是文件过滤器
- 自定义类实现接口,重写方法 accept
/**
* 自定义文件过滤器
*/
public class MyFileFilter implements FileFilter {
//重写抽象方法
/**
*
* accept方法是由listFiles调用,传递遍历到的路径
* 判断这个路径filename,是.java结尾返回true
* filename = c:\abc\1.docx
*/
public boolean accept(File filename){
//filename.getName() 获取路径中的文件名,返回字符串
//文件名字符串.toLowerCase()变小写
return filename.getName().toLowerCase().endsWith(".java");
}
}
/**
* listFiles方法,遍历目录,只要java文件
* listFiles(传递文件过滤器接口实现类对象)
*/
public static void testListFilesFilter(){
File file = new File("c:/abc");
//调用方法listFiles 目录的遍历
File[] fileArray = file.listFiles( new MyFileFilter() );
for (File f : fileArray){
System.out.println("f = " + f);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OA6PkSP4-1682346063127)(imgs\05135.png)]
3.7 递归遍历所有目录
public class FileDemo06 {
public static void main(String[] args) {
getDir( new File("c:/java") );
}
/**
* 定义方法:遍历指定的目录
* 将要遍历的目录传递参数
*/
public static void getDir(File srcDir){
System.out.println(srcDir);
//遍历目录
File[] arrFile =srcDir.listFiles();
for (File f : arrFile){
//判断 路径 f 是不是文件夹(目录)
if (f.isDirectory()){
//调用自己,传递f 路径
getDir(f);
}else {
System.out.println(f);
}
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qu2vwXyX-1682346063127)(imgs\05136.png)]
第四章 IO流
4.1 IO流对象介绍
IO是两个单词的缩写(Input 输入,Output 输出),流是一种比喻,数据从一个设备流向另一个设备。文件1.txt 可以从硬盘中流向内存中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nxK11qD1-1682346063128)(images/7.png)]
IO流应用:数据从一个计算机流向另一个计算机,网络通信
4.2 IO流对象的分类(非常重要)
- 按照数据流的方向分类:
- 输出流:Java程序中的数据,流到其他设备,写入
- 输入流:数据从其他设备,流到Java的程序中,读取
- 按照操作的数据类型分类:
- 字节流:可以操作任意类型的数据
- 字符流:只能操作文本类型的数据 (文本工具,记事本,Notepad,editplus)打开这个文件,文本工具会自动查询编码表,结果人能看懂,就是文本
- 总结归类 (4大类):
- 字节输出流:可以写入任何数据文件
- 字节输出流顶级父类:
java.io.OutputStream
- 字节输出流顶级父类:
- 字节输入流:可以读取任何数据文件
- 字节输入流顶级父类:
java.io.InputStream
- 字节输入流顶级父类:
- 字符输出流:只能写入文本文件
- 字符输出流顶级父类:
java.io.Writer
- 字符输出流顶级父类:
- 字符输入流:只能读取文本文件
- 字符输入流顶级父类:
java.io.Reader
- 字符输入流顶级父类:
- 字节输出流:可以写入任何数据文件
4.3 编码表
编码表:为了让计算机可以直接识别人类的文字,开发出了编码表,生活中的一个字对应十进制的整数
-
ASCII 编码表:字母,数字,符号,都是1个字节存储,都是正数
- a-z 97-122
- A-Z 65-90
- 0-9 48-57
-
GB系列字符集(多个编码表的集合),简体中文
- GB2312,简体中文编码表,4000多个汉字
- GBK,简体中文编码表,21886多个汉字
- GB18030,简体中文编码表,70244多个汉字
- 中文版操作系统,默认使用的汉字编码表就是GBK
- 简体中文编码表中,一个汉字占用2个字节,是负数,第一个字节肯定是负数
-
Unicode字符集(多个编码表的集合),万国码
- UTF-8,最小用1个字节存储,汉字是三个字节,基本上负数,可变长度的编码
- UTF-16,定长的编码表,无论什么字,都是2个字节存储
- Java中的char类型,和String类型,都是UTF-16编码存储
- JDK9开始char没变,String类型变了
- 如果存储汉字,都是UTF-16编码存储
- 字符串没有汉字,全是字母,数组,拉丁文编码表存储,1个字节存储
- 最常用的编码表UTF-8
-
文件复制+properties
IO流对象
- 今日内容
- 字节输出流
- 字节输入流
- 文件复制
- 缓冲流
- 字符输出流
- 字节输入流
- 缓冲流
第一章 字节输出流
1.1 OutputStream字节输出流
- OutputStream类:所有字节输出流的顶级父类
- 字节输出流的方法,都是写入的方法:write
- write(byte[] b) 写入字节数组
- write(int i) 写入单个字节
- write(byte[],int off,int len) 写入字节数组,开始索引,写入个数
- close()释放资源
- OutputStream类的子类:FileOutputStream
1.2 FileOutputStream写入文件(非常重要)
- FileOutputStream构造器
- FileOutputStream(File file) File对象的路径是文件
- FileOutputStream(String filename) 字符串路径文件
- 构造方法中对象:字节输入流的数据输出目的
- 写入单个字节
/**
* write(int i) 写入单个字节
* FileOutputStream流对象,自动创建文件,如果文件已经存在,覆盖
*/
public static void testWriteByte()throws IOException {
//创建字节输出流,构造方法,传递字符串文件名(输出目的)
FileOutputStream fos = new FileOutputStream("e:/haha.txt");
//调用流对象的方法write,写单个字节
fos.write(49);
fos.write(48);
fos.write(48);
//释放资源,关闭次流对象
fos.close();
}
- 流对象的close()方法问题
close()关闭流,释放系统资源,IO流技术,读写数据,JVM(虚拟机)很聪明,任何一个操作系统(Windows还是Linux)本身就有IO流技术,JVM的IO流技术找操作系统来完成
- 写入字节数组
/**
* write(byte[] b) 写入字节数组
*/
public static void testWriteByteArray()throws IOException{
//创建字节输出流对象,构造方法绑定字符串路径
FileOutputStream fos = new FileOutputStream("e:/haha.txt");
byte[] bytes = {97,98,99,100,101,102};
//流对象的方法write写入数组
fos.write(bytes);
fos.close();
}
- 写入字节数组一部分
/**
* write(byte[],int off,int len) 写入字节数组,开始索引,写入个数
*/
public static void testWriteByteArrayPart()throws IOException{
//创建字节输出流对象,构造方法绑定字符串路径
FileOutputStream fos = new FileOutputStream("e:/haha.txt");
byte[] bytes = {97,98,99,100,101,102};
//流对象的方法write写入数组,的一部分
//1索引开始,写3个
fos.write(bytes,1,3);
fos.close();
}
1.3 流对象的异常处理
/**
* 字节输出流,写文件
* IO流对象添加异常处理try catch
* close() 释放资源,写在finally中
*/
public static void testWriteByteArray(){
//提升变量fos的作用域
//try外定义变量,try内建立对象
FileOutputStream fos = null;
try {
//创建对象失败,fos值依然还是null
fos = new FileOutputStream("e:/haha.txt");
byte[] bytes = {97, 98, 99, 100, 101, 102};
//流对象的方法write写入数组,的一部分
//1索引开始,写3个
fos.write(bytes, 1, 3);
}catch (IOException ex){
ex.printStackTrace();
}finally {
//close()方法抛出异常,继续try catch
//释放资源之前,fos对象判断空
if( fos != null)
try {
fos.close();
}catch (IOException ex){
ex.printStackTrace();
}
}
}
1.4 字节流的追加写入
FileOutputStream构造方法:第二个参数写true,写入文件就是追加写入,不会覆盖原有的文件
FileOutputStream fos = new FileOutputStream("e:/haha.txt",true);
第二章 字节输入流
2.1 InputStream字节输入流
- InputStream类:所有字节输入流的顶级父类
- 字节输入流的方法,都是读取的方法:read
- int read() 读取单个字节
- int read(byte[] b) 读取字节,存储在字节数组中
- close()释放资源
- InputStream类的子类:FileInputStream
2.2 FileInputStream读取文件(非常重要)
-
FileInputStream构造器
- FileInputStream(File file) File对象的路径是文件
- FileInputStream(String filename) 字符串路径文件
- 构造方法中对象:字节输入流读取的数据源
-
读取单个字节
/**
* int read() 读取单个字节
* 将返回读取到的字节,读取到文件末尾返回 -1
*/
public static void readByte()throws IOException {
//创建字节输入流对象,构造方法,传递字符串文件名 数据源
FileInputStream fis = new FileInputStream("e:/haha.txt");
//fis输入对象的方法read,读取单个字节
//使用循环读取,结束就是read()返回-1,循环结束
//定义变量,保存read()方法的返回值
int i = 0;
while ( (i = fis.read()) != -1 ){
System.out.print((char) i);
}
fis.close();
}
- 读取字节存储数组中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bOXSorbI-1683640554224)(imgs\05151.png)]
/**
* int read(byte[] b) 读取字节,存储在字节数组中
* read()方法的返回值:
* 返回read()方法每次读取到的字节个数
* 流末尾,返回-1
*/
public static void readByteArray()throws IOException{
//创建字节输入流对象,构造方法,传递字符串文件名 数据源
FileInputStream fis = new FileInputStream("e:/haha.txt");
//定义字节数组,数组长度:建议写1024整数倍
//1024字节,1KB,1MB
byte[] bytes = new byte[1024];
//定义变量,接收 read()读取方法的返回值
int i = 0;
while ( (i = fis.read(bytes)) != -1){
//直接输出数组,不行,读到几个字节,就输出几个字节
System.out.print(new String(bytes,0,i));
}
fis.close();
}
第三章 文件复制(重要应用)
3.1 字节流读写单个字节方式
/**
* 数据源: c:/a.avi
* 数据目的:e:/a.avi
* FileInputStream 读取数据源
* FileOutputStream 写入数据目的
* 读取一个字节,写入一个字节
*/
public static void copy_1(){
//定义好需要的流对象
FileInputStream fis = null;
FileOutputStream fos = null;
try {
//建立字节输入流对象,绑定数据源
fis = new FileInputStream("c:/a.avi");
//建立字节输出流对象,绑定数据目的
fos = new FileOutputStream("e:/a.avi");
//循环读写字节
int i = 0;
//read方法读取1个字节
while ( (i = fis.read()) != -1){
//写入1个字节
fos.write(i);
}
}catch (IOException ex){
ex.printStackTrace();
}finally {
if (fis != null)
try {
fis.close();
}catch (IOException ex){}
if (fos != null)
try {
fos.close();
}catch (IOException ex){}
}
}
3.2 字节流读写字节数组方式
/**
* 数据源: c:/a.avi
* 数据目的:e:/a.avi
* FileInputStream 读取数据源
* FileOutputStream 写入数据目的
* 读取一个字节数组,写入一个字节字节数组
*/
public static void copy_2(){
//定义好需要的流对象
FileInputStream fis = null;
FileOutputStream fos = null;
try {
//建立字节输入流对象,绑定数据源
fis = new FileInputStream("c:/a.avi");
//建立字节输出流对象,绑定数据目的
fos = new FileOutputStream("e:/a.avi");
//循环读写字节
int i = 0;
//定义字节数组
byte[] bytes = new byte[1024];
//read方法读取字节,保存数组中
while ( (i = fis.read(bytes)) != -1){
//写入字节数组,0索引开始,读取到几个,写入几个
fos.write(bytes,0,i);
}
}catch (IOException ex){
ex.printStackTrace();
}finally {
if (fis != null)
try {
fis.close();
}catch (IOException ex){}
if (fos != null)
try {
fos.close();
}catch (IOException ex){}
}
}
3.3 字节流缓冲流
-
java.io.BufferedOutputStream
字节输出流缓冲区流-
BufferedOutputStream继承OutputStream,本质上也是一个字节输出流
-
BufferedOutputStream构造方法:必须传递字节输出流
-
BufferedOutputStream(OutputStream out),能传递的流只有FileOutputStream
-
对于缓冲流:构造方法传递哪个流,就会提高哪个流的效率
-
-
java.io.BufferedInputStream
字节输入流缓冲区流-
BufferedInputStream继承InputStream,本质上也是一个字节输入流
-
BufferedInputStream构造方法:必须传递字节输入流
-
BufferedInputStream(InputStream in),能传递的流只有FileInputStream
-
对于缓冲流:构造方法传递哪个流,就会提高哪个流的效率
-
/**
*
* 数据源: c:/a.avi
* 数据目的:e:/a.avi
* FileInputStream 读取数据源
* FileOutputStream 写入数据目的
* BufferedInputStream 缓冲流 提高 FileInputStream 的读取速度
* BufferedOutputStream 缓冲流 提高 FileOutputStream 的写入速度
*/
public static void copy_3(){
//定义缓冲区流对象
BufferedOutputStream bos = null;
BufferedInputStream bis = null;
try{
//建立字节输出流缓冲区流对象,构造方法,传递字节输出流
bos = new BufferedOutputStream( new FileOutputStream("e:/a.avi") );
//建立字节输入流缓冲区流对象,构造方法,传递字节输入流
bis = new BufferedInputStream( new FileInputStream("c:/a.avi") );
int i = 0 ;
byte[] bytes = new byte[1024];
while ( (i = bis.read(bytes)) != -1){
bos.write(bytes,0,i);
}
}catch (IOException ex){
ex.printStackTrace();
}finally {
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
第四章 字符流
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-57jjOWDH-1683640554226)(imgs\05152.png)]
4.1 Writer类
java.io.Writer
类:所有字符输出流的顶级父类- 可以写入文本文件数据:写入数据的方法 write
- write(int c) 写入单个字符
- write(char[] ch) 写入字符数组
- write(char[] ch ,int off ,int len) 写入字符数组一部分,开始索引,写入个数
- write(String s) 写入一个字符串
- flush() 刷新该流的缓冲
4.2 OutputStreamWriter类(非常重要)
-
OutputStreamWriter:字符输出流,继承Writer
-
OutputStreamWriter流:是字符流通向字节流的桥梁
-
可以将字符转成字节流
-
OutputStreamWriter又称为转换流
-
字符流 = 字节流 + 编码表构成
-
-
OutputStreamWriter构造方法(必须传递字节输出流)
/**
* 字符输出流 OutputStreamWriter 写文本文件
* 指定编码表,GBK
* 在 OutputStreamWriter的构造方法,第二个参数,写编码表名
*/
public static void writeGBK()throws IOException{
//创建字节输出流,绑定文件数据目的
FileOutputStream fos = new FileOutputStream("e:/gbk.txt");
//转换流对象,构造方法传递字节输出流,指定使用GBK编码表
OutputStreamWriter osw = new OutputStreamWriter(fos,"GBK");
osw.write("你好");
//osw.flush();
osw.close();
}
/**
* 字符输出流 OutputStreamWriter 写文本文件
* 平台默认编码表写入文本
* 平台就是操作系统(中文版操作系统:默认编码表 GBK)
* 字符流写数据,要刷新,方法flush
* close()关闭流,但是要先刷新
* 建议写一次刷新一次
* IDEA干的,在JVM启动的时候,添加了一个参数,将编码表改为UTF-8
* -Dfile.encoding=UTF-8
*/
public static void writeUTF()throws IOException {
//创建字节输出流,绑定文件数据目的
FileOutputStream fos = new FileOutputStream("e:/utf.txt");
//转换流对象,构造方法传递字节输出流
OutputStreamWriter osw = new OutputStreamWriter(fos);
osw.write("你好");
//osw.flush();
osw.close();
}
4.3 Reader类
java.io.Reader
类:所有字符输入流的顶级父类- 可以读取文本文件数据:读取数据的方法 read
- int read() 读取单个字符
- int read(char[] ch) 读取字符,存储数组中
字符输入流,不能直接读取字符串
4.4 InputStreamReader(非常重要)
-
java.io.InputStreamReader
类:继承Reader,字符输入流- 可以读取文本类型的数据
- InputStreamReader类,是字节流通向字符流的桥梁
- 字节转成字符流
-
InputStreamReader构造方法(必须传递字节输入流)下课休息:16:08回
/**
* InputStreamReader读取gbk文件
* InputStreamReader构造方法的第二个参数,传递编码表
*/
public static void readGBK()throws IOException{
//创建字节流对象,绑定数据源文件
FileInputStream fis = new FileInputStream("e:/gbk.txt");
//创建转换流对象,构造方法传递字节输入流,构造方法的第二个参数,传递编码表
InputStreamReader isr = new InputStreamReader(fis,"gbk");
//字符数组
char[] ch = new char[1024];
int i = 0; //保存输入流read方法返回值
while ( (i = isr.read(ch)) !=-1){
//输出数组,数组转成字符串输出
System.out.print(new String(ch,0,i));
}
isr.close();
}
/**
* InputStreamReader读取utf文件
* IDEA的开发环境中,使用默认编码表utf-8
*/
public static void readUTF()throws IOException {
//创建字节流对象,绑定数据源文件
FileInputStream fis = new FileInputStream("e:/utf.txt");
//创建转换流对象,构造方法传递字节输入流
InputStreamReader isr = new InputStreamReader(fis);
//字符数组
char[] ch = new char[1024];
int i = 0; //保存输入流read方法返回值
while ( (i = isr.read(ch)) !=-1){
//输出数组,数组转成字符串输出
System.out.print(new String(ch,0,i));
}
isr.close();
}
4.5 字符流操作的便捷类
java.io.FileWriter
字符输出流- 继承转换流OutputStreamWriter
- FileWriter构造方法,直接传递字符串文件名即可
- 只能使用默认的编码写入数据,不能指定编码表
java.io.FileReader
字符输入流- 继承转换流InputStreamReader
- FileReader构造方法,直接传递字符串文件名
- 只能使用默认的编码写入数据,不能指定编码表
/**
* FileReader类 读取文本文件
*/
public static void readFile()throws IOException{
//创建字符输入流便捷类,构造方法直接传递文件名
FileReader fr = new FileReader("e:/heihei.txt");
int i = 0;
char[] ch = new char[1024];
while ( (i = fr.read(ch)) != -1){
System.out.print(new String(ch,0,i));
}
fr.close();
}
/**
* FileWriter类 写文本文件
* IDEA开发环境,默认编码表UTF-8
*/
public static void writeFile()throws IOException {
//创建便捷类对象,构造直接写文件名
FileWriter fw = new FileWriter("e:/heihei.txt");
fw.write("你好,我好,大家好!!");
fw.close();
}
4.6 BufferedWriter
java.io.BufferedWriter
:字符输出流缓冲区流- BufferedWriter继承Writer
- BufferedWriter构造方法中,必须传递字符输出流
- void newLine() 写入换行,平台无惯性
- 安装的JVM是Windows版本的,newLine()方法里面写是 \r\n
- 安装的JVM是Linux版本的,newLine()方法里面写是 \n
/**
* BufferedWriter类的方法newLine()实现换行,平台无关性
*/
public static void write()throws IOException {
//创建字符输出流便捷类
FileWriter fw = new FileWriter("e:/line.txt");
//创建字符输出流缓冲区对象,构造方法中,传递字符输出流
BufferedWriter bfw = new BufferedWriter(fw);
bfw.write("第一行");
bfw.newLine();
bfw.flush();
bfw.write("第二行");
bfw.newLine();
bfw.flush();
bfw.write("第三行");
bfw.flush();
bfw.close();
}
4.7 BufferedReader
java.io.BufferedReader
:字符输入流缓冲区流- BufferedReader继承Reader
- BufferedReader构造方法中,必须传递字符输入流
- String readLine() 读取文本一行
- 读取文本一行,返回字符串,不包含换行符号
- 读取到流的末尾,返回null
public static void read() throws IOException {
//创建字符输入流对象
FileReader fr = new FileReader("e:/line.txt");
//创建字符输入流缓冲区对象,构造方法,传递字符输入流
BufferedReader bfr = new BufferedReader(fr);
//定义字符串,保存读取到的文本行
String line = null;
while ( (line = bfr.readLine()) != null ){
System.out.println(line);
}
bfr.close();
}
4.8 字符流复制文本文件
字符流复制文本文件:错误应用,根本就不能做
第五章 Properties集合
Properties类的方法:load方法 (字节输入流),集合IO关联使用,字节输入流读取文件,文件中保存数据加载到集合中。IO流对象读取的文件,必须写键值对(不能写中文)
/**
* 集合IO关联使用
* 字节输入流读取文件 config.properties
* 文件中的键值对,存储集合Properties
*
* 集合中的数据,不再是开发人员在源码中写好的
* 而是通过文件获取的(文本)
*/
public class PropertiesDemo {
public static void main(String[] args)throws IOException {
//字节输入流,读取 config.properties
//文件的存储位置到当前的模块中,直接写模块名
FileInputStream fis = new FileInputStream("day23/config.properties");
//创建集合Properties
Properties prop = new Properties();
//集合对象方法load,传递字节输出流
prop.load(fis);
fis.close();
System.out.println("prop = " + prop);
}
}
- config.properties
name=zhangsan
age=21
#address=beijingshi
email=1234567@qq.com
第六章 对象序列化(非常重要)
6.1 对象序列化介绍
- 对象的序列化:就是写对象
- 对象的反序列化:就是读对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oBnpBKIs-1683640554226)(imgs\05153.png)]
6.2 对象的序列化
java.io.ObjectOutputStream
类:写对象的字节输出流- 继承OutputStream
- 构造方法,必须传递字节输出流:FileOutputStream
- 序列化方法,写入对象的方法 writeObject(Object obj)
/**
* 对象序列化
* ObjectOutputStream
*/
public static void writeObj()throws IOException {
//创建字节输出流,绑定目的文件
FileOutputStream fos = new FileOutputStream("e:/person.txt");
//创建对象的序列化流
ObjectOutputStream oos = new ObjectOutputStream(fos);
//写入对象
Person person = new Person("张三",20);
oos.writeObject(person);
oos.close();
}
6.3 对象的反序列化
java.io.ObjectInputStream
:读取对象的字节输入流- 继承InputStream
- ObjectInputStream 构造方法传递字节输入流:FileInputStream
- 反序列化的方法,读取对象的方法 Object readObject()
/**
* 对象反序列化
* ObjectInputStream
* ClassNotFoundException异常:类找不到异常
* 反序列化必须有class文件支持
*/
public static void readObj()throws IOException,ClassNotFoundException{
//创建字节输入流,绑定序列化文件
FileInputStream fis = new FileInputStream("e:/person.txt");
//创建对象的反序列化流,构造方法,传递字节输入流
ObjectInputStream ois = new ObjectInputStream(fis);
//读取对象
Object obj = ois.readObject();
System.out.println("obj = " + obj);
ois.close();
}
6.4 序列化的异常
目前程序可以正常的反序列化成功,修改成员的修饰符:
private String name; //原始,修改为public
public String name;
不进行对象的序列化,不重新写对象,直接进行反序列化,直接读取:
Exception in thread "main" java.io.InvalidClassException: com.atguigu.object.Person; local class incompatible: stream classdesc serialVersionUID = -5016735515273745116, local class serialVersionUID = -6237413986566242958
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1885)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1751)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2042)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)
at com.atguigu.object.ObjectStreamDemo.readObj(ObjectStreamDemo.java:25)
at com.atguigu.object.ObjectStreamDemo.main(ObjectStreamDemo.java:10)
这个异常,序列号冲突异常
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7kwet7UK-1683640554227)(imgs\05154.png)]
即使我们改变了成员的修饰符,但是不影响后期的功能使用,不序列化,直接可以反序列化成功
不让javac为我们计算序列号,我们要自定义序列号,只要我们写序列号了,javac就不计算了
static final long serialVersionUID = 20000L;