反射入门2(使用反射生成和操作对象)

来源:互联网 发布:mac双系统开机切换 编辑:程序博客网 时间:2024/06/10 02:43

/*<<java学习笔记>>读后思
反射入门2(使用反射生成和操作对象)
author: shine
*/
1 生成对象
1) 若一个类有无参数的构造函数,则可以使用Class的newInstance()方法,按照无参的构造函数实例化一个对象,实例化对象以Object类型返回。
Class c = Class.forName(className);
Object obj = c.newInstance();

exp1:
package flectUser;

import java.util.List;

public class NewInstanceDemo {

 public static void main(String[] args) {
  // TODO Auto-generated method stub
  try {
   Class c = Class.forName("java.util.ArrayList");
   List list = (List)c.newInstance();
   for (int i = 0; i < 5; i++) {
    list.add("element "+i);
   }
   for (Object o : list.toArray()) {
    System.out.println(o);
   }
  } catch (ClassNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (InstantiationException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (IllegalAccessException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }
}
输出结果:
element 0
element 1
element 2
element 3
element 4

2)若要使用反射按照一个有参的构造函数生成实例。就要给newInstance传入参数:
exp2:
//Student.java
package flectUser;

public class Student {
 private String name;
 private int score;
 
 public Student() {
  name = "N/A";
 }
 
 public Student(String name,int score) {
  this.name = name;
  this.score = score;
 }

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

 public int getScore() {
  return score;
 }

 public void setScore(int score) {
  this.score = score;
 }
 
 public String toString() {
  return name + ":" + score;
 }
}

//NewInstanceDeom2
package flectUser;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class NewInstanceDeom2 {
 public static void main(String[] args) {
  try {
   Class c = Class.forName("flectUser.Student");
   //指定参数类型
   Class[] params = new Class[2];
   //第一参数是String
   params[0] = String.class;
   //第二个参数是int
   params[1] = Integer.TYPE;
   //取得对应参数列的构造函数
   Constructor constructor = c.getConstructor(params);
   
   //指定变量内容
   Object[] argObjs = new Object[2];
   argObjs[0] = "shine";
   argObjs[1] = 100;
   
   
   //指定变量并实例化(带参数)
   Object obj = constructor.newInstance(argObjs);
   System.out.println(obj);
  } catch (ClassNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (SecurityException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (NoSuchMethodException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (IllegalArgumentException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (InstantiationException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (IllegalAccessException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (InvocationTargetException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }
}

输出结果:
shine:100

小结:
代码中
params[1] = Integer.TYPE; 获得Integer对应的原始数据类型int
params[1] = Integer.class; 获得Integer(封装类)
............................................................................................................................................................................................................

2 调用方法
1)可以使用反射取回类的方法的对象代表(java.lang.reflect.Method)的实例,通过invoke()方法来动态调用指定的方法。
exp3: (继续使用exp2中flectUser.Student)
package flectUser;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class InvokeMethodDemo {
 public static void main(String[] args) {
  try {
   Class c = Class.forName("flectUser.Student");
   Object targetObj = c.newInstance();
   //设定setName方法参数类型
   Class[] params = new Class[]{String.class};
   //根据方法名和参数类型取回方法对象setName
   Method setName = c.getMethod("setName", params);
   //设定setName传入的参数值
   Object[] argObjs1 = new Object[]{"shine"};
   //调用setName方法
   setName.invoke(targetObj, argObjs1);
   
   //设定setScore方法的参数类型
   Class[] params2= new Class[]{Integer.TYPE};
   //取得方法对象setScore
   Method setScore = c.getMethod("setScore", params2);
   //设定setScore方法传入的参数值
   Object[] argObjs2 = new Object[]{100};
   //调用setScore方法
   setScore.invoke(targetObj, argObjs2);
   
   System.out.println(targetObj);
  } catch (ClassNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (InstantiationException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (IllegalAccessException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (SecurityException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (NoSuchMethodException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (IllegalArgumentException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (InvocationTargetException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }
}

输出结果:
shine:100

2)上例中是通过getMethod方法来获得方法对象,看到reflect api中除了getMethod以外,还有getDeclaredMethod方法,他们之间有什么区别和联

系呢?
getMethod只能获得public的方法,而如果要得到private或protect的方法,可以通过getDeclaredMethod方法获得。

Method privateMethod = c.getDeclaredMethod.("somePrivateMethod",new Class[] paramTypes);
privateMethod.setAccessible(true);
privateMethod.invoke(targetObj,argObjs);
............................................................................................................................................................................................................


3. 修改成员值
尽管直接存取类的域成员(Field)是不被鼓励的,但仍可以直接存取公开的域成员。甚至于私有域变量。
exp4:
//TestUser.java
package flectUser;

public class TestField {
 public int testInt;
 private String testStr;
 
 public String toString() {
  return testInt + " : " + testStr;
 }
}

//AssignFieldDemo.java
package flectUser;

import java.lang.reflect.Field;

public class AssignFieldDemo {
 public static void main(String[] args) {
  try {
   Class c = Class.forName("flectUser.TestField");
   Object targetObj = c.newInstance();
   //取得公有属性testInt
   Field testInt = c.getField("testInt");
   //取得私有属性testStr
   Field testStr = c.getDeclaredField("testStr");
   testStr.setAccessible(true);
   //改变域变量的值
   testInt.setInt(targetObj, 100);
   testStr.set(targetObj, "shine");
   
   System.out.println(targetObj);
   
   
  } catch (ClassNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (InstantiationException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (IllegalAccessException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (SecurityException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (NoSuchFieldException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }
}

输出结果:
100 : shine
............................................................................................................................................................................................................

4.再来看看反射在数组中的应用。
1)静态数组
exp5:
package flectUser;

public class ArrayDemo {
 public static void main(String[] args) {
  short[] sArr = new short[5];
  int[] iArr = new int[5];
  long[] lArr = new long[5];
  char[] cArr = new char[5];
  String[] strArr = new String[5];
  byte[] bArr = new byte[5];
  boolean[] booArr = new boolean[5];
  float[] fArr = new float[5];
  double[] dArr = new double[5];
  
  System.out.println("short 数组类:"+sArr.getClass());
  System.out.println("int 数组类:"+iArr.getClass());
  System.out.println("long 数组类:"+lArr.getClass());
  System.out.println("char 数组类:"+cArr.getClass());
  System.out.println("String 数组类:"+strArr.getClass());
  System.out.println("byte 数组类:"+bArr.getClass());
  System.out.println("boolean 数组类:"+booArr.getClass());
  System.out.println("float 数组类:"+fArr.getClass());
  System.out.println("double 数组类:"+dArr.getClass());
  }
}

输出结果:
short 数组类:class [S
int 数组类:class [I
long 数组类:class [J
char 数组类:class [C
String 数组类:class [Ljava.lang.String;
byte 数组类:class [B
boolean 数组类:class [Z
float 数组类:class [F
double 数组类:class [D

小结:
使用toString()显示数组对象的类名称时,以class[开始,之后跟随一个类型表示字符。

2)动态数组,使用反射机制可以动态生成数组,使用java.lang.reflect.Array
exp6:
package flectUser;

import java.lang.reflect.Array;

public class NewArrayDemo {
 public static void main(String[] args) {
  Class c = String.class;
  //定义数组的元素类型和数组大小
  Object objArr = Array.newInstance(c, 5);
  
  for(int i=0; i < 5; i++) {
   Array.set(objArr, i, i+"");
  }
  
  for (int i = 0; i < 5; i++) {
   System.out.println(Array.get(objArr, i));
  }
  System.out.println();
  
  String[] strs = (String[])args;
  for (String string : strs) {
   System.out.println(string + " ");
  }
 }
}

输出结果:
0
1
2
3
4

3)可以通过Class实例的getComponetType()方法,取得集合类元素的Class实例:
int[] arr = new int[5]
system.out.println(iArr.getClass().getComponentType());

输出结果:
int
...........................................................................................................................................................................................................

5. Proxy类
java.lang.reflect.Proxy类,可协助实现动态代理功能。举个例子,打算开发一个HelloSpeaker类,其中有一个hello方法,想要在hello()调用前后加上

记录功能,但又不想将记录的功能写入到HelloSpeaker类中,这是可以使用Proxy类来实现动态代理。
exp7:
//IHello.java
package com.shine.proxy;

public interface IHello {
 public void hello(String name);
}


//HelloSpeaker.java
package com.shine.proxy;

public class HelloSpeaker implements IHello{
 public void hello(String name) {
  System.out.println("Hello: " + name);
 }
}


//LogHandler,java代理类
package com.shine.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.logging.Level;
import java.util.logging.Logger;

public class LogHandler implements InvocationHandler{
 private Logger logger = Logger.getLogger(this.getClass().getName());
 private Object delegate;
 
 //绑定要代理的对象
 public Object bind(Object delegate) {
  this.delegate = delegate;
  return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), delegate.getClass().getInterfaces(), this); 
 }
 
 //代理要调用的方法,并在前后增加行为
 public Object invoke(Object proxy,Method method, Object[] args) throws Throwable {
  Object result = null;
  try {
   logger.log(Level.INFO, "method starts..." + method.getName());
   result = method.invoke(delegate, args);
   logger.log(Level.INFO, "method ends..." + method.getName());
  } catch (Exception e) {
   e.printStackTrace();
  }  
  return result; 
 }
}
注:
要完成代理类必须实现InvocationHandler(调用代理)接口,这个接口只有一个必须实现的方法;
public Object invoke(Object proxy,Method method, Object[] args) throws Throwable 该方法有三个参数:
1) 代理类实例(在这里就是指LogHandler)。
2) 代理类调用的方法对象。
3) 传入该方法的参数数组。
实际上就是在调用方法还是靠" method.invoke(delegate, args); "


//Test.java 测试
package com.shine.proxy;

public class Test {
 public static void main(String[] args) {
  LogHandler handler = new LogHandler();
  IHello speaker = new HelloSpeaker();
  
  //代理speaker的对象
  IHello speakerProxy = (IHello)handler.bind(speaker);
  speakerProxy.hello("shine");
 }
}


输出结果:
2008-4-4 21:42:00 com.shine.proxy.LogHandler invoke
信息: method starts...hello
Hello: shine
2008-4-4 21:42:00 com.shine.proxy.LogHandler invoke
信息: method ends...hello
...........................................................................................................................................................................................................
2008-4-4
 

原创粉丝点击