JavaSE:反射
概述
Reflection(反射):是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
加载完类之后,在对内存的方法区中就产生了一个class类型的对象(一个类只有一个class对象),这个对象就包含了完整的类的结构信息。外面可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。
动态语言:
是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构。
主要动态语言:Object-C、C#、JavaScript、PHP、Python、Erlang
静态语言:
与动态语言相对的,运行时结构不可变的语言就是静态语言。如:Java、C、C++。
Java不是动态语言,但Java可以称为“准动态语言”。即Java由一定的动态性,外面可以利用反射机制、字节码操作获得类似动态语言的特性。
反射机制提供的功能:
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
关于java.lang.Class类的理解
- 类的加载过程:
- 程序在经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)
- 接着使用java.exe命令对某个字节码文件进行解释运行,像用于将某个字节码文件加载到内存中,此过程就称为类的加载。
- 加载到内存中的类,外面就i称为运行时类,就作为Class的一个实例。
- 换句话说,Class的实例对应着一个运行时类。
- 加载到内存中的运行时类,会缓存一定的时间。在此时间内,我们可以通过不同的方式来获取此运行时类。
类的加载过程:
通过反射创建运行时类的对象
对比
@Test
public void test1() throws Exception{
//通过new 的方式创建对象,使用对象调用public 修饰的属性和方法
Person person = new Person("张三", 21);
System.out.println(person);
person.age = 18;
System.out.println(person);
person.show();
System.out.println("******************* 反射 *******************");
//使用反射
//1.获取Class类的实例对象
Class<Person> p = Person.class;
//2.通过反射获取Person类的构造器
Constructor<Person> constructor = p.getConstructor(String.class, int.class);
//3.创建Person类对象
Person person1 = constructor.newInstance("李四", 22);
System.out.println(person1);
//4.获取Person类属性
Field age = p.getDeclaredField("age");
age.set(person1,18);
System.out.println(person1);
//5.获取Person类的方法
Method show = p.getDeclaredMethod("show");
show.invoke(person1);
//通过反射获取Person类中的私有构造器、私有属性、私有方法
System.out.println("******************* 反射获取私有结构 *******************");
Class<Person> p2 = Person.class;
Constructor<Person> constructor1 = p2.getDeclaredConstructor(String.class);
constructor1.setAccessible(true);
Person person2 = constructor1.newInstance("王五");
System.out.println(person2);
Field name = p2.getDeclaredField("name");
name.setAccessible(true);
name.set(person2,"赵六");
System.out.println(person2);
Method getNation = p2.getDeclaredMethod("getNation", String.class);
getNation.setAccessible(true);
String str = (String) getNation.invoke(person2, "中国");
System.out.println(str);
}
运行结果
Class类的实例化
@Test
public void test2() throws Exception{
//Class类的实例化
//方式一
Class clazz = Person.class;
//方式二:
Person p = new Person("Tom", 28);
Class clazz2 = p.getClass();
//方式三:使用Class类的 静态方法
Class clazz3 = Class.forName("Person");
System.out.println( clazz == clazz2);
System.out.println(clazz == clazz3);
//方式四:通过类加载器加载
ClassLoader loader = p.getClass().getClassLoader();
Class clazz4 = loader.loadClass("Person");
System.out.println(clazz == clazz4);
}
使用ClassLoader加载配置文件
@Test
public void test3() throws Exception{
//使用ClassLoader加载配置文件
Properties pro = new Properties();
//方式一:此时默认路径在当前Module下
FileInputStream fis = new FileInputStream("jdbc.properties");
pro.load(fis);
//方式二:此时当前默认路径在src下
// ClassLoader loader = ReflectionTest.class.getClassLoader();
// InputStream is = loader.getResourceAsStream("jdbc.properties");
// pro.load(is);
String username = pro.getProperty("username");
String password = pro.getProperty("password");
System.out.println("用户名为:" + username + ",密码为:" + password);
}
使用反射获取运行时类的完整结构
属性
//获取运行时类的属性结构及其内部结构
@Test
public void test(){
Class clazz = Person.class;
//获取所有类及其父类中声明为public的属性
Field[] fields = clazz.getFields();
for(Field f:fields){
System.out.println(f);
}
System.out.println("*********************************************************");
//获取类中的所有属性
Field[] field = clazz.getDeclaredFields();
for(Field f:field){
// System.out.println(f);
//获取属性的权限修饰符
int modifier = f.getModifiers();
System.out.print(Modifier.toString(modifier) + "\t");
Class<?> type = f.getType();
System.out.print(type.getName() + "\t");
String name = f.getName();
System.out.print(name);
System.out.println();
}
}
方法
@Test
public void test2(){
//获取运行时类的方法的内部结构
Class clazz = Person.class;
//获取所有类及其父类中声明为public的方法
// Method[] methods = clazz.getMethods();
// for (Method method:methods){
// System.out.println(method);
// }
//获取当前类的说有方法
Method[] methods = clazz.getDeclaredMethods();
for (Method method:methods){
//获取权限修饰符
int modifiers = method.getModifiers();
System.out.print(Modifier.toString(modifiers) + "\t");
//获取返回值类型
Class<?> type = method.getReturnType();
System.out.print(type.getName() + "\t");
//获取方法名
String name = method.getName();
System.out.print(name+ "\t");
//获取参数列表
System.out.print("(");
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length > 0){
for (int i = 0; i < parameterTypes.length; i++) {
if (i == parameterTypes.length - 1){
System.out.print(parameterTypes[i].getName() + "\targs_" + i);
break;
}
System.out.print(parameterTypes[i].getName() + "\targs_" + i + ",");
}
}
System.out.print(")");
//获取异常
Class<?>[] exceptionTypes = method.getExceptionTypes();
if (exceptionTypes.length > 0){
System.out.print("\tthrows\t");
for (int i = 0; i < exceptionTypes.length; i++) {
if (i == exceptionTypes.length - 1){
System.out.print(exceptionTypes[i].getName());
break;
}
System.out.print(exceptionTypes[i].getName() + ",");
}
}
System.out.println("{}");
}
}
构造器同上。
获取带泛型的父类以及其泛型
@Test
public void test3(){
Class clazz = Person.class;
//获取当前类的带泛型的父类
Type type = clazz.getGenericSuperclass();
//强转为ParameterizedType类
ParameterizedType paramType = (ParameterizedType) type;
//获取当前类的带泛型的父类的泛型
Type[] args = paramType.getActualTypeArguments();
for(Type arg:args){
System.out.println(arg.getTypeName());
}
}
获取运行时类的接口、所在包、注解
@Test
public void test4(){
//获取运行时类的接口、所在包、注解
Class clazz = Person.class;
Class[] interfaces = clazz.getInterfaces();
for (Class interfacess:interfaces){
System.out.println(interfacess);
}
Package aPackage = clazz.getPackage();
System.out.println(aPackage);
Annotation[] annotations = clazz.getAnnotations();
for (Annotation anno:annotations){
System.out.println(anno);
}
}
动态代理
代理设计模式的原理:
使用一个代理将对象包装起来,然后用此代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。
静态代理:特征是代理类和目标类对象的类都是在编译期间确定下来的,不利于程序的扩展。同时,每个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。最好可以通过一个代理类完成全部的代理功能。
动态代理是指客户通过代理类来调用其他对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。
动态代理相比静态代理的优点:
抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更灵活和统一的处理众多的方法。
动态代理代码示例:
//动态代理测试
public class ProxyTest {
public static void main(String[] args) {
ANTAClothFactory a = new ANTAClothFactory();
clothFactory instance = (clothFactory) ProxyFactory.getInstance(a);
instance.productCloth();
String name = instance.getName("ANTA");
System.out.println(name);
}
}
interface clothFactory{
void productCloth();
String getName(String name);
}
class ANTAClothFactory implements clothFactory{
@Override
public void productCloth() {
System.out.println("ANTA生产了一批衣服");
}
@Override
public String getName(String name) {
return name;
}
}
//根据加载到内存中的被代理类,动态创建一个代理类以及其对象
//当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法。
class ProxyFactory{
//调用此方法,返回一个代理类对象
public static Object getInstance(Object obj){//obj-->被代理类
MyInvocationHandler handler = new MyInvocationHandler();
handler.bind(obj);
ClassLoader loader = obj.getClass().getClassLoader();
return Proxy.newProxyInstance(loader, obj.getClass().getInterfaces(),handler);
}
}
class MyInvocationHandler implements InvocationHandler{
private Object obj;
public void bind(Object obj){//绑定被代理类
this.obj = obj;
}
//当我们通过代理类的对象,调用方法A时,就会自动调用如下方法,invoke()
//将被代理对象要执行的方法A的功能就声明在invoke方法中
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//method即为代理类对象调用方法,此方法也就作为了被代理类对象要调用的方法
//obj被代理类对象
System.out.println("动态代理");
return method.invoke(obj,args);
}
}
运行结果