Java------Java中的反射机制

一、什么是反射机制

一般情况下,我们使用一个类时必定是知道这个类是用来干什么的,然后对它进行实例化,之后使用这个类的对象直接直接进行操作。

Student student = new Student();
  student.setName("张三");

这种我们可以理解为正,反射就是反着来。
Java反射机制是指在运行状态下,对于任意一个类我们都能够知道这个类的所有的属性和方法;对于任意一个对象都能够调用它的所有的方法和属性(包括私有的),这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

二、反射的原理

你的程序执行的时候jvm读取加载的是你的源代码生成的.class文件,而你类的属性和方法什么的都会包含在.class文件中
  所以jvm可以通过解析.class文件实现反射的那些功能

三、为什么要有反射

现在也只是一个初学者,没有做过实际的项目开发,也不是很理解反射机制的作用,但是看到了几个例子让我有点茅塞顿开的感觉,我也就贴上来吧

1. 操作数据库

比如链接jdbc 我们Class.forName(“com…”),不就是个驱动类么,为什么需要反射创建。
  因为Java本身只是提供的接口,具体的实现是由mysql,oracel,sql server这些数据库厂商实现的,如果你想要使用java连接到这些数据库,必然要使用这些厂商提供的驱动,厂商提供的驱动都是一些类,类是不会执行的,jvm要为他们生成对象。
  Class.forName(“com…”)这句话调用后会生成一个驱动的对象会被加载到jvm里,进而可以完成数据库的操作。

2. 方便代码的编写以及维护更新
  • 假如我们有两个程序员,一个程序员在写程序的时候,需要使用第二个程序员所写的类,但第二个程序员并没完成他所写的类。那么第一个程序员的代码能否通过编译呢?这是不能通过编译的。但是利用Java反射的机制,只要知道类名,就可以让第一个程序员在没有得到第二个程序员所写的类的时候,来完成自身代码的编译。
  • 如果你的项目成规模。要调用的对象很多或很多方法需要掉同一个对象,这时候你总不能一个个new吧。你可以直接通过反射 通过java类得名字和方法的名字就可以获得该方法的返回数据。
  • 比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编 译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如 这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能 的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。
3.使用框架

很少情况下是非用反射不可的。大多数情况下反射是为了提高程序的灵活性。
因此一般框架中使用较多。因为框架要适用更多的情况。对灵活性要求较高。

  • 黑客利用反射机制来绕过private保护机制。

四、反射的缺点

尽管反射非常强大,但也不能滥用。如果一个功能可以不用反射完成,那么最好就不用。
在我们使用反射技术时,下面几条内容应该牢记于心:

1. 性能第一

反射包括了一些动态类型,所以 JVM 无法对这些代码进行优化。因此,反射操作的效
率要比那些非反射操作低得多。我们应该避免在经常被 执行的代码或对性能要求很高的程
序中使用反射。

2. 安全限制

使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有
安全限制的环境中运行,如 Applet,那么这就是个问题了。

3. 内部暴露

由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方
法),所以使用反射可能会导致意料之外的副作用--代码有功能上的错误,降低可移植性。
反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。
搬运工—原博地址

五、反射机制的使用

1. 获取类的字节码文件对象

要使用反射机制首先我们得拿到所需要得该类的字节码文件,获取.class 文件对应的字节码对象Class主要有三种方式:
注意:一个类不同的实例对象所获取得到的字节码文件不是一个
1. 根据类名:类名.class

      Class  studentClass = Student.class;
        Class studentClass2 = Student.class;
        Class studentClass3 = Student.class;
        System.out.println(studentClass==studentClass2);//true
        System.out.println(studentClass2 == studentClass3);//true

2. 根据对象:对象.getClass()

  Student student = new Student();  //Student.class ----->Class
        student.setName("张三");
        //方式1 通过该方法,获取该类对应的字节码文件对象
        Class aClass = student.getClass();
        Class aClass2 = student.getClass();
        System.out.println(aClass == aClass2); //true

        Student student1 = new Student();
        Class aClass3 = student1.getClass();
        System.out.println(aClass2 == aClass3);//true

3. 根据全限定类名:Class.forName(全限定类名)

    //通过Class 类中的静态方法来获取一个类的字节码文件对象
        //一个类的全路径:包含包名demo3.Student
        //全路径:包名+类名
        Class aClass = Class.forName("demo3.Student");
        Class aClass2 = Class.forName("demo3.Student");
        System.out.println(aClass==aClass2);//true
  • 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
 Student student = new Student();
        Class  aClass = student.getClass();
        Class<Student> studentClass = Student.class;
        Class<?> aClass1 = Class.forName("demo3.Student");

        System.out.println(aClass==aClass1);
        System.out.println(studentClass==aClass);

当我们获取到一个类的字节码文件对象后,我们要就可以用这个字节码文件对象,去剖析这个类的构成
一个类的构成,有
构造方法 Constructor
成员变量 Field
成员方法 Method

获取构造方法

1. 获取所有的构造方法

  1. 获取所有的构造方法不包含私有的
    public Constructor<?>[] getConstructors ()
  2. 获取所有的构造方法 包括私有的
    public Constructor<?>[] getDeclaredConstructors ()

代码演示:

public class MyTest {
    public static void main(String[] args) throws ClassNotFoundException {
        Class aClass = Class.forName("demo4.Student");
  
        //获取所有构造方法
        //public Constructor<?>[] getConstructors () 获取所有的构造方法不包含私有的
        //public Constructor<?>[] getDeclaredConstructors () 获取所有的构造方法 包括私有的
        Constructor[] constructors = aClass.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }

        System.out.println("--------------------------");
 //获取所有构造方法包括私有
        Constructor[] declaredConstructors = aClass.getDeclaredConstructors();
        for (Constructor declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
        }
    }
}

2. 获得单个的构造方法
获取单个构造方法
获取单个的构造方法 不包含私有的:
public Constructor getConstructor (Class < ? >…parameterTypes)
获取单个的构造方法包含私有的:
public Constructor getDeclaredConstructor (Class < ? >…parameterTypes)
具体选择哪一个构造方法这个取决于你传递的参数,想当于方法重载;
代码演示:
Student类:

package demo4;
public class Student {
    public Student() {
        System.out.println("空参构造执行了");
    }

    public Student(String name) {
        System.out.println("一个参构造执行了" + name);
    }

    public Student(String name, int age) {
        System.out.println("两个个参构造执行了" + name + "==" + age);
    }

    private Student(String name, int age, char sex) {
        System.out.println("三个参构造执行了" + name + "==" + age + "===" + sex);
    }
}

非私有构造方法:

  Class aClass = Class.forName("demo4.Student");//通过全类名获得字节码文件对象
   //获取空参构造方法对象
        Constructor constructor = aClass.getConstructor();
        //Student student = new Student(); //以前创建对象的方式

        //通过反射创建出一个类的对象
        Object o = constructor.newInstance(); //通过反射方式创建对象的方式

        //选择一个参数的构造方法
   Constructor constructor = aClass.getConstructor(String.class);
   //传统方法
        //Student student = new Student("abc");
        //通过反射的方式,创建出该类的对象
        Object o1 = constructor.newInstance("bbb");
        //选择两个参数的构造方法
  Constructor constructor = aClass.getConstructor(String.class, int.class);

私有构造方法:

  • 当要获得私有的构造方法创建实例时,除了使用
    public Constructor getDeclaredConstructor (Class < ? >…parameterTypes),还需要取消语法检测;
    代码演示:
public class MyTest5 {
    public static void main(String[] args) throws Exception {
        //new String("wangwu",25,'男');
        Class aClass = Class.forName("demo4.Student");
       // Constructor constructor = aClass.getConstructor(String.class, int.class, char.class);
        //获取单个的私有的构造方法对象
        Constructor declaredConstructor = aClass.getDeclaredConstructor(String.class, int.class, char.class);
        declaredConstructor.setAccessible(true);//取消语法检测
        Object o = declaredConstructor.newInstance("zhaoliu", 26, '女');
    }
}


3. 使用反射获得通过空参构造创建一个类的两种方式

public class MyTest6 {
    public static void main(String[] args) throws Exception {
        //使用反射时通过空参构造创建一个类的对象,有两种方式
        Class aClass = Class.forName("org.westos.demo4.Student");
        //aClass.getConstructor();
        //方式1:通过构造方法对象里面的newInstance()方法来创建对象的
        Constructor constructor = aClass.getDeclaredConstructor();
        Object o = constructor.newInstance();

        //方式2通过 Class里面的newInstance()来创建对象
        Object o1 = aClass.newInstance();
        System.out.println(o==o1);//true


    }
}
获取属性

一通百通,属性还有成员方法都是与构造方法类似的
Student类

public class Student {
        //定义成员变量
        public String name;
        private int age;
        public char sex;
        private Student() {
        }
}

1. 获取所有的属性

  Class aClass = Class.forName("org.westos.demo.Student");
        //获取该类中所有的字段对象,私有字段获取不到
        Field[] fields = aClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        System.out.println("----------------------");
        //获取该类中所有的字段对象,包括私有的
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }

2. 获取单个的属性

      Class aClass = Class.forName("org.westos.demo.Student");
        //获取某个非私有的字段对象
        Field name = aClass.getField("name");
        System.out.println(name.getName());

        //获取私有的字段对象
        Field sex = aClass.getDeclaredField("sex");
        System.out.println(sex);

        以前给成员变量设置值
        //Student student = new Student();
        //student.name="zhangsan";
        Object o = aClass.newInstance();
       // Object o1 = aClass.getConstructor().newInstance();
        name.set(o,"zhangsan");
        Student student= (Student) o;
        System.out.println(student.name);

3. 对私有属性进行设值

 Class aClass = Class.forName("demo.Student");
        //通过反射,把该类的对象创建出来
        Constructor declaredConstructor = aClass.getDeclaredConstructor();
        declaredConstructor.setAccessible(true); //取消语法检测
        Object obj = declaredConstructor.newInstance();
        //给一个私有字段设置
        Field ageField = aClass.getDeclaredField("age");
        ageField.setAccessible(true); //取消语法检测
        ageField.set(obj,10);
        Student student= (Student) obj;
        //System.out.println(student.age);
        Object o = ageField.get(obj);
        Integer integer= (Integer) o;
        System.out.println(integer);
获取成员方法

Student类

public class Student {

public void test(){
    System.out.println("这是一个空参的方法");
}

public void test2(String name,int age) {
    System.out.println("这是一个有参的方法"+name+"==="+age);
}

public String show(String name, int age) {
    System.out.println("这是一个有参的方法" + name + "===" + age);

    return name+"哈哈";
}

private String show2(String name, int age) {
    System.out.println("这是一个有参的方法" + name + "===" + age);

    return name + "哈哈";
}

}


** 1. 获取所有的成员方法**

  Class aClass = Class.forName("org.westos.demo2.Student");
        Object obj = aClass.newInstance();

        //获取该类所有的成员方法对象,不包括私有的方法,包括父类继承下来方法
        Method[] methods = aClass.getMethods();
        for (Method method : methods) {
            System.out.println(method.getName());
        }

        System.out.println("-----------------------");
        //获取该类中所有的方法,包括私有的
        Method[] declaredMethods = aClass.getDeclaredMethods();
        for (Method m : declaredMethods) {
            System.out.println(m.getName());
        }

** 获取单个的成员方法**

 Class aClass = Class.forName("org.westos.demo2.Student");
        Object obj = aClass.newInstance();
        //获取单个的非私有方法的对象
        Method test = aClass.getMethod("test");
        System.out.println(test);

        以前要调用一个方法
        //Student student = new Student();
        //student.test();
        test.invoke(obj);

        System.out.println("--------------------");
        //获取一个有参数的方法对象
        Method test2 = aClass.getMethod("test2", String.class, int.class);
        test2.invoke(obj,"abc",100);

        //获取一个有参数,有返回值的方法对象,并调用该方法执行
        Method show = aClass.getMethod("show", String.class, int.class);
        Object result = show.invoke(obj, "张三", 19);
        String str= (String) result;
        System.out.println(str);

          //获取一个私有的方法对象
        Method show2 = aClass.getDeclaredMethod("show2", String.class, int.class);
        show2.setAccessible(true);//取消语法检测
        show2.invoke(obj, "王五", 25);

总结

第一感觉就是这货在Java中简直就是强大到一个Bug级别,上天无所不能,完全是以上帝视角的存在,后来看别人的文章,总结下来就是一句话:Java反射给我们带来极大灵活性的同时,极大的方便了我们的编程,而且反射堪称各大框架的基础。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值