Java反射详解


目录

  • 一、反射概述
    • 1. java.lang.Class:是反射的源头
  • 二、如何获取Class的实例
    • 1.调用运行时类本身的.class属性
    • 2.通过运行时类的对象获取
    • 3.通过Class的静态方法获取,通过此方式,体会反射的动态性
    • 4.通过类的加载器
  • 三、创建运行时类对象
    • 1. 获取Class的实例
    • 2.创建运行时类对象
  • 四、通过反射获取类的完整结构
    • 1.获取运行时类的属性
    • 2.获取属性的各个部分的内容
    • 3.获取运行时类的方法(重点)
    • 4.获取方法的各个部分的内容
    • 5.获取构造器
    • 6.获取运行时类的父类
    • 7.获取带泛型的父类
    • 8.获取父类的泛型(重点)
    • 9.获取实现的接口
    • 10.获取所在的包
  • 五、调用运行时类的指定结构
    • 1.调用运行时类指定的属性并赋值
    • 2.调用运行时类中指定的方法
    • 3.调用指定的构造器,创建类对象
  • 六、ClassLoader
    • 1. 描述一下JVM加载class文件的原理机制?

一、反射概述

1. java.lang.Class:是反射的源头

我们创建一个类,通过编译,生成对应的.calss文件,之后使用java.exe加载(jvm的类加载器)此.class文件,此.class文件加载到内存以后,就是一个运行时类,存在缓存区,那么这个运行时类的本身就是一个class的实例

  • 每一个运行时类只加载一次
  • 有了Class实例以后,我们才可以进行如下的操作:
    • 创建对应的运行时类的对象(重点)
    • 可以获取对象的运行时类的完整结构(属性、方法、构造器、内部类、、、)(理解)
    • 调用对应的运行时类的指定的结构(属性、方法)(重点)

在反射以前,如何创建一个类的对象,并调用其中的方法属性

public void test1() {
        Person p = new Person();
        p.setAge(10);
        p.setName("AA");
        p.show();
        System.out.println(p);
    }

有了反射,可以通过反射创建一个类的对象,并调用其中的方法,下面详细说

public void test2() throws Exception {

        Class clazz = Person.class;

        //1.创建clazz对应的运行时类Person类的对象
        Person p = (Person)clazz.newInstance();
        System.out.println(p);

        //2.通过反射调用运行时类的指定属性,public name的修改方式
        Field f1 = clazz.getField("name");
        f1.set(p, "LiuDaHua");
        System.out.println(p);

        //private age的方式
        Field f2 = clazz.getDeclaredField("age");
        f2.setAccessible(true);
        f2.set(p, 20);
        System.out.println(p);

        //3.通过反射调用运行时类的指定方法 public修饰的
        Method m1 = clazz.getMethod("show");
        // 执行
        m1.invoke(p);
        // 带参数的方法
        Method m2 = clazz.getMethod("display", String.class);
        m2.invoke(p, "cn");

    }

二、如何获取Class的实例

1.调用运行时类本身的.class属性

Class clazz = Person.class;
System.out.println(clazz.getName());

Class clazz1 = String.class;
System.out.println(clazz1.getName());

2.通过运行时类的对象获取

Person p = new Person();
Class clazz2 = p.getClass();
System.out.println(clazz.getName());

3.通过Class的静态方法获取,通过此方式,体会反射的动态性

String className = "com.atguigu.java.Person";

Class clazz4 = Class.forName(className);       
System.out.println(clazz4);

4.通过类的加载器

ClassLoader classLoader = this.getClass().getClassLoader();
Class clazz5 = classLoader.loadClass(className);
System.out.println(clazz5.getName());

整个代码

public void test4() throws Exception {
        //1.调用运行时类本身的.class属性
        Class clazz = Person.class;
        System.out.println(clazz.getName());

        Class clazz1 = String.class;
        System.out.println(clazz1.getName());

        //2.通过运行时类的对象获取
        Person p = new Person();
        Class clazz2 = p.getClass();
        System.out.println(clazz.getName());

        //3.通过Class的静态方法获取,通过此方式,体会反射的动态性
        String className = "com.atguigu.java.Person";

        Class clazz4 = Class.forName(className);
        System.out.println(clazz4);


        //4.通过类的加载器
        ClassLoader classLoader = this.getClass().getClassLoader();
        Class clazz5 = classLoader.loadClass(className);
        System.out.println(clazz5.getName());
    }

三、创建运行时类对象

1. 获取Class的实例

通常直接使用类名.class , 例如Person.class

当然了,上面的几种方法都是可以的

String className = "com.atguigu.java.Person";
Class clazz = Class.forName(className);

2.创建运行时类对象

创建对应的运行时类的对象,使用的是newInstance(),实际上就是运用了运行时类的空参数的构造器

要想能够创建成功

  • 要求对应的运行时类要有空参数的构造器
  • 构造器的权限修饰符的权限要足够
Object obj = clazz.newInstance();//调用的是空参构造器
Person p = (Person)obj;
System.out.println(p);

全部代码

public void test1() throws Exception {
    // 获取Class实例
        Class clazz = Person.class;

        //创建对应的运行时类的对象,使用的是newInstance(),实际上就是运用了运行时类的空参数的构造器
        //要想能够创建成功,①要求对应的运行时类要有空参数的构造器,②构造器的权限修饰符的权限要足够
        Object obj = clazz.newInstance();//调用的是空参构造器
        Person p = (Person)obj;
        System.out.println(p);      
    }

四、通过反射获取类的完整结构

1.获取运行时类的属性

1.getFields() 返回 :表示公共字段的 Field 对象的数组,只能获取运行时类中以及父类中声明的为public的属性

Field[] fiels = clazz.getFields();
for(int i=0;i<fiels.length;i++) {
    System.out.println(fiels[i]);
    }

2.getDeclaredFields() :获取运行时类本身声明的所有的属性,包括私有的

Field[] fiels1 = clazz.getDeclaredFields();
for(int i=0;i<fiels1.length;i++) {
    System.out.println(fiels1[i].getName());
    }
        //增强for循环
for(Field f:fiels1) {
    System.out.println(f.getName());
}

2.获取属性的各个部分的内容

权限修饰符 变量类型 变量名

1.获取每个属性的权限修饰符

Field[] field = clazz.getDeclaredFields();
    for(Field i:field) {
    //1.获取每个属性的权限修饰符
        int a = i.getModifiers();
        String str1 = Modifier.toString(a);
        System.out.print(str1+"   ");
    }

2.获取属性的变量类型

Field[] field = clazz.getDeclaredFields();
        for(Field i:field) {
        //2.获取属性的变量类型
        Class type = i.getType();
        System.out.print(type+"  ");
    }

3.获取属性名

Class clazz = Person.class;
        Field[] field = clazz.getDeclaredFields();
        for(Field i:field) {
        //3.获取属性名
        System.out.print(i.getName());
        System.out.println();
    }

3.获取运行时类的方法(重点)

1.getMethods() 获取运行时类及其父类中所有声明为public的方法

Class clazz = Person.class;
Method[] m1 = clazz.getMethods();
    for(Method m:m1) {
    System.out.println(m);
    }

2.getDeclaredMethods() 获取运行时类本身声明的所有的方法

Method[] methods = clazz.getDeclaredMethods();
    for(int i=0;i<methods.length;i++) {
        System.out.println(methods[i]);
    }

4.获取方法的各个部分的内容

注解 权限修饰符 返回值类型 方法名 形参列表 异常

1.注解

Class clazz = Person.class;

    Method[] m1 = clazz.getMethods();
    for(Method m:m1) {
    Annotation[] an = m.getAnnotations();
    for(Annotation a:an) {
        System.out.println(a);
    }
}

2.权限修饰符

int a = m.getModifiers();
String str1 = Modifier.toString(a);
System.out.print(str1+"  ");

3.返回值类型

Class return1 = m.getReturnType();
System.out.print(return1+"  ");

4.方法名

System.out.print(m.getName()+"   ");

5.形参列表

System.out.print("(");
Class[] params = m.getParameterTypes();
for(Class p : params) {
    System.out.print(p.getName());
}
System.out.println(")"+"   ");

6.抛的异常

Class[] ex = m.getExceptionTypes();
    for(Class e:ex) {
        System.out.print(e.getName());
    }
 //   for(int i=0;i<ex.length;i++) {
//  System.out.print(ex[i].getName());
 //       }

5.获取构造器

@Test
    public void test5() throws Exception {

        Class clazz = Class.forName("com.atguigu.java.Person");

        Constructor[] cons = clazz.getDeclaredConstructors();
        for(Constructor c : cons) {
            System.out.println(c);
        }


    }

6.获取运行时类的父类

@Test
    public void test6() {
        Class clazz = Person.class;
        Class super1 = clazz.getSuperclass();
        System.out.println(super1);
    }

7.获取带泛型的父类

@Test
    public void test7() {
        Class clazz = Person.class;
        Type type1 = clazz.getGenericSuperclass();
        System.out.println(type1);
    }

8.获取父类的泛型(重点)

@Test
    public void test8() {
        Class clazz = Person.class;
        Type type1 = clazz.getGenericSuperclass();

        ParameterizedType param= (ParameterizedType)type1;
        Type[] ars = param.getActualTypeArguments();
        System.out.println((Class)ars[0]);
    }

9.获取实现的接口

@Test
    public void test9() {
        Class clazz = Person.class;
        Class[] i = clazz.getInterfaces();
        for(Class a:i) {
            System.out.println(a);
        }
    }

10.获取所在的包

@Test
    public void test10() {
        Class clazz = Person.class;
        Package p = clazz.getPackage();
        System.out.println(p);
    }

全部代码如下:

package com.atguigu.java;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import org.junit.Test;

/**
 * 通过反射获取类的完整结构
 * 
 * @author MD
 *
 */
public class TestField {


    /**
     * 1.获取运行时类的属性
     */
    @Test
    public void test1() {
        Class clazz = Person.class;

        //1.getFields() 返回 :表示公共字段的 Field 对象的数组 
        // 只能获取运行时类中以及父类中声明的为public的属性
//      Field[] fiels = clazz.getFields();
//      for(int i=0;i<fiels.length;i++) {
//          System.out.println(fiels[i]);
//      }

        //2.getDeclaredFields() :获取运行时类本身声明的所有的属性
        Field[] fiels1 = clazz.getDeclaredFields();
        for(int i=0;i<fiels1.length;i++) {
            System.out.println(fiels1[i].getName());
        }
        //增强for循环
        for(Field f:fiels1) {
            System.out.println(f.getName());
        }


    }

    /**
     * 权限修饰符 变量类型  变量名
     * 2.获取属性的各个部分的内容
     * 
     */

    @Test
    public void tset2() {
        Class clazz = Person.class;
        Field[] field = clazz.getDeclaredFields();
        for(Field i:field) {
            //1.获取每个属性的权限修饰符
            int a = i.getModifiers();
            String str1 = Modifier.toString(a);
            System.out.print(str1+"   ");
            //2.获取属性的变量类型
            Class type = i.getType();
            System.out.print(type+"  ");
            //3.获取属性名
            System.out.print(i.getName());
            System.out.println();
        }
    }





    /**
     * 3.获取运行时类的方法
     */
    @Test
    public void test3() {
        Class clazz = Person.class;

        //1.getMethods() 获取运行时类及其父类中所有声明为public的方法

//      Method[] m1 = clazz.getMethods();
//      for(Method m:m1) {
//          System.out.println(m);
//      }


        //2.getDeclaredMethods() 获取运行时类本身声明的所有的方法
        Method[] methods = clazz.getDeclaredMethods();
        for(int i=0;i<methods.length;i++) {
            System.out.println(methods[i]);
        }
    }

    /**
     * 4.获取方法的各个部分的内容
     * 注解  权限修饰符   返回值类型    方法名    形参列表    异常
     * 
     */
    @Test
    public void test4() {
        Class clazz = Person.class;

        Method[] m1 = clazz.getMethods();
        for(Method m:m1) {
            //1.注解
            Annotation[] an = m.getAnnotations();
            for(Annotation a:an) {
                System.out.println(a);
            }

            //2.权限修饰符
            int a = m.getModifiers();
            String str1 = Modifier.toString(a);
            System.out.print(str1+"  ");

            //3.返回值类型
            Class return1 = m.getReturnType();
            System.out.print(return1+"  ");


            //4.方法名
            System.out.print(m.getName()+"   ");

            //5.形参列表
            System.out.print("(");
            Class[] params = m.getParameterTypes();
            for(Class p : params) {
                System.out.print(p.getName());
            }
            System.out.println(")"+"   ");

            //6.抛的异常
            Class[] ex = m.getExceptionTypes();
            for(Class e:ex) {
                System.out.print(e.getName());
            }
//          for(int i=0;i<ex.length;i++) {
//              System.out.print(ex[i].getName());
//          }

            System.out.println();
        }


    }

    /**
     * 5.获取构造器
     * @throws Exception 
     */

    @Test
    public void test5() throws Exception {

        Class clazz = Class.forName("com.atguigu.java.Person");

        Constructor[] cons = clazz.getDeclaredConstructors();
        for(Constructor c : cons) {
            System.out.println(c);
        }
    }

    /**
     * 
     * 6.获取运行时类的父类
     */
    @Test
    public void test6() {
        Class clazz = Person.class;
        Class super1 = clazz.getSuperclass();
        System.out.println(super1);
    }


    /**
     * 7.获取带泛型的父类
     */

    @Test
    public void test7() {
        Class clazz = Person.class;
        Type type1 = clazz.getGenericSuperclass();
        System.out.println(type1);
    }


    /**
     * 8.获取父类的泛型
     */
    @Test
    public void test8() {
        Class clazz = Person.class;
        Type type1 = clazz.getGenericSuperclass();

        ParameterizedType param= (ParameterizedType)type1;
        Type[] ars = param.getActualTypeArguments();
        System.out.println((Class)ars[0]);
    }

    /**
     * 9.获取实现的接口
     */
    @Test
    public void test9() {
        Class clazz = Person.class;
        Class[] i = clazz.getInterfaces();
        for(Class a:i) {
            System.out.println(a);
        }
    }

    /**
     * 10.获取所在的包
     */
    @Test
    public void test10() {
        Class clazz = Person.class;
        Package p = clazz.getPackage();
        System.out.println(p);
    }
}

五、调用运行时类的指定结构

1.调用运行时类指定的属性并赋值

1. 获取指定的属性

getField(String fieldName):获取运行时类中声明为public的指定的属性名为fieldName的属性

Field name = clazz.getField("name");

2.创建运行时类的对象

Person p = (Person) clazz.newInstance();
System.out.println(p);

3.将运行时类的指定属性赋值

name.set(p, "Jerry");
System.out.println(p);

给age赋值,private需要注意

getDeclareField(String fieldName):获取运行时类中指明为filedName的属性

Field age = clazz.getDeclaredField("age");
    //由于属性权限修饰符的限制,为了保证可以给属性赋值,需要在操作前使得此属性可被操作
    age.setAccessible(true);//私有的设置成可以访问的
    age.set(p,1);
    System.out.println(p);

给id赋值,默认的修饰符

Field id = clazz.getDeclaredField("id");
    id.set(p,10);
    System.out.println(p);

2.调用运行时类中指定的方法

1.getMethod(String methodName,Class...params)获取指定的public方法,方法名,参数列表

Class clazz = Person.class;
Method m1 = clazz.getMethod("show");

2.创建运行时类的对象

Person p =(Person)clazz.newInstance();

3.和属性相似,这里是invoke关键字里面是对象和参数列表,或许还有返回值,用Object接收

Object returnVal = m1.invoke(p);
System.out.println(returnVal);//没返回值的打印为null

4.获取toString()有返回值的

Method m2 = clazz.getMethod("toString");
Object returnVal1 = m2.invoke(p);
System.out.println(returnVal1);

5.获取display()带参数的

Method m3 = clazz.getMethod("display",String.class);
m3.invoke(p, "china");

6.获取info()静态的方法

Method m4 = clazz.getMethod("info");
m4.invoke(Person.class);

7.获取Test() 私有的带参数的有返回值的

Method m5 = clazz.getDeclaredMethod("Test",String.class,Integer.class);
m5.setAccessible(true);
Object o = m5.invoke(p,"测试",5);
System.out.println(o);

3.调用指定的构造器,创建类对象

public void test3() throws InstantiationException, Exception {
        Class clazz = Person.class;
        Constructor cons = clazz.getDeclaredConstructor(String.class,int.class);
        cons.setAccessible(true);
        Person p = (Person)cons.newInstance("迪丽热巴",20);
        System.out.println(p);


    }

全部代码

package com.atguigu.java;

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

import org.junit.Test;

/**
 * ******调用对应的运行时类的指定的结构(属性、方法)
 * @author MD
 *
 */
public class TestField1 {
    /**
     * 调用运行时类指定的属性
     * @throws Exception 
     * @throws NoSuchFieldException 
     */

    @Test
    public void test1() throws Exception {
        Class clazz = Person.class;

        //1.获取指定的属性
        //getField(String fieldName):获取运行时类中声明为public的指定的属性名为fieldName的属性
        Field name = clazz.getField("name");
        //2.创建运行时类的对象
        Person p = (Person) clazz.newInstance();
        System.out.println(p);
        //3.将运行时类的指定属性赋值
        name.set(p, "Jerry");
        System.out.println(p);

        //给age赋值,private需要注意
        //getDeclareField(String fieldName):获取运行时类中指明为filedName的属性
        Field age = clazz.getDeclaredField("age");
        //由于属性权限修饰符的限制,为了保证可以给属性赋值,需要在操作前使得此属性可被操作
        age.setAccessible(true);//私有的设置成可以访问的
        age.set(p,1);
        System.out.println(p);

        //给id赋值,默认的修饰符
        Field id = clazz.getDeclaredField("id");
        id.set(p,10);
        System.out.println(p);

    }

    /**
     * 调用运行时类中指定的方法
     * @throws Exception 
     * @throws NoSuchMethodException 
     */
    @Test
    public void test2() throws NoSuchMethodException, Exception {
        Class clazz = Person.class;

        //getMethod(String methodName,Class...params)获取指定的public方法,方法名,参数列表
        Method m1 = clazz.getMethod("show");

        //创建运行时类的对象
        Person p =(Person)clazz.newInstance();

        //和属性相似,这里是invoke关键字里面是对象和参数列表,或许还有返回值,用Object接收
        Object returnVal = m1.invoke(p);
        System.out.println(returnVal);

        //获取toString()有返回值的
        Method m2 = clazz.getMethod("toString");
        Object returnVal1 = m2.invoke(p);
        System.out.println(returnVal1);

        //获取display()带参数的
        Method m3 = clazz.getMethod("display",String.class);
        m3.invoke(p, "china");

        //获取info()静态的方法
        Method m4 = clazz.getMethod("info");
        m4.invoke(Person.class);

        //获取Test() 私有的带参数的有返回值的
        Method m5 = clazz.getDeclaredMethod("Test",String.class,Integer.class);
        m5.setAccessible(true);
        Object o = m5.invoke(p,"测试",5);
        System.out.println(o);  
    }


    /**
     * 调用指定的构造器,创建类对象
     * @throws Exception 
     * @throws InstantiationException 
     */

    @Test
    public void test3() throws InstantiationException, Exception {
        Class clazz = Person.class; 
        Constructor cons = clazz.getDeclaredConstructor(String.class,int.class);
        cons.setAccessible(true);
        Person p = (Person)cons.newInstance("迪丽热巴",20);
        System.out.println(p);
    }
}

六、ClassLoader

类加载器是用来把类(class)装载进内存

1.获取一个系统类加载器

ClassLoader loader1 = ClassLoader.getSystemClassLoader();
System.out.println(loader1);

2.获取系统类加载器的父类加载器,即扩展类加载器

ClassLoader loader2 = loader1.getParent();
System.out.println(loader2);

3.获取扩展类加载器的父类加载器,即引导类加载器,加载的是核心库,打印为null

ClassLoader loader3 = loader2.getParent();
System.out.println(loader3);

4.测试当前类由哪个类加载器进行加载

Class clazz1 = Person.class;
ClassLoader loader4 = clazz1.getClassLoader();
System.out.println(loader4);//系统类加载器

1. 描述一下JVM加载class文件的原理机制?

JVM中类的装载是由ClassLoader和它的子类来实现的,

Java ClassLoader 是一个重要的Java运行时系统组件。它负责在运行时查找和装入类文件的类。


原文链接:https://www.cnblogs.com/mengd/p/13400005.html