注解和反射
注解(Annotation)
什么是注解
从JDK5开始,Java增加对元数据的支持,也就是注解,注解与注释是有一定区别的,可以把注解理解为代码里的特殊标记,这些标记**可以在编译,类加载,运行时被读取,并执行相应的处理。**通过注解开发人员可以在不改变原有代码和逻辑的情况下在源代码中嵌入补充信息。
内置注解
Java的5个内置注解(基本注解):
-
@Deprecated 过时
- 用于过时的类、方法、成员变量等
-
@Override
- 覆盖父类方法
-
@SuppressWarning
- 阻止警告
-
@FunctionaInterface
- 指定接口必须为函数式接口
-
@SafeVarargs
- 一直"堆污染警告"
元注解
- 元注解的作用就是用来负责注解其他注解,java定义了4个标准的meta-annotation类型,他们被用来提供对其他annotation类型作说明
- 他们可以在java.lang.annotation包中找到,分别是(@Target,@Retention,@Documented,@Inherited)
- @Target:用于描述注解的使用范围
- @Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(SOURCE<CLASS<RUNTIME)
- @Documented:说明该注解将被包含在javadoc中
- @Inherited:说明子类可以继承父类中的该注解
自定义注解
使用 @interface可以自定义注解,自动实现java.lang.annotation.Annotation接口
-
//注解的参数: 参数类型 + 参数名(); String name() default "";
-
int id() default -1;//如果默认值为-1代表不存在
-
如果注解只有一个属性,把属性名设置为value,使用的时候可以省略不写
反射(Java Reflection)
动态语言:可以在运行时改变其结构的语言
主要动态语言:Object-C、C#、JavaScript、PHP、Python等
静态语言:运行时结构不可改变的语言就是静态语言,如Java、C、C++
java通过反射可以实现动态语言的特性,可以称为“准动态语言”
1. java反射机制概述
Reflection(反射)是java被视为动态语言的关键,反射机制允许程序在执行期借助与Reflection API获取任何类的内部信息,并能直接操作任意对象的内部属性及方法
Class c = Class.forName("java.lang.String")
2. 理解Class类并获取Class实例
Person person = new Student();
//方式一:通过new对象获取
Class c1 = person.getClass();
System.out.println(c1.getName());
//方式二:通过forName获取
Class<?> c2 = Class.forName("com.Student");
System.out.println(c2.hashCode());
//方式三:通过类名.class获取
Class<Student> c3 = Student.class;
System.out.println(c3.hashCode());
//方式四:基本内置类型的包装类都有一个Type属性
Class<Integer> c4 = Integer.TYPE;
System.out.println(c4);
//获得父类类型
Class c5 = c1.getSuperclass();
System.out.println(c5);
哪些类型可以有Class对象
class:类
interface:接口
[]:数组
Enum:枚举
annotation:注解@interface
primitiva type:基本数据类型
void:
3. 类的加载与ClassLoader
-
加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象
-
链接
:将java类的二进制代码合并到JVM的运行状态中的过程;
- 验证:确保加载的类符合JVM规范;
- 准备:正式为类变量(static)分配内存并设置变量默认初始值(非任何显示赋值),这些内存都在方法区中分配;
- 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程
-
初始化
:JVM负责对类进行初始化;
- 执行类构造器()方法的: 此方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的(类构造器是构造类信息的,并非new对象构造器)
- 如其父类为进行初始化,则初始化操作从先从父类进行;
- 虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步;
什么时候会发生类的初始化
-
类的主动引用(一定会发生类的初始化)
-
- 当虚拟机启动,先初始化main方法所在的类
- new一个类的对象
- 调用类的静态成员(除了final常量)和静态方法
- 使用java.lang.reflect包的方法对类进行反射调用。
- 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
-
类的被动引用(不会发生类的初始化)
-
- 当访问一个静态域时,只要真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化
- 通过数组定义类引用,不会触发此类的初始化
- 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中)
//1.主动引用
Son son = new Son();
//反射也会产生主动引用
Class<?> aClass = Class.forName("com.Father");
}
类加载器ClassLoader的作用:
除了上面提到的作用,还有一个类缓存机制:一旦某个类被加载到内存中,将位置加载(缓存)一段时间,相当于一个缓存了一个Class对象,无论此类创建多少个实例,都是从这唯一的结构中获取信息;GC也可以回收这些Class对象;
JVM规范定义的类的加载器类型如下:
4. 创建运行时的对象
调用Class对象的newInstance方法
//构造一个对象
User user = (User)c1.newInstance();//本质上是调用了无参构造器
//通过构造器创建对象
Constructor declaredConstructor = c1.getDeclaredConstructor(int.class,String.class,int.class);//调用有参构造方法
Object jack = declaredConstructor.newInstance(001,"马云", 50);
System.out.println(jack);
//通过反射调用普通方法
User user3 = (User)c1.newInstance();
//通过反射获取一个方法
Method setName = c1.getDeclaredMethod("setName", String.class);
setName.invoke(user3,"小明");
System.out.println(user3.getName());
//通过反射操作属性
User user4 = (User)c1.newInstance();
Field name = c1.getDeclaredField("name");
name.setAccessible(true); //设置为true可以允许访问private属性
name.set(user4,"小红");
System.out.println("========================");
System.out.println(user4.getName());
5. 反射操作泛型(了解即可)
public class Demo10 {
public void test01(Map<String,User> map, List<User> list){
System.out.println("test01");
}
public Map<String,User> test02(){
System.out.println("test02");
return null;
}
public static void main(String[] args) throws NoSuchMethodException {
Method test01 = Demo10.class.getMethod("test01", Map.class, List.class);
Type[] genericParameterTypes = test01.getGenericParameterTypes();
for (Type genericParameterType : genericParameterTypes) {
System.out.println(genericParameterType);
if(genericParameterType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
Method test02 = Demo10.class.getMethod("test02",null);
Type genericReturnType = test02.getGenericReturnType();
if(genericReturnType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
}
6. 反射获取注解
public class Demo11 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c1 = Class.forName("com.Student2");
//通过反射获取注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//获得注解的value的值
TableLee annotation = (TableLee)c1.getAnnotation(TableLee.class);
String value = annotation.value();
System.out.println(value);
//获得类指定的注解
Field name = c1.getDeclaredField("name");
fieldLee annotation1 = name.getAnnotation(fieldLee.class);
System.out.println(annotation1.columnName());
System.out.println(annotation1.type());
System.out.println(annotation1.length());
}
}
@TableLee("db_student")
class Student2{
@fieldLee(columnName = "db_id",type = "int",length = 10)
private int id;
@fieldLee(columnName = "db_age",type = "int",length = 10)
private int age;
@fieldLee(columnName = "db_name",type = "varchar",length = 10)
private String name;
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableLee{
String value();
}
//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface fieldLee{
String columnName();
String type();
int length();
}