反射
类加载
过程
-
加载
- 通过类加载器获得二进制字节流。
- 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
-
链接
-
验证:确保被加载类的正确性(正确性的校验)
-
准备:负责为类的静态成员分配内存并设置默认初始化值
-
static int a = 10
-
解析:将类中的符号引用替换为直接引用(真实的内存地址)
-
-
初始化
- 给静态成员变量赋初值,执行静态代码块内容
类加载器
分类
Bootstrap ClassLoader 根类加载器
负责Java运行时核心类的加载,JDK中JRE的lib目录下rt.jar
Extension ClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载,在JDK中JRE的lib目录下ext目录
Sysetm(App) ClassLoader 系统类加载器/应用加载器
负责加载自己定义的Java类 classPath
双亲委派模型
逻辑上的父子关系,并不是继承关系
类加载是一个懒加载的过程,类加载器也是,我们的程序一开始会在APP 的cache中寻找,如果缓存中存在,那就直接返回,否则将会交给Extension,继续在缓存中查找是否找到,然后再交给Boot,再寻找缓存,如果都没有的话就再向下交给Extension和APP
总之就是,低级的类加载器并不会自己立马加载,而是会向上抛给父亲,依次抛给Boot后再往回抛
类加载时机
-
创建类的实例(首次创建该类对象)
-
访问类的静态变量(首次)
-
调用类的静态方法(首次)
-
使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
-
加载某个类的子类,会先触发父类的加载
-
直接使用java.exe命令来运行某个主类,也就是执行了某个类的main()方法
java代码的3个阶段
反射
获取运行时类的各种信息的手段 或 技术
获取字节码文件对象的3种方式
反射技术的起点就是获取字节码文件对象
Class.forName(“全类名”) :常用
类名.Class
对象.getClass()
配置文件(Properties)
一般是以键值对的形式来存储信息
配置文件放配置信息 放数据库配置信息 第三方的账号信息
构造方法
Properties() 创建一个无默认值的空属性列表
成员方法
void | load(InputStream inStream) 从输入流中读取属性列表(键和元素对)。 |
---|---|
void | load(Reader reader) 按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。 |
String | getProperty(String key) 用指定的键在此属性列表中搜索属性 |
---|---|
注意:
在配置文件中
- 注释是#
- 连接符号一般用=,用:也可以 但是一般用=
获取构造方法(Constructor)
通过反射获取所有构造方法
Constructor[] getConstructors()
Constructor[] getDeclaredConstructors()
获取指定构造方法
Constructor getConstructor(Class<?>... parameterTypes) Constructor getDeclaredConstructor(Class<?>… parameterTypes)
指定形参列表的类型的Class
实例化对象
newInstance(Object… initargs) 使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
也可以直接用Class对象来实例化,但前提是该类有无参构造
暴力破解
void setAccessible(boolean flag) 可以调用私有的构造方法
Declared方法获取方法可以不受访问权限的限制,但不会得到父类
获取成员变量(Field)
通过反射获取所有成员变量
Field[] getFields()
Field[] getDeclaredFields()
获取指定成员变量
Field getField(String name)
Field getDeclaredField(String name)
通过Field读写对象的成员变量(可暴力破解)
Object get(Object obj):获取值,传入对象
void set(Object obj, Object value):赋值,传入对象
获取成员方法(Method)
获取所有成员方法
Method[] getMethods()
Method[] getDeclaredMethods()
获取指定的成员方法
Method getMethod(String name, Class<?>... parameterTypes) Method getDeclaredMethod(String name, Class<?>… parameterTypes)
利用Method调用对象的方法
Object invoke(Object obj, Object… args)
获取注解
先判断方法/类上是否有注解
isAnnotationPresent(MethodInterface.class)
//获取注解
getAnnotation(MethodInterface.class)
最后再通过注解名访问注解中的信息
注解
作用:传递信息 → 编译器看
可以参与编译
跟我们的类 接口是一个级别的
自定义注解
语法
// 注解定义
权限修饰符 @interface 注解名称{
// 注解体定义
// 属性类型 属性名称();
// 属性类型 属性名称();
// 属性类型 属性名称();
//....
}
public @interface MyAnno{
int age();
String name();
}
属性类型
- java基本数据类型
- String类型
- Class类型
- 枚举类型(Enum)
- 注解类型
- 以及以上类型的数组
注解的使用
@注解名(给每个属性都要赋值)
@interface Age{
// 属性
int minAge();
int maxAge();
}
class Stu{
@Age(maxAge = 10,minAge = 5)
int age;
String name;
}
注意:
- 使用的时候每个属性都要赋值
- 可以选择不赋值,但是必须要有默认值
- 数组的时候,赋值用{ }
- 如果只有一个属性名是value ,可以简化赋值
- 如果是引用类型,不能是null
元注解
元注解:描述注解的注解(注解的注解) 元数据 meta data
常用元注解:
1. @Retention元注解,来定义我们自己定义的注解的保留级别.
1. RetentionPolicy.RUNTIME
2. RetentionPolicy.CLASS (默认是)
3.RetentionPolicy.SOURCE
2. @Target元注解,注解可以作用的目标
对于注解而言,可以作用的目标:
1. 整个类 ElementType.TYPE
2. 成员变量 ElementType.FIELD
3. 构造方法 ElementType.CONSTRUCTOR
4. 成员方法 ElementType.METHOD
GC
jvm运行时数据区
程序计数器
程序计数器(Program Counter Register)是一块较小的内
存空间,它的作用可以看做是当前线程所执行的字节码的行
号指示器。(每个线程都有自己的程序计数器,线程隔离)
Java虚拟机栈
它描述的是Java 方法执行的内存模型:每个方法被执行的时候都
会同时创建一个栈帧(Stack Frame )用于存储局部变量表、操
作栈、动态链接、方法出口等信息。线程私有(线程隔离)
本地方法栈(线程私有)
本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是
非常相似的,其区别不过是虚拟机栈为虚拟机执行Java 方法(也
就是字节码)服务,而本地方法栈则是为虚拟机使用到的
Native 方法服务。
Java堆
此内存区域的唯一目的就是存放对象,一个JVM实例只
存在一个堆,堆内存的大小是可以调节的。
堆内存是线程共享的。totalMemory 默认是系统64分之一 250M maxMemory 默认是系统的四分之一 4g
-Xms200m -Xmx300m
方法区(线程共享)
方法区(Method Area)与Java 堆一样,是各个线程共享
的内存区域,它用于存储已被虚拟机加载的类信息、常量
、静态变量等数据。
搜索垃圾
引用计数算法
当一个对象没有任何引用指向他,即无法访问到,该对象会被认为是垃圾
根搜索算法
通过一系列的“GC Roots”的对象作为起始点,从这些节点开始往下搜索,搜索的走过的路径称为引用链,当一个对象到“GC Roots”没有引用链可达时,就被看作为垃圾
java中可以作为GC Roots对象包括以下几种:
1.虚拟机栈(栈帧中的本地变量表)中的引用对象。
2.方法区中的类静态属性引用的对象。
3.方法区中的常量引用的对象。
4.本地方法栈中JNI(也即一般说的Native方法)的引用的对象。
回收垃圾
标记清除算法
标记出所有要回收的对象,然后再统一回收
缺点:回收后内存碎片化,无法分配一个连续的内存空间
标记复制算法
将内存划分为相等的两块,每次只使用一块,当这一块内存使用完之后,将活着的对象复制到另一块内存中并作为新的可用内存块,将原来的内存块直接一次性清空,如此反复进行
缺点:内存利用率低,如果有大量存活的对象,需要大量的复制,浪费时间
标记整理算法
在标记清除结束之后,让存活的对象向内存的一端移动,然后清理掉边界以外的内存,得到一个连续的内存空间
分代收集算法
- 基于2个假说
- 弱分代假说(Weak Generational Hypothesis):
- 绝大多数对象都是朝生夕灭的.
- 强分代假说(Strong Generational Hypothesis):
- 熬过越多次垃圾收集过程的对象就越难以消亡。(简单理解就是越老的对象就具有”老而不死”的特性)
什么时候回收垃圾
- 申请heap space失败后会触发GC回收
- 系统进入idle后一段时间会进行回收
- 主动调用GC进行回收System.gc()