黑马程序员——Java语言基础——07.IO流(2)File类和其他流

本文详细介绍了Java中File类的使用方法,包括文件和目录的创建、删除、获取属性等操作,以及如何利用递归来处理目录结构。此外,还深入探讨了Properties类的应用场景,如实现软件使用次数的限制。最后,文章讲解了几种常用的IO流,如打印流、序列流、RandomAccessFile等,展示了它们在实际项目中的应用。

------- android培训java培训、期待与您交流! ----------

2-1 File类

将文件系统中的文件和文件夹封装成了对象。提供了更多的属性和行为可以对这些文件和文件夹进行操作。这些是流对象办不到的,因为流只操作数据

2-1-1 File类常见方法

1:创建。

boolean createNewFile():在指定目录下创建文件,如果该文件已存在,则不创建。而对操作文件的输出流而言,输出流对象已建立,就会创建文件,如果文件已存在,会覆盖。除非续写。

boolean mkdir():创建此抽象路径名指定的目录。

boolean mkdirs():创建多级目录。 

2:删除。

boolean delete():删除此抽象路径名表示的文件或目录。

void deleteOnExit():在虚拟机退出时删除。

注意:在删除文件夹时,必须保证这个文件夹中没有任何内容,才可以将该文件夹用delete删除。

window的删除动作,是从里往外删。注意:java删除文件不走回收站。要慎用。

3:获取.

long length():获取文件大小。

String getName():返回由此抽象路径名表示的文件或目录的名称。

String getPath():将此抽象路径名转换为一个路径名字符串。

String getAbsolutePath():返回此抽象路径名的绝对路径名字符串。

String getParent():返回此抽象路径名父目录的抽象路径名,如果此路径名没有指定父目录,则返回 null。

long lastModified():返回此抽象路径名表示的文件最后一次被修改的时间。

File.pathSeparator:返回当前系统默认的路径分隔符,windows默认为 “;”。

File.Separator:返回当前系统默认的目录分隔符,windows默认为 “\”。

4:判断:

boolean exists():判断文件或者文件夹是否存在。

boolean isDirectory():测试此抽象路径名表示的文件是否是一个目录。

boolean isFile():测试此抽象路径名表示的文件是否是一个标准文件。

boolean isHidden():测试此抽象路径名指定的文件是否是一个隐藏文件。

boolean isAbsolute():测试此抽象路径名是否为绝对路径名。

5:重命名。

 boolean renameTo(File dest):可以实现移动的效果。剪切+重命名。

String[] list():列出指定目录下的当前的文件和文件夹的名称。包含隐藏文件。

如果调用list方法的File 对象中封装的是一个文件,那么list方法返回数组为null。如果封装的对象不存在也会返回null。只有封装的对象存在并且是文件夹时,这个方法才有效。

示例:

class FileDemo
{
	public static void main(String[] args)throws Exception
	{
		method4();
	}

	public static void method1()
	{
		try
		{
			File f = new File("c:\\shabi.txt");
			f.createNewFile();

			File file2 = new File("D:\\temp");// D;/temp 为一个目录
			File tempFile1= file2.createTempFile("msg", ".tmp",file2);//指定目录创建
			File tempFile2 = file2.createTempFile("msg", ".tmp");//系统默认目录创建
			System.out.println(tempFile2.getAbsolutePath());

			f.delete();
		}
		catch (IOException e)
		{
			throw new RuntimeException("nishishabi");
		}
	}

	public static void method2()throws Exception
	{
		File dir = new File("nish\\b");

		System.out.println(dir.mkdirs());//多级目录
		System.out.println(dir.mkdir());//单个目录
	}

	public static void method3()throws Exception
	{
		File file = new File("d:\\workspace\\lianxi\\day20");

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

	public static void method4()throws Exception
	{
		File f = new File("d:\\workspace\\lianxi\\day20\\FileDemo.java");
		sop(f.getPath());
		sop(f.getAbsoluteFile());
		sop(f.getAbsolutePath());
		sop(f.getParent());
		sop(f.getParentFile());
	}

	public static void sop(Object obj)
	{
		System.out.println(obj);
	}	
}
示例二:使用list和listFiles分别获取文件夹中的文件名称和文件对象

class FileDemo2 
{
	public static void main(String[] args) throws Exception
	{
		File dir = new File("d:\\workspace\\lianxi\\day20");
		File[] files = dir.listFiles();
		//这个方法是获取到了对象,list仅仅获取了名称

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

	public static void method1()throws Exception
	{
		/*File[] f = File.listRoots();
		for (File f1 : f)
		{
			sop(f1);
		}*/
		File file = new File("d:\\workspace\\lianxi\\day20"); 
		String[] str = file.list();

		for (String s : str)
		{
			sop(s);
		}
		
	}

	public static void method2()throws Exception
	{
		File file = new File("d:\\workspace\\lianxi\\day20"); 

		//使用匿名内部类的方式构造过滤器
		String[] str = file.list(new FilenameFilter()//很重要,要熟记。
		{
			public boolean accept(File dir,String name)
			{
				return name.endsWith(".java");
			}
		});

		for (String s : str)
		{
			sop(s);
		}
	}
	
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}

2-1-2 递归

递归:就是函数自身调用自身。

什么时候用递归呢?

当一个功能被重复使用,而每一次使用该功能时的参数不确定,都由上次的功能元素结果来确定。

简单说:功能内部又用到该功能,但是传递的参数值不确定。(每次功能参与运算的未知内容不确定)。

递归的注意事项:

1:一定要定义递归的条件。

2:递归的次数不要过多。容易出现 StackOverflowError 栈内存溢出错误。

其实递归就是在栈内存中不断的加载同一个函数。

示例一:使用递归获取一个目录中的所有文件名

class FileDemo3 
{
	public static void main(String[] args) 
	{
		File dir = new File("d:\\workspace\\lianxi");
		fileRecursion(dir,0);
	}


	public static String getLevel(int level)
	{
		StringBuilder sb = new StringBuilder();
		for (int i=0; i<=level; i++)
		{
			sb.append("|--");
		}
		return level+" "+sb.toString();
	}
	public static void fileRecursion(File dir,int level)
	{
		
		sop(getLevel(level)+dir);
		level++;
		File[] f = dir.listFiles();

		for (File f1 : f)
		{
			if(f1.isDirectory())
				fileRecursion(f1,level);
			if(f1.isFile())
				sop(dir);
		}
	}

	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}
示例二:删除一个目录中的所有文件

class FileListDemo
{
	public static void main(String[] args)throws IOException
	{
		File dir = new File("d:\\workspace\\lianxi");
		List<File> li = new ArrayList<File>();

		getFile(dir,li);
		toText(li);
	}

	public static void getFile(File dir,List<File> li)
	{
		File[] f = dir.listFiles();
		li.add(dir);
		for (File f1 : f)
		{
			if(f1.isDirectory())
				getFile(f1,li);
			else
				li.add(f1);
		}
		//删除时这里需要加一句delete,打印和添加到集合不需要
	}

	public static void toText(List<File> li)throws IOException
	{
		BufferedWriter bufw = new BufferedWriter(new FileWriter("sb.txt"));

		for (File f : li)
		{
			String s = f.getAbsolutePath();
			bufw.write(s);
			bufw.newLine();
			bufw.flush();
		}

		bufw.close();
	}
}

2-2 Properties类

特点:1:可以持久化存储数据。2:键值都是字符串。3:一般用于配置文件。

|-- load():将流中的数据加载进集合。

原理:其实就是将读取流和指定文件相关联,并读取一行数据,因为数据是规则的key=value,所以获取一行后,通过 = 对该行数据进行切割,左边就是键,右边就是值,将键、值存储到properties集合中。

|-- store():写入各个项后,刷新输出流。

|-- list():将集合的键值数据列出到指定的目的地

Properties是hashtable的子类,也就是说它具备map集合的特点,而且它里面存储的键值对都是字符串,是集合中和IO技术相结合的集合容器。
该对象的特点:可以用于键值对形式的配置文件。
那么在加载数据时,需要数据有固定格式:键=值。

练习:限制程序运行次数。当运行次数到达5次时,给出,请您注册的提示。并不再让该程序执行。

思路:

如果使用次数已到,那么给出注册提示,很容易想到的是:计数器。
可是该计数器定义在程序中,随着程序的运行而在内存中存在,并进行自增。
可是随着该应用程序的退出,该计数器也在内存中消失了,下一次在启动该程序,又重新开始从0计数,这样不是我们想要的。
程序即使结束,该计数器的值也存在,下次程序启动在会先加载该计数器的值并加1后在重新存储起来。
所以要建立一个配置文件。用于记录该软件的使用次数,该配置文件使用键值对的形式。这样便于阅读数据,并操作数据。
键值对数据是map集合。
数据是以文件形式存储,使用io技术。那么map+io -->properties.

配置文件可以实现应用程序数据的共享。

class PropertiesTest 
{
	public static void main(String[] args) throws Exception
	{
		Properties p = new Properties();
		File f = new File("count.ini");
		if (! f.exists())
			f.createNewFile();

//		Properties p = new Properties();
//
//		File f = new File("count.ini");
//		if(!f.exists())
//			f.createNewFile();
		
		FileInputStream fis = new FileInputStream(f);

		p.load(fis);

		int num = 0;

		String times = p.getProperty("time");
		if (times != null)
		{
			num = Integer.parseInt(times);
		}
		
		if (num >=5)
		{
			System.out.println("使用次数已满,请充值");
			return ;//返回值为void的函数中return,用来终止函数运行
		}

		num++;

		p.setProperty("time",num+"");

		FileOutputStream fos = new FileOutputStream(f);

		p.store(fos,"ruanjianshiyongcishu");

		fos.close();
		fis.close();
	}
}

2-3 IO流其他常用流

2-3-1 打印流

PrintStream可以操作目的:1:File对象。2:字符串路径。3:字节输出流。

前两个都JDK1.5版本才出现。而且在操作文本文件时,可指定字符编码了。

当目的是一个字节输出流时,如果使用的println方法,可以在printStream对象上加入一个true参数。这样对于println方法可以进行自动的刷新,而不是等待缓冲区满了再刷新。最终print方法都将具体的数据转成字符串,而且都对IO异常进行了内部处理。

既然操作的数据都转成了字符串,那么使用PrintWriter更好一些。因为PrintWrite是字符流的子类,可以直接操作字符数据,同时也可以指定具体的编码。

PrintWriter:具备了PrintStream的特点同时,还有自身特点:

该对象的目的地有四个:1:File对象。2:字符串路径。3:字节输出流。4:字符输出流。

开发时尽量使用PrintWriter。

方法中直接操作文件的第二参数是编码表。

直接操作输出流的,第二参数是自动刷新。

示例:

class PrintStreamDemo 
{
	public static void main(String[] args) throws IOException
	{
		BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
		PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter("abc.txt")),true);

		String line = null;

		while ((line = bufr.readLine()) != null)
		{
			if("over".equals(line))
				break;
			pw.println(line.toUpperCase());
		}

		bufr.close();
		pw.close();
	}
}

2-3-2 序列流

作用就是将多个读取流合并成一个读取流。实现数据合并。

表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。

这样做,可以更方便的操作多个读取流,其实这个序列流内部会有一个有序的集合容器,用于存储多个读取流对象。

该对象的构造函数参数是枚举,想要获取枚举,需要有Vector集合,但不高效。需用ArrayList,但ArrayList中没有枚举,只有自己去创建枚举对象。

但是方法怎么实现呢?因为枚举操作的是具体集合中的元素,所以无法具体实现,但是枚举和迭代器是功能一样的,所以,可以用迭代替代枚举。

合并原理:多个读取流对应一个输出流。

示例:

class SequenceDemo 
{
	public static void main(String[] args) throws IOException
	{
		Vector<FileInputStream> v = new Vector<FileInputStream>();

		v.add(new FileInputStream("c:\\1.txt"));
		v.add(new FileInputStream("c:\\2.txt"));
		v.add(new FileInputStream("c:\\3.txt"));

		SequenceInputStream sis = new SequenceInputStream(v.elements());
		BufferedReader bufr = new BufferedReader(new InputStreamReader(sis));

		BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("c:\\4.txt")));
		
		String line = null;

		while ((line = bufr.readLine()) != null)
		{
			bufw.write(line);
			bufw.flush();
		}


		bufw.close();
		bufr.close();
	}
}

切割原理:一个读取流对应多个输出流

示例:

class SplitDemo
{
	public static void main(String[] args)throws IOException
	{
		merge();
	}


    public static void merge()throws IOException
	{
		File f = new File("d:\\1.jpg");
		List<FileInputStream> list = new ArrayList<FileInputStream>();
		
		for (int i=1; i<=3; i++)
		{
			list.add(new FileInputStream(f+".part"+i));
		}

		final Iterator<FileInputStream> it = list.iterator();
		Enumeration<FileInputStream> en = new Enumeration<FileInputStream>()
		{
			public boolean hasMoreElements()
			{
				return it.hasNext();
			}

			public FileInputStream nextElement()
			{
				return it.next();
			}
		};

		SequenceInputStream sis = new SequenceInputStream(en);
		FileOutputStream fos = new FileOutputStream("d:\\4.jpg");
		
		byte[] buf = new byte[1024];
		int len = 0;
		while ((len = sis.read(buf)) != -1)
		{
			fos.write(buf,0,len);
		}

		sis.close();
		fos.close();
		
	}
	public static void split()throws IOException
	{
		File f = new File("d:\\1.jpg");
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream(f));

		byte[] buf = new byte[1024*100];
		int len = 0;
		
		int count = 0;
		while ((len = bis.read(buf)) != -1)
		{
			count++;
			FileOutputStream fos = new FileOutputStream(f+".part"+count);

			fos.write(buf,0,len);

			fos.close();
		}
	}
}

2-3-3 RandomAccessFile

特点:

1:该对象即可读取,又可写入。

2:该对象中的定义了一个大型的byte数组,通过定义指针来操作这个数组。

3:可以通过该对象的getFilePointer()获取指针的位置,通过seek()方法设置指针的位置。

4:该对象操作的源和目的必须是文件。 

5:其实该对象内部封装了字节读取流和字节写入流。

注意:实现随机访问,最好是数据有规律。

class RandomAccessFileDemo 
{
	public static void main(String[] args) throws IOException
	{
		writeFile();
		readFile();
	}

	public static void writeFile()throws IOException
	{
		RandomAccessFile raf = new RandomAccessFile("shabi.txt","rw");

		raf.seek(8);

		raf.write("sb".getBytes());
		raf.writeInt(27);

		raf.close();
	}

	public static void readFile()throws IOException
	{
		RandomAccessFile raf = new RandomAccessFile("shabi.txt","rw");

//		byte[] buf = new byte[4];
//		raf.read(buf);
//
//		String s = new String(buf);

		//调整对象中指针。
		//raf.seek(8*1);

		//跳过指定的字节数
		//raf.skipBytes(8);

		raf.seek(4);
		int age = raf.readInt();

		byte[] buf = new byte[4];
		raf.read(buf);
		String s = new String(buf);

		System.out.println(age+s);

		raf.close();
	}
}

2-3-4 管道流

道读取流和管道写入流可以像管道一样对接上,管道读取流就可以读取管道写入流写入的数据。

注意:需要加入多线程技术,因为单线程,先执行read,会发生死锁,因为read方法是阻塞式的,没有数据的read方法会让线程等待。

2-3-5 DataInputStream与DataOutputStream

可以用于操作基本数据类型的数据的流对象。

class DataStreamDemo 
{
	public static void main(String[] args) throws IOException
	{
		//writeData();
		//readData();

		//writeUTFDemo();

//		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk.txt"),"gbk");
//
//		osw.write("你好");
//		osw.close();

//		readUTFDemo();

	}
	public static void readUTFDemo()throws IOException
	{
		DataInputStream dis = new DataInputStream(new FileInputStream("utf.txt"));

		String s = dis.readUTF();

		System.out.println(s);
		dis.close();
	}



	public static void writeUTFDemo()throws IOException
	{
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfdate.txt"));

		dos.writeUTF("你好");

		dos.close();
	}

	public static void readData()throws IOException
	{
		DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));

		int num = dis.readInt();
		boolean b = dis.readBoolean();
		double d = dis.readDouble();

		System.out.println("num="+num);
		System.out.println("b="+b);
		System.out.println("d="+d);

		dis.close();
	}
	public static void writeData()throws IOException
	{
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));

		dos.writeInt(234);
		dos.writeBoolean(true);
		dos.writeDouble(9887.543);

		dos.close();

		ObjectOutputStream oos = null;
		oos.writeObject(new O());

		
	}
}

2-3-6 ByteArrayInputStream与ByteArrayOutputStream

ByteArrayInputStream :在构造的时候,需要接收数据源,。而且数据源是一个字节数组。
ByteArrayOutputStream: 在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组。
这就是数据目的地。
因为这两个流对象都操作的数组,并没有使用系统资源。
所以,不用进行close关闭。

示例:

class ByteArrayStream 
{
	public static void main(String[] args) 
	{
		
		read();
	}

	public static void read()
	{
		ByteArrayInputStream bais = new ByteArrayInputStream("shabi".getBytes());
		ByteArrayOutputStream baos = new ByteArrayOutputStream();

		int buf = 0;
		while((buf = bais.read()) != -1)
		{
			baos.write(buf);
		}

		System.out.println(baos.toString());
	}
}

------- android培训java培训、期待与您交流! ----------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值