本关任务:实现获取Class
对象的三种方式
为了完成本关任务,你需要掌握:
Class
对象;Class
类型的实例的方法;
Object
类中的getClass()
方法;Class.forName("全类名")
;Class.class
。Class
对象之间的区别。Java
运行时系统始终为所有的对象维护一个被称为运行时的类型标识(RTTI
)。这个信息跟踪着每个对象所属的类。虚拟机利用运行时类型信息选择相应的方法执行。保存这些信息的类被称为Class
,可以通过专门的Java
类访问这些信息。Class
类的实例表示正在运行的Java
应用程序中的类和接口。其中枚举是一种特殊的类,注释是一种特殊的接口。每个数组属于被映射为Class
对象的一个类,所有具有相同元素类型和维数的数组都共享该Class
对象基本的Java
类型(boolean
、byte
、char
、short
、int
、long
、float
和double
)和关键字void
也表示为Class
对象。Class
对象就是用来创建类的所有的”常规”对象的。每个类都有一个Class
对象,每当编写一个并且编译了一个新类,就会产生一个Class
对象(保存在体同名的 .class
文件中)。Class
没有公共构造方法。Class
对象是在加载类时由Java
虚拟机以及通过调用类加载器中的defineClass
方法自动构造的。方法1:通过Object
类中的getClass()
方法返回一个Class
类型的实例
示例如下:
Person person = new Person();
Class clazz = person.getClass();
方法2:通过静态方法Class.forName("全类名")
获取类名对应的Class
对象
Class.forName()
方法原型:
public static Class<?> forName(String className) throws ClassNotFoundException
若无法根据类路径className
找到对应的 .class
文件会抛出 ClassNotFoundException
异常,因此使用forName()
方法需要捕获异常或向上抛出异常。
示例如下:
Class clazz = null;
String className = "step1.Person";
try {
clazz = Class.forName(className);
} catch(ClassNotFoundException e) {
}
方法3:通过类字面常量Class.class
获取
示例如下:
Class clazz = Person.class;
该方法不仅更简单,而且更安全,因为它在编译时就会受到检查(因此不需要置于try
语句块中)。并且它根除了对forName()
方法的调用,所以更高效。
每个类都有一个Class
对象,因此对于某个类使用三种方式获取的 Class
对象都是相等。
请仔细阅读右侧代码,结合相关知识,在Begin-End
区域内进行代码补充,完成三个方法getPersonClass1()
、getPersonClass2()
、getPersonClass3()
的代码编写,要求分别使用三种方式获取Person
类的Class
对象并返回。 注意:无需修改main()
方法的输出内容。
平台会对你编写的代码进行测试:
预期输出:
通过Object 类中的 getClass() 获取的 Class 对象为:class step1.Person
通过静态方法 Class.forName() 获取的 Class 对象为:class step1.Person
通过类字面常量获取 Class 的对象为:class step1.Person
开始你的任务吧,祝你成功!
package step1;
/**
* 学员任务文件
*/
public class Reflect_stu {
public static void main(String[] args) {
System.out.println("通过Object 类中的 getClass() 获取的 Class 对象为:" + getPersonClass1());
System.out.println("通过静态方法 Class.forName() 获取的 Class 对象为:" + getPersonClass2());
System.out.println("通过类字面常量获取 Class 的对象为:" + getPersonClass3());
}
/**
* 通过 Object 类中的 getClass() 获取的 Class 对象
*
* @return
*/
public static Class getPersonClass1() {
/********** Begin *********/
Person person = new Person();
Class clazz1 = person.getClass();
return clazz1;
/********** End *********/
}
/**
* 通过静态方法 Class.forName() 获取的 Class 对象
*
* 注意:Person 类的全路径为: step1.Person
*
* @return
*/
public static Class getPersonClass2() {
/********** Begin *********/
String className = "step1.Person";
Class clazz2 = null;
try {
clazz2 = Class.forName(className);
} catch (ClassNotFoundException e) {
}
return clazz2;
/********** End *********/
}
/**
* 通过类字面常量获取 Class 的对象
*
* @return
*/
public static Class getPersonClass3() {
/********** Begin *********/
Class clazz3 = Person.class;
return clazz3;
/********** End *********/
}
}
package step1;
/**
* 评测执行文件
*/
public class Reflect_run {
public static void main(String[] args) {
// Class clazz1 = Person.class;
2、通过对象的getClass()方法返回一个Class类型的实例
// Person person = new Person();
// Class clazz2 = person.getClass();
3、通过静态方法Class.forName()获取类名对应的Class对象
// Class clazz3 = null;
// try {
// clazz3 = Class.forName("step1.Person");
// } catch (ClassNotFoundException e) {
// e.printStackTrace();
// }
//
// // 使用 "==" 进行比较 clazz1 与 clazz2 的关系
// System.out.println(clazz1 == clazz2);
// // 使用 "==" 进行比较 clazz2 与 clazz3 的关系
// System.out.println(clazz2 == clazz3);
System.out.println("通过Object 类中的 getClass() 获取的 Class 对象为:" + getPersonClass1());
System.out.println("通过静态方法 Class.forName() 获取的 Class 对象为:" + getPersonClass2());
System.out.println("通过类字面常量获取 Class 的对象为:" + getPersonClass3());
}
/**
* 通过 Object 类中的 getClass() 获取的 Class 对象
*
* @return
*/
public static Class getPersonClass1() {
return new Person().getClass();
}
/**
* 通过静态方法 Class.forName() 获取的 Class 对象
*
* @return
*/
public static Class getPersonClass2() {
Class clazz = null;
try {
clazz = Class.forName("step1.Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return clazz;
}
/**
* 通过类字面常量获取 Class 的对象
*
* @return
*/
public static Class getPersonClass3() {
return Person.class;
}
}
package step1;
public class Person {
}
本关任务:利用反射获取Apple
类的的所有的方法和构造器签名,以及全部域名。
为了完成本关任务,你需要回顾上节所学Class
对象的相关知识, 以及需要掌握以下知识:
Class
对象与反射之间的关系;Class
的类结构;反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。
Java
反射机制允许程序在运行时取得任何一个已知名称的Class
的内部信息,包括其 modifiers
(修饰符)、fields
(属性),methods
(方法)等,并可于运行时改变 fields
内容或调用 methods
。那么我们便可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;还有动态代理的实现等等;但是需要注意的是反射使用不当会造成很高的资源消耗!
在java.lang.reflect
包中有三个类Field
、Method
和Constructor
分别用于描述类的域、方法和构造器。Class
类中的getFields()
、getMethods()
和getConstructors()
方法将分别返回类提供的 public
域、方法和构造器,其中包括超类的共有成员。Class
类中的getDeclareFields()
、getDeclareMethods()
和getDeclareConstructors()
方法将分别返回类中声明的全部域、方法和构造器,其中包括私有和受保护的成员,但不包括超类的成员。
如:
public Field[] getFields() throws SecurityException
public Method[] getMethods() throws SecurityException
public Constructor<?>[] getConstructors() throws SecurityException
通过获取某个类的Class
对象,并通过Class
类的 getFields()
、getMethods()
和getConstructors()
获得所有域、方法和构造器。
示例:
class Person {
public String name;
String sex;
protected String height;
private int age;
public Person() {
}
private Person(String name) {
this.name = name;
}
public Person(String name, String sex, String height, int age) {
...省略
}
}
public static void main(String[] args) {
Person person = new Person();
printConstructors(person.getClass());
}
public static void printConstructors(Class clazz) {
Constructor[] constructors = clazz.getConstructors();
for (Constructor constructor : constructors) {
String name = constructor.getName();
System.out.print(" ");
String modifiers = Modifier.toString(constructor.getModifiers());
if (modifiers.length() > 0) {
System.out.print(modifiers + " ");
}
System.out.print(name + "(");
Class[] paramTypes = constructor.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 Person();
public Person(java.lang.String,java.lang.String,java.lang.String,int);
请仔细阅读右侧代码,结合相关知识,在Begin-End
区域内进行代码补充,打印Apple
类的所有public
域、方法和构造器。已分别提供了方法声明printConstructors
、printFields
、printMethods
,请将代码补充完整,且按照打印格式要求输出。
提示:
Method.getReturnType()
可以获得方法的返回类型。printModifiers()
方法printParamTypes()
方法Field
的getType
方法可以获得域类型、getName
方法可以获得域的名称预期输出:
private java.lang.String name;
public step2.Apple();
public step2.Apple(java.lang.String);
public void setName(java.lang.String);
平台会对你编写的代码进行测试。
开始你的任务吧,祝你成功!
package step2;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
class Apple {
private String name;
public Apple() {
}
public Apple(String name) {
}
public void setName(String name) {
this.name = name;
}
}
public class Reflect_stu {
public static void main(String[] args) {
// 请根据提供的 classPath 获取 step2.Apple 的 Class 对象, 请使用 Class.forName() 方法, 注意捕获异常
// 通关之后,你也可以修改 clasapath 为其他类路径,分析某个类的能力, 例如: java.util.Date
String classPath = "step2.Apple";
Class clazz = null;
/********** Begin *********/
try {
clazz = Class.forName(classPath);
} catch (ClassNotFoundException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
/********** End *********/
printFields(clazz);
printConstructors(clazz);
printMethods(clazz);
}
/**
* 请打印类的每个域,输出格式为:修饰符 类型 变量名;
*
* @param clazz
*/
public static void printFields(Class clazz) {
/********** Begin *********/
try {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.print(Modifier.toString(field.getModifiers()) + " ");
System.out.print(field.getType().getTypeName() + " ");
System.out.println(field.getName() + ";");
}
} catch (Exception e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
/********** End *********/
}
/**
* 打印构造函数,输出格式为:修饰符 方法名称(参数)
*
* @param clazz
*/
public static void printConstructors(Class clazz) {
Constructor[] constructors = clazz.getDeclaredConstructors();
for (Constructor constructor : constructors) {
Class[] paramTypes = null;
/********** Begin *********/
paramTypes = constructor.getParameterTypes();
System.out.print(Modifier.toString(constructor.getModifiers()) + " ");
System.out.print(constructor.getName() + "(");
/********** End *********/
printParamTypes(paramTypes);
}
}
/**
* 请针对每个方法打印其签名,格式为:修饰符 返回值类型 方法名称(参数);
*
* @param clazz
*/
public static void printMethods(Class clazz) {
Method[] methos = clazz.getDeclaredMethods();
for (Method method : methos) {
Class[] paramTypes = null;
/********** Begin *********/
paramTypes = method.getParameterTypes();
System.out.print(Modifier.toString(method.getModifiers()) + " " + method.getReturnType().getName() + " "
+ method.getName() + "(");
/********** End *********/
printParamTypes(paramTypes);
}
}
/**
* 打印方法参数
*
* @param paramTypes
*/
private static void printParamTypes(Class[] paramTypes) {
for (int j = 0; j < paramTypes.length; ++j) {
if (j > 0) {
System.out.print(",");
}
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
}
package step2;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class Reflect_run {
public static void main(String[] args) {
// 请根据提供的 classPath 获取 step2.Apple 的 Class 对象
String classPath = "step2.Apple";
Class clazz = null;
try {
clazz = Class.forName(classPath);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
printConstructors(clazz);
printFields(clazz);
printMethods(clazz);
}
public static void printConstructors(Class clazz) {
Constructor[] constructors = clazz.getDeclaredConstructors();
for (Constructor constructor : constructors) {
String name = constructor.getName();
printModifiers(clazz);
System.out.print(name + "(");
Class[] paramTypes = constructor.getParameterTypes();
printParamTypes(paramTypes);
}
}
private static void printModifiers(Class clazz) {
System.out.print(" ");
String modifiers = Modifier.toString(clazz.getModifiers());
if (modifiers.length() > 0) {
System.out.print(modifiers + " ");
}
}
public static void printMethods(Class clazz) {
Method[] methos = clazz.getDeclaredMethods();
for (Method method : methos) {
Class returnType = method.getReturnType();
String name = method.getName();
printModifiers(clazz);
System.out.print(returnType.getName() + " " + name + "(");
Class[] paramTypes = method.getParameterTypes();
printParamTypes(paramTypes);
}
}
private static void printParamTypes(Class[] paramTypes) {
for (int j = 0; j < paramTypes.length; ++j) {
if (j > 0) {
System.out.print(",");
}
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
public static void printFields(Class clazz) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
Class type = field.getType();
String name = field.getName();
printModifiers(clazz);
System.out.println(type.getName() + " " + name + ";");
}
}
}
package step2;
public class Person {
public String name;
protected String height;
String sex;
private int age;
public Person() {}
public Person(String name) {
this.name = name;
}
public Person(String name, String sex, String height, int age) {
this.name = name;
this.sex = sex;
this.height = height;
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getHeight() {
return height;
}
public void setHeight(String height) {
this.height = height;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
本关任务:完成一个可供任意类使用的通用toString(Object)
方法。
为了完成本任务,你需要掌握:
Field
类的get
方法获取对象域;Java
安全访问控制获取私有对象域;Field
类的 get
方法获取对象域利用反射机制可以查看在编译时还不清楚的对象域。而查看对象域的关键方法就是Field
类中的get
方法。
Object | get(Object obj) 返回指定对象上此Field表示字段的值。 |
如果f
是一个Field
类型的对象(例如,通过getDeclaredFields()
得到的对象),obj
是一个包含f
域的类的对象, 那么f.get(obj)
将返回一个对象,其值为obj
域的当前值。如此就可以在运行时获得对象的域。
示例:
class Person {
public Integer weight;
private Integer age;
private double height;
// 省略构造器、setter 和 getter 方法
}
public void test() {
Person person = new Person(123, 19);
Class clazz = person.getClass();
Field field = clazz.getDeclaredField("weight");
Object name = field.get(person);
System.out.println(name); // 其打印结果为: "123"
}
Java
安全访问控制获取私有对象域那如果要获取对象的私有域呢,例如,要获取Perso
小明的年龄呢,直接修改上面示例为getDeclaredField("age")
可以吗。答案是,不行,因为age
是一个私有域,所以get
方法会抛出一个 IllegalAccessException
异常。
只有利用get
方法才能得到可访问域的值。除非拥有访问权限,Java
安全机制只允许查看任意对象有哪些域,而不允许读取它们的值。
反射机制的默认行为受限于 Java
的访问控制。然而,如果一个 Java
程序没有收到安全管理器的控制,就可以覆盖访问控制。调用 Field
、Method
或Constructor
对象的setAccessible
方法可以突破这种限制。
示例:
public void test() {
Person person = new Person(123, 19, 175);
Class clazz = person.getClass();
Field field = clazz.getDeclaredField("age");
// 获取私有域的访问权限
field.setAccessible(true);
Object name = field.get(person);
System.out.println(name); // 其打印结果为: "19"
}
Object | get(Object obj) 返回指定对象上此Field表示字段的值。 |
get
方法还有一个要解决的问题。name
域是一个String
,因此可以作为Object
返回。但是,如果要查看height
域,它属于double
类型,而 Java
中数值类型不是对象。因此要使用Field
的其他 getXXX()
方法。反射机制会自动的将这个域值打包到相应的对象包装器中。
double | getDouble(Object obj) 获取double类型或另一个通过扩展转换可以转换为double类型的基本类型的静态或实例字段的值。 |
---|---|
float | getFloat(Object obj) 获取float类型或另一个通过扩展转换可以转换为float类型的基本类型的静态或实例字段的值。 |
Type | getGenericType() 返回一个Tpye对象,它表示此Field对象所表示字段的声明类型。 |
int | getInt(Object obj) 获取int类型或另一个通过扩展转换可以转换为int类型的基本类型的静态或实例字段的值。 |
long | getLong(Object obj) 获取long类型或另一个通过扩展转换可以转换为long类型的基本类型的静态或实例字段的值。 |
请仔细阅读右侧代码,结合相关知识,在Begin-End
区域内进行代码补充,完成通用toString()
方法。
提示:
快速设置访问权限: ``AccessibleObject.setAccessible(fields, true); ``
获得所有域:``Class.getDeclaredFields()``
平台会对你编写的代码进行测试。
示例:
public static void toString(Object obj) {
// 请完成代码
}
public static void main(String[] args) {
Person person = new Person(123, 19, 175);
toString(person);
}
预期输出: [weight=[value=123],age=[value=19],height=[value=175.0]]
开始你的任务吧,祝你成功!
package step3;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class Reflect_stu {
public static String toString(Object obj) {
Class cl = obj.getClass();
String r = "";
r += "[";
// 请获取所有 Field 并设置访问权限为 true
/********** Begin *********/
Field[] fields = null;
fields = cl.getDeclaredFields();
AccessibleObject.setAccessible(fields, true);
/********** End *********/
for (Field f : fields) {
// 此处 if,逻辑为判断 Field 域是否为非静态域
if (!Modifier.isStatic(f.getModifiers())) {
if (!r.endsWith("["))
r += ",";
r += f.getName() + "=";
try {
// 请获取域的类型及值
/********** Begin *********/
Class t = null;
Object val = null;
t = f.getType();
val = f.get(obj);
/********** End *********/
// isPrimitive() 用于判断是否为基本数据类型,若为基础数据类型直接拼接,否则递归调用 toString 方法
if (t.isPrimitive())
r += val;
else
r += toString(val);
} catch (Exception e) {
e.printStackTrace();
}
}
}
r += "]";
return r;
}
public static void main(String[] args) {
Person person = new Person(88, 19, 175);
System.out.println(toString(person));
}
}
class Person {
public Integer weight;
private Integer age;
private Double height;
public Person(Integer weight, Integer age, double height) {
this.weight = weight;
this.age = age;
this.height = height;
}
}
package step3;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class Reflect_run {
public static String toString(Object obj) {
Class cl = obj.getClass();
String r = "";
r += "[";
Field[] fields = cl.getDeclaredFields();
AccessibleObject.setAccessible(fields, true);
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 += "]";
return r;
}
public static void main(String[] args) {
Person person = new Person(88, 19, 175);
System.out.println(toString(person));
}
}
本关任务:利用反射创建对象并调用其方法。
为了完成本关任务,你需要掌握:
反射创建类对象主要有两种方式,通过Class
对象的newInstance()
方法、通过Constructor
对象的 newInstance()
方法。
第一种:通过Class
对象的newInstance()
方法。
Class clazz = Apple.class;
Apple apple = (Apple)clazz.newInstance();
第二种:通过Constructor
对象的newInstance()
方法
Class clazz = Apple.class;
Constructor constructor = clazz.getConstructor();
Apple apple = (Apple)constructor.newInstance();
通过Constructor
对象创建类对象可以选择特定构造方法,而通过 Class
对象则只能使用默认的无参数构造方法。
示例:(调用有参构造方法进行类对象的初始化)
Class clz = Apple.class;
Constructor constructor = clz.getConstructor(String.class, int.class);
Apple apple = (Apple)constructor.newInstance("红富士", 15);
利用Method
的invoke
方法可以调用执行对象obj
的方法
More ActionsObjectinvoke(Object obj,Object… args) 对带有指定参数的指定对象调用由此Method对象表示的底层方法。 |
参数: obj
表示要调用的Method
方法对象。 args
表示要调用的方法的参数,是可变长参数类型。
示例:
// 获取类的 Class 对象实例
Class clz = Class.forName("Apple");
// 根据 Class 对象实例获取 Constructor 对象
Constructor appleConstructor = clz.getConstructor();
// 使用 Constructor 对象的 newInstance 方法获取反射类对象
Object appleObj = appleConstructor.newInstance();
// 而如果要调用某一个方法,则需要经过下面的步骤:
// 1、获取方法的 Method 对象
Method setPriceMethod = clz.getMethod("setPrice", int.class);
// 2、用 invoke 方法调用方法
setPriceMethod.invoke(appleObj, 14);
class Apple {
public void setPrice(int price) {
//省略
}
// 省略
}
请仔细阅读右侧代码,结合相关知识,在Begin-End
区域内进行代码补充,使用反射调用 Apple
类的 setPrice()
方法,设置苹果价格为 14
,并打印价格。接着还要用反射去调用getTotal
方法获取单价为 20
,数量 24
的总金额并打印。
预期输出:
14.0
480.0
平台会对你编写的代码进行测试。
开始你的任务吧,祝你成功!
package step4;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Reflect_stu {
public static void main(String[] args) throws InvocationTargetException {
// 使用反射调用
Class clazz = null;
try {
clazz = Class.forName("step4.Apple");
/********** Begin *********/
Constructor cons = clazz.getConstructor();
Apple apple = (Apple) cons.newInstance();
Method method = clazz.getMethod("setPrice", double.class);
method.invoke(apple, 14);
Method getPrice = clazz.getMethod("getPrice");
System.out.println(getPrice.invoke(apple));
Method getTotal = clazz.getMethod("getTotal", double.class, int.class);
System.out.println(getTotal.invoke(apple, 20, 24));
/********** End *********/
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Apple {
private double price;
private int count;
public Apple() {
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public double getTotal(double price, int count) {
return price * count;
}
}
package step4;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Reflect_run {
public static void main(String[] args) throws InvocationTargetException {
//使用反射调用
Class clazz = null;
try {
clazz = Class.forName("step4.Apple");
Method setPriceMethod = clazz.getMethod("setPrice", double.class);
Constructor appleConstructor = clazz.getConstructor();
Object apple = appleConstructor.newInstance();
setPriceMethod.invoke(apple, 20);
Method getPriceMethod = clazz.getMethod("getPrice");
System.out.println(getPriceMethod.invoke(apple));
Method getTotal = clazz.getMethod("getTotal", double.class, int.class);
System.out.println(getTotal.invoke(apple, 20, 24));
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException e) {
e.printStackTrace();
}
}
}