CH12-Java反射机制

文章目录

    • 【案例12-1】:重写toString()方法
      • **【案例介绍】**
      • **【案例目标】**
      • **【案例分析】**
      • **【案例实现】**
    • 【案例12-2】:速度计算
      • **【案例介绍】**
      • **【案例目标】**
      • **【案例分析】**
      • **【案例实现】**
    • 【案例12-3】:利用反射实现通过读取配置文件对类进行实例化
      • **【案例介绍】**
      • **【案例目标】**
      • **【案例分析】**
      • **【案例实现】**

【案例12-1】:重写toString()方法

【案例介绍】

1.案例描述

​ 为了方便输出对象,Object类提供了toString()方法。但是该方法的默认值是由类名和哈希码组成的,实用性并不强。通常需要重写该方法以提供更多的对象信息。

​ 本案例要求使用反射重写类的toString()方法,并通过反射输出类的包名、类名、类的公共构造方法、类的公共域和类的公共方法。

2.运行结果

img

【案例目标】

  • 学会分析“利用反射重写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.运行结果

img

【案例目标】

  • 学会分析“速度计算程序设计”实现的逻辑思路。

  • 能够独立完成“速度计算程序设计”的源代码编写、编译及运行。

  • 掌握反射机制的应用。

【案例分析】

​ (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

情景二:

配置文件中的信息:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tdosCoiR-1649842587140)(C:/Users/23061/AppData/Local/Temp/msohtmlclip1/01/clip_image004.png)]

运行结果:

img

【案例目标】

  • 学会分析“利用反射实现通过读取配置文件信息信息对类进行实例化”程序任务实现的逻辑思路。

  • 能够独立完成“利用反射实现通过读取配置文件信息对类进行实例化” 程序的源代码编写、编译及运行。

  • 掌握反射机制的应用。

【案例分析】

​ (1)通过任务描述可知,需要先在工程的根目录下创建一个test.properties文件,在配置文件中配置一个className属性和一个methodName属性,className属性值是类的全限定类名,methodName属性值是方法名。

​ (2)然后创建两个类:Person类和Student类且Student类继承Person类。在Person类中编写一个sleep()方法,在Student类中重写Person类的sleep()方法;

​ (3)最后编写一个测试类ReflexTest,在main()方法中,编写程序,具体步骤描述如下:

  1. 利用反射加载配置文件

  2. 获取配置文件中的数据,获取类的全路径名及方法名

  3. 根据获取的类的全路径名,利用反射将该类加载进内存

  4. 创建该类对象

  5. 根据在配置文件中获取的方法名获取对象方法

  6. 执行方法

【案例实现】

情景一

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
  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

绿洲213

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值