反射的概念及应用

一、什么是反射
反射总是围绕Class对象进行的,先来了解一下对象的加载过程。
1.一个.java的文件经过编译成功后,生成一个.class的文件

2.当我们执行了初始化操作(有可能是new、有可能是子类初始化 父类也一同被初始化、也有可能是反射…等),会将.class文件通过类加载器装载到jvm中

3.将.class文件加载器加载到jvm中,又分了好几个步骤,其中包括 加载、连接和初始化

4.其中在加载的时候,会在Java堆中创建一个java.lang.Class类的对象,这个Class对象代表着类相关的信息。

一切反射相关的代码都是从获取java.lang.Class类对象开始,Class对象包含了一个类的所有信息,各种与类相关的信息都可以在Class类里找到,并且可以对这个类的对象进行操作。

二、获取Class对象的途径

public static void main(String[] args) throws ClassNotFoundException {
        //1.通过类名.class获取
        Class clazz1=Person.class;
        System.out.println(clazz1);
        
        //2.通过Class类的静态方法过去
        Class clazz2=Class.forName("Person");
        System.out.println(clazz2);
        //3.通过Object类的getClass方法获取
        Person person=new Person();
        Class clazz3=person.getClass();
        System.out.println(clazz3);

        System.out.println(clazz1==clazz2);
        System.out.println(clazz2==clazz3);
    }
}

结果:

class Person
class Person
class Person
true
true

Process finished with exit code 0

三、通过反射来操作对象
1.通过反射获取构造器并创建对象

 Class clazz1=Person.class;
        System.out.println(clazz1);
            //获取此对象的构造器集合
            Constructor[] constructors=clazz1.getConstructors();
            for(Constructor constructor:constructors){
                System.out.println(constructor);
            }
            Constructor constructor1=clazz1.getConstructor();
            //获取默认构造器
            //前提示有无参构造器且为public
            Object o1=constructor1.newInstance();
            Person person=(Person)o1;
            //获取指定带参数构造器
            Constructor constructor2=clazz1.getConstructor(String.class,String.class);
            Object o2=constructor2.newInstance("张三","100");
            Person person1=(Person)o2;
            System.out.println(person1.getName()+" "+person1.getAge());

结果:

class Person
public Person(int)
public Person(java.lang.String,java.lang.String)
public Person()
张三 100

2.通过反射获取成员变量并操作

Class clazz1=Person.class;
        Person person=(Person)clazz1.newInstance();
        Field[] fields=clazz1.getFields();
        for(Field f:fields){
            System.out.println(f);//由于age是私有属性无法访问。只会显示共有属性
        }
        //获取Person类的共有属性
        Field field1=clazz1.getField("name");
        field1.set(person,"李四");
        System.out.println(person.getName());
        //如果需要获取Person类的私有属性需要暴力访问
        Field field2=clazz1.getDeclaredField("age");//获取私有成员变量
        field2.setAccessible(true);//暴力访问
        field2.set(person,"100");
        System.out.println(person.getAge());

结果:

public java.lang.String Person.name
李四
100

Process finished with exit code 0

3.反射获取方法并使用

 Class clazz1=Person.class;
        Person person=(Person)clazz1.newInstance();
        //获得Person类方法的集合,还包括从父类继承过来的方法
       Method[] methods=clazz1.getMethods();
       for(Method m: methods){
           System.out.println(m);
       }
       System.out.println("===============================================");
       //获取public的指定方法
       Method method1=clazz1.getMethod("setName", String.class);
       method1.invoke(person,"张三");
       System.out.println(person.getName());
    //获取Person类的私有方法并使用
       Method method2=clazz1.getDeclaredMethod("setAge", String.class);
       method2.setAccessible(true);//暴力访问
       method2.invoke(person,"100");
       System.out.println(person.getAge());

结果:

public java.lang.String Person.getName()
public void Person.setName(java.lang.String)
public java.lang.String Person.getAge()
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()
===============================================
张三
100

Process finished with exit code 0

4.利用反射向泛型加入其他类型参数
我们知道,我们再申明了泛型的类型之后,例如

ArrayList<String> list=new ArrayList<>();

此时listl里面只能添加String类型的参数,添加其他类型,例如Integer,编译器会报错,但是可以利用反射绕过泛型,直接调用ArrayList.class文件里的add方法,来添加其他的类型参数。
这是由于在源文件(.java)编译后,泛型中的类型参数会类型擦除,成为最原始类型,那就是所有类的祖宗类Object(泛型中没有申明界限的话是Object,申明边界的话就是被擦除成父类,例如,在.class文件中会以Animal存在),所以说在.class中是没有泛型存在的。那么我们可以在这一时期对泛型类做手脚

ArrayList<String> list=new ArrayList<>();
        list.add("张三");
        list.add("李四");
        Class<? extends ArrayList> clazz=list.getClass();
        Method add=clazz.getMethod("add",Object.class);
        System.out.println(list);
        System.out.println(add);
        add.invoke(list,100);
        System.out.println(list);

结果:

[张三, 李四]
public boolean java.util.ArrayList.add(java.lang.Object)
[张三, 李四, 100]

5.反射利用配置文件运行功能
先准备配置文件
在这里插入图片描述
文件里要以键=值得形式存在

public class Animal {
    public void eat(){
        System.out.println("动物都需要吃东西");
    }
}

public class Person{
public void talk(){
        System.out.println("人会说话");
    }
    }

在这里插入图片描述

  //使用输入流读取配置好的文件
        InputStream is=Main.class.getClassLoader().getResourceAsStream("config.test");
        //创建Properties对象
        Properties properties=new Properties();
        //将读取的加载Properties里,将配置文件里的键写入
        properties.load(is);
        is.close();
        String className=properties.getProperty("className");
        String methodName=properties.getProperty("methodName");
        String $className=properties.getProperty("$className");
        String $methodName=properties.getProperty("$methodName");
        //通过反射获取指定类的class文件对象
        Class<?> clazz1=Class.forName(className);
        Class<?> clazz2=Class.forName($className);
        //创建对象
        Object o1=clazz1.newInstance();
        //获取指定方法
        Method talk=clazz1.getMethod(methodName);
        talk.invoke(o1);

        System.out.println("===================================");
        Object o2=clazz2.newInstance();
        Method eat=clazz2.getMethod($methodName);
        eat.invoke(o2);

运行结果:

人会说话
===================================
动物都需要吃东西
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值