黑马程序员——IO(二)——File类、Properties类、打印流、序列流等 .
来源:互联网 发布:wi fi万能钥匙mac版 编辑:程序博客网 时间:2024/06/06 02:18
黑马程序员--IO(二)--File类、Properties类、打印流、序列流等
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
一、File类
File类用来将文件或文件夹封装成对象,方便对文件与文件夹的属性信息进行操作。File对象可以作为参数传递给流的构造函数。一旦创建,File对象表示的抽象路径名将永不改变。
- //封装对象的方式:可以将已存在或不存在的文件或目录封装成file对象。
- import java.io.File;
- class FileDemo
- {
- public static void main(String[] args)
- {
- constructorDemo();
- }
- public static void constructorDemo()
- {
- //方式一:
- File f1 = new File("d:\\demo\\a.txt");
- //方式二:
- File f2 = new File("d:\\demo","a.txt");
- //方式三:
- File f = new File("d:\\demo");
- File f3 = new File(f,"a.txt");
- //跨系统方式.File.separator是与系统有关的默认名称分隔符。UNIX系统上值为'/';
- File f4 = new File("d:"+File.separator+"demo"+File.separator+"a.txt");
- }
- }
//封装对象的方式:可以将已存在或不存在的文件或目录封装成file对象。import java.io.File;class FileDemo{public static void main(String[] args) {constructorDemo();}public static void constructorDemo(){//方式一:File f1 = new File("d:\\demo\\a.txt");//方式二:File f2 = new File("d:\\demo","a.txt");//方式三:File f = new File("d:\\demo");File f3 = new File(f,"a.txt");//跨系统方式.File.separator是与系统有关的默认名称分隔符。UNIX系统上值为'/';File f4 = new File("d:"+File.separator+"demo"+File.separator+"a.txt");}}PS:
流只能操作数据。
1、常见方法:
1.1 创建
boolean creatNewFile():在指定位置创建文件,如果该文件已经存在,则不创建,返回false。
和输出流不一样,输出流对象一建立创建文件。而且文件已经存在,会覆盖。
boolean mkdir():创建文件夹。只能创建一级文件夹。
boolean mkdirs():创建多级文件夹
1.2 删除
boolean delete():删除文件或目录。文件存在,返回true。文件不存在或正在被执行,返回false。
void deleteOnExit():在程序退出时删除指定文件。避免程序异常时,删除动作执行不到的问题。
1.3 判断
boolean canExecute():测试程序是否可以执行此路径名表示的文件。
boolean exists():判断文件是否存在。
boolean isDirectory():判断路径名是否是目录(文件夹)。
boolean isFile():判断路径名是否是文件。
boolean isHidden():判断路径名是否是一个隐藏文件。
boolean isAbsolute():判断路径名是否是绝对路径名。
PS:
判读文件对象是否是文件或目录时,必须要判断该文件对象封装的内容是否存在。
1.4 获取信息。
String getName():获取文件名。
String getPath():获取文件的相对路径。即创建的对象传入的参数是什么就获取到什么。
String getParent():返回文件绝对路径中的父目录。若获取相对路径,有明确父目录时,返回父目录,否则返回null。
String getAbsolutePath():获取文件的绝对路径。
long lastModified():返回文件最后一次被修改时间。判断文件是否被修改过。
long length():返回文件长度。
- //File类常见方法:
- import java.io.*;
- class FileDemo1
- {
- public static void main(String[] args) throws IOException
- {
- method_create();
- method_delete();
- method_judge();
- method_get();
- method_list();
- }
- public static void method_create() throws IOException
- {
- File f = new File("file.txt");
- //指定位置创建文件。因为它调用、启用资源有产生异常的可能。
- sop("create:"+f.createNewFile());
- File dir1 = new File("abc");
- //创建一级文件夹
- sop("mkdirs:"+dir1.mkdir());
- File dir2 = new File("abc\\a\\aad\\e");
- //创建多级文件夹
- sop("mkdirs:"+dir2.mkdirs());
- }
- public static void method_delete() throws IOException
- {
- File f = new File("a.txt");
- //删除文件或目录。文件存在,返回true。文件不存在或正在被执行,返回false。
- sop("delete:"+f.delete());
- //在程序退出时删除指定文件。
- File f2 = new File("file.txt");
- f2.deleteOnExit();
- }
- public static void method_judge() throws IOException
- {
- File f = new File("d:\\Demo\\IO3\\FileDemo\\file.txt");
- sop("canExecute:"+f.canExecute());
- //如果流存在才可以读写,所以exists方法可以直接判断流存在问题。
- sop("exists:"+f.exists());
- f.mkdir();
- //当判断文件对象是否是文件或目录时,必须要先判断该文件对象封装的内存是否存在。
- sop("dir:"+f.isDirectory());
- sop("file:"+f.isFile());
- sop("hidden:"+f.isHidden());
- sop(f.isAbsolute());
- }
- public static void method_get() throws IOException
- {
- File f = new File("d:\\a.txt");
- sop("path:"+f.getPath());
- sop("abspath:"+f.getAbsolutePath());
- sop("parent:"+f.getParent());
- sop("length:"+f.length());
- }
- public static void method_list() throws IOException
- {
- File f1 = new File("d:\\helloworld.java");
- File f2 = new File("d:\\a.java");
- //列出可以文件系统根目录
- sop("root"+f2.listRoots());
- //列出当前目录下所有文件
- sop("list"+f2.list());
- sop("rename:"+f1.renameTo(f2));
- }
- public static void sop(Object obj)
- {
- System.out.println(obj);
- }
- }
//File类常见方法:import java.io.*;class FileDemo1{public static void main(String[] args) throws IOException {method_create();method_delete();method_judge();method_get();method_list();}public static void method_create() throws IOException{File f = new File("file.txt");//指定位置创建文件。因为它调用、启用资源有产生异常的可能。sop("create:"+f.createNewFile());File dir1 = new File("abc");//创建一级文件夹sop("mkdirs:"+dir1.mkdir());File dir2 = new File("abc\\a\\aad\\e");//创建多级文件夹sop("mkdirs:"+dir2.mkdirs());}public static void method_delete() throws IOException{File f = new File("a.txt");//删除文件或目录。文件存在,返回true。文件不存在或正在被执行,返回false。sop("delete:"+f.delete());//在程序退出时删除指定文件。File f2 = new File("file.txt");f2.deleteOnExit();}public static void method_judge() throws IOException{File f = new File("d:\\Demo\\IO3\\FileDemo\\file.txt");sop("canExecute:"+f.canExecute());//如果流存在才可以读写,所以exists方法可以直接判断流存在问题。sop("exists:"+f.exists());f.mkdir();//当判断文件对象是否是文件或目录时,必须要先判断该文件对象封装的内存是否存在。sop("dir:"+f.isDirectory());sop("file:"+f.isFile());sop("hidden:"+f.isHidden());sop(f.isAbsolute());}public static void method_get() throws IOException{File f = new File("d:\\a.txt");sop("path:"+f.getPath());sop("abspath:"+f.getAbsolutePath());sop("parent:"+f.getParent());sop("length:"+f.length());}public static void method_list() throws IOException{File f1 = new File("d:\\helloworld.java");File f2 = new File("d:\\a.java");//列出可以文件系统根目录sop("root"+f2.listRoots());//列出当前目录下所有文件sop("list"+f2.list());sop("rename:"+f1.renameTo(f2));}public static void sop(Object obj){System.out.println(obj);}}1.5 列出文件及文件过滤
static File[] listRoots():列出可用的文件系统根目录,即系统盘符。
String[] list():列出当前目录下所有文件,包括隐藏。调用list方法的file对象必须是封装了一个已存在的目录。
String[] list(FilenameFilter filter):返回一个字符串数组,获取目录中满足指定过滤器的文件和目录。
FilenameFilter:文件名过滤器,是一个接口,其中包含一个方法:accept(File dir,String name),返回的是boolean。
File[] listFiles():返回一个抽象路径名数组,获取当前文件夹下的所有文件和文件夹。
boolean renameTo(File dest):重命名。
list和listFiles区别:
list返回当前目录下的文件和名称;listFiles返回当前目录下的文件和文件对象。
即list只能获取名,listFiles可以通过方法获取更多属性。
- //list方法
- import java.io.*;
- class FileDemo2
- {
- public static void main(String[] args)
- {
- listRootsDemo();
- listDemo();
- listDemo_2();
- listfileDemo();
- }
- public static void listfileDemo()
- {
- File dir = new File("D:\\Demo\\IO3");
- File[] files = dir.listFiles();
- for(File f:files)
- {
- System.out.println(f.getName()+"::"+f.length());
- }
- }
- public static void listDemo_2()
- {
- File dir = new File("D:\\Demo\\IO3");
- String[] arr = dir.list(new FilenameFilter()
- {
- //list依据accept的返回值来判断数组内容。
- public boolean accept(File dir,String name)
- {
- return name.endsWith(".txt");
- }
- });
- System.out.println("len:"+arr.length);
- for(String name:arr)
- {
- System.out.println(name);
- }
- }
- public static void listDemo()
- {
- File f = new File("D:\\");
- //调用list方法的file对象必须是封装了一个目录。该目录必须存在。
- String[] names = f.list();
- for(String name:names)
- {
- System.out.println(name);
- }
- }
- //打印系统有效盘符。
- public static void listRootsDemo()
- {
- File[] files = File.listRoots();
- for(File f:files)
- {
- System.out.println(f);
- }
- }
- }
//list方法import java.io.*;class FileDemo2{public static void main(String[] args) {listRootsDemo();listDemo();listDemo_2();listfileDemo();}public static void listfileDemo(){File dir = new File("D:\\Demo\\IO3");File[] files = dir.listFiles();for(File f:files){System.out.println(f.getName()+"::"+f.length());}}public static void listDemo_2(){File dir = new File("D:\\Demo\\IO3");String[] arr = dir.list(new FilenameFilter(){//list依据accept的返回值来判断数组内容。public boolean accept(File dir,String name){return name.endsWith(".txt");}});System.out.println("len:"+arr.length);for(String name:arr){System.out.println(name);}}public static void listDemo(){File f = new File("D:\\");//调用list方法的file对象必须是封装了一个目录。该目录必须存在。String[] names = f.list();for(String name:names){System.out.println(name);}}//打印系统有效盘符。public static void listRootsDemo(){File[] files = File.listRoots();for(File f:files){System.out.println(f);}}}
2、递归
2.1 定义
递归:函数自身调用自身的表现形式或编程手法。因为目录中还有目录,只有使用同一个
列出目录功能的函数完成即可。在列出过程中出现的还是目录的话,还可以再次调用本功能。
2.2 注意事项:
a 限定次数。
b 注意递归次数,避免内存溢出。因为每次调用自己都会执行下一次调用自己的方法,所以会不断
在栈内存中开辟新空间,次数过多,会导致内存溢出。
- /*
- 列出指定目录下文件或文件夹,包含子目录的内容。
- 因为目录中还有目录,只有使用同一个列出目录功能的函数完成即可。
- 在列出过程中出现的还是目录的话,还可以再次调用本功能。
- 也就是函数自身调用自身。
- 这种表现形式或编程手法,称为递归。
- 递归注意事项:
- 1,限定次数。
- 2,注意递归次数,避免内存溢出。
- */
- import java.io.*;
- class FileDemo3
- {
- public static void main(String[] args)
- {
- File dir = new File("D:\\Demo");
- showDir(dir);
- //int n = getSum(25000);
- //System.out.println(n);
- }
- //递归会不断开辟栈对象次数过多,有内存溢出风险。
- public static int getSum(int n)
- {
- if(n==1)
- return 1;
- return n+getSum(n-1);
- }
- //递归:自我调用。因为没有次数限制,会成为无限循环,失去价值。
- public static void method()
- {
- method();
- }
- public static void showDir(File dir)
- {
- //打印目录
- System.out.println(dir);
- File[] files = dir.listFiles();
- //递归
- for(int x=0; x<files.length; x++)
- {
- //如果文件中有文件夹,再次调用自己。
- if(files[x].isDirectory())
- showDir(files[x]);
- System.out.println(files[x]);
- }
- }
- }
/*列出指定目录下文件或文件夹,包含子目录的内容。因为目录中还有目录,只有使用同一个列出目录功能的函数完成即可。在列出过程中出现的还是目录的话,还可以再次调用本功能。也就是函数自身调用自身。这种表现形式或编程手法,称为递归。递归注意事项:1,限定次数。2,注意递归次数,避免内存溢出。*/import java.io.*;class FileDemo3{public static void main(String[] args) {File dir = new File("D:\\Demo");showDir(dir);//int n = getSum(25000);//System.out.println(n);}//递归会不断开辟栈对象次数过多,有内存溢出风险。public static int getSum(int n){if(n==1)return 1;return n+getSum(n-1);}//递归:自我调用。因为没有次数限制,会成为无限循环,失去价值。public static void method(){method();}public static void showDir(File dir){//打印目录System.out.println(dir);File[] files = dir.listFiles();//递归for(int x=0; x<files.length; x++){//如果文件中有文件夹,再次调用自己。if(files[x].isDirectory())showDir(files[x]);System.out.println(files[x]);}}}
二、Properties类
Properties是hashtable的子类。所以它具备Map集合的特点。而且它存储的键值对都是字符串。无泛型定义。它是集合和IO技术集合的集合容器。
1、特点:
a 可以用于键值对形式的配置文件。
b 在加载数据时,需要数据有固定格式:键=值。
c 集合中的数据可以保存到流中或从流中获取。
2、特有方法:
2.1 设置
Object setProperty(String key,String value):设置键和值,调用Hashtable的方法put。
2.2 获取
String getProperty(String key):通过键获取值。
Set<String> stringPropertyNames():返回此属性列表中的键集。将map变成set,带泛型。
3.3 加载与存储
void load(InputStream ism):将字节流中的数据加载进集合。
void load(Reader reader):将字符流中的数据加载进集合。
void list(PrintStream out):将属性列表输出到指定的输出流中。
void store(OutputStream out,String comments):对应load(InputStream)将属性列表(键值对)写入输出流。comments注释信息。
void store(Writer writer,String comments):对应load(Reader)将属性列表(键值对)写入输出流。
- /*
- Properties类实例:
- 本文件解决问题:
- 1,设置和获取元素。
- 2,将流中的数据存储到集合中。
- 思路:
- 1,用一个流和info.txt文件关联。
- 2,读取一行数据,将该行数据用“=”进行切割。
- 3,等号左边作为键,右边作为值。存入到Properties集合中即可。
- */
- import java.io.*;
- import java.util.*;
- class PropertiesDemo
- {
- public static void main(String[] args) throws IOException
- {
- setAndGet();
- method_1();
- loadDemo();
- }
- public static void loadDemo() throws IOException
- {
- Properties prop = new Properties();
- FileInputStream fis = new FileInputStream("info.txt");
- //将流中的数据加载进集合
- prop.load(fis);
- //修改数据信息,但是无法改源文件。
- prop.setProperty("wangwu","39");
- FileOutputStream fos = new FileOutputStream("info.txt");
- //将属性列表(键值对)写入输出流。
- prop.store(fos,"haha");
- //System.out.println(prop);
- //另一种输出方式。
- prop.list(System.out);
- fos.close();
- fis.close();
- }
- //模拟load方法。将流中的数据存储到集合中。
- public static void method_1() throws IOException
- {
- BufferedReader bufr = new BufferedReader(new FileReader("info.txt"));
- String line = null;
- Properties prop = new Properties();
- while((line=bufr.readLine())!=null)
- {
- String[] arr = line.split("=");
- //将键值对存入Properties中
- prop.setProperty(arr[0],arr[1]);
- }
- bufr.close();
- System.out.println(prop);
- }
- //设置和获取元素。
- public static void setAndGet()
- {
- Properties prop = new Properties();
- //设置
- prop.setProperty("zhangsan","30");
- prop.setProperty("lisi","34");
- //System.out.println(prop);
- //获取
- String value = prop.getProperty("lisi");
- //System.out.println(value);
- //修改信息、
- prop.setProperty("lisi",98+"");
- //取出所有元素
- Set<String> names = prop.stringPropertyNames();
- for(String s : names)
- {
- System.out.println(s+":"+prop.getProperty(s));
- }
- }
- }
/*Properties类实例:本文件解决问题:1,设置和获取元素。2,将流中的数据存储到集合中。思路:1,用一个流和info.txt文件关联。2,读取一行数据,将该行数据用“=”进行切割。3,等号左边作为键,右边作为值。存入到Properties集合中即可。*/import java.io.*;import java.util.*;class PropertiesDemo{public static void main(String[] args) throws IOException {setAndGet();method_1();loadDemo();}public static void loadDemo() throws IOException {Properties prop = new Properties();FileInputStream fis = new FileInputStream("info.txt");//将流中的数据加载进集合prop.load(fis);//修改数据信息,但是无法改源文件。prop.setProperty("wangwu","39");FileOutputStream fos = new FileOutputStream("info.txt");//将属性列表(键值对)写入输出流。prop.store(fos,"haha");//System.out.println(prop);//另一种输出方式。prop.list(System.out);fos.close();fis.close();}//模拟load方法。将流中的数据存储到集合中。public static void method_1() throws IOException{BufferedReader bufr = new BufferedReader(new FileReader("info.txt"));String line = null;Properties prop = new Properties();while((line=bufr.readLine())!=null){String[] arr = line.split("=");//将键值对存入Properties中prop.setProperty(arr[0],arr[1]);}bufr.close();System.out.println(prop);}//设置和获取元素。public static void setAndGet(){Properties prop = new Properties();//设置prop.setProperty("zhangsan","30");prop.setProperty("lisi","34");//System.out.println(prop);//获取String value = prop.getProperty("lisi");//System.out.println(value);//修改信息、prop.setProperty("lisi",98+"");//取出所有元素Set<String> names = prop.stringPropertyNames();for(String s : names){System.out.println(s+":"+prop.getProperty(s));}}}应用:创建文件访问计数器。
- /*
- 需求:记录应用程序运行次数。如果使用次数已到,给出注册提示。
- 思路:
- 1、在程序中定义计数器,随着程序的运行存在并自增。但是普通计数器会随着程序退出而清0;
- 2、建立一个配置文件,记录该软件的使用次数。
- 3、为便于阅读、操作数据,该配置文件使用键值对的形式。
- 4、键值对数据是map集合;数据是以文件形式存储,使用io技术;那么map+io -->properties。
- 配置文件可以实现应用程序数据的共享。
- */
- import java.io.*;
- import java.util.*;
- class RunCount
- {
- public static void main(String[] args) throws IOException
- {
- Properties prop = new Properties();
- //将文件独立封装可以使用判读方法,防止文件不存在时抛出异常。
- File file = new File("count.ini");
- if(!file.exists())
- file.createNewFile();
- FileInputStream fis = new FileInputStream(file);
- prop.load(fis);
- int count = 0;
- String value = prop.getProperty("time");
- if(value!=null)
- {
- //将字符串转成基本数据类型。静态装换方法
- count = Integer.parseInt(value);
- if(count>=5)
- {
- System.out.println("使用次数已到,请充值!");
- return;
- }
- }
- count++;
- prop.setProperty("time",count+"");
- FileOutputStream fos = new FileOutputStream(file);
- prop.store(fos,"");
- fos.close();
- fis.close();
- }
- }
/*需求:记录应用程序运行次数。如果使用次数已到,给出注册提示。思路:1、在程序中定义计数器,随着程序的运行存在并自增。但是普通计数器会随着程序退出而清0;2、建立一个配置文件,记录该软件的使用次数。3、为便于阅读、操作数据,该配置文件使用键值对的形式。4、键值对数据是map集合;数据是以文件形式存储,使用io技术;那么map+io -->properties。配置文件可以实现应用程序数据的共享。*/import java.io.*;import java.util.*;class RunCount{public static void main(String[] args) throws IOException{Properties prop = new Properties();//将文件独立封装可以使用判读方法,防止文件不存在时抛出异常。File file = new File("count.ini");if(!file.exists())file.createNewFile();FileInputStream fis = new FileInputStream(file);prop.load(fis);int count = 0;String value = prop.getProperty("time");if(value!=null){//将字符串转成基本数据类型。静态装换方法count = Integer.parseInt(value);if(count>=5){System.out.println("使用次数已到,请充值!");return;}}count++;prop.setProperty("time",count+"");FileOutputStream fos = new FileOutputStream(file);prop.store(fos,"");fos.close();fis.close();}}
三、打印流
打印流:PrintStream、PrintWriter。可直接操作输入流和文件,将传入数据原样打印。1、PrintStream:字节打印流。为其他输出流添加打印各种数据值的功能。特点:不会抛出IOException。
PrintStream打印的所有字符都使用平台的默认字符编码转换为字节。当需要写入字符时应使用PrintWriter类。
构造函数可以接收的参数类型:
1.1 File对象。File
1.2 字符串路径。String
1.3 字节输出流。OutputStream
2、PrintWriter:字符打印流。
构造函数可以接收的参数类型:
2.1 File对象。File
2.2 字符串路径。String
2.3 字节输出流。OutputStream
2.4 字符输出流。Writer。
- //打印流:提供打印方法,可以将各种数据类型的数据原样打印。
- import java.io.*;
- class PrintStreamDemo
- {
- public static void main(String[] args) throws IOException
- {
- PrintStream ps = new PrintStream("print.txt");
- //write(int b):只写最低8位。
- ps.write(97);//a
- //print方法原数打印
- ps.print(97);//97
- BufferedReader bufr =
- new BufferedReader(new InputStreamReader(System.in));
- //PrintWriter构造函数的第二个参数设置为true,表示自动刷新。
- PrintWriter pw = new PrintWriter(System.out,true);
- String line = null;
- while((line=bufr.readLine())!=null)
- {
- if("over".equals(line))
- break;
- pw.println(line.toUpperCase());//ln是刷新标记
- //pw.flush();
- }
- ps.close();
- pw.close();
- bufr.close();
- }
- }
//打印流:提供打印方法,可以将各种数据类型的数据原样打印。import java.io.*;class PrintStreamDemo{public static void main(String[] args) throws IOException{PrintStream ps = new PrintStream("print.txt"); //write(int b):只写最低8位。ps.write(97);//a//print方法原数打印ps.print(97);//97BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));//PrintWriter构造函数的第二个参数设置为true,表示自动刷新。PrintWriter pw = new PrintWriter(System.out,true);String line = null;while((line=bufr.readLine())!=null){if("over".equals(line))break;pw.println(line.toUpperCase());//ln是刷新标记//pw.flush();}ps.close();pw.close();bufr.close();}}
四、序列流
SequenceInputStream:序列流(合并流)。多源对应一个目的时,省去部分关联和续写步骤,简化代码。SequenceInputStream(InputStream s1, InputStreamS2 ): 依顺序读取s1,s2
SequenceInputStream(Enumeration<? extends InputStream> e):多个参数合并
- /*
- SequenceInputStream:序列流(合并流)。
- 需求:将1.txt、2.txt、3.txt合并为一个文件4.txt。
- */
- import java.io.*;
- import java.util.*;
- class SequenceDemo
- {
- public static void main(String[] args) throws IOException
- {
- Vector<FileInputStream> v = new Vector<FileInputStream>();
- v.add(new FileInputStream("D:\\1.txt"));
- v.add(new FileInputStream("D:\\2.txt"));
- v.add(new FileInputStream("D:\\3.txt"));
- Enumeration<FileInputStream> en = v.elements();
- //合并流,统一源。
- SequenceInputStream sis = new SequenceInputStream(en);
- //目的。
- FileOutputStream fos = new FileOutputStream("D:\\4.txt");
- byte[] buf = new byte[1024];
- int len = 0;
- while((len=sis.read(buf))!=-1)
- {
- fos.write(buf,0,len);
- }
- fos.close();
- sis.close();
- }
- }
/*SequenceInputStream:序列流(合并流)。需求:将1.txt、2.txt、3.txt合并为一个文件4.txt。*/import java.io.*;import java.util.*;class SequenceDemo{public static void main(String[] args) throws IOException{Vector<FileInputStream> v = new Vector<FileInputStream>();v.add(new FileInputStream("D:\\1.txt"));v.add(new FileInputStream("D:\\2.txt"));v.add(new FileInputStream("D:\\3.txt"));Enumeration<FileInputStream> en = v.elements();//合并流,统一源。SequenceInputStream sis = new SequenceInputStream(en);//目的。FileOutputStream fos = new FileOutputStream("D:\\4.txt");byte[] buf = new byte[1024];int len = 0;while((len=sis.read(buf))!=-1){fos.write(buf,0,len);}fos.close();sis.close();}}
五、操作对象
操作对象:ObjectInputStream、ObjectOutputStream。ObjectOutputStream:将Java对象的基本数据类型和图形写入OutputStream。
被操作的对象需要实现Serializable接口才能使用序列化功能。
Serializable接口是标记接口,它用于给被序列化的类加入ID号,以判断类和对象是否是同一个版本。
静态不能被序列化,因为静态在方法区中,不在堆中。
serialVersionUID:给类定义固定标识,方便序列化。新类可以操作曾经被序列化的对象。
transient:是让非静态成员无法序列化的关键字。保证它的值在堆内存中存在,而不显示在文本文件中。
- /*
- 操作对象:ObjectInputStream、ObjectOutputStream
- NotSerializableException:某个要序列化的对象不能实现 java.io.Serializable 接口异常。
- */
- import java.io.*;
- class Person implements Serializable
- {
- //serialVersionUID:给类定义固定标识,使新类还可以用之前的序列化对象。
- public static final long serialVersionUID = 42L;
- private String name;
- transient int age;//不想非静态成员序列化加关键字:transient
- //静态不能被序列化,因为静态在方法区中,不在堆中。
- static String country = "cn";
- Person(String name,int age,String country)
- {
- this.name=name;
- this.age=age;
- this.country = country;
- }
- public String toString()
- {
- return name+":"+age+":"+country;
- }
- }
- class ObjectStreamDemo
- {
- public static void main(String[] args) throws Exception
- {
- writeObj();
- readObj();
- }
- public static void readObj() throws Exception
- {
- ObjectInputStream ois =
- new ObjectInputStream(new FileInputStream("obj.txt"));
- Person p = (Person)ois.readObject();
- System.out.println(p);
- ois.close();
- }
- public static void writeObj() throws IOException
- {
- //目的:将对象保存成文件
- ObjectOutputStream oos =
- new ObjectOutputStream(new FileOutputStream("obj.txt"));
- oos.writeObject(new Person("lisi0",339,"USA"));
- oos.close();
- }
- }
/*操作对象:ObjectInputStream、ObjectOutputStreamNotSerializableException:某个要序列化的对象不能实现 java.io.Serializable 接口异常。*/ import java.io.*;class Person implements Serializable{//serialVersionUID:给类定义固定标识,使新类还可以用之前的序列化对象。public static final long serialVersionUID = 42L;private String name;transient int age;//不想非静态成员序列化加关键字:transient//静态不能被序列化,因为静态在方法区中,不在堆中。static String country = "cn"; Person(String name,int age,String country){this.name=name;this.age=age;this.country = country;}public String toString(){return name+":"+age+":"+country;}}class ObjectStreamDemo {public static void main(String[] args) throws Exception{writeObj();readObj();}public static void readObj() throws Exception{ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));Person p = (Person)ois.readObject();System.out.println(p);ois.close();}public static void writeObj() throws IOException{//目的:将对象保存成文件ObjectOutputStream oos =new ObjectOutputStream(new FileOutputStream("obj.txt"));oos.writeObject(new Person("lisi0",339,"USA"));oos.close();}}PS:
1 对象的实体化存储:将对象存储在硬盘上。也可称为对象的序列化、对象的可串联性。
2 标记接口:没有方法的接口。只是为了标识其信息。
六、RandomAccessFile
RandomAccessFile:随机访问文件,自身具备读写方法。它直接继承自Object,不算是IO体系中子类。1、特点:
1.1 具备读和写功能;
1.2 该对象内部封装了一个byte数组,并通过指针可以操作数组中的元素;
1.3 可以通过getFilePointer方法获取指针位置,通过seek方法改变指针位置。
1.4 该对象只能操作文件,而且操作文件还有模式:只读:r,读写:rw。
PS:
1 读写原理:内部封装了字节输入流和输出流。
2 操作文件模式:
如果模式为只读 r:不创建文件。只读取一个已存在的文件,如果该文件不存在,则会出现异常。
如果模式为读写rw:操作的文件不存在,会自动创建。如果存在不会覆盖。
2、常用方法:
skipBytes()跳过指定的字节数;
seek()调整对象中指针;
- //RandomAccessFile:随机访问文件。
- import java.io.*;
- class RandomAccessFileDemo
- {
- public static void main(String[] args) throws IOException
- {
- writeFile();
- readFile();
- writeFile_2();
- }
- public static void readFile() throws IOException
- {
- RandomAccessFile raf = new RandomAccessFile("ran.txt","r");//只读
- //通过seek访问任意位置。
- //raf.seek(8);
- //跳过指定的字节数。不能回跳
- raf.skipBytes(8);
- byte[] buf = new byte[4];
- raf.read(buf);
- String name = new String(buf);
- int age = raf.readInt();
- System.out.println("name="+name);
- System.out.println("age="+age);
- raf.close();
- }
- public static void writeFile() throws IOException
- {
- RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
- raf.write("Lisi".getBytes());
- //使用write方法写入最后一个字节。只取8位,超过256时存在数据丢失。
- raf.write(257);
- //使用writeInt方法写入四个字节(int类型)
- raf.writeInt(97);
- raf.write("wagnwu".getBytes());
- raf.writeInt(99);
- raf.close();
- }
- public static void writeFile_2() throws IOException
- {
- RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
- //往指定位置写入数据
- raf.seek(2*8);
- raf.write("周期".getBytes());
- raf.writeInt(103);
- raf.close();
- }
- }
//RandomAccessFile:随机访问文件。import java.io.*;class RandomAccessFileDemo {public static void main(String[] args) throws IOException {writeFile();readFile();writeFile_2();}public static void readFile() throws IOException{RandomAccessFile raf = new RandomAccessFile("ran.txt","r");//只读//通过seek访问任意位置。//raf.seek(8);//跳过指定的字节数。不能回跳raf.skipBytes(8);byte[] buf = new byte[4];raf.read(buf);String name = new String(buf);int age = raf.readInt();System.out.println("name="+name);System.out.println("age="+age);raf.close();}public static void writeFile() throws IOException{RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");raf.write("Lisi".getBytes());//使用write方法写入最后一个字节。只取8位,超过256时存在数据丢失。raf.write(257);//使用writeInt方法写入四个字节(int类型)raf.writeInt(97);raf.write("wagnwu".getBytes());raf.writeInt(99);raf.close();}public static void writeFile_2() throws IOException{RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");//往指定位置写入数据raf.seek(2*8);raf.write("周期".getBytes());raf.writeInt(103);raf.close();}}
七、管道流
管道流:PipedInputStream、PipedOutputStream。1、特点:
1.1 将输入流和输出流连接,省去了用数组存储数据的中转环节。
1.2 使用多线程解决管道执行顺序问题,防止死锁情况。
2、连接方式:
2.1 connect(PipedOutputStream src):将PipedInputStream连接到PipedOutputStream。
2.2 PipedInputStream():创建尚未连接的PipedInputStream。
2.3 PipedOutputStream():创建尚未连接的PipedOutputStream。
- //管道流:PipedInputStream、PipedOutputStream
- import java.io.*;
- class Read implements Runnable
- {
- private PipedInputStream in;
- Read(PipedInputStream in)
- {
- this.in = in;
- }
- public void run()
- {
- try
- {
- byte[] buf = new byte[1024];
- System.out.println("读取前。。。未阻塞");
- int len = in.read(buf);
- System.out.println("读取后。。。阻塞结束");
- String s = new String(buf,0,len);
- System.out.println(s);
- in.close();
- }
- catch (IOException e)
- {
- throw new RuntimeException("管道读取流失败");
- }
- }
- }
- class Write implements Runnable
- {
- private PipedOutputStream out;
- Write(PipedOutputStream out)
- {
- this.out = out;
- }
- public void run()
- {
- try
- {
- System.out.println("6秒后写入数据");
- //释放执行权
- Thread.sleep(6000);
- out.write("piped coming".getBytes());
- out.close();
- }
- catch (Exception e)
- {
- throw new RuntimeException("管道输出流失败");
- }
- }
- }
- class PipedStreamDemo
- {
- public static void main(String[] args) throws IOException
- {
- PipedInputStream in = new PipedInputStream();
- PipedOutputStream out = new PipedOutputStream();
- //连接输入流和输出流
- in.connect(out);
- Read r = new Read(in);
- Write w = new Write(out);
- new Thread(r).start();
- new Thread(w).start();
- }
- }
//管道流:PipedInputStream、PipedOutputStreamimport java.io.*;class Read implements Runnable{private PipedInputStream in;Read(PipedInputStream in){this.in = in;}public void run(){try{byte[] buf = new byte[1024];System.out.println("读取前。。。未阻塞");int len = in.read(buf);System.out.println("读取后。。。阻塞结束");String s = new String(buf,0,len);System.out.println(s);in.close();}catch (IOException e){throw new RuntimeException("管道读取流失败");}}}class Write implements Runnable{private PipedOutputStream out;Write(PipedOutputStream out){this.out = out;}public void run(){try{System.out.println("6秒后写入数据");//释放执行权Thread.sleep(6000);out.write("piped coming".getBytes());out.close();}catch (Exception e){throw new RuntimeException("管道输出流失败");}}}class PipedStreamDemo {public static void main(String[] args) throws IOException{PipedInputStream in = new PipedInputStream();PipedOutputStream out = new PipedOutputStream();//连接输入流和输出流in.connect(out);Read r = new Read(in);Write w = new Write(out);new Thread(r).start();new Thread(w).start();}}PS:
集合中涉及到IO流的是Properies;IO中涉及到多线程的是管道流。
八、其他
1、操作基本数据类型:DataInputStream、DataOutputStream:用于操作基本数据类型的流对象。相关方法:
writeUTF(String str):以与机器无关方式使用UTF-8修改版编码将一个字符串写入基础输出流。
意味着:只能通过对应的方法读取数据,用转换流读不出来。
readUTF(String str):专门用于读取UTF-8修改版编码。
PS:
UTF-8修改版占8个字节;UTF-8占6个字节;gbk占4个字节。假如用其他方法读取UTF-8修改版编码,
会出现EOFException异常,因为它需要读取8个字节才能结束,用其他方法读取会提前结束。
- //操作基本数据类型:DataInputStream、DataOutputStream
- import java.io.*;
- class DataStreamDemo
- {
- public static void main(String[] args) throws IOException
- {
- writeData();
- readData();
- writeUTFDemo();
- readUTFDemo();
- }
- public static void readUTFDemo() throws IOException
- {
- DataInputStream dis = new DataInputStream(new FileInputStream("utf.txt"));
- String s = dis.readUTF();
- System.out.println(s);
- dis.close();
- }
- public static void writeUTFDemo() throws IOException
- {
- DataOutputStream dos = new DataOutputStream(new FileOutputStream("utf.txt"));
- dos.writeUTF("你好");
- dos.close();
- }
- public static void readData() throws IOException
- {
- DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
- //读取顺序要与写入顺序相同
- int num = dis.readInt();
- boolean b = dis.readBoolean();
- double d = dis.readDouble();
- System.out.println("num="+num);
- System.out.println("b="+b);
- System.out.println("d="+d);
- dis.close();
- }
- public static void writeData() throws IOException
- {
- DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
- dos.writeInt(255);
- dos.writeBoolean(true);
- dos.writeDouble(9885.1516);
- dos.close();
- }
- }
//操作基本数据类型:DataInputStream、DataOutputStreamimport java.io.*;class DataStreamDemo {public static void main(String[] args) throws IOException {writeData();readData();writeUTFDemo();readUTFDemo();}public static void readUTFDemo() throws IOException{DataInputStream dis = new DataInputStream(new FileInputStream("utf.txt"));String s = dis.readUTF();System.out.println(s);dis.close();}public static void writeUTFDemo() throws IOException{DataOutputStream dos = new DataOutputStream(new FileOutputStream("utf.txt"));dos.writeUTF("你好");dos.close();}public static void readData() throws IOException{DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));//读取顺序要与写入顺序相同int num = dis.readInt();boolean b = dis.readBoolean();double d = dis.readDouble();System.out.println("num="+num);System.out.println("b="+b);System.out.println("d="+d);dis.close();}public static void writeData() throws IOException{DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));dos.writeInt(255);dos.writeBoolean(true);dos.writeDouble(9885.1516);dos.close();}}2、操作字节数组
ByteArrayInputStream:在构造时,需要接收数据源,而且数据源是一个字节数组。
ByteArrayOutputStream:在构造时,不用定义数据目的。因为该对象中已经在内部封装了可变长度的字节数组,
也就是数据目的地。其缓冲区随着数据的不断写入而自动增长。
特点:这两个流对象都操作的是数组,没使用系统资源,不用进行close关闭。
优点:增加了对象的封装性;代码的复用性;用流的读写思想来操作数组,简化了书写。
流操作规律:
源设备:
键盘 System.in 硬盘 FileStream 内存 ArrayStream
目的设备:
控制台 System.out 硬盘 FileStream 内存 ArrayStream
- //操作字节数组:ByteArrayInputStream、ByteArrayOutputStream
- import java.io.*;
- class ByteArrayStream
- {
- public static void main(String[] args)
- {
- //数据源
- ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEFG".getBytes());
- //数据目的
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- int by = 0;
- while((by=bis.read())!=-1)
- {
- bos.write(by);
- }
- //获取输出数组大小
- System.out.println(bos.size());
- System.out.println(bos.toString());
- //将此byte数组输出流的全部内容写入到指定的输出流参数中,会报出异常。
- //bos.writeTo(new FileOutputStream("a.txt"));
- }
- }
//操作字节数组:ByteArrayInputStream、ByteArrayOutputStreamimport java.io.*;class ByteArrayStream {public static void main(String[] args) {//数据源ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEFG".getBytes());//数据目的ByteArrayOutputStream bos = new ByteArrayOutputStream();int by = 0;while((by=bis.read())!=-1){bos.write(by);}//获取输出数组大小System.out.println(bos.size());System.out.println(bos.toString());//将此byte数组输出流的全部内容写入到指定的输出流参数中,会报出异常。//bos.writeTo(new FileOutputStream("a.txt"));}}3、操作字符数组:CharArrayReader、CharArrayWrite
4、操作字符串:StringReader、StringWriter
九、字符编码
1、编码表由来:计算机只能识别二进制数据,早期由来是电信号。为了方便用于计算机,让它可以识别各个国家的文字。
就将各个国家的文字用数字来表示,并一一对应,形成一张表--->编码表。
2、分类
ASCII:美国标准信息交换码:用一个字节的7位表示;
ISO8859-1:拉丁码表(欧洲码表):用一个字节的8位表示;
GB2312:中国的中文编码表;两个字节表示一个字符,两个字节的高位都是1,兼容和避免了跟ASCII码重复;
GBK:升级版的中文编码表,融合了更多的中文文字符号;有两万左右字符;
Unicode:国际标准码,融合入了多种文字。所有文字都用两个字节来表示,java语言使用的就是Unicode;
UTF-8:按字符大小分配存储空间,最多三个字节表示一个字符,并在首字母加入标识符,便于区分。
3、字符编码
3.1字符流的出现为了方便操作字符,更重要的是加入了编码转换。
编码转换通过子类转换流来完成:InputStreamReader、OutputStreamWriter。
在两个对象进行构造的时候可以加入字符集。
- //字符编码:编码转换:InputStreamReader、OutputStreamWriter
- import java.io.*;
- class EncodeStream
- {
- public static void main(String[] args) throws IOException
- {
- writeText();
- readText();
- }
- public static void readText() throws IOException
- {
- InputStreamReader isr = new InputStreamReader(new FileInputStream("utf.txt"),"UTF-8");
- char[] buf = new char[10];
- int len =isr.read(buf);
- String str = new String(buf,0,len);
- System.out.println(str);
- isr.close();
- }
- public static void writeText() throws IOException
- {
- OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"),"UTF-8");
- osw.write("你好");
- osw.close();
- }
- }
//字符编码:编码转换:InputStreamReader、OutputStreamWriterimport java.io.*;class EncodeStream {public static void main(String[] args) throws IOException{writeText();readText();}public static void readText() throws IOException{InputStreamReader isr = new InputStreamReader(new FileInputStream("utf.txt"),"UTF-8");char[] buf = new char[10];int len =isr.read(buf);String str = new String(buf,0,len);System.out.println(str);isr.close();}public static void writeText() throws IOException{OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"),"UTF-8");osw.write("你好");osw.close();}}3.2 编码:
字符串变字节数组:String-->byte[];
byte[] getBytes(String charsetName):使用指定的字符集将此String解码为字节序列,并存储到数组中。
3.3 解码:
字节数组变字符串:byte[]-->String;
String(byte[],charsetName):将数组变成字符串。
- /*
- 编码:字符串变字节数组;解码:字节数组变字符串。
- 注意utf-8和GBK都识别中文的情况,因为都是别中文,它们之间解码会造成源码改变的情形。
- */
- import java.util.*;
- class EncodeDemo
- {
- public static void main(String[] args) throws Exception
- {
- String s = "你好";
- //编码
- byte[] b1 = s.getBytes("gbk");
- System.out.println(Arrays.toString(b1));
- //错误解码 因为ISO8859-1是拉丁码表,不包含中文码表,必然造成乱码。
- String s1 = new String(b1,"ISO8859-1");
- System.out.println("s1="+s1);
- //再编码 虽然s1是乱码,但也是一个字符对象,它的字节数组未变。
- //所以我们依旧可以通过字节数组获取正确的解码值。
- byte[] b2 = s1.getBytes("ISO8859-1");
- System.out.println(Arrays.toString(b2));
- //正确解码
- String s2 = new String(b2,"gbk");
- System.out.println("s2="+s2);
- }
- }
/*编码:字符串变字节数组;解码:字节数组变字符串。注意utf-8和GBK都识别中文的情况,因为都是别中文,它们之间解码会造成源码改变的情形。*/import java.util.*;class EncodeDemo{public static void main(String[] args) throws Exception{String s = "你好";//编码byte[] b1 = s.getBytes("gbk");System.out.println(Arrays.toString(b1));//错误解码 因为ISO8859-1是拉丁码表,不包含中文码表,必然造成乱码。String s1 = new String(b1,"ISO8859-1");System.out.println("s1="+s1);//再编码 虽然s1是乱码,但也是一个字符对象,它的字节数组未变。//所以我们依旧可以通过字节数组获取正确的解码值。byte[] b2 = s1.getBytes("ISO8859-1"); System.out.println(Arrays.toString(b2));//正确解码String s2 = new String(b2,"gbk");System.out.println("s2="+s2);}}运行结果:
十、应用
- /*
- 有五个学生,每个学生有3门课的成绩。
- 从键盘输入以上数据(包括姓名,三门课成绩),
- 输入的格式:如:zhangsan,30,40,60计算出总成绩。
- 并把学生的信息和总分数从高到低存放在磁盘文件"stud.txt"中。
- 步骤:
- 1,描述学生对象。
- 2,定义一个可操作学生对象的工具类。
- 思路:
- 1,通过获取键盘录入一行数据,并将该行中的信息取出封装成学生对象。
- 2,学生有很多,那么就需要存储,使用到集合。要对学生的总分排序。可以使用TreeSet。
- 3,将集合的信息写入到一个文件中。
- */
- import java.io.*;
- import java.util.*;
- class Student implements Comparable<Student>
- {
- private String name;
- private int ma,cn,en;
- private int sum;
- Student(String name,int ma,int cn,int en)
- {
- this.name = name;
- this.ma = ma;
- this.cn = cn;
- this.en = en;
- sum = ma + cn + en;
- }
- //自然排序:返回负整数、零或正整数。根据此对象是小于、等于还是大于指定对象。
- public int compareTo(Student s)
- {
- int num = new Integer(this.sum).compareTo(new Integer(s.sum));
- if(sum==0)
- return this.name.compareTo(s.name);
- return num;
- }
- public String getName()
- {
- return name;
- }
- public int getSum()
- {
- return sum;
- }
- public int hashCode()
- {
- return name.hashCode()+sum*78;//保证值的唯一性。
- }
- //判断是否是同一个人。
- public boolean equals(Object obj)
- {
- if(!(obj instanceof Student))
- throw new ClassCastException("类型不匹配");
- Student s = (Student)obj;
- return this.name.equals(s.name) && this.sum==s.sum;
- }
- public String toString()
- {
- return "student["+name+","+ma+","+cn+","+en+"]";
- }
- }
- class StudentInfoTool
- {
- //默认比较方式
- public static Set<Student> getStudents() throws IOException
- {
- return getStudents(null);
- }
- //比较器
- public static Set<Student> getStudents(Comparator<Student> cmp) throws IOException
- {
- BufferedReader bufr =
- new BufferedReader(new InputStreamReader(System.in));
- String line = null;
- Set<Student> stus = null;
- if(cmp==null)
- stus = new TreeSet<Student>();
- else
- stus = new TreeSet<Student>(cmp);
- while((line=bufr.readLine())!=null)
- {
- if("over".equals(line))
- break;
- String[] info = line.split(",");
- //解析一个字符串,并返回一个整数
- Student stu = new Student(info[0],Integer.parseInt(info[1]),
- Integer.parseInt(info[2]),
- Integer.parseInt(info[3]));
- stus.add(stu);
- }
- bufr.close();
- return stus;
- }
- public static void writeFile(Set<Student> stus) throws IOException
- {
- BufferedWriter bufw = new BufferedWriter(new FileWriter("stuinfo.txt"));
- for(Student stu : stus)
- {
- bufw.write(stu.toString() + "\t");
- bufw.write(stu.getSum() + "");
- bufw.newLine();
- bufw.flush();
- }
- bufw.close();
- }
- }
- class StudentInfoTest
- {
- public static void main(String[] args) throws IOException
- {
- //定义比较器,强行逆转
- Comparator<Student> cmp = Collections.reverseOrder();
- Set<Student> stus = StudentInfoTool.getStudents(cmp);
- StudentInfoTool.writeFile(stus);
- }
- }
0 0
- 黑马程序员——IO(二)——File类、Properties类、打印流、序列流等 .
- 黑马程序员--IO(二)--File类、Properties类、打印流、序列流等
- 黑马程序员——Java基础---IO(二)--File类、Properties类、打印流、序列流(合并流)
- 黑马程序员——Java基础---IO(二)--File类、Properties类、打印流、序列流(合并流)
- 黑马程序员——Java基础---IO(二)--File类、Properties类、打印流、序列流(合并流)
- 黑马程序员——Java基础---IO(二)--File类、Properties类、打印流、序列流(合并流)
- 黑马程序员——Java基础---IO(二)--File类、Properties类、打印流、序列流、其他类
- 黑马程序员——Java基础:IO(二):File类、Properties类、打印流、序列流......
- 黑马程序员——IO流(File类、Properties类、打印流、序列流)
- 黑马程序员——Java IO流(二)之流操作规律总结、File类、Properties类、序列流等
- 黑马程序员——Java基础---IO、File类、Properties类、打印流、序列流(合并流)
- 黑马程序员——Java基础----IO(File类、Properties类、打印流、序列流、合并流)(3)
- 黑马程序员--IO(二)-File类、Properties类、打印流、序列流(合并流)
- 黑马程序员——IO流(二)内存操作流、打印流、标准输入输出流、序列流、Properties类
- 黑马程序员——IO File类,Properties,打印流 ,合并流
- 黑马程序员——Java基础——File、Properties类、 打印流、序列流
- 黑马程序员——IO流--Properties类、打印流等常见类
- 黑马程序员--Java基础学习之IO流之File类、Properties对象、打印流、序列流等
- 在haproxy+nginx+php-fpm环境下访问网站URL被自动加上了nginx服务的私网端口号的故障分析
- 会话管理-cookie,session,禁用cookie的URL重写,token的单态设计模式,异常抛出,UUID,MD5,base64编码
- pvcreate 创建pv出现 Can't initialize physical volume"/dev/loop2" of volume group "cinder-volumes2" witho
- 360浏览器td问题
- webview的物理返回键和顶部返回键的写法
- 黑马程序员——IO(二)——File类、Properties类、打印流、序列流等 .
- xml形式装配bean——spring in action chapter 2
- 计算机书目分享
- jquery ui 页面布局多个table,多个tab,earyui-datagid的fit失效,中间的table格式不对齐
- 在 Linux 上配置一个 syslog 服务器
- 个人windows工作中故障集合
- A cycle was detected in the build path of project
- Android4.4深入浅出之SurfaceFlinger总体结构
- 在RDO自动安装的OpenStack Liberty单机中搭建能够内外网互访的网络(五)-创建云主机实例