黑马程序员——JAVA学习笔记十三(高新技术二)

8,    注解

Annotation(注解)是JDK5.0及以后版本引入的。

注解是以 @注解名 的形式标识

注解不会影响程序语义,只作为标识

注解是新的类型(与接口很相似),它与类、接口、枚举是在同一个层次,它们都称作为java的一个类型(TYPE)。

它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。

它的作用非常的多,例如:进行编译检查、生成说明文档、代码分析等

注释类型 是一种特殊的接口

用 @interface 声明如

[email protected] MyAnnotation

{

属性

方法

}

注解可以有属性和方法,但只能定义 public abstract 的方法和  static final 的属性

所有定义的方法被编译器默认都是 public abstract

方法的返回类型可以是基本数据类型,String ,Class , 枚举 , 注解及这些类的数组

可以在方法声明中定义默认返回值 ,如果注解中有一个名称为value的属性,且你只想设置value属性(即其他属性都采用默认值或者你只有一个value属性),那么可以省略value=部分,例如:@MyAnnotation("lhm")。

[email protected] 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) java.lang包下的注解   

a ), @Override

Override 的源码

@Target(ElementType.METHOD) //只能用来注解方法

@Retention(RetentionPolicy.SOURCE) //只保留在源文件 , 编译后去掉

public @interface Override {

}

用在方法定义之前表示在重写父类某个方法 ,如果方法利用此注释类型进行注解但没有重写超类方法,

则编译器会生成一条错误消息

b ), @Deprecated其作用是标记某个过时的类或方法 , 不赞成使用

c),  @SuppressWarnings

SuppressWarnings 的源码

//可以注解接口,类,枚举,注解类型,域,方法,参数,构造方法,局部变量

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})

@Retention(RetentionPolicy.SOURCE) //保留到源文件 , 编译后去掉

public @interface SuppressWarnings {

String[] value();

}

其作用是告诉编译器 不要发出被注解元素(以及包含在该元素中的所有程序元素)的某些警告信息

参数有

deprecation,使用了过时的类或方法时的警告   

unchecked,执行了未检查的转换时的警告  

 fallthrough,当 Switch 程序块直接通往下一种情况而没有 Break 时的警告  

 path,在类路径、源文件路径等中有不存在的路径时的警告  

 serial,当在可序列化的类上缺少serialVersionUID 定义时的警告  

 finally ,任何 finally 子句不能正常完成时的警告  

 all,关于以上所有情况的警告

(2)java.lang.annotation包下的注解类 , 用来注解注解类

a), @Target表示该注解用于什么地方,Target 注解类源码

@Documented

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.ANNOTATION_TYPE)

public @interface Target {

ElementType[] value();

}

ElementType 是枚举

public enum ElementType {

TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR,

LOCAL_VARIABLE, ANNOTATION_TYPE,PACKAGE

}

可能的 ElementType 参数包括:

ElementType.PACKAGE 包声明

ElementType.TYPE 类Class,接口Interface,枚举Enum, 注解类型Annotation 声明

ElementType.FIELD 域声明(包括 enum 实例)

ElementType.CONSTRUCTOR 构造器声明

ElementType.METHOD 方法声明

ElementType.PARAMETER 参数声明

ElementType.LOCAL_VARIABLE 局部变量声明

ElementType.ANNOTATION_TYPE 注解类型声明

b),@Retention 表示在什么级别保存该注解信息。Retention注解类源码

@Documented

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.ANNOTATION_TYPE)

public @interface Retention {

RetentionPolicy value();

}

返回类型是枚举

public enum RetentionPolicy {

SOURCE,

CLASS,

RUNTIME

}

可选的 RetentionPolicy 参数包括:

RetentionPolicy.SOURCE 注解将被编译器丢弃

RetentionPolicy.CLASS 注解在class文件中可用,但会被JVM丢弃

RetentionPolicy.RUNTIME JVM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。

c), @Documented  声明注解类型信息可以显示到 java doc文档中

d),  @Inherited 允许子类继承父类中的注解

注解的生命期

注解的生命期有三个 , 默认只保留到class文件 , 在运行时就会消失

可以通过在一个注解类前定义了@Retetion  明确保留到什么时期

源码(.java) ,

通过 Retetion(RetentionPolicy.SOURCE) 注解的注解类,注解只保留在一个源文件当中,

当编译器将源文件编译成class文件时,编译器将其去掉

编译后文件(.class) ,

通过 Retetion(RetentionPolicy.CLASS)注解的注解类,注解在源码、编译好的.class文件中保留,

当加载class文件到内存时,虚拟机将其去掉

运行时

通过 Retetion(RetentionPolicy.RUNTIME)注解的注解类,该注解在源码、编译好的.class文件中保留

在程序运行期间都会存在内存当中。此时,我们可以通过反射来获得定义在某个类上的所有注解

注解的使用 :       定义注解>使用注解-->注解信息的提取


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");

    }

    

    

}

通过反射获得注解

对于生命周期为运行期间的注解,都可以通过反射获得该元素上的注解实例。

1、声明在一个类中的注解可以通过该类Class对象的getAnnotation或getAnnotations方法获得。

2、声明在一个字段中的注解通过Field对象的getAnnotation或getAnnotations方法获得

3、声明在一个方法中的注解通过Method对象的getAnnotation或getAnnotations方法获得

9,    类加载器

顾名思义,类加载器(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 应用运行的初始线程的上下文类加载器是系统类加载器。在线程中运行的代码可以通过此类加载器来加载类和资源。
时间: 2024-10-27 00:30:35

黑马程序员——JAVA学习笔记十三(高新技术二)的相关文章

黑马程序员——JAVA学习笔记十四(高新技术三)

10 代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供定的服务. 按照代理的创建时期,代理类可以分为两种. 静态代理:由程序员创建或特定工具自动生成源代码,再对其编译.在程序运行前,代理类的.class文件就已经存在了. 动态代

黑马程序员——JAVA学习笔记十(IO)

1,    可以从其中读入一个字节序列的对象成为输入流,可以向其中写入一个字节序列的对象就做输出流.这些字节序列的来源地可以是文件,也可以是网络连接或者内存块.抽象类InputStream和OutputStream构成了I/O层次的基础.因为字节流不便于处理以Unicode存储的信息,所以有了Writer和Reader抽象类,这些从其中继承的类都是读入和写入基于2个字节的Unicode码元,而不是单个字节.   2,    InputStream 与OutputStream: abstract

黑马程序员——JAVA学习笔记五(异常)

1,     异常:是在运行时期发生的不正常情况. 在java中用类的形式对不正常情况进行了描述和封装对象.描述不正常的情况的类,就称为异常类. 以前正常流程代码和问题处理代码相结合,现在将正常流程代码和问题处理代码分离,提高阅读性. 其实异常就是java通过面向对象的思想将问题封装成了对象,用异常类对其进行描述. 不同的问题用不同的类进行具体的描述.比如角标越界.空指针异常等等. 问题很多,意味着描述的类也很多,将其共性进行向上抽取,形成了异常体系. 不正常情况分成了两大类: Throwabl

黑马程序员——JAVA学习笔记十二(高新技术一)

 1,    静态导入: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.solaire.enhance; import static java.lang.Math.max;  //import   语句可以导入一个类或某个包中的所有类 //import static  静态导入 JDK5以后才有.语句导入一个类中的某个静态方法或所有静态方法 //无名包和有包名中的类在一起,没有package,则为无名包. //一个类

黑马程序员——JAVA学习笔记十一(文件操作File)

为了很方便的代表文件的概念,以及存储一些对于文件的基本操作,在java.io包中设计了一个专门的类——File类. 在File类中包含了大部分和文件操作的功能方法,该类的对象可以代表一个具体的文件或文件夹,所以以前曾有人建议将该类的类名修改成FilePath,因为该类也可以代表一个文件夹,更准确的说是可以代表一个文件路径. 1.创建文件 1)boolean java.io.File.createNewFile() throws IOException用来创建文件,如果文件存在,创建失败,返回fa

黑马程序员——JAVA学习笔记六(多线程)

1,    什么是多线程?一个程序可以执行多个任务,每一个任务称为一个线程,运行多个线程的程序称为多线程程序. 进程:正在进行中的程序(直译). 线程:进程中一个负责程序执行的控制单元(执行路径).   多线程的好处:解决了多部分代码同时运行的问题.多线程的弊端:线程太多,会导致效率的降低. 其实,多个应用程序同时执行都是CPU在做着快速的切换完成的.这个切换是随机的.CPU的切换是需要花费时间的,从而导致了效率的降低 2 ,    创建线程方式:  创建线程方式一:继承Thread类 1.定义

黑马程序员——JAVA学习笔记四(继承、接口、内部类)

1,    通过extends关键字让类与类之间产生继承关系.多个类中存在相同属性和行为时,将这些内容抽取到单独的一个类中,那么多个类无需定义这些属性和行为,只要继承那个类即可,已存在的类叫做超类,基类,或父类.新类称为子类,派生类,孩子类. 子类可以直接访问父类中的非私有的属性和行为.子类无法继承父类中私有的内容.JAVA不支持多继承,只支持单继承,多实现. 继承提高了代码复用性,让类与类之间产生了关系.为多态提供了前提. 2,    super关键字代表父类中成员变量内存空间的标示.两个作用

黑马程序员——JAVA学习笔记二(语法基础)

1,     Java程序都是以类的形式存在的,所以需要告诉虚拟机需要加载类的位置,那么可以设置classpath变量.classpath变量如果有;,则表示还会在当前目录查找,没有;则不会在当前目录查找. 当然如果没有classpath也会在当前目录查找. 2,    rd删除目录,加/S可以删整个目录,Windows从里向外删除. 3,    一个文件只有一个public类,文件名必须和类名相同,如果不是友好类,则不需要同名. 4,    JAVA一共有50个关键字 关键字是电脑语言里事先定

黑马程序员——JAVA学习笔记八(集合)

1,    JAVA最初版本只为最常用的数据结构提供了很少的一组类:Vector.Stack.Hashtable.BitSet与Enumeration接口,从JAVA1.2版本开始推出了一组功能完善的的数据结构. 集合类的由来:  对象用于封装特有数据,对象多了需要存储,如果对象的个数不确定.  就使用集合容器进行存储. 集合特点: 1,用于存储对象的容器. 2,集合的长度是可变的. 3,集合中不可以存储基本数据类型值. 4,接口与实现相互分离. 集合框架是为表示和操作集合而规定的一种统一的标准