反射
反射,指的是加载类的字节码到内存,并以编程的方法解刨出类中的各个成分(成员变量、方法、构造器等)。
1.获取类的字节码 (3种方式)
public class Test1Class{
public static void main(String[] args){
Class c1 = Student.class;
System.out.println(c1.getName()); //获取全类名
System.out.println(c1.getSimpleName()); //获取简单类名
Class c2 = Class.forName("com.casey.reflect.Student");
System.out.println(c1 == c2); //true
Student s = new Student();
Class c3 = s.getClass();
System.out.println(c2 == c3); //true
}
}
2.获取构造器
通过第一步获取的字节码对象获取构造器。
作用
构造器的作用:初始化对象并返回。
由于构造器是private修饰的,先需要调用setAccessible(true)
表示禁止检查访问控制,然后再调用newInstance(实参列表)
就可以执行构造器,完成对象的初始化了。反射可以破坏封装性
3.获取成员变量
如何使用
Filed类中提供给给成员变量赋值和获取值的方法
4.获取成员方法
如何使用
在Method类中提供了方法,可以将方法自己执行起来。
注解
让其他程序根据注解信息决定怎么执行该程序
自定义注解
注解的属性名如果是value的话,并且只有value没有默认值,使用注解时value名称可以省略。
1.注解本质上是接口,每一个注解接口都继承子Annotation接口
2.注解中的属性本质上是抽象方法
3.@MyTest1实际上是作为MyTest接口的实现类对象
4.@MyTest1(aaa="孙悟空",bbb=false,ccc={"Python","前端","Java"})里面的属性值,可以通过调用aaa()、bbb()、ccc()方法获取到。
元注解
元注解是修饰注解的注解
@Retetion是用来声明注解保留周期,比如:源代码时期、字节码时期、运行时期
@Retetion(RetetionPloicy.SOURCE): 注解保留到源代码时期、字节码中就没有了
@Retetion(RetetionPloicy.CLASS): 注解保留到字节码中、运行时注解就没有了
@Retetion(RetetionPloicy.RUNTIME):注解保留到运行时期【自己写代码时,比较常用的是保留到运行时期】
解析注解
把获取类上、方法上、变量上等位置注解及注解属性值的过程称为解析注解。
1.如果注解在类上,先获取类的字节码对象,再获取类上的注解
2.如果注解在方法上,先获取方法对象,再获取方法上的注解
3.如果注解在成员变量上,先获取成员变量对象,再获取变量上的注解
总之:注解在谁身上,就先获取谁,再用谁获取谁身上的注解
用例:
要求有@MyTest注解的方法可以被框架执行,没有@MyTest注解的方法不能被框架执行。
1.自定义注解
@Target(ElementType.METHOD)
@Retetion(RetetionPloicy.RUNTIME)
public @interface MyTest{
}
2.写一个测试类AnnotationTest4,在类中定义几个被@MyTest注解标记的方法
public class AnnotationTest4{
@MyTest
public void test1(){
System.out.println("=====test1====");
}
@MyTest
public void test2(){
System.out.println("=====test2====");
}
public void test3(){
System.out.println("=====test2====");
}
public static void main(String[] args){
AnnotationTest4 a = new AnnotationTest4();
//1.先获取Class对象
Class c = AnnotationTest4.class;
//2.解析AnnotationTest4类中所有的方法对象
Method[] methods = c.getDeclaredMethods();
for(Method m: methods){
//3.判断方法上是否有MyTest注解,有就执行该方法
if(m.isAnnotationPresent(MyTest.class)){
m.invoke(a);
}
}
}
}
动态代理
细品~~~
先把有唱歌和跳舞方法的接口,和实现接口的明星类定义出来。
生成动态代理对象
提供的一个生成代理对象的类叫Proxy类。
通过Proxy类的newInstance(...)方法可以为实现了同一接口的类生成代理对象。
public class ProxyUtil {
public static Star createProxy(BigStar bigStar){
/* newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
参数1:用于指定一个类加载器
参数2:指定生成的代理长什么样子,也就是有哪些方法
参数3:用来指定生成的代理对象要干什么事情
*/
// Star starProxy = ProxyUtil.createProxy(s);
// starProxy.sing("好日子") starProxy.dance()
Star starProxy = (Star) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),
new Class[]{Star.class}, new InvocationHandler() {
@Override // 回调方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 代理对象要做的事情,会在这里写代码
if(method.getName().equals("sing")){
System.out.println("准备话筒,收钱20万");
}else if(method.getName().equals("dance")){
System.out.println("准备场地,收钱1000万");
}
return method.invoke(bigStar, args);
}
});
return starProxy;
}
}
调用代理工具类,为Star生成代理对象
public class Test {
public static void main(String[] args) {
BigStar s = new BigStar("杨超越");
Star starProxy = ProxyUtil.createProxy(s);
String rs = starProxy.sing("好日子");
System.out.println(rs);
starProxy.dance();
}
}