黑马程序员 Java基础IO流之File类

时间:2023-02-18 20:34:22

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

File的概述

流操作的是数据,但是不能操作文件夹,或者文件的属性,这时Java就为我们提供了一个File类,来为方便我们对文件或者文件夹的属性进行操作。

演示实例:

import java.io.*;
class JavaCollection1_80
{
public static void main(String[] args)
{
//这里使用的是相对路径,将a.txt封装成对象。可以将已有或者未出现文件或者文件夹封装成对象。
File f1=new File("a.txt");
//第一个参数是父目录,第二个参数是子目录
File f2=new File("d:\\","b.txt");
File d=new File("d:\\");
//将一个File对象作为参数传递给一个子对象。
File f3=new File(d,"a.txt");
//在不同的操作平台下,使用的文件分隔符不相同,为了使用相同保持平台的一致性,所以我们可以使用File.seperator()来表示分隔符,
File f4=new File("d:"+File.separator+"text"+File.separator+"a.txt");
sop(f1);
sop(f2);
sop(f3);
sop(f4);
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
运行结果如下:

黑马程序员 Java基础IO流之File类
了解如何是用File来封装对象,接下来我们可以使用Fil类来进行文件的操作

1 文件的创建  boolean createNewFile() //因为调用的是底层的资源,所以就这个方法就会抛出IOException,使用这个方法就会在指定的位置创建文件,如果文件已经存在,就创建失败,如果文件不存在,就会创建成功。而使用流就会直接创建文件,如果文件已经存在,就会覆盖已经存在的文件。

2删除文件 boolean delete()//使用这个方法删除指定位置的文件,如果删除成功,那么返回值是true,如果此时有指定的流在操作文件,那么这个删除就有可能失败,

   void deleteOnExit()//使用这个方法指定当虚拟机结束的时候就会删除文件,一定会删除成功。

3判断文件是否存在:

boolean exists()//判断文件是否存在。

boolean canexecute()//文件是否可执行

boolean isFile()//判断file对象时是否是文件

boolean isDirectory()//判断file对象是否是目录

boolean isHidden()//判断file对象是否是隐藏文件  

4获取文件的信息:

static String getAbsolutePath()//获取文件的绝对路径

static String getPath()//获取文件的相对路径。

static String getName()//获取文件或者是目录的名称

static String getParent()//获取文件或者目录的父目录,如果该文件没有父目录,那么就返回null(如果File对象封装的是相对路径);

static long lastModified()//获取文件最后一次修改的时间。

boolean renameTo(File dest)//文件的重命名

实例代码如下:

import java.io.*;
class JavaCollection1_81
{
public static void main(String[] args)throws IOException
{
method_create();
method_delete();
}
//文件的创建
public static void method_create()throws IOException
{
File f1=new File("d:\\JavaDemo\\IODemo\\a.txt");
sop(f1.createNewFile());
}
//文件的删除
//1 delete
public static void method_delete()throws IOException
{
File f2=new File("d:\\Javademo\\IODemo\\aaa.txt");
//立即删除文件的操作,不一定会成功。
sop(f2.delete());
File f1=new File("d:\\JavaDemo\\IODemo\\a.txt");
//虚拟机结束时就删除文件。
f1.deleteOnExit();
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
实例代码:

import java.io.*;
class JavaCollection1_82
{
public static void main(String[] args)
{
//method_1();
//method_2();
method_3();
method_4();
}
//判断文件是否存在。
public static void method_2()
{
File f2=new File("text.mp3");
sop(f2.exists());

}
//判断file兑现是文件还是目录
public static void method_4()
{
File f3=new File("text.mp3");
File dir=new File("txt_dir");
//在判断是否是文件或者是目录时,必须先判断文件是否存在。
if(dir.exists())
{
//判断是否是文件
sop(dir.isFile());
//判断是否是目录
sop(dir.isDirectory());
//判断文件是否是隐藏文件。
sop(dir.isHidden());
}
}
public static void method_3()
{
/*创建目录(mkdir只能创建一级目录)*/
File dir=new File("txt_dir");
sop(dir.mkdir());
//File dirs=new File("txt_dir\\1\\2\\3\\4\\5");
//File.mkdirs()用来创建多级目录。
//sop(dirs.mkdirs());
}
public static void method_1()
{
File f1=new File("txtmm.jpg");
//判断文件是否是可执行。
sop(f1.canExecute());

}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
实例练习:

import java.io.*;
class JavaCollection1_83
{
public static void main(String[] args)
{
//method();
method_2();
}
public static void method_2()
{
File f_source=new File("D:\\JavaDemo\\IODemo\\text.mp3");
File f_destination=new File("D:\\JavaDemo\\IODemo\\text_rename.mp3");
//如果需要重命名的File对象在同一个文件下,那么久相当于重命名,如果两个File对象的路径不在一个文件下,那么就相当于是剪切。
sop(f_source.renameTo(f_destination));
}
public static void method()
{
File f1=new File("text.mp3");
sop(f1.getPath());//getPath,如果File封装的是相对路径,那么就返回相对路径,如果封装的是绝对路径,那么就返回绝对路径。
sop(f1.getAbsolutePath());//getAbsolutePath()//只是返回绝对路径。不管file对象封装的是相对路径,还是绝对路径。
sop(f1.getParent());//该方法时是返回绝对路径下的父目录,如果是相对路径,那么就返回Null,如果相对路径中有上一层目录,那么该目录就是返回结果。
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
//byte short int long floate double boolean char
//在文件操作时,我们往往需要列出可用的系统盘符 Java为我们提供了File对象的listRoots()方法来获取当前平台下可用的盘符。

//String[] list该方法返回的是指定目录下的所有的文件。(同时也包含隐藏文件和非隐藏文件);
代码如下:

import java.io.*;
class JavaCollection1_84
{
public static void main(String[] args)
{
method_1();
method_2();
}
//listRotts()返回的是当前系统平台下所有可用的盘符。
public static void method_1()
{
File[] files=File.listRoots();
for(File file : files)
{
System.out.println(file);
}
}
//该方法返回指定路径下所有的目录或者是文件。注意这里的File对象必须封装的是目录,而不能是文件。如果是文件,那么就会抛出异常。
public static void method_2()
{
File fi=new File("d:\\");
String[] strings=fi.list();
for(String file : strings)
{
System.out.println(file);
}
}
}
运行结果如下:

黑马程序员 Java基础IO流之File类
有时候,我们需要过滤指定的文件类型,这时我们查阅文档,发现javaFile类帮我们提供了list(FileNameFilter filter);

而FIleNameFilter是一个接口,而这个接口中只有一个方法,accept(File dir,string Name)

使用这个方法就可以帮我们过滤指定的文件类型。

实例:指定文件夹,并返回该文件下指定后缀名为.txt的文件。并返回文件前缀是JavaCollection1_的文件

代码如下:

import java.io.*;
class JavaCollection1_85
{
public static void main(String[] args)
{
//这里是覆盖FilenameFilter接口中的accept()方法,调用的是list方法,返回的是一个字符串类型的数组,存储的只是文件的文件名,而不是文件这个对象。


method_1();
sop("***********************************");
method_2();
}
public static void method_1()
{

File files=new File("d:\\JavaDemo\\IODemo\\");//指定需要过滤的文件夹//覆盖FileNameFilter这个接口的accept()方法来过滤指定后缀名的文件。
//如果指定的文件类型符合条件,那么返回true,如果返回为false,表示该文件被过滤掉了,使用了匿名内部类。
String[] strings=files.list(
new FilenameFilter()
{
public boolean accept(File dir,String filename)
{
//sop(dir);//通过打印我们发现这个dir就是上面我们指定要过滤的文件夹。而filename则指的是要过滤文件下所有的文件。
//sop(filename);
//过滤后缀名为.txt的文件,如果符合,就返回true,否则返回false;
return filename.endsWith(".txt");
}
}

);
for(String str:strings)
{
sop(str);
}
}
public static void sop(Object obj)
{
System.out.println(obj);
}
//这里也是用的覆盖FilenameFilter这个接口中的accept这个方法来过滤指定的文件类型。但是这里使用的是listFiles()这个方法,返回的是一个File类型的数组,可以对File对象进行操作。
public static void method_2()
{
File files_1=new File("d:\\JavaDemo\\IODemo\\");
File[] files=files_1.listFiles(
new FilenameFilter()
{
public boolean accept(File dir,String filename)
{
return filename.startsWith("JavaCollection1_");
}
}

);
for(File file:files)
{
sop(file.getName()+"------"+file.length());
}
}
}

运行结果如下:

黑马程序员 Java基础IO流之File类

下面使用递归方法来获得当前目录及其子目录下所有的文件名,并打印到控制台

import java.util.*;
import java.io.*;
class JavaCollection1_86
{
public static void main(String[] args)
{
File dir=new File("D:\\Exercise\\bin\\");
getFileName(dir);
}
public static void sop(Object obj)
{
System.out.println(obj);
}
//使用递归获得一个一个目录及其子目录下所有的文件。
public static void getFileName(File dir)
{
sop(dir);
File[] files=dir.listFiles();
for(int i=0;i<files.length;i++)
{
if(files[i].isDirectory())
{
getFileName(files[i]);
}
sop(files[i].getName());
}
}
}
运行结果:

黑马程序员 Java基础IO流之File类


有时候我们在操作文件时,需要删除目录以及目录中所有的子目录以及文件。

这时我们需要采用递归的形式来删除,我们删除的方法是先删除子目录中的文件,然后目录,从里层到外层,由里向外层层的删除。

代码如下:

import java.io.*;
class JavaCollection1_87
{
public static void main(String[] args)
{
File dir=new File("D:\\txtdir\\");
deleteAllFile(dir);
}
public static void deleteAllFile(File dir)
{
File[]files=dir.listFiles();
for(int i=0;i<files.length;i++)
{
if(files[i].isDirectory())
{
deleteAllFile(files[i]);
}
else
{
//删除目录下的文件
sop("file:"+files[i]+"---"+files[i].delete());
}
sop("dir:"+dir+"-----"+dir.delete());
}

}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
运行结果如下:

黑马程序员 Java基础IO流之File类

在文件操作中,我们往往想要在指定的目录下的指定类型的文件的绝对路径存储到一个文件中,制作一个文件清单列表

思路:

1对当前的目录进行递归

2获取递归过程中的所有的指定类型的文件的绝对路径

3将绝对路径存储到一个集合中

4将集合中的中的绝对路径存储到文件中

代码如下:

import java.util.*;
import java.io.*;
class JavaCollection1_88
{
public static void main(String[] args)
{
File dir=new File("D:\\JavaDemo\\Collection\\");
List<File> list=new ArrayList<File>();
FileSavaToList(dir,list);

File dest=new File(dir,"javalist.txt");
try
{
listSaveToFile(list,dest.toString());
//sop(list);
}
catch (IOException e)
{
throw new RuntimeException(e.getMessage());
}

}
//将指定目录下的指定文件类型存储到集合中
public static void FileSavaToList(File dir,List<File> list)
{
File[]files=dir.listFiles();
for(int i=0;i<files.length;i++)
{
if(files[i].isDirectory())
{
FileSavaToList(files[i],list);
}
else
{
if(files[i].getName().endsWith(".java"))
{
list.add(files[i]);
}
}
}
}
//将集合中的文件路径存储到文件中
public static void listSaveToFile(List<File>list,String path) throws IOException
{
FileWriter fw=null;
BufferedWriter bfw=null;
try
{
fw=new FileWriter(path);
bfw=new BufferedWriter(fw);
for(File f : list)
{

bfw.write(f.getAbsolutePath());
bfw.newLine();
bfw.flush();
}
}
catch (IOException e)
{
throw new RuntimeException("文件写入失败");
}
finally
{
try
{
if(bfw!=null)
bfw.close();
}
catch (IOException e)
{
throw new RuntimeException("字符读取流缓冲区关闭失败");
}
}
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
运行结果如下:

黑马程序员 Java基础IO流之File类

Properties类

Properties是HashTable的子类,所以它也具备了map集合的特性,里面存储的是键值对,存储的是字符串类型的,

Properties是集合类和IO流两种技术相结合的一种集合容器。

该对象的特点:可以用于键值对类型的配置信息。

Properties获取设置,遍历集合中所有的键值对。

代码如下:

import java.io.*;
import java.util.*;
class JavaCollection1_89
{
public static void main(String[] args)
{
method_Properties();
}
public static void method_Properties()
{
//map集合的子类,键值对,存储的都是String类型的
Properties pro=new Properties();
//设置键值对
pro.setProperty("张三","24");
pro.setProperty("李四","30");
pro.setProperty("王五","80");
sop(pro.getProperty("张三"));
Set<String> spn=pro.stringPropertyNames();
for(String str :spn)
{
sop(str+"=="+pro.getProperty(str));
}
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
运行结果如下:

黑马程序员 Java基础IO流之File类

想要将集合中的键值数据存储到存到集合,并进行操作,

代码如下:

思路: 1用一个流和文件进行关联

     2读取一行数据,然后用等号将这个行数据切割

   3然后将的等号的左边作为键,然后将等号的右边作为值存储到集合中

代码如下:

import java.io.*;
import java.util.*;
class JavaCollection1_90
{
public static void main(String[] args)throws IOException
{
//laodDemo();
method_load();
}

//Properties的load方法的原理
public static void laodDemo()throws IOException
{
BufferedReader bfr=new BufferedReader(new FileReader("d:\\javademo\\IODemo\\demo.txt"));
Properties p=new Properties();
String line=null;
while((line=bfr.readLine())!=null)
{
String[] s=line.split("=");
p.setProperty(s[0],s[1]);
}
Set<String> spn=p.stringPropertyNames();
for(String s :spn)
{
sop(s+"="+p.getProperty(s));
}
}
public static void method_load()throws IOException
{
//将文件和流进行关联
FileInputStream bfr=new FileInputStream("d:\\javademo\\IODemo\\demo.txt");
Properties pro=new Properties();
//从输入流中读取属性列表(键值对);
pro.load(bfr);
//获取Properties中所有的键值。
//通过键值我们就可以获取Properties中的键所对应的值。
Set<String> spn=pro.stringPropertyNames();
for(String s :spn)
{
sop(s+"="+pro.getProperty(s));
}
//我们可以通过修改Properties中的数据,并保存到文件中去
//store方法必须和一个写入流进行关联
pro.setProperty("zhangsan","84");
FileOutputStream fos=new FileOutputStream("d:\\javademo\\IODemo\\demo.txt");
//将修改后的Properties中的数据保存到关联的文件中。
pro.store(fos,"edit by xiongtao");

}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
运行结果如下:

黑马程序员 Java基础IO流之File类
在生活中,我们往往会出现这种状态,就是有一些软件会有一个试用期,如果超过了试用期,那么就会让我们购买软件,而往往这种软件就是采用将用户使用软件的次数放在配置文件中,然后每次使用软件的时候加载配置文件,读取这个次数。一点次数超过了,软件就会报提示。

需求:使用Properties记录应用程序使用的次数,如果使用次数,超过次数,需要注册。

代码如下:

/*自定义配置文件,用于存储应用程序的信息*/
import java.io.*;
import java.util.*;
class JavaCollection1_91
{
public static void main(String[] args) throws IOException
{
Properties pro=new Properties();
File f=new File("count1.ini");
if(!f.exists())
f.createNewFile();
FileInputStream fis=new FileInputStream(f);
pro.load(fis);
int count=0;
String val=pro.getProperty("time");
if(val!=null)
{
System.out.println(val);
count=Integer.parseInt(val);
if(count>=5)
{
System.out.println("试用期已过,请付费");
return;
}
}
count++;
pro.setProperty("time",count+"");
FileOutputStream fos=new FileOutputStream(f);
pro.store(fos,"edit by xiongtao");
fis.close();
fos.close();
}
}
运行结果如下:

黑马程序员 Java基础IO流之File类
接下来,我们来学习一下IO包中的另一个流打印流,为其他的流添加了功能,能够打印不同的数据。

对基本数据类型能够进行能够原样打印。该流提供了打印方法,

打印流的分类:PrintStream ,构造函数 可以接受的参数对象

1 File对象 

2字符串路径

3字节输出流

PrintWriter能够接受的参数数据类型

1 File对象

2字符串文件路径

3字节输出流

4字符输出流

实例演示:

将键盘中数据打印到控制台,打印到文件中。

代码如下:

import java.io.*;
class JavaCollection1_92
{
public static void main(String[] args)throws IOException
{
/*BufferedReader bfr=new BufferedReader(new InputStreamReader(System.in));
//将键盘中输入的字符串打印到控制台。
//注意下面PrintWriter打印流的第二个构造参数就是指定打印流是否能够自动刷新缓冲区
PrintWriter pw=new PrintWriter(System.out,true);
String line=null;
while((line=bfr.readLine())!=null)
{
if(line.equals("over"))
break;
//注意打印流也是使用的缓冲区,所以需要每次手动进行刷新操作。
pw.print(line.toUpperCase());//这里使用的pw.print就不能进行刷新.
//pw.flush();
}
pw.close();//关闭打印流
*/
method_1();
}
public static void method_1()throws IOException
{
BufferedReader bfr=new BufferedReader(new InputStreamReader(System.in));
//使用打印流,将键盘上输入的东西打印到文件中去。
PrintWriter pw=new PrintWriter(new BufferedWriter(new FileWriter("a.txt")),true);
String line=null;
while((line=bfr.readLine())!=null)
{
pw.println(line);//因为使用了自动刷新,所以这里就不需要手动进行刷新了,但是前题是我们必须给自动刷新提供表示,这里的println printf format就是自动刷新标示,当程序遇到println这种标记时,程序就会自动刷新。
}
//打印流在关闭时会自动刷新一次。
pw.close();
}
}
在文件操作中我们往往需要将多个文件中的文件写入到一个文件中,那么这时候Java就为我们提供了一个SequenceInputStream这个合并流,能够将多个读取流合并成一个读取流,然后进行操作。

实例代码如下:

import java.util.*;
import java.io.*;
class JavaCollection1_93
{
public static void main(String[] args)throws IOException
{
//在Vector中存储多个字节读取流对象。
Vector<FileInputStream>vec=new Vector<FileInputStream>();
vec.add(new FileInputStream("1.txt"));
vec.add(new FileInputStream("2.txt"));
vec.add(new FileInputStream("3.txt"));
//在jdk1.0版本中,我们使用Enumeration来进行集合的遍历。
Enumeration<FileInputStream> en=vec.elements();
SequenceInputStream sis=new SequenceInputStream(en);
//这里将多个字节读取流合并成一个读取流,SequenceInputStream起到了合并流的作用。
/*FileOutputStream fos=new FileOutputStream("4.txt");
int len=0;
byte[]by=new byte[1024];
//使用字节数组作为缓冲区,来存储数据
while((len=sis.read(by))!=-1)
{
fos.write(by,0,len);

}
*/
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("4.txt"));
byte[]by =new byte[1024];
int len=0;
while((len=sis.read(by))!=-1)
{
bos.write(by,0,len);
}
sis.close();
bos.close();
}
}
运行结果如下:

黑马程序员 Java基础IO流之File类
在对文件操作时,我们往往需要对文件进行分割操作,也就是将一个文件分成多个,这就意味着一个读取流对应三个写入流。

代码如下:

import java.io.*;
import java.util.*;
class JavaCollection1_95
{
public static void main(String[] args)throws IOException
{
/*切割文件的原理就是一个读取流对应多个写入流。将一个文件写入到多个文件中
//源文件。
BufferedInputStream bis=new BufferedInputStream(new FileInputStream("C:\\Users\\Public\\Music\\Sample Music\\Sleep Away.mp3"));
//目的文件
BufferedOutputStream bos=null;
byte[]by=new byte[1024*1024];
int count=0;
int len;
while((len=bis.read(by))!=-1)
{
bos=new BufferedOutputStream(new FileOutputStream("d:\\javademo\\IODemo\\"+(count++)+".part"));
bos.write(by,0,len);
}
bis.close();
bos.close();
*/
combineFile();

}
public static void combineFile()throws IOException
{

ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();

for(int x=0; x<=4; x++)
{
al.add(new FileInputStream("d:\\javademo\\IODemo\\"+x+".part"));
}

final Iterator<FileInputStream> it=al.iterator();
//实现Enumeration接口,并重写里面的两个方法 hasMoreElements和nextElement();
Enumeration<FileInputStream> en = new Enumeration<FileInputStream>()
{
public boolean hasMoreElements()
{
return it.hasNext();
}
public FileInputStream nextElement()
{
return it.next();
}
};
SequenceInputStream sis=new SequenceInputStream(en);
BufferedOutputStream fos =new BufferedOutputStream(new FileOutputStream("love.mp3"));
byte[] by=new byte[1024];
int len=0;
while((len=sis.read(by))!=-1)
{
fos.write(by,0,len);
}
fos.close();
sis.close();
}
}
文件和的合并和分割

对象的序列化:java可以允许我们在内存中创建对象,但是一旦jvm关闭后,对象就会从内存中消失,因为它的生命周期不可能比jvm还长。但是在有些情况下,我们希望能够长期的存储对象,下一次直接从文件中读取这些对象。不用又去创建,那么java对象的序列化就帮我们提供了这个功能。对象的序列化就是保存对象的状态,也就是对象的成员,因此对于方法区的静态数据就不会进行关注。在java中,如果想要对象进行序列化,那么只需要该对象实现Serializable这个接口即可,我们不需要实现这个接口中的方法。因为这个对象就是一个标记码,在类中会生成一个uid,用来表示对象的唯一,而这个uid是根据类的成员来生成的。

1Java中为我们提供了一类,也就是ObjectOutputStream这个类来将对象写入到一个OutputStream对象中通过在流中使用文件来实现文件的序列化,而同样我们可以使用ObjectInputStream这个类来实现对象的反序列化,也就是从文件中将对象加载到内存中。

演示实例如下:

import java.io.*;
class JavaCollection1_96
{
public static void main(String[] args)
{
try
{
//method_ObjectOutPutStream();
method_ObjectInputStream();
}
catch (IOException e)
{
throw new RuntimeException(e.toString());
}
catch(Exception e)
{
throw new RuntimeException(e.toString());
}

}
public static void method_ObjectOutPutStream()throws IOException
{
//对象的序列化,将文件存储到一个文件中;
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("text.object"));
oos.writeObject(new Person("zhangsan",34,"yc"));//将对象序列化存储到文件中
oos.close();
}
//对象的反序列化
public static void method_ObjectInputStream()throws IOException,ClassNotFoundException
{
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("text.object"));
//对象的反序列化
Person p=(Person)ois.readObject();
System.out.println(p);

}
}


//注意这个Serializable这个接口没有重写任何方法,这就是一个标记接口。这个接口会让类产生一个UId表示。也就是序列号,java编译器也就是通过这个序列号来判断该类和对象是不是用的同一个Uid。
import java.io.*;
class Person implements Serializable
{
//static final long serialVersionUID = 42L;手动的添加一个序列号,这样修改类中的成员,其他类中之间调用也不会包错。
private String name;//UID的生成就是根据成员来的,它不会关注静态的成员,也就是不会序列化静态成员
//使用transient这个关键字来防止被序列化,
//当某个字段被声明为transient后,默认序列化机制就会忽略该字段。此处将Person类中的age字段声明为transient,所以在结果中就是i零,如果是引用类型的就是null

transient privateint age=23;
static String country="yichang";
Person(String name,int age,String country)
{
this.name=name;
this.age=age;
this.country=country;
}
public String toString()
{
return name+"-"+age+":"+country;
}
}
在以前读取流和写入流之间是没有关系的,我们往往中间需要一个中转站,写入流将数据写到中转站中,读取流在中转站中将数据读取到读取流中,那么我们可不可以不用中转站呢,直接操作两个流呢,Java中,为我们提供了管道流PipedInputStream和PipedOutputStream这两个流,使用connet这个方法将两个流连接起来,但是
通常,数据由某个线程写入 PipedOutputStream 对象,并由其他线程从连接的 PipedInputStream 读取。不建议对这两个对象尝试使用单个线程,因为这样可能会造成该线程死锁。

接下来我们来演示管道流的实例:

//使用两个单独的线程来操作文件,使用一个线程将数据写入到PipedOutputStream,然后使用PipedInputStream管道流连接,然后在使用另一个线程将数据从PipedInputStream中读取。
import java.io.*;
class JavaCollection1_97
{
public static void main(String[] args)throws IOException
{
PipedInputStream pis=new PipedInputStream();
PipedOutputStream pos=new PipedOutputStream();
//使用connect()这个方法将管道写入流和管道读取流连接起来。
pis.connect(pos);
write w=new write(pos);
read r=new read(pis);
new Thread(w).start();
new Thread(r).start();
}

}
//一个线程来将数据写入到PipedOutputStream中
class write implements Runnable
{
private PipedOutputStream pos;
write(PipedOutputStream pos)
{
this.pos=pos;
}
public void run()//run方法中异常只能try,不能抛出。
{
try
{
Thread.sleep(6000);
System.out.println("程序等待六秒钟后开始写入数据");
byte[] by="xiongtao".getBytes();
pos.write(by);
pos.close();
}
catch(InterruptedException e)
{
System.out.println(e.getMessage());
}
catch (IOException e)
{
throw new RuntimeException("管道输出流写入失败");
}

}
}
class read implements Runnable
{
private PipedInputStream pis;
read(PipedInputStream pis)
{
this.pis=pis;
}
public void run()
{
try
{
System.out.println("读取数据前,管道读取流处于阻塞状态");
byte[] by=new byte[1024];
int len=0;
len=pis.read(by);
String s=new String(by,0,len);
System.out.println(s);
System.out.println("读取数据后,阻塞状态解除");
}
catch (IOException e)
{
throw new RuntimeException("管道读取流读取失败");
}
}
}
运行结果如下:

黑马程序员 Java基础IO流之File类
RandomAccessFile该类不算是IO体系中的子类,而是直接继承Object类,但是它是IO中的成员,内部封装了一个数组,内部通过指针对数组进行操作,通过getFilePointer来获取指针的位置,通过来Seek来改变指针的位置。其实内部封装了字节输入流和字节输出流。该类有局限性,该类只能操作文件。而且操作文件和模式。

演示实例代码:

import java.io.*;
class JavaCollection1_98
{
public static void main(String[] args)
{
try
{
//read();
//write();
writeMethod();
}
catch (IOException e)
{
throw new RuntimeException("文件写入失败");
}
}
public static void read() throws IOException
{
RandomAccessFile faf=new RandomAccessFile("ran.txt","r");//指定为只读格式模式。
//byte[] by=new byte[4];
//faf.read(by);
//int age=faf.readInt();//readInt()方法会自动读取四个字节然后在转换成Int类型的,因为Int类型的是四个字节的吗,存储在RandomAccessFile中也是存储四个字节。
//System.out.println(new String(by));
//System.out.println(age);
//调整对象中的指针,指定指针的位置
/*faf.seek(8);//将指针的位置设置为8;可以指定任意位置。指针可以向前,也可以向后。
byte[]by=new byte[4];
faf.read(by);
System.out.println(new String(by));
int age=faf.readInt();
System.out.println(age);
//faf.write("李四".getBytes());//所以不允许写入写入数据。*/
faf.skipBytes(8);//指针可以向前跳过8个字节,不能往回跳。
byte[] by=new byte[4];
faf.read(by);
System.out.println(new String(by));
System.out.println(faf.readInt());
faf.close();
}
public static void write()throws IOException
{
RandomAccessFile faf=new RandomAccessFile("ran.txt","rw");//只读,r 读写rw,
//该对象的构造函数要操作的文件不存在会自动创建,如果存在则不会覆盖。如果模式是r,不会创建文件,会读取一个存在的文件,如果该文件不存在,则会报异常。如果模式为rw,那么操作,构造函数的操作不会覆盖原文件。
//faf.write("李四".getBytes());
//faf.write(198);//使用write方法,就只会写入二进制的后八位,因此如果超过了八位就会造成数据的丢失。所以RandomAccessFile提供的writeInt方法来防止数据的丢失。
faf.write("李四".getBytes());//一个汉字是两个字节
faf.writeInt(97);
faf.write("王五".getBytes());
faf.writeInt(96);
faf.close();
}
public static void writeMethod() throws IOException
{
RandomAccessFile faf=new RandomAccessFile("ran.txt","rw");
faf.write("赵六".getBytes());
faf.writeInt(98);
faf.seek(8*3);
faf.write("不在乎".getBytes());
faf.writeInt(99);
}
}
//通过上面的实例我们发现,随机访问文件可以对文件的随机创建,修改,访问,等操作。
//随机读写访问,可以通过seek()来指定来指针的位置,从而可以用多个线程来向一个文件中写入同一个文件,这样来实现多线程分段下载。
//一个线程会负责一段文件的下载。多个线程之间不会互相干扰。
通过上面的实例,我们可以发现可以使用RandomAccessFile来访问文件的任意位置。但是平时我们需要操作不同的基本数据类型。比如Int,double,boolean类型的数据,那么java为我们提供了DataInputStream,DataOutStream来操作不同的数据类型。

接下来我们来演示实例:

import java.io.*;
class JavaCollection1_99
{
public static void main(String[] args) throws IOException
{
//write();
//read();
/*WriteUTF();//使用经过修饰的UTF模式//一个汉字有四个字节。使用DataOutputStream这个方法的就不能用转换流进行读取,必须使用DataInputStream类的readUTF()这个方法来进行读取。
writeUTF();//使用普通的UTF-8编码//一个汉字三个字节
writeGBK();//使用GBK编码 //一个汉字两个字节
*/
readUTF();
}
public static void write()throws IOException
{
//将基本类型的数据写入到一个文件中,DataOutputStream用来操作基本数据类型的。
DataOutputStream dos=new DataOutputStream(new FileOutputStream("data.txt"));
dos.writeInt(12);//四个字节
dos.writeBoolean(true);//一个字节
dos.writeDouble(13.21);//8个字节
}
public static void read()throws IOException
{
DataInputStream dis=new DataInputStream(new FileInputStream("data.txt"));
int num=dis.readInt();
boolean tbool=dis.readBoolean();
double dnum=dis.readDouble();
System.out.println(num);
System.out.println(tbool);
System.out.println(dnum);
}
public static void WriteUTF()throws IOException
{
DataOutputStream dos=new DataOutputStream(new FileOutputStream("xsUTF.txt"));
dos.writeUTF("你好");//使用UTF编码,将字符串写入到一个基础类型输出流中去。使用这个方法和普通的UTF格式流不一样。是经过修饰的
}
public static void writeUTF()throws IOException
{
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("utf-8.txt"),"utf-8");
osw.write("你好");
osw.close();
}
public static void writeGBK()throws IOException
{
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("gbk.txt"),"GBK");
osw.write("你好");
osw.close();
}
public static void readUTF()throws IOException
{
DataInputStream dis=new DataInputStream(new FileInputStream("xsUTF.txt"));
String s=dis.readUTF();
System.out.println(s);
}
}
除了能够操作基本数据类的流和操作对象的流,接下我们来演示操作字节数组的流对象。主要使用于操作内存的数组。

源设备:键盘(System.in),硬盘(FileInputStream),内存(ByteArrayInputStream)

目的设备:控制台(System.out),硬盘(FileOuputStream),内存(ByteArrayOuputStream)

因为字节数组流对象时操作的是数组,所以没有调用底层的方法,所以就不需要进行资源的关闭.不涉及底层资源的操作。所以就不用进行抛出,所以也不需要进行关闭。

演示实例代码:


/*ByteArrayInputStream 在构造的时候,需要接受数据源,而且数据源是一个字节数组
ByteArrayOutputStream 在构造的时候,不需要定义数据源,因为在内部已经封装了一个长度可变的数组。
因为两个流对象操作的是数组,两个对象都是操作的是内存对象,所以不用进行close方法,而且流对象还是会一直使用
*/
import java.io.*;
class JavaCollection1_100 
{
<span style="white-space:pre"></span>public static void main(String[] args) throws IOException
<span style="white-space:pre"></span>{
<span style="white-space:pre"></span>//数据源是一个字符数组
<span style="white-space:pre"></span>ByteArrayInputStream bai=new ByteArrayInputStream("xiongtao".getBytes());
<span style="white-space:pre"></span>//不用数据源,而且这两个方法没有调用底层的资源,所以就不需要进行抛出异常。
<span style="white-space:pre"></span>ByteArrayOutputStream bas=new ByteArrayOutputStream();
<span style="white-space:pre"></span>int len=0;
<span style="white-space:pre"></span>while((len=bai.read())!=-1)
<span style="white-space:pre"></span>{
<span style="white-space:pre"></span>bas.write(len);
<span style="white-space:pre"></span>}
<span style="white-space:pre"></span>System.out.println(bas.size());
<span style="white-space:pre"></span>System.out.println(bas.toString());
<span style="white-space:pre"></span>//我们可以通过字节数组流最后将内容一次性的写入到文件中
<span style="white-space:pre"></span>/*File f=new File("aaa.txt");
<span style="white-space:pre"></span>if(!f.exists())
<span style="white-space:pre"></span>f.createNewFile();
<span style="white-space:pre"></span>*/
<span style="white-space:pre"></span>//只有字节数组写入流会报IOException这个异常。其他的方法不会报异常。
<span style="white-space:pre"></span>bas.writeTo(new FileOutputStream("aaa.txt"));
<span style="white-space:pre"></span>}
}
运行结果如下:

黑马程序员 Java基础IO流之File类
大家都知道加入字符流是为了更好的操作的数据,InputStreamReader和OuputStreamWrite在构造时,就可以传入编码字符集,为了让计算机能够识别各个国家的语言,所以才出现字符编码。

常见的字符编码:ASCILL美国标准信息交换码。用一个字节的7位表示。

ISO8859-1 拉丁码表,欧洲的编码表,用一个字节的8位表示。

GB2312:中国的中文编码,

GBK:中国的中文编码表升级,融合了更多的中文字符。

UnICode:国际标准码:包括各种文字。所有的文字都是用两个字节来表示,Java默认使用的编码就是UniCode来表示。

实例代码如下:

import java.io.*;
class JavaCollection101
{
public static void main(String[] args)throws IOException
{
method_write();
method_read();
}
public static void method_write()throws IOException
{
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("demo.txt"),"UTF-8");
osw.write("你好");//默认的是GBK编码形式。一个汉字用两个字节来表示比如你好的编码形式是 -12 -22
// -33 -23
osw.close();
}
public static void method_read()throws IOException
{
InputStreamReader isr=new InputStreamReader(new FileInputStream("demo.txt"),"UTF-8");
//这里默认读取采用的编码是UFT-8的字符编码,一个汉字用三个汉字来表示,所以会从demo.txt中读取三个字节,但是demo.txt中采用的编码形式是使用GBK编码,采用的是两个字节,所以最终读取的结果就会出现乱码。
//-21 -22 -12
//-24 -53 -43
char[]by=new char[10];
int len=0;
len=isr.read(by);
String s=new String(by,0,len);
System.out.println(s);
}
}
运行结果如下:

黑马程序员 Java基础IO流之File类
字符串变成字节数组就是编码:字节数组变成字符就是解码。在文件的操作中,往往文件存储时使用的编码和文件读取使用的编码不一致就会导致产生乱码,所以文件的存储和读取尽量要使用同一种编码。而在文件的操作中同样也是会碰到需要文件的

代码如下:

//编码 就是将字符串转换成字节数组。String.getBytes();
//解码 就是将字节数组转换成字符串。new String(byte[],String charset) charset表示编码集
import java.io.*;
class JavaCollection102
{
public static void main(String[] args)throws Exception
{
Encode();
}
public static void Encode()throws IOException
{
String s1="你好";
//这里采用了编码。将字符串转换成字节数组,并指定编码集
byte[] s=s1.getBytes("UTF-8");
String ret=new String(s,"GBK");
System.out.println(ret);//然后再字节数组转换成字符串(解码)但是这里采用的编码集进行解码时,产生了错误,所以这里就需要对刚才错误解码的字符串进行重新编码,然后在用正确的字符集进行解码。
byte[]s2=ret.getBytes("GBK");
System.out.println(new String(s2,"UTF-8"));


}
}
运行结果如下:

黑马程序员 Java基础IO流之File类
字符编码之联通:

当我们往记事本存储“联通两个汉字”,关闭保存后再次打开文件,然后我们发现里面的文字已经是乱码了,本来我们存储汉字的编码表是GBK形式的,但是在用记事本再打开这个文件的时候,记事本根据“联通”的字节编码进行对应的编码表进行解析,但是由于这两个字的特殊的标志,所以导致记事本在解码时采用的编码表示UTF-8的模式,为什么会出现这种情况呢?原因就是UTF-8是用三个字节来表示一个汉字,但是在解码的时候,可以只解析一个字节,也可以是两个,也可以是三个,所以为了区分到底是解码几个字节,所以在每个字节的前面都才用了一个标示位,具体的标示位置如下:

黑马程序员 Java基础IO流之File类

通过上述的UTF-8编码表我们发现,如果读取一个字节,前面的标记为是0,如果是读取两个字节,第一个字节的标记时110,如果是三个字节一起读取的话,那么前面第一个开头的字节标记是1110,但是我们通过将“联通”两个字符转换GBK的字节数组我们会发现开头也是110,所以这样记事本解码时就会误以为用UTF-8进行解码,当然也就会产生乱码。

演示代码如下:

import java.io.*;
class JavaCollection103
{
public static void main(String[] args) throws IOException
{
String s="联通";
byte[]by=s.getBytes("GBK");
for(byte b : by)
{
System.out.println( Integer.toBinaryString(b&255));
}
}
}


黑马程序员 Java基础IO流之File类

通过演示代码我们发现,获取使用GBK编码的字符的字节数组,发现这两个字节的首位也是110所以这样导致记事本以为是要用UTF-8进行解码。所以这样就产生了乱码。代码演示:

有五个学生,每个学生有3们课程成绩,从键盘上输入以上的成绩,格式按照 zhangsan,12,32,41然后计算出学生的总成绩,并把学生的成绩按照总成绩的高低存放在磁盘文件stud.txt文件中。

1描述学生类,

2建立一个学生工具类,

通过将从键盘上录入的数据解析成学生的信息,并将该行中的信息封装成学生对象。·

因为学生对象有很多,所以需要存储,所以存储对象,那么久需要集合。而且还要对学生的成绩进行排序,所以就需要使用TreeSet集合,将集合中信息存储到一个文件中。

代码如下:

import java.io.*;
import java.util.*;
class JavaCollection105
{
public static void main(String[] args) throws IOException
{
Set<Student> al=new TreeSet<Student>();
/*使用默认比较器
al=StudentTool.getStudList();
StudentTool.SaveTOFile(al);
*/

//使用比较器
Comparator<Student> comp=Collections.reverseOrder();//将比较器进行反向。
al=StudentTool.getStudList(comp);
StudentTool.SaveTOFile(al);
}
}
class Student implements Comparable<Student>
{
private String name;
private int chinese;
private int math;
private int eng;
private int sum;
Student(String name,int chinese,int math,int eng)
{
this.name=name;
this.math=math;
this.chinese=chinese;
this.eng=eng;
sum=math+chinese+eng;
}
public int hashCode()
{
return name.hashCode()+sum*34;
}
public boolean equals(Object obj)
{
if(!(obj instanceof Student))
throw new ClassCastException("该对象不是学生类型");
Student s=(Student)obj;
return this.name.equals(s.name)&&this.sum==s.sum;
}
public String toString()
{
return "name["+name+"]:"+chinese+" "+math+" "+eng+" Sum="+sum;
}
public int compareTo(Student s)
{



int num1=0;
num1=this.sum-s.sum;
if(num1==0)
return this.name.compareTo(s.name);
return num1;
}
}
class StudentTool
{
public static void SaveTOFile(Set<Student> al)throws IOException
{
BufferedWriter bw =new BufferedWriter(new FileWriter("d:\\student.txt"));
String line=null;
for(Student i : al)
{
bw.write(i.toString());
bw.newLine();
bw.flush();
}

}
public static Set<Student> getStudList() throws IOException
{

return getStudList(null);
}
public static Set<Student> getStudList(Comparator<Student> comp) throws IOException
{
BufferedReader br=null;
try
{
br=new BufferedReader(new InputStreamReader(System.in));
TreeSet<Student> stus;
String line=null;
if(comp==null)
stus=new TreeSet<Student>();
else
stus=new TreeSet<Student>(comp);
while((line=br.readLine())!=null)
{
if("over".equals(line))
break;
else
{
String[]arr=line.split(",");
Student s=new Student(arr[0],Integer.parseInt(arr[1]),Integer.parseInt(arr[1]),Integer.parseInt(arr[2]));
stus.add(s);
System.out.println(s.toString());

}
}
return stus;
}
catch (IOException e)
{
throw new RuntimeException("文件存储失败");
}
finally
{
try
{
if(br!=null)
br.close();
}
catch (IOException e)
{
throw new RuntimeException(e.toString());
}
}
}
}
运行结果如下:

黑马程序员 Java基础IO流之File类