Java~如何让代码“自省”--认识面向对象编程的其中一个重要特性--反射

我们常说面向对象编程的三大特性是“继承封装多态”,但是绝对不是只有这三种“组合抽象反射”,也是面向对象编程的特性。

定义

  • Java的反射(reflection)机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法(无论pubilc还是private);对于任意一个对象,都能够调用它的任意方法和属性,既然能拿到那么,我们就可以修改部分类型信息;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射(reflection)机制

用途(目前只做了解)

1、在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放,这时候就可以利用Java的反射机制通过反射来获取所需的私有成员或是方法 。

2、反射最重要的用途就是开发各种通用框架,比如在spring中,我们将所有的类Bean交给spring容器管理,无论是XML配置Bean还是注解配置,当我们从容器中获取Bean来依赖注入时,容器会读取配置,而配置中给的就是类的信息,spring根据这些信息,需要创建那些Bean,spring就动态的创建这些类。

反射的基本内容

  • Java程序中许多对象在运行时会出现两种类型:运行时类型(RTTI)和编译时类型,例如Person p = newStudent();这句代码中p在编译时类型为Person,运行时类型为Student。程序需要在运行时发现对象和类的真实信心。而通过使用反射程序就能判断出该对象和类属于哪些类。

反射相关的类(重要):

  • Class类
    代表类的实体,在运行的Java应用程序中表示类和接口

  • Field类
    代表类的成员变量、类的属性

  • Method类
    代表类的方法

  • Constructor类
    代表类的构造方法

如何利用反射获取类对象:

  • Class类对象

Java文件被编译后,生成了.class文件,JVM此时就要去解读.class文件 ,被编译后的Java文件.class也被JVM解析为一个对象,这个对象就是 java.lang.Class .这样当程序在运行时,每个java文件就最终变成了Class类对象的一个实例(注意,这个Class对象存放在方法区,不在堆里面的,所以一个类无论在代码中实际被使用了多少次,但对应的类对象是一个单例,一定只有一个,并且被实例化一次,而且同一个类不需要重复加载)。我们通过Java的反射机制应用到这个实例,就可以去获得甚至去添加改变这个类的属性和动作,使得这个类成为一个动态的类

三种获取Class对象的方式

  • 第一种,使用 Class.forName(“类的全路径名”)是静态方法

前提:已明确类的全路径名有包的时候就必须加包的路径,还要有异常处理,优点就是在写代码的时候可以不用知道类的具体名字,它可以自动在运行时获取,也可以文本读入、用户输入等方式获得。

//通过路径获得class对象,注意forName输入的字符串是类的全路径,如果有包必须得加包的路径
        //有好处就是在编译的时候不用知道类名
        Class c3 = Class.forName("Cat");
  • 第二种,使用 .class 方法。

说明:仅适合在编译前就已经明确要操作的 Class,最为安全可靠,程序性能更高 这说明任何一个类都有一个隐含的静态成员变量 class

 Class c2 = Cat.class;
  • 第三种,使用类对象的 getClass() 方法

说明:每个类都有隐藏的getClass()方法

Cat cat = new Cat("咪咪", 10);
        Class c1 = cat.getClass();
  • 一个类在 JVM 中只会有一个 Class 实例,即我们对上面获取的 c1,c2,c3进行 == 对象比较,发现都是true
System.out.println(c1 == c2);
        System.out.println(c1 == c3);
        System.out.println(c2 == c3);

执行结果:

true
true
true

完整代码

class Cat {
    public String name;
    public int age;

    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void eat() {
        System.out.println("Eating");
    }
}

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        //第一种或得class对象的方法,根据实例化对象获得class对象
        Cat cat = new Cat("咪咪", 10);
        Class c1 = cat.getClass();
        //第二种,通过类的隐藏静态变量获得class对象,所以每一个类都会有一个隐藏的静态成员变量class
        Class c2 = Cat.class;
        //通过路径获得class对象,注意forName输入的字符串是类的全路径,如果有包必须得加包的路径
        //第三种有好处就是在编译的时候不用知道类名
        Class c3 = Class.forName("Cat");
        //因为Class对象在方法区存储,所以一个class对象只能有一个
        System.out.println(c1 == c2);
        System.out.println(c1 == c3);
        System.out.println(c2 == c3);
    }
}

猜你喜欢

转载自blog.csdn.net/Shangxingya/article/details/106166877