• 黑马程序员——Java基础--IO流(一)---File类以及其他流对象


    ------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

    一、File类

      File类是将文件系统中的文件和文件夹封装成了对象。提供了更多的属性和行为可以对这些文件和文件夹进行操作。这些是流对象办不到的,因为流只操作数据。File对象可以作为参数传递给流对象的构造函数。File 类的实例是不可变的;也就是说,一旦创建,File 对象表示的抽象路径名将永不改变。 

    1、File类中的方法

    (1)、File类的构造方法以及字段

    1 new   File(String pathname);//通过给定路径名创建一个File对象
    2 new   File(String parent, String child);//通过父目录和文件名创建File对象
    3 new    File(File parent,Stirng child);//通过把文件目录路径封装对象和文件名创建File对象
    4 String  separator:表示目录分隔符,可以跨平台使用。在 UNIX 系统上,此字段的值为 '/';在 Microsoft Windows 系统上,它为 '\'。

    创建File文件对象的三种方式: 

     1 import java.io.*;
     2 class FileDemo 
     3 {
     4     public static void main(String[] args) 
     5     {
     6         //可以将一个已存在的,或者不存在的文件或者目录封装成file对象
     7         File file=null;
     8         try
     9         {
    10             ////创建File对象方式一:直接指定字符串路径名
    11             file=new File("E:\Demo.txt");
    12 
    13             //创建File对象方式二:将目录和文件名封装成字符串,传参进去
    14             String parent="E:\";
    15             String child="Demo.txt";
    16             file=new File(parent,child);
    17 
    18             //创建File对象方式三:将目录封装成文件对象和文件名创建对象。这种方式更加灵活,降低了文件于父目录的关联性
    19             File parent=new File("D:\");
    20             String child="Demo.txt";
    21             file=new File(parent,child);
    22 
    23         }
    24         catch (Exception e)
    25         {
    26             e.printStackTrace();
    27         }
    28     }
    29 }

     (2)、File类的成员方法

    1.创建

    1 boolean    createNewFile();//在指定位置上创建文件,如果该文件已经存在,则不创建,返回false。该方法和输出流不一样,输出流对象一建立就创建文件,而且文件已经存在会覆盖。
    2 boolean    mkdir();//创建文件夹,只能一级
    3 boolean    mkdirs()//创建多级文件夹文件夹

     2.删除

    1 boolean    delete();//删除文件目录或者文件,如果删除失败返回false。如果文件正字被使用,则删除不了,返回false。
    2 void    deleteOnExit();//在程序退出时删除指定文件

     注意:在删除文件夹时,必须保证这个文件夹中没有任何内容,才可以将该文件夹用delete删除。

      Window的删除动作是从里往外删的。而java删除文件不走回收站,要谨慎操作。

    3.判断

    1 boolean    canExecute();//判断文件是否是可执行文件
    2 boolean    exists();//判断文件是否存在
    3 boolean    isFile();//判断是否是文件
    4 boolean    isDirectory();//判断是否是目录
    5 boolean    isHidden();//判断是否是隐藏文件
    6 boolean    isAbsolute();//判断是否是绝对路径

     注意:在判断文件对象是否是文件或者目录时,必须要先判断该文件对象封装的内容是否存在。通过exists判断。

    4.获取信息

     1 String    getName();//获取文件名称
     2 String    getPath();//获取文件相对路径
     3 String    getParent();  //获取文件父目录。返回的是绝对路径中的父目录。如果获取的是相对路径,返回null。如果相对路径中有上一层目录,那么该目录就是返回结果。
     4 String    getAbsolutePath();//获取文件绝对路径
     5 Long    lastModified();//获取最后一次文件修改时间
     6 Long    length();//获取文件大小
     7 File[]    listRoots();//获取全部系统根目录也就是盘符
     8 Stirng[]    list();//获取指定目录下的文件以及文件夹的名称,包含隐藏文件。调用list方法的File对象中封装的必须是目录,否则会产生NullPointerException,如果访问的是系统级目录也会发生空指针异常,如果             目录存在但是没有内容,会返回一个数组,但是长度为0
     9 
    10 String[]    list(FilenameFilter filter);//获取指定目录中满足指定过滤器的文件和目录。其中FilenameFilter是个接口,里面有个方法boolean  accept(File dir, String  name),返回的是:当且仅当该名                          称应该包含在文件列表中时2返回true,否则返回false。
    11 
    12 File[]    listFiles()//获取指定目录下的文件以及文件夹的对象,有了对象就可以操作更多的方法,而list方法只能获取当前目录下的文件和文件夹的名称,这是两者最大的不同之处。同时,list()方法返回的是没有完整路径只有文件名(相对路径名),而listFiles()方法返回的是所有完整路径的的文件名(绝对路径)。System.out.println(File)时,会调用File的toString方法,而File的toString方法会返回getPath()取得的字符串路径。

    13 File[]  listFiles(FilenameFilter filter);//返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录

     5.重命名

    1 boolean    renameTo(File dest);//重命名此文件名

     代码演示:

     1 import java.io.*;
     2 class FileDemo 
     3 {
     4     public static void main(String[] args) throws Exception
     5     {
     6         create();
     7         is();
     8         get();
     9         method();
    10         method_1();
    11     }
    12     public static void create()throws Exception{
    13         File parent=new File("E:\");
    14         File file=new File(parent,"黑马.txt");
    15         if(!file.exists())
    16             sop("文件是否创建成功:"+file.createNewFile());//指定的位置上创建了一个文件
    17         File dir=new File(parent,"黑马\安卓\JAVA\JVAVAEE\IOS\PHP");
    18         sop("路径是否创建成功"+dir.mkdirs());//创建多级目录,如果创建一级目录用mkdir就可以
    19         sop("路径是否被删除"+dir.delete());//多级目录被删除的是最里面的那个目录,PHP文件夹被删除了
    20 
    21     }
    22     public static void is()throws Exception{
    23         File file=new File("E:\HelloJava.txt");
    24         sop("是否存在:"+file.exists());
    25         if(!file.exists())
    26             file.createNewFile();//指定的位置上创建了一个文件
    27         sop("是否是可执行:"+file.canExecute());        
    28         sop("是否是文件:"+file.isFile());
    29         sop("是否是路径:"+file.isDirectory());
    30         sop("是否隐藏:"+file.isHidden());
    31         sop("是否是绝对路径:"+file.isAbsolute());
    32         //上面的判断返回的都是false,因为判断的file对象根本就不存在,
    33         //所以切记在判断是否是文件还是目录的时候一定要先判断该文件对象封装的内容是否存在。
    34     }
    35     public static void get()throws Exception{
    36         File file=new File("E:\黑马\安卓\java.txt");
    37         sop("文件大小:"+file.length());
    38         sop("文件名称:"+file.getName());
    39         sop("文件相对路径:"+file.getPath());//该方法返回的是绝对路径中的父目录。
    40         //如果获取的是相对路径,返回null。如果相对路径中有上一层目录那么该目录就是返回结果。
    41         sop("文件父目录:"+file.getParent());
    42         sop("文件绝对路径:"+file.getAbsolutePath());
    43         sop("文件最后一次修改时间:"+file.lastModified());
    44     }
    45     public static void method()throws Exception{
    46         File file=new File("D:\Demo.java");
    47         File[] dir=file.listRoots();//获取盘符
    48         for(File f:dir){
    49             sop(f);
    50         }
    51         file=new File("D:\");//必须是路径
    52         String[] s=file.list();//获取路径下的所有文件名,包括隐藏文件,相对路径文件名
    53         for(String f:s){
    54             sop(f);
    55         }
    56 
    57         File[] f=file.listFiles();//获取路径下的所有文件名,包括隐藏文件,绝对路径文件名
    58         for(File name:f){
    59             sop(name);//name.getName()和list获取的信息一样,都是相对路径
    60         }
    61 
    62     }
    63     public static void method_1()throws Exception{
    64         File file=new File("D:\");//获取D盘目录下后缀名为.java的文件。
    65         String[] dir=file.list(new FilenameFilter(){
    66             public boolean accept(File dir,String name){//匿名内部类,覆盖了accept方法
    67                 return name.endsWith(".java");
    68             }
    69         });
    70         for(String name:dir){
    71             sop(name);    
    72         }
    73     }
    74     public static void rename()throws Exception{
    75         //下面的语句可以重新命名文件名
    76         File f1 = new File("c:\Test.java");
    77         File f2 = new File("d:\hahah.java");
    78         sop("rename:"+f1.renameTo(f2));//这种情况有点像剪切文件一样。把f1的文件剪切到了f2目录下。
    79 
    80     }
    81     public static void sop(Object obj){
    82         System.out.println(obj);
    83     }
    84 }

     需求:获取D盘目录下后缀名为.java的文件。

     1 import java.io.*;
     2 class MyFilenameFilter implements FilenameFilter
     3 {
     4     private String suffix;
     5     MyFilenameFilter(String suffix){        
     6         this.suffix=suffix;
     7     }
     8     public boolean accept(File dir,String name){//复写accept方法
     9         return name.endsWith(suffix);
    10     }
    11 }
    12 class MyFilenameFilterDemo
    13 {
    14     public static void main(String[] args)throws Exception 
    15     {
    16         File file=new File("D:");
    17         String[] dir=file.list(new MyFilenameFilter(".txt"));//只需要往这里传值就可以了。
    18         for(String name:dir){
    19             System.out.println(name);
    20         }
    21     }
    22 }

    需求:获取D盘目录下的隐藏文件。

     1 import java.io.*;
     2 class HiddenFile
     3 {
     4     public static void main(String[] args) throws Exception
     5     {
     6         File file=new File("C:");
     7         File[] dir=file.listFiles(new FilenameFilter(){
     8             public boolean accept(File dir,String name){
     9                 return dir.isHidden();
    10             }
    11         });
    12         for(File name:dir){
    13             System.out.println(name);
    14         }
    15     }
    16 }

     2、递归

    递归就是:函数自身调用自身。

    什么时候用递归呢?

    当一个功能被重复使用,而每一次使用该功能时的参数不确定,都由上次的功能元素结果来确定。

    简单说:功能内部又用到该功能,但是传递的参数值不确定。(每次功能参与运算的未知内容不确定)。

    递归的注意事项:

    1:一定要定义递归的条件。

    2:递归的次数不要过多。容易出现 StackOverflowError 栈内存溢出错误。

    其实递归就是在栈内存中不断的加载同一个函数。

    需求:对指定目录进行所有内容的列出(包含子目录中的内容)

     1 import java.io.*;
     2 class FileListDemo 
     3 {
     4     public static void main(String[] args)throws Exception 
     5     {
     6         File file=new File("D:\mystudy\java");
     7         show(file);
     8     }
     9     public static void show(File file){
    10         System.out.println("........"+file);
    11         //获取指定目录下当前的所有文件夹或者文件对象
    12         File[] name=file.listFiles();
    13         for(File dir:name){
    14             if(dir.isDirectory())//如果是文件夹在进去递归遍历
    15                 show(dir);
    16             else
    17                 System.out.println(dir);
    18         }
    19     }
    20 }

    需求:利用递归求6的二进制值。

     1 public class Demo{
     2        public static void main(String[] args){
     3             toBin(6);
     4       }
     5 
     6        public static void toBin(int num){
     7              if(num > 0){
     8                   toBin(num/2);
     9                   System.out.print(num%2);
    10             }
    11       }
    12 }

    需求:利用递归求1到10的和。

     1 public class Demo{
     2        public static void main(String[] args){
     3              int sum = getSum(10);
     4             System.out.println(sum);
     5       }
     6 
     7        public static int getSum(int num){
     8              if(num == 1)
     9                    return 1;
    10              return num + getSum(num - 1);
    11       }
    12 }

    需求:删除一个带内容的目录。 

     1 import java.io.*;
     2 class DeleteFile 
     3 {
     4     public static void main(String[] args) throws Exception
     5     {
     6         File file=new File("E:\javatest");
     7         removeDir(file);
     8     }
     9     public static void removeDir(File file){
    10         File[] dir=file.listFiles();
    11         for(File name:dir){
    12             if(name.isDirectory()){//判断是否是路径,如果是进入递归的遍历
    13                 removeDir(name);                
    14             }
    15             else
    16                 System.out.println(name+"... "+name.delete());            
    17         }
    18         System.out.println(file+"::"+file.delete());//最后把文件夹删掉
    19     }
    20 }

    需求:将一个指定目录下的java文件的绝对路径,存储到一个文本文件中。建立一个java文件列表文件。 

    思路:

    1,对指定的目录进行递归。

    2,获取递归过程所以的java文件的路径。

    3,将这些路径存储到集合中。

    4,将集合中的数据写入到一个文件中。

     1 import java.io.*;
     2 import java.util.*;
     3 class JavaFileList
     4 {
     5     public static void main(String[] args) throws Exception
     6     {
     7         File file=new File("E:\javatest");
     8         List<File> list=new ArrayList<File>();
     9         fileToList(file,list);
    10         listToFile(list);
    11     }
    12     //把符合条件的目录存放到List集合中
    13     public static void fileToList(File dir,List<File> list){
    14         File[] file=dir.listFiles();
    15         for(File name:file){
    16             if(name.isDirectory())
    17                 fileToList(name,list);
    18             else{
    19                 if(name.getName().endsWith(".java"))//文件名为.java的添加进集合中
    20                     list.add(name);
    21             }
    22         }
    23     }
    24 
    25     //集合中存放的数据写到指定文件内
    26     public static void listToFile(List<File> list)throws IOException{
    27         BufferedWriter bfw=new BufferedWriter(new FileWriter("E:\黑马.txt"));
    28         for(File name:list){
    29             bfw.write(name.getAbsolutePath());//获取文件的绝对路径名
    30             bfw.newLine();
    31             bfw.flush();
    32         }
    33         bfw.close();
    34     }
    35 }

     需求:将指定目录下所有.java文件拷贝到另一个目的中,并将扩展名改为.txt 

     1 //编写程序,将指定目录下所有.java文件拷贝到另一个目的中,并将扩展名改为.txt
     2 import java.io.*;
     3 class  CopyFile
     4 {
     5     public static void main(String[] args)throws Exception 
     6     {
     7         FileInputStream fis=null;
     8         FileOutputStream fos=null;
     9         File src=new File("E:\javatest\12");
    10         File des=new File("E:\安卓");
    11         if(!des.exists())
    12             des.mkdir();
    13         String[] dir=src.list(new FilenameFilter(){
    14             public boolean accept(File dir,String name){
    15                 return name.endsWith(".java");
    16             }
    17         });
    18 
    19         for(String name:dir){
    20             System.out.println(name);
    21             fis=new FileInputStream(new File(src,name));
    22             fos=new FileOutputStream(new File(des,name.replace(".java",".txt")));
    23             int len=0;
    24             byte[] buf=new byte[1024];
    25             while ((len=fis.read(buf))!=-1)
    26             {
    27                 fos.write(buf,0,len);
    28             }
    29         }
    30         fis.close();
    31         fos.close();
    32     }    
    33 }

    二、Properties类

    继承体系:

    Map集合

      |--Hashtable

        |--Properties

    用于属性配置文件,键和值都是字符串类型。

    Properties值Hashtable的子类。也就是说它具备map集合的特点。而且它里面存储的键值对都是字符串,没有泛型定义。是集合中和IO技术相结合的结合容器。该对象的特点就是:可以用于键值对形式的配置文件。那么在加载数据时需要数据有固定格式:键=值。

    特点:1:可以持久化存储数据。2:键值都是字符串。3:一般用于配置文件。

    1、Properties类中常用方法

    (1)、设置键和值。相当与Map集合中的put方法

    1 Object    setProperty(String key,String value);//添加元素

     (2)、获取

    1 String    getProperty(String key);//根据键获取值
    2 Set<String>    stringPropertyNames();//返回属性列表键集,存入Set集合

     (3)、加载和存入流

    1 void    load(InputStream in);//将字节读取流中的数据加载进集合中
    2 void    load(Reader reader);//JDK1.6才有,将字符读取流加载进集合
    3 void    store(OutputStream out,String comments);//将属性列表(键值对)写入输出流。comments属性列表的描述。可以不写。
    4 void    store(Writer writer,String comments);//JDK1.6才有。对应load(Reader)将属性列表(键值对)写入输出流。comments属性列表的描述。
    5 void    list(PrintStream out);//将属性输出到输出流中
    6 void    list(PrintWriter);//将属性输出到输出流中

     代码演示:

     1 import java.util.*;
     2 import java.io.*;
     3 class PropertiesDemo
     4 {
     5     public static void main(String[] args)throws Exception 
     6     {
     7         Properties pro=new Properties();
     8         pro.setProperty("6","小明");
     9         pro.setProperty("2","老王");
    10         pro.setProperty("3","赵四");
    11         pro.setProperty("5","黑马");
    12         method_1(pro);
    13         method_2();
    14         method_3();
    15     }
    16     public static void method_1(Properties pro){
    17         System.out.println(pro.getProperty("3"));
    18         //返回Set集合中存放的是Properties的键集
    19         Set<String> set=pro.stringPropertyNames();
    20         for(String key:set){
    21             System.out.println(key+"::"+pro.getProperty(key));
    22         }
    23     }
    24     //模拟load方法
    25     public static void method_2()throws Exception{
    26         Properties pro=new Properties();
    27         BufferedReader br=new BufferedReader(new FileReader("info.txt"));
    28         String line=null;
    29         while ((line=br.readLine())!=null)
    30         {
    31             String[] arr=line.split("=");            
    32             pro.setProperty(arr[0],arr[1]);
    33 
    34         }
    35         System.out.println(pro);
    36     }
    37     //对已有的配置文件中的信息进行修改
    38     //读取这个文件。并将这个文件中的键值数据存储到集合中。再通过集合对数据进行修改。load方法
    39     //再通过流将修改后的数据存储到文件中stroe方法
    40     public static void method_3()throws Exception{
    41         Properties pro=new Properties();
    42         FileReader fr=new FileReader("info.txt");        
    43         pro.load(fr);
    44         pro.setProperty("1","会更好");
    45         FileWriter fw=new FileWriter("info.txt");
    46         pro.store(fw,"");
    47         System.out.println(pro);
    48     }
    49 }

     需求:限制程序运行次数。当运行次数到达5次时,给出,请您注册的提示。并不再让该程序执行。

    思路:

    1、应该有计数器。每次程序启动都需要计数一次,并且是在原有的次数上进行计数。

    2、计数器就是一个变量。突然冒出一想法,程序启动时进行计数,计数器必须存在于内存并进行运算。可是程序一结束,计数器消失了。那么再次启动该程序,计数器又重新被初始化了。    而我们需要多次启动同一个应用程序,使用的是同一个计数器。这就需要计数器的生命周期边长,从内存存储到硬盘文件中。

    3、如何使用这个计数器呢?首先,程序启动时,应该先读取这个用于记录计数器信息的配置文件。获取上一次计数器次数。并进行使用次数的判断。其次,对该次数进行自增,并自增后的次数重新存储到配置文件中。

    4、文件中的信息该如何进行存储并体现。直接存储次数值可以,但是不明确该数据的含义。所以起名字就变得很重要。这就有了名字和值的对应,所以可以使用键值对。可是映射关系map集合搞定,又需要读取硬盘上的数据,所以map+io=properties。

     1 import java.io.*;
     2 import java.util.*;
     3 class RunCount{
     4     public static void main(String[] args)throws IOException{
     5         File file=new File(“count.ini”);
     6         if(!file.exists())
     7             file.createNewFile();
     8         FileReader fr=new FileReader(file);
     9         Properties pro=new Properties();
    10         pro.load(fr);
    11         int count=0;
    12         String value=pro.getProperty(“time”);
    13         if(value!=null){
    14             count=Integet.parseInt(value);
    15             if(count>=5)
    16                throw new RuntimeException(“使用次数已到”);
    17     }
    18     count++;
    19     pro.setProperty(“time”,Integer.toString(count));//String.valueOf(count)  把int型转成字符串
    20     FileWriter fw=new FileWriter(file);
    21     pro.store(fw,””);
    22     fr.close();
    23     fw.close();
    24   }
    25 }

    三、IO包中其他类

    1、PrintStream、PrintWriter打印流

    打印流最厉害的地方就是有很多打印方法可供我们选择。

    PrintStream继承体系

    OutputStream

      |--FilterOutputStream

        |--PrintStream

    PrintWriter继承体系

    Writer

      |--PrintWriter

    PrintWriter与PrintStream:可以直接操作输入流和文件。

    (1)、PrintStream字节打印流

    PrintStream为其他输出流添加了功能,使它们能够方便地打印各种数据值并保持数据的表示形式。与其他输出流不同,PrintStream永远不会抛出IOException。

    PrintStream打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是写入字节的情况下,应该使用PrintWriter类。

    PrintStream构造函数,接收三种类型的值:无空参构造函数

    1. 字符串路径

    2. File对象

    3. 字节输出流

    前两个都JDK1.5版本才出现。而且在操作文本文件时,可指定字符编码了。

    构造函数:

    1 new    PrintStream(File file);//创建具有指定文件且不带自动行刷新的新打印流
    2 new    PrintStream(File file,String csn);//创建具有指定文件名称和字符集且不带自动行刷新的新打印流
    3 new    PrintStream(OutputStream out);//创建新的打印流
    4 new    PrintStream(OutputStream out,boolean autoFlush);//创建新的打印流,并且可以自带自动刷新
    5 new    PrintStream(OutputStream out,boolean autoFlush,String encoding);//创建新的打印流,既能自动刷新又能指定编码表
    6 new    PrintStream(String fileName);//创建具有指定文件名称且不带自动行刷新的新打印流
    7 new    PrintStream(String fileName, String csn) ;//创建具有指定文件名称且能指定编码表的新打印流

     代码演示:

     1 import java.io.*;
     2 class PrintStreamDemo
     3 {
     4     public static void main(String[] args) throws IOException
     5     {
     6         File file=new File("安卓.txt");
     7         if(!file.exists())
     8             file.createNewFile();
     9         PrintStream out=new PrintStream(new FileOutputStream(file),true);
    10         //write(int b)方法只写最低8位
    11         //print方法将7878先变成字符串保持原样将数据打印到目的地
    12         out.println(7878);//运行结果:7878
    13     }
    14 }

    (2)、PrintWriter字符打印流

    PrintWriter构造函数,接收四种类型的值:(重点)无空参

    1.字符串路径

    2.File对象

    3.字节输出流

    4.字符输出流

    构造方法:

    1 new    PrintWriter(File file);//使用指定文件创建不具有自动行刷新的新 PrintWriter.
    2 new     PrintWriter(File file,String csn);//创建具有指定文件和字符集且不带自动刷行新的新 PrintWriter.
    3 new    PrintWriter(OutputStream out);// 创建不带自动行刷新的新 PrintWriter。
    4 new    PrintWriter(OutputStream out, boolean autoFlush)// 创建打印流,并且可以自动刷新。
    5 new    PrintWriter(Writer out);// 创建不带自动行刷新的新 PrintWriter
    6 PrintWriter(Writer out, boolean autoFlush)//创建自动行刷新的新 PrintWriter
    7 new    PrintWriter(String fileName, String csn) ;//创建具有指定文件名称和字符集且不带自动行刷新的新 PrintWriter

     方法中直接操作文件的第二参数是编码表。

    直接操作输出流的,第二参数是自动刷新。

     1 import java.io.*;
     2 class PrintWriterDemo
     3 {
     4     public static void main(String[] args) throws IOException
     5     {
     6         File file=new File("安卓.txt");
     7         if(!file.exists())
     8             file.createNewFile();
     9         PrintWriter out=new PrintWriter(new FileWriter(file),true);
    10         //write(int b)方法只写最低8位
    11         //print方法将7878先变成字符串保持原样将数据打印到目的地
    12         out.println(78);//运行结果:78
    13     }
    14 }

     需求:将键盘录入的数据写入到指定的文件中。

     1 import java.io.*;
     2 class PrintWriterDemo 
     3 {
     4     public static void main(String[] args)throws IOException 
     5     {        
     6         BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
     7 //        PrintWriter out=new PrintWriter(new FileOutputStream("黑马.ini",true));
     8         PrintWriter out=new PrintWriter(new FileWriter("黑马.txt"),true);//true代表的是自动刷新
     9         String line=null;
    10         while ((line=br.readLine())!=null)
    11         {
    12             if("over".equals(line))
    13                 break;
    14             out.println(line);//Println方法代表是是输出并且自动换行
    15         }
    16         out.close();
    17     }
    18 }

     2、SequenceInputStream 序列流

    将多个读取流合并成一个读取流,实现数据合并。

    表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。这样做,可以更方便的操作多个读取流,其实这个序列流内部会有一个有序的集合容器,用于存储多个读取流对象。

    该对象的构造函数参数是Enumeration,想要获取Enumeration,需要有Vector集合。但是Vector集合效率太低,已经被ArrayList所取代。这时候可以用Collections工具类中的Collections.enumeration()该方法返回的就是Enumeration接口。这样会大大提高效率的。

    构造函数:

    1 new SequenceInputStream(Enumeration<? extends InputStream> e);//该构造函数适合多个读取字节流对象操作
    2 new SequenceInputStream(InputStream s1, InputStream s2);//该构造函数适合两个读取字节流对象操作

     需求:将1.txt、2.txt、3.txt文件中的数据合并到一个文件中。

     1 import java.io.*;
     2 import java.util.*;
     3 class SequenceInputStreamDemo 
     4 {
     5     public static void main(String[] args)throws IOException 
     6     {
     7         method_1();
     8     }
     9     public static void method_1()throws IOException{
    10         //定义Vector对象用来存储字节读取流对象
    11         Vector<FileInputStream> v=new Vector<FileInputStream>();
    12         v.add(new FileInputStream("E:\1.txt"));
    13         v.add(new FileInputStream("E:\2.txt"));
    14         v.add(new FileInputStream("E:\3.txt"));
    15 
    16         //用Vector对象中的elements方法获取Enumeration
    17         Enumeration<FileInputStream> en=v.elements();
    18 
    19         //创建SequenceInputStream对象,和流关联
    20         SequenceInputStream sis=new SequenceInputStream(en);
    21         FileOutputStream fw=new FileOutputStream("E:\4.txt");
    22         int len=0;
    23         byte[] buf=new byte[1024];
    24         while ((len=sis.read(buf))!=-1)
    25         {
    26             fw.write(buf,0,len);
    27         }
    28         sis.close();
    29         fw.close();
    30     }
    31     //上面用的Vector对象。这个对象比较低效,已经被ArrayList所替代了。所以下面我们用ArrayList集合来操作。
    32     public static void method_2()throws IOException{
    33         //创建ArrayList集合容器用来存放字节读取流对象,该容器效率比Vector集合效率高
    34         Collection<FileInputStream> coll=new ArrayList<FileInputStream>();
    35         //循环获取FileInputStream对象,添加进ArrayList集合中。
    36         for (int x=1;x<4 ;x++ )
    37         {
    38             coll.add(new FileInputStream("E:\"+x+".txt"));
    39         }
    40         //用Collections工具类中的enumeration方法获取Enumeration接口
    41         Enumeration<FileInputStream> en=Collections.enumeration(coll);
    42         //创建SequenceInputStream对象,和流关联
    43         SequenceInputStream sis=new SequenceInputStream(en);
    44         FileOutputStream fos=new FileOutputStream("E:\4.txt");
    45         int len=0;
    46         byte[] buf=new byte[1024];
    47         while ((len=sis.read(buf))!=-1)
    48         {
    49             fos.write(buf,0,len);
    50         }
    51         fos.close();
    52         sis.close();
    53     }
    54 }

     需求:对文件的切割。

     1 import java.io.*;
     2 import java.util.*;
     3 class  SplitDemo
     4 {
     5     public static void main(String[] args) throws IOException
     6     {
     7         File file=new File("D:\年轮.mp3");
     8         //用读取流关联源文件
     9         FileInputStream fis=new FileInputStream(file);
    10         FileOutputStream fos=null;
    11         //创建目标文件
    12         File dir=new File("E:\年轮");
    13         if(!dir.exists())
    14             dir.mkdir();
    15         int len=0;
    16         //定义一个计数器用来记录切割后碎片的文件名
    17         int count=1;
    18         //定义一个1M的缓冲区
    19         byte[] buf=new byte[1024*1024];
    20         while ((len=fis.read(buf))!=-1)
    21         {
    22             fos=new FileOutputStream(new File(dir,(count++)+".part"));
    23             fos.write(buf,0,len);
    24         }
    25         //切割文件时,必须记录住被切割文件的名称,以及切割出来碎片文件的个数,以方便于合并。
    26         //这个信息为了进行描述,使用键值对的方式,用到了properties对象。
    27         Properties pro=new Properties();
    28         //将被切割文件的信息保存到pro集合中
    29         pro.setProperty("Count",Integer.toString(count));
    30         pro.setProperty("Name",file.getName());
    31         fos=new FileOutputStream(new File(dir,count+".properties"));
    32         //将pro集合中的数据存储到文件中
    33         pro.store(fos,"");
    34         fos.close();
    35         fis.close();
    36     }
    37 }

     需求:从上例读取配置信息,并合并文件

     1 import java.io.*;
     2 import java.util.*;
     3 class HeBingFile 
     4 {
     5     public static void main(String[] args)throws IOException 
     6     {        
     7         FileInputStream fis=new FileInputStream("E:\年轮\6.properties");
     8         Properties pro=new Properties();
     9         pro.load(fis);
    10         //获取配置信息中切割的个数
    11         int count=Integer.parseInt(pro.getProperty("Count"));
    12         //获取配置信息中别切割的文件名
    13         String name=pro.getProperty("Name");
    14 //        System.out.println(name);        
    15         File file=new File("E:\年轮");
    16         //将碎片文件和流对象关联并存储到集合中
    17         ArrayList<FileInputStream> al=new ArrayList<FileInputStream>();
    18         //获取该目录下的所有碎片文件,并添加到集合中
    19         for (int x=1;x<count ; x++)
    20         {
    21             al.add(new FileInputStream(new File(file,x+".part")));
    22         }
    23         Enumeration<FileInputStream> en=Collections.enumeration(al);
    24         SequenceInputStream sis=new SequenceInputStream(en);        
    25         FileOutputStream fos=new FileOutputStream("E:\"+name);
    26         int len=0;
    27         byte[] buf=new byte[1024];
    28         while ((len=sis.read(buf))!=-1)
    29         {
    30             fos.write(buf,0,len);
    31         }
    32         
    33     }
    34 }

     3、ObjectInputStream、ObjectOutputStream 操作对象的流

    被操作的对象需要实现Serializable接口。类通过实现java.io.Serializable接口以启用序列化功能,Serializable只是一个标记接口。

    Serializable:用于启动对象的序列化功能,可以强制让指定类具备序列化功能,该接口中没有成员,这是一个标记接口。这个标记接口用于给序列化类提供UID。这个uid是依据类中的成员的数字签名进行运行获取的。如果不需要自动获取一个uid,可以在类中,手动指定一个名称为serialVersionUID id号。依据编译器的不同,或者对信息的高度敏感性。最好每一个序列化的类都进行手动显示的UID的指定。

    注意:静态数据不能被序列化,因为静态数据不在堆内存中,是存储在静态方法区中。 

    如何将非静态的数据不进行序列化?用transient 关键字修饰此变量即可。

    ObjectInputStream类中方法:

     1 new ObjectInputStream(InputStream in);// 创建从指定 InputStream 读取的 ObjectInputStream
     2 int    available();//返回可以不受阻塞地读取的字节数。
     3 boolean    readBoolean();//读取一个 boolean 值
     4 byte    readByte();//读取一个 8 位的字节
     5 char    readChar();//读取一个 16 位的 char 值
     6 double    readDouble();//读取一个 64 位的 double 值
     7 float    readFloat();//读取一个 32 位的 float 值
     8 int    readInt();//读取一个 32 位的 int 值
     9 long  readLong();//读取一个 64 位的 long 值
    10 short  readShort();//读取一个 16 位的 short 值
    11 Object  readObject();// 从 ObjectInputStream 读取对象
    12 String  readUTF();//读取 UTF-8 修改版格式的 String

     ObjectOutputStream类中的方法:

     1 new ObjectOutputStream(OutputStream Out);// 创建写入指定 OutputStream 的 ObjectOutputStream
     2 void  writeBoolean(boolean val);//写入一个 boolean 值
     3 void  writeByte(int val);// 写入一个 8 位字节
     4 void  writeBytes(String str);//以字节序列形式写入一个 String
     5 void  writeChar(int val);//写入一个 16 位的 char 值
     6 void  writeDouble(double val);//读取一个 64 位的 double 值
     7 void  writeFloat(float val);//写入一个 32 位的 float 值
     8 void  writeInt(int val);//  写入一个 32 位的 int 值
     9 void  writeLong(long val);//写入一个 64 位的 long 值
    10 void  writeObject(Object obj);//将指定的对象写入 ObjectOutputStream
    11 void  writeShort(int val);//写入一个 16 位的 short 值
    12 void  writeUTF(String str);//  以 UTF-8 修改版格式写入此 String 的基本数据

    代码演示:

    Person.java文件

     1 import java.io.*;
     2 //实现Serializable接口以启用序列化功能
     3 class Person implements Serializable
     4 {
     5     //用于给被序列化的类加入ID号,用于判断类和对象是否是同一个版本
     6     //此时,再将Person类中属性修饰符修改为public,也不会出现任何异常
     7     private static final long serialVersionUID = 2332;
     8     private String name;
     9 //    private transient String name;//用transient修饰后name将不会进行序列化
    10     private int age;
    11     Person(String name,int age){
    12         this.name=name;
    13         this.age=age;
    14     }
    15     public void setName(String name){
    16         this.name=name;
    17     }
    18     public String getName(){
    19         return name;
    20     }
    21     public void setAge(int age){
    22         this.age=age;
    23     }
    24     public int getAge(){
    25         return age;
    26     }
    27     public String toString(){
    28         return name+"::"+age;
    29     }
    30 }

     ObjectOutputStreamDemo.java文件

     1 import java.io.*;
     2 class ObjectOutputStreamDemo 
     3 {
     4     public static void main(String[] args)throws Exception
     5     {    
     6 //        writeObj();
     7         readObj();
     8     }
     9     public static void writeObj()throws IOException{
    10         ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("obj.object"));
    11         oos.writeObject(new Person("黑马",120));
    12         oos.close();
    13     }
    14     public static void readObj()throws Exception{
    15         ObjectInputStream ois=new ObjectInputStream(new FileInputStream("obj.object"));
    16         Person p=(Person)ois.readObject();
    17         System.out.println(p);//运行结果:黑马::120
    18         ois.close();
    19     }
    20 }

     4、管道流

    PipedInputStream和PipedOutputStream:输入输出可以直接进行连接,通过结合线程使用。

     1 import java.io.*;
     2 class Read implements Runnable
     3 {
     4     private PipedInputStream in;
     5     Read(PipedInputStream in){
     6         this.in=in;
     7     }
     8     public void run(){
     9         byte[] buf=new byte[1024];
    10         try
    11         {
    12             System.out.println("读取前。。没有数据阻塞");
    13             int len=in.read(buf);//read方法是阻塞式方法,没有收到数据前就是等待
    14             System.out.println("读到数据。。阻塞结束");
    15             System.out.println(new String(buf,0,len));
    16             
    17         }
    18         catch (IOException e)
    19         {
    20             System.out.println(e);
    21         }
    22         finally{
    23             try
    24             {
    25                 in.close();
    26             }
    27             catch (IOException e)
    28             {
    29                 System.out.println("关闭失败");
    30             }
    31         }
    32         
    33     }
    34 }
    35 class Write implements Runnable
    36 {
    37     private PipedOutputStream out;
    38     Write(PipedOutputStream out){
    39         this.out=out;
    40     }
    41     public void run(){
    42         try
    43         {
    44             System.out.println("开始写入数据,等待6秒后。");
    45             Thread.sleep(6000);//让线程等待6秒后继续执行
    46             out.write("我是管道流".getBytes());
    47         }
    48         catch (Exception e)
    49         {
    50             System.out.println(e);
    51         }
    52         finally
    53         {
    54             try
    55             {
    56                 out.close();
    57             }
    58             catch (IOException e)
    59             {
    60                 System.out.println("关闭失败");
    61             }
    62         }        
    63         
    64     }
    65 }
    66 
    67 class PipedStreamDemo 
    68 {
    69     public static void main(String[] args) throws IOException
    70     {
    71         PipedInputStream in=new PipedInputStream();
    72         PipedOutputStream out=new PipedOutputStream();
    73         in.connect(out);//管道输入流连接到管道输出流
    74         new Thread(new Read(in)).start();
    75         new Thread(new Write(out)).start();
    76     }
    77 }

     5、RandomAccessFile 随机访问文件

    随机访问文件,自身已经具备了读和写的方法。该类不算是IO体系中的子类,而是直接继承自Object。但是它是IO包中的成员,是因为它具备了读写功能,内部其实封装了一个数组,而且通过指针对数组的元素进行操作。可以通过getFilePointer获取指针位置,同时也可以通过seek改变指针的位置。其实完成读写的原理就是内部封装了字节输入流和输出流。

    通过构造函数可以看出,该类只能操作文件,而且操作文件还有模式:只读r,读写rw等。

    如果模式为只读r,则不会创建文件,会去读取一个已经存在文件。如果该文件不存在,则会出现异常。

    如果模式为rw,操作的文件不存在,会自动创建。如果存在则不会覆盖。

    特点:

    1.该对象即可读取,又可写入。

    2.该对象中的定义了一个大型的byte数组,通过定义指针来操作这个数组。

    3.可以通过该对象的getFilePointer()获取指针的位置,通过seek()方法设置指针的位置。

    4.该对象操作的源和目的必须是文件。

    5.其实该对象内部封装了字节读取流和字节写入流。

    注意:实现随机访问,最好是数据有规律。

     1 import java.io.*;
     2 class RandomAccessFileDemo 
     3 {
     4     public static void main(String[] args) throws IOException
     5     {
     6         //writeFile_2();
     7         //readFile();
     8         }
     9     public static void readFile()throws IOException
    10     {
    11         RandomAccessFile raf = new RandomAccessFile("ran.txt","r");
    12         //调整对象中指针。
    13         //raf.seek(8*1);
    14         //跳过指定的字节数
    15         raf.skipBytes(8);
    16         byte[] buf = new byte[4];
    17         raf.read(buf);
    18         String name = new String(buf);
    19         int age = raf.readInt();
    20         System.out.println("name="+name);
    21         System.out.println("age="+age);
    22         raf.close();
    23     }
    24     public static void writeFile_2()throws IOException
    25     {
    26         RandomAccessFile raf =new RandomAccessFile("ran.txt","rw");
    27         //往指定位置写入数据
    28         raf.seek(8*0);
    29         raf.write("小明".getBytes());
    30         raf.writeInt(103);
    31         raf.close();
    32     }
    33     public static void writeFile()throws IOException
    34     {
    35          //如果文件不存在,则创建,如果文件存在,不创建
    36         RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
    37         raf.write("李四".getBytes());
    38         raf.writeInt(97);
    39         raf.write("王五".getBytes());
    40         raf.writeInt(99);
    41         raf.close();
    42     }
    43 }

     6、DataInputStream与DataOutputStream

    可以用于操作基本数据类型的数据的流对象。

     1 import java.io.*;
     2 class DataStreamDemo  
     3 {
     4     public static void main(String[] args)throws IOException 
     5     {
     6 //        write();
     7         read();
     8     }
     9     public static void read()throws IOException{
    10         DataInputStream dis=new DataInputStream(new FileInputStream("data.txt"));
    11         //输入流中读取基本数据类型
    12         int num=dis.readInt();
    13         double d=dis.readDouble();
    14         boolean b=dis.readBoolean();
    15         //读取一个UTF-8编码的字符串
    16         String s=dis.readUTF();
    17         System.out.println("num="+num);
    18         System.out.println("d="+d);
    19         System.out.println("b="+b);
    20         System.out.println("s="+s);
    21     }
    22     public static void write()throws IOException{
    23         DataOutputStream dos=new DataOutputStream(new FileOutputStream("data.txt"));
    24         //基本数据类型写入输出流中
    25         dos.writeInt(45);
    26         dos.writeDouble(45.2);
    27         dos.writeBoolean(true);
    28         //使用 UTF-8 修改版编码将一个字符串写入输出流,此写出的数据必须用readUTF()方法读取,转换流是不行的。
    29         dos.writeUTF("你好");
    30     }
    31 }

     7、 ByteArrayInputStream与ByteArrayOutputStream 操作字节数组

    用于操作字节数组的流对象。

    ByteArrayInputStream :在构造的时候,需要接收数据源,。而且数据源是一个字节数组。

    ByteArrayOutputStream: 在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组。这就是数据目的地。

    因为这两个流对象都操作的数组,并没有使用系统资源。所有的操作都是在内存中完成的。所以,不用进行close关闭。

    1 String  toString();// 使用平台默认的字符集,通过解码字节将缓冲区内容转换为字符串。 
     1 import java.io.*;
     2 class ByteArrayStreamDemo 
     3 {
     4     public static void main(String[] args) 
     5     {
     6         //数据源。
     7         ByteArrayInputStream in=new ByteArrayInputStream("nihaojava".getBytes());
     8         //数据目的
     9         ByteArrayOutputStream out=new ByteArrayOutputStream();
    10         int by=0;
    11         while ((by=in.read())!=-1)
    12         {
    13             out.write(by);
    14         }
    15 
    16         System.out.println(out.toString());
    17     }
    18 }

    8、 CharArrayReader与CharArrayWrite 操作字符数组

    9、StringReader 与 StringWriter 操作字符串

    这两个流对象和ByteArrayStream的原理一样,可参考ByteArrayStream。

    10、编码和解码

    编码表的由来:

    计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识别各个国家的文字。就将各个国家的文字用数字来表示,并一一对应,形成一张表,这就是编码表。

    常见的编码表:

    ASCII:美国标准信息交换码。用一个字节的7位可以表示。ISO8859-1:拉丁码表,欧洲码表。用一个字节的8位表示。

    GB2312:中国的中文编码表。

    GBK:中文编码表升级融合了更多的文字符号。两个字节。

    Unicode:国际标准码,融合了多种文字。

    所有文字都用两个字节来表示,Java语言使用的就是unicode

    UTF-8:最多用三个字节来表示一个字符。

    编码:字符串变成字节数组

    Stringàbyte[]:   str.getBytes(charsetName);

    解码:字节数组变成字符串

    byte[]àString:   new String(byte[],charsetName);

    如果编码编错了,解不出来。

    如果编对了,解错了,又可能有救。可以在编一次,再解一次。

     1 import java.util.*;
     2 class EncodeDemo 
     3 {
     4     public static void main(String[] args) throws Exception
     5     {
     6         String s="你好";
     7         byte[] by=s.getBytes("GBK");
     8         System.out.println(Arrays.toString(by));
     9 
    10         //解码
    11         String s1=new String(by,"UTF-8");
    12         System.out.println(s1);//解错了
    13 
    14         //在按照GBK编码一次
    15         byte[] b=s.getBytes();
    16         System.out.println(Arrays.toString(b));
    17 
    18         //在用UTF-8解码一次
    19         String s2=new String(b,"GBK");
    20         System.out.println(s2);
    21     }
    22 }

     运行结果:

    [-60, -29, -70, -61]
    ???
    [-60, -29, -70, -61]
    你好

    如果编对了,解错了,也可能没救了。 

     1 import java.util.*;
     2 public class EncodeDemo{
     3        public static void main(String[] args) throws Exception{
     4             String s = "您好"; 
     5             //编码
     6             byte[] by = s.getBytes("gbk" );
     7             System.out.println(Arrays.toString(by));
     8             //解码
     9             String str= new String(by,"UTF-8" );
    10             System.out.println(str);
    11             //再编一次
    12             byte[] b = str.getBytes("UTF-8" );           
    13             System.out.println(Arrays.toString(b));
    14             //再解码一次
    15             System.out.println(new String(b,"gbk" ));
    16       }
    17     
    18 }

     运行结果: 

    [-60, -6, -70, -61]
    ????
    [-17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67]
    锟斤拷锟斤拷

    原因分析:

    “您好”的gbk编码在UTF-8码表中查不到对应的字符,所以已经用“?”代替。“?”在UTF-8中的编码为-17 -65 -67

    故即使使用UTF-8码表进行编码,然后再用GBK接码获取的字节也不是“您好”的gbk编码后的字节。所以再也不能成功解码了。

    “谢谢”的gbk编码在UTF-8码表中可以查到对应的字符,为“ππ”。因此,使用UTF-8码表对“ππ”进行解码,获取的字节也依然是“您好”的gbk编码后的字节。   所以,不会出现“您好”发生的情况。

     

    分析:联通乱码问题。

    在记事本中输入联通,保存。关闭,重新打开此文件,发现乱码。这是怎么回事呢?

    原因分析:

    “联通”经过gbk编码后成四个字节:11000001、10101010、11001101、10101000。

    正好符合UTF-8的编码规则。所以,记事本按照UTF-8进行了解码,从而出现了乱码现象。

     

    练习:有五个学生,每个学生有3门课的成绩,从键盘输入以上数据(包括姓名,三门课成绩),输入的格式:如:zhangsan,30,40,60计算出总成绩,并把学生的信息和计算出的总分数高低顺序存放在磁盘文件"stud.txt"中。

    思路:

    1,通过获取键盘录入一行数据,并将该行中的信息取出封装成学生对象。

    2,因为学生有很多,那么就需要存储,使用到集合。因为要对学生的总分排序。所以可以使用TreeSet。

     

     1 /*
     2 有五个学生,每个学生有3门课的成绩,
     3 从键盘输入以上数据(包括姓名,三门课成绩),
     4 输入的格式:如:zhagnsan,30,40,60计算出总成绩,
     5 并把学生的信息和计算出的总分数高低顺序存放在磁盘文件"stud.txt"中。
     6 */
     7 import java.io.*;
     8 import java.util.*;
     9 class Student implements Comparable<Student>
    10 {
    11     private String name;
    12     private int cn,ma,en;
    13     private int sum;
    14     Student(String name,int cn,int ma,int en){
    15         this.name=name;
    16         this.cn=cn;
    17         this.ma=ma;
    18         this.en=en;
    19         sum=cn+ma+en;
    20     }
    21     public String getName(){
    22         return name;
    23     }
    24     public int getSum(){
    25         return sum;
    26     }
    27     public int compareTo(Student s){
    28         int num=this.sum-s.sum;
    29         return num==0?this.name.compareTo(s.name):num;
    30     }
    31     public int hashCode(){
    32         return name.hashCode()+sum*37;
    33     }
    34     public boolean equals(Object obj){
    35         if(!(obj instanceof Student))
    36             throw new RuntimeException("类型错误");
    37         Student stu=(Student)obj;
    38         return this.name.equals(stu.name)&&this.sum==stu.sum;
    39     }
    40     public String toString(){
    41         return "student["+name+", "+cn+", "+ma+", "+en+"]";
    42     }
    43 
    44 }
    45 class StuTools
    46 {
    47     public static Set<Student> getStu()throws IOException{
    48         return getStu(null);//如果没有传比较器,返回没有传比较器的排序
    49     }
    50     public static Set<Student> getStu(Comparator<Student> comp)throws IOException{
    51         TreeSet<Student> ts=null;
    52         BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
    53         if(comp==null)//这是Student自身排序
    54             ts=new TreeSet<Student>();
    55         else
    56             ts=new TreeSet<Student>(comp);//传入比较器
    57         String line=null;
    58         while ((line=br.readLine())!=null)
    59         {
    60             if("over".equals(line))
    61                 break;
    62             String[] info=line.split(" ");
    63             ts.add(new Student(info[0],Integer.parseInt(info[1]),Integer.parseInt(info[2]),Integer.parseInt(info[3])));
    64         }
    65         return ts;
    66     } 
    67     public static void write2File(Set<Student> set)throws IOException{
    68         BufferedWriter out=new BufferedWriter(new FileWriter("stu.txt"));
    69         for(Student stu:set){
    70             out.write(stu.toString()+"	");
    71             out.write(String.valueOf(stu.getSum()));//把Integer转换成String
    72             out.newLine();
    73             out.flush();
    74         }
    75         out.close();
    76     }
    77 }
    78 class  StudentInfoTest 
    79 {
    80     public static void main(String[] args) throws IOException
    81     {
    82         Comparator<Student> comp=Collections.reverseOrder();//反转现在Comparable接口的排序方式。
    83         Set<Student> set=StuTools.getStu(comp);
    84         StuTools.write2File(set);
    85     }
    86 }

    注:菜鸟日记,如有错误欢迎大神指正,欢迎探讨!!

  • 相关阅读:
    scnner02 (nextLine)
    Scanner01
    Spring 框架 (初学)
    查询自己写了多少行代码
    jdbc事务
    jdbc(预编译插入数据)
    jdbc(java连接数据库)
    监听器扩展
    listener(监听器)
    Filter过滤器
  • 原文地址:https://www.cnblogs.com/521Android/p/4729325.html
Copyright © 2020-2023  润新知