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

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();
                }
            }
        }

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

1.1 Java语言发展简史2 1.2 认识Java语言3 1.2.1 Java语言特性3 1.2.2 JavaApplet4 1.2.3 丰富的类库4 1.2.4 Java的竞争对手5 1.2.5 Java在应用领域的优势7 1.3 Java平台的体系结构7 1.3.1 JavaSE标准版8 1.3.2 JavaEE企业版10 1.3.3 JavaME微型版11 1.4 JavaSE环境安装和配置12 1.4.1 什么是JDK12 1.4.2 JDK安装目录和实用命令工具介绍12 1.4.3 设置环境变量13 1.4.4 验证配置的正确性14 1.5 MyEcilpse工具介绍JavaSE环境安装和配置15 1.6 本章练习16 第2章 2.1 什么是程序18 2.2 计算机中的程序18 2.3 Java程序19 2.3.1 Java程序中的类型19 2.3.2 Java程序开发三步曲21 2.3.3 开发Java第一个程序21 2.3.4 Java代码中的注释23 2.3.5 常见错误解析24 2.4 Java类库组织结构和文档27 2.5 Java虚拟机简介28 2.6 Java技术两种核心运行机制29 2.7 上机练习30 第3章 3.1 变量32 3.1.1 什么是变量32 3.1.2 为什么需要变量32 3.1.3 变量的声明和赋值33 3.1.4 变量应用实例33 3.2 数据的分类34 3.2.1 Java中的八种基本数据类型34 3.2.2 普及二进制36 3.2.3 进制间转换37 3.2.4 基本数据类型间转换38 3.2.5 数据类型应用实例38 3.2.6 引用数据类型39 3.3 关键字.标识符.常量39 3.3.1 变量命名规范39 3.3.2 经验之谈-常见错误的分析与处理40 3.3.3 Java标识符命名规则41 3.3.4 关键字42 3.3.5 常量42 3.4 运算符43 3.4.1 算术运算符43 3.4.2 赋值操作符45 3.4.3 关系操作符47 3.4.4 逻辑操作符48 3.4.5 位操作符49 3.4.6 移位运算符49 3.4.7 其他操作符50 3.5 表达式52 3.5.1 表达式简介52 3.5.2 表达式的类型和值52 3.5.3 表达式的运算顺序52 3.5.4 优先级和结合性问题52 3.6 选择结构54 3.6.1 顺序语句54 3.6.2 选择条件语句54 3.6.3 switch结构59 3.6.4 经验之谈-常见错误的分析与处理65 3.6.5 Switch和多重if结构比较66 3.7 循环语句66 3.7.1 While循环67 3.7.2 经验之谈-常见while错误70 3.7.3 do-while循环72 3.7.4 for循环74 3.7.5 经验之谈-for常见错误76 3.7.6 循环语句小结78 3.7.7 break语句79 3.7.8 continue语句82 3.8 JavaDebug技术84 3.9 本章练习85 第4章 4.1 一维数组90 4.1.1 为什么要使用数组90 4.1.2 什么是数组91 4.1.3 如何使用数组92 4.1.4 经验之谈-数组常见错误97 4.2 常用算法98 4.2.1 平均值,最大值,最小值98 4.2.3 数组排序102 4.2.3 数组复制103 4.3 多维数组105 4.3.1 二重循环105 4.3.2 控制流程进阶107 4.3.3 二维数组111 4.4 经典算法113 4.4.1 算法-冒泡排序113 4.4.2 插入排序115 4.5 增强for循环116 4.6 本章练习117 第5章 5.1 面向过程的设计思想120 5.2 面向对象的设计思想120 5.3 抽象121 5.3.1 对象的理解121 5.3.2 Java抽象思想的实现122 5.4 封装124 5.4.1 对象封装的概念理解124 5.4.2 类的理解125 5.4.3 Java类模板创建125 5.4.4 Java中对象的创建和使用127 5.5 属性130 5.5.1 属性的定义130 5.5.2 变量131 5.6 方法132 5.6.1 方法的定义132 5.6.2 构造方法135 5.6.4 方法重载138 5.6.5 自定义方法138 5.6.6 系统提供方法139 5.6.7 方法调用140 5.6.8 方法参数及其传递问题144 5.6.9 理解main方法语法及命令行参数147 5.6.1 0递归算法147 5.7 this关键字148 5.8 JavaBean149 5.9 包150 5.9.1 为什么需要包?150 5.9.2 如何创建包151 5.9.3 编译并生成包:151
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值