Java反射机制

Java反射机制 Java Reflection

reflection被视为动态语言的关键,反射机制允许程序在运行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

编译期是指把源码交给编译器编译成计算机可以执行的文件的过程。在 Java 中也就是把 Java 代码编成 class 文件的过程。编译期只是做了一些翻译功能,并没有把代码放在内存中运行起来,而只是把代码当成文本进行操作,比如检查错误。

运行期是把编译后的文件交给计算机执行,直到程序运行结束。所谓运行期就把在磁盘中的代码放到内存中执行起来。

类的加载过程

  • 反射机制提供的功能

    在运行时判断任意一个对象所属的类

    在运行时构造任意一个类的对象

    在运行时判断任意一个类所具有的成员变量和方法

    在运行时获取范型信息

    在运行时调用任意一个对象的成员变量和方法

    在运行时处理注解

    生成动态代理

反射机制👇

public class Person{
  private String name;
  public int age;
  //省略getter、setter方法
  public Person(String name, int age){
    this.name = name;
    this.age = age;
  }
  private Person(String name){
    this.name = name;
  }
  public Person(){
    
  }
  public void show(){
    System.out.println("I am a person.")
  }
  private String showNation(String nation){
    System.out.println("My nation is " + nation);
    retrun nation;
  }
}
//如果不使用反射机制,就只能调用上面public的对象和方法
//使用反射机制
public class ReflectionTest{
  public void test2() throws Exception {
    Class clazz = Person.class;//反射的源头 
    //Class的实例就对应着一个运行时类
    //通过反射,创建Person类的对象
    Constructor cons = clazz.getConstructor(String.class, int.class);    
    Object obj = cons.newInstance("Tom", 12);//newInstance()创建对应的运行时类的对象
    System.out.println(obj.toString());
    //or
    Person p = (Person)obj;
    System.out.println(p.toString());
    //通过反射,调用对象指定的属性、方法
    Field age = clazz.getDeclaredField("age");
    age.set(p, 10);
    System.out.println(p.toString());
    
    Method show = clazz.getDeclaredMethod("show");
    show.invoke(p);
    
    //通过反射,可以调用Person私有结构
    //私有构造器
    Constructor cons1 = clazz.getDeclaredConstructor(String.class);
    cons1.setAccessible(true);
    Person p1 = (Person)cons1.newInstance("Jerry");
    System.out.println(p1);
    //私有属性
    Field name = clazz.getDeclaredField("name");
    name.setAccessible(true);
    name.set(p1, "Lisa");
    System.out.println(p1);
    //私有方法
    Method showNation = clazz.getDeclaredMethod("showNation", String.class);
    showNation.setAccessible(true);
    String nation = showNation.invoke(p1, "China"); //相当于p1.showNation("China");
    System.out.println(nation);
  }
}
/*
 * 通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员;
 *
 * 1.获取构造方法:
 *         1).批量的方法:
 *             public Constructor[] getConstructors():所有"公有的"构造方法
            public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
     
 *         2).获取单个的方法,并调用:
 *             public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
 *             public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
 *         
 *             调用构造方法:
 *             Constructor-->newInstance(Object... initargs)
 */
/*
 * 获取成员变量并调用:
 *
 * 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:要为字段设置的值;
 *
 */
/*
 * 获取成员方法并调用:
 *
 * 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:调用方式时所传递的实参;
):
 */
//获取Class的实例的方式
public void test(){
  //方式一:调用运行时类的属性.class
  Class clazz1 = Person.class;
  //方式二:调用运行时类的对象getClass()
  Person p1 = new Person();
  Class clazz2 = p1.getClass();
  //方式三:调用Class的静态方法forName(String classPath) 这种方法用的多
  Class clazz3 = Class.forName("com.atguigu.jaca.Person");//路径
  
  //clazz1 clazz2 clazz3 clazz4指向的是同一个对象
    
  //方式四:使用类的加载器ClassLoader 了解即可
  ClassLoader classLoader = ReflectionTest.class.getClassLoader();
  Class clazz4 = classLoader.loadClass("com.atguigu.java.Person");
}
/*
在运行期间,一个类只有一个Class对象产生
*/
  • 通过new的方式或反射的方式都可以调用公共的结构,建议用直接new的方式

    什么时候用反射的方式:编译的时候确定不下来new哪个对象

    反射:动态性

  • 反射机制与面向对象中的封装性是不是矛盾的?

    封装性是建议你怎么调用的问题

    反射是能不能调用的问题

  • 在javabean中要求提供一个public的空参构造器,

    便于通过反射,创建运行时类的对象

    便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器

反射main方法

package fanshe.main;
public class Student{
  public static void main(String[] args){
    System.out.println("main方法执行了...");
  }
}
package fanshe.main;
import java.lang.reflect.Method;
/*
获取Student类的main方法,不是当前的main方法,不要搞混
*/
public class Main{
  public static void main(String[] args){
    try{
      //1.获取Student对象的字节码
      Class clazz = Class.forName("fanshe.main.Student");
      //2.获取main方法
      Method methodMain = clazz.getMethod("main", String[].class);//第一个参数:方法名称。第二个参数:方法形参的类型
      //3.调用main方法
      //methodMain.invoke(null, new String[]{"a","b","c"});
      //第一个参数:对象类型,因为方法是static的,所以可以为null。第二个参数new String[]{"a","b","c"}拆成三个对象,所以需要将它强转
      methodMain.invoke(null, (Object)new String[]{"a","b","c"});//method 1
      //methodMain.invoke(null, new Object[]{new String[]{"a","b","c"}});//method 2
    }catch(Exception e){
      e.printStackTrace();
    }
  }
}
posted @ 2021-01-18 11:56  GladysChloe  阅读(37)  评论(0)    收藏  举报