JAVA反射

目录

 

一、概念:

二、反射的作用

三、JAVA 反射API

1、Class类:反射的核心类,可以获取类的属性,方法等信息。

2、Field类:Java.lang.reflec包中的类,表示类的成员变量,可以用来获取和设置类之中的属性值。

3、Method类:Java.lang.reflec包中的类,表示类的方法,它可以用来获取类中的方法信息或者执行方法。

4、Constructor类:Java.lang.reflec包中的类,表示类的构造方法。

5、获取继承关系

四、反射应用(动态代理)

五、Optional类(轻量级代理)


一、概念:

      “反射”机制:允许我们在运行时发现和使用类的信息。指在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法;并且对于任意一个对象,都能够调用它的任意一个方法;这种动态获取信息以及动态调用对象方法的功能成为Java语言的反射机制。

从反射角度说JAVA属于半动态语言,动态语言,是指程序在运行时可以改变其结构:新的函数可以引进,已有的函数可以被删除等结构上的变化。比如常见的JavaScript就是动态语言,除此之外Ruby,Python等也属于动态语言,而C、C++则不属于动态语言。

二、反射的作用

  •  反射是为了解决在运行期,对某个实例一无所知的情况下,如何调用其方法。
  •  编译时类型和运行时类型,在Java程序中许多对象在运行是都会出现两种类型:编译时类型和运行时类型。编译时的类型由声明对象时使用的类型来决定,运行时的类型由实际赋值给对象的类型决定。如:
Fruit fruit = new Apple();

 其中编译时类型为Fruit,运行时类型为Apple;在编译时fruit无法获取apple具体方法(非继承)。

  •  程序在运行时还可能接收到外部传入的对象,该对象的编译时类型为Object,但是程序有需要调用该对象的运行时类型的方法。为了解决这些问题,程序需要在运行时发现对象和类的真实信息。然而,如果编译时根本无法预知该对象和类属于哪些类,程序只能依

   靠运行时信息来发现该对象和类的真实信息,此时就必须使用到反射了。

三、JAVA 反射API

1、Class类:反射的核心类,可以获取类的属性,方法等信息。

  • Java的类型全部都是class(除了基本类型外),所以class(包括interface)的本质是数据类型(Type)。无继承关系的数据类型无法赋值:而class是由JVM在执行过程中动态加载的。
  • JVM在第一次读取到一种class类型时,将其加载进内存,每加载一种class,JVM就为其创建一个Class类型的实例,并关联起来。

        注意:这里的Class类型是一个名叫Class的class。它长这样:

public final class Class {
    private Class() {}
}

      以String类为例,当JVM加载String类时,它首先读取String.class文件到内存,然后,为String类创建一个Class实例并关联起来:
      Class cls = new Class(String);

     
       这个Class实例是JVM内部创建的,如果我们查看JDK源码,可以发现Class类的构造方法是private,只有JVM能创建Class实例,我们自己的Java程序是无法创建Class实例的。所以,JVM持有的每个Class实例都指向一个数据类型(class或interface): 由于          JVM为每个加载的class创建了对应的Class实例,并在实例中保存了该class的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等,因此,如果获取了某个Class实例,我们就可以通过这个Class实例获取到该实例对应的class的所有信息。

  •      获取Class对象的3种方法

            1、调用某个对象的getClass()方法

                 Person p=new Person();

                 Class clazz=p.getClass();

           2、调用某个类的class属性来获取该类对应的Class对象

                Class clazz=Person.class;

            3、使用Class类中的forName()静态方法(最安全/性能最好)

                Class clazz=Class.forName("类的全路径"); (最常用)

            当我们获得了想要操作的类的Class对象后,可以通过Class类中的方法获取并查看该类中的方法和属性。

  •  动态加载应用
// Commons Logging优先使用Log4j:
LogFactory factory = null;
if (isClassPresent("org.apache.logging.log4j.Logger")) {
    factory = createLog4j();
} else {
    factory = createJdkLog();
}

boolean isClassPresent(String name) {
    try {
        Class.forName(name);
        return true;
    } catch (Exception e) {
        return false;
    }
}

       这就是为什么我们只需要把Log4j的jar包放到classpath中,Commons Logging就会自动使用Log4j的原因。

 

2、Field类:Java.lang.reflec包中的类,表示类的成员变量,可以用来获取和设置类之中的属性值。

  •     获取属性API
  1.  Field getField(name):根据字段名获取某个public的field(包括父类)
  2.  Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
  3.  Field[] getFields():获取所有public的field(包括父类)
  4.  Field[] getDeclaredFields():获取当前类的所有field(不包括父类)
  •     一个Field对象包含了一个字段的所有信息:
  1. getName():返回字段名称,例如,"name"
  2. getType():返回字段类型,也是一个Class实例,例如,String.class
  3. getModifiers():返回字段的修饰符,它是一个int,不同的bit表示不同的含义。
  •  Field.setAccessible(true)的意思是,别管这个字段是不是public,一律允许访问
public class Fruit {
    private String name;

    public Fruit() {
    }

    public Fruit(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }
}

class Apple extends Fruit {
    public int color;
    private int shape ;
}


public class FruitTest {
    @Test
    public void test() throws NoSuchFieldException, IllegalAccessException {
        Class appleClass = Apple.class;
        // 获取public字段"color":
        System.out.println(appleClass.getField("color"));
        // 获取继承的public字段"name":
//        System.out.println(appleClass.getField("name"));
        // 获取private字段"shape":
        System.out.println(appleClass.getDeclaredField("shape"));

        // 获取所有public的field(包括父类)
        List<Field> fields = Arrays.asList(appleClass.getFields());
        for (Field field: fields) {
            System.out.println(field);
        }

        // 获取当前类的所有field(不包括父类)
        List<Field> declaredfields = Arrays.asList(appleClass.getDeclaredFields());
        for (Field field: declaredfields) {
            System.out.println(field);
        }

        // 获取public字段"color":
        Field field = appleClass.getField("color");
        // 返回字段名称
        System.out.println(field.getName());
        // 返回字段类型
        System.out.println(field.getType());
        // 返回字段的修饰符
        System.out.println(field.getModifiers());
        int m = field.getModifiers();
        Modifier.isFinal(m); // false
        Modifier.isPublic(m); // true
        Modifier.isProtected(m); // false
        Modifier.isPrivate(m); // true
        Modifier.isStatic(m); // false

        // 获取字段值
        Object obj = new Fruit("ping guo");
        Class objClass = obj.getClass();
        Field name = objClass.getDeclaredField("name");
        name.setAccessible(true);
        Object value = name.get(obj);
        System.out.println(value); // "ping guo"

        // 设置字段值
        Fruit fruit = new Fruit("ping guo");
        System.out.println(fruit.getName());
        Class c = fruit.getClass();
        Field f = c.getDeclaredField("name");
        f.setAccessible(true);
        f.set(fruit, "Xiao jiao");
        System.out.println(fruit.getName());


    }

}

3、Method类:Java.lang.reflec包中的类,表示类的方法,它可以用来获取类中的方法信息或者执行方法。

  • 获取Method API;
  1. getName():返回方法名称,例如:"getScore";
  2. getReturnType():返回方法返回值类型,也是一个Class实例,例如:String.class;
  3. getParameterTypes():返回方法的参数类型,是一个Class数组,例如:{String.class, int.class};
  4. getModifiers():返回方法的修饰符,它是一个int,不同的bit表示不同的含义。
  • 获取Method方法信息API
  1. getName():返回方法名称,例如:"getScore";
  2. getReturnType():返回方法返回值类型,也是一个Class实例,例如:String.class;
  3. getParameterTypes():返回方法的参数类型,是一个Class数组,例如:{String.class, int.class};
  4. getModifiers():返回方法的修饰符,它是一个int,不同的bit表示不同的含义。
  • 调用方法,:Object invoke(Object instance, Object... parameters);
  • 调用静态方法,由于无需指定实例对象,所以invoke方法传入的第一个参数永远为null
  • 调用非public方法,通过设置setAccessible(true)来访问非public方法;
  • 使用反射调用方法时,仍然遵循多态原则

    

public class Person {
    public String getName() {
        return "xiaom";
    }
}

class Student extends Person {
    public int getScore(String type) {
        return 100;
    }
    private int getGrade(int year) {
        return 6;
    }
}

public class PersonTest {
    @Test
    public void test() throws Exception {
        Class stdClass = Student.class;
        // 获取public方法getScore,参数为String:
        System.out.println(stdClass.getMethod("getScore", String.class));
        // 获取继承的public方法getName,无参数:
        System.out.println(stdClass.getMethod("getName"));
        // 获取private方法getGrade,参数为int:
        System.out.println(stdClass.getDeclaredMethod("getGrade", int.class));


        /**
         * 调用方法
         */
        // String对象:
        String s = "Hello world";
        // 获取String substring(int)方法,参数为int:
        Method m = String.class.getMethod("substring", int.class);
        // 在s对象上调用该方法并获取结果:
        String r = (String) m.invoke(s, 6);
        // 打印调用结果:
        System.out.println(r);


        /**
         * 调用静态方法
         */
        // 获取Integer.parseInt(String)方法,参数为String:
        Method method = Integer.class.getMethod("parseInt", String.class);
        // 调用该静态方法并获取结果:
        Integer n = (Integer) method.invoke(null, "89898");
        // 打印调用结果:
        System.out.println(n);

        /**
         *  调用非public方法
         */

        Car car = new Car();
        Method staticMethod = car.getClass().getDeclaredMethod("setName", String.class);
        staticMethod.setAccessible(true);
        staticMethod.invoke(car, "宝马");
        System.out.println(car.name);
        
    }
}

4、Constructor类:Java.lang.reflec包中的类,表示类的构造方法。

  • 获取构造方法API
  1. getConstructor(Class...):获取某个public的Constructor;
  2. getDeclaredConstructor(Class...):获取某个Constructor;
  3. getConstructors():获取所有public的Constructor;
  4. getDeclaredConstructors():获取所有Constructor。
  • 创建对象
  1. 使用Class对象的newInstance()方法来创建该Class对象对应类的实例,但是这种方法要求该Class对象对应的类有默认的空构造器。
  2. 先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建Class对象对应类的实例,通过这种方法可以选定构造方法创建实例。
//获取Person类的Class对象
Class clazz=Class.forName("reflection.Person"); 
//使用.newInstane方法创建对象
Person p=(Person) clazz.newInstance();
//获取构造方法并创建对象
Constructor c=clazz.getDeclaredConstructor(String.class,String.class,int.class);
//创建对象并设置属性
Person p1=(Person) c.newInstance("张三","女",18);

5、获取继承关系

通过Class对象可以获取继承关系:

  • Class getSuperclass():获取父类类型;
  • Class[] getInterfaces():获取当前类实现的所有接口。

通过Class对象的isAssignableFrom()方法可以判断一个向上转型是否可以实现。

 

四、反射应用(动态代理)

  •  代理是基本的设计模式之一。一个对象封装真实对象,代替其提供其他或不同的操作---这些操作通常涉及到与“真实”对象的通信,因此代理通常充当中间对象。不仅动态创建代理对象而且动态处理对代理方法的调用。在动态代理上进行的所有调用都被重定向到单个调用处理程序,该处理程序负责发现调用的内容并决定如何处理。
  •  比较Java的class和interface的区别,可以实例化class(非abstract);不能实例化interface,标准库提供了一种动态代理(Dynamic Proxy)的机制:可以在运行期动态创建某个interface的实例 
  •  在运行期动态创建一个interface实例的方法如下:
  1. 定义一个InvocationHandler实例,它负责实现接口的方法调用;
  2. 通过Proxy.newProxyInstance()创建interface实例,它需要3个参数:

         使用的ClassLoader,通常就是接口类的ClassLoader

         需要实现的接口数组,至少需要传入一个接口进去;

         用来处理接口方法调用的InvocationHandler实例。

    3.  将返回的Object强制转型为接口。

import java.lang.reflect.*;

class DynamicProxyHandler implements InvocationHandler {
    private Object proxied;

    DynamicProxyHandler(Object proxied) {
        this.proxied = proxied;
    }

    @Override
    public Object
    invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println(
                "**** proxy: " + proxy.getClass() +
                        ", method: " + method + ", args: " + args);
        if (args != null)
            for (Object arg : args)
                System.out.println("  " + arg);
        return method.invoke(proxied, args);
    }
}

class SimpleDynamicProxy {
    public static void consumer(Interface iface) {
        iface.doSomething();
        iface.somethingElse("bonobo");
    }

    public static void main(String[] args) {
        RealObject real = new RealObject();
        consumer(real);
        // Insert a proxy and call again:
        Interface proxy = (Interface) Proxy.newProxyInstance(
                Interface.class.getClassLoader(),
                new Class[]{Interface.class},
                new DynamicProxyHandler(real));
        consumer(proxy);
    }
}

 

五、Optional类(轻量级代理)

         Optional 是 Java 8 为了支持流式编程才引入的,Optional 对象可以防止你的代码直接抛出 NullPointException。实际上,在所有地方都使用 Optional 是没有意义的,有时候检查一下是不是 null 也挺好的,或者有时我们可以合理地假设不会出现 null,甚至有时候检查 NullPointException 异常也是可以接受的。Optional 最有用武之地的是在那些“更接近数据”的地方,在问题空间中代表实体的对象上。举个简单的例子,很多系统中都有 Person 类型,代码中有些情况下你可能没有一个实际的 Person 对象(或者可能有,但是你还没用关于那个人的所有信息)。这时,在传统方法下,你会用到一个 null 引用,并且在使用的时候测试它是不是 null。而现在,我们可以使用 Optional:

class Person {
    public final Optional<String> first;
    public final Optional<String> last;
    public final Optional<String> address;
    // etc.
    public final Boolean empty;

    Person(String first, String last, String address) {
        this.first = Optional.ofNullable(first);
        this.last = Optional.ofNullable(last);
        this.address = Optional.ofNullable(address);
        empty = !this.first.isPresent()
                && !this.last.isPresent()
                && !this.address.isPresent();
    }

    Person(String first, String last) {
        this(first, last, null);
    }

    Person(String last) {
        this(null, last, null);
    }

    Person() {
        this(null, null, null);
    }

    @Override
    public String toString() {
        if (empty)
            return "<Empty>";
        return (first.orElse("") +
                " " + last.orElse("") +
                " " + address.orElse("")).trim();
    }

    public static void main(String[] args) {
        System.out.println(new Person());
        System.out.println(new Person("Smith"));
        System.out.println(new Person("Bob", "Smith"));
        System.out.println(new Person("Bob", "Smith",
                "11 Degree Lane, Frostbite Falls, MN"));
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值