反射
1.反射机制
- 概念:
- 简述:在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性==;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
- 详述:Java反射是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过ReflectionAPIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static等)、superclass(例如Object),实现之interfaces(例如Cloneable),也包括fields和methods的所有信息,并可于运行时改变fields内容或唤起methods。
- 类型信息在jvm里也是作为一个对象 只保留一份
2.反射机制功能
- 在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。
3. Reflection API
- 当然JDK提供了反射API,我们可以学习里面的方法来取得任何已知名称的类的内部信息。
Class对象的获取
- 对象的getClass()方法;
- 类的.class(最安全/性能最好)属性;
- 运用Class.forName(String className)动态加载类,className需要是类的全限定名(最常用).
对象创建
- 通过反射来生成对象的方式有两种:
- 使用Class对象的newInstance() 方法来创建该Class对象对应类的实例(这种方式要求该Class对象的对应类有默认构造器).
- 先使用Class对象获取指定的Constructor对象, 再调用Constructor对象的newInstance() 方法来创建该Class对象对应类的实例(通过这种方式可以选择指定的构造器来创建实例).
从Class中获取信息
- 获取类内信息
内容 | 方法 | 说明 |
---|---|---|
构造器 | 类对象.getConstructors() | 获取所有public修饰的构造方法, 返回数组 |
构造器 | 类对象.getDeclareConstructors() | 获取本类所有构造方法,返回数组 |
构造器 | 类对象.getConstructor(参数) | 获取指定的构造方法,返回对象 |
构造器 | 类对象.getConstructor() | 获取无参构造,返回对象 |
包含的方法 | 类对象.getMethods() | 获取所有public方法,包括继承的 返回数组 |
包含的方法 | 类对象.getDeclaredMethods() | 获取本类的所有方法,返回数组 |
包含的方法 | 类对象.getMethod(String name, Class<?>… parameterTypes) | 获取指定的公共方法 ,返回对象 |
包含的方法 | getDeclaredMethod(String name, Class<?>… parameterTypes) | 返回一个Method对象,该对象反映此Class对象表示的类或接口的指定声明方法 。 |
获取属性信息 | 类对象.getFields() | 获取所有的公共属性 (包括继承) |
获取属性信息 | 类对象.getDeclareFields() | 获取本类属性 |
获取属性信息 | 类对象.getField(属性名) | 根据属性名找属性 公共的 包括继承 |
获取属性信息 | 类对象.getDeclaredField(属性名) |
本类所有属性
- 上面的仅是一些使用基础API,具体的请参照Reflection API
4.调用方法
- 当获取到某个类对应的Class对象之后, 就可以通过该Class对象的getMethod来获取一个Method数组或Method对象.每个Method对象对应一个方法,在获得Method对象之后,就可以通过调用invoke方法来调用该Method对象对应的方法.
- 缺点:调用复杂,效率底
- 优点:可以调用私有方法
5.setAccessible方法(性能)
- Method/Constructor/Field/Element都继承了AccessibleObject,AccessibleObject类中有一个setAccessible方法
- 该方法有两个作用:
- 启用/禁用访问安全检查开关:值为true,则指示反射的对象在使用时取消Java语言访问检查;值为false,则指示应该实施Java语言的访问检查;
- 可以禁止安全检查, 提高反射的运行效率.
6.代码–方便理解
代码摘自大神博客敬业的小码哥
package fanshe;
/**
* 获取Class对象的三种方式
* 1 Object ——> getClass();
* 2 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
* 3 通过Class类的静态方法:forName(String className)(常用)
*
*/
public class Fanshe {
public static void main(String[] args) {
//第一种方式获取Class对象
Student stu1 = new Student();//这一new 产生一个Student对象,一个Class对象。
Class stuClass = stu1.getClass();//获取Class对象
System.out.println(stuClass.getName());
//第二种方式获取Class对象
Class stuClass2 = Student.class;
System.out.println(stuClass == stuClass2);//判断第一种方式获取的Class对象和第二种方式获取的是否是同一个
//第三种方式获取Class对象
try {
Class stuClass3 = Class.forName("fanshe.Student");//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
System.out.println(stuClass3 == stuClass2);//判断三种方式是否获取的是同一个Class对象
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
```java
注意:在运行期间,一个类,只有一个Class对象产生。
三种方式常用第三种,第一种对象都有了还要反射干什么。第二种需要导入类的包,依赖太强,不导包就抛编译错误。一般都第三种,一个字符串可以传入也可写在配置文件中等多种方法。
```java
package fanshe;
public class Student {
//---------------构造方法-------------------
//(默认的构造方法)
Student(String str){
System.out.println("(默认)的构造方法 s = " + str);
}
//无参构造方法
public Student(){
System.out.println("调用了公有、无参构造方法执行了。。。");
}
//有一个参数的构造方法
public Student(char name){
System.out.println("姓名:" + name);
}
//有多个参数的构造方法
public Student(String name ,int age){
System.out.println("姓名:"+name+"年龄:"+ age);//这的执行效率有问题,以后解决。
}
//受保护的构造方法
protected Student(boolean n){
System.out.println("受保护的构造方法 n = " + n);
}
//私有构造方法
private Student(int age){
System.out.println("私有的构造方法 年龄:"+ age);
}
}
package fanshe;
import java.lang.reflect.Constructor;
/*
* 通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员;
*
* 1.获取构造方法:
* 1).批量的方法:
* public Constructor[] getConstructors():所有"公有的"构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
* 2).获取单个的方法,并调用:
* public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
* public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
*
* 调用构造方法:
* Constructor-->newInstance(Object... initargs)
*/
public class Constructors {
public static void main(String[] args) throws Exception {
//1.加载Class对象
Class clazz = Class.forName("fanshe.Student");
//2.获取所有公有构造方法
System.out.println("**********************所有公有构造方法*********************************");
Constructor[] conArray = clazz.getConstructors();
for(Constructor c : conArray){
System.out.println(c);
}
System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************");
conArray = clazz.getDeclaredConstructors();
for(Constructor c : conArray){
System.out.println(c);
}
System.out.println("*****************获取公有、无参的构造方法*******************************");
Constructor con = clazz.getConstructor(null);
//1>、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型
//2>、返回的是描述这个无参构造函数的类对象。
System.out.println("con = " + con);
//调用构造方法
Object obj = con.newInstance();
// System.out.println("obj = " + obj);
// Student stu = (Student)obj;
System.out.println("******************获取私有构造方法,并调用*******************************");
con = clazz.getDeclaredConstructor(char.class);
System.out.println(con);
//调用构造方法
con.setAccessible(true);//暴力访问(忽略掉访问修饰符)
obj = con.newInstance('男');
}
}
- 输出
**********************所有公有构造方法*********************************
public fanshe.Student(java.lang.String,int)
public fanshe.Student(char)
public fanshe.Student()
************所有的构造方法(包括:私有、受保护、默认、公有)***************
private fanshe.Student(int)
protected fanshe.Student(boolean)
public fanshe.Student(java.lang.String,int)
public fanshe.Student(char)
public fanshe.Student()
fanshe.Student(java.lang.String)
*****************获取公有、无参的构造方法*******************************
con = public fanshe.Student()
调用了公有、无参构造方法执行了。。。
******************获取私有构造方法,并调用*******************************
public fanshe.Student(char)
姓名:男