网站首页 > 技术文章 正文
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。—百度百科
动态:运行期间动态绑定执行规则。
静态:编译以后就已经确定的执行过程。
一、动态获取类的信息
动态获取类的方法信息
Foo类(package:demo):
//编译以后是 demo.Foo.class
//类的全名是 demo.Foo
class Foo{
public int test(){
return 5;
}
public double test1(){
return 5d;
}
}
动态获取Foo类的方法:
@Test
public void testClassForName(){
/*
* 动态的加载类信息到方法区
* 并且返回对应的Class对象!
* Class 对象可以访问类的全部信息!
*
* 将className对应的类文件,从磁盘中加载
* 内存方法区,返回这个类的信息
*/
String className = "demo.Foo";
try {
Class<?> cls = Class.forName(className);
Method[] all = cls.getDeclaredMethods();
for (Method method : all) {
System.out.println(method.getName());
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//输出:
// test
// test1
动态获取类的属性信息
Eoo类:
class Eoo{
int id;
String name;
double salary;
String meno;
public Eoo(){}
public Eoo(int id, String name, double salary, String meno) {
super();
this.id = id;
this.name = name;
this.salary = salary;
this.meno = meno;
}
// get,set略
}
动态获取Eoo类的属性:
@Test
public void testField()throws Exception {
/*
* 动态获取一个类的全部属性信息
* 1 动态加载一个类到方法区
* 2 动态获取类的属性信息
*/
String className = "demo.Eoo";
// 动态加载类
Class<?> cls = Class.forName(className);
// 动态获取类声明的属性信息
Field[] all = cls.getDeclaredFields();
for (Field field : all) {
// getName 获取属性的名字
System.out.print(field.getName() + " ");
}
}
// id name salary meno
动态获取类的构造器信息
@Test
public void testCon() throws Exception{
/*
* 1 动态加载类
*/
String className = "demo.Eoo";
Class<?> cls = Class.forName(className);
Constructor[] all = cls.getDeclaredConstructors();
for (Constructor c : all) {
System.out.print(c.getName());
//获取构造器的参数类型列表
// Parameter 参数 Type类型
// Class[] 代表所有参数的类型列表
Class[] types = c.getParameterTypes();
System.out.println(Arrays.toString(types));
}
}
// demo.Eoo[]
// demo.Eoo[int, class java.lang.String, double, class java.lang.String]
二、动态创建对象
调用无参构造器创建对象
如果没有无参数构造器,将发生异常!Class 提供了方法 newInstance()。
@Test
public void testNewInstance() throws Exception{
/*
* 动态调用无参数构造器创建对象
* 1 动态加载类
* 2 利用class 的方法 newInstance 执行
* 无参数构造器常见对象
* 注意:类必须有无参数,否则出异常
*/
String className = "java.util.Date";
Class<?> cls = Class.forName(className);
// cls.newInstance()调用无参数构造器创建对象
Object obj = cls.newInstance();
System.out.println(obj);
//静态的创建对象!编译已经就固定了!
Date date = new Date();
}
// Fri Sep 16 20:04:55 CST 2020
调用有参构造器创建对象
如果没有对应有参数构造器!将发生异常!参数传递错误、将发生异常!
/**
* 调用 className 类名对应的类的有参数构造器,paramTypes 代表对应构造器的参数列表
* className + paramTypes 共同决定调用哪个构造器!执行构造器还需要具体的参数params
*/
public Object create(String className,Class[] paramTypes,Object[] params)
throws Exception{
// 动态加载类
// 动态获取指定参数类型的构造器
// 执行这个构造器,传递 params 参数。
Class<?> cls = Class.forName(className);
//getDeclaredConstructor 在类信息中查找
//给定参数类型的构造器信息
Constructor c = cls.getDeclaredConstructor(paramTypes);
//执行构造器 c.newInstance() 方法,创建对象
//返回值就是这个构造器创建的对象
Object obj = c.newInstance(params);
return obj;
}
@Test
public void testCreate() throws Exception {
String className = "java.util.Date";
//类型列表==Class类型的数组
Class[] paramTypes = {long.class};
//实际参数列表
Object[] params={-1000L * 60 * 60 * 24 * 365};
Object obj = create(className, paramTypes, params);
System.out.println(obj);
//思考:如何动态调用 new String("Hello");
className = "java.lang.String";
/*
* {} 只能拥有声明变量时候直接初始化
* 不能用于赋值语句!
* 赋值语句可以使用 new Object[]{"Hello"}
*/
paramTypes = new Class[]{String.class};
params = new Object[]{"Hello"};
obj = create(className, paramTypes, params);
System.out.println(obj);//Hello
//思考:如何动态调用 new String(byte[],"utf-8");
obj = create("java.lang.String", new Class[]{byte[].class, String.class},
new Object[]{new byte[]{65,66,67,68}, "UTF-8"});
System.out.println(obj);
}
// Wed Jan 01 08:00:00 CST 1969
// Hello
// ABCD
三、动态获取类的属性值
实现过程(如何利用反射API实现动态属性访问):
1、找到对象的类型信息(方法区)
2、在信息中找属性信息(Field)
3、在对象上获取属性的值!
类Goo:
public class Goo {
public int id;
public String name;
public Goo() {}
public Goo(int id, String name) {
super();
this.id = id;
this.name = name;
}
}
动态获取属性的值:
/**
* 获取obj对象的 fieldName 对应属性的值
* @param obj
* @param fieldName
* @return 属性值
*/
public Object get(Object obj, String fieldName)throws Exception{
//1 获取类信息
/*
* Java 中对象的getClass()方法可以获取 对象的类型信息!
* Java 中 有3种方法可以获取Class信息
* 1. 类名.class 获取类信息(静态)
* 2. Class.forName("类名") 获取类信息 动态
* 3. obj.getClass() 获取类信息。运行期间,通过当前对象获取类信息
*/
Class<?> cls = obj.getClass();
//找到属性:
/*
* getDeclaredField 按照属性名在cls中查找
* 类信息。 当属性没有找到时候,抛出异常!
*/
Field field = cls.getDeclaredField(fieldName);
//在对象上获取属性的值!
/*
* get方法:在一个对象上获取属性的值,对象上没有对应的属性,抛出异常
*/
Object value = field.get(obj);
return value;
}
@Test
public void testGetField()throws Exception {
/*
* 动态获取对象的属性
*/
Goo goo = new Goo(5, "Tom");
Object v1 = get(goo, "id");
Object v2 = get(goo, "name");
System.out.println(v1+" "+v2);
}
// 5 Tom
四、动态调用类的方法
@Test
public void testinvoke()throws Exception{
List<String> list=new ArrayList<String>();
list.add("tom");
list.add("jack");
// 动态获取类信息
Class<?> cls = list.getClass();
// 通过方法名和参数类型找到对应的方法
Method method = cls.getDeclaredMethod("remove", new Class[]{int.class});
// 调用方法,传递对象和具体参数
Object value=method.invoke(list,new Object[]{0});
System.out.println(value); //tom
}
五、反射的意义
常见的框架的底层都是使用反射实现的!如:Spring、MyBatis、Struts2、Hibernate …
现有application.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="date" class="java.util.Date"></bean>
<bean id="list" class="java.util.ArrayList"></bean>
</beans>
模拟Spring框架的getBean()方法:
public class ApplicationContext {
private HashMap<String, Object> map= new HashMap<>();
public ApplicationContext(String xml) {
SAXReader reader = new SAXReader();
try{
//读取xml
InputStream in = this.getClass().getClassLoader().getResourceAsStream(xml);
Document doc = reader.read(in);
//解析XML内容 获取全部的<bean>
List<Element> beans = doc.getRootElement().elements();
for (Element e : beans) {
//e 是每个 <bean> 元素
String id = e.attributeValue("id");
String className = e.attributeValue("class");
//利用反射创建对象
Class cls = Class.forName(className);
Object obj = cls.newInstance();
//对象缓存到 map中
map.put(id, obj);
}
}catch(Exception e){
e.printStackTrace();
throw new RuntimeException(e);
}
}
public Object getBean(String id){
return map.get(id);
}
public static void main(String[] args) {
String conf = "application.xml";
ApplicationContext ac = new ApplicationContext(conf);
Object o = ac.getBean("date");
System.out.println(o);
}
}
// Fri Sep 16 21:30:07 CST 2020
猜你喜欢
- 2024-09-22 MyBatis模拟实现
- 2024-09-22 成都校区*精品*Dom4J解析XML的范例浅析
- 2024-09-22 Jenkins Nested View插件XXE漏洞(CVE-2021-21680)分析
- 2024-09-22 Java互联网架构-Spring IOC底层源码分析
- 2024-09-22 XML文件
- 2024-09-22 Dom4j解析xml的小demo
- 2024-09-22 java全栈CMS:AOP+Freemarke代码自动生成4
- 2024-09-22 专科生逆袭入职阿里P6,成人生赢家。网友:我应该也可以
- 2024-09-22 在IDEA的maven项目中连接使用MySQL8.0方法教程
- 2024-09-22 Spring:IOC 控制反转
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)