一、缓冲流

1.1、字符流的缓冲区

缓冲区的出现是为了提高IO的读写效率

对应类

BufferedReader

BufferedWriter

缓冲区要结合流才可以使用

在流的基础上对流的功能进行了增强

1.2、BufferedReader、BufferedWriter

public class BufferedWriterextends Writer

构造方法摘要
BufferedWriter(Writer out)
创建一个使用默认大小输出缓冲区的缓冲字符输出流。
BufferedWriter(Writer out,
int sz)


创建一个使用给定大小输出缓冲区的新缓冲字符输出流。

将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。

可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。

该类提供了 newLine() 方法,它使用平台自己的行分隔符概念,此概念由系统属性 line.separator 定义。并非所有平台都使用新行符 ('\n') 来终止各行。因此调用此方法来终止每个输出行要优于直接写入新行符。

通常 Writer 将其输出立即发送到底层字符或字节流。除非要求提示输出,否则建议用 BufferedWriter 包装所有其 write() 操作可能开销很高的 Writer(如 FileWriters 和 OutputStreamWriters)。例如,

PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("foo.out")));

将缓冲 PrintWriter 对文件的输出。如果没有缓冲,则每次调用 print() 方法会导致将字符转换为字节,然后立即写入到文件,而这是极其低效的。

public class BufferedReaderextends Reader

构造方法摘要
BufferedReader(Reader in)
创建一个使用默认大小输入缓冲区的缓冲字符输入流。
BufferedReader(Reader in,
int sz)

创建一个使用指定大小输入缓冲区的缓冲字符输入流。

从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。

可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。

通常,Reader 所作的每个读取请求都会导致对底层字符或字节流进行相应的读取请求。因此,建议用 BufferedReader 包装所有其 read() 操作可能开销很高的 Reader(如 FileReader 和 InputStreamReader)。例如,

BufferedReader in
= new BufferedReader(new FileReader("foo.in"));

将缓冲指定文件的输入。如果没有缓冲,则每次调用 read() 或 readLine() 都会导致从文件中读取字节,并将其转换为字符后返回,而这是极其低效的。

通过用合适的 BufferedReader 替代每个 DataInputStream,可以对将 DataInputStream 用于文字输入的程序进行本地化

1.3、示例

在创建缓冲区之前,必须先有流对象

package com.pb.io.demo2;

/**
创建缓冲区之间必须,先创建流
使用缓冲区,要flush,刷新
*/
/**
创建缓冲区之间必须,先创建流
使用缓冲区,要flush,刷新
*/
import java.io.*;
class BufferedReaderAndWriterDemo1
{
public static void main(String[] args)
{
File source=new File("d:\\demo.txt");
File objFile=new File("d:\\a.txt");
useBuffered(source,objFile);
}
public static void useBuffered(File source,File objFile){ BufferedReader br=null;
BufferedWriter bw=null; try{
//声明缓冲区对象,并将字符流做为参数传入
br=new BufferedReader(new FileReader(source));
bw=new BufferedWriter(new FileWriter(objFile));
//接收读取的长度
String line=null; //读取内容并放入buf中,判断是不是最后
while((line=br.readLine())!=null){
//写入,有多长写多长
bw.write(line,0,line.length());
bw.newLine(); }
System.out.println("读写完成!");
}catch(IOException e){
System.out.println(e.toString()); }finally{
try{
if(bw!=null)
bw.flush();
bw.close();
}catch(IOException e){
System.out.println(e.toString());
}
try{
if(br!=null)
br.close(); }catch(IOException e){
System.out.println(e.toString());
} }
}
}

二、MyBuffered

2.1、示例

package com.pb.io.demo2;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException; public class MyBuffered { private FileReader fr; public MyBuffered(FileReader fr) {
this.fr = fr;
} /*
* 可以一次读一行的方法
*/
public String myReadLine() throws IOException {
// 定义一个临时 容器,原BufferedReader封装的是字符数组
// 为了方便,定义一个StringBuilder
StringBuilder sb = new StringBuilder();
int ch = 0; while ((ch = fr.read()) != -1) {
if (ch == '\r') {
continue;
}
if (ch == '\n') {
return sb.toString();
}
// 读一个存一个
sb.append((char)ch);
}
if(sb.length()!=0){
return sb.toString();
}
return null;
}
public void myClose() throws IOException{
if (fr != null)
fr.close();
}
public static void main(String[] args) throws IOException { File file=new File("d:\\demo.txt");
MyBuffered mb=new MyBuffered(new FileReader(file));
String line=null;
while((line=mb.myReadLine())!=null){
System.out.println(line);
}
mb.myClose();
}
}

三、装饰设计模式

3.1、装饰设计模式

装饰设计模式:

当想要对已经有的对象进行功能增强时,

可以定义类,将已经对象传入,基于已经有的功能,并提供加强功能。

那么自定义的该类就称为装饰类

package com.pb.io.demo3;

class Person{
public void eat(){
System.out.println("吃饭");
}
}
class SuperPerson{
private Person p; public SuperPerson(Person p) {
super();
this.p = p;
} public void superEat(){
System.out.println("开胃酒");
p.eat();
System.out.println("甜点");
System.out.println("来一根");
}
} public class PersonTest { public static void main(String[] args) {
Person p=new Person();
p.eat();
SuperPerson sp=new SuperPerson(p);
sp.superEat(); } }

3.2、与继承之间的关系

装饰类通常会通过构造方法接收被被装饰的对象,并基于被装饰的对象的功能,提供更强的功能

装饰模式比继承灵活,避免了继承体系臃肿

而且降低了类于类之间的关系

装饰类因为增强已有对象,具备的功能和已经的是相同的,只不过提供了更强的功能。

所以装饰类和被装饰类通常是都属于一个体系中。

四、LineNumberReader

4.1、

构造方法摘要
LineNumberReader(Reader in)
使用默认输入缓冲区的大小创建新的行编号 reader。
LineNumberReader(Reader in,
int sz)

创建新的行编号
reader,将字符读入给定大小的缓冲区。


方法摘要
int getLineNumber()

获得当前行号。
void mark(int readAheadLimit)

标记该流中的当前位置。
int read()

读取单个字符。
int read(char[] cbuf,
int off, int len)

将字符读入数组中的某一部分。
String readLine()

读取文本行。
void reset()

将该流重新设置为最新的标记。
void setLineNumber(int lineNumber)

设置当前行号。
long skip(long n)

跳过字符。


4.2、示例

package com.pb.io.demo4;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader; public class LineNumberReaderDemo { public static void main(String[] args) {
LineNumberReader lnr=null;
try {
lnr=new LineNumberReader(new FileReader("d:\\demo.txt"));
//设置开始行叼
lnr.setLineNumber(100);
String line=null;
while((line=lnr.readLine())!=null){
//getLineNumber默认从1开始,设置后从设置的行号开始
System.out.println(lnr.getLineNumber()+":"+line);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
if(lnr!=null)
lnr.close();
} catch (IOException e) {
e.printStackTrace();
}
} } }

4.3、自定义

package com.pb.io.demo4;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader; public class MyLineNumberReader { private Reader reader;
private int lineNumber; public MyLineNumberReader(Reader reader) {
this.reader = reader;
} /*
* 读一行
*/
public String myReadLine() throws IOException {
lineNumber++;
StringBuilder sb = new StringBuilder();
int ch=0;
while ((ch = reader.read()) != -1) {
if (ch == '\r')
continue;
if (ch == '\n')
return sb.toString();
sb.append((char) ch);
}
if (sb.length() != 0)
return sb.toString();
return null; } public void myClose() throws IOException{
reader.close();
} public Reader getReader() {
return reader;
} public void setReader(Reader reader) {
this.reader = reader;
} public int getLineNumber() {
return lineNumber;
} public void setLineNumber(int lineNumber) {
this.lineNumber = lineNumber;
} public static void main(String[] args) throws IOException {
File file=new File("d:\\demo.txt");
MyLineNumberReader mln=new MyLineNumberReader(new FileReader(file));
String line=null;
//mln.setLineNumber(100);
while((line=mln.myReadLine())!=null){
System.out.println(mln.getLineNumber()+":"+line);
}
mln.myClose();
} }

五、字节流

5.1、字节流

OutputStream写、InputStream读

FileOutputStream、FileInputStream 字节流输出、输入流

构造方法摘要
OutputStream()


方法摘要
void close()

关闭此输出流并释放与此流有关的所有系统资源。
void flush()

刷新此输出流并强制写出所有缓冲的输出字节。
void write(byte[] b)

b.length 个字节从指定的 byte 数组写入此输出流。
void write(byte[] b,
int off, int len)

将指定 byte 数组中从偏移量 off 开始的
len 个字节写入此输出流。
abstract
void
write(int b)

将指定的字节写入此输出流。


构造方法摘要
InputStream()


方法摘要
int available()

返回此输入流下一个方法调用可以不受阻塞地从此输入流读取(或跳过)的估计字节数。
void close()

关闭此输入流并释放与该流关联的所有系统资源。
void mark(int readlimit)

在此输入流中标记当前的位置。
boolean markSupported()

测试此输入流是否支持 markreset 方法。
abstract
int
read()

从输入流中读取数据的下一个字节。
int read(byte[] b)

从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
int read(byte[] b,
int off, int len)

将输入流中最多 len 个数据字节读入 byte
数组。
void reset()

将此流重新定位到最后一次对此输入流调用 mark 方法时的位置。
long skip(long n)

跳过和丢弃此输入流中数据的 n 个字节。


5.2、FileInputStream与FileOutputStream

package com.pb.io.demo5;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException; /**
* 使用字节流复制文件
*
*/
public class FileOutputAndFileInputStreamDemo { public static void main(String[] args) {
File source=new File("d:\\1.jpg");
File objFile=new File("d:\\4.jpg");
InputAndOutput2(source,objFile);
}
public static void InputAndOutput(File source,File objFile){
//声明字节流输入,输出对象
FileInputStream fis=null;
FileOutputStream fos=null;
try {
fis=new FileInputStream(source);
fos=new FileOutputStream(objFile);
//声明缓冲区
byte [] buf=new byte[1024];
int len=0;
while((len=fis.read(buf))!=-1){
fos.write(buf, 0, len);
}
System.out.println("=======读写完成========");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
if(fos!=null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fis!=null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
} }
/*
* 不建议使用
*/
public static void InputAndOutput2(File source,File objFile){
//声明字节流输入,输出对象
FileInputStream fis=null;
FileOutputStream fos=null;
try {
fis=new FileInputStream(source);
fos=new FileOutputStream(objFile);
//声明大小刚刚缓冲区
byte [] buf=new byte[fis.available()]; //不建议使用available fis.read(buf);
fos.write(buf,0,buf.length); System.out.println("=======读写完成========");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
if(fos!=null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fis!=null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
} }
}

六、字节流缓冲区

6.1、示例复制.mp3

package com.pb.io.demo5;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException; public class BufferedStreamDemo { public static void main(String[] args) {
File file1=new File("d:\\gm.mp3");
File file2=new File("d:\\mg.mp3");
copy1(file1,file2);
}
public static void copy1(File file1,File file2){
//声明字节流缓冲区对象
BufferedOutputStream bos=null;
BufferedInputStream bis=null;
try {
bis=new BufferedInputStream(new FileInputStream(file1));
bos=new BufferedOutputStream(new FileOutputStream(file2)); //声明缓冲区大小
int by=0;
while((by=bis.read())!=-1){
bos.write(by);
} } catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
if(bos!=null)
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(bis!=null)
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
} } }

6.2、自定义

package com.pb.io.demo5;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream; public class MyBufferedStream { private InputStream is;
private byte[] buf = new byte[1024]; private int pos = 0;
private int count = 0; public MyBufferedStream(InputStream is) {
this.is = is;
} /*
* 一次读一个字节,从缓冲区的(字节数组)获取
*/
public int myRead() throws IOException {
// 通过in对象读取硬盘上的数据,并存在buf中
if (count == 0) {
count = is.read(buf);
if (count < 0) {
return -1;
}
pos = 0;
byte b = buf[pos];
count--;
pos++;
return b & 0xff;
} else if (count > 0) {
byte b = buf[pos];
count--;
pos++;
return b & 0xff;
}
return -1; } public void myClose() throws IOException {
is.close();
} public static void main(String[] args) {
File file1 = new File("d:\\gm.mp3");
File file2 = new File("d:\\kk.mp3");
copy1(file1, file2);
} public static void copy1(File file1, File file2) {
// 声明字节流缓冲区对象
BufferedOutputStream bos = null;
MyBufferedStream mbs = null;
try {
mbs = new MyBufferedStream(new FileInputStream(file1));
bos = new BufferedOutputStream(new FileOutputStream(file2)); // 声明缓冲区大小
int by = 0;
while ((by = mbs.myRead()) != -1) {
bos.write(by);
} } catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bos != null)
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
mbs.myClose();
} catch (IOException e) {
e.printStackTrace();
}
} } }

七、转换

7.1、字节流转换字符流

InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。

BufferedReader in
= new BufferedReader(new InputStreamReader(System.in));

OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。

Writer out
= new BufferedWriter(new OutputStreamWriter(System.out));

7.2、示例

接收键盘输入,使用转换流实现

package com.pb.io.demo6;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
/**
* 接收键盘输入,并将内容写入文件
*
*/
public class Demo { public static void main(String[] args) { BufferedReader br=null;
BufferedWriter bw=null; try {
//声明输入流,使用转换流实现
br=new BufferedReader(new InputStreamReader(System.in)); //声明输出流,使用转换流实现
bw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("d:\\t.txt"))); //接收键盘录入
String str=null;
System.out.println("请输入要写入文件的内容,输入over结束!");
//输入over结束
while(!(str=br.readLine()).equals("over")){
bw.write(str);
bw.newLine();
} } catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
if(bw!=null)
bw.close();
} catch (IOException e) {
e.printStackTrace();
} try {
if(br!=null)
br.close();
} catch (IOException e) {
e.printStackTrace();
}
} } }

八、流操作的规律

1、明确源和目的

  源:输入流。InputStream,Reader

  目的:输出流 .OutputStream,Writer

2、操作的数据是否为纯文本

  是:字符流。Reader、Writer

  不是:字节流。InputStream、OutputStream

3、当体系明确后,在明确要使用哪个具体对象。

  通过设备来进行区别。

  源设备:内存、硬盘、键盘

  目的设备:内存、硬盘、控制台

九、重定向IO

9.1、示例

//设置输入流的
System.setIn(new FileInputStream("d:\\demo.txt"));
//设置的输出流的方向
System.setOut(new PrintStream("d:\\zz.txt"));

十、系统信息

10.1、示例

package com.pb.io.demo6;

import java.util.Properties;

public class Demo1 {

    public static void main(String[] args) {
Properties prop=System.getProperties();
prop.list(System.out); //可以设置为输出流对象new PrintWriter(文件)
} }
-- listing properties --
java.runtime.name=Java(TM) SE Runtime Environment
sun.boot.library.path=C:\Java\jre1.8.0_60\bin
java.vm.version=25.60-b23
java.vm.vendor=Oracle Corporation
java.vendor.url=http://java.oracle.com/
path.separator=;
java.vm.name=Java HotSpot(TM) 64-Bit Server VM
file.encoding.pkg=sun.io
user.script=
user.country=CN
sun.java.launcher=SUN_STANDARD
sun.os.patch.level=
java.vm.specification.name=Java Virtual Machine Specification
user.dir=F:\work\IODemo
java.runtime.version=1.8.0_60-b27
java.awt.graphicsenv=sun.awt.Win32GraphicsEnvironment
java.endorsed.dirs=C:\Java\jre1.8.0_60\lib\endorsed
os.arch=amd64
java.io.tmpdir=C:\Users\ADMINI~1\AppData\Local\Temp\
line.separator= java.vm.specification.vendor=Oracle Corporation
user.variant=
os.name=Windows 7
sun.jnu.encoding=GBK
java.library.path=C:\Java\jre1.8.0_60\bin;C:\Windows\Su...
java.specification.name=Java Platform API Specification
java.class.version=52.0
sun.management.compiler=HotSpot 64-Bit Tiered Compilers
os.version=6.1
user.home=C:\Users\Administrator
user.timezone=
java.awt.printerjob=sun.awt.windows.WPrinterJob
file.encoding=UTF-8
java.specification.version=1.8
user.name=Administrator
java.class.path=F:\work\IODemo\bin
java.vm.specification.version=1.8
sun.arch.data.model=64
java.home=C:\Java\jre1.8.0_60
sun.java.command=com.pb.io.demo6.Demo1
java.specification.vendor=Oracle Corporation
user.language=zh
awt.toolkit=sun.awt.windows.WToolkit
java.vm.info=mixed mode
java.version=1.8.0_60
java.ext.dirs=C:\Java\jre1.8.0_60\lib\ext;C:\Window...
sun.boot.class.path=C:\Java\jre1.8.0_60\lib\resources.jar...
java.vendor=Oracle Corporation
file.separator=\
java.vendor.url.bug=http://bugreport.sun.com/bugreport/
sun.cpu.endian=little
sun.io.unicode.encoding=UnicodeLittle
sun.desktop=windows
sun.cpu.isalist=amd64

JAVA基础学习day20--IO流二-缓冲流、字节流的更多相关文章

  1. 【java基础学习】IO流

    IO流 字节流InputStream和OutputStream 字符流Writer和Reader 装饰模式

  2. java基础学习总结——GUI编程(二)

    一.事件监听

  3. java基础学习总结——GUI编程(二) 未学习

    一.事件监听

  4. JAVA基础学习之路(二)方法定义,重载,递归

    一,方法的定义: package test; public class test1 { public static void main(String args[]) { int result = ad ...

  5. 转载-java基础学习汇总

    共2页: 1 2 下一页  Java制作证书的工具keytool用法总结 孤傲苍狼 2014-06-24 11:03 阅读:25751 评论:3     Java基础学习总结——Java对象的序列化和 ...

  6. java基础(24):转换流、缓冲流

    1. 转换流 在学习字符流(FileReader.FileWriter)的时候,其中说如果需要指定编码和缓冲区大小时,可以在字节流的基础上,构造一个InputStreamReader或者OutputS ...

  7. java的 IO流之缓冲流(转载)

    java缓冲流本身不具IO功能,只是在别的流上加上缓冲提高效率,像是为别的流装上一种包装.当对文件或其他目标频繁读写或操作效率低,效能差.这时使用缓冲流能够更高效的读写信息.因为缓冲流先将数据缓存起来 ...

  8. java:IO流(处理流(缓冲流,转换流,数据流),对象的序列化,Properties)

    字节缓冲流:(BufferedInputStream,BufferedOutStream) *按照流的功能来分:节点流和处理流 *节点流可以直接操作数据源: *InputStream *--FileI ...

  9. java - >IO流_缓冲流(高效流)

    缓冲流(高效流) 在我们学习字节流与字符流的时候,大家都进行过读取文件中数据的操作,读取数据量大的文件时,读取的速度会很慢,很影响我们程序的效率,那么,我想提高速度,怎么办? Java中提高了一套缓冲 ...

随机推荐

  1. AngularJS的学习--TodoMVC的分析

    最近一段时间一直在看AngularJS,趁着一点时间总结一下. 官网地址:http://angularjs.org/ 先推荐几个教程 1. AngularJS入门教程 比较基础,是官方Tutorial ...

  2. Testing - Selenium

    Selenium http://www.seleniumhq.org/ User Guide http://www.seleniumhq.org/docs/ Webdriver中文社区 http:// ...

  3. 开放产品开发(OPD):开篇

    OPD?这是什么玩意?google一下.忘记说了,最近google被封锁的厉害,那就百度一下吧.可惜,OPD找不出是什么.你今天你找不到是正常的,因为之前还没有OPD,而现在才开始有OPD这个东东.相 ...

  4. Android 学习笔记之AndBase框架学习(五) 数据库ORM..注解,数据库对象映射...

    PS:好久没写博客了... 学习内容: 1.DAO介绍,通用DAO的简单调度过程.. 2.数据库映射关系... 3.使用泛型+反射+注解封装通用DAO.. 4.使用AndBase框架实现对DAO的调用 ...

  5. 一个android应用开发的感悟

    对于客户端的开发,以我个人现在的水准,很难进行一个系统的讲解,只能分享下遇到的几个问题点好了! 1:对于tabhost的使用,这个东西真的是过时了:第一个版本,我是用的tabhost确实是很难用,不过 ...

  6. .NET中Main函数使用小技巧

    摘要:任何语言开发出来的程序,都会有一个程序入口函数,可能每个语言所使用的程序入口函数名称不一样,但是它们的作用都是一样的,都是被操作系统去调用.那么本文主要总结.NET中的程序入口函数Main使用的 ...

  7. Apache log4net™ 手册——概述【翻译】

    原文地址 本文内容 概述 框架 日志(Loggers)和追加器(Appenders) 日志层次(Logger hierarchy) 追加器(Appenders) 筛选(Filters) 布局(Layo ...

  8. SQL Server case when 日期字符串转换 多表查询 嵌套子查询

    select distinct stu.*, dbo.GetClassNameByStudentCode(stu.Code) as ClassName, dbo.GetCourseNameByStud ...

  9. 【循序渐进学Python】4. Python中的序列——字典

    字典是Python内建的六种序列之一.字典作为一种常用的数据结构,字典中的值没有特定顺序,每个值都对应于一个唯一的键.键可以是数字.字符串甚至是元组. 1. 创建和使用字典 Python中字典可以使用 ...

  10. 【Bootstrap基础学习】04 Bootstrap的HTML和CSS编码规范

    HTML 用两个空格来代替制表符(tab) -- 这是唯一能保证在所有环境下获得一致展现的方法. 嵌套元素应当缩进一次(即两个空格) 对于属性的定义,确保全部使用双引号,绝不要使用单引号. 不要在自闭 ...