注解
注解概念
注解 Annotation 可以对程序做出解释,与注释的区别是:注解可以被编译器读取,而注释不行,只是解释一段代码。
注解可以在package、class、method、field上使用,相当于给他们添加了额外的辅助信息,可以通过反射机制实现对这些元数据的访问。格式:@注解名 后面可以跟参数值
内置注解
@Override 修饰方法,表示是重写父类中的一个方法
@Deprecated 可修饰方法、属性、类 表示已经弃用的方法或类,不推荐使用,但是可以使用
@SuppressWarnings 用来抑制编译时的警告信息,需要传递参数才能使用
元注解
元注解就是负责对其他注解进行说明的注解 目前定义了四个
@Target 表示注解可以用在哪些地方
@Retention 表示注解在什么地方还有效(runtime运行 < class编译 > sources源码)
@Document 表示注解是否包含在Javadoc中
@Inherited 表示子类可以继承父类的注解
自定义注解
使用@interface 进行自定义注解 会自动继承 java.lang.annotation.Annotation接口
@interface 用来声明一个注解 其中的每一个方法实际上是声明了一个配置参数 方法名就是参数名
返回值类型就是参数类型,如果只有一个参数,一般参数名为value,注解元素必须要有值
在定义注解时,可以使用空字符串或者0 作为默认值
反射
静态语言与动态语言
动态语言就是一类在运行时可以改变其结构的语言,在运行时代码可以根据某些条件改变其自身结构,主要的动态语言有:C#、JavaScript、PHP、Python等
静态语言就是运行时结构不变的语言,如Java、C、C++等
反射概念
反射 是Java被视为动态语言的关键 反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息,能直接操作任意对象的内部属性和方法
在加载完类之后,在堆内存的方法区会产生一个class类型的对象,包含了完整的类结构信息。可通过这个对象看到类的结构。
通过对象反射出一个类的名字
反射的优缺点
优点:可以实现动态创建对象和编译,体现出很大的灵活性
缺点:对性能有影响,使用反射基本上是一种解释操作,相当于告诉JVM,希望他帮我们做什么,这类操作慢与直接执行相同的操作。
Class类
每个类都会有一个Class对象,这个Class对象包含了类的结构
Class本身已是一个类,一个加载的类在JVM中只会有一个Class实例,每个类的实例都会记得自己对应的Class对象。通过Class可以完整的得到一个类中的所有被加载的结构。
获取Class实例的方法
-
若已知具体的类,通过类的class属性获取,最为可靠,程序性能最高
-
已知某个类的实例,调用该对象的getClass()获取Class对象
-
已知类的全类名,且该类在类路径下,可通过Class类的静态方法forname()来获取
-
内置基本数据类型可直接用类名.Type
-
利用ClassLoader
Java内存分析
在Java中,有三块存放内存的地方,分别是 堆内存、栈内存、方法区内存。
堆内存
-
存放new的对象和数组
-
可以被所有的线程共享,不会存放别的对象引用
栈内存
-
存放基本数据类型(包含这个基本类型的具体数值)
-
引用对象的变量(会存放这个引用在堆里面的具体地址)
方法区内存
-
可以被所有线程共享
-
包含了所有的class和static变量
类的加载过程
类的加载过程分为三个阶段:类的加载、类的链接、类的初始化
类的加载
将类的class文件读入内存,创建Class对象,由类加载器完成
类的链接
将类的二进制数据合并到JRE中
类的初始化
JVM负责对类进行初始化
简单来说就是:
-
加载到内存,产生一个Class对象
-
链接,进行默认赋值
-
执行clinit()方法进行初始化
类的主动/被动引用
类的主动引用
-
当虚拟机启动,先初始化main方法所在的类
-
new一个类的对象
-
调用类的静态成员和静态方法(常量除外)
-
使用反射对类进行调用
-
当初始化一个类,若父类未初始化,先初始化父类
类的被动引用
当访问一个静态域,真正声明这个域的类会初始化,比如子类调用父类静态变量,不会对子类进行初始化
通过数组定义类引用,不会触发此类的初始化
引用常量不会触发此类的初始化(因为常量在链接阶段就存入调用类的常量池中了)
类加载器的作用
一个字节码文件加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后再堆中生成一个代表这个类的Class对象,作为方法区中类数据的访问入口。
类缓存 一旦类被加载到加载器中,它将维持加载一段时间,JVM的垃圾回收机制可以回收这些Class对象
使用反射执行对象方法
创建类的对象,可以通过调用Class对象的newInstance()方法来获得
执行方法:通过Method类完成,通过Class类的getMethod()获得一个Method对象,再调用Method对象的invoke()方法进行调用,传入的参数是 具体的哪个对象,以及方法需要的参数
如果方法的声明是private,需要在调用此方法之前,调用方法对象的setAccessible(true)方法,即可访问
setAccessible()作用的启动和禁用访问安全检查的开关,参数为true表示取消访问检查,设置为true之后,可提高反射的效率