黑马程序员_基础加强(二)

---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! --------------------

注解(Annotation)
        所谓的注解(Annotation)其实就是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用Annotation,程序员可以在不改变原有逻辑的情况下,在源文件嵌入一些补充的信息。Annotation提供了一条为程序元素设置元数据的方法,从某些方面看,Annotation就像修饰符一样被使用,可以用于修饰包、类、构造器、方法、属性、局部变量的声明,这些信息被存储在Annotation中的“name=value”对中。
       Annotation是一个接口,程序可以通过反射来获取指定程序元素的Annotation对象,然后通过Annotation对象来取得注释里的元数据。
基本的Annottaion
       java.lang包下有3个基本的Annotation:
1)@Override:限定重写父类方法。它可以强制一个子类必须覆盖父类的方法,如果没有覆盖,编译器会出现一条错误信息。注意:@Override只能作用于方法,不能作用于其它元素。
2)@Deprecated:标记已过时。用于表示某个程序元素(类、方法等)已过时,当其它程序元素使用已过时的类、方法时,编译器将会给出警告。
3)@SuppressWarnings:抑制编译器警告。它指示被Annotation标识的程序元素(以及在该程序元素中所有子元素)取消显示指定的编译器警告。@SuppressWarnings会一直作用于该
        程序元素的所有子元素,例如使用@SuppressWarnings标识一个类来取消显示某个编译器警告,同时又标识该类里某个方法取消显示另一个编译器警告,那么将在此方法中同时取消显示这两个编译器错误。

自定义Annotation

定义新的Annotation类型使用@interface关键字,它用于定义新的Annotation类型; Annotation还可以带有成员变量,Annotation的成员变量在Annotation定义中以无参方法的形式来声明。其方法名和返回值定义了该成员的名字和类型,如以下形式:

public @interface MetaAnnotation {
String value();
}

如果为Annotation的成员变量指定了默认值,使用该Annotation则可以不为这些成员变量指定值,而是直接使用默认值。当然也可以在使用MyTagAnnotation时为成员变量指定值,如果为MyTag的成员变量指定了值,则默认值不会起作用。根据Annotation是否可以包含成员变量,可以把Annotation分为如下两类:
1)标记Annotation:一个没有成员定义的Annotation类型被称为标记。这种Annotation仅使用自身的存在与否来为我们提供信息。
2)元数据Annotation:那些包含成员变量的Annotation,因为它们可接受更多元数据,所以被称为元数据Annotation。

JDK除了在java.lang下提供了3个基本的Annotation之外,还在java.lang.annotation包下提供了四个Meta Annotation(元Annotation),这四个Annotation都是用于修饰其它Annotation定义。

 @Retention只能用于修饰一个Annotation定义,用于指定该Annotation可以保留多长时间,@Retention包含一个RetentionPolicy类型的value成员变量,所以使用@Retention时必须为该value成员变量指定值。value成员变量的只能是如下三个:
》》RetentionPolicy.CLASS:编译器将把注释记录在class文件中。当运行Java程序时,JVM不再保留注释。这是默认值。
》》RetentionPolicy.RUNTIME:编译器将把注释记录在class文件中。当运行Java程序时,JVM也会保留注释,程序可以通过反射获取该注释。
》》RetentionPolicy.SOURCE:编译器直接丢弃这种策略的注释

@Target也是用于修饰一个Annotation定义,它用于指定被修饰的Annotation能用于修饰哪些程序元素。@Target Annotation也包含一个名为value的成员变量,该成员变量的值只能是如下几个:
》》ElementType.ANNOTATION_TYPE:指定该策略的Annotation只能修饰Annotation。
》》ElementType.CONSTRUCTOR:指定该策略的Annotation只能修饰构造器。
》》ElementType.FIELD:指定该策略的Annotation只能修饰成员变量。
》》ElementType.LOCAL_VARIABLE:指定该策略的Annotation只能修饰局部变量。
》》ElementType.METHOD:指定该策略的Annotation只能修饰方法定义。
》》ElementType.PACKAGE:指定该策略的Annotation只能修饰包定义。
》》ElementType.PARAMETER:指定该策略的Annotation只能修饰参数。
》》ElementType.TYPE:指定该策略的Annotation可以修饰类、接口(包括注释类型)或枚举定义。
       注意:和使用@Rentention类似,使用@Target也可以直接在括号里指定value值,可以无须使用name=value的形式。

  @Documented用于指定被该元Annotation修饰的Annotation类将被javadoc工具提取成文档,如果定义Annotation类时使用了@Documented修饰,则所有使用Annotation修饰的程序元素的API文档中将会包含该Annotation说明

 @Inherited元Annotation指定被它修饰的Annotation将具有继承性:如果某个类使用了A Annotation(定义该Annotation时使用了@Inherited修饰)修饰,则其子类将自动具有A注释。

@ItcastAnnotation(annotationAttr=@MetaAnnotation("flx"),color="red",arrayAttr=2, value = "abc")
public class AnnotationTest {
/**
* @param args
*/
@SuppressWarnings("deprecation")//压缩警告:告诉编译器不用提醒我方法过时
@ItcastAnnotation("xyz")
public static void main(String[] args) {
System.runFinalizersOnExit(true);
if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)){
ItcastAnnotation annotation = (ItcastAnnotation) AnnotationTest.class.getAnnotation(ItcastAnnotation.class);
System.out.println(annotation.color());
System.out.println(annotation.value());
System.out.println(annotation.arrayAttr().length);
}
}
//注解相当于一种标记
@Deprecated
public static void sayHello(){
System.out.println("hi,ITCAST");
}
}

//元注解                        以后有元数据,元信息  。       信息的信息

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface ItcastAnnotation {
String color() default "blue";  //属性  //defalut缺省值可以不填
String value();
int[] arrayAttr() default {3,4,4};
MetaAnnotation annotationAttr() default @MetaAnnotation("lhm");
}

————————————————————————————————————————————

类加载器
        类加载器负责加载所有的类,系统为所有被载入内存中的类生成一个java.lang.Class实例。一旦一个类被载入JVM中,同一个类就不会被再次载入了。当JVM启动时,会形成由三个类加载器组成的初始类加载器的层次结构:
》》Bootstrap ClassLoader:根类加载器,它用来加载 Java 的核心库,是用原生代码来实现的,并不继承自 java.lang.ClassLoader。主要加载位于<JAVA_HOME>/jre/lib目录下的类,如rt.jar,jce.jar等。
》》Extension ClassLoader:扩展类加载器,它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。主要加载位于<JAVA_HOME>/jre/lib/ext或者java.ext.dirs这个系统属性指定的路径下的代码。
》》System ClassLoader:系统类加载器,它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。
      来自命令java中的-classpath选项或java.class.path系统属性,或CLASSPATH环境变量所指定的JAR包和类路径。
另外:类加载器也是Java类,所以Java类加载器本身也要被类加载器加载,显然必须由一个类加载器不是Java编写的类,这正是Bootstrap ClassLoader。它主要加载

1.2,类加载机制
JVM的类加载机制主要有如下三种机制:
》》全盘负责:所谓全盘负责,就是说当一个类加载器负责加载某个Class的时候,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显式使用另外一个类加载器来载入。
》》父类委托:所谓父类委托则是先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类。
》》缓存机制:缓存机制将会保证所有被加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存中搜寻该Class,只有当缓存中不存在该Class对象时,系统才会重读该类对应的二进制数据,并将其转换成Class对象,并存入cache。这就是为什么我们修改了Class后,程序必须重新启动JVM,程序所作的修改才会生效的原因。
类加载器加载Class大致要经过如下8个步骤:
(1)检测此Class是否载入过(即在缓存中是否有此Class),如果有则直接进入第(8)步,否则接着执行第(2)步。
(2)如果父加载器不存在(如果没有父加载器,则要么parent一定是根加载器,要么本身就是跟加载器),则跳到第(4)步执行。如果父加载器存在,则接着执行第(3)步。
(3)请求父加载器载入目标类,如果成功载入则跳到第(8)步,不成功接着执行第(5)步。
(4)请求使用根加载器来载入目标类,如果成功到(8)。如果不成功跳到第(7)步。
(5)寻找Class文件(从与此ClassLoader相关的类路径中寻找)。如果找到则执行第(6)步,如果找不到则跳到第(7)步。
(6)从文件中载入Class,成功载入后跳到第(8)步。
(7)抛出ClassNotFoundException。
(8)返回Class。

1.3,创建并使用自定义的类加载器
        JVM中除根加载器之外的所有类加载器都是ClassLoader子类的实例,开发者可以通过扩展ClassLoader的子类,并重写该ClassLoader所包含的方法来实现自定义的类加载器。
ClassLoader类有如下三个关键方法:
Class<?> loadClass(String name): 
protected  Class<?> loadClass(String name, boolean resolve):该方法为ClassLoader的入口点,根据指定的二进制名称来加载类,系统就是调用ClassLoader的该方法来获取指定类对应的Class对象。
protected  Class<?> findClass(String name):根据二进制名称来查找类。
附加:如果需要实现自定义的ClassLoader,可以通过重写以上三个方法来实现,当然我们推荐重写findClass()方法,而不是重写loadClass()方法。
protected  Class<?> defineClass(String name, byte[] b, int off, int len):该方法负责将指定类的字节码文件(即class文件)读入字节数组:byte[] b内,并把它转化为Class对象,该字节码文件可以来源于文件、网络等。
 除此之外,ClassLoader里还包含如下一些普通方法:
protected  Class<?> findSystemClass(String name):从本地文件系统装入文件。它在本地文件系统中寻找类文件,如果存在,就使用defineClass将原始字节转换成Class对象,以将文件转换成类。
static ClassLoader getSystemClassLoader():用于返回系统类加载器。
ClassLoader getParent():获取该类加载器的父类加载器。
protected  void resolveClass(Class<?> c):链接指定的类。类加载器可以使用此方法来链接类c。
protected  Class<?> findLoadedClass(String name):如果此Java虚拟机已装载了名name的类,则直接返回该类对应的Class实例;否则,返回null。该方法是Java类加载里缓存机制的体现。

——————————————————————————————————————

动态代理
         1.代理模式是常用的java设计模式,就是对目标对象提供一种代理以控制对这个对象的访问,代理类与目标类(委托类)有同样的接口,代理是为了给目标类提供额外的处理或者不同的操作,如异常处理、运行时间、日志、事物管理等。代理类的对象本身并不真正实现服务,而是通过调用目标类/委托类对象的相关方法提供服务。
静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理:在程序运行时,运用反射机制动态创建而成。

2,应用
         采用代理是为了通过不修改源代码的情况下给程序动态统一添加功能,利用代理技术可以将业务逻辑中一些非业务逻辑的代码分离出来,把他们独立到业务逻辑类外,比如日志记录,性能统计,安全控制,事务处理,异常处理等。这样做,不仅降低了业务逻辑和非业务逻辑的耦合性,提高程序的可重用性,同时提高了开发的效率。
        如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,只要在配置文件中明确要用目标类还是代理类。这样以后很容易切换,如果想要日志功能时,
就配置代理类,否则配置目标类。这样,增加系统功能很容易,以后运行一段时间后,又想换掉系统功能也很容易。



  AOP面向切面编程系统中存在着交叉业务(安全、事务、日志等功能要贯穿于好多个模块中,所以他们就是交叉业务。)一个交叉业务就是要切入到系统中的一个方面。

        交叉业务的编程问题即面向方面的编程(AOP,Aspect Orentied Program),AOP的目标就是使交叉业务模块化,可以采用将切面代理移动到原始方法的周围,这与直接在方法中编写切面代理的过程效果是一样的。


使用代码正好能解决这个问题,代理是实现AOP功能的核心和关键技术。

4,动态代理技术
       要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!写成百上千个代理类,太繁琐了。
      JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
      JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
      CGLIB(Code Generation Library)库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
      代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
1,在调用的目标方法之前
2,在调用目标方法之后
3,在调用目标方法前后
4,在处理目标方法异常的catch块中

5,使用Proxy和InvocationHandler创建动态代理
         Proxy提供用于创建动态代理类和代理对象的静态方法,它也是所有动态代理类的父类,如果我们在程序中为一个或多个接口动态地生成实现类,就可以使用Proxy来创建动态代理类;如果需要为一个或多个接口动态地创建实例,也可以使用Proxy来创建动态代理实例。
Proxy提供了如下方法来创建动态代理类和动态代理实例:
static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
创建一个动态代理类所对应的Class对象,该代理类将实现interfaces所指定的多个接口。第一ClassLoader指定生成动态代理类的类加载器。
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
直接创建一个动态代理对象,该代理对象的实现类实现了interfaces指定的系列接口,执行代理对象的每个方法时都会被替换执行InvocationHandler对象的invoke方法。

基础加强的内容,我感觉蛮难的,我任需要去看,去理解它了才行,还需要敲老师的代码,去熟悉他们。

---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! --------------------

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值