文章目录
反射
一.类加载器
1.类的加载
当我们的程序在运行后,第一次使用某个类的时候,会将此类的class文件读取到内存,并将此类的所有信息存储到一个Class对象中
2.类的加载时机
- 创建类的实例。
- 类的静态变量,或者为静态变量赋值。
- 类的静态方法。
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。
- 初始化某个类的子类。
- 直接使用java.exe命令来运行某个主类。
以上六种情况的任何一种,都可以导致JVM将一个类加载到方法区。
3.类加载器
类加载器:是负责将磁盘上的某个class文件读取到内存并生成Class的对象。
Java中有三种类加载器,它们分别用于加载不同种类的class:
-
BootstrapClassLoader 根类加载器
C语言写的,我们获取不到,也被称为引导类加载器,负责Java核心类的加载。比如System,String等。jre/lib/rt.jar下的类都是核心类 -
ExtClassLoader 扩展类加载器,
负责JRE的扩展目录中jar包的加载。在JDK中JRE的lib目录下ext目录 -
AppClassLoader 系统类加载器
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包(第三方jar包)和类路径。或者自定义的类
4.获取加载器对象
类名.class.getClassLoader
5.双亲委派机制
工作过程
- 某个"类加载器"收到类加载的请求,它首先不会尝试自己去加载这个类,而是把请求交给父级类加载器。
- 因此,所有的类加载的请求最终都会传送到顶层的"启动类加载器"中。
- 如果"父级类加载器"无法加载这个类,然后子级类加载器再去加载。
双亲委派机制的好处
双亲委派机制的一个显而易见的好处是:Java的类随着它的类加载器一起具备了一种带有优先级的层次关系。例如:java.lang.Object。它存放在rt.jar中。无论哪一个类加载器要加载这个类,最终都是委派给处于顶端的"启动类加载器"进行加载,因此java.lang.Object类在程序的各种类加载器环境中都是同一个类。相反,如果没有"双亲委派机制",如果用户自己编写了一个java.lang.Object,那么当我们编写其它类时,这种隐式的继承使用的将会是用户自己编写的java.lang.Object类,那将变得一片混乱。
二.反射
1.反射的概念
反射是一种机制,利用该机制可以在程序运行过程中对类进行解剖并操作类中的所有成员(成员变量,成员方法,构造方法),各种框架的设计和学习 比如Spring,Hibernate,Struct,Mybaits…都会用到反射。
2.使用反射操作类成员的前提
要获得该类字节码文件对象,就是Class对象
3.获取Class对象的三种方式:
- Class.forName(类的全限定名)
- 类名.Class
- 对象.getClass()
三种方式中forName最常用:
forName扩展性最好,灵活性最高
因为:forName方法参数为一个字符串,将来我们可以将字符串放在文件中,然后用IO读取这个字符串,然后放在forName方法中,这样,我想获取不同的Class对象,直接改文件就可以了不用修改源代码
4.获取Class对象中的构造方法
获取Class对象中的构造:
1. Constructor[] getConstructors()
获取所有的public修饰的构造方法
2. Constructor getConstructor(Class... parameterTypes)
根据参数类型获取构造方法对象,只能获得public修饰的构造方法。
如果只获取空参构造参数不用写
3.根据获取出来的构造方法对象,创建对象
T newInstance(Object... initargs)
如果new无参构造,参数不用写
5.利用空参构造快速创建对象
Class类中也有一个方法:
T newInstance() -->根据空参构造创建对象
6.利用反射操作私有构造
获取私有的构造:
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
可以获取私有的构造
Constructor<?>[] getDeclaredConstructors()
可以获取所有的构造,包括私有的
AccessibleObject中的方法
void setAccessible(boolean flag)
flag:false-->代表的是不能访问私有的
flag:true-->解除了私有的权限
7.利用反射获取方法对象
获取Class对象中的成员方法:
Method[] getMethods() 获取所有的方法,public
Method getMethod(String name, Class<?>... parameterTypes)
name:获取的方法名
parameterTypes:该方法的参数类型
Object invoke(Object obj, Object... args)
obj: 调用指定对象的方法
args:调用方法时传递的参数
注解
1.概念
注解(Annotation)相当于一种标记,在程序中加入注解就等于为程序打上某种标记,以后,javac编译器、开发工具和其他程序可以通过反射来了解你的类及各种元素上有无何种 标记,看你的程序有什么标记,就去干相应的事,标记可以加在包、类,属性、方法,方法的参数以及局部变量上。
2.自定义注解
- 自定义注解格式:
修饰符 @interface 注解名{
属性
}
- 属性的格式
- 格式1:数据类型 属性名();
- 格式2:数据类型 属性名() default 默认值;
- 属性适用的数据类型
- 八种基本数据类型(int,float,boolean,byte,double,char,long,short)。
- String类型,Class类型,枚举类型,注解类型。
- 以上所有类型的一维数组。
演示
创建
public @interface Book{
// 书名
String name();
// 作者
String[] authros ();
}
使用
public class AnnotationDemo01 {
@Book(name= "红楼梦",authros = {"曹雪芹","高鶚"})
public void show(){
}
}
3.注解注意事项:
-
空注解可以直接使用
-
一个对象中不能连续使用同一个注解多次,但是一个对象中可以使用多个不同的注解
(不同的位置可以使用一样的注解,但是同样的位置不能使用一样的注解) -
使用带有属性注解的时候,注解中的属性一定要赋值,如果有多个属性,用,隔开
如果注解中的属性有数组,那么如果数组只有一个元素值,那么{}不用写,反之用写 -
如果注解中的属性值有默认值,那么我们不必要写,也不用重新赋值,反之必须写上
-
如果注解中只有一个属性,并且属性名叫value,那么使用注解的时候,属性名不用写
4.元注解
元注解概述
- Java官方提供的注解
- 用来定义注解的注解
- 任何官方提供的非元注解的定义都使用到了元注解。
元注解对我们的注解进行控制
- 1: 控制我们的注解,可以写在哪里,(类,方法,变量上,包…)
- 2: 控制我们的注解的生命周期
常用的元注解
-
@Target
作用:用来标识注解使用的位置,如果没有使用该注解标识,则自定义的注解可以使用在任意位置。
可使用的值定义在ElementType枚举类中,常用值如下
TYPE,类,接口
FIELD, 成员变量
METHOD, 成员方法
PARAMETER, 方法参数
CONSTRUCTOR, 构造方法
LOCAL_VARIABLE, 局部变量 -
@Retention
作用:用来标识注解的生命周期(有效范围)
可使用的值定义在RetentionPolicy枚举类中,常用值如下
SOURCE:注解只作用在源码阶段,生成的字节码文件中不存在
CLASS:注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值
RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段