【Java】反射、注解、动态代理总结(一)

简介: 【Java】反射、注解、动态代理总结

重点

1.反射 (反射方法 构造方法)
2.注解(使用注解)
3.动态代理(框架底层的,作用)

01_类的加载


路径

  1. 类的加载过程
  2. 类的加载时机
  3. 类的加载
  • 当程序在运行后,第一次使用某个类的时候,会将此类的class文件读取到内存,并将此类的所有信息存储到一个Class对象中
  • 说明:Class对象是指java.lang.Class类的对象,此类由Java类库提供,专门用于存储类型的信息

类的加载机制

在以下情况下会加载类:

  1. 创建一个类的对象(第1次)
  2. 调用类的静态方法,静态变量 (第1次)
  3. 使用一个类的子类时 (第1次)
  4. 通过反射进行加载类
  5. java命令执行某一个类 (第1次)运行java程序
public class HelloWorld{
    public static void main(String[] args){
        System.out.println("Hello World!");
    }
}
//编译: javac HelloWorld.java    //生成:HelloWorld.class  字节码文件
//运行: Java HelloWorld   //使用java命令执行HelloWorld类(默认调用main方法)
                          //底层: HelloWorld.main(null);

小结

问题1:Student.class文件中都包含什么内容?

  • 构造方法、成员变量、成员方法

在jvm执行某个类时,如果该类是第一次被执行:

  • 先把该类的.class文件读取到内存中
  • 基于.class文件创建一个Class对象(方法区)
  • Class对象中存储的是.class文件中的内容:构造方法、成员变量、成员方法
  • Class对象中存储的构造方法:构造器对象 Constructor对象
  • Class对象中存储的成员变量:字段对象 Field对象
  • Class对象中存储的成员方法:方法对象 Method对象

02_类加载器


目标

  • 了解类加载器的概念 【了解】

路径

  1. 类加载器的作用
  2. 类加载器的分类
  3. 获取类加载器的方式

类加载器的作用

什么是类加载器?

  • 加载器是Java运行时环境的一部分,负责加载字节码文件
  • 类加载器的作用:
  • 负责将磁盘上的某个class文件读取到内存并生成Class的对象

类加载器的分类:

Java中有三种类加载器,它们分别用于加载不同种类的class:

启动类加载器(Bootstrap ClassLoader):用于加载系统类库<JAVA_HOME>\bin目录下的class,例如:rt.jar。

扩展类加载器(Extension ClassLoader):用于加载扩展类库<JAVA_HOME>\lib\ext目录下的class。

  • 应用程序类加载器(Application ClassLoader):用于加载我们自定义类的加载器。

获取类加载器的方式

来自Class类型获取类加载器的方法:

public ClassLoader getClassLoader()  //返回该类的类加载器
//有些实现可能使用null来表示引导类加载器(启动类加载器)
//如果该类由引导类加载器(启动类加载器)加载,则此方法在这类实现中将返回 null 

代码实践:

//自定义类型
class Student{
    private int age;
}
//测试类
public class Demo01 {
    public static void main(String[] args){
        //获取自定义类的类加载器
        //1. 先获取Student对应的Class对象
        Class<Student> cls = Student.class;  
        //2. 再通过该class对象获取类加载器
        ClassLoader loader = cls.getClassLoader();
        System.out.println("loader = "+loader);//ClassLoaders$AppClassLoader@2437c6dc
        //获取String类的类加载器
        ClassLoader loader2 = String.class.getClassLoader();
        System.out.println("loader2 = " + loader2);
    }
}

小结

类加载器的作用:

  • 把硬盘上的.class文件读取到内存中并创建Class对象

类加载器可以分为3种:

  • 引导类加载器(启动类加载器) 负载加载系统类库(java.lang包)
  • 扩展类加载器 负责加载jdk扩展的类库
  • 应用程序类加载器 负责加载程序员自己写的类

03_双亲委派机制


目标

  • 了解双亲委派机制的工作过程 【了解】

路径

  1. 3种类加载器的关系
  2. 双亲委派机制
  3. 双亲委派机制的好处

3种类加载器的关系

image.png

从上图可知,三种类加载器存在一定的关系:

  • 应用程序类加载器的父级类加器是扩展类加载器
  • 扩展类加载器的父级类加载器是启动类加载器
  • 结论:这种关系称为类加载器的"双亲委派模型"

双亲委派机制

"双亲委派模型"的工作机制:

  • 某个"类加载器"收到类加载的请求,它首先不会尝试自己去加载这个类,而是把请求交给父级类加载器
  • 因此,所有的类加载的请求最终都会传送到顶层的"启动类加载器"中
  • 如果"父级类加载器"无法加载这个类,然后子级类加载器再去加载
  • “双亲委派模型"中,除了顶层的启动类加载器外,其余的类加载器都应当有自己的"父级类加载器”

小结

双亲委派机制:

  • 在使用一个类时,类并不会由应用程序类加载器来加载,而是由"双亲:启动类加载器、扩展类加载器"先来进行加载,当"双亲"无法加载时,才由应用程序类加载器来完成.class的加载及Class对象的创建

04_反射:概述


目标

  • 了解反射技术的作用 【了解】

路径

  1. 反射的概述
  2. 反射技术的作用

反射

反射技术:

  • 其实就是对类进行解剖的技术
  • 类中有什么?
  • 构造方法
  • 成员变量
  • 成员方法

结论:反射技术就是把一个类进行了解剖,然后获取到 构造方法、成员变量、成员方法

反射技术的应用案例:

  • idea
  • 框架技术:Spring

想要使用反射技术有一个必备条件:

  • Class对象
  • 原因:.class文件由类加载器读取并创建Class对象。Class对象中存储了.class文件中的内容:构造方法、成员变量、成员方法

反射技术的作用

使用反射技术,可以对类进行解剖,可以获取到类中的:构造方法、成员变量、成员方法

  • 构造方法: 可以创建对象
  • 成员方法: 可以调用执行
  • 成员变量: 赋值、取值

反射技术的作用:

  • 不用使用new关键字,就可以创建对象
  • 不用使用"对象名.方法"形式,就可以调用方法
  • 不用使用"对象名.属性"形式,就可以给属性赋值、取值
  • 通常在类中属性,都被修饰为private(私有的:外部不能访问)
  • 反射技术,可以做到对私有成员进行操作

示例:

"cn.itcast.pojo.Student" stu = new cn.itcast.pojo.Student();
给一个字符串:"cn.icast.pojo.Student"
创建一个对象:?????   //使用new做不到  
           使用反射技术可以实现

实例:

给一个Student.class
程序在运行中,不能停止, 动态的获取一个Student.class
    //使用反射技术,可以在JVM运行状态下,动态的获取并加载Student.class

小结

反射技术 :对类进行解剖的技术

反射技术的作用:可以不通过传统方式,来实现类的实例化、方法的调用

  • 实现的提前:需要使用Class对象

05_反射:Class类


目标

  • 掌握Class对象的获取方式 【掌握】

路径

  1. Class类
  2. 获取Class类对象的方式
  3. Class类中的常用方法

Class类

  • Class就是用来描述正在运行的java类型
  • Class类的实例表示Java中任何正在运行的类型,每一个类型都有与之对应的Class对象
  • 比如:类,接口,枚举,注解,数组,基本数据类型,void 都有与之对应的Class对象
类名.class
接口名.class
int.class
boolean.class
array.class    

获取Class对象

获取Class类对象的方式有3种:

方式一:类型名.class   //Student.class
方式二:对象.getClass()  //对象名.getClass()
方式三:Class.forName(String className) //className是全路径类名 = 包名+类型名
//方式1:类型名.class
//应用场景: 当类名明确时,可以直接使用"类名.class"
Class clz = String.class 
Class clz = int.class 
Class clz = double.class
//方式2:对象.getClass()
//应用场景:通常是应用方法中
 public void method(Student stu){
    Class clz = stu.getClass();   
 } 
//方式3: Class.forName("类的全名称");//带有包名的类
//应用场景: 通常使用在读取配置文件中的类型
pro.properties文件
--------------文件内容------------------    
className=cn.icast.pojo.Student   
---------------------------------------   
//代码实现    
ResourceBundler r = ResourceBundler.getBundler("pro");
String className = r.getString("className");//"cn.icast.pojo.Student"
Class StudentClass = Class.forName(className);//className="cn.icast.pojo.Student" 
//当获取到Class对象了,就可以对类进行解剖了

Class类中的常用方法

String getSimpleName()  // 获得类名字符串:类名
String getName()   // 获得类全名:包名+类名
T newInstance() // 创建Class对象关联类的对象 (前提:类中有一个无参构造方法)
//示例:
      Studentod类    cn.itcast.pojo.Student     //public Student(){}
      Class stuClass = Student.class;
      Object obj = stuClass.newInstance();//调用Student()  创建Student对象
      Student stu = (Student) obj;

代码实现:

public class Test01 {
    @Test
    public void testMethod3() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Class stuClass = Class.forName("com.itheima.cls.demo2.Student");
        Student stu = (Student) stuClass.newInstance();
        stu.study();
    }
    @Test
    public void testMethod2() throws IllegalAccessException, InstantiationException {
        Student stu =new Student();
        //对象名.getClass()
        Class studentClass = stu.getClass();
        Student student = (Student) studentClass.newInstance();
        student.study();
    }
    @Test
    public void testMethod1() throws IllegalAccessException, InstantiationException {
        // 类型名.class
        Class studentClass = Student.class;
        //System.out.println(studentClass);
        System.out.println("带有包名的类:"+studentClass.getName());
        System.out.println("类名:"+studentClass.getSimpleName());
        //实例化Student对象
        Object obj = studentClass.newInstance();
        Student stu = (Student) obj;
        stu.age=20;
        stu.name="张三";
        System.out.println(stu.name+"==="+stu.age);
        stu.study();
    }
}

小结

获取Class对象的方式:

  1. 类型名.class //明确了具体的类型时,直接使用:类名.class
  2. 对象名.getClass() //当方法中传递的对象时,使用:对象名.getClass()
  1. Class类中的静态方法:forName(“带有包名的类”) //从配置文件中读取到类的全名称时

06_反射:构造器


目标

  • 能够使用Class对象获取构造器Constructor对象 【掌握】

路径

  1. Constructor类
  2. 获取构造器Constructor对象的方式
  3. Constructor类中常用方法

Constructor类

Class中的类型:

构造方法对应的类型:  Constructor类型
字段:Field
方法:Method

Constructor类

  • 代表构造方法(构造器)
  • 类中的每一个构造方法都是一个Constructor类的对象
  • 反射技术中构造器的目的:
  • 获得Constructor对象来创建类的对象
  • 大白话:不使用new关键字,通过Constructor来创建对象

获取Constructor对象的方式

Constructor对象的获取和Class类中方法有关:

Constructor[] getConstructors()
        //获得类中的所有构造方法对象,只能获得public的
Constructor[] getDeclaredConstructors()
        //获得类中的所有构造方法对象
      //可以是public、protected、(默认)、private修饰符的构造方法    
Constructor getConstructor( Class... parameterTypes)
        //根据参数类型获得对应的Constructor对象   获取public修饰的
        //只能获得public修饰的构造方法
/*示例: Student       public Student(String name, int age)    public Student(int age) 
        Class stuClass = Student.class;
        //根据给定的参数类型,来获取匹配的构造器对象
        Constructor c = stuClass.getConstructor( String.class , int.class );
*/  
Constructor getDeclaredConstructor(Class... parameterTypes)
        //根据参数类型获得对应的Constructor对象
      //可以是public、protected、默认、private修饰符的构造方法

Constructor类常用方法

T newInstance(Object... initargs)
  //根据指定的参数创建对象
/*
        Class stuClass = Student.class;
        //根据给定的参数类型,来获取匹配的构造器对象
        Constructor c = stuClass.getConstructor( String.class , int.class );
        //使用构造器对象,来实例化Student
        c.newInstance( "张三", 22 );//Student(String name, int age)
        //无参构造
        Constructor c = stuClass.getConstructor();
        Student stu = (Student) c.newInstance();
*/    
void setAccessible(true)//应用场景:仅适用于访问有权限检查的成员
   //设置"暴力反射" ——是否取消权限检查,true取消权限检查,false表示不取消

代码实现:

public class Test1 {
    @Test
    public void testMethod1() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
       //获取构造器的步骤
        /*1、先获取到Class对象
          2、使用Class对象中的方法,获取Constructor对象
          3、使用Constructor对象,实例化类
       */
        //获取Class对象(Student.class)
        Class stuClass = Student.class;
        //获取Constructor对象: public Student()
        Constructor con1 = stuClass.getConstructor();
        System.out.println(con1);
        Student stu = (Student)con1.newInstance();
        stu.name="小崔";
        System.out.println(stu.name);
    }
}

小结

Constructor类:

  • 代表类中的一个构造方法

获取Constructor类的方式:

//获取public修饰的构造方法
Constructor getConstructor(Class... parameterTypes)
//获取非public修饰的方法
Constructor getDeclaredConstructor(Class... parameterTypes)

常用方法:

Object newInstance(Object... param)  //利用构造器对象,实例化自定义对象
void setAccessible(true) //消除JVM对权限的检查操作 (一次性的。只是对当前操作去除)    

07_反射:使用构造器创建对象


目标

  • 能够使用构造器Constructor对象创建对象 【掌握】

路径

  1. 案例:使用无参构造器创建对象
  2. 案例:使用有参构造器创建对象
  3. 案例:使用私有构造器创建对象

案例:使用无参构造器创建对象

@Test
    public void testMethod1() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //获取Class对象(Student.class)
        Class stuClass = Student.class;
        //利用Class对象,来获取构造器对象
        Constructor con = stuClass.getConstructor();//方法中的参数为可变参数,可以不传递值
        //使用Constructor对象中的方法,来实例化Student类对象
        Student stu = (Student) con.newInstance();//方法中的参数是可变参数
        stu.study();
    }

案例:使用有参构造器创建对象

//有参构造方法
    @Test
    public void testMethod2() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //获取Class对象(Student.class)
        Class stuClass = Student.class;
        //public Student(String name, int age, String gender)
        //获取带有参数的构造器对象
        //参数:是用来设置构造方法中参数的类型是什么
        Constructor con = stuClass.getConstructor(String.class, int.class, String.class);
        //实例化有参构造方法
        //参数:要传递给Student(String name, int age, String gender)的数据
        Student stu = (Student) con.newInstance("熊大", 22, "男");
        //调用对象中的方法
        stu.study();
    }

案例:使用私有构造器创建对象

 @Test
    public void testMethod3() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //获取Class对象(Student.class)
        Class stuClass = Student.class;
        // private Student(String name)
        //获取私有构造器对象
        Constructor con = stuClass.getDeclaredConstructor(String.class);
        //当需要对私有成员操作时,需要先取消JVM对访问权限的检查操作
        con.setAccessible(true);//暴力破解(取消权限检查) //仅在当前次取消
        //使用私有构造器,实例化Student对象
        Student stu = (Student) con.newInstance("文平");
        System.out.println(stu.name);
        stu.study();
    }

小结

当获取到私有构造器后,要使用该构造器创建对象,需要:

  • 取消权限检查:setAccessible(true)

08_反射:方法


目标

  • 掌握Method对象的获取方式 【掌握】

路径

  1. Method类
  2. 获取Method对象的方式
  3. Method类常用方法

Method

Method类

  • 代表一个成员方法
  • 每一个成员方法都是一个Method类的对象

反射技术中使用Method的目的:

  • 通过Method对象来调用成员方法

获取Method对象的方式

Method对象的获取和Class类中方法有关:

Method[] getMethods();
    //获得当前类和其父类中的所有public成员方法对象,返回数组
Method[] getDeclaredMethods();
    //获得当前类中的所有成员方法对象,返回数组
    //只获得本类的,包括public、protected、默认、private的
Method getMethod(String name,Class...args);
    //根据方法名和参数类型获得对应的成员方法对象,只能获得public的
    //参数说明:
             name : 类中方法的名字
             args : 方法中参数类型的Class     例:int.class     
Method getDeclaredMethod(String name,Class...args);
    //根据方法名和参数类型获得对应的成员方法对象,包括public、protected、(默认)、private的

Method类常用方法

//使用方法对象,调用对象中的方法执行(入栈执行)
Object invoke(Object obj, Object... args) 
    // obj: 对象   //"对象名.方法"
    // args:调用方法时传递的实参
//返回值: Object类型      
void setAccessible(true)
    // 设置"暴力访问"  ——是否取消权限检查,true取消权限检查,false表示不取消

代码实现:

//获取Method对象的步骤:
1、先获取Class对象
2、使用Class对象,获取Method对象
3、使用Method对象,执行方法
public class Test01 {
    @Test
    public void testMethod1() throws ClassNotFoundException {
        //获取Class对象
        Class stuClass  = Class.forName("com.itheima.method.demo1.Student");
        //使用Class对象,获取Method对象
        Method[] methods = stuClass.getMethods();//获取本类及父类中所有的public方法
        for (Method m : methods){
            System.out.println(m);
        }
    }
    @Test
    public void testMethod2() throws ClassNotFoundException {
        //获取Class对象
        Class stuClass  = Class.forName("com.itheima.method.demo1.Student");
        //使用Class对象,获取Method对象
        Method[] methods = stuClass.getDeclaredMethods();//获取本类中所有方法(包含私有)
        for (Method m : methods){
            System.out.println(m);
        }
    }
}    

小结

在反射中获取Method对象的步骤:

1、 先获取Class对象

2、基于Class对象,获取Method对象

3、使用Method对象,执行该方法

获取Method对象的方式:

//可以获取到本类及父类中public修饰的方法(指定方法名及方法中的参数类型)
Method getMethod(String name,Class...args);
//获取本类中任意的方法(指定方法名及方法中的参数类型)
Method getDeclaredMethod(String name,Class...args);

Method类中常用方法:

//执行Method对象所代表的方法
Object invoke(Object 对象 ,  Object... 方法中需要的实参);
void setAccessible(true)

回顾上午内容:

  • 类加载过程
  • 在java程序中,当第一次访问类时,会把类的.class文件加载到内存中,并基于.class文件,创建一个Class对象(针对.class文件进行了封装)
  • 类加载器
  • java程序要运行,需要依赖于JRE(java运行环境),而jre中有类加载器,利用类加载器来加载.class文件并创建Class对象
  • 获取类加载器的方式:
Class类中有一个方法:
    ClassLoader cl = Class对象.getClassLoader();

反射:

  • 什么是反射技术:
  • 反射技术,就是对类进行解剖,解剖出:构造方法、成员方法、成员变量
  • 构造方法: java.lang.reflect.Constructor类
  • 成员方法: java.lang.reflect.Method类
  • 成员变量: java.lang.reflect.Field类
  • 反射技术的应用:
  • 框架技术开发
  • 反射技术的使用:
  • 核心:需要利用Class对象
  • Class类
//获取Class对象
//方式1: Class clazz = 类名.class
//方式2: Class clazz = 对象名.getClass()
//方式3: Class clazz = Class.forName("类的全名称")

常用方法:

String getName() //获取带有包名的类
String getSimpleName() //获取类名(不包含包名称)
T  newInstance() //实例化对象(注意:需要有无参构造方法)    

构造器:Constructor类

//获取Constructor对象
//方式1: 获取public修饰的构造方法
Constructor cons = Class对象.getConstructor(Class... 参数类型)
//方式2: 获取private修饰的构造方法
Constructor cons  = Class对象.getDeclaredConstructor(Class... 参数类型) 
//获取所有的构造器对象:
Constructor[]  cons = Class对象.getConstructors(); 
Constructor[]  cons = Class对象.getDeclaredConstructors();

常用方法:

//实例化对象
T  newInstance(Object... 实参)
//去除权限检查
void setAccessible(true)  //针对非public修饰的  

成员方法:Method类

//获取Method对象
//方式1: 获取本类或父类中的public修饰的成员方法
Method  m = Class对象.getMethod("方法名", Class... 方法中参数类型)
//方式2: 获取本类中private修饰的成员方法
Method  m = Class对象.getDeclaredMethod("方法名", Class... 方法中参数类型)    
//获取所有的成员方法:
Method[]  m = Class对象.getMethods() 
Method[]  m = Class对象.getDeclaredMethods()    

常用方法:

//调用方法
Object invoke(Object  实例对象 , Object...  实参)
//去除权限检查
void setAccessible(true)
相关文章
|
26天前
|
XML Java 编译器
Java学习十六—掌握注解:让编程更简单
Java 注解(Annotation)是一种特殊的语法结构,可以在代码中嵌入元数据。它们不直接影响代码的运行,但可以通过工具和框架提供额外的信息,帮助在编译、部署或运行时进行处理。
86 43
Java学习十六—掌握注解:让编程更简单
|
21天前
|
Java 开发者 Spring
[Java]自定义注解
本文介绍了Java中的四个元注解(@Target、@Retention、@Documented、@Inherited)及其使用方法,并详细讲解了自定义注解的定义和使用细节。文章还提到了Spring框架中的@AliasFor注解,通过示例帮助读者更好地理解和应用这些注解。文中强调了注解的生命周期、继承性和文档化特性,适合初学者和进阶开发者参考。
43 14
|
21天前
|
前端开发 Java
[Java]讲解@CallerSensitive注解
本文介绍了 `@CallerSensitive` 注解及其作用,通过 `Reflection.getCallerClass()` 方法返回调用方的 Class 对象。文章还详细解释了如何通过配置 VM Options 使自定义类被启动类加载器加载,以识别该注解。涉及的 VM Options 包括 `-Xbootclasspath`、`-Xbootclasspath/a` 和 `-Xbootclasspath/p`。最后,推荐了几篇关于 ClassLoader 的详细文章,供读者进一步学习。
29 12
|
23天前
|
设计模式 Java API
[Java]静态代理与动态代理(基于JDK1.8)
本文介绍了代理模式及其分类,包括静态代理和动态代理。静态代理分为面向接口和面向继承两种形式,分别通过手动创建代理类实现;动态代理则利用反射技术,在运行时动态创建代理对象,分为JDK动态代理和Cglib动态代理。文中通过具体代码示例详细讲解了各种代理模式的实现方式和应用场景。
18 0
[Java]静态代理与动态代理(基于JDK1.8)
|
23天前
|
存储 Java
[Java]反射
本文详细介绍了Java反射机制的基本概念、使用方法及其注意事项。首先解释了反射的定义和类加载过程,接着通过具体示例展示了如何使用反射获取和操作类的构造方法、方法和变量。文章还讨论了反射在类加载、内部类、父类成员访问等方面的特殊行为,并提供了通过反射跳过泛型检查的示例。最后,简要介绍了字面量和符号引用的概念。全文旨在帮助读者深入理解反射机制及其应用场景。
16 0
[Java]反射
|
1月前
|
安全 Java 测试技术
🌟Java零基础-反射:从入门到精通
【10月更文挑战第4天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
25 2
|
15天前
|
Java 编译器
Java进阶之标准注解
Java进阶之标准注解
28 0
|
26天前
|
Java
Java代码解释静态代理和动态代理的区别
### 静态代理与动态代理简介 **静态代理**:代理类在编译时已确定,目标对象和代理对象都实现同一接口。代理类包含对目标对象的引用,并在调用方法时添加额外操作。 **动态代理**:利用Java反射机制在运行时生成代理类,更加灵活。通过`Proxy`类和`InvocationHandler`接口实现,无需提前知道接口的具体实现细节。 示例代码展示了两种代理方式的实现,静态代理需要手动创建代理对象,而动态代理通过反射机制自动创建。
|
1月前
|
IDE Java 编译器
java的反射与注解
java的反射与注解
16 0
|
1月前
|
设计模式 缓存 Java
从源码学习Java动态代理|8月更文挑战
从源码学习Java动态代理|8月更文挑战