反射

 

反射就是Reflection,Java的反射是指程序在运行期可以拿到一个对象的所有信息。

 

 从java基础的阶段我们了解到,我们使用某个类时必定知道它是什么类,是用来做什么的。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。

package com.qijie.java;

public class Person {


    private String name;
    private  int age;

    public Person() {
    }

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

    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("我是一个人");
    }
    public void  display(String nation){
        System.out.println("我的国籍是:"+nation);
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

 

测试类

import org.junit.Test;

public class TestReflection {
    //    有了反射可以通过反射创建一个类的对象并调用其中的结果
//    有反射前 我们如何创建一个类的对象并对用其中的方法或属性
    @Test
    public  void Test1(){
        Person person=new Person();
        person.setAge(12);
        person.setName("石晨霖");
        System.out.println(person);
        person.show();
        person.display("HK");
    }

}

 

反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。

前提须知:

JDK提供的反射API

java反射机制提供的功能
在运行期间判断任意一个对象所属的类
在运行期间构造任意一个类的对象
在运行期间判断任意一个类所具有的成员变脸和方法
在运行期间调用任何一个对象的成员变量和方法
生成动态代理
 
 
反射相关的主要API
java.lang.Class 代表一个类
java.lang.reflect.Method方法
java.lang.reflect.Filed代表成员的变量
java.lang.reflect.Constructor代表类的构造方法
 
举个反射的例子
Class clz = Class.forName("com.atqijie.reflect.SCL");
Method method = clz.getMethod("setPrice", int.class);
Constructor constructor = clz.getConstructor();
Object object = constructor.newInstance();
method.invoke(object, 4);

仔细观察一下上述代码直接的异同,我们从结果来看都获取了方法,但是获取的方式不同。

 第一种的方式是我们再java基础阶段最普遍的创建对象 获取方法,属于在运行的时候我们已经知道了这到底是什么类(Person) 

 第二种我们在运行时通过字符串值才知道了运行的类是(SCL)

 

 

实际反射就是可以通过对象反射求出类的名称
反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。
 
正常方式 映入需要的包类名称 通过new实例化 取得实例对象
反射方式 实例化对象 getClass方法 得到完整的包类名称

 

1 java.lang.Class:是反射的源头
  我们创建了一个类 通过编译javac.exe生成对应的.class文件 之后我们使用java.exe加载(JVM的类加载器完成的)此。class文件 此加载到内存以后就是一个运行时类 存放在缓存区。那么这个运行时类本身就是一个Class的实例 相当于这个运行实例就是这个Class类本身 注意点:每一个运行时类只加载一次 clazz只是一个对象实例
 
Class clazz= Person.class;
一个叫Class的类 相当于Person运行时本身这个类充当了Class的实例 与new Person一样直接指向创建clazz对应的运行时类Person类的对象
Class clazz1=String.class;   运行时类是谁就对应Class是谁
 
2有了Class实例以后我们才能进行如下操作
1}创建对应的运行时类的对象
2}可以获取对应时类的完整结构 属性方法构造器内部类父类所在的包以及异常和注解等等
3}调用对应的运行时的类的指定的结构 属性方法和构造器
4}反射的应用 动态代理

 

如何获取Class的实例3种
1直接调用运行时类本身的.class属性
  1 调用运行时类本身的。class属性

          */
@Test
public  void Test4() throws ClassNotFoundException {
        Class clazz1= Person.class;
        System.out.println(clazz1.getName());
                Class clazz2=String.class;
        System.out.println(clazz2.getName());

2第二种方式 通过运行时类的对象来获取


Person p =new Person();
Class clazz3=p.getClass();
    System.out.println(clazz3.getName());

 

3第三种通过Class的静态方法ForName来获取

/第三种:反射的动态性就是下面两行代码不用动更具你传进来的不同 来创建的对象也不一样 调用的也不一样
    Class clazz4= Class.forName(className);//获取包名路径
// clazz4.newInstance();通过newInstance创建实例
        System.out.println(clazz4.getName());
                }

 

 

有了class对象以后能做什么
创建类的对象 调用Class对象的newInstance()方法
要求:1类必须要有一个无参数的构造器
          2类的构造器的访问权限必须足够
 
   运行时类创建类的时候 尽量保留一个空参构造器
  好处:有可能需要通过反射去创建一个类的对象newINstance需要空构造器   每一个类都有子类 都需要调父类空参构造器

 

public class TestConstructor {
    @Test
    public  void Test1() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
//        第三种通过Class的静态方法来获取
        String className="com.qijie.java.Person";
        Class clazz=Class.forName(className);
//        创建对应的运行时类的对象 使用newInstance方法 实际上就是调用了运行时类的空参构造器
//        要想创建成功 要求对应的运行类要有空参的构造器 构造器的权限修饰符的权限要足够
        Object object= clazz.newInstance();//默认的返回时object类型的  newInstance调用的就是空参的构造器
        Person p=(Person) object;
        System.out.println(p);
    }
}

 

我们在获取类之后需要创建对应的对象 所以需要newInstance方法 这个时候就需要调用到空的构造器且构造器的权限需要够大

clazz.newInstance 创建了对象 但是由于我们不知道这个对象时哪个类型 所以我们定义为Object类型

再强制转换为我们获取得到的Person类 最后调用。

 

通过反射我们可以获取类的完整结构
实现全部的接口
所继承的父类
全部的构造器
全部的方法
全部的Field

 

获取实现的接口 和获取注解

public class TestInterface {
    @Test
//    获取实现的接口
    public  void Test(){
        Class clazz=Person.class;
        Class[] interfaces=   clazz.getInterfaces();
        for(Class i:interfaces){
            System.out.println(i);
        }

    }
    //    获取注解
    @Test
    public  void test(){
        Class clazz=Person.class;
        Annotation[] annotations=clazz.getAnnotations();
        for(Annotation annotation:annotations){

        }

 

根据 Class 对象实例获取 Constructor (结构体)对象

 Constructor appleConstructor = clz.getConstructor();

 

要调用某一个方法,则需要经过下面的步骤:

  • 获取方法的 Method 对象
Method setPriceMethod = clz.getMethod("setPrice", int.class);
  • 利用 invoke 方法调用方法

setPriceMethod.invoke(appleObj, 14);
1调用指定方法
通过反射调用类中的方法,通过Method类完成
通过Class类的getMethod()方法取得一个Method对象,并设置此方法操作时需要的参数类型
2之后使用objectinvoke()进行调用 并向方法中传递要设置的obj对象的参数信息

 

举例:



    //    调用运行时类中指定的方法
    @Test
    public  void Test3() throws Exception {
        Class clazz=Person.class;
//        getMethod运行时声明为public的指定的方法
        Method method1=clazz.getMethod("show");//有形参需要写
        Method method2=clazz.getMethod("toString");
        Person p= (Person) clazz.newInstance();
//        调用指定的方法是invoke 有形参的话需要些具体参数 否则包异常  方法是有返回值的 如果没有返回值的话需要 使用Object
        Object returnValue1= method1.invoke(p);//我是一个人
        Object returnVulue2=method2.invoke(p);
        System.out.println(returnValue1);//因为没有返回值所以是null
        System.out.println(returnVulue2);//Perosn name=null age=0
//        对于运行类静态方法的调用
        Method method3=clazz.getMethod("info");
        method3.invoke(Person.class);
//getDeclaredMehtod(String。。。)获取运行时类中声明了的指定的方法
        Method method4= clazz.getDeclaredMethod("display", String.class,Integer.class);//首先获取方法
        //其次调用方法 由于此方法时私有属性苏哦一需要setAccessible
        method4.setAccessible(true);
        Object Value=method4.invoke(p,"CNN",10);//我的国际时 CNN 10
        System.out.println(Value);//输出的返回值10


    }
    //调用指定的构造器 创建运行时类的对象 反射使用的时newInstance来通过空构造器来创建类的对象
//    所以我们一般提前设置好空构造器 但是没有空构造器我们如何来创建对象 我们可以调用指定的构造器来创建

    @Test
    public void Test4() throws Exception {
        String className="com.qijie.java.Person";
        Class clazz=Class.forName(className);
        Constructor constructor=clazz.getDeclaredConstructor(String.class,int.class);
        constructor.setAccessible(true);
        Person person2=(Person) constructor.newInstance("scl" ,21);
        System.out.println(person2);


    }


}

 

注意一点的是,在我们获取方法时候,很多时候方法private而非public,此时我们使用getMethod()将无法获取方法,需要使用getDecaredMethod()。且设置属性

为属性赋值需要先获取属性
Accessable属性是继承自AccessibleObject 类. 功能是启用或禁用安全检查
setAccessible()
允许访问私有属性
 
使用上述两条即可获取我们所需要的所有方法。
要有一个注意点就是 我们的方法通常是有参数的,当我们通过getMethod获取方法的时候 需要将其中的参数类型标注,如上述代码所示
与此同时我们在调用invoke方法的时候同样要有参数 

 

反射与new的区别
反射和new都是创建对象实例的,

不同的是

new对象无法调用该类里面私有private的属性,而反射可以调用类中private的属性
new属于静态编译。就是在编译的时候把所有的模块都确定,如果有添加或删除某些功能,需要重新编译。但系统不可能一次就把把它设计得很完美,当发现需要更新某些功能时,采用静态编译的话,需要把整个程序重新编译一次才可以实现功能的更新。也就是说,用户需要把以前的软件卸载了,再重新安装才会重新编译!这样的系统耦合严重,难以扩展

反射属于动态编译。在运行时确定类型并创建对象,通过反射指定模板,动态的向模板中传入要实例化的对象。动态编译最大限度发挥了Java的灵活性,体现了多态的应用,有以降低类之间的藕合性。其中spring中ioc的核心就是利用了反射解耦合。

 

 

反射在ioc的应用

 

 

原来我们创建类和调用

public interface Fruit {
    public  void Love();
}

 

实现类1

public class FruitImpl1 implements Fruit{
    @Override
    public void Love() {
        System.out.println("大雄喜欢静香");
    }
}

 

实现类2

public class FruitImpl2 implements Fruit{
    @Override
    public void Love() {
        System.out.println("胖虎喜欢静香、。。。。");
    }
}

 

 

创建工厂类

public class FruitFactory {
    public  static Fruit getInstance(String FruitName){
        Fruit f=null;
        if("daxiong".equals(FruitName)){
            f=new FruitImpl1();
        }
        if("panghu".equals(FruitName)){
            f=new FruitImpl2();
        }
        return f;

    }
}
class hello{
    public static void main(String[] a){
        Fruit f=FruitFactory.getInstance("panghu");
        f.Love();
    }
}

 

上面的缺点就是如果我需要修改一个类 其中的类也需要随之更改,耦合度太高 所谓牵一发而动全身

这时候我们就需要联想到我们之前所学到的有关ioc容器的知识

 


public class FruitFactory {
    public  static Fruit getInstance(String ClassName) throws Exception {
        Fruit f=null;
        f=(Fruit)Class.forName(ClassName).newInstance();
        return f;

    }
}
class hello {
    public static void main(String[] args) throws Exception {

        Fruit f = FruitFactory.getInstance("FruitImpl1");
        if (f != null) {
            f.Love();
        }
    }
}

 

posted @ 2022-02-21 16:00  你问人生何  阅读(31)  评论(0编辑  收藏  举报

载入天数...载入时分秒...