注解,反射,类加载内存方式,反射操作注解

注解,反射,类加载内存方式,反射操作注解

注解的简介

什么是注解Annotation

  • 不是程序本身,可以对程序做出解释
  • 可以被其他程序读取

Annotation的格式

@注释名

例:@Override

Annotation在哪里使用

可以附加在package,class,method,field上面,相当于给他们添加了额外的辅助信息

我们可以通过反射机制编程实现对这些元数据的访问

内置注解

  1. @Override:表示一个方法声明打算重写超类中的另一个方法声明

  2. @Deprecated:表示不鼓励程序员使用这个的元素,通常是因为它很危险或者存在更好的选择

  3. @SupperessWarnings:用来抑制编译时的警告信息,必须添加一个参数才能正常使用

    @SupperessWarnings(“all”)

元注解

元注解负责注解其他注解,被用来提供对其他annotation类型作说明

类型:meta-annotation

  • @Target:用于描述注解的适用范围:注解可以用在什么地方
  • @Retention:需要在什么级别保存该注释信息,描述注解的生命周期(SOUCRE<CLASS<RUNTIME),一般使用RUNTIME
  • @Document:说明该注解将被包含在javadoc中
  • @Inherited:说明子类可以继承父类的注解

反射

动态语言:运行时可以改变其结构的语言,运行时代码可以根据某些条件改变自身结构

静态语言:运行时结构不可变

定义

反射Reflection,反射机制运行程序在执行期借助于ReflectionAPI取得任何类的内部信息,并能直接操作任意对象的内部属性及方法.

Class c =Class.forName("java.lang.String");

加载完类后,在堆内存的方法去产生一个Class类型的对象,这个对象包含了完整的类的结构信息.

一个类在内存中只有一个Class对象

一个类呗加载后,类的整个结构会被封装在Class对象中

获得对象的方式

正常方式:引入包->通过new实例化->取得实例化对象

反射方式:实例化对象->getClass方法->得到完整的包类名称

public static void main(String[] args) {
    try {
        Class user = Class.forName("User");
        System.out.println(user);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

}

源码分析

public static Class<?> forName(String className)
            throws ClassNotFoundException {
    Class<?> caller = Reflection.getCallerClass();
    return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
 private static native Class<?> forName0(String name, boolean initialize,
                                            ClassLoader loader,
                                            Class<?> caller)
        throws ClassNotFoundException;

在object类中,定义了getclass方法,此方法会被所有子类继承

public final native Class<?> getClass();

Class类

通过反射可以得到某个类的属性,方法和构造器.某个类到底实现了那些借口.

对于每个类而言,JRE为其保留一个不变的Class类型对象.

  1. Class本身也是一个类
  2. Class对象只能由系统建立对象
  3. 一个加载的类在JVM中只有一个Class实例
  4. 一个Class对象对应的是一个加载到JVM的一个.class文件
  5. 每个类的实例都会记得自己是由哪个Class实例所生成
  6. 通过Class可以完整地得到一个类中的所有被加载的结构

常用方法

public static Class<?> forName(String className)		//获取指定类名的Class对象
public T newInstance()					Object newInstance()  //调用缺省构造函数,返回Class对象的一个实例
public String getName() {				//返回Class所代表的的实体的名称
public native Class<? super T> getSuperclass();			//返回当前Class对象的父类的Class对象
public Class<?>[] getInterfaces() {						//获取当前Class对象的接口,接口可能不止一个
public ClassLoader getClassLoader() {					//返回该类的类加载器
public Constructor<?>[] getConstructors() throws SecurityException {			//返回一个包含某些Constructor 对象的数组
public Method getMethod(String name, Class<?>... parameterTypes)			//返回一个Method对象,对象的形参类型为paramType
public Field[] getDeclaredFields() throws SecurityException {				//返回Field对象的一个数组

源码分析

public static Class<?> forName(String className)		//获取指定类名的Class对象
            throws ClassNotFoundException {
    Class<?> caller = Reflection.getCallerClass();						
    return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
 public T newInstance()					Object newInstance()  //调用缺省构造函数,返回Class对象的一个实例
 public String getName() {				//返回Class所代表的的实体的名称
        String name = this.name;
        if (name == null)
            this.name = name = getName0();
        return name;
    }    
public native Class<? super T> getSuperclass();			//返回当前Class对象的父类的Class对象
public Class<?>[] getInterfaces() {						//获取当前Class对象的接口,接口可能不止一个
        ReflectionData<T> rd = reflectionData();
        if (rd == null) {
            // no cloning required
            return getInterfaces0();
        } else {
            Class<?>[] interfaces = rd.interfaces;
            if (interfaces == null) {
                interfaces = getInterfaces0();
                rd.interfaces = interfaces;
            }
            // defensively copy before handing over to user code
            return interfaces.clone();
        }
    }
 public ClassLoader getClassLoader() {					//返回该类的类加载器
        ClassLoader cl = getClassLoader0();
        if (cl == null)
            return null;
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            ClassLoader.checkClassLoaderPermission(cl, Reflection.getCallerClass());
        }
        return cl;
    }
public Constructor<?>[] getConstructors() throws SecurityException {			//返回一个包含某些Constructor 对象的数组
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return copyConstructors(privateGetDeclaredConstructors(true));
    }
public Method getMethod(String name, Class<?>... parameterTypes)			//返回一个Method对象,对象的形参类型为paramType
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        Method method = getMethod0(name, parameterTypes, true);
        if (method == null) {
            throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
        }
        return method;
    }
public Field[] getDeclaredFields() throws SecurityException {				//返回Field对象的一个数组
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        return copyFields(privateGetDeclaredFields(false));
    }

获取Class对象的实例

  1. 已知具体的类,通过类的Class属性获取 Class person = Person.class;
  2. 已知某个类的实例,调用该实例的getClass()方法获取Class对象 Person person = new Person(); Class personclass = person.getClass();
  3. 已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取
  4. 内置基本数据类型可以直接用类名.Type

所有类型的Class对象

类,接口,注解,void,数组

类加载内存方式

  • 堆:存放new的对象和数组,可以被所有的线程共享,不会存放别人对象引用
  • 栈:存放基本变量类型,包含这个基本类型的具体数值和引用对象的变量(存放这个引用在对立面的具体地址)
  • 方法区:可以被所有的线程共享,包含了所有的class和static变量

类的加载过程

当程序主动使用某个类时,如果该类未被加载到内存中,系统会通过三个步骤对该类进行初始化

  1. 类的加载:将类的class文件读入内存,并创建一个java.lang.Class对象,由类加载器完成

    将class文件字节码加载到内存中

  2. 类的链接:将类的二进制数据合并到JRE中

    验证:确保加载的类符合JVM规范,没有安全方面的问题

    准备:为类变量static分配内存并设置类变量默认初始值的阶段,这些内存将在方法区中进行分配

    解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程

  3. 类的初始化:JVM负责对类进行初始化

    • 执行类构造器方法的过程
    • 初始化一个类时,若父类未初始化,则先出法对父类的初始化
    • 虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步

类的初始化

类的主动引用(一定会发生类的初始化)

  • 虚拟机启动,先初始化main方法所在的类
  • new一个类的对象
  • 调用类的静态成员(除了final常量)和静态方法
  • 使用java.lang.reflect包方法对类进行反射调用
  • 初始化一个类,如果其父类没有被初始化,则先会初始化它的父类

类的被动引用(不会发生类的初始化)

  • 访问一个静态域时,只有真正声明这个域的类才会被初始化.通过子类引用父类的静态变量,不会导致子类的初始化
  • 通过数组定义类引用,不会触发此类的初始化
  • 引用常量不会触发此类的初始化

类加载器的作用

将class文件字节码内容加载到内存中,并将这些静态数据转换成方法去的运行时数据结构,然后在堆重生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口

类缓存:标准的JavaSer类加载器可以按要求寻找类,一旦某个类被加载到类加载器中,它将维持加载缓存一段时间,jvm垃圾回收机制可以回收这些Class对象

分类
  • 引导类加载器,用c++编写,JVM自带的类加载器,负责Java平台核心库,用来装载核心类库,该加载器无法直接获取
  • 扩展类加载器:负责jre/lib/ext目录下的jar包 或-D java.ext.dirs指定目录下的jar包装入工作库
  • 系统类加载器:负责java -classpath或 -D java.class.path所指的目录下的类与jar包装入工作
public class Classloader {												//获取一个系统类的加载器
    public static void main(String[] args) {
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);
        System.out.println(systemClassLoader.getParent());					//获取父类  扩展类加载器
        System.out.println(systemClassLoader.getParent().getParent());			//获取根加载器
    }
}
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@372f7a8d
null													//根加载器为C++ 无法读取
 
    //获取当前类是哪个加载器加载的
  try {
            System.out.println(Class.forName("Classloader").getClassLoader());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }   
sun.misc.Launcher$AppClassLoader@18b4aac2    //由系统类加载器加载
 	//获得系统类的可以加载的路径
System.out.println(System.getProperty("java.class.path"));

创建运行时类的对象

获取运行时类的完整结构

//获得类的名字
System.out.println(classloader.getName());  //获得包名+类名
System.out.println(classloader.getSimpleName()); //获得类名
//获得类的属性
        Field[] fields = classloader.getFields();  //只能获取public属性
		fields   = classloader.getDeclaredFields(); //获取全部的属性  包括 private
        for (Field field: fields) 
        {
            System.out.println(field);    
        }
Method[] methods = classloader.getMethods();        //获得本类和父类的全部public方法
Method[] declaredMethods = classloader.getDeclaredMethods();//获得本类的所有方法
//获得指定方法
        Method getName = classloader.getMethod("getName", null);
        Method setName = classloader.getMethod("setName", String.class);
        System.out.println(getName);
        System.out.println(setName);

动态创建对象执行方法

创建类的对象,使用Class对象的newInstance方法

  1. 类必须有一个无参数的构造器

  2. 类的构造器访问权限需要足够

    //获得Class对象
     Class<?> classloader = Class.forName("Classloader");
     /*
         构造对象
         本质调用无参构造器
      */
    Classloader o = (Classloader) classloader.newInstance();     
     
    

若没有无参构造器,则操作的时候需要明确的调用类中的构造器,并将参数传递进去之后,才可以实例化操作

  1. 通过Class类的getDeclaredConstructor取得本类的指定形参类型的构造器
  2. 向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数
  3. 通过Constructor实例化对象
/* 通过构造器创造对象 */
Constructor<?> declaredConstructor = classloader.getDeclaredConstructor(int.class, String.class);
Classloader o1 = (Classloader) declaredConstructor.newInstance(5, "100");
//通过反射调用普通方法
//invoke 激活了 传递一个对象
Method setName = classloader.getDeclaredMethod("setName", String.class);
setName.invoke(o,"Lyc");
System.out.println(o.getName());
//通过反射操作属性
Classloader o2 = (Classloader) classloader.newInstance();
Field name = classloader.getDeclaredField("name");
name.setAccessible(true);
name.set(o2,"lyc2");

获取注解信息,反射操作注解

public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
    Class aClass = Class.forName("Student");

    //通过反射获得注解

    for (Annotation annotation : aClass.getAnnotations()) {
        System.out.println(annotation);
    }
    //获得注解value的值
    Table annotation = (Table) aClass.getAnnotation(Student.class);
    System.out.println(annotation.value());

    //获得类指定的注解
    Field name = aClass.getDeclaredField("name");
    Field1 annotation1 = name.getAnnotation(Field1.class);
    System.out.println(annotation1.columnName());
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
    String value();
}

//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Field1{
    String columnName();
    String type();
    int     length();
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
    String value();
}

//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Field1{
    String columnName();
    String type();
    int     length();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值