Java反射及其应用

定义

大家都知道,要让Java程序能够运行,那么就得让Java类要被Java虚拟机加载。Java类如果不被Java虚拟机加载,是不能正常运行的。现在我们运行的所有的程序都是在编译期的时候就已经知道了你所需要的那个类的已经被加载了。

Java的反射机制是在编译并不确定是哪个类被加载了,而是在程序运行的时候才加载、探知、自审。使用在编译期并不知道的类,这样的特点就是反射

作用

Java的反射机制可以知道类的基本结构,这种对Java类结构探知的能力,我们称为Java类的“自审”。

大家都用过IDEA和eclipse。当我们构建出一个对象的时候,去调用该对象的方法和属性的时候,一按点(智能提示),编译工具就会自动的把该对象能够使用的所有的方法和属性全部都列出来,供用户进行选择。这就是利用了Java反射的原理,是对我们创建对象的探知、自审。

解释

反射之中包含了一个“反”的概念,所以要想解释反射就必须先从“正”开始解释,一般而言,当用户使用一个类的时候,应该先知道这个类,而后通过这个类产生实例化对象,但是“反”指的是通过对象找到类。

基础Person类:

package com.xm.reflect;

/**
 * Created by xuming on 2016/7/21.
 */
public class Person {
    private Long id;

    private String name;

    private int age;

    public Person() {

    }

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

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

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    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;
    }

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

应用示例:

package com.xm.reflect;

public class BaseDemo {
    public static void main(String[] args) throws Exception {
        Person per = new Person(); // 正着操作
        System.out.println(per.getClass().getName()); // 反着来
    }
}

以上的代码使用了一个getClass()方法,而后就可以得到对象所在的“包.类”名称,这就属于“反”了,但是在这个“反”的操作之中有一个getClass()就作为发起一切反射操作的开端。

Person的父类是Object类,而上面所使用getClass()方法就是Object类之中所定义的方法。

取得Class对象:public final Class<?> getClass(),反射之中的所有泛型都定义为?,返回值都是Object。

取class对象的三种方法

  1. 通过Object类的getClass()方法取得,基本不用
  2. 使用“类.class”取得,在Hibernate开发的时候使用
  3. 使用Class类内部定义的一个static方法,主要使用

应用示例:

package com.xm.reflect;

/**
 * Created by xuming on 2016/7/21.
 */
public class ClassDemo {
    public static void main(String[] args) throws Exception {
        Person per = new Person(); // 正着操作
        Class<?> cls = per.getClass(); // 取得Class对象
        System.out.println(cls.getName() + ";toString:" + cls.toString()); // 反着来

        Class<?> clss = Person.class; // 取得Class对象
        System.out.println(clss.getName()); // 反着来

        Class<?> cls1 = Class.forName("com.xm.reflect.Person"); // 取得Class对象
        System.out.println(cls1.getName()); // 反着来

        Object obj = cls.newInstance(); // 实例化对象,和使用关键字new一样
        Person pers = (Person) obj; // 向下转型
        System.out.println(pers);

    }
}

结果:

上面用到了通过反射实例化对象:public T newInstance() throws InstantiationException, IllegalAccessException;,可以发现,对于对象的实例化操作,除了使用关键字new之外又多了一个反射机制操作,而且这个操作要比之前使用的new复杂一些,可是有什么用?

对于程序的开发模式之前一直强调:尽量减少耦合,而减少耦合的最好做法是使用接口,但是就算使用了接口也逃不出关键字new,所以实际上new是造成耦合的关键元凶

工厂模式问题造成的关键性的病因是new,那么如果说现在不使用关键字new了,变为了反射机制呢?

反射机制实例化对象的时候实际上只需要“包.类”就可以,于是根据此操作,修改工厂设计模式。

    package com.xm.reflect;

    /**
     * Created by xuming on 2016/7/21.
     */

    interface Fruit {
        void eat();
    }

    class Apple implements Fruit {
        public void eat() {
            System.out.println("吃苹果。");
        }

    }

    class Orange implements Fruit {
        public void eat() {
            System.out.println("吃橘子。");
        }

    }

    class Factory {
        public static Fruit getInstance(String className) {
            Fruit f = null;
            try {
                f = (Fruit) Class.forName(className).newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return f;
        }
    }

    public class FactoryDemo {
        public static void main(String[] args) {
            Fruit f = Factory.getInstance("com.xm.reflect.Orange");
            f.eat();
        }
    }

发现,这个时候即使增加了接口的子类,工厂类照样可以完成对象的实例化操作,这个才是真正的工厂类,可以应对于所有的变化。一些框架技术这个就是它实现的命脉,在日后的程序开发上,如果发现操作的过程之中需要传递了一个完整的“包.类”名称的时候几乎都是反射机制作用

调用构造及实例化

取得构造方法有两个,实例化对象还是newInstance:

  • 取得一个类的全部构造:
public Constructor<?>[] getConstructors() throws SecurityException
  • 取得一个类的指定参数构造:
public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
  • 实例化对象:
public T newInstance(Object... initargs) throws InstantiationException, IllegalAccessException,IllegalArgumentException, InvocationTargetException

应用示例:

package com.xm.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * Created by xuming on 2016/7/21.
 */
public class ConstructDemo {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class<?> cls = Class.forName("com.xm.reflect.Person"); // 取得Class对象
        Constructor<?> cons[] = cls.getConstructors(); // 取得全部构造
        for (int x = 0; x < cons.length; x++) {
            System.out.println(cons[x]);
        }

        Object obj = cls.newInstance(); // 实例化对象
        System.out.println(obj);

        Constructor<?> cons1 = cls.getConstructor(String.class,int.class) ;
        Object obj1 = cons1.newInstance("张三", 20); // 为构造方法传递参数
        System.out.println(obj1);

    }
}

结果:

调用普通方法

  • 取得全部方法:public Method[] getMethods() throws SecurityException;
  • 取得指定方法:public Method getMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException

发现以上的方法返回的都是java.lang.reflect.Method类的对象。

取得了Method类对象之后还有一个最大的功能,就是可以利用反射调用类中的方法:

  • 调用方法:public Object invoke(Object obj, Object... args)

之前调用类中方法的时候使用的都是“对象.方法”,但是现在有了反射之后,可以直接利用Object类调用指定子类的操作方法。

应用示例:

package com.xm.reflect;

import java.lang.reflect.Method;

/**
 * Created by xuming on 2016/7/21.
 */
public class MethodDemo {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("com.xm.reflect.Person"); // 取得Class对象
        Method met[] = cls.getMethods(); // 取得全部方法
        for (int x = 0; x < met.length; x++) {
            System.out.println(met[x]);
        }

        Object obj = cls.newInstance(); // 实例化对象,没有向Person转型
        String attribute = "name"; // 要调用类之中的属性
        Method setMet = cls.getMethod("set"+ initcap(attribute), String.class);// setName()
        Method getMet = cls.getMethod("get"+ initcap(attribute));// getName()
        setMet.invoke(obj, "张三") ; // 等价于:Person对象.setName("张三")
        System.out.println(getMet.invoke(obj));// 等价于:Person对象.getName()
    }
    public static String initcap(String str) {
        return str.substring(0,1).toUpperCase().concat(str.substring(1)) ;
    }

}

结果:

调用成员

  • 取得本类的全部成员:public Field[] getDeclaredFields() throws SecurityException
  • 取得指定的成员:public Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException
  • 设置属性内容(类似于:对象.属性= 内容):

    public void set(Object obj, Object value)

  • 取得属性内容(类似于:对象.属性):public Object get(Object obj) throws IllegalArgumentException, IllegalAccessException

可是从类的开发要求而言,一直都强调类之中的属性必须封装,所以现在调用之前要想办法解除封装。

  • 解除封装:public void setAccessible(boolean flag) throws SecurityException

应用示例:

Field nameField = cls.getDeclaredField("name") ; // 找到name属性
nameField.setAccessible(true) ; // 解除封装了
nameField.set(obj, "张三") ; // Person对象.name = "张三"
System.out.println(nameField.get(obj)); // Person对象.name

利用反射拷贝对象的应用实例

希望大家通过该例子进一步掌握反射的应用方法:

package com.xm.reflect;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * Created by xuming on 2016/7/21.
 */
public class ReflectDemo {
    /**
     * 通过反射拷贝对象
     *
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public Object copy(Object object) throws Exception {
        Class<?> classType = object.getClass();
        Object objectCopy = classType.getConstructor(new Class[]{}).newInstance(new Object[]{});
        //获取对象的所有成员变量
        Field[] fields = classType.getDeclaredFields();
        for (Field field : fields) {
            String name = field.getName();//获得成员变量的名称
            String firstLetter = name.substring(0, 1).toUpperCase();//将属性的首字母变为大写

            String getMethodName = "get" + firstLetter + name.substring(1);
            String setMethodeName = "set" + firstLetter + name.substring(1);

            Method getMethod = classType.getMethod(getMethodName, new Class[]{});
            Method setMethod = classType.getMethod(setMethodeName, new Class[]{field.getType()});

            Object value = getMethod.invoke(object, new Object[]{});
            setMethod.invoke(objectCopy, new Object[]{value});
        }
        return objectCopy;
    }

    public static void main(String[] args) throws Exception {
        Person p = new Person("lili", 23);
        p.setId(1L);
        ReflectDemo test = new ReflectDemo();
        Person person = (Person) test.copy(p);
        System.out.println(person);
        System.out.println("ID:" + person.getId() + ",name:" + person.getName() + ",age:" + person.getAge());
    }
}

理解:

1. 要想使用反射,首先需要获得待处理类或对象所对应的Class对象。
2. 获取某个类或某个对象所对应的Class对象的常用的3种方式:
     a) 使用Class类的静态方法forName:Class.forName(“java.lang.String”);
     b)使用类的.class语法:String.class;
     c) 使用对象的getClass()方法:String s = “aa”; Class<?> clazz = s.getClass();
3. 若想通过类的不带参数的构造方法来生成对象,我们有两种方式:
    a) 先获得Class对象,然后通过该Class对象的newInstance()方法直接生成即可:   Class<?> classType = String.class;   Object obj = classType.newInstance();
    b) 先获得Class对象,然后通过该对象获得对应的Constructor对象,再通过该Constructor对象的newInstance()方法生成: Class<?> classType = Customer.class;   Constructor cons = classType.getConstructor(new Class[]{});   Object obj = cons.newInstance(new Object[]{});
4. 若想通过类的带参数的构造方法生成对象,只能使用下面这一种方式:   Class<?> classType = Customer.class;   Constructor cons = classType.getConstructor(new Class[]{String.class, int.class});   Object obj = cons.newInstance(new Object[]{“hello”, 3});
5. Integer.TYPE返回的是int,而Integer.class返回的是Integer类所对应的Class对象。

结果:

以上具体代码见我的github

参考文献:

sql驱动的反射机制:https://segmentfault.com/q/1010000000315618

知乎中关于反射的讨论:https://www.zhihu.com/question/24304289

利用反射拷贝的方法:http://inotgaoshou.iteye.com/blog/849676

时间: 2024-11-09 00:30:13

Java反射及其应用的相关文章

Java反射

1. 介绍 反射是一种能够在程序运行时动态访问.修改某个类中任意属性和方法的机制. 具体:对于任意一个类,都能够知道这个类的所有属性和方法对于任意一个对象,都能够调用它的任意一个方法和属性 在运行时,当加载完类之后,JVM在堆内存中会自动产生一个Class类型的对象,这个对象包含了完整的类的结构信息 这个Class对象就像一面镜子,透过这个镜子看到类的结构 那么,如何得到这个Class对象呢?以下可否 Class c = new Class(); 答案是不行的,因为Class的构造函数定义为私有

Java 反射详解

反射反射,程序员的快乐,今天你快乐了吗?如果你不快乐,没关系,接下来让你快乐起来! 一.什么是反射? 通过百度百科我们可以知道,Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:并且能改变它的属性.而这也是Java被视为动态(或准动态,为啥要说是准动态,因为一般而言的动态语言定义是程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言.从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C

java反射机制(一)—— 利用反射机制实例化对象

一.Java有着一个非常突出的动态相关机制:Reflection,用在Java身上指的是我们可以于运行时加载.探知.使用编译期间完全未知的classes.换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体.或对其fields设值.或唤起其methods.(度娘文库是这么说的) 二.这篇文章主要介绍一下通过反射机制去实例化一个类的对象,然后调用其方法.本文主要介绍两种方式,第一种就是通过构造函数来实例化,第二种就是通过Cl

java 反射 详解

本文来自:blog.csdn.net/ljphhj JAVA反射机制:   通俗地说,反射机制就是可以把一个类,类的成员(函数,属性),当成一个对象来操作,希望读者能理解,也就是说,类,类的成员,我们在运行的时候还可以动态地去操作他们. 理论的东东太多也没用,下面我们看看实践 Demo - Demo: package cn.lee.demo; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import

【java】java反射机制,动态获取对象的属性和对应的参数值,并属性按照字典序排序,Field.setAccessible()方法的说明【可用于微信支付 签名生成】

方法1:通过get()方法获取属性值 package com.sxd.test.controller; public class FirstCa{ private Integer num; private String name; private Boolean flag; public Integer getNum() { return num; } public void setNum(Integer num) { this.num = num; } public String getNam

java 反射类的理解与应用

本文主要解析的类是: ClassLodaer,Class,Field,Method,Constructor. 本文的目标很简单,只是对这些常用的反射类进行简单解释.对这些类中常用方法进行介绍. JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法:这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制.Java反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类:在运行时构造任意一个类的对象:在

Java 反射机制

使用 Java 反射机制可以在运行时期检查 Java 类的信息,检查 Java 类的信息往往是你在使用 Java 反射机制的时候所做的第一件事情,通过获取类的信息你可以获取以下相关的内容: Class 对象 类名 修饰符 包信息 父类 实现的接口 构造器 方法 变量 注解 除了上述这些内容,还有很多的信息你可以通过反射机制获得,如果你想要知道全部的信息你可以查看相应的文档 JavaDoc for java.lang.Class 里面有详尽的描述. 在本节中我们会简短的涉及上述所提及的信息,上述的

Java 反射,开发框架必备技能

通过反射技术我们将上面的统一资源定位付(URL) 映射到Class 相当于 class: news method: list parameter: 2 差不多就是下面样子 class News{ public String list(String catagory_id){ ... ... } } 我们只需要在框架核心中分析 url 然后调用对应的方法下载,于此同时将参数传递过去. Class<?> cls = Class.forName("cn.netkiller.reflect.

Java反射与代理

Java反射机制与动态代理,使得Java更加强大,Spring核心概念IoC.AOP就是通过反射机制与动态代理实现的. 1       Java反射 示例: User user = new User(); user.setTime5Flag("test"); Class<?> cls = Class.forName("com.test.User"); //接口必须public,无论是否在本类内部使用!或者使用cls.getDeclaredMethod()

Java反射机制浅析

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制. "程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言".从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言.但是JAVA有着一个非常突出的动态相关机制:Reflection,用在Java身上指的是我们可以于运行时加载.探知.使用