Java核心技术1-反射

  |  

摘要: 本文是《Java核心技术 10th》中反射的要点总结

【对算法,数学,计算机感兴趣的同学,欢迎关注我哈,阅读更多原创文章】
我的网站:潮汐朝夕的生活实验室
我的公众号:算法题刷刷
我的知乎:潮汐朝夕
我的github:FennelDumplings
我的leetcode:FennelDumplings


一个类的相关信息是通过一个 Class 对象来表示的。位于 java.lang

java.lang.reflect 包中,还定义了对应于类中字段、方法、修饰符等的一些类。利用 Class 与反射 API,我们可以摆脱使用 new 运算符来创建对象的传统方式,在程序运行期间,随时随地创建任意类的对象,并调用对象的方法


反射

反射库是一个提供能够动态操纵 Java 代码的工具。

反射机制的主要用途:

  • 在运行时分析类的能力。
  • 在运行时查看对象,例如,编写一个 toString 方法供所有类使用。
  • 实现通用的数组操作代码。
  • 利用 Method 对象,这个对象很像 C++ 中的函数指针。

Class 类

Java 运行时系统始终为所有对象维护一个运行时的类型标识。这个信息跟踪着每个对象所属的类。虚拟机利用运行时类型信息选择相应的方法执行。

保存这些信息的类称为 Class,Object 类中的 getClass 方法会返回一个 Class 类的实例。

  • getName 方法:返回类名,如果类在一个包里,包的名字也作为类名的一部分。
1
2
3
Random generator = new Random();
Class cl = generator.getClass();
String name = cl.getName(); // java.util.Random
  • 静态方法 forName:获得类名对应的 Class 对象。
1
2
String className = "java.util.Random";
Class cl = Class.forName(className);

className 不是类名或接口名时,forName 方法将抛出一个 checkedexception(已检查异常)。无论何时使用这个方法,都应该提供一个异常处理器(exception handler)。

  • T.class:获取类型 T 的 Class 类对象

除了 obj.getclass()Class.forName(),获得 Class 类对象还有三种方法:T.class,其中 T 是任意类型或 void。

1
2
3
Class cl1 = Random.class;
Class cl2 = int.class;
Class cl3 = Double[].class;

从上面的代码可以看出:一个 Class 对象实际上表示一个类型,而这个类型未必是类,例如 int。

Class 类实际上是一个泛型类。例如,Employee.class 的类型是 Class

虚拟机为每个类型管理一个Class对象。因此,可以利用 == 运算符实现两个类对象比较的操作:

1
2
3
if(e.getClass() == Employee.class) {
...
}
  • newInstance() 方法:动态地创建一个类的实例

下面代码创建一个与 e 具有相同类型的实例。newInstance 调用默认构造器,如果没有则抛出异常。

1
e.getClass().newInstance();

本剧存储在字符串中的类型名创建一个对象。

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

C++ 中的虚拟构造器与 Java 中的 Class 类的区别和联系

C++ 中的虚拟构造器不是语言特性,需要库的支持。
Class 类与 C++ 中的 type_info 类似。
getClass 方法与 C++ 中的 typeid 运算符等价。
但 Java 中的 Class 类比 C++ 中的 type_info 的功能强,C++ 中的 type_info 只能以字符串形式显示类型名,不能创建那个类的对象。

捕获异常

异常有两种类型:未检查异常和已检查异常。对于已检查异常,编译器将会检查是否提供了处理器。未检查异常,例如访问 null 引用,编译器不会查看是否为这些错误提供了处理器。

Class.forName 就是一个抛出已检查异常的例子。将可能抛出已检查异常的代码放在 try 块中,然后在 catch 子句中提供处理代码。

1
2
3
4
5
6
7
try {
String name = ...; // 类名
Class cl = Class.forName(name);
// 对 cl 操作
} catch(Exception e) {
e.printStackTrace();
}

利用反射分析类的能力

反射机制最重要的内容:检查类的结构。

java.lang.reflect 包中有三个类:

  • Field: 描述类的域
  • Method: 描述类的方法
  • Constructor: 描述类的构造器

主要方法如下:

  • getName: 三个类都有,用来返回项目的名称。
  • getType: Field 类的方法,用来返回描述域所属类型的 Class 对象。
  • getParameterTypes: Method 和 Constructor 类的,报告参数类型。
  • getReturnType: Method 类的方法,可以报告返回类型。
  • getModifiers: 三个类都有的方法,返回一个整型数值,用不同的位开关描述 public 和 static 这样的修饰符使用状况。还可以利用 java.lang.reflect 包中的 Modifier 类的静态方法(isPublic、isPrivate、isFinal)分析 getModifiers 返回的整型数值。

我们需要做的全部工作就是调用 Modifier 类的相应方法,并对返回的整型数值进行分析,另外,还可以利用 Modifier.toString 方法将修饰符打印出来。

Class 类中也有一些与类信息相关的方法:

  • Class 类中的 getFields、getMethods 和 getConstructors 方法将分别返回类提供的 public 域、方法和构造器数组,其中包括超类的公有成员。
  • Class 类的 getDeclareFields、getDeclareMethods 和 getDeclaredConstructors 方法将分别返回类中声明的全部域、方法和构造器,其中包括私有和受保护成员,但不包括超类的成员。

打印一个类的全部信息的方法 (代码模板)

下面的代码接受用户输入类名,然后输出类中所有的方法和构造器的签名,以及全部域的名称。

这个程序可以分析 Java 解释器能够加载的任何类,而不仅仅是编译程序时可以使用的类。还可以查看 Java 编译器自动生成的内部类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package reflection;

import java.util.*;
import java.lang.reflect.*;

/**
* This program use reflection to print all features of a class.
* @version 1.1 2004-02-21
* @author Cay Horstmann
*/

public class ReflectionTest {
public static void main(String[] args) {
// read class name from command lline args or user input
String name;
if(args.length > 0) {
name = args[0];
}
else {
Scanner in = new Scanner(System.in);
System.out.println("Enter class name (e.g. java.util.Date): ");
name = in.next();
}

try {
// print class name and superclass (if != Object)
Class cl= Class.forName(name);
Class supercl = cl.getSuperclass();
String modifiers = Modifier.toString(cl.getModifiers());
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());
}

System.out.print("\n{\n");
printConstructors(cl);
System.out.println();
printMethods(cl);
System.out.println();
printFields(cl);
System.out.println("}");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.exit(0);
}

/**
* Pring all constructors of a clas
* @param cl a class
*/
public static void printConstructors(Class cl) {
Constructor[] constructors = cl.getDeclaredConstructors();

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(");");
}
}

/**
* 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(" ");
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(");");
}
}

/**
* Print 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 + ";");
}
}
}

例如输入 java.util.Random 结果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class java.util.Random                                                       
{
public java.util.Random();
public java.util.Random(long);

public synchronized void setSeed(long);
final long internalNextLong(long, long);
final int internalNextInt(int, int);
final double internalNextDouble(double, double);
public long nextLong();
public boolean nextBoolean();
public float nextFloat();
public synchronized double nextGaussian();
public java.util.stream.IntStream ints();
public java.util.stream.IntStream ints(long);
public java.util.stream.IntStream ints(int, int);
public java.util.stream.IntStream ints(long, int, int);
public java.util.stream.LongStream longs(long, long, long);
public java.util.stream.LongStream longs();
public java.util.stream.LongStream longs(long);
public java.util.stream.LongStream longs(long, long);
public java.util.stream.DoubleStream doubles();
public java.util.stream.DoubleStream doubles(long, double, double);
public java.util.stream.DoubleStream doubles(double, double);
public java.util.stream.DoubleStream doubles(long);
private static long seedUniquifier();
private static long initialScramble(long);
public void nextBytes([B);
private void resetSeed(long);
protected int next(int);
private void readObject(java.io.ObjectInputStream);
private synchronized void writeObject(java.io.ObjectOutputStream);
public int nextInt(int);
public int nextInt();
public double nextDouble();

static final long serialVersionUID;
private final java.util.concurrent.atomic.AtomicLong seed;
private static final long multiplier;
private static final long addend;
private static final long mask;
private static final double DOUBLE_UNIT;
static final java.lang.String BadBound;
static final java.lang.String BadRange;
static final java.lang.String BadSize;
private static final java.util.concurrent.atomic.AtomicLong seedUniquifier;
private double nextNextGaussian;
private boolean haveNextNextGaussian;
private static final [Ljava.io.ObjectStreamField; serialPersistentFields;
private static final jdk.internal.misc.Unsafe unsafe;
private static final long seedOffset;
}

java.lang.reflect 中的常用方法总结

  • Field、Method、Constructor
方法 描述
Class getDeclaringClass() 返回一个用于描述类中定义的构造器、方法或域的Class对象。
Class[] getExceptionTypes()(在Constructor和Method类中) 返回一个用于描述方法抛出的异常类型的Class对象数组。
int getModifiers() 返回一个用于描述构造器、方法或域的修饰符的整型数值。使用Modifier类中的这个方法可以分析这个返回值。
String getName() 返回一个用于描述构造器、方法或域名的字符串。
Class[] getParameterTypes()(在Constructor和Method类中) 返回一个用于描述参数类型的Class对象数组。
Class getReturnType() (在Method类中)返回一个用于描述返回类型的Class对象。
  • Modifier
1
2
3
4
5
6
7
8
9
10
11
12
static String toString(int modifiers)
static boolean isAbstract(int modifiers)
static boolean isFinal(int modifiers)
static boolean isInterface(int modifiers)
static boolean isNative(int modifiers)
static boolean isPrivate(int modifiers)
static boolean isProtected(int modifiers)
static boolean isPublic(int modifiers)
static boolean isStatic(int modifiers)
static boolean isStrict(int modifiers)
static boolean isSynchronized(int modifiers)
static boolean isVolatile(int modifiers)

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

前面我们了解了如何查看任意对象的数据域名称和类型,主要有两步:

  1. 获得相应的 Class 对象
  2. 通过 Class 对象调用 getDeclareFields。

这一小节我们解决如何进一步查看数据域实际内容,以及如何利用反射机制查看在编译时还不清楚的对象域。

假设 f 是一个 Field 对象,例如通过 getDeclareFields 得到;obj 是某个包含 f 域的类的对象,则 f.get(obj) 返回一个对象,其值为 obj 中该域的当前值。例子如下 (throws Exception 必须加上)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.util.*;
import java.lang.reflect.*;

public class EmployeeTest {
public static void main(String[] args) throws Exception {
Employee harry = new Employee("Harry Hacker", 35000, 1989, 10, 1);
// the class Object representing Employee
Class cl = harry.getClass();
// the name field of the Employee class
Field f = cl.getDeclaredField("name");
// 解除 Java 访问控制
f.setAccessible(true);
// the value of the name field of the harry object
Object v = f.get(harry);
System.out.println(v);

Field f2 = cl.getDeclaredField("salary");
f2.setAccessible(true);
Object v2 = f2.get(harry);
System.out.println(v2);
}
}

由于 name 是 Employee 的私有域,因此上面代码的 f.setAccessible(true) 非常关键,否则会报 IllegalAccessException

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

setAccessible 方法是 AccessibleObject 类中的一个方法,它是 Field、Method 和 Constructor 类的公共超类。这个特性是为调试、持久存储和相似机制提供的。

f.get 方法返回的是 Object,如果 f 的域是基本类型,反射机制会自动将这个域的值打包到相应的对象包装器中。

可供任意类使用的通用 toString 方法

主要功能:给定任意类,打印出类名,然后打印出数据域名称和值

整体流程如下:

  • 使用 getDeclaredFileds 获得所有的数据域
  • 然后使用 setAccessible 将所有的域设置为可访问的
  • 对于每个域,获得了名字和值

循环引用将有可能导致无限递归,因此,ObjectAnalyzer 将记录已经被访问过的对象。

为了能够查看数组内部,需要采用一种不同的方式。有关这种方式的具体内容将在下一节中详细论述。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package objectAnalyzer;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;

public class ObjectAnalyzer {
private ArrayList<Object> visited = new ArrayList<>();

/**
* Converts an object to a string representation that lists all fields.
* @param obj an object
* @return a string with the object's class name and all field names and values
*/
public String toString(Object obj) {
if(obj == null) {
return "null";
}
if(visited.contains(obj)) {
return "...";
}
visited.add(obj);
Class cl = obj.getClass();
if(cl == String.class) {
return (String) obj;
}
if(cl.isArray()) {
String r = cl.getComponentType() + "[]{";
for(int i = 0; i < Array.getLength(obj); i++) {
if(i > 0) {
r += ",";
}
Object val = Array.get(obj, i);
if(cl.getComponentType().isPrimitive()) {
r += val;
} else {
r += toString(val);
}
}
return r + "}";
}

String r = cl.getName();
// inspect the fields of this class and all superclasses
do {
r += "[";
Field[] fields = cl.getDeclaredFields();
AccessibleObject.setAccessible(fields, true);
// get the names and values of all fields
for(Field f: fields) {
if(!Modifier.isStatic(f.getModifiers())) {
if(!r.endsWith("[")) {
r += ",";
}
r += f.getName() + "=";
try {
Class t = f.getType();
Object val = f.get(obj);
if(t.isPrimitive()) {
r += val;
} else {
r += toString(val);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
r += "]";
cl = cl.getSuperclass();
} while(cl != null);

return r;
}
}

用途 1: 查看任意对象的内部信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package objectAnalyzer;

import java.util.ArrayList;

/**
* This program uses reflection to spy on objects.
* @version 1.0 2022-05-19
* @author Cay Horstmann
*/

public class ObjectAnalyzerTest {
public static void main(String[] args) {
ArrayList<Integer> squares = new ArrayList<>();
for(int i = 1; i <= 5; i++) {
squares.add(i * i);
}
System.out.println(new ObjectAnalyzer().toString(squares));
}
}

直接运行 java objectAnalyzer/ObjectAnalyzerTest 会有警告

1
2
3
4
5
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by objectAnalyzer.ObjectAnalyzer (file:/home/ppp/tmp/java/) to field java.util.ArrayList.serialVersionUID
WARNING: Please consider reporting this to the maintainers of objectAnalyzer.ObjectAnalyzer
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

很多库都用了反射,Java 9 以后如果使用反射访问非公有的内容,会给出警告。例如前面代码中,访问了 ArrayList 和 Integer 对象的内部。

可以用下面的命令行参数的方式取消这个警告,细节可以参考 (Core Java 卷2 的第 9 章)

1
2
3
java --add-opens java.base/java.util=ALL-UNNAMED \
--add-opens java.base/java.lang=ALL-UNNAMED \
objectAnalyzer.ObjectAnalyzerTest

结果如下

1
java.util.ArrayList[elementData=class java.lang.Object[]{java.lang.Integer[value=1][][],java.lang.Integer[value=4][][],java.lang.Integer[value=9][][],java.lang.Integer[value=16][][],java.lang.Integer[value=25][][],null,null,null,null,null},size=5][modCount=5][][]

用途 2: 实现自己类中的 toString

1
2
3
public String toString() {
return new ObjectAnalyzer().toString(this);
}

使用反射编写泛型数组代码

java.lang.reflect.Array 允许动态地创建数组。这个特性用在了 java.util.Array.copyOf 方法中,可以用于扩展已经填满的数组。例如:

1
2
3
Employee[] a = new Employee[100];
// a 满了之后执行下面的代码
a = Arrays.copyOf(a, 2 * a.length);

下面我们利用反射实现一下 Arrays.copyOf 的功能。其中用到 System.arraycopy,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package arrays;

import java.lang.reflect.Array;
import java.util.Arrays;

/**
* This program demostrates the use of reflection for manipulation arrays
* @version 1.0 2022-05-19
* @author Cay Horstmann
*/
public class CopyOfTest {
public static void main(String[] args) {
int[] a = {1, 2, 3};
int[] aa = Arrays.copyOf(a, a.length);
a = (int[]) goodCopyOf(a, 10);
System.out.println(Arrays.toString(a));
aa = Arrays.copyOf(aa, 2 * aa.length);
System.out.println(Arrays.toString(aa));

String[] b = {"Tom", "Dick", "Harry"};
String[] bb = Arrays.copyOf(b, b.length);
b = (String[]) goodCopyOf(b, 10);
System.out.println(Arrays.toString(b));
bb = Arrays.copyOf(bb, 2 * bb.length);
System.out.println(Arrays.toString(bb));

System.out.println("The following call will generate an exception.");
b = (String[]) badCopyOf(b, 10);
}

/**
* This method attempts to grow an array by allocating a new array and copying all elements.
* @param a the array to grow
* @param newLength the new length
* @return a larger array that contains all elements of a.
* However, the returned array has type Object[], not the same type as a
*/
public static Object[] badCopyOf(Object[] a, int newLength) {
Object[] newArray = new Object[newLength]; // 一开始就是 Object[]
System.arraycopy(a, 0, newArray, 0, Math.min(a.length, newLength));
return newArray;
}

/**
* this method grows an array by allocating a new array of the same type and copying all elements.
* @param a the array to grow. This can be on object array or a primitive type array
* @return a larger array that contains all elements of a.
*/
public static Object goodCopyOf(Object a, int newLength) {
Class cl = a.getClass();
if(!cl.isArray()) {
return null;
}
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;
}
}

其中 badCopyOf 返回的是 Object[],是不对的实现,原因是因为 Java 数组会记住每个元素的类型,即创建数组时 new 表达式中使用的元素类型。将一个 Employee[] 临时地转换成 Object[] 数组,然后再把它转换回来是可以的,但一个从开始就是 Object[] 的数组却永远不能转换成 Employee[] 数组。

因此需要能够创建与原数组类型相同的新数组。为此,需要 java.lang.reflect.Array 类的一些方法。其中最关键的是 Array.newInstance,在调用它时必须提供两个参数,一个是数组的元素类型,一个是数组的长度。

同时返回类型应该是 Object 而不是 Object[],因为 int[] 可以转换成 Object,但不能转换为 Object[]。

调用任意方法

C/C++ 中,可以用函数指针执行任意函数。

而 Java 中没有提供方法指针,方便将一个方法的地址传给另一个方法。有 2 个替代的方法:

  • 接口
  • 反射机制

类似于 Field 类的 get 方法查看对象域的过程。Method 类有一个 invoke 方法,允许调用包装在当前 Method 对象中的方法。签名如下:

1
Object invoke(Object obj, Object... args)

如果是静态方法,第一个参数给 null 即可。例如:

ml 代表 Employee 类的 getName 方法的 Method 对象;m2 代表 Employee 类的 getSalary 方法的 Method 对象。

调用方法如下:

1
2
3
4
5
Method m1 =  Employee.class.getMethod("getName");
String n = (String) m1.invoke(harry);

Method m2 = Employee.class.getMethod("raiseSalary", double.class);
double s = (Double) m2.invoke(harry);

方法返回值为基本类型的话,invoke 会返回包装器类型,必须类型转换,自动拆箱后变为 double。

得到 Method 对象的方法

  • 调用 getDeclareMethods,然后对返回的 Method 对象数组遍历
  • 通过 Class 类中的 getMethod 方法,与 getField 类似。但注意,由于可能会有同名的方法,因此还需要提供参数类型,签名如下:
    1
    Method getMethod(String name, Class... parameterTypes)

invoke 模拟函数指针的例子: 打印数学函数值表

使用 Method 对象实现 C 中函数指针的操作,但注意,这种操作有一些缺点:

  • 函数指针容易出错
  • invoke 的参数和返回值必须是 Object,因此需要多次类型转换,这样做将会使编译器错过检查代码的机会。

因此最好用 lambda 表达式,或者接口来代替 Method 对象。如果要实现回调功能,最好也用接口代替 Method 对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package methods;

import java.lang.reflect.*;

/**
* This program shows how to invoke methods through reflection
* @version 1.0 2022-05-20
* @author Cay Horstmann
*/
public class MethodTableTest {
public static void main(String[] args) throws Exception {
// get method pointers to the square and sqrt methods
Method square = MethodTableTest.class.getMethod("square", double.class);
Method sqrt = Math.class.getMethod("sqrt", double.class);

// print tables of x- and y-values
printTable(1, 10, 10, square);
printTable(1, 10, 10, sqrt);
}

/**
* Returns the square of a number
* @param x a number
* @return x squared
*/
public static double square(double x) {
return x * x;
}

/**
* Prints a table with x- and y-values for a method
* @param from the lower bound for the x-values
* @param to the upper bound for the x-values
* @param n the number of rows in the table
* @param f a method eith a double parameter and double return value
*/
public static void printTable(double from, double to, int n, Method f) {
// print out the method as table header
System.out.println(f);
double dx = (to - from) / (n - 1);

for(double x = from; x <= to; x += dx) {
try {
double y = (Double) f.invoke(null, x);
System.out.printf("%10.4f | %10.4f%n", x, y);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static double methods.MethodTableTest.square(double)
1.0000 | 1.0000
2.0000 | 4.0000
3.0000 | 9.0000
4.0000 | 16.0000
5.0000 | 25.0000
6.0000 | 36.0000
7.0000 | 49.0000
8.0000 | 64.0000
9.0000 | 81.0000
10.0000 | 100.0000
public static double java.lang.Math.sqrt(double)
1.0000 | 1.0000
2.0000 | 1.4142
3.0000 | 1.7321
4.0000 | 2.0000
5.0000 | 2.2361
6.0000 | 2.4495
7.0000 | 2.6458
8.0000 | 2.8284
9.0000 | 3.0000
10.0000 | 3.1623

Share