反射的概述
Reflection(反射)是被视为动态语言的关键,反射机制允许程序在运行时借助于Reflection API获取任何类的内部信息,并能直接操作任意对象的内部属性及方法。
加载完类之后,在堆的方法区中就产生了一个Class类型的对象,一个只有一个Class对象,这个对象就包含了完整的2类的结构信息。我们可以通过对象看到类的结构,这个对象就像一面镜子,透过这个镜子看到类的内部结构,所以我们称之为反射。
通过反射调用类型中声明的相关的结构
示例:
这里声明了一个person类
public class Person {
public String name;
public int age;
private Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
System.out.println("Person实例创建。。。。。");
}
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 show(){
System.out.println("我是一个·Person类的方法");
}
private String showNation(String str){
return "我的国籍是" + str ;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + ''' +
", age=" + age +
'}';
}
}
通过反射去调用Person类中的相关的属性和方法,修改相关属性的值【以下代码均使用throws的方式去解决异常问题】;
通过反射获取类中的属性:
@Test
public void test02() throws InstantiationException, IllegalAccessException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException {
Class<Person> clazz = Person.class;
Person person = clazz.newInstance();//调用了Person类中的构造器。
System.out.println(person);
//调用属性
Field ageField = clazz.getField("age");
//给age属性赋值
ageField.set(person, 10);
System.out.println(person.getAge());
//另一种获取属性的方法
System.out.println(ageField.get(person));
//一样的效果
//调用方法show()
Method showMethoid = clazz.getMethod("show");
showMethoid.invoke(person);
}
通过反射去调用类中的构造器创建实例:
@Test
public void test03() throws Exception {
//调用私有的构造器创建对象
Class<Person> personClass = Person.class;
Constructor<Person> declaredConstructor = personClass.getDeclaredConstructor(String.class, int.class);
declaredConstructor.setAccessible(true);//设置为可以访问
Person tom = declaredConstructor.newInstance("yom", 12);
System.out.println(tom);
/**
*访问私有的属性
*/
Field declaredField = personClass.getDeclaredField("name");
declaredField.setAccessible(true);
declaredField.set(tom, "Tom");
System.out.println(tom.getClass());
System.out.println(tom);
System.out.println(declaredField.get(tom));
//调用私有化的方法
Method showNation = personClass.getDeclaredMethod("showNation", String.class);
showNation.setAccessible(true);
Object chn = showNation.invoke(tom, "CHN");
System.out.println(chn);
}
注意:通过反射来获取类中的构造器有两种方法,一个是getConstructor,另一个是getDeclaredConstructure 二者的区别是前者只能获取权限足够大的构造器,后者则是可以获取全部的已经声明在类中的构造器。不过在获取私有的构造器时会出无法访问的异常,这时只需要调用setAccessible方法去设置为可以访问即可。
通过反射获取并调用类中声明的方法:
@Test
public void test03() throws NoSuchFieldException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//调用指定的方法
Class<Person> personClass = Person.class;
Person person = personClass.newInstance();
Method showNationMethod = personClass.getDeclaredMethod("showNation", String.class);
showNationMethod.setAccessible(true);
String s = (String) showNationMethod.invoke(person, "CHN");
System.out.println(s);
}
创建Class实例的四种途径:
@Test
public void test04() throws ClassNotFoundException {
//创建Class的四种方式
//第一种调用运行时类的静态属性
Class<Person> personClass = Person.class;
//第二种调用运行时类的对象的getclass方法
Person p1 = new Person();
Class<? extends Person> aClass = p1.getClass();
//比较二者的地址、
System.out.println(aClass == personClass);//true
//调用Class的静态方法froName(String className)
String className = "ReflectTest.Person";
Class<?> aClass1 = Class.forName(className);
//方法四,使用类的加载器
Class clazz = ClassLoader.getSystemClassLoader().loadClass(className);
System.out.println(personClass == clazz);//true
}
反射的优点:
提高了java程序的灵活性与扩展性,降低了耦合性,提升了自适应能力
允许程序创建和控制任何类的对象,无需提前硬编码目标类
对CLass类的理解
针对于编写好的.java源文件进行编译,会生成一个或者多个.class文件。接着,我们使用java.exe命令对指定的.class文件进行解释运行,这个解释运行的过程中,我们需要将.calss字节码文件加载到内存中,加载到内存中的.calss文件对应的结构即为CLass的一个实例。
理解:CLass可以看作反射的源头。