反射
类加载
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类进行初始化。如果不出现意外情况,JVM将会联系完成这三个步骤,所以有时也把这三个步骤成为类加载或者类初始化
- 类的加载
- 就是指将class文件读入内存,并为之创建一个java.lang.Class对象
- 任何类被使用时,系统都会为之建立一个java.lang.Class对象
- 类的连接
- 验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致
- 准备阶段:负责为类的类变量分配内存,并设置默认初始化值
- 解析阶段:将类的二进制数据中的符号引用替换为直接引用
- 类的初始化
- 在该阶段,主要就是对类变量进行初始化
- 类的初始化步骤
- 假如该类还未被加载和连接,则程序先加载并连接该类
- 假如该类的直接父类还未被初始化,则先初始化其直接父类
- 假如该类中有初始化语句,则系统依次执行这些初始化语句
- 类的初始化时机
- 创建类的实例
- 调用类的类方法
- 访问类或者接口的类变量,或者为该类变量赋值
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类
- 直接使用java.exe命令来运行某个主类
类加载器
-
类加载器的作用
- 负责将.class文件加载到内存中,并为之生成对应的java.lang.Class对象
- 虽然我们不用过分关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行
-
JVM的类加载机制
- 全盘负责:就是当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入
- 父类委托:就是当一个类加载器负责加载某个Class时,先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
- 缓存机制:保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区中搜索该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存储到缓存区
-
Java运行时具有以下内置类加载器
- Bootstrap class loader:它是虚拟机的内置类加载器,通常表示为null,并且没有父null
- Platform class loader:平台类加载器可以看到所有平台类,平台类包括由平台类加载器或其祖先定义的Java SE平台API,其实现类和JDK特定的运行时类
- System class loader:它也被称为应用程序类加载器,与平台类加载器不同。系统类加载器通常用于定义应用程序类路径,模块路径和JDK特定工 具上的类
- 类加载器的继承关系:System的父类加载器为Platform,而Platform的父类加载器为Bootstrap
-
ClassLoader中的两个方法
- static ClassLoader getSystemClassLoader():返回用于委派的系统类加载器
- ClassLoarder getParent():返回父类加载器进行委派
反射
Java反射机制:是指在运行时去获取一个类的变量和方法信息。然后通过获取到的信息来创建对象,调用方法的一种机制。由于这种动态性,可以极大地增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展
- 获取Class类的对象
- 使用类的class属性来获取该类对应的Class对象。举例:Student.class将会返回Student类对应的Class对象
- 调用对象的getClass()方法,返回该对象所属类对应的Class对象,该方法是Object类中的方法,所有的Java对象都可以调用该方法
- 使用Class类中的静态方法forName(String className),该方法需要传入字符串参数,该字符串参数的值是某一个类的全路径,也就是完整包名的路径
public static void main(String[] args) throws ClassNotFoundException {
//使用类的class属性来获取该类对应的Class对象
Class<Student> c1 = Student.class;
System.out.println(c1);
//调用对象的getClass()方法,返回该对象所属类对应的Class对象
Student s = new Student();
Class<? extends Student> c2 = s.getClass();
System.out.println(c1 == c2);
//使用Class类中的静态方法forName(String className)
Class<?> c3 = Class.forName("ClassLoader.demo01.Student");
System.out.println(c1 == c3);
}
public class Student {
//成员变量,一个私有,一个默认,一个公有
private String name;
int age;
public String address;
//构造方法,一个私有,一个默认,两个公有
public Student(){
}
private Student(String name){
this.name = name;
}
Student(String name,int age){
this.name = name;
this.age = age;
}
public Student(String name,int age,String address){
this.name = name;
this.age = age;
this.address = address;
}
//成员方法:一个私有,四个公有
private void function(){
System.out.println("function");
}
public void method1(){
System.out.println("method");
}
public void method2(String s){
System.out.println("method" + s);
}
public String method3(String s,int i){
return s + "," + i;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
-
Class类中用于获取构造方法的方法
- Constructor<?>[] getConstructors():返回所有公共构造方法对象的数组
- Constructor<?>[] getDeclaredConstructors():返回所有构造方法对象的数组
- Constructor getConstructor(Class<?>… parameterTypes):返回单个公共构造方法对象
- Constructor getDeclaredConstructor(Class<?>…parameterTypes):返回单个构造方法对象
-
Constructor类中用于创建对象的方法
- T newInstance(Object… initargs):根据指定的构造方法创建对象
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<?> c = Class.forName("ClassLoader.demo01.Student");
//Constructor<?>[] getConstructors():返回所有公共构造方法对象的数组
Constructor<?>[] cons = c.getConstructors();
for (Constructor s:cons){
System.out.println(s);
}
System.out.println("=============================");
//onstructor<?>[] getDeclaredConstructors():返回所有构造方法对象的数组
Constructor<?>[] cons1 = c.getDeclaredConstructors();
for (Constructor s:cons1){
System.out.println(s);
}
System.out.println("=============================");
//Constructor<T> getConstructor(Class<?>... parameterTypes):返回单个公共构造方法对象
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
System.out.println(obj);
System.out.println("=============================");
//Constructor<?> declaredConstructor = c.getDeclaredConstructor(String.class);
//Object obj1 = declaredConstructor.newInstance("jkl");
//System.out.println(obj1);
//这里会产生异常Class ClassLoader.demo02.ReflectDemo01 can not access a member of class ClassLoader.demo01.Student with modifiers "private"
//因为这个构造方式是私有的,这里就要使用public void setAccessible(boolean flag):值为true,取消访问检查
//Constructor<T> getDeclaredConstructor(Class<?>...parameterTypes):返回单个构造方法对象
Constructor<?> con1 = c.getDeclaredConstructor(String.class);
con1.setAccessible(true);//取消访问检查
Object obj1 = con1.newInstance("jkl");
System.out.println(obj1);
}
- Class类中用于获取成员变量的方法
- Field[] getFields():返回所有公共成员变量对象的数组
- Field[] getDeclaredFields():返回所有成员变量对象的数组
- Field getField(String name):返回单个公共成员变量对象
- Field getDeclaredField(String name):返回单个成员变量对象
- Field类中用于给成员变量赋值的方法
- void set(Object obj,Object value):给obj对象的成员变量赋值为value
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<?> c = Class.forName("ClassLoader.demo01.Student");
//Field[] getFields():返回所有公共成员变量对象的数组
Field[] fields = c.getFields();
for (Field f:fields){
System.out.println(f);
}
System.out.println("====================================");
//Field[] getDeclaredFields():返回所有成员变量对象的数组
Field[] fields1 = c.getDeclaredFields();
for (Field f:fields1){
System.out.println(f);
}
System.out.println("====================================");
//获取无参构造方法创建对象
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
//Field getField(String name):返回单个公共成员变量对象
Field addressfield = c.getField("address");
//void set(Object obj,Object value):给obj对象的成员变量赋值为value
addressfield.set(obj,"北京");
System.out.println(obj);
System.out.println("====================================");
//Field getDeclaredField(String name):返回单个成员变量对象
Field namefield = c.getDeclaredField("name");
namefield.setAccessible(true);//取消访问检查
namefield.set(obj,"张三");
System.out.println(obj);
}
- Class类中用于获取成员方法的方法
- Method[] getMethods():返回所有公共成员方法对象的数组,包括继承的
- Method[] getDeclaredMethods():返回所有成员方法对象的数组,不包括继承的
- Method getMethod(String name,Class<?>… parameterTypes):返回单个公共成员方法对象
- Method getDeclaredMethod(String name,Class<?>… parameterTypes):返回单个成员方法
- Method类中用于调用成员方法的方法
- Object invoke(Object obj,Object… args):调用obj对象的成员方法,参数是args,返回值是Object类型
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<?> c = Class.forName("ClassLoader.demo01.Student");
//Method[] getMethods():返回所有公共成员方法对象的数组,包括继承的
Method[] methods = c.getMethods();
for (Method m:methods){
System.out.println(m);
}
System.out.println("====================================");
//Method[] getDeclaredMethods():返回所有成员方法对象的数组,不包括继承的
Method[] methods1 = c.getDeclaredMethods();
for (Method m:methods1){
System.out.println(m);
}
System.out.println("====================================");
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
//Method getMethod(String name,Class<?>... parameterTypes):返回单个公共成员方法对象
Method m = c.getMethod("method1");
m.invoke(obj);
System.out.println("=====================================");
//Method getDeclaredMethod(String name,Class<?>... parameterTypes):返回单个成员方法
Method m1 = c.getDeclaredMethod("method2", String.class);
m1.setAccessible(true);
m1.invoke(obj,"222");
}
注解
-
定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
-
作用分类:
-
编写文档:通过代码里标识的注解生成文档【生成文档doc文档】
-
代码分析:通过代码里标识的注解对代码进行分析【使用反射】
-
编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】
-
-
JDK中预定的一些注解
- @Override:检测被该注解标注的方法是否是继承自父类(接口)的
- @Deprecated:该注解标注的内容,表示已过时
- @SuppressWarnings:压制警告
- 一般传递参数all @SuppressWarnings(“all”)
-
自定义注解
- 格式:
元注解
public @interface 注释名称{
属性列表
}
-
本质:注解本质上就是一个接口,该接口,,默认继承Annotation接口
-
属性:接口中可以定义的成员方法
- 要求
- 属性的返回值类型有下列取值
- 基本数据类型
- String
- 枚举
- 注解
- 以上类型的数组
- 定义了属性,在使用时需要给属性赋值
- 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值
- 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可
- 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}省略
- 属性的返回值类型有下列取值
- 要求
-
元注解:用于描述注解的注解
- @Target:描述注解能够作用的位置
- ElementType取值:
- TYPE:可以作用于类上
- METHOD:可以作用于方法上
- FIELD:可以作用于成员变量上
- ElementType取值:
- @Retention:描述注解被保留的阶段
- @Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到
- @Documented:描述注解是否被抽取到api文档中
- @Inherited:描述注解是否被子类继承
- @Target:描述注解能够作用的位置
-
在程序中使用(解析)注解:获取注解中定义的属性值
- 获取注解定义的位置的对象(Class,Method,Field)
- 获取指定的注解
- 调用注解中的抽象方法获取配置的属性值