【Java高级程序设计学习笔记】类型信息概述

对计算机而言,每个对象都有自己的信息,通常用字段的值来表示它的状态或信息,而用于描述对象所属类的信息被称为类型信息。类型信息记录了类的名称、拥有的字段、实现的方法、继承的父类或接口等信息,有了类型信息,JVM才能认识和识别对象,才能让程序富有动态性。

1.1类型信息的存储**

编译一个java的类文件会产生一个.class文件,它们之间有什么关系?
1.class文件与Java文件
Java的编译器在编译java类文件的时候,会把原有的文本文件翻译成二进制的字节码,并把这些字节码存储在.class文件中。
(1).java类中只有一个类或接口的情况下,编译后只产生一个.class文件

public class Person {
}

编写一个类Person存放在Person.java文件中,编译后可在bin目录下发现一个同名的.class的文件Person.class。编译器把编译后的Person类的字节码存放在Person.class文件中。
Person.class请添加图片描述Person.java
(2)如果Java类文件中存在内部类,编译这个文件的时候就会产生多个.class文件,并且这些.class文件(除外部类)的文件都以外部类名+$+内部类名来命名。

public class Person {
    class Toll {
    }
    interface Communication {
        public void speak();
    }
}

编写一个外部类Person,并在外部类中又定义了一个内部类和一个内部接口,并将这些代码全部存放在Person.java中。编译后可在bin目录下发现三个.class文件(我的是在out目录下)
在这里插入图片描述
(3)如果.java文件中存在多个并行类(接口),编译的时候也会产生多个.class文件,并且这些文件都以类名来命名。

public class Person {
}
class Monkey{
}
interface Thinking{
    void think();
}

编写了两个类和一个接口,并把这些类和接口存放在了Person.java文件中。编译后bin目录下存在三个.class文件。

在这里插入图片描述

2 .class文件结构

.class文件的内部结构如图所示:
在这里插入图片描述
在这里插入图片描述
从上表可以看出,Java类文件中的属性、方法,以及类中的常量信息都被分别存储在.class文件中。当Java在编译一个类文件时,除了翻译类文件外,还会为该类动态的添加一个共有的静态常量属性.class,这个属性记录了类的相关信息,即类型信息,他是Class类的一个实例。

**

1.2 类型信息的加载

Java提供两种类的装载模式:预先装载和按需装载。按需加载可以在程序启动的时候不用把所有类都装载到JVM中。大部分类都被延迟到使用的时候动态加载(java的运行时动态装载机制)。可以使得修改代码的时候无需全盘编译。
1.Java基础类的加载
采用预先装载机制。启动一个程序,Java会在JDK目录下找到并载入jvm.dll,然后启动虚拟机。此时会进行一些初始化操作。接着创建一个Bootstrap Loader对象,称它为启动类的装载器(C++编写),负责在虚拟机启动时一次性加载基础类。
2.含main()函数类的加载
Bootstrap Loader可以装载定义在sun.misc命名空间下的Launcher类,它有两个内部类 ExtClassLoader和AppClassLoader。 ExtClassLoader的父加载器被设置为null,表示为Bootstrap Loader,AppClassLoader的父加载器为ExtClassLoader,拥有main()函数的入口类,在程序启动时加载。

public class Boostrap {
    static {
        System.out.println("Bootstrap prepare!");
    }

    public static void main(String[] args) {
        ClassLoader loader=Boostrap.class.getClassLoader();
        System.out.println(loader);
        System.out.println(loader.getParent());
        System.out.println(loader.getParent().getParent());
    }
}

运行结果:
在这里插入图片描述
程序一开始就输出了“Bootstrap prepare!”,说明Bootstrap类在程序启动的时候就加载了(因为Java类装载的时候会执行静态域代码)。然后可以就看出Bootstrap类的加载器为sun.misc.Launcher A p p C l a s s L o a d e r @ 18 b 4 a a c 2 。 它 的 父 类 为 s u n . m i s c . L a u n c h e r AppClassLoader@18b4aac2。 它的父类为sun.misc.Launcher AppClassLoader@18b4aac2sun.misc.LauncherExtClassLoader@1b6d3586。而ExtClassLoader的父类加载器为空,由Bootstrap Loader直接装载。

3.按需装载
(1)装载条件

当程序运行时,当一个类的静态成员被第一次引用,JVM就会去装载它包括:
1.静态方法
2.静态属性
3.构造方法

注意:当访问常量静态常量属性时,JVM加载类的过程不会进行类的初始化的工作,虽然构造方法没有被显示的声明为静态方法,但而能够作为类的静态成员特例,当new一个对象时,也会被当作类静态成员的引用,也会触发JVM加载这个类。
例:在这里插入图片描述
由此可以看到,打印常量静态字段时,Person没有执行静态域代码,真正的初始化发生在new一个对象的时候。
(2)按需装载流程
当要使用一个类的时候,JVM首先会检查这个类的Class对象是否已加载,已加载就执行想执行的代码,未加载,JVM就会装载这个类,流程如下:
在这里插入图片描述
注意:当访问一个类的常量静态属性的时候,类的初始化不会进行,真正的初始化工作被延迟到对静态方法或者非常量静态属性首次访问时。初始化有效实现了尽可能的惰性,以减少不必要的内存使用。

4.类加载器

每次创建一个Java类的实例的时候,必须先把该类加载到内存中。JVM使用类加载器加载类。Java加载器在Java核心类库和CLASSPATH环境下面的所有类中查找类,找不到则抛出java.lang.ClassNotFoundException异常。

JVM使用三种类加载器:bootstrap类加载器、extension类加载器、system类加载器。它们是父子关系,bootstrap类加载器在顶端,而system加载器在架构的最底层。

  1. bootstrap类加载器用于引导JVM,一旦调用java.exe程序,bootstrap类加载器就开始工作。因此它必须使用本地代码实现,然后加载JVM需要的类到函数中。另外,它也负责加载所有的java核心类,如java.lang 和java.io包.还会查找核心类库,如rt.jar等。
  2. extension类加载器负责加载标准扩展目录下面的类。这样就使得编写程序更简单,只需要把JAR文件复制到扩展目录下即可,类加载器会自动的在下面查找。不同供应商提供的扩展类库是不同的。Sun公司提供的是/jdk/jre/lib/ext。
  3. system加载器是默认加载器,它在环境变量CLASSPATH目录下面查找相应的类。每次类需要加载,system类加载器首先调用,但不会马上加载类,会委派任务给它的父类extension类加载器。extension类加载器又把任务委派给它的父类bootstrap类加载器。因此bootstrap类加载器会首先加载,如果找不到extension类加载器会尝试加载。如果扩展类加载器也失败,system类加载会执行任务。如果的找不到,会抛出java.lang.ClassNotFoundExeception异常。

Java类加载机制优势在于可以通过扩展java.lang.ClassLoader抽象类来扩展自己的类加载器,自定义自己的类加载器的原因包括以下几个:
(1)要制定类加载器的某些特定规则,例如加载指定目录下的类文件,加载经过加密的.class文件。
(2)缓存以前加载的类
(3)实现加载类预备使用
(4)当.class文件修改后自动加载新的文件。

在这里插入图片描述
对于上表中给出的方法,表示类名称的name参数的值是类的二进制名称。

下面通过扩展java.lang.ClassLoader抽象类来扩展自定义的类加载器。下面的FileSystemClassLoader从文件中加载类。


import javax.imageio.IIOException;
import java.io.*;

public class FileSystemClassLoader extends ClassLoader{

    private String rootDir;

    public FileSystemClassLoader(String rootDir){
        this.rootDir=rootDir;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] classData= new byte[0];
        try {
            classData = getClassData(name);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (classData==null){
            throw new ClassNotFoundException();
        }
        else {
            return defineClass(name,classData,0,classData.length);
        }
    }

    private byte[] getClassData(String classname) throws IOException {
        String path=classNameToPath(classname);
        try {
            InputStream ins=new FileInputStream(path);
            ByteArrayOutputStream baos=new ByteArrayOutputStream();
            int bufferSize=4096;
            byte[] buffer=new byte[bufferSize];
            int bytesNumRead=0;
            while ((bytesNumRead=ins.read(buffer))!=-1){
                baos.write(buffer,0,bufferSize);
            }
            return baos.toByteArray();
        }catch (IIOException | FileNotFoundException e){
            e.printStackTrace();
        }
        return null;
    }

    private String classNameToPath(String className){
        return rootDir+ File.pathSeparatorChar+className.replace(',',File.separatorChar)+".class";
    }
}

5.类加载顺序
1)父类与子类的加载顺序

ublic class Person {
    public static final int ID=1;
    static {
        System.out.println("Person prepare");
    }
}
class Teacher extends Person {
    static {
        System.out.println("Teacher prepare!");
    }
}

class Bootstrap {
    public static void main(String[] args) {
        new Teacher();
    }
}

运行结果:
Person prepare
Teacher prepare!

可见当new一个实例的时候,先装载的是它的父类。由此可得出,当一个类具有继承关系的时候,装载是从顶级类开始的。

2)引用类的加载
把引用类分为:1.未初始化的静态引用2.初始化的静态引用

public class Course {
    static {
        System.out.println("Course prepare!");
    }
}
class teacher{
    static {
        System.out.println("Teacher prepare!");
    }
    public static Course course;
}
class bootstrap {
    public static void main(String[] args) {
        new teacher();
    }
}

运行结果:
Teacher prepare!
由结果可以看出,Course类没有被装载进JVM,
把teacher类中的public static course course改为public static Course course=new Course();
优化后:
Teacher prepare!
Course prepare!
可以看出JVM首先加载的是Teacher类再加载Course类

1.3信息类型的表示

Java类文件编译后,会在类中添加一个静态属性,这个属性是Class类的实例,用于描述类型信息。
描述类信息的类架构图如图:
在这里插入图片描述
Class对象提供了一组方法,我们可以方便的获取类的属性、方法、构造方法等相关信息。并用Field、Method、Constructor类来进行描述,可以通过这些描述类来分析类型信息,并运行类型信息来进行一些动态操作,如反射、类型识别等。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

天的命名词

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

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

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

打赏作者

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

抵扣说明:

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

余额充值