反射(1)Class和类的加载

 

1 反射的出现背景

Java程序中,所有的对象都有两种类型:编译时类型运行时类型,而很多时候对象的编译时类型和运行时类型不一致。 Object obj = new String("hello"); obj.getClass()

例如:某些变量或形参的声明类型是Object类型,但是程序却需要调用该对象运行时类型的方法,该方法不是Object中的方法,那么如何解决呢?

解决这个问题,有两种方案:

方案1:在编译和运行时都完全知道类型的具体信息,在这种情况下,我们可以直接先使用instanceof运算符进行判断,再利用强制类型转换符将其转换成运行时类型的变量即可。

方案2:编译时根本无法预知该对象和类的真实信息,程序只能依靠运行时信息来发现该对象和类的真实信息,这就必须使用反射。

 

2、Class的理解

一个 Class 对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void/[])的有关信息。

1概念性的理解

  • Class本身也是一个类
  • Class 对象只能由系统建立对象
  • 一个加载的类在 JVM 中只会有一个Class实例
  • 一个Class对象对应的是一个加载到JVM中的一个.class文件
  • 每个类的实例都会记得自己是由哪个 Class 实例所生成
  • 通过Class可以完整地得到一个类中的所有被加载的结构包括已经私有化的结构,可以通过Field类get获取set修改
  • Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象

 

针对于编写好的.java源文件进行编译,会生成一个或多个.class字节码文件。接着,我们使用java.exe命令对指定的.class解释运行,这个解释运行的过程中,需要将.class字节码文件加载到内存中。加载到内存中的.class文件对应的结构即为class 的一个实例

 

加载到内存中的Person类,作为Class 的一个实例。

Class clazz = Person.class

 

(2)获取Class实例的四种方法:

@Test
public void test1() throws ClassNotFoundException {
    //获取Class实例的方式1:获取指定的类的class文件
    Class clazz = User.class;
    System.out.println(clazz);

    //2、调用类的对象的getClass实例
    User user = new User();
    Class clazz1 = user.getClass();

    System.out.println(clazz==clazz1);//true
    //类会在内存中缓存起来,整个执行期间,只加载一次

    //3、调用Class的静态方法forName
    String name = "User";
    Class<?> clazz2 = Class.forName(name);
    System.out.println(clazz2);

    //4、通过类的加载器得到Class的实例
    Class clazz3 = ClassLoader.getSystemClassLoader().loadClass("User");
    System.out.println(clazz3==clazz2);//true
}

 

 

运行时类在内存中会缓存起来,整个执行期间,只加载一次。

(3)关于class指向数组时

数组的维度与数据类型相同时,即认为是同一个Class,与数组的长度无关。

举例:

@Test
public void test2(){
    int a[] = new int[10];
    int b[] = new int[100];
    Class clazz1 = a.getClass();
    Class clazz2 = b.getClass();
    System.out.println(clazz2==clazz1);//true
}

 

 

3、类的加载

(1)装载

将类的class文件读入内存,并位置闯将java.lang.Class独享,此过程由类加载器完成。

 

(2)链接

1、验证(verify):确保加载的类信息符合JVM规范,例如,以cafebabe开头,没有安全方面的问题。

2、准备(prepare):正式为类变量(static)分配内存并设置类变量默认初始值的阶段。这些内存都将在方法区中进行分配。

3、解析(revolve):虚拟机常绿内的符号引用

 

(3)初始化

执行类构造器<cliinit>()方法的过程

类构造器<clinit>()方法是有编译器自动收集类中多有的类变量的赋值。

 

 

关于类的加载器:

1、作用:负责类的加载,并对应于一个Class实例

2、分类:

(1)BoottrapClassLoader:启动类加载器、引导类加载器

>使用C/C++语言编写,不能通过Java代码获取实例。(无法获得,返回null)

>负责加载Java的核心库(JAVA_HOME/jre/lib/rt.jar或者sun.boot.class.path路径下的内容)

>加载扩展类和应用程序类加载器,并指定为他们的父类加载器。

 

(2)Extension  ClasssLoader  扩展类加载器

负责加载java.ext.dirs系统属性所制定的目录中加载类库,或从JDK的安装目录的jre/lib/ext子目录下加载类库

 

(3)SystemClassLoaner/ApplicationClassLooader:系统类加载器、程序应用类加载器

>用户自定义的类、默认使用的类的加载器

  • 应用程序中的类加载器默认是系统类加载器。
  • 通过ClassLoader的getSystemClassLoader()方法可以获取到该类加载器

 

(4)用户自定义类的加载器

因为同一个加载器只能加载一次,想要对同一个类加载多次,需要用户自己定义类加载器。

 

意义;实现应用的隔离(同一个类在一个应用程序中可以加载多份);实现数据的加密

 

@Test
public void test1() {
    //get 系统类加载器
    ClassLoader classLoader1 = ClassLoader.getSystemClassLoader();
    //jdk.internal.loader.ClassLoaders$AppClassLoader@63947c6b
    System.out.println(classLoader1);

    //getParent()  获取扩展类加载器
    ClassLoader classLoader2 = classLoader1.getParent();
    //jdk.internal.loader.ClassLoaders$PlatformClassLoader@1888ff2c
    System.out.println(classLoader2);

    //获取引导类加载器
    ClassLoader classLoader3 = classLoader2.getParent();
    System.out.println(classLoader3);//null
}

@Test
public void test2() throws ClassNotFoundException {
    //用户自定义的类是系统类加载器得到的
    Class clazz = User.class;
    ClassLoader classLoader = clazz.getClassLoader();
    System.out.println(classLoader);//jdk.internal.loader.ClassLoaders$AppClassLoader@63947c6b

    //对于java核心api的使用,都使用引导类加载器
    Class clazz2 = Class.forName("java.lang.String");
    ClassLoader classLoader1 = clazz2.getClassLoader();
    System.out.println(classLoader1);//null
}

@Test
public void test3()throws IOException {
    Properties pros = new Properties();
    //读取文件的默认路径是当前的module
    FileInputStream fis = new FileInputStream(new File("D:\\idea\\javaCode\\Reflection\\Person"));

    pros.load(fis);

    String name = pros.getProperty("name");
    String age = pros.getProperty("age");
    System.out.println(name+":"+age);
}

@Test
public void test4()throws IOException{
    Properties pros = new Properties();
    InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("PersonInfo");

    pros.load(is);

    String name = pros.getProperty("name");
    String heitght = pros.getProperty("height");
    System.out.println(name+":"+heitght);    }

 

 

用户自定义类的加载器:

因为同一个加载器只能加载一次,想要对同一个类加载多次,需要用户自己定义类加载器。

意义;实现应用的隔离(同一个类在一个应用程序中可以加载多份);实现数据的加密

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值