文章目录
- 【案例12-1】:重写toString()方法
- **【案例介绍】**
- **【案例目标】**
- **【案例分析】**
- **【案例实现】**
- 【案例12-2】:速度计算
- **【案例介绍】**
- **【案例目标】**
- **【案例分析】**
- **【案例实现】**
- 【案例12-3】:利用反射实现通过读取配置文件对类进行实例化
- **【案例介绍】**
- **【案例目标】**
- **【案例分析】**
- **【案例实现】**
【案例12-1】:重写toString()方法
【案例介绍】
1.案例描述
为了方便输出对象,Object类提供了toString()方法。但是该方法的默认值是由类名和哈希码组成的,实用性并不强。通常需要重写该方法以提供更多的对象信息。
本案例要求使用反射重写类的toString()方法,并通过反射输出类的包名、类名、类的公共构造方法、类的公共域和类的公共方法。
2.运行结果
【案例目标】
- 学会分析“利用反射重写toString()方法”实现的逻辑思路。
- 能够独立完成“利用反射重写toString()方法”的源代码编写、编译及运行。
- 掌握反射机制的应用。
【案例分析】
(1)通过任务的描述可知,此程序需要利用反射重写toString()方法,因此,需要先创建一个类,并在该类中定义两个方法,一个是toString()方法,用于输出类的包、类的名字、类的公共构造方法、类的公共域和类的公共方法等信息;另一个是main()方法,用来进行测试。
(2)由于是重写Object类的toString()方法,因此需要给toString()方法传递一个Object对象。
(3)由于需要利用反射输出类的包、类的名字、类的公共构造方法、类的公共域和类的公共方法,故需要先通过Object对象.getClass()获得代表该类的Class对象,再通过类的Class对象. getPackage()获得类所在的包,通过类的Class对象.getSimpleName()获得类的简单名称,通过类的Class对象.getDeclaredConstructors()获得所有代表构造方法的Constructor数组,遍历数组,判断如果是访问控制符为“public”即为公共构造方法。通过类的Class对象.getDeclaredFields()获得代表所有域的Field数组,遍历数组,判断如果是访问控制符为“public”即为公共域。通过类的Class对象.getDeclaredMethods()获得代表所有方法的Method[]数组,遍历数组,判断如果是访问控制符为“public”即为公方法。
【案例实现】
StringUtils.java
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class StringUtils {
@SuppressWarnings("unchecked")
public String toString(Object object) {
// 获得代表该类的Class对象
Class clazz = object.getClass();
// 利用StringBuilder来保存字符串
StringBuilder sb = new StringBuilder();
// 获得类所在的包
Package packageName = clazz.getPackage();
// 输出类所在的包
sb.append("包名:" + packageName.getName() + "\t");
String className = clazz.getSimpleName(); // 获得类的简单名称
sb.append("类名:" + className + "\n"); // 输出类的简单名称
sb.append("公共构造方法:\n");
// 获得所有代表构造方法的Constructor数组
Constructor[] constructors = clazz.getDeclaredConstructors();
for (Constructor constructor : constructors) {
String modifier =
Modifier.toString(constructor.getModifiers());// 获得方法修饰符
if (modifier.contains("public")) {// 查看修饰符是否含“public”
sb.append(constructor.toGenericString() + "\n");
}
}
sb.append("公共域:\n");
// 获得代表所有域的Field数组
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
String modifier = Modifier.toString(field.getModifiers());
if (modifier.contains("public")) {// 查看修饰符是否含“public”
sb.append(field.toGenericString() + "\n");
}
}
sb.append("公共方法:\n");
// 获得代表所有方法的Method[]数组
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
String modifier = Modifier.toString(method.getModifiers());
// 查看修饰符是否含有“public”
if (modifier.contains("public")) {
sb.append(method.toGenericString() + "\n");
}
}
return sb.toString();
}
public static void main(String[] args) {
System.out.println(new StringUtils().toString(new Object()));
}
}
上述代码中,第50行代码的new StringUtils().toString(new Object())创建了一个StringUtils对象并调用了其toString()方法,并向toString()方法中传递了一个Object对象。第9行代码通过Object对象.getClass()获得代表该类的Class对象;
第13行代码通过Object对象. getPackage()获得类所在的包;第20行代码通过Object对象. getDeclaredConstructors()获得所有代表构造方法的Constructor[]数组,第21-27行代码为遍历Constructor数组,判断如果是访问控制符为“public”即为公共构造方法。
第30行代码通过类的Class对象.getDeclaredFields()获得代表所有域的Field[]数组,第31-36行代码为遍历Field数组,判断如果是访问控制符为“public”即为公共域。第39行代码通过类的Class对象.getDeclaredMethods()获得代表所有方法的Method[]数组,第40-48行代码为遍历Method[]数组,判断如果是访问控制符为“public”即为公方法。
【案例12-2】:速度计算
【案例介绍】
1.案例描述
本案例要求使用反射技术编写一个速度计算程序,计算某种交通工具的行驶速度。现有两种工具:Bike和 Plane,其中Bike的速度运算公式为:A*B/C,Plane的速度运算公式为:A+B+C。
用户可通过输入交通工具名称选择自己想要使用的交通工具,选择交通工具之后,自动计算出该交通工具的行驶速度。此外,在未来如果增加第3种交通工具的时候,不必修改以前的任何程序,只需要编写新的交通工具的程序即可。
2.运行结果
【案例目标】
-
学会分析“速度计算程序设计”实现的逻辑思路。
-
能够独立完成“速度计算程序设计”的源代码编写、编译及运行。
-
掌握反射机制的应用。
【案例分析】
(1)通过任务描述可知,有两种交通工具Plane和Bike:Plane类、Bike类。
(2)由于任务要求在未来如果增加第3种交通工具的时候,不必修改以前的任何程序,只需要编写新的交通工具的程序,故还需要编写一个接口Common,且Plane类和Bike类都继承Common接口。
(3)最后编写一个测试类CaculateSpeed,在main()方法中,编写程序,提示用户输入自己想要使用的交通工具,并利用反射来计算交通工具的速度。
【案例实现】
Common.java
public interface Common {
double getSpeed(double a,double b,double c);
}
上述代码中,定义了一个Common接口,在Common接口中定义了一个抽象方法getSpeed()。
Bike.java
public class Bike implements Common {
@Override
public double getSpeed(double a, double b, double c) {
return a*b/c;
}
}
上述代码中,定义了一个Bike类并实现了Common接口,在Bike类中对getSpeed()方法方法进行了重写。
Plane.java
public class Plane implements Common {
@Override
public double getSpeed(double a, double b, double c) {
return a+b+c;
}
}
上述代码中,定义了一个Plane类并实现了Common接口,在Plane类中对getSpeed()方法进行了重写。
CaculateSpeed.java
import java.util.Scanner;
public class CaculateSpeed {
public static void main(String[] args){
Scanner in = new Scanner(System.in);
System.out.println("请输入您要使用的交通工具名称:");
String choice =in.nextLine();
String transport = "fanshe."+choice; //
double a = 23, b = 24, c = 25;
try {
Common newtransport = (Common)
Class.forName(transport).newInstance();
System.out.println(choice+" speed is :
"+newtransport.getSpeed(a,b,c));
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
上述代码中,定义了一个测试类CaculateSpeed,其中第7行代码声明了一个transport对象,用于获取全限定类名;第10~11行代码是通过反射对transport对象进行实例化。
【案例12-3】:利用反射实现通过读取配置文件对类进行实例化
【案例介绍】
1.案例描述
现在有一个项目,项目中创建了一个Person类,在Person类中定义了一个sleep()方法。在工程中还定义了一个Student类继承Person类,在Student类中重写了Person类的sleep()方法。项目有一个配置文件,名称为test.properties,在配置文件中配置了一个className属性和一个methodName属性,className属性值是类的全限定类名,methodName属性值是方法名。
本案例要求通过读取配置文件对类进行实例化,具体如下:
(1)获取test.properties配置文件中的className属性值(类的全限定类名),利用反射对该类进行实例化。
(2)获取test.properties配置文件中的methodName属性值(方法名),利用反射获取对象方法,并执行该方法。
2.运行结果
情景一:
配置文件中的信息:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HOJI2Mmb-1649842587138)(C:/Users/23061/AppData/Local/Temp/msohtmlclip1/01/clip_image001.png)]
运行结果:
情景二:
配置文件中的信息:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tdosCoiR-1649842587140)(C:/Users/23061/AppData/Local/Temp/msohtmlclip1/01/clip_image004.png)]
运行结果:
【案例目标】
-
学会分析“利用反射实现通过读取配置文件信息信息对类进行实例化”程序任务实现的逻辑思路。
-
能够独立完成“利用反射实现通过读取配置文件信息对类进行实例化” 程序的源代码编写、编译及运行。
-
掌握反射机制的应用。
【案例分析】
(1)通过任务描述可知,需要先在工程的根目录下创建一个test.properties文件,在配置文件中配置一个className属性和一个methodName属性,className属性值是类的全限定类名,methodName属性值是方法名。
(2)然后创建两个类:Person类和Student类且Student类继承Person类。在Person类中编写一个sleep()方法,在Student类中重写Person类的sleep()方法;
(3)最后编写一个测试类ReflexTest,在main()方法中,编写程序,具体步骤描述如下:
-
利用反射加载配置文件
-
获取配置文件中的数据,获取类的全路径名及方法名
-
根据获取的类的全路径名,利用反射将该类加载进内存
-
创建该类对象
-
根据在配置文件中获取的方法名获取对象方法
-
执行方法
【案例实现】
情景一
test.properties
className = fanshe.Person
methodName = sleep
上述配置文件中,定义了classNmae和methodName属性并赋值。
Person.java
public class Person {
public void sleep() {
System.out.println("sleep......");
}
}
上述代码中,创建了一个Person类,并在Person类中编写了一个sleep方法。
Student.java
public class Student extends Person{
@Override
public void sleep() {
super.sleep();
System.out.println("呼噜呼噜~~~");
}
public void s1() {
super.sleep();
System.out.println("hello");
}
}
上述代码中,定义了一个Student类并继承了Person类。在Student类中重写了父类Person中的sleep()方法,然后,编写了一个s1()方法。
ReflexTest.java
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
public class ReflexTest
{
public static void main(String[] args) throws IOException,
ClassNotFoundException, InstantiationException,
IllegalAccessException, NoSuchMethodException,
SecurityException, IllegalArgumentException,
InvocationTargetException
{
/*1、加载配置文件
* 用类名.class.getResourceAsStream("/xx")或者
* 类名.class.getClassLoader().getResourceAsStream("xx");
* 区别在于前者是需要反斜杠,后者不需要
* */
Properties properties = new Properties();
properties.load(RelectTestMain.class.getResourceAsStream("/test.properties"));
//2、获取配置文件中定义的数据
String className = properties.getProperty("className");
String methodName = properties.getProperty("methodName");
//3、加载该类进内存
Class cls = Class.forName(className);
//4、创建类对象
Object obj = cls.newInstance();
//5、获取对象方法
Method method = cls.getMethod(methodName);
//6、执行方法
method.invoke(obj);
}
}
上述代码中,第15-17行代码,利用反射加载了test.properties配置文件;第19-20行代码,获取配置文件中className和methodName属性的信息;第22行代码,根据获取的类的全路径名,利用反射将该类加载进内存;第24行代码,创建在配置文件中获取的类的对象;第26行代码,根据在配置文件中获取的方法名获取对象方法;第28行,执行该方法。
情景二
只需要修改test.properties配置文件即可。
test.properties
sclassName = fanshe.Student
methodName = s1