类加载、反射、注解、动态代理(JDK)
类加载
程序要使用某个类时如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤对类进行初始化。正常情况JVM会连续完成这三个步骤,所以有时把这三个步骤统称为类加载或类初始化。
类的加载
- 将class文件读入内存,并为之创建一个java.lang.Class对象
- 任何类被使用时,系统都会为之建立一个java.lang.Class对象
类的连接
- 验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致
- 准备阶段:负责为类的类变量分配内存,并设置默认初始化值
- 解析阶段:将类的二进制数据中的符号引用替换为直接引用
类的初始化
主要对类变量进行初始化。
类的初始化步骤
- 假如类还未被加载和连接, 则程序先加载并连接该类
- 假如该类的直接父类还未被初始化,则先初始化其直接父类
- 假如类中有初始化语句,则系统依次执行这些初始化语句
注意:在执行第2个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤1-3
类的初始化时机: .
- 创建类的实例
- 调用类的类方法
- 访问类或者接口的类变量,或者为该类变量赋值
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类,
- 直接使用java.exe命令来运行某个主类
类加载器
负责将.class文件加载到内存中,并为之生成对应的java.lang.Class对象
JVM的类加载机制
- 全盘负责: 就是当一个类加载器负责加载某个Class时, 该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类载器来载入
- 父类委托:就是当一个类载器负责加载某个Class时,先让父类加载器试图加载该Class, 只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
- 缓存机制:保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,类载器先从缓存区中搜索该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存储到缓存区
ClassLoader:负责加载类的对象
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():返回用于委派的系统类加载器
- ClassLoader getParent(): 返回父类加载器进行委派
反射
是指在运行时去获取一个类的变量和方法信息,然后通过获取到的信息来创建对象,调用方法的一种机制。由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展。
获取Class类的对象
我们要想通过反射去使用一个类,首先我们要获取到该类的字节码文件对象,也就是类型为Class类型的对象
以下三种方式获取Class类型的对象
- 使用类的class属性来获取该类对应的Class对象。 举例: Student.class将会返回Student类对应的Class对象
- 调用对象的getClass(方法,返回该对象所属类对应的Class对象,该方法是Object类中的方法,所有的Java对象都可以调用该方法
- 使用Class类中的静态方法forName(StringclassName), 该方法需要传入字符串参数,该字符串参数的值是某个类的全路径,也就是完整包名的路径
反射获取构造方法并使用
Class类中用于获取构造方法的方法
- Constructor<?> [] getConstructors(:返回所有公共构造方法对象的数组
- Constructor<?> [] getDeclaredConstructors0:返回所有构造方法对象的数组
- Constructor<?>getConstructor(Class <?> …parameterTypes):返回单个公共构造方法对象
- Constructor<?> getDeclaredC onstructor(Class<?> … parameterTypes):返回单个构造方法对象
Constructor类中用于创建对象的方法
- T newlnstance(Object… initargs):根据指定的构造方法创建对象
public void setAccessible(boolean flag):值为true,取消访问检查。则反射可使用私有构造方法来创建对象。
反射获取成员变量并使用
Class类中用于获取成员变量的方法
- Field[] getFields():返回所有公共成员变对象的数组
- Field[] getDeclaredFields():返回所有成员变量对象的数组
- Field getField(String name):返回单个公共成员变量对象
- Field getDeclaredField(String name):返回单个成员变量对象
Field类中用于给成员变量赋值的方法
- void set(Object obj, Object value):给obj对象的成员变量赋值为value
反射获取成员方法并使用
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类型
注解
定义:注解(Annotation) ,也叫元数据。一 种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同-一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
作用分类:
- 编写文档:通过代码里标识的注解生成文档[生成文档doc文档]
- 代码分析:通过代码里标识的注解对代码进行分析[使用反射]
- 编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查[override]
JDK中预定义的一些注解
- @override : 检测被该注解标注的方法是否是继承自父类(接口)的
- @Deprecated :该注解标注的内容,表示已过时
- @SuppressWarnings :压制警告
*一 般传递参数all @SuppressWarnings(“all”)
自定义注解
一.格式:
- 元注解
public @interface 注解名称{
属性列表;
}
二.本质:注解本质上就是一个接口,该接口默认继承Annotation接口
- public interface MyAnno extends java .lang . annotation.Annotation {}
三.属性:接口中的抽象方法
*要求:
1.属性的返回值类型有下列取值
- 基本数据类型
- String
- 枚举
- 注解
- 以上类型的数组
2.定义了属性,在使用时需要给属性赋值
- 1.如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
- 2.如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。
- 3.数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略
四.元注解:用于描述注解的注解
- @Target :描述注解能够作用的位置
*ElementType取值 :
*TYPE :可以作用于类上
*METHOD :可以作用于方法上
*FIELD:可以作用于成员变量上 - @Retention :描述注解被保留的阶段
*@Retent ion( RetentionPolicy . RUNTIME) :当前被描述的注解,会保留到class字节码文件中,并被JVM读取到 - @Documented :描述注解是否被抽取到api文档中
- @Inherited :描述注解是否被子类继承
在程序使用(解析)注解:获取注解中定义的属性值
1.获取注解定义的位置的对象(class, Method, Field)
2.获取指定的注解
*getAnnotation(Class)
3.调用注解中的抽象方法获取配置的属性值
动态代理(JDK)
动态代理是实现代理模式的一种方法,他比静态代理强在了他使用了反射,可以在运行时动态创建代理类。
代码实现:
代理对象接口类
package proxy;
public interface Star
{
String sing(String name);
String dance(String name);
}
代理对象接口实现类
package proxy;
public class LiuDeHua implements Star
{
@Override
public String sing(String name)
{
System.out.println("给我一杯忘情水");
return "唱完" ;
}
@Override
public String dance(String name)
{
System.out.println("开心的马骝");
return "跳完" ;
}
生成代理类
方法CreatProxyedObj返回的对象才是我们的代理类,需要三个参数,前两个参数的意思是在同一个classloader下通过接口创建出一个对象,该对象需要一个属性,也就是第三个参数,它是一个InvocationHandler。需要注意的是这个CreatProxyedObj方法不一定非得在我们的StarProxy类中,往往放在一个工厂类中。
package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class StarProxy implements InvocationHandler
{
// 目标类,也就是被代理对象
private Object target;
public void setTarget(Object target)
{
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
// 这里可以做增强
System.out.println("收钱");
Object result = method.invoke(target, args);
//该InvocationHandler包含被代理对象,并负责分发请求给被代理对象,分发前后均可以做增强
return result;
}
// 生成代理类
public Object CreatProxyedObj()
{
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
}
一般使用代理的代码过程
//new一个目标对象
Star ldh = new LiuDeHua();
//new一个InvocationHandler,将目标对象set进去
StarProxy proxy = new StarProxy();
proxy.setTarget(ldh);
//通过CreatProxyedObj创建代理对象,强转为目标对象的接口类型即可使用,实际上生成的代理对象实现了目标接口。
Object obj = proxy.CreatProxyedObj();
Star star = (Star)obj;
本文地址:https://blog.csdn.net/weixin_49369478/article/details/107373264
上一篇: PHP去除所有的空格
下一篇: 一句话拿到客户端IP