华为面试官问我Java反射机制,先跟你聊两块钱儿的

一、类的加载与ClassLoader的理解

1、加载

将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.class对象。

2、链接

将Java类的二进制代码合并到JVM的运行状态之中的过程。

  • 验证:确保加载的类信息符合JVM规范,没有安全方面的问题;
  • 准备:正式为类变量分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区内进行分配;
  • 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。

3、初始化

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

二、什么时候会发生类初始化

1、类的主动引用(一定会发生类的初始化)

  • 当虚拟机启动,先初始化main方法所在的类;
  • new一个类的对象;
  • 调用类的静态成员(除了final常量)和静态方法;
  • 使用java.lang.reflect包的方法对类进行反射调用;
  • 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类;

2、类的被动调用(不会发生类的初始化)

  • 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化;
  • 通过数组定义类引用,不会触发此类的初始化;
  • 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了);

三、类加载器的作用

将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。

四、动态创建对象执行方法

package com.reflection;

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

public class Test03 {
    public static void main(String[] args) throws Exception {
        //获得class对象
        Class c1 = Class.forName("com.reflection.User");
        //1、构造一个对象,本质是无参构造器
        User user1 = (User) c1.newInstance();
        System.out.println(user1);

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

        //3、通过反射调用普通方法
        User user3 = (User) c1.newInstance();
        Method setName = c1.getDeclaredMethod("setName", String.class);
        //invoke激活
        setName.invoke(user3,"素小暖");
        System.out.println(user3.getName());

        //4、通过反射操作属性
        User user4 = (User) c1.newInstance();
        Field name = c1.getDeclaredField("name");
        //true:取消Java语言访问检查
        name.setAccessible(true);
        name.set(user4,"素小暖2");
        System.out.println(user4.getName());
    }
}

五、通过反射获取泛型信息

1、代码实例

package com.reflection;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

public class Test04 {
    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 = Test04.class.getMethod("test01", Map.class, List.class);
        Type[] genericParameterTypes = method.getGenericParameterTypes();
        for (Type genericParameterType : genericParameterTypes) {
            System.out.println("***"+genericParameterType);
            if(genericParameterType instanceof ParameterizedType){
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println(actualTypeArgument);
                }
            }
        }

        method = Test04.class.getMethod("test02", null);
        Type genericReturnType = method.getGenericReturnType();
        if(genericReturnType instanceof ParameterizedType){
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println("test02,"+actualTypeArgument);
            }
        }
    }
}

2、控制台输出 

六、通过反射获取注解信息

1、代码实例

package com.reflection;

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

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

        //通过反射获取注解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }

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

        //获得类指定的注解
        Field field = c1.getDeclaredField("name");
        FieldSu annotation = field.getAnnotation(FieldSu.class);
        System.out.println(annotation.columnName());
        System.out.println(annotation.type());
        System.out.println(annotation.length());
    }
}

@TableSu("db_student")
class Student{
    @FieldSu(columnName = "db_id",type = "int",length = 10)
    private int id;
    @FieldSu(columnName = "db_name",type = "varchar2",length = 10)
    private String name;
    @FieldSu(columnName = "db_age",type = "int",length = 10)
    private int age;

    public Student() {
    }

    public Student(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.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;
    }

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

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

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

2、控制台输出 

前一篇:Java基础知识总结(绝对经典)

后一篇: Spring常用注解(绝对经典)

猜你喜欢

转载自blog.csdn.net/guorui_java/article/details/108351185