-----------android培训、java培训、java学习型技术博客、期待与您交流!------------
六、流操作规律总结
1.明确源和目的:
源:
字符流:FileReader(纯文本文件)。
字节流:FileInputStream(非纯文本文件)、System.in(键盘录入)。
目的:
字符流:FileWriter(纯文本文件)。
字节流:FileOutputStream(非纯文本文件)、System.out(控制台输出)。
2.明确是否需要加入缓冲区来提高效率:
源:需要提高效率。
字符流:BufferedReader(字符流读取缓冲区)。
字节流:BufferedInputStream(字节流读取缓冲区)。
目的:需要提高效率。
字符流:BufferedWriter(字符流写入缓冲区)。
字节流:BufferedOutputStream(字节流写入缓冲区)。
3.对字节流操作是否需要加入转换流:转换流在转换时,可以指定编码表。
源:需要。
字节流:InputStreamReader(字节流转字符流) 。
目的:需要。
字节流:OutputStreamWriter(字符流转字节流)。
注:下面两种情况下,应加入转换流进行操作。
情况1:源或目的操作需要进行键盘录入(System.in)或控制台输入(System.out)。
源:InputStreamReader isr=new InputStreamReader(System.in)
目的:OutputStreamWriter osr=new OutputStreamWriter(System.out)
情况2:文件读取或写入时,需要指定编码表。
源:InputStreamReader isr=new InputStreamReader(new FileInputStream("指定路径下文件名"),"编码表")
目的:OutputStreamReader osr=new OutputStreamReader(new FileOutputStream("指定路径下文件名"),"编码表")
七、File类
1.概述:
File类用来将文件或者文件夹封装成对象,方便对文件与文件夹的属性信息进行操作。File对象可以作为参数传递给流的构造函数。
2.常用方法:
1)创建:
boolean createNewFile():在指定位置创建文件,如果该文件已经存在,则不创建,返回false。。
boolean mkdir():创建文件夹,一次只能创建一层。
boolean mkdirs():创建多级文件夹。
2)删除:
boolean delete():删除失败返回false,如果文件正在被使用,则删除不了返回false。
void deleteOnExit():在程序退出时删除指定文件。即使发生异常也不会影响删除操作。
3)判断:
boolean exises():文件是否存在。
isFile():判断是否为文件。
isDrectory():判断是否为目录。
isHidden():判断是否为隐藏文件或目录。
isAbsolute():测试此抽象路径名是否为绝对路径名。
4)获取:
getName():返回由此抽象路径名表示的文件或目录的名称。
getPath():获取相对路径,如果抽象路径名为绝对路径,则获取的为绝对路径。
getParent():返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回 null。
getAbsolutePath():返回此抽象路径名的绝对路径名字符串。
lastModified():返回此抽象路径名表示的文件最后一次被修改的时间。
3.代码练习:
练习1:列出目录下的所有内容(递归)。
import java.io.*;
class FileDemo2
{
public static void main(String[] args)
{
File dir=new File("F:\\电影和电视剧\\电影");
showDir(dir,0);
}
//给各级目录文件加带层次
public static String getLevel(int level)
{
StringBuilder sb=new StringBuilder();
for (int x=0;x<=level ;x++ )
{
if (x==level)
{
sb.append("|--");
}
else
sb.append("| ");
}
return sb.toString();
}
//用递归遍历多级目录的所有内容
public static void showDir(File dir,int level)
{
//打印目录
System.out.println(getLevel(level)+dir);
level++;
//把该目录下的所有文件对象都存储到数组中
File[] files=dir.listFiles();
//对数组进行遍历
for (int i=0;i<files.length ;i++ )
{
//如果文件是目录,则继续遍历
if (files[i].isDirectory())
{
showDir(files[i],level);
}
else
//打印该文件名
System.out.println(getLevel(level)+files[i].getName());
}
}
}
程序的运行结果的部分截图如下所示:
练习2:删除指定目录下的所有内容(递归)。
import java.io.*;
class FileDemo3
{
public static void main(String[] args)
{
File dir=new File("F:\\单机游戏\\cs - 副本");
deleteDir(dir);
}
//用递归遍历多级目录的所有内容
public static void deleteDir(File dir)
{
// //打印目录
// System.out.println(dir);
//把该目录下的所有文件对象都存储到数组中
File[] files=dir.listFiles();
//对数组进行遍历
for (int i=0;i<files.length ;i++ )
{
//如果文件是目录,则继续遍历
if (files[i].isDirectory())
{
deleteDir(files[i]);
}
else
//删除该文件
System.out.println(files[i].getName()+"--file--"+files[i].delete());
}
System.out.println(dir+"--dir--"+dir.delete());
}
}
练习3:创建扩展名为java的文件列表。
/*
思路:
1.用递归遍历指定目录下的特定文件。
2.将遍历到的特定文件的绝对路径名储存到集合中。
3.遍历集合,将集合的内容写入文件中。
*/
import java.io.*;
import java.util.*;
class FileDemo4
{
public static void main(String[] args)
{
File dir=new File("E:\\heima\\exercise");
ArrayList<String> al=new ArrayList<String>();
showDir(dir,al);
String str=dir.toString()+"file list";
writeFile(al,str)
}
//用递归遍历多级目录的所有内容
public static void showDir(File dir,List list)
{
//把该目录下的所有文件对象都存储到数组中
File[] files=dir.listFiles();
//对数组进行遍历
for (int i=0;i<files.length ;i++ )
{
//如果文件是目录,则继续遍历
if (files[i].isDirectory())
{
showDir(files[i]);
}
else
if (files[i].getName().endsWith(".java"))
{
list.add(files[i].getAbsolute());
}
}
}
public static void writeFile(List list,String str)
{
BufferedWriter bufw=null;
try
{
for (String s:list)
{
bufw=new BufferedWriter(new FileWriter(str));
bufw.write(s);
bufw.newLine();
bufw.flush();
}
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
if (bufw!=null)
{
try
{
bufw.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
}
}
程序的运行后的部分截图如下图所示:
八、Properties类
1.概述:
Properties是hashtable的子类。也就是说它具备Map集合的特点。而且它里面存储的键值对都是字符串。是集合中和IO技术相结合的集合容器。
注:具体方法使用参见API文档。
2.主要功能:
用来配置文件信息。可以用于键值对形式的配置文件。
3.代码练习:
需求:写一个程序,只能让这个程序免费试用5次。
/*
练习:写一个程序,只能让这个程序免费试用5次。
思路:
1.创建配置文件,将次数以键值对的形式保存在配置文件中。
2.每次启动程序,将使用次数自增1,并保存到配置文件中.
3.如果次数超过5次,则停止程序使用。
*/
import java.util.*;
import java.io.*;
class PropertiesTest
{
public static void main(String[] args)
{
Properties prop=new Properties();
File file=new File("E:\\heima\\count.ini");
FileReader fr=null;
FileWriter fw=null;
try
{
//判读文件是否存在
if (!file.exists())
{
//如果不存在,则创建文件
file.createNewFile();
}
fr=new FileReader(file);
//读取配置文件信息到集合中
prop.load(fr);
//获取试用次数值
String value=prop.getProperty("time");
int count=0;
if (value!=null)
{
//如果试用次数不为null,则将值赋给count
count=Integer.parseInt(value);
//如果试用次数超过5次,反馈信息给用户
if (count>=5)
{
System.out.println("试用次数已到,请付费后使用");
//程序正常终止
System.exit(0);
}
}
//每使用一次,count自增1
count++;
//将count值以键值对形式存储到集合中
prop.setProperty("time",count+"");
fw=new FileWriter(file);
//将集合的信息写入配置文件中
prop.store(fw,"used time");
System.out.println("已使用"+count+"次");
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
if (fw!=null)
{
try
{
fw.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
if (fr!=null)
{
try
{
fr.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
}
}
程序运行后的结果如下图所示:
九、PrintWriter(打印流)
1.概述:
PrintWriter类是Writer类的直接子类,该流提供了各种数据类型的打印方法,可打印的数据类型有:boolean、char、char[]、int、long、float、double、Object对象、String。
2.打印流的特点:
字节打印流:PrintStream。
构造函数的主要接收类型:
1)file对象:File。
2)字符串路径:String。
3)字节输出流:OutputStream。
字符打印流:PrintWriter。
构造函数的主要接收类型:
1)file对象:File。
2)字符串路径:String。
3)字节输出流:OutputStream。
4)字符输出流:Writer。
3.主要功能:
向文本输出流打印对象的格式化表示形式,可以将各种数据类型的数据都原样打印。
4.代码练习:
题目:用键盘录入的方式,将录入的所有小写字母转换成大写,然后保存到文件中。
import java.io.*;
class PrintWriterDemo
{
public static void main(String[] args)
{
BufferedReader bufr=null;
PrintWriter out=null;
try
{
bufr=new BufferedReader(new InputStreamReader(System.in));
// out=new PrintWriter(System.out); //输出到控制台
// out=new PrintWriter("E:\\heima\\11.txt"); //该方法不能进行自动刷新
out=new PrintWriter(new FileOutputStream("E:\\heima\\PrintWriter.txt"),true); //该方法可以对Println、printf或format方法自动刷新。
String line=null;
while ((line=bufr.readLine())!=null)
{
if (line.equals("over"))
{
break;
}
out.println(line.toUpperCase());
// out.write(line.toUpperCase()); //该方法不能换行
// out.flush();
}
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
if (bufr!=null)
{
try
{
bufr.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
out.close();//该方法没有抛出IO异常
}
}
}
程序运行后的结果如下图:
控制台录入截图:
文件内容的截图:
十、SequenceInputStream(合并流或序列流)
1.概述:
SequencenInputStream表示其他输入流的逻辑串联,也成合并流或序列流,可将多个输入流合并为一个输入流。SequenceInputStream会将与之相连接的流集组合成一个输入流并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末 尾为止。
2.合并的两种构造函数方式:
方式1:SequenceInputStream(Enumeration<? extends InputStream> e)
方式2:SequenceInputStream(InputStream s1, InputStream s2)
3.主要功能:
合并流主要用于将多个源合并成一个源,可用于多个文件内容的合并。
4.代码练习:
题目1:用合并流的方式将多个文件的内容进行合并。
import java.util.*;
import java.io.*;
class SequenceInputStreamDemo
{
public static void main(String[] args)
{
Vector<FileInputStream> v=new Vector<FileInputStream>();
BufferedOutputStream bos=null;
try
{
v.add(new FileInputStream("E:\\heima\\1.txt"));
v.add(new FileInputStream("E:\\heima\\2.txt"));
v.add(new FileInputStream("E:\\heima\\3.txt"));
Enumeration<FileInputStream> en=v.elements();
SequenceInputStream sis=new SequenceInputStream(en);
bos=new BufferedOutputStream(new FileOutputStream("E:\\heima\\0.txt"));
byte[] buf=new byte[1024];
int count=0;
while ((count=sis.read(buf))!=-1)
{
bos.write(buf,0,count);
}
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
if (bos!=null)
{
try
{
bos.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
}
}
合并前的文件内容如下截图:
程序运行后的合并文件内容截图如下所示:
题目2:将视频文件进行切割成多段,然后再将切割后的多段文件进行合并。
import java.util.*;
import java.io.*;
class SplitFileDemo
{
public static void main(String[] args)
{
splitFile();
merge();
}
//将切割后的文件进行合并
public static void merge()
{
ArrayList<FileInputStream> al=new ArrayList<FileInputStream>();
FileOutputStream fos=null;
try
{
for (int x=1;x<4 ;x++ )
{
al.add(new FileInputStream("E:\\heima\\merge\\3"+x+".flv"));
}
Iterator<FileInputStream> it=al.iterator();
Enumeration<FileInputStream> en=new Enumeration<FileInputStream>()
{
public boolean hasMoreElements()
{
return it.hasNext();
}
public FileInputStream nextElement()
{
return it.next();
}
};
SequenceInputStream sis=new SequenceInputStream(en);
fos=new FileOutputStream("E:\\heima\\merge\\123.flv");
byte[] buf=new byte[1024];
int len=0;
while ((len=sis.read(buf))!=-1)
{
fos.write(buf,0,len);
}
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
if (fos!=null)
{
try
{
fos.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
}
//切割文件,如果被切割的为视频文件,切割后的文件不能进行播放。
public static void splitFile()
{
FileInputStream fis=null;
FileOutputStream fos=null;
try
{
fis=new FileInputStream("E:\\heima\\merge\\1.flv");
byte[] buf=new byte[1024*1024];
int len=0;
int pos1=1,pos2=1;
while ((len=fis.read(buf))!=-1)
{
if (pos2==1)
{
fos=new FileOutputStream("E:\\heima\\merge\\3"+(pos1++)+".flv");
}
fos.write(buf,0,len);
pos2++;
if (pos2>5)
{
pos2=1;
}
}
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
if (fis!=null)
{
try
{
fis.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
if (fos!=null)
{
try
{
fos.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
}
}
十一、对象的序列化
1.概述:
序列化:序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。对象的序列化,也称对象的持久化存储或对象的可串行性。
反序列化:把字节序列恢复为对象的过程称为对象的反序列化。
对象的序列化有ObjectInputStream类来操作完成,而对象的反序列化有ObjectOutputStream类来操作完成。
如果对象要被序列化,那么该对象需要实现接口Serializable,该接口是一个标记接口。
序列化的对象类在内部建议进行显示的声明,格式为: static final long serialVersionUID = 42L。原因是计算默认的 serialVersionUID 对类的详细信息具有较高的敏感性,根据编译器实现的不同可能千差万别,这样在反序列化过程中可能会导致意外的 InvalidClassException。因此,为保证 serialVersionUID 值跨不同 java 编译器实现的一致性,序列化类必须声明一个明确的 serialVersionUID 值。
2.特点:
如果某个类能够被序列化,其子类也可以被序列化。声明为static和transient类型的成员数据不能被序列化。因为static代表类的状态, transient代表对象的临时数据。
3.主要应用情况:
1)对象序列化可以实现分布式对象。例如:RMI要利用对象序列化运行远程主机上的服务,就像在本地机上运行对象时一样。
2)要求在JVM停止运行之后能够保存(持久化)指定的对象,并在将来重新读取被保存的对象。java对象序列化不仅保留一个对象的数据,而且递归保存对象引用的每个对象的数据。可以将整个对象层次写入字节流中,可以保存在文件中或在网络连接上传递。利用对象序列化可以进行对的"深复制",即复制对象本身及引用的对象本身。序列化一个对象可能得到整个对象序列。
4.代码示例:
题目:自定义可序列化类。
/定义可被序列化类
class Person implements Serializable
{
//对该可序列化类进行显示声明serialVersionUID
static final long serialVersionUID=42L;
String name;
int age;
//当字段被关键字transient修饰后,默认序列化机制就会忽略该字段。
// transient int id;
Person(String name,int age)
{
this.name=name;
this.age=age;
}
public String toString()
{
return name+":"+age;
}
}
1.概述:
描述管道流有两个类:PipedInputStream(管道输入流)和PipedOutputStream(管道输出流)。
PipedOutputStream类:将数据写入到管道输入流中的缓冲区中。
PipedInputStream类:将缓冲区中的数据读取出来。PipedInputStream运用的是一个1024字节固定大小的循环缓冲区。写入PipedOutputStream的数据实际上保存到对应的 PipedInputStream的内部缓冲区中。
2.管道流的连接方式:
方式一:用PipedInputStream的构造函数连接。
示例:
PipedInputStream in=new PipedInputStream(new PipedOutputStream());
方式二:用PipedInputStream类中的connect方法连接。
示例:
PipedInputStream in=new PipedInputStream();
PipedOutputStream out=new PipedOutputStream();
int.connect(out);
注:使用管道流,不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程。应采用多线程的方式。
3.主要作用:
主要用来连接两个线程间的通信。它具有将一个程序的输出当作另一个程序的输入的能力。
4.代码练习:
题目:用管道流的方式完成多线程的信息传输。
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*1024];
System.out.println("读取前...阻塞中");
int len=in.read(buf);
System.out.println("收到数据...阻塞结束");
System.out.println(new String(buf,0,len));
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
try
{
in.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
}
//定义管道输出流的运行线程
class Write implements Runnable
{
private PipedOutputStream out;
Write(PipedOutputStream out)
{
this.out=out;
}
public void run()
{
try
{
System.out.println("请等待,正在传输数据...");
Thread.sleep(2000);
out.write("piped data".getBytes()); //将数据写入到管道输入流的缓冲区中
out.flush(); //刷新此输出流并强制写出所有缓冲的输出字节。
}
catch (IOException e)
{
System.out.println(e.toString());
}
catch (InterruptedException ex)
{
System.out.println(ex.toString());
}
finally
{
try
{
out.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
}
class PipedStreamDemo
{
public static void main(String[] args)
{
PipedInputStream in=new PipedInputStream();
PipedOutputStream out=new PipedOutputStream();
try
{
//将管道输入流与管道输出流进行连接
in.connect(out);
}
catch (IOException e)
{
System.out.println(e.toString());
}
Read r=new Read(in);
Write w=new Write(out);
new Thread(r).start();
new Thread(w).start();
}
}
程序运行后的结果如下图所示:
十三、RandomAccessFile类
1.概述:
RandomAccessFile类直接继承与Object,并算是IO体系中的类,但是它是IO包中的成员,因为它具备读和写功能。该类内部封装了一个数组,可以通过指针对数组的元素进行操作。、
完成读写操作的原理:内部封装了字节输入流和字节输出流。
2.特点:
1)该类只能操作文件,而且操作文件具有四种模式,即r、rw、rws、rwd。四种模式的含义如下:
"r" :以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。
"rw":打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。
"rws":打开以便读取和写入,对于 "rw",还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。
"rwd":打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备。
2)如果该类操作的文件不存在且模式不为只读,则会自动创建,如果存在,则不会覆盖。
3)如果该类操作的文件不存在且模式为只读,则会发生FileNotFoundException异常。
4)当该类操作的文件存在且模式为只读时,如果出现非只读模式操作,如wirte方法等,则会发生IO异常。
3.常见方法操作:
1.获取指针位置。
2.改变指针位置。
3.跳过指定字节数,只能一直往后跳,不能往前。
4.读取或写入多种基本数据类型的数据。
注:该类的更多方法操作,参见API文档。
4.主要应用:
由于该类可对文件的指定位置进行写入,那么可以实现同时让多个线程对同一个文件写入数据,因此可用于多线程的下载。
十四、IO包中的其他类
1.DataInputStream与DataOutputStream:
主要作用:用对基本数据类型的读写操作。
当该类写入数据时用的是writeUTF方法写入,则该方法指定的编码表为UTF—8的修改版。那么就只能用对应的readUTF方法进行读取。如果用readUTF方法读取编码为UTF-8或GBK的文件,则会抛出EOFException异常。
代码示例:
import java.io.*;
class DataStreamDemo
{
public static void main(String[] args)
{
// writeData();
readData();
}
//从文件读取数据
public static void readData()
{
DataInputStream dis=null;
try
{
dis=new DataInputStream(new FileInputStream("E:\\heima\\data.txt"));
//需要按照文件存储的顺序进行读取,否则将会错乱。
int i=dis.readInt();
boolean b=dis.readBoolean();
double d=dis.readDouble();
System.out.println("i="+i);
System.out.println("b="+b);
System.out.println("d="+d);
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
if (dis!=null)
{
try
{
dis.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
}
//写入数据到文件
public static void writeData()
{
DataOutputStream dos=null;
try
{
dos=new DataOutputStream(new FileOutputStream("E:\\heima\\data.txt"));
//写入int类型数据,4个字节
dos.writeInt(34);
//写入boolean类型数据,1个字节
dos.writeBoolean(true);
//写入double类型数据,8个字节
dos.writeDouble(3434.3424);
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
if (dos!=null)
{
try
{
dos.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
}
}
程序运行后的结果如下图所示:
2.ByteArrayInputStream和ByteArrayOutputStream:
主要作用: 用于对字节数组的操作。
特点:
1)ByteArrayOutputStream不用定义数据目的,内部封装了一个长度可变的字节数组。就是数据的目的地。
2)ByteArrayInputStream和ByteArrayOutputStream都操作的是数组,并没有使用系统资源,因此不用进行close关闭。
3)ByteArrayInputStream在构造的时候需要接受数据源,而且数据源是一个字节数组。
代码示例:
import java.io.*;
//操作字节数组的类
class ByteArrayStreamDemo
{
public static void main(String[] args)
{
//数据源
ByteArrayInputStream bais=new ByteArrayInputStream("safkdsl".getBytes());
//数据目的
ByteArrayOutputStream baos=new ByteArrayOutputStream();
int ch=0;
while ((ch=bais.read())!=-1)
{
//写入进数组的数据可通过ByteArrayOutputStream类中的方法进行操作
baos.write(ch);
}
System.out.println(baos.size());
System.out.println(baos.toString());
try
{
//将数组中的数据写到文件中
baos.writeTo(new FileOutputStream("E:\\heima\\ByteArray.txt"));
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
程序运行后的结果如下图所示:
3.CharArrayReader和CharArrayWriter:
主要作用:用于对字符数组的读写操作。
注:理解该类更多功能可参考ByteArrayInputStream和ByteArrayOutputStream。
4.StringReader和StringWriter:
主要作用:用于对字符串的读写操作。
注:理解该类更多功能可参考ByteArrayInputStream和ByteArrayOutputStream。
十五、字符编码
常见的编码表:
1.ASCLL(美国标准信息交换码):用一个字节的7位可以表示。
2.ISO8859-1(拉丁码表,欧洲码表):用一个字节的8位表示。
3.GB2312:中国的中文码表。
4.GBK:中国的中文编码表升级,融合了更多的中文文字符号。用两个字节表示。
5.Unicode:国际标准码,融合了多种文字。所有文字都用两个字节表示,java语言使用的就是unicode。
6.UTF-8:最多用三个字节来表示一个字符。
编码:将字符串变成字节数组。
String——>byte[]: byte[] getBytes(charsetName);
解码:将字节数组变成字符串。
byte[]——>String:new String(byte[] b,charsetName);
代码示例:
import java.util.*;
class UnicodeDemo
{
public static void main(String[] args) throws Exception
{
String s="你好";
// byte[] buf=s.getBytes(); //默认编码为GBK
//用“UTF-8"进行编码
byte[] b1=s.getBytes("UTF-8");
System.out.println(Arrays.toString(b1));
// String str=new String(b1); //默认解码为GBK
//用”ISO8859-1"进行解码,解码出错
String str1=new String(b1,"ISO8859-1");
System.out.println("str1="+str1);
//对解错码的字符串用“ISO8859-1”重新编码
byte[] b2=str1.getBytes("ISO8859-1");
//用"UTF-8”重新解码,解码正确
String str2=new String(b2,"UTF-8");
System.out.println("str2="+str2);
}
}
//注:对解码错误字符串可以进行反解码,但是用“UTF-8”解错的编码,则不能再用“UTF-8"进行反解码。
//原因:对”UTF-8“编码表来说,所有未知字符对应为同一个字符"?",该字符只有一个编码。当很多未知字符被
//该编码表解码后,都对应为同一个字符"?",而进行反解码的时候就出现都为同一个编码。即不能用反解码进行
//还原。
代码练习:
题目:有四个学生,每个学生有三门课程的成绩,从键盘输入以上数据(包括姓名,三门课程的成绩),输入的格式为:张三,30,40,60。计算出总成绩,并把学生的信息和计算出的总分数按高低顺序存放在磁盘的文件中。
import java.util.*;
import java.io.*;
class UnicodeTest
{
public static void main(String[] args)
{
//返回一个比较器,并强行逆转TreeSet集合比较的自然顺序
Comparator<Student> cmp=Collections.reverseOrder();
TreeSet<Student> ts=StudentInfoTool.getStudents(cmp);
StudentInfoTool.writeFile(ts);
}
}
//定义学生工具类
class StudentInfoTool
{
//将学生对象存储到TreeSet集合中,不指定比较器
public static TreeSet<Student> getStudents()
{
return getStudents(null);
}
//将学生对象存储到TreeSet集合中,并指定比较器
public static TreeSet<Student> getStudents(Comparator<Student> cmp)
{
BufferedReader br=null;
TreeSet<Student> ts=null;
if (cmp==null)
ts=new TreeSet<Student>();
else
ts=new TreeSet<Student>(cmp);
Student stu=null;
try
{
br=new BufferedReader(new InputStreamReader(System.in));
String line=null;
while ((line=br.readLine())!=null)
{
if (line.equals("over"))
{
break;
}
String[] s=line.split(",");
stu=new Student(s[0],Integer.parseInt(s[1]),
Integer.parseInt(s[2]),
Integer.parseInt(s[3]));
ts.add(stu);
}
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
if (br!=null)
{
try
{
br.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
return ts;
}
//将TreeSet集合中的学生对象写到文件中
public static void writeFile(TreeSet<Student> ts)
{
BufferedWriter bw=null;
try
{
bw=new BufferedWriter(new FileWriter("E:\\heima\\StudentList.txt"));
for(Student s:ts)
{
bw.write(s.toString()+'\t');
bw.write(s.getSum()+"");
bw.newLine();
bw.flush();
}
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
if (bw!=null)
{
try
{
bw.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
}
}
//定义学生类
class Student implements Comparable<Student>
{
private String name;
private int math;
private int english;
private int chinese;
Student(String name,int math,int english,int chinese)
{
this.name=name;
this.math=math;
this.english=english;
this.chinese=chinese;
}
public String getName()
{
return name;
}
public int getMath()
{
return math;
}
public int getEnglish()
{
return english;
}
public int getChinese()
{
return chinese;
}
public int getSum()
{
return math+english+chinese;
}
public int compareTo(Student s)
{
int num=new Integer(this.getSum()).compareTo(s.getSum());
if (num==0)
{
return this.name.compareTo(s.name);
}
return num;
}
public int hashCode()
{
return name.hashCode()+math+english+chinese*23;
}
public boolean equals(Object obj)
{
if (!(obj instanceof Student))
{
throw new RuntimeException("类型不匹配");
}
Student s=(Student)obj;
return this.name.equals(s.name)&&this.getSum()==s.getSum();
}
public String toString()
{
return "Student["+name+", "+math+", "+english+", "+chinese+"]";
}
}
运行结果如下图所示: