l枚举类的实现
JDK1.5之前需要自定义枚举类
JDK1.5新增的enum关键字用于定义枚举类
l若枚举只有一个对象,则可以作为一种单例模式的实现方 式。
枚举类的属性
枚举类对象的属性不应允许被改动,所以应该使用privatefinal修饰
枚举类的使用privatefinal修饰的属性应该在构造器中为其赋值
若枚举类显式的定义了带参数的构造器,则在列出枚举值时也必须对应的传入参数
枚举的底层,实际上被转换为了继承java.lang.Enum类的实体类,枚举的成员变成了对应实体类型的字段,并且生成了一个新的构造函数,如果原来定义了构造函数,则在其基础上加上两个参数,生成新的构造函数。(Enum类不能手动扩展,它内部的构造函数只能通过编译器调用)
注解
注解本质上是一个接口,它需要被第三方识别并使用包括编译器、框架、甚至是注解本身
注解的作用:生成文档、被框架反射读取(如依赖注入)、编译时格式检查如override。
元注解,是被注解识别的注解。
【1】@Target用来约束注解的修饰位置。使用枚举类型ElementType,取值包括type、field、method、parameter等
【2】@Retention用于约束注解的声明周期,使用枚举类型RetentionPolicy,取值包括runtime、class、source。只有runtime会保留到运行期,被反射技术读取到,而class只会被保留在class文件中,运行时被擦除,通常用于编译器检查如@Override、@Deprecated和@SuppressWarnning
【3】@Document用于生成文档。
【4】@Inherited允许子类继承父类的注解
注解支持的元素包括8大基本类型以及对应数组类型、枚举、注解、Class、字符串。但是不支持包装类型以及其他引用类型。
原理
注解底层使用到了动态代理技术。
因为注解本质上是一个接口(继承自java.lang.annotio的子接口),但是我们可以通过Class对象的getAnnotation方法获得一个annotation的实例(这就是一个代理对象,调用自定义方法的时候,最终调用的是invocationHandler的invoke方法)。
该方法会从memberValue这个string-object的map中取值(k是注解属性名称,v是具体的赋值),所有生命周期在runtime的注解都会加载进这个map,并且通过引用传递给invocationHandler实例。而invoke的执行核心就是——通过方法名返回注解的属性值
进行反射获取注解(调用getAnnotations()方法)的时候,JVM会将所有生命周期在RUNTIME的注解取出来,放入一个map中,并创建一个AnnotationInvocationHandler实例,并将注解map传递给它,最后JVM 通过动态代理的方式生成目标注解的代理类,并初始化好处理器
反射
JAVA反射机制是在运行状态中,获取任意一个类的结构 , 创建对象 , 得到方法,执行方法 , 属性 !; 这种在运行状态动态获取信息以及动态调用对象方法的功能被称为java语言的反射机制。
Java类加载器(Java Classloader)是Java运行时环境(Java Runtime Environment)的一部分, 负责动态加载Java类到Java虚拟机的内存空间中。
java默认有三种类加载器,BootstrapClassLoader、ExtensionClassLoader、App ClassLoader。
BootstrapClassLoader(引导启动类加载器):
嵌在JVM内核中的加载器,该加载器是用C++语言写的,主要负载加载JAVA_HOME/lib下的类库,引导启动类加载器无法被应用程序直接使用。
ExtensionClassLoader(扩展类加载器):
ExtensionClassLoader是用JAVA编写,且它的父类加载器是Bootstrap。
是由sun.misc.Launcher$ExtClassLoader实现的,主要加载JAVA_HOME/lib/ext目录中的类 库。
它的父加载器是BootstrapClassLoader
App ClassLoader(应用类加载器):
App ClassLoader是应用程序类加载器,负责加载应用程序classpath目录下的所有jar和class文 件。
它的父加载器为Ext ClassLoade
要想了解一个类,必须先要获取到该类的字节码文件对象. 在Java中,每一个字节码文件,被夹在到内存后,都存在一个对应的Class类型的对象
- 如果在编写代码时, 指导类的名称, 且类已经存在, 可以通过 包名.类名.class 得到一个类的 类对象
- 如果拥有类的对象, 可以通过
Class 对象.getClass() 得到一个类的 类对象 - 如果在编写代码时, 知道类的名称 , 可以通过 Class.forName(包名+类名): 得到一个类的 类对象
上述的三种方式, 在调用时, 如果类在内存中不存在, 则会加载到内存 ! 如果类已经在内存中存在, 不 会重复加载, 而是重复利用 ! - 如果在编写代码时, 指导类的名称, 且类已经存在, 可以通过 包名.类名.class 得到一个类的 类对象
- 如果拥有类的对象, 可以通过
Class 对象.getClass() 得到一个类的 类对象 - 如果在编写代码时, 知道类的名称 , 可以通过 Class.forName(包名+类名): 得到一个类的 类对象
上述的三种方式, 在调用时, 如果类在内存中不存在, 则会加载到内存 ! 如果类已经在内存中存在, 不 会重复加载, 而是重复利用 !
(一个class文件 在内存中不会存在两个类对象 )
特殊的类对象
基本数据类型的类对象: 基本数据类型.clss
包装类.type 基本数据类型包装类对象:
包装类.class获取Constructor
通过class对象 获取一个类的构造方法
- 通过指定的参数类型, 获取指定的单个构造方法 getConstructor(参数类型的class对象数组) 例如: 构造方法如下: Person(String name,int age) 得到这个构造方法的代码如下:
Constructor c = p.getClass().getConstructor(String.class,int.class);
2. 获取构造方法数组 getConstructors();
3. 获取所有权限的单个构造方法 getDeclaredConstructor(参数类型的class对象数组)
4. 获取所有权限的构造方法数组 getDeclaredConstructors();Constructor 创建对象
常用方法: newInstance(Object… para) 调用这个构造方法, 把对应的对象创建出来 参数: 是一个Object类型可变参数, 传递的参数顺序 必须匹配构造方法中形式参数列表的顺 序! setAccessible(boolean flag) 如果flag为true 则表示忽略访问权限检查 !(可以访问任何权限的方法)获取Method
通过class对象 获取一个类的方法
- getMethod(String methodName , class.. clss) 根据参数列表的类型和方法名, 得到一个方法(public修饰的)
2 getMethods(); 得到一个类的所有方法 (public修饰的)
3. getDeclaredMethod(String methodName , class.. clss) 根据参数列表的类型和方法名, 得到一个方法(除继承以外所有的:包含私有, 共有, 保护, 默认)
4. getDeclaredMethods(); 得到一个类的所有方法 (除继承以外所有的:包含私有, 共有, 保护, 默认)Method 执行方法
invoke(Object o,Object… para) : 调用方法 , 参数1. 要调用方法的对象 参数2. 要传递的参数列表 getName() 获取方法的方法名称 setAccessible(boolean flag) 如果flag为true 则表示忽略访问权限检查 !(可以访问任何权限的方法)获取Field
通过class对象 获取一个类的属性
- getDeclaredField(String filedName) 根据属性的名称, 获取一个属性对象 (所有属性)
2. getDeclaredFields() 获取所有属性
3. getField(String filedName) 根据属性的名称, 获取一个属性对象 (public属性)
4. getFields() 获取所有属性 (public)Field 属性的对象类型
常用方法: 1. get(Object o ); 参数: 要获取属性的对象 获取指定对象的此属性值
2. set(Object o , Object value); 参数1. 要设置属性值的 对象 参数2. 要设置的值 设置指定对象的属性的值
3. getName() 获取属性的名称
4. setAccessible(boolean flag) 如果flag为true 则表示忽略访问权限检查 !(可以访问任何权限的属性)获取注解信息
获取类/属性/方法的全部注解对象
Annotation[] annotations01 = Class/Field/Method.getAnnotations();
for (Annotation annotation : annotations01) { System.out.println(annotation); }根据类型获取类/属性/方法的注解对象
注解类型 对象名 = (注解类型) c.getAnnotation(注解类型.class);
动态代理
动态代理,通俗点说就是:无需声明式的创建java代理类,而是在运行过程中生成”虚拟”的代理类,被ClassLoader加载。从而避免了静态代理那样需要声明大量的代理类。
l代理设计模式的原理:
使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原 始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原 始对象上。
3.一周学习不足与需要改进的地方
动态代理这一块确实是还没学清楚,导致后面写作业,代码写的很呆,很拉.没能融合起来
4.一周学习状态
学习状态还行,一直都在认真吸收,只是课下功夫确实用少了