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,删除失败,因为它有一个子目录bSystem.out.println("删除d:/a是否成功: " + new File("d:/a").delete());}
}
🌔 6、综合练习
(1)判断指定目录下是否有后缀名为 .jpg的文件。如果有,就输出该文件名称
public class FindJPGFileTest {//方法1:@Testpublic 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:@Testpublic 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)*/@Testpublic void test3(){File srcFile = new File("d:\\code");File[] subFiles = srcFile.listFiles(new FilenameFilter() {@Overridepublic 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;//捕获一下可能存在的IOExceptiontry{//创建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,覆盖了原来的内容}
}
🌔 3、通过几个案例来练习一下
(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();}}
}
(2)实现图片的加密和解密操作
加密提示:在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异或会自动将精度提升为intbuffer[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));}//普通流完成文件copypublic 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);//copyint 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) {//演示readLinetry{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 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> set = map.entrySet();Iterator> it = set.iterator();while (it.hasNext()) {Map.Entry end = (Map.Entry) 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对象转化成与平台无关的二进制流。从而把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。
序列化机制包括两个过程:
🌔 2、序列化机制有什么意义?
序列化是 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;}@Overridepublic 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 lists = new ArrayList();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 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 lists = (ArrayList)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;}@Overridepublic 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 keyboardimport java.io.*;public class MyInput {// Read a string from the keyboardpublic static String readString() {BufferedReader br = new BufferedReader(new InputStreamReader(System.in));// Declare and initialize the stringString string = "";// Get the string from the keyboardtry {string = br.readLine();} catch (IOException ex) {System.out.println(ex);}// Return the string obtained from the keyboardreturn string;}// Read an int value from the keyboardpublic static int readInt() {return Integer.parseInt(readString());}// Read a double value from the keyboardpublic static double readDouble() {return Double.parseDouble(readString());}// Read a byte value from the keyboardpublic static double readByte() {return Byte.parseByte(readString());}// Read a short value from the keyboardpublic static double readShort() {return Short.parseShort(readString());}// Read a long value from the keyboardpublic static double readLong() {return Long.parseLong(readString());}// Read a float value from the keyboardpublic 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 {@Testpublic 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();}@Testpublic 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();}}
}
上一篇:vim 快捷键