
- 一、简介
- 反射介绍:
- 类的简介:
- 二、重要的类
- 1>.Class类
- 1.获取Class对象的方式:
- 2.重要方法
- · *** 作成员:
- ·查看性质:
- 2>.Field类
- 3>.Constructor类
- 4>.Method类
- 5>.Annotation类
- 案例:
- 6>.Properties类
- 案例:
- 三、总结
- *** 作属性:
- 构造方法:
- *** 作方法:
- 四、案例
- demo1:通过反射创建对象
- demo2:通过注解创建对象
- demo3:配置文件创建对象
框架:半成品软件。可以在框架的基础上进行软件开发,简化编码。
反射:将类的各个组成部分封装为其他对象,这就是反射机制。反射是框架设计的灵魂。
注:写框架用到反射,用框架不需要反射。
好处:
- 可以在程序运行过程中, *** 作这些对象
例如:String str =“a”;
str. 会发现idea给我们好多方法提示
前提:idea是一直在运行。
(内部用的反射机制,定义一个字节码文件,把字节码文件封装成对象,把所有的方法放到Method对象数组中,用的时候把数组里面的所有方法展示) - 可以解耦,提高程序的可扩展性。
用以下6个类,就可以描述任何一个类。
| 类名 | 解释说明 |
|---|---|
| Class | 用于描述类本身 |
| Package | 用于描述类所属的包 |
| Field | 用于描述类中的属性 |
| Constructor | 用于描述类中的构造方法 |
| Method | 用于描述类中的方法 |
| Annotation | 用于描述类中的注解 |
Class类 用于描述类本身。我们知道,类是用来描述具有相同特征的一组对象的,而这个Class类是用来描述类本身的。
1.获取Class对象的方式:- Class.forName(“包名.类名”);
将字节码文件加载进内存,返回Class对象。
多用于配置文件,将类名定义在配置文件中,读取文件,加载类。 - 类名.class;
通过类名的属性.class获取,多用于参数的传递。 - 对象.getClass();
getClass()方法在Object类中定义着,多用于对象的获取字节码方式。
结论:
同一个字节码( .class)文件,在一次程序运行过程中只会被加载一次。即:不论通过哪种方式获取Class对象,其实得到的都是同一个对象(用 " == " 号判断会返回true)。
| 方法 | 返回值 | 解释说明 |
|---|---|---|
| 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()方法查看包名。 |
Field类 用于描述类中的属性。
| 方法 | 返回值 | 解释说明 |
|---|---|---|
| set(Object obj, Object value) | void | 赋值 *** 作: 调用此方法,可以给某个实例对象的当前属性赋具体值。 obj表示想要 *** 作的实例对象,value是赋给当前属性的具体值。 |
| get(Object obj) | Object | 取值 *** 作: 调用此方法,可以取出某个实例对象,当前属性的实际值。 |
| setAccessible(true) | void | 实现暴力反射,调用此方法后,可以 *** 作非public修饰的属性。 |
| getType() | Class | 用于获取当前属性的数据类型(包名.类名)。 |
| getModifiers() | int | 用于获取当前属性的修饰符(包括权限和特征), 每个修饰符都用一个整数来表示,详情看上面Class表的描述。 |
| getName() | String | 用于获取当前属性的属性名。 |
Constructor类 用于描述类中的构造方法。
| 方法 | 返回值 | 解释说明 |
|---|---|---|
| newInstance(Object … initargs) | Object | 用于创建一个当前类的实例对象, 相当于调用了当前类相应的构造方法。 参数 initargs表示的是当前构造方法的形参表列。 |
| getParameterTypes() | Class[] | 用于获取当前方法的形参表列的数据类型(包名.类名)。 |
| getExceptionTypes() | Class[] | 获取当前方法抛出异常的数据类型(包名.类名)。 |
| setAccessible(true) | void | 实现暴力反射,调用此方法后,可以 *** 作非public修饰的构造方法。 |
| getModifiers() | int | 用于获取当前属性的修饰符(包括权限和特征), 每个修饰符都用一个整数来表示,详情看上面Class表的描述。 |
| getName() | String | 用于获取当前属性的属性名。 |
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 | 用于获取当前方法的方法名。 |
Annotation类 用于描述类中的注解。
-
注解可以放置的位置:
类的上面、属性上面、构造方法上面、普通方法上面、参数前面 -
注解中可以携带的信息(信息不能随意写,信息的类型只能是如下的类型。):
- 基本数据类型(不可以是包装类)
- String类型
- 枚举类型enum
- 注解类型@
- 数组类型[](数组的内部需要是如上的四种类型)
- 自定义一个 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();
}
- 创建一个 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(){
}
}
- 编写一个测试类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() :
用于获取当前方法的形参表列的数据类型(包名.类名)。
四、案例
demo1:通过反射创建对象
题目要求:
/**
* 利用反射技术,编写一个可以创建JavaBean对象的方法:
* 根据传入的一个String型的类全名,
* 返回一个对应的实例对象。
*/
代码实现:
- 实体类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;
}
}
- 方法工具类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;
}
}
- 测试类TestMain:
public class TestMain {
public static void main(String[] args) {
Person person = (Person)MySpring.getBean("demo1.Person");
System.out.println(person);
}
}
demo2:通过注解创建对象
题目要求:
/**
* 编写一个可以创建JavaBean对象的方法:
* 根据传入的一个String型的类全名,
* 根据其无参构造方法上的自定义注解,返回一个对应的实例对象。
*/
代码实现:
- 自定义注解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();
}
- 实体类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;
}
}
- 方法工具类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
- 测试类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下
*/
代码实现:
- 实体类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;
}
}
- 实体类Student:
public class Student{
public void eat() {
System.out.println("Student.eat()方法!");
}
}
- 配置文件 test.properties:
# demo3/TestUtil1 的测试样例样例:
className1 = demo3.Student
methodName1 = eat
# demo2/TestUtil2 的测试样例样例:
className2 = demo3.Person
methodName2 = study
arg2 = LYQ
- 第一个测试类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条)