一篇文章让你真正了解Java

280 阅读10分钟

“你学习一门技术的最佳时机是三年前,其次是现在。”这句话对于哪一种行业都很适用,如果你已经学习过Java,那么恭喜你你很有先见之明,如果你并不了解Java,这篇文章带你快速掌握Java的几个核心知识点。

一、Java特点

1、 面向对象

尽管受到其前辈的影响,但Java没被设计成兼容其他语言源代码的程序。这允许Java开发组自由地从零开始。这样做的一个结果是,Java语言可以更直接、更易用、更实际的接近对象。Java的对象模型既简单又容易扩展,对于简单数据类型,例如整数,它保持了高性能,但不是对象。

2、 解释性和高性能

字节码可以在提供Java虚拟机(JVM)的任何一种系统上被解释执行。早先的许多尝试解决跨平台的方案对性能要求都很高。其他解释执行的语言系统,如BASIC,Tcl,PERL都有无法克服的性能缺陷。然而,Java却可以在非常低档的CPU上顺利运行。前面已解释过,Java确实是一种解释性语言,Java的字节码经过仔细设计,因而很容易便能使用JIT编译技术将字节码直接转换成高性能的本机代码。Java运行时系统在提供这个特性的同时仍具有平台独立性,因而“高效且跨平台”对Java来说不再矛盾。

3、 动态

Java程序带有多种的运行时类型信息,用于在运行时校验和解决对象访问问题。这使得在一种安全、有效的方式下动态地连接代码成为可能,对小应用程序环境的健壮性也十分重要,因为在运行时系统中,字节码内的小段程序可以动态地被更新。

二、面向对象的编程

2.1 抽象

面向对象编程的一个实质性的要素是抽象。人们通过抽象(abstraction)处理复杂性。

例如,人们不会把一辆汽车想象成由几万个互相独立的部分所组成的一套装置,而是把汽车想成一个具有自己独特行为的对象。这种抽象使人们可以很容易地将一辆汽车开到杂货店,而不会因组成汽车各部分零件过于复杂而不知所措。传统的面向过程程序的数据经过抽象可用若干个组成对象表示,程序中的过程步骤可看成是在这些对象之间进行消息收集。这样,每一个对象都有它自己的独特行为特征。你可以把这些对象当作具体的实体,让它们对告诉它们做什么事的消息作出反应。这是面向对象编程的本质。面向对象的概念是Java 的核心。

2.2面向对象编程的3个原则

所有面向对象的编程语言都提供帮助你实现面向对象模型的机制,这些机制是封装,继承及多态性。现在让我们来看一下它们的概念。

封装

封装(Encapsulation)是将代码及其处理的数据绑定在一起的一种编程机制,该机制保证了程序和数据都不受外部干扰且不被误用。理解封装性的一个方法就是把它想成一个黑匣子,它可以阻止在外部定义的代码随意访问内部代码和数据。对黑匣子内代码和数据的访问通过一个适当定义的接口严格控制。

Java封装的基本单元是类。尽管类将在以后章节详细介绍。现在仍有必要对它作一下简单的讨论。类是一种逻辑结构,而对象是真正存在的物理实体。如果你对C/C++熟悉,可以这样理解:Java程序员所称的方法,就是C/C++程序员所称的函数(function)。在完全用Java编写的程序中,方法定义如何使用成员变量。这意味着一个类的行为和接口是通过方法来定义的,类这些方法对它的实例数据进行操作。

继承

继承(Inheritance)是一个对象获得另一个对象的属性的过程。继承很重要,因为它支持了按层分类的概念。使用了继承,一个对象就只需定义使它在所属类中独一无二的属性即可,因为它可以从它的父类那儿继承所有的通用属性。

继承性与封装性相互作用。如果一个给定的类封装了一些属性,那么它的任何子类将具有同样的属性,而且还添加了子类自己特有的属性。这是面向对象的程序在复杂性上呈线性而非几何性增长的一个关键概念。新的子类继承它的所有祖先的所有属性。它不与系统中其余的多数代码产生无法预料的相互作用。

多态性

多态性是允许一个接口被多个同类动作使用的特性,具体使用哪个动作与应用场合有关,下面我们以一个后进先出型堆栈为例进行说明。假设你有一个程序,需要3种不同类型的堆栈。一个堆栈用于整数值,一个用于浮点数值,一个用于字符。尽管堆栈中存储的数据类型不同,但实现每个栈的算法是一样的。如果用一种非面向对象的语言,你就要创建3个不同的堆栈程序,每个程序一个名字。但是,如果使用Java,由于它具有多态性,你就可以创建一个通用的堆栈程序集,它们共享相同的名称。多态性的概念经常被说成是“一个接口,多种方法”。这意味着可以为一组相关的动作设计一个通用的接口。多态性允许同一个接口被必于同一类的多个动作使用,这样就降低了程序的复杂性。选择应用于每一种情形的特定的动作(specific action)(即方法)是编译器的任务,程序员无需手工进行选择。你只需记住并且使用通用接口即可。

三、 hashmap hashtable

HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。HashMap 继承于AbstractMap,实现了Map、Cloneable、java.io.Serializable接口。

HashMap 的实现不是同步的,这意味着它不是线程安全的。它的key、value都可以为null。此外,HashMap中的映射不是有序的。HashMap的实例有两个参数影响其性能:“初始容量” 和 “加载因子”。容量是哈希表中桶的数量,初始容量 只是哈希表在创建时的容量。加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 rehash 操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。

四. jvm 内存模型

程序计数器

每个线程有要有一个独立的程序计数器,记录下一条要运行的指令。线程私有的内存区域。如果执行的是JAVA方法,计数器记录正在执行的java字节码地址,如果执行的是native方法,则计数器为空。

虚拟机栈

线程私有的,与线程在同一时间创建。管理JAVA方法执行的内存模型。

本地方法区

和虚拟机栈功能相似,但管理的不是JAVA方法,是本地方法

方法区

线程共享的,用于存放被虚拟机加载的类的元数据信息:如常量、静态变量、即时编译器编译后的代码。也称为永久代。

JAVA 堆

线程共享的,存放所有对象实例和数组。垃圾回收的主要区域。可以分为新生代和老年代(tenured)。

五、 运行时类型信息(RTTI + 反射)

概念—RTTI:运行时类型信息使得你可以在程序运行时发现和使用类型信息。

使用方式:Java是如何让我们在运行时识别对象和类的信息的,主要有两种方式(还有辅助的第三种方式,见下描述):

一种是“传统的”RTTI,它假定我们在编译时已经知道了所有的类型,比如Shape s = (Shape)s1;另一种是“反射”机制,它运行我们在运行时发现和使用类的信息,即使用Class.forName()。其实还有第三种形式,就是关键字instanceof,它返回一个bool值,它保持了类型的概念,它指的是“你是这个类吗?或者你是这个类的派生类吗?”。而如果用==或equals比较实际的Class对象,就没有考虑继承—它或者是这个确切的类型,或者不是。

工作原理

要理解RTTI在Java中的工作原理,首先必须知道类型信息在运行时是如何表示的,这项工作是由称为Class对象的特殊对象完成的,它包含了与类有关的信息。Java送Class对象来执行其RTTI,使用类加载器的子系统实现。

无论何时,只要你想在运行时使用类型信息,就必须首先获得对恰当的Class对象的引用

反射与RTTI的区别

RTTI与反射之间真正的区别只在于:对RTTI来说,编译器在编译时打开和检查.class文件(也就是可以用普通方法调用对象的所有方法);而对于反射机制来说,.class文件在编译时是不可获取的,所以是在运行时打开和检查.class文件。

六. 即时编译器技术 — JIT

Java虚拟机中有许多附加技术用以提升速度,尤其是与加载器操作相关的,被称为“即时”(Just-In-Time,JIT)编译器的技术。这种技术可以把程序全部或部分翻译成本地机器码(这本来是JVM的工作),程序运行速度因此得以提升。当需要装载某个类时,编译器会先找到其.class文件,然后将该类的字节码装入内存。此时,有两种方案可供选择:

(1)一种就是让即时编译器编译所有代码。

(2)另一种做法称为惰性评估(lazy evaluation),意思是即时编译器只在必要的时候才编译代码,这样,从不会被执行的代码也许就压根不会被JIT所编译。

七、 final关键字

对final关键字的误解

当final修饰的是基本数据类型时,它指的是数值恒定不变(就是编译期常量,如果是static final修饰,则强调只有一份),而对对象引用而不是基本类型运用final时,其含义会有一点令人迷惑,因为用于对象引用时,final使引用恒定不变,一旦引用被初始化指向一个对象,就无法再把它指向另一个对象。然而,对象其自身却是可以被修改的,Java并未提供使任何对象恒定不变的途径(但可以自己编写类以取得使对象恒定不变的效果),这一限制同样适用数组,它也是对象。