Annotation(注解)是JDK5.0及以后版本引入的。
注解是以 @注解名 的形式标识
注解不会影响程序语义,只作为标识
注解是新的类型(与接口很相似),它与类、接口、枚举是在同一个层次,它们都称作为java的一个类型(TYPE)。
它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
它的作用非常的多,例如:进行编译检查、生成说明文档、代码分析等
注释类型 是一种特殊的接口
用 @interface 声明如
public@interface MyAnnotation
{
属性
方法
}
注解可以有属性和方法,但只能定义 public abstract 的方法和 static final 的属性
所有定义的方法被编译器默认都是 public abstract
方法的返回类型可以是基本数据类型,String ,Class , 枚举 , 注解及这些类的数组
可以在方法声明中定义默认返回值 ,如果注解中有一个名称为value的属性,且你只想设置value属性(即其他属性都采用默认值或者你只有一个value属性),那么可以省略value=部分,例如:@MyAnnotation("lhm")。
public@interface MyAnnotation
{
static final 属性名;
public abstract 返回类型方法名() default 默认值;
}
1
2
3
4
5
6
7
8
9
10
11
12
|
//元注解,元数据,元信息。注解的注解。
//生命周期 SOURECE CLASS RUNTIME:
@Retention (RetentionPolicy.RUNTIME)
@Target ({ElementType.METHOD,ElementType.TYPE}) //什么地方被标记
public @interface AnnotationDemo {
String color() default "blue" ;
int value() default 0 ;
Class<?> clazz() default String. class ;
int [] arrayAttr() default { 1 , 2 }; //数组类型
EnumType.TrafficLamp lamp () default EnumType.TrafficLamp.GREEN;
MetaAnnotation metaannotation () default @MetaAnnotation ( "metaannotation" );
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
@AnnotationDemo ( 3 )
public class AnnotationTest {
@SuppressWarnings ( "unused" )
//忽略警告
private int x;
@SuppressWarnings ( "unused" )
//忽略警告
private int y;
public AnnotationTest( int x, int y) {
super ();
this .x = x;
this .y = y;
}
@Override
//表示已经覆盖
public int hashCode() {
// TODO Auto-generated method stub
return super .hashCode();
}
@Override
//表示已经覆盖
public boolean equals(Object obj) {
// TODO Auto-generated method stub
return super .equals(obj);
}
@SuppressWarnings ( "deprecation" )
//忽略警告
@AnnotationDemo (color= "red" , arrayAttr={ 4 , 5 , 6 },
value= 9 , lamp=EnumType.TrafficLamp.RED,
clazz = System. class , metaannotation= @MetaAnnotation ( "hello" ))
public static void main(String[] args) {
// TODO Auto-generated method stub
System.runFinalizersOnExit( true );
if (AnnotationTest. class .isAnnotationPresent(AnnotationDemo. class )){
AnnotationDemo anno = AnnotationTest. class .getAnnotation(AnnotationDemo. class );
System.out.println(anno.color()+ ".........." +
anno.value()+ "......." +anno.lamp()+ "......." +
anno.clazz().getName()+ "......" +anno.metaannotation().value());
for ( int i:anno.arrayAttr()) {
System.out.print(i+ "," );
}
}
Method[] methods = AnnotationTest. class .getMethods();
for (Method method: methods) {
if (method.getName().equals( "main" )){
AnnotationDemo anno = method.getAnnotation(AnnotationDemo. class );
System.out.println( "\r\n" +anno.color()+ ".........." +anno.value()+ "......." +anno.lamp()
+ "......." +anno.clazz().getName()+ "......" +anno.metaannotation().value());
for ( int i:anno.arrayAttr()) {
System.out.print(i+ "," );
}
}
}
}
@Deprecated
//过时了
public static void sayHello(){
System.out.println( "hi,wufei" );
}
}
|
顾名思义,类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class类的一个实例。每个这样的实例用来表示一个 Java 类。通过此实例的 newInstance()方法就可以创建出该类的一个对象。实际的情况可能更加复杂,比如 Java 字节代码可能是通过工具动态生成的,也可能是通过网络下载的。
基本上所有的类加载器都是 java.lang.ClassLoader类的一个实例。下面详细介绍这个 Java 类。
java.lang.ClassLoader类的基本职责就是根据一个指定的类的名称,找到或者生成其对应的字节代码,然后从这些字节代码中定义出一个 Java 类,即 java.lang.Class类的一个实例。除此之外,ClassLoader还负责加载 Java 应用所需的资源,如图像文件和配置文件等。不过本文只讨论其加载类的功能。为了完成加载类的这个职责,ClassLoader提供了一系列的方法,比较重要的方法如 表 1所示。关于这些方法的细节会在下面进行介绍。
ClassLoader 中与加载类相关的方法
方法 说明
getParent() 返回该类加载器的父类加载器。
loadClass(String name) 加载名称为 name的类,返回的结果是 java.lang.Class类的实例。
findClass(String name) 查找名称为 name的类,返回的结果是 java.lang.Class类的实例。
findLoadedClass(String name) 查找名称为 name的已经被加载过的类,返回的结果是 java.lang.Class类的实例。
defineClass(String name, byte[] b, int off, int len) 把字节数组 b中的内容转换成 Java 类,返回的结果是 java.lang.Class类的实例。这个方法被声明为 final的。
resolveClass(Class<?> c) 链接指定的 Java 类。
类加载器的树状组织结构
Java 中的类加载器大致可以分成两类,一类是系统提供的,另外一类则是由 Java 应用开发人员编写的。系统提供的类加载器主要有下面三个:
引导类加载器(bootstrap class loader):它用来加载 Java 的核心库,是用原生代码来实现的,不是用JAVA类,并不继承自 java.lang.ClassLoader。
扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。
1
2
3
4
5
6
7
8
9
10
11
|
public class ClassLoaderTree {
public static void main(String[] args) {
ClassLoader loader = ClassLoaderTree. class .getClassLoader();
while (loader != null ) {
System.out.println(loader.toString());
loader = loader.getParent();
}
}
}
|
类加载器的代理模式:
类加载器在尝试自己去查找某个类的字节代码并定义它时,会先代理给其父类加载器,由父类加载器先去尝试加载这个类,依次类推。
加载类的过程
在前面介绍类加载器的代理模式的时候,提到过类加载器会首先代理给其它类加载器来尝试加载某个类。这就意味着真正完成类的加载工作的类加载器和启动这个加载过程的类加载器,有可能不是同一个。真正完成类的加载工作是通过调用 defineClass来实现的;而启动类的加载过程是通过调用 loadClass来实现的。前者称为一个类的定义加载器(defining loader),后者称为初始加载器(initiating loader)。在 Java 虚拟机判断两个类是否相同的时候,使用的是类的定义加载器。也就是说,哪个类加载器启动类的加载过程并不重要,重要的是最终定义这个类的加载器。两种类加载器的关联之处在于:一个类的定义加载器是它引用的其它类的初始加载器。如类 com.example.Outer引用了类 com.example.Inner,则由类 com.example.Outer的定义加载器负责启动类 com.example.Inner的加载过程。
方法 loadClass()抛出的是 java.lang.ClassNotFoundException异常;方法 defineClass()抛出的是 java.lang.NoClassDefFoundError异常。
类加载器在成功加载某个类之后,会把得到的 java.lang.Class类的实例缓存起来。下次再请求加载该类的时候,类加载器会直接使用缓存的类的实例,而不会尝试再次加载。也就是说,对于一个类加载器实例来说,相同全名的类只加载一次,即 loadClass方法不会被重复调用。
虽然在绝大多数情况下,系统默认提供的类加载器实现已经可以满足需求。但是在某些情况下,您还是需要为应用开发出自己的类加载器。比如您的应用通过网络来传输 Java 类的字节代码,为了保证安全性,这些字节代码经过了加密处理。这个时候您就需要自己的类加载器来从某个网络地址上读取加密后的字节代码,接着进行解密和验证,最后定义出要在 Java 虚拟机中运行的类来一般来说,自己开发的类加载器只需要覆写 findClass(String name)方法即可。java.lang.ClassLoader类的方法 loadClass()封装了前面提到的代理模式的实现。该方法会首先调用 findLoadedClass()方法来检查该类是否已经被加载过;如果没有加载过的话,会调用父类加载器的 loadClass()方法来尝试加载该类;如果父类加载器无法加载该类的话,就调用 findClass()方法来查找该类。因此,为了保证类加载器都正确实现代理模式,在开发自己的类加载器时,最好不要覆写 loadClass()方法,而是覆写 findClass()方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
private String classDir ;
@Override
protected Class<?> findClass (String name) throws ClassNotFoundException {
String classPath = classDir + "\\" +name+ ".class" ;
FileInputStream fis = null ;
try {
fis = new FileInputStream(classPath);
ByteArrayOutputStream buf = new ByteArrayOutputStream();
cyher(fis, buf);
System.out.println( "heh" );
byte [] bufr = buf.toByteArray();
return defineClass( null ,bufr, 0 , bufr.length);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (IOException e) {
} finally {
if (fis != null )
try {
fis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null ;
}
|
线程上下文类加载器
线程上下文类加载器(context class loader)是从 JDK 1.2 开始引入的。类 java.lang.Thread中的方法 getContextClassLoader()和 setContextClassLoader(ClassLoader cl)用来获取和设置线程的上下文类加载器。如果没有通过 setContextClassLoader(ClassLoader cl)方法进行设置的话,线程将继承其父线程的上下文类加载器。Java 应用运行的初始线程的上下文类加载器是系统类加载器。在线程中运行的代码可以通过此类加载器来加载类和资源。