Java 之 反射

时间:2023-03-09 18:19:27
Java 之 反射

1.反射

  a.意义:允许运行中的Java程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性

  b.概括:运行时探究和使用编译时未知的类

  c.反射的核心原理:

    ①JVM在加载一个类的时候,会把该类的信息存放到一个Class对象中。该对象又被称之为类模板对象。JVM可以通过检索该对象,

     得到这个类所拥有的各种信息(名字、所在包、父类、实现接口、属性、构造、行为......)

    ②JDK提供API,允许程序员获取到类的Class对象,导致程序员也可以检索到这个类的这些信息,即便这个类不是这个程序(或程序员)所实现的。

  d.三步骤:

    ①获取到Class对象

    ②探究类的信息

    ③使用探究到的信息

2.获取到Class对象

  a.通过类型名获取Class对象

Class stuClass = StudentBean.class;//类
Class strClass = String.class;
Class runnableClass = Runnable.class;//接口
Class intArrayClass = int[].class;//数组
Class intClass = int.class;//基本数据类型---JDK1.5以后才有的
Class intClass0 = Integer.TYPE;//JDK1.5之前使用对应包装类.TYPE获取基本类型的Class对象
Class voidClass = void.class;

  特点:①所有的类型都可以获得到Class对象

     ②因为在编写代码时已知类型名,所以没有动态性

  b.通过实例对象获取Class对象

StudentBean stu = new StudentBean();
Class stuClass1 = stu.getClass();
Class strClass1 = "hello".getClass();
int[] intArray = new int[5];
Class intArrayClass1 = intArray.getClass();

  特点:①只有非抽象类类型和数组类型可以通过该方式获取到Class对象

     ②抽象类、接口、基本数据类型、void都不可以

     ③因为在编写代码时已得到实例对象,所以也没有动态性

  c.通过类型名的字符串形式获取Class对象

String className = JOptionPane.showInputDialog("请输入你要加载的类的类名");
Class stuClass2 = Class.forName(className);

  特点:①类和接口可以通过该方式获取Class对象,细节:1、必须填写类全名(包含包名);2、先完成主动加载,然后返回Class对象

     ②这是唯一一种动态性的体现,以后会大量使用,表现在各种框架中书写配置文件

3.通过Class对象探究类的信息

  a.探究类的基本信息

String className = stuClass.getName();//得到类全名
String classSimpleName = stuClass.getSimpleName();//类的简单名
String packageName = stuClass.getPackage().getName();//类的包名
String superClassName = stuClass.getSuperclass().getName();//得到该类父类类名
Class[] allInterfaces = stuClass.getInterfaces();//得到该类实现的接口
String classMod = Modifier.toString(stuClass.getModifiers());//得到修饰符

  b.探究属性——Field

Field[] allFields = stuClass.getFields();//探究所有的公共属性(包括从父类继承而来的)
Field[] allDeclaredFields = stuClass.getDeclaredFields();//探究所有被声明的属性(不包括从父类继承而来的) Field theField = stuClass.getField("name");//探究指定的公共属性(包括从父类继承而来的)
Field theDeclaredField = stuClass.getDeclaredField("name");//探究指定的被声明的属性(不包括从父类继承而来的) String fieldName = field.getName();//属性名
String fieldMod = Modifier.toString(field.getModifiers());//修饰符
String fieldType = field.getType().getName();//类型名

  c.探究构造——Constructor(基本同上)

Class[] conParams = con.getParameterTypes();//形参类型
Constructor theDeclaredCon = stuClass.getDeclaredConstructor(String.class,int.class);//探究指定的被声明的构造

  d.探究行为——Method(基本同上)

String methRrturnParams = meth.getReturnType().getName();//返回类型
Class[] methThrow = meth.getExceptionTypes();//异常类型
Method theDeclaredCon = stuClass.getDeclaredMethod("study");//探究指定的被声明的方法

4.使用探究到的信息

  a.探究到Constructor,就可以产生实例对象

StudentBean theStu = null;
Constructor theDeclaredCon = stuClass.getDeclaredConstructor(String.class,int.class);
theStu = (StudentBean)theDeclaredCon.newInstance("张三",28);

  b.探究到Method,就可以调用方法

Method m = stuClass.getDeclaredMethod("study", int.class);
m.invoke(theStu, 5);

  c.探究到Field,就可以赋值/取值

Field f = stuClass.getDeclaredField("name");
f.setAccessible(true);
f.set(theStu, "王小二");
System.out.println(f.get(theStu));

5.在使用探究到的信息时,有一些细节:

  a.反射产生实例对象,是Java中我们学到的第三种产生对象的方式(new, 反序列化,反射)

  b.在反射产生实例对象时,可以直接调用Class对象的newInstance方法获得实例对象;

   但它只能调用公共无参构造!这就是JavaBean的第一规范的原因。

  c.私有属性在反射中操作,都是调用它公共的get和set方法。这是JavaBean第二规范的原因

  d.反射是Java的底层实现,其实是可以破坏封装性直接操作任意访问修饰符的构造/属性/方法的;

   但是这违背了OO的设计思想,不准用!

StudentBean stu0 = (StudentBean) stuClass.newInstance();