File 类以及本章中的各种流都定义在 java.io 包下
一个File对象代表硬盘或网络中可能存在的一个文件或文件夹(文件目录)
File 能新建、删除、重命名 文件和目录,但 File不能访问文件内容本身。如果我们想要访问文件内容本身,就需要使用 输入/输出流
在Java程序中表示一个真实存在的文件或目录用File对象,但定义的File对象不一定对应真实存在的文件或目录(file not found)
简述一下绝对路径和相对路径的概念:
代码演示:
package com.zwh.shangguigu.fileandio_;
import java.io.File;
/**
* @author Bonbons
* @version 1.0
*/
public class FileObjectTest {
public static void main(String[] args) {
//通过pathname创建File对象
String path = "D:\\dataBase";
File file1 = new File(path);
//通过父路径+子路径创建File对象
String parent = "C:\\aaa";
String child = "bbb.txt";
File file2 = new File(parent, child);
//通过File对象+子路径创建File对象
File parentDir = new File("d:\\aaa");
String childFile = "bbb.txt";
File file3 = new File(parentDir, childFile);
//我们来打印一些File对象相关的信息
System.out.println("file1 = " + file1);
System.out.println("文件/目录的名称: " + file2.getName());
System.out.println("文件/目录的构造路径名: " + file3.getPath());
System.out.println("文件/目录的绝对路径名: " + file1.getAbsoluteFile());
System.out.println("文件/目录的父目录名: " + file2.getParent());
}
}
无论该路径下是否存在文件或者目录,都不影响File对象的创建
window的路径分隔符使用“\”,而Java程序中的“\”表示转义字符,所以在Windows中表示路径需要使用\\
或/
,或者用File.separator常量值表示
File file2 = new File(“d:” + File.separator + “atguigu” + File.separator + “info.txt”);
当构造路径是绝对路径时,那么getPath和getAbsolutePath结果一样
当构造路径是相对路径时,那么getAbsolutePath的路径 = user.dir的路径 + 构造路径
1、获取文件和目录的基本信息
有一个值得我们注意的:
如果File对象代表的文件或目录存在,则File对象实例初始化时,就会用硬盘中对应文件或目录的属性信息(例如,时间、类型等)为File对象的属性赋值,否则除了路径和名称,File对象的其他属性将会保留默认值 【可以看下图辅助理解】
我来编写代码具体演示一下:file1存在、file2不存在
package com.zwh.shangguigu.fileandio_;
import java.io.File;
/**
* @author Bonbons
* @version 1.0
*/
public class FileInfoMethod {
public static void main(String[] args) {
//存在的文件
File file1 = new File("d:/小红书Java后端开发.png");
System.out.println("文件构造路径: " + file1.getPath());
System.out.println("文件名称: " + file1.getName());
System.out.println("文件长度: " + file1.length() + "字节");
System.out.println("文件最后修改时间: " + file1.lastModified());
System.out.println();
//虚构的文件
File file2 = new File("d:" + File.separator + "aaa.txt");
System.out.println("文件构造路径: " + file2.getPath());
System.out.println("文件名称: " + file2.getName());
System.out.println("文件长度: " + file2.length() + "字节");
System.out.println("文件最后修改时间: " + file2.lastModified());
}
}
2、列出目录的下一级
代码演示一下:
package com.zwh.shangguigu.fileandio_;
import java.io.File;
/**
* @author Bonbons
* @version 1.0
*/
public class DirListFiles {
public static void main(String[] args) {
File file1 = new File("d:\\cfhd");
File file2 = new File("d:/DataBase");
//采用两种方式获取目录的子文件或子目录名,并打印输出
String [] list = file1.list();
File [] listFiles = file2.listFiles();
for(String child : list){
System.out.print(child + " ");
}
System.out.println();
for (File file : listFiles){
System.out.print(file + " ");
}
}
}
3、File 类的重命名功能
代码测试失败了
4、判断功能的方法
代码演示
package com.zwh.shangguigu.fileandio_;
import java.io.File;
/**
* @author Bonbons
* @version 1.0
*/
public class DirListFiles {
public static void main(String[] args) {
File file1 = new File("d:\\WeGame");
System.out.println("d:/WeGame 是否存在: " + file1.exists());
System.out.println("d:\\aaa 是否存在: " + new File("d:\\aaa").exists());
System.out.println("d:/WeGame 是否是目录: " + file1.isDirectory());
System.out.println("d:/WeGame 是否是文件: " + file1.isFile());
System.out.println(file1 + "是否可读: " + file1.canRead());
System.out.println(file1 + "是否可写: " + file1.canWrite());
System.out.println(file1 + "是否处于隐藏状态: " + file1.isHidden());
}
}
5、创建、删除功能
代码演示
package com.zwh.shangguigu.fileandio_;
import java.io.File;
import java.io.IOException;
/**
* @author Bonbons
* @version 1.0
*/
public class FileCreateDelete{
public static void main(String[] args) throws IOException {
//创建一个本来不存在的文件
File f = new File("d:/test/aaa.txt");
System.out.println(f + "是否存在: " + f.exists());
System.out.println("创建文件是否成功: " + f.createNewFile());
System.out.println(f + "是否存在: " + f.exists());
//创建一个本来不存在的目录(上一级目录存在)
File f2 = new File("d:/test/newDir");
System.out.println("newDir是否存在: " + f2.exists());
System.out.println("newDir是否创建: " + f2.mkdir());
System.out.println("newDir是否存在: " + f2.exists());
//创建一个目录,但是他的上一级目录不存在
System.out.println("falseDir是否创建成功: " + new File("d:/dask/falseDir").mkdir());
//直接创建多级目录
File f3 = new File("d:/a/b/ccc");
System.out.println(f3 + "是否存在: " + f3.exists());
System.out.println(f3 + "是否创建成功: " + f3.mkdirs());
System.out.println(f3 + "是否存在: " + f3.exists());
//删除创建的文件
System.out.println("删除d:/test/aaa.txt是否成功: " + f.delete());
//删除目录,删除d:/a/b/ccc,它是一个空目录
System.out.println("删除d:/a/b/ccc的ccc目录是否成功: " + f3.delete());
//试图直接删除d:/a,删除失败,因为它有一个子目录b
System.out.println("删除d:/a是否成功: " + new File("d:/a").delete());
}
}
6、综合练习
(1)判断指定目录下是否有后缀名为 .jpg的文件。如果有,就输出该文件名称
public class FindJPGFileTest {
//方法1:
@Test
public void test1(){
File srcFile = new File("d:\\code");
String[] fileNames = srcFile.list();
for(String fileName : fileNames){
if(fileName.endsWith(".jpg")){
System.out.println(fileName);
}
}
}
//方法2:
@Test
public void test2(){
File srcFile = new File("d:\\code");
File[] listFiles = srcFile.listFiles();
for(File file : listFiles){
if(file.getName().endsWith(".jpg")){
System.out.println(file.getAbsolutePath());
}
}
}
//方法3:
/*
* File类提供了两个文件过滤器方法
* public String[] list(FilenameFilter filter)
* public File[] listFiles(FileFilter filter)
*/
@Test
public void test3(){
File srcFile = new File("d:\\code");
File[] subFiles = srcFile.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".jpg");
}
});
for(File file : subFiles){
System.out.println(file.getAbsolutePath());
}
}
}
(2)遍历指定目录所有文件名称,包括子文件目录中的文件
public static void printSubFile(File dir) {
// 打印目录的子文件
File[] subfiles = dir.listFiles();
for (File f : subfiles) {
if (f.isDirectory()) {// 文件目录
printSubFile(f);
} else {// 文件
System.out.println(f.getAbsolutePath());
}
}
}
public void listAllSubFiles(File file) {
if (file.isFile()) {
System.out.println(file);
} else {
File[] all = file.listFiles();
// 如果all[i]是文件,直接打印
// 如果all[i]是目录,接着再获取它的下一级
for (File f : all) {
listAllSubFiles(f);// 递归调用:自己调用自己就叫递归
}
}
}
(3)计算指定目录占用空间大小
public long getDirectorySize(File file) {
// file是文件,那么直接返回file.length()
// file是目录,把它的下一级的所有file大小加起来就是它的总大小
long size = 0;
if (file.isFile()) {
size = file.length();
} else {
File[] all = file.listFiles();// 获取file的下一级
// 累加all[i]的大小
for (File f : all) {
size += getDirectorySize(f);// f的大小;
}
}
return size;
}
(4)删除指定文件目录及其下的所有文件
public void deleteDirectory(File file) {
// 如果file是文件,直接delete
// 如果file是目录,先把它的下一级干掉,然后删除自己
if (file.isDirectory()) {
File[] all = file.listFiles();
// 循环删除的是file的下一级
for (File f : all) {// f代表file的每一个下级
deleteDirectory(f);
}
}
// 删除自己
file.delete();
}
java.io 包下提供了各种"流"类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据
按数据的流向不同分为:输入流和输出流
按操作数据单位的不同分为:字节流(8bit) 和 字符流(16bit)
根据 IO 角色不同分为:节点流 和 处理流
一图总结:
常见的字节流:
常用处理流:
1、字符输入流:Reader
It’s importent: 当完成流的操作时,必须调用close()方法,释放系统资源,否则会造成内存泄漏
2、字符输出流:Writer
public void write(int c): 写出单个字符
public void write(char [] cbuf): 写出字符数组
public void write(char [] cbuf, int off, int len): 写出字符数组的一部分
public void write(String str): 写出字符串
public void write(String str, int off, int len):写出字符串的某一部分
public void flush(): 刷新该流的缓冲
public void close(): 关闭此流
和字符输入流一样,在完成流操作之后要调用 close 方法释放资源,否则可能会导致内存泄漏的问题
1、FileReader
通过代码演示——如何从一个具体的文件中读取数据
package com.zwh.shangguigu.fileandio_;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/**
* @author Bonbons
* @version 1.0
*/
public class FileReaderWriterTest {
public static void main(String[] args) {
//创建文件类读取的对象
FileReader fr = null;
//捕获一下可能存在的IOException
try{
//创建File类的对象,对应着物理磁盘上的某个文件
File file = new File("d:/test/zbc.txt");
//创建FileReader流对象,将File类的对象作为参数传递到FileReader的构造器中
try{
fr = new FileReader(file);
}catch (FileNotFoundException e){
e.printStackTrace();
}
int data = 0;
//这里如果不将读取的字符赋值给data就会出现乱码,我还不知道为啥
while((data = fr.read()) != -1){
System.out.print((char)data);
}
}catch (IOException e){
e.printStackTrace();
}finally {
//4、关闭相关的流资源,避免出现内存泄漏
try{
if(fr != null){
fr.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
}
2、FileWriter
代码演示:
package com.zwh.shangguigu.fileandio_;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
/**
* @author Bonbons
* @version 1.0
*/
public class FWWrite {
public static void main(String[] args) {
try{
FileWriter fw = new FileWriter(new File("d:/test/zbc"));
char [] ch = {'a', 'b', 'c'};
String s = "zhenbucuo";
try{
fw.write("z");
fw.write(ch);
fw.write(ch, 1, 2);
fw.write(s);
fw.write(s, 2, 3);
//关闭资源
fw.close();
}catch (IOException e){
e.printStackTrace();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
总结:
因为出现流资源的调用,为了避免内存泄漏,需要使用try-catch-finally处理异常
对于输入流来说,File类的对象必须在物理磁盘上存在,否则执行就会报FileNotFoundException。如果传入的是一个目录,则会报IOException异常。
对于输出流来说,File类的对象是可以不存在的。
1、字节输入流:InputStream
2、字节输出流:OutputStream
1、FileInputStreamStream
通过代码演示字节流文件的读取操作:
package com.zwh.shangguigu.fileandio_;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InvalidObjectException;
/**
* @author Bonbons
* @version 1.0
*/
public class FISRead {
public static void main(String[] args) throws IOException {
//先准备一个文件
File f = new File("d:/test/abc.txt");
// f.createNewFile();//为了省事我直接抛出了
//演示几种以字节流的方式读取文件的操作
FileInputStream fis = new FileInputStream(f);
FileInputStream fis2 = new FileInputStream(f);
FileInputStream fis3 = new FileInputStream(f);
FileInputStream fis4 = new FileInputStream(f);
//需要用read存一下,read()方法返回的是一个字节
int read = fis.read();
System.out.println((char)read);
fis.close();
System.out.println("*********************");
while((read = fis2.read()) != -1){
System.out.println((char)read);
}
fis2.close();
System.out.println("*********************");
//准备一个字节数组
byte [] b = new byte[2];
int len = 0;
while((len = fis3.read(b)) != -1){
System.out.println(new String(b));
}
fis3.close();
System.out.println("*********************");
while((len = fis4.read(b)) != -1){
System.out.println(new String(b, 0, len));
}
fis4.close();
}
}
2、FileOutputStream
通过代码来演示具体的用法:
package com.zwh.shangguigu.fileandio_;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @author Bonbons
* @version 1.0
*/
public class FOSWrite {
public static void main(String[] args) throws IOException {
//使用File对象创建流对象
FileOutputStream fos = new FileOutputStream(new File("d:/test/abc.txt"));
//写入一个字节
fos.write(97);
//写入一个字节数组
byte [] b = "abcd".getBytes();
fos.write(b);
//写入字节数组的一部分
fos.write(b, 1, 2); //bc
//使用FileInputStream读取一下文件
FileInputStream fis = new FileInputStream("d:/test/abc.txt");
int data = 0;
while((data = fis.read()) != -1){
System.out.println((char)data);
}
fis.close();
fos.close();
//写入结果:aabcdbc,覆盖了原来的内容
}
}
(1)实现文件的copy功能,就是将一个文件的读取结果,写入到另一个文件中
package com.zwh.shangguigu.fileandio_;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @author Bonbons
* @version 1.0
*/
public class FIOSCopyTest {
public static void main(String[] args) {
FileInputStream fis = null;
FileOutputStream fos = null;
try{
fis = new FileInputStream(new File("d:/test/a.txt"));
fos = new FileOutputStream("d:/test/b.txt");
//因为InputStream、OutputStream操作的是字节流
byte [] buffer = new byte[1024];
int len = 0;
while((len = fis.read(buffer)) != -1){
fos.write(buffer, 0, len);
}
System.out.println("内容复制成功!");
}catch (IOException e){
e.printStackTrace();
throw new RuntimeException(e);
}finally {
//最后我们要释放资源
try{
if(fis != null){
fis.close();
}
}catch (IOException e){
e.printStackTrace();
throw new RuntimeException(e);
}
try{
if(fos != null){
fos.close();
}
}catch (IOException e){
e.printStackTrace();
throw new RuntimeException(e);
}
}
//最后我们再检查一下是否写入成功
try{
FileInputStream fis2 = new FileInputStream("d:/test/b.txt");
int data = 0;
while((data = fis2.read()) != -1){
System.out.print((char)data);
}
}catch (IOException e){
e.printStackTrace();
}
}
}
加密提示:在Java中 ^ 代表异或操作;采用异或一个特定值加密,那么解密再次异或这个特定值就可以
package com.zwh.shangguigu.fileandio_;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @author Bonbons
* @version 1.0
* 图片的加密与解密
*/
public class FileSecretTest {
public static void main(String[] args) {
encryption();
decryption();
}
//加密 [实际应该把加密的功能抽出来,但是我为了在一个类中实现
// 加密和解密,就把完整操作放在一个静态方法中了,通过主方法
// 调用来完成测试]
public static void encryption(){
FileInputStream fis = null;
FileOutputStream fos = null;
try{
fis = new FileInputStream(new File("d:/test/kun.jpg"));
fos = new FileOutputStream("d:/test/kun_secret.jpg");
//每次读取一个字节效率很低,所以采用1024字节数组读取
byte [] buffer = new byte[1024];
int len = 0;
while((len = fis.read(buffer)) != -1){
//对每一个字节都进行加密
for(int i = 0; i < len; i++){
//需要强转,因为与5异或会自动将精度提升为int
buffer[i] = (byte)(buffer[i] ^ 5);
}
//写到字节输出流中
fos.write(buffer, 0, len);
}
System.out.println("图片加密成功!");
}catch (IOException e){
//在实际开发中打印异常信息到控制台很少,大多采用抛出异常来提醒出问题了
e.printStackTrace();
throw new RuntimeException(e);
}finally{
//资源释放
try{
if(fis != null){
fis.close();
}
}catch (IOException e){
throw new RuntimeException(e);
}
try{
if(fos != null){
fos.close();
}
}catch (IOException e){
throw new RuntimeException(e);
}
}
}
//图片的解密
public static void decryption(){
FileInputStream fis = null;
FileOutputStream fos = null;
try{
fis = new FileInputStream(new File("d:/test/kun_secret.jpg"));
fos = new FileOutputStream("d:/test/kun_unsecret.jpg");
int len = 0;
byte [] buffer = new byte[1024];
while((len = fis.read(buffer)) != -1){
//解密
for(int i = 0; i < len; i++){
buffer[i] = (byte) (buffer[i] ^ 5);
}
//将解密后的字节数组写入到目标数据流中
fos.write(buffer, 0, len);
}
System.out.println("图片解密成功!");
}catch (IOException e){
throw new RuntimeException(e);
}finally {
try{
if (fos != null) {
fos.close();
}
}catch (IOException e){
throw new RuntimeException(e);
}
try{
if (fis != null) {
fis.close();
}
}catch (IOException e){
throw new RuntimeException(e);
}
}
}
}
代码演示:
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("abc.jpg"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("abc_copy.jpg"));
代码演示
BufferedReader br = new BufferedReader(new FileReader("br.txt));
BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt));
查询API,缓冲流读写方法与基本的流是一致的,我们通过复制大文件测试他们的读写效率
package com.zwh.shangguigu.fileandio_;
import java.io.*;
/**
* @author Bonbons
* @version 1.0
* 比较普通流和缓冲流的复制文件速度
*/
public class CopyFileWithFileStream {
public static void main(String[] args) {
String srcPath = "d:/test/Flutter.rar";
String destPath = "d:/test/copy_Flutter.rar";
long start = System.currentTimeMillis();
ordinaryStream(srcPath, destPath);
long end = System.currentTimeMillis();
System.out.println("ordinary流完成复制花费的时间为: " + (end - start));
start = System.currentTimeMillis();
bufferedStream(srcPath, "d:/test/copy2_Flutter.rar");
end = System.currentTimeMillis();
System.out.println("bufferedStream流完成复制花费的时间为: " + (end - start));
}
//普通流完成文件copy
public static void ordinaryStream(String srcPath, String destPath){
//创建字节流文件读取的对象
FileInputStream fis = null;
FileOutputStream fos = null;
try{
fis = new FileInputStream(new File(srcPath));
fos = new FileOutputStream(new File(destPath));
//复制操作
byte [] buffer = new byte[100];
int len = 0;
while((len = fis.read(buffer)) != -1){
fos.write(buffer, 0, len);
}
System.out.println("复制成功!");
}catch (IOException e){
throw new RuntimeException(e);
}finally{
try{
if (fis != null) {
fis.close();
}
}catch (IOException e){
throw new RuntimeException(e);
}
try{
if (fos != null) {
fos.close();
}
}catch (IOException e){
throw new RuntimeException(e);
}
}
}
public static void bufferedStream(String srcPath, String destPath){
//字节输入输出流对象,以及字节缓冲输入输出流对象
FileInputStream fis = null;
FileOutputStream fos = null;
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try{
fis = new FileInputStream(new File(srcPath));
fos = new FileOutputStream(new File(destPath));
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
//copy
int len = 0;
byte [] buffer = new byte[100];
while((len = bis.read(buffer)) != -1){
bos.write(buffer, 0, len);
}
System.out.println("复制成功!");
}catch (IOException e){
throw new RuntimeException(e);
}finally{
try{
if (bos != null) {
bos.close();
}
}catch (IOException e){
throw new RuntimeException(e);
}
try{
if(bis != null){
bis.close();
}
}catch (IOException e){
throw new RuntimeException(e);
}
}
}
}
通过代码演示一下具体的用法:
package com.zwh.shangguigu.fileandio_;
import java.io.*;
/**
* @author Bonbons
* @version 1.0
*/
public class BufferedIOLine {
public static void main(String[] args) {
//演示readLine
try{
BufferedReader br = new BufferedReader(new FileReader(new File("d:/test/cc.txt")));
//定义一个字符串保存读取的一行文字
String line = "";
while((line = br.readLine()) != null){
System.out.println(line);
}
br.close();
}catch (IOException e){
e.printStackTrace();
}
//演示写入换行
try{
BufferedWriter bw = new BufferedWriter(new FileWriter(new File("d:/test/cc.txt")));
bw.write("奇衡三");
bw.newLine();
bw.write("可以");
bw.write("召唤");
bw.newLine();
bw.write("魁拔的脉兽");
bw.flush();
bw.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
我们通过一个练习来进一步数据缓冲流:
public static void main(String[] args) {
//key对应姓,value为出现次数
HashMap<String, Integer> map = new HashMap<>();
//之所以要选取字符缓冲流,是因为要用它特有的readLine()读取一行数据
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(new File("e:/name.txt")));
String value = null; // 临时接收文件中的字符串变量
StringBuffer buffer = new StringBuffer();
flag:
while ((value = br.readLine()) != null) { // 开始读取文件中的字符
char[] c = value.toCharArray();
for (int i = 0; i < c.length; i++) {
//拼接姓到buffer里,注意姓可能不是一个字
if (c[i] != ' ') {
buffer.append(String.valueOf(c[i]));
} else {
//我用了HashMap的getOrDefault优化了一下
if(map != null){
map.put(buffer.toString(), map.getOrDefault(buffer.toString(), 0) + 1);
}else{
map.put(buffer.toString(), 1)
/*
if (map.containsKey(buffer.toString())) {
int count = map.get(buffer.toString());
map.put(buffer.toString(), count + 1);
} else {
map.put(buffer.toString(), 1);
}
*/
//目的是将buffer重置为空串
buffer.delete(0, buffer.length());
continue flag;
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//排序
Set<Map.Entry<String, Integer>> set = map.entrySet();
Iterator<Map.Entry<String, Integer>> it = set.iterator();
while (it.hasNext()) {
Map.Entry<String, Integer> end = (Map.Entry<String, Integer>) it.next();
System.out.println(end);
}
}
1、InputStreamReader
//两种构造方法演示
InputStreamReader isr = new InputStreamReader(new FileInputStream("in.txt"));
OutputStreamReader osr = new OutputStreamReader(new FileOutputStream("out.txt"));
通过代码演示具体的用法
package com.zwh.shangguigu.fileandio_;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* @author Bonbons
* @version 1.0
*/
public class InputStreamReaderDemo {
public static void main(String[] args) throws IOException {
//创建转换流的对象,指定编码字符集
InputStreamReader isr = new InputStreamReader(new FileInputStream(new File("d:/test/a.txt")), "GBK");
int data = 0;
while((data = isr.read()) != -1){
System.out.print((char)data);
}
System.out.println();
if (isr != null) {
isr.close();
}
//创建字节流到字符流的转换流对象,不设置编码
InputStreamReader isr2 = new InputStreamReader(new FileInputStream("d:/test/a.txt"));
int charData = 0;
while((charData = isr2.read()) != -1){
System.out.print((char)charData);
}
if (isr2 != null) {
isr2.close();
}
}
}
2、OutputStreamWriter
OutputStreamWriter isr = new OutputStreamWriter(new FileOutputStream("out.txt"));
OutputStreamWriter isr2 = new OutputStreamWriter(new FileOutputStream("out.txt"), "GBK");
通过代码演示具体的使用方法:
package com.zwh.shangguigu.fileandio_;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.OutputStreamWriter;
/**
* @author Bonbons
* @version 1.0
* 从字符流到字节流转换的桥梁
*/
public class OutputStreamWriterDemo {
public static void main(String[] args) throws IOException {
String FileName = "d:/test/a.txt";
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(FileName));
osw.write("你好");//一个汉字在UTF-8里占3个字节
osw.close();
OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream("d:/test/aaa.txt"), "GBK");
osw2.write("你好");//一个汉字在GBK里占两个字节
osw2.close();
}
}
1、编码和解码的概念
计算机中存储的信息都是用二进制数表示的
字符编码(Character Encoding):就是一套自然语言的字符与二进制数之间的对应规则
编码表:生活中文字和计算机中二进制的对应规则
什么情况下会出现乱码的现象呢?
2、各种字符集的介绍
字符集Charset:也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。
计算机要准确的存储和识别各种字符集符号,需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBK字符集、Unicode字符集等
可见,当指定了编码,它所对应的字符集自然就指定了,所以编码才是我们最终要关心的
(1)ASCII字符集 :
(2)ISO-8859-1字符集:
(3)GBxxx字符集:
(4)Unicode字符集 :
(5)UTF-8字符集:
在中文操作系统上,ANSI(美国国家标准学会、AMERICAN NATIONAL STANDARDS INSTITUTE: ANSI)编码即为GBK;在英文操作系统上,ANSI编码即为ISO-8859-1
如果需要将内存中定义的变量(包括基本数据类型或引用数据类型)保存在文件中,那怎么办呢?
数据流: DataOutputStream、DataInputStream
数据输入流和数据输出流的方法类似:【read-write相互替换即可】
然而对象流既支持基本数据类型的读写,又支持Java对象的读写
对象流最大的优势就是:可以把Java对象写入到数据源中,也可以把对象从数据源中还原回来
1、ObjectOutputStream 的构造器与常用方法:
public ObejectOutputStream(OutputStream out):
创建一个指定的ObjectOutputStreamObjectOutputStream 中的常用方法:
2、ObjectInputStream 的构造器与常用方法:
public ObjectInputStream(InputStream in):
创建一个指定的ObjectInputStreamObjectInputStream 中的常用方法:
1、什么是对象序列化机制?
对象序列化机制:允许把内存中的Java对象转化成与平台无关的二进制流。从而把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。
序列化机制包括两个过程:
序列化是 RMI(Remote Method Invoke、远程方法调用)过程的参数和返回值都必须实现的机制,而 RMI 是 JavaEE 的基础。因此序列化机制是 JavaEE 平台的基础。
序列化的好处,在于可将任何实现了Serializable接口的对象转化为字节数据,使其在保存和传输时可被还原
3、序列化机制的实现原理是什么?
4、如何实现序列化机制?
如果我们想让某个对象支持序列化机制,就必须让其所属类实现 java.io.Serializable 接口
package com.zwh.shangguigu.fileandio_;
import java.io.*;
/**
* @author Bonbons
* @version 1.0
*/
public class ReadWriteObject {
public static void main(String[] args) {
//创建学生类对象
Student s = new Student("042040133", "JiaSiting", 21);
//序列化
try{
//创建对象输入流,将我们的Java对象写入到指定的文件中
File f = new File("d:/test/student.txt");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(f));
oos.writeObject(s);
oos.close();
System.out.println("Serialized data is saved");
}catch (IOException e){
e.printStackTrace();
}
//反序列化
try{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("d:/test/student.txt")));
try{
Student stu = (Student) ois.readObject();
ois.close();
System.out.println(stu);
}catch (ClassNotFoundException e){
e.printStackTrace();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
//创建学生类让它实现Serializable接口,使其对象支持序列化机制
class Student implements Serializable {
private String sno;
private String sname;
private int age;
public Student(String sno, String sname, int age) {
this.sno = sno;
this.sname = sname;
this.age = age;
}
public String getSno() {
return sno;
}
public void setSno(String sno) {
this.sno = sno;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"sno='" + sno + '\'' +
", sname='" + sname + '\'' +
", age=" + age +
'}';
}
}
1、反序列化失败的两种情况:
对于JVM可以反序列化对象,他必须是能够遭到 class 文件的类。否则会抛出 CLassNotFoundException 异常
能够找到class文件,但是在序列化对象之后发生了修改,那么反序列化操作也会失败,会抛出 InvalidClassException 异常
2、那么如何解决上面的问题呢?
static final long serialVersionUID = 234242343243L; //它的值由程序员随意指定即可。
简单来说,Java 的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。
在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常(InvalidCastException)。
package com.atguigu.object;
import java.io.Serializable;
public class Employee implements Serializable {
private static final long serialVersionUID = 1324234L; //增加serialVersionUID
}
3、综合练习
(1)谈谈你对java.io.Serializable接口的理解,我们知道它用于序列化,是空方法接口,还有其它认识吗?
实现了Serializable接口的对象,可将它们转换成一系列字节,并可在以后完全恢复回原来的样子。这一过程亦可通过网络进行。这意味着序列化机制能自动补偿操作系统间的差异。换句话说,可以先在Windows机器上创建一个对象,对其序列化,然后通过网络发给一台Unix机器,然后在那里准确无误地重新“装配”。不必关心数据在不同机器上如何表示,也不必关心字节的顺序或者其他任何细节。
由于大部分作为参数的类如String、Integer等都实现了java.io.Serializable的接口,也可以利用多态的性质,作为参数使接口更灵活。
(2)要求如图:
package com.zwh.shangguigu.fileandio_;
import com.sun.org.apache.xpath.internal.operations.Or;
import java.io.*;
import java.util.ArrayList;
import java.util.Scanner;
/**
* @author Bonbons
* @version 1.0
* 需求说明:
* 网上购物时某用户填写订单,订单内容为产品列表,保存在“save.bin”中。
* 运行时,如果不存在“save.bin”,则进行新订单录入,如果存在,则显示并计算客户所需付款。
* 分析:
* 编写Save()方法保存对象到“save.bin”
* 编写Load()方法获得对象,计算客户所需付款
*/
public class OrderTest {
public static void main(String[] args) {
ArrayList<OrderInformation> lists = new ArrayList<OrderInformation>();
Scanner sc = new Scanner(System.in);
char op = 'y';
while(op == 'y'){
System.out.print("请输入产品名:");
String name = sc.next();
System.out.print("请输入单价:");
Double price = sc.nextDouble();
System.out.print("请输入数量:");
int count = sc.nextInt();
//创建商品订单对象
OrderInformation order = new OrderInformation(name, price, count);
lists.add(order);
//是否继续操作
System.out.print("是否继续(y/n):");
op = sc.next().charAt(0);
}
System.out.println("订单已保存");
//将完整订单写到目标文件中
save(lists);
System.out.println();
System.out.println();
load();
}
//存储订单信息
public static void save(ArrayList<OrderInformation> o){
ObjectOutputStream oos = null;
try{
oos = new ObjectOutputStream(new FileOutputStream(new File("d:/test/save.bin")));
oos.writeObject(o);
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if (oos != null) {
oos.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
//加载订单信息,获取总金额
public static void load(){
ObjectInputStream ois = null;
try{
ois = new ObjectInputStream(new FileInputStream(new File("d:/test/save.bin")));
//计算客户需要支付的总金额
Double countMoney = 0.0;
ArrayList<OrderInformation> lists = (ArrayList<OrderInformation>)ois.readObject();
System.out.println("产品名" + "\t" + "单价" + "\t\t" + "数量");
for(OrderInformation order : lists){
countMoney += order.getPrice() * order.getCount();
System.out.println(order.getOrderName() + "\t\t" + order.getPrice() + "\t\t" + order.getCount());
}
System.out.println("您需要支付的金额为: " + countMoney);
}catch (IOException | ClassNotFoundException e){
e.printStackTrace();
}
}
}
class OrderInformation implements Serializable {
//定义一个序列号
private static final long serialVersionUID = 123456789L;
//订单属性
private String orderName;
private Double price;
private int count;
public OrderInformation(){}
public OrderInformation(String orderName, double price, int count) {
this.orderName = orderName;
this.price = price;
this.count = count;
}
public String getOrderName() {
return orderName;
}
public void setOrderName(String orderName) {
this.orderName = orderName;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
@Override
public String toString() {
return "OrderInformation{" +
"orderName='" + orderName + '\'' +
", price=" + price +
", count=" + count +
'}';
}
}
1、 通过案例来分析如何使用标准输入、输出流
package com.zwh.shangguigu.fileandio_;
import java.io.*;
/**
* @author Bonbons
* @version 1.0
* 从键盘输入字符串,要求将读取到的整行字符串转成大写输出。
* 然后继续进行输入操作,直至当输入“e”或者“exit”时,退出程序
*/
public class SystemTest {
public static void main(String[] args) {
System.out.println("请输入信息(退出输入e 或 exit):");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
String s = null;
try{
while((s = br.readLine()) != null){
//判断输入的是否为跳出操作
if(s.equalsIgnoreCase("e") || s.equalsIgnoreCase("exit")){
break;
}
pw.println(s.toUpperCase());
pw.flush();
System.out.println("继续输入信息");
}
}catch (IOException e){
e.printStackTrace();
}finally {
try{
//释放连接
if (br != null) {
br.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
}
2、System类中有三个常量对象:System.out、System.in、System.err
public final static InputStream in = null;
public final static PrintStream out = null;
public final static PrintStream err = null;
final声明的常量,表示在Java的语法体系中它们的值是不能修改的,而这三个常量对象的值是由C/C++等系统函数进行初始化和修改值的,所以它们故意没有用大写,也有set方法。
public static void setOut(PrintStream out) {
checkIO();
setOut0(out);
}
public static void setErr(PrintStream err) {
checkIO();
setErr0(err);
}
public static void setIn(InputStream in) {
checkIO();
setIn0(in);
}
private static void checkIO() {
SecurityManager sm = getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("setIO"));
}
}
private static native void setIn0(InputStream in);
private static native void setOut0(PrintStream out);
private static native void setErr0(PrintStream err);
3、练习
Create a program named MyInput.java: Contain the methods for reading int, double, float, boolean, short, byte and String values from the keyboard.
package com.atguigu.java;
// MyInput.java: Contain the methods for reading int, double, float, boolean, short, byte and
// string values from the keyboard
import java.io.*;
public class MyInput {
// Read a string from the keyboard
public static String readString() {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// Declare and initialize the string
String string = "";
// Get the string from the keyboard
try {
string = br.readLine();
} catch (IOException ex) {
System.out.println(ex);
}
// Return the string obtained from the keyboard
return string;
}
// Read an int value from the keyboard
public static int readInt() {
return Integer.parseInt(readString());
}
// Read a double value from the keyboard
public static double readDouble() {
return Double.parseDouble(readString());
}
// Read a byte value from the keyboard
public static double readByte() {
return Byte.parseByte(readString());
}
// Read a short value from the keyboard
public static double readShort() {
return Short.parseShort(readString());
}
// Read a long value from the keyboard
public static double readLong() {
return Long.parseLong(readString());
}
// Read a float value from the keyboard
public static double readFloat() {
return Float.parseFloat(readString());
}
}
- PrintStream和PrintWriter的输出不会抛出IOException异常
- PrintStream和PrintWriter有自动flush功能
- PrintStream 打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是写入字节的情况下,应该使用 PrintWriter 类。
- System.out返回的是PrintStream的实例
通过代码演示具体用法:自定义一个日志工具
package com.zwh.shangguigu.fileandio_;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author Bonbons
* @version 1.0
*/
public class LoggerTest {
public static void main(String[] args) {
Logger.log("调用了System类的gc()方法,建议启动垃圾回收");
Logger.log("调用了TeamView的addMember()方法");
Logger.log("用户尝试进行登录,验证失败");
}
}
class Logger{
public static void log(String msg){
try{
// 指向一个日志文件
PrintStream out = new PrintStream(new FileOutputStream("log.txt"), true);
// 改变输出方向
System.setOut(out);
// 日期当前时间
Date nowTime = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String strTime = sdf.format(nowTime);
System.out.println(strTime + ": " + msg);
}catch (FileNotFoundException e){
e.printStackTrace();
}
}
}
构造方法:
常用方法:
通过代码演示具体用法:
package com.atguigu.systemio;
import org.junit.Test;
import java.io.*;
import java.util.Scanner;
public class TestScanner {
@Test
public void test01() throws IOException {
Scanner input = new Scanner(System.in);
PrintStream ps = new PrintStream("1.txt");
while(true){
System.out.print("请输入一个单词:");
String str = input.nextLine();
if("stop".equals(str)){
break;
}
ps.println(str);
}
input.close();
ps.close();
}
@Test
public void test2() throws IOException {
Scanner input = new Scanner(new FileInputStream("1.txt"));
while(input.hasNextLine()){
String str = input.nextLine();
System.out.println(str);
}
input.close();
}
}
IOUtils类的使用
- 静态方法:IOUtils.copy(InputStream in,OutputStream out)传递字节流,实现文件复制。
- 静态方法:IOUtils.closeQuietly(任意流对象)悄悄的释放资源,自动处理close()方法抛出的异常。
public class Test01 {
public static void main(String[] args)throws Exception {
//- 静态方法:IOUtils.copy(InputStream in,OutputStream out)传递字节流,实现文件复制。
IOUtils.copy(new FileInputStream("E:\\Idea\\io\\1.jpg"),new FileOutputStream("E:\\Idea\\io\\file\\柳岩.jpg"));
//- 静态方法:IOUtils.closeQuietly(任意流对象)悄悄的释放资源,自动处理close()方法抛出的异常。
/* FileWriter fw = null;
try {
fw = new FileWriter("day21\\io\\writer.txt");
fw.write("hahah");
} catch (IOException e) {
e.printStackTrace();
}finally {
IOUtils.closeQuietly(fw);
}*/
}
}
FileUtils类的使用:
- 静态方法:void copyDirectoryToDirectory(File src,File dest):整个目录的复制,自动进行递归遍历
参数:
src:要复制的文件夹路径
dest:要将文件夹粘贴到哪里去
- 静态方法:void writeStringToFile(File file,String content):将内容content写入到file中
- 静态方法:String readFileToString(File file):读取文件内容,并返回一个String
- 静态方法:void copyFile(File srcFile,File destFile):文件复制
public class Test02 {
public static void main(String[] args) {
try {
//- 静态方法:void copyDirectoryToDirectory(File src,File dest);
FileUtils.copyDirectoryToDirectory(new File("E:\\Idea\\io\\aa"),new File("E:\\Idea\\io\\file"));
//- 静态方法:writeStringToFile(File file,String str)
FileUtils.writeStringToFile(new File("day21\\io\\commons.txt"),"柳岩你好");
//- 静态方法:String readFileToString(File file)
String s = FileUtils.readFileToString(new File("day21\\io\\commons.txt"));
System.out.println(s);
//- 静态方法:void copyFile(File srcFile,File destFile)
FileUtils.copyFile(new File("io\\yangm.png"),new File("io\\yangm2.png"));
System.out.println("复制成功");
} catch (IOException e) {
e.printStackTrace();
}
}
}