Java Reflection概述
- Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
- 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息,我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射
- 正常方式:1.引入需要的“包类”名称 2.通过new实例化 3.取得实例化对象
- 反射方式:1.实例化对象 2.getClass()方法 3.得到完整的“包类”名称
正常方式与反射方式创建对象的区别
创建一个Student类,供测试类调用
public class Student {
private String name;
public int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
private Student(String name) {
this.name = name;
}
public 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 show(){
System.out.println("你好,我是一个学生");
}
private String showNation(String nation){
System.out.println("我的国籍是:"+nation);
return nation;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
创建一个测试类,对Student类里的构造器,属性,方法进行调用
public class ReflectionTest {
//反射之前,对于Student的操作
@Test
public void test1(){
//1.创建Student类的对象
Student stu = new Student("Tom",12);
//2.通过对象,调用其内部的属性、方法
stu.age = 10;
System.out.println(stu.toString());
stu.show();
//在Student类外部,不可以通过Student类的对象调用其内部私有结构
//比如:name、showNation()以及私有的构造方法
}
//反射之后,对于Student类的操作
@Test
public void test2() throws Exception{
Class clazz = Student.class;
//1.通过反射,创建Student的对象
Constructor cons = clazz.getConstructor(String.class, int.class);
Student stu = (Student) cons.newInstance("Dawn1",12);
System.out.println(stu.toString());
//2.通过反射,调用对象指定的属性、方法
//调用属性
Field age = clazz.getDeclaredField("age");
age.set(stu,10);
System.out.println(stu.toString());
//调用方法
Method show = clazz.getDeclaredMethod("show");
show.invoke(stu);
//通过反射,可以调用Student类的私有结构,比如:私有的构造器、方法、属性
//调用私有的构造器
Constructor cons1 = clazz.getConstructor(String.class);
Student stu1 = (Student) cons1.newInstance("Dawn2");
System.out.println(stu1);
//调用私有的属性
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(stu1,"Dawn3");
System.out.println(stu1);
//调用私有的方法
Method showNation = clazz.getDeclaredMethod("showNation", String.class);
showNation.setAccessible(true);
String nation = (String) showNation.invoke(stu1, "中国");//相当于String nation = stu1.showNation("中国")
System.out.println(nation);
}
}
疑问:面试常问的两个问题
1.通过直接new的方式或反射的方式都可以调用公共的结构,开发中到底用哪个?
建议用new的方式,因为和反射比较,代码量少,并且是日常开发中的大多数创建对象的方式
什么时候用反射的方式:编译的时候不知道要new什么类型的对象,就可以使用反射。比如客户端和服务器端在运行期间,客户端有可能要进行登录的操作,也有可能要进行注册的操作或其他的一些操作,但服务器不知道客户端到底要干什么,此时就可以利用反射在服务器端动态获取客户端的请求对象。
2.反射与面向对象的封装性是不是矛盾的?如何看待这两个技术?
不矛盾。封装性相当于一种提示,是建议你去调什么方法,公共的就可以去调,私有的就不要去调了,但是如果你一定要调用私有的,那就可以使用反射,封装性更像是建议你去怎么调这个方法更好,而反射解决的问题更像是我能不能调这个方法。
关于java.lang.Class的理解
1.类的加载过程
程序(.java结尾)经过javac.exe命令编译后,会生成一个或多个字节码文件(.class结尾)
接着我们使用java.exe命令对某个字节码文件进行解释运行,相当于把某个字节码文件加载到内存中,此过程就叫做类加载(从java.exe命令开始),加载到内存中的类,我们就称为运行时类,此类就作为Class的一个实例
2.换句话说,Class的实例就对应着一个运行时类
3.加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类
获取Class的实例的方式
//获取Class的实例的方式
@Test
public void test3() throws Exception{
//方式一:调用运行时类的属性:.class
Class clazz1 = Student.class;
System.out.println(clazz1);
//方式二:调用运行时类的对象,调用getClass()
Student student = new Student();
Class clazz2 = student.getClass();
System.out.println(clazz2);
//方式三:调用Class的静态方法:forName(String classPath)
Class clazz3 = Class.forName("com.ff.reflection.Student");
System.out.println(clazz3);
//获取的是内存当中同一运行时类,只是获取的方式不同
System.out.println(clazz1==clazz2);
System.out.println(clazz1==clazz3);
//方式四:使用类的加载器:ClassLoader (了解)
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class clazz4 = classLoader.loadClass("com.ff.reflection.Student");
System.out.println(clazz4);
System.out.println(clazz1==clazz4);
}