框架的基础——反射&注解&动态代理
反射
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。
-
在一个类的外部,是不能通过类的实例化对象调用私有化的属性和方法。
-
通过反射可以调用类的私有化属性和方法。
-
Q:通过直接new对象和反射的方式都可以调用公共结构,开发中用哪个?
A:建议直接使用new的方式。在编译中无法确定需要new谁的对象,则使用反射的方式。
-
Q:反射和封装是否矛盾?
A:不矛盾。反射的特征:动态性。可以将
封装
视为一种提示,提示你需要调用私有化属性或方法时,可以调用公共的属性和方法来代替直接调用它。反射表示的是一种能力,表示能不能调用属性和方法。 -
类的加载过程:
- 程序经过
javac.exe
命令以后,会生成一个或多个字节码文件(.class结尾)。 - 接着我们使用
java.exe
命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,就作为CLass
的一个实例。
- 程序经过
-
获取Class的实例
//方式一:调用运行时类的属性 Class clazz1 = Person.class; //方式二:通过运行时类的对象 Person p1 =new Person(); Class clazz2 = p1.getClass; //方式三:调用Class的forname方法 Class clazz3 = Class.forname("Person");//JDBC用的就是这种方法 //方法四:类的加载器(了解) ClassLoader classLoader = ReflectionTest.class.getClassLoader(); Class clazz4= classLoader.loadClass("Person"); //以上四种方法获取的地址值相同。他们获得的是唯一存在的运行时类。
获取Class对象后,可以通过newInstance()
方法可以获取运行时类的对象
Class<User> userClass = User.class;
User user = userClass.newInstance();
//可以通过该对象调用属性和方法。
//实际上也是调用无参构造器来创建对象,所以如果存在有参构造器,则必须有无参构造器,不然会报错。
//空参构造器的访问权限至少得够(默认为public)
反射的动态性的体现
- 编译完成后不能确定需要创建哪一个对象
- 只有在运行时才知道创建哪一个类的对象
获取运行时类的属性及其内部结构
Class<User> userClass = User.class;
Field[] fields = userClass.getFields();
//该方法只能获取当前运行时类及其父类权限修饰为Public的属性
Field[] declaredFields = userClass.getDeclaredFields();
//该方法可以获得当前运行时类任意权限的属性。
//通过增强for循环可以获得属性的内部结构
for (Field f:declaredFields) {
//获取权限修饰符
int modifiers = f.getModifiers();
String s = Modifier.toString(modifiers);
//获取数据类型
Class<?> type = f.getType();
//获取变量名
String name = f.getName();
}
获取运行时类的方法及其内部结构
Method[] methods = userClass.getMethods();
//该方法只能获取当前运行时类及其父类权限修饰为Public的方法
Method[] declaredMethods = userClass.getDeclaredMethods();
//该方法可以获得当前运行时类任意权限的方法。
//获取方法的方法名、返回值类型、修饰符、参数列表与属性的类似。
//通过getConstructor()可以获得运行时类public修饰的构造器
Class<User> userClass = User.class;
Constructor constructor = userClass.getConstructor();
//通过getDeclaredConstructors()可以获得运行时类的全部构造方法。
Constructor[] declaredConstructors = userClass.getDeclaredConstructors();
//也可以获得构造器数组
Constructor[] constructors = userClass.getConstructors();
通过运行时类获取指定的结构:属性或方法
- 通过属性名获取属性
- 通过
getField()
方法获取当前运行时类及其父类的public属性 - 通过
getDeclaredField()
方法获取当前运行时类的所有属性
- 通过
通过getField()
方法获取当前运行时类及其父类的public属性
//获取Class实例
Class<User> userClass = User.class;
//通过Class实例获取当前运行时类的对象
User user = userClass.newInstance();
//通过Class实例获取指定运行时类的属性
Field userName = userClass.getField("userName");
//对某个对象的当前属性值进行赋值
userName.set(user,"Tom");
//获取某个对象的当前属性值
String uName = (String) userName.get(user);
System.out.println(uName);
//打印结果为Tom
我的理解:以往都是通过对象为属性值进行赋值,现在是通过指定的属性值为对象进行赋值,体现了反射的动态性,即编译时不知道应该创建哪一个类的对象,只有运行时才能知道。
通过getDeclaredField()
方法获取当前运行时类的所有属性
//获取Class实例
Class<User> userClass = User.class;
//通过Class实例获取当前运行时类的对象
User user = userClass.newInstance();
//通过Class实例获取当前运行时类的属性
Field userName1 = userClass.getDeclaredField("userName");
//通过当前的属性值解除访问限制
userName1.setAccessible(true);
//对某个对象的当前属性值进行赋值
userName1.set(user,"Jerry");
//获取某个对象的当前属性值
String uName = (String) userName1.get(user);
System.out.println(uName);
通过setAccessible(true)
可以解除访问限制
通过运行时类调用指定的方法
- 非静态方法
//获取Class实例
Class<User> userClass = User.class;
//通过Class实例获取当前运行时类的对象
User user = userClass.newInstance();
//通过Class实例获取指定方法,如果存在方法的重载则在后续的参数中填写参数列表。
//String为String.class
Method add = userClass.getDeclaredMethod("add");
//通过指定方法解除访问限制
add.setAccessible(true);
//通过指定方法的invoke方法去选择用哪一个对象去调用
//如果需要传参数,则在后续的参数列表中填写参数
//如果是静态方法,则这个位置写啥都行
add.invoke(user);
注解(Annotation)
注解就是代码里的特殊标记
框架=反射+注解+设计模式
三大基本注解
@Override
:限制重写父类的方法。@Deprecated
:表示所修饰的类或是方法已经过时。@SuppressWarnings
:抑制编译器警告
- 元注解:修饰注解的注解成为元注解
- 元数据:修饰数据的数据成为元数据。
元注解
- @Retention:指明所修饰注解(Annotation)的生命周期
- Resource:在编译时就会被抛弃。
- Class:不会被加载到内存中,默认情况。
- Runtime:会被保存在内存中。只有声明为RUNTIME的注解才能通过反射获取。
- @Target:指明所修饰的注解能修饰哪些元素。
- @Documented:被修饰的注解会被javadoc提取成文档。默认情况下javadoc是不包含注解的。
- @Inherited:被修饰的注解具有继承性,某个类使用了该注解,则其子类默认继承该注解。
动态代理
要想实现动态代理,需要解决的问题。
- 如何根据加载到内存中的被代理类,动态地创建一个代理类的及其对象。
- 当通过代理类的对象调用方法时,如何动态地去调用被代理类的同名方法。
//接口
interface Human{
String belief(String belief);
void eat();
}
//接口的实现类(被代理类)
class SuperMan implements Human{
@Override
public String belief(String belief) {
System.out.println("我信仰"+belief);
return belief;
}
@Override
public void eat() {
System.out.println("我吃鱼");
}
}
class ProxyFactory{
//类似于工厂模式。
public static Object getObject(Object obj){
//创建Handler对象,用来解决:如何动态地去调用被代理类的同名方法。
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),myInvocationHandler);
}
}
class MyInvocationHandler implements InvocationHandler{
private Object obj;//需要使用被代理类的对象进行赋值。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//method为代理类对象调用的方法,obj为被代理对象,args为参数列表。
//通过反射的动态性,使用方法找对象。
Object invoke = method.invoke(obj, args);
return invoke;
}
public MyInvocationHandler(Object obj) {
this.obj = obj;
}
}