java收藏
本篇文章为在工作中使用JAVA反射的经验总结,也可以说是一些小技巧,以后学会新的小技巧,会不断更新。\O-NO(uJ A V A S\v~;`~_NSN© 2004-2008 EDZH.COM.\O-NO(uJ A V A S\v~;`~_NSN本文不准备讨论JAVA反射的机制,网上有很多,大家随便google一下就可以了。\O-NO(uJ A V A S\v~;`~_NSN© 2004-2008 EDZH.COM.\O-NO(uJ A V A S\v~;`~_NSN
在开始之前,我先定义一个测试类Student,代码如下:
- package chb.test.reflect;
- public class Student {
- private int age;
- private String name;
- 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 static void hi(int age,String name){
- System.out.println("大家好,我叫"+name+",今年"+age+"岁");
- }
- }
- package chb.test.reflect;
- public class Student {
- private int age;
- private String name;
- 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 static void hi(int age,String name){
- System.out.println("大家好,我叫"+name+",今年"+age+"岁");
- }
- }
一、JAVA反射的常规使用步骤
反射调用一般分为3个步骤:
-
得到要调用类的class
-
得到要调用的类中的方法(Method)
-
方法调用(invoke)
代码示例:
- Class cls = Class.forName("chb.test.reflect.Student");
- Method m = cls.getDeclaredMethod("hi",new Class[]{int.class,String.class});
- m.invoke(cls.newInstance(),20,"chb");
- Class cls = Class.forName("chb.test.reflect.Student");
- Method m = cls.getDeclaredMethod("hi",new Class[]{int.class,String.class});
- m.invoke(cls.newInstance(),20,"chb");
二、方法调用中的参数类型
在方法调用中,参数类型必须正确,这里需要注意的是不能使用包装类替换基本类型,比如不能使用Integer.class代替int.class。\O-NO(uJ A V A S\v~;`~_NSN© 2004-2008 EDZH.COM.\O-NO(uJ A V A S\v~;`~_NSN
如我要调用Student的setAge方法,下面的调用是正确的:
- Class cls = Class.forName("chb.test.reflect.Student");
- Method setMethod = cls.getDeclaredMethod("setAge",int.class);
- setMethod.invoke(cls.newInstance(), 15);
- Class cls = Class.forName("chb.test.reflect.Student");
- Method setMethod = cls.getDeclaredMethod("setAge",int.class);
- setMethod.invoke(cls.newInstance(), 15);
而如果我们用Integer.class替代int.class就会出错,如:
- Class cls = Class.forName("chb.test.reflect.Student");
- Method setMethod = cls.getDeclaredMethod("setAge",Integer.class);
- setMethod.invoke(cls.newInstance(), 15);
- Class cls = Class.forName("chb.test.reflect.Student");
- Method setMethod = cls.getDeclaredMethod("setAge",Integer.class);
- setMethod.invoke(cls.newInstance(), 15);
jvm会报出如下异常:
- java.lang.NoSuchMethodException: chb.test.reflect.Student.setAge(java.lang.Integer)
- at java.lang.Class.getDeclaredMethod(Unknown Source)
- at chb.test.reflect.TestClass.testReflect(TestClass.java:23)<PRE>PRE>
- java.lang.NoSuchMethodException: chb.test.reflect.Student.setAge(java.lang.Integer)
- at java.lang.Class.getDeclaredMethod(Unknown Source)
- at chb.test.reflect.TestClass.testReflect(TestClass.java:23)<PRE>PRE>
三、static方法的反射调用
static方法调用时,不必得到对象示例,如下:
- Class cls = Class.forName("chb.test.reflect.Student");
- Method staticMethod = cls.getDeclaredMethod("hi",int.class,String.class);
- staticMethod.invoke(cls,20,"chb");//这里不需要newInstance
- //staticMethod.invoke(cls.newInstance(),20,"chb");
- Class cls = Class.forName("chb.test.reflect.Student");
- Method staticMethod = cls.getDeclaredMethod("hi",int.class,String.class);
- staticMethod.invoke(cls,20,"chb");//这里不需要newInstance
- //staticMethod.invoke(cls.newInstance(),20,"chb");
四、private的成员变量赋值
如果直接通过反射给类的private成员变量赋值,是不允许的,这时我们可以通过setAccessible方法解决。\O-NO(uJ A V A S\v~;`~_NSN© 2004-2008 EDZH.COM.\O-NO(uJ A V A S\v~;`~_NSN代码示例:
- Class cls = Class.forName("chb.test.reflect.Student");
- Object student = cls.newInstance();//得到一个实例
- Field field = cls.getDeclaredField("age");
- field.set(student, 10);
- System.out.println(field.get(student));
- Class cls = Class.forName("chb.test.reflect.Student");
- Object student = cls.newInstance();//得到一个实例
- Field field = cls.getDeclaredField("age");
- field.set(student, 10);
- System.out.println(field.get(student));
运行如上代码,系统会报出如下异常:
- java.lang.IllegalAccessException: Class chb.test.reflect.TestClass can not access a member of class chb.test.reflect.Student with modifiers "private"
- at sun.reflect.Reflection.ensureMemberAccess(Unknown Source)
- at java.lang.reflect.Field.doSecurityCheck(Unknown Source)
- at java.lang.reflect.Field.getFieldAccessor(Unknown Source)
- at java.lang.reflect.Field.set(Unknown Source)
- at chb.test.reflect.TestClass.testReflect(TestClass.java:20)<PRE>PRE>
- java.lang.IllegalAccessException: Class chb.test.reflect.TestClass can not access a member of class chb.test.reflect.Student with modifiers "private"
- at sun.reflect.Reflection.ensureMemberAccess(Unknown Source)
- at java.lang.reflect.Field.doSecurityCheck(Unknown Source)
- at java.lang.reflect.Field.getFieldAccessor(Unknown Source)
- at java.lang.reflect.Field.set(Unknown Source)
- at chb.test.reflect.TestClass.testReflect(TestClass.java:20)<PRE>PRE>
解决方法:
- Class cls = Class.forName("chb.test.reflect.Student");
- Object student = cls.newInstance();
- Field field = cls.getDeclaredField("age");
- field.setAccessible(true);//设置允许访问
- field.set(student, 10);
- System.out.println(field.get(student));
- Class cls = Class.forName("chb.test.reflect.Student");
- Object student = cls.newInstance();
- Field field = cls.getDeclaredField("age");
- field.setAccessible(true);//设置允许访问
- field.set(student, 10);
- System.out.println(field.get(student));
其实,在某些场合下(类中有get,set方法),可以先反射调用set方法,再反射调用get方法达到如上效果,代码示例:
- Class cls = Class.forName("chb.test.reflect.Student");
- Object student = cls.newInstance();
- Method setMethod = cls.getDeclaredMethod("setAge",Integer.class);
- setMethod.invoke(student, 15);//调用set方法
- Method getMethod = cls.getDeclaredMethod("getAge");
- System.out.println(getMethod.invoke(student));
一.获得控制台用户输入的信息 可以返回用户输入的信息,不足之处在于不支持中文输入,有待进一步改进。\O-NO(uJ A V A S\v~;`~_NSN© 2004-2008 EDZH.COM.\O-NO(uJ A V A S\v~;`~_NSN 二.复制文件 1.以文件流的方式复制文件 该方法经过测试,支持中文处理,并且可以复制多种类型,比如txt,xml,jpg,doc等多种格式 2.利用FileChannel和ByteBuffer来复制文件 但是,在实际操作中,上述方法并不是处理该类操作的最佳方法,我们可以用transferTo方法或transferFrom来实现。\O-NO(uJ A V A S\v~;`~_NSN© 2004-2008 EDZH.COM.\O-NO(uJ A V A S\v~;`~_NSN 3.利用transferTo方法实现文件复制 三.写文件 1.利用PrintStream写文件 2.利用StringBuffer写文件 该方法可以设定使用何种编码,有效解决中文问题。\O-NO(uJ A V A S\v~;`~_NSN© 2004-2008 EDZH.COM.\O-NO(uJ A V A S\v~;`~_NSN 3.利用BufferedWriter写入文件内容 利用Buffer操作IO速度会稍微快一点。\O-NO(uJ A V A S\v~;`~_NSN© 2004-2008 EDZH.COM.\O-NO(uJ A V A S\v~;`~_NSN 四.文件重命名 注:如果重命名的目标文件已经存在,则不会进行任何操作 五.转移文件目录 转移文件目录不等同于复制文件,复制文件是复制后两个目录都存在该文件,而转移文件目录则是转移后,只有新目录中存在该文件。\O-NO(uJ A V A S\v~;`~_NSN© 2004-2008 EDZH.COM.\O-NO(uJ A V A S\v~;`~_NSN
/**获得控制台用户输入的信息
* @return
* @throws IOException
*/
public String getInputMessage() throws IOException{
System.out.println("请输入您的命令∶");
byte buffer[]=new byte[1024];
int count=System.in.read(buffer);
char[] ch=new char[count-2];//最后两位为结束符,删去不要
for(int i=0;i<count-2;i++)
ch[i]=(char)buffer[i];
String str=new String(ch);
return str;
}
/**以文件流的方式复制文件
* @param src 文件源目录
* @param dest 文件目的目录
* @throws IOException
*/
public void copyFile(String src,String dest) throws IOException{
FileInputStream in=new FileInputStream(src);
File file=new File(dest);
if(!file.exists())
file.createNewFile();
FileOutputStream out=new FileOutputStream(file);
int c;
byte buffer[]=new byte[1024];
while((c=in.read(buffer))!=-1){
for(int i=0;i<c;i++)
out.write(buffer[i]);
}
in.close();
out.close();
}
/**复制文件
* @author 崔红保
* @param oldpath 以前的目录
* @param newpath 新目录
* @param filename 文件名
* @throws IOException
*/
public void copyFile(String oldpath,String newpath,String filename) throws IOException{
FileChannel in=new FileInputStream(oldpath+File.separator+filename).getChannel();
FileChannel out=new FileOutputStream(newpath+File.separator+filename).getChannel();
ByteBuffer buffer=ByteBuffer.allocate(1024);
while(in.read(buffer)!=-1){
buffer.flip();
out.write(buffer);
buffer.clear();
}
in.close();
out.close();
}
/**复制文件
* @author 崔红保
* @param oldpath 以前的目录
* @param newpath 新目录
* @param filename 文件名
* @throws IOException
*/
public void copyFile(String oldpath,String newpath,String filename) throws IOException{
FileChannel in=new FileInputStream(oldpath+File.separator+filename).getChannel();
FileChannel out=new FileOutputStream(newpath+File.separator+filename).getChannel();
in.transferTo(0,in.size(),out);
in.close();
out.close();
}
/**
* 文件输出示例
*/
public void PrintStreamDemo(){
try {
FileOutputStream out=new FileOutputStream("D:/test.txt");
PrintStream p=new PrintStream(out);
for(int i=0;i<10;i++)
p.println("This is "+i+" line");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public void StringBufferDemo() throws IOException...{
File file=new File("/root/sms.log");
if(!file.exists())
file.createNewFile();
FileOutputStream out=new FileOutputStream(file,true);
for(int i=0;i<10000;i++)...{
StringBuffer sb=new StringBuffer();
sb.append("这是第"+i+"行:前面介绍的各种方法都不关用,为什么总是奇怪的问题 ");
out.write(sb.toString().getBytes("utf-8"));
}
out.close();
}
/**
* @param filename
*/
public void writeFile(String filename){
File file=new File(filename);
try {
if(!file.exists())
file.createNewFile();
FileWriter fw=new FileWriter(file,true);//传入true表示如果该文件存在,则将新内容添加到文件末尾
BufferedWriter bw=new BufferedWriter(fw);
for(int i=0;i<1000;i++)
bw.write("这是第"+(i+1)+"行,应该没错哈 ");
//关闭
bw.close();
bw=null;
fw.close();
fw=null;
} catch (IOException e) {
e.printStackTrace();
}
}
/**文件重命名
* @param path 文件目录
* @param oldname 原来的文件名
* @param newname 新文件名
*/
public void renameFile(String path,String oldname,String newname){
if(!oldname.equals(newname)){//新的文件名和以前文件名不同时,才有必要进行重命名
File oldfile=new File(path+"/"+oldname);
File newfile=new File(path+"/"+newname);
if(newfile.exists())//若在该目录下已经有一个文件和新文件名相同,则不允许重命名
System.out.println(newname+"已经存在!");
else{
oldfile.renameTo(newfile);
}
}
}
/**转移文件目录
* @param filename 文件名
* @param oldpath 旧目录
* @param newpath 新目录
* @param cover 若新目录下存在和转移文件具有相同文件名的文件时,是否覆盖新目录下文件,cover=true将会覆盖原文件,否则不操作
*/
public void changeDirectory(String filename,String oldpath,String newpath,

