1.基本IO操作
有时候我们编写的程序除了自身会定义一些数据信息外,还需要引用外界的数据,或是将自身的数据发送到外界,这时我们需要使用输入与输出。
1)输入与输出
2)节点流与处理流
按照流是否直接与特定的地方(如磁盘、内存、设备等)相连,分为节点流和处理流两类。
处理流的构造方法总是要带一个其他的流对象做参数,一个流对象经过其他流的多次包装,称为流的链接。
3)IS和OS常用方法
InputStream是所有字节输入流的父类,其定义了基础的读取方法,如下:
OutputStream是所有字节输出流的父类,其定义了基础的写出方法,如下:
4)创建FOS对象(重写模式)
FileOutputStream是文件的字节输出流,使用该流可以以字节为单位将数据写入文件。
注意点:若指定的文件已经包含内容,那么当使用FOS对其写入数据时,会将该文件中原有的数据全部清楚。
案例1:FOS覆盖方式写文件
import java.io.FileOutputStream;
import java.io.IOException;
public class Test {
	public static void main(String[] args) throws IOException  {
		FileOutputStream fos = new FileOutputStream("fos.txt");
		String str = "Hello,World";
		byte[] date = str.getBytes();
		fos.write(date);
		System.out.println("写出完毕");
		fos.close();
	}
}
5)创建FOS对象(追加模式)
若想在文件的原有数据之后追加新数据,则需要以下构造方法创建FOS:
以上两个构造方法中,第二个参数若为true,那么通过该FOS写出的数据都是在文件末尾追加的。
案例2:FOS追加模式写文件
import java.io.FileOutputStream;
import java.io.IOException;
public class Test {
	public static void main(String[] args) throws IOException  {
		FileOutputStream fos = new FileOutputStream("fos.txt",true);
		String str = "Hello,World";
		byte[] date = str.getBytes();
		fos.write(date);
		System.out.println("写出完毕");
		fos.close();
	}
}
6)创建FIS对象
FileInputStream是文件的字节输入流,我们使用该流可以以字节为单位从文件中读取数据。FileInputStream有两个常用的构造方法,如下:
案例3:FIS读取文件数据
import java.io.FileInputStream;
import java.io.IOException;
public class Test {
	public static void main(String[] args) throws IOException {
		FileInputStream fis = new FileInputStream("fos.txt");
		int d = -1;
		while((d=fis.read())!=-1){
			System.out.print((char)d);
		}
		fis.close();
	}
}
/*
 * 运行结果:
 * Hello,WorldHello,World
 */
7)read()和write(int d)方法
FileInputStream继承自InputStream,其提供了以字节为单位读取文件数据的方法read。
FileOutputStream继承自OutputStream,其提供了以字节为单位向文件写数据的方法write。
8)read(byte[] d)和write(byte[] d)方法
FileInputStream也支持批量读取字节数据的方法:
FileOutputStream也支持批量写出字节数据的方法:
2.缓冲流
1)BOS基本工作原理
在向硬件设备做写出操作时,增大写出次数无疑会降低写出效率,为此我们可以使用缓冲输出流来一次性批量写出若干数据减少写出次数来提高写出效率。BufferedOutputStream缓冲输出流内部维护着一个缓冲区,每当我们向该流写数据时,都会先将数据存入缓冲区,当缓冲区已满时,缓冲流会将数据一次性全部写出。
案例4:BOS演示
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Test {
	public static void main(String[] args) throws IOException {
		//创建缓冲字节输出流
		FileOutputStream fos = new FileOutputStream("fos.txt");
		//所有字节被存入缓冲区,等待一次性写出
		BufferedOutputStream bos = new BufferedOutputStream(fos);
		String str = "Hello Java";
		byte[] date = str.getBytes();
		bos.write(date);
		//关闭流之前,缓冲输出流会将缓冲区内容一次性写出
		bos.close();
		System.out.println("写出完毕");
	}
}
2)BOS的flush方法
使用缓冲输出流可以提高写出效率,但是这也存在一个问题,就是写出数据缺乏即时性。有时需要在执行完某些写出操作后,就希望将这些数据确实写出,而不是在缓冲区中保存知道缓冲区满后才写出。这就需要flush方法了。
案例5:flush演示
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Test {
	public static void main(String[] args) throws IOException {
		FileOutputStream fos = new FileOutputStream("fos.txt");
		BufferedOutputStream bos = new BufferedOutputStream(fos);
		String str = "Hello Java";
		byte[] date = str.getBytes();
		bos.write(date);
		bos.flush(); //强制将缓冲区已缓存数据一次性写出
		System.out.println("写出完毕");
		bos.close();
	}
}
3)BIS基本工作原理
在读取数据时若以字节为单位读取数据,会导致读取次数过于频繁从而大大降低读取效率。为此我们可以通过提高一次读取的字节数量减少读写次数来提高读取的效率。BufferedInputStream是缓冲字节输入流。其内部维护着一个缓冲区(字节数组),使用该流在读取一个字节时,该流会尽可能多的一次性读取若干字节并存入缓冲区,然后逐一的将字节返回,直到缓冲区中的数据被全部读取完毕,会再次读取若干字节从而反复,这样就减少了读取的次数,提高了读取效率。
BIS是一个处理流,该流为我们提供了缓冲功能。
案例6:BIS演示
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
public class Test {
	public static void main(String[] args) throws IOException {
		//创建缓冲字节输入流
		FileInputStream fis = new FileInputStream("fos.txt");
		BufferedInputStream bis = new BufferedInputStream(fis);
		int d = -1;
		//缓冲读入,实际上并非是一个字节一个字节从文件读取
		while((d=bis.read())!=-1){
			System.out.println(d);
		}
		bis.close();
	}
}
3.对象流
1)对象序列化
对象是存在于内存中的。有时候我们需要将对象保存到硬盘上或者将对象传输到另一台计算机上等,这时需要将对象转换为一个字节序列,而这个过程就成为对象序列化。相反,这样一个字节序列需要将其转换为对应的对象,这个过程成为对象的反序列化。

2)OOS实现对象序列化
ObjectOutputStream是用来对对象进行序列化的输出流,方法如下:
3)OIS实现对象反序列化
ObjectInputStream是用来对对象进行反序列化的输入流,方法如下:
4)Serializable接口
ObjectOutputStream在对对象进行序列化时有一个要求,就是需要序列化的对象所属的类必须实现Serializable接口。实现该接口不需要重写任何方法,其只是作为可序列化的标志。通常实现该接口的类需要提供一个常量serialVersionUID,表明该类的版本。若不显示的声明,在对象序列化时也会根据当前类的各个方面计算该类的默认serialVersionUID,但不同平台编译器实现有所不同,所以若想跨平台,都应显示的声明版本号。
如果声明的类的对象序列化存到硬盘上面,之后随着需求的变化更改了类的属性(增加或减少或改名),那么当反序列化时,就会出现InvalidClassException,这样会造成不兼容的问题。但当serialVersionUID相同时,它就会将不一样的field以type的预设值反序列化,可避开不兼容性问题。
案例7:OOS序列化演示
//创建类Person
import java.io.Serializable;
import java.util.List;
public class Person implements Serializable{
	private static final long serialVersionUID = 1L;
	private String name;
	private int age;
	private char gender;
	private double salary;
	private List<String> otherInfo;
	
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + ", gender=" + gender + ", salary=" + salary + ", otherInfo="
				+ otherInfo + "]";
	}
	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 char getGender() {
		return gender;
	}
	public void setGender(char gender) {
		this.gender = gender;
	}
	public double getSalary() {
		return salary;
	}
	public void setSalary(double salary) {
		this.salary = salary;
	}
	public List<String> getOtherInfo() {
		return otherInfo;
	}
	public void setOtherInfo(List<String> otherInfo) {
		this.otherInfo = otherInfo;
	}
}
进行序列化
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/*
* 对象流是一组高级流,作用是方便读写java中任何对象.
* 对象输出流,可以写出java对象
* 工作原理:将给定java对象按照其结构转化为一组字节然后写出.
*/
public class OOSDemo {
	public static void main(String[] args) throws IOException {
		Person p = new Person();  //引用对象并设置相关信息
		p.setName("暮");
		p.setAge(20);
		p.setGender(‘男‘);
		p.setSalary(5000);
		List<String> otherInfo = new ArrayList<String>();
		otherInfo.add("是一位诗人");
		otherInfo.add("也是一位程序猿");
		p.setOtherInfo(otherInfo);
		System.out.println(p.toString());
		
		//开始写出对象
		FileOutputStream fos = new FileOutputStream("mu.obj");
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		//当调用oos.writeObject时,实际上做了两件事:将p对象按照结果转换为了一组字节(对象序列化)
		//然后再将这组字节通过FOS写入到文件中,将数据写入硬盘的过程成为:持久化
		oos.writeObject(p);     //对象序列化
		//可能会报错: java.io.NotSerializableException,这是因为对象所属的类没有继承Serializable接口,需要继承接口并生成serialVersionUID
		System.out.println("序列化完成");
		oos.close();
	}
}
/*
 * 运行结果:
 * 反序列化完成
 * Person [name=暮, age=20, gender=男, salary=5000.0, otherInfo=[是一位诗人, 也是一位程序猿]]
 */
案例8:反序列化演示
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class OISDemo {
	public static void main(String[] args) throws ClassNotFoundException, IOException {
		FileInputStream fis = new FileInputStream("mu.obj");
		ObjectInputStream ois = new ObjectInputStream(fis);
		Person p = (Person)ois.readObject();
		System.out.println(p);
		System.out.println("反序列化完成");
		ois.close();
	}
}
/*
 * 运行结果:
 * Person [name=暮, age=20, gender=男, salary=5000.0, otherInfo=[是一位诗人, 也是一位程序猿]]
 * 反序列化完成
 */
5)transient关键字
对象在序列化后得到的字节序列往往比较大,有时我们在对一个对象进行序列化时可以忽略某些不必要的属性,从而对序列化后得到的字节序列“瘦身”。
案例9:transient演示
//修改类Person
import java.io.Serializable;
import java.util.List;
public class Person implements Serializable{
	private static final long serialVersionUID = 1L;
	private String name;
	private int age;
	private char gender;
	transient private double salary;  //修改部分
	transient private List<String> otherInfo;  //修改部分
	
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + ", gender=" + gender + ", salary=" + salary + ", otherInfo="
				+ otherInfo + "]";
	}
	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 char getGender() {
		return gender;
	}
	public void setGender(char gender) {
		this.gender = gender;
	}
	public double getSalary() {
		return salary;
	}
	public void setSalary(double salary) {
		this.salary = salary;
	}
	public List<String> getOtherInfo() {
		return otherInfo;
	}
	public void setOtherInfo(List<String> otherInfo) {
		this.otherInfo = otherInfo;
	}
}
序列化操作
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/*
* 对象流是一组高级流,作用是方便读写java中任何对象.
* 对象输出流,可以写出java对象
* 工作原理:将给定java对象按照其结构转化为一组字节然后写出.
*/
public class OOSDemo {
	public static void main(String[] args) throws IOException {
		Person p = new Person();  //引用对象并设置相关信息
		p.setName("暮");
		p.setAge(20);
		p.setGender(‘男‘);
		p.setSalary(5000);
		List<String> otherInfo = new ArrayList<String>();
		otherInfo.add("是一位诗人");
		otherInfo.add("也是一位程序猿");
		p.setOtherInfo(otherInfo);
		System.out.println(p.toString());
		
		//开始写出对象
		FileOutputStream fos = new FileOutputStream("mu1.obj");
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		//当调用oos.writeObject时,实际上做了两件事:将p对象按照结果转换为了一组字节(对象序列化)
		//然后再将这组字节通过FOS写入到文件中,将数据写入硬盘的过程成为:持久化
		oos.writeObject(p);     //对象序列化
		//可能会报错: java.io.NotSerializableException,这是因为对象所属的类没有继承Serializable接口,需要继承接口并生成serialVersionUID
		System.out.println("序列化完成");
		oos.close();
	}
}
/*
 * 运行结果:
 * 反序列化完成
 * Person [name=暮, age=20, gender=男, salary=5000.0, otherInfo=[是一位诗人, 也是一位程序猿]]
 */
反序列化,会发现加上transient关键字的部分输出变成了默认值输出。
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class OISDemo {
	public static void main(String[] args) throws ClassNotFoundException, IOException {
		FileInputStream fis = new FileInputStream("mu1.obj");
		ObjectInputStream ois = new ObjectInputStream(fis);
		Person p = (Person)ois.readObject();
		System.out.println(p);
		System.out.println("反序列化完成");
		ois.close();
	}
}
/*
 * 运行结果:
 * Person [name=暮, age=20, gender=男, salary=0.0, otherInfo=null]
 * 反序列化完成
 */
4.字符转换流
1)Reader和Writer
字符流原理:
Reader常用方法:
Writer的常用方法:
2)字符转换流原理
3)指定字符编码
IntputStreamReader的构造方法允许我们设置字符集:
OutputStreamWriter的构造方法:
4)OSW
案例10:OSW演示
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
public class Test {
	public static void main(String[] args) throws IOException {
		FileOutputStream fos = new FileOutputStream("demo.txt");
		OutputStreamWriter writer = new OutputStreamWriter(fos,"UTF-8");
		String str = "Java,你好!";
		writer.write(str);
		writer.close();
	}
}
5)ISR
案例11:ISR演示
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class Test {
	public static void main(String[] args) throws IOException {
		FileInputStream fis = new FileInputStream("demo.txt");
		InputStreamReader reader = new InputStreamReader(fis,"utf-8");
		int c = -1;
		while((c=reader.read())!=-1){
			System.out.print((char)c);
		}
		reader.close();
	}
}
/*
运行结果:
Java,你好!
*/
5.缓冲字符流
1)PrintWriter
  PrintWriter是具有自动行刷新的缓冲字符输出流,提供了丰富的构造方法:
PrintWriter(File file) PrintWriter(String fileName) PrintWriter(OutputStream out) PrintWriter(OutputStream out,boolean autoFlush) PrintWriter(Writer writer) PrintWriter(Writer writer,boolean autoFlush)
其中OutputStream与Writer的构造方法提供了一个可传入boolean值参数,该参数用于表示PrintWriter是否具有自动行刷新。
2)PrintWriter的重载print和println方法
使用PrintWriter写出字符串时我们通常不使用Writer提供的writer()相关方法,而是使用print和println等方法,PrintWriter提供了若干重载的print和println方法,其中println方法是在写出数据后自动追加一个系统支持的换行符。
重载方法:
void print(int i) //打印整数 void print(char c) //打印字符 void print(boolean b) //打印boolean值 void print(char[] c) //打印字符数组 void print(double d) //打印double值 void print(float t) //打印float值 void print(long l) //打印long值 void print(String str) //打印字符串
println的方法参数与上面相同。
3)使用PW输出字符数据
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
public class PW {
	public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {
		FileOutputStream fos = new FileOutputStream("pw.txt");
		OutputStreamWriter osw = new OutputStreamWriter(fos,"GBK");
		//创建带有自动行刷新的PW
		PrintWriter pw = new PrintWriter(osw,true);
		pw.println("或许青春根本就没有明天");
		pw.println("青春只有现在");
		System.out.println("写出完毕");
		pw.close();
	}
}
4)BufferedReader
BufferedReader是缓冲字符输入流,其内部提供了缓冲区,可以提高读取效率。
常用构造方法:
BufferedReader(Reader reader)
创建一个BufferedRader缓冲字符输入流:
FileInputStream fis = new FileInputStream("pw.txt");
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(isr);
因为BufferedReader在构造实例时需要传入一个字符流,所以当我们想基于一个字节流进行读取时,要先将字节流转换为字符流后猜可以创建缓冲字符输入流BufferedReader。
5)BR读取字符串
BufferedReader读取一行字符串:
String readLine()
该方法连续读取一行字符串,直到读取到换行符为止,返回的字符串中不包含该换行符,若EOF则返回null。
6)使用BR读取一行字符串
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class BR {
	public static void main(String[] args) throws IOException {
		FileInputStream fis = new FileInputStream("pw.txt");
		InputStreamReader isr = new InputStreamReader(fis);
		BufferedReader br = new BufferedReader(isr);
		String str = null;
		while((str=br.readLine())!=null){
			System.out.println(str);
		}
		br.close();
	}
}
6.案例演示
案例1:FileOutputStream写操作演示
import java.io.FileOutputStream;
import java.io.IOException;
public class FOS {
	public static void main(String[] args) throws IOException {
		FileOutputStream fos = new FileOutputStream("fos.txt");
		String str = "如果这注定是座荒芜的坟墓,那么我将硬生生的变成一座美丽的花园!";
		byte[] date = str.getBytes("GBK");
		fos.write(date);
		System.out.println("写出完毕");
		fos.close();
	}
}
案例2:FileInputStream读操作演示
import java.io.FileInputStream;
import java.io.IOException;
public class FIS {
	public static void main(String[] args) throws IOException {
		FileInputStream fis = new FileInputStream("fos.txt");
		byte[] date = new byte[100];
		int len = fis.read(date);
		String str = new String(date,0,len,"GBK");
		System.out.println(str);
		fis.close();
	}
}
//运行结果:如果这注定是座荒芜的坟墓,那么我将硬生生的变成一座美丽的花园!
案例3:缓冲输出流写操作演示
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class BOSDemo {
	public static void main(String[] args) throws IOException {
		FileOutputStream fos = new FileOutputStream("bos.txt");
		BufferedOutputStream bos = new BufferedOutputStream(fos);
		String str = "东风吹落花满路,一片相思却似无.";
		byte[] date = str.getBytes("GBK");
		bos.write(date);
		bos.flush(); 
		System.out.println("写出完毕");
		bos.close();
	}
}
案例4:缓冲输入流读操作演示
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
public class BOSDemo {
	public static void main(String[] args) throws IOException{
		FileInputStream fis = new FileInputStream("bos.txt");
		BufferedInputStream bis = new BufferedInputStream(fis);
		byte[] date = new byte[100];
		int len = bis.read(date);
		String str = new String(date,0,len,"GBK");
		System.out.println(str);
		bis.close();
	}
}
/*
运行结果:
东风吹落花满路,一片相思却似无.
*/
案例5:使用文件流复制文件
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class CopyDemo {
	public static void main(String[] args) throws IOException {
		FileInputStream fis = new FileInputStream("src.mp3");
		FileOutputStream fos = new FileOutputStream("desc.mp3");
		byte[] date = new byte[1024*10];
		int len = -1;
		while((len = fis.read(date))!=-1){
			fos.write(date,0,len);
		}
		System.out.println("复制完毕");
		fis.close();
		fos.close();
	}
}
案例6:缓冲流复制文件
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/*
 * 缓冲流 缓冲流是一对高级流,作用是提高读写效率
 */
public class CopyDemo02 {
	public static void main(String[] args) throws IOException {
		FileInputStream fis = new FileInputStream("music.mp3");
		BufferedInputStream bis = new BufferedInputStream(fis);
		
		FileOutputStream fos = new FileOutputStream("musiccopy.mp3");
		BufferedOutputStream bos = new BufferedOutputStream(fos);
		
		int d = -1;
		while((d=bis.read())!=-1){
			bos.write(d);
		}
		System.out.println("复制完毕");
		bis.close();
		bos.close();
	}
}
案例7:OSW转换流写出演示
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
public class OSW {
	public static void main(String[] args) throws IOException {
		FileOutputStream fos = new FileOutputStream("osw.txt");
		OutputStreamWriter osw = new OutputStreamWriter(fos,"GBK");
		osw.write("我从远方来,带着笔墨纸砚");
		osw.write("我打江南走过,留下一纸旧约");
		System.out.println("写出完毕");		
		osw.close();
	}
}
案例8:ISR转换流读取演示
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class ISR {
	public static void main(String[] args) throws IOException {
		FileInputStream fis = new FileInputStream("osw.txt");
		InputStreamReader isr = new InputStreamReader(fis,"GBK");
		int d = -1;
		while((d=isr.read())!=-1){
			System.out.print((char)d);
		}
		isr.close();
	}
}
/*
运行结果:
我从远方来,带着笔墨纸砚我打江南走过,留下一纸旧约
*/
案例9:PW缓冲字符输入流直接对文件写操作
import java.io.FileNotFoundException;
import java.io.PrintWriter;
/*
 * PW提供了直接针对文件写操作的构造方法:
 * PrintWriter(String path)
 * PrintWriter(File file)
 * 按照指定字符集对文件写操作:
 * PrintWriter(String path,String csn)
 * PrintWriter(File file,String csn)
 */
public class PWDemo {
	public static void main(String[] args) throws FileNotFoundException {
		PrintWriter pw = new PrintWriter("PW.txt");
		pw.println("青春是一座坟墓");
		pw.println("流年走过");
		pw.println("留下不同的墓志铭");
		System.out.println("写出完毕");
		pw.close();
	}
}
案例10:简易记事本
要求:程序启动后要求用户输入文件名,然后针对该文件写入数据,每输入一行字符串就按行写入文件中,输入"exit"时退出程序,最后将用户写出的数据读取到控制台上。
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Scanner;
public class Note {
	public static void main(String[] args) throws IOException {
		Scanner scan = new Scanner(System.in);
		System.out.println("请输入文件名:");
		String fileName = scan.nextLine();
		PrintWriter pw = new PrintWriter(fileName,"GBK");
		System.out.println("请输入内容:");
		while(true){
			String line = scan.nextLine();
			if(line.equals("exit")){
				break;
			}
			pw.println(line);
		}
		System.out.println("写出完毕,程序退出");
		pw.close();
		System.out.println("=====输入内容展示=====");
		FileInputStream fis = new FileInputStream(fileName);
		InputStreamReader isr = new InputStreamReader(fis,"GBK");
		BufferedReader br = new BufferedReader(isr);
		String line = null;
		while((line=br.readLine())!=null){
			System.out.println(line);
		}
		br.close();
	}
}
/*
运行结果:
请输入文件名:
note.txt
请输入内容:
青春是一场盛宴
不繁华不罢休
exit
写出完毕,程序退出
=====输入内容展示=====
青春是一场盛宴
不繁华不罢休
 */
原文:http://www.cnblogs.com/jmwm/p/6931503.html