注解Annotation
注解概述:
annotation:是一种代码级别的说明。和类、接口平级;
(注释是给人看的,注解是给计算机(虚拟机)看的)
注解作用:
* 执行编译时期的检查,例如Override
* 分析代码(主要用途:替代配置文件)
JDK中的三类注解
1)@Override 标识重写父类方法,描述方法的重写
2)@SuppressWarnings(“all”)压制警告
3)@Deprecated 标识方法过时,例如Date.toLocalString();
自定义注解:
eclipse里面可以直接new 一个,和类、接口是平级关系
@interface 注解名 { }
可以看出注解的本质就是一个接口
插一句:
找到annotation编译出来class文件,shift +右键,打开命令行,javap指令,把class扔进去,就可以看到下图信息:可以看出annotation的本质上就是一个接口
注解本质上就是一个接口,接口中可以定义变量(常量)和方法(抽象),注解中的方法叫注解属性
注解属性:
1)基本数据类型;
2)String类型
3)枚举类型
4)注解类型
5)Class类型
6)以上类型的一位数组类型
注意:一旦注解有了属性,在使用的时候就一定要带上注解的属性。
注解属性:
格式:
定义注解属性的时候:(不写修饰符的时候默认是public abstract修饰,且只能是这个)
@Target(value = {ElementType.METHOD })
public @interface AnnotationTest01 {
int i() default 1;
String s();
}
定义注解属性的时候可以指定默认值:
属性类型 属性名() default 默认值;
@Target(value = {ElementType.METHOD })
public @interface AnnotationTest01 {
int i() default 1;
}
使用注解属性的时候
@注解名(属性名=值,属性名2=值2)
eg:@MyAnnotation3(i = 0,s="23")
特殊情况:
1)若属性类型的一维数组的时候,当数组的值只有一个的时候可以有以下下两种写法
两种写法:
@MyAnnotation4(ss = { “a” })
@MyAnnotation4(ss = “a”)
2)如果属性名字为value的时候,且只有一个属性的时候
赋值的时候value可以省略
3)一个类上,注解不能够重复使用,但是可以用不同的注解
元注解:
定义在注解上的注解
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD })
public @interface AnnotationTest01 {
int i() default 1;
}
四个元注解:后两个重要
1)Target:定义该注解可以使用在什么上面,值为:ElementType的枚举值:
* METHOD:方法
* TYPE:类 接口
* FIELD:字段
* CONSTRUCTOR:构造方法声明
如果自定义的注解上没有加target元注解,都默认为该注解可以定义在任何位置上(方法、类、字段、构造函数)
2)Retention:定义该注解保留到哪个代码阶段,值为RetentionPolocy类型
.java(源码阶段)——>.class(字节码阶段)——>运行阶段
*
* SOURCE:只在源码上保留
* CLASS:在源码和字节码上保留
* RUNTIME:在所有的阶段都保留
不会分就直接写RUNTIME即可;默认情况下是SOURCE阶段
例子:(如果忘记了具体的值怎么写,可以把鼠标移动到value上,按下ctrl点击进去查看写法)
@Target(value = {ElementType.METHOD,ElementType.TYPE })
@Retention(value = RetentionPolicy.RUNTIME)
public @interface MyAnnotation03 {
int a();
String b();
}
注解Api(如果要找以下函数的Api,需要搜索AnnotatedElement)
( java.lang.reflect.AnnotatedElement)
(调用一下api的可以是字节码对象中的method、field、class等等)
* - T getAnnotation(ClassannotationType):得到指定类型的注解引用。没有返回null。
* - Annotation[] getAnnotations():得到所有的注解,包含从父类继承下来的。
* - Annotation[] getDeclaredAnnotations():得到自己身上的注解。
* - boolean isAnnotationPresent(Class
@MyTest
class Demo {
public void fun01(){
}
}
new Demo().getClass().isAnnotationPresent(MyTest.class);
//判断Demo这个类上是否有MyTest的注解
2)getAnnotation()得到指定类型的注解的引用,没有返回null
method.isAnnotationPresent(MyTest.class);
//判断一个方法上是否有MyTest的注解
案例:判断指定类中的每个函数是不是被指定的Annotation修饰了,如果是,就执行
思路分析:
- 定义两个类(TestDemo,MainTest)和一个注解(@MyTest)
- 在MainTest这个类的main方法里面:
获得TestDemo里面的所有方法
遍历这些方法, 判断此方法上是否有@MyTes注解,
如果有,就执行该方法
有个细节,要加上元注解Retention,让注解在所有阶段都存在。
使得当前注解保留到运行(任何)阶段
ManTest:
public class MainTest {
public static void main(String[] args) throws Exception {
//获得要目标类的字节码文件
Class clazz = Class.forName("com.itheima.mainTest.Test01");
//获得目标类中的所有函数
Method[] methods = clazz.getDeclaredMethods();
//遍历目标类中的所有函数
for (Method method : methods) {
//判定遍历出来的函数是不是被指定的Annotation修饰了
if(method.isAnnotationPresent(AnnotationTest01.class)){
//如果该函数有AnnotationTest01注解
method.invoke(clazz.newInstance());
//newInstance()使用的是无参数的构造函数,这也是javaBean中为了一定需要无参数的构造函数的原因,因为框架里面都会用到无参数的构造方法。
AnnotationTest01 annotation = method.getAnnotation(AnnotationTest01.class);
System.out.println(annotation);
}
}
}
}
Test01
public class Test01 {
@AnnotationTest01
public void test01(){
System.out.println("test01函数执行了");
}
@AnnotationTest01
public void test02(){
System.out.println("test02函数执行了");
}
public void test03(){
System.out.println("test03函数执行了");
}
}
Annotation的定义
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD })
public @interface AnnotationTest01 {
int i() default 1;
}
输出结果为:
代理!!
ProxyPattern(代理模式),23中常见的面向对象软件的设计模式之一;
代理概念:
代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
意义:增强一个类中的某个方法.
代理模式和装饰者模式挺像。
动态代理:
动态代理它可以直接给某一个目标对象生成一个代理对象,而不需要代理类存在。
动态代理与代理模式原理是一样的,只是它没有具体的代理类,直接通过反射生成了一个代理(虚拟)对象。
静态代理和动态代理的区别:
静态代理一定需要有一个具体的类存在的。
动态代理不需要一个具体的类,通过虚拟机来创建一个代理(虚拟)对象出来
JDK中的动态代理:
Java.lang.reflect.Proxy类可以直接生成一个代理对象
Proxy.newProxyInstance(ClassLoader loader, Class
public class ProxyCarDemo {
@Test
public void test01(){
final Car myCar = new QqCar();
Car proxyCar = (Car)Proxy.newProxyInstance(myCar.getClass().getClassLoader(), myCar.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().equalsIgnoreCase("run")){
System.out.println("代理改装后,可以跑120迈");
return null;
}
return method.invoke(myCar, args);
}
});
proxyCar.run();
proxyCar.stop();
proxyCar.addOil(93);
}
/**
* @Title: ProxyCarDemo.java
* @Description: TODO(定义一个接口)
* @author jjizh
* @date 2017年7月10日 上午10:02:19
* @version V1.0
*/
interface Car {
public void run();
public void stop();
public void addOil(int num);
}
/**
* @Title: ProxyCarDemo.java
* @Description: TODO(定义一个接口的实现类qqCar)
* @author jjizh
* @date 2017年7月10日 上午10:02:58
* @version V1.0
*/
class QqCar implements Car{
@Override
public void run() {
System.out.println("QqCar可以跑60迈");
}
@Override
public void stop() {
System.out.println("car都具有刹车功能");
}
@Override
public void addOil(int num) {
System.out.println("QqCar需要加【"+num+"】号油");
}
}
}
案例二:统一GET和POST中文乱码的处理(动态代理)
利用filter和动态代理
需求分析:
在整个网站中,可能会有get请求或post请求向服务器提交参数.参数中往往有中文信息.在后台每个Servlet中都需要去处理乱码.
我们想做的是:无论get请求或者是post请求提交到Servlet中.就可以直接调用getParameter方法将乱码处理好.(使用动态代理)
Filter的主要代码
public class CodeFilter implements Filter {
public void destroy() {
// TODO Auto-generated method stub
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
//强制类型转换
final HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest myRequest = (HttpServletRequest) Proxy.newProxyInstance(request.getClass().getClassLoader(), request.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().equalsIgnoreCase("getParameter")){
//判断是get提交方式还是post提交方式
if(request.getMethod().equalsIgnoreCase("get")){
String value = (String) method.invoke(request, args);
value = new String(value.getBytes("iso8859-1"),"utf-8");
return value;
}else if(request.getMethod().equalsIgnoreCase("post")){
request.setCharacterEncoding("utf-8");
return method.invoke(request, args);
}
}
return method.invoke(request, args);
}
});
// pass the request along the filter chain
chain.doFilter(myRequest, response);
}
public void init(FilterConfig fConfig) throws ServletException {
}
}
知识点补充: 类加载器
类加载器就是将.class文件加载到内存生成Class对象
反射的原理图:
1.2JVM中的三类加载器
- BootStrap:引导类加载器 ,负责加载JRE/lib/rt.jar ( 加载JDK中绝大部分的类)runtime
- ExtClassLoader:扩展类加载器,负责加载JRE/lib/ext/*.jar
- AppClassLoader:应用类加载器,负责加载在classpath环境变量中的所有类,我们自己编写的
1.3父类委托机制
保证了字节码只被加载一次