目录
一、获取类对象
获取类对象是反射的开始。
// Java程序的入口点,main方法是程序从这里开始执行
public static void main(String[] args) throws ClassNotFoundException {
// 获取User类的Class对象
Class<User> c1 = User.class;
// 打印c1的值
System.out.println(c1);
// 获取User类实例的Class对象
Class<?> c2 = new User().getClass();
// 判断c2和c1是否相等,并打印结果
System.out.println(c2 == c1);
// 使用Class.forName方法获取User类的Class对象
Class<?> c3 = Class.forName("week5.day25.clazz.User");
// 判断c3和c1是否相等,并打印结果
System.out.println(c1 == c3);
}
这段代码主要演示了三种获取Java类的Class对象的方式,其中包括:
- 使用类名.class语法获取Class对象;
- 使用对象的getClass()方法获取Class对象;
- 使用Class.forName()方法根据类的完整路径名获取Class对象。
最后,代码还使用了==操作符比较三种方式获取的Class对象是否相等,并将比较结果输出到控制台。
在这三种方式中用得最多的是第三种。
我们作为使用者一般用第一种或者第三种,但作为设计者只能用第三种。
二、反射创建对象
public class InstanceUtil {
// 你给我一个路径,我给你一个对象
// 这是getInstance方法的作用,即获取指定类名的实例对象
public static Object getInstance(String className) {
// 定义Class类型的变量clazz
Class<?> clazz;
try {
// 根据传入的类名className获取对应的Class对象
clazz = Class.forName(className);
// 使用newInstance()方法创建该类的实例对象
Object obj = clazz.newInstance();
// 返回该实例对象
return obj;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
// 如果发生异常或未找到对应的类,则返回null
return null;
}
}
-
类名存在:getInstance方法需要传入一个类名,这个类名必须是已存在的类名,否则会抛出ClassNotFoundException异常。
-
可以实例化:getInstance方法使用clazz.newInstance()方法创建该类的实例对象,因此必须保证这个类是可实例化的,即它必须包含一个公共的默认构造函数,否则会抛出InstantiationException异常。
-
有权限访问:getInstance方法使用clazz.newInstance()方法创建该类的实例对象,因此必须保证这个类的默认构造函数是公共的,否则会抛出IllegalAccessException异常。
public static void main(String[] args) {
Object stu = (Object)InstanceUtil.getInstance("week5.day25.clazz.Student");
System.out.println(stu);
}
三、反射操作构造函数
1、获取指定的构造方法
//获取类对象
Class<?> clazz = Class.forName("week5.day25.User");
//获取无参构造方法
Constructor<?> c1 = clazz.getConstructor();
//使用无参构造方法创建对象
Object obj = c1.newInstance();
//输出创建的对象
System.out.println(obj);
//获取带参构造方法
Constructor<?> c2 = clazz.getConstructor(String.class);
//使用带参构造方法创建对象
Object obj2 = c2.newInstance("张三丰");
//输出创建的对象
System.out.println(obj2);
//获取带有多个参数的构造方法
Constructor<?> c3 = clazz.getConstructor(String.class, int.class);
//使用带有多个参数的构造方法创建对象
Object obj3 = c3.newInstance("张三丰", 20);
//输出创建的对象
System.out.println(obj3);
在这个示例中,首先使用Class.forName()
方法获取了week5.day25.User
类的类对象。然后,通过clazz.getConstructor()
方法获取类的构造方法。接着,使用构造方法创建对象,这里使用了三种不同的构造方法:无参构造方法,带有一个字符串参数的构造方法和带有一个字符串参数和一个整数参数的构造方法。最后,使用System.out.println()
方法输出创建的对象。
2、获取指定的私有方法
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("week5.day5.User");
//这行代码使用getDeclaredConstructor方法获取了User类的String参数构造函数的构造器对象。
//getDeclaredConstructor方法是用来获取该类的指定构造函数的构造器对象。
Constructor<?> c =clazz.getDeclaredConstructor(String.class);
//这行代码通过调用setAccessible方法设置了构造器的可访问性,即使构造器是私有的也可以访问。
//这是为了避免调用受限制的构造函数时出现错误。
c.setAccessible(true);
Object object=c.newInstance("醉梦");
//这行代码使用构造器对象c创建了一个新的User对象,并将其赋值给object变量。
//newInstance方法是用来创建类实例的,它接收构造函数所需的参数并返回一个新的对象实例。
System.out.println(object);
}
-
使用Class.forName方法获取User类的Class对象,该方法需要传入类的全限定名。
-
通过Class对象获取User类中名为String的构造函数的Constructor对象。
-
使用setAccessible方法将Constructor对象的访问权限设置为可访问,因为该构造函数是私有的。
-
使用Constructor的newInstance方法创建一个User类的对象,传入的参数是一个字符串"醉梦"。
-
将创建的对象打印到控制台。在这个例子中,User类必须定义一个接受字符串参数的构造函数才能成功创建对象。
3、分析构造方法中的参数
public class TestAll {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("week5.day25.User");
// 获取所有构造
Constructor<?>[] dcs = clazz.getDeclaredConstructors();
for (Constructor<?> dc : dcs) {
System.out.println(dc);
// 这里需要调用每个构造去创建对象
// 获取参数
int count = dc.getParameterCount();
Object[] params = new Object[count];
System.out.println("\t" + count);
Class<?>[] pts = dc.getParameterTypes();
for (int i = 0; i < pts.length; i++) {
Class pt = pts[i];
System.out.println("\t" + pt.getName());
params[i] = getParam(pt.getName());
}
Object obj = dc.newInstance(params);
System.out.println("\t" + obj);
}
}
private static Object getParam(String name) {
Object o = null;
switch (name) {
case "int":
o = 0;
}
return o;
}
}
这个程序演示了如何使用反射获取一个类的所有构造方法,并使用每个构造方法创建一个对象。
在程序中,使用 Class.forName()
方法获取名为 "week5.day25.User" 的类的 Class
对象。然后,使用 getDeclaredConstructors()
方法获取该类中的所有构造方法,保存在一个 Constructor
数组中。
接下来,循环遍历所有构造方法。对于每个构造方法,首先使用 getParameterCount()
方法获取其参数数量,并使用 getParameterTypes()
方法获取其参数类型。然后,使用一个循环遍历参数类型数组,通过 getParam()
方法获取每个参数的值,并将其保存在一个 Object
数组中。最后,使用 newInstance()
方法创建对象并打印输出。
在 getParam()
方法中,根据参数类型的名称返回一个对应类型的默认值。例如,如果参数类型是 int
,则返回一个整数 0。
需要注意的是,在程序中使用了 throws Exception
来抛出异常,这是因为在使用反射时可能会抛出很多异常,为了简化程序,这里直接将所有异常抛出。
4、分析泛型参数
public class TestGeneric {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("week5.day25.User");
//获取所有构造
Constructor<?>[] dcs = clazz.getDeclaredConstructors();
for(Constructor<?> dc : dcs) {
System.out.println(dc);
//这里需要调用每个构造去创建对象
//获取参数
int count = dc.getParameterCount();
System.out.println("\t"+count);
Type[] gpts = dc.getGenericParameterTypes();
for(Type gpt : gpts) {
System.out.println("\t"+gpt.getTypeName());
if(gpt instanceof ParameterizedType) {
ParameterizedType paramType = (ParameterizedType)gpt;
System.out.println("\t\t"+paramType.getTypeName());
System.out.println("\t\t"+Arrays.toString(paramType.getActualTypeArguments()));
}
}
}
}
private static Object getParam(String name) {
Object o = null;
switch(name) {
case "int":
o = 0;
}
return o;
}
}
这个程序它演示了如何使用反射获取一个类的所有构造方法,并打印出每个构造方法的参数类型和泛型信息。
在程序中,使用 Class.forName()
方法获取名为 "week5.day25.User" 的类的 Class
对象。然后,使用 getDeclaredConstructors()
方法获取该类中的所有构造方法,保存在一个 Constructor
数组中。
接下来,循环遍历所有构造方法。对于每个构造方法,首先使用 getParameterCount()
方法获取其参数数量,并使用 getGenericParameterTypes()
方法获取其参数类型,包括泛型信息。然后,使用一个循环遍历参数类型数组,打印输出每个参数类型的名称,并判断其是否是参数化类型。如果是参数化类型,则将其强制转换为 ParameterizedType
并打印出其实际类型参数。
在 getParam()
方法中,根据参数类型的名称返回一个对应类型的默认值。例如,如果参数类型是 int
,则返回一个整数 0。
四、反射分析方法
1、获取方法
Class<?> clazz = Class.forName("week5.day25.User");
//获取方法
Method m = clazz.getDeclaredMethod("m2", String.class,int.class);
System.out.println(m);
Object obj = clazz.newInstance();
//执行方法
m.setAccessible(true);
m.invoke(obj, "大家哈",88);
获取所有方法:
//获取声明的方法
Method[] dms = clazz.getDeclaredMethods();
for(Method m:dms) {
System.out.println(m);
}
//获取公有方法
Method[] dms1 = clazz.getMethods();
for(Method m:dms1) {
System.out.println(m);
}
获取执行的结果:
Class<?> clazz = Class.forName("week5.day25.User");
Method m = clazz.getMethod("m2", String.class,int.class);
//System.out.println(m.getReturnType());
//接收执行的结果
Object obj = m.invoke(clazz.newInstance(), "张三丰",30);
System.out.println(obj);
这个程序演示了如何使用反射获取一个类的方法并执行该方法。
在程序中,使用 Class.forName()
方法获取名为 "week5.day25.User" 的类的 Class
对象。然后,使用 getMethod()
方法获取名为 "m2" 的公有方法,并将其保存在一个 Method
对象中。注意,这里使用了 getMethod()
方法而不是 getDeclaredMethod()
方法,因为 "m2" 方法是一个公有方法,所以只能使用 getMethod()
方法来获取。
接下来,使用 newInstance()
方法创建一个 User
对象,并将其保存在一个 Object
对象中。然后,使用 setAccessible(true)
方法将 m2
方法的访问权限设置为可访问,并使用 invoke()
方法执行该方法,并将执行结果保存在一个 Object
对象中。
需要注意的是,在执行 invoke()
方法时,需要传递一个对象作为方法的调用者。如果方法是静态方法,则可以将该参数设置为 null
;否则,需要传递一个对象作为该方法的调用者。在本例中,由于 "m2" 方法不是静态方法,所以需要传递一个 User
对象作为其调用者。
在程序中还演示了如何使用 getDeclaredMethods()
方法获取一个类中的所有声明的方法,并使用 getMethods()
方法获取一个类中的所有公有方法。这两个方法都返回一个 Method
数组,可以使用一个循环遍历该数组并打印输出每个方法的信息。注意,使用 getDeclaredMethods()
方法只能获取到一个类中声明的方法,而使用 getMethods()
方法则可以获取到一个类中所有的公有方法,包括继承自其父类的公有方法。
2、获取方法的访问修饰
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("week5.day25.User");
Method m = clazz.getDeclaredMethod("m1", String.class);
int mds = m.getModifiers();
System.out.println(mds);
System.out.println(Modifier.toString(mds));
System.out.println(Modifier.isFinal(mds));
}
这段代码展示了如何使用Java的反射来获取一个方法的修饰符信息。
Class.forName()
方法用于获取指定类的Class对象,getMethod()
方法用于获取指定方法的Method对象,getModifiers()
方法用于获取该方法的修饰符,返回值是一个int类型的数字,每一位表示一个修饰符。可以使用Modifier.toString()
方法将修饰符数字转换为字符串形式,便于阅读和理解。该方法还可以使用一些Modifier
类提供的静态方法判断一个修饰符是否存在于修饰符数字中。
public static String toString(int mod) {
StringBuilder sb = new StringBuilder();
int len;
if ((mod & PUBLIC) != 0) sb.append("public ");
if ((mod & PROTECTED) != 0) sb.append("protected ");
if ((mod & PRIVATE) != 0) sb.append("private ");
/* Canonical order */
if ((mod & ABSTRACT) != 0) sb.append("abstract ");
if ((mod & STATIC) != 0) sb.append("static ");
if ((mod & FINAL) != 0) sb.append("final ");
if ((mod & TRANSIENT) != 0) sb.append("transient ");
if ((mod & VOLATILE) != 0) sb.append("volatile ");
if ((mod & SYNCHRONIZED) != 0) sb.append("synchronized ");
if ((mod & NATIVE) != 0) sb.append("native ");
if ((mod & STRICT) != 0) sb.append("strictfp ");
if ((mod & INTERFACE) != 0) sb.append("interface ");
if ((len = sb.length()) > 0) /* trim trailing space */
return sb.toString().substring(0, len-1);
return "";
}
该方法用于将修饰符的整数值转换为字符串表示。修饰符在 Java 中被定义为整数值,其中每个位都表示不同的修饰符。例如,PUBLIC 是第 0 位,PROTECTED 是第 1 位,PRIVATE 是第 2 位,STATIC 是第 3 位,FINAL 是第 4 位等等。该方法使用按规范顺序列出修饰符的顺序,从而将整数值转换为可读的字符串。例如,如果 mod 参数的值为 9,则返回 "public static"。如果 mod 参数的值为 25,则返回 "public static final"。如果 mod 参数的值为 1025,则返回 "public static abstract"。如果 mod 参数的值不包含任何已知修饰符,则返回空字符串。