IO:
在整个java.io包中最重要的就是5个类和3个接口,掌握了这些IO的核心操作
5个类:
1.File类:数据源,文件类
2.InputStream:字节输入流(我们操作的数据有两种:字符跟字节)
3.OutputStream:字节输出流
4.Reader:字符输入流
5.Writer:字符输出流
3个接口:文件不是直接存在java中,而是存在硬盘中,那这个硬盘就需要操作系统去操作,因此java程序需要跟操作系统进行交流,
当java已经读完了这个文件,需要告诉操作系统可以释放你操作系统该文件的资源了,这里就涉及到流关闭接口了。
1.Closeable:关闭流接口
2.Flushable:刷新流接口
3.Serializable:序列化接口
当我们手上拿到一个对象的时候,如果放在内存中,当电脑一关,数据就没了,因此为了便于对对象的传输,定期
将其存储到存储文件中,这时候我们就把这种存储对象称作序列化。
流分类1:(按直接操作数据源还是间接操作数据源来分)
字节流:直接从数据源或目的地读写数据
处理流(包装流):不直接连接到数据源或目的地,是其他流进行封装。目的主要是简化操作和提高性能。
节点流和处理流的关系:
1.节点流处于io操作的第一线,所有操作都必须通过他们进行;
2.处理流可以对其他流进行处理(提高效率或操作灵活性)。
流分类2:按数据类型分为字节流跟字符流(底层还是基于字节流操作,自动搜索了指定码表)。
File类:
separator 是路径分隔符
建议用这种方式:
1.String path = "E:/APP/" 用/这种,虽然也可以用\但需要转义,也即\\
2.String path = "E:"+File.separator+"APP"+File.separator 这里File.separator就是/
//构建File对象
1.
String path = "E:/app/Java/jre/README.txt";
File src = new File(path);
System.out.println(src.length());
2.只要能拼起来一条路径就行
File src1 = new File("E:/app/Java/","jre/README.txt");
System.out.println(src1.length());
3.
File src2 = new File(new File("E:/app/Java/"),"jre/README.txt");
System.out.println(src2.length());
API:
pathSeparator separator:路径|路径分隔符
构造器:没有盘符以user.dir作为相对路径
File(String parent,String child)
File(File parent,String child)
File(String name)
文件名、路径名:
getName()、getPath()、getAbsolutePath()、getParent()
判断状态:File对象可能不存在,如果存在就要么是文件,要么是文件夹
exists()、isFile()、isDirectory()
文件长度:length()是能取到文件的长度的(字节长度),但是取不到文件夹的长度,因此length=0存在两种情况,要么是文件夹,要么文件不存在
length()
创建新文件、删除文件:
createNewFile() 不存在才能创建成功,如果存在就不能创建成功、delete()
创建目录,如果父目录不存在一同创建:
mkdir():确保上级目录存在,不存在创建失败 mkdirs():上级目录可以不存在,不存在一同来创建
下级名称:是下一级,不是子孙级,如果要子孙级就采用递归,自己调自己
list()
下级File:
listFiles()
根路径:
listRoots()
递归输出子孙级的文件名称:
public static void PrintName(File s,int level){
for(int i=0;i
}
System.out.println(s.getName());
if(!s.exists()){
return;
}else if(s.isDirectory()){
File[] next = s.listFiles();
for(File s1:next){
PrintName(s1,level++);
}
}
}
由字符到字节叫编码,由字节到字符叫解码
标准步骤:数据源(new File)->选择流(FileInputStream)->操作(读还是写)->释放资源(close)
将一个文件复制到另一个文件,通过文件输入流和文件输出流合二为一进行
public class TestCopyFile {
public static void main(String[] args) {
File src = new File("dest.txt");
File dest = new File("Aftercopy1.txt");
InputStream is = null;
OutputStream os = null;
try {
is = new FileInputStream(src);
os = new FileOutputStream(dest,true);
//也可以使用分段读取
byte[] flush = new byte[1024];
int len = -1;
while ((len = is.read(flush)) != -1) {
os.write(flush,0,len); //我这里容易出问题
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
文件字符输出流与文件字节输出流不同之处在于对于一个String类型的str,字符输出流是字符->字符数组(msg.toCharArray),
字节输出流是字符->字节数组(编码,msg.getBytes()实现).
文件字符输出流有三种输出方式:String msg = "我爱我家"
1.转换成字符数组
Char[] datas = msg.toCharArray();
writer.write(datas,0,datas.length);
2.直接输出字符
writer.write(msg);
writer.flush();
3.使用append方法来:
writer.append(msg).append()....可以链式追加,类似之前StringBuilder中的insert方法
我们前面所涉及到的FileInputStream FileOutputStream FileReader FileWriter都是存放在硬盘上的文件(源头),java虚拟机是
无权直接访问的,必须要借助os操作系统,借助完之后需要跟它通知释放资源。那么接下来将源头从文件换成另外一个源头,
换成我们电脑上的一块内存,也就是所谓的字节数组,这个字节数组(ByteArray),要么把它看成是同个电脑上的一块内存,要么把它看成是
网络上或者服务器上的一块内存,不再是文件了,而是内存,既然是内存,那么java是可以直接访问的了,也就与操作系统无关了。
所以这个东西是由垃圾回收(GC)来释放了,所以字节数组也就不用关闭了。
任何东西都可以转换成字节数组,但字节数组是内存,所以有限不能太大。
1.创建源:字节数组,不要太大 byte[] src = "msg".getBytes();
2.选择流:ByteArrayInputStream(src);
3.操作
4.释放资源:可以不用处理
字节数组
输出流是有自己的新增方法的toByteArray,所以可以直接通过toByteArray方法或toString直接获取数据
* 测试字节数组流输出
* 创建源:内部维护
* 选择流:不关联源
* 操作:
* 释放资源:可以不用
public class TestByteArrayOutputStream {
public static void main(String[] args) {
byte[] src = "talk is cheap show me the code".getBytes();
ByteArrayOutputStream os = null;
try{
os = new ByteArrayOutputStream();
os.write(src);
byte[] dest = os.toByteArray();
String result = new String(dest);
System.out.println(result);
} catch (IOException e) {
e.printStackTrace();
}
}
}
文件与文件之间是可以直接通过FileInputStream FileOutputStream相互中转,那么文件到字节,字节到文件该怎么实现呢?
字节缓冲流:BufferedInputStream BufferedOutputStream
字符缓冲流:BufferedReader BufferedWriter
缓冲流的存在提高了操作的性能,读写的性能,因为IO的操作是影响性能的一个瓶颈,所以内部维护了个缓冲区。
缓冲流只需要套在字节流上,不管怎么套,最底层一定是个节点流,后期这个流一层一层的该怎么释放呢,释放规则为从里到外释放。
多层的时候释放只需要释放最外层的那个流,它内部会自动找到最里层的节点流进行释放。如果想手动一个一个释放,那规则就是从里到外。
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("dest.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(("dest.txt")));
只要用新增方法,就不要用多态,不要使用父类指向子类引用
BufferedReader reader = new BufferedReader(new FileReader("dest.txt"));
String line = null;
while((line = reader.readLine())!=null){
System.out.println(line);
}
这里的readLine与之前的read不一样了(逐行读取)
BufferedWriter writer = new BufferedWriter(new FileWriter("dest.txt"));
writer.newLine();可以不需要自己手动补充\r\n了
转换流:
InputStreamReader/OutputStreamWriter:是字节流与字符流之间的桥梁,能将字节流转换为字符流,并且能为字节流指定字符集,
可处理一个个的字符,但前提是给转换流的全是纯文本的内容,在处理流的过程中,可以指定字符集
建议:一般的,涉及到字符串,凡涉及到字符串的,就都加个Buffered
数据流:
DataInputStream & DataOutputStream 主要作用是处理基本数据类型,和字符串的,说白了就是它不仅保留了数据,还保留了数据类型
就可以直接获取数据类型,而不需要强制转换类型
对象流:
ObjectInputStream(反序列化) & ObjectOutputStream(序列化) 有个特殊的叫法:序列化Serialization和反序列化(Deserialization)
不是所有的对象都可以序列化的,必须实现Serialization接口
对象经序列化后进入文件、数据库或内存中,然后再经过反序列化成为对象。
打印流:
PrintStream,是个装饰流
PrintStream ps = System.out;
ps.println("打印流");
ps.close();
//重定向输出端
PrintStream ps = new PrintStream(new BufferedOutputStream(new FileOutputStream("print.txt")),true);
System.setOut(ps);
System.out.println("change");
//重定向回控制台
System.setOut(new PrintStream(new BufferedOutputStream(new FileOutputStream(FileDescriptor.out)),true));
System.out.println("i am back!!!");
PrintStream还有个兄弟,PrintWriter,比较类似
RandomAccessFile:随机访问,之前的都是顺序访问的
多个class文件-》jar包
多个jar包-》组件
多个组件-》框架
之前学的IO所有关于读取文件写文件都可以通过FileUtils包实现