注解与反射

1 篇文章 0 订阅
1 篇文章 0 订阅

一. 注解

注解(Annotation):不是程序本身,可以对程序做出解释,也可以被其他程序读取。

注解格式:@注释名,也可以添加一些参数,如:@SuppressWarnings(value="unchecked")

1. 内置注解

常用的三种内置注解:
① @Override : 重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。只能修饰方法。
② @Deprecated : 标记过时方法。所标注内容,不再被建议使用。可以修饰方法、属性、类。
③ @SuppressWarnings : 让编译器对"它所标注的内容"的某些警告保持静默。可以修饰类、方法。

2. 元注解

元注解的作用就是负责注解其他注解,即修饰其他的注解。
常用的四种元注解:
① @Retention : 标识这个注解怎么保存,是只在代码中(SOURSE),还是编入class文件中(CLASS),
或者是在运行时可以通过反射访问(RUNTIME)。如@Retention(RetentionPolicy.SOURCE)。
	SOURSE<CLASS<RUNTIME
② @Documented : 标记这些注解将被包含在用户文档(JavaDoc)中。
③ @Inherited : 标记子类可以继承父类的注解。
④ @Target : 标记这个注解的使用范围。如@Target(value=ElementType.METHOD)。
	TYPE 意味着,它能标注"类、接口(包括注释类型)或枚举声明"。
	FIELD 意味着,它能标注"字段声明"。
	METHOD 意味着,它能标注"方法"。
	PARAMETER 意味着,它能标注"参数"。
	CONSTRUCTOR 意味着,它能标注"构造方法"。
	LOCAL_VARIABLE 意味着,它能标注"局部变量"。

3. 自定义注解

使用@Interface自定义注解,自动继承了java.lang.annotation.Annotation接口。
其中的每一个方法实际上是声明了一个配置参数,方法的名称就是参数的名称,返回值类型就是参数的类型。
可以通过default来声明参数的默认值
如果只有一个参数成员,一般声明参数名为value
注解元素必须有值

自定义注解举例:

public class Demo1DefineAnnotation {
    @MyAnnotation1(name = "张三") // 每个参数如果没有默认值必须赋值
    public void test1() {}

    @MyAnnotation2("张三") // 当注解中只有一个参数且参数名为value,写的时候可以省略参数名
    public void test2(){}
}

@Inherited // 表示被这个注解修饰的类的子类可以继承父类的注解
@Documented // 表示注解可以被保存到JavaDoc中
@Target({ElementType.TYPE,ElementType.METHOD}) // 表示这个注解MyAnnotation1可以修饰类和方法
@Retention(RetentionPolicy.RUNTIME) // 表示这个注解MyAnnotation1可以保存到程序运行时
@interface MyAnnotation1{  // 自定义注解
    String name(); // 注解的参数,不是方法
    int age() default 0; // 设置age的默认值为0
}

@interface MyAnnotation2{  // 自定义注解
    String value(); // 当只有一个参数成员,一般声明参数名为value
}

二. 反射

Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

加载完类之后,在堆内存的方法区中就产生了一个不变的Class类型(类)的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构,这个对象就像一面镜子,通过这个镜子看到类的结构,所以我们形象的称之为反射。

Class对象是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象。

1. 获取Class对象的方式:

① 通过对象获得:Class c1 = person.getClass()
② 通过forName获得:Class c2 = Class.forName(“Annotation.Student”)
③ 通过类名.class获得:Class c3 = Student.class
④ 通过基本内置类型的包装类的Type属性:Class c4 = Integer.TYPE

// 获取Class对象的方式
public class TestReflection {
    public static void main(String[] args) throws ClassNotFoundException {
        Student student = new Student();

        // 方法一:通过对象获得
        Class c1 = student.getClass();
        System.out.println(c1+"\t"+c1.hashCode());

        //方拾二:forName获得
        Class c2 = Class.forName("Annotation.Student");
        System.out.println(c2+"\t"+c2.hashCode());

        //方式三:通过类名.class获得
        Class c3 = Student.class;
        System.out.println(c3+"\t"+c3.hashCode());

        //方式四:基本内置类型的包装类都有一个Type属性
        Class c4 = Integer.TYPE;
        System.out.println(c4);
    }
}

class Student {}

结果:

class Annotation.Student	460141958
class Annotation.Student	460141958
class Annotation.Student	460141958
int
同一个类只有一个Class对象

2. Class类的常用方法:

static Class forName(String name):返回指定类名name的Class对象
Object newInstance():返回一个Class对象的实例
String getName():返回此Class对象所表示的类的名称(包名+类名)
String getSimpleName():返回此Class对象所表示的类的名称(类名)
Class getSuperClass():返回当前Class对象的父类的Class对象
Class[] getInterfaces():获取被当前Class对象实现的接口
ClassLoader getClassLoader():获取该类的类加载器
Constructor[] getConstructors():获得本类的所有构造函数的对象数组
Method getMethod(String name, Class<?>... parameterTypes):返回指定名称和参数类型对象的public方法对象(包括继承父类的,但不包括构造方法)
Method[] getMethods():获得类中的所有public方法(包括继承父类的,但不包括构造方法)
Method getDeclaredMethod(String name, Class<?>... parameterTypes):返回本类指定名称和参数类型对象的方法对象(不包括继承的和构造方法)
Method[] getDeclaredMethods():获得本类的所有方法(不包括继承父类的和构造方法)
Field getField(String name):返回这个类中指定的属性(包括继承的),这个属性必须是public
Field[] getFields():返回这个类中所有(包括继承的)声明的属性对象数组,这个字段必须是public
Field getDeclaredField(String name):返回这个类中指定的属性(不包括继承的)
Field[] getDeclaredFields():返回这个类中所有(不包括继承的)声明的属性的对象数组
// Class对象的常用方法
public class Demo4Reflection {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class c1 = Class.forName("Annotation.Teacher"); //获取Class对象

        // 获得类的名字
        System.out.println(c1.getName()); // Annotation.Teacher
        System.out.println(c1.getSimpleName()); // Teacher
        System.out.println("================================");

        // 获得类的属性
        System.out.println(c1.getField("name")); // 获得指定的public属性
        System.out.println("---------------------------------");
        Field[] fields = c1.getFields(); // 获得所有的包括继承的public属性
        for (Field field:fields) {
            System.out.println(field+"\t");
        }
        System.out.println("---------------------------------");
        System.out.println(c1.getDeclaredField("age")); // 获得指定的声明的属性
        System.out.println("---------------------------------");
        fields = c1.getDeclaredFields(); // 获得所有的不包括继承的属性
        for (Field field:fields) {
            System.out.println(field);
        }
        System.out.println("================================");

        // 获得类的方法
        System.out.println(c1.getMethod("setName",String.class)); // 获得指定名称和参数类型对象的方法,包括继承的
        System.out.println("---------------------------------");
        Method[] methods = c1.getDeclaredMethods(); // 获得所有的不包括继承的方法
        for (Method method:methods) {
            System.out.println(method);
        }
        System.out.println("================================");

        //获得类的构造器
        Constructor[] constructors = c1.getConstructors(); // 获得本类所有的构造器
        for (Constructor constructor:constructors) {
            System.out.println(constructor);
        }
        System.out.println("---------------------------------");
        constructors = c1.getDeclaredConstructors(); // 获得本类所有的构造器
        for (Constructor constructor:constructors) {
            System.out.println(constructor);
        }
        System.out.println("---------------------------------");
        // 获得指定的构造函数
        System.out.println(c1.getConstructor(String.class,int.class,int.class,String.class));
    }
}
class Person{
    public String name;
    private int id;

    public Person() {
    }
    public Person(String name, int id) {
        this.name = name;
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
}
class Teacher extends Person {
    private int age;
    String sex;

    public Teacher() {
    }
    public Teacher(String name, int id, int age, String sex) {
        super(name, id);
        this.age = age;
        this.sex = sex;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
}

结果:

Annotation.Teacher
Teacher
================================
public java.lang.String Annotation.Person.name
---------------------------------
public java.lang.String Annotation.Person.name	
---------------------------------
private int Annotation.Teacher.age
---------------------------------
private int Annotation.Teacher.age
java.lang.String Annotation.Teacher.sex
================================
public void Annotation.Person.setName(java.lang.String)
---------------------------------
public int Annotation.Teacher.getAge()
public java.lang.String Annotation.Teacher.getSex()
public void Annotation.Teacher.setSex(java.lang.String)
public void Annotation.Teacher.setAge(int)
================================
public Annotation.Teacher()
public Annotation.Teacher(java.lang.String,int,int,java.lang.String)
---------------------------------
public Annotation.Teacher()
public Annotation.Teacher(java.lang.String,int,int,java.lang.String)
---------------------------------
public Annotation.Teacher(java.lang.String,int,int,java.lang.String)

3. 哪些类型有Class对象

class类、interface接口、[]数组、enum枚举、annotation注解、基本数据类型、void 等
public class TestReflection {
    public static void main(String[] args) {
        Class c1 = Object.class; // 类
        Class c2 = Comparable.class; // 接口
        Class c3 = String[].class; // 一维数组
        Class c4 = int[][].class; // 二维数组
        Class c5 = Override.class; // 注解
        Class c6 = ElementType.class; // 枚举
        Class c7 = Integer.class; // 基本数据类型
        Class c8 = void.class; // void
        Class c9 = Class.class; // Class

        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c4);
        System.out.println(c5);
        System.out.println(c6);
        System.out.println(c7);
        System.out.println(c8);
        System.out.println(c9);

        int[] a = new int[10];
        int[] b = new int[5];
        // 只要元素类型和维度一致,就是同一个Class
        System.out.println(a.getClass().hashCode()+" "+b.getClass().hashCode());
    }
}

结果:

class java.lang.Object
interface java.lang.Comparable
class [Ljava.lang.String;
class [[I
interface java.lang.Override
class java.lang.annotation.ElementType
class java.lang.Integer
void
class java.lang.Class
460141958 460141958

4. 类的加载过程

编译:代码首先被编译成二进制字节码文件(.class文件)
加载:将class文件字节码内容加载到内存中,并将静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的Class对象
链接:将Java类的二进制代码合并到JVM的运行状态之中的过程

  • 验证:确保加载的类信息符合JVM规范,没有安全方面的问题
  • 准备:为类变量(static)分配内存并设置类变量默认初始值
  • 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)

初始化:执行类构造器方法的过程,类构造器方法是由编译期自动收集类中的所有类变量(static)的赋值动作和静态代码块的语句合并产生的(类构造器是构造类信息的,不是构造该类对象的构造方法)。初始化一个类的时候,当发现其父类还没有初始化,需要先初始化其父类。

5. 类加载器

类加载器的作用是把类装载到内存中,即将class文件字节码内容加载到内存中,并将静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的Class对象

类加载器有三种:

  • 引导(根)类加载器(Bootstap Classloader):是JVM自带的类加载器,负责Java平台核心库,用来加载核心类库,无法直接获取
  • 扩展类加载器(Extension Classloader):
  • 系统类加载器(System Classloader):是最常用的加载器
    Bootstap Classloader > Extension Classloader > System Classloader
// 类加载器
public class ClassLoader {
    public static void main(String[] args) throws ClassNotFoundException {
        // 获取系统类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);

        // 获取系统类加载器的父类加载器-->拓展类加载器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);

        // 获取拓展类加载器的父类加载器-->根加载器
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1); // 无法直接获得,输出null

        // 测试当前类是哪个类加载器加载的
        ClassLoader classLoader = Class.forName("Annotation.ClassLoader").getClassLoader();
        System.out.println(classLoader);

        // 测试JDK内置的类是哪个加载器加载的
        classLoader = Class.forName("java.lang.Object").getClassLoader(); // 根加载器
        System.out.println(classLoader);
    }
}

结果:

sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@1b6d3586
null
sun.misc.Launcher$AppClassLoader@18b4aac2
null
	双亲委派机制:
当一个类要被加载时,首先会在System ClassLoader中检查是否加载过,如果有那就无需再加载了。
如果没有,那么会拿到父加载器,父类中同理也会先检查自己是否已经加载过,如果没有再往上。
注意这个类似递归的过程,直到到达Bootstrap classLoader之前,都是在检查是否加载过,并不会选择自己去加载。
直到BootstrapClassLoader,已经没有父加载器了,这时候开始考虑自己是否能加载了。
如果自己无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException。

一般自定义的类都由System ClassLoader加载

6. 有Class对象能做什么

① 通过反射动态的创建对象和调用方法与属性
// 通过反射动态的创建对象
// 通过反射调用方法和属性
public class Demo7 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        Class c1 = Class.forName("Annotation.User"); // 获得Class对象

        // 通过反射创建一个实例对象
        User user = (User) c1.newInstance(); // 相当于调用无参构造器
        System.out.println(user.toString());

        // 通过构造器创建实例对象
        Constructor constructor = c1.getDeclaredConstructor(int.class,String.class,int.class);
        user = (User) constructor.newInstance(10101,"张三",18);
        System.out.println(user.toString());

        // 通过反射调用方法
        Method setName = c1.getDeclaredMethod("setName", String.class); // 获得本类的指定方法setName
        setName.invoke(user,"李四"); // invoke:激活
        System.out.println(user);

        // 通过反射操作属性
        Field name = c1.getDeclaredField("name");
        // 不能直接操作private属性,需要关闭程序的安全检测
        name.setAccessible(true); // 关闭安全检测
        name.set(user,"王五");  // 相当于 user.name = "王五"
        System.out.println(user.getName());
    }
}
class User{
    private int id;
    private String name;
    private int age;

    public User() {
    }
    public User(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "User{id="+this.id+",name="+this.name+",age="+this.age+"}";
    }
}

结果:

User{id=0,name=null,age=0}
User{id=10101,name=张三,age=18}
User{id=10101,name=李四,age=18}
王五
Method、Field、Constructor对象都有setAccessible()方法,作用是启动和禁用访问安全检查的开关
关闭安全检查setAccessible(true)能提高反射执行的效率
② 获取注解信息
// 通过反射获取注解信息
public class Demo10 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c1 = Class.forName("Annotation.User2");

        //通过反射获得注解
        Annotation[] annotations = c1.getAnnotations(); // 获得所有的注解
        for (Annotation annotation:annotations) {
            System.out.println(annotation);
        }

        //获得注解的参数值
        ClassAnnotation classAnnotation = (ClassAnnotation) c1.getAnnotation(ClassAnnotation.class); // 获得指定的注解
        String value = classAnnotation.value();
        System.out.println(value);

        //获得指定属性的注解
        Field id = c1.getDeclaredField("id"); // 获得指定的属性
        FieldAnnotation fieldAnnotation = id.getAnnotation(FieldAnnotation.class);
        System.out.println(fieldAnnotation);
        System.out.println(fieldAnnotation.name());
        System.out.println(fieldAnnotation.type());
        System.out.println(fieldAnnotation.Length());
    }
}

@ClassAnnotation("User2")
class User2{
    @FieldAnnotation(name = "id",type = "int",Length = 10)
    private int id;
    @FieldAnnotation(name = "name",type = "String",Length = 5)
    private String name;
    @FieldAnnotation(name = "age",type = "int",Length = 10)
    private int age;

    public User2() {
    }
    public User2(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "User2{id="+this.id+",name="+this.name+",age="+this.age+"}";
    }
}

// 类的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface ClassAnnotation{ // 自定义注解
    String value(); // 参数
}

// 属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldAnnotation{
    String name(); // 参数:属性名字
    String type(); // 参数:属性类型
    int Length(); // 参数:属性长度
}

结果:

@Annotation.ClassAnnotation(value=User2)
User2
@Annotation.FieldAnnotation(name=id, type=int, Length=10)
id
int
10
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值