-------Android培训 、 Java培训 、期待与您交流!-------
一.反射机制的基本概念
1.1 反射机制
Java程序在运行期间可以动态加载,解析和使用一些在编译阶段不确定的类型,动态的获取类的信息以及动态调用对象的方法的机制被称为反射(Reflection),也叫做内省(Introspection).通俗的说就是把一个类的组成元素全部都映射成为一个Class对象,例如:类的组成元素:属性(Field),方法(Method),构造器(Constructor),数组(Array);类的属性反射过后对应的就是Field类,方法反射过后对应的就是Method类,构造器反射之后对应的就是Constructor类,数组反射后就对应Array类。
1.2 反射相关的类
Class:反射的第一步就是你要获得你反射元素(属性,方法,构造器,数组)的所在类的字节码对象,获取字节码对象的途径:Class.forName(String name):name的值是你的类的完全限定名;当前对象调用getClass();类.class。
Method:此类就是方法反射过后所对应的类。与方法反射相关的方法:
getMethod(String name,Class>...parameterTypes) | 返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。name 参数是一个 String ,用于指定所需方法的简称。parameterTypes 参数是按声明顺序标识该方法形参类型的Class 对象的一个数组。 如果 parameterTypes 为 null 则按空数组处理。 |
getMethed() | 返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。共)member方 法。返回数组类返回从 Object 类继承的所有(公数组中的元素没有排序,也没有任何特定的顺序。如果此Class对象表示没有公共成员方法的类或接口,或者表示一个基本类型或 void,则此方法返回 长度为 0 的数组。 |
getDeclaredMethod(String name,Class>...parameterTypes) | 返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。name 参数是一个 String 它指定所需方法的简称,parameterTypes 参数是 Class 对象的一个数组,它按声明顺序标识该方法的形参类型。如果在某个类中声明了带有相同参数类型的多个方法,并且其中有一个方法的 返回类型比其他方法的返回类型都特殊,则返回该方法;否则将从中任选一个方法。 |
getDeclaredMethods(String name,Class>...parameterTypes) | 返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。返回数组中的元素没有排序,也没有 任何特定的顺序。如果该类或接口不声明任何方法,或者此 Class 对象表示一个基本类型、一个数组类或 void,则此方法返回一个长度为 0 的数组。类初始化方法 不包含在返回数组中。如果该类声明带有相同参数类型的多个公共成员方法,则它们都包含在返回的数组中。 |
invoke(Object obj,Object...obj) | 对带有指定参数的指定对象调用由此Method对象表示的底层方法。个别参数被自动解包,以便与基本形参相 匹配,基本参数和引用参数都随需服从方法调用转换。如果底层方法是静态的,那么可以忽略指定的 obj 参数。 该参数可以为null。如果底层方法所需的形参数为0,则所提供的args数组长度可以为0或null。如果底 层方法是实例方法,则使用动态方法查找来调用它,这一点记录在JavaLanguage Specification,如果底层方 Second Edition的第 15.12.4.4 节中;在发生基于目标对象的运行时类型的重写时更应该这样做。法 是静态的并且尚未初始化声明此方法的类,则会将其初始化。如果方法正常完成,则将该方法返回的值返 回给调用者如果该值为基本类型,则首先适当地将其包装在对象中。但是,如果该值的类型为一组基本 类型,则数组元素不被包装在对象中;换句话说,将返回基本类型的数组。如果底层方法返回类型为void, 则该调用返回 null。 |
Field:此类就是属性反射过后所对应的类。与属性反射相关的方法:
getField(String name) | 返回一个Field对象,它反映此Class对象所表示的类或接口的指定公共成员字段。name参数是一个String,用于指定所 需字段的简称。 |
getFields() | 返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。返回数 组中的元素没有排序,也没有任何特定的顺序。如果类或接口没有可访问的公共字段,或者表示一个数组类、一个基本 类型或void,则此方法返回长度为 0 的数组。特别地,如果该 Class 对象表示一个类,则此方法返回该类及其所有超 类的公共字段。如果该 Class 对象表示一个接口,则此方法返回该接口及其所有超接口的公共字段。该方法不反映数组 类的隐式长度字段。用户代码应使用Array 类的方法来操作数组。 |
getDeclaredField(String name) | 返回一个 Field 对象,该对象反映此Class对象所表示的类或接口的指定已声明字段。name 参数是一个 String,它指定 所需字段的简称。注意,此方法不反映数组类的length字段。 |
getDeclaredFields() | 返回Field对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。包括公共、保护、默认 (包)访问和私有字段,但不包括继承的字段。返回数组中的元素没有排序,也没有任何特定的顺序。如果该类或接口 不声明任何字段,或者此 Class对象表示一个基本类型、一个数组类或 void,则此方法返回一个长度为 0 的数组。 |
get(Object obj) | 返回指定对象上此Field表示的字段的值。如果该值是一个基本类型值,则自动将其包装在一个对象中。底层字段的值是 按以下方式获得的:如果底层字段是一个静态字段,则忽略 obj变量;它可能为 null。否则,底层字段是一个实例字段 如果指定的 obj 变量为 null,则该方法将抛出一个 NullPointerException。如果指定对象不是声明底层字段的类或接 口的实例,则该方法将抛出一个 IllegalArgumentException。如果此 Field 对象强制实施 Java 语言访问控制,并且底 层字段是不可访问的,则该方法将抛出一个IllegalAccessException。如果底层字段是静态的,并且声明该字段的类尚未 初始化,则初始化这个类。否则,从底层实例字段或静态字段中获取该值。如果该字段是一个基本类型字段,则在返回前 将该值包装在一个对象中,否则照原样返回。如果字段隐藏在 obj 的类型中,则根据前面的规则获得字段的值。 |
set(Object obj,Object value) | 将指定对象变量上此Field对象表示的字段设置为指定的新值。如果底层字段的类型为基本类型,则对新值进行自动解包。 进行此操作的方式如下:如果底层字段是静态字段,则忽略obj变量;它可能为null。否则底层字段是一个实例字段。如 果指定对象变量为null,则该方法将抛出一个NullPointerException。如果指定对象变量不是声明底层字段的类或接口的 实例,则该方法将抛出一个 IllegalArgumentException。如果此 Field 对象实施 Java 语言访问控制,并且底层字段是不 可访问的,则该方法将抛出一个IllegalAccessException。如果底层字段为final 字段,则该方法将抛出一个 IllegalAccessException,除非 setAccessible(true) 已经继承该字段并且该字段是一个非静态字段。在通过程 序的其他部分可以访问类的实例之前,只有使用空白 final 字段反序列化或重构类的实例期间,以这种方式设置final字 段才有意义。在其他任何上下文中使用该方法都可能会有不可预知的结果,包括程序的其他部分继续使用该字段的原始值 的情况。如果底层字段的类型为某一基本类型,则可以尝试使用解包转换将新值转换为基本类型的值。如果该尝试失败, 则此方法将抛出一个IllegalArgumentException。如果在进行可能的解包之后,无法通过某一标识或扩展转换将新值转换 为底层字段的类型,则此方法将抛出一个IllegalArgumentException。如果底层字段是静态的,并且声明该字段的类尚未初始化, 则初始化这个类。字段被设置为可能已解包并扩大的新值。如果字段隐藏在 obj 的类型中,则根据前面的规则设置字段的值。 |
Constructor:此类就是构造器反射过后所对应的类。与构造器反射相关的方法:
getConstructor(Class>... parameterTypes) | 返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。parameterTypes 参数是 Class 对象的一个数组,这些 Class 对象按声明顺序标识构造方法的形参类型。如果此 Class 对象表示非静态 上下文中声明的内部类则形参类型作为第一个参数包括显示封闭的实例。 |
getConstructors() | 返回一个包含某些 Constructor对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。 如果该类没有公共构造方法,或者该类是一个数组类,或者该类反映一个基本类型或void,则返回一个长度为 0的数组。注意,此方法返回 Constructor 是 Constructor>[],不是预期的 Constructor 后,该数组可能被修改以保存不同类的 Constructor对象,而这将违反 Constructor |
getDeclaredConstructor(Class>... parameterTypes) | 返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。parameterTypes 参数是Class对象的一个数组,它按声明顺序标识构造方法的形参类型。如果此 Class 对象表示非静态上下文 中声明的内部类,则形参类型作为第一个参数包括显示封闭的实例。 |
getDeclaredConstructors() | 返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。它们是公共 、保护、默认(包)访问和私有构造方法。返回数组中的元素没有排序,也没有任何特定的顺序。如果该类存 在一个默认构造方法,则它包含在返回的数组中。如果此Class 对象表示一个接口、一个基本类型、一个数组 类或 void,则此方法返回一个长度为 0 的数组。 |
newInstance(Object... initargs) | 使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化 该实例。个别参数会自动解包,以匹配基本形参,必要时,基本参数和引用参数都要进行方法调用转换。如果底 层构造方法所需形参数为0,则所提供的 initargs 数组的长度可能为 0 或 null。如果构造方法的声明类是非静 态上下文的内部类,则构造方法的第一个参数需要是封闭实例;请参阅Java 语言规范 第 15.9.3 节。如果所需的 访问检查和参数检查获得成功并且实例化继续进行,这时构造方法的声明类尚未初始化,则初始化这个类。如果 构造方法正常完成,则返回新创建且已初始化的实例。 |
Array:此类就是数组反射过后所对应的类。与数组反射相关的方法:
newInstance(Class<> componentType,int... dimensions) |
创建一个具有指定的组件类型和维度的新数组。如果 componentType 表示一个非数组类或接口, 则新数组具有dimensions.length 维度,并且将 componentType 作为其组件类型。如果 componentType表示一个数组类,则新数组的维数等于dimensions.length 和 componentType 的维数的总和。在这种情况下,新数组的组件类型为componentType 的组件类型。新数组的 维数不能超过该实现所支持的数 组维数(通常为 255)。 |
get(Object array,int index)
|
返回指定数组对象中索引组件的值。如果该值是一个基本类型值,则自动将其包装在一个对象中。 |
set(Object array,int index,Object value) |
将指定数组对象中索引组件的值设置为指定的新值。如果数组的类型为基本组件类型,则新值第 一个被自动解包。 |
AccessibleObject
AccessibleObject 类是 Field、Method 和 Constructor 对象的基类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用 Field、Method 或 Constructor 对象来设置或获取字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问检查。在反射对象中设置accessible 标志允许具有足够特权的复杂应用程序(比如 Java Object Serialization 或其他持久性机制)以某种通常禁止使用的方式来操作对象。
ReflectPermission
反射操作的 Permission 类。ReflectPermission 是一种指定权限,没有动作。当前定义的唯一名称是 suppressAccessChecks,它允许取消由反射对象在其使用点上执行的标准 Java 语言访问检查 - 对于 public、default(包)访问、protected、private 成员。
二. 反射应用举例
2.1 反射的用途
反射的特性就是动态,它能够动态的对某个类的相关元素进行调用,你不需要显示的创建某个类的实例就可以实现调用。
2.2 构造器反射
package reflect;
import java.lang.reflect.Constructor;
//构造器反射
public class ConstructorReflect {
public static void main(String[] args) throws Exception
{
/*私有无参构造器的反射案例*/
Constructor> con1= ExamA.class.getDeclaredConstructor();//如果一个类的构造器是私有的,就要调用该方法,获取所有声明的无参构造器对象。
con1.setAccessible(true);//通过设置此标记(true)可以绕过访问权限,访问一些私有的东西.
ExamA a1=(ExamA) con1.newInstance();
a1.test("测试1");
/*私有有参构造器的反射案例*/
System.out.println("---------------------");
Constructor> con2= ExamA.class.getDeclaredConstructor(int.class);//int.class!=Integer.class但int.class==Integer.Type
//参数是原生类型,例如,如果参数是String类型,那么就要用String.class。
con2.setAccessible(true);
ExamA a2=(ExamA) con2.newInstance(1);
a2.test("测试2");
/*公有有参构造器的反射案例*/
System.out.println("---------------------");
Constructor> con3= ExamB.class.getConstructor();
ExamB b1=(ExamB) con3.newInstance();
b1.exam("测试3");
/*公有有参构造器的反射案例*/
System.out.println("---------------------");
Constructor> con4= ExamB.class.getConstructor(String.class);
ExamB b2=(ExamB) con4.newInstance("1");
b2.exam("测试4");
}
}
//被测试类
class ExamA
{
private ExamA()
{
System.out.println("私有无参构造器反射");
}
public void test(String str)
{
System.out.println(str+"->通过反射构造的对象:"+this);
}
private ExamA(int i)
{
System.out.println("私有有参构造器反射");
}
}
class ExamB
{
public ExamB()
{
System.out.println("公有无参构造器反射");
}
public ExamB(String str)
{
System.out.println("公有有参构造器反射");
}
public void exam(String str)
{
System.out.println(str+"->通过反射构造的对象:"+this);
}
}
2.3 方法反射
package reflect;
import java.lang.reflect.Method;
public class MethodReflect {
public static void main(String[] args) throws Exception {
Class> clazz=Class.forName("reflect.Exam");
Exam exam=new Exam();
//公有无参方法反射
Method m1=clazz.getMethod("Test1");
m1.invoke(exam);//方法调用的方法
//公有有参方法反射
System.out.println("----------------------------");
Method m2=clazz.getMethod("Test1",String.class);
m2.invoke(exam,"china");
//私有无参方法反射
System.out.println("----------------------------");
Method m3=clazz.getDeclaredMethod("Test2");
m3.setAccessible(true);
m3.invoke(exam);
//私有有参方法反射
System.out.println("----------------------------");
Method m4=clazz.getDeclaredMethod("Test2",int.class);
m4.setAccessible(true);
m4.invoke(exam,250);
}
}
//被测试类
class Exam
{
public int Test1()
{
System.out.println("测试1:公有无参方法反射");
return 1;
}
public void Test1(String str)
{
System.out.println("测试2:公有有参方法反射->"+"参数:"+str);
}
@SuppressWarnings("unused")
private void Test2()
{
System.out.println("测试3:私有无参方法反射");
}
@SuppressWarnings("unused")
private void Test2(int tag)
{
System.out.println("测试4:私有有参方法反射->"+"参数:"+tag);
}
}
2.4 属性反射
package reflect;
import java.lang.reflect.Field;
public class FieldReflect {
public static void main(String[] args) throws Exception {
Class> clazz=ExamField.class;
ExamField exam=new ExamField();
//公有实例属性反射
Field f1=clazz.getField("X");
System.out.println("通过反射访问的属性值:"+f1.get(exam));
f1.set(exam, 100);//通过反射改变属性的值
System.out.println("通过反射更改之后的属性值:"+f1.get(exam));
//公有静态属性反射
System.out.println("----------------------");
Field f2=clazz.getField("Y");
System.out.println("通过反射访问的属性值:"+f2.get(clazz));
f2.set(clazz, 200);
System.out.println("通过反射更改之后的属性值:"+f2.get(clazz));
//私有实例静态属性反射
System.out.println("----------------------");
Field f3=clazz.getDeclaredField("Z");
f3.setAccessible(true);
System.out.println("通过反射访问的属性值:"+f3.get(exam));
f3.set(exam,300);
System.out.println("通过反射更改之后的属性值:"+f3.get(exam));
//私有静态属性反射
System.out.println("----------------------");
Field f4=clazz.getDeclaredField("E");
f4.setAccessible(true);
System.out.println("通过反射访问的属性值:"+f4.get(clazz));
f4.set(clazz, 400);
System.out.println("通过反射更改之后的属性值:"+f4.get(clazz));
}
}
class ExamField
{
public int X=10;
public static int Y=20;
private int Z=30;
private static int E=40;
}
2.5 数组反射
package reflect;
import java.util.HashMap;
import java.util.Map;
public class ClassType {//数组反射所需的Class容器
public static Class>getType(String key)throws Exception
{
if(!ClassType.Type.TYPE().containsKey(key))
throw new Exception("类型容器中无词关键字");
Class>clazz=ClassType.Type.TYPE().get(key);
return clazz;
}
private static class Type
{
private static final Map>map=new HashMap>();
private static final Map> TYPE()
{
map.put("byte",byte.class);map.put("char",char.class);
map.put("short",short.class);map.put("int", int.class);
map.put("long",long.class);map.put("float",float.class);
map.put("double",double.class);map.put("Byte", Byte.class);
map.put("Character",Character.class);map.put("Short", Short.class);
map.put("Integer", Integer.class);map.put("Long",Long.class);
map.put("Float", Float.class);map.put("Double", Double.class);
map.put("void", void.class);map.put("Void", Void.class);
map.put("Object", Object.class);map.put("Class", Class.class);
map.put("String", String.class);
return map;
}
}
}
package reflect;
import java.lang.reflect.Array;
public class ArrayReflect {
public static void main(String[] args) throws Exception {
String key="int";
Class>clazz=ClassType.getType(key);//获取Class对象
int [] b=(int[]) Array.newInstance(clazz, 5);//数组反射
for(int i=0;i
2.6 反射综合
package reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Reflect {
public static void main(String[] args) throws Exception {
Class> clazz=Class.forName("reflect.Test");
Constructor> con=clazz.getDeclaredConstructor(int.class,String.class);
con.setAccessible(true);
Object obj=con.newInstance(1949,"中国");
Test t=null;
if(obj instanceof Test)
t=(Test)obj;
Field f1=clazz.getDeclaredField("X");
Field f2=clazz.getDeclaredField("Y");
f1.setAccessible(true);
f2.setAccessible(true);
System.out.println("X静态属性值:"+f1.get(clazz));
System.out.println("Y实例属性值:"+f2.get(t));
Method m=clazz.getDeclaredMethod("Info",String.class);
m.setAccessible(true);
Object obj1=m.invoke(t, " 你好!");
System.out.println("反射调用"+m.getName()+"方法----->运行结果:"+obj1);
}
}
class Test
{
@SuppressWarnings("unused")
private static int X;
private String Y;
private Test(int x,String y)
{
X=x;
Y=y;
System.out.println("通过反射构造的构造器----对象:"+this);
}
@SuppressWarnings("unused")
private String Info(String str)
{
return Y+str;
}
}
2.7 反射性能
反射是一种强大的工具,但也存在一些不足。一个主要的缺点是对性能有影响。使用反射基本上是一种解释操作,您可以告诉JVM您希望做什么并且它满足您的要求。这类操作总是慢于只直接执行相同的操作。
字段接入性能测试代码:
public int accessSame(int loops) {
m_value = 0;
for (int index = 0; index < loops; index++) {
m_value = (m_value + ADDITIVE_VALUE) *
MULTIPLIER_VALUE;
}
return m_value;
}
public int accessReference(int loops) {
TimingClass timing = new TimingClass();
for (int index = 0; index < loops; index++) {
timing.m_value = (timing.m_value + ADDITIVE_VALUE) *
MULTIPLIER_VALUE;
}
return timing.m_value;
}
public int accessReflection(int loops) throws Exception {
TimingClass timing = new TimingClass();
try {
Field field = TimingClass.class.
getDeclaredField("m_value");
for (int index = 0; index < loops; index++) {
int value = (field.getInt(timing) +
ADDITIVE_VALUE) * MULTIPLIER_VALUE;
field.setInt(timing, value);
}
return timing.m_value;
} catch (Exception ex) {
System.out.println("Error using reflection");
throw ex;
}
}
方法接入性能测试代码:
public int callDirectArgs(int loops) {
int value = 0;
for (int index = 0; index < loops; index++) {
value = step(value);
}
return value;
}
public int callReferenceArgs(int loops) {
TimingClass timing = new TimingClass();
int value = 0;
for (int index = 0; index < loops; index++) {
value = timing.step(value);
}
return value;
}
public int callReflectArgs(int loops) throws Exception {
TimingClass timing = new TimingClass();
try {
Method method = TimingClass.class.getMethod
("step", new Class [] { int.class });
Object[] args = new Object[1];
Object value = new Integer(0);
for (int index = 0; index < loops; index++) {
args[0] = value;
value = method.invoke(timing, args);
}
return ((Integer)value).intValue();
} catch (Exception ex) {
System.out.println("Error using reflection");
throw ex;
}
}
3.1 方法句柄基本介绍
方法句柄类型:方法句柄类型只与返回值的原生类型和参数原生类型有关,与方法的名称无关。
调用一个方法所需要的工具类有:MethodHandles,Lookup类,MethodType类。
获取方法句柄的途径:
1.MethodType.methodType(参数)参数的第一个值为返回值的Class对象,其余的参数是方法参数的Class对象。
2.MethodType.genercMethodType(int)只有当返回值和参数都是Object对象的时候才可以用,参数指定的是方法参数的个数.
3.2 方法句柄的调用
静态方法的方法句柄调用不需指定接收者对象,而实例方法的方法句柄调用则需要指定接收者对象。
InvokeExat:该方法在调用的时候严格要求类型匹配,参数类型和返回值类型也在考虑之内,如果一个方法的返回值是String类型,你必须在调用该方法的时候进行强制转换为String类型,否则就会报错,如果没有指定返回值也是会报错,因为它会认为被调用的方法类型为Void。
Invoke:该方法的调用与InvokeExat相比较,显得比较随意。它会调用asType方法将原来的方法句柄适配到目标方法句柄。具体的适配种类为:
种类1:子类转换为父类,接口实例类型转换为接口类型
种类2:基本类型的宽化扩展(如 int->long)
种类3:基本类型装箱和拆箱机制(如 int->Integer)
种类4:如果原句柄有返回值,而目标句柄的返回值为void,则原句柄的返回值舍弃
种类5:如果原句柄的返回值为void,而目标句柄的返回值为引用类型,则目标句柄的返回值为null
种类6:如果原句柄的返回值为void,而目标句柄的返回值为基本类型,则目标句柄的返回值为0
3.3 方法句柄举例
package reflect;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
public class MethodHandleTest {
public static void main(String[] args) throws Throwable {
Handle handle=new Handle();
//RGA方法的方法句柄测试
MethodHandles.Lookup lookup=MethodHandles.lookup();
MethodType mt1=MethodType.methodType(void.class);
MethodHandle mh1=lookup.findVirtual(Handle.class, "RGA", mt1);
mh1.invokeExact(handle);
//RGB方法的方法句柄测试
MethodType mt2=MethodType.methodType(void.class,String.class);
MethodHandle mh2=lookup.findVirtual(Handle.class, "RGB", mt2);
mh2.invokeExact(handle,"我是RGB");
//KFC方法的方法句柄测试
MethodType mt3=MethodType.methodType(void.class);
MethodHandle mh3=lookup.findStatic(Handle.class, "KFC", mt3);
mh3.invokeExact();
//KFD方法的方法句柄测试
MethodType mt4=MethodType.methodType(void.class,String.class);
MethodHandle mh4=lookup.findStatic(Handle.class,"KFD", mt4);
mh4.invokeExact("我是KFD");
//getLanguage方法的方法句柄测试
MethodType mt5=MethodType.methodType(String.class,String.class);
MethodHandle mh5=lookup.findVirtual(Handle.class, "getLanguage", mt5);
String str=(String)mh5.invokeExact(handle,"我说中国话");
System.out.println(str);
// getPeoples方法的方法句柄测试
MethodType mt6=MethodType.methodType(int.class,int.class);
MethodHandle mh6=lookup.findStatic(Handle.class,"getPeoples", mt6);
int p=(int)mh6.invokeExact(56);//没有返回值会出错
System.out.println("中国有"+p+"个民族");
}
}
class Handle
{
public void RGA()
{
System.out.println("我是RGA");
}
public void RGB(String str)
{
System.out.println(str);
}
public static void KFC()
{
System.out.println("我是KFC");
}
public static void KFD(String str)
{
System.out.println(str);
}
public String getLanguage(String language)
{
return language;
}
public static int getPeoples(int p)
{
return p;
}
private int getData(int d)
{
return d;
}
}