Java基础——IO流(下)之字节流


个人小结:数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。根据处理数据类型的不同可分为:字符流和字节流。本篇主要介绍IO流中的字节流。

一、字节流

1、如果要操作除文本数据类型之外的数据,比如图片,就要用到 字节流。

2、读写字节流:InputStream   读取(输入)流, OutputStream  写入(输出)流

<span style="font-family:Microsoft YaHei;font-size:12px;">/*
需求:
	复制一个图片
思路:
1、用字节读取流对象和图片关联
2、用字节写入流对象创建一个图片文件,用于存储获取到的图片数据
3、通过循环读写,完成数据存储
4、关闭资源

注意:不要用字符流来处理除文本以外的文件,容易出问题!
*/
import java.io.*;
class  CopyPic
{
	public static void main(String[] args) 
	{
		FileInputStream fis= null;
		FileOutputStream fos= null;
		try
		{
			//读取流关联要复制的文件
			fis = new FileInputStream("D:\\JAVA学习\\线程运行状态.jpg");
			//写入流关联指定复制的文件名
			fos = new FileOutputStream("c:\\1.jpg");
			//定义一个缓冲数组
			byte[] buf = new byte[1024];
			int len = 0;
			//边读边写
			while ((len = fis.read(buf))!=-1)
			{
				fos.write(buf,0,len);
			}
		}
		catch (IOException e)
		{
			throw new RuntimeException("复制文件失败!");
		}
		finally
		{
			try
			{
				if(fis!=null)
					fis.close();//关流
			}
			catch (IOException e)
			{
				throw new RuntimeException("读取关闭失败");
			}
			try
			{
				if(fis!=null)
					fos.close();//关流
			}
			catch (IOException e)
			{
				throw new RuntimeException("写入关闭失败");
			}
		}
	}
}</span>

二、字节流缓冲区

BufferedOutputStreamBufferedInputStream
1、作用:提高了字节流的读写效率。
2、特点:
read():会将字节byte型值提升为int型值
        write():会将int型强转为byte型,即保留二进制数的最后八位。
3、原理:先将数据读取一部分,循环,直到数据全部读取完毕。        
4、自定义读取字节流缓冲区
        需求:根据字节流缓冲区的原理,自定义一个字节流缓冲区。
注意:
        1、字节流的读一个字节的read方法为什么返回值类型不是byte,而是int。
        因为有可能会读到连续8个二进制1的情况,8个二进制1对应的十进制是-1。那么就会出现数据还没有读完就结束的情况。因为我们判断读取结束是通过结尾标记-1来确定的。
       所以,为了避免这种情况将读到的字节进行int类型的提升。并在保留原字节数据的情况前面了补了24个0,变成了int类型的数值。而在写入数据时,只写该int类型数据的最低8位。
        2、byte类型的-1提升为int类型时还是-1。原因:因为在二进制位中的8个1前面补的全是1导致的。如果在8个1前面补0,既可以保留原字节数据不变,又可以避免-1的出现。这时只用将byte型数据 &255 即可。
示例代码:演示自定义缓冲区,并用来拷贝MP3文件。

<span style="font-family:Microsoft YaHei;font-size:12px;">import java.io.*;
class  MyBufferedInputStream
{
	private InputStream in;
	private byte[] buf = new byte[1024*4];
	private int pos = 0,count=0;
	MyBufferedInputStream(InputStream in)
	{
		this.in = in;
	}
	//一次读一个字节,从缓冲区(字符数组)获取
	public int myRead() throws IOException
	{
		//通过in对象读取硬盘上的数据,存储到buf中。
		if (count==0)
		{
			count = in.read(buf);

			if (count<0)//文件数据全部被读取出来了
				return -1;

			pos = 0;//初始化指针
			byte b = buf[pos];
			count--;//每读一个字节,数组中的字节数就少一个
			pos++;
			return b&255;//返回的byte类型提升为int类型
		}
		else if (count>0)//如果数组中数据还没被读完,就继续读
		{
			byte b = buf[pos];
			count--;
			pos++;
			return b&0xff;//返回的byte类型提升为int类型
		}
		return -1;
	}
	//定义关闭资源动作
	public void myClose()  throws IOException
	{
		in.close();
	}
}</span>
//通过自定义缓冲区,演示MP3的复制。
import java.io.*;
class  CopyMp3
{
	public static void main(String[] args) throws IOException
	{
		long start = System.currentTimeMillis();
		copy_2();
		long end = System.currentTimeMillis();
		System.out.println((end-start)+"毫秒");//查看复制所需时间
	}
	//通过自定义缓冲区演示
	public static void copy_2() throws IOException
	{
		//创建缓冲区,并关联复制对象文件
		MyBufferedInputStream bufis = new MyBufferedInputStream(new FileInputStream("c:\\0.mp3"));
		//创建缓冲区,关联目标文件
		BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c:\\1.mp3"));

		int by = 0 ;

		System.out.println("第一个字节:"+bufis.myRead());
		//不停地读取写入
		while ((by=bufis.myRead())!=-1)
		{
			bufos.write(by);
		}
		bufos.close();//关资源
		bufis.myClose();//关资源
	}
}

三、读取键盘录入
System.out: 标准输出设备:控制台
System.in : 标准输入设备:键盘
示例:
需求:通过键盘录入数据,当录入一行数据后,就将该行数据转成大写并进行打印,如果录入的数据是over,那么停止录入。
代码:

<span style="font-family:Microsoft YaHei;font-size:12px;">import java.io.*;
class ReadIn 
{ 
	public static void main(String[] args) throws IOException
	{
		//获取键盘录入对象。
		InputStream in = System.in;
		//创建一个容器存储键盘录入的数据
		StringBuilder sb = new StringBuilder();

		while (true)
		{
			int ch= in.read();
			
			if(ch=='\r')
				continue;
			if(ch=='\n')
			{
				String s = sb.toString();
				if("over".equals(s))//如果录入的数据是over,那么停止录入
					break;
				System.out.println(s.toUpperCase());//将读取的字符串转成大写后打印
				sb.delete(0,sb.length());
			}
			else
				sb.append((char)ch);
		}
	}
}</span>
运行结果:

四、读取转换流

通过刚才的键盘录入一行数据并打印其大写,发现其实就是读一行数据的原理,即 readLine()方法。
那么能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?
不能!由于readLine方法是BufferedReader类中的方法,而键盘录入的read方法是字节流InputStream的方法。
那么能不能将字节流转成字符流再使用字符流缓冲区的readLine方法呢?可以!用转换流 InputStreamReader!
那么上例代码可改为:

<span style="font-family:Microsoft YaHei;font-size:12px;">import java.io.*;
class TransStreamDemo 
{
	public static void main(String[] args) throws IOException
	{
		//获取键盘录入对象。
		//InputStream in = System.in;

		//将字节流对象转成字符流对象,使用转换流,InputStreamReader
		//InputStreamReader isr = new InputStreamReader(in);

		//为了提高效率,将字符流进行缓冲区技术的高效操作,使用BufferedReader

		//BufferedReader bufr = new BufferedReader(isr);

		//键盘录入最常见写法!强记!
		BufferedReader bufr = 
			new BufferedReader(new InputStreamReader(System.in));

		/*
		OutputStream out = System.out;
		OutputStreamWriter osw = new OutputStreamWriter(out);
		BufferedWriter bufw = new BufferedWriter(osw);
		*/
		BufferedWriter bufw = 
			new BufferedWriter(new OutputStreamWriter(System.out));

		String line = null;
		while ((line=bufr.readLine())!=null)
		{
			if ("over".equals(line))
				break;
			bufw.write(line.toUpperCase()); 
			bufw.newLine();
			bufw.flush();
		}
		bufr.close();		
	}
}</span>

五、IO流操作规律(最重要部分)

1、需求:将键盘输入数据打印出来
源:键盘录入
目的:控制台
2、需求:想把键盘录入的数据存储到一个文件中
源:键盘
目的:文件
3、需求:想要将一个文件的数据打印在控制台上。
源:文件
目的:控制台
最痛苦的就是流对象有很多,不知道该用哪个?

通过三个明确来完成:
1、明确源和目的。
源:输入流。InputStream  、Reader
目的:输出流。OutputStream  、Writer
2、操作的数据是否是纯文本?
是:用字符流
不是:用字节流
3、当体系明确后,再明确要使用哪个具体对象,
通过设备来进行区分:
源设备:内存,硬盘,键盘
目的设备:内存,硬盘,控制台

应用规律实例分析:
需求1:将一个文本文件中的数据存储到另一个文件中,复制文件
1、源:因为是源,所以使用读取流:InputStream  或 Reader
是不是操作文本文件?是!这时就可以选择 Reader
2、接下来明确要使用该体系中的哪个对象?
明确设备:硬盘上的一个文件
Reader体系中可以操作文件的对象是FileReader
3、是否要提高效率?
是!加入Reader体系中的缓冲区: BufferedReader。
4、写出代码:
FileReader fr = new FileReader("a.txt");
BufferedReader bufr  = new BufferedReader(fr);
5、目的:是目的就要用 OutputStream 或者Writer
是不是纯文本?  是!用Writer!
6、明确设备:硬盘的一个文件
Writer 体系中可以操作文件的对象是FileWriter
7、是否需要提高效率?
是! 加入Writer体系中的缓冲区: BufferedWriter
8、写出代码:
FileWriter fw = new FileWriter("b.txt");
BufferedWriter bufw = new BufferedWriter(fw);

练习:将一个图片文件中的数据存储到另一个文件中,复制文件,要按照以上格式自己完成三个明确。
1、源:硬盘中的一个文件
是不是操作文本文件?--不是!所以用 InputStream.
明确使用该体系中哪个对象?--设备是硬盘,所以用 FileInputStream
是否要提高效率?--要!加入该体系中的缓冲区 BufferedInputStream
2、目的:硬盘
是不是操作文本?--不是,所以用 OutputStream
用哪个对象?--设备是硬盘,FileOutputStream
是否要提高效率?--要!加入该体系中的缓冲区 BufferedOutputStream

需求2:将键盘录入的数据保存到一个文件中。
1、源:InputStream 或  Reader
是不是纯文本?是!用 Reader
设备:键盘。对应的对象是 System.in.
不是选择Reader吗? System.in 不是字节流吗?
为了操作键盘的文本数据方便,转成字符流 按照字符串来操作最方便。
所以,既然明确了Reader,那么就将System.in转成字符流Reader,
用到了 Reader体系中的转换流,即 InputStreamReader.
InputstreamReader  isr = new InputStreamReader(System.in);
要提高效率吗?要! 用 BufferedReader
代码:BufferedReader bufr = new BufferedReader(isr);
2、目的:OutputStream 或  Writer
是否是纯文本?是! 用 Writer
设备:硬盘,一个文件,使用FileWriter
需要提高效率吗?需要!用BufferedWriter
代码:FileWriter fw = new FileWriter("c.txt");
  BufferedWriter bufw =new BufferedWriter(fw);

扩展一下,想要把录入的数据按照指定的编码表(utf-8),将数据存到文件中。
目的:OutputStream  或 Writer
是否是纯文本?是! 用 Writer
设备:硬盘,一个文件,使用FileWriter,但是 FileWriter是使用默认编码表:GBK。
因为存储时需要加入指定的编码表UTF-8,而指定的编码表只有转换流才可以指定。所以要使用的对象是OutputStreamWriter
而该转换流对象要接收一个字节输出流,而且还可以操作文件的字节输出流。
所以代码:OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("d.txt"),"UTF-8");
需要高效吗?要!BufferedWriter bufw = new BufferedWriter(osw);
所以,记住!转换流什么时候用呢?
转换流是字符和字节之间的桥梁,通常涉及到字符编码转换时需要用到。

练习:将一个文本数据打印在控制台上。按照以上格式完成三个明确。
1、源:文本,所以   Reader
设备:硬盘,一个文件,所以  FileReader
效率:要!所以BufferedReader
2、目的:文本,so  Writer
设备:控制台,so   System.out
但System.out是字节流,所以需要先转换成Writer,这就要用到Writer体系中的转换流:OutputStreamWriter
代码:OutputStreamWriter osw = new OutputStreamWriter(System.out);
要提高效率:BufferedWriter = new BufferedWriter(osw);
示例代码:

<span style="font-family:Microsoft YaHei;font-size:12px;">import java.io.*;
class TransStreamDemo2 
{
	public static void main(String[] args) throws IOException
	{
		System.setIn(new FileInputStream("PersonDemo.java"));//设置源
		System.setOut(new PrintStream("zzz.txt"));//设置目的		
		
		BufferedReader bufr = 
			new BufferedReader(new InputStreamReader(System.in));//需求2
			//new BufferedReader(new InputStreamReader(new FileInputStream("CopyPic.java")));//需求3
			//new BufferedReader(new FileReader("out0.txt"));

		BufferedWriter bufw = 
			//new BufferedWriter(new OutputStreamWriter(new FileOutputStream("out0.txt")));//需求2
			new BufferedWriter(new OutputStreamWriter(System.out));//需求2

		String line = null;
		while ((line=bufr.readLine())!=null)
		{
			if ("over".equals(line))
				break;
			bufw.write(line);
			bufw.newLine();
			bufw.flush();
		}
		bufr.close();
	}
}</span>

六、保存异常的日志信息
        正常开发中,程序在执行的时候出现的问题,需要用文件存储起来,这样方便查看和调整。
示例:

<span style="font-family:Microsoft YaHei;font-size:12px;">import java.io.*;
import java.util.*;
import java.text.*;
class ExceptionInfo 
{
	public static void main(String[] args) throws IOException
	{
		try
		{
			int[] arr = new int[2];
			System.out.println(arr[3]);
		}
		catch (Exception e)
		{
			try
			{
				Date d = new Date();//创建日期对象 
				//将日期格式化
				SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
				String s = sdf.format(d);
				//打印流对象
				PrintStream ps = new PrintStream("EXCEPTIONINFO.log");
				ps.println(s);//打印时间
				System.setOut(ps);//设置输出
			}
			catch (IOException ex)
			{
				throw new RuntimeException("日志文件创建失败!");
			}
			//将异常信息输出到指定输出流
			e.printStackTrace(System.out);
		}		
	}
}</span>

七、将系统属性信息保存到指定文本中 

示例代码:
<span style="font-family:Microsoft YaHei;font-size:12px;">import java.util.*;
import java.io.*;
class  SystemInfo
{
	public static void main(String[] args) throws IOException
	{
		Properties prop = System.getProperties();//获取系统信息

		//System.out.println(prop);
		//prop.list(System.out);	
		prop.list(new PrintStream("sysinfo.txt"));
	}
}</span>

八、File类
1、它是文件和目录路径名的抽象表现形式
        2、用来将文件或文件夹封装成对象
        3、方便对文件与文件夹的属性信息进行操作
4File对象可以作为参数传递给流的构造函数
        4、File类的实例是不可变的;即File 对象一旦创建,其表示的抽象路径名将永不改变

九、File类的常见方法

1、创建
blooean createNewFile(); 在指定位置创建文件,返回true,如果该文件已存在,则不创建,并返回false.
和输出流不一样,输出流对象一创建文件,如果该文件已存在,则会直接覆盖。
boolean mkdir():创建文件夹
boolean mkdirs():创建多级文件夹
2、删除
boolean delete();//删除文件或目录。文件存在,返回true;文件不存在或者正在被执行,返回false。
void deleteOnExit();//当程序结束时才删掉文件。
3、判断
boolean  canExecute();//是否是可执行文件
boolean  exists()://文件是否存在,用的较频繁。
isFile();//是否是文件
isDirectory();//是否是文件夹
isHidden(); //是否是隐藏文件
isAbsolute();//文件是否是绝对路径
4、获取信息
getName();//获取文件名
getPath(); //获取文件的相对路径(即创建的对象传入的参数是什么就获取到什么)
getParent();//获取文件父目录。返回的是绝对路径中的父目录。如果是相对路径,则返回null。如果相对路径中有上一层目录,那么该目录就是返回结果。
getAbsolutePath();//获取文件的绝对路径
long lastModified();//返回文件最后一次被修改的时间
long length();//返回文件长度

代码示例:
import java.io.*;
class  FileDemo
{
	public static void main(String[] args) throws IOException
	{
		//conMethod();
		//method_1();
		//method_2();
		//method_3();
		//method_4();

		method_5();		
	}

	public static void method_5()
	{
		File f1 = new File("c:\\yuanwenjian.txt");
		File f2 = new File("c:\\hahha.java");

		sop("rename:"+f1.renameTo(f2));
	}

	public static void method_4()
	{
		File f = new File("c:\\abc\\a.txt");

		sop("path:"+f.getPath());
		sop("abspath:"+f.getAbsolutePath());
		sop("parent:"+f.getParent());//该方法返回的是绝对路径中的父目录。
		//如果获取的是相对路径,返回空null.
		//如果相对路径中有上一层目录,那么该目录就是返回结果.
	}

	public static void method_3() throws IOException
	{
		File f = new File("D:\\java0913\\day20\\kkk\\file1.txt");

		//f.createNewFile();

		f.mkdir();
		
		//记住:在判断文件是否是文件或者目录时,必须要先判断该文件
		//对象封装的内容是否存在,通过exists判断。
		sop("dir:"+f.isDirectory());

		sop("file:"+f.isFile());

		sop(f.isAbsolute());
	}

	public static void method_2()
	{
		File f = new File("file.txt");

		//sop("execute:"+f.canExecute());

		//sop("exists:"+f.exists());

		//创建文件夹
		File dir = new File("abc");

		sop("mkdir:"+dir.mkdir());

		//创建多级文件夹
		File dirs = new File("D:\\java0913\\day20\\kk\\hh\\yy\\fdfdfd");

		sop("mkdirs:"+dirs.mkdirs());

	}

	public static void method_1() throws IOException
	{
		File f = new File("file.txt");
		sop("create:"+f.createNewFile());
	}
	//创建File对象
	public static void conMethod()
	{
		//将a.txt封装成File对象。可以将已有的和未出现的文件或者文件夹封装成对象。
		File f1 = new File("a.txt");

		//目录和文件分开
		File f2 = new File("c:\\abc","b.txt");

		File d = new File("c:\\abc");
		File f3 = new File(d,"c.txt");

		sop("f1:"+f1);	
		sop("f2:"+f2);
		sop("f3:"+f3);

		File f4 = new File("c:"+File.separator+"abc"+File.separator+"zzz"+File.separator+"a.txt");

		sop("f4:"+f4);
	}

	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}

5、列出文件及文件过滤
   
static File[ ] listRoots();//列出可用的文件系统根目录,即系统盘符
    String[ ] list(); //列出当前目录下所有文件,包括隐藏。调用list方法的file对象必须是封装了一个目录。该目录还必须存在。
    String[ ]list(FilenameFilter filter);//返回一个字符串数组,获取目录中满足指定过滤器的文件或目录。
    FilenameFilter:文件名过滤器,是一个接口,其中包含一个方法,accept(Filedir,String name),返回的是boolean型,对不符合条件的文件过滤掉。
    File[ ] listFiles();//返回一个抽象路径名数组,获取当前文件夹下的所有文件和文件夹
    File[ ] ListFiles(FilenameFilterfilter);//返回抽象路径名数组,获取目录中满足指定过滤器的文件或目录。
代码示例:
import java.io.*;
class FileDemo2 
{
	public static void main(String[] args) 
	{
		//listRootsDemo();
		//listDemo();

		File dir = new File("c:\\");

		File[]  files = dir.listFiles();//listFiles返回的是所指定目录下的文件及文件夹的对象所包装成的数组。

		for (File f : files )
		{
			System.out.println(f.getName()+"::"+f.length());
		}

	}

	public static void listDemo2()
	{
		
		File dir = new File("d:\\java0913\\day18");

		String[] arr = dir.list(new FilenameFilter()//list返回的是所指定目录下的文件及文件夹的名称所包装成的数组。
		{
			public  boolean accept(File dir,String name)
			{
				//System.out.println("dir:"+dir+"....name::"+name);
				return name.endsWith(".java");
			}
		});

		for (String name : arr )
		{
			System.out.println(name);
		}
	}

	public static void listDemo()
	{
		File f = new File("c:\\abc.txt");

		String[] names = f.list();//调用list方法的file对象必须是封装了一个目录,该目录还必须存在。
		for (String name : names )
		{
			System.out.println(name);
		}
		System.out.println(names.length);//此时因为abc.txt文件夹存在,所以返回来一个长度为0的数组。

	}

	public  static void listRootsDemo()
	{
		File[] files = File.listRoots();

		for (File f :files)
		{
			System.out.println(f);
		}
	}
}

十、递归
需求:列出指定目录下文件或者文件夹,包含子目录中的内容也就是列出指定目录下所有内容。
分析:因为目录中还有目录,只要使用同一个列出目录功能的函数完成即可,在列出过程中出现的还是目录的话,还可以再次调用本功能。也就是函数自身调用自身,这种编程方式称为:递归!
 注意:
1、限定条件,是来结束循环调用,否则是死循环。
2、要注意递归的次数,尽量避免内存溢出。因为每次调用自身的时候都会先执行下一次调用自己的方法,所以会不断在栈内存中开辟新空间,次数过多,会导致内存溢出。

代码示例:
import java.io.*;
class FileDemo3 
{
	public static void main(String[] args) 
	{		
		//关联指定路径
		File dir = new File("d:\\java0913");
		showDir(dir,0);//列出指定路径下的所有.java文件
		//toBin(6);
		//long n = getSum(1000);//内存溢出
		//System.out.println("n="+n);
	}
	//带层次的列表
	public static String getLevel(int level)
	{
		StringBuilder sb = new StringBuilder();
		sb.append("|--");
		//每多一级目录,就多输出指定字符
		for (int x= 0;x<level ;x++ )
		{
			sb.insert(0,"  ");
		}
		return sb.toString();
	}
	//列出指定目录下的所以内容
	public static void showDir(File dir,int level)
	{
		//有层次的输出
		System.out.println(getLevel(level)+dir.getName());
		level++;
		File[] files = dir.listFiles();//获取本目录下的所有文件和目录的抽象路径
		for (int x = 0;x<files.length ; x++)
		{
			if(files[x].isDirectory())//如果目录下还是目录,则继续调用本函数
				showDir(files[x],level);
			else//否则列出文件
				System.out.println(getLevel(level)+files[x]);
		}
	}
	//用递归求和
	public static long getSum(long n)
	{
		if(n==1)
			return 1;
		return n+getSum(n-1);
	}
	//用递归转二进制
	public static void toBin(int num)
	{
		if (num>0)
		{
			toBin(num/2);
			System.out.println(num%2);			
		}
	}	
}
运行结果:


练习:删除一个带内容的目录
/*
删除一个带内容的目录
删除原理:
在windows中,删除目录是从里面往外删。
既然是从里往外删除,那就需要用到递归。
*/
import java.io.*;
class RemoveDir 
{
	public static void main(String[] args) 
	{
		File dir = new File("testDir");//指定目录
		removeDir(dir);
	}
	//删除目录封装成方法
	public static void removeDir(File dir)
	{
		//列出目录下的所有文件和文件夹
		File[] files  = dir.listFiles();

		for (int x= 0; x<files.length;x++ )
		{
			if(files[x].isDirectory())
				removeDir(files[x]);//如果还是目录,继续调用本函数
			else
				System.out.println(files[x].toString()+"::"+files[x].delete());//删除文件 
		}
		System.out.println(dir+"::dir::"+dir.delete());//删除目录
	}
}
练习:将一个指定目录下的java文件的绝对路径,存储到一个文本文件中。建立一个java文件列表文件。
/*
练习:
将一个指定目录下的java文件的绝对路径,存储到一个文本文件中。
建立一个java文件列表文件。

思路:
1.对指定的目录进行递归。
2、获取递归过程中所有的java文件的路径
3、将这些路径存储到集合中。
4、将集合中的数据写入到一个文件中。
*/
import java.io.*;
import java.util.*;
class JavaFileList 
{
	public static void main(String[] args) 
	{
		File dir = new File("d:\\java0913"); //指定目录 

		List<File> list = new ArrayList<File>();//定义一个List集合,用于存储.java文件的File对象

		fileToList(dir,list);//调用获取文件路径方法
		System.out.println(list.size());//打印集合大小

		File file = new File(dir,"javaListFile.txt");//指定存入目的文件
		writeToFile(list,file.toString());//调用写入文件方法
	}
	//获取指定文件夹内的所有java文件的绝对路径,并存入集合中的方法
	public static void fileToList(File dir,List<File> list)
	{
		File[] files = dir.listFiles();//列出dir路径下的所以文件和目录

		for (File file : files )
		{
			if (file.isDirectory())
				fileToList(file,list);
			else //将是.java文件的绝对路径存入
				if(file.getName().endsWith(".java"))
					list.add(file);
		}		
	}
	//将集合中元素写入到一个文本文件中的方法
	public static void writeToFile(List<File> list,String javaList)
	{
		BufferedWriter bufw = null;
		try
		{
			//使用字符流缓冲区对象关联目的文件
			bufw = new BufferedWriter(new FileWriter(javaList));			
			for (File f : list)
			{
				String path = f.getAbsolutePath();//获取绝对路径
				bufw.write(path);//写入路径
				bufw.newLine();//换行
				bufw.flush();//刷新
			}				
		}
		catch (IOException e)
		{
			throw new RuntimeException("创建列表文件失败");
		}
		finally
		{
			try
			{
				if(bufw!=null)
					bufw.close();//关资源
			}
			catch (IOException e)
			{
				throw new RuntimeException("关闭资源失败");
			}
		}	
	}
}
运行结果:


十一、Properties类
Properties 是hashtable的子类。也就是说他具备map集合的特点,而且他里面存储的键值对都是字符串。它是集合中和IO技术相结合的集合容器。
特点:可以用于键值对形式的配置文件。
那么在加载数据时,需要数据有固定格式:键=值。
示例代码:
import java.io.*;
import java.util.*;
class PropertiesDemo 
{
	public static void main(String[] args) throws IOException
	{
		//setAndGet();
		//method_1();
		loadDemo();
	}

	public static void loadDemo() throws IOException
	{
		Properties prop = new Properties();
		FileInputStream fis = new FileInputStream("info.txt");

		//将流中的数据加载进集合。用load方法
		prop.load(fis);

		prop.setProperty("wangwu","85");

		FileOutputStream fos = new FileOutputStream("info.txt");
		prop.store(fos,"haha");
		//System.out.println(prop);
		prop.list(System.out);
		fos.close();
		fis.close();
	}

	//演示:如何将流中的数据存储到集合中。
	//想要将info.txt中键值数据存到集合中进行操作。
	/*
	思路:
		1、用一个流和info.txt文件关联。
		2、读取一行数据,将该行数据用"="切割
		3、等号左边作为键,右边作为值,存入到Properties集合中即可。
	*/

	public static void method_1() throws IOException
	{
		//使用字符读取缓冲流关联文件
		BufferedReader bufw = new BufferedReader(new FileReader("info.txt"));

		String line = null;
		//定义Properties集合
		Properties prop = new Properties();
		while((line=bufw.readLine())!=null)
		{
			String[] arr = line.split("=");//切割
			//System.out.println(arr[0]+"......."+arr[1]);
			prop.setProperty(arr[0],arr[1]);//将等号左边存为键,将等号右边存为值
		}		
		bufw.close();//关流
		System.out.println(prop);
	} 
	//设置和获取元素。
	public static void setAndGet()
	{
		Properties prop = new Properties();

		prop.setProperty("zhangsan","30");
		prop.setProperty("lisi","39");

		System.out.println(prop);
		String value = prop.getProperty("lisi");
		System.out.println(value);

		prop.setProperty("lisi",89+"");

		Set<String> names = prop.stringPropertyNames();//返回此属性列表中的键集,其中该键及其对应值是字符串.
		for (String s : names )
		{
			System.out.println(s+"::"+prop.getProperty(s));
		}
	}
}
练习:用于记录应用程序运行次数。如果使用次数已到,那么给出注册提示。
/*
练习:用于记录应用程序运行次数。如果使用次数已到,那么给出注册提示。

很容易想到的是:计数器
可是该计数器定义在程序中,随着程序的运行而在内存中存在,并进行了自增,
可是随着该应用程序的退出,该计数器也在内存中消失了,

下一次再启动该程序,又重新开始从0计数。这不是我们想要的。

程序即使结束,该计数器的值也存在,下次程序启动再会先加载该计数器的值并加1后再重新存储起来。

所以要建立一个配置文件,用于记录该软件的使用次数。

该配置文件使用键值对的形式。这样便于阅读数据,并操作数据。

键值对数据是map集合。数据是以文件形式存储,使用io技术。
那么map+io-->properties

配置文件可以实现应用程序数据的共享。
*/
import java.io.*;
import java.util.*;
class  RunCount
{
	public static void main(String[] args) throws IOException
	{
		Properties prop = new Properties();//创建集合对象

		File file = new File("count.ini");//建立一个配置文件

		if(!file.exists())
			file.createNewFile();
		//将文件跟读取流进行关联
		FileInputStream fis = new FileInputStream(file);

		prop.load(fis);//加载流中的文件数据到集合中

		int count = 0;//定义计数器
		String value = prop.getProperty("time");//获取操作次数值

		if(value!=null)//如果值不为null,则将其赋值给count
		{
			count = Integer.parseInt(value);
			if(count>=5)
			{
				//如果程序被使用了超过5次,则终止使用,并提示信息
				System.out.println("您好!使用次数已到,拿钱!");
				return ;
			}
		}			
		count++;//程序每启动一次就自增一次 
		prop.setProperty("time",count+"");//将次数记录进集合
		//创建输出流
		FileOutputStream fos = new FileOutputStream(file);
		//将集合中的数据通过输出流存入硬盘文件中
		prop.store(fos,"");

		fos.close();//关流 
		fis.close();
	}
}

十二、打印流
该流提供了打印方法,可以将各种数据类型的数据都原样打印。

字节打印流:PrintStream 
构造函数可以接收的参数类型
1、file对象,file
2、字符串路径,String.
3、字节输出流。OutputStream

字符打印流:PrintWriter  
构造函数可以接收的参数类型:
1、file对象,file
2、字符串路径,String.
3、字节输出流。OutputStream
4、字符输出流,Writer.

示例:
import java.io.*;
class  PrintStreamDemo
{
	public static void main(String[] args)  throws IOException
	{
		//缓冲区关联键盘录入
		BufferedReader bufr = 
			new BufferedReader(new InputStreamReader(System.in));
		//打印流关联文件,自动刷新,不用flush
		PrintWriter out = new PrintWriter(new FileWriter("a.txt"),true);

		String line = null;
		while ((line=bufr.readLine())!=null)
		{
			if("over".equals(line))//结束字符
				break;
			//out.write(line.toUpperCase());
			out.println(line.toUpperCase());//转成大写并输出			
			//out.flush(); 如果上方PrintWriter方法里的,第二个参数是autoFlush 为true,则自动刷新。
		}
		out.close();//关流
		bufr.close();
	}
}

运行结果:


十三、序列流
SequenceInputStream  用于对多个流进行合并。
       常见合并多个流文件步骤:
        1、创建集合,并将流对象添加进集合
        2、创建Enumeration对象,将集合元素加入
        3、创建SequenceInputStream对象,合并流对象
        4、创建写入流对象,FileOutputStream关联写入文件
        5、利用SequenceInputStream对象和FileOutputStream对象读数据进行反复读写操作。
示例:

/*
需求:将三个文本文件中的数据合并到一个文本文件中 
思路:1、创建Vector集合,将三个文本文件字节流添加到集合中 
      2、创建Enumeration对象,创建SequnceInputStream对象关联Enumeration 
      3、输出流关联新文本文件 
      4、反复读写操作
*/
import java.io.*;
import java.util.*;
class SequenceDemo 
{
	public static void main(String[] args) throws IOException
	{
		//创建vector
		Vector<FileInputStream> v =new Vector<FileInputStream>();
		//添加相关流对象
		v.add(new FileInputStream("1.txt"));
		v.add(new FileInputStream("2.txt"));
		v.add(new FileInputStream("3.txt"));
		//创建Enumeration枚举对象
		Enumeration<FileInputStream> en = v.elements(); 
		//合并流
		SequenceInputStream sis = new SequenceInputStream(en);
		//关联写入文件
		FileOutputStream fos = new FileOutputStream("4.txt");

		byte[] buf = new byte[1024];

		int len = 0;
		while ((len=sis.read(buf))!=-1)
		{
			fos.write(buf,0,len);//反复读写
		}
		fos.close();//关流
		sis.close();
	}
}
运行结果:


练习:切割文件 和 合并文件
/*
练习:切割文件 和 合并文件
需求:将一个图片文件切割成几部分,然后根据这个及部分合并成一张图片
*/
import java.io.*;
import java.util.*;
class  SplitFile  
{
	public static void main(String[] args) throws IOException
	{
		//SplitFile();//切割方法
		merge();//合并方法
	}
	//切割方法
	public static void SplitFile() throws IOException
	{
		//关联要切割的文件
		FileInputStream fis = new FileInputStream("1.bmp");

		FileOutputStream fos = null;
		//定义1M大小存储容器
		byte[] buf = new byte[1024*1024];
		int len = 0;
		int count = 1;//定义1个计数器,作为部分编号
		while ((len = fis.read(buf))!=-1)
		{
			//每满1M就写入一个新文件中
			fos = new FileOutputStream("splitfiles\\"+(count++)+".part");
			fos.write(buf,0,len);
			fos.close();//每写完一个部分文件要关流 
		}
		fis.close();
	}
	 //合并方法
	public static void merge() throws IOException
	{
		//定义一个集合存储这些部分文件并关联路径数据
		ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();
		for (int x= 1; x<5 ; x++ )
		{
			al.add(new FileInputStream("splitfiles\\"+x+".part"));
		}
		
		final Iterator<FileInputStream> it = al.iterator();
		//由于Enumeration是Vector的迭代方法,这里创建一个Enumeration类型的匿名内部类
		Enumeration<FileInputStream> en = new Enumeration<FileInputStream>()
		{
			public boolean hasMoreElements()
			{
				return it.hasNext();
			}
			public FileInputStream nextElement()
			{
				return it.next();
			}
		};
		//关联Enumeration对象
		SequenceInputStream sis = new SequenceInputStream(en);
		//将合并的文件数据写入指定文件中
		FileOutputStream fos = new FileOutputStream("splitfiles\\tupian.bmp");

		byte[] buf = new byte[1024];
		int len= 0;
		while ((len= sis.read(buf))!=-1)
		{
			fos.write(buf,0,len);//边读边写
		}
		fos.close();//关流
		sis.close();
	}	
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值