一、Java反射机制
在运行状态中,对于任意一个类都能知道它的属性和方法,对于任意一个对象都能调用它的任意属性和方法,这种获取的信息以及动态调用对象方法的功能称为java语言的反射机制。
RTTI:运行时类型信息,假定我们在编译时已经知道了所有的类型信息,类型信息是指一个类里的属性、方法等信息。比如正常创建对象的过程。
反射机制:允许我们在运行时发现和使用类的信息。
二、创建一个对象
1、正常new一个对象的过程
①JVM把.java文件编译成.class文件;
②类的加载:类加载器首先会去方法区判断这个类的Class对象是否已经被加载过,如果没有被加载,类加载器会加载.class文件并把Class对象存到方法区中;
③类实例化:引用类型,栈指向堆。
2、利用反射动态创建对象
第一步:获取class对象
public static void main(String[] args) throws Exception{
Animal dog=new Animal("狗","男",2);
Animal cat = new Animal("猫","女",3);
//方法一:利用getClass()方法
Class c1= dog.getClass();
Class c2 = cat.getClass();
System.out.println(c1==c2);
//方法二:利用class属性
Class c3 = Animal.class;
System.out.println(c1==c3);
//方法三:利用Class.forName("类的全限定名")方法
Class c4=Class.forName("com.etime01.Animal");
System.out.println(c3==c4);
Animal类的class对象是唯一的
第二步:动态创建对象
//方法一:newInstance()创建对象
Object obj1 = c3.newInstance();
System.out.println(obj1.toString());
//方法二:构造方法创建对象
Constructor constructor = c3.getConstructor(String.class,String.class,int.class);
Object obj2 = constructor.newInstance("猪","男",12);
System.out.println(obj2.toString());
通过newInstance()方法创建对象要保证Class对象对应类(Animal)有无参构造函数
三、利用反射调用方法
public static void main(String[] args) throws Exception {
//获取class对象
Class c1= Class.forName("com.etime01.Animal");
//创建对象实例
Animal obj1 = (Animal)c1.newInstance();
obj1.setName("人");
//通过反射获取method对象
Method method = c1.getMethod("getName");
//通过method对象的invoke方法调用方法
Object invoke = method.invoke(obj1);
System.out.println(invoke);
四、动态操作属性(get、set方法)
Class c2 = Class.forName("com.etime01.Animal");
Animal obj2 =(Animal)c2.newInstance();
//获取属性
Field[] fields = c2.getFields();
for(Field f :fields){
System.out.println("公有属性"+f.getName());
}
Field[] fields1 = c2.getDeclaredFields();
for(Field f :fields1){
System.out.println("私有属性"+f.getName());
//设置私有属性的访问权限
f.setAccessible(true);
//根据属性类型给属性赋值
if(f.getType().equals(String.class)){
f.set(obj2,"123");
}else if(f.getType().equals(int.class)){
f.set(obj2,123);
}
}
System.out.println(obj2.toString());
//调用get、set方法
PropertyDescriptor p =new PropertyDescriptor("name",c2);
//获取写方法
Method method1 = p.getWriteMethod();
method1.invoke(obj2,"小狗");
//获取读方法
Method readMethod = p.getReadMethod();
System.out.println(readMethod.invoke(obj2));
//或者拼接一个get或则set方法的名称
String fname="name";
String mname="get"+fname.substring(0,1).toUpperCase()+fname.substring(1);
Method method2 = c2.getMethod(mname);
System.out.println(method2.invoke(obj2));
五、反射的简单应用
以简单工厂模式计算器为例
1、简单工厂模式
简单工厂模式一般通过一个接口和多个实现类来完成计算器的功能,首先定义接口的抽象方法是操作,多个实现类分别是加、减、乘、除。
这儿为什么不在一个接口直接定义加减乘除四个抽象方法呢?
原因是如果有增加新的计算操作,那这个接口的改动也非常大,所以把接口的抽象方法直接定义为操作,那么包含了所有的计算操作,以后再想增加某些操作时,直接创建一个实现类即可,拓展性比较强。
接口:
package com.etime04;
public interface Operation {
double operation(double a, double b);
}
4个实现类:
package com.etime04;
public class AddImpl implements Operation {
@Override
public double operation(double a, double b) {
return a+b;
}
}
package com.etime04;
public class SubImpl implements Operation {
@Override
public double operation(double a, double b) {
return a-b;
}
}
package com.etime04;
public class MulImpl implements Operation {
@Override
public double operation(double a, double b) {
return a*b;
}
}
package com.etime04;
public class DivImpl implements Operation {
@Override
public double operation(double a, double b) {
return a/b;
}
}
2、配置xml文件
<?xml version="1.0" encoding="UTF-8"?>
<root>
<oper>
<name>+</name>
<value>com.etime04.AddImpl</value>
</oper>
<oper>
<name>-</name>
<value>com.etime04.SubImpl</value>
</oper>
<oper>
<name>*</name>
<value>com.etime04.MulImpl</value>
</oper>
<oper>
<name>/</name>
<value>com.etime04.DivImpl</value>
</oper>
</root>
3、解析xml配置文件
**
* 该方法用于解析xml中操作的实现类的全限定名
*/
private static String getClazz(String oper) throws ParserConfigurationException, IOException, SAXException {
File f =new File("src/com/etime04/config.xml");
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(f);
NodeList n1 = doc.getElementsByTagName("oper");
String clazz = "";
for(int i=0;i<n1.getLength();i++){
//获取<name>的值
String s = doc.getElementsByTagName("name").item(i).getFirstChild().getNodeValue();
System.out.println(s);
//判断输入的操作与获取的xml的<name>的值是否相等
if(s.equals(oper)){
//获取实现类的全限定名
clazz=doc.getElementsByTagName("value").item(i).getFirstChild().getNodeValue();
break;
}
}
System.out.println("clazz:"+clazz);
return clazz;
}
4、测试
java
public class Test {
private static Operation operation;
public static void main(String[] args){
Double result=cal(5,4,"+");
System.out.println("result:"+result);
}
public static double cal(double a,double b,String oper){
try {
//通过getClazz方法获取到操作对应的实现类的全限定名
String clazz = getClazz(oper);
//创建一个class对象
Class<Operation> c = (Class<Operation>) Class.forName(clazz);
//通过class对象创建一个实现类的实例对象
operation = c.newInstance();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
//通过对象.方法名 获得最终的计算值
return operation.operation(a,b);
}
}
此文章为学习记录文章,参考了别的文章,如有不正之处请指教。