目录
3.io流的基类(InputStream/Reader OutputSteam/writer)
4. 文件流(FileInputStream/FileReader FileOutputStream/FileWriter)
5.缓冲流(BufferedInputStream/BufferedReader,BufferedOutputStream/BufferedWriter/PrintWriter)
6.转换流(InputStreamReader/OutputStreamWriter)
8.数据流(DataOutputStream/DateInputStream)
一、file类型(java.io.file)
1. 概念
可以用来创建、删除文件/目录(文件夹称目录)的属性信息。但是不能修改文件里的数据,如果要修改文件内数据,就要使用IO流
2.常用构造器
Constructor and Description |
---|
File(File parent, String child) 在指定parent路径下,创建一个child的file对象 |
File(String pathname) 创建一个指定路径的File对象 |
File(String parent, String child) 在指定parent路径下,创建一个child的file对象 |
File(URI uri) 通过将给定的 file: URI转换为抽象路径名来创建新的 File实例。 |
3.绝对路径与相对路径
绝对路径是从根目录开始写
windows:D:\A\AA.cat(文件)
C:\b(文件夹)
linux :/home/MS/xx(文件)
/home/Ms(目录)
4.常用方法
文件/目录操作方法
boolean exists(); | 判断指定的路径是否存在 |
boolean isFile(); | 判断指定路径是不是文件 |
boolean isDirectory(); | 判断指定路径是不是目录 |
String getName()); | 获取文件/目录名称 |
long lastModified(); | 获取文件/目录的最后修改时间 |
boolean isAbsolute(); | 判断指定路径是不是绝对路径 |
String getAbsolutePath(); | 获取绝对路径 |
String getParent(); | 获取父目录的路径 |
long length(); | 获取文件大小 |
文件/目录创建方法
boolean createNewFile(); | 创建文件 |
boolean mkdir(); | 创建目录 |
boolean mkdirs(); | 创建多级目录 文件/目录的删除方法 |
boolean delete() | 可以删除文件,删除目录时, 需要目录下没有文件或子目录 |
File[] listFiles() | 获取目录里的file对象 |
递归删除目录
public static void deleteDirectory(String pathname){
File A=new File(pathname);
if(A.exists()){
if(A.isDirectory()){
File[] fs=A.listFiles();
if(A.length()!=0){
for(File s : fs){
deleteDirectory(s.getAbsolutePath());
}
}
}
A.delete();
}else{
System.out.println("目录/文件不存在");
}
}
io流(java.io)
1.概念
java的io是实现输入和输出的基础,可以方便的实现数据的输入和输出操作。在java中把不同的输入/输出源(键盘,文件,网络连接等)抽象表述为“流”(stream)。通过流的形式允许java程序使用相同的方式来访问不同的输入/输出源。stram是从起源(source)到接收的(sink)的有序数据。
2.io流的分类
java Io流共涉及40多个类,这些类看上去很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的联系
我们还可以按照不同的分类方式,可以把流大体分为两个方向。一般流有三种分类方式
按照流的流向分
按照流的流向分 我们把流大体分为两类
1.输入流:只能从中读取数据,不能向中写入数据,主要是InputStream和Reader作为基类
2.输出流:只能向中写入数据,不能从中读取数据,,outputStream和Writer作为基类注: 此处的输入,输出涉及一个方向的问题,数据从内存到硬盘,通常称为输出流——也就是说,这里的输入,输出都是从程序运行所在的内存的角度来划分的。
数据从服务器通过网络流向客户端,在这种情况下,Server端的内存负责将数据输出到网络里,因此Server端的程序使用输出流;Client端的内存负责从网络中读取数据,因此Client端的程序应该使用输入流。
按照操作单元划分
按照操作单元划分,我们把流大体分为两类
1.字节流:操作的单元是数据单元是8位的字节,主要以InputSteam和OutputSteam为基类
2.字符流:操作的单元是数据单元是16位的字节,主要以Reader和Writer
注: 字节流和字符流的用法几乎完成全一样,区别在于字节流和字符流所操作的数据单元不同
按照流的角色划分
按照流的角色划分,我们把流大体分为两类
1.节点流(低级流):可以向特定的I/O设备进行读写操作。
2.处理流(高级流):对一个已存在的流进行连接和封装,使用封装后的流进行读、写操作。
注:当使用处理流进行输入/输出时,程序并不会直接连接到实际的数据源,没有和实际的输入和输出节点连接。使用处理流的一个明显的好处是,只要使用相同的处理流,程序就可以采用完全相同的输入/输出代码来访问不同的数据源,随着处理流所包装的节点流的变化,程序实际所访问的数据源也相应的发生变化。
我们来分析下这张图,首先,大体分为字节流和字符流两个分支,字节流和字符流,他们有各自的抽象父类。
- InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
- OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
3.io流的基类(InputStream/Reader OutputSteam/writer)
因为字节流和字符流虽然将其分为两类,但那只是两者面对的字节大小不同,但是它们的使用方式和各种操作区别不大。我们直接将其的基类放到一起。
InputStream/Reader
InputStream和Reader是所有输入流的抽象基类,本身并不能创建实例来执行输入,但它们将成为所有输入流的模板,所以它们的方法是所有输入流都可使用的方法。
InputSteam
abstract int
read()
从输入流读取数据的下一个字节。返回所读取的字符数据(字节数据可直接转换为int类型)。
int
read(byte[] b)
从输入流读取一些字节数,并将它们存储到字节数组
b
。返回所读取的字符数据int
read(byte[] b, int off, int len)
从输入流读取最多
len
字节的数据到一个字节数组,从数组的off开始。返回所读取的字符数据
Reader
int
read()
从输入流读取数据的下一个字节。返回所读取的字符数据(字节数据可直接转换为int类型)。
int
read(char[] cbuf)
从输入流读取一些字节数,并将它们存储到字节数组
b
。返回所读取的字符数据abstract int
read(char[] cbuf, int off, int len)
从输入流读取最多
len
字节的数据到一个字节数组,从数组的off开始。返回所读取的字符数据
对比InputStream和Reader所提供的方法,就不难发现这两个基类的功能基本是一样的。InputStream和Reader都是将输入数据抽象成如水管一样的容器,所以程序即可以通过read()方法每次读取一个”水滴“,也可以通过read(char[] chuf)或者read(byte[] b)方法来读取多个“水滴”。当使用数组作为read()方法中的参数, 我们可以理解为使用一个“试管”到水管中取水,read(char[] cbuf)方法的参数可以理解成一个”试管“,程序每次调用输入流read(char[] cbuf)或read(byte[] b)方法,就相当于用“试管”从输入流中取出一筒“水滴”,程序得到“试管”里面的”水滴“后,转换成相应的数据即可;程序多次重复这个“取水”过程,直到最后。程序如何判断取水取到了最后呢?直到read(char[] chuf)或者read(byte[] b)方法返回-1,即表明到了输入流的结束点。
然后InputStream和Reader提供的一些移动指针的方法。
如mark、markSupported、 reset、skip等,如有兴趣可以自行去查api
api1.8中文版:百度某盘:https://pan.baidu.com/s/1NbMEyXiGO9PaCWduwkSqrA 密码318n
OutputStream和Writer:
OutputStream和Writer的用法也非常相似,两个流都提供了如下三个方法:
void
write(byte[] b)
将
b.length
字节从指定的字节数组写入此输出流。void
write(byte[] b, int off, int len)
从指定的字节数组写入
len
个字节,从偏移off
开始输出到此输出流。abstract void
write(int b)
将指定的字节写入此输出流。
因为字符流直接以字符作为操作单位,所以Writer可以用字符串来代替字符数组,即以String对象作为参数。Writer里面还包含如下两个方法。
void
write(String str)
写一个字符串
void
write(String str, int off, int len)
写一个字符串的一部分。
4. 文件流(FileInputStream/FileReader FileOutputStream/FileWriter)
前面说过InputStream和Reader都是抽象类,本身不能创建实例,但它们分别有一个用于读取文件的输入流:FileInputStream和FileReader,它们都是节点流——会直接和指定文件关联
字节文件输入流:FileInputStream
/**
* FileInputStream的常用构造器
* 1:FileInputStream(File file)
* 2:FileInputStream(String[] args)
*/
public class FileInputStreamDemo01 {
public static void main(String[] args) throws IOException {
FileInputStream fis=null;
try {
File file=new File("io2.txt");
//创建一个当前目录下的文件 io2.txt
fis=new FileInputStream(file);
//创建字节流
byte[] bs=new byte[20];
//创建一个长度为20的容器
int len=fis.read(bs);
//调用read方法,读出一个数据装进bs容器内
System.out.println(len);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
fis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
字符文件输入流:FileReader
public class FileReaderTest {
public static void main(String[] args) {
FileReader fr=null;
try {
fr=new FileReader("fw.txt");
char[] a=new char[10];
System.out.println(a);
System.out.println(fr.read(a));
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
fr.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
可以看出使用FileInputStream和FileReader进行文件的读写并没有什么区别,只是操作单元不同而且。
FileOutputStream/FileWriter是Io中的文件输出流,他们主要的特点就是构造器分为两种,重写模式构造器和追加模式构造器,顾名思义,重写是将文件全部覆盖成最新的输出流。追加则是在原有的数据的基础上进行追加新的输出流
/** * 研究文件输出流FileOutputStream的构造器 * 重写模式构造器 * FileOutputStream(File file) * FileOutputStream(String pathname) * 追加模式构造器 * FileOutputStream(File file,boolean append) * FileOutputStream(String pathname,boolean append) */ public class FileOutputStreamDemo01 { public static void main(String[] args) { FileOutputStream fos=null; try { File file=new File("io2.txt"); //fos =new FileOutputStream(file);重写模式构造器 /*追加模式构造器*/ fos=new FileOutputStream(file,true); //创建流 fos=new FileOutputStream("io2.txt",true); //一字符串构造器创建流 fos.write(1025); //输出1025 fos.write("我恨你".getBytes()); //输出“为恨你”的字符数组 } catch (Exception e) { e.printStackTrace(); }finally{ try { fos.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
FileWriter的使用方式和FileOutputStream基本类似,这里就不再赘述。
注: 使用java的io流执行输出时,不要忘记关闭输出流,关闭输出流除了可以保证流的物理资源被回收之外,可能还可以将输出流缓冲区中的数据flush到物理节点中里(因为在执行close()方法之前,自动执行输出流的flush()方法)。java很多输出流默认都提供了缓存功能,其实我们没有必要刻意去记忆哪些流有缓存功能,哪些流没有,只有正常关闭所有的输出流即可保证程序正常。这个需要注意一下,当我们使用处理流套接到节点流上的使用的时候,只需要关闭最上层的处理就可以了。java会自动帮我们关闭下层的节点流。
5.缓冲流(BufferedInputStream/BufferedReader,BufferedOutputStream/BufferedWriter/PrintWriter)
字节缓冲输入流:BufferedInputStream
/**
* 字节缓冲输入流:BufferedInputStream
* 构造器:
* BufferedInputStream(InputStream is)
* BufferedInputStream(InputStream is,int size)
* 内部维护了个缓冲区 作用是尽可能的一次性读取更多的字节
* 存到缓冲区,缓冲区默认大小为8k,我们从缓冲区里读取数据当缓冲区里的数据被读取完,缓冲区会再次
* 存储新数据,这样就减少了从硬盘到内存的次数,提高了读取效率
* PS :当一次性需要读取的数据超出缓冲区的大小时 ,直接从硬盘上读取。缓冲区不工作
*/
public class BufferedInputStreamDemo01 {
public static void main(String[] args) {
BufferedInputStream bis=null;
try {
bis=new BufferedInputStream(
new FileInputStream("io3.txt"));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
bis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
字符缓冲输入流 BufferedReader
/**
* 字符缓冲输入流 BufferedReader
* 内部维护着一个缓冲区
* 构造器:
* BufferedReader(Reader reader)
* BufferedReader(Reader reader,int sz)
* ps:通常 此输入流都是与PrintWriter来成对使用的
* 即:如果使用的是PrinterWrite写的文件
* 那么就要BufferedReader来读取文件
* 方法:
* 此类中提供了一个读取一行数据的方法
* readLine();
* 即:此方法会读到换行符
*/
public class BufferedReaderDemo01 {
public static void main(String[] args) {
BufferedReader br=null;
try {
br=new BufferedReader(
new InputStreamReader(
new FileInputStream("bo.txt"), "utf-8"));//这里传入的是一个转换流
/*
* 当读到文件末尾时 readLine返回null
*
*/
String line;
while((line=br.readLine())!=null){
System.out.println(line);
}
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
我们可以看到,字符缓冲流和字节 缓冲流区别在于 readLine();这和方法,是因为我们一般不使用缓冲字符输入流BufferedWirter这个类。我们一般使用PrintWirter这个字符缓冲输入流。这个会单独说。特点是提供了一个方法println(),他会在写出数据之后自动写出一个换行符。所以readLine()这个方法就是对应出现的。
字节缓冲输出流 :BufferedOutputStream
/**
* 字节缓冲输出流 :BufferedOutputStream
* 内部维护了一个缓冲区(字节数组),当我们使用该流写数据是,会先写入缓冲区,才会一次性写出到目的地
* 这样,减少了写出的 次数,提高了写出的效率 但是缺乏即时性,因此提高了flush方法 可以去清空未满的缓冲区
* 强制写出
* 构造器:
* BufferedOutputStream(OutputStream os)
* 此构造器内部维护了一个8K大小的缓冲区(字节数组)
* BufferedOutputStream(OutputStream os, int size)
* 此构造器我们可以自定义缓冲区大小
*
*
*PS:任何输出流在关闭前都将流内的数据写出
* :当一次性写出的字节数组长度超出大于等于缓冲区 就不使用缓冲区 ,而是直接写出
*
*/
public class BufferedOutputStreamDemo01 {
public static void main(String[] args) {
BufferedOutputStream bos=null;
try {
bos=new BufferedOutputStream(
new FileOutputStream("io3.txt"));
bos.write("你好,你咋那么 漂亮呢".getBytes());
bos.flush();
} catch (Exception e) {
e.printStackTrace();
}finally
{
try {
bos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
注:我们不适用BufferdeWirter。我们使用功能更强大的PrintWirter 原因是它功能强大,提供非常多的方法和构造器。
/**
* 缓冲字符输入流:PrintWriter
* 此类型提高了行自动刷新功能,并且提供了丰富的构造器及其方法
* 通常用此类型来代替BifferedWriter
* 内部维护了个缓冲区 用来提升效率
* 需要注意的是
* 带有ln的方法println();再写出数据后一定会再写出一个换行符
*
* PrintWriter(File file)
* PrintWriter(String pathname)
* printWriter(OutputStream os)
* printWriter(OutputStream os,booleanautoFlush)
* printWriter(Writer writer,booleanautoFlush)
* printWriter(Writer writer,booleanautoFlush)
*/
public class PrintWirterDemo01 {
public static void main(String[] args) {
PrintWriter pw=null;
try {
//pw=new PrintWriter("bo.txt");
/*会自动再你好后面加上一个换行符 换行符两个字节*/
//pw.println("你好");
pw =new PrintWriter(
new OutputStreamWriter(
new FileOutputStream("bo.txt"),"utf-8"),true);
/*当使用自动行刷新构造器时 只能使用println才能触发行自动刷新 别的方法不行*/
pw.println("你好");
pw.println('我');
pw.println(1025);
} catch (Exception e) {
e.printStackTrace();
}finally{
//PrintWriter的关闭方法没有声明异常 无需处理
pw.close();
}
}
}
6.转换流(InputStreamReader/OutputStreamWriter)
转换输入流:InputStreamReader
/**
* 字符输入流的抽象父类:reader
* 处理单位:字符,底层是字节流
* 原理:字符流将字节流读取的字节数据按照指定字符集进行处理
* 实现类之一 转换输入流:InputStreamReader
* 构造器 InputStreamReader(InputStreamis ,String charsetname)
* InputStreamReader(InputStreamis)
* */
public class InputStreamReaderDemo01 {
public static void main(String[] args) {
Reader reader=null;
try {
reader=new InputStreamReader(
new BufferedInputStream(
new FileInputStream("ch.txt")),"utf-8");
//而且输入内容都是文本内容,所以可以使用InputStreamReader将其包装成BufferedReader,利用BufferedReader的readLine()方法可以一次读取一行内容
char[] chs=new char[100];
int length=reader.read(chs);
String info=new String(chs);
System.out.println(length+info);
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
转换输出流 OutputStreamwriter
/**
* 字符流的抽象父类:writer
* 处理单位是按照字符处理的 只不过底层依然是字节流
* 字节流:即底层将字符按照向应的字符集转成向应的字节数据
* 实现类之一 转换输出流 OutputStreamwriter
* 构造器
* OutputStreamwriter(OutputStream os ,String charsetname)
* OutputStreamwriter(OutputStream os )
*
* ps:字符流只能对纯文本文件进行操作
*
*/
public class OutputStreamWriterDemo01 {
public static void main(String[] args) {
Writer writer=null;
try {
writer =new OutputStreamWriter(
new BufferedOutputStream(
new FileOutputStream("ch.txt")),"utf-8");
/*写一个字符*/
writer.write('你');
writer.write("我喜欢你".toCharArray());
/*写一个字符串*/
writer.write("我讨厌你");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
writer.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
7.对象流
对象是存在于内存中的,有的候需要将对象保存到硬盘上,或者将对象传输到另一台计算机上的等等操作。想要使用对象流,我们首先要知道Serializable这个接口
如果想将对象序列化到硬盘上 的操作 那么此对象的类型就需要实现序列化接口
Serializable
此接口内没有任何东西 只是一个序列化的规范接口
当继承Serializable 是如果不显示声明那么该对象序列化会根据各种属性方法默认生成一个serialVersionUID
序列号 用来判断读取或写出是版本号是否对的上 只有对的上才可以进行读写读写操作 那么问题就是如果你
没有显示声明一个UID 当你改变类之后。就无法反序列化成功 于是 我们就要显式声明一个UID来规范这个
类 让他可以在更改类属性后还可以进行序列化和反序列化
private static final long serialVersionUID = 1L;
UID对应的上的情况下 你反序列化会出现两种情况
当你减少类属性时 会不输出该属性
增加类属性时 则会输出该属性默认值。
transient关键字
类如果用transient 修饰一个属性 那么当序列化和返序列化时会忽略该属性
我们来写一个例子
首先创建一个类person 然后实现Serializable接口,然后为了使其可以进行返序列化操作,我们就要显式声明一个UID
import java.io.Serializable;
import java.util.Date;
public class person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
private char gender;
private Date birth;
public person(String name, int age, char gender, Date birth) {
super();
this.name = name;
this.age = age;
this.gender = gender;
this.birth = birth;
}
public person(){}
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((birth == null) ? 0 : birth.hashCode());
result = prime * result + gender;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
person other = (person) obj;
if (age != other.age)
return false;
if (birth == null) {
if (other.birth != null)
return false;
} else if (!birth.equals(other.birth))
return false;
if (gender != other.gender)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "person [name=" + name + ", age=" + age + ", gender=" + gender + ", birth=" + birth + "]";
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
}
对象序列化 ObjectOutputStream:将对象序列化到磁盘上
/**
* ObjectOutputStream :对象序列化
* 作用:将对象序列化编程字节序列
*
*/
public class ObjectOutputStreamDemo01 {
public static void main(String[] args) {
ObjectOutputStream oos=null;
try {
oos=new ObjectOutputStream(new FileOutputStream("Object.txt"));
//创建一个对象
person p=new person("zs",23,'男',new Date());
System.out.println(p);
//将对象序列化到硬盘上
oos.writeObject(p);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
oos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
反序列化 ObjectInputStream:将字符序列反序列化成对象
/**
* ObjectInputStream
* 作用:将字节序列返序列化为对象
*/
public class ObjectInputStreamDemo01 {
public static void main(String[] args) {
ObjectInputStream ois=null;
try {
ois=new ObjectInputStream(
new FileInputStream("Object.txt"));
person p=(person) ois.readObject();
System.out.println(p);
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
ois.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public class TestPerson {
public static void main(String[] args) throws Exception {
ObjectOutputStream oos=null;
ObjectInputStream ois=null;
try {
oos=new ObjectOutputStream(
new FileOutputStream("obj1.txt"));
ois=new ObjectInputStream(
new FileInputStream("obj1.txt"));
person p=new person("gyy",15,'女',new Date());
oos.writeObject(p);
person p1=(person) ois.readObject();
System.out.println(p==p1);
/*结果为false 说明序列化前的对象与反序列化对象回来的不是一个对象 */
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
oos.close();
ois.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
8.数据流(DataOutputStream/DateInputStream)
提供了一些可以直接写出/读取八大基本数据类型的方法,还提供了writeUTF(String s) / readUTF();
数据输入流没什么可说的,就是提供了相应的基本数据类型的方法。
FateInputStream 数据输入流
/**
* 数据输入流:FateInputStream
* 继承自FileInputStream
* 提供了基本数据类型的读取方法
*
*/
public class DateInputStreamDemo01 {
public static void main(String[] args) {
DataInputStream dis=null;
try {
dis=new DataInputStream(
new FileInputStream("file1.txt"));
System.out.println(dis.readByte());
System.out.println(dis.readLong());
//System.out.println(dis.readChar());
System.out.println(dis.readBoolean());
System.out.println(dis.readDouble());
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
DataOutputStream 数据输出流,里面涉及了无符号无符号右移这个问题
/**
* 数据输出流 DataOutputStream
* 继承自FileOutputStream
* 扩展了一下功能 如可以直接写基本数据类型的方法
*
*writeInt(int v)
* public final void writeInt(int v) throws IOException {
* out.write((v >>> 24) & 0xFF);
* out.write((v >>> 16) & 0xFF);
* out.write((v >>> 8) & 0xFF);
* out.write((v >>> 0) & 0xFF);
* incCount(4);
* }
* 底层调用的是OutputStream中的write(int b)方法,这个方法传递的是低八位的int;
*
* 可见writeInt(int v)不过是通过4次的移位,把int类型的4个字节分别移动到低8位(转成byte类型)上传递;
*
* &0xFF是将8位的数据转化为32位的int类型。
*
* writeUTF(String s)也是类似的,先判断字符占几个字节,再通过字节数决定移位次数。
*
*>>>:无符号右移 针对基本数据类型的二进制
* 数据>>>位数
*>>有符号右移
* 正数头部补0
* 负数头部补1
* <<左移
* 硬性左移指定位数 尾部补0可能变换符号 如00000000 00000000 00000000 10000000 左移24位 数据<<24 就变成10000000
* 00000000 00000000 00000000 就变成负数了.
* &位运算 与 二进制对比 一0则0 双一为1 结果是把二进制转换成自定义类型
* |位运算 或 二进制对比 遇1则1 双0为0
* ^位运算 非 二进制对比 同0异1
* dos.writeInt(1025);
* 是将int类型的二进制数据依次用低八位取出 依次将32位二进制一一向右移动24.16.8.0位 然后取低八位 也就是将四个八位二进制依次写出
* dos.writeLong(1025);
* 类比int 只是将long的64位二进制依次向右移动取出每节8位二进制存到一个数组中
* writeBuffer[0] = (byte)(v >>> 56);/将long类型强转成byte类型 也就是将long数据有序的拆成一个byte数组
writeBuffer[1] = (byte)(v >>> 48);
writeBuffer[2] = (byte)(v >>> 40);
writeBuffer[3] = (byte)(v >>> 32);
writeBuffer[4] = (byte)(v >>> 24);
writeBuffer[5] = (byte)(v >>> 16);
writeBuffer[6] = (byte)(v >>> 8);
writeBuffer[7] = (byte)(v >>> 0);
out.write(writeBuffer, 0, 8);//将整个数组按一节八位二进制依次输出
incCount(8);
dos.writeUTF(String s)他存的是字符串 首先将每个字符拆开 判断字符长度 字母和汉字可能存的长度不同
然后将长度和偏移量一起存入数组 但是写出时比想象中的字符长度多两个字节
*
*/
public class DataOutputStreamDemo01 {
public static void main(String[] args) {
DataOutputStream dos=null;
try {
dos=new DataOutputStream(
new FileOutputStream("file1.txt"));
//dos.write(10);
dos.writeByte(10);
dos.writeLong(10);
//dos.writeChars("lalala");
dos.writeBoolean(true);
dos.writeDouble(3.14);
dos.writeChar('我');
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
dos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
9.其他流
System.out:为PrintStream类型,代表标准输出流,默认的数据输出是控制台
out:是System类的一个静态成员变量
PrintStream ps = System.out;
System.setOut(new PrintStream("out.txt")); //更改输出目的地
System.err:为PrintStream类型,代表标准错误输出流,默认的数据输出是控制台(其实就是把输出的黑色字体换成了红色字体,也可以用于日志文件)
System.in:为InputStream类型,代表标准输入流,默认的数据源为键盘(就是读取键盘输入。scanner(控制台扫面类)常用)
10.NIO
我们使用InputStream从输入流中读取数据时,如果没有读取到有效的数据,程序将在此处阻塞该线程的执行。其实传统的输入里和输出流都是阻塞式的进行输入和输出。 不仅如此,传统的输入流、输出流都是通过字节的移动来处理的(即使我们不直接处理字节流,但底层实现还是依赖于字节处理),也就是说,面向流的输入和输出一次只能处理一个字节,因此面向流的输入和输出系统效率通常不高。
从JDk1.4开始,java提供了一系列改进的输入和输出处理的新功能,这些功能被统称为新IO(NIO)。新增了许多用于处理输入和输出的类,这些类都被放在java.nio包及其子包下,并且对原io的很多类都以NIO为基础进行了改写。新增了满足NIO的功能。
NIO采用了内存映射对象的方式来处理输入和输出,NIO将文件或者文件的一块区域映射到内存中,这样就可以像访问内存一样来访问文件了。通过这种方式来进行输入/输出比传统的输入和输出要快的多。JDk1.4使用NIO改写了传统Io后,传统Io的读写速度和NIO差不了太多。
用流完成的一个简单复制程序
package com.hyxy.fuzhiship; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class zuoye { public static void main(String[] args) throws IOException { BufferedInputStream dy=null; BufferedOutputStream chdy=null; try { dy=new BufferedInputStream( new FileInputStream("D://eclipse//eclipse workpasce//2018.10.21//bandicam 2018-10-21 09-07-57-058.mp4")); chdy=new BufferedOutputStream(new FileOutputStream("D://bandicam 2018-10-21 09-07-57-058.mp4")); long tim1=System.currentTimeMillis(); byte[] xdy=new byte[1024];//每次复制一点 int i=dy.read(xdy);//实际复制的长度 while((i=dy.read(xdy))!=-1){ chdy.write(xdy,0,i);//将数据存储到缓冲区内 //缓冲区有8k上限,所以只能8k8k慢慢输入输出 所以要用个循环 循环判断条件是是否还能复制 也就是 //判断dy.read(xdy)这是实际复制长度 只要这个值不等于-1,则还有数据没有复制完成 如果等于-1则 //复制完成 终止循环 //chdy.flush();将缓冲区内的数据强行写出 } long tim2=System.currentTimeMillis(); System.out.println(tim2-tim1); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ try { dy.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }