反射与注解

一、简介

反射介绍:

框架:半成品软件。可以在框架的基础上进行软件开发,简化编码。

反射:将类的各个组成部分封装为其他对象,这就是反射机制。反射是框架设计的灵魂。

注:写框架用到反射,用框架不需要反射。

好处:

  1. 可以在程序运行过程中,操作这些对象
    例如:String str =“a”;
    str. 会发现idea给我们好多方法提示
    前提:idea是一直在运行。
    (内部用的反射机制,定义一个字节码文件,把字节码文件封装成对象,把所有的方法放到Method对象数组中,用的时候把数组里面的所有方法展示)
  2. 可以解耦,提高程序的可扩展性。

类的简介:

用以下6个类,就可以描述任何一个类。

类名解释说明
Class用于描述类本身
Package用于描述类所属的包
Field用于描述类中的属性
Constructor用于描述类中的构造方法
Method用于描述类中的方法
Annotation用于描述类中的注解

二、反射

1.Class类

Class类 用于描述类本身。我们知道,类是用来描述具有相同特征的一组对象的,而这个Class类是用来描述类本身的。

1.获取Class对象的方式:

  1. Class.forName(“包名.类名”);
    将字节码文件加载进内存,返回Class对象。
    多用于配置文件,将类名定义在配置文件中,读取文件,加载类。
  2. 类名.class;
    通过类名的属性.class获取,多用于参数的传递。
  3. 对象.getClass();
    getClass()方法在Object类中定义着,多用于对象的获取字节码方式。

结论:
同一个字节码( .class)文件,在一次程序运行过程中只会被加载一次。即:不论通过哪种方式获取Class对象,其实得到的都是同一个对象(用 " == " 号判断会返回true)。

2.重要方法

·操作成员:
方法返回值解释说明
newInstance()Object用于创建一个当前类的实例对象,
相当于调用了当前类的无参构造方法。
获取类中的属性
getField(“属性名”)Field用于获取指定名称的、 public修饰的属性,
也可以是父类中的 public属性。
getFields()Field[]用于获取所有的public修饰的属性,
也可以是父类中的 public属性。
getDeclaredField(“属性名”)Field获取指定名称的属性,不考虑修饰符,
但只能获取本类中的属性,不可获取父类中的属性 。
getDeclaredFields()Field[]获取当前类中的所有属性,不考虑修饰符。
但只能获取本类中的属性,不可获取父类中的属性 。
获取类的构造方法
getConstructor(
Class<?>… parameterTypes)
Constructor获取当前类中某个public修饰的构造方法。
Class<?>是该构造方法的形参表列,这里需传入参数.class类型。如果使用空参数构造方法创建对象,操作可以简化为:Class对象的newInstance方法.
getConstructors()Constructor[]用于获取所有的public修饰的构造方法。
getDeclaredConstructor(
Class<?>… parameterTypes)
Constructor获取类中的某个构造方法,不考虑修饰符。
Class<?>是该构造方法的形参表列,这里需传入参数.class类型。
getDeclaredConstructors()Constructor[]用于获取类中的所有构造方法,不考虑修饰符。
获取类中的方法:
getMethod(“方法名”
,Class<?>… parameterTypes)
Method获取指定名称的、 public修饰的类内方法,
也可以是父类中的 public方法。
Class<?>是该方法的形参表列,这里需传入参数.class类型。
getMethods()Method[]用于获取所有的public修饰的方法,
也可以是父类中的 public方法。
getDeclaredMethod(“方法名”
, Class<?>… parameterTypes)
Method用于获取指定名称的方法,不考虑修饰符,
但只能获取本类中的方法,不可获取父类中的方法 。
getDeclaredMethods()Method[]获取当前类中的所有方法,不考虑修饰符。
但只能获取本类中的方法,不可获取父类中的方法 。
获取类、属性、方法上的注解:
getDeclaredAnnotationsByType
(Class annotationClass)
Annotation[]通过指定的注解类型,获取所有注解。
getAnnotation(Class annotationClass)Annotation通过指定的注解类型,获取注解。
getAnnotations()Annotation[]获取所有的注解。
getDeclaredAnnotation
(Class annotationClass)
Annotation通过指定的注解类型,获取注解。
getDeclaredAnnotations()Annotation[]获取所有的注解。
·查看性质:
方法返回值解释说明
getModifiers()int用于获取类的修饰符,包括权限修饰符和特征修饰符。
每个修饰符都用一个固定不变的static修饰的int来表示,
从0开始,依次是0、1、2、4、8 、16、32 …
如果该类的修饰符有多个,则返回的int值是多个修饰符的和。
(0–默认不写、1–public、2–private、4–protected、8–static、
16–final、32–synchronize、64–volatile、128–transient、
256–native、512–interface、1024–abstract、2048–strict)
getSuperclass()Class获取当前类 单继承的父类。
getInterfaces()Class[]获取当前类 多实现的所有接口。
getClasses()Class[]获取当前类中,public修饰的内部类。
getName()String获取类的全名,包括包名和类名。
getSimpleName()String只获取当前类的类名。
getPackage()Package获取当前类所属的包。
通常会调用Package对象下的getName()方法查看包名。

2.Field类

Field类 用于描述类中的属性。

方法返回值解释说明
set(Object obj, Object value)void赋值操作:
调用此方法,可以给某个实例对象的当前属性赋具体值。
obj表示想要操作的实例对象,value是赋给当前属性的具体值。
get(Object obj)Object取值操作:
调用此方法,可以取出某个实例对象,当前属性的实际值。
setAccessible(true)void实现暴力反射,调用此方法后,可以操作非public修饰的属性。
getType()Class用于获取当前属性的数据类型(包名.类名)。
getModifiers()int用于获取当前属性的修饰符(包括权限和特征),
每个修饰符都用一个整数来表示,详情看上面Class表的描述。
getName()String用于获取当前属性的属性名。

3.Constructor类

Constructor类 用于描述类中的构造方法。

方法返回值解释说明
newInstance(Object … initargs)Object用于创建一个当前类的实例对象,
相当于调用了当前类相应的构造方法。
参数 initargs表示的是当前构造方法的形参表列。
getParameterTypes()Class[]用于获取当前方法的形参表列的数据类型(包名.类名)。
getExceptionTypes()Class[]获取当前方法抛出异常的数据类型(包名.类名)。
setAccessible(true)void实现暴力反射,调用此方法后,可以操作非public修饰的构造方法。
getModifiers()int用于获取当前属性的修饰符(包括权限和特征),
每个修饰符都用一个整数来表示,详情看上面Class表的描述。
getName()String用于获取当前属性的属性名。

4.Method类

Method类 用于描述类中的方法。

方法返回值解释说明
invoke(Object obj, Object… args)Object让某个实例对象obj,调用当前方法。
后面的args是执行此方法时需要的实参。
而此invoke方法的返回值,就是执行此方法后的实际返回值。
getReturnType()Class用于获取当前方法的返回值的数据类型(包名.类名)。
getParameterTypes()Class[]用于获取当前方法的形参表列的数据类型(包名.类名)。
setAccessible(true)void实现暴力反射,调用此方法后,可以操作非public修饰的方法。
getAnnotation(Class annotationClass)泛型T用于获取当前方法上的某个特定注解。
getExceptionTypes()Class[]获取当前方法抛出异常的数据类型(包名.类名)。
getModifiers()int用于获取当前方法的修饰符(包括权限和特征),
每个修饰符都用一个整数来表示,详情看上面Class表的描述。
getName()String用于获取当前方法的方法名。

5.Annotation类

Annotation类 用于描述类中的注解。

  1. 注解可以放置的位置:
    类的上面、属性上面、构造方法上面、普通方法上面、参数前面

  2. 注解中可以携带的信息(信息不能随意写,信息的类型只能是如下的类型。):

  • 基本数据类型(不可以是包装类)
  • String类型
  • 枚举类型enum
  • 注解类型@
  • 数组类型[](数组的内部需要是如上的四种类型)

案例:

  1. 自定义一个 MyAnnotation 注解类型:
import java.lang.annotation.*;

/**
 * 如何自定义一个注解:
 * 1.通过 @interface 定义一个注解类型.
 *
 * 2.发现写法与接口非常相似(可以利用接口的特点来记忆注解)
 * 可以描述public static final的属性,但实际应用中较为少见
 * 可以描述public abstract的方法,但方法的返回值不能是void,要求返回值必须是注解内可以携带信息的那些数据类型中的
 *
 * 3.我们自己定义的注解如果想要拿来使用
 * 光定义是不够的,还需要做很多细致的说明(需要利用Java提供好的元注解来进行说明)
 * 元注解:也是注解,不过不是拿来使用的,而是用来说明注解的。
 * @Target: 描述当前这个自定义注解可以放置的位置
 * @Retention: 描述当前的这个注解存在什么作用域中
 * 作用域包括:源代码文件--->编译-->字节码文件--->类加载--->在内存中执行
 *           SOURCE              CLASS              RUNTIME
 * @Inherited: 描述当前这个注解,能否被子类对象继承(添加表示能被子类继承)
 */
@Target({ElementType.FIELD,ElementType.CONSTRUCTOR,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MyAnnotation {

//    public static final String field = "LYQ";


    // 接口内,方法的返回值可以为void。
    // 但注解不可以,注解内的方法必须有返回值,
    // 返回值的类型和注解内可以携带信息的那些数据类型相同
    public abstract int annotationMethod();

    String[] value();


}
  1. 创建一个 MyClass 类,在类中使用自定义注解@MyAnnotation:
/**
 * 创建一个类,使用自定义注解。
 *
 * 使用自己定义的注解时:
 * 问题1.
 * 在注解里面描述了一个方法,方法没有参数(有返回值String[]),使用注解的时候让我们传递参数?
 * 答:可以理解为,注解中的方法将我们传递给他的参数搬运走了,传给了别人让别人去做处理。
 *
 * 问题2.
 * 使用别人写好的注解时,不用写方法名就可以给方法赋值,而我们自己定义的注解必须写方法名?
 * 答:如果我们自己定义的注解只有一个方法,且方法名字叫value,在使用的时候就可以省略方法名直接赋值。
 * 如果传递的信息是一个数组,且数组内只有一个元素时,可以省略{}。
 * 如果方法是两个以上,则每一个方法必须写方法名。
 */

public class MyClass {

    @MyAnnotation(annotationMethod = 1,value = "name")
    private String name;



    @MyAnnotation(annotationMethod = 2,value = {"test","Method"})
    public void test(){

    }


    @MyAnnotation(annotationMethod = 3,value = {"print","Method"})
    public void print(){

    }


}
  1. 编写一个测试类TestAnnotation,用于获取MyClass 类中,自定义注解@MyAnnotation内的信息:
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

/**
 * 1.注解可以放置的位置:
 * 类的上面、属性上面、构造方法上面、普通方法上面、参数前面
 *
 * 2.注解中可以携带的信息:
 * 信息不能随意写,信息的类型只能是如下的类型。
 * 1.基本数据类型(不可以是包装类)
 * 2.String类型
 * 3.枚举类型enum
 * 4.注解类型@
 * 5.数组类型[](数组的内部需要是如上的四种类型)
 */

public class TestAnnotation {
    public static void main(String[] args) throws Exception {
        //1.首先获取类
        Class clazz = MyClass.class;
        //2.根据类获得属性
        Method method = clazz.getDeclaredMethod("print");
        //3.根据属性获得上面的注解
        Annotation annotation = method.getAnnotation(MyAnnotation.class);

        //4.执行注解对象里面的方法
        String[] values = ((MyAnnotation)annotation).value();
        System.out.println("------ MyClass 类中,print方法上注解中的内容-----");
        for (String s:values){
            System.out.print(s+"\t");
        }


    }
}

6.Properties类

Properties类,用于读取配置文件。

Properties类继承于HashTable,类型上说,是一个map类型的集合。但实际作用是用来读取文件信息的 ,类似于一个流(高级流)。读取的文件后缀名是. properties,文件中的内容,需以 key = value 形式存在。

案例:

import java.io.FileReader;
import java.util.Enumeration;
import java.util.Properties;

/**
 * Properties类:
 * 继承于HashTable,类型上说,是一个map类型的集合。
 * 但作用是用来读取文件信息的 ---类似于一个流(高级流)。
 * 读取的文件后缀名是. properties
 * 文件中的内容,需是以 key = value 形式存在的。
 */

public class TestProperties {
    public static void main(String[] args) throws Exception {
        Properties properties = new Properties();

        properties.load(new FileReader("src/.../test.properties"));
       
        //通过遍历 读取配置文件中的值

        Enumeration enumeration = properties.propertyNames();//类似于 map.keySet()取出所有的key
        String key;
        String value;
        while (enumeration.hasMoreElements()){
            key = (String) enumeration.nextElement();
            value = properties.getProperty(key);
            System.out.println(key+" = "+value);
        }


    }
}

>总结

操作属性:

1. 获取成员属性:

Field[] getFields() :
  用于获取所有的public修饰的成员变量,也可以是父类中的 public属性。
Field getField(String name) :
  用于获取指定名称的、 public修饰的成员变量,也可以是父类中的 public属性。
 
Field[] getDeclaredFields() :
  用于获取当前类中的所有成员变量,不考虑修饰符。
  但只能获取本来类中的属性,不可获取父类中的属性 。
Field getDeclaredField(String name):
  用于获取指定名称的成员变量,不考虑修饰符。
  但只能获取本来类中的属性,不可获取父类中的属性 。
 
成员变量 Field:
重要操作:
 1. void set(Object obj, Object value):
   赋值操作:调用此方法,可以给某个实例对象的当前属性赋具体值。
   obj表示想要操作的实例对象,value是赋给当前属性的具体值。
 2. Object get(Object obj):
   取值操作:调用此方法,可以取出某个实例对象,当前属性的实际值。
 3. void setAccessible(true):
   实现暴力反射,调用此方法后,可以操作非public修饰的属性。

构造方法:

2. 获取构造方法:
 
Constructor<?>[] getConstructors():
  用于获取所有的public修饰的构造方法。
Constructor<T> getConstructor(<?>... parameterTypes):
  用于获取当前类中某个public修饰的构造方法。
  Class<?>是该构造方法的形参表列,这里需传入参数.class类型。

Constructor<T> getDeclaredConstructor(<?>... parameterTypes):
  用于获取类中的某个构造方法,不考虑修饰符。
  Class<?>是该构造方法的形参表列,这里需传入参数.class类型。
Constructor<?>[] getDeclaredConstructors():
  用于获取类中的所有构造方法,不考虑修饰符。


构造方法Constructor:
1.T newInstance(Object... initargs):
  用于创建一个当前类的实例对象,相当于调用了当前类相应的构造方法。
  参数 initargs表示的是当前构造方法的形参表列。
  如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法.
2.Class[] getParameterTypes():
  用于获取当前方法的形参表列的数据类型(包名.类名)。
3.setAccessible(true):
  实现暴力反射,调用此方法后,可以操作非public修饰的构造方法。

操作方法:

3.获取普通方法:

Method[] getMethods():
    用于获取所有的public修饰的方法,也可以是父类中的 public方法。
Method getMethod(String name, Class<?>... parameterTypes):
	用于获取指定名称的、 public修饰的类内方法,也可以是父类中的 public方法。
 	Class<?>是该方法的形参表列,这里需传入参数.class类型。

Method[] getDeclaredMethods():
  	获取当前类中的所有方法,不考虑修饰符。但只能获取本类中的方法,不可获取父类中的方法 。
Method getDeclaredMethod(String name, Class<?>... parameterTypes):
  	用于获取指定名称的方法,不考虑修饰符,但只能获取本类中的方法,不可获取父类中的方法 。


方法对象 Method:
 重要操作:
 1.Object invoke(Object obj, Object... args):
   让某个实例对象obj,调用当前方法。
   后面的args是执行此方法时需要的实参。而此invoke方法的返回值,就是执行此方法后的实际返回值。 

 2. Class getReturnType() :
   用于获取当前方法的返回值的数据类型(包名.类名)。
 3.Class[] getParameterTypes() :
   用于获取当前方法的形参表列的数据类型(包名.类名)。

三、注解

1.元注解

注解自定义属性的格式为:属性类型 属性名称()

其中默认值可以写也可以不写,如果需要给这个属性赋默认值,那么就使用 使用default关键字 来为这个属性赋予默认值,这样就已经完成了自定义注解。

自定义注解,至少需要指定两个基本条件:

①注解的作用范围

②注解的生命周期

那么就需要用到元注解,Java为我们提供了四个元注解,作用如下:

注解作用
@Target指定注解作用范围(比如:该注解是作用在类上,方法上,还是属性上等等)
@Retention指定注解的生命周期(也就是注解的保留时间,是在编译器有效,还是运行时有效等等)
@Documented是一个标记注解,里面没有任何属性,用 @Documented 注解修饰的注解类会被 JavaDoc 工具提取成文档。【不常用,可选项】
@Inherited也是一个标记注解,没有定义属性,作用是为了表示该注解可以被继承(比如说:你自定义了一个A注解,并且使用了@Inherited修饰,然后在paren类使用了A注解,那么paren的所有子类都默认继承了注解A)。【不常用】

四、案例

demo1:通过反射创建对象

题目要求:

/**
 * 利用反射技术,编写一个可以创建JavaBean对象的方法:
 * 根据传入的一个String型的类全名,
 * 返回一个对应的实例对象。
 */

代码实现:

  1. 实体类Person:
public class Person {
    private String name;
    private Integer age;
    private String sex;

    public Person() {    }
    public Person(String name, Integer age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

}
  1. 方法工具类MySpring:
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Scanner;

public class MySpring {

    /**
     * 编写一个可以创建JavaBean对象的方法:
     * 根据传入的一个String型的类全名,
     * 返回一个对应的实例对象。
     */
    public static Object getBean(String className){

        Object obj = null;
        Scanner input = new Scanner(System.in);
        try {
            Class clazz = Class.forName(className);
            obj = clazz.newInstance();

            //这里进行DI依赖注入
            //通过得到所有的set方法,给对应属性赋值
            //1.找到类中的所有属性名
            Field[] fields = clazz.getDeclaredFields();
            for (Field field:fields){
                //获取属性名
                String fieldName = field.getName();
                //属性名首字母变大写
                String firstName = fieldName.substring(0,1).toUpperCase();
                //属性除了首字母以外的其他字母
                String otherName = fieldName.substring(1);

                //2.获取当前属性的数据类型
                Class fieldClass = field.getType();

                //3.根据属性名,拼接String类型的方法名
                StringBuilder setMethodName = new StringBuilder("set");
                setMethodName.append(firstName);
                setMethodName.append(otherName);

                //4.通过拼接好的set方法名,找寻类中公有的set方法对象
                Method method = clazz.getMethod(setMethodName.toString(),fieldClass);


                //5.通过invoke()方法执行set函数
                System.out.print("请输入"+ className+"类的"+fieldName+"属性的值:");
                //输入属性值
                String fieldValue = input.nextLine();
                //执行set方法给属性赋值时,不一定都是String类型变量
                //8个基本数据类型中的7个包装类中,都含有带String的构造函数
                Constructor fieldConstructor = fieldClass.getConstructor(String.class);
                method.invoke(obj,fieldConstructor.newInstance(fieldValue));

            }

        } catch (Exception e) {
            System.out.println("类创建失败,没有找到此类:"+className);
            e.printStackTrace();
        }

        System.out.println("对象创建成功!!!");
        return obj;
    }

}
  1. 测试类TestMain:
public class TestMain {

    public static void main(String[] args) {

        Person person = (Person)MySpring.getBean("demo1.Person");
        System.out.println(person);

    }
}

demo2:通过注解创建对象

题目要求:

/**
 * 编写一个可以创建JavaBean对象的方法:
 * 根据传入的一个String型的类全名,
 * 根据其无参构造方法上的自定义注解,返回一个对应的实例对象。
 */

代码实现:

  1. 自定义注解MyAnnotation:
import java.lang.annotation.*;

/**
 * 如何自定义一个注解:
 * 1.通过 @interface 定义一个注解类型.
 *
 * 2.发现写法与接口非常相似(可以利用接口的特点来记忆注解)
 * 可以描述public static final的属性,但实际应用中较为少见
 * 可以描述public abstract的方法,但方法的返回值不能是void,要求返回值必须是注解内可以携带信息的那些数据类型中的
 *
 * 3.我们自己定义的注解如果想要拿来使用
 * 光定义是不够的,还需要做很多细致的说明(需要利用Java提供好的元注解来进行说明)
 * 元注解:也是注解,不过不是拿来使用的,而是用来说明注解的。
 * @Target: 描述当前这个自定义注解可以放置的位置
 * @Retention: 描述当前的这个注解存在什么作用域中
 * 作用域包括:源代码文件--->编译-->字节码文件--->类加载--->在内存中执行
 *           SOURCE              CLASS              RUNTIME
 * @Inherited: 描述当前这个注解,能否被子类对象继承(添加表示能被子类继承)
 */
@Target({ElementType.FIELD,ElementType.CONSTRUCTOR,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MyAnnotation {

//    public static final String field = "LYQ";


    // 接口内,方法的返回值可以为void。
    // 但注解不可以,注解内的方法必须有返回值,
    // 返回值的类型和注解内可以携带信息的那些数据类型相同
    public abstract int annotationMethod();

    String[] value();


}
  1. 实体类Person:
public class Person {
    private String name;
    private Integer age;
    private String sex;

    @MyAnnotation(annotationMethod = 1,value = {"李祎晴","20","女"})
    public Person() {    }
    public Person(String name, Integer age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

}
  1. 方法工具类MySpring:
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class MySpring {

    /**
     * 设计一个方法,
     * 通过一个String型的类全名,返回一个类对象,
     * 并且将注解内的值赋给对象中的属性
     */
    public static Object getBean(String className) throws Exception {
        Object obj = null;

        //1.通过反射获取对象
        Class clazz = Class.forName(className);
        //2.获取此对象的无参构造器
        Constructor constructor = clazz.getConstructor();
        //3.通过无参构造器创建一个无参的空对象
        obj = constructor.newInstance();

        //4.将注解内的值赋给对象中的属性
        // 首先获取无参构造器上的注解对象
        Annotation annotation = constructor.getAnnotation(MyAnnotation.class);
        //方法1:
        //直接获取注解对象内的信息
//        String[] values = ((MyAnnotation)annotation).value();


        //方法2:
        // 利用反射实现:首先获得注解类对象
        Class annClass = annotation.getClass();
        //获取注解类对象内的指定方法
        Method annClassMethod = annClass.getMethod("value");
        //执行方法,获取里面的值
        String[] values = (String[]) annClassMethod.invoke(annotation);

        //5.获取类对象中的所有属性
        Field[] fields = clazz.getDeclaredFields();
        for (int i = 0;i<fields.length;i++){
            //获取属性名
            String fieldName = fields[i].getName();

            //6.利用属性拼接set方法名:set + 属性名首字母变大写 + 属性名后面的所有字母
            String setMethodName = "set"+fieldName.substring(0,1).toUpperCase()+fieldName.substring(1);
            //获得属性类型
            Class fieldType = fields[i].getType();
            //7.通过拼接好的set方法名和参数类型,找到对应的set方法
            Method setMethod = clazz.getMethod(setMethodName,fieldType);

            //执行找到的set方法,需要将注解内String类型的值,转换成形参需要的参数类型
            setMethod.invoke(obj,fieldType.getConstructor(String.class).newInstance(values[i]));

        }

        System.out.println("对象创建成功!");
        return obj;
    }

}
  1. 测试类TestMain:
public class TestMain {

    public static void main(String[] args) {
        Person person;
        try {
            person = (Person) MySpring.getBean("demo2.Person");
            System.out.println(person);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

demo3:配置文件创建对象

题目要求:

/**
 * 需求:写一个"框架",
 * 在不改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
 * 实现:
 *      1. 配置文件
 *      2. 反射
 * 步骤:
 *      1. 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
 *      2. 在程序中加载读取配置文件
 *   	3. 使用反射技术来加载类文件进内存
 *      4. 创建对象
 *      5. 执行方法resources
 *
 *     注意:maven工程的配置文件要放在resources下
 */

代码实现:

  1. 实体类Person:
public class Person {
    private String name = "李晴晴";
    private Integer age;

    //公有的成员变量
    public String sex ="男";


    public Person() { }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }



    public void eat(){
        System.out.println("Person.eat()方法!");
    }
    private void sleep(){
        System.out.println("private的 Person.sleep()方法!");
    }
    public void study(String name){
        System.out.println("Person.study()方法! "+name+" 正在学习");
    }
    public void study(String name,int age){
        System.out.println("Person.study()方法! "+age+"岁的"+name+" 正在学习");
    }


    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
  1. 实体类Student:
public class Student{

    public void eat() {
        System.out.println("Student.eat()方法!");
    }

}
  1. 配置文件 test.properties:
# demo3/TestUtil1 的测试样例样例:
className1 = demo3.Student
methodName1 = eat



# demo2/TestUtil2 的测试样例样例:
className2 = demo3.Person
methodName2 = study
arg2 = LYQ
  1. 第一个测试类TestUtil1:
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * 需求:写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
 * 实现:
 *      1. 配置文件
 *      2. 反射
 * 步骤:
 *      1. 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
 *      2. 在程序中加载读取配置文件
 *      3. 使用反射技术来加载类文件进内存
 *      4. 创建对象
 *      5. 执行方法resources
 *
 *     注意:maven工程的配置文件要放在resources下
 */

public class TestUtil1 {
    public static void main(String[] args) throws Exception {
        //1.加载配置文件
        Properties properties = new Properties();

        //2.获取class目录下的配置文件
        //获取字节码文件的类加载器(把它加载到内存中)
        ClassLoader classLoader = TestUtil1.class.getClassLoader();

        InputStream inputStream = classLoader.getResourceAsStream("test.properties");
        properties.load(inputStream);


        //3.获取配置文件中的定义数据
        String classname = properties.getProperty("className1");
        System.out.println("className: "+classname);
        String methodName = properties.getProperty("methodName1");
        System.out.println("methodName: "+methodName);



        //4.创建对象
        Class clazz = Class.forName(classname);
        Object object = clazz.newInstance();

        //获取方法
        Method method = clazz.getMethod(methodName);

        //执行方法
        System.out.println("\n\n-------- 执行方法 --------");
        method.invoke(object);

    }
}

5.第二个测试类TestUtil2:

import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * 需求:写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
 * 实现:
 *      1. 配置文件
 *      2. 反射
 * 步骤:
 *      1. 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
 *      2. 在程序中加载读取配置文件
 *   3. 使用反射技术来加载类文件进内存
 *      4. 创建对象
 *      5. 执行方法
 *
 *     注意:maven工程的配置文件要放在resources下
 */

public class TestUtil2 {
    public static void main(String[] args) throws Exception {
        //1.加载配置文件
        Properties properties = new Properties();

        //2.获取class目录下的配置文件
        //获取字节码文件的类加载器(把它加载到内存中)
        ClassLoader classLoader = TestUtil2.class.getClassLoader();

        InputStream inputStream = classLoader.getResourceAsStream("test.properties");
        properties.load(inputStream);


        //3.获取配置文件中的定义数据
        String classname = properties.getProperty("className2");
        System.out.println("className: "+classname);
        String methodName = properties.getProperty("methodName2");
        System.out.println("methodName: "+methodName);
        String arg = properties.getProperty("arg2");
        System.out.println("arg: "+arg);


        //4.创建对象
        Class clazz = Class.forName(classname);
        Object object = clazz.newInstance();

        
        String argss = "";
        //获取该类中的所有方法
        Method[] methods = clazz.getMethods();
        for (Method method:methods){
            //获得目标方法的参数类型
            if (method.getName().equals(methodName)){
                Class[] methodClasses = method.getParameterTypes();
                for (Class c:methodClasses){
                    argss = c.getName();
                }
            }
        }

        Method method = clazz.getMethod(methodName,Class.forName(argss));

        //执行方法
        System.out.println("\n\n-------- 执行方法 --------");
        method.invoke(object,arg);

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值