深入理解反射-类加载机制初识

本文详细介绍了Java反射机制,包括Class类的使用、获取Class对象的三种方法、类的加载与ClassLoader的工作原理,以及双亲委派机制。通过实例展示了如何获取类的属性、方法、构造器,并动态创建对象。同时,还探讨了反射的性能测试、操作泛型和注解的方法。学习反射对于理解框架的底层工作原理至关重要。
摘要由CSDN通过智能技术生成

目录

什么是反射?

Class类

Class类的常用方法

反射中获得class对象的三种方法

 哪些类型可以有Class对象?

类的加载与ClassLoader

类加载器

扩展:双亲委派机制

反射获取类的属性(Filed,Method,Constructor)

用反射动态创建对象

反射性能测试

反射操作泛型(了解)

反射操作注解


什么是反射?

 

Class类

 对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定某个结构(class/interfacelenum/annotation/primitive type/void/)的有关信息。

Class本身也是一个类

Class 对象只能由系统建立对象,我们只能通过反射去获取这个对象

一个加载的类在JVM中只会有一个Class实例

一个Class对象对应的是一个加载到JVM中的一个.class文件

每个类的实例都会记得自己是由哪个Class 实例所生成

通过Class可以完整地得到一个类中的所有被加载的结构

Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象

Class类的常用方法

反射中获得class对象的三种方法

  一个类在内存中只要一个Class对象

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        //用反射去获取类对象
        Class c1= Class.forName("com.wjc.User");
        Class c2= Class.forName("com.wjc.User");
        Class c3= Class.forName("com.wjc.User");

        //一个类在内存中只有一个Class对象
        //一个类被加载后,类的整个结构都会被封装在Class对象中
        System.out.println(c1.hashCode());
        System.out.println(c2.hashCode());
        System.out.println(c3.hashCode());

    }
}

可以看出他们的hasCode值是相同的!

反射中获得class对象的三种方法

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        //一个类在内存中只有一个Class对象
        //一个类被加载后,类的整个结构都会被封装在Class对象中

        //方法一:通过对象获取Class
        User user=new UserImpl();
        Class c1 = user.getClass();
        System.out.println(c1.hashCode());        //1554874502
        //方法二:用反射去获取类对象
        Class c2= Class.forName("com.wjc.UserImpl");
        System.out.println(c2.hashCode());           //1554874502

        //方法三:通过类名获取class
        Class c3 = UserImpl.class;
        System.out.println(c3.hashCode());           //1554874502

        //八大基本类型的包装类都有Type方法
        Class<Integer> type = Integer.TYPE;
        Class<Double> type1 = Double.TYPE;
        System.out.println(type);         //int
        System.out.println(type1);        //double

        //获取类的父类
        Class superclass = c1.getSuperclass();
        System.out.println(superclass);          //class com.wjc.User

        //一个类在内存中只有一个Class对象
        //一个类被加载后,类的整个结构都会被封装在Class对象中


    }
}

 哪些类型可以有Class对象?

public class Test02 {
    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 = Integer.class;  //包装类
        Class c7= ElementType.class;  //枚举类
        Class c8=void.class;   //void
        Class c9=Class.class;   //Class本身也是个类

        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c4);
        System.out.println(c5);
        System.out.println(c6);
        System.out.println(c7);
        System.out.println(c8);
        System.out.println(c9);

        //只要元素类型与维度一样,就是同一个Class
        int a[]=new int[10];
        int b[]=new int[100];
        System.out.println(a.getClass().hashCode());  //1554874502
        System.out.println(b.getClass().hashCode());   //1554874502

    }
}

类的加载与ClassLoader

 

当初始化子类时,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化

当创建一个类的实例时(例如: new关键词,通过反射,克隆,反序列化)

当调用类是静态方法时(即当使用了字节码invokestatic指令)

当使用类,接口的静态字段时(final修饰特殊考虑)(例如: getstatic或putstatic指令)

如果一个接口定义了default方法,那么直接实现或间接实现该接口的类的初始化,该接口要在其之前初始化

当初始调用MethodHandle实例时,初始化该MethodHandle指向的方法所在的类()

 .class不会初始化 为什么.class不会引起类的初始化-CSDN论坛

public class Test03 {
    static {
        System.out.println("Main被加载");
    }

    public static void main(String[] args) throws  Exception{
        int m = Son.m;    //子类和父类被初始化了

        int m1 = Son.M;   //子类和父类都未被初始化,M是常量

        int m2=Son.b;       //父类被初始化了,子类未被初始化,子类引用父类的静态变量,不会导致子类初始化


        //1.主动引用
        Son son=new Son();

        //反射也会产生主动引用
       Class.forName("com.wjc.Son");

       Class sonClass = Son.class;  //不会初始化类,思考为什么呢?
        Son.class.newInstance();  //类初始化了,为什么呢?

    }
}

class  Father{
    static  int b=2;
    static {
        System.out.println("父类被加载");
    }
}

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

.class不会初始化 为什么.class不会引起类的初始化-CSDN论坛

类加载器

 

 


public class Test04 {
    public static void main(String[] args) throws  Exception{
        //获取系统类的加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);   //sun.misc.Launcher$AppClassLoader@18b4aac2

        //获取系统类加载器的父类加载器-->扩展类加载器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);              //sun.misc.Launcher$ExtClassLoader@5cad8086

        //获取扩展类加载器的父类加载器-->根加载器(c/c++)
        //java获取不到,为打印出null
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);             //null

        //测试当前类是哪个加载器的
        ClassLoader loader = Class.forName("com.wjc.UserImpl").getClassLoader();
        System.out.println(loader);         //系统类加载器,打印为sun.misc.Launcher$AppClassLoader@18b4aac2

        ClassLoader loader2 = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(loader2);          //根加载去加载,打印为null


//打印类加载器可以加载的路径
        System.out.println(System.getProperty("java.class.path"));
        /*
        C:\Users\86153\.jdks\corretto-1.8.0_312\jre\lib\charsets.jar;
        C:\Users\86153\.jdks\corretto-1.8.0_312\jre\lib\ext\access-bridge-64.jar;
        C:\Users\86153\.jdks\corretto-1.8.0_312\jre\lib\ext\cldrdata.jar;
        C:\Users\86153\.jdks\corretto-1.8.0_312\jre\lib\ext\dnsns.jar;
        C:\Users\86153\.jdks\corretto-1.8.0_312\jre\lib\ext\jaccess.jar;
        C:\Users\86153\.jdks\corretto-1.8.0_312\jre\lib\ext\jfxrt.jar;
        C:\Users\86153\.jdks\corretto-1.8.0_312\jre\lib\ext\localedata.jar;
        C:\Users\86153\.jdks\corretto-1.8.0_312\jre\lib\ext\nashorn.jar;
        C:\Users\86153\.jdks\corretto-1.8.0_312\jre\lib\ext\sunec.jar;
        C:\Users\86153\.jdks\corretto-1.8.0_312\jre\lib\ext\sunjce_provider.jar;
        C:\Users\86153\.jdks\corretto-1.8.0_312\jre\lib\ext\sunmscapi.jar;
        C:\Users\86153\.jdks\corretto-1.8.0_312\jre\lib\ext\sunpkcs11.jar;
        C:\Users\86153\.jdks\corretto-1.8.0_312\jre\lib\ext\zipfs.jar;
        C:\Users\86153\.jdks\corretto-1.8.0_312\jre\lib\jce.jar;
        C:\Users\86153\.jdks\corretto-1.8.0_312\jre\lib\jfr.jar;
        C:\Users\86153\.jdks\corretto-1.8.0_312\jre\lib\jfxswt.jar;
        C:\Users\86153\.jdks\corretto-1.8.0_312\jre\lib\jsse.jar;
        C:\Users\86153\.jdks\corretto-1.8.0_312\jre\lib\management-agent.jar;
        C:\Users\86153\.jdks\corretto-1.8.0_312\jre\lib\resources.jar;
        C:\Users\86153\.jdks\corretto-1.8.0_312\jre\lib\rt.jar;
        D:\Javaproject\spring-study\ReflectionDemo\target\classes;
        D:\apache-maven-3.8.1\repository\org\springframework\spring-webmvc\5.3.15\spring-webmvc-5.3.15.jar;
        D:\apache-maven-3.8.1\repository\org\springframework\spring-aop\5.3.15\spring-aop-5.3.15.jar;
        D:\apache-maven-3.8.1\repository\org\springframework\spring-beans\5.3.15\spring-beans-5.3.15.jar;
        D:\apache-maven-3.8.1\repository\org\springframework\spring-context\5.3.15\spring-context-5.3.15.jar;
        D:\apache-maven-3.8.1\repository\org\springframework\spring-core\5.3.15\spring-core-5.3.15.jar;
        D:\apache-maven-3.8.1\repository\org\springframework\spring-jcl\5.3.15\spring-jcl-5.3.15.jar;
        D:\apache-maven-3.8.1\repository\org\springframework\spring-expression\5.3.15\spring-expression-5.3.15.jar;
        D:\apache-maven-3.8.1\repository\org\springframework\spring-web\5.3.15\spring-web-5.3.15.jar;
        D:\IntelliJ IDEA 2021.1.3\lib\idea_rt.jar
         */
    }
}

扩展:双亲委派机制

双亲委派机制大概就创建对象后先向上找包,找不到了在用自己的。如自己写了个String类是不会生效的。双亲委派机制保证核心类不被破坏

反射获取类的属性(Filed,Method,Constructor)

//用反射来获得类的信息
public class Test05 {
    public static void main(String[] args) throws  Exception{

        Class c1 = Class.forName("com.wjc.User");

        //获得类的名字
        System.out.println(c1.getName());   //包名+类名  com.wjc.User
        System.out.println(c1.getSimpleName());   //类名  User
        
        //获得类的属性
        Field[] fields = c1.getFields();   //只能找到public修饰的字段
        for (Field field : fields) {
            System.out.println(field);
        }

        Field[] declaredFields = c1.getDeclaredFields();   //找到全部字段
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);

        }
        c1.getField("name");   //得到指定的public修饰的字段
        c1.getDeclaredField("name");// 得到指定名字的字段,不受限制

        System.out.println("=================================");

        c1.getMethods();   //找到public修饰的方法
        c1.getDeclaredMethods();  //找到所有的方法

        c1.getMethod("getName",null);   //得到指定的public修饰的方法  必须要给它参数的类型,否则识别不了,要考虑重载
        c1.getMethod("setName",String.class);   //得到指定的public修饰的方法

        System.out.println("=================================");
        c1.getConstructor();   //得到public修饰的构造器
        c1.getDeclaredConstructor();  //获得全部的构造器

        c1.getDeclaredConstructor(String.class, String.class);  //获得指定的构造器



    }
}

用反射动态创建对象

//动态创建对象,通过反射
public class Test06 {
    public static void main(String[] args) throws  Exception{
        //获得Class对象
        Class c1 = Class.forName("com.wjc.User");

         //构造一个对象
        User user =(User) c1.newInstance();
        System.out.println(user);

        //通过构造器创建对象
        User user2 = (User) c1.getDeclaredConstructor(String.class,String.class).newInstance("小明","12");
        System.out.println(user2.toString());

        //反射获取方法,必须要有无参构造
        User user3=(User) c1.newInstance();
        Method setName = c1.getDeclaredMethod("setName", String.class);

        //invoke:激活的意思
        //(对象,“方法的值”)
        setName.invoke(user3,"小明");
        System.out.println(user3.getName());

        //通过反射获取属性
        User user4=(User) c1.newInstance();
        Field field = c1.getDeclaredField("id");  //id在User里面是私有的
        field.setAccessible(true); //赋予操作权限,可以修改private字段!!!!!!注意语句先后顺序,先给权限再操作

        field.set(user4,"小红");
        System.out.println(field.get(user4));
    }
}

反射性能测试

public class Test07 {

    //普通方式调用
    public 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));
    }
    


    //反射方式调用
    public static void test02() throws  Exception{
        User user=new User();
        Class c1 = Class.forName("com.wjc.User");
        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));
    }
    
    //反射方式调用(关闭检测)
    public static void test03() throws  Exception{
        User user=new User();
        Class c1 = Class.forName("com.wjc.User");
        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));
    }

    public static void main(String[] args) throws Exception {
        test01();
        test02();
        test03();
    }
}

反射操作泛型(了解)

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

public class Test08 {

    //参数为泛型,无返回类型
    public  void test01(Map<String,User> map, List<User> list){
        System.out.println("test01");

    }

      //返回类型为参数类型,无参
    public  Map<String,User> test02(){
        System.out.println("test02");
        return  null;
    }

    public static void main(String[] args) throws  Exception{
        Method method = Test08.class.getDeclaredMethod("test01", Map.class, List.class);

        Type[] genericParameterTypes = method.getGenericParameterTypes(); //获得方法的形参类型(包括泛型)

        for (Type genericParameterType : genericParameterTypes) {
          //  System.out.println(genericParameterTypes);
            if(genericParameterType instanceof ParameterizedType){    //判断是否是参数化类型
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();//获得真实参数信息
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println(actualTypeArgument);
                }
            }
        }

        Method method2 = Test08.class.getDeclaredMethod("test02",null);   //获得方法的返回类型
        Type genericReturnType = method2.getGenericReturnType();
        if(genericReturnType instanceof ParameterizedType){    //判断是否是参数化类型
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();//获得真实参数信息
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println(actualTypeArgument);
            }
        }
    }

}

反射操作注解

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

public class Test09 {
    public static void main(String[] args) throws  Exception{
        Class c1 = Class.forName("com.wjc.Student");

        //通过反射获得注解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);    //@com.wjc.Table01(value=db_01)
        }

        //获得注解的value的值
        Table01 table01 = (Table01) c1.getAnnotation(Table01.class);
        String value = table01.value();
        System.out.println(value);    //db_01


        //获得类指定的注解
        Field name = c1.getDeclaredField("name");
        Filed01 annotation = name.getAnnotation(Filed01.class);
        System.out.println(annotation.colunmName());  //db_filed_03
        System.out.println(annotation.type());  //String
        System.out.println(annotation.length());   //30
    }


}

@Table01("db_01")
class Student {
    @Filed01(colunmName = "db_filed_01",type = "int",length =10)
    private  int id;
    @Filed01(colunmName = "db_filed_02",type = "int",length =20)
    private  int age;
    @Filed01(colunmName = "db_filed_03",type = "String",length =30)
    private  String name;

    public Student() {
    }

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

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

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



//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table01{
    String value();
}

//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Filed01{
    String colunmName();
    String type();
    int length();
}

当学了框架之后,就会更加理解框架里的注解是怎么生效的!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JagTom

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值