《java基础与案例开发详解》(四)

来源:互联网 发布:python可以建网站吗 编辑:程序博客网 时间:2024/05/18 21:42

      • Java对文件和目录的操作
      • File类的构造
      • File类常用属性和方法
      • 对文件目录操作
      • 浏览目录中文件和子目录的方法
      • Java IO原理
      • Java中流分类
      • IO流的抽象类
        • InputStream常用方法
        • OutStream常用方法
        • Reader常用方法
        • Writer常用方法
      • 文件流
      • 缓冲流
        • 缓冲区流的分类
        • 转换流
        • 数据流
        • 对象流
        • 序列化版本
        • 随机存取文件流

Java对文件和目录的操作

在java中,对物理存储介质中的文件和目录进行了抽象,使用java.io.File类代表存储介质中的文件和目录。

File类的构造

通常用文件或者目录路径来构造,这个路径可以是绝对路径也可以是相对路径。

File类常用属性和方法

这里写图片描述

这里写图片描述

代码示例:

    File f = new File("E:\\Hello.java");            System.out.println(f.exists());//文件是否存在            System.out.println(f.isFile());//是文件吗            System.out.println(f.isDirectory());//是目录            System.out.println(f.getName());//获取文件名称            System.out.println(f.getAbsolutePath());//获取文件绝对地址            try {                System.out.println(f.getCanonicalPath());//获取绝对路径的规范表示            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }            System.out.println(f.lastModified());//获取文件最后修改时间            System.out.println(f.length());//获取文件大小,按字节算。        }

对文件、目录操作

这里写图片描述

浏览目录中文件和子目录的方法

这里写图片描述

代码如下:

//遍历文件是用的是递归        public static void ergodic(File f) {            File[] files = f.listFiles();           for(File file:files) {               System.out.println(file.getName());               if(file.isDirectory()) {                   ergodic(file);               }           }        }        //递归删除        public static void delete(File f) {            if(f.isFile()) {                f.delete();//如果是文件直接删除            }else {//如果是目录,需要先删除子文件                File[] files = f.listFiles();                for(File file:files) {                 delete(file);                }            }    f.delete();//删除完子文件之后,最后删除文件夹。

Java IO原理

在java程序中要想获取数据源中的数据,需要在程序和数据源之间建立一个数据输入的通道,这样就能从数据源中获取数据了,如果Java程序中把数据写到数据源中,也需要在程序和数据源之间建立一个数据输出的通道。在java程序中创建流对象时,会自动建立这个数据输入通道,而创建输出流对象时会自动建立这个数据输出通道。

IO原理图

Java中流分类

1) 数据流方向

  • 输入流
  • 输出流

    2) 数据传输单位

  • 字节流

  • 字符流

    3)流功能

  • 节点流:用于直接操作数据源的流。

  • 过滤流:也叫过滤流,是对一个已经存在的流的封装和连接,提供更加强大的读写功能。

IO流的抽象类

这里写图片描述

InputStream常用方法

                 //is.read();//从输入流中读取数据的下一个字节,返回读到的字节值(这个字节值就是一个字节的十进制的值,比如读a,a是一个字符,就占一个字节,所以返回的值就是97),若遇到流结尾返回-1.                 //is.read(b);//从输入流中读取b.len个字节数据并存储到缓冲区b数组中,返回是实际读到的字节数。                 //is.read(b, 0,b.length);//读取b.len个字节的数据,并从数组b的off位置开始写到数组中。

OutStream常用方法

                //out.write(b);//将b.length个字节从指定的byte数组写入到输出流                out.write(97);//指定将规定的一个字节数写入此输出流,(相对应的你写97,就是a)                //out.write(b, 0, b.length);//将指定的byte数组从偏移量off开始的len个字节写入此输出流。                out.flush();//刷新此输出流,并强制写出所有缓冲的输出字节。

Reader常用方法

主要以字符为单位进行处理。

            a = reader.read();//读取单个字符,(请注意一个汉字就是一个字符),返回作为整数读取的字符,如果到末尾就是-1            //reader.read(c);//将字符读入数组中,返回读取的字符数            //reader.read(c, 0, c.length);//读取len个字符数据,并从数组的off位置开始写到数组中。

Writer常用方法

主要以字符为单位进行处理。

            //w.write("老枪好帅");//写入字符串            w.write(c);//写入字符数组            w.write(c);//写入单个字符            w.write(c, a,c.length);//写入字符数据的一部分            w.write("", 0, len);//写入字符串的一部分            w.flush();//刷新该流的缓冲,全部写出来。

文件流

  1. FileInputStream
  2. FileOutputStream
  3. FileReader
  4. FileWriter
            InputStream in = new FileInputStream("");//传入相应处理的文件路径            OutputStream out  = new FileOutputStream("");            Reader reader = new FileReader("");            Writer writer = new FileWriter("");

上面是抽象的Io的实现,你在处理时,根据不同的文件进行相应的选择处理。

缓冲流

为了提高数据的读写速度,java提供了带缓冲流的流类,在使用这些带缓冲功能的流类,它会在内部创建缓冲区数组,在读取字节(创建字节数组)或者字符(创建字符数组)的时候,会把数据先读到缓冲区,然后在返回,在写入字节或者字符,会先把数据填充到内部缓冲区,然后一次性写到目标数据源中。

缓冲区流的分类

  1. BufferInputStream
  2. BufferOutputStream
  3. BufferReader
  4. BufferWriter

缓冲流属于过滤流,也就是说缓冲流不直接操作数据源,而是对直接操作数据源节点流的一个包装,增加功能。

节点流和过滤流区别

下面给出操作字节字符缓冲流代码:

    BufferedOutputStream bos  = null;        OutputStream out  = null;        InputStream in  = null;        BufferedInputStream bis  = null;        int i = 0;        try {             in = new FileInputStream("E:\\Hello.java");             out  =new FileOutputStream("E:\\Hello1.java");             bos = new BufferedOutputStream(out);             bis = new BufferedInputStream(in);            while((i = bis.read())!=-1){//这里是从缓冲区上一次读一个                bos.write(i);            }            bos.flush();            } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }finally {            if(bos!=null) {                try {                    bos.close();                } catch (IOException e) {                    // TODO Auto-generated catch block                    e.printStackTrace();                }            }            if(bis!=null) {                try {                    bis.close();                } catch (IOException e) {                    // TODO Auto-generated catch block                    e.printStackTrace();                }            }        }
 Reader r = null;      Writer w = null;      BufferedReader br = null;      BufferedWriter bw = null;      String str = null;     try {         r = new FileReader("E:\\Hello.java");         w = new FileWriter("E:\\Hello2.java");         br = new BufferedReader(r);         bw = new BufferedWriter(w);         while((str = br.readLine())!=null) {//这个操作字符流和原来不一样,一次读一行。             bw.write(str);         }         bw.flush();    } catch (FileNotFoundException e) {        // TODO Auto-generated catch block        e.printStackTrace();    } catch (IOException e) {        // TODO Auto-generated catch block        e.printStackTrace();    }finally {        if(br!=null) {            try {                br.close();            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }        if(bw!=null) {            try {                bw.close();            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }    }

在实际操作中,还是推荐使用第二种操作文件更方便。

注意在使用过滤流的过程中,它是对底层的节点流封装,所以在我们手动关闭了过滤流的时候,就不需要手动关闭节点流。

转换流

有时我们需要在字节流和字符流中进行转化,使用到InputStream Reader 和OutputStreamWriter。

  1. InputStream Reader
        //InputStreamReader isr = new InputStreamReader(in);//传入字节流,转化成默认字符流读。        //InputStreamReader isr1 = new InputStreamReader(in, charsetName);//传入字节流,按照指定编码转化为字符流读。

用于将字节流中读取到的字节按字符集解码成字符。

  1. OutputStreamWriter

用于将要写入到字节流中的字符按照置顶字符集编码成字节。

        OutputStreamWriter osw = new OutputStreamWriter(out);//传入字节流,转化成默认的字符流写入        OutputStreamWriter osw1 = new OutputStreamWriter(out, cs);//传入字节流,按照指定编码转化为字符流写 

例子:

    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        //从键盘上获取的是字符,通过转换流来变成字符流操作,更便捷。        try {            while((content = br.readLine())!=null) {                if(content.equalsIgnoreCase("e")) {                    break;                }else {                    System.out.println(content.toUpperCase());                }            }        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }

数据流

用于操作Java语言的基本数据类型的数据,主要有这两个类:DataInputStream和DataOutputStream。

这里写图片描述

代码如下:

DataOutputStream dis  = null;        DataInputStream is = null;        InputStreamReader isr = null;        BufferedReader br = null;        String content = null;        try {            dis= new DataOutputStream(new FileOutputStream("E:\\data.txt"));            dis.writeInt(87);            dis.writeBoolean(true);            dis.writeUTF("中国");        } catch (FileNotFoundException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }        try {            is = new DataInputStream(new FileInputStream("E:\\data.txt"));            int a = is.readInt();//需要注意的是这里在读取的时候必须使用DataInputStream            //否则出现乱码            boolean b = is.readBoolean();            String c = is.readUTF();            System.out.println(a);            System.out.println(b);            System.out.println(c);        } catch (FileNotFoundException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }

对象流

ObjectOutputStream和ObjectInputStream类是用来存储和读取基本类型数据或对象的过滤流,它的强大之处就是可以把Java中的对象写到数据源中,也能把对象从数据源中还原回来。用ObjectOutputStream类保存基本数据类型或者对象叫序列化,用ObjectInputStream类读取基本数据类型或者对象的机制叫做反序列化。ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量。

序列化:

//定义一个可以序列化的类public class Student implements Serializable{   private String name;   private   transient int age;//不能序列化的属性   private String sex;public Student(String name, int age, String sex) {    super();    this.name = name;    this.age = age;    this.sex = sex;}public Student() {    super();    // TODO Auto-generated constructor stub}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;}public String getSex() {    return sex;}public void setSex(String sex) {    this.sex = sex;}}

在这个序列化中,它的成员变量不能被保存和读取。

序列化的好处在于,它可以将任何实现Serializable接口的对象转化为字节数据。这些数据可以保存在数据源中,以后仍可以恢复成原来的对象状态,即使这些数据通过网络也可以还原。

序列化和反序列化代码:

/*ObjectOutputStream out = null;            try {                 out = new ObjectOutputStream(new FileOutputStream("E:\\data2.txt"));                 out.writeObject(new Student("laoqiang", 23, "男"));                 out.flush();            } catch (FileNotFoundException e) {                // TODO Auto-generated catch block                e.printStackTrace();            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }finally {                if(out!=null) {                    try {                        out.close();                    } catch (IOException e) {                        // TODO Auto-generated catch block                        e.printStackTrace();                    }                }            }*/            ObjectInputStream ois = null;            try {                ois = new ObjectInputStream(new FileInputStream("E:\\data2.txt"));            Student s = (Student) ois.readObject();            System.out.println(s.toString());            } catch (FileNotFoundException e) {                // TODO Auto-generated catch block                e.printStackTrace();            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            } catch (ClassNotFoundException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }finally {                if(ois!=null) {                    try {                        ois.close();                    } catch (IOException e) {                        // TODO Auto-generated catch block                        e.printStackTrace();                    }                }            }

序列化版本

凡是实现Serializable接口的类都有一个表示序列化版本标识的静态变量。用来表明类的不同版本之间的兼容性,默认情况下,如果没有直接定义这个变量,它的值是由java运行时根据累的内部细节自动生成,对于不同额java编译器,它的值可能相同也可能不相同。如果对类的源代码修改或者重新编译,该值会变。如果这时还用老版本的类来反序列化的,就会出错。我们建议,用户应该自己定一个,并且给它相应的值。

显式定义serialVersionUID的用途:

  1. 在某些场合,希望类的不同版本对序列化兼容,因此我们需要保证serialVersionUID相同。
  2. 在某些场合,不希望类的不同版本对序列化兼容,因此我们需要保证具有不同的serialVersionUID。

随机存取文件流

RandomAcessFile是一种特殊的流类,它可以在文件的任何地方读取或者写入数据,这是因为它提供文件指针,可以指定下次读取字节的位置。通过seek,可以将文件指针移动到文件内部的任意字节的位置。

随机文件存取流,提供两种方式可读(r),或者是同时读写(rw),不可以单独写。

随机存取文件执限于磁盘文件,不能访问来自网络,或者内存映像的流。

下面的是多线程下载文件:

   private static final String URL= "www.baidu.com" ;       private static int contentlength = 0;//资源的总字节数       private static int threadnumber = 10;//线程个数       private static int everythreaddownoadlength = 0;//每一个线程的需要下载的长度        private static int extra = 0;       private static int start,end = 0;//记录每一个线程的开始和结束       private static File file = new File("E:\\data3.txt");        public static void main(String[] args) {            try {                URL url = new URL(URL);                URLConnection conn = url.openConnection();                contentlength = conn.getContentLength();                everythreaddownoadlength = contentlength/threadnumber;//计算每个线程需要下载长度                extra = contentlength%threadnumber;//如果在上面计算每一个线程下载长度不能整除。                for(int i = 0;i<threadnumber;i++) {                    start = threadnumber*i;                    end = start+everythreaddownoadlength-1;//0~9,10~19,20~29                    if(i ==  threadnumber-1) {//这里的意思,将不能平均的下载量全加给最后一个,因为剩余的不够分给一个线程                        end = end+extra;                    }                    MyThread mt = new MyThread(start, end, file,URL);                    Thread t = new Thread(mt);                    t.start();                }            } catch (MalformedURLException e) {                // TODO Auto-generated catch block                e.printStackTrace();            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }}class MyThread implements Runnable{    private int start,end,length = 0;    private File endfile = null;    private String url = null;    private BufferedInputStream bis = null;    private byte[] b = new byte[2048];    public MyThread(int start, int end, File endfile,String url) {        super();        this.start = start;        this.end = end;        this.endfile = endfile;        this.url = url;    }    @Override    public void run() {        // TODO Auto-generated method stub        URL url1  =null;        URLConnection conn = null;        try {            url1 = new URL(url);             conn = url1.openConnection();        } catch (MalformedURLException e1) {            // TODO Auto-generated catch block            e1.printStackTrace();        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }        RandomAccessFile raf = null;        try {             raf = new RandomAccessFile(endfile, "rw");             raf.seek(start);//将文件指针移动到线程开始的位置             bis = new BufferedInputStream(conn.getInputStream());             while((length=bis.read(b))!=-1) {                 raf.write(b, 0, length);//将资源写到目的的文件。             }             System.out.println(Thread.currentThread().getName()+"下载好了");        } catch (FileNotFoundException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }finally {            if(raf!=null) {                try {                    raf.close();                } catch (IOException e) {                    // TODO Auto-generated catch block                    e.printStackTrace();                }            }            if(bis!=null) {                try {                    bis.close();                } catch (IOException e) {                    // TODO Auto-generated catch block                    e.printStackTrace();                }            }        }

大致思路:获取网路哦资源总长,按照线程个数,让每一个线程从不同开始位置下载。

阅读全文
0 0
原创粉丝点击