反射回顾总结及类初始化相关的面试题

一、复习
1、为什么用反射?
为了在“运行时”动态的加载某个类,并且创建它的对象,或者调用它的成员(成员变量、成员方法),或者某个类型的详细信息。
反过来说,如果不用反射,就必须在“编译时”就需要确定类型等。

2、反射的根源(起点):Class类的对象。

这么理解:
编写的代码编译后在“硬盘”上以xx.class文件的形式存在。
这些代码是要先“加载”到内存中,然后才能用。在内存中每一个xxx.class文件要以对象的形式存储在内存中,
这个对象的类型就是Class类型。

每一个Java的类型  <==> xx.class文件或数据
Class对象  《==》  xx.class文件或数据
Class对象   ==  一个类型

3、Class对象的获取方式:4种
(1)类型名.class
(2)某个类型的实例对象.getClass()
(3)Class.forName("类型的全名称")
(4)类加载器对象.loadClass("类型的全名称")

如何选择四种方式?
第一:如果编写当前代码时,这个类型是已知的,确定的,存在的,那么可以直接  类型名.class 最直接最简单
第二:如果说某个对象的类型是不确定的,但是对象有某个变量(通常这个变量是父类或父接口类型的),
    这个时候如果需要获取对象的运行时的类型,选择  (2)
第三:如果编写当前代码时,这个类型是未知的,不确定的,或暂时还不存在,需要通过读取.xml,.properties,注解信息等方式
才能获取到类型名称的,那么用(3)
第四:一般是自定义类加载器,或者是需要加载某个特定目录下的字节码文件,才会用第(4)种方式。

4、反射的应用
(1)获取类的详细信息
java.lang.Class类
java.lang.reflect(反射)包下的类型:
    Package、Modifier、Constructor、Field、Method、Array....
java.lang.Annotation

(2)运行时动态的创建任意类型的对象
A:直接用Class对象.newInstance()
    这种方式要求这个类型必须有公共的无参构造
B:先获取类型的构造器Constructor的对象,然后用构造器对象.newInstance(...)

(3)运行时动态的操作任意的成员变量
操作静态变量步骤:
A:获取Class对象
B:获取你要操作的静态变量的Field对象
C:调用Field对象的set/get方法
如果Field对象代表的成员变量的权限修饰符有限制的话,可以调用Field对象的setAccessible(true)

操作非静态变量步骤:
A:获取Class对象
B:获取你要操作的静态变量的Field对象
C:创建Class对象代表的类型的实例对象
    例如:Class对象代表的是Student类型,那么要创建Student的对象
D:调用Field对象的set/get方法
如果Field对象代表的成员变量的权限修饰符有限制的话,可以调用Field对象的setAccessible(true)

(4)运行时动态的调用任意的方法
调用静态方法步骤:
A:获取Class对象
B:获取你要调用的静态方法的Method对象
C:调用Method对象的invoke方法
如果Method对象代表的静态方法的权限修饰符有限制的话,可以调用Method对象的setAccessible(true)

调用非静态方法步骤:
A:获取Class对象
B:获取你要调用的静态方法的Method对象
C:创建Class对象代表的类型的实例对象
    例如:Class对象代表的是Student类型,那么要创建Student的对象
D:调用Method对象的invoke方法
如果Method对象代表的静态方法的权限修饰符有限制的话,可以调用Method对象的setAccessible(true)

(5)运行时获取泛型父类的信息
(6)运行时获取某个类、方法、成员变量等上面的注解信息
(7)动态的使用Array类型来创建,操作数组
(8)运行时获取某个类的内部类信息,或者外部类信息

5、类的加载
类的加载步骤:
(1)加载
(2)连接:检查、准备、解析
(3)初始化
类的加载的结果:Class对象
类的加载程序:类加载器

类加载器分为4个等级:
(1)引导类加载器
(2)扩展类加载器
(3)应用程序类加载器(程序员自己写的类基本上是它加载的)
(4)自定义类加载器

类加载器的工作模式:双亲委托模式

public class TestReview {
    public static void main(String[] args) throws Exception {
        //(1)获取int类型的Class对象
        Class clazz1 = int.class;

        //(2)获取com.atguigu.bean包下某个Javabean类型,这个类型还不确定,要通过info.properties文件才能确定具体的类名
        Properties properties = new Properties();//创建一个集合对象,容器对象,用来装从info.properties读取的key,value键值对
        properties.load(TestReview.class.getClassLoader().getResourceAsStream("info.properties"));
//        Class clazz2 = Class.forName(properties.getProperty("className"));


        Student s1 = new Student(1,"张三");
        Student s2 = new Student(2,"李四");

        System.out.println(s1.equals(s2));
        System.out.println(s1.equals("张三"));

    }


    public static void method(Object obj){
        //(3)在这里获取obj对象的运行时类型
        Class<?> c3 = obj.getClass();
    }
}

class Student{
    private int id;
    private String name;

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()){
            System.out.println("类型不一致");
            return false;
        }
        System.out.println("类型一致,都是Student对象");
        Student student = (Student) o;
        return id == student.id &&
                Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
}

某些类加载的过程,不会立刻触发类初始化。
(1)使用某个类的静态的常量
(2)如果通过子类调用父类的静态成员,子类不会初始化,但是会初始化父类。
(3)使用某个类声明数组,并创建数组对象,但是还未创建元素对象

这三种以外的情况,对类的使用会立刻触发类初始化。
(1)创建这个类的对象
(2)调用这个类的静态成员
(3)通过反射使用某个类
(4)如果使用子类,子类初始化时,会触发父类的初始化
(5)main方法所在的类,在运行main之前,先要对这个类进行初始化

public class TestExam {
    static{
        System.out.println("main方法所在类的静态代码块");
    }

    public static void main(String[] args) throws ClassNotFoundException {
        System.out.println(MyClass.MAX_VALUE);//100

        Son.method();//通过子类名去调用父类静态方法

        MyClass[] arr = new MyClass[5];//产生了一种新的类型MyClass[]类型,对这种新的类型进行初始化
        arr[0] = new MyClass();

        Class.forName("com.atguigu.review.Son");//导致Son类初始化

    }
}
class MyClass{
    public static final int MAX_VALUE = 100;

    static {
        System.out.println("MyClass静态代码块");
    }
}
class Father{
    static {
        System.out.println("1.父类的静态代码块");
    }
    public static void method(){
        System.out.println("2.父类的静态方法");
    }
}
class Son extends Father{
    static {
        System.out.println("3.子类的静态代码块");
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值