《Java技术及应用》笔记(四)

1.文件访问

1.1.File类

File类提供了一套独立于操作系统的方法,使用户能访问文件系统中的文件和目录信息,可以用4种方法创建File类的实例:

注意

在java文件路径中,可以用正斜杠/或反斜杠\ ,但反斜杠在java中作为转义字符存在,所以在文件路径中要使用\\才能表示\,可以认为/等同于\\,但由于java是跨平台的,所以最好用/,windows、linux、unix都能识别。

1、File(String path)

以路径目录结构创建一个File目录对象,例如有一个文件的路径为C:\java\demo\abc.txt。

File dir=new File("C:/java/demo"); 这是把目录的绝对路径作为参数。

File dir=new File("demo"); 这是用相对于当前C:/java目录的相对路径作参数。

2、File(String path,String name)

这种构造方法为具体文件创建File类实例

File afile=new File("demo","abc.txt");

3、File(File dir,String name)

直接用File类实例作目录

File afile=new File(dir,"abc.txt");

File类有许多方法可以得到文件和目录信息,具体使用时再查找即可。

1.2.RandomAccessFile类

RandomAccessFile类可以读写文件以及任意地访问文件的任何地方,同时实现了DataInput和DataOutput接口还支持文件指针的概念,文件指针用于指示当前的读写位置

当创建RandomAccessFile类的对象时,必须指明要读还是写文件(要写则必须要读),有两种创建格式: 

1、public RandomAccessFile(String name,String mode)

用字符串给出文件名,并且用字符串给出访问方式。如果程序没有访问该文件的权限,会产生一个IOException。访问方式用r表示读,用rw表示读写。例如:

RandomAccessFile raf=new RandomAccessFile("abc.txt","r");

2、public RandomAccessFile(File file,String mode)

由于文件名与系统相关,所以用File类实例表示文件名更好,例如:

File afile=new File("demo","abc.txt");

RandomAccessFile raf=new RandomAccess(afile,"rw");

import java.io.*;
class RandomFile{
 public static void main(String[] args){
  float data[]={14.3f,4.5f,5.7f,6.8f};
  File afile=new File("./str/ra.dat");
  try{
   RandomAccessFile rwfile=new RandomAccessFile(afile,"rw");
   for(float a:data)
    rwfile.writeFloat(a);
   rwfile.seek(12);   //float类型的数占4个字节,12是第4个字节开始的位置
   System.out.println(rwfile.readFloat);
   rwfile.close();
  }catch(FileNotFoundException e){   //没找到文件
   System.out.println("File Not Found");
  }catch(IOException e){    //没有权限访问文件
   System.out.println(e);
  }
 }
}

1.2.1.RandomAccessFile的readLine方法中文乱码问题

链接:RandomAccessFile类的readLine方法中文乱码分析

如果想理解下编码解码乱码的知识可以看下。

2.字节流

Java的java.io包中定义了两种类型的流:字节流和字符流。字节流以字节为单位进行数据处理;字符流以Unicode码表示的字符为单位进行数据处理,通常进行文本数据读写时会使用,在底层所有的输入输出都是以字节为单位进行的。

字节流的顶端是两个抽象超类:InputStream和OutputStream,它们定义了支持字节流输入输出的共有操作。

2.1.InputStream及其子类

InputStream是一个抽象超类,由它派生出各种输出流

1、FileInputStream类

以本地文件系统作为输入流起点,与其他和文件有关的类配合可以对文件进行读写操作。

2、PipedInputStream类

是管道的输入部件,管道是把一个程序的输出连通到另一个程序的输入。

3、ByteArrayInputStream类

以字节数组为输入流起点,可以用read方法读内存里的数组数据。

4、SequenceInputStream类

把几个输入流变成单个输入流,可以用来连接文件。

5、ObjectInputStream类

对象的输入流,利用其中的readObject方法可以直接读取一个对象。

6、FilterInputStream类

过滤输入流,其特点是把过滤流连接到另一个输入流上,例如将过滤流连接到标准输入流。

InputStream是所有输入流的超类,除了几个直接子类外,还提供了最少的编程接口和一部分实现了的输入流。下面列出其接口声明的一系列方法: 

1、available:返回可读取的字节数

2、close:关闭数据流

3、mark:将现在的输入位置标记

4、marksupported:确定输入流是否支持mark和reset

5、read:从流中读数据

6、reset:将输入位置放在mark处

7、skip:跳过部分数据

上述方法中最主要的方法是read(),最常用的调用方法是System.in.read(),这是系统的标准输入流从标准输入设备(键盘)中读入字符,该方法不用引用java.io包。 

若read()方法读到文件末尾,将返回-1,表示已读完

read方法的重载方法:public int read(byte b[])

一个byte[]数组作为方法的参数,可用于从标准输入一次读入若干字符,但长度不超过数组容量,返回读取的字节数。

class Arrayinput throws java.io.IOException{
 public static void main(String[] args){
  byte b[]=new byte[40];
  System.in.read(b);
  String s=new String(b);   //String构造方法
  System.out.println(s);   //数组里没有输入的将会在这里输出空格
 }
}

read方法的又一重载方法:public int read(byte[],int off,int len) 

指明从数组第off个字节开始放数据,长度为len。

2.2.OutputStream及其子类 

OutputStream同样是个抽象超类,其子类为:(与InputStream子类相近不赘述)

1、FileOutputStream

2、PipedOutputStream

3、ByteArrayOutputStream

4、FilterOutputStream

5、ObjectOutputStream

OutputStream类的输出流接口声明的方法

1、write:将数据输出到数据流中

2、close:关闭输出数据流,释放所占用的资源

3、flush:将缓冲区内的数据输出,刷空输出流 

write方法的重载方法与read相近,不赘述:

public void write(byte b[]).

public void write(byte[],int off,int len). 

2.3.文件字节流(FileInputStream和FileOutputStream)

2.3.1.FileInputStream

FileInputStream是InputStream的直接子类,它重写了超类的read、skip、available和close方法,但不支持mark和reset方法。

创建对象格式为:FileInputStream fis=new FileInputSream("Myfile");

Myfile可以是文件名,也可以是File类对象。

若文件不存在,将产生一个异常FileNotFoundException。

2.3.2.FileOutputStream

FileOutputStream重写了OutputStream的write()和close()方法。

创建格式为:FileOutputStream fog=new FileOutputStream("Myfile");

若该文件不存在,将建立一个新文件,若存在,则覆盖旧内容,再加一个true参数可以不覆盖旧内容;若文件为只读属性,将产生一个IOException异常。

2.3.3.文件字节流测试

import java.io.*;

public class Test{

	public static void main(String[] args) throws IOException {
		// TODO 自动生成的方法存根
     File dir=new File("D:/test.txt");
     OutputStream fos=new FileOutputStream(dir,true);
     String str1="Java技术及应用";
     fos.write(str1.getBytes());  //默认gbk编码格式
     fos.close();
     
     InputStream fis=new FileInputStream(dir);
     byte b[]=new byte[50];
     fis.read(b);
     String str2=new String(b);   //默认gbk编码格式
     System.out.println(str2);
     fis.close();
	}

}

1.read和write操作都是对字节进行操作。

2.提一嘴,由于我编译器和电脑操作系统的默认编码格式都是gbk,所以getBytes()和new String(b)的默认编码格式都是gbk。也可以写成getBytes("gbk")和new String(b,"gbk")。

2.4.管道流(PipedInputStream和PipedOutputStream) 

管道流就是一个程序的输出连通另一个程序的输入进行数据流通。若某个应用程序的输出结果必须送到另一应用程序作为输入数据进行处理,用管道流是最为方便的,还可以利用管道流进行不同线程间的通信。

2.4.1.PipedInputStream

管道输入流是管道的接收端,PipedInputStream提供了connect()方法进行连接,并且重写了read方法。

创建格式为:PipedInputStream pis=new PipedInputStream();

假设有个管道输出流对象pos,可以:PipedInputStream pis=new PipedInputStream(pos);

也可以这样连接:pis.connect(pos);

2.4.2.PipeOutputStream 

管道输出流是管道的发送流,提供了connect方法,重写了write方法,创建格式与PipedInputStream雷同,不赘述。

2.4.3.管道流测试

public class Test{

	public static void main(String[] args) throws IOException {
		// TODO 自动生成的方法存根
     String str="Java技术及应用";
     PipedInputStream pis=new PipedInputStream();
     PipedOutputStream pos=new PipedOutputStream(pis);
     pos.write(str.getBytes());
     byte[] b=new byte[30];
     System.out.println(pis.read());
     pis.read(b);
     System.out.println(new String(b));
     pis.close();
     pos.close();
	}

}

第一个read()读取了一个字节并返回这个字节的int值;第二个read(d)读取了剩下的字节并返回了读取的字节数(程序中没有打印返回的字节数,输出了剩下的字符)。

2.5.数据流

2.5.1.DataInput接口

该接口描述了各种独立于机器的读Java基本数据类型的readxxx()方法(不赘述,具体要用再查),由DataInputStream类和RandomAccessFile类实现这些方法。

2.5.2. DataOutput接口

该接口描述了各种writexxx()方法,由DataOutPutStream和RandomAccessFile实现。

2.5.3.DataInputStream 

DataInputStream是FilterInputStream的子类,也是过滤流,同时还实现了java.io的DataInput接口。要使用DataInputStream过滤流,必须把它连接到另一个输入流,例如:

DataInputStream dis=new DataInputStream(new FileInputStream("Myfile"));

2.5.4.DataOutputStream

与DataInputStream雷同,不赘述。

2.5.5.数据流测试

import java.io.*;

public class Test{

	public static void main(String[] args) throws IOException {
		// TODO 自动生成的方法存根
     String str="Java技术及应用";
     int i=12345;
     File file=new File("D:/test.txt");
     DataInputStream dis=new DataInputStream(new FileInputStream(file));
     DataOutputStream dos=new DataOutputStream(new FileOutputStream(file));
     dos.writeUTF(str);
     dos.writeInt(i);
     System.out.println(dis.readUTF());
     System.out.println(dis.readInt());
     dis.close();
     dos.close();
	}
}

具体的作用暂时不知道,体感是方便一点,直接按类型调用方法,需要注意的是,writexxx和readxxx的顺序要一致,不然会出错,比如交换writeUTF和writeInt位置会出错。

2.6.字节缓冲流

2.6.1.BufferedInputStream

父类是FilterInputStream,这种过滤流具有缓冲机制,若将它连接到某个输入流,会把输入数据读入到缓冲区,其后读操作直接访问缓冲区,这就减少了计算机访问数据源的次数,提高程序执行效率。除此之外,BufferedInputStream还可以实现mark()和reset()方法。

创建格式有两种:

1.采用默认的缓冲区,大小为8192字节。

InputStream is=new BufferedInputStream(InputStream in);

in是连接到的输入流。应该是利用上转,也可以用BufferedInputStream is=.....

2.自己定义缓冲区的大小,通常小于8912字节。

InputStream is=new BufferedInputStream(InputStream in,int size);

实际应用:

InputStream is=new BufferedInputStream(new FileInputStream("Myfile"),1024); 

2.6.2.BufferedOutputStream

将该过滤流连接到某个输出流,会把输出数据写到缓冲区,这就减少了计算机访问数据终点的次数,提高程序执行效率。数据送到缓冲区后,若缓冲区不满,数据不会送到所连的输出流。若用BufferedInputStream的flush方法,就会强迫缓冲区的内容全部送到输出流

2.6.3.字节缓冲流测试 

public class Test{

	public static void main(String[] args) throws IOException {
		// TODO 自动生成的方法存根
     String str="Java技术及应用";
     File file=new File("D:/test.txt");
     InputStream is=new BufferedInputStream(new FileInputStream(file),12);
     OutputStream os=new BufferedOutputStream(new FileOutputStream(file),15);
     os.write(str.getBytes());
     //os.flush();
     byte[] b=new byte[20];
     is.read(b);
     System.out.println(new String(b));
     is.close();
     os.close();
	}
}

当输出缓冲区容量大于传输的字节数时,就比如代码里15大于传输字节数14,这时用read方法是读不到字节的,因为要传输的字节还在缓冲区里,没有进入输出流,此时可以用flush方法把缓冲区内容送入输出流;当容量小于传输字节数时,所有传输字节进入输出流。

输入缓冲区有点奇怪,尽管容量小于传输的字节数,但还是可以完整的读取出来,容量大于传输字节数也没有影响。

2.7.字节打印流

父类是FilterOutputStream,PrintStream是最方便的输出流,它提供了系统的标准输出,有两个方法:print()和println()。所谓标准输出是将字符送到显示器上,经常用的System.out就是PrintStream的对象,用System.out来调用print和println,不用引入java.io包。

1.System.out.print()

该方法送字符到一个缓冲区,直到有换行符'\n'把他们送到显示器,这意味着字符不会马上显示在屏幕上。可以输出字符或字符串。

有点奇怪,经测试发现print的特点是不会换行输出,除非手动加换行符,暂没发现上面说的不会马上显示。

2.System.out.println()

这个就很常见了,不赘述。 

2.8.字节数组流

2.8.1.ByteArrayInputStream

该类以字节数组为输入流起点,可以用read()方法读内存中的数组数据。此外,该类还重写了超类InputStream的skip()、available()和reset()方法,其中reset方法会把输入起始位置放回数组开头。

创建格式为:

1.ByteArrayInputStream bais=new ByteArrayInputStream(b);

2.ByteArrayInputStream bais=new ByteArrayInputStream(b,3,5);

b为字节数组,第二条代码表示字节数组流从数组b的第3个字节开始,读5个字节的数据。

2.8.2.ByteOutputStream

该类的输出流终点是字节数组,可以用write方法写数据到内存中的数组

ByteOutputStream定义的其他方法:

reset:清除输出流的缓冲区。

size:返回输出流的有效字节数。

writeTo:将当前输出内容写入另一输出流。

toByteArray:将当前输出内容复制到一个字节数组中,并返回该数组。

toString:将当前输出内容复制到一个字符串,并返回该字符串。 

创建格式:

1.采用默认的字节输出缓冲区。

ByteArrayOutputStream baos=new ByteArrayOutputStream();

2. 采用自定义的字节输出缓冲区。

ByteArrayOutputStream baos=new ByteArrayOutputStream(1024);

2.8.3.字节数组流测试

import java.io.*;

public class Test{

	public static void main(String[] args) throws IOException {
		// TODO 自动生成的方法存根
     File file=new File("D:/test.txt");
     int i;
     byte[] b=str.getBytes();
     ByteArrayInputStream bais=new ByteArrayInputStream(b);
     ByteArrayOutputStream baos=new ByteArrayOutputStream(16);
     while((i=bais.read())!=-1)baos.write(i+2);
     //baos.reset();  //如果写了这行代码,输出缓冲区就被清空了,没有东西输出
     System.out.println(baos.toString());
	}
}

字节数组输入流bais用read方法读取字节数组b的字节,然后再通过字节数组输出流baos对读取到的字节加密,重新写入流中。(挺迷糊的,一系列流下来)

2.9.对象流

序列化一个对象是指将它的状态按某种方式转为字节流,使得该字节以后能被还原成对象的副本。如果一个java对象的类或它的超类实现了java.io.Serializable接口或其子接口java.io.Externalizable,该对象就可以序列化。反序列化是将序列化形式的对象还原成该对象的副本的过程。

字节流中的ObjectInputStream和ObjectOutputStream可以创建对象流支持对象的输入输出,能够序列化的对象才能形成对象流。这两类分别实现了ObjectInput和ObjectOutput接口,而这两个接口又是继承DataInput和DataOutput接口,增加了读写对象的方法readObject和writeObject,不过它们的参数是类创建的对象。

2.9.1.对象流示例

import java.io.*;

public class Test{

	public static void main(String[] args) throws IOException, ClassNotFoundException {
		// TODO 自动生成的方法存根
     String str="Java技术及应用";
     File file=new File("D:/test.txt");
     Book book=new Book(2,str,45.0f);
     FileOutputStream fos=new FileOutputStream(file);
     ObjectOutputStream os=new ObjectOutputStream(fos);
     os.writeObject(book);
     os.close();
     
     ObjectInputStream is=new ObjectInputStream(new FileInputStream(file));
     Book booktemp=(Book)is.readObject();
     is.close();
     booktemp.show();
	}
}

class Book implements Serializable{
	int id;
	String name;
	Float price;
	public Book(int id,String name,Float price){
		this.id=id;
		this.name=name;
		this.price=price;
	}
	public void show() {
		System.out.println(id+" "+name+" "+price);
	}
}

实现Serializable接口无须实现任何方法,只需表明该对象是可序列化的。

3.字符流 

Java平台采用16位的Unicode字符集处理字符。

3.1.Reader及其子类

Reader类是读字符流的抽象类,它的子类有:

BufferedReader:缓冲输入字符流,与BufferedInputStream类似。

CharArrayReader:从指定的字符数组创建字符输入流,与ByteArrayInputStream类似。

FilterReader:与FilterInputStream类似,但只有一个子类PushbackReader

InputStreamReader:读入字节并按指定字节集转为字符。

PipedReader:与PipedInputStream类似。

StringReader:从字符串读入字符流 

3.2.Writer类及其子类 

子类为BufferedWriter、CharArrayWriter、FilterWriter、OutputStreamWriter、PipedWriter、PrintWriter、StringWriter,不赘述。

3.3.字符缓冲流

3.3.1.BufferedReader

构造方法有两种:

1.public BufferedReader(Reader in);

2.public BufferedReader(Reader in,int size); //size表示缓冲区大小

还提供readLine():读取分行文本。

3.3.2.BufferedWriter 

构造方法与BufferedReader一样,不赘述。

还提供newLine()方法:写入行分隔符。

3.3.3.字符缓冲流示例 

import java.io.*;

public class Test{

	public static void main(String[] args) throws IOException {
		// TODO 自动生成的方法存根
     File file=new File("D:/test.txt");
     BufferedReader br=new BufferedReader(new FileReader(file));
     BufferedWriter bw=new BufferedWriter(new FileWriter("test2.txt"));
     String s;
     while((s=br.readLine())!=null) {
    	 bw.write(s);
    	 bw.newLine();
     }
     //bw.flush();
     br.close();   //close()会先调用flush()再关闭流
     bw.close();
	}
}

3.4.转换流

实现字节流与字符流之间的转换。

3.4.1.InputStreamReader

读入字节并按指定字符集转成字符

常用构造方法有两种:

1.public InputStreamReader(InputStream in)

例如:BufferedReader in=new BufferedReader(new InputStreamReader(System.in));

将键盘输入字节按默认字符集转成字符,并加缓冲。

2.public InputStreamReader(InputStream in,String charsetName)

指定字符集。 

3.4.2.OutputStreamWriter 

将输出的字符流转变为字节流

构造方法为:

1.public OutputStreamWriter(OutputStream out)

例如:Writer out=new BufferedWriter(new OutputStreamWriter(System.out));

将字符转成字节,加缓冲送到显示器。 

2.public OutputStreamWriter(OutputStream out,String charsetName)

3.4.3.转换流示例 

import java.io.*;

public class Test{

	public static void main(String[] args) throws IOException {
		// TODO 自动生成的方法存根
     File file=new File("D:/test.txt");
     BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
     BufferedWriter bw=new BufferedWriter(new OutputStreamWriter
(new FileOutputStream(file)));
     String s;
     while(!((s=br.readLine()).equals("end"))) {
    	 bw.write(s);
    	 bw.newLine();
     }
     //bw.flush();
     bw.close();
     br.close();
	}
}

System.in是从键盘输入的字节流,被InputStreamRead转换成字符流,然后进入缓冲输入区,然后bw将字符串s写进缓冲输出区,经OutputStreamWriter转换成字节流输出到file,即test.txt,如果将new FileOutputStream(file)替换成System.out,将会把字节流输出到显示器。

3.5.字符打印流 

PrintWriter是打印对象的格式化表示到文本输出流,类似PrintStream。

4.装饰器模式 

参考1:设计模式之装饰器模式(Decorator) - 知乎 (zhihu.com)

参考2:java设计模式-装饰器模式(Decorator) - 简书 (jianshu.com)

有时我们想要给某个对象而不是整个类增加一些功能。类是静态结构,类实现的功能是类的组成部分,这部分功能在运行时是无法改变的,但是,我们有时需要某种灵活性,让我们能在运行时“改变”对象的某种行为。这个时候,我们就不应该把功能绑定在类上,而是动态的赋予对象某种额外的行为。这时就用到装饰器模式。

装饰器模式有抽象构件角色、具体构件角色、抽象装饰角色、具体装饰角色。具体我们以上面所学习的InputStream类来讲解。(Java的I/O标准库是装饰模式很著名的一种应用)

1、抽象构件角色:由InputStream扮演,这是一个抽象类,为各种子类提供了统一的方法。

2、具体构件角色:由ByteArrayInputStream、FileInputStream等类扮演,它们实现了抽象构件角色InputStream所规定的方法。

3、抽象装饰角色:由FilterInputStream、ObjectInputStream等类扮演,它们实现了InputStream所规定的方法。

4、具体装饰角色:由BufferedInputStream、DataInputStream等类扮演。

具体构件角色的实例就是我们要附加额外行为的对象,而具体装饰角色就是所要附加的功能。

抽象构件角色提供了统一的方法,具体构件角色和抽象装饰角色继承或重写了它的方法,具体装饰角色在InputStream的装饰模式里增加了自己的方法,我们把这称为半透明的装饰模式,如果具体装饰角色也没有增加自己的方法,那就叫透明的装饰模式。比如:

1、(假设)透明的装饰模式

InputStream in=

       new DataInputStream(new BufferedInputStream(new FileInputStream(file)));

我们可以直接定义一个InputStream的对象调用子类的方法。

2、(但实际)半透明的装饰模式

DataInputStream in=

       new DataInputStream(new BufferedInputStream(new FileInputStream(file)));

如果不定义DataInputStream类对象,我们就要经过下转才能调用该类增加的方法。

纯粹的透明装饰模式在真实系统中很难找到,一般我们见到的都是这种半透明的装饰模式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值