java中的反射

本文详细介绍了Java的反射机制,包括反射的作用、如何通过反射创建和操作对象,以及反射与动态语言的区别。同时,通过代码示例展示了使用反射与不使用反射的不同,探讨了Class类的理解,获取Class实例的四种方式,以及newInstance()和ClassLoader的使用。此外,还列举了反射在框架、动态代理等场景的应用及其优缺点。
摘要由CSDN通过智能技术生成
1.反射的介绍:
反射是被视为动态语言的关键,反射机制允许程序在执行期间借助Reflection ApI取得任何类相关的内部信息,并能直接任意操作对象的内部属性及方法
* 类加载完之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类
* 只有一个Class对象),这个对象包含了完整的类的结构信息。我们可以通过
* 这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构
* 所以我们形象的称之为:反射
* 正常方式:引入需要的包类名称-->通过new实例化-->取得实例化对象
* 反射方式:实例化对象-->getClass()方法-->得到完整的包类名名称
* 动态语言:运行时代码可以根据某些自身条件改变自身结构
* 主要动态语言:PHP python C# javaScript
* 静态语言:运行时结构不不可变的语言就是静态语言
* 主要静态语言:java c c++
*
* java不是动态语言,但java可以称为”准动态语言“。即java有一定的动态性,
* 我们可以利用反射机制,字节码操作获得类似动态语言的特性。java的动态性让编程的时候更加灵活

* 关于java.lang.Class类的理解:
* 1.类的加载过程:
* 程序经过java.exe命令以后,会生成一个或多个字节码文件(.class拓展名文件)。
* 接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中
* 此过程就叫做类的加载。加载到内存中的类,我们称为运行时类,此运行时类,就作为Class的一个实例
* 2.Class的实例就对应着一个运行时类
* 3.加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。

2.使用反射和没有使用反射的对比:

@Test
    public void test(){
        //反射之前,对Person的操作
        //1.创建Person对象
        Person p=new Person("cht",23);

        //2.通过对象,调用其内部的属性方法
        p.age=10;
        System.out.println(p.toString());
        p.show();

        //在person类外部,不可以通过Person类对象调用其内部私有结构
        //比如:name、showNation()以及私有构造器
    }
    //反射之后,对person的操作
    @Test
    public void test2() throws Exception{
        Class<Person> personClass = Person.class;
        //通过反射,创建Person类的对象
        Constructor<Person> constructor = personClass.getConstructor(String.class, int.class);
        Person hyj = constructor.newInstance("hyj", 22);
        System.out.println(personClass.toString());
        //2.通过反射,调用对象指定的属性,方法
        //调用属性
        Field a = personClass.getDeclaredField("age");
        a.set(hyj,18);
        System.out.println(hyj.toString());

        //调用方法
        Method shows = personClass.getDeclaredMethod("show");
        shows.invoke(hyj);
        System.out.println("***********************");

        //通过反射,可以调用Person类中的私有结构,比如私有属性,方法,构造器
        //调用私有构造器
        Constructor<Person> declaredConstructor = personClass.getDeclaredConstructor(String.class);
        declaredConstructor.setAccessible(true);
        Person jerry = declaredConstructor.newInstance("jerry");
        System.out.println(jerry);
        //调用私有的属性
        Field name = personClass.getDeclaredField("name");
        name.setAccessible(true);
        name.set(hyj,"漂亮妹妹");
        System.out.println(hyj);

        //调用私有方法
        Method showNation = personClass.getDeclaredMethod("showNation", String.class);
        showNation.setAccessible(true);
        showNation.invoke(hyj,"中国");//相当于hyj.showNation("中国")


    }

3.获取Class的实例的4种方式:

1.知道具体类的情况下可以使用:

2.通过对象实例instance.getClass()获取:

3.通过 Class.forName()传入类的路径获取通过 Class.forName()传入类的路径获取

4.通过类加载器xxxClassLoader.loadClass()传入类路径获取:

 //获取Class的实例的方式
    @Test
    public void test3() throws ClassNotFoundException {
        //方式一:调用运行时类的属性.class
        Class<Person> pc = Person.class;
        System.out.println(pc);
        //方式二:通过运行时类的对象,调用getClass()
        Person p1=new Person();
        Class pc1=p1.getClass();
        System.out.println(pc1);
        //方式三:调用Class的静态方法:forName(String classPath)
        Class pc2 = Class.forName("reflection.Person");
        System.out.println(pc2);
        System.out.println(pc==pc1);
        System.out.println(pc==pc2);
        //方式四:使用类加载器:ClassLoader
        ClassLoader classLoader = ReflectionTest.class.getClassLoader();
        Class<?> pc3 = classLoader.loadClass("reflection.Person");
        System.out.println(pc3);
        System.out.println(pc==pc3);

    }

Person类:


/**
 * @Author:阿土伯
 * @Date: 2022/2/15 22:54
 */
public class Person {
    private String name;
    public int age;
    public Person(){
        System.out.println("此时调用的是运行时类的空参构造器  且该空参构造器的权限必须为public");
    }

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

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

    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 +
                '}';
    }
    private String showNation(String nation){
        System.out.println("我的国籍是"+nation);
        return nation;
    }
    public void show(){
        System.out.println("我是一个闽南人");
    }
}

补充:只要数组的元素类型与维度一样,就是同一个Class

@Test
    public void test4(){
        Class c1=Object.class;
        Class c2=Comparable.class;
        Class c3=String[].class;
        Class c4=int[][].class;
        Class c5= ElementType.class;
        Class c6=Override.class;
        Class c7=int.class;
        Class c8=void.class;
        Class c9=Class.class;

        int[] a=new int[10];
        int[] b=new int[100];
        Class c10=a.getClass();
        Class c11=b.getClass();
        //只要数组的元素类型与维度一样,就是同一个Class
        System.out.println(c10==c11);
    }

4.newInstance的使用

 @Test
    public void test1() throws InstantiationException, IllegalAccessException {
        Class<Person> personClass=Person.class;
        /*
        newInstance():调用此方法,创建对应的运行时类,内部调用了运行时类的空参构造器

        想要此方法正常的创建运行时类的对象,要求:
        1.运行时类必须提供空参构造器
        2.空参的构造器访问权限得够,通常设置为public

        在javabean中要求提供一个public得空参构造器得原因:
        1.便于通过反射,创建运行时类得对象
        2.便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器
         */
        Person obj=personClass.newInstance();
        System.out.println(obj);

    }

5.ClassLoader的使用

@Test
    public void test1() throws Exception{
        Properties pros=new Properties();
        //此时的文件默认在当前的module下
        //读取配置文件的方式一:
//        FileInputStream fis=new FileInputStream("jdbc.properties");
//        FileInputStream fis=new FileInputStream("src\\jdbc1.properties");
//        pros.load(fis);


        //方式二:使用ClassLoader类加载器
        //配置文件默认识别为:当前module的src下
        ClassLoader classLoader=ClassLoaderTest.class.getClassLoader();
        InputStream is=classLoader.getResourceAsStream("jdbc1.properties");
        pros.load(is);


        String user=pros.getProperty("name");
        String password=pros.getProperty("password");
        System.out.println("user="+user+",password="+password);


    }

6.反射的应用场景

6.1框架中使用的动态代理依赖反射实现的

6.2当编译的时候还不确定实例化的对象时使用反射来动态获取

    //体现反射的动态性  框架的时候大量使用反射
    //编译的时候不确定要造哪一个类对象  后面通过反射来获取所要造的类对象
    @Test
    public void test2(){
        for(int i=0;i<100;i++){
            int num=new Random().nextInt(3);//0,1,2
            String classPath="";
            switch (num){
                case 0:
                    classPath="java.util.Date";
                    break;
                case 1:
                    classPath="java.lang.Object";
                    break;
                case 2:
                    classPath="reflection.Person";
                    break;
            }
            try {
                Object obj=getInstance(classPath);
                System.out.println(obj);
            } catch (Exception e) {
                e.printStackTrace();
            }

        }


    }

7.反射的优缺点:

优点 : 可以让咱们的代码更加灵活、为各种框架提供开箱即用的功能提供了便利

缺点 :让我们在运行时有了分析操作类的能力,这同样也增加了安全问题。比如可以无视泛型参数的安全检查(泛型参数的安全检查发生在编译时)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值