java反射机制

参考博客:https://blog.csdn.net/sinat_38259539/article/details/71799078

一、反射的概述

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法(指的是所有,任何的私有,静态,等等……都可以被获取到);对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象。
以上的总结就是什么是反射
反射就是把java类中的各种成分映射成一个个的Java对象——很重要,后面的事情都是根据这个目标来进行的。
例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把一个个组成部分映射成一个个对象
     (其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述)
如图是类的正常加载过程:反射的原理在与class对象。
熟悉一下加载的时候:Class对象的由来是jvm虚拟机通过去本地磁盘找到相应从class文件,将class文件读入内存中并为之创建一个Class对象。
如图:

二、下面,我们先来介绍一下Class类(基于1.7的API)

 

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。也就是jvm中有N多的实例每个类都有该Class对象(包括基本数据类型)

Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。

Class类中有64个方法,具体用到哪个再详细介绍哪个。

 

三.获取一个类的Class对象的3种方法

  3.1 Object类下的getClas()方法

    3.2任何的数据类型(包括基本数据类型)都有一个“静态的”calss属性

    3.3通过Class类的静态方法:  Class.forName(String className)  //className是全路径名——包名+类名,但是并不是磁盘的绝对路径 (常用)

  public void f1() throws ClassNotFoundException {
        //获取class对象
        /*
        * 1.通过对象来获取字节码对象
        *   缺点:我对象都有了。。。还要字节码对象做啥子*/
        Student student=new Student();  //这样一创建 其实创建了2个对象  student对象 以及Student类对应的class对象
        Class stuClass1=student.getClass();
        /*
        * 2.通过每个类型都有的“静态的”class属性来获取
        *       缺点:依赖性太强,得导入相应的类的包;否则编译不过*/
        Class stuClass2=Student.class;
        /*
        * 常用,需要掌握  
        * Class forName(String className) 注意:全限类名*/
        Class stuClass3=Class.forName("com.kylin.FanShe.Student");

        System.out.println(stuClass1==stuClass2);
        System.out.println(stuClass1==stuClass3);
        System.out.println(stuClass2==stuClass3);

        //结果3个都是true,说明——一个类只有一个class对象
    }

四,通过class对象获取一个类的构造方法以及通过构造方法创建对象

  通过calss对象来获取构造器,主要分获取一批构造器和获取单个构造器;    
    1.获取一批构造器      
      1.1获取所有 “共有的”构造器        
        public Constructor[] getConstructors();所有"公有的"构造方法;这个Constructor是 反射包里的 java.lang.reflect.Constructor
      1.2获取所有的(任何访问类型--包访问/共有/保护/私有)的构造器
         public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
      2.获取一个构造器(如果是无参构造器,那么参数写null或者不写
   2.1获取单个 共有的构造器
      public Constructor getConstructor(Class ... paramterTypes) ;//获取单个的“公有的”构造方法(参数是构造器参数的类型;注意是构造器参数的类型!
     2.2.获取单个任何类型的构造器
      public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有
   3.通过构造方法对象来创建对象
      Object newInstance(构造器的参数) //构造方法的一个方法,用于创建对象,返回Object类型 需要自己手动的类型转换一下

Student.java
package com.kylin.FanShe;

public class Student {
    private String name;  //姓名
    private String sex;  //性别
    private String id;   //学号
    private int age; //年龄

    //4种访问类型的构造器   包访问类型  共有 保护 私有

    Student(){
        System.out.println("包访问类型的构造器");
    }
    //共有
    public Student(String name){
        this.name=name;
        System.out.println("共有访问类型的构造器,且名字为:"+name);
    }

    public Student(String id, int age){
        this.id=id;
        this.age=age;
        System.out.println("共有的构造器,id,年龄"+id+" "+age);
    }
    //保护
    protected Student(String name,String sex){
        this.name=name;
        this.sex=sex;
        System.out.println("保护类型的构造器,且名字为:"+name+"性别:"+sex);
    }
    //私有
    private Student(String name,String id,String sex){
        this.name=name;
        this.id=id;
        this.sex=sex;
        System.out.println("私有的构造器:"+name+" "+id+" "+sex);
    }



    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

获取构造方法代码: (一开始引入了:import java.lang.reflect.Constructor)
  public static void main(String []args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

//        获取class对象
        Class clazz=Class.forName("com.kylin.FanShe.Student");
        //1.1获取所有的公有的构造器
        Constructor []constructors=clazz.getConstructors();
        for (Constructor constructor:constructors){
            System.out.println(constructor);
        }
          /*
            * 结果:
            *   public com.kylin.FanShe.Student(java.lang.String,int)
                public com.kylin.FanShe.Student(java.lang.String)
            * */
        System.out.println("-----------------------------美丽的分割线-------------------------------");
        //1.2获取所有任何类型的构造方法
        Constructor []allConstructors=clazz.getDeclaredConstructors();
        for (Constructor constructor:allConstructors){
            System.out.println(constructor);
        }
        /*
        * 结果:
        *   private com.kylin.FanShe.Student(java.lang.String,java.lang.String,java.lang.String)
            protected com.kylin.FanShe.Student(java.lang.String,java.lang.String)
            public com.kylin.FanShe.Student(java.lang.String,int)
            public com.kylin.FanShe.Student(java.lang.String)
            com.kylin.FanShe.Student()*/

        System.out.println("-----------------------------美丽的分割线-------------------------------");

        //2.1获取某个公有的 构造器
        Constructor myPubConstructor=clazz.getConstructor(String.class);  //参数是:构造器参数的类型
        System.out.println(myPubConstructor);
//        输出:public com.kylin.FanShe.Student(java.lang.String)

        System.out.println("-----------------------------美丽的分割线-------------------------------");
        //2.2获取某个任何类型的构造器
        Constructor myEveConstructor=clazz.getDeclaredConstructor(String.class,String.class,String.class);
        System.out.println(myEveConstructor);
//        输出:private com.kylin.FanShe.Student(java.lang.String,java.lang.String,java.lang.String)

        //通过构造方法来创建对象
        Student student= (Student) myPubConstructor.newInstance("name");
        System.out.println(student.getName());


//          这样会抛出异常,因为一个私有的构造器不能被用来创建对象 所以需要设置访问权限
//        Student student1= (Student) myEveConstructor.newInstance("1","22","333");
//        System.out.println(student1.getName()+" "+student1.getId()+" "+student1.getSex());
        myEveConstructor.setAccessible(true);//暴力访问
        Student student1= (Student) myEveConstructor.newInstance("111","222","333");
        System.out.println(student1.getName()+" "+student1.getSex()+" "+student1.getId());

    }

  输出结果:

public com.kylin.FanShe.Student(java.lang.String,int)
public com.kylin.FanShe.Student(java.lang.String)
-----------------------------美丽的分割线-------------------------------
private com.kylin.FanShe.Student(java.lang.String,java.lang.String,java.lang.String)
protected com.kylin.FanShe.Student(java.lang.String,java.lang.String)
public com.kylin.FanShe.Student(java.lang.String,int)
public com.kylin.FanShe.Student(java.lang.String)
com.kylin.FanShe.Student()
-----------------------------美丽的分割线-------------------------------
public com.kylin.FanShe.Student(java.lang.String)
-----------------------------美丽的分割线-------------------------------
private com.kylin.FanShe.Student(java.lang.String,java.lang.String,java.lang.String)
共有访问类型的构造器,且名字为:name
name
私有的构造器:111 222 333
111 333 222

 五,通过class对象获取成员变量并调用

teacher.java

package com.kylin.FanShe;

public class Teacher {
    public String name;
    public String name1;
    String school;
    protected String sex;
    private int age;

    public Teacher(){

    }
    @Override
    public String toString(){
        return "Teacher[name="+name+",school=" +school+
                ",sex="+sex+",age="+age+",name1="+name1;
    }

}

 

测试获取成员变量并且调用

package com.kylin.FanShe;

import org.junit.Test;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

//获取成员变量
public class GetFiled {
    /*
    *  获取成员变量并调用:
     *
     * 1.批量的
     *         1).Field[] getFields():获取所有的"公有字段"
     *         2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;
     * 2.获取单个的:
     *         1).public Field getField(String fieldName):获取某个"公有的"字段;
     *         2).public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)
     *
     *      设置字段的值:
     *         Field --> public void set(Object obj,Object value):
     *                     参数说明:
     *                     1.obj:要设置的字段所在的对象;
     *                     2.value:要为字段设置的值*/

    public static void main(String []args) throws ClassNotFoundException, NoSuchFieldException, 
    NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//获取class对象 Class clazz=Class.forName("com.kylin.FanShe.Teacher"); //获取所有的共有的属性 Field []fields=clazz.getFields(); for (Field field:fields){ System.out.println(field); } System.out.println("-----------------------------美丽分割线-------------------------"); //获取所有的属性 (任何访问类型) Field []allFields=clazz.getDeclaredFields(); for (Field field:allFields){ System.out.println(field); //这也是一个对象来的 } System.out.println("-----------------------------美丽分割线-------------------------"); //获取指定的共有属性并且赋值给对象 Field nameField=clazz.getField("name"); System.out.println(nameField); Teacher teacher= (Teacher) clazz.getConstructor(null).newInstance(); nameField.set(teacher,"苏桃"); System.out.println(teacher); System.out.println("-----------------------------美丽分割线-------------------------"); //获取指定的任何类型(这里获取私有来测试)的属性 并且赋值给对象 Field priAgeField=clazz.getDeclaredField("age"); //女人的年龄是秘密嘛 System.out.println(priAgeField); //私有的需要设置权限 这里是暴力访问 priAgeField.setAccessible(true); priAgeField.set(teacher,20); System.out.println(teacher); } }

输出:

 

public java.lang.String com.kylin.FanShe.Teacher.name
public java.lang.String com.kylin.FanShe.Teacher.name1
-----------------------------美丽分割线-------------------------
public java.lang.String com.kylin.FanShe.Teacher.name
public java.lang.String com.kylin.FanShe.Teacher.name1
java.lang.String com.kylin.FanShe.Teacher.school
protected java.lang.String com.kylin.FanShe.Teacher.sex
private int com.kylin.FanShe.Teacher.age
-----------------------------美丽分割线-------------------------
public java.lang.String com.kylin.FanShe.Teacher.name
Teacher[name=苏桃,school=null,sex=null,age=0,name1=null
-----------------------------美丽分割线-------------------------
private int com.kylin.FanShe.Teacher.age
Teacher[name=苏桃,school=null,sex=null,age=20,name1=null

 六,使用class对象获取成员方法并且调用(调用肯定得是该类型的对象或者子类才可以调用)

people.java

package com.kylin.FanShe;

public class People {

    public void show1(String s){
        System.out.println("调用了:公有的,String参数的show1(): s = " + s);
    }
    protected void show2(){
        System.out.println("调用了:受保护的,无参的show2()");
    }
    void show3(){
        System.out.println("调用了:默认的,无参的show3()");
    }
    private String show4(int age) {
        System.out.println("调用了,私有的,并且有返回值的,int参数的show4(): age = " + age);
        return "abcd";
    }

    }

获取成员方法并且调用

/*
       获取成员方法并调用
                * 1.批量的:
               *         public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
                *         public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
     2.获取单个的:
               *         public Method getMethod(String name,Class<?>... parameterTypes):
              *                     参数:
                *                         name : 方法名;
             *                         Class ... : 形参的Class类型对象
            public Method getDeclaredMethod(String name,Class<?>... parameterTypes)

            *      调用方法:
               *         Method --> public Object invoke(Object obj,Object... args):
               *                     参数说明:
               *                     obj : 要调用方法的对象,这个对象一定要是和方法同一类/或者子类也应该可以的;
                *                     args:调用方式时所传递的实参;
               ):
                */

        public static void main(String[] args) throws Exception {
            //1.获取Class对象
            Class clazz = Class.forName("com.kylin.FanShe.People");
            //2.获取所有公有方法
            System.out.println("***************获取所有的”公有“方法*******************");
            Method[] methodArray = clazz.getMethods();
            for (Method m : methodArray) {
                System.out.println(m);
            }
            System.out.println("***************获取所有的方法,包括私有的*******************");
            methodArray = clazz.getDeclaredMethods();
            for (Method m : methodArray) {
                System.out.println(m);
            }
            System.out.println("***************获取公有的show1()方法*******************");
            Method pubMethod = clazz.getMethod("show1", String.class);
            System.out.println(pubMethod);
            //实例化一个Student对象
            People people= (People) clazz.getConstructor().newInstance();
            pubMethod.invoke(people,"刘德华");  //方法对象被调用执行,则输出了那句话
            System.out.println("***************获取私有的show4()方法******************");
            Method priMethod = clazz.getDeclaredMethod("show4", int.class);
            System.out.println(priMethod);
            priMethod.setAccessible(true);//解除私有限定
            System.out.println("11111111111111111111111");

            //抛出异常 可能是得该类的对象才可以调用
//            Object object=new Object();

            People people1= (People) clazz.getConstructor().newInstance();
            priMethod.invoke(people1, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
//            System.out.println("返回值:" + result);


        }

 总结:

  反射可以获取到一个类的所有的东西——静态的非静态的,私有的公有的……只要是在类中的,或者继承于父类的东西,都可以被获取成一个对象,然后进行调用……当然也可以获取main方法等等,这里主要介绍了通过类的字节码对象获取构造方法,成员变量,方法等……反射同时也是很多框架实现的基础,要好好琢磨……

posted @ 2018-08-30 11:54  Shan-KyLin  阅读(246)  评论(0编辑  收藏  举报