- Annotation注解,是一种代码级别的说明。与类、接口、枚举在同一个层次。
-
注释是给开发人员看的。
注解
是给计算机提供相应信息的。 - 注解可以在变量,方法,类之上加载;注解可以有属性也可以没有属性;
-
注解的作用范围:
1、源码(如类中注释[帮助文档]中的注解):String类上的@Author,@Since,@See,作用:使用命令javadoc命令将当前的源码生成帮助文档,可以识别String类上的相关注解;
2、编译期间(如@override,@suppressWarnings,@deprecated):告诉编译器部分信息;
3、运行期间(如@Test(timeout=1000)):当我们在当前代码上以Junit方式运行时,Junit会运行方法上包含@Test注解的方法。 -
作用总结:
1、编译检查:通过代码里的标识注解,让编译能够实现基本的编译检查。
2、代码分析:通过代码里的标识注解,对代码进行分析,从而达到取代XML的目的。
3、编写文档:通过代码里的标识注解,辅助生成帮助文档对应的内容。 -
自定义注解:
public @interface MyAnno01 { //自定义注解 //注解的属性支持的类型有:基本数据类型,String,Class,Annotation,枚举类型,以及以上类型的一维数组类型 public long timeout() default -1; //给注解定义了一个属性 public Class cc() default java.util.Date.class; //属性支持字节码类型 }
public @interface 注解名称{
public 属性类型 属性名称();
public 属性类型 属性名称() default 默认值;
} -
注解作用:配置作用
配置:开发的时候部分信息不希望写死在程序中,例如数据库的用户名和密码,可以将用户名和密码存放在.txt,.properities,.xml文件中,利用程序来读取文件中的内容。 - 框架:一大堆工具类的组合,目的:加速项目开发。框架部分hibernate,spring,struts2很多信息需要配置,提供了2种形式的配置(XML和注解)。
- 如果配置信息不会发生频繁的改动,如servlet路径,建议使用注解的形式。
-
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Test { boolean ignore() default false; }
使用注解后,可以在运行时,
通过反射的方式取出方法的注解
(读取字节码信息),如果这个注解是@Test,并且没有被忽略,那就可以通过反射的方式去执行这个方法了。 -
public class TestUtil { // 在运行时通过反射的方式取得方法的注解,通过判断注解来执行。 public static void main(String[] args) throws Exception { // run(new TestTest());//方式一:new 对象再反射获取 run1();//方式2:反射方式获取 } public static void run(Object obj) throws Exception { for(Method m : obj.getClass().getMethods()){ Test t = m.getDeclaredAnnotation(Test.class); if(t != null && !t.ignore()){ m.invoke(obj); } } } public static void run1() throws ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException { // 将TestTest的字节码文件加载到内存中,Class对象(代表字节码文件在内存中的对象) Class clazz = Class.forName("AnnotationTest.Tests.TestTest"); Object obj = clazz.newInstance(); // 获取字节码上的所有方法 for(Method m : clazz.getMethods()){ // System.out.println(m.getName());//打印方法名称 // System.out.println( m.isAnnotationPresent(Test.class));//打印方法上是否有注解 Test t = m.getAnnotation(Test.class); if(t != null && !t.ignore()){ // m.invoke(obj);//方式1 m.invoke(new TestTest());//方式2 } } } }
-
其在开发中的地位:
1)类似dom4j解析XML文件,程序员不会去解析XML文件。
2)自己的开发中不会自定义注解,也不会使用反射读取注解信息。 -
设计模式初步:
1)
单例模式
:如QQ客户端与腾讯服务器连接,保证一个客户端与服务器只有一次连接(仅生成一个对象)(如:Connection conn = DriverManager.getConnection(); ) -
public class Stu { private Stu() { } private static Stu stu = new Stu(); public static Stu getInstance(){ return stu; } }
2)
装饰者模式
:Java提供了ICar接口、GoogleCar提供了接口的实现;希望在谷歌Car接入到生态圈平台时,增加启动方法中的一些功能。
使用场景:二次开发的时候,无法获取到源码,无法使用继承前提下,要对已经存在对象上的功能进行增强。
解决:自己定义一个装饰类MyCar实现ICar接口,为自定义装饰类传递被修饰的对象。
弊端:如果被实现的接口中的方法过多,装饰类中的方法过多冗余public class MyCar implements Icar{ Icar icar; public MyCar(Icar car){ this.icar = car;//构造方法需要传入GoogleCar即扩展了GoogleCar的实现 } @Override public void run() { icar.run(); } @Override public void start() { System.out.println("天气");//增加的功能 icar.start(); } @Override public void stop() { icar.stop(); } }
-
动态代理
来解决上述弊端问题:原理:通过虚拟机在内存中创建类似MyClass.class文件public class Test { public static void main(String[] args){ // Proxy.newProxyInstance(TestCar.class.getClassLoader(), // GoogelCar.class.getInterfaces(), new liu()); // 使用匿名内部类 // 参数1:类加载器加载内存中创建的字节码文件 // 参数2:告诉虚拟机内存中正在被创建的字节码文件中应该有哪些方法 // 参数3:告诉虚拟机正在被创建的字节码上的各个方法如何处理 Icar car = (Icar) Proxy.newProxyInstance(Test.class.getClassLoader(), GoogelCar.class.getInterfaces(), new InvocationHandler() { // invoke方法:method代表正在执行的方法;args代表正在执行的方法中的参数;Object代 // 表方法执行完毕之后的返回值 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // method代表当前正在执行的每个方法 // System.out.println(method.getName()); // method.invoke(new GoogelCar()); if(method.getName().equalsIgnoreCase("start")){ method.invoke(new GoogelCar()); System.out.println("天气"); }else { method.invoke(new GoogelCar()); } return null; } }); car.run(); car.start(); car.stop(); } } class liu implements InvocationHandler{ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; } }
-
代理(Proxy)
可以在运行时创建一个实现了一组给定接口的新类。
要创建一个代理对象,
需要使用Proxy类的newProxyInstance方法
,这个方法有三个参数:
1)一个类加载器;
2)一个Class对象数组,每个元素都是需要实现的接口;
3)一个调用处理器。public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
构造实现指定接口的代理类的一个新实例;所有方法会调用给定处理器对象的
invoke
方法;
无论何时调用代理对象的方法,调用处理器的invoke方法都会被调用,并向其传递Method对象和原始的调用参数。调用处理器必须给出处理调用的放式。 -
例子:使用代理对象对二分查找进行跟踪,首先用1~1000整数的代理填充数组,然后调用Arrays类中的binarySearch方法在数组中查找一个随机整数。最后,打印出与之匹配的元素。
1)实现InvocaHandler接口:-
class TraceHandler implements InvocationHandler { private Object target; public TraceHandler(Object t) { target = t; } public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { System.out.print(target); System.out.print("." + m.getName() + "("); if (args != null) { for (int i = 0; i < args.length; i++) { System.out.print(args[i]); if (i < args.length - 1) System.out.print(", "); } } System.out.print(")"); return m.invoke(target, args); } }
2)实现过程:
public class ProxyTest { public static void main(String[] args) { Object[] elements = new Object[100]; for (int i = 0; i < elements.length; i++) { Integer value = i + 1;//Interger类型的对象实现了Comparable接口 InvocationHandler handler = new TraceHandler(value);//调用处理器 Object proxy = Proxy.newProxyInstance(null, new Class[]{Comparable.class}, handler);//创建的一个实现Comparable接口的一个代理对象 elements[i] = proxy; } Integer key = new Random().nextInt(elements.length) + 1; int result = Arrays.binarySearch(elements, key); if (result >= 0) System.out.print(elements[result]); } }
-
版权声明:本文为liuzewei2015原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。