前言
反射是java开发进阶的一项重要内容,在我们使用的框架如spring中的aop中就有其最佳实践的案例,我们有必要熟悉其Api,并且深刻理解它的作用,今天就让我们一起来看看java的反射-reflect吧。
正篇
一:java反射加载类的三种方式
首先创建测试类:
接口类
package com.lly.springtest1.reflect;
/**
* @ClassName IPerson
* @Description TODO
* @Author lly
* @Date 2019/2/21 2:42 PM
* @Version 1.0
**/
public interface IPerson {
void sayHello();
}
实现类
package com.lly.springtest1.reflect;
/**
* @ClassName ChineseEntity
* @Description TODO
* @Author lly
* @Date 2019/2/20 3:24 PM
* @Version 1.0
**/
public class ChineseEntity implements IPerson{
private String name;
private int age;
private String phone;
public String addres;
public ChineseEntity() {
}
public ChineseEntity(String name, int age, String phone) {
this.name = name;
this.age = age;
this.phone = phone;
}
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;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
@Override
public String toString() {
return "ChineseEntity{" +
"name='" + name + '\'' +
", age=" + age +
", phone='" + phone + '\'' +
'}';
}
public String getAddres() {
return addres;
}
public void setAddres(String addres) {
this.addres = addres;
}
private void getTestName(){
}
@Override
public void sayHello() {
System.out.println("Chinese say hello");
}
public void test(String word) {
System.out.println("testWord:"+word);
}
}
package com.lly.springtest1.reflect;
/**
* @ClassName ChineseEntity
* @Description TODO
* @Author lly
* @Date 2019/2/20 3:24 PM
* @Version 1.0
**/
public class AmericanEntity implements IPerson{
private String name;
private int age;
private String phone;
public String addres;
public AmericanEntity() {
}
public AmericanEntity(String name, int age, String phone) {
this.name = name;
this.age = age;
this.phone = phone;
}
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;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
@Override
public String toString() {
return "ChineseEntity{" +
"name='" + name + '\'' +
", age=" + age +
", phone='" + phone + '\'' +
'}';
}
public String getAddres() {
return addres;
}
public void setAddres(String addres) {
this.addres = addres;
}
private void getTestName(){
}
@Override
public void sayHello() {
System.out.println("American say hello");
}
}
1:Class.forName()
public static void typeOne() {
try {
log.info("通过类的全路径获取类");
// 动态加载类,在需要的时候才加载所需要的类
Class<?> pClass = Class.forName("com.lly.springtest1.reflect.ChineseEntity");
log.info("获取该类所有的属性");
Arrays.asList(pClass.getDeclaredFields()).stream().forEach(e -> System.out.println(e.getName()));
log.info("获取该类所有的所有公开属性");
Arrays.asList(pClass.getFields()).stream().forEach(e -> System.out.println(e.getName()));
log.info("获取类的实例");
ChineseEntity person = (ChineseEntity) pClass.newInstance();
person.sayHello();
person.setAge(10);
log.info("打印类的一个属性");
System.out.println("年龄:" + person.getAge());
log.info("返回某个类的所有公用(public)方法包括其继承类的公用方法,当然也包括它所实现接口的方法");
Arrays.asList(pClass.getMethods()).stream().forEach(e -> {
System.out.print(e.getName() + "(");
Arrays.asList(e.getParameterTypes()).stream().forEach(e1 -> System.out.print(e1.getName() + ","));
System.out.println(")");
return;
});
log.info("类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。当然也包括它所实现接口的方法");
Arrays.asList(pClass.getDeclaredMethods()).stream().forEach(e -> System.out.println(e.getName()));
log.info("获取指定的方法");
Method test = pClass.getDeclaredMethod("test", String.class);
//方法有返回值,返回实际的值.没有返回值返回null
Object person1 = test.invoke(person, "person");
System.out.println(person1);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
2:Object.class
public static void typeTwo() {
// 静态加载需要的类
Class<ChineseEntity> pClass = ChineseEntity.class;
Arrays.asList(pClass.getDeclaredFields()).stream().forEach(e -> System.out.println(e.getName()));
}
3:new Object()
public static void typeThree() {
// new 对象是静态加载类,在编译时刻就需要加载所有需要可能的类
ChineseEntity chineseEntity = new ChineseEntity();
Class<? extends ChineseEntity> aClass = chineseEntity.getClass();
Arrays.asList(aClass.getDeclaredFields()).stream().forEach(e -> System.out.println(e.getName()));
}
运行结果
15:33:58.553 [main] INFO com.lly.springtest1.reflect.ReflectTest - 通过类的全路径获取类
15:33:58.561 [main] INFO com.lly.springtest1.reflect.ReflectTest - 获取该类所有的属性
name
age
phone
addres
15:33:58.687 [main] INFO com.lly.springtest1.reflect.ReflectTest - 获取该类所有的所有公开属性
addres
15:33:58.688 [main] INFO com.lly.springtest1.reflect.ReflectTest - 获取类的实例
Chinese say hello
15:33:58.688 [main] INFO com.lly.springtest1.reflect.ReflectTest - 打印类的一个属性
年龄:10
15:33:58.688 [main] INFO com.lly.springtest1.reflect.ReflectTest - 返回某个类的所有公用(public)方法包括其继承类的公用方法,当然也包括它所实现接口的方法
toString()
getName()
setName(java.lang.String,)
test(java.lang.String,)
sayHello()
setAge(int,)
getAge()
setPhone(java.lang.String,)
getAddres()
getPhone()
setAddres(java.lang.String,)
wait(long,int,)
wait(long,)
wait()
equals(java.lang.Object,)
hashCode()
getClass()
notify()
notifyAll()
15:33:58.691 [main] INFO com.lly.springtest1.reflect.ReflectTest - 类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。当然也包括它所实现接口的方法
toString
getName
setName
test
sayHello
setAge
getAge
setPhone
getAddres
getPhone
setAddres
getTestName
15:33:58.691 [main] INFO com.lly.springtest1.reflect.ReflectTest - 获取指定的方法
testWord:person
null
Process finished with exit code 0
二:java反射的实际应用
了解过设计模式的同学肯定知晓工厂模式,该模式是帮助我们获取一个类的实例,那么其中实现的原理是什么呢,没错,就是java的反射,通过反射获取到具体类的实例,上面我们已经定义了2个实体类中国人和美国人,并且实现了IPerson接口,这样我们在获取其中一个国家人的实例的时候,通常我们会用
ChineseEntity chineseEntity = new ChineseEntity();
这种写法,但是如果我们以后要获取到美国人或者其他国家的人恩,是不是就需要修改代码了,那么我们就用了接口的有点,实现统一的标准,方便扩展,再利用工厂类来获取我们指定的实例就大大优化了我们打代码,下面我们来实现以下
工厂类
package com.lly.springtest1.reflect;
/**
* @ClassName PersonFactory
* @Description persion工厂类
* @Author lly
* @Date 2019/2/22 2:44 PM
* @Version 1.0
**/
public class PersonFactory {
public static IPerson getiPersonInstance(String className) {
IPerson iPerson = null;
try {
iPerson = (IPerson) Class.forName(className).newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
return iPerson;
}
}
测试类
package com.lly.springtest1.reflect;
/**
* @ClassName ReflectAndFactoryTest
* @Description TODO
* @Author lly
* @Date 2019/2/22 2:50 PM
* @Version 1.0
**/
public class ReflectAndFactoryTest {
public static void main(String[] args) {
IPerson chinese = PersonFactory.getiPersonInstance("com.lly.springtest1.reflect.ChineseEntity");
chinese.sayHello();
IPerson american = PersonFactory.getiPersonInstance("com.lly.springtest1.reflect.AmericanEntity");
american.sayHello();
}
}
结果
三:关于setAccessible()方法
首先我们来看看jdk中的解释:
将此对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。并不是代表反射类的属性能否访问的开关,而是是否检查语言访问,我们来做个试验
Method test = pClass.getDeclaredMethod("test", String.class);
//方法有返回值,返回实际的值.没有返回值返回null
long stime = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
test.invoke(person, "person");
}
long etime = System.currentTimeMillis();
log.info("时间:{}", etime - stime);
test.setAccessible(true);
long stime1 = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
test.invoke(person, "person");
}
long etime1 = System.currentTimeMillis();
log.info("时间:{}", etime1 - stime1);
大家觉得结果是什么呢,都执行10000次哪个更快呢,结果是
15:47:49.967 [main] INFO com.lly.springtest1.reflect.ReflectTest - 时间:101
15:47:50.038 [main] INFO com.lly.springtest1.reflect.ReflectTest - 时间:68
事实胜于雄辩,原来是由于JDK的安全检查耗时较多.所以通过setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的目的。
总结
反射的三种方式中第一种使我们经常使用的,例如我们在springAop,jdbc底层加载数据库驱动包等等,其他2种由于我们已经知道了所学要的类,不存在动态加载,所以基本上不使用。