File类并不会操作文件内容
File file = new File("E:\\文件测试\\测试2"); System.out.println(file.exists()); //true
File file = new File("E:\\文件测试\\测试2"); System.out.println(file.isFile()); //false
File file = new File("E:\\文件测试\\测试2\\lala"); if(file.createNewFile()) {<!-- --> System.out.println("create file success!"); }else {<!-- --> System.out.println("create file fail"); } System.out.println(file.isFile()); //true
File提供的:public boolean mkdirs()
File file = new File("E:\\文件测试\\测试3\\测试3-1"); if(file.mkdirs()) {<!-- --> System.out.println("创建目录成功"); }else {<!-- --> System.out.println("创建目录失败"); }
File类不支持文件内容处理,如果要处理文件内容,必须要通过流的操作模式来完成。流分为输入流和输出流。
在java.io包中,流分为两种:字节流与字符流
- 字节流:InputStream、OutputStream
- 字符流:Reader、Writer
OutPutStream、InputStream 都是抽象类,所以不能直接创建对象使用,需要用它的子类,看它们的定义:
public abstract class InputStream implements Closeable public abstract class OutputStream implements Closeable, Flushable
Closeable这个接口主要是关于close的方法,Flushable是关于flush(写入时刷新,读取不需要)的方法
OutPutStream、InputStream 都是抽象类,所以不能直接创建对象使用,需要用它的子类,看它的定义:
//如果是 E:\\文件测试\\测试 就会编译报错,路径必须为一个具体文件,而不能为文件夹 File file = new File("E:\\文件测试\\测试\\hello.txt"); //第一种写法: //写入数据时一定要判断父路径是否存在,若果不存在则给他创建 // if(!file.getParentFile().exists()) {<!-- --> // file.getParentFile().mkdirs(); // } // OutputStream out = new FileOutputStream(file); // String str = "i love you"; // byte[] buff = str.getBytes(); // out.write(buff); // out.flush(); // out.close(); //第二种写法: if(!file.getParentFile().exists()) {<!-- --> file.getParentFile().mkdirs(); } try(OutputStream out = new FileOutputStream(file)){<!-- --> String str = "i love you"; byte[] buff = str.getBytes(); out.write(buff); //刷新缓存,最好写上 out.flush(); }catch(IOException e) {<!-- --> e.printStackTrace(); }
对于IO操作属于资源处理,所有的资源处理操作(IO操作、数据库操作、网络)最后必须要进行关闭; 第一种写法和第二种写法的区别就是:第一种要手动close,第二种是从JDk1.7开始追加了一个AutoCloseable接口, 这个接口的主要目的是自动进行关闭处理,使用它必须结合try…catch;
在操作字节输出流时,给定的file对象必须应该为文件类型(⭐),即不能是一个文件夹,如果这个文件不存在,(前提是这个文件的父目录必须存在),则会自动创建这个文件,即不用使用createNewFile方法来新建; ⭐
进行写入时,每执行的时候,会将目标写入文件中清空,然后写入,若果想保留目标写入文件中的内容,继续添加内容的话,需要在创建OutputStream类对象时调用另一个有参构造,即:(上面代码只用改这一处就行)
OutputStream out = new FileOutputStream(file,true)
public abstract class Writer implements Appendable, Closeable, Flushable
该类实现了三个接口,相对于字节输出流多了Appendable接口
该类也有write方法,如下所示,
public void write(String str) throws IOException
字符输出流与字节输出流的使用方法几乎一模一样,**最大的区别就是该类提供方的write方法可以直接以字符串作为参数进行写入,无须转为字节,**除了这其他使用基本不变,同样的,Writer作为一个抽象方法,我们需要用FileWriter这个子类来为它实例化对象。
File file = new File("E:\\文件测试\\测试2\\test.txt"); try(Writer writer = new FileWriter(file,true)) {<!-- --> writer.write("i like you dream!!!"); }catch (IOException e) {<!-- --> e.printStackTrace(); }
输入输出其实都差不多,这里直接呈上代码:
File file = new File("E:\\文件测试\\测试2\\test.txt"); try(InputStream in = new FileInputStream(file)) {<!-- --> byte[] arr = new byte[10]; //如果这里就先定义了 int len = in.read(arr) 的话,下面while条件里就会从第二次开始读取了 //⭐ int len = -1; while((len = in.read(arr)) != -1) {<!-- --> String str = new String(arr,0,len); System.out.println(str); } }
在上面的例子中,我们定义的byte数组用来接受每次读取的字节数据,你可以随意设置这个数组的大小,但一般为1024*1024,这里我们为了凸显特性选取了10,假如我们没有这个while循环来一直读取的话,读一次是读不完的,
package com.bitten.file; import java.io.*; /** * @Author : YangY * @Description : 文件拷贝 * @Time : Created in 16:33 2019/3/31 */ public class FileCopyUtil {<!-- --> public static void main(String[] args) {<!-- --> //destFilePath必须是一个具体文件的路径,不能为文件夹;⭐⭐ cp("E:\\文件测试\\测试\\hello.txt","E:\\文件测试\\测试2\\test.txt"); } /** * * @param sourceFilePath:源文件 * @param destFilePath:目的文件 */ public static void cp(String sourceFilePath,String destFilePath) {<!-- --> checkArgumentNotNull(sourceFilePath,"souce file path must not be NULL!!"); checkArgumentNotNull(destFilePath,"dest file path must not be NULL!!"); File souceFile = new File(sourceFilePath); checkFile(souceFile,"SouceFile must not be null!!"); File destFile = new File(destFilePath); checkDestFile(destFile); dataCopy(souceFile,destFile); } public static void checkArgumentNotNull(Object args, String msg) {<!-- --> if(args == null ) {<!-- --> throw new IllegalArgumentException(msg); } } private static void checkDestFile(File file) {<!-- --> //如果父目录不存在,则会copy失败,为了避免这,如果不存在,我们给它创建对应的父目录; if(!file.getParentFile().exists()) {<!-- --> file.getParentFile().mkdirs(); } } private static void checkFile(File file,String msg) {<!-- --> //源文件不能为空,而且必须为文件,是目录的话就不行,得抛出异常; if(file == null || !file.exists() ) {<!-- --> throw new IllegalArgumentException(msg); } } /** * 真正实现拷贝得方法 * @param souce * @param dest */ private static void dataCopy(File souce, File dest) {<!-- --> long start = System.currentTimeMillis(); try(InputStream in = new FileInputStream(souce); OutputStream out = new FileOutputStream(dest); ){<!-- --> byte[] buff = new byte[1024*1024]; //每次传输1024*1024个字节,即1M int length = -1; //当read方法返回值为-1时,代表已经将数据读取完; while((length=in.read(buff)) != -1) {<!-- --> out.write(buff,0,length); } out.flush(); long end = System.currentTimeMillis(); System.out.println("COPY SUCCESS!!!"+"耗时:"+(end-start)); }catch(IOException e) {<!-- --> e.printStackTrace(); } } }
在Java中,还提供了字节流转换为字符流的类:
OutputStreamWriter :将字节输出流变为字符输出流(Writer对于文字的输出要比OutputStream方便)InputStreamReader :将字节输入流变为字符输入流(InputStream读取的是字节,不方便中文的处理)
看看这两个类的定义:
public class OutputStreamWriter extends Writer public class InputStreamReader extends Reader
我们可以看到他们的继承关系,可以作为了解
了解:在开发之中使用的编码只有一个:UTF-8
对于内存流也分为两类:
- 字节内存流:ByteArrayInputStream、ByteArrayOutputStream
- 字符内存流:CharArrayReader、CharArrayWriter
下面看一个对两个文件进行合并的例子:
package com.bitten.file; import java.io.*; /** * @Author : YangY * @Description : 进行两个文件的合并 * @Time : Created in 19:06 2019/4/5 */ public class TestFileMerge {<!-- --> /* 第一种思路 1. data-a.txt => data.txt 2. data-b.txt append => data.txt 第二种思路 : 适合小量的合并,一般不会采取这种措施 1. data-a.txt => ByteArrayOutputStream 2. data-b.txt => ByteArrayOutputStream 3. ByteArrayOutputStream byte[] => FileOutputStream */ public static void main(String[] args) {<!-- --> String fileA = "E:" + File.separator + "文件测试" + File.separator + "测试" + File.separator + "data-a.txt"; String fileB = "E:" + File.separator + "文件测试" + File.separator + "测试" + File.separator + "data-b.txt"; String file = "E:" + File.separator + "文件测试" + File.separator + "测试" + File.separator + "data.txt"; try (FileInputStream ain = new FileInputStream(fileA); FileInputStream bin = new FileInputStream(fileB); ByteArrayOutputStream bout = new ByteArrayOutputStream(); FileOutputStream fout = new FileOutputStream(file); ) {<!-- --> byte[] buff = new byte[4]; int len = -1; //ain -> bout while ((len = ain.read(buff)) != -1) {<!-- --> bout.write(buff, 0, len); } //bin -> bout while ((len = bin.read(buff)) != -1) {<!-- --> bout.write(buff, 0, len); } //bout -> fout byte[] joinData = bout.toByteArray(); fout.write(joinData); fout.flush(); } catch (IOException e) {<!-- --> } } }
打印流分为字节打印流:PrintStream、字符打印流:PrintWriter,以后使用PrintWriter几率较高。
来看他们的定义:
public class PrintStream extends FilterOutputStream implements Appendable, Closeable
public class PrintWriter extends Writer
打印流的设计属于装饰设计模式:核心依然是某个类的功能,但是为了得到更好的操作效果,让其支持的功能更多一些
PrintWriter printWriter = new PrintWriter(new FileOutputStream(new File("E:"+ File.separator+"文件测试" + File.separator +"测试"+File.separator+ "hello.txt"))); printWriter.print(3); printWriter.print("lalallala"); printWriter.print(3.16); //要习惯加上flush,不然有时候会看不到输出 printWriter.flush();
面试考点:System.out.println()分别代表啥
System表示类名
out表示对象,printStream
print属于方法,属于out对象的方法
我们可以看到System类中out的定义如下:(可见out为一个PrintStream类的对象常量)
public final static PrintStream out = null;
系统输出一共有两个常量:out、err,并且这两个常量表示的都是PrintStream类的对象。
- out输出的是希望用户能看到的内容
- err输出的是不希望用户看到的内容
out和err其实在开发中用处很少,会被日志所代替
下面看一段简单代码:
PrintStream outputStream = System.out; outputStream.println("lalalla");
System.in对应的类型是InputStream ,而这种输入流指的是由用户通过键盘进行输入(用户输入)。java本身并没有
直接的用户输入处理,如果要想实现这种操作,必须使用java.io的模式来完成;
在实际操作中,很少用System.in,因为这个用起来还要结合内存流,很麻烦;
这个是十多年前Java用的输入类,所以不多讲解;
打印流解决的是OutputStream类的缺陷,BufferedReader解决的是InputStream类的缺陷。而Scanner解决的是
BufferedReader类的缺陷(替换了BufferedReader类)
Scanner是一个专门进行输入流处理的程序类,利用这个类可以方便处理各种数据类型,同时也可以直接结合正
则表达式进行各项处理,在这个类中主要关注以下方法:
public Scanner(InputStream source) {<!-- --> this(new InputStreamReader(source), WHITESPACE_PATTERN); } //可见它需要一个InputStream作为参数,而我们又知道,System.in正是InputStream类型;
Scanner scanner = new Scanner(System.in); System.out.println( scanner.hasNextInt());
取得指定类型的数据: public 数据类型 nextXxx()
定义分隔符:public Scanner useDelimiter(Pattern pattern)
Scanner scanner = new Scanner(System.in) ; System.out.println("请输入数据:") ; if (scanner.hasNext()) {<!-- --> // 有输入内容,不判断空字符串 System.out.println("输入内容为: "+scanner.next()); } scanner.close() ;
输出:
请输入数据:
ijbhu
输入内容为: ijbhuProcess finished with exit code 0
public class Test {<!-- --> public static void main(String[] args) throws Exception{<!-- --> Scanner scanner = new Scanner(new FileInputStream("E://文件测试测试print.txt")); //以c为分隔符; scanner.useDelimiter("c"); if(scanner.hasNext()) {<!-- --> System.out.println(scanner.next()); } scanner.close(); } }
输出:
aaa
bbbbb
已知在文件测试测试print.txt中的数据为:
aaa
bbbbb
ccccc
对象序列化指的是:将内存中保存的对象变为二进制数据流的形式进行传输,或者是将其保存在文本中。但是并不
意味着所有类的对象都可以被序列化,严格来讲,需要被序列化的类对象往往需要传输使用,同时这个类必须实现
java.io.Serializable接口。但是这个接口并没有任何的方法定义,只是一个标识而已。
如果要想实现序列化与反序列化的对象操作,在java.io包中提供有两个处理类:ObjectOutputStream、
ObjectInputStream