反射与类操作

作者: 江湖非良人 | 来源:发表于2019-07-31 19:47 被阅读4次

    在反射机制的处理过程之中,不仅仅只是实例化对象的处理操作,更多的情况下还有类的组成结构操作,任何一个类的基本组成结构都是父类(父接口)、包、属性、方法(构造方法、普通方法)。

反射与类操作

获取类的基本信息 Package

    一个类的基本信息主要包括类所在的包名称、父类的定义
父接口的定义。
范例:定义一个程序类

interface IMessageService {
    void send();
}
interface IChannelService {
    String NAME="baidujava";
    boolean connect();
}
abstract class AbstractBase {
    protected static final String BASE="www.baidu.com";
    private String info="hello info";
    public AbstractBase(){}
    public AbstractBase(String msg){}
}
public class Person extends AbstractBase implements  IChannelService,IMessageService {
    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;
    }
    @Override
    public String toString() {
        return String.format("姓名:%s、年龄:%s",name,age);
    }
    @Override
    public void send() {
        if (this.connect()) {
            System.out.println("【信息发送】www.baidu.com");
        }
    }
    @Override
    public boolean connect() {
        return true;
    }
}

  如果此时想获得该类的一些基础信息则可以通过Class类中的如下方法:
    1、获取包名称:public Package getPackage()
    2、获取继承父类:public Class<? super T> getSuperclass()
    3、获取实现父接口:public Class<?>[] getInterfaces()
  当获取了一个类的Class对象后,就意味着这个对象可以获取类中的一切继承结构信息。

范例:获得包名称

public class JavaAPIDemo {
    public static void main(String[] args) {
        Class<?> cls = Person.class;//获取指定类的Class对象
        Package pack = cls.getPackage();//获取指定类的包定义
        System.out.println(pack.getName());//获取包名称:com.mldn.demo
    }
}

范例:获得父类信息

public class JavaAPIDemo {
    public static void main(String[] args) {
        Class<?> cls = Person.class;
        Class<?> parent = cls.getSuperclass();//获取父类的Class对象
        System.out.println(parent.getName());//或父类的对象名称
    }
}

范例:获得父接口

public class JavaAPIDemo {
    public static void main(String[] args) {
        Class<?> cls = Person.class;
        Class<?>[] parents = cls.getInterfaces();//获取父接口的Class对象
        for (Class parent:parents){
            System.out.println(parent.getName());//获取接口父的对象名称
        }
    }
}

获取构造方法 Constructor

  在一个类中除了有继承的关系外,最为重要的操作就是类中的结构处理了,而类中的结构中首先需要观察的就是构造方法的使用问题,在之前通过反射实例化对象的啥时候就已经接触到构造方法的问题了:

  • 实例化方法替代:clazz.getDeclaredConstructor().newInstance()
      所有类的构造方法的获取都可以直接通过Class类来完成,该类中定义有如下的几个方法:
  • 获取所有构造方法:
      public Constructor<?>[] getDeclaredConstructors() throws SecurityException
      public Constructor<?>[] getConstructors()throws SecurityException
  • 获取指定构造方法:
      public Constructor<T> getConstructor​(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
      public Constructor<T> getConstructor​(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
    范例:获取构造
import java.lang.reflect.Constructor;
public class JavaAPIDemo {
    public static void main(String[] args) {
        Class<?> cls = Person.class;
        Constructor<?>[] declaredConstructors=cls.getDeclaredConstructors();//获取全部构造(本类)
        for (Constructor<?> cons:declaredConstructors){
            System.out.println(cons);
        }
        Constructor<?>[] constructors=cls.getConstructors();//获取全部构造(父子类)
        for (Constructor<?> cons:constructors){
            System.out.println(cons);
        }
     }
}

此时获取的是类中的全部构造方法,但是也可以获取一个指定参数的构造。如果要用有参构造方法对Person类进行对象实例化,则必须指明要调用的构造,而后通过Constructor类中提供的实例化方法操作:
  public T newInstance​(Object... initargs) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException

范例:调用指定构造实例化对象

import java.lang.reflect.Constructor;
public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Person.class;
        Constructor<?> constructor = cls.getConstructor(String.class, int.class);
        Object obj = constructor.newInstance("小强", 20);
        System.out.println(obj.toString());//姓名:小强、年龄:20
    }
}

虽然程序代码本身允许开发者调用有参构造,但是如果从实际的开发角度出发,所有使用反射的类中最好都提供有无参构造,因为这样的实例化可以达到统一性。

获取方法 Method

  在进行反射处理的时候也可以通过反射来获取类之中的全部方法,但要注意的是,如果想要通过反射调用这些方法,有一个前提条件:类之中要提供实例化对象。
  在Class类中提供了如下的操作可以获取对象的方法:

  • 获取全部方法:
      【本类】public Method[] getDeclaredMethods() throws SecurityException
      【全部】public Method[] getMethods() throws SecurityException
  • 获取指定方法
      【本类】public Method getDeclaredMethod​(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
      【全部】public Method getMethod​(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException

范例:获取全部方法

import java.lang.reflect.Method;
public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Person.class;
        Method[] methods=cls.getMethods();//获取全部方法
        for (Method method:methods){
            System.out.println(method);
        }
        System.out.println("————————————分割线——————————————————");
        methods=cls.getDeclaredMethods();//获取本类全部方法
        for (Method method:methods){
            System.out.println(method);
        }
    }
}

但是需要注意的是,这时的方法信息的获取是依靠Method类提供的toString()方法完成的,很多时候也可以由用户来拼凑方法信息的展示形式。

范例:自定义方法信息显示

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Person.class;
        Method[] methods=cls.getMethods();//获取全部方法
        for (Method method:methods){
            int mod=method.getModifiers();//修饰符
            System.out.print(Modifier.toString(mod)+" ");
            System.out.print(method.getReturnType().getName()+" ");
            System.out.print(method.getName()+"(");
            Class<?>[] paramTypes=method.getParameterTypes();//获取参数类型
            StringBuilder sb=new StringBuilder();
            for (int i = 0; i < paramTypes.length; i++) {
                sb.append(", ");
                sb.append(paramTypes[i].getName());
                sb.append(" arg"+i);
            }
            if(sb.length()>0){
                sb.delete(0,2);
            }
            System.out.print(sb.toString()+")");
            Class<?>[] exps=method.getExceptionTypes();
            if(exps!=null&&exps.length>0){
                System.out.print(" throws ");
                sb=new StringBuilder();
                for (int i = 0,l=exps.length; i < l; i++) {
                    sb.append(","+exps[i].getName());
                }
                sb.deleteCharAt(0);
                System.out.print(sb.toString());
            }
            System.out.println();
        }
    }
}

以上代码只需要清楚可以根据反射获取方法的结构即可,但在Method类中有一个非常重要方法:
  public Object invoke​(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException

范例:在不导入指定类开发包的情况下实现属性的配置

import java.lang.reflect.Method;
public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("com.mldn.demo.Person");
        String value="小强";//要设置的属性内容
        Object obj=cls.getDeclaredConstructor().newInstance();
        String setMethodName="setName";//方法名称
        Method method=cls.getDeclaredMethod(setMethodName,String.class);//获取指定方法
        method.invoke(obj,value);
        String getMethodName="getName";
        method=cls.getDeclaredMethod(getMethodName);
        Object v=method.invoke(obj);
        System.out.println(v);
    }
}

利用此类操作整体的形式上不会有任何明确的类对象产生,一切都是依靠反射机制处理的,这样的处理避免了与某一个类耦合问题。

获取成员 Field

  类结构中的最后一个核心的组成就是成员(Field),大部分情况下都会将其称为成员属性,对于成员信息的获取也是通过Class类完成,在这个类中提供了以下几个方法获取成员属性:

  • 获取全部成员属性:
      【本类】public Field[] getDeclaredFields() throws SecurityException
      【父类】public Field[] getFields() throws SecurityException
  • 获取指定成员属性:
      【本类】public Field getDeclaredField​(String name) throws NoSuchFieldException, SecurityException
      【父类】public Field getField​(String name) throws NoSuchFieldException, SecurityException
    范例:获取类中的成员
import java.lang.reflect.Field;
public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("com.mldn.demo.Person");
        Field[] fields=cls.getFields();
        for (Field f:fields){
            System.out.println(f);
        }
        System.out.println("---------以上为父类公共的成员属性,以下为子类的成员属性-------------");
        fields=cls.getDeclaredFields();
        for (Field f:fields){
            System.out.println(f);
        }
    }
}

在Field类中最为重要的操作形式并不是获取全部成员,而是以下三个方法:

  • 设置属性内容:
      public void set​(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException
  • 获取属性内容:
      public Object get​(Object obj) throws IllegalArgumentException, IllegalAccessException
    所有成员是在对象实例化后进行空间分配的,所以此时一定要先有实例化对象后才可以进行成员的操作。

范例:直接调用Person类中的name私有成员

import java.lang.reflect.Field;
public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("com.mldn.demo.Person");
        Object obj=cls.getDeclaredConstructor().newInstance();
        Field nameField=cls.getDeclaredField("name");
        nameField.setAccessible(true);//对私有属性必须先解除封装,
        nameField.set(obj,"小强");
        System.out.println(nameField.get(obj));
    }
}

类中的构造、方法、成员属性都可以通过反射实现调用,但是对于成员的反射很少这样直接处理,大部分操作都应该setter和getter处理,所以对于以上的代码只能够说是反射的特性,但是一般情况下不会使用,而对于Field类在实际开发中只有一个方法最为常用:

  • 获取成员类型:public Class<?> getType();

范例:获取Person类中name成员的类型

import java.lang.reflect.Field;
public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("com.mldn.demo.Person");
        Field nameField=cls.getDeclaredField("name");
        System.out.println(nameField.getType().getSimpleName());//String
    }
}

在以后开发中进行反射处理的时候,往往会利用Field类和Method类实现类中的setter方法的调用。

Unsafe工具类

  反射是Java的第一大特点,学习了反射就可以有了更加丰富的类设计形式。除了JVM本身支持的反射处理之外,在Java中也提供了一个Unsafe类(不安全的操作),这个类的主要特点是可以利用反射来获取对象,并且直接使用底层的C++来代替JVM执行,即:可以绕过JVM的相关的对象管理机制,一旦使用Unsafe,那么项目之中将无法继续使用JVM的内存管理机制以及垃圾回收处理。
  如果要想使用Unsafe类首先就需要确认一下这个类中定义的构造方法和常量问题:

  • 构造方法:private Unsafe() {}
  • 私有常量:private static final Unsafe theUnsafe
      需要注意的是,在这个Unsafe类中并没有提供static的方法,即:不能通过类似于传统的单例设计模式中提供的样式来进行操作,如果想要获得这个类的对象,就必须利用反射机制来完成。
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get(null);
    }
}

在传统的开发中,一个程序类必须要通过实例化对象后才可以调用类中的普通方法,尤其是以单例设计模式为例。

范例:使用Unsafe类绕过实例化对象的管理

import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get(null);
        Singleton singleton= (Singleton) unsafe.allocateInstance(Singleton.class);
        singleton.print();
    }
}
class Singleton{
    private Singleton(){
        System.out.println("****** Singleton构造********");
    }
    public void  print(){
        System.out.println("www.baidu,com");
    }
}

Unsafe只能说为开发提供了一些更加方便的处理机制,但是这种操作由于不受JVM的管理,所以不是必须的情况下不建议使用。

相关文章

  • 反射与类操作

        在反射机制的处理过程之中,不仅仅只是实例化对象的处理操作,更多的情况下还有类的组成结构操作,任何一个类的基...

  • 27.Python之面向对象的反射

    Python之面向对象的反射 反射的定义通过字符串来操作类与对象的属性(attribute)、实例变量、绑定方法等...

  • 2018-10-23 Python33 内置函数

    一、数学运算类 二、集合类操作 三、逻辑判断 四、反射 五、IO操作

  • 反射和程序集

    反射 反射就是一个操作metadata元数据的一个类库(可以把反射当做一个小工具来读取或者操作元数据的,类,方法,...

  • Java的反射机制

    Class简介 反射之中所有的核心操作都是通过Class类对象来展开的, 可以说Class类对象是反射操作的根源所...

  • *反射机制2(Class类对象实例化)

    java.lang.Class类是所有反射操作的源头,即:所有的反射操作都要从此类开始进行,而最关键的是,这个类有...

  • java反射和动态代理

    java反射 反射是程序运行时,通过反射直接操作对象或者类。获取类声明的属性和方法,调用方法或者构造对象,设置修改...

  • 反射

    一、反射概述 (一)反射就是在运行时通过代码操作类。 想要操作类,就必须先获得该类的字节码对象.Class。通过C...

  • Python面向对象:反射(hasattr和getattr和se

    一、反射在类中的使用 反射就是通过字符串来操作类或者对象的属性 反射本质就是在使用内置函数,其中反射有以下四个内置...

  • 注解 - 反射,运行时动态获取注解信息

    反射 什么是反射? 反射:是一种机制,利用该机制可以在程序运行过程中对类进行解剖并操作类中的方法,属性,构造方法等...

网友评论

    本文标题:反射与类操作

    本文链接:https://www.haomeiwen.com/subject/orcxdctx.html