一、Java中反射机制的基本概念
Java 反射机制是 Java 语言的一个重要特性。在学习 Java 反射机制前,大家应该先了解两个概念,编译期和运行期。
编译期是指把源码交给编译器编译成计算机可以执行的文件的过程。在 Java 中也就是把 Java 代码编成 class 文件的过程。编译期只是做了一些翻译功能,并没有把代码放在内存中运行起来,而只是把代码当成文本进行操作,比如检查错误。
运行期是把编译后的文件交给计算机执行,直到程序运行结束。所谓运行期就把在磁盘中的代码放到内存中执行起来。
Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制。简单来说,反射机制指的是程序在运行时能够获取自身的信息。在 Java 中,只要给定类的名字,就可以通过反射机制来获得类的所有信息。
二、获取CLass实例的方式
//获取CLass的实例的方式(前三种方式需要掌握)
@Test
public void test3() throws ClassNotFoundException {
//方式一:调用运行时类的属性:.class
Class clazz1 = Person.class;
System.out.println(clazz1);
//方式二:通过运行时类的对象
Person p1 = new Person();
Class clazz2 = p1.getClass();
System.out.println(clazz2);
//方式三:调用Class的静态方法:forName(String classPath)
Class clazz3 = Class.forName("PartOne.Person");
System.out.println(clazz3);
System.out.println(clazz1 == clazz2);
System.out.println(clazz1 == clazz3);
//方式四:使用类的加载器:ClassLoader(了解)
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class clazz4 = classLoader.loadClass("PartOne.Person");
}
其中,关于java.lang.Class类的理解:
1.类的加载过程:
程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class)结尾,接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中,此过程就称为类的加载。加载到内存中的类就称为运行时类,此运行时类,就作为Class的一个实例。
2.换句话说,Class的实例就对应着一个运行时类
3.加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。
三、反射机制常用的API
通过反射设置或获取运行时类中的属性值
@Test
public void testField() throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class clazz = Person.class;
//创建运行时类的对象
Person p = (Person) clazz.getDeclaredConstructor().newInstance();
//获取指定类中的属性
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
/*
设置当前属性的值
set():参数1:指明设置那个对象的属性 参数2:将此属性值设置为多少
*/
name.set(p,"Sam");
/*
设置当前属性的值
get():参数1:指明设置那个对象的属性
*/
String pName = (String) name.get(p);
System.out.println(pName);
}
通过反射执行运行时类中的指定方法
@Test
public void testMethod() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class clazz = Person.class;
//创建运行时类的对象
Person p = (Person) clazz.getDeclaredConstructor().newInstance();
/*
1.获取指定的某个方法
getDeclaredMethod():参数1:指明获取的方法的名称 参数2:指明获取的方法的形参列表
*/
Method show = clazz.getDeclaredMethod("show", String.class);
//2.保证当前方法是可访问的
show.setAccessible(true);
/*
invoke():参数1:方法的调用者 参数2:给方法形参赋值的实参
invoke()的返回值即为对应类中调用的方法的返回值
*/
Object returnValue = show.invoke(p,"CHN");
System.out.println(returnValue);
System.out.println("**********如何调用静态方法**********");
//调用静态方法
Method showDesc = clazz.getDeclaredMethod("showDesc");
showDesc.setAccessible(true);
showDesc.invoke(Person.class);
}
通过反射调用运行时类中指定的构造器
@Test
public void testConstructor() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class clazz = Person.class;
//private Person(String name)
/*
1.获取指定的构造器
getDeclaredConstructor():参数:指明构造器的参数列表
*/
Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class);
//2.保证此构造器是可访问的
declaredConstructor.setAccessible(true);
//3.创建运行时类的对象
Person per = (Person) declaredConstructor.newInstance("Tom");
System.out.println(per);
}
四、反射机制的优点及缺点
优点:
1.能够运行时动态获取类的实例,大大提高系统的灵活性和扩展性。
2.与 Java 动态编译相结合,可以实现无比强大的功能。
3.对于 Java 这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。
缺点:
1.反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射;
2.反射调用方法时可以忽略权限检查,获取这个类的私有方法和属性,因此可能会破坏类的封装性而导致安全问题。
同时,在我们日常学习反射的时候,我们通常会产生一个疑问,java中的反射机制是不是跟java的封装性矛盾了?因为通过反射机制,我们可以获取类中的私密方法或者私密属性等。
答案是不矛盾的。
对于封装性我们设计一个类的时候,属性,方法,构造器等等。该私有的时候私有(private),该公共的时候公共(public)。
封装性给我们的启示是:当我们看到一个类写了一些私有的方法,一些公共的方法时,就告诉我们私有的方法就不要用了,就用公共的方法就可以了。因为私有的方法可能类内部用了 这里体现了封装性。比如单例模式,你要想造对象,就不要用私有的构造器了,我已经把对象造好了直接用就行。
反射告诉我们可以调,但是不建议调私有的方法,因为可能公共的方法更好,加了一些逻辑。
总结:封装性解决的问题是:建议调那个的问题,公共的调就可以了,私有的不要调了。私有的属性不建议你直接修改,建议你通过get,set方法修改。
而反射解决的是能不能调的问题。
五、反射机制能带给我们什么
首先,我们可以先创建一个Person类,通过测试可以看到拥有反射机制前后,我们对于Person类操作的变化。
public class Person {
private String name;
public int age;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
}
public Person(String name) {
this.name = name;
}
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;
}
}
反射之前,对于Person的操作
@Test
public void test1(){
//1.创建Person类的对象
Person p1 = new Person("Tom",12);
//2.通过对象,调用其内部的属性,方法
p1.age = 10;
System.out.println(p1.toString());
p1.show();
//在Person类的外部,不可以通过Person类的对象调用其内部私有结构
//比如:name,showNation()以及私有的构造器
}
反射之后,对于Person的操作
@Test
public void test2() throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException, NoSuchFieldException {
Class clazz = Person.class;
//1.通过反射,创建Person类的对象
Constructor cons = clazz.getConstructor(String.class,int.class);
Object obj = cons.newInstance("Tom",12);
Person p = (Person) obj;
System.out.println(p.toString());
//2.通过反射,调用对象指定的属性,方法
Field age = clazz.getDeclaredField("age");
age.set(p,10);
System.out.println(p.toString());
//调用方法
Method show = clazz.getDeclaredMethod("show");
show.invoke(p);
//通过反射,可以调用Person类的私有结构的。比如:私有的构造器,属性,方法
//调用私有构造器
Constructor cons1 = clazz.getDeclaredConstructor(String.class);
cons1.setAccessible(true);
Person p1 = (Person) cons1.newInstance("Jerry");
System.out.println(p1);
//调用私有的属性
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(p1,"HanMeimei");
System.out.println(p1);
//调用私有的方法
Method showNation = clazz.getDeclaredMethod("showNation", String.class);
showNation.setAccessible(true);
String nation = (String) showNation.invoke(p1,"中国");//相当于p1.showNation("中国");
System.out.println(nation);
}
那么Java反射有什么作用呢?
假如我们有两个程序员,一个程序员在写程序的时候,需要使用第二个程序员所写的类,但第二个程序员并没完成他所写的类。那么第一个程序员的代码能否通过编译呢?这是不能通过编译的。利用Java反射的机制,就可以让第一个程序员在没有得到第二个程序员所写的类的时候,来完成自身代码的编译。
Java的反射机制它知道类的基本结构,这种对Java类结构探知的能力,我们称为Java类的“自审”。当我们构建出一个对象的时候,去调用该对象的方法和属性的时候。一按点,编译工具就会自动的把该对象能够使用的所有的方法和属性全部都列出来,供用户进行选择。这就是利用了Java反射的原理,是对我们创建对象的探知、自审。