【Java基础】反射

首先来看一下什么是反射?

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键

通俗点就是反射能操作一个类所拥有的任何东西,类的字段、方法、构造器、注解等。

那么怎么使用反射呢?
要想使用反射技术,首先得拿到该类的Class对象,方法有如下3种。
以Dog类为例

public class Demo1 {

    public static void main(String[] args) throws Exception{

        Class clazz1 = Class.forName("com.sunrise.eHealth.day0505.Dog");
        Class clazz2 = Dog.class;
        Dog dog = new Dog();
        Class clazz3 = dog.getClass();
        System.out.println(clazz1);
        System.out.println(clazz2);
        System.out.println(clazz3);
    }

}

输出结果

class com.sunrise.eHealth.day0505.Dog
class com.sunrise.eHealth.day0505.Dog
class com.sunrise.eHealth.day0505.Dog

拿到了Class对象,下面我们就可以对Class对象中的内容进行操作了,首先看一下类的构造器
所谓万物皆对象,构造器本身也是一个Class,java中Constructor表示构造器
在这里插入图片描述
由java api可以看到有这么几个方法可以获取到构造器,实际上一共有5个方法可以获取构造器,这里就不一一列举了。

Constructor<Dog> dogConstructor = clazz2.getConstructor();

此方法是获取Dog类的默认的无参构造器,至于Constructor菱形里为何要加上Dog,请看【Java基础】泛型,这里就不再赘述了。
同理,类中的方法是用Method表示,字段用Field表示,注解用Annotation表示

还有Class对象的内部类,外部类,Class对象对应类所继承的父类、所实现的接口、返回类的修饰符、获取此类的包等等都可以在api中查到,如果想要什么对象去调用对应的方法即可。

下面看一个综合例子

//使用两个注解修饰该类
@SuppressWarnings(value = "unchecked")
@Deprecated
public class ClassTest {
    //为该类定义一个私有的构造器
    private ClassTest(){}
    //定义一个有参数的构造器
    public ClassTest(String name){
        System.out.println("执行有参数的构造器");
    }
    //定义一个无参数的info方法
    public void info(){
        System.out.println("执行无参数的info方法");
    }
    //定义一个有参数的info方法
    public void info(String str){
        System.out.println("执行有参数的info方法" + str);
    }

    //定义一个测试用的内部类
    class Inner{}

    public static void main(String[] args) throws Exception{
        //获取ClassTest对应的Class对象
        Class<ClassTest> clazz = ClassTest.class;
        //获取该Class对象所对应的全部构造器
        Constructor[] constructors = clazz.getDeclaredConstructors();
        System.out.println("ClassTest的全部构造器如下:");
        for (Constructor c : constructors){
            System.out.println(c);
        }
        //获取该Class对象所对应的全部public构造器
        Constructor[] publicCtors = clazz.getConstructors();
        System.out.println("ClassTest的全部public构造器如下:");
        for (Constructor c : publicCtors){
            System.out.println(c);
        }
        //获取该Class对象所对应的全部public方法
        Method[] methods = clazz.getMethods();
        System.out.println("ClassTest全部public方法如下:");
        for (Method m : methods){
            System.out.println(m);
        }
        //获取该Class对象所对应类的指定方法
        System.out.println("ClassTest里带一个字符串参数的的info方法为:" + clazz.getMethod("info", String.class));
        //获取该ClassTest对象所对应的全部注解
        Annotation[] annotations = clazz.getAnnotations();
        System.out.println("ClassTest的全部Annotation如下:");
        for (Annotation an : annotations){
            System.out.println(an);
        }
        System.out.println("该Class元素上的@SuppressWarnings注解为:" + clazz.getAnnotation(SuppressWarnings.class));
        //获取该Class对象所对应类的全部内部类
        Class[] inners = clazz.getDeclaredClasses();
        System.out.println("ClassTest的全部内部类如下:");
        for (Class c : inners){
            System.out.println(c);
        }
        //使用Class.forName()方法加载ClassTest的内部类
        Class innerClazz = Class.forName("com.sunrise.eHealth.day0505.ClassTest$Inner");
        System.out.println("通过getDeclaringClass() 访问该类所在的外部类为:" + innerClazz.getDeclaringClass());
        System.out.println("ClassTest的包为:" + clazz.getPackage());
        System.out.println("ClassTest的父类为:" + clazz.getSuperclass());


    }



}

输出结果为

ClassTest的全部构造器如下:
private com.sunrise.eHealth.day0505.ClassTest()
public com.sunrise.eHealth.day0505.ClassTest(java.lang.String)
ClassTest的全部public构造器如下:
public com.sunrise.eHealth.day0505.ClassTest(java.lang.String)
ClassTest全部public方法如下:
public static void com.sunrise.eHealth.day0505.ClassTest.main(java.lang.String[]) throws java.lang.Exception
public void com.sunrise.eHealth.day0505.ClassTest.info(java.lang.String)
public void com.sunrise.eHealth.day0505.ClassTest.info()
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
ClassTest里带一个字符串参数的的info方法为:public void com.sunrise.eHealth.day0505.ClassTest.info(java.lang.String)
ClassTest的全部Annotation如下:
@java.lang.Deprecated()
该Class元素上的@SuppressWarnings注解为:null
ClassTest的全部内部类如下:
class com.sunrise.eHealth.day0505.ClassTest$Inner
通过getDeclaringClass() 访问该类所在的外部类为:class com.sunrise.eHealth.day0505.ClassTest
ClassTest的包为:package com.sunrise.eHealth.day0505
ClassTest的父类为:class java.lang.Object

在上面的输出中可以看到该Class元素上的@SuppressWarnings注解为:null
这里@SupperssWarnings注解输出为什么为空呢?
在这里插入图片描述
我们可以看到@SuppressWarnings的保留期是在源码编译时期,所以运行时是拿不到的,后面我会再写一期关于注解了,这里就不作过多解释了

接下来看一下用反射是如何创建对象的
反射创建对象有2种方式

        //直接通过Class类的静态方法newInstance,实际调用的是默认的构造器
        Class clazz1 = Class.forName("com.sunrise.eHealth.day0505.Dog");
        Dog dog1 = (Dog) clazz1.newInstance();

        //先拿到构造器,再创建对象
        Constructor constructor = clazz1.getConstructor(String.class);
        Dog dog2 = (Dog) constructor.newInstance("旺财");
        System.out.println(dog2.getName());

第二种可以拿到不同参数的构造器,第一种只能使用默认的无参构造器

那有人会问,通过反射创建有什么优点呢,同样是创建对象,我new一个不就好了,何必要整这么麻烦呢?

通过反射创建对象当然有它的好处了,Class.forName(String className)方法参数是字符串,那参数是字符串的话就很灵活了,我可以把类名配置在配置文件中,Spring采用的就是XML配置文件来管理对象
还有一点是如果构造器是private私有的,那么用new是没办法创建对象的
反射则可以拿到私有的构造器然后提高权限则可以创建对象,所以单例模式可以通过反射去破坏

        Constructor constructor1 = clazz1.getDeclaredConstructor(String.class, Integer.class);
        //设置权限允许访问
        constructor1.setAccessible(true);
        Dog dog3 = (Dog) constructor1.newInstance("小强", 2);

Method方法示例

        //拿到方法Method对象
        Method method = clazz1.getMethod("setAge", Integer.class);
        Dog obj = (Dog) clazz1.newInstance();
        method.invoke(obj, 3);
        System.out.println(obj.getAge());

Field方法示例

        //拿到private修饰的name字段
        Field name = clazz1.getDeclaredField("name");
        //设置允许访问
        name.setAccessible(true);
        Dog dog = (Dog) clazz1.newInstance();
        name.set(dog, "哈士奇");
        System.out.println(dog.getName());

通过这种方法可以动态地给对象属性赋值,Spring框架就是通过这种将Field值以及依赖对象都放在配置文件中进行管理的,从而实现了较好的解耦。这也是Spring框架的IoC的原理。

以上就是反射的基础内容,掌握反射对框架的理解是很有帮助的
所以大家都应该要熟练掌握这些基本知识
只有打牢基础,才能造更高的楼不是吗
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值