File
File类概述和构造方法
File:他是文件和目录路径名的抽象表示
- 文件和目录是可以通过File封装成对象的
- 对于FIle而言,其封装的并不是一个真正存在的文件,仅仅是一个路径名而已。它可以是存在的,也可以是不存在的,将来是要通过具体的操作吧这个路径的内容转换为具体存在的。
构造方法:
- FIle( String pathname ):通过将给定的路径名字符串转换为抽象路径名来创建新的File实例
- File( String parent , String child ):从父路径名字符串和子路径名字符串创建新的File实例
- File( File parent , String child ): 从父抽象路径名和子路径名字符串创建新的File实例
public static void main(String[] args) {
File f1 = new File("G:/new.txt");
System.out.println(f1); // G:\new.txt
File f2 = new File("G:/", "new.txt");
System.out.println(f2); // G:\new.txt
File f3 = new File("G:/");
File f4 = new File(f3, "new.txt");
System.out.println(f4); // G:\new.txt
}
File类创建功能
- public boolean createNewFile():当具有该名称的文件部存在时,创建一个由该抽象路径名命名的新空文件
- public boolean mkdir():创建由此抽象路径名命名的目录
- public boolean mkdirs():创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录
注意:文件的创建方法,在创建位置不包含需要创建的文件/目录时,会返回true,如果需要创建的文件/目录已经存在,那么会返回false。判断文件/目录是否存在是根据方法来的,如果使用创建目录的方法来创建一个后缀为 .txt 的文件,那么会创建文件夹成功,文件夹的名字为 filename.txt。同一级目录下不能同名存在,即使一个为文件,一个为文件夹,也是不允许的。
/*
在G盘下创建一个test目录
在test目录下创建一个test.txt文件
在test目录下创建一个JavaSE目录
在test目录下创建一个JavaWeb/JavaScript多级目录
*/
public static void main(String[] args) throws IOException {
File f1 = new File("G:\\test");
System.out.println(f1.mkdir()); // true
File f2 = new File("G:\\test\\test.txt");
System.out.println(f2.createNewFile()); // true
File f3 = new File("G:\\test\\JavaSE");
System.out.println(f3.mkdir()); // true
File f4 = new File("G:\\test\\JavaWeb\\JavaScript");
System.out.println(f4.mkdirs()); // true
}
File类判断和获取功能
- public boolean isDirectory():测试此抽象路径名表示的File是否为目录
- public boolean isFile():测试此抽象路径名表示的File是否为文件
- public boolean exists():测试此抽象路径名表示的File是否存在
- public String getAbsolutePath():返回此抽象路径名的绝对路径字符串
- public String getPath():将此抽象路径名转换为路径字符串
- public String getName():返回由此抽象路径名表示的文件或目录的名称
- public String[] list():返回此抽象路径名表示的目录中的文件和目录的名称字符串数组
- public File[] listFiles():返回此抽象路径名表示的目录中的文件和目录的File对象数组
public static void main(String[] args) throws IOException {
File f1 = new File("G:\\test\\test.txt");
System.out.println(f1.isDirectory()); // false
System.out.println(f1.isFile()); // true
System.out.println(f1.exists()); // true
System.out.println(f1.getAbsolutePath()); // G:\test\test.txt
System.out.println(f1.getPath()); // G:\test\test.txt
System.out.println(f1.getName()); // test.txt
System.out.println("========================");
File f2 = new File("G:\\test");
String[] list = f2.list();
File[] files = f2.listFiles();
for (String s: list) {
System.out.print(s + "\t"); // JavaSE JavaWeb test.txt
}
System.out.println();
for (File file: files) {
System.out.print(file + "\t"); // G:\test\JavaSE G:\test\JavaWeb G:\test\test.txt
}
}
File类删除功能
- public boolean delete():删除由此抽象路径名表示的文件或目录
绝对路径:完整的路径名,不需要任何其他信息就可以定位它所表示的文件,例如:G:/JavaWeb/JavaScript
相对路径:必须使用取自其他路径名的信息进行解释,如:Java/java.txt
**如果一个目录中有内容(文件,目录),那么不能直接删除这个目录,需要将目录下的所有内容删除,才能删除该目录。
递归遍历文件夹下所有内容
要求:遍历指定位置下的所有内容,如果是多级文件,则要把每一层的所有内容都遍历输出
思路:用指定路径创建一个file对象,使用listFiles()方法获取此目录下的所有内容,遍历这个数组,如果为文件夹,则深入遍历,并且输出文件夹名称,如果为文件,则直接输出文件名称
public static void main(String[] args) throws IOException {
File f1 = new File("G:\\test"); // 用G:/test创建对象
File[] files = f1.listFiles(); // 遍历内容,形成数组
if(files != null) { // 不为空则开始遍历
for (File file : files) { // 调用打印方法
printFile(file);
}
}
}
public static void printFile(File file){ // 这是一个递归方法
if(file.isDirectory()){ // 判断是否为文件夹
System.out.println("这是一个文件夹:" + file.getName());
File[] listFiles = file.listFiles(); // 遍历此文件夹下的所有内容
for (File listfiles: listFiles) {
printFile(listfiles); // 实现递归调用
}
}else{ // 这是一个文件
System.out.println("这是一个文件:" + file.getName());
}
}
/*
这是一个文件夹:JavaSE
这是一个文件:JavaEE.txt
这是一个文件夹:JavaWeb
这是一个文件夹:JavaScript
这是一个文件:PHP.txt
这是一个文件:test.txt
*/
/*
实际的目录结构如下:
G:/test 为指定目录,接下来为他的所有内容
JavaSE ———— JavaEE.txt (表示JavaEE.txt 在 JavaSE 文件夹下,JavaSE 在test下)
JavaWeb ———— JavaScript ———— PHP.txt
test.txt
*/
字节流
IO流概述和分类
- IO :输入/输出(Input/Output)
- 流:是一种抽象概念,是对数据传输的总称,也就是收数据在设备间的传输称为流,流的本质是数据传输
- IO流就是用来处理设备间数据传输问题的,常见的应用:文件复制,文件上传,文件下载
分类:
-
按照数据的流向
- 输入流:读数据
- 输出流:写数据
-
按照数据类型
- 字节流
- 字节输入流
- 字节输出流
- 字符流
- 字符输入流
- 字符输出流
- 字节流
如果文件通过记事本打开,我们能够读懂其中的内容,那么就使用字符流读写数据,如果我们读不懂,则使用字节流读取。无法选择使用那种方式读取的时候,使用字节流。
字节流写数据
字节流抽象基类
- InputStream:这个抽象类使表示字节输入流的所有类的超类
- OutputStream:这个抽象类是表示字节输出流的所有类的超类
- 子类名特点:子类名称都以其父类名作为子类名的后缀。****InputStream
使用字节输出流写数据的步骤:
- 创建字节输出流对象(调用系统功能创建了文件,创建字节输出流对象,让字节输出对象指向文件),如果文件已经存在,则不会创建
- 调用字节输出流对象的写数据方法
- 释放资源(关闭文件,释放系统资源),所有IO流操作最后都要释放资源
public static void main(String[] args) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream("G:\\test.txt");
fileOutputStream.write(86); // 这里的86是ASCII码,对应的内容是V
fileOutputStream.close();
}
字节流写数据的三种方式
- void write( int b ):将指定的字节写入此文件输出流,一次写入一个数据
- void write( byte[] b ):将 b 数组写入此文件中,一次写一个字节数组数据
- void write( byte[] b, int off, int len ):将len字节从指定字节数组开始,从偏移量off开始写入此文件输出流,一次写入一个字节数组的部分数据。总共写入len个数据。(如:bys为(A,B,C,D,E),那么 write(bys,1,3) 则会写入 BCD 三个数据, write(bys,2,3) 则会写入 CDE 三个数据)
在写入一串字符串时,如果要自行查ASCII码,会变得非常麻烦,可以使用String的getBytes()方法
String.getBytes():返回字符串所对应的字节数组
byte[] bys = "hello world!".getBytes();
fos.write(bys);
字节流写数据中的换行与追加写入
不同的操作系统所识别的换行符是不一样的。
- windows:\r\n
- Linux:\n
- mac:\r
// 换行
for (int i = 50; i < 60; i++) {
fileOutputStream.write(i);
fileOutputStream.write("\n".getBytes());
}
// 追加写入
// public FileOutputStream ( String name, boolean append )
// 创建文件输出流以指定的名称写入文件
// 如果第二个参数为true,则字节将写入文件的末尾而不是开头
FileOutputStream fileOutputStream = new FileOutputStream("G:\\test.txt" , true );
字节流写数据的异常处理
使用 try catch 语句块将IO操作抓取异常,如果出现异常,结束进程并且释放资源,所以应该使用finally来释放资源
FileOutputStream fos = null;
try{
// IO操作
}catch (IOException e){
e.prtinStackTrace();
}finally{
fos.close
}
而在实际应用中,我们还需要考虑更多,如下
public static void main(String[] args) throws IOException {
FileOutputStream fos = null; // 由于finally要关闭资源,使用fos,所以定义在try catch外面,初始值给null,否则会报空指针异常
try{
fos = new FileOutputStream("X:\\test"); // 没有X盘,创建失败,fos为空
fos.write(97);
}catch (IOException e){
e.printStackTrace();
}finally{
if(fos != null){ // 如果直接使用fos.close,如果在创建文件时失败,那么fos就为空,此时使用fos.close会出现空指针异常
try { // 这个try catch 是因为初始化fos的时候给了 null ,所以存在IO异常
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
字节流读数据
一次性读一个字节数据
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("G:\\test.txt");
int by;
// -1 表示已经到了文件末尾
while((by = fis.read()) != -1){ // 把fis.read()赋值给by,然后判断是否为-1,
System.out.print((char)by); // 使用print输出,是因为它能读取到文件中的换行,会自动换行,输出格式与记事本中格式一致
}
}
一次读一个字节数组数据
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("G:\\新建文本文档.txt");
byte[] bys = new byte[1024]; // 长度一般为1024或者1024的整数倍
int len; // len是实际读取到数据的个数
while((len = fis.read(bys)) != -1){ // 如果为 -1 表示到文件末尾
System.out.print(new String(bys,0,len)); // 从 0 读取到 len
}
fis.close();
}
System.out.print(new String(bys,0,len)); 这一句话非常重要,要设置输出为 0 到 len,这样才会读取多少数据则输出多少数据,不会输出过多的数据。
String( byte[] , offset , int length) 把字节数组的一部分转成字符串
字节流复制文本文件
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("G:\\test.txt");
FileOutputStream fos = new FileOutputStream("D:\\test.txt");
int by;
while((by = fis.read()) != -1){
fos.write((char)by);
}
fis.close();
fos.close();
}
字节流复制图片
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("G:\\图片\\风景素材\\hai.jpg");
FileOutputStream fos = new FileOutputStream("D:\\hai.jpg");
byte[] bys = new byte[1024];
int len;
while((len = fis.read(bys)) != -1){
fos.write(bys,0,len);
}
fis.close();
fos.close();
}
字节缓冲流
- BufferOutputStream:该类实现缓冲输出流,通过设置这样的输出流,应用程式可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用
- BufferedInputStream:创建BufferedInputStream将创建一个内部缓冲区数组,当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节
构造方法
- 字节缓冲输出流:BufferedOutputStream( OutputStream out )
- 字节缓冲输入流:BufferedIutputStream( IutputStream in )
为什么构造方法需要的是字节流,而不是具体的文件或者是路径呢?
- 字节缓冲流仅仅提供缓冲区,而真正的读写数据还得依靠基本的字节流对象进行操作
public static void main(String[] args) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("G:\\新建文本文档.txt"));
byte[] bys = new byte[1024];
int len;
while((len = bis.read(bys)) != -1){
System.out.print(new String(bys,0,len));
}
bis.close();
/*
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("G:\\test.txt"));
bos.write("hello world!".getBytes());
bos.close();*/
}
字节流复制视频
public static void main(String[] args) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("G:\\心灵的声音\\01 - 出击.flv"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("G:\\01 - 出击.flv"));
byte[] bys = new byte[102400];
int len;
while((len = bis.read(bys)) != -1){
bos.write(bys,0,len);
}
bis.close();
bos.close();
}
使用字节缓冲流,效率比其他方式要高
字符流
为什么会出现字符流
一个汉字存储,如果是GBK编码,占用2个字节,如果是UTF-8编码,占用3个字节。不管使用哪种编码,汉字的第一个字节一定是负数,所以使用字节流读写,复制文件时,中文也不会出错
public static void main(String[] args) throws IOException {
String s = "中国";
byte[] bys = s.getBytes("UTF-8");
byte[] bys2 = s.getBytes("GBK");
System.out.println(Arrays.toString(bys)); // [-28, -72, -83, -27, -101, -67]
System.out.println(Arrays.toString(bys2)); // [-42, -48, -71, -6]
}
字符流 = 字节流 + 编码表,方便操作中文
编码表
字符集
- 是一个系统支持的所有字符的集合,包括各国家的文字、数字、标点符号、图形符号等
- 计算机需要准确的存储和识别各种字符集型号,就需要进行字符编码,一套字符集至少有一套字符编码
GBK:最常用的中文码表,是在GB2312(简体中文码表)标准基础上的扩展规范
Unicode字符集:
- 为表达任意语言的任意字符而设计,是业界的一种标准,也称为统一码,标准万国码。有三种编码方案:UTF-8 , UTF-16 ,UTF-32,其中最为常用的为UTF-8。
UTF-8:它使用一至四个字节为每个字符编码,编码规则:
- 128个US-ASCII字符,只需要一o个字节编码
- 拉丁文等字符,需要两个字节编码
- 大部分常用字(包含中文),使用三个字节编码
- 其他极少使用的Unicode辅助字符,使用四字节编码
对应的编码规则要使用相应的解码规则进行解码,否则会乱码。
字符串中的编码解码问题
编码
- byte[] getBytes():使用平台的默认字符集将该String编码为一系列字节,将结果存储到新的字节数组中
- byte[] getBytes( String charsetName ):使用指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中
解码
- String( byte[] bytes ):使用平台的默认字符集解码指定的字节数组来构造新的String
- String( byte[] bytes , String charsetName ):通过指定的字符集解码指定的字节数组来构造新的String
public static void main(String[] args) throws IOException {
String s = "中国";
byte[] bys = s.getBytes("UTF-8");
byte[] bys2 = s.getBytes("GBK");
String gbk = new String(bys, "GBK"); // 不对应解码
System.out.println(gbk); // 涓浗
}
字符流中的编码解码问题
字符流抽象基类
- Reader:字符输入流的抽象类
- Writer:字符输出流的抽象类
字符流中和编码解码问题相关的两个类
- InputStreamReader:使用指定的charset读取字节并将其解码为字符
- OutputStreamWriter:使用指定的charset将写入的字符编码为字节
构造方法:
编码
-
OutputStreamWriter( OutputStream out ):使用默认字符集
-
OutputStreamWriter( OutputStream out , String charsetName):使用指定字符集
解码
-
InputStreamReader( InputStream in ):创建一个使用默认字符集的InputStreamReader
-
InputStreamReader( InputStream in , String charsetName ):创建使用指定字符集
public static void main(String[] args) throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("G:\\test\\test.txt"), "GBK");
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("G:\\test\\test.txt"), "GBK");
osw.write("这是一串中文");
osw.close(); // 注意这个关闭的位置
int ch;
while((ch = isr.read()) != -1){
System.out.print((char)ch);
}
isr.close();
}
如果使用字符流编码解码读取数据,那么输出流必须在读取数据操作之前关闭,否则不会在控制台输出数据
字符流写数据的五种方式
- void write( int c ):写入一个字符
- void write( char cbuf[ ] ):写入一个字符数组
- void write( char[ ] cbuf, int off, int len ):写入字符数组的一部分
- void write( String str ):写入一个字符串
- void write( String str, int off, int len ):写入一个字符串的一部分
void flush():刷新流
void close():方法会在关闭流之前先进行刷新
流一旦被关闭,就不能再调用进行读取或写入数据
字符流读数据的两种方式
- int read():一次读一个字符数据
- int read( char[] cbuf ):一次读一个字符数组数据
字节流读数据与字符流读数据格式是一样的,只不过前者是读字节,后者是读字符
字符流的便捷类
当我们不用指定编码,只使用默认编码时,可以使用转换流的子类进行操作
-
FileReader:用于读取字符文件的便捷类
FileReader( String fileName )
-
FileWriter:用于写入字符文件的便捷类
FileWriter( String fileName )
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("G:\\test\\test.txt");
// 相当于如下语句
// InputStreamReader isr = new InputStreamReader(new FileInputStream("G:\test\test.txt"));
FileWriter fw = new FileWriter("G:\\test.txt");
// OutputStreamWriter ows = new OutputStreamWriter(new FileOutputStream("G:\\test.txt"));
char[] chr = new char[1024];
int len;
while((len = fr.read(chr)) != -1){
fw.write(chr,0,len);
}
fr.close();
fw.close();
}
字符缓冲流
- BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可指定缓冲区大小,一般使用默认大小( 8192 )
- BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可指定缓冲区大小,一般使用默认大小。
构造方法
- BufferedWriter( Writer out )
- BufferedReader( Reader in )
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("G:\\test\\test.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("G:\\test.txt"));
char[] chr = new char[1024];
int len;
while((len = br.read(chr)) != -1){
bw.write(chr,0,len);
}
br.close();
bw.close();
}
字符缓冲流的特有功能
BufferedWriter
- void newLine():写一行行分隔符,行分隔符字符串由系统属性定义( Linux : \n Mac : \r)
BufferedReader
- public String readLine():读一行文字,结果包含行的内容的字符串,不包括任何终止字符,如果流的结尾已经到达,则为null
BufferedWriter bw = new BufferedWriter(new FileWriter("G:\\test.txt"));
for(int i = 0 ; i < 10 ; i++ ){
bw.write("hello" + i); // 写数据的三个步骤
bw.newLine();
bw.flush();
}
bw.close();
BufferedReader br = new BufferedReader(new FileReader("G:\\test\\test.txt"));
String line;
while((line = br.readLine()) != null){
System.out.println(line); // 注意此处使用了println,是因为readLine()不会读取换行符,需要手动换行
}
br.close();
使用 readLine() 写数据三步骤:①.写入数据 ②.写入换行符 ③.刷新流
最常用的字符流复制文件方法
如果复制照片以及视频文件,会存在无法正常开启的现象
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("G:\\test\\test.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("G:\\test.txt"));
String line;
while((line = br.readLine()) != null){
bw.write(line);
bw.newLine();
bw.flush();
}
br.close();
bw.close();
}