java.io.file.sync_Java(25)IO流和File类

IO流+File类

File类

讲IO流之前先来讲以下File类。Java的标准库Java.io提供了File类来操作文件和目录。操作可以有:新建、删除、重命名等,但是不能访问文件本身的内容,如果想要访问,需要使用IO流。

新建File对象:

package day01;

import java.io.File;

class Battery{

public static void main(String[] args) {

File a=new File("C:\\Users\\97464\\Desktop\\test\\File1.txt");

File b=new File("C:\\Users\\97464\\Desktop","test\\File1.txt");

/*

1.上面两种方式创建的FIle对象是一个意思,File类的不同构造方法导致可以有不同的参数。

2.构造File对象的时候,既可以传入绝对路径,也可以传入相对路径。

3.注意Windows平台使用\作为路径分隔符,在Java字符串中需要用\\表示一个\。Linux平台使用/作为路径分隔符。传入相对路径时,相对路径前面加上当前目录就是绝对路径。

4. .代表当前目录..代表上级目录

*/

}

}

获取文件或者目录的路径和名字等操作

package day01;

import java.io.File;

import java.io.IOException;

class Battery{

public static void main(String[] args){

File tt=new File("F:\\test\\test\\..\\hello.txt");

//获取文件或者目录的路径:

System.out.println(tt.getPath()); //返回构造方法传入的路径

System.out.println(tt.getAbsolutePath()); //返回绝对路径

try {

System.out.println(tt.getCanonicalPath());

//返回规范路径(C:\\Users\\..----->C:\\)

}catch (IOException e){

e.printStackTrace();

}

//获取文件或者目录的名字:

System.out.println(tt.getName());

//返回一个用当前文件的绝对路径构建的File对象;

File gg=tt.getAbsoluteFile();

System.out.println(gg.getName());

//返回文件或目录的父级目录:

System.out.println(tt.getParent());

//重命名文件或目录:

tt.renameTo(new File("F:\\test\\test\\..\\hello2.txt"));

}

}

/*运行结果为:

F:\test\test\..\hello.txt

F:\test\test\..\hello.txt

F:\test\hello.txt

hello.txt

hello.txt

F:\test\test\..

*/

/*----------------补充:--------------------------------

上面的getCanonicalPath()方法,如果去查看它的源码可以发现它抛出了IOException异常,如果想要使用它,需要在调用的时候try...catch捕获异常,或者由main()继续抛出异常,交给JVM处理。

*/

文件检测

package day01;

import java.io.File;

class Battery{

public static void main(String[] args){

File a=new File("F:\\test\\hello2.txt");

//判断文件或者目录是否存在:

System.out.println(a.exists());

//判断文件是否可读或可写:

System.out.println(a.canRead());

System.out.println(a.canWrite());

//判断当前File对象是不是文件或者目录:

System.out.println(a.isFile());

System.out.println(a.isDirectory());

}

}

/*运行结果为:

true

true

true

true

false

*/

获取文件大小和文件的最后修改时间

package day01;

import java.io.File;

class Battery{

public static void main(String[] args){

File a=new File("F:\\test\\hello2.txt");

System.out.println(a.length()); //返回文件的大小,以字节为单位

System.out.println(a.lastModified()); //返回最后修改时间,是一个毫秒数

}

}

/*运行结果为:

11

1580719765617

*/

新建和删除文件或目录

package day01;

import java.io.File;

import java.io.IOException;

class Battery{

public static void main(String[] args){

File a=new File("F:\\test\\hello.txt");

if(!a.exists()){ //判断文件是否不存在

try{

a.createNewFile();

}catch (IOException e){

e.printStackTrace();

}

}

a.delete(); //删除文件

File b=new File("F:\\test2");

b.mkdir(); //创建单层目录

File c=new File("F:\\test3\\kobe\\number24");

c.mkdirs();//创建多层目录

}

}

遍历目录下的文件和子目录

package day01;

import java.io.File;

class Battery{

public static void main(String[] args){

File d=new File("F:\\test3");

System.out.println(d.list().getClass());

//可以看到d.list()返回的是数据类型是class [Ljava.lang.String;

//[:表示返回的类型是数组,L:表示数组元素是一个对象实例

// Java.land.String表示对象是String类型

//下面进行循环输出目录下的文件或者子目录:

String []strArr=d.list();

for (String i:strArr){

System.out.println(i);

}

File e=new File("F:\\test3");

File [] arrFile=e.listFiles();

//e.listFiles()返回test3目录下的文件或子目录的file对象

for (File i: arrFile){

System.out.println(i);

}

}

}

/*运行结果为:

class [Ljava.lang.String;

kobe

F:\test3\kobe

*/

上面遍历文件和子目录是单层遍历,如果想要进行多层遍历(子目录的子目录也会被遍历),可以进行递归遍历。

案例:

package day01;

import java.io.File;

class Rabbit{

public void fds(File a){

if(a.isFile()){

System.out.println(a.getName());

}else {

System.out.println(a.getName());

File []fileArr=a.listFiles();

for (File i:fileArr){

fds(i); //递归

}

}

}

}

class RunClass{

public static void main(String[] args) {

Rabbit test=new Rabbit();

File tf=new File("F:\\test3");

test.fds(tf);

}

}

IO流

IO概念:

IO是指Input/Output,即输入和输出,以内存为中心:

Input指从外部读入数据到内存。----例如把文件从磁盘读取到内存,从网络读取数据到内存等

Output指把数据从内存输出到外部。----例如把数据从内存写入文件,把数据从内存输出到网络等

为什么要以内存为中心?

------因为数据被读取到内存中才能被处理,代码是在内存中运行的,数据也必须内存。

IO流概念:

IO流是一种顺序读写数据的模式,它的特点是单向流动。

------例如: 想要把一张图片放入一个文件夹,不能整张直接塞进去,而是需要把图片转化为一个数据集(例如二进制),把这些数据一点一点传到文件夹,这个数据的传递类似于自来水在水管中的流动,称为IO流。

同步和异步

同步IO是指,读写IO时代码必须等待数据返回后才继续执行后续代码,它的优点是代码编写简单,缺点是CPU执行效率低。

而异步IO是指,读写IO时仅发出请求,然后立刻执行后续代码,它的优点是CPU执行效率高,缺点是代码编写复杂。

Java标准库的包java.io提供了所有的同步IO,而java.nio则是异步IO。

下面我们即将讨论的InputStream、OutputStream、Reader和Writer都是同步IO的抽象类的具体实现类

流的分类

按操作数据单位不同分为:字节流(8 bit),字符流(16 bit)

按数据流的流向不同分为:输入流,输出流

按流的角色不同分为:节点流,处理流

抽象基类

字节流

字符流

输入流

InputStream

Reader

输出流

OutputStream

Writer

Java的IO流共有40多个相关类,实际上都是从上面的四种抽象基类派生的。

9d32ec13007ea017b2b455bbf8a5a6f4.png

字节流

IO流以byte(字节)为最小单位,称为字节流。字节流非常通用,不仅可以用来操作字符型文档,还可以操作任何地其它类型文件(图片、压缩包等),因为字节流本身使用地就是二进制。

╔════════════╗

║ Memory ║ 内存

╚════════════╝

│0x48

│0x65

│0x6c

│0x6c

│0x6f

│0x21

╔═══════════╗

║ Hard Disk ║ 硬盘

╚═══════════╝

//上面,内存从硬盘(磁盘)读入了6个字节的数据,是按顺序读入的,是输入字节流。

//反过来就是输出字节流:

╔════════════╗

║ Memory ║ 内存

╚════════════╝

│0x21

│0x6f

│0x6c

│0x6c

│0x65

│0x48

╔═══════════╗

║ Hard Disk ║ 硬盘

╚═══════════╝

InputStream

InputStream是Java标准库提供的最基本的输入字节流。位于Java.io这个包里。InputStream是一个抽象类,是所有输入流的父类,这个抽象类定义的最重要的方法是int read()。源代码如下:

public abstract int read() throws IOException;

构建InputStream对象:

import java.io.IOException;

import java.io.InputStream;

class MainClass{

public static void main(String[] args) {

InputStream is=new InputStream() {

@Override //可以看到新建InputStream输入流对象必须重写抽象方法int read()

public int read() throws IOException {

return 0;

}

};

}

}

read()这个方法会读取输入流的下一个字节,并返回字节表示的int值(0-255)(ascii码),读到末尾就会返回-1

read()方法还可以传递参数——read(byte [] b),作用是一次读取一个字节数组,把输入流读取到的内容存放到这个字节数组中,并返回存放到数组的内容的字节数,同样是到末尾就返回-1。byte数组是作为一个缓冲区,每次存入数组大小为byte.length的数据,存入的数据是一个int

FileInputStream

FileInputStream是InputStream的一个子类,用来从文件中读取数据。FileInputStream类的构造方法有:

FileInputStream(File file): 传递一个文件的File类对象

FileInputStream(String name): 传递一个String类型的文件路径

案例1: int read()的使用

F:\\test\\hello.txt的文件内容:

abclove

package day01;

import java.io.FileInputStream;

import java.io.IOException;

class MainClass{

public static void main(String[] args) throws IOException { //所有与IO操作相关的代码都必须正确处理IOException

FileInputStream fis=new FileInputStream("F:\\test\\hello.txt"); //创建流对象

for(;;){ //无限循环

int n = fis.read(); //反复调用read()方法,直到n=-1

if (n==-1){

break;

}

System.out.print(n+":"); //打印read()返回的byte值

System.out.println((char)n);

}

fis.close(); //关闭流,释放对应的底层资源

}

}

/*运行结果为

97:a

98:b

99:c

108:l

111:o

118:v

101:e

*/

//从运行结果可知,read()返回的单字节整数值,是对应字符的acsii码。通过(char)就可以转换为对应字符。

案例2:int read(byte [] b)的使用(缓冲)

package day01;

import java.io.FileInputStream;

import java.io.IOException;

import java.io.InputStream;

class MainClass{

public static void main(String[] args) throws IOException {

InputStream cup=new FileInputStream("F:\\test\\hello.txt");

byte [] b=new byte[3];

//b数组是作为一个缓冲区,在下面的循环中,每循环一次,b就会更新一次,输入流把读取到的内容放到b中

int len=0; //初始化len为0

while((len=cup.read(b))!=-1){ //len代表存放到数组b的数据的字节数

System.out.print(len+":");

System.out.println(new String(b,0,len)); //用法看下面的补充

}

cup.close();

}

}

/*运行结果为:

3:abc

3:lov

1:e

*/

/*-------------------------补充:----------------------------------------

String类的构造方法public String(byte bytes[], int offset, int length)的用法:

作用:将字节数组的某部分转为字符串

参数:byte bytes[]表示要被转的数组

int offset表示偏移量,即从数组的第几个位置开始转为字符串,

int length表示总共要转化的字节数

read()方法是阻塞的(blocking)。读取IO流相比执行普通代码,速度要慢很多。

package day01;

import java.io.FileInputStream;

import java.io.IOException;

import java.io.InputStream;

class MainClass{

public static void main(String[] args) throws IOException {

InputStream a=new FileInputStream("F:\\test\\hello.txt");

System.out.println("kobe");

int b=a.read(); // 必须等待read()方法返回才能执行下一行代码

System.out.println("gigi");

}

}

OutputStream

OutputStream是Java标准库提供的最基本的输出流。OutputStream和InputStream一样,也是抽象类,是所有输出流的父类。抽象类定义的一个重要的方法是void write(int b),源代码如下:

public abstract void write(int b) throws IOException;

write()还有重载方法:

write(byte b[]):用于一次性写入多个字节

write(byte b[], int off, int len):将数组b从off位置开始,把长度为len字节的数据写入到文件中

和InputStream一样,OutputStream的write()方法也是阻塞的。

FileOutputStream

FileOutputStream是OutputStream的子类。FileOutputStream类的构造方法有:

FileOutputStream(File file): 传递一个文件的File类对象

FileOutputStream(String name): 传递一个String类型的文件路径

案例1:一次写入一个字节

package day01;

import java.io.*;

class MainClass{

public static void main(String[] args) throws IOException {

OutputStream ops=new FileOutputStream("F:\\test\\outPut.txt");

ops.write(97); //往文件写入97ascii码代表的字符a

ops.write(98); //往文件写入98ascii码代表的字符b

ops.write(99); //往文件写入99ascii码代表的字符c

ops.flush();

ops.close();

}

}

//运行结果是:往F:\\test\\outPut.txt这个文件写入“abc",如果这个文件不存在,则会新建文件。

从案例1可以看到,OutputStream还提供了一个flush()方法,它的目的是将缓冲区的内容真正输出到目的地。

为什么要有flush()?因为向磁盘、网络写入数据的时候,出于效率的考虑,操作系统并不是输出一个字节就立刻写入到文件或者发送到网络,而是把输出的字节先放到内存的一个缓冲区里(本质上就是一个byte[]数组),等到缓冲区写满了,再一次性写入文件或者网络。对于很多IO设备来说,一次写一个字节和一次写1000个字节,花费的时间几乎是完全一样的,所以OutputStream有个flush()方法,能强制把缓冲区内容输出。

通常情况下,我们不需要调用这个flush()方法,因为缓冲区写满了OutputStream会自动调用它,并且,在调用close()方法关闭OutputStream之前,也会自动调用flush()方法。

但是,在某些情况下,我们必须手动调用flush()方法。举个例子:

小明正在开发一款在线聊天软件,当用户输入一句话后,就通过OutputStream的write()方法写入网络流。小明测试的时候发现,发送方输入后,接收方根本收不到任何信息,怎么肥四?

原因就在于写入网络流是先写入内存缓冲区,等缓冲区满了才会一次性发送到网络。如果缓冲区大小是4K,则发送方要敲几千个字符后,操作系统才会把缓冲区的内容发送出去,这个时候,接收方会一次性收到大量消息。

解决办法就是每输入一句话后,立刻调用flush(),不管当前缓冲区是否已满,强迫操作系统把缓冲区的内容立刻发送出去。

实际上,InputStream也有缓冲区。例如,从FileInputStream读取一个字节时,操作系统往往会一次性读取若干字节到缓冲区,并维护一个指针指向未读的缓冲区。然后,每次我们调用int read()读取下一个字节时,可以直接返回缓冲区的下一个字节,避免每次读一个字节都导致IO操作。当缓冲区全部读完后继续调用read(),则会触发操作系统的下一次读取并再次填满缓冲区。

案例2:一次性写入若干字节

package day01;

import java.io.*;

/**

* 一次性写入若干字节,通过void write(byte [])来实现

*/

class MainClass{

public static void main(String[] args) throws IOException {

OutputStream ops=new FileOutputStream("F:\\test\\outPut.txt");

ops.write("hello Krystal".getBytes("utf-8") );

ops.flush();

ops.close();

}

}

/*----------------------补充:------------------------------------------

String.getBytes(String decode)方法会根据指定的decode编码返回某字符串在该编码下的byte数组

*/

案例3:复制文件

package day01;

import java.io.*;

class MainClass{

public static void main(String[] args) throws IOException {

Stero st=new Stero();

st.fun1("F:\\test\\outPut.txt","F:\\test\\outPut2.txt");

//把outPut.txt复制为outPut2.txt

}

}

class Stero{

void fun1(String intputPath,String outputPath) throws IOException {

InputStream ips=new FileInputStream(intputPath);

OutputStream ops=new FileOutputStream(outputPath);

byte[]a=new byte[3];

int len;

while((len=ips.read(a)) != -1){

ops.write(a,0,len);

}

ops.close(); //先关闭输出流

ips.close(); //后关闭输入流

//最早开的最晚关

}

}

正确关闭流

上面的流的案例,存在一个潜在的问题,如果读取或者写入过程中,发生了IO错误,流就没法及时关闭,资源也无法释放。 Java7引入了新的try(resource)语法,只需要编写try语句,就可让编译器自动为我们关闭资源。

案例:自动正确地关闭流

class Cable{

public static void main(String[] args) throws IOException{

//把新建流对象地语句放在try()里面即可

try(FileInputStream sf=new FileInputStream("F:\\test\\hello.txt")){

int n;

while((n=sf.read())!=-1){

System.out.println((char)n);

}

}

}

}

字符流

如果我们需要读写的是字符(不是图片什么的),并且字符不全是单字节表示的ASCII字符,那么按照char来读写更方便,这种流称为字符流。字符流传输的最小数据单位是char,Java提供了Reader和Writer两个基类来操作字符流。

Reader和Writer本质上是一个能自动编解码的InputStream和OutputStream。

使用Reader,数据源虽然是字节,但我们读入的数据都是char类型的字符,原因是Reader内部把读入的byte做了解码,转换成了char。使用InputStream,我们读入的数据和原始二进制数据一模一样,是byte[]数组,但是我们可以自己把二进制byte[]数组按照某种编码转换为字符串。究竟使用Reader还是InputStream,要取决于具体的使用场景。如果数据源不是文本,就只能使用InputStream,如果数据源是文本,使用Reader更方便一些。Writer和OutputStream是类似的。

Reader

Reader是Java的IO库提供的另一个输入流接口。和InputStream的区别是,InputStream是一个字节流,即以byte为单位读取,而Reader是一个字符流,即以char为单位读取,Reader是所有字符输入流的父类。Reader类的主要方法是int read(),源代码是:

public int read() throws IOException;

FileReader

FileReader是Reader的子类,FileReader的用法和FileInputStream的用法极其相似。

案例1:

//hello.txt的内容如下(注意编码格式要为utf-8,要不然会出错,另存为就可以改编码格式)

武汉,加油。

China,add oil.

package day01;

import java.io.FileReader;

import java.io.IOException;

import java.io.Reader;

class RunClas{

public static void main(String[] args) {

Water w=new Water();

try {

w.fun1("F:\\test\\hello.txt");

} catch (IOException e) {

e.printStackTrace();

}

}

}

class Water{

public void fun1(String rPath) throws IOException {

try(Reader fr=new FileReader(rPath)){

char[] a=new char[4]; //创建用于缓冲的字符数组

int len;

while((len=fr.read(a))!=-1){

System.out.println(new String(a,0,len));

}

}

}

}

Writer

Reader是带编码转换器的InputStream,它把byte转换为char,而Writer就是带编码转换器的OutputStream,它把char转换为byte并输出。

Writer是所有字符输出流的超类,它提供的方法主要有:

写入一个字符(0~65535):void write(int c);

写入字符数组的所有字符:void write(char[] c);

写入String表示的所有字符:void write(String s)。

FileWriter

FileWriter是Writer的一个子类。FileWriter的用法和FileOutputStream的用法很相似。

案例1:使用void write(String s)方法

package day01;

import java.io.*;

class RunClas {

public static void main(String[] args) {

Desk d=new Desk();

try {

d.funF("F:\\test\\FileWriterOutput.txt","Hello,krystal,i'm missing you.");

} catch (IOException e) {

e.printStackTrace();

}

}

}

class Desk {

public void funF(String oPath, String content) throws IOException {

try (Writer a = new FileWriter(oPath)) {

a.write(content);

a.flush();

a.close();

}

}

}

案例2:用字符流进行复制文件

package day01;

import java.io.*;

class RunClas {

public static void main(String[] args) throws IOException {

Desk d = new Desk();

d.funF("F:\\test\\hello.txt", "F:\\test\\hello3.txt");

}

}

class Desk {

public void funF(String iPath, String oPath) throws IOException {

Reader ra = new FileReader(iPath);

Writer wa = new FileWriter(oPath);

char[] charArr = new char[100];

int len;

while ((len = ra.read(charArr)) != -1) {

wa.write(charArr);

}

wa.flush();

wa.close();

}

}

流对文件的操作注意事项:

在写入一个文件时,目录下的同名文件会被覆盖

在读取一个文件时,必须保证改文件是存在的,否则报异常

缓冲流

为了提高 数据读写的速度,Java提供了带缓冲功能的流类,在使用这些流类时,会创建一个内部缓冲区数组。(基于内存的)。

BufferedInputStream-->FileInputStream

BufferedOutputStream-->FileOutputStream

BufferedReader-->FileReader

BufferedWriter-->FileWriter

缓冲流先把数据缓冲到内存里,然后在内存中做io操作,基于内存的io操作比基于硬盘的操作快7500倍。

案例1:BufferedInputStream的使用

package day01;

import java.io.*;

class ExaF{

public static void main(String[] args) throws IOException {

//创建File流对象:

FileInputStream a=new FileInputStream("F:\\test\\hello.txt");

//创建缓冲流对象:

BufferedInputStream b=new BufferedInputStream(a); //需要传入FIle流作为参数

byte[] bArr=new byte[100];

int len;

while((len=b.read(bArr))!=-1){

System.out.println(new String(bArr,0,len));

}

}

}

案例2:BufferedOutputStream的使用

package day01;

import java.io.*;

class ExaF{

public static void main(String[] args) throws IOException {

FileOutputStream a=new FileOutputStream("F:\\test\\BOutput.txt");

BufferedOutputStream b=new BufferedOutputStream(a);

b.write("I love you,krystal".getBytes());

b.flush();

b.close();

}

}

案例3:使用字节流+缓冲流实现文件复制

package day01;

import java.io.*;

class ExaF{

public static void main(String[] args) throws IOException {

//创建File流对象:

FileInputStream ia=new FileInputStream("F:\\test\\hello.txt");

FileOutputStream oa=new FileOutputStream("F:\\test\\Chello.txt");

//创建缓冲流对象:

BufferedInputStream ib=new BufferedInputStream(ia);

BufferedOutputStream ob=new BufferedOutputStream(oa);

byte[] bArr=new byte[100];

int len;

while((len=ib.read(bArr))!=-1){

ob.write(bArr);

}

ob.flush();

ob.close();

ib.close();

oa.close();

ia.close();

}

}

案例4:BufferedReader的使用

package day01;

import java.io.*;

class ExaB{

public static void main(String[] args) throws IOException {

FileReader a=new FileReader("F:\\test\\hello.txt");

BufferedReader b=new BufferedReader(a);

char []cArr=new char[100];

int len;

while((len=b.read(cArr))!=-1){

System.out.println(new String(cArr,0,len));

}

b.close();

a.close();

}

}

案例5:BufferedWriter的使用

package day01;

import java.io.*;

class ExaB{

public static void main(String[] args) throws IOException {

FileWriter a=new FileWriter("F:\\test\\brout.txt");

BufferedWriter b=new BufferedWriter(a);

b.write("Hello,krystal!");

b.flush();

b.close();

a.close();

}

}

案例6:使用字符流+缓冲流实现文件的复制

package day01;

import java.io.*;

class ExaB{

public static void main(String[] args) throws IOException {

BufferedReader br=new BufferedReader(new FileReader("F:\\test\\hello.txt"));

BufferedWriter bw=new BufferedWriter(new FileWriter("F:\\test\\em.txt"));

char [] cArr=new char[100];

int len;

while((len=br.read(cArr))!=-1){

bw.write(cArr);

}

bw.flush();

bw.close();

br.close();

}

}

转换流

转换流是指InputStreamReader和OutputStreamWriter,上面讲了,流的数据都是字符时,转成字符流更高效,那么转换流就是用来将字节流转换成字符流的,并且可以指定字符集的编码解码格式。

案例1:转换字节输入流为字符输入流

//"F:\\test\\aa.txt"文件的编码为GBK,文件内容如下:

中国我爱你。

I love you,China.

package day01;

import java.io.*;

class Bear{

public static void main(String[] args) throws IOException {

//创建文件字节输入流对象

FileInputStream fis=new FileInputStream("F:\\test\\aa.txt");

//创建转换流对象

InputStreamReader isr=new InputStreamReader(fis,"utf-8");//指定字符集编码

char []cArr=new char[10];

int len;

while((len=isr.read(cArr))!=-1){

System.out.println(new String(cArr,0,len));

}

isr.close();

fis.close();

}

}

/*运行结果为:

�й��Ұ��㡣

I love you

,China.

*/

/*出现了乱码,是因为代码中指定的字符集编码与读取的文件的数据的编码格式不一致,

指定编码的一行代码改成:InputStreamReader isr=new InputStreamReader(fis,"GBK");即可避免乱码*/

案例2:转换字节输出流为字符输出流

package day01;

import java.io.*;

class Bear{

public static void main(String[] args) throws IOException {

FileOutputStream fos=new FileOutputStream("F:\\test\\ors.txt");

OutputStreamWriter osw=new OutputStreamWriter(fos,"utf-8");

osw.write("中国,我爱你");

osw.flush();

osw.close();

fos.close();

}

}

标准输入输出流

System.out和System.in是系统标准的输入和输出设备(键盘和显示器)

System.in的类型是InputStream

System.out的类型是PrintStream。

案例1:创建一个接受键盘输入的标准输入流

package day01;

import java.io.*;

class Bear{

public static void main(String[] args) throws IOException {

RedPap rd=new RedPap();

rd.fun();

}

}

class RedPap{

public void fun() throws IOException {

//创建接受键盘输入的输入流

InputStreamReader isr=new InputStreamReader(System.in);

//把输入流放到缓冲流里:

BufferedReader bfr=new BufferedReader(isr);

//创建临时接受数据的字符串:

String str="";

while((str=bfr.readLine())!=null){ //readLine()是缓冲输入字符流提供的按行读取终端数据的方法,每一次调用会以字符串形式返回一行内容,读取完会返回null

System.out.println(str);

}

}

}

//运行结果如下图:

651d61bb0baebf415a1cc8a2efea906e.png

案例2:将控制台的输入内容输出到文件krystal.txt中,控制台出现”over“时结束输出。

package day01;

import java.io.*;

class Bear{

public static void main(String[] args) throws IOException {

Krystal cf=new Krystal();

cf.fun("F:\\test\\Krystal.txt");

}

}

class Krystal{

public void fun(String kPath) throws IOException {

BufferedReader bfr=new BufferedReader(new InputStreamReader(System.in));

BufferedWriter bfw=new BufferedWriter(new FileWriter(kPath));

String str="";

while((str=bfr.readLine())!=null){

if(str.equals("over")){

break;

}

bfw.write(str);

}

bfw.close();

bfr.close();

}

}

f34ab724c4f1b48f21919805e82d307b.png

序列化与反序列化

首先思考两个问题:

1、如果想把一个类的实例化对象存到电脑的硬盘上,要怎么做?

硬盘存储的基础是二进制,需要把对象转化为一个二进制的字节流,然后把流保存到硬盘上。如果要用存到硬盘里的对象,又得把流转化为对象再使用。

2、如果想把一个对象通过网络传到另一台机器上,要怎么做?

网络通信的基础也是二进制,需要把对象转化为二进制的数据流,然后通过网络传输流。接收者如果想要使用接收的对象得先把对象的流转为对象。

因为上面两类问题的存在,产生了对象的输入与输出流。

序列化(Serialize):序列化是指把一个Java对象变成二进制内容,本质上就是一个byte[]数组,用ObjectOutputStream类将一个对象写入IO流中。

反序列化(Deserialize):用ObjectInputStream类从IO流中恢复对象。

注意事项:

序列化和反序列化针对的是实例化对象的各种属性,不包括类的属性。

一个Java对象要能序列化,必须实现一个特殊的java.io.Serializable接口

实现Serializable接口的类的对象的是可序列化的

Serializable接口没有定义任何方法,它是一个空接口。

我们把这样的空接口称为“标记接口”(Marker Interface),实现了标记接口的类仅仅是给自身贴了个“标记”,并没有增加任何方法。

案例1:

package day01;

import java.io.*;

class AbleObj implements Serializable {

String name;

int age;

String school;

double weigth;

}

class ObjOut{ //序列化类

public void funO(String oosPath) throws IOException {

ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream(oosPath)); //定义对象输出流

AbleObj ao=new AbleObj();

ao.name="krystal";

ao.age=20;

ao.school="SongSanHu";

oos.writeObject(ao);

oos.flush();

oos.close();

}

}

class ObjIn{ //反序列化类

public void funI(String oisPath) throws IOException, ClassNotFoundException {

ObjectInputStream ois =new ObjectInputStream(new FileInputStream(oisPath));

Object a=ois.readObject();

AbleObj b=(AbleObj)a; //强制转换为AbleObj类型

/*对象的序列化和反序列化使用的类要严格一致,序列化是什么类反序列化就用什么类,

包名、类名、类结构等等所有都要一致。*/

System.out.println(b.name);

System.out.println(b.school);

ois.close()

}

}

public class Test01{ //运行类

public static void main(String[] args) throws IOException, ClassNotFoundException {

ObjOut tt=new ObjOut();

tt.funO("F:\\test\\KrystalInfo.txt");

ObjIn kk=new ObjIn();

kk.funI("F:\\test\\KrystalInfo.txt");

}

}

/*运行结果为:

krystal

SongSanHu

*/

安全性

因为Java的序列化机制可以导致一个实例能直接从byte[]数组创建,而不经过构造方法,因此,它存在一定的安全隐患。一个精心构造的byte[]数组被反序列化后可以执行特定的Java代码,从而导致严重的安全漏洞。

实际上,Java本身提供的基于对象的序列化和反序列化机制既存在安全性问题,也存在兼容性问题。更好的序列化方法是通过JSON这样的通用数据结构来实现,只输出基本类型(包括String)的内容,而不存储任何与代码相关的信息。

随机存取流

RandomAccessFile类支持"随机访问"的方式,即程序可以直接跳到文件的任意地方进行读写操作。RandomAccessFile对象包含一个记录指针,用来标记当前读写开始位置,通过void seek(long pos)方法可以将记录指针定位到pos位置。

案例1:随机访问文件(任意位置读取)

//F:\\test\\AccessTest.txt文件的内容为:

123456789I love you,krystal. Where are you?

package day01;

import java.io.FileInputStream;

import java.io.RandomAccessFile;

public class Test01{

public static void main(String[] args) throws Exception {

RandomIn ii=new RandomIn();

ii.Rfi("F:\\test\\AccessTest.txt");

}

}

class RandomIn{

public void Rfi(String path1) throws Exception{

RandomAccessFile raf=new RandomAccessFile(path1,"rw");

/*参数2是mode模式:

"r":以只读模式打开文件

"rw":打开以便读取和写入(常用)

"rwd":打开以便读取和写入,同步文件内容的更新

"rws":打开以便读取和写入,同步文件内容和元数据的更新

*/

raf.seek(5); //设置读取文件内容的起点

byte [] bArr=new byte[100];

int len;

while((len=raf.read(bArr))!=-1){

System.out.println(new String(bArr,0,len));

}

raf.close();

}

}

/*运行结果为:

6789I love you,krystal. Where are you?

*/

案例2:随机写入

//F:\\test\\AccessTest.txt文件的内容为:

123456789I love you,krystal. Where are you?

package day01;

import java.io.IOException;

import java.io.RandomAccessFile;

public class Test01{

public static void main(String[] args) throws Exception {

RandomOut oo=new RandomOut();

oo.rof("F:\\test\\AccessTest.txt");

}

}

class RandomOut{

public void rof(String path2) throws IOException {

RandomAccessFile rdaf=new RandomAccessFile(path2,"rw");

rdaf.seek(0);

rdaf.write("Hi,this is Jimmy! ".getBytes());

rdaf.seek(rdaf.length()); //这个相当于给文件末尾追加内容

rdaf.write(" I miss you so much!".getBytes());

rdaf.close();

}

}

/*运行完成后,F:\\test\\AccessTest.txt文件的内容变化如下:

123456789I love you,krystal. Where are you?

Hi,this is Jimmy! u,krystal. Where are you? I miss you so much!

可以看到,在开头或者中间写入内容时,会覆盖掉等长度的原内容。

*/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/534853.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

val_loss突然变很大_女朋友突然变得很冷淡是怎么回事?该怎么办

原本和女朋友恋爱之后她一直都表现得比较比较热情,但是最近这段时间她突然对自己冷淡起来,很多男生可能就会很疑惑:女朋友突然变得很冷淡是怎么回事?该怎么办呢?一、女朋友突然变得很冷淡原因不管怎么说,女…

进程调度rr算法java实现_Java实现进程调度算法(二) RR(时间片轮转)

一、概述因为这次os作业对用户在控制台的输入输出有要求,所以我花了挺多的代码来完善控制台的显示。也因为我这次要实现多个类似算法,所以将一些共性单独提取出来作为一个类。如果只想要和算法有关的核心代码,看RR类的calc()即可。实现思路&a…

string 长度_String源码解析

本章源码分析基于JDK1.7实现的接口String类被final修饰词修饰,代表不可修改的特性,它实现了三个接口,Serializable是序列化接口,Compareble是排序接口,Char是字符序列接口。主要成员变量char[]:String通过c…

将你一张表的值覆盖_山西联通携手华为完成长风商务区宏微协同,立体覆盖,打造5G精品网络...

近日,中国联通山西分公司(以下简称“山西联通”)在太原长风商务区继5G CA超高速率升级之后,又针对长风商务区两层活动区域进行了5G宏微协同的立体覆盖,实现了该区域5G网络的连续部署。长风商务区建筑结构设计新颖,占地面积3.06平方…

局域网内文件传输速度_详解蒲公英路由器组网 实现文件共享

蒲公英路由器,除了具备普通路由器的功能之外,如图:最大的特色是可以实现智能组网:最大的特色是可以实现智能组网:采用全新自主研发的Cloud VPN技术替代传统VPN,基于SD-WAN智能组网方案,快速组建…

java emoji显示乱码_Java 解决Emoji表情过滤问题

Emoji表情从三方数据中获取没有过滤,导致存入DB的时候报错。原因:UTF-8编码有可能是两个、三个、四个字节。Emoji表情是4个字节,而Mysql的utf8编码最多3个字节,所以数据插不进去。方法1.将已经建好的表也转换成utf8mb42&#xff0…

mongotemplate中save抛出异常_异常处理的三个好习惯 | Python 工匠

文 | piglei 编辑 | EarlGrey推荐 | 编程派(微信ID:codingpy)前言如果你用 Python 编程,那么你就无法避开异常,因为异常在这门语言里无处不在。打个比方,当你在脚本执行时按 ctrlc 退出,解释器就会产生一个 KeyboardI…

组态王能直接读取仪表数据吗_液晶多功能网络电力仪表PD800H

液晶多功能网络电力仪表PD800H-H44三相三线多功用电力表面,一般也被称作网络电力表面,它是一种数字化的监控设备,其功用集成了电量测量,情况监控,远程通讯为一体,作业原理上选用了现代核算机技术和数字信号…

php养老院管理系统,XYCMS养老院建站系统 v3.8

XYCMS养老院建站系统是一个专为养老院而设计的养老院建筑系统。中心信息管理:包括基本信息管理,添加,问答中心信息管理新闻动态管理:管理新闻信息内容,管理相关分类,添加或者删除生活环境内容管理&#xff…

超过响应缓冲区限制_Nginx如何限制并发连接数和连接请求数?

全网最全1500份Java学习资料、500份BAT面试真题:关注公众号,输入“面试题”,获取提取码!首先讲解两个算发:算法思想是:令牌以固定速率产生,并缓存到令牌桶中;令牌桶放满时&#xff0…

跨域产生的原因和解决方法_板式家具开料机加工过程产生崩边原因及解决方法...

家具厂数控开料机加工材料的时候会遇到材料崩边的问题,下面我们系统的分析下产生的原因以及解决的办法产生崩边现象的原因?其一是材料本身问题。目前除了实木家具,目前使用较多的就是 板式贴皮的材料,板材的优点就是标准化生产&am…

facade 门面 php,php设计模式之门面(Facade)模式

该模式属于结构型模式什么是门面模式?定义:门面模式(有时候也称为外观模式)是指提供一个统一的接口去访问多个子系统的多个不同的接口,它为子系统中的一组接口提供一个统一的高层接口。外部与子系统的通信是通过一个门面(Facade)对象进行。其…

架构师一般做到多少岁_《迷茫中的我们该如何突破瓶颈——成长为一名架构师》...

如何成长为一名架构师?架构师是一个既需要掌控整体又需要洞悉局部瓶颈并依据具体的业务场景给出解决方案的团队领导型人物。一个架构师得需要足够的想像力,能把各种目标需求进行不同维度的扩展,为目标客户提供更为全面的需求清单。很多程序员想成为一名架…

php-fpm初始化失败,FPM的初始化 - [ PHP7的内核剖析 ] - 在线原生手册 - php中文网

FPM的初始化接下来看下fpm的启动流程,从main()函数开始://sapi/fpm/fpm/fpm_main.cint main(int argc, char *argv[]){... //注册SAPI:将全局变量sapi_module设置为cgi_sapi_modulesapi_startup(&cgi_sapi_module);... //执行php_module_staru…

360手柄摇杆漂移修复_彻底解决你的Switch手柄摇杆问题,最省钱的完美修复。

我想很多Switch的消费者都遇到了一个问题,用久了之后的手柄失灵,移动不精准,卡顿,自动位移等现象。玩个游戏都非常的糟心。动一下摇杆角色都会自动移动...这些问题的出现主要原因是摇杆内部进了灰尘,才导致各种现象的出…

libzdb 连接mysql,数据库连接池库libzdb使用教程

Libzdb挺强大, 支持Mysql Oracle SQLite PostgreSQL,支持C和C Object C,不能在Window下用(看源码是因为基于Linux线程机制编写实现)。遗憾的是找个资料太费劲,只能到Libzdb官网:点此进入 ,今正看着上面英文…

数值分析方程求根实验matlab,数值分析实验之非线性方程求根(MATLAB实现)

一、实验目的1. 了解一般非线性方程的求根是比较复杂的事情:要讨论(或知道)它有无实根,有多少实根;知道求近似根常用的几种方法,每种方法的特点是什么。2. 用通过二分法(区间半分法)、不动点(也Picard)迭代…

iis php 数据库乱码,如何解决php插入数据乱码问题

php插入数据乱码的解决办法:首先要设置数据表的字符集为utf8;然后修改字符集格式;接着建立字符集为utf-8的数据库;最后通过php mysql语句插入数据即可。mysql数据库乱码问题解决办法我们在使用数据库(mysql)的时候最怕的就是数据库…

vc 通过句柄修改窗口大小_VC应用(1)通过VC修改销售订单行项目的字段

VC是SAP中非常重要的功能,过去多年来,我参与了不少使用VC的项目,我将通过多篇文章介绍VC的一些应用,本文介绍通过VC修改销售订单行项目的字段01 概览在销售订单创建时,对于可配置物料来说,不同的配置可能会…

springboot starter工作原理_98,谈谈SpringBoot的工作原理

对技术的探索,一切源于好奇心,保持好奇心,才能让人更年轻。至今,我们已经有了很多创建SpringBoot项目的经验,比如我们要创建一个支持web开发的项目,我们只需要引入web-starter模块即可。那么,Sp…