Java中按照流向分,分为输入流和输出流,按照处理数据单元分,分为字符流和字节流。这个总结开始会简单写一点方法,然后会写一下常用模板,套用就好了。
File类的常用方法:
方法 | 说明 |
boolean exists( ) | 测试文件是否存在 |
String getAbsolutePath( ) | 返回此对象表示的文件的绝对路径 |
String getName( ) | 返回此对象表示的文件的名称 |
String getParent( ) | 返回此File对象的路径名的上一级,如果路径名没有上一级,则返回null |
boolean delete( ) | 删除此对象指定的文件 |
boolean createNewFile( ) | 创建空文件,不创建文件夹 |
boolean isSirectory( ) | 测试此File对象表示的是否为目录 |
boolean mkdir( ) | 创建一个目录,它的路径名由当前File对象指定 |
boolean mkdirs( ) | 创建包括父目录的目录 |
Java中的输出流主要由OutputStream和Write组成,输入流主要由InputStream和Reader作为基类。都是抽象类。
InputStream常用的子类有FileInputStream,用于从文件中读取数据。常用方法:
方法 | 说明 |
int read( ) | 从输入流中读取下一个字节数据 |
int read(byte [ ] b) | 从输入流中读取数据,并将数据存储在缓冲区数组b中,返回实际读取的字节数 |
int read(byte [ ] b,int off,int len) | 从输入流中读取最多len长度的字节,保存到字节数组b中,保存的位置从off开始 |
void close() | 关闭输入流 |
Reader类的常用子类有BufferedReader,接受Reader对象作为参数,并对其添加字符缓冲器。常用方法:
方法 | 说明 |
int read( ) | 从输入流中读取单个字符,返回所读取的字符数据 |
int read(byte [ ] c) | 从输入流中最多读取c.length个字符,保存到字符数组c中,返回实际读取的字符数 |
int read(char [ ] c,int off.int len) | 从输入流中读取最多len个字符,保存到字符数组c中,保存的位置从off开始,返回实际读取的字符数 |
void close( ) | 关闭流 |
OutputStream类的常用子类为FileOutputStream,用于向文件写数据。常用方法:
方法 | 说明 |
void weite( int c) | 将指定的字节数据写入此输出流中 |
void weite( byte [ ] buf) | 将数组buf中的所有字节写入此输出流中 |
void write(byte [ ] b,int off.int len) | 将字节数组中从偏移量off开始的长度为len的字节数据输出到输出流中 |
void close( ) | 关闭输出流 |
Write类的常用子类为BufferedWriter,用于将数据缓冲到字符输出流。常用方法:
方法 | 说明 |
void write( String str) | 将str字符串里包含的字符输出到指定的输出流中 |
void write( String str,int off,int len) | 将str字符串从off位置开始,长度为len的多个字符串输出到输出流中 |
void close( ) | 关闭输出流 |
void flush( ) | 刷新输出流 |
读写二进制文件:
利用DataInputStream类读二进制文件,利用DataOutputStream类写二进制文件。
顺带一提,这两个类搭配使用,可以按照与平台无关的方式从流中读取基本数据类型的数据,如int、float、double和boolean等,此外,DataInputStream的readUTF()方法能读取采用UTF-8字符集编码的字符串。同样,在向外输出时也遵循此规则,但是,方法中并没有readString( )和writeString( )方法。
序列化和反序列化:
序列化就是将对象的状态存储到特定存储介质中的过程,也就是将对象状态转换为可保持或可传输格式的过程。序列化后的对象保存的是二进制状态。
Java中只有实现了java.io.Serializable接口的类的对象才能被序列化。
反序列化是从特定存储介质中读取数据并重新构建成对象的过程。反序列化要进行强制类型转换。(如果一个可序列化的类,有多个父类(包括直接父类或间接父类),则这些父类要么是可序列化的,要么有无参数的构造器,否则会抛出异常)。对于一些不想被序列化的敏感信息,可以用transient来修饰。
当需要序列化某个特定对象时,它的各个成员对象也必须是可序列化的。所有保存到磁盘中的对象都有一个序列号,当程序试图序列化一个对象时,会检查是否已经被序列化,只有序列化后的对象才能被转换成字节序列输出。如果对象已经被序列化,则程序直接输出一个序列化编号。序列化编号也是一个序列化后的对象,在反序列化时也得到一个对象。
通常用到输入输出时的写法:
字节流:
(输入流)FileInputStream fis =new FileInputStream( 路径 );
BufferedInputStream bis =new BufferedInputStream( fis );
(输出流)FileOutputStream fos =new FileOutputStream(路径);
BufferedOutputStream bos =new BufferedOutputStream (fos);
字符流:
(输入)FileReader fd =new FileReader (路径);
BufferedReader br =new BufferedReader (fd);
(输出)FileWriter fw =new FileWriter (路径) ;
BufferedWriter bw=new BufferedWriter (fw) ;
转换格式时:
(输入)FileInputStream fis =new FileInputStream (路径) ;
InputStreamReader isr =new InputStreamReader (fis,"格式") ;
BufferedReader br =new BufferedReader (isr);
(输出)FileOutputStream fos =new FileOutputStream (路径);
OutputStreamWriter osw =new OutputStreamWriter (fos,"格式") ;
BufferedWriter bw =new BufferedWriter (osw) ;
二进制文件:
(输入)FileInputStream fis =new FileInputStream (路径);
DataInputStream dis =new DataInputStream (fis) ;
(输出)FileOutputStream fos =new FileOutputStream (路径) ;
DataOutputStream dos =new DataOutputStream (fos) ;
序列化和反序列化:
(序列化)FileOutputStream fos =new FileOutputStream (路径) ;
ObjectOutputStream oos =new ObjectOutputStream (fos) ;
(反序列化)FileInputStream fis =new FileOutputStream (路径) ;
ObjectInputStream ois =new ObjectInputStream (fis) ;
基本格式差不多就是这样了,最后一个很重要的点,开了的流记得关,先开的后关,后开的先关。
反射:
反射这方面我的理解也并不是太深,就简单讲讲表面。
Java的反射机制是Java的特性之一,反射机制是构建框架技术的基础所在。Java反射机制是指在运行状态中,动态获取信息以及动态调用对象方法的功能。Java反射有三个动态性质,分别是运行时生成对象实例,运行期间调用方法,运行时更改属性。
通过Java反射,可以实现以下功能:
在运行时判断任意一个对象所属的类;
在运行时构造任意一个类的对象;
在运行时判断任意一个类所具有的方法和属性;
在运行时调用任意一个对象的方法;
Java反射常用API:Class类,反射的核心类。Filed类,表示类的属性。Method类,表示类的方法。Constructor类,表示类的构造方法。
Java程序中获得Class对象通常有如下三种方法:
第一种:调用对象的getClass( )方法
Student stu=new Student();
Class cla=stu.getClass();
第二种:调用类的class属性
Class cla1=Student.class;
第三种:使用Class类的forName( )静态方法
Class cla2=Class.forName("某个类的全名");
相比之下调用某个类的class属性来获取该类对应的Class对象这种方式更有优势。因为代码更安全,程序在编译阶段就可以检查需要访问的Class对象是否存在。而且程序性能更高,因为这种方式无需调用方法,所以性能更好。
获得Class对象后,可以用Class对象获得该类里的成员,包括方法,构造方法以及属性,方法由Method表示,构造方法由Constructor表示,属性由Field表示。这个方法太繁杂了,我就不手打了,可以自己去看看API。
结合两个例子来看看怎么用。
这是随手写的一个学生类。
public class Student{
private String name;
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Student() {
}
public Student(int age, String name) {
this.age = age;
this.name = name;
}
使用Class对象创建一个该Class对象对应的对象,并直接获取name属性,然后修改name属性
try {
Student stu=new Student();
Class cla=stu.getClass();
Field nameFiele=cla.getDeclaredField("name");
nameFiele.setAccessible(true);
nameFiele.set(stu,"熊大");
System.out.println(stu.getName());
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
提一句,getField( )方法只能获取public访问权限的属性,而使用getDeclaredField( )方法则可以获取所有访问权限的属性。
使用Class对象创建一个该Class对象对应的对象,并操作对象里面设置姓名的方法,更改对象的姓名
Student stu=new Student();
Class cla=stu.getClass();
try {
Method nameMethod=cla.getMethod("setName",String.class);
nameMethod.invoke(stu,"熊二");
System.out.println(stu.getName());
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
通过Method的invoke( )方法调用方法时,Java会要求程序必须有调用该方法的权限,如果程序确实需要调用某个对象的private方法,可以先调用setAccessible( )方法,将Method对象的accessible标志设置为指定的布尔值,值为true则代表该Method在使用时应该取消Java语言访问权限检查,值为false则表示该Method在使用时应该进行Java语言访问权限检查。
差不多就是这个样子吧,动态操作嘛~
关于反射的详细说明建议看看别的博客,我这里写的比较粗浅。
练习:
1.编写一个程序将file1.txt文件中的内容复制到file2.txt文件中。
FileReader fd=null;
BufferedReader br=null;
FileWriter fw=null;
BufferedWriter bw=null;
try {
fd=new FileReader("file1.txt");
br=new BufferedReader(fd);
fw=new FileWriter("file2.txt");
bw=new BufferedWriter(fw);
String s=null;
while((s=br.readLine())!=null){
bw.write(s);
bw.newLine();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
bw.close();
fw.close();
br.close();
fd.close();
} catch (IOException e) {
e.printStackTrace();
}
}
2.编写一个Java程序读取Windows目录下的win.ini文件,并输出内容。
FileReader fd=null;
BufferedReader br=null;
try {
fd=new FileReader("c:\\Windows\\win.ini");
br=new BufferedReader(fd);
String line=br.readLine();
while(line!=null){
System.out.println(line);
line=br.readLine();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
br.close();
fd.close();
} catch (IOException e) {
e.printStackTrace();
}
}
3.编写一个程序,运行Java控制台程序,检测本地是否存在学生对象(反序列化),如果保存,则输出学生信息,如果没有保存,则通过学生类Student创建一个学生对象,将学生信息输出并保存到本地文件(序列化)中。
FileInputStream fis=null;
ObjectInputStream ois=null;
FileOutputStream fos=null;
ObjectOutputStream oos=null;
Student stu=new Student();
try {
fis=new FileInputStream("d:\\stu.txt");
if (fis.available()>0){
ois=new ObjectInputStream(fis);
stu=(Student)ois.readObject();
System.out.println("本地有学生信息,学生信息为:");
System.out.println("姓名:"+stu.getName());
System.out.println("年龄:"+stu.getAge());
System.out.println("性别:"+stu.getSex());
}else{
System.out.println("本地没有学生信息,自动添加一位学员信息");
fos=new FileOutputStream("d:\\stu.txt");
oos=new ObjectOutputStream(fos);
stu=new Student(18,"张三",'男');
oos.writeObject(stu);
System.out.println("添加学员信息为:");
System.out.println("姓名:"+stu.getName());
System.out.println("年龄:"+stu.getAge());
System.out.println("性别:"+stu.getSex());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
try {
if(null!=oos){
oos.close();
}
if(null!=fos){
fos.close();
}
if(null!=ois){
ois.close();
}
if(null!=fis){
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
这个题的finally如果不判断是否开启了对应的流,会边运行边报错,虽然结果是对的,但是看起来不舒服,所以所有关闭流的时候建议都判断一下。