Java基础<十六>---> IO之其他流对象

第一  File类

一、概述:File类是有文件或文件件封装而来的对象,可以操作其属性信息,这个类的出现弥补了流的不足,流只能操作数据

1、特点:

1)用来将文件或文件夹封装成对象

2)方便于对文件与文件夹的属性信息进行操作

3)File对象可以作为多数传递给流的构造函数

2、File类常见方法:
1、创建。
boolean createNewFile():在指定位置创建文件,如果该文件已经存在,则不创建,返回false。
和输出流不一样,输出流对象一建立创建文件。而且文件已经存在,会覆盖。
boolean mkdir():创建文件夹。
boolean mkdirs():创建多级文件夹。
2、删除。
boolean delete():删除失败返回false。如果文件正在被使用,则删除不了返回false。
void deleteOnExit();在程序退出时删除指定文件。
3、判断。
boolean exists() :文件是否存在.
isFile():是否是文件
isDirectory();是否是目录
isHidden();是否是隐藏的对象
isAbsolute();//判断是否是绝对路径
4、获取信息。
getName():获取对象名字
getPath():获取路径,存的是什么路径就是什么路径
getParent():文件夹路径
getAbsolutePath() 获取绝对路径getpath()+getparent();
long lastModified() 文件最后一次被修改的时间
long length() 返回文件的长度

实例:

public class FileDemo {
	public static void main(String[] args) {
//		consMethod();
//		method();
//		listFilesDemo();
//		File dir = new File("E:\\develop\\eclipse space\\android workspace\\Test_java\\src");
//		showDir(dir);
//		method_1();
//		method_2(100);
	}
	
	/**
	 * 获取指定文件名称的文件名
	 */
	public static void method(){
		File dir = new File("C:\\");
		String[] names = dir.list(new FilenameFilter() {
			
			@Override
			public boolean accept(File dir, String name) {
//				System.out.println("dir:" + dir + ".....name::" + name);
				return name.endsWith(".txt");
			}
		});
		
		System.out.println("list.len:::" + names.length);
		for(String name : names){
			System.out.println(name);
		}
	}

	
	/**
	 * listFiles()方法返回File对象
	 */
	public static void listFilesDemo(){
		File dir = new File("C:\\");
		File[] files = dir.listFiles();
		for(File f : files){
			System.out.println(f.getName() + "..." + f.length());
		}
	}
	
	/**
	 * 构造方法演示
	 */
	public static void consMethod(){
		//创建file对象,可以将已有的和未出现的文件或目录封装成对象
		File f1 = new File("C:" + File.separator + "a.txt");//File.separator跨平台的目录分隔符
		File f2 = new File("c:\\abc","b.txt");//C:\\abc\\b.txt一样,但是两个参数更灵活,可以目录不变,文件变
		
		File d = new File("c:\\abc");
		File f3 = new File(d,"c.txt");
		
		System.out.println("f1: " + f1);
		System.out.println("f2: " + f2);
		System.out.println("f3: " + f3);
	}
	
	/**
	 * 演示File类的一些常用方法
	 */
	public static void method_1(){
		File f = new File("file.txt");
		//exists()方法查看文件或目录是否存在
		System.out.println("exists:" + f.exists());
		//测试应用程序是否可以执行此抽象路径名表示的文件。
		System.out.println("canExecute:" + f.canExecute());
		//创建文件夹
//		File dir = new File("C:\\ab\\c\\v\\g");
//		System.out.println("mkdir:" + dir.mkdir());//最多只能创建两级目录,已经存在或失败返回false
//		System.out.println("mkdirs:" + dir.mkdirs());//创建多级目录
		
		File f1 = new File("c:\\ab\\d.java");
		try {
			f1.createNewFile();//createNewFile创建新文件
		} catch (IOException e) {
			e.printStackTrace();
		}
		//判断文件对象是否是文件或目录时,必须要先判断文件对象封装内容是否存在
		//通过exists方法
		System.out.println("isDir:" + f1.isDirectory());//是否是目录
		System.out.println("isFile:" + f1.isFile());//是否是文件
		
		//获取路径
		System.out.println("path: " + f1.getPath());
		System.out.println("abspath: " + f1.getAbsolutePath());
		System.out.println("parent: " + f1.getParent());//该方法返回的是绝对路径中的父目录。如果获取的是相对路径,返回null。
		//如果相对路径中有上一层目录那么该目录就是返回结果。
		
		//重命名,其实就是远文件再创建一个新文件,再把内容写入文件
		File f2 = new File("c:\\ab\\d.java");
		File f3 = new File("c:\\d.java");
		System.out.println("rename:" + f2.renameTo(f3));
	}
}

二、递归:对于每次循环都是用同一个功能的函数,即函数自身调用自身,这种表现形式或手法,称为递归。

注意:

1、限定条件,是作为结束循环用的,否则是死循环

2、注意递归的次数,尽量避免内存溢出。因为每次调用自身的时候都会先执行下一次调用自己的发那个发,所以会不断在栈内存中开辟新空间,次数过多,会导致内存溢出。

实例一:求和和求二进制

/**
	 * 递归方法演示
	 */
	public static int method_2(int num){
//		//求和
//		if(num == 1){
//			return 1;
//		}
//		return num + method_2(num - 1);
		
		//求二进制
		StringBuilder sb = new StringBuilder();
		if(num > 0){
			method_2(num/2);
			int i = num%2;
			sb.append(i);
		}
		System.out.println(sb.toString());
		return -1;
	}

实例二:显示所有目录下的文件,也就是说只要是目录就被循环

思路:既然要显示所有文件,就是要循环每一个文件夹,找出所有文件,这里操作文件夹的动作都是一样的,所有需要用到递归:

/**
	 * 显示所有目录下的文件,也就是说只要是目录就被循环
	 * 这里用到了递归调用
	 */
	public static void showDir(File dir){
		System.out.println(dir);
		File[] files = dir.listFiles();
		for(File f : files){
			if(f.isDirectory()){
				showDir(f);
			}else{
				System.out.println(f);
			}
		}
	}

实例三:将一个指定目录下的java文件的绝对路径,存储到一个文本文件中。

思路:
  1、对指定目录进行递归
  2、获取递归过程中所有的java文件
  3、把路径存放在集合中
  4、把集合中的数据写入文件

/*
 * 将一个指定目录下的java文件的绝对路径,存储到一个文本文件中。
 * 
 * 思路:
 * 1、对指定目录进行递归
 * 2、获取递归过程中所有的java文件
 * 3、把路径存放在集合中
 * 4、把集合中的数据写入文件
 */
public class JavaFileList {
	public static void main(String[] args) {
		// 指定查找路径
		File dir = new File(
				"E:\\develop\\eclipse space\\android workspace\\Test_java");
		// 定义集合用于存储取到的java路径
		List<File> list = new ArrayList<File>();
		fileToList(dir, list);// 调用查找文件方法
		// System.out.println(list.size());

		// 指定写入文件路径
		File file = new File("c:\\", "javalist.txt");
		writeToFile(list, file.toString());// 调用写入文件的方法
	}

	/*
	 * 获取指定目录下的java文件
	 */
	public static void fileToList(File dir, List<File> list) {
		File[] files = dir.listFiles();// 返回该目录下的文件对象
		for (File f : files) {
			if (f.isDirectory()) {
				fileToList(f, list);
			} else {
				if (f.getName().endsWith(".java"))
					;// 找出java文件
				list.add(f);
			}
		}
	}

	/*
	 * 将数据写入文件中
	 */
	public static void writeToFile(List<File> list, String filepath) {
		BufferedWriter bfw = null;
		try {
			bfw = new BufferedWriter(new FileWriter(filepath));

			for (File f : list) {
				String path = f.getAbsolutePath();// 获取文件绝对路径
				bfw.write(path);
				bfw.newLine();
				bfw.flush();
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (bfw != null) {
				try {
					bfw.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

第二  Properties类

一、概述:

1、Properties是Hashtable的子类,也就是map集合的子类,所有具备Map集合的特点,而且它里面还有存储的键值对,都是字符串,无泛型定义。是集合中和IO技术相结合的集合容器。

2、特点:

1)可用于键值对形式的配置文件

2)在加载时,需要数据有固定的格式,常用的是:键=值

二、特有方法:

1、设置和获取元素:

Object setProperty(String key,String value)调用Hashtable 的方法put,将键和值存入到properties对象中

String getProperty(String key)用指定的键在此属性列表中搜索属性

Set<String> stringPropertyName()返回此属性列表中的键集

void load(InputStream ism)从输入流中读取属性列表(键和元素对)。

void load(Reader reader)按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。

实例:

/*
 * Properties是hashtable的子类
 * 也就是说它具备map集合的特点,而且它里面存放的键值对都是字符串
 * 是集合和IO技术相结合的集合容器
 * 该对象的特点可以用于键值对形式的配置文件
 * 那么在加载时需要数据有固定的格式:键=值
 * 
 */
public class ProPertiesDemo {
	public static void main(String[] args) {
		getAndSet();
//		method();
		loadDemo();
	}
	
	/*
	 * 设置和获取元素。
	 */
	public static void getAndSet(){
		Properties pro = new Properties();
		
		pro.setProperty("zhangsan", "20");
		pro.setProperty("lisi", "30");
		System.out.println(pro);
		
		String value = pro.getProperty("lisi");
		System.out.println("getProperty: " + value);
		pro.setProperty("lisi", "90");
		
		Set<String> names = pro.stringPropertyNames();
		for(String name : names){
			System.out.println(name + "::" + pro.getProperty(name));
		}
		
	}
	
	/*
	 * 演示如何将流中的数据存储到集合中
	 * 想要将info.txt中的数据存放在集合中进行操作
	 * 
	 * 思路:
	 * 1、用流关联info.txt文件
	 * 2、读取一行数据,将改行数据用“=”切割
	 * 3、将等号左边作为键,右边作为值存放到properties集合中
	 */
	public static void method(){
		BufferedReader bfr = null;
		Properties pro = null;
		try {
			bfr = new BufferedReader(new FileReader("info.txt"));
			pro = new Properties();
			
			String line = null;
			while((line=bfr.readLine()) != null){
//				System.out.println(line);
				String[] arr = line.split("=");
				pro.setProperty(arr[0], arr[1]);
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			if(bfr != null){
				try {
					bfr.close();
					System.out.println(pro);
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	/*
	 * 用Properties对象提供的方法加载文件中的键值对
	 */
	public static void loadDemo(){
		Properties pro = new Properties();
		FileInputStream fis = null;
		FileOutputStream fos = null;
		try {
			fis = new FileInputStream("info.txt");
			//用properties的load方法把流中的数据加载进集合
			pro.load(fis);
			System.out.println(pro);
			
			//修改
			pro.setProperty("wangwu", "90");//这里修改只是在内存中,而没有修改文件
			fos = new FileOutputStream("info.txt");
			//通过store方法将修改的数据修改到文件中区
			pro.store(fos, "change comments");
			
			pro.list(System.out);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			if(fis != null){
				try {
					fis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

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

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

	public static void main(String[] args) {
		// method();
		tMethod();
	}

	/*
	 * 我的思路: 程序没运行一次,就在相应的文件中写入一次记录,每次运行都去读取文件记录, 当文件中的记录达到五条是,就限制程序再运行
	 */
	public static void method() {
		Properties pro = new Properties();
		FileOutputStream fos = null;
		FileInputStream fis = null;
		int i = 0;
		try {
			// 读取数据
			fis = new FileInputStream("info.txt");
			pro.load(fis);
			Set<String> names = pro.stringPropertyNames();
			i = names.size() + 1;
			System.out.println(names.size());
			if (names.size() == 5) {
				throw new RuntimeException("请您注册");
			}

			// 写入数据
			i++;
			fos = new FileOutputStream("info.txt", true);
			pro.setProperty(i + "", "key");
			pro.store(fos, "comments");

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/*
	 * 老师的思路 很容易想到的是:计数器。 可是该计数器定义在程序中,随着程序的运行而在内存中存在,并进行自增。
	 * 可是随着该应用程序的退出,该计数器也在内存中消失了。
	 * 
	 * 下一次在启动该程序,又重新开始从0计数。 这样不是我们想要的。
	 * 
	 * 程序即使结束,该计数器的值也存在。 下次程序启动在会先加载该计数器的值并加1后在重新存储起来。
	 * 
	 * 所以要建立一个配置文件。用于记录该软件的使用次数。
	 * 
	 * 该配置文件使用键值对的形式。 这样便于阅读数据,并操作数据。
	 * 
	 * 键值对数据是map集合。 数据是以文件形式存储,使用io技术。 那么map+io -->properties.
	 * 
	 * 配置文件可以实现应用程序数据的共享。
	 */
	public static void tMethod() {
		Properties pro = new Properties();

		// 需要操作文件,用File对象判断文件是否存在
		File file = new File("count.ini");
		FileInputStream fis = null;
		FileOutputStream fos = null;

		if (!file.exists()) {
			try {
				file.createNewFile();// 不存在就创建一个
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		try {
			fis = new FileInputStream(file);
			pro.load(fis);// 将流中的数据加载进文件

			// 定义计数器
			int count = 0;
			// 获取计数器
			String value = pro.getProperty("time");

			if (value != null) {
				count = Integer.parseInt(value);
				if (count >= 5) {
					System.out.println("请您注册");
					return;
				}
			}

			count++;
			pro.setProperty("time", count + "");

			fos = new FileOutputStream(file);
			pro.store(fos, null);// 写入文件
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if(fis != null){
				try {
					fis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(fos != null){
				try {
					fos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

第三  打印流

一、概述:

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

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

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

实例:

public class PrintStreamDemo {
	public static void main(String[] args) {
		method();
	}

	public static void method() {
		BufferedReader bfr = new BufferedReader(
				new InputStreamReader(System.in));

		// PrintWriter out = new
		// PrintWriter(System.out);//PrintStream(OutputStream out)
		// PrintStream(OutputStream out, boolean autoFlush)
		// autoFlush - boolean 变量;如果为 true,则每当写入 byte 数组、调用其中一个 println
		// 方法或写入换行符或字节 ('\n') 时都会刷新缓冲区
		// PrintWriter out = new PrintWriter(System.out, true);//输出到控制台

		// 输出到文件
		PrintWriter out = null;
		try {
			// PrintStream(File file)和PrintStream(OutputStream out, boolean
			// autoFlush)结合
			out = new PrintWriter(new BufferedWriter(new FileWriter("a.txt")),
					true);
		} catch (IOException e1) {
			e1.printStackTrace();
		}

		String line = null;
		try {
			while ((line = bfr.readLine()) != null) {
				if (line.equals("over")) {
					break;
				}
				out.println(line.toUpperCase());
				// out.flush();
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (bfr != null) {
				try {
					bfr.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (out != null) {
				out.close();
			}
		}
	}
}

第四  合并流

一、概述:

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

二、如何合并多个文件:

1、创建集合,并将流对象添加进集合或者SequenceInputStream(InputStream s1,InputStream s2)加入两个输入流

2、创建Enumeration对象,将集合元素加入。

3、创建SequenceInputStream对象,合并流对象

4、创建写入流对象,FileOutputStream,将数据写入流资源

5、定义数组,将读取流数据存入数组,并将数组中元素写入文件中。

实例:

/*
 * SequenceInputStream 表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,
 * 直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。 
 */
public class SequenceInputStreamDemo {
	public static void main(String[] args) {
		Vector<FileInputStream> vector = new Vector<FileInputStream>();
		
		//sequenceinputstream操作多个对象
		SequenceInputStream sequence = null;
		BufferedOutputStream bfos = null;
		try {
			vector.add(new FileInputStream("1.txt"));
			vector.add(new FileInputStream("2.txt"));
			vector.add(new FileInputStream("3.txt"));
			
			//通过vector获取枚举Enumeration
			Enumeration<FileInputStream> enumeration = vector.elements();
			
			sequence = new SequenceInputStream(enumeration);
			
			FileOutputStream fos = new FileOutputStream("4.txt");
			bfos = new BufferedOutputStream(fos);
			byte[] byf = new byte[1024];
			
			int len = 0;
			while((len = sequence.read(byf)) != -1){
				bfos.write(byf,0,len);
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			if(sequence != null){
				try {
					sequence.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(bfos != null){
				try {
					bfos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

6、切割文件

重点,定义一个你需要把文件切成多大的数组,然后把数据读到数组中,读满后写出到文件中,这样就完成了切割:

/*
 * 文件的切割和合并
 */
public class SplitFile {
	public static void main(String[] args) throws IOException {
//		splitFile();
		sequence();
	}
	
	/*
	 * 切割文件
	 */
	public static void splitFile() throws IOException
	{
		FileInputStream fis = new FileInputStream("0.jpg");
		FileOutputStream fos = null;
		
		byte[] byf = new byte[1024*1024];//定义需要切割大小的空间
		
		int len = 0;
		int count = 1;//为文件取名用的数字
		while((len=fis.read(byf)) != -1){
			fos = new FileOutputStream((count++) + ".patch");//创建指定的文件
			fos.write(byf,0,len);//把读取到的数据写入指定的文件中
			fos.close();//关闭资源
		}
		fis.close();//关闭资源
	}
	
	/*
	 * 合并文件
	 */
	public static void sequence() throws IOException{
		ArrayList<FileInputStream> list = new ArrayList<FileInputStream>();
		list.add(new FileInputStream("1.patch"));
		list.add(new FileInputStream("2.patch"));
		list.add(new FileInputStream("3.patch"));
		
		//通过迭代器方法获取枚举
		final Iterator<FileInputStream> it = list.iterator();//内部类使用的成员变量必须是final修饰的
		
		//通过内部类实现枚举
		Enumeration<FileInputStream> en = new Enumeration<FileInputStream>() {
			
			@Override
			public FileInputStream nextElement() {
				return it.next();
			}
			
			@Override
			public boolean hasMoreElements() {
				return it.hasNext();
			}
		};
		
		SequenceInputStream sequence = new SequenceInputStream(en);
		
		FileOutputStream fos = new FileOutputStream("10.jpg");
		
		byte[] byf = new byte[1024];
		int len = 0;
		while((len=sequence.read(byf)) != -1){
			fos.write(byf,0,len);
		}
		sequence.close();
		fos.close();
	}
}

第五  Objectstream

一、概述:

ObjectStream是可以操作对象的流。它的写方法是ObjectOutputStream,读方法是ObjectInputStream。它主要操作的是对象,而对象中也能封装数据,所以它也具备操作基本数据类型的方法。被它操作的对象必须是实现了序列化的对象也就是Serializable接口,但是输入流还多支持一种Externalizable 接口的对象。

实例:

/*
 * 对对象进行读写操作
 */
public class ObjectstreamDemo {
	public static void main(String[] args) throws Exception {
//		writeObj();
		readObj();
	}
	
	/*
	 * 写对象
	 */
	public static void writeObj() throws Exception{
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.object"));
		
		oos.writeObject(new Person("zhangsan", 23, "ASC"));
		oos.close();
	}
	
	/*
	 * 读取对象
	 */
	public static void readObj() throws Exception{
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.object"));
		
		Person p = (Person) ois.readObject();
		System.out.println(p);
		ois.close();
	}
}
用作被操作的类:

/*
 * 用作对象的持久化存储,也就是用流存放到硬盘上
 * 
 * 静态不能被序列化
 * transient关键字修饰的不能被序列化
 */
public class Person implements Serializable//实现这个接口,让该类被序列化
{
	//给类指定一个序列化标识,方便序列化,不定义会自动生成一个
	private static final long serialVersionUID = 42L;
	
	private String name;
	private int age;
	private static String country = "cn";
	
	Person(String name, int age, String country){
		this.name = name;
		this.age = age;
		Person.country = country;
	}
	
	public String toString(){
		return name+":"+age+":"+country;
	}
}

第六  管道流

一、概述:

可以将管道输出流连接到管道输入流来创建通信管道。管道输出流是管道的发送端。通常,数据由某个线程写入PipedOutputStream 对象,并由其他线程从连接的PipedInputStream 读取。不建议对这两个对象尝试使用单个线程,因为这样可能会造成该线程死锁。如果某个线程正从连接的管道输入流中读取数据字节,但该线程不再处于活动状态,则该管道被视为处于毁坏状态

二、使用步骤:

1、要先创建一个读和写的两个类,实现Runnable接口,因为是两个不同的线程,覆盖run方法,注意,需要在内部处理异常,因为重写run方法

2、创建两个管道流,并用connect()方法将两个流连接

3、创建读写对象,并传入两个线程内,并start执行

实例:

/*
 * 管道流
 * 用多线操作,单线程容易造成死锁
 */
public class PipedStreamDemo {
	public static void main(String[] args) throws IOException {
		PipedInputStream in = new PipedInputStream();
		PipedOutputStream out = new PipedOutputStream();
		in.connect(out);

		Read r = new Read(in);
		Write w = new Write(out);

		new Thread(r).start();
		new Thread(w).start();
	}

}

class Write implements Runnable {
	private PipedOutputStream out;

	Write(PipedOutputStream out) {
		this.out = out;
	}

	@Override
	public void run() {
		try {
			Thread.sleep(4000);
			out.write("piped stream come".getBytes());
			out.close();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

}

class Read implements Runnable {
	private PipedInputStream in;

	Read(PipedInputStream in) {
		this.in = in;
	}

	@Override
	public void run() {
		byte[] byf = new byte[1024];
		int len = 0;

		try {
			while ((len = in.read(byf)) != -1) {
				String str = new String(byf, 0, len);
				System.out.println(str);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

第七  RandomAccessFile

一、概述:

1、RandomAccessFile称之为随机访问文件的类,自身具备读写方法。

2、该类不算是IO体系中的子类,而是直接继承Object,但是它是IO包成员,因为它具备读写功能,内部封装了一个数组,且通过指针对数组的元素进行操作,同时可通过seek改变指针的位置。

3、可以完成读写的原理:内部封装了字节输入流

4、构造函数:RandomAccessFile(File file,String mode),可已从它的构造函数中看出,该类只能操作文件(也有字符串),而且操作文件还有模式。

模式传入值:”r“:以只读方式打开;”rw“:打开以便读写

如果模式为只读,则不会创建文件,会去读一个已存在的文件,若文件不存在,则会出现异常,如果模式为rw,且该对象的构造函数要操作的文件不存在,会自动创建,如果存在,则不会覆盖,也可通过seek方法修改。

二、特有方法:

1、seek(int n):设置指针,可以将指针设置到前面或后面

2、skipBytes(int n):跳过指定字节数,不可往前跳

实例:

/*
 * 随机访问文件的类
 * 该类不算是IO体系中子类。而是直接继承自Object。
 * 但是它是IO包中成员。因为它具备读和写功能。
 * 内部封装了一个数组,而且通过指针对数组的元素进行操作。
 * 可以通过getFilePointer获取指针位置,同时可以通过seek改变指针的位置。
 * 
 * 其实完成读写的原理就是内部封装了字节输入流和输出流。
 * 通过构造函数可以看出,该类只能操作文件。而且操作文件还有模式:只读r,,读写rw等。
 * 如果模式为只读 r。不会创建文件。会去读取一个已存在文件,如果该文件不存在,则会出现异常。
 * 如果模式rw。操作的文件不存在,会自动创建。如果存则不会覆盖。
 */
public class RandomAccessFileDemo {
	public static void main(String[] args) throws IOException {
		writeFile();
//		readFile();
	}
	
	public static void writeFile() throws IOException{
		RandomAccessFile raf = new RandomAccessFile("random.txt","rw");
		
		raf.write("张三".getBytes());
		raf.writeInt(97);//write方法只读取最低8位
		raf.write("李四".getBytes());
		raf.writeInt(98);
		
		//********往指定位置上存储数据,也可以修改数据
		raf.seek(8*4);
		raf.write("王五".getBytes());
		raf.writeInt(103);
		
		raf.close();
	}
	
	public static void readFile() throws IOException{
		RandomAccessFile raf = new RandomAccessFile("random.txt","r");
		
		//调整对象中的指针
//		raf.seek(8*1);//根据数组定,数组大小*跳几个
		
		//跳过指定的字节数,只能向前不能向后
		raf.skipBytes(8);
		
		byte[] buf = new byte[4]; 
		raf.read(buf);
		
		String name = new String(buf);
		int age = raf.readInt();
		System.out.println(name + " : " + age);
		raf.close();
	}
}

第八  DataStream

一、概述:

DataStream是可以用于操作基本数据类型的数据的流对象:DataInputStream与DataOutputStream,它主要的特点就是操作基本数据类型。

实例:

/*
 * DataInputStream与DataOutputStream
 * 
 * 可以用于操作基本数据类型的数据的流对象。
 */
public class DataStreamDemo {
	public static void main(String[] args) throws IOException {
//		write();
//		read();
//		writeUTF();
		readUTF();
		
		//一般加入编码的方法
//		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"),"utf-8");
//		osw.write("你好");
//		osw.close();
	}
	
	public static void write() throws IOException{
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
		
		dos.writeInt(236);
		dos.writeBoolean(false);
		dos.writeDouble(2323.02154);
		
		dos.close();
	}
	
	public static void read() throws IOException{
		DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
		
		int i = dis.readInt();
		boolean b = dis.readBoolean();
		double d = dis.readDouble();
		
		System.out.println("int: " + i);
		System.out.println("boolean: " + b);
		System.out.println("double: " + d);
		
		dis.close();
	}
	
	//加入了改编后的UTF-8编码,所以只有它自己能读
	public static void writeUTF() throws IOException{
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
		
		dos.writeUTF("你家婆");
		
		dos.close();
		
	}
	
	public static void readUTF() throws IOException{
		DataInputStream dis = new DataInputStream(new FileInputStream("utf.txt"));
		
		String s = dis.readUTF();
		System.out.println(s);
		
		dis.close();
		
	}
	
}

第九  ByteArraytStream

一、概述:

ByteArrayInputStream :在构造的时候,需要接收数据源,而且数据源是一个字节数组。

ByteArrayOutputStream: 在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组,这就是数据目的地。

注:因为这两个流对象都操作的数组,并没有使用系统资源。所以,不用进行close关闭,而且关闭也是无效的。

源设备,
  键盘 System.in,硬盘 FileStream,内存 ArrayStream
  目的设备:
   控制台 System.out,硬盘FileStream,内存 ArrayStream。

/*
 * ByteArrayInputStream :在构造的时候,需要接收数据源,。而且数据源是一个字节数组。
 * ByteArrayOutputStream: 在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组。
 * 这就是数据目的地。
 * 
 * 因为这两个流对象都操作的数组,并没有使用系统资源。
 * 所以,不用进行close关闭,而且关闭也是无效的。
 * 
 * 源设备,
 * 	键盘 System.in,硬盘 FileStream,内存 ArrayStream
 * 目的设备:
 * 	控制台 System.out,硬盘FileStream,内存 ArrayStream。
 */
public class ByteArrayStreamDemo {
	public static void main(String[] args) {
		//数据源
		ByteArrayInputStream bis = new ByteArrayInputStream("defef".getBytes());
		
		//目的地
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		
		int ch = 0;
		
		while((ch=bis.read()) != -1){
			bos.write(ch);
		}
		
		System.out.println(bos.size());
		System.out.println(bos.toString());
		
		try {
			//将此 byte 数组输出流的全部内容写入到指定的输出流参数中
			bos.writeTo(new FileOutputStream("b.txt"));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

第十  字符编码

一、‘常见的编码表:

1、ASCII:美国标准信息交换码表。用一个字节的7位表示

2、IOS8859-1:拉丁码表;欧洲码表。用一个字节的8位表示

3、GB2312:中国的中文编码表

4、GBK:中国的中文编码表升级,融合了更多的中文文字字符。打头的是两个高位为1的两个字节编码。为负数

5、Unicode:国际标准码,融合了多种文字

6、UTF-8:升级版国际码表,是可变长度的码表。最多用三个字节表示一个字符的编码表,包括:一位、两位、三位表示的字符

      UTF-8有自己的字节码:

一个字节:0开头

两个字节:字节一  ---> 110     位数:10 ~ 6

                    字节二  --->  10      位数:5 ~ 0

三个字节:字节一  ---> 1110     位数:15 ~ 12

                    字节二  --->  10      位数:11 ~ 6

                    字节三 --->  10       位数:5 ~ 0

二、编码和解码:

1、编码:字符串变成字节数组

      解码:字节数组变成字符串

2、转换:

1)默认字符集:

      String  --->  byte[]   :srt.getBytes()

      byte[]   --->  String  :new String(byte[])

2)指定字符集:

      String  --->  byte[]   :srt.getBytes(charsetName)

      byte[]   --->  String  :new String(byte[],charsetName)

三、对于编码和解码的字符集转换

1、如果编码失败,解码就没意义了。

2、如果编码成功,解码出来的是乱码,,则需对乱码通过再次编码(用解错码的编码表),然后再通过正确的编码表解码。针对于IOS8859-1是通用的。

3、如果用的是GBK编码,UTF-8解码,那么再通过2的方式,就不能成功了,因为UTF-8也支持中文,在UTF-8解的时候,会将对应的字节数改变,所以不会成功。

特别注意:对于中文的”联通“,这两个字比较特别,它的二进制位正好是和在UTF-8中两个字节打头的相同,可以找到对应的符号,但不再是”联通“了。但是只要他前面还有其他汉字就不会被被UTF-8解码了,也就不会出现乱码了。

实例:

/*
 * 编码:字符串变成字节数组。
 *  解码:字节数组变成字符串。
 *   String-->byte[];  str.getBytes(charsetName);
 *   byte[] -->String: new String(byte[],charsetName);
 */
public class EncodeDemo {
	public static void main(String[] args) {

		method();
	}

	public static void show() {
		String s = "你好";
		try {
			byte[] b = s.getBytes("GBK");
			System.out.println(Arrays.toString(b));
			String str = new String(b, "iso8859-1");// 编码正确,解码错误
			System.out.println(str);

			// 对str进行iso8859-1编码。以这样的方式解决乱码
			byte[] b1 = str.getBytes("iso8859-1");
			System.out.println(Arrays.toString(b1));
			String str1 = new String(b1, "GBK");
			System.out.println(str1);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
	}

	/*
	 * 联通
	 */
	public static void method() {
		String s = "联通";
		try {
			byte[] bs = s.getBytes("GBK");

			for (byte b : bs) {
				// Integer.toBinaryString将其转换为二进制,&255取出前面多余的1,保留最低8位
				// 联通的二进制是110...10...110...10...这个刚好符合utf-8的读取两个字节的编码形式,
				// 所以当度存在会被默认为utf-8编码来解读,从而造成乱码
				/*
				 * '\u0001' 到 '\u007F' 范围内的所有字符都是用单个字节表示的:
				 * 
				 * 位值 字节一 0 位 6-0
				 * 
				 * 
				 * null 字符 '\u0000' 以及从 '\u0080' 到 '\u07FF' 的范围内的字符用两个字节表示:
				 * 
				 * 位值 字节一 1 1 0 位 10-6
				 * 
				 * 字节二 1 0 位 5-0
				 * 
				 * 
				 * 
				 * '\u0800' 到 '\uFFFF' 范围内的 char 值用三个字节表示:  字节一  1 1 1 0 位
				 * 15-12
				 * 
				 * 字节二 1 0 位 11-6
				 * 
				 * 字节三 1 0 位 5-0
				 */
				System.out.println(Integer.toBinaryString(b & 255));
			}
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
	}
}

练习

五个学生,每个学生有3门课程的成绩,从键盘输入以上数据(姓名,三门课成绩),

输入格式:如:zahngsan,30,40,60计算出总成绩,并把学生的信息和计算出的总分数高低按顺序存放在磁盘文件stud.txt中

步骤:
1、描述学生对象
2、定义一个可操作学生对象的工具类

思路:
1、通过获取键盘录入一行的数据,并将该行数据的信息取出,封装成学生对象
2、因为学生对象很多,则需要存储,使用集合,因为要对学生总分排序
所以可以使用TreeSet
3、将集合中的信息写入到一个文件中

public class StudentInfoTest {
	public static void main(String[] args) {
		try {
			Comparator<StudentInfo> cmp = Collections.reverseOrder();// 强行反正比较顺序
			// Set<StudentInfo> stus = StudentInfoTool.getStudents();//默认比较
			Set<StudentInfo> stus = StudentInfoTool.getStudents(cmp);// 自定义比较器
			StudentInfoTool.write2File(stus);//调用写方法,将数据写文件中去
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

class StudentInfo implements Comparable<StudentInfo> {

	private String name;// 姓名
	private int ma, cn, en;// 数学、语文、英语成绩
	private int sum;// 总分

	//初始化就需要具备姓名,数学、语文、英语成绩
	StudentInfo(String name, int ma, int cn, int en) {
		this.name = name;
		this.ma = ma;
		this.cn = cn;
		this.en = en;

		sum = ma + cn + en;//计算出总成绩
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getMa() {
		return ma;
	}

	public void setMa(int ma) {
		this.ma = ma;
	}

	public int getCn() {
		return cn;
	}

	public void setCn(int cn) {
		this.cn = cn;
	}

	public int getEn() {
		return en;
	}

	public void setEn(int en) {
		this.en = en;
	}

	public int getSum() {
		return sum;
	}

	public void setSum(int sum) {
		this.sum = sum;
	}

	@Override
	public int compareTo(StudentInfo o) {//建立自己的比较方法
		int num = new Integer(this.sum).compareTo(new Integer(o.sum));//比较分数
		if (num == 0) {
			return this.name.compareTo(o.name);//主条件相同判定次要条件
		}
		return num;//返回比较的值,大于正数,相同0,小于负数
	}

	@Override
	public int hashCode() {//重写hashcode方法是因为数据有可能存到hashtable中
		return name.hashCode() + sum * 23;//尽可能保证hashcode值不相同,减少比较次数
	}

	@Override
	public boolean equals(Object obj) {//建立自己独有的判断相同方式
		if (!(obj instanceof StudentInfo)) {
			throw new ClassCastException("类型不匹配");
		}

		StudentInfo si = (StudentInfo) obj;
		return this.name.equals(si.name) && this.sum == si.sum;//姓名和总分相同视为同一人
	}

	@Override
	public String toString() {//建立自己的toString方法
		return "studentinfo[" + name + ", " + ma + ", " + cn + ", " + en + "]";
	}
}

class StudentInfoTool {
	//定义默认比较顺序
	public static Set<StudentInfo> getStudents() throws IOException {

		return getStudents(null);
	}

	//定义自定义的比较顺序
	public static Set<StudentInfo> getStudents(Comparator<StudentInfo> cmp)
			throws IOException {
		BufferedReader bfr = new BufferedReader(
				new InputStreamReader(System.in));

		Set<StudentInfo> stus = null;
		if (cmp == null) {
			stus = new TreeSet<StudentInfo>();
		} else {
			stus = new TreeSet<StudentInfo>(cmp);
		}
		String line = null;
		while ((line = bfr.readLine()) != null) {
			if ("over".equals(line)) {
				break;
			}
			String[] info = line.split(",");//将数据以“,”切开,形成新的数据

			StudentInfo si = new StudentInfo(info[0],
					Integer.parseInt(info[1]), Integer.parseInt(info[2]),
					Integer.parseInt(info[3]));

			stus.add(si);
		}

		bfr.close();
		return stus;
	}

	public static void write2File(Set<StudentInfo> stus) throws IOException {
		BufferedWriter bfw = new BufferedWriter(new FileWriter("stuinfo.txt"));

		for (StudentInfo si : stus) {
			bfw.write(si.toString() + "\t");
			bfw.write(si.getSum() + "");
			bfw.newLine();
			bfw.flush();
		}

		bfw.close();
	}
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值