黑马程序员-----基础加强-注解,类加载器

来源:互联网 发布:冒险岛138版本数据库 编辑:程序博客网 时间:2024/05/07 06:56

------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

注解

最开始接触注解时,误理解为注释的另一种说法,十分不重视,都想跳过这部分。学习之后,感觉到注解时很有用的。

注解按照我的理解应该主要做为辅助使用,是一种技巧性编程的思想。

1.概述(注解是什么):

1、注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记,没加,则没有某种标记。

2、以后,java编译器、开发工具和其他应用程序就可以用反射来了解自己的类及各种元素上有无何种标记,有什么标记,就会做出相应的处理。

3、标记可以加在包、类、字段、方法、方法参数,以及局部变量上等等。

4、在java.lang包中提供了最基本的annotation,即注解。

5、格式:@注解类名()。如果有属性,则在括号中加上属性名(可省略)和属性值。

2.java中三种最基本的注解:

1@SuppressWarning(”deprecation”)--->忽略警告

SupressWarning是告知编译器或开发工具等提示指定的编译器警告;“deprecation”是告知具体的信息即方法已过时。

2@Deprecated--->提示成员等已经过时,不再推荐使用。

源代码标记@Deprecated是在JDK1.5中作为内置的annotation引入的,用于表明类(class)、方法(method)、字段(field)已经不再推荐使用,并且在以后的JDK版本中可能将其删除,编译器在默认情况下检测到有此标记的时候会提示警告信息。

例如:假定之前的某个类升级了,其中的某个方法已经过时了,不能够将过时的方法删除,因为可能会影响到调用此类的这个方法的某些程序,这是就可以通过在方法上加这个注解。

3@Override--->提示覆盖(父类方法)

加上此注解,,可对自己类中的方法判断是否是要覆盖的父类的方法,典型的例子即在集合中覆盖equals(Object obj)方法,其中的参数类型必须是Object,才能被覆盖,若不是,加上此注解就会提示警告。

3.注解的定义、使用与反射注解

[java] view plaincopy
  1. <span style="font-size:14px;">/** 
  2.  * 需求:自定义一个注解类型并使用,通过反射获得注解对象 
  3.  */  
  4. package cn.itcast.annocation;  
  5. public @interface MyAnnotation {  
  6.   
  7. }  
  8.   
  9. @MyAnnotation  
  10. // 这就相当于使用自定义的注解类型,但在 Class 解析的时候可以把这解析为注解对象  
  11. public class MyAnnotationTest {  
  12.   
  13.     public static void main(String[] args) {  
  14.         // TODO Auto-generated method stub  
  15.         // 判断某个类的修饰中是否存在 某个 Annotation 修饰  
  16.         if (MyAnnotationTest.class.isAnnotationPresent(MyAnnotation.class)) {  
  17.             // 如果存在该 Annotation 修饰,那么获取该 Annotation 的对象,跟获取 Method、Feild 对象一样  
  18.             MyAnnotation myAnnotationObject = (MyAnnotation)MyAnnotationTest.class  
  19.                     .getAnnotation(MyAnnotation.class);  
  20.             System.out.println(myAnnotationObject);  
  21.         }  
  22.     }  
  23. }</span>  

上述程序运行后无打印结果,分析后得知注解是有声明周期的,进而引出元注解的概念。

4.元注解

元注解就和元数据、元类型一样,因为注解上面也可以有注解,元注解就是最基本的注解,只用于注解上面的注解

@Retention

注释类型 Retention

@Documented
@Retention(value=RUNTIME)                // 说明这个注解保持到运行时阶段
@Target(value=ANNOTATION_TYPE)  // 这说明这个注解只用于注解上面
public @interface Retention

指示注释类型的注释要保留多久。如果注释类型声明中不存在 Retention 注释,则保留策略默认为 RetentionPolicy.CLASS。 只有元注释类型直接用于注释时,Target 元注释才有效。如果元注释类型用作另一种注释类型的成员,则无效。


@Target

注释类型 Target

@Documented
@Retention(value=RUNTIME)               // 说明这个注解保持到运行时阶段
@Target(value=ANNOTATION_TYPE) // 这说明这个注解只用于注解上面
public @interface Target

指示注释类型所适用的程序元素的种类。如果注释类型声明中不存在 Target 元注释,则声明的类型可以用在任一程序元素上。如果存在这样的元注释,则编译器强制实施指定的使用限制。默认为所有类型上都可以使用。

这里引出了 ElementType 类型的枚举

5.为注解增加属性

当两个类使用了相同的注解,那么他们如何通过注解来对他们进行区分呢?这就需要为注解添加属性,在不同调用该注解的类中设定不同的属性进行区分。

[java] view plaincopy
  1. /** 
  2.  * 需求:演示在类上使用有属性的注解和用反射获取有属性注解的属性值 
  3.  */  
  4. package cn.itcast.annocation;  
  5. @Retention(value = RetentionPolicy.RUNTIME)//元注解,标明自定义注解的生命周期  
  6. public @interface MyAnnotation {  
  7.     String color();   
  8.     String name() default cb;  
  9. }  
  10. @MyAnnotation(color = "red")  
  11. //因为name设定了缺省值,可以不定义  
  12. // 这就相当于使用自定义的注解类型,但在 Class 解析的时候可以把这解析为注解对象  
  13. public class MyAnnotationTest {  
  14.   
  15.     public static void main(String[] args) {  
  16.         // TODO Auto-generated method stub  
  17.         // 判断某个类的修饰中是否存在 某个 Annotation 修饰  
  18.         if (MyAnnotationTest.class.isAnnotationPresent(MyAnnotation.class)) {  
  19.             // 如果存在该 Annotation 修饰,那么通过 Class 反射获取该 Annotation 的对象,跟获取 Method、Feild 对象一样  
  20.             MyAnnotation myAnnotationObject = (MyAnnotation)MyAnnotationTest.class  
  21.                     .getAnnotation(MyAnnotation.class);  
  22.             // 使用该 Annotation 对象  
  23.             System.out.println(myAnnotationObject.color());  
  24.             System.out.println(myAnnotationObject.name());  
  25.         }  
  26.     }  
  27. }  

类加载器

类加载器的层级结构

JVM 虚拟机中允许安装多个类加载器,默认有 3 个类加载器,每个类加载器负责加载特定位置的类,这 3 个类加载器分别是:BootStrap、ExtClassLoader、AppClassLoader。类加载器也是 java 类 ClassLoader,类加载器本身也需要被加载,所有最终有一个类加载器不是 java 类,这就是 BootStrap。

类加载器被组织成一种层级结构关系,也就是父子关系。其中,Bootstrap是所有类加载器的父亲。如下图所示:


类加载器的委托机制:

1.加载类的方式

Java虚拟机要加载一个类时,到底要用哪个类加载器加载呢?

1)首先,当前线程的类加载器去加载线程中的第一个类。

2)若A引用类B(继承或者使用了B),Java虚拟机将使用加载类的类加载器来加载类B

3)还可直接调用ClassLoaderLoaderClass()方法,来制定某个类加载器去加载某个类。

2、加载器的委托机制:每个类加载器加载类时,又先委托给上级类加载器。

每个ClassLoader本身只能分别加载特定位置和目录中的类,但他们可以委托其他类的加载器去加载,这就是类加载器的委托模式,类加载器一级级委托到BootStrap类加载器,当BootStrap在指定目录中没有找到要加载的类时,无法加载当前所要加载的类,就会一级级返回子孙类加载器,进行真正的加载,每级都会先到自己相应指定的目录中去找,有没有当前的类;直到退回到最初的类装载器的发起者时,如果它自身还未找到,未完成类的加载,那就报告ClassNoFoundException的异常。

简单说,就是先由发起者将类一级级委托为BootStrap,从父级开始找,找到了直接返回,没找到再返回给其子级找,直到发起者,再没找到就报异常。

3委托机制的优点:可以集中管理,不会产生多字节码重复的现象

补充:面试题

可不可以自己写个类为:java.lang.System呢?

回答:第一、通常是不可以的,由于类加载器的委托机制,会先将System这个类一级级委托给最顶级的BootStrap,由于BootStrap在其指定的目录中加载的是rt.jar中的类,且其中有System这个类,那么就会直接加载自己目录中的,也就是Java已经定义好的System这个类,而不会加载自定义的这个System

第二、但是还是有办法加载这个自定义的System类的,此时就不能交给上级加载了,需要用自定义的类加载器加载,这就需要有特殊的写法才能去加载这个自定义的System类的。

2.自定义类加载器

1、自定义的类加载器必须继承抽象类ClassLoader,要覆写其中的findClass(String name)方法,而不用覆写loadClass()方法。

2、覆写findClass(String name)方法的原因:

1)是要保留loadClass()方法中的流程,因为loadClass()中调用了findClass(String name)这个方法,此方法返回的就是去寻找父级的类加载器。

2)在loadClass()内部是会先委托给父级,当父级找到后就会调用findClass(String name)方法,而找不到时就会用子级的类加载器,再找不到就报异常了,所以只需要覆写findClass方法,那么就具有了实现用自定义的类加载器加载类的目的。

代码示例:

[java] view plaincopy
  1. //加密文件  
  2. package cn.itcast.text2;  
  3. import java.util.Date;  
  4. import java.io.*;  
  5. public class ClassLoaderAttachment extends Date {  
  6.     //对此类进行加密  
  7.         public String toString(){  
  8.             return "hello world";  
  9.         }  
  10.         public static void main(String [] args){  
  11.               
  12.         }  
  13. }  
  14.   
  15. //自定义类加载器  
  16. //继承抽象类ClassLoader  
  17. public class MyClassLoader  extends ClassLoader {  
  18.     public static void main(String[] args) throws Exception {  
  19.         //传入两个参数,源和目标  
  20.         String scrPath = args[0];  
  21.         String destDir = args[1];  
  22.         //将数据读取到输入流中,并写入到输出流中  
  23.         FileInputStream fis = new FileInputStream(scrPath);  
  24.         String destFileName =   
  25.                 scrPath.substring(scrPath.lastIndexOf('\\')+1);  
  26.         String destPath = destDir + "\\" + destFileName;  
  27.         FileOutputStream fos = new FileOutputStream(destPath);  
  28.         //加密数据  
  29.         cypher(fis,fos);  
  30.         fis.close();  
  31.         fos.close();  
  32.     }  
  33.     //定义加密数据的方法  
  34.     private static void cypher(InputStream ips,OutputStream ops)throws Exception{  
  35.         int b = 0;  
  36.         while((b=ips.read())!=-1){  
  37.             ops.write(b ^ 0xff);  
  38.         }  
  39.     }  
  40.     //定义全局变量  
  41.     private String classDir;  
  42.     @Override//覆写findClass方法,自定义类加载器  
  43.     protected Class<?> findClass(String name) throws ClassNotFoundException {  
  44.         String classFileName = classDir + "\\" + name + ".class";   
  45.         try {  
  46.             //将要加载的文件读取到流中,并写入字节流中  
  47.             FileInputStream fis = new FileInputStream(classFileName);  
  48.             ByteArrayOutputStream bos = new ByteArrayOutputStream();  
  49.             cypher(fis,bos);  
  50.             fis.close();  
  51.             byte[] bytes = bos.toByteArray();  
  52.             return defineClass(bytes, 0, bytes.length);  
  53.               
  54.         } catch (Exception e) {  
  55.             // TODO Auto-generated catch block  
  56.             e.printStackTrace();  
  57.         }  
  58.         //如果没找到类,则用父级类加载器加载  
  59.         return super.findClass(name);  
  60.     }  
  61.     //构造函数  
  62.     public MyClassLoader(){}  
  63.     public MyClassLoader(String classDir){  
  64.         this.classDir = classDir;  
  65.     }  
  66. }  


0 0
原创粉丝点击