上一篇总结了 序列流,内存操作流,对象操作流,打印流(点此传送门)
由于上一篇篇幅有点长了,所以这一篇接下去整理一些”琐碎“流
标准输入输出流
什么是标准输入输出流
System.in是InputStream, 标准输入流, 默认可以从键盘输入读取字节数据
System.out是PrintStream, 标准输出流, 默认可以向Console中输出字符和字节数据
public static void main(String[] args) throws IOException {
InputStream is = System.in;
int x = is.read();//以字节读取
System.out.println(x);
is.close();
}
/* Input:62
* Output:54
*/
上面的read方法是以字节读取的,所以输入62,它将只读取第一个字节,把将其当做码表中的一个字符,通过查码表可得字符‘6’(占一个字节)对应的为54
int y = '6';
System.out.println(y);
上述输出的为54.
再来看下面一种情况:
public static void main(String[] args) throws IOException {
InputStream is = System.in;
int x = is.read();
System.out.println(x);
is.close();
InputStream is2 = System.in;
int y = is2.read();
System.out.println(y);
}
你觉得结果会如何?
public static void main(String[] args) throws IOException {
InputStream is = System.in;
int x = is.read();
System.out.println(x);
is.close();
InputStream is2 = System.in;
int y = is2.read();
System.out.println(y);
}
/* Input:62
* Output:54
* Exception in thread "main" java.io.IOException: Stream closed
* ...
*/
异常,Stream被关闭了,下面我们不是重新创建了一个 is2 的对象,为什么
查看System里面的in和out
public final class System {
public final static InputStream in = null;
public final static PrintStream out = null;
}
可以发现,其是static和final的,那么可以说明标准输入输出流在系统中只存在一个,java有提供关闭的方法,但没有提供打开的方法,所以一旦关闭,自然你下一次使用的时候也是关闭的。前面说过当硬盘和内存有产生关联时(读取文件)必须关闭流,那么这里的标准输入输出流不关也可。记住标准输入输出只存在一个(比如同一时间键盘和屏幕只有一个),如果你确保后序程序不会使用,那么关闭即可。
修改标准输入输出流
修改输入流: System.setIn(InputStream)
修改输出流: System.setOut(PrintStream)
修改标准输入输出流拷贝文件:
public static void main(String[] args) throws IOException {
System.setIn(new FileInputStream("desk.jpg"));
System.setOut(new PrintStream("desk_copy.jpg"));
InputStream is = System.in;
PrintStream ps = System.out;
int len;
byte[] arr = new byte[1024*8];
while((len = is.read(arr)) != -1) {
ps.write(arr, 0, len);
}
is.close();
ps.close();
}
注:以上代码只是为了让大家知道有这么一回事,一般我们也不会去修改,了解即可
两种方式实现键盘录入
1.BufferedReader的readLine方法
2.Scanner
public static void main(String[] args) throws IOException {
//使用InputStreamReader转换流
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = br.readLine();
System.out.println(line);
br.close();
//一般使用Scanner,更方便,功能更强大
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
System.out.println(str);
sc.close();
}
随机访问流(RandomAccessFile)
随机访问流概述
RandomAccessFile类不属于流,是Object类的子类。但它融合了InputStream和OutputStream的功能。
支持对随机访问文件的读取和写入。
构造方法:
public RandomAccessFile(File file,String mode) throws FileNotFoundException
public RandomAccessFile(String name,String mode) throws FileNotFoundException
这个String mode是什么?查看API
值 含意
"r" 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。
"rw" 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。
"rws" 打开以便读取和写入,对于 "rw",还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。
"rwd" 打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备。
第一种只可读,第二种可读可写,我们知道前面两种即可,后面两种的同步更新指的是你在编写的时候他就会边存储,而元数据就是文件右键属性里面的详细信息,了解即可
public static void main(String[] args) throws IOException {
RandomAccessFile raf = new RandomAccessFile("File.txt", "rw");
//raf.write(97);
int x = raf.read();
System.out.println(x);
raf.close();
}
/*
* Output:97
*/
RandomAccessFile形象的说是雌雄同体,以上的程序是分两步进行的,首先先进行write后将其注释,然后进行读写。为什么两个不能同时进行读写?这里没看过源码,但是根据之前学的流分析:其流内部都有pos,用于记录读入或写出的位置,要是两个一起,先write,那么pos就指向了最后,在进行读的时候因为pos到了最后便返回-1了。
再来了解一个方法:
public void seek(long pos) throws IOException
设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。偏移量的设置可能会超出文件末尾。偏移量的设置超出文件末尾不会改变文件的长度。只有在偏移量的设置超出文件末尾的情况下对文件进行写入才会更改其长度。
上面是API上解释,简单的讲就是设置pos位置,当pos位置超出文件末尾时,会改变文件长度。
public static void main(String[] args) throws IOException {
RandomAccessFile raf = new RandomAccessFile("File.txt", "rw");
raf.seek(10);
raf.write(98);//对应第二幅图结果
raf.seek(5);
raf.write(99);//对应第三幅图结果
raf.close();
}
原始数据
可以看出来他这里中间以空格补齐了,而seek(10)出现了11字节说明他是从0开始计数的,和数组一样arr[10] 里面当然有11位。后面将seek(5)设置为5于是他就在第六位替换为write的字符。
以前在学习FileOutputStream时说过,重新new一次就会自动把文件清空,而这里却不同,程序不会将文件清空而做了一次seek(0)的操作,仅仅把指针指向了第一位,这样你在写数据会把第一位值给替换了。
这个设置指针的方式可以在指定位置读写有什么好处呢?
它的好处就是可以用来多线程下载。下图是我随便网上找个资源使用某雷下载时截的图
数据输入输出流
什么是数据输入输出流
DataInputStream, DataOutputStream可以按照基本数据类型大小读写数据
例如按Long大小写出一个数字, 写出时该数据占8字节. 读取的时候也可以按照Long类型读取, 一次读取8个字节.
DataOutputStream
DataOutputStream(OutputStream), writeInt(), writeLong()
public static void main(String[] args) throws IOException {
DataOutputStream dos = new DataOutputStream(new FileOutputStream("File.txt"));
dos.writeInt(997);
dos.writeInt(998);
dos.writeInt(999);
dos.close();
}
DataInputStream
DataInputStream(InputStream), readInt(), readLong()
public static void main(String[] args) throws IOException {
DataInputStream dis = new DataInputStream(new FileInputStream("File.txt"));
int x = dis.readInt();
int y = dis.readInt();
int z = dis.readInt();
System.out.println(x);
System.out.println(y);
System.out.println(z);
}
/*
* Output:
* 997
* 998
* 999
*/
以上,流的内容基本上都已复习完毕了
Properties
首先Properties不是流,而是一个Map集合(继承于Hashtable),一般用于写配置文件,由于可以和流搭配使用,便在此讲解
Properties的概述
Properties 类表示了一个持久的属性集。
Properties 可保存在流中或从流中加载。
属性列表中每个键及其对应值都是一个字符串。
查看API,之前说它是一个集合,但是为什么没有指定泛型呢?因为它用于写配置文件的,以键值对的形式存储(name = "..."),而存储在配置文件中都是以字符串的形式存储的,正是因为以字符串形式存储,就省去了泛型(使用泛型后表明可以存储任意类型)
Properties作为Map集合的使用
Properties是Hashtable的子类
public static void main(String[] args) throws IOException {
Properties prop = new Properties();
prop.put("name", "张三");
System.out.println(prop);
}
/*
* Output:
* {name=张三}
*/
Properties的特殊功能
public Object setProperty(String key,String value)
public String getProperty(String key)
public Enumeration<String> stringPropertyNames()
public static void main(String[] args) throws IOException {
Properties prop = new Properties();
prop.setProperty("name", "张三");
prop.setProperty("age", "24");
Enumeration<String> en = (Enumeration<String>)prop.propertyNames();
while(en.hasMoreElements()) {//遍历
String key = en.nextElement();//获取Properties中的每一个键
String value = prop.getProperty(key);//根据键获取值
System.out.println(key + "=" + value);
}
}
/*
* Output:
* age=24
* name=张三
*/
可以看出来由于其底层也是使用哈希算法,所以其不能保证顺序的一致。
配合IO流的使用
Properties的load()和store()功能
public static void main(String[] args) throws IOException {
Properties prop = new Properties();
prop.load(new FileInputStream("config.properties"));//将文件上的键值对读取到集合中
System.out.println(prop);
prop.setProperty("QQ", "666666");
//将prop中内容重新写回去,第二个参数是对列表参数的描述,可以给值,也可以给null
prop.store(new FileOutputStream("config.properties"), "properties");
System.out.println(prop);
}
/*
* Output:
* {age=10, QQ=123321, name=xiaoming}
* {age=10, QQ=666666, name=xiaoming}
*/
原始数据
可以发现使用Properties的写入多了两行,第一行就是描述信息,给null则不显示,第二行为时间(前面的#号代表注释)
简单的说load()方法其实就是使用流read数据,store()就是使用流write数据
IO流的学习到此已经基本结束了。