栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

注解与反射+部分内存分析和类的加载

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

注解与反射+部分内存分析和类的加载

注解和反射
注解
注解入门
  • Annotation
  • 不是程序本身,但可以对程序做出解释 (这一点和注释 (comment) 没区别)
  • 可以被其他程序读取
  • 格式:@注解名,可以在注解名后加括号来添加一些参数值
内置注解 @Override

定义在 java.lang.Override 中,此注解只适用于修辞方法,表示一个方法声明打算重写超类中的另一个方法声明

@Deprecated

定义在 java.lang.Deprecated 中,此注解可以用于修饰方法,属性,类,表示不鼓励程序员使用这样的元素,通常是因为它很危险或者

存在更好的选择

@SuppressWarnings

定义在 java.lang.SuppressWarnings 中,用来抑制编译时的警告信息,需要添加一个参数才能正确使用,这些参数都是已经定义好的,

需要选择性使用

@SuppressWarnings(“all”) 抑制所有警告信息

自定义注解,元注解 自定义注解
  • 使用 @interface 自定义注解时,自动继承 java.lang.annotation.Annotation 接口
  • 格式:public @interface 注解名 {定义内容}
  • 其中的每一个方法实际上是声明了一个配置参数
    • 参数格式:参数类型 + 参数名 ();
  • 方法的名称就是参数的名称
  • 返回值类型就是参数的类型 (返回值只能是基本类型,Class,String,enum)
  • 可以通过 default 来声明参数的默认值
  • 如果只有一个参数成员,一般参数名为 value,这样赋值时可以直接赋值,不用写参数名
  • 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0 作为默认值
package ZJ;

import java.lang.annotation.*;

public class Text01 {
    @MyAnnotation(name="123456",a=2)       //注解可以赋值,在注解没有声明默认值时,必须赋值,多个注解赋值时可以不按顺序
    public void a(){
    }
}
@Target(value = {ElementType.METHOD,ElementType.TYPE})       //表示我们定义的注解可以用在哪些地方
@Retention(RetentionPolicy.RUNTIME)       //表示我们定义的注解在哪些地方还有用
//自定义的注解
@interface MyAnnotation{
    String name() default "";       //给参数声明默认值
    int a() default -1;       //如果默认值为-1,代表不存在
}
元注解
  • 负责注解其他注解

  • 定义了 4 个标准的 meta-annotation 类型,他们被用来提供对其他 annotation 类型作说明,都在 java.lang.annotation 包中可以找

    • @Target:用于描述注解的使用范围 (即:被描述的注解用在什么地方)
    • @Retention:表示需要在什么级别保存该注解信息,用于描述注解的生命周期
      • (SOURCE < CLASS < RUNTIME)
    • @document:说明该注解将被包含在 javadoc 中
    • @Inherited:说明子类可以继承父类中的该注解
package ZJ;

import java.lang.annotation.*;

public class Text01 {
}
@Target(value = {ElementType.METHOD,ElementType.TYPE})       //@Target表示我们定义的注解可以用在哪些地方
@Retention(value = RetentionPolicy.RUNTIME)       //@Retention表示我们定义的注解在哪些地方还有用
@documented       //@documented表示将我们的注解生成在JAVAdoc中
@Inherited       //@Inherited表示子类可以继承父类的注解
//自定义的注解
@interface MyAnnotation{   
}
反射机制
  • Reflection
  • 让 java 拥有动态性
静态语言与动态语言 静态语言
  • 运行时结构不可变的语言
  • Java,C,C++
  • Java 不是动态语言,但是可以称为 “准动态语言”,因为 Java 有反射机制
动态语言
  • 是一类在运行时可以改变其结构的语言
  • 在运行时代码可以根据某些条件改变自身结构
  • 主要动态语言:Object-C,C#,Javascript,PHP,Python等
Reflection (反射)
  • Reflection (反射) 是 Java 被视为动态语言的关键,反射机制允许程序在执行期借助与 Reflection API 取得任何类的内部信息,并能直

    接操作任意对象的内部属性及方法

  • Class c=Class.forName(“java.lang.String”)

  • 加载完毕后,在堆内存的方法区中就产生了一个 Class 类型的对象 (一个类只有一个 Class 对象),这个对象就包含了完整类的结构信

提供的主要功能
  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的的成员变量和方法
  • 在运行时获取泛型信息
  • 在运行时调用任意一个对象的成员变量
  • 在运行时处理注解
  • 生成动态代理
优点
  • 可以实现动态创建对象和编译,体现出很大的灵活性
缺点
  • 对性能有影响。使用反射基本上是一种解释操作,我们可以告诉 JYM ,我们希望做什么并且它满足我们的要求。这类操作总是慢于直

    接执行相同的操作

实体类
  • 只有一些属性及构造方法的类
反射相关的主要 API Class 类
  • java.lang.Class:代表一个类,被所有子类继承
  • Class 本身也是一个类
  • Class 对象只能由系统建立
  • 一个加载的类在 JVM 中只会有一个 Class 实例
  • 一个 Class 对象对应的是一个加载到 JVM 中的一个 .class 文件
  • 每个类的实例都会记得自己是由哪个 Class 实例所生成
  • 通过 Class 可以完整地得到一个类中的所有被加载的结构
  • Class 类是 Reflection 的根源,针对任何你想动态加载,运行的类,唯有先获得相应的 Class 对象
  • 只要元素类型和维度一样,就是同一个 Class
public final Class getClass()
常用方法
  • 返回指定类名 name 的 Class 对象
static ClassforName(String name)
  • 通过反射创建一个对象
Object newInstance()
  • 返回实体 (类,接口,数组类或 void) 的名字
getName()
  • 返回当前 Class 对象的父类的 Class 对象
Class getSuperClass()
  • 获取当前 Class 对象的接口
Class[] getinterfaces()
  • 返回该类的类加载器
ClassLoader getClassLoader()
  • 返回一个包含某些 Constructor 对象的数组
Constructor[] getConstructor()
  • 返回一个 Method 对象,此对象的形参类型为 paramType
Method getMothed(String name,Class.. T)
  • 返回 Field 对象的一个数组
Field[] getDeclaredFields()
获取 Class 类的实例
  1. 若已知具体的类,通过类的 class 属性获取,该方法最为安全可靠,程序性能最高
Class clazz=Person.class;
  1. 已知某个类的实例,调用该实例的 getClass() 方法获取 Class 对象
Class clazz=person.getClass();
  1. 已知一个类的全类名,且该类在类路径下,可通过 Class 类的静态方法 forName() 获取,可能抛出 ClassNotFoundException
Class clazz=Class.forName("demo01.Student");
  1. 内置基本数据类型可以直接用类名.TYPE 获取其 Class 对象
  2. 还可以利用 ClassLoader
哪些类可以有 Class 对象
  • class:外部类,成员 (成员内部类,静态内部类),局部内部类,匿名内部类
Class c1=Obejct.class;
  • interface:接口
Class c2=Comparable.class;
  • []:数组
Class c3=String[].class;
Class c4=int[][].class;
  • enum:枚举
Class c5=ElementType.class;
  • annotation:注解 @interface
Class c6=Override.class;
  • primitive type:基本数据类型
Class c7=Integer.class;
  • void
Class c8=void.class;
java 内存分析 基础构成 堆
  • 存放 new 的对象和数组
  • 可以被所有的线程共享,不会存放别的对象引用
  • 存放基本变量类型 (会包含这个基本类型的具体数值)
  • 引用对象的变量 (会存放这个引用在堆里面的具体地址)
方法区
  • 一个特殊的堆

  • 可以被所有的线程共享

  • 包含了所有的 class 和 static 变量

类的加载过程
package ZJ;

public class TextF1 {
    public static void main(String[] args){
        Child c=new Child();
        System.out.println(Child.m);
    }
}
class Child{
    static {
        m=100;
    }
    static int m=10;
    public Child(){
    }
}
类的加载 (Load)

将类的 class 文件读入内存,并为之创建一个 java.lang.Class 对象。此过程由类加载器完成

  • 将类中的信息 (静态变量,静态方法,常量池,代码等) 放入方法区
  • 在堆中生成类相对应的 java.lang.Class 对象 (包含类的所有东西)
类的连接 (link)

将类的二进制数据合并到 JRE 中

  • 在栈中设置常量,类的静态变量的默认初始值
类的初始化 (Initialize)

JVM 负责对类进行初始化

  • 在堆中 new 一个类的对象,通过堆中类的 java.lang.Class 对象获得类的所有数据
  • 执行 () 方法,将类中所有类变量的赋值动作和静态代码块中的语句合并
什么时候会发生类的初始化
  • 类的主动引用 (一定会发生类的初始化)
    • 当虚拟机启动,先初始化 main 方法所在的类
    • new 一个类的对象
    • 调用类的静态成员 (除了 final 常量) 和静态方法
    • 使用 java.lang.reflect 包的方法对类进行反射调用
    • 当初始化一个类时,如果其父类没有被初始化,则先会初始化它的父类
package ZJ;

public class TextF1 {
    static {
        System.out.println("main类被加载");
    }
    public static void main(String[] args) throws ClassNotFoundException {
        Child c=new Child();       //主动引用
        Class.forName("ZJ.Child");       //反射也会产生主动引用
    }
}
class Person{
    static {
        System.out.println("父类被加载");
    }
}
class Child extends Person{
    static {
        System.out.println("子类被加载");
    }
}
  • 类的被动引用 (不会发生类的初始化)

    • 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量时,不会导致子类初始化
    package ZJ;
    
    public class TextF1 {
        static {
            System.out.println("main类被加载");
        }
        public static void main(String[] args) throws ClassNotFoundException {
            System.out.println(Child.m);       //通过子类引用父类的静态变量时,子类不会被加载
        }
    }
    class Person{
        static int m=2;
    }
    class Child extends Person{
    }
    
    • 通过数组定义类引用,不会触发此类的初始化
    package ZJ;
    
    public class TextF1 {
        static {
            System.out.println("main类被加载");
        }
        public static void main(String[] args) throws ClassNotFoundException {
            Child[] c=new Child[5];       //给数组分配空间和命名,类不会被加载
        }
    }
    class Person{
    }
    class Child extends Person{
    }
    
    • 引用常量也不会触发此类的初始化 (常量在链接阶段就存入调用类的常量池中)
    package ZJ;
    
    public class TextF1 {
        static {
            System.out.println("main类被加载");
        }
        public static void main(String[] args) throws ClassNotFoundException {
            System.out.println(Child.M);       //引用常量类不会被加载
        }
    }
    class Person{
        static int m=2;
    }
    class Child extends Person{
        static final int M=10;
    }
    
类加载器
  • 类加载的作用:将 class 文件字节码内容加载到内存中。

  • JVM 规范定义了如下类型的类的加载器

    • 引导类加载器 (Bootstap Classloader)

      用 C++编写的,是 JVM 自带的类加载器,负责 java 平台核心库 (rt.jar),用来装载核心类库。该加载器无法直接获取

    • 扩展类加载器 (Extension Classloader)

      负责 jre/lib/ext 目录下的 jar 包或 -D java.ext.dirs 指定目录下的 jar 包装入工作库

    • 系统类加载器 (System Classloader)

      负责 java-classpath 或 -D java.class.path 所指的目录下的类与 jar 包装入工作,最常用的加载器

  • 双亲委派机制

    当自己定义的类开始加载时,它先会依次在系统类加载器,扩展类加载器,引导类加载器中寻找,如果其中有与该类同名的包,则用

    系统的包替换自己定义的包

    创建运行时类的对象
获取
  • 通过反射获取运行时类的完整结构

    Field,Method,Constructor,Superclass,Inetrface,Annotation

package ZJ;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Text02 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class c=Class.forName("ZJ.sun");
        //获得名称
        System.out.println(c.getName());       //获得包名和类名
        System.out.println(c.getSimpleName());       //获得类名
        //获得属性
        Field[] f = c.getFields();       //只能获得public属性
        Field[] f1=c.getDeclaredFields();       //可以获得所有属性
        //获得指定属性
        Field f2 = c.getField("name");       //只能获得指定的public属性
        Field f3 =c.getDeclaredField("name");       //可以获得指定的所有属性
        //获得类的方法
        Method[] m = c.getMethods();       //获得本类及其父类的全部public方法
        Method[] m1 = c.getDeclaredMethods();       //获得本类的所有方法
        //获得指定的方法
        //因为有重载,所以在获得方法时需要添加参数,才能准确地获得想要的方法
        Method getName = c.getMethod("getName", null);
        Method setName = c.getMethod("setName", String.class);
        //获得构造器
        Constructor[] cc = c.getConstructors();       //只能获public构造器
        Constructor[] cc1 = c.getDeclaredConstructors();       //可以获得所有构造器
        //获得指定的构造器
        Constructor cc2 = c.getDeclaredConstructor(String.class,int.class,int.class);
    }
}
创建类的对象
  • 调用 Class 对象的 newInstance() 方法
    • 类必须有一个无参构造
    • 类的构造器的访问权限必须足够
package ZJ;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Text03 {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        Class c = Class.forName("ZJ.Sun");       //获取
        Sun sun = (Sun)c.newInstance();       //本质上是调用类的无参构造器
        System.out.println(sun);
        //通过构造器创建对象
        Constructor dc = c.getDeclaredConstructor(String.class, int.class, int.class);
        Sun s = (Sun)dc.newInstance("小明",2018,18);
        System.out.println(s);
        //通过反射调用普通方法
        Sun sun1 = (Sun)c.newInstance();
        Method setName = c.getDeclaredMethod("setName", String.class);       //通过反射获取方法
        setName.invoke(sun1,"小白");       //invoke激活,(对象,"方法值")
        System.out.println(sun1.getName());
        Sun sun2 = (Sun)c.newInstance();
        Field field = c.getDeclaredField("name");       //通过反射获取方法
        //不能直接操作私有属性,需要开启访问权限
        field.setAccessible(true);
        field.set(sun2,"145");
        System.out.println(sun2.getName());

    }
}
setAccessible
  • Method 和 Field,Constructor 对象都有 setAccessible() 方法
  • 作用,打开或关闭访问检查的权限
  • 当反射调用很多时,可以打开访问权限,提高反射的效率
  • 默认值为 false,关闭访问检查权限
反射操作注解
package ZJ;

import java.lang.annotation.*;
import java.lang.reflect.Field;

public class Text04 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c = Class.forName("ZJ.Child1");
        //获得注解
        Annotation[] annotations = c.getAnnotations();
        for (Annotation annotation:annotations) {
            System.out.println(annotation);
        }
        //获得注解的value的值
        MyZJ myZJ = (MyZJ) c.getAnnotation(MyZJ.class);
        String value = myZJ.value();
        System.out.println(value);
        //获得类指定的注解
        Field f = c.getDeclaredField("name");
        MyZJ1 annotation = f.getAnnotation(MyZJ1.class);
        System.out.println(annotation.name());
        System.out.println(annotation.type());
        System.out.println(annotation.length());
    }

}
@MyZJ("db_child")
class Child1{
    @MyZJ1(name="db_id",type="int",length =10)
    private int id;
    @MyZJ1(name="db_name",type="varchar",length =10)
    private String name;
    @MyZJ1(name="db_age",type="int",length =10)
    private int age;

    public Child1() {
    }

    public Child1(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 "child{" +
                "id=" + id +
                ", name='" + name + ''' +
                ", age=" + age +
                '}';
    }
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface MyZJ{
    String value();
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyZJ1{
    String name();
    String type();
    int length();
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/490264.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号