Java学习笔记-Java反射的原理和作用

Java反射机制允许程序在运行时动态获取类的信息并调用其方法。它在框架实现、类加载与实例化等方面有广泛应用,如JDBC的driver加载。然而,反射可能导致性能下降、类型安全问题和内部暴露等缺点。在测试中,反射可以用于创建对象、访问构造器、方法和字段,甚至修改私有成员变量。
摘要由CSDN通过智能技术生成

Java编译和运行

Java程序在编译期间将.java文件编译成.class文件,然后在运行期间将.class文件添加到内存中。
在这里插入图片描述
在这里插入图片描述

原理

反射就是程序在运行状态,可以动态的获得类的信息并调用类的方法。
通过获得class对象,进而获得class对象所属类的属性和方法。

应用场景

一些框架使用反射实现
当我们编写程序时不知道使用的是哪个类,而是要根据运行时动态的判断是哪个类时,可以使用反射。
可以通过反射进行类的加载与实例化,这样,当类改变时,可直接通过配置文件修改类名,而不需要修改代码
比如:JDBC中的driver通过Class.forName()进行类的加载,这样当我的driver改变时,只需修改配置文件的driver名即可,不许修改代码。
为什么用class.forname

功能

得到一个对象所属的类
获得一个类的所有成员变量和方法
在运行时创建对象
在运行时调用对象的方法

缺点

  1. 反射调用比直接调用慢。我们能在每一版 JVM 的发布中看到关于反射 API 性能提升的改进,JIT 编译器的优化算法越来越好,但是反射方法的调用还是比直接调用要慢三倍以上。
  2. 类型安全,如果在代码中使用了方法引用,那么这只是对方法的引用而已。如果写了代码通过反射来调用方法并传错了参数,这个调用会在运行时失败,而并不是在编译或者加载时。
  3. 内部暴露,由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用--代码有功能上的错误,降低可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。
  4. 可追踪性 - 如果一个反射方法调用失败,找到导致问题的代码行可能会很棘手,因为 stack trace 通常很大。需要跟踪到很深地方查许多 invoke() 和 proxy() 调用。

代码测试

package reflect;

import java.lang.reflect.*;

class Test{
    private int num = 1;
    public int sum = 10;
    public Object t;
    public Test(){
        System.out.println("public Constructor");
    }
    private Test(int i){
        System.out.println("private Constructor");
    }
    public Test(char i){
        System.out.println("public Constructor");
    }
    private void method(){
        System.out.println("private method");
    }
    public int getNum(){
        System.out.println("public method getNum");
        return num;
    }
    public void getMethod(){
        System.out.println("public method getMethod");
    }
}

public class Reflect {
    public static void main(String[] args){
        try {
            //加载类
            System.out.println("*获得类对象*");
            Class<?> tClass = Class.forName("reflect.Test");
            System.out.println(tClass);

            Test test = new Test();
            Class<?> ttClass = test.getClass();
            System.out.println(ttClass);

            Class<?> tttClass = Test.class;
            System.out.println(tttClass);

            System.out.println(tClass==ttClass&&ttClass==tttClass);//true


            System.out.println("=====================");
            System.out.println("*获得构造器*");
            //getConstructor获得公共的构造函数
            Constructor<?> cs = tClass.getConstructor();
            Constructor<?> csH = tClass.getConstructor(char.class);
            System.out.println(cs);
            System.out.println(csH);
            csH.newInstance('1');

            System.out.println("==============================");
            //获得所有公共的构造函数
            Constructor<?>[] csAll = tClass.getConstructors();

            //getDeclaredConstructors获得所有构造函数,all是按照声明的逆序输出的
            Constructor<?>[] all = tClass.getDeclaredConstructors();
            for(Constructor<?> a:all){
                System.out.println(a);
            }

            System.out.println("==============================");
            System.out.println("*获得方法*");
            //getMethod("methodName")获得指定方法,public
            Method mt = tClass.getMethod("getNum");
            System.out.println(mt);

            //使用方法要先实例化对象
            Object ob = tClass.getConstructor().newInstance();
            mt.invoke(ob, null);
            //获得所有public 方法,Object类下的也会有,即可以访问从其他类继承来的方法,也是倒序
            Method[] mts = tClass.getMethods();
            for(Method m:mts){
                System.out.println(m);
            }

            System.out.println("========================");

            Method gdm = tClass.getDeclaredMethod("method");
            System.out.println(gdm);
            //getDeclaredMethods(),该类的所有方法,不包括从其他类继承来的,也是倒序
            Method[] mtds = tClass.getDeclaredMethods();
            for(Method m:mtds){
                System.out.println(m);
            }

            System.out.println("========================");
            System.out.println("*获得字段,即成员变量*");

            Field fd = tClass.getField("sum");
            System.out.println(fd);

            //获得所有公共成员变量,public,按定义顺序
            Field[] fds = tClass.getFields();
            for(Field f:fds){
                System.out.println(f.toString());
            }

            System.out.println("=======================");
            Field gfd = tClass.getDeclaredField("num");
            System.out.println(gfd);
            //获得所有成员变量,按声明顺序
            Field[] fields = tClass.getDeclaredFields();
            for(Field f:fields){
                System.out.println(f);
            }
            //可以通过get获得成员变量的值

            System.out.println("====================");
            System.out.println("*实例化*");

            Test tt = new Test();
            Class<?> tc = Class.forName("reflect.Test");
            Test tt2 = (Test)tc.getConstructor().newInstance();

            System.out.println("======================");
            System.out.println("通过AccessibleObject+Field修改私有成员变量");

            //获取单个私有成员变量的方式
            Field fdo = tt.getClass().getDeclaredField("num");
            System.out.println(fdo.isAccessible());
            fdo.setAccessible(true);
            System.out.println(fdo.get(tt));

            //获取所有私有成员变量的方式
            Field[] fldd = tt.getClass().getDeclaredFields();
//            System.out.println(fldd[0].get(tt));//这样获取不到
            AccessibleObject.setAccessible(fldd,true);
            System.out.println(fldd[0].get(tt));
            fldd[0].setInt(tt,10);
            System.out.println(fldd[0].get(tt));
            /**
             * 一般情况下,我们并不能对类的私有字段进行操作,利用反射也不例外,
             * 但有的时候,例如要序列化的时候,我们又必须有能力去处理这些字段,
             * \这时候,我们就需要调用AccessibleObject上的setAccessible()方法来允许这种访问,
             * 而由于反射类中的Field,Method和Constructor继承自AccessibleObject,
             * 因此,通过在这些类上调用setAccessible()方法,我们可以实现对这些字段的操作。
             * 但有的时候这将会成为一个安全隐患,
             * 为此,我们可以启用java.security.manager来判断程序是否具有调用setAccessible()的权限。
             * 默认情况下,内核API和扩展目录的代码具有该权限,而类路径或通过URLClassLoader加载的应用程序不拥有此权限。
             * 例如:当我们以这种方式来执行上述程序时将会抛出异常
             */

        }catch (ClassNotFoundException e){
            e.printStackTrace();
        }catch (NoSuchMethodException e){

        }catch (InstantiationException e){

        }catch (IllegalAccessException e){
            e.printStackTrace();

        }catch (InvocationTargetException e){

        }catch (NoSuchFieldException e){

        }catch (IllegalArgumentException e){
            e.printStackTrace();
        }
    }
}

输出
获得类对象
class reflect.Test
public Constructor
class reflect.Test
class reflect.Test
true
=====================
获得构造器
public reflect.Test()
public reflect.Test(char)
public Constructor
==============================
public reflect.Test(char)
private reflect.Test(int)
public reflect.Test()
==============================
获得方法
public int reflect.Test.getNum()
public Constructor
public method getNum
public void reflect.Test.getMethod()
public int reflect.Test.getNum()
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()
========================
private void reflect.Test.method()
public void reflect.Test.getMethod()
private void reflect.Test.method()
public int reflect.Test.getNum()
========================
获得字段,即成员变量
public int reflect.Test.sum
public int reflect.Test.sum
public java.lang.Object reflect.Test.t
=======================
private int reflect.Test.num
private int reflect.Test.num
public int reflect.Test.sum
public java.lang.Object reflect.Test.t
====================
实例化
public Constructor
public Constructor
======================
通过AccessibleObject+Field修改私有成员变量
false
1
1
10

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值