Java I/O和反射机制
一、操作文件或目录属性
1、File
File类构造方法
方法 | 说明 |
---|---|
File(String pathname) | 用指定的文件路径构造文件 |
File(String dir,String subpath) | 在指定的目录下创建指定文件名的文件 |
File(File parent,String subpath) | 根据一个文件对象和一个字文件构造文件对象 |
File类常用方法
方法 | 说明 |
---|---|
file.exists() | 判断文件是否存在 |
file.createNewFile() | 创建文件 |
file.delete() | 删除此对象指定的对象 |
file.getName() | 返回此对象表示的文件名 |
file.mkdir() | 创建目录(文件夹) |
file.isFile() | 判断此对象是否是文件 |
file.isDirectory() | 判断此对象是否是目录 |
file.length() | 获取文件或目录的大小 |
file.getPath() | 获取文件或目录的路径 |
file.getAbsolutePath() | 获取文件或目录的绝对路径 |
file.getParent() | 返回此File对象的路径名的上一级,若无上一级则返回null |
2、例
import java.io.File;
import java.io.IOException;
public class Demo01 {
public static void main(String[] args) {
//可以代表已有文件,也可以代表不存在的文件
File file=new File("d:/abc");
//创建此文件
try {
file.mkdirs();//创建目录
file.createNewFile();//创建文件
} catch (IOException e) {
e.printStackTrace();
}
//删除此文件(彻底删除不过回收站)
file.delete();
//判断是否存在此文件
boolean flag=file.exists();
System.out.println(flag);
}
}
二、Java流
-
读文件是指把文件中的数据读取到内存中。写文件是把内存中的数据写到文件中。
-
通过流读写文件,流是一串流动的字符,是以先进先出的方式发送和接受数据的通道。
-
流分为输入流和输出流,所谓的输入和输出是相对内存来说的。
-
按照处理数据单元划分,流分为字节流和字符流。
-
字符流在操作时使用了缓冲区,字节流在操作时直接操作文件,不使用缓冲区。
1、字节流:
- 字节流是8位通用字节流,其基本单位是字节。
- 字节流的基类是InputStream类和OutputStream类,他们都是抽象类。
- 输入:InputStream,从文件中读取文字,多用其子类FileInputStream(文件输入流)。
- 输出:OutputStream,向文件写数据,多用其子类FileOutStream(文件输出流)。
InputStream类的常用方法
方法 | 说明 |
---|---|
int read() | 从输入流中读取下一个字节数据 |
int read(byte[] b) | 从输入流中读取数据,并将数据存储在缓冲区数组b中,返回实际读取的字节数 |
int read(byte[] b,int off,int len) | 从输入流中读取最多len长度的字节,保存到字节数组b中,保存的位置从off开始 |
void close() | 关闭输入流 |
OutputStream类的常用方法
方法 | 说明 |
---|---|
void write(int c) | 将指定的字节数据写入此输出流中 |
void write(byte[] buf) | 将数组buf中的所有字节写入此输出流中 |
void write(byte[] b,int off,int len) | 将字节数组中从偏移量off开始的长度为len的字节数据输出到输出流中 |
void close() | 关闭输出流 |
2、字符流
- 字符流是16位的Unicode字符流,基本单位是Unicode字符。
- 字符流最适合用来处理字符串和文本,因为他们支持国际上大多数的字符集和语言。
- 字符的的基类是Reader类和Writer类,他们都是抽象类。
- 输入:Reader,常用子类BufferedReader,接受Reader对象作为参数,并对其添加字符缓冲器。
- 输出:Writer,常用子类BufferedWriter,用于将数据缓冲到字符输出流。
Reader类的常用方法
方法 | 说明 |
---|---|
int read() | 从输入流读取单个字符,返回所读取的字符数据 |
int read(byte[] c) | 从输入流中最多读取c.lenght个字符,保存到字符数组c中,返回实际读取的字符数 |
int read(char[] c,int off,int len) | 从输入流中读取最多len个字符,保存到字符数组c中,保存的位置从off位置开始,返回实际读取的字符数 |
void close() | 关闭流 |
Writer类的常用方法
方法 | 说明 |
---|---|
void write(String str) | 将String字符串里包含的字符输出到指定的输出流中 |
void write(String str,int off,int len) | 将str字符串里从off位置开始,长度为len的多个字符输出到输出流中 |
void close() | 关闭输出流 |
void flush() | 刷新输出流 |
3、读写文件
1)读写文本文件
①使用字节流读写文件
package section1;
/**
* 使用字节流读取文本文件
*/
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
public class Demo02 {
public static void main(String[] args) throws IOException {
InputStream one=new FileInputStream("D:\\操作\\hello.txt");//创建流对象
System.out.println("可读取的字节数:"+one.available());//获取对象字节数
int date;//创建接受对象读取返回值的变量
while((date=one.read())!=-1) {//循环读取流对象所关联的文本文件
System.out.print((char)date+" ");//read()方法返回整数型,强制类型转换为字符型
}
one.close();//关闭流对象
}
}
- **read()**方法返回整数,如果读取的是字符串,需进行强制类型转换。
- 流对象使用完毕后需要关闭
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* 使用字节流向文本文件中写入数据
*
*/
public class Demo03 {
public static void main(String[] args) throws IOException {
//创建对象流,以追加方式写入文件
OutputStream two=new FileOutputStream("D:\\操作\\hello.txt",true);
//创建要写入的字符串
String str="好好学习,天天向上!";
//将字符串加入缓冲区
byte[] words=str.getBytes();
//将缓冲区里的数据通过流写入文本文件
two.write(words);
//关闭流
two.close();
}
}
- 在创建FileOutputStream实例时,如果相应文件并不存在,就会自动创建一个空的文件。
- 如果对象表示的文件路径存在,但是代表一个文件目录,则会抛出FileNotFoundException类型异常。
- 默认情况下,向文件写数据时将覆盖文件中原有的内容。
- 若创建流对象的第二个参数为true,表示在文件末尾添加数据。
②使用字符流读写文件
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/**
* 使用字符流读取文件内容
*
*/
public class Demo04 {
public static void main(String[] args) throws IOException {
//创建流对象
FileReader fr=new FileReader("D:\\操作\\hello.txt");
BufferedReader br=new BufferedReader(fr);
//读取一行数据,返回字符串
String line=br.readLine();
//输出读取的内容
while (line!=null) {
System.out.println(line);
line=br.readLine();
}
//关闭流
br.close();
fr.close();
}
}
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
/**
* 使用字符流写入文件
*
*/
public class Demo05 {
public static void main(String[] args) throws IOException {
//创建FileWirte对象,创建BufferedWirte对象
FileWriter fw=new FileWriter("D:\\操作\\hello.txt",true);
BufferedWriter bw=new BufferedWriter(fw);
//写入内容
bw.write("hello world!");
bw.write("nonono");
//换行
bw.newLine();
//写入内容
bw.write("666");
//换行
bw.newLine();
//写入内容
bw.write("沙袋");
//刷新缓冲区
bw.flush();
//关闭流
bw.close();
fw.close();
}
}
2)读写二进制文件
- 常见的文件读写中还有一种二进制文件读写。
- 读写二进制文件的常用类有DataInputStream和DateOutputStream。
①使用字节流读写文件
- 利用DataInputStream读二进制文件与利用FileInputStream类读文件极其相似,也要用到FileInputStream类关联二进制文件。
②使用字符流读写文件
利用DataOutputStream读二进制文件与利用FileOutputStream类读文件极其相似,也要用到FileOutputStream类关联二进制文件。
例:
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 读二进制文件
* @author Administrator
*
*/
public class Demo06 {
public static void main(String[] args) throws IOException {
//创建流对象
FileInputStream fis=new FileInputStream("D:\\操作\\one.class");
DataInputStream dis=new DataInputStream(fis);
FileOutputStream fos=new FileOutputStream("D:\\操作\\two.class");
DataOutputStream dos=new DataOutputStream(fos);
int two;
while ((two=dis.read())!=-1) {//读数据
dos.write(two);//把读取的数据写入到two.class文件中
}
//关闭流
fis.close();
dos.close();
}
}
三、重定向标准I/O
1、标准I/O
System.in和System.out,他们是Java提供的两个标准输入/输出流,主要用于从键盘接受数据和向屏幕输出数据。
1)System.in常用方法:
int read():此方法从键盘接收一个字节的数据,返回值是该字符的ASCII码。
int read(byte []buf):此方法从键盘接收多个字节的数据,保存至buf中,返回值是接收字节数据的个数。
2)System.out常用方法:
print():向屏幕输出数据,不换行,参数可以是Java的任意数据类型。
println():向屏幕输出数据,换行,参数可以是Java的任意数据类型。
2、重定向
使用标准I/O对文件进行读写很方便,如果想要使用标准I/O对文件进行读写就需要重定向标准I/O,也就是说将标准I/O重定向到其他I/O的设备。
1)重定向标准I/O方法
方法 | 说明 |
---|---|
static void setErr(PrintStream err) | 重定向标准错误输出流 |
static void setIn(InputStream in) | 重定向标准输入流 |
static void setOut(PrintStream out) | 重定向标准输出流 |
2)例
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
/**
* 重定向标准I/O
* @author Administrator
*
*/
public class Demo07 {
public static void main(String[] args) throws FileNotFoundException {
//创建PrintStream输出流
PrintStream ps=new PrintStream(new FileOutputStream("D:\\操作\\hello.txt",true));
//将标准输出流重定向到文件
System.setOut(ps);
//向文件中输出内容
System.out.print("我的测试,重定向到文件666!");
}
}
四、序列化
1、认识序列化
-
序列化就是将对象的状态存储到特定存储介质的过程,也就是将**对象状态转换为可保持或可传输格式**的过程。
-
序列化后可以将Java对象转化为字节流,再把字节流写入数据流,存储到存储介质(文件)中。
-
序列化后的对象保存的是二进制状态,可以借助网络进行传输,在不同平台间进行传输,通过反序列化得到相同对象,无须担心数据因平台问题显示异常,实现了平台的无关性。
2、序列化保存对象信息
- Java中只有实现了java.io.Serializable接口的类的对象才能被序列化。
- Serializable表示可串行、可序列化的,序列化又称串行化。
- String类、包装类、Data类都实现了Serializable接口。
例:
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
/**
* 使用序列化将学生对象保存到文件中
* @author Administrator
*
*/
public class Demo08 {
public static void main(String[] args) throws IOException {
//创建对象输出流,包装了文件输出流
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("D:\\操作\\hello.txt"));
//创建对象
Student stu=new Student("小明",18,"男");
// //如要序列化多个对象,如下
// Student stu1=new Student("小红",18,"女");
// ArrayList<Student> list=new ArrayList<Student>();
// list.add(stu);
// list.add(stu1);
// oos.writeObject(list);
//对象序列化,写入输出流
oos.writeObject(stu);
oos.close();
}
}
3、反序列化获取对象信息
- 将对象状态读取出来称为反序列化。
- 反序列化是从特定存储介质中读取数据并重构对象的过程。
- 如果向文件中使用序列化机制写入多个对象,那么反序列化恢复对象时,必须按照写入的顺序读取。
- 如果一个可序列化的类,有多个父类(直接或间接),则这些父类要么是可序列化的,要么有无参数的构造器,否则会抛出异常。
例:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
public class Demo09 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//创建ObjectInputStream输入流,包装文件输入流
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("D:\\操作\\hello.txt"));
// //如要将写入文件的集合反序列化,如下
// //反序列化,强制类型转换
// ArrayList<Student> list=(ArrayList<Student>)ois.readObject();
// //输出集合中对象的信息
// for (Student stu : list) {
// System.out.println("姓名为:"+stu.getName());
// System.out.println("年龄为:"+stu.getAge());
// System.out.println("性别为:"+stu.getSex());
// }
//反序列化,进行强制类型转换
Student stu=(Student)ois.readObject();
//输出生成后的对象信息
System.out.println("姓名为:"+stu.getName());
System.out.println("年龄为:"+stu.getAge());
System.out.println("性别为:"+stu.getSex());
ois.close();
}
}
五、反射
1、认识反射
①Java反射机制是指在运行状态中,动态获取信息以及动态调用对象方法的功能。
②Java反射有3个动态性质:运行时生成对象实例、运行期间调用方法、运行时更改属性。
③通过Java反射,可以实现以下功能:
- 在运行时判断任意一个对象所属的类。
- 在运行时构造任意一个类的对象。
- 在运行时判断任意一个类所具有的方法和属性。
- 在运行时调用任意一个对象的方法。
④Java反射技术常用的类如下:
- Class类:反射的核心类。
- Field类:表示类的属性。
- Method类:表示类的方法。
- Constructor类:表示类的构造方法。
⑤在Java程序中使用反射的基本步骤:
- 导入java.lang.reflect.*。
- 获得需要操作的类的java.lang.Class对象。
- 调用Class的方法获取Field、Method等对象。
- 使用反射API进行操作。
2、反射的应用
1)获取类的信息
①第一步,获取Class对象
-
通过getClass()方法,得到该对象所属类的Class对象。
(Class cla=stu.getClass()😉
-
调用某个类的class属性得到该类对应的Class对象。*此方法最优
(Class cla=Student.class;)
-
使用Class类的forName()静态方法获取,该方法需传入字符串参数,该字符串参数是某个类的全名。
(Class cla=Class.forName(“com.pd.jadv.reflection.Student”)😉
②第二部,从Class对象获取该类信息
- 访问Class对象所对应类的构造方法的常用方法:
方法 | 说明 |
---|---|
Constructor getConstructor(Class[] params) | 返回此Class对象所包含的类的指定的public构造方法 |
Constructor[] getConstructor() | 返回此Class对象所包含的类的所有public构造方法 |
Constructor getDeclaredConstructor(Class[] params) | 返回此Class对象所包含的类的指定构造方法,与构造方法的访问级别无关 |
Constructor[] getDeclaredConstructor() | 返回此Class对象所包含的类的所有构造方法,与构造方法的访问级别无关 |
- 访问Class对象所对应类的方法的常用方法:
方法 | 说明 |
---|---|
Method getMethod(String name,Class[] params) | 返回此Class对象所包含的类的指定的public方法 |
Method[] getMethods() | 返回此Class对象所包含的类的所有public方法 |
Method getDeclaredMethod(String name,Class[] params) | 返回此Class对象所包含的类的指定方法,与方法的访问级别无关 |
Method[] getDeclaredMethods() | 返回此Class对象所包含的类的全部方法,与方法的访问级别无关 |
- 访问Class对象所对应类的属性的常用方法:
方法 | 说明 |
---|---|
Field getField(String name) | 返回此Class对象所包含的类的指定的public属性 |
Field[] getField() | 返回此Class对象所包含的类的所有public属性 |
Field getDeclaredField(String name) | 返回此Class对象所包含的类的指定属性,与方法的访问级别无关 |
Field[] getDeclaredField() | 返回此Class对象所包含的类的全部属性,与方法的访问级别无关 |
- 访问Class对象所对应类的注解的常用方法:
方法 | 说明 |
---|---|
A getAnnotation(ClassannotationClass) | 试图获取该Class对象所表示类上指定类型的注解,如果该类型的注解不存在则返回null。 |
Annotation[] getAnnotations() | 返回此类上存在的所有注解 |
Annotation[] getDeclaredAnnotations() | 返回直接存在于此类上的所有注解 |
- 访问Class对象所对应类的构其他信息的常用方法:
方法 | 说明 |
---|---|
Class[] getDeclaredClasses() | 返回该Class对象所对应类里包含的全部内部类 |
Class[] getDeclaringClass() | 访问Class对应的类所在的外部类 |
Class[] getInterfaces() | 返回该Class对象对应类所实现的全部接口 |
int getModifers() | 返回此类或接口的所有修饰符 |
Package getPackage() | 获取此类的包 |
String getName() | 以字符串形式返回此Class对象所表示的类的名称 |
String getSimpleName | 以字符串形式返回此Class对象所表示的类的简称 |
Class getSuperclass | 返回该Class所表示的类的超类对应的Class对象 |
2)创建对象
通过反射创建对象又两种方式:
①使用Class对象的newInstance()方法创建对象。
此方法要求该Class对象的对应类有默认构造方法。
例:
package section1;
import java.util.Date;
import javax.xml.crypto.Data;
/**
* 通过Class对象的newInstance()方法创建对象
* @author Administrator
*
*/
public class Demo10 {
public static void main(String[] args) throws Exception{
Class cla=Date.class;
Date d=(Date)cla.newInstance();
System.out.println(d.toString());
}
}
②使用Constructor对象创建对象。
此方法要使用Class对象获取指定的Constructor对象,
再调用Constructor对象的newInstance()方法创建该Class对象对应类的实例。
此方式可以选择使用某个类的指定构造方法来创建实例。
例:
package section1;
import java.lang.reflect.Constructor;
import java.sql.Date;
/**
* 利用Constructor对象指定的构造方法创建对象
* @author Administrator
*
*/
public class Demo11 {
public static void main(String[] args) throws Exception{
Class cla=Date.class;
Constructor cu=cla.getConstructor(long.class);
Date d=(Date)cu.newInstance(1987);
System.out.println(d.toString());
}
}
3)访问类的属性
通过Field对象可以对属性进行取值或赋值操作,方法如下:
方法 | 说明 |
---|---|
Xxx getXxx(Object obj) | 该方法中Xxx对应8个基本数据类型。obj为该属性所在的对象。 |
Object get(Object obj) | 得到引用类型属性值。 |
void setXxx(Object obj,Xxx val) | 将obj对象的该属性设置成val值。针对8个基本数据类型 |
void set(Object obj,object val) | 将obj对象的该属性设置成val值。针对引用类型赋值 |
void setAccessible(bool flag) | 对获取到的属性设置访问权限。参数为true,可对私有属性进行取值和赋值 |
例:
class Student{
private String name;
private int age;
public String toString() {
return "name is "+name +",age is "+age;
}
}
import java.lang.reflect.Field;
public class Demo12 {
public static void main(String[] args) throws Exception{
//创建一个Student对象
Student p=new Student();
//获取Student对应的Class对象
Class cla=Student.class;
//获取Student类的name属性,使用getDeclaredField()方法可获取各种访问级别的属性
Field nameField=cla.getDeclaredField("name");
//设置通过反射访问该Field时取消权限检查
nameField.setAccessible(true);
//调用set()方法为p对象的制定Field设置值
nameField.set(p, "Jack");
//获取Student类的age属性,使用getDeclaredField()方法可获取各种访问级别的属性
Field ageField=cla.getDeclaredField("age");
//设置通过反射访问该Field时取消权限检查
ageField.setAccessible(true);
//调用setInt()方法为p对象的指定Field设置值
ageField.setInt(p, 20);
System.out.println(p);
}
}
4)访问类的方法
-
使用Method对象可以调用对象的方法。
-
在Method类中包含一个invoke()方法,方法定义如下:
Object invoke(Object obj,Object args)
其中,obj是执行该方法的对象,args是执行该方法时传入该方法的参数。
例:
package section1;
import java.lang.reflect.Method;
public class Demo13 {
public static void main(String[] args) throws Exception{
//获取Student对应的Class对象
Class cla=Student.class;
//创建Student对象
Student p=new Student();
//得到setName方法
Method met1=cla.getMethod("setName", String.class);
//调用setName,为name赋值
met1.invoke(p, "jack");
//得到getName方法
Method met=cla.getMethod("getName", null);
//调用getName,获取name的值
Object o=met.invoke(p, null);
System.out.println(o);
}
}
5)使用Array类动态创建和访问数组
在java.lang.reflect包下还提供了一个Array类,此Array类的对象可以代表所有的数组。
程序可以通过使用Array类来动态的创建数组、操作数组元素等。
例:
package section1;
/**
* 使用Array类动态操作数组
*/
import java.lang.reflect.Array;
public class Demo14 {
public static void main(String[] args) {
//创建一个元素类型为String,长度为10的数组
Object arr=Array.newInstance(String.class,10);
//依次为arr数组中index为5、6的元素赋值
Array.set(arr, 5, "jack");
Array.set(arr, 6, "john");
//依次取出arr数组中index为5、6的元素的值
Object o1=Array.get(arr,5);
Object o2=Array.get(arr,6);
//输出o1,o2的值
System.out.println(o1.toString()+o2.toString());
}
}