JAVA SE基础(八)—— IO

目录

IO

File 类

IO流

IO原理

流的分类

补充

【注】


 

IO

File 类

  • java.io.File类:文件和目录路径名的抽象表示形式,与平台无关
  • File 能新建、删除、重命名文件和目录,但 File 不能访问文件内容本身。如果需要访问文件内容本身,则需要使用输入/输出流。
  • File对象可以作为参数传递给流的构造函数
  • File类的常见构造方法:
  1. public File(String pathname)  :以pathname为路径创建File对象,可以是绝对路径或者相对路径,如果pathname是相对路径,则默认的当前路径在系统属性user.dir中存储。
  2. public File(String parent,String child):以parent为父路径,child为子路径创建File对象。
  • File的静态属性String separator存储了当前系统的路径分隔符。 在UNIX中,此字段为‘/’,在Windows中,为‘\\’
//访问文件名:
getName()//获取文件名
getPath()//获取文件路径
getAbsoluteFile()//获取绝对路径文件名
getAbsolutePath()//获取绝对路径
getParent()//文件所在目录
renameTo(File newName)
/*当前file必须存在,newName文件必须不存在,当文件名中存在目录,则目录必须存在,可实现不同盘之间的重命名(文件存在新盘下,旧盘中不存在)
*/
//文件检测
//获取常规文件信息
lastModified()
length()
/*
File file1 = new File("iofiletest.txt");
*/
//exists();
file1.exists();//true
//canRead():文件是否可读
file1.canRead();//true
//canWrite():文件是否可写
file1.canWrite();//true
//canExecute():文件是否可执行
file1.canExecute();//true
//isFile():判断是否是文件
file1.isFile();//true
//isDirectory():是否是文件目录
file1.isDirectory();//false
//lastModified():文件的最后的修改时间
file1.lastModified();//1588062890754L
new Date(file1.lastModified());//Tue Apr 28 16:34:50 CST 2020
//length():文件的长度/大小
file1.length();//45



//文件操作相关
createNewFile()//新建文件
delete()//删除文件

//目录操作相关
mkDir()//新建文件夹(文件目录)
mkDirs()//新建多层目录文件
list()//String[]: 返回目录下的文件列表(string[]数组存储文件名)
listFiles()//File[]: 返回文件列表

 

IO流

IO原理

  • IO流用来处理设备之间的数据传输。
  • Java程序中,对于数据的输入/输出操作以”流(stream)” 的方式进行。
  • java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据
  • 输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。
  • 输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中

流的分类

  • 按操作数据单位不同分为:字节流(8 bit),字符流(16 bit)
  • 按数据流的流向不同分为:输入流,输出流
  • 按流的角色的不同分为:节点流,处理流
抽象基类字节流字符流
输入流InputStreamReader
输出流OutputStreamWriter
  1. Java的IO流共涉及40多个类,实际上非常规则,都是从如下4个抽象基类派生的。
  2. 由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。
  3. 节点流可从一个特定的数据源读写数据
  4. 处理流是“连接”在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能。
  5. 缓冲流“套接”在节点流之上
分类字节输入流字节输出流字符输入流字符输出流
抽象基类InputStreamOutputStreamReaderWriter
访问文件FileInputStreamFileOutputStreamFileReaderFileWriter
访问数组ByteArrayInputStreamByteArrayOutputStreamCharArrayReaderCharArrayWriter
访问管道PipeInputStreamPipedOutputStreamPipedReaderPipedWriter
访问字符串  StringReaderStringWriter
缓冲流BufferedInputStreamBufferedOutputStreamBufferedReaderBufferedWriter
转换流  InputStreamReaderOutputSreamWriter
对象流ObjectInputStreamObjectOutputStream  
 FilterInputStreamFilterOutputStreamFilterReaderFilterWriter
打印流 PrintStream  
退回输入流PushbackInputStream PushbackReaderPrintWriter
特殊流DataInputStreamDataOutputStream  
  • InputStream&Reader
  1. InputStream 和 Reader 是所有输入流的基类。
  2. InputStream(典型实现:FileInputStream)【int read();int read(byte[] b) ;int read(byte[] b, int off , int len);】
  3. Reader(典型实现:FileReader)【int read();int read(char [] c);int read(char [] c, int off, int len)】
  4. 程序中打开的文件 IO 资源不属于内存里的资源,垃圾回收机制无法回收该资源,所以应该显式关闭文件 IO 资源。
  • OutputStream & Writer
  1. OutputStream 和 Writer 也非常相似:【void write(int b/int c);void write(byte[] b/char[] cbuf);void write(byte[] b/char[] buff, int off, int len);void flush();void close(); 需要先刷新,再关闭此流】
  2. 因为字符流直接以字符作为操作单位,所以 Writer 可以用字符串来替换字符数组,即以 String 对象作为参数【void write(String str);void write(String str, int off, int len);】
// 使用try-catch处理异常更合理:保证流的关闭一定可以执行
// 创建FileInputStream类的对象
FileInputStream fis = null;
try {
	// 创建File类的对象
	File file = new File("iofiletest.txt");
	fis = new FileInputStream(file);
	// 3、调用FileInputStream的方法,实现file文件的读取
/*
* read():读取一个文件的字节。当执行到文件结尾,返回-1 多次读取文件
*/
	int b = fis.read();
	while (b != -1) {// 正常读取文件
		// while((b=fis.read())!=-1) {//出现文件内容读取缺失的问题
		System.out.print((char) b);// abcdefghijklmnopqrstuvwxy z
		b = fis.read();
	}

} catch (IOException e) {
		e.printStackTrace();
    } finally {
		// 4、关闭相应的流
		try {
			fis.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
/*
* read(byte[] b1):将文件的字节码一次(依次)读入到byte[]中
*/
byte[] b = new byte[1];// 读取的数据存放的字节数组
int len;// 每次读入到byte中字节的长度
while ((len=fis.read(b)) != -1) {
//	for (int i=0;i<len;++i) {
//		System.out.print((char) b[i]);// abcdefghijklmnopqrstuvwxy z
//	}
	String str = new String(b);				                    
    System.out.print(str);//abcdefghijklmnopqrstuvwxy    z
}
//FileInputStream & File OutputStream 实现文件的复制

//1、提供读入、写出的文件
File filein = new File("6.png");
File fileout = new File("1.png");
		
//2、提供相应的类
FileInputStream  fis = new FileInputStream(filein); 
FileOutputStream fos = new FileOutputStream(fileout);

//3、实现文件的复制
byte[] b= new byte[20];
int length;
while((length=fis.read(b))!=-1) {
	fos.write(b,0,length);
}
fos.close();
fis.close();
//FileReader  &  FileWriter  实现文件的复制

		File file = new File("iofile2.txt");
		File file1 = new File("iofile2W.txt");
		FileReader fr = new FileReader(file);
		FileWriter fw = new FileWriter(file1);
		char[] c= new char[50];
		int length;
		while((length=fr.read(c))!=-1) {
			String str = new String(c,0,length);
			System.out.print(str);
			str+="\nFileWriter end OVER";
			fw.write(str);
		}
		if(fr!=null) 
			fr.close();
		if(fw!=null) 
			fw.close();

【注意】 对于非文本文件只能使用 字节流 操作; 

  • 缓冲流
  1. 为了提高数据读写的速度,Java API提供了带缓冲功能的流类,在使用这些流类时,会创建一个内部缓冲区数组
  2. 根据数据操作单位可以把缓冲流分为:【BufferedInputStream 和 BufferedOutputStream、BufferedReader 和 BufferedWriter】
  3. 缓冲流要“套接”在相应的节点流之上,对读写的数据提供了缓冲的功能,提高了读写的效率,同时增加了一些新的方法
  4. 对于输出的缓冲流,写出的数据会先在内存中缓存,使用flush()将会使内存中的数据立刻写出
/**
**缓冲流:FileBufferedInputStream、FileBufferedOutputStream
*加速字节流的操作
*/

// 处理字节流文件
// 实现非文本文件的复制

// 1、提供读入、写出的文件
File file = new File("1.png");
File file1 = new File("2.png");

// 2、创建相应的节点流:FileInputStream\FileutputStream
FileInputStream	fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(file1);

// 3、将创建的节点流的对象作为形参传递给缓冲流的构造器中
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);

// 4、具体的实现文件按复制的操作
byte[] b = new byte[100];
int length;
while ((length = bis.read(b)) != -1) {
	bos.write(b, 0, length);
	bos.flush();
}
// 5、关闭相应的流
bos.close();
bis.close();


/**
**缓冲流:BufferReader、BufferWriter
*/

FileReader fr = new FileReader(file);
FileWriter fw = new FileWriter(file1);

BufferedReader br = new BufferedReader(fr);
BufferedWriter bw = new BufferedWriter(fw);

//			char[] c = new char[1024];
//			int length;
//			while ((length = br.read(c)) != -1) {
//				String str = new String(c, 0, length);
//				System.out.print(str);
//			}
			
String str;
while((str=br.readLine())!=null) {
		bw.write(str);
		System.out.println(str);
		bw.newLine();
		bw.flush();
}
	br.close();
	bw.close();
  • 转换流
  1. 转换流提供了在字节流和字符流之间的转换
  2. Java API提供了两个转换流:【InputStreamReader和OutputStreamWriter】
  3. 字节流中的数据都是字符时,转成字符流操作更高效。
/**
*InputStreamReader:
*    用于将字节流中读取到的字节按指定字符集解码成字符。需要和InputStream“套接”。
*    构造方法
*        1、public InputStreamReader(InputStream in)
*        2、public InputSreamReader(InputStream in,String charsetName)
*/




/**
*OutputStreamWriter:
*    用于将要写入到字节流中的字符按指定字符集编码成字节。需要和OutputStream“套接”。
*    构造方法:
*        1、public OutputStreamWriter(OutputStream out)
*        2、public OutputSreamWriter(OutputStream out,String                                                                                  charsetName)
*/


/**
	 * 转换流:InputStreamReader、OutputStreamWriter 编码:字符串-->字节数组 解码:字节数组-->字符串
	 */
// 解码
File file = new File("iofile2.txt");

FileInputStream fis = new FileInputStream(file);
InputStreamReader isr = new InputStreamReader(fis, "GBK");
BufferedReader br = new BufferedReader(isr);

// 编码
File file1 = new File("iofile21.txt");
FileOutputStream fos = new FileOutputStream(file1);
OutputStreamWriter osw = new OutputStreamWriter(fos, "GBK");
BufferedWriter bw = new BufferedWriter(osw);
String str;
while ((str = br.readLine()) != null) {
	bw.write(str);
	bw.newLine();
	bw.flush();
}		
bw.close();
br.close();
  • 输入输出流
  1. System.in和System.out分别代表了系统标准的输入和输出设备
  2. 默认输入设备是键盘,输出设备是显示器
  3. System.in的类型是InputStream
  4. System.out的类型是PrintStream,其是OutputStream的子类FilterOutputStream 的子类
  5. 通过System类的setIn,setOut方法对默认设备进行改变。

            ①public static void setIn(InputStream in)

            ②public static void setOut(PrintStream out)

InputStream is = System.in;
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
System.out.println("请输入字符串: ");
String str;
while(true) {
    str = br.readLine();//hello world!
	if(str.equalsIgnoreCase("e")||str.equalsIgnoreCase("exit")) {
		break;
	}
String str1=str.toUpperCase();
System.out.println(str1);//HELLO WORLD!

br.close();
  • 打印流
  1. 在整个IO包中,打印流是输出信息最方便的类。
  2. 【PrintStream(字节打印流)和PrintWriter(字符打印流)】PrintStream(字节打印流)和PrintWriter(字符打印流),PrintStream和PrintWriter的输出不会抛出异常,PrintStream和PrintWriter有自动flush功能,System.out返回的是PrintStream的实例
public void testPrintStream() {
		//字节型输出流
		FileOutputStream fos = null;
		try {
			fos = new FileOutputStream(new File("iofile2.txt"));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} 
		// 创建打印输出流,设置为自动刷新模式(写入换行符或字节 '\n' 时都会刷新输出缓冲区)
		PrintStream ps = new PrintStream(fos, true);
//		if (ps != null) { 
//			// 把标准输出流(控制台输出)改成文件iofile2.txt
//			System.setOut(ps);
//		}
		for (int i = 0; i <= 255; i++) { 
			// 输出ASCII字符
			System.out.print((char) i);
			if (i % 50 == 0) { // 每50个数据一行
				System.out.println(); // 换行
			}
		}
		ps.close();
	}
  • 数据流
  1. 为了方便地操作Java语言的基本数据类型的数据,可以使用数据流。
  2. 数据流有两个类:(用于读取和写出基本数据类型的数据)【DataInputStream 和 DataOutputStream】分别“套接”在 InputStream 和 OutputStream 节点流上
  3. DataInputStream中的方法
  4. DataOutputStream中的方法
boolean readBoolean()		
byte readByte()
char readChar()
float readFloat()
double readDouble()
short readShort()
long readLong()			
int readInt()
String readUTF()                                
void readFully(byte[] b)


/**
* 数据流:
*/
	@Test
	public void testDataInputStream() {
		DataInputStream dis = null;
		try { 
			// 创建连接到指定文件的数据输出流对象
			dis = new DataInputStream(new FileInputStream("destData.txt"));
			//byte[] b=new byte[10];
			//int length;
			//while((length = dis.read(b))!=-1) {
			//	System.out.println(new String(b,0,length));
			//}
            String str = dis.readUTF();
            System.out.println(str);
		} catch (IOException e) {
			e.printStackTrace();
		} finally { // 关闭流对象
			try {
				if (dis != null) {
					// 关闭过滤流时,会自动关闭它包装的底层节点流
					dis.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	@Test
	public void testDataOutputStream() {
		DataOutputStream dos = null;
		try { 
			// 创建连接到指定文件的数据输出流对象
			dos = new DataOutputStream(new FileOutputStream("destData.txt"));
			dos.writeUTF("ab中国"); // 写UTF字符串
			dos.writeBoolean(false); // 写入布尔值
			dos.writeLong(1234567890L); // 写入长整数
			System.out.println("写文件成功!");
		} catch (IOException e) {
			e.printStackTrace();
		} finally { // 关闭流对象
			try {
				if (dos != null) {
					// 关闭过滤流时,会自动关闭它包装的底层节点流
					dos.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

	}
  • 对象流
  1. 【ObjectInputStream和OjbectOutputSteam 】用于存储和读取对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。
  2. 序列化(Serialize):用ObjectOutputStream类将一个Java对象写入IO流中
  3. 反序列化(Deserialize):用ObjectInputStream类从IO流中恢复该Java对象
  4. ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量
  5. 对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其它程序获取了这种二进制流,就可以恢复成原来的Java对象
  6. 序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据,使其在保存和传输时可被还原
  7. 序列化是 RMI(Remote Method Invoke – 远程方法调用)过程的参数和返回值都必须实现的机制,而 RMI 是 JavaEE 的基础。因此序列化机制是 JavaEE 平台的基础
  8. 如果需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一: Serializable、 Externalizable
  9. 凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量:【private static final long serialVersionUID; serialVersionUID用来表明类的不同版本间的兼容性;如果类没有显示定义这个静态变量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的源代码作了修改,serialVersionUID 可能发生变化。故建议,显式声明】
  10. 显示定义serialVersionUID的用途【希望类的不同版本对序列化兼容,因此需确保类的不同版本具有相同的serialVersionUID;不希望类的不同版本对序列化兼容,因此需确保类的不同版本具有不同的serialVersionUID】
  11. 若某个类实现了 Serializable 接口,该类的对象就是可序列化的:【创建一个 ObjectOutputStream;调用 ObjectOutputStream 对象的 writeObject(对象) 方法输出可序列化对象。注意写出一次,操作flush()】
  12. 反序列化【创建一个 ObjectInputStream ;调用 readObject() 方法读取流中的对象】
  13. 强调:如果某个类的字段不是基本数据类型或 String  类型,而是另一个引用类型,那么这个引用类型必须是可序列化的,否则拥有该类型的 Field 的类也不能序列化
// 对象的序列化过程
Person p1 = new Person("马冬梅", 18);
Person p2 = new Person("墨菲", 13);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.txt"));
oos.writeObject(p1);
oos.flush();
oos.writeObject(p2);
oos.flush();
oos.close();
//

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.txt"));
Person p1 = (Person) ois.readObject();
System.out.println(p1);
Person p2 = (Person) ois.readObject();
System.out.println(p2);
ois.close();
//Person [name=马冬梅, id=18]
//Person [name=墨菲, id=13]

class Person implements Serializable{
    private static final long serialVersionUID = 2718931278391L;
……}
  • RandomAccessFile 类
  1. ​​​​​​​RandomAccessFile 类支持 “随机访问” 的方式,程序可以直接跳到文件的任意地方来读、写文件;【支持只访问文件的部分内容 ;可以向已存在的文件后追加内容】
  2. RandomAccessFile 对象包含一个记录指针,用以标示当前读写处的位置。RandomAccessFile 类对象可以自由移动记录指针:【long getFilePointer():获取文件记录指针的当前位置;void seek(long pos):将文件记录指针定位到 pos 位置】
  3. 构造器 【public RandomAccessFile(File file, String mode) ;public RandomAccessFile(String name, String mode)】
  4. 创建 RandomAccessFile 类实例需要指定一个 mode 参数,该参数指定 RandomAccessFile 的访问模式:【r: 以只读方式打开; rw:打开以便读取和写入 ;rwd:打开以便读取和写入;同步文件内容的更新 ;rws:打开以便读取和写入;同步文件内容和元数据的更新
        RandomAccessFile raf1 = null;
		RandomAccessFile raf2 = null;
			raf1 = new RandomAccessFile(new File("iofile21.txt"),"r");
			raf2 = new RandomAccessFile(new File("file1.txt"),"rw");
			//实现复制
			byte[] b = new byte[50];
			int length;
			while((length = raf1.read(b))!=-1) {
				raf2.write(b,0,length);
			}
			//实现覆盖(20位置,“12”覆盖两个字符)
			raf2.seek(20);
			raf2.write("12".getBytes());
			//实现单行插入
			raf2.seek(20);
			String str = raf2.readLine();
			long lpos = raf2.getFilePointer();
			System.out.println(lpos);
			raf2.write("123".getBytes());
			raf2.write(str.getBytes());
			
			//实现多行插入
			raf2.seek(20);
			byte[] b = new byte[20];
			int length;
			StringBuffer strb = new StringBuffer();
			while((length=raf2.read(b))!=-1) {
				strb.append(new String(b,0,length));
			}
			raf2.seek(20);
			raf2.write("123456789".getBytes());
			raf2.write(strb.toString().getBytes());

			raf1.close();
			raf2.close();

补充

  • 字符编码
  1. 编码表的由来 计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识别各个国家的文字。就将各个国家的文字用数字来表示,并一一对应,形成一张表。这就是编码表。
  2. 常见的编码表
  3. ASCII:美国标准信息交换码。 用一个字节的7位可以表示。
  4. ISO8859-1:拉丁码表。欧洲码表 用一个字节的8位表示。
  5. GB2312:中国的中文编码表。
  6. GBK:中国的中文编码表升级,融合了更多的中文文字符号。
  7. Unicode:国际标准码,融合了多种文字。 所有文字都用两个字节来表示,Java语言使用的就是unicode
  8. UTF-8:最多用三个字节来表示一个字符。
  9. 编码:字符串字节数组
  10. 解码:字节数组字符串
  11. 转换流的编码应用 【可以将字符按指定编码格式存储;可以对文本数据按指定编码格式来解读;指定编码表的动作由构造器完成。】

 

 

【注】

本文章属个人整理学习使用,如有不当之处望联系指正或删除。

【学习视频来源】尚硅谷http://www.atguigu.com/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值