Java反射
一、理解Class类
1.1 Class类的介绍
Class类是一个对象照镜子后的结果。对象照镜子后可以得到的信息;某个类的属性、方法、构造器、实现的接口等。对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定某个类的有关信息。
- class对象只能由系统建立对象(在类加载阶段建立了类对象,不是new出来,而是系统创建)
- 对于某个类的Class对象,在内存中只有一份,因为类只加载一次。
- 每个类的实例都会记得自己是由那个Class实例所生成。
- Class也是类,因此也继承Object类
- 通过Class对象可以完整地得到一个类的完整结构,通过一系列API
- Class对象是存放在堆的
- 类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据。
通俗的来说,Class类是一个对象的基本信息的描述,对象通过照镜子,看到自己的全部特征。而class类就是描述这些全部特征的一个类对象。
1.2 Class类常用方法
二、java反射机制
2.1 反射机制的作用
通过这种反射机制:
- 在运行时判断一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时得到任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的成员变量和方法
- 生成动态代理
2.2 反射相关的主要类
java.lang.Class
Class对象表示某个类加载后在堆中的对象
java.lang.reflect.Method
代表类的方法,Method对象表示某个类的方法
java.lang.reflect.Filed
代表类的成员变量,Field对象表示某个类的成员变量
java.lang.reflect.Constructor
代表类的构造方法,Constructor对象表示构造器
2.3 利用反射机制读取配置文件实例化对象
通过读取配置文件,而不改变原来程序就能实例化对象!Spring框架的核心功能的底层实现就是依赖这种反射机制
public static void main(String[] args) throws Exception {
//1. 使用 Properties 类, 可以读写配置文件
Properties properties = new Properties();
properties.load(new FileInputStream("src\\re.properties"));
String classfullpath = properties.get("classfullpath").toString();//"com.hspedu.Cat"
String methodName = properties.get("method").toString();//"hi"
//2. 使用反射机制解决
//(1) 加载类, 返回 Class 类型的对象 cls
Class cls = Class.forName(classfullpath);
//(2) 通过 cls 得到你加载的类 com.hspedu.Cat 的对象实例
Object o = cls.newInstance();
System.out.println("o 的运行类型=" + o.getClass()); //运行类型
//(3) 通过 cls 得到你加载的类 com.hspedu.Cat 的 methodName"hi" 的方法对象
// 即: 在反射中, 可以把方法视为对象(万物皆对象)
Method method1 = cls.getMethod(methodName);
//(4) 通过 method1 调用方法: 即通过方法对象来实现调用方法
System.out.println("=============================");
method1.invoke(o); //传统方法 对象.方法() , 反射机制 方法.invoke(对象)
//java.lang.reflect.Field: 代表类的成员变量, Field 对象表示某个类的成员变量
//得到 name 字段
//getField 不能得到私有的属性
Field nameField = cls.getField("age"); //
System.out.println(nameField.get(o)); // 传统写法 对象.成员变量 , 反射 : 成员变量对象.get(对象)
//java.lang.reflect.Constructor: 代表类的构造方法, Constructor 对象表示构造器
Constructor constructor = cls.getConstructor(); //()中可以指定构造器参数类型, 返回无参构造器
System.out.println(constructor);//Cat()
Constructor constructor2 = cls.getConstructor(String.class); //这里传入的 String.class 就是 String 类的
Class 对象
System.out.println(constructor2);//Cat(String name)
}
2.4反射机制的优缺点
- 优点
反射可以在不知道会运行哪一类的情况下,获取到类的信息,创建对象以及操作对象。方便于拓展,所以反射是框架设计的灵魂,因为框架在设计的时候,为了降低耦合度,肯定是需要考虑扩展功能的,不能将类型写死。降低耦合度,变得更灵活,在运行时去确定类型,绑定对象,体现了多态功能。 - 缺点
反射是需要动态类型的,JVM没有办法优化这部分代码,执行效率相对直接初始化对象较低。一般业务代码不介意使用。反射可以修改权限,这是会破坏封装性,存在安全隐患。
可以通过setAccessible来关闭安全检查来提高一下执行效率
三、有关反射的具体操作
3.1 获取对象的包名以及类名
package invocation;
public class MyInvocation {
public static void main(String[] args) {
getClassNameTest();
}
public static void getClassNameTest(){
MyInvocation myInvocation = new MyInvocation();
System.out.println("class: " + myInvocation.getClass());
System.out.println("simpleName: " + myInvocation.getClass().getSimpleName());
System.out.println("name: " + myInvocation.getClass().getName());
System.out.println("package: " +
"" + myInvocation.getClass().getPackage());
}
}
1.getClass():打印会带着class+全类名
2.getClass().getSimpleName():只会打印出类名
3.getName():会打印全类名
4.getClass().getPackage():打印出package+包名
getClass()获取到的是一个对象,getPackage()也是。
3.2 获取Class对象
在java中,一切皆对象。java中可以分为两种对象,实例对象和Class对象。这里我们说的获取Class对象,其实就是第二种,Class对象代表的是每个类在运行时的类型信息,指和类相关的信息。比如有一个Student类,我们用Student student = new Student()new一个对象出来,这个时候Student这个类的信息其实就是存放在一个对象中,这个对象就是Class类的对象,而student这个实例对象也会和Class对象关联起来。我们有三种方式可以获取一个类在运行时的Class对象,分别是
Class.forName(“com.Student”)
student.getClass()
Student.class
package invocation;
public class MyInvocation {
public static void main(String[] args) {
getClassTest();
}
public static void getClassTest(){
Class<?> invocation1 = null;
Class<?> invocation2 = null;
Class<?> invocation3 = null;
try {
// 最常用的方法
invocation1 = Class.forName("invocation.MyInvocation");
}catch (Exception ex){
ex.printStackTrace();
}
invocation2 = new MyInvocation().getClass();
invocation3 = MyInvocation.class;
System.out.println(invocation1);
System.out.println(invocation2);
System.out.println(invocation3);
}
}
3.3 newIntence; newInstance()是一个无参构造方法
class Student{
private int age;
private String name;
public Student() {
}
public Student(int age) {
this.age = age;
}
public Student(String name) {
this.name = name;
}
public Student(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
public static void getInstanceTest() {
try {
Class<?> stduentInvocation = Class.forName("invocation.Student");
Student student = (Student) stduentInvocation.newInstance();
student.setAge(9);
student.setName("Hahs");
System.out.println(student);
}catch (Exception ex){
ex.printStackTrace();
}
}
输出结果如下:
Student{age=9, name='Hahs'}
3.4 通过构造函数对象实例化对象
可以先获取一个类的所有的构造方法,然后遍历输出:
public static void testConstruct(){
try {
Class<?> stduentInvocation = Class.forName("invocation.Student");
Constructor<?> cons[] = stduentInvocation.getConstructors();
for(int i=0;i<cons.length;i++){
System.out.println(cons[i]);
}
}catch (Exception ex){
ex.printStackTrace();
}
}
不同构造函数创建对象
public static void constructGetInstanceTest() {
try {
Class<?> stduentInvocation = Class.forName("invocation.Student");
Constructor<?> cons[] = stduentInvocation.getConstructors();
// 一共定义了4个构造器
Student student1 = (Student) cons[0].newInstance(9,"Sam");
Student student2 = (Student) cons[1].newInstance("Sam");
Student student3 = (Student) cons[2].newInstance(9);
Student student4 = (Student) cons[3].newInstance();
System.out.println(student1);
System.out.println(student2);
System.out.println(student3);
System.out.println(student4);
} catch (Exception ex) {
ex.printStackTrace();
}
四、通过反射获取类的结构信息
获取类中的结构信息时,当要通过反射设置私有属性时,系统会报错!此时我们可以使用对象.setAccessible(true);来进行爆破。与含有Declared中的方法搭配使用。