注解和反射的笔记

注解

注解:Annotation(从JDK5.0 引入的)Comment(注释)

内置注解

@OverRide:重写(只能修饰方法)

@Deprecated:(修饰方法、属性、类)废弃的

@suppresswarnings:镇压警告

元注解(解释其他注解的注解4个标准)

@Target

@Retention

@Documented

@Inherited

//注解的使用范围参数为一个数组(这个注解可以用在那些地方)//(作用域)
@Target(value = {ElementType.METHOD,ElementType.TYPE})
//注解祖存在的状态(runtime[运行时]>class[类]>source[源码])
@Retention(value = RetentionPolicy.RUNTIME)
@Documented//表示我们是否将注解生成在java文档中
    @Inherited//子类可以继承父类的注解
@interface YuanAnnotation{
}
//注解的使用
    @YuanAnnotation
        public void tets(){
            System.out.println("测试元注解");
        }
}

自定注解

@interface

public class Test2 {
    @diyAnnotation(age = 10 ,arr={"1","2"})
    public void test(){

    }
}

@Target(value = ElementType.METHOD)
@Retention(value = RetentionPolicy.RUNTIME)
@interface diyAnnotation{
    //注解的参数     参数类型+参数名()
    String name() default "name" ;
    int age() default 0;
    int age2() default -1;//如果默认值为-1代表不存在 
    String[] arr();
}

反射 (reflection)

java是静态语言(伪动态语言)

反射可以互获取类的任何内部信息并操作任意对象的的内部属性及方法

Class c=Class.forName("java.util.String")

反射机制提供的功能

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时获取泛型信息
  • 在运行时调用任意一个对象的成员变量和方法
  • 在运行时处理注解
  • 生成动态代理

优点:实现动态创建和编译对象,提现强大的灵活性

缺点:对性能有影响。基本上使用反射是一种解释性操作、我们告诉jvm,我们希望做什么并且他满足我们的需求。这类操作总是慢于直接执行相同操作。

一个类在内存中只用一个Class

一个类被加载后,类的整个结构都会被封装在Class

image-20211101172333101 image-20211101172621803

Class类的创建方式

目标类

public class Target {
    public void  show(){
        System.out.println("测试反射的目标方法");
    };
}
  1. Class.forName
 Class  t1= Class.forName("com.cxp.reflection.Target");
            System.out.println("class.forName=="+t1.hashCode());
  1. 类名.getClass()
  Class t2 = target.getClass();
         System.out.println("对象.getClass=="+t2.hashCode());
  1. 类名.class
 Class t3 = Target.class;
        System.out.println("类名.class====="+t3.hashCode());
  1. 基本内置类型的包装类都有一个Type属性
Class t4 = Integer.TYPE;
        System.out.println("内置类型的type属性="+t4.hashCode());

测试类

 public static void main(String[] args) throws ClassNotFoundException {
        Target target=new Target();
        // 1、通过Class。forName获取目标对象(在Class对象中可以获取到目标类的方法、属性、注解、类名)
          Class  t1= Class.forName("com.cxp.reflection.Target");
            System.out.println("class.forName=="+t1.hashCode());
          Class t2 = target.getClass();
         System.out.println("对象.getClass=="+t2.hashCode());
       Class t3 = Target.class;
        System.out.println("类名.class====="+t3.hashCode());
             Class t4 = Integer.TYPE;
        System.out.println("内置类型的type属性="+t4.hashCode());
     	//可以通过.getSuperclass();获得Class对象的父类
    }

输出结果

class.forName==1956725890
对象.getClass===1956725890
类名.class=====1956725890
内置类型的type属性=356573597
过程分析

类加载器(类加载器加载Class对象 。不能创建Calss对象只能获取Class对象)

加载----------------------->链接---------------------->初始化(类构造器()[JVM做的工作]

虚拟机会保证一个类的方法在多线程环境转中正确加锁和同步

几乎所有的类都有Class对象

类的初始化

什么时候会发生类的初始化

  • 类的主动引用
  • new一个类的对象
  • 调用了静态变量和静态方法
  • 反射的调用
  • 初始化一个类如果父类没有被初始化,则会先初始化父类

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

  • 访问一个静态域(通过子类调用父类的静态变量,不会导致子类初始化)
  • 通过数组定义引用
  • 引用常量
类加载器

作用

  1. 将class文件加载到内存中,并将静态数据转化成方法区运行是数据结构,然后在堆里生成代表这个类的Class对象,作为方法区中类数据的访问入口
  2. 类缓存:标准的JavaSE类加载可一按照要求查找类,但一但某个类被加载到类加载器中,它将维持加载一段时间。不过JVM垃圾回收机制可以将这些类Class对象回收

引导类加载器:jvm 是用C++ 编写的(Bootstap Classloader)核心类的jar在 rt.jar

拓展类加载器:拓展类在ext文件夹下(Extension Classloder)

系统类加载器:(system Classloader)最常用加载器

image-20211103165311239
  • 启动类加载器
  • 扩展类加载器
  • 应用程序加载器

练习代码:

public class TestClassLoader {
    public static void main(String[] args) {
        //获取系统类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);
        //获取系统类加载器的父加载器------>拓展类加载器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);
        //获取拓展类加载器父加载器------------>根加载器
        ClassLoader parent1 = parent.getParent();//(c/c++)
        System.out.println(parent1);
    }
}

输出结果

sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@7f31245a
null

创建运行时类的对象

  1. 获取类的信息

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        //获取类的信息
        Class c1 = Class.forName("com.cxp.reflection.Pojo");
        System.out.println("类的信息="+c1);
        //获取类的名字
        System.out.println("包名+类名="+c1.getName());
        //获取类的简单名字
        System.out.println("类的简单名字="+c1.getSimpleName());
        Pojo pojo=new Pojo();
        //通过对象获取包名+类名
        Class p = pojo.getClass();
        System.out.println("对象.Class 获取包名+类名="+p);
        System.out.println("================获得属性值===================");
        //获取类的属性
        Field[] fields = c1.getFields();
        for (Field field1 : fields) {//只能找到public属性
            System.out.println("getFields获取的类的属性="+field1);
        }
        Field[] declaredField = c1.getDeclaredFields();
        for (Field field2 : declaredField) {//获取到全部的信息
            System.out.println("getDeclaredField获取累类的属性="+field2);
        }
        //获取指定属性的值
       /* Field name = c1.getField("name");  //只能找打public的属性
        System.out.println("getField获取name的值=" + name);*/
        Field name = c1.getDeclaredField("name");
        System.out.println("getDeclareFiel获取name的值="+ name );
        System.out.println("================获得方法==================");
        Method[] method = c1.getMethods();      //获得本类和父类的方法所有的public方法
        for (Method method1 : method) {
            System.out.println("getMethod获取的方法="+method1);
        }
        Method[] declaredMethods = c1.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {     //获取本类的所有方法
            System.out.println("declaredMethod获取方法="+declaredMethod);
        }
        System.out.println("=================获得指定的构造器============================");
        Constructor constructor = c1.getConstructor();
        System.out.println("constructor获取的构造器="+constructor);
        Constructor[] constructors = c1.getConstructors();
        for (Constructor constructor1 : constructors) {
            System.out.println("getConstructors获取的构造器="+constructor1);
        }
        //获取指定构造器
        Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, Integer.class);
        System.out.println("getdeclaredConstructord方法获取指定构造器="+declaredConstructor);
    
    }
    

动态创建执行方法

获取Class对象

​ newInstance()方法创造对象的要求

  1. 必须要有无参构造器
  2. 类构造器的权限要足够

1、获取Calss对象

实体类


package com.cxp.reflection;

public class Pojo {
    @Override
    public String toString() {
        return "Pojo{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    private  String name;
    private  Integer age;

    public Pojo() {
    }

    public Pojo(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

获取Calss对象

 //获得Class对象
        Class c1 = Class.forName("com.cxp.reflection.Pojo");
  1. 创建对象(newInstance()方法)本质是调用了pojo类的无参构造方法

  //创建对象
        Pojo p1 = (Pojo) c1.newInstance();//本质是调用了无参构造
  1. 要想实例化pojo对象只能动态创建对象

    先获取对象的构造方法

    通过getDeclaredConstructor()获取对象的有参构造方法

  Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, Integer.class);
 Object p2 = declaredConstructor.newInstance("张三", 23);
 System.out.println(p2);
  1. 通过反射获取普通方法

    通过invoke(); 方法激活

    invoke方法的两个参数

    1. 对象
    2. 方法的值
 Method setName = c1.getDeclaredMethod("setName", String.class);
  setName.invoke(p3,"李四");
 System.out.println(p3.getName());
  1. 通过反射操作属性

    通过getDeclaredField方法获取属性该方法只能访问票pojo类中的public资源要获取private的属性需要

    setAccessible(true);方法。该方法默认false 禁止访问private资源改为true即可访问pojo类的private属性

Pojo p4 = (Pojo) c1.newInstance();
        Field name = c1.getDeclaredField("name");
	name.setAccessible(true);
        name.set(p4,"找六");
System.out.println(p4.getName());
关于setAccessible()方法

method(方法) field(属性) constructor(构造器)等对象都有setAccessible()方法setAccessible的作用是启动和禁用访问安全的开关
参数为true时指示反射的对象在使用是应取消java的语言访问开关

  • 提高反射的效率。如果代码值代码必须使用反射,而代码需要频繁调用,那么请设置为true

  • 可以是原本无法访问的私有成员也可以访问

参数设置问false则指示反射的对象应该实施java语言访问检查

性能分析

package com.cxp.reflection;

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

public class XingNengFengXi {


    public static void tets1(){
        //普通方法调用
        Pojo po=new Pojo();
        Long start=System.currentTimeMillis();

        for (int i = 0; i < 1000000000; i++) {
            po.getName();
        }
        Long end=System.currentTimeMillis();
        System.out.println("运行时间为="+(end-start)+"ms");

    }
    public static void test2() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //反射方法调用
        Pojo po=new Pojo();
        Class c1 = po.getClass();
        Long start=System.currentTimeMillis();
        Method getName = c1.getDeclaredMethod("getName", null);
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(po,null);
        }
        Long end=System.currentTimeMillis();
        System.out.println("运行时间为="+(end-start)+"ms");
    }
	//反射方法调用 禁用访问
        public static void Test3() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
            Pojo po=new Pojo();
            Class c1 = po.getClass();
            Long start=System.currentTimeMillis();
            Method getName = c1.getDeclaredMethod("getName", null);
            getName.setAccessible(true);
            for (int i = 0; i < 1000000000; i++) {
                getName.invoke(po,null);
            }
            Long end=System.currentTimeMillis();
            System.out.println("运行时间为="+(end-start)+"ms");
        }

    public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        tets1();
        test2();
        Test3();
    }

}

输出结果

运行时间为=6ms
运行时间为=2310ms
运行时间为=948ms
posted @ 2021-11-03 23:40  萌新Newcxp  阅读(45)  评论(0)    收藏  举报