【无标题】

本文深入解读Java注解的原理,包括内置注解如@Documented、@Target和@Retention,自定义注解的声明、作用范围及属性定义。同时探讨了反射机制,涉及如何获取反射对象、Class类的不同获取方式以及内存分析。
摘要由CSDN通过智能技术生成

一、注解

主要内容

1.1 注解的含义
1.2内置注解
1.3元注解
1.4自定义注解

1.1注解的含义

注解就是代码里的特殊标志,这些标志可以在编译,类加载,运行时被读取,并执行相应的处理,以便于其他工具补充信息或者进行部署。
注解可以被其他程序(比如:编译器等)读取,开发人员可以在不改变原有代码和逻辑的情况下在源代码中嵌入补充信息。

1.2内置注解

内置注解:Java已经提供的注解,叫做内置注解。

1.3 元注解

元注解:

注解名称作用
@Documented标着这个注解是否包含在用户文档中
@Target标记这个注解能够作用在哪些成员上面,例如:类上面、属性上面、方法上面等等
@Retention标记这个注解能够保存到哪个阶段,有三个可选值:源码阶段、字节码阶段、运行阶段
@Inherited标记某个注解是继承自哪个注解类(注解默认是不继承任何类的)
1.4自定义注解
1.4.1声明注解

Java中通过【@interface】来声明一个注解。
@interface就好比class、interface等关键字,用于声明类、接口,只不过@interface用于声明注解

1.4.2指定注解作用范围

自定义注解声明之后,我们还需要通过元注解对自定义的注解进行一些额外信息的定义。
通过【@Target】元注解,指定自定义注解能够作用的范围。
@Target注解,它需要一个属性值,它的可选值只能是【ElementType】枚举类中定义的值。
public enum ElementType {
TYPE, // 作用在类、接口、枚举上面

FIELD, // 作用在属性上面

METHOD, // 作用在方法上面

PARAMETER, // 作用在参数上面

CONSTRUCTOR, // 作用在构造方法上面

LOCAL_VARIABLE, // 作用在局部变量上面

ANNOTATION_TYPE, // 声明注解类型

PACKAGE, // 作用在包上面

TYPE_PARAMETER, // 1.8新增

TYPE_USE // 1.8新增

}

1.4.3 指定作用保存阶段

通过【@Retention】元注解,指定自定义注解的保存阶段。
@Retention注解的属性值,也是一个可选枚举类,里面包含三个可选值。
public enum RetentionPolicy {
SOURCE, // 源码阶段,编译阶段

CLASS, // 字节码阶段,这个是默认的选项值

RUNTIME // 运行阶段

}

1.4.4定义注解属性
语法格式

数据类型 注解属性名称() [default 默认值];
注意:定义注解属性的时候,必须加上小括号,这是规范,不加括号编译报错。定义注解的时候,可以通过default关键字,给注解属性设置一个默认值,这个是可选的,也可以不设置默认值。

@Target(value = { ElementType.TYPE, ElementType.METHOD })
@Retention(value = RetentionPolicy.RUNTIME)
public @interface CustomAnnotation {
    String value(); // 没有默认值的属性
    int age() default 20; // 具有默认值的属性
}
注解的使用
使用格式:

通过@注解名称(属性名称1=属性值1,属性名称2=属性值2…)即可
注意:如果注解中只有一个属性,并且属性名称是value,那么在使用的时候可以不用写属性名称,如果不是value名称,那必须写出具体的属性名称。
如果注解属性设置了默认值,那么在使用注解的时候,可以不写这个注解属性。如果注解属性是一个数组类型,则使用时候需要使用{}括起来。

@Target(value = { ElementType.TYPE, ElementType.METHOD })
@Retention(value = RetentionPolicy.RUNTIME)
@interface MyAnnotation01 {
    String value(); // 只有一个value属性
}
 
@Target(value = { ElementType.TYPE, ElementType.METHOD })
@Retention(value = RetentionPolicy.RUNTIME)
@interface MyAnnotation02 {
    String value();
    int age();
    String sex() default "man"; // 默认值
    String[] array(); // 数组类型
}
 
// 注解的使用
class AnnoTest {
    @MyAnnotation01(value = "不省略value属性名称")
    public void test01() {
    }
    @MyAnnotation01("省略value属性名称")
    public void test02() {
    }
 
    @MyAnnotation02(value = "不可以省略value属性名称", age = 20, array = {"Tom", "Jack"})
    public void test03() {
    }
    @MyAnnotation02(value = "不可以省略value属性名称", age = 30, sex = "woman", array = {"Tom", "Jack"})
    public void test04() {
    }
}

二、反射

主要内容:

2.1反射的含义
2.2获得反射对象
2.3获取class类的几种方式
2.4所有类型的class对象

2.1反射的含义

Java反射机制是指在运行状态中,动态获取信息以及动态调用对象方法的功能。
动态语言的示例
入eval函数执行x 的表达式里的代码
eval函数执行x 的表达式里的代码

2.2获得反射对象

获取对象的三种方式:
1 Object ——> getClass();
2 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
3 通过Class类的静态方法:forName(String className)(常用)

public class Fanshe {
	public static void main(String[] args) {
		//第一种方式获取Class对象  
		Student stu1 = new Student();//这一new 产生一个Student对象,一个Class对象。
		Class stuClass = stu1.getClass();//获取Class对象
		System.out.println(stuClass.getName());
		
		//第二种方式获取Class对象
		Student stu2 = new Student();//这一new 产生一个Student对象,一个Class对象。
	    Student.class.getName();
		System.out.println(Student.class.getName());
		
		//第三种方式获取Class对象
		
		String className = "equals.Student";
		try {
			Class stuClass3 = Class.forName(className);
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(className);
	
 
	}
}
2.3获取Class类的几种方式

1.调用运行时类本身的.class属性
Class clazz = String.class;
2,通过运行时类的对象获取
Person p = new Person();
Class clazz = p.getClass();
3.通过Class的静态方法获取:体现反射的动态性
String className = “java.util.commons”;
Class clazz = Class.forName(className);
4.通过类的加载器
String className = “java.util.commons”;
ClassLoader classLoader = this.getClass().getClassLoader();
Class clazz = classLoader.loadClass(className);


//测试class类的创建方式
public class Test03 {
    public static void main(String[] args) throws ClassNotFoundException{
        Person person = new Student();
        System.out.println("这个人是"+person.name);

        //方法一:通过对象获得
        Class c1 = person.getClass();
        System.out.println(c1.hashCode());

        //方法二:forname获得
        Class c2 = Class.forName("com.faq.reflection.Student");
        System.out.println(c2.hashCode());

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

    }

}

2.4所有类型的Class类对象

public class Test04 {

    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 c72 = int.class;    
        //void      
        Class c8 = void.class;       
         // Class类
        Class c9 = Class.class;
    }
}

三、Java内存分析

主要内容

3.1类加载内存分析
3.2分析类初始化
3.3类加载器
3.4获取类的运行时结构
3.5动态创建对象执行方法
3.6性能对比分析
3.7获取泛型信息
3.8获取注解信息

3.1类的加载内存分析

主要步骤:加载—>链接—>初始化
加载
将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象

链接:将java类的二进制代码合并到JVM的运行状态之后的过程。
   链接又分成三个过程:
验证:确保加载的类信息符合JVM规范,没有安全方面的问题。
准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。

初始化:
  执行类构造器< clinit>()方法的过程。类构造器< clinit>()方法是由编译器自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。
  当初始化一个类的时候,如果发现其父类害没有初始化,则需要先触发其父类的初始化。
  虚拟机会保证一个类的< clinit>()方法在多线程环境中被正确加锁和同步。

package com.chao.reflection;
//测试类什么时候会初始化
public class Test06 {
    static{
        System.out.println("Main类被加载");
    }
    public static void main(String[] args) throws ClassNotFoundException {
        //1. 主动引用
        //Son son = new Son();
        //反射也会产生主动调用
        //Class.forName("com.chao.reflection.Son");

        //不会产生类的引用的方法
        //System.out.println(Son.b);
        //Son[] array = new Son[5];
        System.out.println(Son.M);
    }
}
class Father{

    static  int b = 2;
    static{
        System.out.println("父类被加载");
    }
}
class Son extends Father{
    static {
        System.out.println("子类被加载");
        m = 300;
    }
    static int m = 100;
    static final int M = 1;
}

3.3类加载器

类加载器就是加载所有的类的工具,它加载的类在内存中只会存在一份,也就是生成的堆中的Class对象。不可以重复加载
类加载器种类:
BootstrapClassLoader(启动类加载器) :最顶层的加载类,由 C++实现,负责加载 %JAVA_HOME%/lib目录下的 jar 包和类或者被 -Xbootclasspath参数指定的路径中的所有类。
ExtensionClassLoader(扩展类加载器) :主要负责加载 %JRE_HOME%/lib/ext 目录下的 jar 包和类,或被 java.ext.dirs 系统变量所指定的路径下的 jar 包。
AppClassLoader(应用程序类加载器) :面向我们用户的加载器,负责加载当前应用 classpath 下的所有 jar 包和类

private final ClassLoader parent;
protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 首先,检查请求的类是否已经被加载过
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {//父加载器不为空,调用父加载器loadClass()方法处理
                        c = parent.loadClass(name, false);
                    } else {//父加载器为空,使用启动类加载器 BootstrapClassLoader 加载
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                   //抛出异常说明父类加载器无法完成加载请求
                }

                if (c == null) {
                    long t1 = System.nanoTime();
                    //自己尝试加载
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

3.4 获取类的运行时结构

getFields():获取当前运行时类及其所有父类当中,权限为public的属性
getDeclaredFields():获取当前运行时类当中,不限权限的所有属性
getModifiers():获取权限修饰符,返回值为int类型,通过Modifier.toString(int modifiers)可以转换成权限修饰符
getType():获取数据类型
getName():获取属性名

获取方法

getMethods():获取运行时类及其所有父类当中,权限为public的所有方法
getDeclaredMethods():获取运行时类当中,所有权限的所有方法
getReturnType():获取返回值类型
getAnnotations():获取所有注解
getParameterTypes():获取参数类型
getExceptionTypes():获取异常

3.5创建对象的动态执行方法
//动态的创建对象,通过反射
public class Test09 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        //获得Class对象
        Class c1 = Class.forName("com.kudo.reflection.User");

        //构造一个对象
        User user = (User)c1.newInstance(); //本质是调用了类的无参构造器
        System.out.println(user);

        //通过构造器创建对象
        Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        User user2 = (User)constructor.newInstance("kudo", 1, 21);
        System.out.println(user2);

        //通过反射调用普通方法
        User user3 = (User)c1.newInstance();
        //通过反射获取一个方法
        Method setName = c1.getDeclaredMethod("setName", String.class);
        //invoke: 激活 (对象, 参数)
        setName.invoke(user3, "kudo");
        System.out.println(user3.getName());

        //通过反射操作属性
        User user4 = (User)c1.newInstance();
        Field name = c1.getDeclaredField("name");

        //不能直接操作私有属性,我们需要关闭程序的安全检测
        name.setAccessible(true);
        name.set(user4, "kudo2");
        System.out.println(user4.getName());
    }
}


3.6性能分析对比
public class Test01{
    //普通方式调用
    publlic static void test01(){
    User user=new User();
     long startTime=System.currentTimeMillis();
     for(int i=0;i<1000000000;i++){
     user.getName();
     }
     long endTime=System.currentTimeMillis();
     System.out.println("普通执行10亿次:"+(endTime-startTime)+"ms");
     }
     //反射方式调节
     publlic static void test01(){
    User user=new User();
     Class c1=user.getClass();
     Method getName=c1.getDeclaredMethod("getName",null);

     long startTime=System.currentTimeMillis();
     for(int i=0;i<1000000000;i++){
     getName.invoke(user,null );
     }
     long endTime=System.currentTimeMillis();
     System.out.println("普通执行10亿次:"+(endTime-startTime)+"ms");
     //反射方式调用 关闭检测
      publlic static void test01(){
    User user=new User();
     Class c1=user.getClass();
     Method getName=c1.getDeclaredMethod("getName",null);
     getName.setAccessible(true);

     long startTime=System.currentTimeMillis();
     for(int i=0;i<1000000000;i++){
     getName.invoke(user,null );
     }
     long endTime=System.currentTimeMillis();
     System.out.println("关闭检测执行10亿次:"+(endTime-startTime)+"ms");
 }
 public static void main(String[] args)
   test01();
   test02();
   test03();
  }
}
3.7获取泛型信息

parameterizedType:表示一种参数化类型,比如Collection< String>
GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型
TypeVariable:是各种类型变量的公共接口
WildcardType:代表一种通配符类型表达式

3.8获取注解信息
public class Test06 {
    public static void main(String[] args) throws Exception {
        Class cls = Student.class;
        //获得注解
        Table table = (Table) cls.getAnnotation(Table.class);
        //获取注解信息并打印
        System.out.println(table.value());
        //获取属性
        Field filed = cls.getDeclaredField("id");
        //获取属性注解
        Filed filedInfo = filed.getAnnotation(Filed.class);
        //获取属性注解值并打印
        String s1 = filedInfo.columnName();
        String type = filedInfo.type();
        System.out.println(s1 + "  " + type);
    }
}

@Table(value = "Stu")
class Student {
    @Filed(columnName = "db_id", type = "int", length = 10)
    private int id;
    @Filed(columnName = "db_name", type = "varchar", length = 20)
    private String name;
    @Filed(columnName = "db_age", type = "int", length = 10)
    private int age;

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", 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;
    }

    public Student() {
    }

    public Student(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
}

//创建一个类注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table {
    String value();
}

//创建一个属性注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Filed {
    String columnName();

    String type();

    int length();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值