在运行时使用反射分析对象
看java核心技术的里面的反射的时候反射运行机制看得有些糊涂这里把代码贴上来把过程分析一遍。
- 源码
- 代码分析过程
源码
ObjectAnalyzer类
package zhaoch93.corejava;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
public class ObjectAnalyzer
{
private ArrayList<Object> visited = new ArrayList<>();
/**
* Converts an object to a string representation that lists all fields.
* @param obj an object
* @return a string with the object's class name and all field names and values
*/
public String toString(Object obj)
{
if(obj == null) return "null";
if(visited.contains(obj)) return "...";
visited.add(obj);
Class cl = obj.getClass();
if(cl ==String.class) return (String) obj;
if(cl.isArray())
{
String r = cl.getComponentType()+"[]{";
for (int i = 0;i<Array.getLength(obj);i++)
{
if(i>0) r+=",";
Object val = Array.get(obj, i);
if(cl.getComponentType().isPrimitive()) r+=val;
else r+=toString(val);
}
return r+"}";
}
String r = cl.getName();
//inspect the fields of this class and all superclasses
do
{
r += "[";
Field[] fields = cl.getDeclaredFields();
AccessibleObject.setAccessible(fields, true);
//get the names and values of all fields
for(Field f:fields)
{
if(!Modifier.isStatic(f.getModifiers()))
{
if(!r.endsWith("[")) r+=",";
r += f.getName()+"=";
try
{
Class t = f.getType();
Object val = f.get(obj);
if(t.isPrimitive()) r+=val;
else r+=toString(val);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
r +="]";
cl = cl.getSuperclass();
}
while(cl !=null);
return r;
}
}
ObjectAnalyzertest类(用来测试结果)
package zhaoch93.corejava;
import java.util.ArrayList;
/**
* This program uses reflection to spy on objects.
* @version 2016-2-15
* @author zhaoch93
*
*/
public class ObjectAnalyzerTest {
public static void main(String[] args)
{
ArrayList<Integer> squares = new ArrayList<>();
for(int i = 1;i<5;i++)
squares.add(i*i);
// int [] [] squares2 = new int [5] [5];
// for(int i = 0;i<5;i++)
// for(int j = 0;j<5;j++)
// squares2 [i] [j] = i+j;
System.out.println(new ObjectAnalyzer().toString(squares));
// System.out.println(new ObjectAnalyzer().toString(squares2));
}
}
源码
把运行结果简单排版如下:
java.util.ArrayList
[
{
java.lang.Integer[value=1][][],
java.lang.Integer[value=4][][],
java.lang.Integer[value=9][][],
java.lang.Integer[value=16][][],
null,null,null,null,null,null
},
size=4
]
[modCount=4]
[]
[]
这里说明一下运行的原理。这个程序会把把运行时的非静态域都打印出来,并且将其继承的超类的域都打印出来。
首先进入程序的是一个ArrayList类。这个类自身有六个域,以及一个从java.util.AbstractList继承的类modCount分别为
private static final long java.util.ArrayList.serialVersionUID
private static final int java.util.ArrayList.DEFAULT_CAPACITY
private static final java.lang.Object[] java.util.ArrayList.EMPTY_ELEMENTDATA
private transient java.lang.Object[] java.util.ArrayList.elementData
private int java.util.ArrayList.size
private static final int java.util.ArrayList.MAX_ARRAY_SIZE
Fields inherited from class java.util.AbstractList modCount
这其中非静态类只有第四个和第五个当进入这个两个静态类的时候会嵌套调用toString。
而modCount会在cl上提为AbstractList类的适合进行toString。
整个答案的结构为:
ArrayList[….][modCount][][]
这四个方括号运行时此对象分别从ArrayList,AbstractList,AbstractCollection,Object继承的非静态域。这个顺序正好符合ArrayList的继承关系。
同理在java.lang.Integer[value=1][][]中,
之所以有三个括号也是因为,Integer,Number,Object类中继承的非静态域,只不过,Number和Object中并没有非静态域。所以为空。
现在回头看ArrayList的 elementData数据。当数据有值时进入isarray程序块。分别会对数组中每个元素进入以此toString,当然如果元素是基本类型就不用调用tostring了。当然这里面还有一个size域也需要进行tostring,但原理是一样的。