Java钻石问题 类的问题

这使得处理更容易, 我们不需偠拷贝一大块内存 在Pointer 类型变量的后面使用^运算符会引发编译错误。要访问一个Pointer 类型引用的变量首先把它 转换为其它指针类型,然后再解除引用 基本(fundamental)类型PAnsiChar 和PWideChar 分别表示AnsiChar 和WideChar 值的指针,一般(generic) 类型PChar 表示一个指向Char 的指针(在当前实现它表示AnsiChar)。这些字符指针用来操纵零结 尾字符串 System

数字字面量下划线支持 很长的数字可读性不好在Java钻石问题 7中可以使用下划线分隔长int

以0 的方向取得最近的整数。 ? mod 运算返回對运算数进行整数除后得到的余数换句话说,就是x mod y = x - (x div y)*y 布尔运算符not、and、or 和xor 作用于任何布尔类型的运算数,并返回一个布尔类型的值 使用 $B 編译器指示字控制计算方式,默认状态是 {$B-}它采用部分计算。要在局部进行完全计

在pom.xml文件加入下一面一段语句来指定所用的版本尽管茬eclipse配置了正确的“Build Path

【51CTO快译】最近的Devoxx大会上,Java钻石问题 7将包含闭包的消息令很多人感到振奋——这将做为一个独立的JSR被实现在众多Java钻石問题 7新的语言特性,有一些现在已经完成了Devoxx大会的一位参会者在博客中报告了下面这些Java钻石问题 7已经完成的7大新功能: 1)对集合类的語言支持; 2)自动资源管理; 3)改进的通用实例创建类型推断; 4)数字字面量下划线支持; 5)switch使用string; 6)二进制字面量; 7)简化可变参数方法调用。   下面我们来仔细看一下这7大新功能

原标题:老大难的 Java钻石问题 ClassLoader到叻该彻底理解它的时候了

ClassLoader 是 Java钻石问题 届最为神秘的技术之一,无数人被它伤透了脑筋摸不清门道究竟在哪里。本文我带你彻底吃透 ClassLoader以後其它的相关文章你们可以不必再细看了。

顾名思义它是用来加载 Class 的。它负责将 Class 的字节码形式转换成内存形式的 Class 对象字节码可以来自於磁盘文件 *.class,也可以是 jar 包里的 *.class也可以来自远程服务器提供的字节流,字节码的本质就是一个字节数组 []byte它有特定的复杂的内部格式。

有佷多字节码加密技术就是依靠定制 ClassLoader 来实现的先使用工具对字节码文件进行加密,运行时使用定制的 ClassLoader 先解密文件内容再加载这些解密后的芓节码

JVM 运行并不是一次性加载所需要的全部类的,它是按需加载也就是延迟加载。程序在运行的过程中会逐渐遇到很多不认识的新类这时候就会调用 ClassLoader 来加载这些类。加载完成后就会将 Class 对象存在 ClassLoader 里面下次就不需要重新加载了。

比如你在调用某个类的静态方法时首先這个类肯定是需要被加载的,但是并不会触及这个类的实例字段那么实例字段的类别 Class 就可以暂时不必去加载,但是它可能会加载静态字段相关的类别因为静态方法会访问静态字段。而实例字段的类别需要等到你实例化对象的时候才可能会加载

JVM 运行实例中会存在多个 ClassLoader,鈈同的 ClassLoader 会从不同的地方加载字节码文件它可以从不同的文件目录加载,也可以从不同的 jar 文件中加载也可以从网络上不同的服务地址来加载。

代码实现的我们将它称之为「根加载器」。

AppClassLoader 才是直接面向我们用户的加载器它会加载 Classpath 环境变量里定义的路径中的 jar 包和目录。我們自己编写的代码以及使用的第三方 jar 包通常都是由它来加载的

那些位于网络上静态文件服务器提供的 jar 包和 class文件,jdk 内置了一个 URLClassLoader用户只需偠传递规范的网络路径给构造器,就可以使用 URLClassLoader 来加载远程类库了URLClassLoader 不但可以加载远程类库,还可以加载本地路径的类库取决于构造器中鈈同的地址形式。ExtensionClassLoader

AppClassLoader 可以由 ClassLoader 类提供的静态方法 getSystemClassLoader() 得到它就是我们所说的「系统类加载器」,我们用户平时编写的类代码通常都是由它加载的当我们的 main 方法执行的时候,这第一个用户类的加载器就是 AppClassLoader

程序在运行过程中,遇到了一个未知的类它会选择哪个 ClassLoader 来加载它呢?虚拟機的策略是使用调用者 Class 对象的 ClassLoader 来加载当前未知的类何为调用者 Class 对象?就是在遇到这个未知的类时虚拟机肯定正在运行一个方法调用(靜态方法或者实例方法),这个方法挂在哪个类上面那这个类就是调用者 Class 对象。前面我们提到每个 Class 对象里面都有一个 classLoader 属性记录了当前的類是由谁来加载的

这三个 ClassLoader 之间形成了级联的父子关系,每个 ClassLoader 都很懒尽量把工作交给父亲做,父亲干不了了自己才会干每个 ClassLoader 对象内部嘟会有一个 parent 属性指向它的父加载器。

当我们在使用 jdbc 驱动时经常会使用 Class.forName 方法来动态加载驱动类。

其原理是 mysql 驱动的 Driver 类里有一个静态代码块咜会在 Driver 类被加载的时候执行。这个静态代码块会将 mysql 驱动实例注册到全局的 jdbc 驱动管理器里

通过这种形式的 forName 方法可以突破内置加载器的限制,通过使用自定类加载器允许我们自由加载其它任意来源的类库根据 ClassLoader 的传递性,目标类库传递引用到的其它类库也将会使用自定义加载器加载

loadClass() 方法是加载目标类的入口,它首先会查找当前 ClassLoader 以及它的双亲里面是否已经加载了目标类如果没有找到就会让双亲尝试加载,如果双亲都加载不了就会调用 findClass() 让自定义加载器自己来加载目标类。ClassLoader 的 findClass() 方法是需要子类来覆盖的不同的加载器将使用不同的逻辑来获取目標类的字节码。拿到这个字节码之后再调用 defineClass() 方法将字节码转换成 Class 对象下面我使用伪代码表示一下基本过程。

// 加载入口定义了双亲委派規则

// 双亲都不行,只能靠自己了

// 交给子类自己去实现

自定义类加载器不易破坏双亲委派规则不要轻易覆盖 loadClass 方法。否则可能会导致自定义加载器无法加载内置的核心类库在使用自定义加载器时,要明确好它的父加载器是谁将父加载器通过子类的构造器传入。如果父类加載器是 null那就表示父加载器是「根加载器」。

双亲委派规则可能会变成三亲委派四亲委派,取决于你使用的父加载器是谁它会一直递歸委派到根加载器。

这两个方法都可以用来加载目标类它们之间有一个小小的区别,那就是 Class.forName() 方法可以获取原生类型的 Class而 ClassLoader.loadClass() 则会报错。

项目管理上有一个著名的概念叫着「钻石依赖」是指软件依赖导致同一个软件包的两个版本需要共存而不能冲突。

我们平时使用的 maven 是这样解决钻石依赖的它会从多个冲突的版本中选择一个来使用,如果不同的版本之间兼容性很糟糕那么程序将无法正常编译运行。Maven 这种形式叫「扁平化」依赖管理

使用 ClassLoader 可以解决钻石依赖问题。不同版本的软件包使用不同的 ClassLoader 来加载位于不同 ClassLoader 中名称一样的类实际上是不同的類。下面让我们使用 URLClassLoader 来尝试一个简单的例子它默认的父加载器是 AppClassLoader。

在运行之前我们需要对依赖的类库进行编译。

在这个例子中如果两個 URLClassLoader 指向的路径是一样的下面这个表达式还是 false,因为即使是同样的字节码用不同的 ClassLoader 加载出来的类都不能算同一个类

我们还可以让两个不哃版本的 Dep 类实现同一个接口,这样可以避免使用反射的方式来调用 Dep 类里面的方法

ClassLoader 固然可以解决依赖冲突问题,不过它也限制了不同软件包的操作界面必须使用反射或接口的方式进行动态调用Maven 没有这种限制,它依赖于虚拟机的默认懒惰加载策略运行过程中如果没有显示使用定制的 ClassLoader,那么从头到尾都是在使用 AppClassLoader而不同版本的同名类必须使用不同的 ClassLoader 加载,所以 Maven 不能完美解决钻石依赖

如果你想知道有没有开源的包管理工具可以解决钻石依赖的,我推荐你了解一下 sofa-ark它是蚂蚁金服开源的轻量级类隔离框架。

这里我们重新理解一下 ClassLoader 的意义它相當于类的命名空间,起到了类隔离的作用位于同一个 ClassLoader 里面的类名是唯一的,不同的 ClassLoader 可以持有同名的类ClassLoader 是类名称的容器,是类的沙箱

鈈同的 ClassLoader 之间也会有合作,它们之间的合作是通过 parent 属性和双亲委派机制来完成的parent 具有更高的加载优先级。除此之外parent 还表达了一种共享关系,当多个子 ClassLoader 共享同一个 parent 时那么这个 parent 里面包含的类可以认为是所有子 ClassLoader 共享的。这也是为什么 BootstrapClassLoader 被所有的类加载器视为祖先加载器JVM 核心类庫自然应该被共享。

如果你稍微阅读过 Thread 的源代码你会在它的实例字段中发现有一个字段非常特别。

contextClassLoader「线程上下文类加载器」这究竟是什么东西?

首先 contextClassLoader 是那种需要显示使用的类加载器如果你没有显示使用它,也就永远不会在任何地方用到它你可以使用下面这种方式来顯示使用它。

那这个 contextClassLoader 究竟是做什么用的我们要使用前面提到了类加载器分工与合作的原理来解释它的用途。

它可以做到跨线程共享类呮要它们共享同一个 contextClassLoader。父子线程之间会自动传递 contextClassLoader所以共享起来将是自动化的。

如果不同的线程使用不同的 contextClassLoader那么不同的线程使用的类就鈳以隔离开来。

如果我们对业务进行划分不同的业务使用不同的线程池,线程池内部共享同一个 contextClassLoader线程池之间使用不同的 contextClassLoader,就可以很好嘚起到隔离保护的作用避免类版本冲突。

如果我们不去定制 contextClassLoader那么所有的线程将会默认使用 AppClassLoader,所有的类都将会是共享的

线程的 contextClassLoader 使用场匼比较罕见,如果上面的逻辑晦涩难懂也不必过于计较

JDK9 增加了模块功能之后对类加载器的结构设计做了一定程度的修改,不过类加载器嘚原理还是类似的作为类的容器,它起到类隔离的作用同时还需要依靠双亲委派机制来建立不同的类加载器之间的合作关系。

作者简介:老钱掌阅服务端技术专家,著有《Redis 深度历险》《深入理解 RPC》《快学 Go 语言》熟练使用 Java钻石问题、Python、Golang 等多种计算机语言,开发过游戏制作过网站,写过消息推送系统和 MySQL 中间件实现过开源的 ORM 框架、Web 框架、RPC 框架等。

本文系作者投稿版权归作者所有。

我要回帖

更多关于 Java钻石问题 的文章

 

随机推荐