1.反射库(reflection library)提供了一个非常丰富且精心设计的工具集,以便编写能够动态操纵Java代码的程序。

  能够分析类能力的程序称为反射(reflection)。反射机制的功能极其强大,例如:

  • 在运行时分析类的能力
  • 在运行时查看对象
  • 实现通用的数组操作代码
  • 利用Method对象

  

  2.Class类

  在程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。虚拟机利用运行时类型信息选择相应的方法执行。然而,可以通过Java类访问这些信息,保存这些信息的类被称为Class。

  获得Class类的实例一共有三种方法:

  (1)Object类中的getClass方法将会返回一个Class类型的实例,一个Class类型的实例表示一个特定类的属性,最常用的Class方法时getName方法,这个方法将返回类的名字。

      Class class1 = alice1.getClass();
System.out.println(class1.getName());  // 打印:包名.Emplayee
Class class2 = carl.getClass();
System.out.println(class2.getName());  // 打印:包名.Manager

  (2)可以通过静态方法forName获得类名对应的Class对象:如果类名保存在字符串中,并想要在运行中改变,就可以使用这个方法。这个方法只能在className是类名或接口名时才能够执行。否则,将抛出一个checked exception异常。因此,使用这个方法的时候需要提供一个异常处理器。

      String className = "java.util.Random";
Class class3 = Class.forName(className);
System.out.println(class3.getName());  // 打印:java.util.Random

  实际应用时应该提供一个异常处理器:

      try
{
// print class name and superclass name (if != Object)
Class cl = Class.forName(name);
      do something with cl;
}
catch (Exception e)
{
e.printStackTrace();
}

  (3)如果T是任意的Java类型,则T.class将代表匹配的类对象。一个Class类对象实际上表示的是一个类型,而这个类型未必一定是一种类。int不是类,但int.class是一个Class类型的对象。

      Class cl1 = int.class;
System.out.println(cl1.getName());  // 打印:int
Class cl2 = Double.class;
System.out.println(cl2.getName());  // 打印:java.lang.Double

  其他Class方法:

  (1)比较两个类对象:

Employee e;
if(e.getClass() == Employee.class) ...;

  (2)动态地创建一个类的实例:newInstance方法调用类的默认的构造器,即没有参数的构造器初始化新创建的类对象。如果没有默认构造器,就抛出一个异常。

e.getClass().newInstance();

  (3)将forName与newInstance配合使用,就可以创建执行类名的类的一个实例

String className = "java.util.Random";
Object m = Class.forName(s).newInstance();

  3.利用反射分析类的能力

  反射机制最重要的就是检查类的结构。

  (1)打印含修饰符的类名和父类类名

         // print class name and superclass name (if != Object)
         Class cl = Class.forName(name);      
         System.out.println(cl.getName());                // 打印:java.lang.Double
         Class supercl = cl.getSuperclass();
         System.out.println(supercl.getName());             // 打印:java.lang.Number
// getModifiers()方法将返回一个整型数值,用不同的位开关描述public和static这样的修饰符使用情况。
// Modifier类还有isPublic、isPrivate等等用来判断方法或构造器是否是Public、Private等等
         System.out.println(cl.getModifiers());             // 打印:17
         String modifiers = Modifier.toString(cl.getModifiers());  // 打印:public final
         System.out.println(modifiers);
         if (modifiers.length() > 0) System.out.print(modifiers + " ");
         System.out.print("class " + name);
         if (supercl != null && supercl != Object.class) System.out.print(" extends "
               + supercl.getName()); 输入:String name = “java.lang.Double”
输出:public final class java.lang.Double extends java.lang.Number

  (2)打印类中所有的构造器名

   /**
* Prints all constructors of a class
* @param cl a class
*/
public static void printConstructors(Class cl)
{
Constructor[] constructors = cl.getDeclaredConstructors(); // 返回全部构造器,不包括由超类继承的
                                       // getConstructors()将返回类提供的构造器数组,包括超类的构造器
for (Constructor c : constructors)
{
String name = c.getName();
System.out.print(" ");
String modifiers = Modifier.toString(c.getModifiers());
if (modifiers.length() > 0) System.out.print(modifiers + " ");
System.out.print(name + "("); // print parameter types
Class[] paramTypes = c.getParameterTypes();
for (int j = 0; j < paramTypes.length; j++)
{
if (j > 0) System.out.print(", ");
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
} 输出:
   public java.lang.Double(double);
   public java.lang.Double(java.lang.String)

  (3)打印类中所有的方法名

   /**
* Prints all methods of a class
* @param cl a class
*/
public static void printMethods(Class cl)
{
Method[] methods = cl.getDeclaredMethods();   // 返回全部方法,不包括由超类继承的 for (Method m : methods)
{
Class retType = m.getReturnType();
String name = m.getName(); System.out.print(" ");
// print modifiers, return type and method name
String modifiers = Modifier.toString(m.getModifiers());
if (modifiers.length() > 0) System.out.print(modifiers + " ");
System.out.print(retType.getName() + " " + name + "("); // print parameter types
Class[] paramTypes = m.getParameterTypes();
for (int j = 0; j < paramTypes.length; j++)
{
if (j > 0) System.out.print(", ");
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
} 输出:
   public boolean equals(java.lang.Object);
   public static java.lang.String toString(double);
   public java.lang.String toString();
  ...

  (3)打印类中所有的域名

   /**
* Prints all fields of a class
* @param cl a class
*/
public static void printFields(Class cl)
{  
Field[] fields = cl.getDeclaredFields();    // 返回全部域,不包括由超类继承的 for (Field f : fields)
{
Class type = f.getType();                        // 域类型
String name = f.getName();                       // 域名
System.out.print(" ");
String modifiers = Modifier.toString(f.getModifiers());     // 修饰符
if (modifiers.length() > 0) System.out.print(modifiers + " ");
System.out.println(type.getName() + " " + name + ";");
}
} 输出:
   public static final double POSITIVE_INFINITY;
   public static final double NEGATIVE_INFINITY;
   public static final double NaN;
  ...
  private final double value;
   private static final long serialVersionUID

  4.在运行时使用反射分析对象

  在前面的代码中,已经得到了全部域的域名和类型,如何查看数据域的实际内容是分析对象的重要内容。

  一个例子如下,还是以Employee类为例,Employee类有三个域,它们都是私有域

public class Employee
{
private String name;
private double salary;
private LocalDate hireDay;
...
}

  使用getDeclaredField("name");带参数的方法可以得到Field类型的域name域对象f,然后通过f.get(obj)方法,其中obj是某个包含f域的类的对象,就可以返回一个对象,这个对象就是obj的f域。但是,这个时候会抛出一个IllegalAccessException异常,是因为name域是私有域。这时,除非有访问权限,否则Java安全机制不允许读取私有域的值。

      Employee alice1 = new Employee("Alice Adams", 75000, 1987, 12, 15);
Class class1 = alice1.getClass();
Field f = class1.getDeclaredField("name");
Object vObject = f.get(alice1);
输出:Exception in thread "main" java.lang.IllegalAccessException: Class bbbTest.EqualsTest can not access a member of class bbbTest.Employee with modifiers "private"...

  反射机制的默认行为受限于Java的访问控制。然而,如果一个Java程序没有收到安全管理器的控制,就可以覆盖访问控制。这是,可以调用Field、Method或Constructor对象的setAccessible方法可设置为可以访问。

      Employee alice1 = new Employee("Alice Adams", 75000, 1987, 12, 15);
Class class1 = alice1.getClass();
Field f = class1.getDeclaredField("name");
f.setAccessible(true);
Object vObject = f.get(alice1);
System.out.println(vObject);  // 打印:Alice Adams

  加入要查看double类型的salary域,这时因为double是数值类型,而不是对象类型,所以要想解决这个问题,可以使用Field类中的getDouble方法,也可以调用get方法,这时,反射机制将会自动地将这个域值打包到相应的对象包装器找那个,这里将打包成Double。

  在get到域值后,可以使用set将obj对象的f域设置成新值。

      Employee alice1 = new Employee("Alice Adams", 75000, 1987, 12, 15);
Class class1 = alice1.getClass();
Field f = class1.getDeclaredField("name");
f.setAccessible(true);
Object vObject = f.get(alice1);
System.out.println(vObject);
f.set(alice1, "Lian Jiang");
Object vObject1 = f.get(alice1);
System.out.println(vObject1); // 打印:Lian Jiang

  5.使用反射编写泛型数组

  java.lang.reflect包中的Array类允许动态地创建数组。其中,可以使用Array类中的newInstance方法,这个方法可以构造新数组,在调用它时必须提供两个参数,一个是数组的元素类型,另一个是数组的长度。

Object newArray = Array.newInstance(componentType, newLength);

  因此,现在有两步要做:1.获得数组的长度。2.获得数组的元素类型

  (1)获得数组的长度可以通过Array.getLength(a)或者Array类的静态getLength方法返回任意数组a的长度。

  (2)在前面已经知道,可以通过Class类的getComponentType方法确定数组对应的类型。

   public static Object goodCopyOf(Object a, int newLength)
{
Class cl = a.getClass();          // 首先取得数组a的类对象
if (!cl.isArray()) return null;        // 然后确认数组a是一个数组
Class componentType = cl.getComponentType();   // 接着确定数组对应的类型
int length = Array.getLength(a);
Object newArray = Array.newInstance(componentType, newLength);  // 创建数组
System.arraycopy(a, 0, newArray, 0, Math.min(length, newLength));  // 将原数组中的元素拷贝到新数组中
return newArray;
}

  例如:

      int[] a = { 1, 2, 3 };
a = (int[]) goodCopyOf(a, 10);
System.out.println(Arrays.toString(a));  // 打印:[1, 2, 3, 0, 0, 0, 0, 0, 0, 0] String[] b = { "Tom", "Dick", "Harry" };
b = (String[]) goodCopyOf(b, 10);      // 打印:[Tom, Dick, Harry, null, null, null, null, null, null, null]
System.out.println(Arrays.toString(b));

  6.使用反射调用任意方法

  (1)首先,使用Method类getMethod获得Method对象

      Method square = MethodTableTest.class.getMethod("square", double.class);
System.out.println(square);  // 打印:public static double aaaaTest.MethodTableTest.square(double)
Method sqrt = Math.class.getMethod("sqrt", double.class);
System.out.println(sqrt);    // 打印:public static double java.lang.Math.sqrt(double)

  (2)然后通过invoke方法调用:f是square或sqrt对象,第一个参数null是因为是静态方法,第二个参数是调用的方法的参数。另外,invoke方法的参数和返回值必须是Object类型的,因此必须进行多次的类型转换。

double x = 10.000;
double y = (Double) sqrt.invoke(null, x);
System.out.printf("%10.4f%n", y);  // 打印:3.1623

Java基础(九)反射(reflection)的更多相关文章

  1. Java基础(九)--反射

    什么是反射? 在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性 这种动态获取的信息以及动态调用对象的方法的功能称为反射机制. 反射的前 ...

  2. 黑马程序员:Java基础总结----反射

    黑马程序员:Java基础总结 反射   ASP.Net+Android+IO开发 . .Net培训 .期待与您交流! 反射 反射的基石:Class类 Class类代表Java类,它的各个实例对象又分别 ...

  3. Java基础九--抽象类

    Java基础九--抽象类 一.抽象类介绍 /*抽象类:抽象:笼统,模糊,看不懂!不具体. 特点:1,方法只有声明没有实现时,该方法就是抽象方法,需要被abstract修饰. 抽象方法必须定义在抽象类中 ...

  4. 4 Java学习之 反射Reflection

    1. 反射概念  反射机制就是:动态地获取类的一切信息,并利用这些信息做一些你想做的事情. java反射机制能够知道类名而不实例化对象的状态下,获得对象的属性或调用方法. JAVA反射机制是在运行状态 ...

  5. java基础(十一 )-----反射——Java高级开发必须懂的

    本文我们通过一个实际的例子来演示反射在编程中的应用,可能之前大家对反射的学习,仅仅是停留在概念层面,不知道反射究竟应用在哪,所以是一头雾水.相信通过这篇教程,会让你对反射有一个更深层次的认知. 概念 ...

  6. Java基础之一反射

    反射是框架设计的灵魂 (使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码))   一.反射的概述 JAVA反射机制是在运行状态中,对于任意一个类,都能够 ...

  7. Java基础之—反射

    反射是框架设计的灵魂 (使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码))   一.反射的概述 JAVA反射机制是在运行状态中,对于任意一个类,都能够 ...

  8. 【Java基础】反射和注解

    前言 在Java中,反射机制和注解机制一直是一个很重要的概念,那么他们其中的原理是怎么样呢,我们不仅仅需要会使用,更要知其然而之所以然. 目录 反射机制 反射如何使用 注解定义 注解机制原理 注解如何 ...

  9. JAVA基础知识|反射

    一.理解反射 1.1.基础概念 反射:在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功能称为ja ...

  10. Java基础之反射总结

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制. ...

随机推荐

  1. JsonConfig的jsonConfig.setExcludes的用法

    1.问题描述 在项目中经常会有两个类存在一对多或者多对一的关联关系,这样在查询多的一方时,会深入查询关联的一方,而我们可能并不需要去深入查询那些数据,此时使用JsonConfig的jsonConfig ...

  2. 多tomcat服务和nginx负载均衡配置

    1.nginx服务安装及配置,详见:linux 配置之安装nginx 2.多个tomcat服务安装及配置,详见:linux 配置多个tomcat 3.关键配置nginx.conf文件 http { i ...

  3. ouc_software第一次作业:OUC二手物品交易

    一.前言 1.项目名称:ouc二手物品交易 2.项目简介 (1)创办一个网上校内二手物品交易平台,供校内师生进行交易二手物品. (2)经过身份认证的用户,可将自己想要交易的二手物品发布至平台,供其他用 ...

  4. vue2.0项目记住密码和用户名实例

    的今天突来兴致,试了一下将用户名和密码存在cookie和localStorage里如何实现:从代码难易程度来讲,果断选择了将用户名和密码存在localStorage里面.当然菜鸟上这么说的,楼下. 也 ...

  5. python pytesseract——3步识别验证码的识别入门

    验证码识别是个大工程,但入门开始只要3步.需要用到的库PIL.pytesserac,没有的话pip安装.还有一个是tesseract-ocr 下载地址:https://sourceforge.net/ ...

  6. mydumper 介绍及使用

    1 Mydumper 介绍 Mydumper是一个针对MySQL和Drizzle的高性能多线程备份和恢复工具. Mydumper主要特性: 轻量级C语言写的 多线程备份,备份后会生成多个备份文件 事务 ...

  7. bugku--web--输入密码查看flag

    首先打开网页链接 随机五位数的密码爆破,先用python写一个脚本来生成随机五位数: x=range(0,10) f=open("3.txt",'w') for i in x: f ...

  8. wampserver 运行橙色,80端口没有被占用,查看错误日志方法

    wampserver运行时橙色,经检查80端口并没有被占用,试了很多种方法都无效,去查看错误日志吧 1.以管理员身份打开CMD 注意这里必须是管理员身份的CMD ,powershell不行的 进入wa ...

  9. 声学回声消除(Acoustic Echo Cancellation)

    回声就是声音信号经过一系列反射之后,又听到了自己讲话的声音,这就是回声.一些回声是必要的,比如剧院里的音乐回声以及延迟时间较短的房间回声:而大多数回声会造成负面影响,比如在有线或者无线通信时重复听到自 ...

  10. Python爬虫工程师必学——App数据抓取实战 ✌✌

    Python爬虫工程师必学——App数据抓取实战 (一个人学习或许会很枯燥,但是寻找更多志同道合的朋友一起,学习将会变得更加有意义✌✌) 爬虫分为几大方向,WEB网页数据抓取.APP数据抓取.软件系统 ...