反射不是1.5新特性,是从1.2开始就有的,三大框架以及一些小框架都要用到反射技术
JAVA反射技术,不像我们开始学习那样,编写好一个一个类,如果想要调用别的类方法,直接调用就行(不同包,得加载包,静态属性: 类名.属性,非静态属性:对象.属性)。它是一种动态的机制,主要用在框架中,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。我的理解:
在程序中设置一个变量,这个变量的值在配置文件中赋值,如变量classname, 在程序中我们用到的类名是什么?答案是不知道。我们只能通过反射技术,通过变量classname来找我们用到的类。
变量classname的值,在配置文件中设置,可以修改。我们配置哪个类,程序中就找哪个类来加载。
好处:
这样如果我们对程序进行功能扩展,就不需要修改源代码了。新写出代码后,设置配置文件就行了
比如在反射中,调用一个未知类的main(),未知类是后来的。这个反射就实现了一个小框架。框架是提前做好的,框架调用用户的类,就像房子是框架,新门是用户类。反射做的框架有个好处:将来的类可以调用以前的类。框架不需要知道将来的类叫么。
以上是我对反射总的理解,下面是反射的知识点
反射的基础:Class类
Class类:这个类是把我们写的那些普通类当做对象,向上抽取出来的类。
例如,一个Java类中用一个Class类的对象来表示一个类中的组成部分:成员变量,方法,构造方法,包等信息也用一个个
Java类来表示。就像汽车是一个类,其中的发动机,变速箱等等也是一个类。
表示Java类的Class类显然要提供一系列的方法,来获取其中的变量,方法,构造方法,修饰符,包等信息。
些信息就是用相应类的实例对象来表示,他们是Field、Method、Contructor、Package等等。
Class类的实例对象是字节码文件,什么是字节码?
一个类被加载器加载到内存中占有一片存储空间,这个空间里面的内容就说字节码;不同类的字节码是不同的,所以它们在内 存中的内容是不同的。我们编译程序后所生成的类.class文件就是字节码文件。
九个预定义的Class对象:
byte,short,int,long,char,float,double,boolean以及关键字void。
得到Class类的三种方式:
1.类名.class
如:Person.class;
2.对象.getclass();
如:得到创建该对象的字节码:new Demo().getClass();
3.Class.forName():
如: Class.forName("Java.util.date");返回字节码。
forName()方法先在内存中判断有没有字节码,有就返回,没有就用类加载器加载一个,然后在返回。
注意:看到上面三个方法,是不是有个疑问,方法1和方法2,用到了类名和对象名,和反射机制不符合啊?
确实, 方法1和方法2不符合,这里说的是得到Class类的方式。在反射中我们用到的时方法3。
字节码文件就是把所有类的字节码文件抽取出的共性(属性、方法、构造函数)封装为一个统一的Class类,
而每个字节码文件就是这个Class类的一个实例对象。
但是把每个类的字节码文件的属性、方法、构造函数等共性分别抽取成一个个类,分别是Field类(描述属性的共性),
Method类(描述方法的共性),Constructor类(描述构造方法的共性)
可以Class对象可以调用Class类的方法得到这些类的对象:
Field getField();
Mehod getMehod();
Constructor getConstructor()等等。
package fs1;
import java.lang.reflect.*;
import java.util.*;
import java.*;
class Person{
private String name;
private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
}
public class fanshe {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
String s1="abc";
sop(Person.class);
Person p=new Person("lisi",23);
//sop(Person.getClass());
System.out.println(123);
//sop(Class.forName("Java.lang.String"));
Class c1=String.class;
Class c2=s1.getClass();
Class c3=Class.forName("java.lang.String");
sop(c1==c2);
sop(c1==c3);
sop(c3==c2);
sop(new String("abc").getClass().getFields());
List l1=new ArrayList();
Constructor[] cons=Class.forName("java.lang.String").getConstructors();
//获取构造方法。可以通过构造方法构造实例对象,得到方法的时候需要参数列型
Constructor cons1=String.class.getConstructor(StringBuffer.class);
//用获取的构造方法创建实例对象,调用活动的方法时,也需要有类型
String str2=(String) cons1.newInstance(new StringBuffer("abc"));
sop(str2.charAt(2));
ReflectPoint pt1=new ReflectPoint(3,5);
//该方法只有私有的草可以获得
Field fieldY=pt1.getClass().getField("y");
sop(fieldY.get(pt1));
Field fieldX=pt1.getClass().getDeclaredField("x");
fieldX.setAccessible(true);
sop(fieldX.get(pt1));
changeStringValue(pt1);
sop(pt1);
//Method类,代表某一个类中的成员方法
Method methodCharAt=String.class.getMethod("charAt", int.class);
sop(methodCharAt.invoke(s1, 1));
//获取main方法
String startingClassName=args[0];
Method mainMethod=Class.forName(startingClassName).getMethod("main", String[].class);
//使用一下两种方式调用main方法
//把整个数组打包为一个数组,因为为了兼容1.4要对数组进行依次解包
mainMethod.invoke(null, new Object[]{new String[]{"11","22","33"}});
//生命该数组是一个对象
//mainMethod.invoke(null, (Object)new String[]{"11","22","33"});
int[] a1=new int[]{1,2,3};
int[] a2=new int[4];
int[][] a3=new int[2][3];
String[] a4=new String[]{"a","b","c"};
//对象要得到字节码组要使用方法,不能直接用。class获取
sop(a1.getClass()==a2.getClass());
sop(a1.getClass()==a3.getClass());
sop(a1.getClass()==a4.getClass());
sop(a1.getClass().getName());
sop(a1.getClass().getSuperclass().getName());
Object Obj1=a1;
Object Obj2=a4;
//基本数据类型的数组是无法装入Object数组中的
//Object[] Obj3=a1;
Object[] Obj4=a3;
Object[] Obj5=a4;
sop(a1);
sop(a4);
//int类型的数组不能传入Object【】中,
sop(Arrays.asList(a1));
sop(Arrays.asList(a4));
Object obj=null;
printObject(a1);
printObject("abc");
}
private static void printObject(Object obj) {
Class clazz=obj.getClass();
if(clazz.isArray()){
int len=Array.getLength(obj);
for(int i=0;i<len;i++){
sop(Array.get(obj, 1));
}
}else{
sop(obj);
}
}
private static void changeStringValue(Object obj) throws IllegalArgumentException, IllegalAccessException {
Field[] fields=obj.getClass().getFields();
for(Field field: fields){
//使用==比较两个字节码,因为两个字节码有可能是同一个
if(field.getType()==String.class){
String oldValue=(String) field.get(obj);
String newValue=oldValue.replace('b', 'a');
field.set(obj, newValue);
}
}
}
public static void sop(Object obj){
System.out.println(obj);
}
}
class Test{
public static void main(String[] args){
System.out.println(123);
}
}