sponsored links

注解 & 类加载器

注解

一、什么是注解

1,注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记,如果没有加,就没有某种标记,编译器可以通过反射来了解类及各种元素上有没有某种标记,有什么样的标记,就去干相对应的事,标记可以标记在包、类、字段、方法、方法的参数以及局部变量上。

一个注解就相当于一个类

2,常用的基本注解:

@SuppressWarnings 属性值 :RetentionPolicy.SOURCE:压缩警告

@Deprecated属性值: RetentionPolicy.RUNTIME:让标记的方法变成过时的

@Override 属性值: RetentionPolicy.SOURCE:覆盖父类方法

注解的结构图:

注解 & 类加载器

注解就相当于一个程序中要调用的类,要在程序中应用某个注解,就要先定义好注解类。

3,自定义注解

步骤:定义一个简单的注解:把注解加在某个类上;用反射进行测试类上是否有自定义的注解;

代码示例:

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})//指定注解所在的位置
public @interface MyAnnotation{

}

@MyAnnotation
public class AnnotationTest{

   @MyAnnotation
   public static void main(String[] args){
      if(AnnotationTest.class.isAnnotationPresent(MyAnnotation.class)){
         MyAnnotation annotation = AnnotationTest.class.getAnnotation(MyAnnotation.class);
         System.out.println(annotation);

      }
   }
}

4,为注解添加基本属性

什么是注解的属性:

一个注解相当与一个牌子,注解的属性就类似于牌子上的信息,如:@MyAnnotation(color = "red");

定义基本类型的属性和应用属性:

在注解类中添加String color();

@MyAnnotation(color = "red")

用反射方式获得注解对应的实例对象后,在通过该对象调用属性对应的方法:

MyAnnotation a = AnnotationTest.class.getAnnotation(MyAnnotation.class);

System.out.println(a.color());

可以认为@MyAnnotation是MyAnnotation类的一个实力对象

为属性指定缺省值:

String color() default "blue";

value属性:

如果注解中有一个名称为value的属性,且只想设置value属性(既其他属性都采用默认值或者只有一个value属性),那么可以省略value = 部分,例如: @MyAnnotation("zxc")

5,注解的高级属性

数组类型的属性:

int[] arrayAttr() default {1,2,3}

@MyAnnotation(arrayAtty ={2,3,4})

如果数组属性中只有一个元素,这时候属性值部分可以省略大括号

枚举类型的属性:

EnumTest.TrafficLamp lamp();

@MyAnnotation(lamp = EnumTest.TrafficLamp.GREEN)

注解类型的属性:

MetaAnnotation annotationAttr() default @MetaAnnotation("xxx");

@MyAnnotation(annotationAttr = @MetaAnnotation("yyy");

总结:枚举和注解都是特殊的类,不能用new创建实例对象,创建枚举的实例对象是在其中添加元素,创建注解的实例对象直接用@放上一个标记即可。

类加载器

一、类加载器介绍

1,类加载器就是加载类的工具;

2,系统默认三个主要的类加载器,每个类负责加载特定位置的类:BootStrap、ExtClassLoader、AppClassLoader

3,类加载器也是Java类,因为其他是java类的类加载器本身也要被类加载器加载,BootStrap类加载器不是java类;

4,Java虚拟机中的所有类加载器都是采用具有父子关系的树形结构进行组织,在实例化每个类加载器对象时,需要为其指定一个父级类加载器对象或者默认采用系统类加载器为其父级类加载。

类加载器之间的父子关系和关系范围:

注解 & 类加载器

二、类加载的委托机制

1,每个ClassLoader本身只能分别加载特定位置的和目录中的类,但他们可以委托其他的类加载器去加载类,这就是类加载器的委托机制。

类加载器一级级委托到BootStrap类加载器,当BootStrap无法加载当前所要加载的类时,然后才一级级回退到子类或子类的子类加载器去进行真正的加载。当回退到最初的类加载器时,如果它自己也不能完成类的加载,那就报告ClassNotFoundExeption异常。

2,当Java虚拟机要加载一个类时,派出哪个类加载器去加载呢?

首先当前线程的类加载器去加载线程中的第一个类;

如果类A中引用了类B,Java虚拟机将使用加载类A的类加载器类加载类B;

还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类;

3,可不可以写个java.lang.System类呢?

通常情况下不可以的,由于类加载器的委托机制,会先将System这个类一级级委托给父类BootStrap,由于BootStrap在加载的目录中可以找到这个类,那么就会加载自己目录中的,而不会加载自定义的这个System。

所以只要在这个类加载的时候不委托给最上级的BootStrap类,那么就自定义一个类来加载。

三、自定义类加载器

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

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

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

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

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;

//加密文件
public class ClassLoaderAttachment extends Date {
    //对此类进行加密
        public String toString(){
            return "hello world";
        }
        public static void main(String [] args){  

        }
}  

//自定义类加载器
//继承抽象类ClassLoader
public class MyClassLoader  extends ClassLoader {
    public static void main(String[] args) throws Exception {
        //传入两个参数,源和目标
        String scrPath = args[0];
        String destDir = args[1];
        //将数据读取到输入流中,并写入到输出流中
        FileInputStream fis = new FileInputStream(scrPath);
        String destFileName =
                scrPath.substring(scrPath.lastIndexOf('\\')+1);
        String destPath = destDir + "\\" + destFileName;
        FileOutputStream fos = new FileOutputStream(destPath);
        //加密数据
        cypher(fis,fos);
        fis.close();
        fos.close();
    }
    //定义加密数据的方法
    private static void cypher(InputStream ips,OutputStream ops)throws Exception{
        int b = 0;
        while((b=ips.read())!=-1){
            ops.write(b ^ 0xff);
        }
    }
    //定义全局变量
    private String classDir;
    @Override//覆写findClass方法,自定义类加载器
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String classFileName = classDir + "\\" + name + ".class";
        try {
            //将要加载的文件读取到流中,并写入字节流中
            FileInputStream fis = new FileInputStream(classFileName);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            cypher(fis,bos);
            fis.close();
            byte[] bytes = bos.toByteArray();
            return defineClass(bytes, 0, bytes.length);  

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        //如果没找到类,则用父级类加载器加载
        return super.findClass(name);
    }
    //构造函数
    public MyClassLoader(){}
    public MyClassLoader(String classDir){
        this.classDir = classDir;
    }
}  
Tags: