Java高级编程

反射与类操作

2019-07-31  本文已影响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

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

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类中提供了如下的操作可以获取对象的方法:

范例:获取全部方法

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类完成,在这个类中提供了以下几个方法获取成员属性:

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类首先就需要确认一下这个类中定义的构造方法和常量问题:

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的管理,所以不是必须的情况下不建议使用。

上一篇 下一篇

猜你喜欢

热点阅读