java底层学习

额,马上就要面试了,Java的底层肯定是需要了解的。网上找了找java的底层文章,做个记号。java底层主要是类的加载、连接和初始化。

本文主要分为四个方面:

(1)java底层概述

(2)new和newInstance()方法的区别

(3)深入探讨java的加载机制

(4)一个完整java程序冲Javaxxx.class执行的完整过程

四个部分都是来自网上的资料,四个部分看完,应该对java的底层有些了解了。

下面文字来自《疯狂java讲义》:

1.类的加载、连接和初始化

当系统主动使用某个类,如果该类还未加载到内存中,系统会加载、连接、初始化三个步骤。
1.类的加载将类的Class文件读入内存中,并为之创建一个java.lang.Class对象。
Class文件的来源:
(1)从本地加载class文件
(2)从jar包中加载(系统api)
(3)从网络加载
2.类的连接:
连接阶段负责将类的二进制数据合并到JRE中。
3.类的初始化
类的初始化时机:
(1)创建类的实例:new操作符、反射创建实例、通过反序列化;
(2)调用某个类的静态方法;
(3)访问某个类的静态属性(final属性除外)。

2.new操作符和newInstance()方法的区别

http://lvqingboy-163-com.iteye.com/blog/657599

在初始化一个类,生成一个实例的时候,newInstance()方法和new关键字除了一个是方法,一个是关键字外,最主要有什么区别?它们的区别在于创建对象的方式不一样,前者是使用类加载机制,后者是创建一个新类。那么为什么会有两种创建对象方式?这主要考虑到软件的可伸缩、可扩展和可重用等软件设计思想。

Java中工厂模式经常使用newInstance()方法来创建对象,因此从为什么要使用工厂模式上可以找到具体答案。 例如:
    class c = Class.forName(“Example”);
    factory = (ExampleInterface)c.newInstance();

其中ExampleInterface是Example的接口,可以写成如下形式:
    String className = "Example";
    class c = Class.forName(className);
    factory = (ExampleInterface)c.newInstance();

进一步可以写成如下形式:
    String className = readfromXMlConfig;//从xml 配置文件中获得字符串
    class c = Class.forName(className);
    factory = (ExampleInterface)c.newInstance();

上面代码已经不存在Example的类名称,它的优点是,无论Example类怎么变化,上述代码不变,甚至可以更换Example的兄弟类Example2 , Example3 , Example4……,只要他们继承ExampleInterface就可以。

从JVM的角度看,我们使用关键字new创建一个类的时候,这个类可以没有被加载。但是使用newInstance()方法的时候,就必须保证:1、这个类已经加载;2、这个类已经连接了。而完成上面两个步骤的正是Class的静态方法forName()所完成的,这个静态方法调用了启动类加载器,即加载javaAPI的那个加载器。

现在可以看出,newInstance()实际上是把new这个方式分解为两步,即首先调用Class加载方法加载某个类,然后实例化。 这样分步的好处是显而易见的。我们可以在调用class的静态加载方法forName时获得更好的灵活性,提供给了一种降耦的手段。

最后用最简单的描述来区分new关键字和newInstance()方法的区别:
    newInstance: 弱类型。低效率。只能调用无参构造。
    new: 强类型。相对高效。能调用任何public构造。

我的理解:

其实,newInstance()方法是显式加载:

Class 类的 forName (String s)方法把自定义类 TestClass 加载进来,并通过 newInstance ()方法把实例初始化

new操作符是隐式加载:JRE在执行到 new 关键字的时候就会把对应的实例类加载进入内存。

3.深入讨论java的类加载器机制

http://www.blogjava.net/William/archive/2006/08/25/65804.html

Java 语言是一种具有动态性的解释型编程语言,当指定程序运行的时候, Java 虚拟机就将编译生成的 . class 文件按照需求和一定的规则加载进内存,并组织成为一个完整的 Java 应用程序。 Java 语言把每个单独的类 Class 和接口 Implements 编译成单独的一个 . class 文件,这些文件对于 Java 运行环境来说就是一个个可以动态加载的单元。正是因为 Java 的这种特性,我们可以在不重新编译其它代码的情况下,只编译需要修改的单元,并把修改文件编译后的 . class 文件放到 Java 的路径当中,等到下次该 Java 虚拟机器重新激活时,这个逻辑上的 Java 应用程序就会因为加载了新修改的 .class 文件,自己的功能也做了更新,这就是 Java 的动态性。

下面用一个简单的例子让大家对 Java 的动态加载有一个基本的认识:

[java] view plain copy

  1. class TestClassA{
  2. publicvoid method(){
  3. System.out.println("LoadingClassA");
  4. }
  5. }
  6. public class ClassLoaderTest {
  7. publicstatic void main(String args[]){
  8. TestClassAtestClassA = new TestClassA();
  9. testClassA.method();
  10. }
  11. }

编译后输入命令: java -verbose:class ClassLoaderTest ,执行文件。 (java-verbose:class:查看class的加载情况)

输出结构如图 (1)

图( 1 )

从运行结果我们可以看到, JRE ( JavaRuntime Environment )首先加载 ClassLoaderTest文件,然后再加载 TestClassA 文件,从而实现了动态加载。

1. 预先加载与依需求加载

Java 运行环境为了优化系统,提高程序的执行速度,在 JRE 运行的开始会将 Java 运行所需要的基本类采用预先加载( pre-loading )的方法全部加载要内存当中,因为这些单元在 Java 程序运行的过程当中经常要使用的,主要包括 JRE 的 rt.jar 文件里面所有的 .class 文件。

当 java.exe 虚拟机开始运行以后,它会找到安装在机器上的 JRE 环境,然后把控制权交给 JRE , JRE 的类加载器会将 lib 目录下的 rt.jar 基础类别文件库加载进内存,这些文件是 Java 程序执行所必须的,所以系统在开始就将这些文件加载,避免以后的多次 IO 操作,从而提高程序执行效率。

图( 2 )我们可以看到多个基础类被加载, java.lang.Object,java.io.Serializable 等等。

图( 2 )

相对于预先加载,我们在程序中需要使用自己定义的类的时候就要使用依需求加载方法( load-on-demand ),就是在 Java 程序需要用到的时候再加载,以减少内存的消耗,因为 Java 语言的设计初衷就是面向嵌入式领域的。

在这里还有一点需要说明的是, JRE 的依需求加载究竟是在什么时候把类加载进入内部的呢?

我们在定义一个类实例的时候,比如 TestClassA testClassA ,这个时候 testClassA 的值为 null ,也就是说还没有初始化,没有调用 TestClassA 的构造函数,只有当执行 testClassA = new TestClassA() 以后, JRE 才正真把TestClassA 加载进来。

2.隐式加载和显式加载

隐式加载:使用new操作符;

显示加载:使用forname()方法:Class 类的 forName (String s) 方法把自定义类 TestClass 加载进来,并通过 newInstance ()方法把实例初始化

Java 的加载方式分为隐式加载( implicit)和显示加载( explicit ),上面的例子中就是用的隐式加载的方式。所谓隐式加载就是我们在程序中用 new 关键字来定义一个实例变量, JRE 在执行到 new 关键字的时候就会把对应的实例类加载进入内存。隐式加载的方法很常见,用的也很多, JRE 系统在后台自动的帮助用户加载,减少了用户的工作量,也增加了系统的安全性和程序的可读性。

相对于隐式加载的就是我们不经常用到的显示加载。所谓显示加载就是有程序员自己写程序把需要的类加载到内存当中,下面我们看一段程序:

[java] view plain copy

  1. class TestClass{
  2. publicvoid method(){
  3. System.out.println("TestClass-method");
  4. }
  5. }
  6. public class CLTest {
  7. publicstatic void main(String args[]) {
  8. try{
  9. Classc = Class.forName("TestClass");
  10. TestClassobject = (TestClass)c.newInstance();
  11. object.method();
  12. }catch(Exceptione){
  13. e.printStackTrace();
  14. }
  15. }
  16. }

我们通过 Class 类的 forName (String s) 方法把自定义类 TestClass 加载进来,并通过 newInstance ()方法把实例初始化。事实上 Class 类还很多的功能,这里就不细讲了,有兴趣的可以参考 JDK 文档。

Class 的 forName() 方法还有另外一种形式:Class forName(String s, boolean flag, ClassLoader classloader) , s 表示需要加载类的名称,flag 表示在调用该函数加载类的时候是否初始化静态区, classloader 表示加载该类所需的加载器。

forName (String s) 是默认通过ClassLoader.getCallerClassLoader() 调用类加载器的,但是该方法是私有方法,我们无法调用,如果我们想使用 Class forName(String s, boolean flag, ClassLoader classloader) 来加载类的话,就必须要指定类加载器,可以通过如下的方式来实现:

Test test = new Test();//Test 类为自定义的一个测试类;

ClassLoader cl = test.getClass().getClassLoader();

//获取 test 的类装载器;

Class c =Class.forName("TestClass", true, cl);

因为一个类要加载就必需要有加载器,这里我们是通过获取加载 Test 类的加载器 cl 当作加载 TestClass 的类加载器来实现加载的。

3. 自定义类加载机制

之前我们都是调用系统的类加载器来实现加载的,其实我们是可以自己定义类加载器的。利用 Java 提供的 java.NET.URLClassLoader 类就可以实现。下面我们看一段范例:

[java] view plain copy

  1. try{
  2. URLurl = new URL("file:/d:/test/lib/");
  3. URLClassLoaderurlCL = new URLClassLoader(new URL[]{url});
  4. Classc = urlCL.loadClass("TestClassA");
  5. TestClassAobject = (TestClassA)c.newInstance();
  6. object.method();
  7. }catch(Exceptione){
  8. e.printStackTrace();
  9. }

我们通过自定义的类加载器实现了 TestClassA 类的加载并调用 method ()方法。分析一下这个程序:首先定义 URL 指定类加载器从何处加载类, URL 可以指向网际网络上的任何位置,也可以指向我们计算机里的文件系统 ( 包含 JAR 文件 ) 。上述范例当中我们从 file:/d:/test/lib/ 处寻找类;然后定义 URLClassLoader 来加载所需的类,最后即可使用该实例了。

4. 类加载器的阶层体系

讨论了这么多以后,接下来我们仔细研究一下 Java 的类加载器的工作原理:

当执行 java ***.class 的时候, java.exe 会帮助我们找到 JRE ,接着找到位于 JRE 内部的 jvm.dll ,这才是真正的 Java 虚拟机器 , 最后加载动态库,激活 Java 虚拟机器。虚拟机器激活以后,会先做一些初始化的动作,比如说读取系统参数等。一旦初始化动作完成之后,就会产生第一个类加载器―― Bootstrap Loader , Bootstrap Loader 是由 C++ 所撰写而成,这个 Bootstrap Loader 所做的初始工作中,除了一些基本的初始化动作之外,最重要的就是加载 Launcher.java 之中的 ExtClassLoader ,并设定其 Parent 为 null ,代表其父加载器为 BootstrapLoader 。然后 Bootstrap Loader 再要求加载 Launcher.java 之中的 AppClassLoader ,并设定其 Parent 为之前产生的 ExtClassLoader 实体。这两个加载器都是以静态类的形式存在的。这里要请大家注意的是, Launcher$ExtClassLoader.class 与Launcher$AppClassLoader.class 都是由 Bootstrap Loader 所加载,所以 Parent 和由哪个类加载器加载没有关系。

下面的图形可以表示三者之间的关系:

父类

父类

载入

载入

BootstrapLoader

PARENT

AppClassLoader

PARENT

ExtClassLoader

这三个加载器就构成我们的 Java 类加载体系。他们分别从以下的路径寻找程序所需要的类:

BootstrapLoader :      sun.boot.class.path

ExtClassLoader:                java.ext.dirs

AppClassLoader:               java.class.path

这三个系统参量可以通过 System.getProperty() 函数得到具体对应的路径。大家可以自己编程实现查看具体的路径。

5. 总结

了解 Java 的类加载机制对我们熟练灵活运用 Java 语言,提高程序的运行效率有着非常重要的作用,知其然也要知其所以然,这样才能从整体提高程序的质量。

4.深入java底层

http://bigwhite.blogbus.com/logs/579744.html

在一个朋友的书架上发现王森著的《Java深度历险》一书,看了书的前言了解该书是关于Java底层技术内幕的。怀着好奇心浏览了一下,谈不上有太多收获,但也记下了一些自认为有益的两点。

Java xxx

我们在命令行下敲入:“java xxx”后会发生什么呢?

流程如下:

1.        找到JRE;

2.        找到JVM.dll;

3.        启动JVM,并进行初始化;

4.        产生Bootstrap Loader;

5.        载入ExtClassLoader;(Ext – Extended)

6.        载入AppClassLoader;

7.        加载xxx类。

书中提到Bootstrap Loader、ExtClassLoader和AppClassLoader构成了Java的“类加载器继承体系--class loader hierarchy”,其中BootstrapLoader是由C++编写的,其他两个是由Java写的。之所以成为“继承体系”是因为这三个loader之间是有联系的。Bootstrap Loader负责加载ExtClassLoader,后者ExtClassLoader就将其parent置为Bootstrap Loader。AppClassLoader较为特殊,虽然由Bootstrap载入,但是其parent却置为ExtClassLoader。其原因是为了实现“委托模型”。简述“委托模型”就是当类加载器有加载类的需求时,会先请求其parent使用其搜索路径帮助加载,如果其parent找不到,才使用自己的搜索路径进行加载。如上述所说当ExtClassLoader想载入AppClassLoader类时它首先请求其parent “Bootstrap Loader”帮忙,Bootstrap Loader将AppClassLoader载入后,由于这个载入是ExtClassLoader请求的,所以AppClassLoader的parent还是置为ExtClassLoader而不是Bootstrap Loader。

类的加载流程

类加载的时候遵循一个原则:“类加载器会依类的继承体系从上至下依次加载”。举个例子:“如果C继承了B并实现了接口I,而B有继承自A”,则类加载器在加载C时,加载的次序会是A-> B->I->C,(注:interface会如同class一样被Java编译器编译为独立的.class文件)

其实我对底层的东西并未给与太多的关注,如果在哪个项目中需要我去了解底层的话,我会去很好的学习。

额,顺便看看什么事DLL

http://baike.baidu.com/view/887.htm

动态链接库英文为DLL,是DynamicLink Library 的缩写形式,DLL是一个包含可由多个程序同时使用的代码和数据的库,DLL不是可执行文件。动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个 DLL 中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。DLL 还有助于共享数据和资源。多个应用程序可同时访问内存中单个DLL 副本的内容。DLL 是一个包含可由多个程序同时使用的代码和数据的库。

时间: 2024-10-26 07:31:43

java底层学习的相关文章

java底层学习---1

JRE: Java Runtime EnvironmentJDK:Java Development Kit JRE顾名思义是java运行时环境,包含了java虚拟机,java基础类库.是使用java语言编写的程序运行所需要的软件环境,是提供给想运行java程序的用户使用的.JDK顾名思义是java开发工具包,是程序员使用java语言编写java程序所需的开发工具包,是提供给程序员使用的.JDK包含了JRE,同时还包含了编译java源码的编译器javac,还包含了很多java程序调试和分析的工具:

Java EE 学习(7):IDEA + maven + spring 搭建 web(3)- 配置数据库

参考: https://my.oschina.net/gaussik/blog/513444 注:在阅读本文前,请先阅读: Java EE 学习(5):IDEA + maven + spring 搭建 web(1) Java EE 学习(6):IDEA + maven + spring 搭建 web(2) 5 数据库配置 下面,就要通过一个简单的例子,来介绍 SpringMVC 如何集成 Spring Data JPA(由 Hibernate JPA 提供),来进行强大的数据库访问,并通过本章节

java基础学习——对面向对象的重识

最近几天在看java的视频,感觉很多东西java语法上很像c#,但是还是有很多内容惊到我: 1,java虚拟机 用过VMWar或者VirtualBox的童鞋都对"虚拟机"这个词有个大致的了解,虚拟机提供了个虚拟的平台,让我们可以模拟一些脱离硬件的环境.java里的虚拟机起到了一个类似以前学过的ODBC驱动程序的作用,它屏蔽了底层的差异,让操作系统通过虚拟机向下面看去,操作系统的感觉都是一样的. 有了这一层东西,我们彻底解开了底层硬件和运行操作系统之间的耦合,实现了一次编译,到处运行的梦

java集合学习一

1.Set存放的元素是无序的不可重复. 2.List存放的元素为有序可重复. 3.重写equals方法,一般最好重写hasCode方法,当对象作为key的时候及索引,会使用hasCode方法进行查找. 4.容器对象在调用remove,contains等方法时需要比较对象是否相等,这会涉及到对象类型的equals方法和 hashCode方法.对于自定义的类型,需要重写equals和hashCode方法以实现自定义的对象相等规则.相等的对象 应该具有相等的hashCode. 5.ArrayList底

深入Java集合学习系列:HashMap的实现原理

参考文献 引用文献:深入Java集合学习系列:HashMap的实现原理,大部分参考这篇博客,只对其中进行稍微修改 自己曾经写过的:Hashmap实现原理 1. HashMap概述: HashMap是基于哈希表的Map接口的非同步实现(Hashtable跟HashMap很像,唯一的区别是Hashtalbe中的方法是线程安全的,也就是同步的).此实现提供所有可选的映射操作,并允许使用null值和null键.此类不保证映射的顺序,特别是它不保证该顺序恒久不变. 2. HashMap的数据结构: 在ja

深入Java集合学习系列:LinkedHashMap的实现原理

1. LinkedHashMap概述: LinkedHashMap是Map接口的哈希表和链接列表实现,具有可预知的迭代顺序.此实现提供所有可选的映射操作,并允许使用null值和null键.此类不保证映射的顺序,特别是它不保证该顺序恒久不变.   LinkedHashMap实现与HashMap的不同之处在于,后者维护着一个运行于所有条目的双重链接列表.此链接列表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序.   注意,此实现不是同步的.如果多个线程同时访问链接的哈希映射,而其中至少一个线

Java EE学习之旅1——HeadFirstJavaEE

因为找到的实习是用Java开发的公司,所以来学习一下Java EE的知识. 首先找来了书<轻量级Java EE企业应用实战>. 啊不得不说学了Java之后直接看这个还是完全不行呢,好多名词看都没有看过啊哈哈. 首先来看看都些啥看不懂的词... 1.JSP.Servlet和JavaBean JSP和Servlet都是用在表现层的东西,而实质上JSP编译成Servlet才运行. 但Servlet开发成本太大,所以用JSP. JavaBean用来通信交换表现层和底层数据. 2.MVC和Struts

转:深入Java集合学习系列:HashSet的实现原理

0.参考文献 深入Java集合学习系列:HashSet的实现原理 1.HashSet概述: HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持.它不保证set 的迭代顺序:特别是它不保证该顺序恒久不变.此类允许使用null元素.HashSet中不允许有重复元素,这是因为HashSet是基于HashMap实现的,HashSet中的元素都存放在HashMap的key上面,而value中的值都是统一的一个private static final Object PRESENT

java框架学习之路——Spring的体系结构

分类: java框架学习 Spring的体系结构 Spring是一个开源的轻量级框架 Spring项目开发流程: 创建项目-->添加必须的类库jar包-->创建源文件编程调用库-->创建bean的配置文件-->运行调试 核心特征: 1.AOP:面向切面编程 2.IOC:控制反转,把对象的创建交给Spring管理,依赖注入DI,对象生成放在了XML定义 3.跨越javaEE三层 Web层:SpringMVC Service层:Spring IOC Dao层:jdbcTemplate