一:异常
1.异常的由来:
编写程序的时候,可能会有很多问题存在,为了将来方便的表示这些问题的原因,类型,位置,java就提供了异常对象供我们使用。
2.概述:程序出现不正常的情况。
3.异常的好处:
A,将问题进行封装。
B,将正常流程代码和问题处理代码相分离,方便于阅读。
4.体系结构:
Throwable
|Error(严重):运行的类不存在或者内存溢出,是不需要处理的;一般情况是需要修改代码的。
|Exception(不严重):
|编译期间:这个是我们需要处理的。
|运行期间:这个是不需要我们处理的,需要我们修改代码。
5.处理方式:
处理Exception有两种方式:
A,在运行时运行出现的一些情况,可以通过try catch finally 。
B,通过throws在方法上声明。(如果函数内throw异常,那么函数就必须声明)
补充:
6.throw和throws的用法:
1.throw定义在方法内,用于抛出异常对象。
2.Throws定义在方法上,用于抛出异常类,可以抛出多个,用逗号隔开。
7.格式:
一:try
{
可能发生异常的方法;
}
catch(接收捕获异常类的对象 异常类引用变量)
{
处理代码;
}
二:try
{
可能发生异常的代码;
}
finally
{
一定会执行的语句(通常用于关闭资源)
}
三:try
{
可能发生异常的方法;
}
catch(接收捕获异常类的对象 异常类引用变量)
{
处理代码;
}
finally
{
一定会执行的语句(通常用于关闭资源)
}
8.出现多个异常:
1)一个一个用异常处理方式解决,
2)针对所有问题写一个try{}catch(){}代码
可以一个try多个catch 。
注意:1.异常后者错误,都是以他们所在体系父类作为后缀名。
XxxExcption XxxError
如果异常是平级关系,没有顺序问题;如果问题存在着子父类问题,分类一定放到最后。
新特性:多个catch用一个catch替代
格式:
catch{异常1|异常2|异常3...}
9.异常有两种:
编译时异常:该异常在编译时,没有处理(没有抛出,也没有try)编译失败。
运行时异常:该异常的发生,建议不处理,让程序停止,需要对代码进行修正。
10.注意:
当函数内有throw抛出异常对象,并未进行try处理,必须要在函数上声明,否则编译失败。
Exception有一个特殊的子类,RuntimeException 。-->也就是说,函数上如果抛出RuntimeException异常,函数上可以不用声明。
11.什么时候使用catch,什么时候使用throws呢?
当功能内容可以解决用catch,解决不了,用throws告诉调用者,由调用者解决。
12:Throwable(抛出)中的方法:
public string getMessage():返回的是异常的消息字符串。
public string toString():返回异常的简单描述 信息。
全路径名:消息字符串。
public void printStackTrace():把错误信息显示控制台。
13.自定义异常:
(java考虑不到的异常,针对这种情况,我们就要编写自定义异常类进行处理)
如何编写?
就是自定义一个类,去继承Exception或者RuntimeExccption 。
开发中:一般继承自RuntimeExccption
一旦你的判断要做某些异常处理的时候,就应该在方法内部用throw关键字把该异常对ixa象抛出。
格式:
方法内部
throw异常对象;
14.Final,finally,finalize的区别:
IO流:
A java中的IO流是用来处理不同设备间输入输出的问题。
B IO流操作的类都在io包中。
C io流分类:
按操作数据不同:
|字节流
|字符流
字符流的出现就是为拉解决文本数据的问题。
注意:建议使用字节流,有些时候iu,为了操作的方便我们使用字字符流,
1.IO流File类
概述
用来将文件或者文件夹封装成对象,方便对文件与文件夹的属性信息进行操作。
File对象可以作为参数传递给流的构造函数。
弥补了流对象的不足,流对象能操作文件,不能操作文件夹,文件属性信息,只能操作数据,File能操作文件夹,文件属性信息
注意:
separator:与系统有关的默认名称分隔符(跨平台分隔符),为了方便,它被表示为一个字符串。
File常见方法
1,创建。
boolean createNewFile():在指定位置创建文件,如果该文件已经存在,则不创建,返回false。
和输出流不一样,输出流对象一建立创建文件。而且文件已经存在,会覆盖。
boolean mkdir():创建文件夹。只能创建一级
boolean mkdirs():创建多级文件夹。
2,删除。
boolean delete():删除失败返回false。如果文件正在被使用,则删除不了返回false。
void deleteOnExit();在程序退出时删除指定文件。
3,判断。
canExecute():文件是否可执行
boolean exists() :文件是否存在.
isFile():是否是文件
isDirectory();文件是否是目录
isHidden();文件是否是隐藏文件
isAbsolute();是否为绝对路径,对象存在即可判断,文件不存在也能判断。(判断的是给出的路径字符串)
4,获取信息。
getName():返回由此抽象路径名表示的文件或目录的名称。
getPath():将此抽象路径名转换为一个路径名字符串;对象存在即可判断,文件不存在也能判断
getParent():返回此抽象路径名父目录的路径名字符串;
getAbsolutePath() :返回此抽象路径名的绝对路径名字符串。对象存在即可判断,文件不存在也能判断。
long lastModified() :返回此抽象路径名表示的文件最后一次被修改的时间。
long length() :返回由此抽象路径名表示的文件的长度。
renameTo(File dest):重新命名此抽象路径名表示的文件。
构造方法:
public File(File parent, String child)
parent 抽象路径名用于表示目录,child 路径名字符串用于表示目录或文件。
File方法运用实例:
class FileDemo
{
public static void main(String[] args) throws IOException
{
method_5();
}
public static void method_5()
{
File f1 = new File("c:\\Test.java");
File f2 = new File("d:\\hahah.java");
sop("rename:"+f2.renameTo(f1));
}
public static void method_4()
{
File f = new File("file.txt");
sop("path:"+f.getPath());
sop("abspath:"+f.getAbsolutePath());
sop("parent:"+f.getParent());//该方法返回的是绝对路径中的父目录。如果获取的是相对路径,返回null。
//如果相对路径中有上一层目录那么该目录就是返回结果。
}
public static void method_3()throws IOException
{
File f = new File("d:\\java1223\\day20\\file2.txt");
//f.createNewFile();
//f.mkdir();
//记住在判断文件对象是否是文件或者目录时,必须要先判断该文件对象封装的内容是否存在。
//通过exists判断。
sop("dir:"+f.isDirectory());//只有对象还没有文件生成时,返回为false
sop("file:"+f.isFile());//只有对象还没有文件生成时,返回为false
sop(f.isAbsolute());
}
public static void method_2()
{
File f = new File("file.txt");
//sop("exists:"+f.exists());
//sop("execute:"+f.canExecute());//是否能执行
//创建文件夹
File dir = new File("abc\\kkk\\a\\a\\dd\\ee\\qq\\aaa");
sop("mkdir:"+dir.mkdirs());
}
public static void method_1()throws IOException
{
File f = new File("file.txt");
//sop("create:"+f.createNewFile());
//sop("delete:"+f.delete());
}
//创建File对象
public static void consMethod()
{
//将a.txt封装成file对象。可以将已有的和未出现的文件或者文件夹封装成对象。此时还不会生成文件
File f1 = new File("a.txt");
//File f2 = new File("c:\\abc\\b.txt");
//将目录,文件分成两个参数传递,文件可变化
File f2 = new File("c:\\abc","b.txt");
File d = new File("c:\\abc");
File f3 = new File(d,"c.txt");
sop("f1:"+f1);//封装什么,打印的就是什么
sop("f2:"+f2);
sop("f3:"+f3);
File f4 = new File("c:"+File.separator+"abc"+File.separator
+"zzz"+File.separator+"a.txt");
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
File方法运用实例(文件列表):
listRoots():列出可用的文件系统根。
String[] list() :返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的文件和目录(包含隐藏的),对象不能封装指定文件 。只返回名称(开发中不常用) (但只能返回当前目录中的文件和文件夹,子目录的不可以)
String[] list(FilenameFilter filter) :返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中满足指定过滤器的文件和目录。
boolean endsWith(String suffix) :测试此字符串是否以指定的后缀结束。
File[] listFiles():返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件和文件夹。 返回对象,可以有更多的操作(但只能返回当前目录中的文件和文件夹,子目录的不可以)
import java.io.*;
class FileDemo2
{
public static void main(String[] args)
{
File dir = new File("c:\\");
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:\\java1223\\day18");
//FilenameFilter类只有一个方法,用匿名内部类
String[] arr = dir.list(new FilenameFilter()
{
public boolean accept(File dir,String name)
{
///System.out.println("dir:"+dir+"....name::"+name);
/*
if(name.endsWith(".bmp"))
return true;
else
return false;
*/
return name.endsWith(".bmp");
}
});
System.out.println("len:"+arr.length);
for(String name : arr)
{
System.out.println(name);
}
}
public static void listDemo()
{
File f = new File("c:\\abc.txt");//对象不能封装指定文件
String[] names = f.list();//调用list方法的file对象必须是封装了一个目录。该目录还必须存在。
for(String name : names)
{
System.out.println(name);
}
}
public static void listRootsDemo()
{
File[] files = File.listRoots();
for(File f : files)
{
System.out.println(f);
}
}
}
File运用实例(打印出指定文件夹下所有的txt类型文件内容):
class Demo
{
public static void main(String[] args)
{
listDemo_2();
}
//返回指定后缀名的文件
public static void listDemo_2()
{
File dir = new File("c:/");
//FilenameFilter类只有一个方法,用匿名内部类
String[] arr = dir.list(new FilenameFilter()
{
public boolean accept(File dir,String name)
{
return name.endsWith(".txt");
}
});
System.out.println("符合条件的txt文件有:"+arr.length+"个");
for(String file : arr)
{
System.out.println(file);
//打印文件内容到控制台
BufferedReader buf = null;
try
{
buf = new BufferedReader(new FileReader(dir+file));
String line = null;
try
{
while((line = buf.readLine())!=null)
{
System.out.println(line);
}
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
catch (FileNotFoundException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
finally
{
try
{
buf.close();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("---------------------------------");
}
}
}
File运用实例(利用递归方法列出目录下所有内容):
列出指定目录下文件或者文件夹,包含子目录中的内容。
也就是列出指定目录下所有内容。
因为目录中还有目录,只要使用同一个列出目录功能的函数完成即可。
在列出过程中出现的还是目录的话,还可以再次调用本功能。
也就是函数自身调用自身。
这种表现形式,或者编程手法,称为递归。
递归执行过程(无返回值):
递归执行过程(有返回值):
递归要注意:
1,限定条件。
2,要注意递归的次数。尽量避免内存溢出。
import java.io.*;
class test
{
public static void main(String[] args)
{
File dir = new File("d:\\testdir");
//showDir(dir,0);
//toBin(6);
//int n = getSum(8000);
//System.out.println("n="+n);
System.out.println(dir.delete());
//method();
}
public static String getLevel(int level)
{
StringBuilder sb = new StringBuilder();
sb.append("|--");
for(int x=0; x<level; x++)
{
//sb.append("|--");
sb.insert(0,"| ");
}
return sb.toString();
}
public static void showDir(File dir,int level)
{
System.out.println(getLevel(level)+dir.getName());
level++;
File[] files = dir.listFiles();
for(int x=0; x<files.length; x++)
{
if(files[x].isDirectory())//判断是否为目录
showDir(files[x],level);
else
System.out.println(getLevel(level)+files[x]);
}
}
public static int getSum(int n)//利用递归求和,当递归次数太大,会出现内存(栈内存)溢出
{
if(n==1)
return 1;
return n+getSum(n-1);
}
public static void toBin(int num)//利用递归求二进制数
{
if(num>0)
{
toBin(num/2);
System.out.println(num%2);
}
}
public static void method()//内存溢出
{
method();
}
}
File运用实例(利用递归方法删除带内容的目录):
/*
删除一个带内容的目录。
删除原理:
在window中,删除目录从里面往外删除的。
既然是从里往外删除。就需要用到递归。
在java中删除的文件是不走回收站的
*/
import java.io.*;
class RemoveDir
{
public static void main(String[] args)
{
File dir = new File("d:\\testdir");
removeDir(dir);
}
public static void removeDir(File dir)
{
File[] files = dir.listFiles();
for(int x=0; x<files.length; x++)//循环结束后,只剩下空目录
{
if(files[x].isDirectory())
removeDir(files[x]);
else
System.out.println(files[x].toString()+":-file-:"+files
[x].delete());//删除文件
}
System.out.println(dir+"::dir::"+dir.delete());//删除文件夹
}
}
File运用实例(创建java文件列表)
/*
练习
将一个指定目录下的java文件的绝对路径,存储到一个文本文件中。
建立一个java文件列表文件。
思路:
1,对指定的目录进行递归。
2,获取递归过程所有的java文件的路径。
3,将这些路径存储到集合中。可打印,可写出
4,将集合中的数据写入到一个文件中。
*/
public class test
{
public static void main(String[] args) throws IOException
{
//将文件路径存储到集合中
File file = new File("D:/myjava_workspace/test_heima");
List<File> list=new ArrayList<File>();
filetolist(file,list);
System.out.println(list.size());
//写出数据
File f = new File(file,"path.txt");//将文件写到读取的目录中
writetofile(list,f.toString());
}
//将路径存储到集合中
public static void filetolist(File dir,List<File> list)
{
File f[]=dir.listFiles();
for(File files:f)
{
if(files.isDirectory())
filetolist(files,list);
else
{
if(files.getName().endsWith(".java"))
{
list.add(files);
}
}
}
}
//将集合中的数据写到文件中
public static void writetofile(List<File> list,String s) throws IOException
{
BufferedWriter buf = null;
try
{
buf=new BufferedWriter(new FileWriter(s));
//取出集合中的数据
for(File file:list)
{
String str=file.getAbsolutePath();
buf.write(str);
buf.newLine();
buf.flush();
}
}catch(IOException e)
{
throw new RuntimeException("写出异常");
}
finally
{
try
{
if(buf!=null)
buf.close();
}
catch(IOException e2)
{
throw new RuntimeException("写出关闭异常");
}
}
}
}
2.IO流Properties
1. 概念
Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。
Properties是hashtable的子类。
也就是说它具备map集合的特点。而且它里面存储的键值对都是字符串。
是集合中和IO技术相结合的集合容器。
Set<String> stringPropertyNames():返回此属性列表中的键集,其中该键及其对应值是字符串,如果在主属性列表中未找到同名的键,则还包括默认属性列表中不同的键。
getProperty(String key) :用指定的键在此属性列表中搜索属性。
void load(InputStream inStream) :从输入流中读取属性列表(键和元素对)。
void store(OutputStream out, String comments): 以适合使用 load(InputStream) 方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。
Comments是注释信息,不会被properties加载,开头用#标识;
2. 特点
可以用于键值对形式的配置文件。
那么在加载数据时,需要数据有固定格式:键=值。
3. 怎么使用?
class PropertiesDemo
{
public static void main(String[] args) throws IOException
{
//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");//haha是备注
//System.out.println(prop);
prop.list(System.out);//将属性列表输出到指定的输出流。
fos.close();
fis.close();
}
//演示,如何将流中的数据存储到集合中。
//想要将info.txt中键值数据存到集合中进行操作。
/*
1,用一个流和info.txt文件关联。
2,读取一行数据,将该行数据用"="进行切割。
3,等号左边作为键,右边作为值。存入到Properties集合中即可。
*/
public static void method_1()throws IOException//此方法每读取一行就要去硬盘读取,效率低,用load()
{
BufferedReader bufr = new BufferedReader(new FileReader("info.txt"));
String line = null;
Properties prop = new Properties();
while((line=bufr.readLine())!=null)
{
String[] arr = line.split("=");
///System.out.println(arr[0]+"...."+arr[1]);
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","39");
//System.out.println(prop);
String value = prop.getProperty("lisi");
//System.out.println(value);
prop.setProperty("lisi",89+"");
Set<String> names = prop.stringPropertyNames();
for(String s : names)
{
System.out.println(s+":"+prop.getProperty(s));
}
}
}
4. 什么时候使用?
将流中的数据存储到集合中时;
记录软件运行次数时,用到的配置文件需要键值对的形式存在(map+io -->properties.);
3.IO流Properties练习-记录程序运行次数
用于记录应用程序运行次数。
如果使用次数已到,那么给出注册提示。
很容易想到的是:计数器。
可是该计数器定义在程序中,随着程序的运行而在内存中存在,并进行自增。
可是随着该应用程序的退出,该计数器也在内存中消失了。
下一次在启动该程序,又重新开始从0计数。
这样不是我们想要的。
程序即使结束,该计数器的值也存在。
下次程序启动在会先加载该计数器的值并加1后在重新存储起来。
所以要建立一个配置文件。用于记录该软件的使用次数。
该配置文件使用键值对的形式。
这样便于阅读数据,并操作数据。
键值对数据是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();
}
}
properties和XML的区别?
properties:
name=zhangsan
age=20
xml:
<persons>
<person id="001">
<name>zhagnsan</name>
<age>30</age>
<address>bj</address>
</person>
<person>
<name
</person>
</persons>
用工具dom4j:dom for java
4.IO打印流PrintWriter
1. 概念
打印流:向文本输出流打印对象的格式化表示形式。
该流提供了打印方法,可以将各种数据类型的数据都原样打印。
PrintWriter(OutputStream out, boolean autoFlush):通过现有的 OutputStream 创建新的PrintWriter。autoFlush- boolean 变量;如果为 true,则 println、printf 或 format 方法将刷新输出缓冲区,print()不会刷新缓冲区;
2. 特点
字节打印流:
PrintStream
构造函数可以接收的参数类型:
1,file对象。File
2,字符串路径。String
3,字节输出流。OutputStream
字符打印流:
PrintWriter
构造函数可以接收的参数类型:
1,file对象。File
2,字符串路径。String
3,字节输出流。OutputStream
4,字符输出流,Writer。
3. PrintWriter和BufferedWriter的区别
1. PrintWriter的print、println方法可以接受任意类型的参数,而BufferedWriter的write方法只能接受字符、字符数组和字符串;
2. PrintWriter的println方法自动添加换行,BufferedWriter需要显示调用newLine方法;
3. PrintWriter的方法不会抛异常,若关心异常,需要调用checkError方法看是否有异常发生;
4. PrintWriter构造方法可指定参数,实现自动刷新缓存(autoflush);
5. PrintWriter的构造方法更广。
4. PrintWriter的缓冲问题
事实上PrintWriter共有八个构造器,除了最后两个以Writer作为参数的构造器外,其他六个构造器都会在内部创建一个BufferedWriter,然后再使用该BufferedWriter作为参数去调用最后一个构造器从而完成PrintWriter对象的创建,因此通过前六个构造器创建的PrintWriter本身是带有BufferedWriter缓冲的,而通过后两个构造器创建的PrintWriter对象是否也有缓冲能力则取决于你提供的Writer参数本身是否带有缓冲,类实现并不自动为其提供。
StreamEncoder缓冲:通过前六个构造器创建的PrintWriter对象都是基于OutputStreamWriter之上的,而OutputStreamWriter又是通过sun.nio.cs.StreamEncoder包装而成的,通过查阅文档,我们可以知道StreamEncoder中已经内置了一个8KB的ByteBuffer缓冲,输出字符经过再编码后保存在该缓冲区中,缓冲满后调用本地方法整体写入IO子系统。
由上可知,一个PrintWriter对象不论其是否带有BufferedWriter缓冲,在字符流和IO系统之间总是存在一个ByteBuffer缓冲,这样呢BufferedWriter缓冲对PrintWriter性能的影响就变得不那么显著了,因为它只起到了减少方法调用次数的作用(实际上是降低了CPU的开销),在成串输出字符模式下这一点体现的会更加明显,因为成串输出本身已显著较少了方法的调用次数。
5. 怎么使用?
class test
{
public static void main(String[] args) throws IOException
{
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));
//用PrintWriter代替BufferedWriter
PrintWriter out = new PrintWriter(new FileWriter("c:/a.txt"),true);//将文件封装成流,就可以刷新缓冲区。如果直接写到文件中,则要等打印流关闭才能写入文件
String line = null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
out.println(line.toUpperCase());
//out.flush();
}
out.close();
bufr.close();
}
}
/*
* 将数组以数组的形式写到dest.txt文件中
*/
class test2
{
public static void sop(Object obj)
{
System.out.println(obj);
}
public static void main(String[] args)
{
int[] arr = {4,6,23,78,56};
PrintWriter pw = null;
FileWriter file= null;
try
{
file = new FileWriter("c:/test/dest.txt");
pw = new PrintWriter(file,true);
pw.print("int[] arr = {");//没有刷新缓存
for(int i=0;i<arr.length;i++)
{
if(i==arr.length-1)//去除最后一个逗号
pw.print(i);
else
pw.print(i+",");
}
pw.println("}");
}
catch (Exception e)
{
// TODO: handle exception
}
}
}
6. 什么时候使用?
将键盘输入数据直接利用打印流保存到文件中
5.IO流SequenceInputStream-合并流
public class test
{
public static void main(String[] args) throws IOException
{
Vector<FileInputStream> v = new Vector<FileInputStream>();
v.add(new FileInputStream("c:/1.txt"));
v.add(new FileInputStream("c:/2.txt"));
v.add(new FileInputStream("c:/3.txt"));
Enumeration<FileInputStream> en = v.elements();
//将多个流合并成一个大流
SequenceInputStream se = new SequenceInputStream(en);//传进多个流,需传进Enumeration对象
//将大流的数据写出到文件
FileOutputStream fos = new FileOutputStream("c:/4.txt");
byte by[] = new byte[1024];
int len=0;
while((len=se.read(by))!=-1)
{
fos.write(by,0,len);
}
fos.close();
se.close();
}
}
6.IO流切割,合并文件
1. 概念
将一个读取流切割成多个读取流
2. 为什么用list集合还要用枚举法取出来呢?
sequenceInputStream流的构造函数只能接收Enumeration类型参数
3. 怎么使用?
class SplitFile
{
public static void main(String[] args) throws IOException
{
//splitFile();
merge();
}
//合并文件
public static void merge()throws IOException
{
//将碎片文件存储到LIST集合中,Vector的效率低
ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();
for(int x=1; x<=3; x++)
{
al.add(new FileInputStream("c:\\splitfiles\\"+x+".part"));
}
//将LIST集合数据用枚举法取出来,创建一个匿名内部类
final Iterator<FileInputStream> it = al.iterator();
Enumeration<FileInputStream> en = new Enumeration<FileInputStream>()
{
public boolean hasMoreElements()
{
return it.hasNext();
}
public FileInputStream nextElement()
{
return it.next();
}
};
SequenceInputStream s = new SequenceInputStream(en);
FileOutputStream fos = new FileOutputStream("c:\\splitfiles\\0.bmp");
byte[] buf = new byte[1024];
int len = 0;
while((len=s.read(buf))!=-1)
{
fos.write(buf,0,len);
}
fos.close();
s.close();
}
//切割文件
public static void splitFile()throws IOException
{
FileInputStream fis = new FileInputStream("c:\\1.bmp");
FileOutputStream fos = null;
byte[] buf = new byte[1024*1024];//1k=1024byte
int len = 0;
int count = 1;
while((len=fis.read(buf))!=-1)
{
fos = new FileOutputStream("c:\\splitfiles\\"+(count++)+".part");
fos.write(buf,0,len);
fos.close();
}
fis.close();
System.out.println("切割完成,共切割:"+(count-1)+"份");
}
}
4. 什么时候使用?
当文件太大时,需分段写入硬盘。
IO流
1. 对象的序列化(持久化存储)
概述:
类通过实现 java.io.Serializable
UID作用:
UID是给类添加固定标识,为了序列化方便
自动生成UID时,class文件的代码如果变动,UID变动,则无法使用原有对象,所以手动生成唯一UID。此时class文件改变了也无法改动UID,能继续使用原有对象
静态是不能被序列化的,;因为他在方法区中(共享区) ,对象在堆内存
非静态的属性,要想不被序列化,加上transient修饰的变量,不能被序列化
方法:
ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。
ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。
用ObjectOutputStream保存的对象只能用ObjectInputStream读取
void writeObject(Object obj):将指定的对象写入 ObjectOutputStream。
Object readObject():从 ObjectInputStream 读取对象。
请看如下示例:
类1:
class Person implements Serializable//标记接口,没有方法
{
public static final long serialVersionUID = 42L;
private String name;
transient int age;
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;
}
}
类2:
import java.io.*;
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
{
//文件后缀不用打开,用txt后缀就没意义了
ObjectOutputStream oos =
new ObjectOutputStream(new FileOutputStream("obj.txt"));
oos.writeObject(new Person("lisi0",399,"kr"));//kr不能序列化
oos.close();
}
}
2. 管道流
概念:
PipedInputStream:管道输入流应该连接到管道输出流;管道输入流提供要写入管道输出流的所有数据字节。通常,数据由某个线程从 PipedInputStream 对象读取,并由其他线程将其写入到相应的 PipedOutputStream。不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程。
void connect(PipedOutputStream src):使此管道输入流连接到管道输出流 src。
void connect(PipedInputStream snk) : 将此管道输出流连接到接收者。
特点
管道输入流包含一个缓冲区,可在缓冲区限定的范围内将读操作和写操作分离开。如果向连接管道输出流提供数据字节的线程不再存在,则认为该管道已损坏。
涉及多线程的IO流是管道流,涉及IO流的MAP集合是properties
怎么使用?
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);//read()是阻塞式的方法,没有读取数据,就会阻塞
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 lai la".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();
}
}
3. RandomAccessFile
概念
随机访问文件,自身具备读写的方法。
通过skipBytes(int x),seek(int x)来达到随机访问。
int read(byte[] b):将最多 b.length 个数据字节从此文件读入 byte 数组。
int readInt():从此文件读取一个有符号的 32 位(4字节)整数。
void seek(long pos): 设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。调整对象中指针。前后都能跳。
skipBytes(int n):尝试跳过输入的 n 个字节以丢弃跳过的字节。不能往回跳。
write()方法只写int类的最低八位,其他会丢失。writeInt()写INT类型的4字节32位
特点
该类不算是IO体系中子类。
而是直接继承自Object。
但是它是IO包中成员。因为它具备读和写功能。
内部封装了一个数组,而且通过指针对数组的元素进行操作。
可以通过getFilePointer获取指针位置,
同时可以通过seek改变指针的位置。
其实完成读写的原理就是内部封装了字节输入流和输出流。(操作数据必然是流)
通过构造函数可以看出,该类只能操作文件。
而且操作文件还有模式:只读r,,读写rw等。
如果模式为只读 r。不会创建文件。会去读取一个已存在文件,如果该文件不存在,则会出现异常。
如果模式rw。操作的文件不存在,会自动创建。如果存在则不会覆盖。
怎么使用?
class RandomAccessFileDemo
{
public static void main(String[] args) throws IOException
{
//writeFile_2();
//readFile();
//System.out.println(Integer.toBinaryString(258));
}
public static void readFile()throws IOException
{
RandomAccessFile raf = new RandomAccessFile("ran.txt","r");
//调整对象中指针。
//raf.seek(8*1);
//跳过指定的字节数
raf.skipBytes(8);
byte[] buf = new byte[4];//一个中文两个字节,刚好读取到两个中文
raf.read(buf);
String name = new String(buf);
int age = raf.readInt();//一次读取四个字节,32位
System.out.println("name="+name);
System.out.println("age="+age);
raf.close();
}
public static void writeFile_2()throws IOException
{
RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
raf.seek(8*0);
raf.write("周期".getBytes());
raf.writeInt(103);//writer()方法只写int类的最低八位,其他会丢失。writeInt()写INT类型的4字节32位
raf.close();
}
public static void writeFile()throws IOException
{
RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
raf.write("李四".getBytes());
raf.writeInt(97);//用write()只写int的后八位,丢失数据
raf.write("王五".getBytes());
raf.writeInt(99);
raf.close();
}
}
什么时候使用?
实现数据的分段写入,每一段自己拥有一个线程,如:下载软件原理,每个软件下载独立执行,分段下载,实现多线程下载
4. 操作基本数据类型的流对象DataStream
概念
可以用于操作基本数据类型的数据的流对象。
gbk:一个中文占2字节
utf-8:一个中文占3字节
utf-8修改版:一个中文占4字节
不同编码写的文件,需要用不同的编码读取,否则读取错误
double readDouble() :读取八个输入字节并返回一个 double 值。
int readInt():读取四个输入字节并返回一个 int 值。
void writeUTF(String s): 将表示长度信息的两个字节写入输出流,后跟字符串 s 中每个字符的 UTF-8 修改版表示形式。
EOFException - 如果此输入流在读取所有字节之前到达末尾。既没读完数据就到结尾了
特点
基本数据类型写入和取出的顺序要保持一致
怎么使用?
class test
{
public static void main(String[] args) throws IOException
{
//基本数据类型的读写操作
write();
read();
//按照固定字符编码格式写入字符
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("c:/t2.txt"),"GBK");
osw.write("你是傻蛋");
osw.close();
BufferedReader osr = new BufferedReader(new InputStreamReader(new FileInputStream("c:/t2.txt")));
String line=null;
while((line=osr.readLine())!=null)
{
sop(line);
}
//writeUTF()方法读写文件
writeUTF();
readUTF();
}
public static void readUTF()throws IOException
{
DataInputStream dif= new DataInputStream(new FileInputStream("c:/t3.txt"));
sop(dif.readUTF());//只能读取writeUTF()方法写的文件
dif.close();
}
public static void writeUTF()throws IOException
{
DataOutputStream dof = new DataOutputStream(new FileOutputStream("c:/t3.txt"));
dof.writeUTF("我很帅");//英文不涉及编码,用中文
dof.close();
}
public static void read()throws IOException
{
DataInputStream dif= new DataInputStream(new FileInputStream("c:/t1.txt"));
sop(dif.readDouble());
sop(dif.readInt());
sop(dif.readBoolean());
sop(dif.readByte());
dif.close();
}
public static void write()throws IOException
{
DataOutputStream dof = new DataOutputStream(new FileOutputStream("c:/t1.txt"));
dof.writeDouble(33.333333);
dof.writeInt(35);
dof.writeBoolean(true);
dof.writeByte(88);
dof.close();
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
4.什么时候使用?
当需要操作基本数据类型的时候
5. 操作字节数组-ByteArrayStream
概念
用于操作字节数组的流对象。
ByteArrayInputStream 包含一个内部缓冲区,该缓冲区包含从流中读取的字节。内部计数器跟踪 read 方法要提供的下一个字节。
ByteArrayInputStream :在构造的时候,需要接收数据源,。而且数据源是一个字节数组。
ByteArrayOutputStream: 在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组。这就是数据目的地。
在流操作规律讲解时:
源设备,
键盘 System.in,硬盘 FileStream,内存 ArrayStream。
目的设备:
控制台 System.out,硬盘FileStream,内存 ArrayStream。
特点
没有调用底层资源,关闭 ByteArrayInputStream 无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException。
因为这两个流对象都操作的数组,并没有使用系统资源。
所以,不用进行close关闭。
怎么使用?
class test
{
public static void main(String[] args)
{
//读取数据源,在内存中会难过
ByteArrayInputStream bas = new ByteArrayInputStream("asdfqwer".getBytes());
//数据目的,在内存中
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int len=0;
while((len=bas.read())!=-1)
{
bos.write(len);
sop("写入的数据是:"+(char)len);
}
//默认数组(缓冲区)的大小
sop(bos.size());
//取出缓冲区数据
sop(bos.toString());
//bos.writeTo(new FileOutputStream("a.txt"));//只有此方法会抛出异常
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
什么时候使用?
当要将硬盘文件读到内存中,放到可变长度的数组里存储起来时(用流的思想操作数组,读和取)
6. 操作字符数组-CharArrayReader与CharArrayWrite
用法与操作字节数组相似
7. 操作字符串数组-StringReader 与 StringWriter
用法与操作字节数组相似
8. 转换流的字符编码
概念
编码表
计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识别各个
国家的文字。就将各个国家的文字用数字来表示,并一一对应,形成一张表。这就是编码表。
常见的编码表
ASCII:美国标准信息交换码。
• 用一个字节的7位可以表示。
l ISO8859-1:拉丁码表。欧洲码表
• 用一个字节的8位表示。
l GB2312:中国的中文编码表。两个字节的8位表示,且两个字节的高位都是1。兼容ASCII码。容纳6000-7000个字。
l GBK:中国的中文编码表升级,融合了更多的中文文字符号。容纳20000多个字。
l Unicode:国际标准码,融合了多种文字。
• 所有文字都用两个字节来表示,Java语言使用的就是unicode。
l UTF-8:最多用三个字节来表示一个字符。(能用一个字节的就用一个字节存储,两个字节的就用两个字节。减少空间)每个字节开头都有标识头,容易区分UTF-8。
当两个编码表都能识别中文,但是同一文字在两张码表中对应的数字不同。所以涉及到了编码转换问题
不同编码进行写出和读取操作原理:
特点
可以将字符以指定编码格式存储。
可以对文本数据指定编码格式来解读。
指定编码表的动作由构造函数完成。
怎么使用?
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"),"gbk");
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();
}
}
什么时候使用?
当需要指定文件的编码格式的时候
9. 字符编码
概念
编码:字符串变成字节数组。
解码:字节数组变成字符串。
编码正确,解码错误,解决方法原理:
注意:
当用UTF-8解码—》重新编码—》解码,结果错误;因为GBK和UTF-8都识别中文。UTF-8有未知编码区域,当UTF-8解码时,查不到指定的中文时就在未知区域返回相似的字符?,重新编码时,返回相似字符的数字,此时数字已经变化了。
特点
编码:String-->byte[]:str.getBytes(charsetName);
解码:byte[] -->String: new String(byte[],charsetName);
Array.toString(byte[]):此方法可以将字节数组转变为字符串,但是不能指定编码格式。
中文不能用ISO8859-1进行编码,出错
怎么使用?
class EncodeDemo
{
public static void main(String[] args)throws Exception
{
String s = "哈哈";
//编码
byte[] b1 = s.getBytes("GBK");
System.out.println(Arrays.toString(b1));
//解码
String s1 = new String(b1,"iso8859-1");//String s1 = new String(b1,"utf-8");
System.out.println("s1="+s1);
//对s1进行iso8859-1编码。
byte[] b2 = s1.getBytes("iso8859-1");//byte[] b2 = s1.getBytes("utf-8");
System.out.println(Arrays.toString(b2));
//对b2进行gbk解码。
String s2 = new String(b2,"gbk");
System.out.println("s2="+s2);
}
}
什么时候使用?
当需要指定格式对数据进行编码,解码时。
10. 字符编码-联通(特殊文字)
UTF-8和GBK编码表的特点
Utf-8根据标识头来判断一次是读一个字节还是两个字节还是三个字节,如下图:
class test
{
public static void main(String[] args) throws Exception
{
String s = "联通";
byte[] by = s.getBytes("gbk");
for(byte b : by)
{
//将十进制的String类型数据转换成二进制,而且只取后八位
System.out.println(Integer.toBinaryString(b&255));
}
}
}
产生乱码的原因:
"联通"用GBK编码表产生的二进制数,与UTF-8的二进制数部分一致,当在记事本写入"联通"两字保存后,重新打开,进行解码。此时因为编码产生的二进制数与utf-8的一致,所以默认用utf-8编码表进行解码。导致解码错误;
解决方法:
在"联通"两字前面加上其他的中文;
11. 练习-将学生信息写到文件中(有比较器)
/*
有五个学生,每个学生有3门课的成绩,
从键盘输入以上数据(包括姓名,三门课成绩),
输入的格式:如:zhagnsan,30,40,60计算出总成绩,
并把学生的信息和计算出的总分数高低顺序存放在磁盘文件"stud.txt"中。
1,描述学生对象。
2,定义一个可操作学生对象的工具类。
思想:
1,通过获取键盘录入一行数据,并将该行中的信息取出封装成学生对象。
2,因为学生有很多,那么就需要存储,使用到集合。因为要对学生的总分排序。
所以可以使用TreeSet。
3,将集合的信息写入到一个文件中。
*/
public class test
{
public static void main(String args[]) throws IOException
{
//默认排序,升序
Set<Student> set = StudentTool.getStudent();
StudentTool.writeStudent(set);
//定义比较器,降序排序,所以要逆转指定比(默认)较器的顺序
/*Comparator<Student> cmp = Collections.reverseOrder();
Set<Student> set1 = StudentTool.getStudent(cmp);
StudentTool.writeStudent(set1);*/
}
}
//定义学生类。因为要排序,所以要实现comparable类,复写hashcode(),equals(),compareTo()方法
class Student implements Comparable<Student>
{
private String name;
private int ch,ma,en;
private int sum;
Student(String name,int ch,int ma,int en)
{
this.name = name;
this.ch = ch;
this.ma = ma;
this.en = en;
sum = ch+ma+en;
}
public String getname()
{
return name;
}
public int getsum()
{
return sum;
}
public int HashCode()
{
return name.hashCode()+sum*39;
}
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 int compareTo(Student s)
{
int num = new Integer(this.sum).compareTo(new Integer(s.sum));//按分数排序,分数相同按照姓名
if(num==0)
return this.name.compareTo(s.name);
return num;
}
public String toString()
{
return "Student["+name+", "+ch+", "+ma+", "+en+"]";
}
}
//定义工具类,操作学生对象
class StudentTool
{
//将数据保存到学生对象中,将学生对象保存到TreeSet集合中
//按照自定义比较器排序,降序
public static Set<Student> getStudent(Comparator<Student> cmp) throws IOException
{
BufferedReader buf = new BufferedReader(new InputStreamReader(System.in));
String line = null;
//因为要按照分数排序,而且要将学生对象存储,所以定义TreeSet集合
Set<Student> set = null;
if(cmp==null)
set = new TreeSet<Student>();
else
set = new TreeSet<Student>(cmp);
while((line = buf.readLine())!=null)
{
if("over".equals(line))
break;
String s[] = line.split(",");
//将数据保存到学生对象中
Student stu = new Student(s[0],
Integer.parseInt(s[1]),
Integer.parseInt(s[2]),
Integer.parseInt(s[3]));
set.add(stu);
}
buf.close();
return set;
}
//按照默认排序,升序
public static Set<Student> getStudent() throws IOException
{
return getStudent(null);
}
//将集合中的数据写到文件中
public static void writeStudent(Set<Student> s) throws IOException
{
BufferedWriter bw = new BufferedWriter(new FileWriter("c:/Student1.txt"));
for(Student stu:s)
{
bw.write(stu.toString()+"\t");
bw.write(stu.getsum()+"");//因为write方法只识别INT类型数据的后八位,所以转换成字符串类型
bw.newLine();
bw.flush();
}
bw.close();
}
}
12. 练习-将学生信息写到文件中(没比较器)
public class Test
{
/**
* 第1题: 有五个学生,每个学生有3门课(语文、数学、英语)的成绩,写一个程序接收从键盘输入学生的信息,
* 输入格式为:name,30,30,30(姓名,三门课成绩),然后把输入的学生信息按总分从高到低的顺序写入到
* 一个名称"stu.txt"文件中。要求:stu.txt文件的格式要比较直观,打开这个文件,
* 就可以很清楚的看到学生的信息。
*
* 思路:
* 1.定义学生对象,因为要排序,所以实现comparable接口,复写Object的Hashcode()和equals()方法,
* comparable接口的compareTo()方法,定义TreeSet集合存储学生对象。
* 2.定义IO输入流获取学生信息,输出流将信息写到文件
* (源:system.in-BufferedReader; 目的:硬盘文件-PrintWriter)
* 3.TreeSet集合是自然排序,升序的,所以要定义比较器;
*/
public static void main(String[] args)
{
//创建集合,调用获得学生信息的方法
Set<Student> students = StudentTool.getStudent();
//将信息写到硬盘文件
StudentTool.OutputStudent(students);
}
}
//定义学生类,实现comparable接口
class Student implements Comparable<Student>
{
private String name;//学生名字
private int chinese;//语文成绩
private int math;//数学成绩
private int english;//英语成绩
private int sum;//总成绩
//构造方法
public Student(String name, int chinese, int math, int english)
{
this.name = name;
this.chinese = chinese;
this.math = math;
this.english = english;
sum = chinese+math+english;
}
//获取学生名字
public String getName()
{
return name;
}
//获取学生语文成绩
public int getChinese()
{
return chinese;
}
//获取学生数学成绩
public int getMath()
{
return math;
}
//获取学生英语成绩
public int getEnglish()
{
return english;
}
//获取学生总成绩
public int getSum()
{
return sum;
}
//复写hashcode()方法
public int HashCode()
{
return name.hashCode()+38*sum;
}
//复写equals()方法
public boolean equals(Object obj)
{
if(!(obj instanceof Student))
new ClassCastException("输入的类型错误!");
Student stu = (Student)obj;
return this.name.equals(stu) && (this.sum)==(stu.sum);
}
//复写compareTo()方法
public int compareTo(Student stu)
{
//在这里换顺序是不能改变对象再集合中的排序方式的
int num = new Integer(stu.sum).compareTo(new Integer(this.sum));
if(num==0)
return this.name.compareTo(stu.name);
return num;
}
//复写此方法,可以输出对象的属性信息
public String toString()
{
return name+"["+"Chinese="+chinese+",Math="+math+",English="+english+"]";
}
}
//定义操作学生信息的工具类
class StudentTool
{
//从键盘获取学生信息,传入一个比较器
public static Set<Student> getStudent()
{
//用缓冲技术提高效率
BufferedReader buf = null;
//因为涉及到对象的排序,所以用TreeSet集合存储学生信息
Set<Student> stuInfo = new TreeSet<Student>();
try
{
//获取键盘输入
buf = new BufferedReader(new InputStreamReader(System.in));
//用来存键盘输入的一行数据
String Line = null;
while((Line=buf.readLine())!=null)
{
//输入end,程序退出
if("end".equals(Line))
break;
//按逗号拆分输入的数据
String[] stubuf = Line.split(",");
//把输入的数据生成学生对象
Student student = new Student(stubuf[0],Integer.parseInt(stubuf[1]),
Integer.parseInt(stubuf[2]),
Integer.parseInt(stubuf[3]));
stuInfo.add(student);
}
}
catch(IOException e)
{
throw new RuntimeException("读取异常");//捕捉到I/O 异常,抛出运行错误;
}
finally
{
if(buf!=null)
{
try
{
buf.close();//关闭输入流
}
catch (IOException e)
{
throw new RuntimeException("关闭输入流异常!");
}
}
}
return stuInfo;//返回集合
}
//将学生信息写到硬盘文件
public static void OutputStudent(Set<Student> stu)
{
//用PrintWriter可以简化代码
PrintWriter pw = null;
try
{
//创建打印流,与stu.txt文件关联
pw = new PrintWriter(new FileWriter("d:/stu.txt"),true);
//通过foreach语句,集合的对象取出来,逐个写到硬盘文件
for(Student s : stu)
{
//pw.println(s.toString()+"||Sum="+s.getSum()+"\r\n");
pw.print(s.toString()+"\t");
pw.println(s.getSum()+"");
}
}
catch(IOException e)
{
throw new RuntimeException("写出文件失败!");//捕捉到I/O 异常,抛出运行错误;
}
finally
{
if(pw!=null)
{
pw.close();//关闭打印流
}
}
}
}