java注解与反射
java注解与反射
- java注解与反射十分重要,是很多框架的底层
注解(Annotataion)
-
注解的作用:
1. 不是程序本身,可以对程序作出解释 1. 可以被其他程序读取
-
注解的格式:@注释名,如@override表示重写方法,而且有些还可以添加一些参数值,如@SuppressWarnings(value="unchecjed")
-
注解可以附加在package,class,method,field等上面,相当于添加了额外的辅助信息。可以通过反射机制编程实现对这些元数据的访问
内置注解
- @Override:重写方法
- @Deprecated:表示不被鼓励使用或者已废弃已过时
- @SuppressWarning:用来抑制编译时的警告信息,但是需要添加一个或多个参数:如("all"),("unchecked")等,平时最好还是不要用这个
使用方法:
@Override
public void run() {
}
其他类似。
元注解
- 负责注解其他注解
- 包括(@Target,@Retention,@Documented,@Inherited),分别表示
- 描述注解使用范围
- 需要上面级别保存该注释信息,用于描述注解的生命周期(SOURCE<CLASS<RUNTIME,分别表示源代码,编译后的class文件,运行时)
- 说明该注解将被包含在javadoc中
- 说明子类可以继承父类中的该注解
//下面只能用到方法上,value的值可以取多个至于那些可以取CTRL+左键点击ElementType类去看,另外可以省略value=
@Target(value = ElementType.METHOD)
//只能存在于源码上。值有那些同上,自己点进去看RetentionPolicy类,一般取RUNTIME
@Retention(value = RetentionPolicy.SOURCE)
//这两个不多说
@Documented
@Inherited
//自定义注解
@interface MyAnnotation{
}
自定义注解
- 使用@interface自定义注解,格式 public @interface 注解名{}
- 其中每一个方法实际上是声明了一个配置参数
- 方法名称就是参数名称
- 返回值类型就是参数类型(返回值只能是基本类型或class,String,enum)
- 可以用default来声明参数的默认值
- 如果只有一个参数,一般参数名为value,只有一个参数时使用可以不写参数名=
- 注解元素必须有值,我们定义注解元素时,经常使用空字符串,默认值为0
//元注解
@Retention(RetentionPolicy.RUNTIME)
@Inherited
//自定义注解
@interface MyAnnotation{
// 注解参数:参数类型+参数名(),可以设置默认值,没默认值的话使用时必须传参
String name() default "";
int a();
int c() default 1;
}
//使用
@MyAnnotation(name = "lihua",a = 1)
反射(Reflection)
java是一个静态语言(还有c,c++),因为它相对于动态语言来说,运行时结构不变,但是java可以利用反射来获得类似动态语言的特性,成为“准动态语言”。
动态语言就是运行时可以改变其结构的语言,如:c#,js,php,python等
-
反射机制运行程序在执行的过程中通过反射取得任何类的内部信息,并且能够直接操作任意对象的内部属性及方法。
-
主要API是Class
-
反射是什么?
参考一下Class对象在java编译运行的那个部分:
反射提供的部分功能:
-
优点与缺点
优点:动态编译和创建,体现很大的灵活性
缺点:对性能有影响
Class类
除了之前描述的,Class类还有一些特征:
- Class 本身也是一个类
- Class对象只能有系统建立对象
- 一个加载的类在JVM中只会有一个Class实例
- 一个Class对象对应的是一个加载到JVM中的一个.class文件
- 每个类的实例都会记得自己是由那个Class实例所生成的
- 通过Class可以完整得到一个类中的所有被加载结构
- Class类是反射的根源,,你要进行的任何针对类的动态加载运行等反射操作,都需要先得到该类的Class对象
public static void main(String[] args) throws ClassNotFoundException {
/*1.下面是通过反射获取Class对象的一种方式,参数是文件路径
2.一个类在内存中只有一个Class类,在多创建几个该类的Class
也是同一个。
3一个类被加载后,类的整个结构都会被封装在Class对象中
4.我们经常用的Object的getClass方法就是返回一个Class对象,也是创建Class对象的一种常用方式
5.反射在某种情况下可以理解为通过一个对象获得类
*/
Class<?> aClass = Class.forName("com.xxx.MyThread");
System.out.println(aClass);
}
//out:class com.xxx.MyThread
Class类的常用方法:
Class类的四种创建方式
虽然上面可能已经描述过Class对象的创建方式的两种,这里还是再来具体写一下:
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
Teacher teacher = new Teacher();
// 方式一:通过对象
Class aClass = teacher.getClass();
System.out.println(aClass);
// 方式二:forname
Class<?> class1 = Class.forName("com.xxx.Teacher");
System.out.println(class1);
// 比较两者
System.out.println(aClass==class1);
// 方式三:通过类名.class获得
Class<Teacher> teacherClass = Teacher.class;
System.out.println(teacherClass);
// 比较
System.out.println(teacherClass==class1);
// 方式四:基本数据类型的包装类的type属性
Class<Integer> type = Integer.TYPE;
System.out.println(type);
// 得到父类Class类型
Class<?> superclass = class1.getSuperclass();
System.out.println(superclass);
// 补充一下,获得注解和void和Class本身的Class对象
// Override.class
// void.class
//Class.class
}
}
class Teacher extends Person{}
/*
class com.xxx.Teacher
class com.xxx.Teacher
true
class com.xxx.Teacher
true
int
class com.xxx.Person
那些类型可以有Class对象?
包括以下:
注意:数组长度即使不一致,只要类型和维度一样,就是同一个Class
Class类的方法
获得类完整结构
public class MyThread {
private int a;
public void run(String a){}
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public MyThread(int a) {
this.a = a;
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
Class c1 = Class.forName("com.xxx.MyThread");
//获取类名
System.out.println(c1.getName());
System.out.println(c1.getSimpleName());
// 获得类属性
Field[] fields = c1.getFields();//只能找到public属性
fields = c1.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
// 获得类方法
System.out.println("-----------------------");
Method[] methods = c1.getMethods();//包括继承的方法
methods=c1.getDeclaredMethods();//只包括本类的方法
for (Method method : methods) {
System.out.println(method);
}
System.out.println("-------------------------------");
// 获得指定方法:没s,两个参数分别是方法名和方法参数类型的Class
System.out.println(c1.getMethod("run",String.class));
// 获得构造器
System.out.println("----------------");
Constructor[] constructors = c1.getConstructors();//获得public构造器
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
// 同理getDeclaredConstructors()获得的所有构造器,
//获得指定的构造器,参数为构造器参数的Class
System.out.println("--------------------------");
Constructor constructor = c1.getConstructor(int.class);
System.out.println(constructor);
}
}
/*out:
public void com.xxx.MyThread.run(java.lang.String)
public void com.xxx.MyThread.setA(int)
public int com.xxx.MyThread.getA()
-------------------------------
public void com.xxx.MyThread.run(java.lang.String)
----------------
public com.xxx.MyThread(int)
--------------------------
public com.xxx.MyThread(int)
操作类结构
public class MyThread {
private int a;
public void run(String a){
System.out.println(a);
}
public int getA() {
return a;
}
public MyThread() {
}
public void setA(int a) {
this.a = a;
}
public MyThread(int a) {
this.a = a;
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchFieldException {
Class<?> aClass = Class.forName("com.xxx.MyThread");
// 构建一个对象:本质是调用无参构造器,其该类必须有无参构造器
// MyThread myThread = (MyThread)aClass.newInstance();
// 通过构造器创建对象,可以用有参构造函数
// Constructor constructor= aClass.getDeclaredConstructor(int.class);
// MyThread myThread1=(MyThread)constructor.newInstance(1);
// 通过反射调用方法
// 通过反射获取一个方法
MyThread myThread = (MyThread) aClass.newInstance();
Method setName = aClass.getMethod("run", String.class);
// invoke:激活,参数:对象名,方法参数
setName.invoke(myThread,"helloworld");
// 通过反射操作属性
Field a=aClass.getDeclaredField("a");
// 取消安全检查,否则无法操作私有属性.method,field,consructor都有它,关闭也可以提高反射效率
a.setAccessible(true);
a.set(myThread,5);
System.out.println(myThread.a);
}
/*out:
helloworld
5
类的加载过程
解释一下符号引用和直接引用:
符号引用你可以理解为要引用的类目前在内存中还没被加载出来,JVM当然不知道它在哪里。所以就用唯一的符号来表示就好像给它赋了一个变量,等到链接的时候类加载器解析地址完成变成直接引用
直接引用就是存储引用类在真实内存中的地址
总而言之还是这张图:
什么时候会发生类的初始化
主动引用比较好理解,下面只演示一下被动引用“
public class Main {
// 初始化时执行一次静态代码块
static {
System.out.println("main函数所在类被加载了");
}
public static void main(String[] args) throws ClassNotFoundException {
System.out.println(son.sonAge);
/*
out:
main函数所在类被加载了
父类被加载了
子类被加载了
10
*/
//------------------------------------------------------
// 这就是第一条的意思
System.out.println(son.fatherAge);
/*
out:
main函数所在类被加载了
父类被加载了
23
*/
//--------------------------------------------------------
// 第二条:本质上只是开辟了一片空间
son[] arr = new son[5];
/*
out:
main函数所在类被加载了
*/
//--------------------------------------------------------
//常量是不会引起初始化的
System.out.println(son.M);
/*
out:
main函数所在类被加载了
1
*/
}
}
class father {
static int fatherAge = 23;
// 初始化时执行一次静态代码块
static {
System.out.println("父类被加载了");
}
}
class son extends father{
static int sonAge = 10;
static final int M = 1;
static {
System.out.println("子类被加载了");
}
}
类加载器
- 类加载的作用:将class文件字节码内容加载到内存中,并且将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class 对象,作为方法区中类数据的访问入口。简单来说,类加载器的作用就是把类(class)装进内存
- 类缓存:标准的JavaSE类加载器可以按照要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段数据。不过JVM垃圾回收机制可以回收这些Class对象
类加载器有那些?
java的主要jar包就是rt.jar,引导类加载器就是加载这一类核心jar包
public static void main(String[] args) throws ClassNotFoundException {
// 获取系统类的加载器
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
System.out.println(classLoader);
// 获取系统类加载器的父类加载器-->拓展加载器
ClassLoader classLoader1 = classLoader.getParent();
System.out.println(classLoader1);
// 获得拓展类加载器的父类加载器-->根加载器(c/c++):这个java是无法获得的
ClassLoader classLoader2 = classLoader1.getParent();
System.out.println(classLoader2);
System.out.println("------------------------");
// 测试当前类是那个加载器加载的
ClassLoader classLoader3 = Class.forName("com.xxx.MyThread").getClassLoader();
System.out.println(classLoader3);
// 测试jdk内部类是谁加载的:根加载器,输出不出来
ClassLoader classLoader4 = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader4);
//如何获得系统类加载器可以加载的路径
System.out.println(System.getProperty("java.class.path"));
}
/*out:
jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc
jdk.internal.loader.ClassLoaders$PlatformClassLoader@5594a1b5
null
------------------------
jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc
null
本处按道理来说应该输出所有jar包路径,但是只说出了文件根目录,可能是因为jdk版本的原因,不确定,本处存疑
反射操作泛型
public class MyThread {
public void test01(Map<String,MyThread> map, List<MyThread> list){
}
public Map<String,Map> test02(){
return null;
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchFieldException {
//获得参数泛型
Method method = MyThread.class.getMethod("test01", Map.class,List.class);
//得到的是方法的参数Map,List
Type[] genericExceptionTypes = method.getGenericParameterTypes();
for (Type genericExceptionType : genericExceptionTypes) {
if(genericExceptionType instanceof ParameterizedType){
System.out.println(genericExceptionType);
//得到的是方法参数的泛型
Type[] actualTypeArguments = ((ParameterizedType) genericExceptionType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
//获得返回值泛型
Method method1 = MyThread.class.getMethod("test02");
Type genericReturnType = method1.getGenericReturnType();
if(genericReturnType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
}
/*
java.util.Map<java.lang.String, com.xxx.MyThread>
class java.lang.String
class com.xxx.MyThread
java.util.List<com.xxx.MyThread>
class com.xxx.MyThread
class java.lang.String
interface java.util.Map
反射操作注解
ORM->对象关系映射
public class MyThread {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class<?> aClass = Class.forName("com.xxx.Teacher");
// 通过反射获得注解
Annotation[] annotations = aClass.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
// 获得注解的value值
Table table = (Table) aClass.getAnnotation(Table.class);
String value = table.value();
System.out.println(value);
// 获得类指定注解
Field f = aClass.getDeclaredField("id");
Table annotation = f.getAnnotation(Table.class);
System.out.println(annotation.value());
}
}
@Table(value = "aaa")
class Teacher{
@Table2(value = "aaa")
private int id;
@Override
public String toString() {
return super.toString();
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
String value();
}
//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Table2{
String value();
}
/*
@com.xxx.Table(value="aaa")
aaa