程序设计基础实践-Java高级特性-Java反射

第1关:了解 Class 对象

100

  • 任务要求
  • 参考答案
  • 评论40

任务描述

本关任务:实现获取Class对象的三种方式

相关知识

为了完成本关任务,你需要掌握:

  1. Class对象;

  2. 三种获取Class类型的实例的方法;

    1. 通过Object类中的getClass()方法;
    2. 通过静态方法Class.forName("全类名")
    3. 通过类字面常量Class.class
  3. 三种方法获取的Class对象之间的区别。

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

  2. Class类的实例表示正在运行的Java应用程序中的类和接口。其中枚举是一种特殊的类,注释是一种特殊的接口。每个数组属于被映射为Class对象的一个类,所有具有相同元素类型和维数的数组都共享该Class对象基本的Java类型(booleanbytecharshortintlongfloatdouble)和关键字void也表示为Class对象。

  3. Class对象就是用来创建类的所有的"常规"对象的。每个类都有一个Class对象,每当编写一个并且编译了一个新类,就会产生一个 Class对象(保存在体同名的 .class 文件中)。

  4. Class没有公共构造方法。Class对象是在加载类时由Java 虚拟机以及通过调用类加载器中的defineClass方法自动构造的。

三种获取Class类型的实例的方法

方法1:通过Object类中的getClass()方法返回一个Class类型的实例

示例如下:

 
  1. Person person = new Person();
  2. Class clazz = person.getClass();

方法2:通过静态方法Class.forName("全类名")获取类名对应的Class对象

Class.forName()方法原型:

 
  1. public static Class<?> forName(String className) throws ClassNotFoundException

若无法根据类路径className找到对应的 .class 文件会抛出 ClassNotFoundException异常,因此使用forName()方法需要捕获异常或向上抛出异常。

示例如下:

 
  1. Class clazz = null;
  2. String className = "step1.Person";
  3. try {
  4. clazz = Class.forName(className);
  5. } catch(ClassNotFoundException e) {
  6. }

方法3:通过类字面常量Class.class获取

示例如下:

 
  1. Class clazz = Person.class;

该方法不仅更简单,而且更安全,因为它在编译时就会受到检查(因此不需要置于try语句块中)。并且它根除了对forName()方法的调用,所以更高效。

三种方法获取的 Class 对象之间的区别

每个类都有一个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 对象
	 * <p>
	 * 注意: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 *********/
	}
}

第2关:利用反射分析类的能力

300

  • 任务要求
  • 参考答案
  • 评论40

任务描述

本关任务:利用反射获取Apple类的的所有的方法和构造器签名,以及全部域名。

相关知识

为了完成本关任务,你需要回顾上节所学Class对象的相关知识, 以及需要掌握以下知识:

  1. 反射的基本概念;

  2. Class对象与反射之间的关系;

  3. Class的类结构;

  4. 利用反射分析类的能力。

反射的基本概念

反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。

Class 对象与反射之间的关系

Java 反射机制允许程序在运行时取得任何一个已知名称的Class 的内部信息,包括其 modifiers(修饰符)、fields(属性),methods(方法)等,并可于运行时改变 fields 内容或调用 methods。那么我们便可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;还有动态代理的实现等等;但是需要注意的是反射使用不当会造成很高的资源消耗!

Class 的类结构

java.lang.reflect包中有三个类FieldMethodConstructor分别用于描述类的域、方法和构造器。Class类中的getFields()getMethods()getConstructors()方法将分别返回类提供的 public 域、方法和构造器,其中包括超类的共有成员。Class类中的getDeclareFields()getDeclareMethods()getDeclareConstructors()方法将分别返回类中声明的全部域、方法和构造器,其中包括私有和受保护的成员,但不包括超类的成员。

如:

 
  1. public Field[] getFields() throws SecurityException
  2. public Method[] getMethods() throws SecurityException
  3. public Constructor<?>[] getConstructors() throws SecurityException
利用反射分析类的能力

通过获取某个类的Class对象,并通过Class类的 getFields()getMethods()getConstructors()获得所有域、方法和构造器。

示例:

 
  1. class Person {
  2. public String name;
  3. String sex;
  4. protected String height;
  5. private int age;
  6. public Person() {
  7. }
  8. private Person(String name) {
  9. this.name = name;
  10. }
  11. public Person(String name, String sex, String height, int age) {
  12. ...省略
  13. }
  14. }
  15. public static void main(String[] args) {
  16. Person person = new Person();
  17. printConstructors(person.getClass());
  18. }
  19. public static void printConstructors(Class clazz) {
  20. Constructor[] constructors = clazz.getConstructors();
  21. for (Constructor constructor : constructors) {
  22. String name = constructor.getName();
  23. System.out.print(" ");
  24. String modifiers = Modifier.toString(constructor.getModifiers());
  25. if (modifiers.length() > 0) {
  26. System.out.print(modifiers + " ");
  27. }
  28. System.out.print(name + "(");
  29. Class[] paramTypes = constructor.getParameterTypes();
  30. for (int j = 0; j < paramTypes.length; ++j) {
  31. if (j > 0) {
  32. System.out.print(",");
  33. }
  34. System.out.print(paramTypes[j].getName());
  35. }
  36. System.out.println(");");
  37. }
  38. }

输出结果: public Person(); public Person(java.lang.String,java.lang.String,java.lang.String,int);

编程要求

请仔细阅读右侧代码,结合相关知识,在 Begin-End 区域内进行代码补充,打印Apple类的所有public 域、方法和构造器。已分别提供了方法声明printConstructorsprintFieldsprintMethods,请将代码补充完整,且按照打印格式要求输出。

提示:

  1. Method.getReturnType()可以获得方法的返回类型。

  2. 打印方法或域的修饰符可以调用提供的printModifiers()方法

  3. 打印方法的参数可以调用提供的printParamTypes()方法

  4. FieldgetType方法可以获得域类型、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(");");
	}

}

第3关:在运行时使用反射分析对象

300

  • 任务要求
  • 参考答案
  • 评论40

任务描述

本关任务:完成一个可供任意类使用的通用toString(Object)方法。

相关知识

为了完成本任务,你需要掌握:

  1. 如何通过Field类的get方法获取对象域;

  2. 如何绕过 Java 安全访问控制获取私有对象域;

  3. 获取对象域时数值类型如何处理。

如何通过 Field 类的 get 方法获取对象域

利用反射机制可以查看在编译时还不清楚的对象域。而查看对象域的关键方法就是Field类中的get方法。

Objectget(Object obj)  返回指定对象上此Field表示字段的值。

如果f是一个Field类型的对象(例如,通过getDeclaredFields() 得到的对象),obj是一个包含f域的类的对象, 那么f.get(obj)将返回一个对象,其值为obj域的当前值。如此就可以在运行时获得对象的域。

示例:

 
  1. class Person {
  2. public Integer weight;
  3. private Integer age;
  4. private double height;
  5. // 省略构造器、setter 和 getter 方法
  6. }
  7. public void test() {
  8. Person person = new Person(123, 19);
  9. Class clazz = person.getClass();
  10. Field field = clazz.getDeclaredField("weight");
  11. Object name = field.get(person);
  12. System.out.println(name); // 其打印结果为: "123"
  13. }
如何绕过 Java 安全访问控制获取私有对象域

那如果要获取对象的私有域呢,例如,要获取Perso小明的年龄呢,直接修改上面示例为getDeclaredField("age")可以吗。答案是,不行,因为age是一个私有域,所以get方法会抛出一个 IllegalAccessException异常。

只有利用get方法才能得到可访问域的值。除非拥有访问权限,Java 安全机制只允许查看任意对象有哪些域,而不允许读取它们的值。

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

示例:

 
  1. public void test() {
  2. Person person = new Person(123, 19, 175);
  3. Class clazz = person.getClass();
  4. Field field = clazz.getDeclaredField("age");
  5. // 获取私有域的访问权限
  6. field.setAccessible(true);
  7. Object name = field.get(person);
  8. System.out.println(name); // 其打印结果为: "19"
  9. }
获取对象域时数值类型如何处理
Objectget(Object obj)  返回指定对象上此Field表示字段的值。

get方法还有一个要解决的问题。name域是一个String,因此可以作为Object返回。但是,如果要查看height域,它属于double类型,而 Java 中数值类型不是对象。因此要使用Field的其他 getXXX() 方法。反射机制会自动的将这个域值打包到相应的对象包装器中。

doublegetDouble(Object obj)  获取double类型或另一个通过扩展转换可以转换为double类型的基本类型的静态或实例字段的值。
floatgetFloat(Object obj)  获取float类型或另一个通过扩展转换可以转换为float类型的基本类型的静态或实例字段的值。
TypegetGenericType()  返回一个Tpye对象,它表示此Field对象所表示字段的声明类型。
intgetInt(Object obj)  获取int类型或另一个通过扩展转换可以转换为int类型的基本类型的静态或实例字段的值。
longgetLong(Object obj)  获取long类型或另一个通过扩展转换可以转换为long类型的基本类型的静态或实例字段的值。
编程要求

请仔细阅读右侧代码,结合相关知识,在 Begin-End 区域内进行代码补充,完成通用toString()方法。

提示:

 
  1. 快速设置访问权限: ``AccessibleObject.setAccessible(fields, true); ``
  2. 获得所有域:``Class.getDeclaredFields()``
测试说明

平台会对你编写的代码进行测试。

示例:

 
  1. public static void toString(Object obj) {
  2. // 请完成代码
  3. }
  4. public static void main(String[] args) {
  5. Person person = new Person(123, 19, 175);
  6. toString(person);
  7. }

预期输出: [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;
	}
}

第4关:利用反射进行方法调用

300

  • 任务要求
  • 参考答案
  • 评论40

任务描述

本关任务:利用反射创建对象并调用其方法。

相关知识

为了完成本关任务,你需要掌握:

  1. 如何通过反射创建对象;

  2. 如何通过反射调用对象方法。

如何通过反射创建对象

反射创建类对象主要有两种方式,通过Class对象的newInstance()方法、通过Constructor对象的 newInstance()方法。

  1. 第一种:通过Class对象的newInstance()方法。

     
      
    1. Class clazz = Apple.class;
    2. Apple apple = (Apple)clazz.newInstance();
  2. 第二种:通过Constructor对象的newInstance()方法

     
      
    1. Class clazz = Apple.class;
    2. Constructor constructor = clazz.getConstructor();
    3. Apple apple = (Apple)constructor.newInstance();

通过Constructor对象创建类对象可以选择特定构造方法,而通过 Class对象则只能使用默认的无参数构造方法。

示例:(调用有参构造方法进行类对象的初始化)

 
  1. Class clz = Apple.class;
  2. Constructor constructor = clz.getConstructor(String.class, int.class);
  3. Apple apple = (Apple)constructor.newInstance("红富士", 15);
如何通过反射调用对象方法

利用Methodinvoke方法可以调用执行对象obj的方法

Objectinvoke(Object obj,Object... args)  对带有指定参数的指定对象调用由此Method对象表示的底层方法。

参数: obj 表示要调用的Method方法对象。 args 表示要调用的方法的参数,是可变长参数类型。

示例:

 
  1. // 获取类的 Class 对象实例
  2. Class clz = Class.forName("Apple");
  3. // 根据 Class 对象实例获取 Constructor 对象
  4. Constructor appleConstructor = clz.getConstructor();
  5. // 使用 Constructor 对象的 newInstance 方法获取反射类对象
  6. Object appleObj = appleConstructor.newInstance();
  7. // 而如果要调用某一个方法,则需要经过下面的步骤:
  8. // 1、获取方法的 Method 对象
  9. Method setPriceMethod = clz.getMethod("setPrice", int.class);
  10. // 2、用 invoke 方法调用方法
  11. setPriceMethod.invoke(appleObj, 14);
  12. class Apple {
  13. public void setPrice(int price) {
  14. //省略
  15. }
  16. // 省略
  17. }
编程要求

请仔细阅读右侧代码,结合相关知识,在 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;
	}
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 《Java核心技术 卷II:高级特性(原书第10)》是由美国著名计算机科学家Cay S. Horstmann所写的一本Java高级编程技术书籍,是Java程序员必读的经典之一。 本书囊括了Java中许多高级特性和技巧,如泛型、集合、并发编程、网络编程、注解、反射等。其中,泛型是本书的一个重点,它能够提高代码的类型安全性和可读性,降低代码编写的复杂度和维护的难度。集合则是Java程序员必备的工具之一,本书详细介绍了集合框架的设计和使用,可以帮助开发人员更加高效地处理数据结构。 并发编程是Java的特色之一,也是实现高性能应用的必备技能。本书详细介绍了Java并发编程的原理、技巧和最佳实践,包括线程、线程池、锁、原子操作、并发容器等。这些技术在提高应用性能的同时,也要注意线程安全和死锁等问题。 网络编程是Java程序员必备的另一个技能,本书介绍了Java中的Socket编程和Java NIO(New IO)技术,能够帮助读者更好地理解网络编程的原理和实现。 此外,本书还介绍了注解和反射高级特性,为Java程序员提供了更加灵活、扩展和自动化的编程方式。 总的来说,《Java核心技术 卷II:高级特性(原书第10)》是一本权威、全面、深入的Java高级编程技术书籍,能够帮助Java程序员提升自己的编程能力,实现高性能、高可靠的应用程序。 ### 回答2: 《Java核心技术 卷II:高级特性(原书第10)》是一本深入讲解Java高级特性的经典书籍。本书从面向对象编程、泛型、反射、注解、枚举、Lambda表达式、并发编程、网络编程、JDBC等多个方面全面阐述了Java语言的高级特性。 在本书中,作者对Java的面向对象编程进行了深入的解析,包括继承、多态、抽象类和接口等重要概念的详细讲解。此外,本书还详细介绍了Java语言中的泛型、反射和注解等高级特性,为读者深入理解Java语言的运行机制提供了良好的基础。 本书还特别强调了Java语言中的并发编程技术。通过讲述线程、锁、原子操作等多个并发编程技术的实现方式和应用场景,让读者深入了解Java语言在多线程编程方面的实现方式和应用方法。 此外,本书还详细介绍了Java语言中的网络编程和JDBC技术。通过讲解网络协议、Socket编程、URL和URLConnection等相关知识点,读者能够深入了解Java语言在网络编程方面的实现方式和运用场景。同时,本书还介绍了Java语言中的JDBC技术,包括数据库连接、事务处理、SQL编程和ORM等内容,让读者全面掌握Java语言在数据库编程方面的应用技巧。 总的来说,《Java核心技术 卷II:高级特性(原书第10)》是一本系统全面介绍Java语言高级特性和应用技术的书籍,对于Java开发人员来说,是一本不可多得的学习资料。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值