反射的概念:
java可以在运行时动态的获取到某个类的类信息,这就是java的反射。
什么叫类信息
- 一个类的类信息包括它继承自哪个类,实现了哪些接口,有哪些属性、方法、构造方法等。
什么叫类对象
- java.lang.Class是一个类,它是反射操作的源头,这个类的对象就叫类对象。
- 类对象都有啥东西
- 类对象就保存了类信息。
三种获得类对象的方法
调用Object类中的getClass()方法
- 就是通过类的对象来获得类对象
- 有实例化对象产生,再取得类对象
- 返回值是某个对象的类对象,也就是说同一类型的实例化对象公用同一个类对象,因此,对于类对象的比较可以使用“==”
- 使用“类名.class“获得类对象
- 没有实例化对象产生
- 调用Class类提供的一个静态方法forName()获得类对象
- forName()方法需要接收一个字符串类型的数据作参数,表示是类的名字,根据类名返回一个类对象
- 强制类加载
- 当JVM第一次遇到一个类,就得找到它到底是个啥,就是通过CLASSPATH来找它对应的字节码文件,就是那个.class文件,找到之后就读入这个文件,这个文件里就有这个类的类信息,JVM就把这些信息保存了,这个保存就是JVM把这些信息封装到对象中去了,就像给对象纹身一样,纹的就是这个对象所属的类到底都有些啥。
- 与第二种方法相比,可以不使用import语句导入一个明确的类,类的名称是采用字符串的形式当作参数描述的。
使用反射获得类的实例化对象
之前都是使用关键字"new"进行对象的实例化,现在如果拿到了Class类对象,就可以利用反射来进行对象的实例化操作。
Class<?> cls =Class.forName("java.util.Date");
Object obj = cls.newInstance();
这就相当于使用new关键字调用无参构造方法来实例化对象。
使用类对象来获取类的信息
我们通过上面三种获得类对象的方式获得类对象之后,就可以调用类对象中的方法来获取这个类的信息。那么类对象都可以调用哪些方法呢:
- getName()
- 获得类的名字,包括包名
- getSimpleName()
- 获得类的名字,但不包括包名
- getSuperClass()
- 获得本类的父类的类对象,注意是类对象!
- getInterface()
- 获得本类所实现的所有接口的类对象,返回值是一个类对象的数组,Class[]。
public class TestClass1 {
public static void main(String[] args) {
Class c = ArrayList.class;//类名.class 的方式获取类对象
String calssName = c.getName();//通过类对象获取类的名称
System.out.println("类名:"+calssName);
String simpleName = c.getSimpleName();//通过类对象获取类的简单名称,不包括类所在包名
System.out.println("简单类名: "+simpleName);
Class superClass = c.getSuperclass();//通过类对象获得本类的父类的类对象
System.out.println("父类:"+superClass.getName());//通过父类的类对象获得父类的名称
Class [] interfaces = c.getInterfaces();
System.out.println("接口:");
for(int i=0; i<interfaces.length;i++){
System.out.println(interfaces[i].getName());//获得本类所实现的所有接口的类对象
}
}
}
使用类对象类获取类中方法的信息
- getMethods[]
返回的Method类型的数组中包括
- 所有的公开方法,包括父类中的公开方法,但私有方法不能被获得
getDeclaredMethods
- 也是返回一个Method数组,包括私有方法,但是不包括父类中的任何方法。
使用类对象来创建类的对象
看清楚,一定要区分这2个到底是指的什么!!!
- newInstance()通过类的无参构造来创建对象
Class c = Student.class;
Student stu = (Student)c.newInstance();
- 给定一个字符串参数,这个参数表示一个类的名字,根据类名来创建该类的一个实例对象
- 接收一个类名的字符串参数,很明显是使用forName()方法
public class Dog {
String name;
int age;
public Dog() {
System.out.println("Dog()");
}
public Dog(String name,int age){
System.out.println("Dog(String,int)");
this.name=name;
this.age=age;
}
@Override
public String toString() {
return name+" "+age;
}
}
public class TestConstructor {
public static void main(String[] args) throws Exception {
Class c = Dog.class;
//利用无参构造方法创建对象,可直接使用Class类的newInstance()方法
Dog d1 = (Dog) c.newInstance();
System.out.println(d1);
//获得构造方法
Constructor con = c.getConstructor(new Class[] {String.class,int.class});
//创建对象时指定构造方法
Dog d2 = (Dog) con.newInstance( //为构造方法传递参数
new Object[]{"Snoopy",new Integer(5)});
System.out.println(d2);
}
}
Field 类
先总结一下前面的内容:
学了啥叫反射-->反射可以有3种方式来获得类对象
拿到类对象
-->可以使用类对象调用类对象所拥有的方法来获得:类的全名、类的简单名字、父类的类对象、所实现接口的类对象
-->可以使用类对象获取类中的方法的信息
-->使用类对象创建类的实例化对象
上面说到类的信息除了继承的类、实现的接口、拥有的方法等,还包括属性信息。
一个Field对象就封装了一个属性的信息。
获取属性
getField()获得本类的公开属性及从父类继承的公开属性,没有私有属性
getDeclaredField() 只能获得本类的属性,包括私有属性
修改、读取属性
有了Field对象之后就可以利用反射来获取、修改属性的值。- 在不用反射来修改属性时,我们需要明确3个要素:一是要对象,我们要修改哪一个对象的属性;二是属性,知道是哪一个对象之后,对象属性很多,到底是修改对象的哪一个属性;三是属性的值,我们知道要哪一个对象的哪一个属性了,那要把把它改成什么值。
- 使用反射来修改属性,同样我们也得知道这3个要素:所以第一步,获得要被修改的属性对应的Field对象;第二步,Field对象调用set方法,set(Object obj,Object value)里面的2个参数就分别表示了要修改哪个属性和把属性设置成哪个值。
- 需要注意的是反射也可以突破私有限制,获得并修改对象的私有属性。
public class Student {
public String name;
private int age;
public Student(){
System.out.println("Student()");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void study(){
System.out.println(name+ " study");
}
public void study(int h){
System.out.println(name+" study for "+h+" hours");
}
public double study(int a,double b){
System.out.println(name+" study "+a+" "+b);
return a*b;
}
private void play(){
System.out.println(name+" play");
}
}
public class TestStudent {
public static void main(String[] args) throws Exception{
Class c = Student.class;
Student student= (Student) c.newInstance();//无参构造方法被调用,创建了一个Student对象
//获取特定属性
Class c1=Student.class;
Field nameField = c1.getField("name");//获得代表Student类的name属性的Field对象
//利用反射来修改属性
Student stu = new Student();
Class c2 =stu.getClass();
Field nameField2 = c2.getField("name");// 获取相应的Field对象,表示要修改哪一个对象的属性;要修改对象的哪一个属性,通过调用getField方法说明要修改的是属性名为name的属性
nameField2.set(stu, "Tom");//第一个参数说明要修改属性的对象;第二个参数是属性值要修改成什么
//获取属性 获得stu对象的name属性值
Field f = c2.getDeclaredField("name");// 获取相应的Field对象--f,要获取对象属性名为name的属性值
Object nameValue = f.get(stu);//利用Field类中的get方法,取到属性值,get方法的参数表明了要读取哪一个对象的属性,get的返回值则表明了读取到的属性值
//获取私有属性,反射可以获得并修改对象的私有属性
Field ageField = c2.getDeclaredField("age");//age属性是私有的,只能使用getDeclaredField方法
//在读取和修改属性值之前调用一个方法
ageField.setAccessible(true);
ageField.set(stu, new Integer(18));//age属性是一个int类型,但是由于set方法第二个参数是Object类型,因此必须把18这个整数值封装成Integer对象
//利用反射调用study方法
Student stu3 = new Student();//实例化一个对象
Class c3 =stu3.getClass();
//确定要调用哪个方法,获得该方法所对应的方法对象
Method m = c3.getDeclaredMethod("study", new Class[]{int.class,double.class});//获得一个代表有2个参数的study方法的Method对象,表示调用哪个方法
Object obj //是invoke方法的返回值
= m.invoke(stu3, //是invoke方法的第一个参数,表明要对stu3对象调用方法,就是确定对象
new Object[]{new Integer(10),new Double(1.5)});//传入2个参数
//也可以调用类中的私有方法,只需要在调用Method对象的invoke方法之前,先调用setAccessible(true)即可
}
}