java基础回顾笔记-io流1
2016-03-07 22:09
411 查看
第一部分:一些对象的使用
system对象中可通过getProperties方法获取Propety对象,从而获取系统属性。
Runtime,每个java应用程序都有一个Runtime实例,使应用程序能够与其运行的环境相连接。
只能通过Runtime.getRuntime方法获取该对象实例,即单例设计。
exec方法可以在单独的进程中执行一条windows指令。
Date对象默认的是格林威治时间
DateFormat是抽象类不能创建对象,我们通常用他的实现类SimpleDateFormat来实现日期的格式化
Calender,对日期中每个单独的时间获取和操作比较方便
Math,指数,对数,平方根,三角函数等数学运算
Math.abs(),返回绝对值
Math.ceil(),返回大于指定数据的最小整数。
Math.floor,返回小鱼指定数据的最大整数。
Math.round,四舍五入
Math.pow(2,3),2的3次方,指数运算
Math.random,取0-1之间的随机数(包括0不包括1),(int)(Math.random()*10+1)–取1-10之间随机数(包括1和10)
Random,生成伪随机数的工具类,
第二部分:I/O流
流按操作数据分为两种:字节流、字符流。
流按流向分为:输入流、输出流。
数据在硬盘、内存或者网络中的体现形式都是字节。
字符流对象里面融合了编码表,可以指定用哪种编码方式解析字符(utf_8,jbk…),字符流是基于字节流的。
字节流的抽象基类:InputStream、OutputStream 字符流的抽象基类:Reader、Writer
注:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。
如:InputStream的子类FileInputStream。
如:Reader的子类FileReader
例子1:FileWriter创建文件并写入,步骤如下:
/*创建一个FileWriter对象,该对象一被初始化就必须要明确被操作的文件。
*而且该文件会被创建到指定的目录下,如果该目录下已有同名文件,将被覆盖。
*/
FileWriter fw = new FileWriter(“demo.txt”);
//条用write方法,将字符串写入到流中。
fw.write(“abcde”);
//刷新流对象中的缓冲数据,将数据刷到目的地。
fw.flush();
//关闭刘子源,但是关闭之前会刷新一次内部缓冲中的数据,将数据刷到目的地。
fw.close();
例子2:对已有文件的数据续写,如下:
//传递一个true参数,代表不覆盖已有的文件,并在有的文件末尾处进行数据的续写。
FileWriter fw = new FileWriter(“demo.txt”);
注:linux系统中\n代表换行,windows系统中\r\n代表换行。
例子3:读取字符文件,如下:
//创建一个文件读取流对象,和指定名称的文件想关联。如果文件不存在会跑出异常FileNotFounException
FileReader fr = new FileReader(“demo.txt”);
//调用流对象的read方法,硬盘中存放的多部分内容之间是有分隔符的,当遇到一个文件的结尾的分隔符的时候说明已经读完,系统会返回-1.
char[] buf = new char[1024];
int num = 0;
while((num = fr.read(buf)) != -1){
System.out.println(new String(buf,0,num));
}
//关闭资源
fr.close();
联系1:把c盘中的一个文本文件复制到d盘,如下:
复制的原理:其实就是将c盘下的文件数据存储到d盘的一个同名文件中。
步骤:
1.在d盘创建一个同名文件,用于存储c盘文件夹中的数据。
2.定义读取流和c盘文件关联
3.通过不断的读写完成数据存储
4.关闭资源
FileWriter fw = new FileWriter(“demo_copy.txt”);
FileReader fr = new FileReader(“demo.txt”);
char[] buf = new char[1024];
int len = 0;
while((len = fr.read(buf)) != -1){
fw.write(buf,0,len);
}
字符流缓冲区的原理:缓冲区存在于内存中,要结合流才可以使用,把读取到的一段段的流的内容先
缓存到内存中,然后再写入到硬盘。这样避免了频繁的读写操作,不需要每读一次就写一次,起到一个
中间缓冲的作用。
缓冲区的出现提高了对数据流的读写效率,所以在创建缓冲区之前必须先有流对象,
在流的基础上对流的功能进行了增强。
对应类:
1.BufferedWriter:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数据和字符串的高效写入。
可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。
例子:
//创建一个字符写入流对象
FileWriter fw = new FileWriter(“demo.txt”);
//为了提高字符写入流效率,加入了缓冲技术。
//只要将需要被提高效率的流对象作为参数换递给缓冲区的构造函数即可。
BufferedWriter bufw = new BufferedWriter(fw);
bufw.write(“adfded”);
//用到缓冲区就要刷新
bufw.flush();
//关闭缓冲区就是在关闭缓冲区中的流对象。
bufw.close();
2.BufferedReader:
例子:
/*
通过缓冲区复制一个.java文件。
*/
import java.io.*;
class CopyTextByBuf
{
public static void main(String[] args)
{
BufferedReader bufr = null;
BufferedWriter bufw = null;
try
{
bufr = new BufferedReader(new FileReader(“BufferedWriterDemo.java”));
bufw = new BufferedWriter(new FileWriter(“bufWriter_Copy.txt”));
String line = null;
while((line=bufr.readLine())!=null)
{
bufw.write(line);
bufw.newLine();
bufw.flush();
}
}
catch (IOException e)
{
throw new RuntimeException(“读写失败”);
}
finally
{
try
{
if(bufr!=null)
bufr.close();
}
catch (IOException e)
{
throw new RuntimeException(“读取关闭失败”);
}
try
{
if(bufw!=null)
bufw.close();
}
catch (IOException e)
{
throw new RuntimeException(“写入关闭失败”);
}
}
}
}
注:readLine方法返回的时候只返回回车符之前的数据内容,并不返回回车符。
readLine原理:无论是读一行还是读取多个字符,其实最终都是在硬盘上一个一个读取。
所以最终使用的还是read方法一次读一个的方法。
字符缓冲区原理:字符缓冲区的原理就是在缓冲区BufferedWriter对象中创建了一个对象,
这个对象中存储了一个字符数组,过程中不断的把传过来的字符存储到这个数组中过渡,从
而起到缓存的作用。
装饰设计模式:
/*
当想要对已有的对象进行功能增强时,
可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。
那么自定义的该类称为装饰类。
装饰类通常会通过构造方法接收被装饰的对象。
并基于被装饰的对象的功能,提供更强的功能。
*/
class Person
{
public void chifan()
{
System.out.println(“吃饭”);
}
}
class SuperPerson
{
private Person p ;
SuperPerson(Person p)
{
this.p = p;
}
public void superChifan()
{
System.out.println(“开胃酒”);
p.chifan();
System.out.println(“甜点”);
System.out.println(“来一根”);
}
}
class PersonDemo
{
public static void main(String[] args)
{
Person p = new Person();
//p.chifan();
SuperPerson sp = new SuperPerson(p);
sp.superChifan();
}
}
//从上面装饰设计模式原理可以看出,BufferedWriter类也是装饰设计模式的类,FileWriter就是被装饰的类。
装饰和继承的区别:
/*
MyReader//专门用于读取数据的类。
|–MyTextReader
|–MyBufferTextReader
|–MyMediaReader
|–MyBufferMediaReader
|–MyDataReader
|–MyBufferDataReader
class MyBufferReader
{
MyBufferReader(MyTextReader text)
{}
MyBufferReader(MyMediaReader media)
{}
}
上面这个类扩展性很差。
找到其参数的共同类型。通过多态的形式。可以提高扩展性。
class MyBufferReader extends MyReader
{
private MyReader r;
MyBufferReader(MyReader r)
{}
}
MyReader//专门用于读取数据的类。
|–MyTextReader
|–MyMediaReader
|–MyDataReader
|–MyBufferReader
以前是通过继承将每一个子类都具备缓冲功能。
那么继承体系会复杂,并不利于扩展。
现在优化思想。单独描述一下缓冲内容。
将需要被缓冲的对象。传递进来。也就是,谁需要被缓冲,谁就作为参数传递给缓冲区。
这样继承体系就变得很简单。优化了体系结构。
装饰模式比继承要灵活。避免了继承体系臃肿。
而且降低了类于类之间的关系。
装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能。
所以装饰类和被装饰类通常是都属于一个体系中的。所以装饰类的优点是体现在java的
api体系设计上,而不是语法和逻辑上。
*/
字节流写文件:
//字符流底层也是用的字节流,只不过需要把字节进行缓冲。
//字节流写文件不需要刷新,因为是对最小单位字节进行处理,直接进行读写,不需要缓冲。
/*
复制一个图片–例子:
思路:
1,用字节读取流对象和图片关联。
2,用字节写入流对象创建一个图片文件。用于存储获取到的图片数据。
3,通过循环读写,完成数据的存储。
4,关闭资源。
*/
import java.io.*;
class CopyPic
{
public static void main(String[] args)
{
FileOutputStream fos = null;
FileInputStream fis = null;
try
{
fos = new FileOutputStream(“c:\2.bmp”);
fis = new FileInputStream(“c:\1.bmp”);
byte[] buf = new byte[1024];
int len = 0;
while((len=fis.read(buf))!=-1)
{
fos.write(buf,0,len);
}
}
catch (IOException e)
{
throw new RuntimeException(“复制文件失败”);
}
finally
{
try
{
if(fis!=null)
fis.close();
}
catch (IOException e)
{
throw new RuntimeException(“读取关闭失败”);
}
try
{
if(fos!=null)
fos.close();
}
catch (IOException e)
{
throw new RuntimeException(“写入关闭失败”);
}
}
}
}
注:不要用字符流去拷贝媒体文件,因为字符流自带默认编码,如果编码格式不对照会导致拷贝后的文件乱码。
//通过字节流的缓冲区完成复制。
public static void copy_1()throws IOException
{
BufferedInputStream bufis = new BufferedInputStream(new FileInputStream(“c:\0.mp3”));
BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream(“c:\1.mp3”));
int by = 0;
while((by=bufis.read())!=-1)
{
bufos.write(by);
}
bufos.close();
bufis.close();
}
自定义字节流缓冲区:
import java.io.*;
class MyBufferedInputStream
{
private InputStream in;
private byte[] buf = new byte[1024*4];
private int pos = 0,count = 0;
MyBufferedInputStream(InputStream in)
{
this.in = in;
}
//一次读一个字节,从缓冲区(字节数组)获取。
public int myRead()throws IOException
{
//通过in对象读取硬盘上数据,并存储buf中。
if(count==0)
{
count = in.read(buf);
if(count<0)
return -1;
pos = 0;
byte b = buf[pos];
count–;
pos++;
return b&255;
}
else if(count>0)
{
byte b = buf[pos];
count–;
pos++;
return b&0xff; //0xff默认是整形,所以,一个byte跟0xff相与会先将那个byte转化成整形运算,这样,结果中的高的24个比特就总会被清0,于是结果总是我们想要的。
}
return -1;
}
public void myClose()throws IOException
{
in.close();
}
}
/*
11111111-111111110000000000101001001010100101010010101001010
byte: -1 —> int : -1;
00000000 00000000 00000000 11111111 255
11111111 11111111 11111111 11111111
11111111 –>提升了一个int类型 那不还是-1吗?是-1的原因是因为在8个1前面补的是1导致的。
那么我只要在前面补0,即可以保留原字节数据不变,又可以避免-1的出现。
怎么补0呢?
11111111 11111111 11111111 11111111
0000-0001
1111-1110
000000001
1111-1111 -1
结论:
字节流的读一个字节的read方法为什么返回值类型不是byte,而是int。
因为有可能会读到连续8个二进制1的情况,8个二进制1对应的十进制是-1.
那么就会数据还没有读完,就结束的情况。因为我们判断读取结束是通过结尾标记-1来确定的。
所以,为了避免这种情况将读到的字节进行int类型的提升。
并在保留原字节数据的情况前面了补了24个0,变成了int类型的数值。
而在写入数据时,只写该int类型数据的最低8位。
*/
system对象中可通过getProperties方法获取Propety对象,从而获取系统属性。
Runtime,每个java应用程序都有一个Runtime实例,使应用程序能够与其运行的环境相连接。
只能通过Runtime.getRuntime方法获取该对象实例,即单例设计。
exec方法可以在单独的进程中执行一条windows指令。
Date对象默认的是格林威治时间
DateFormat是抽象类不能创建对象,我们通常用他的实现类SimpleDateFormat来实现日期的格式化
Calender,对日期中每个单独的时间获取和操作比较方便
Math,指数,对数,平方根,三角函数等数学运算
Math.abs(),返回绝对值
Math.ceil(),返回大于指定数据的最小整数。
Math.floor,返回小鱼指定数据的最大整数。
Math.round,四舍五入
Math.pow(2,3),2的3次方,指数运算
Math.random,取0-1之间的随机数(包括0不包括1),(int)(Math.random()*10+1)–取1-10之间随机数(包括1和10)
Random,生成伪随机数的工具类,
第二部分:I/O流
流按操作数据分为两种:字节流、字符流。
流按流向分为:输入流、输出流。
数据在硬盘、内存或者网络中的体现形式都是字节。
字符流对象里面融合了编码表,可以指定用哪种编码方式解析字符(utf_8,jbk…),字符流是基于字节流的。
字节流的抽象基类:InputStream、OutputStream 字符流的抽象基类:Reader、Writer
注:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。
如:InputStream的子类FileInputStream。
如:Reader的子类FileReader
例子1:FileWriter创建文件并写入,步骤如下:
/*创建一个FileWriter对象,该对象一被初始化就必须要明确被操作的文件。
*而且该文件会被创建到指定的目录下,如果该目录下已有同名文件,将被覆盖。
*/
FileWriter fw = new FileWriter(“demo.txt”);
//条用write方法,将字符串写入到流中。
fw.write(“abcde”);
//刷新流对象中的缓冲数据,将数据刷到目的地。
fw.flush();
//关闭刘子源,但是关闭之前会刷新一次内部缓冲中的数据,将数据刷到目的地。
fw.close();
例子2:对已有文件的数据续写,如下:
//传递一个true参数,代表不覆盖已有的文件,并在有的文件末尾处进行数据的续写。
FileWriter fw = new FileWriter(“demo.txt”);
注:linux系统中\n代表换行,windows系统中\r\n代表换行。
例子3:读取字符文件,如下:
//创建一个文件读取流对象,和指定名称的文件想关联。如果文件不存在会跑出异常FileNotFounException
FileReader fr = new FileReader(“demo.txt”);
//调用流对象的read方法,硬盘中存放的多部分内容之间是有分隔符的,当遇到一个文件的结尾的分隔符的时候说明已经读完,系统会返回-1.
char[] buf = new char[1024];
int num = 0;
while((num = fr.read(buf)) != -1){
System.out.println(new String(buf,0,num));
}
//关闭资源
fr.close();
联系1:把c盘中的一个文本文件复制到d盘,如下:
复制的原理:其实就是将c盘下的文件数据存储到d盘的一个同名文件中。
步骤:
1.在d盘创建一个同名文件,用于存储c盘文件夹中的数据。
2.定义读取流和c盘文件关联
3.通过不断的读写完成数据存储
4.关闭资源
FileWriter fw = new FileWriter(“demo_copy.txt”);
FileReader fr = new FileReader(“demo.txt”);
char[] buf = new char[1024];
int len = 0;
while((len = fr.read(buf)) != -1){
fw.write(buf,0,len);
}
字符流缓冲区的原理:缓冲区存在于内存中,要结合流才可以使用,把读取到的一段段的流的内容先
缓存到内存中,然后再写入到硬盘。这样避免了频繁的读写操作,不需要每读一次就写一次,起到一个
中间缓冲的作用。
缓冲区的出现提高了对数据流的读写效率,所以在创建缓冲区之前必须先有流对象,
在流的基础上对流的功能进行了增强。
对应类:
1.BufferedWriter:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数据和字符串的高效写入。
可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。
例子:
//创建一个字符写入流对象
FileWriter fw = new FileWriter(“demo.txt”);
//为了提高字符写入流效率,加入了缓冲技术。
//只要将需要被提高效率的流对象作为参数换递给缓冲区的构造函数即可。
BufferedWriter bufw = new BufferedWriter(fw);
bufw.write(“adfded”);
//用到缓冲区就要刷新
bufw.flush();
//关闭缓冲区就是在关闭缓冲区中的流对象。
bufw.close();
2.BufferedReader:
例子:
/*
通过缓冲区复制一个.java文件。
*/
import java.io.*;
class CopyTextByBuf
{
public static void main(String[] args)
{
BufferedReader bufr = null;
BufferedWriter bufw = null;
try
{
bufr = new BufferedReader(new FileReader(“BufferedWriterDemo.java”));
bufw = new BufferedWriter(new FileWriter(“bufWriter_Copy.txt”));
String line = null;
while((line=bufr.readLine())!=null)
{
bufw.write(line);
bufw.newLine();
bufw.flush();
}
}
catch (IOException e)
{
throw new RuntimeException(“读写失败”);
}
finally
{
try
{
if(bufr!=null)
bufr.close();
}
catch (IOException e)
{
throw new RuntimeException(“读取关闭失败”);
}
try
{
if(bufw!=null)
bufw.close();
}
catch (IOException e)
{
throw new RuntimeException(“写入关闭失败”);
}
}
}
}
注:readLine方法返回的时候只返回回车符之前的数据内容,并不返回回车符。
readLine原理:无论是读一行还是读取多个字符,其实最终都是在硬盘上一个一个读取。
所以最终使用的还是read方法一次读一个的方法。
字符缓冲区原理:字符缓冲区的原理就是在缓冲区BufferedWriter对象中创建了一个对象,
这个对象中存储了一个字符数组,过程中不断的把传过来的字符存储到这个数组中过渡,从
而起到缓存的作用。
装饰设计模式:
/*
当想要对已有的对象进行功能增强时,
可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。
那么自定义的该类称为装饰类。
装饰类通常会通过构造方法接收被装饰的对象。
并基于被装饰的对象的功能,提供更强的功能。
*/
class Person
{
public void chifan()
{
System.out.println(“吃饭”);
}
}
class SuperPerson
{
private Person p ;
SuperPerson(Person p)
{
this.p = p;
}
public void superChifan()
{
System.out.println(“开胃酒”);
p.chifan();
System.out.println(“甜点”);
System.out.println(“来一根”);
}
}
class PersonDemo
{
public static void main(String[] args)
{
Person p = new Person();
//p.chifan();
SuperPerson sp = new SuperPerson(p);
sp.superChifan();
}
}
//从上面装饰设计模式原理可以看出,BufferedWriter类也是装饰设计模式的类,FileWriter就是被装饰的类。
装饰和继承的区别:
/*
MyReader//专门用于读取数据的类。
|–MyTextReader
|–MyBufferTextReader
|–MyMediaReader
|–MyBufferMediaReader
|–MyDataReader
|–MyBufferDataReader
class MyBufferReader
{
MyBufferReader(MyTextReader text)
{}
MyBufferReader(MyMediaReader media)
{}
}
上面这个类扩展性很差。
找到其参数的共同类型。通过多态的形式。可以提高扩展性。
class MyBufferReader extends MyReader
{
private MyReader r;
MyBufferReader(MyReader r)
{}
}
MyReader//专门用于读取数据的类。
|–MyTextReader
|–MyMediaReader
|–MyDataReader
|–MyBufferReader
以前是通过继承将每一个子类都具备缓冲功能。
那么继承体系会复杂,并不利于扩展。
现在优化思想。单独描述一下缓冲内容。
将需要被缓冲的对象。传递进来。也就是,谁需要被缓冲,谁就作为参数传递给缓冲区。
这样继承体系就变得很简单。优化了体系结构。
装饰模式比继承要灵活。避免了继承体系臃肿。
而且降低了类于类之间的关系。
装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能。
所以装饰类和被装饰类通常是都属于一个体系中的。所以装饰类的优点是体现在java的
api体系设计上,而不是语法和逻辑上。
*/
字节流写文件:
//字符流底层也是用的字节流,只不过需要把字节进行缓冲。
//字节流写文件不需要刷新,因为是对最小单位字节进行处理,直接进行读写,不需要缓冲。
/*
复制一个图片–例子:
思路:
1,用字节读取流对象和图片关联。
2,用字节写入流对象创建一个图片文件。用于存储获取到的图片数据。
3,通过循环读写,完成数据的存储。
4,关闭资源。
*/
import java.io.*;
class CopyPic
{
public static void main(String[] args)
{
FileOutputStream fos = null;
FileInputStream fis = null;
try
{
fos = new FileOutputStream(“c:\2.bmp”);
fis = new FileInputStream(“c:\1.bmp”);
byte[] buf = new byte[1024];
int len = 0;
while((len=fis.read(buf))!=-1)
{
fos.write(buf,0,len);
}
}
catch (IOException e)
{
throw new RuntimeException(“复制文件失败”);
}
finally
{
try
{
if(fis!=null)
fis.close();
}
catch (IOException e)
{
throw new RuntimeException(“读取关闭失败”);
}
try
{
if(fos!=null)
fos.close();
}
catch (IOException e)
{
throw new RuntimeException(“写入关闭失败”);
}
}
}
}
注:不要用字符流去拷贝媒体文件,因为字符流自带默认编码,如果编码格式不对照会导致拷贝后的文件乱码。
//通过字节流的缓冲区完成复制。
public static void copy_1()throws IOException
{
BufferedInputStream bufis = new BufferedInputStream(new FileInputStream(“c:\0.mp3”));
BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream(“c:\1.mp3”));
int by = 0;
while((by=bufis.read())!=-1)
{
bufos.write(by);
}
bufos.close();
bufis.close();
}
自定义字节流缓冲区:
import java.io.*;
class MyBufferedInputStream
{
private InputStream in;
private byte[] buf = new byte[1024*4];
private int pos = 0,count = 0;
MyBufferedInputStream(InputStream in)
{
this.in = in;
}
//一次读一个字节,从缓冲区(字节数组)获取。
public int myRead()throws IOException
{
//通过in对象读取硬盘上数据,并存储buf中。
if(count==0)
{
count = in.read(buf);
if(count<0)
return -1;
pos = 0;
byte b = buf[pos];
count–;
pos++;
return b&255;
}
else if(count>0)
{
byte b = buf[pos];
count–;
pos++;
return b&0xff; //0xff默认是整形,所以,一个byte跟0xff相与会先将那个byte转化成整形运算,这样,结果中的高的24个比特就总会被清0,于是结果总是我们想要的。
}
return -1;
}
public void myClose()throws IOException
{
in.close();
}
}
/*
11111111-111111110000000000101001001010100101010010101001010
byte: -1 —> int : -1;
00000000 00000000 00000000 11111111 255
11111111 11111111 11111111 11111111
11111111 –>提升了一个int类型 那不还是-1吗?是-1的原因是因为在8个1前面补的是1导致的。
那么我只要在前面补0,即可以保留原字节数据不变,又可以避免-1的出现。
怎么补0呢?
11111111 11111111 11111111 11111111
&00000000 00000000 00000000 11111111
00000000 00000000 00000000 111111110000-0001
1111-1110
000000001
1111-1111 -1
结论:
字节流的读一个字节的read方法为什么返回值类型不是byte,而是int。
因为有可能会读到连续8个二进制1的情况,8个二进制1对应的十进制是-1.
那么就会数据还没有读完,就结束的情况。因为我们判断读取结束是通过结尾标记-1来确定的。
所以,为了避免这种情况将读到的字节进行int类型的提升。
并在保留原字节数据的情况前面了补了24个0,变成了int类型的数值。
而在写入数据时,只写该int类型数据的最低8位。
*/
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树