小怪兽 Java反射技术 等你来打

Java反射技术,是java的难点,也是程序员进化过程中的必打小怪兽,这里就根据java api好好研究一下java的反射技术。

Class Fields Methods

请先回忆一下【java类的定义】。

一、反射是什么

反射是什么?百度百科这样定义:JAVA反射机制是在运行状态下,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取对象信息以及动态调用对象方法的功能称为java语言的反射机制。
可以这样理解,大家小时候有木有玩过平面镜,在阳光下,倾斜平面镜把阳光照到黑暗的地方,或对着某个人的眼睛?这就是反射。在java中,反射就是用一个照妖镜,把藏在java类中的看不到的妖魔鬼怪给找出来。因为类的封装机制,有些属性和方法,我们
无法查看和使用,就可以用反射机制动态调用。
二、反射做什么
1、在运行时判断任意一个对象所属的类;
2、在运行时构造任意一个类的对象;
3、在运行时判断任意一个类所具有的成员变量和方法;
4、在运行时调用任意一个对象的方法;
5、生成动态代理。
简单的说就是,我们想使用在api中没有提供接口的类的方法。再简单点就是,类A有一个方法a,但是在api中没有提供,我们想使用,就可以用反射技术调用;或则类A有一个属性b,我们想使用,也可以用反射实现。另一个作用就是,让我们可以了解整个类的结构。
三、反射知识点
1、Class类的实例化
在Java中,每个class都有一个相应的Class对象。也就是说,当我们编写一个类,编译完成后,在生成的.class文件中,就会产生一个Class对象,用于表示这个类的类型信息。
获取Class类对象的三种方法:
a、object.getClass
   例:String str = "I love you";
       Class c = str.getClass();

b、Class.forName("className")

   例:String className = "java.lang.String";
       Class c = Class.forName(className);

c、Object.class
   例:Class c = String.class;
d、c.getSuperclass();
   例:Class c = String.class;
       Class s = c.getSupreclass();
2、Class类的属性和方法
若只是想快速使用java反射机制,请自觉跳过本节,直接到第3节看实例。
a、方法摘要 (这是从Java api文档中摘抄过来的所有方法,若感觉繁琐就跳过本节,直接看我总结的常用方法(见b))
<U> Class<? extends U>
asSubclass(Class<U> clazz)
          强制转换该 Class 对象,以表示指定的 class 对象所表示的类的一个子类。
T cast(Object obj)
          将一个对象强制转换成此 Class 对象所表示的类或接口。
boolean desiredAssertionStatus()
          如果要在调用此方法时,将要初始化该类,则返回将分配给该类的断言状态。
static Class<?> forName(String className)
          返回与带有给定字符串名的类或接口相关联的 Class 对象。
static Class<?> forName(String name, boolean initialize, ClassLoader loader)
          使用给定的类加载器,返回与带有给定字符串名的类或接口相关联的 Class 对象。
<A extends Annotation> A
getAnnotation(Class<A> annotationClass)
          如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null。
Annotation[] getAnnotations()
          返回此元素上存在的所有注释。
String getCanonicalName()
          返回《Java Language Specification》中所定义的基础类的规范化名称。
Class[] getClasses()
          返回一个包含某些 Class 对象的数组,这些对象表示属于此 Class 对象所表示的类的成员的所有公共类和接口,包括从超类和公共类继承的以及通过该类声明的公共类和接口成员。
ClassLoader getClassLoader()
          返回该类的类加载器。
Class<?> getComponentType()
          返回表示数组组件类型的 Class。
Constructor<T> getConstructor(Class... parameterTypes)
          返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。
Constructor[] getConstructors()
          返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。
Annotation[] getDeclaredAnnotations()
          返回直接存在于此元素上的所有注释。
Class[] getDeclaredClasses()
          返回 Class 对象的一个数组,这些对象反映声明为此 Class 对象所表示的类的成员的所有类和接口,包括该类所声明的公共、保护、默认(包)访问及私有类和接口,但不包括继承的类和接口。
Constructor<T> getDeclaredConstructor(Class... parameterTypes)
          返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。
Constructor[] getDeclaredConstructors()
          返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。
Field getDeclaredField(String name)
          返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。
Field[] getDeclaredFields()
          返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段,包括公共、保护、默认(包)访问和私有字段,但不包括继承的字段。
Method getDeclaredMethod(String name, Class... parameterTypes)
          返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。
Method[] getDeclaredMethods()
          返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
Class<?> getDeclaringClass()
          如果此 Class 对象所表示的类或接口是另一个类的成员,则返回的 Class 对象表示该对象的声明类。
Class<?> getEnclosingClass()
          返回基础类的立即封闭类。
Constructor<?> getEnclosingConstructor()
          如果该 Class 对象表示构造方法中的一个本地或匿名类,则返回 Constructor 对象,它表示基础类的立即封闭构造方法。
Method getEnclosingMethod()
          如果此 Class 对象表示某一方法中的一个本地或匿名类,则返回 Method 对象,它表示基础类的立即封闭方法。
T[] getEnumConstants()
          如果此 Class 对象不表示枚举类型,则返回枚举类的元素或 null。
Field getField(String name)
          返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。
Field[] getFields()
          返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。
Type[] getGenericInterfaces()
          返回表示某些接口的 Type,这些接口由此对象所表示的类或接口直接实现。
Type getGenericSuperclass()
          返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的直接超类的 Type。
Class[] getInterfaces()
          确定此对象所表示的类或接口实现的接口。
Method getMethod(String name, Class... parameterTypes)
          返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
Method[] getMethods()
          返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。
int getModifiers()
          返回此类或接口以整数编码的 Java 语言修饰符。
String getName()
          以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。
Package getPackage()
          获取此类的包。
ProtectionDomain getProtectionDomain()
          返回该类的 ProtectionDomain。
URL getResource(String name)
          查找带有给定名称的资源。
InputStream getResourceAsStream(String name)
          查找具有给定名称的资源。
Object[] getSigners()
          获取此类的标记。
String getSimpleName()
          返回源代码中给出的基础类的简称。
Class<? super T> getSuperclass()
          返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class。
TypeVariable<Class<T>>[] getTypeParameters()
          按声明顺序返回 TypeVariable 对象的一个数组,这些对象表示用此 GenericDeclaration 对象所表示的常规声明来声明的类型变量。
boolean isAnnotation()
          如果此 Class 对象表示一个注释类型则返回 true。
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
          如果指定类型的注释存在于此元素上,则返回 true,否则返回 false。
boolean isAnonymousClass()
          当且仅当基础类是匿名类时返回 true。
boolean isArray()
          判定此 Class 对象是否表示一个数组类。
boolean isAssignableFrom(Class<?> cls)
          判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口。
boolean isEnum()
          当且仅当该类声明为源代码中的枚举时返回 true。
boolean isInstance(Object obj)
          判定指定的 Object 是否与此 Class 所表示的对象赋值兼容。
boolean isInterface()
          判定指定的 Class 对象是否表示一个接口。
boolean isLocalClass()
          当且仅当基础类是本地类时返回 true。
boolean isMemberClass()
          当且仅当基础类是成员类时返回 true。
boolean isPrimitive()
          判定指定的 Class 对象是否表示一个基本类型。
boolean isSynthetic()
          如果此类是复合类,则返回 true,否则 false。
T newInstance()
          创建此 Class 对象所表示的类的一个新实例。
String toString()
          将对象转换为字符串。
b、常用方法
public String getName()
以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。

public ClassLoader getClassLoader()
返回该类的类加载器。有些实现可能使用 null 来表示引导类加载器。如果该类由引导类加载器加载,则此方法在这类实现中将返回 null。

public Package getPackage()
获取此类的包。此类的类加载器用于查找该包。如果该类是通过引导类加载器加载的,则搜索从 CLASSPATH 加载的包的集合,以查找该类的包。如果所有包对象都不是用该类的类加载器加载的,则返回 null。

public Field[] getFields() throws SecurityException
返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。返回数组中的元素没有排序,也没有任何特定的顺序。如果类或接口没有可访问的公共字段,或者表示一个数组类、一个基本类型或 void,则此方法返回长度为 0 的数组。
特别地,如果该 Class 对象表示一个类,则此方法返回该类及其所有超类的公共字段。如果该 Class 对象表示一个接口,则此方法返回该接口及其所有超接口的公共字段。
该方法不反映数组类的隐式长度字段。用户代码应使用 Array 类的方法来操作数组。

public Method[] getMethods() throws SecurityException
返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。数组类返回从 Object 类继承的所有(公共)member 方法。返回数组中的元素没有排序,也没有任何特定的顺序。如果此 Class 对象表示没有公共成员方法的类或接口,或者表示一个基本类型或 void,则此方法返回长度为 0 的数组。
类初始化方法 <clinit> 不包含在返回的数组中。如果类声明了带有相同参数类型的多个公共成员方法,则它们都会包含在返回的数组中。

public Field getField(String name) throws NoSuchFieldException, SecurityException
返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。name 参数是一个 String,用于指定所需字段的简称。

public Method getMethod(String name, Class... parameterTypes) throws NoSuchMethodException, SecurityException
返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。name 参数是一个 String,用于指定所需方法的简称。parameterTypes 参数是按声明顺序标识该方法形式参数类型的 Class 对象的一个数组。如果 parameterTypes 为 null,则按空数组处理。

public Field[] getDeclaredFields() throws SecurityException
返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段,包括公共、保护、默认(包)访问和私有字段,但不包括继承的字段。返回数组中的元素没有排序,也没有任何特定的顺序。如果该类或接口不声明任何字段,或者此 Class 对象表示一个基本类型、一个数组类或 void,则此方法返回一个长度为 0 的数组。

public Method[] getDeclaredMethods() throws SecurityException
返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。返回数组中的元素没有排序,也没有任何特定的顺序。如果该类或接口不声明任何方法,或者此 Class 对象表示一个基本类型、一个数组类或 void,则此方法返回一个长度为 0 的数组。类初始化方法 <clinit> 不包含在返回数组中。如果该类声明带有相同参数类型的多个公共成员方法,则它们都包含在返回的数组中。

3、Method类和Field类
a、Method
Method 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。
Method 允许在匹配要调用的实参与基础方法的形参时进行扩展转换;但如果要进行收缩转换,则会抛出 IllegalArgumentException。

public String getName()
以 String 形式返回此 Method 对象表示的方法名称。

public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
对带有指定参数的指定对象调用由此 Method 对象表示的基础方法。个别参数被自动解包,以便与基本形参相匹配,基本参数和引用参数都随需服从方法调用转换。
如果基础方法是静态的,那么可以忽略指定的 obj 参数。该参数可以为 null。
如果基础方法所需的形参数为 0,则所提供的 args 数组长度可以为 0 或 null。
如果基础方法是实例方法,则使用动态方法查找来调用它,这一点记录在 Java Language Specification, Second Edition 的第 15.12.4.4 节中;在发生基于目标对象的运行时类型的重写时更应该这样做。
如果基础方法是静态的,并且尚未初始化声明此方法的类,则会将其初始化。
如果方法正常完成,则将该方法返回的值返回给调用方;如果该值为基本类型,则首先适当地将其包装在对象中。但是,如果该值的类型为一组基本类型,则数组元素不 被包装在对象中;换句话说,将返回基本类型的数组。如果基础方法返回类型为 void,则该调用返回 null。

b、Field
Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。
Array 允许在执行 get 或 set 访问操作期间进行扩展转换,但如果将发生收缩转换,则抛出一个 IllegalArgumentException。

public String getName()
返回此 Field 对象表示的字段的名称。

Method类和Field类还有很多其他的方法,在这里就不叙述了,可以查看api文档。
是不是看的头疼?不要紧,这个只是了解,对于聪明的你,看一下下面的例子就完全明白java的反射机制是什么。

四、反射怎么用
//测试类
public class Programmer {
    private String name;
    private int age;
    private String technology;

    private String program(String s) {
        return this.name + "在使用" + this.technology + "编写" + s;
    }

    public Programmer() {

    }

    public Programmer(String name, int age, String technology) {
        super();
        this.name = name;
        this.age = age;
        this.technology = technology;
    }

    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 String getTechnology() {
        return technology;
    }

    public void setTechnology(String technology) {
        this.technology = technology;
    }
}

//测试
public static void main(String[] args) throws NoSuchMethodException,
            SecurityException, IllegalAccessException,
            IllegalArgumentException, InvocationTargetException,
            NoSuchFieldException {
        // TODO Auto-generated method stub
        Programmer programmer = new Programmer("zhaicaixiansheng", 23, "java");
        Class c = programmer.getClass();//取得Class对象实例
        Method method = c.getDeclaredMethod("program",
                new Class[] { String.class });//第一个参数是方法名,第二个是所需参数类型的class。得到Programmer所定义的方法(任何方法都可以)
        if (method != null) {
            method.setAccessible(true);//设置为true
            String info = (String) method.invoke(programmer,
                    new String[] { "奔跑吧兄弟" });//使用方法,第一个参数是对象名,第二个是参数
            System.out.println(info);
        }

        Field field = c.getDeclaredField("name");
        if (field != null) {
            field.setAccessible(true);
            field.set(programmer, "摘菜先生");
        }

        String info = (String) method.invoke(programmer,
                new String[] { "奔跑吧兄弟" });
        System.out.println(info);
    }

五、反射优缺点
优点:增加程序的灵活性;动态调用;丰富程序接口等。
缺点:破坏程序的封装特性;使用麻烦;
六、注意和总结
注意:虽然反射技术可以增加程序的灵活性,可以动态调用类中看不见的方法和属性,但是,不能过于依赖反射,这样会造成,程序结构混乱,代码可读性下降,安全性差等。
总结:编程其实很容易,哪里不会搜哪里。
I love programming. I love you.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柠檬李先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值