Java学习笔记

Java学习笔记记录:面向对象,虚拟机,内存管理,垃圾回收,类加载等内容

面向对象的4个特性:抽象,封装、继承、多态
封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。实现多态,有二种方式,覆盖(override),重载(overload)
Java虚拟机(Java Virtual Machine,简称 JVM)。编写的Java源码,编译后会生成一种 .class 文件,称为字节码文件。Java虚拟机就是负责将字节码文件翻译成特定平台下的机器码然后运行。Java三大虚拟机:HotSpot, JRockit, IBM J9, Apache Harmony, Dalvik, KVM, CDC/CLDC HotSpot
StringBuilder类和StringBuffer类:主要区别在于StringBuffer类的方法是多线程安全的,而StringBuilder不是线程安全的,相比而言,StringBuilder类会略微快一点。
变量的作用域分为四个级别:类级、对象实例级、方法级、块级。类级变量又称全局级变量或静态变量,在类定义后就已经存在,占用内存空间,可以通过类名来访问,不需要实例化。方法级变量就是在方法内部定义的变量,就是局部变量。块级变量就是定义在一个块内部的变量,变量的生存周期就是这个块。
this 关键字用来表示当前对象本身,或当前类的一个实例,通过 this 可以调用本对象的所有方法和属性
方法重载:在Java中,同一个类中的多个方法可以有相同的名字,只要它们的参数列表不同就可以,这被称为方法重载(method overloading)
覆盖和重载的不同:

  • 方法覆盖要求参数列表必须一致,而方法重载要求参数列表必须不一致。
  • 方法覆盖要求返回类型必须一致,方法重载对此没有要求。
  • 方法覆盖只能用于子类覆盖父类的方法,方法重载用于同一个类中的所有方法(包括从父类中继承而来的方法)。
  • 方法覆盖对方法的访问权限和抛出的异常有特殊的要求,而方法重载在这方面没有任何限制。
  • 父类的一个方法只能被子类覆盖一次,而一个方法可以在所有的类中可以被重载多次。

多态是指一个事物有不同的表现形式或形态。多态存在的三个必要条件:要有继承、要有重写、父类变量引用子类对象。

多态调用: 1) 编译器查看对象的声明类型和方法名。2) 接下来,编泽器将检查调用方法时提供的参数签名。3) 如果方法的修饰符是private、static、final(static和final将在后续讲解),或者是构造方法,那么编译器将可以准确地知道应该调用哪个方法,我们将这种调用方式 称为静态绑定(static binding)。与此对应的是,调用的方法依赖于对象的实际类型, 并在运行时实现动态绑。4)当程序运行,并且釆用动态绑定调用方法时,JVM一定会调用与 obj 所引用对象的实际类型最合适的那个类的方法。
static 的变量是在类装载的时候就会被初始化。也就是说,只要类被装载,不管你是否使用了这个static 变量,它都会被初始化。
静态初始器(Static Initializer)是一个存在于类中、方法外面的静态块。静态初始器仅仅在类装载的时候(第一次使用类的时候)执行一次,往往用来初始化静态变量。
静态导入用来导入类的静态变量和静态方法,导入后,可以在当前类中直接用方法名调用静态方法。
final:在Java中,被 static 或 private 修饰的方法会被隐式的声明为 final,因为动态绑定没有意义。
  • final 修饰的类不能被继承。
  • final 修饰的方法不能被子类重写。
  • final 修饰的变量(成员变量或局部变量)即成为常量,只能赋值一次。
  • final 修饰的成员变量必须在声明的同时赋值,如果在声明的时候没有赋值,那么只有 一次赋值的机会,而且只能在构造方法中显式赋值,然后才能使用。
  • final 修饰的局部变量可以只声明不赋值,然后再进行一次性的赋值。

类与类之间的关系

继承:一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力。
a4905b4c-6001-4ded-95b4-c357b922b1ba
实现:实现指的是一个class类实现interface接口(可以是多个)的功能
378207f5-6e21-47b0-ac9a-7581e987cccf
依赖:简单的理解,依赖就是一个类A使用到了另一个类B,而这种使用关系是具有偶然性的、临时性的、非常弱的,但是类B的变化会影响到类A。
015b20e4-b587-4656-b29a-cc05f5f374b3
关联:关联体现的是两个类之间语义级别的一种强依赖关系,表现在代码层面,为被关联类B以类的属性形式出现在关联类A中,也可能是关联类A引用了一个类型为被关联类B的全局变量。
de401be2-ef1b-40ba-9789-780d0db959d0
聚合:聚合是关联关系的一种特例,它体现的是整体与部分的关系,即has-a的关系。此时整体与部分之间是可分离的,它们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享。
37adcfd4-a9e9-40eb-b6db-12efc456be14
组合:组合也是关联关系的一种特例,它体现的是一种contains-a的关系,这种关系比聚合更强,也称为强聚合。它同样体现整体与部分间的关系,但此时整体与部分是不可分的,整体的生命周期结束也就意味着部分的生命周期结束
590628c7-ed12-4a04-8869-3e2398b1bb9e
在Java中,数据等价的基本含义是指两个数据的值相等。在通过 equals() 和“==”进行比较的时候,引用类型数据比较的是引用,即内存地址,基本数据类型比较的是值。

hashCode() 方法主要用来在集合中实现快速查找等操作,也可以用于对象的比较。

  • 在同一个应用程序执行期间,对同一个对象调用 hashCode(),必须返回相同的整数结果——前提是 equals() 所比较的信息都不曾被改动过。至于同一个应用程序在不同执行期所得的调用结果,无需一致。
  • 如果两个对象被 equals() 方法视为相等,那么对这两个对象调用 hashCode() 必须获得相同的整数结果。
  • 如果两个对象被 equals() 方法视为不相等,那么对这两个对象调用 hashCode() 不必产生不同的整数结果。然而程序员应该意识到,对不同对象产生不同的整数结果,有可能提升hashTable的效率。

abstract:使用 abstract 修饰符来表示抽象方法和抽象类。包含抽象方法的类一定是抽象类,抽象类不一定包含抽象方法。

接口中声明的成员变量默认都是 public static final 的,必须显示的初始化。因而在常量声明时可以省略这些修饰符。接口是若干常量和抽象方法的集合。接口中只能定义抽象方法,这些方法默认为 public abstract 的。接口中没有构造方法,不能被实例化。一个接口不实现另一个接口,但可以继承多个其他接口。
泛型:泛型类定义时类名后面多出了 <T1, T2>,T1, T2 是自定义的标识符,也是参数,用来传递数据的类型,而不是数据的值,我们称之为类型参数。例如:public class Pass<T1,T2>{private T1 t1;public T2 t2;} Pass intPass = new Pass<Integer,String>(); 还可以泛型方法或泛型接口。可以使用<T extends Number>来限制泛型必须使用数字类型,表示 T 只接受 Number 及其子类,传入其他类型的数据会报错。extends,后面可以是类也可以是接口。如果是类,只能有一个;但是接口可以有多个,并以“&”分隔,例如 <T extends Interface1 & Interface2>
泛型通配符?可以设定参数类型的上下限。例如定义调用Pass的方法:public void printInt(Pass<? extends Integer,? super String>){} ,系统调用时可限定该方法调用的参数是否符合要求。
异常:所有异常类型都是内置类Throwable的子类。Throwable下面的是两个把异常分成两个不同分支的子类。一个分支是Exception。另一类分支由Error作为顶层,Error定义了在通常环境下不希望被程序捕获的异常。Error类型的异常用于Java运行时系统来显示与运行时系统本身有关的错误。堆栈溢出是这种错误的一例。
try catch执行顺序:try内部,catch内部,finally内部,return
Java中的异常分为受检查的异常和不受检查的异常。(1)受检查的异常:这种在编译时被强制检查的异常称为”受检查的异常”。即在方法的声明中声明的异常。(2)不受检查的异常:在方法的声明中没有声明,但在方法的运行过程中发生的各种异常被称为”不被检查的异常”。这种异常是错误,会被自动捕获。

线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
线程状态:运行(running),挂起(suspend),恢复(resume),阻塞(block),终止(terminate)
为创建一个新的线程,你的程序必须扩展Thread 或实现Runnable接口。
20150408002007838
大多数情况,通过实例化一个Thread对象来创建一个线程。Java定义了两种方式:实现Runnable 接口;可以继承Thread类。

当两个或两个以上的线程需要共享资源,它们需要某种方法来确定资源在某一刻仅被一个线程占用。达到此目的的过程叫做同步(synchronization)。同步方法(synchronized修饰方法),同步代码块(synchronized代码块,使用特殊域变量(Volatile)实现线程同步,使用重入锁实现线程同步(Lock接口类),使用局部变量实现线程同步(ThreadLocal类)
:把不同类型的输入输出源抽象为流,其中输入和输出的数据称为数据流(Data Stream)
 Java提供了两种类型的输入输出流:一种是面向字节的流,数据的处理以字节为基本单位(Stream);另一种是面向字符的流,用于字符数据的处理(Reader,Writer)。
向量和数组相似,都可以保存一组数据(数据列表)。但是数组的大小是固定的,一旦指定,就不能改变,而向量却提供了一种类似于“动态数组”的功能,向量与数组的重要区别之一就是向量的容量是可变的。
JVM内存管理
根据《Java虚拟机规范(第二版)》
02120142_QQ6v
程序计数器:线程私有。当前线程所执行字节码的行号指示器。虚拟机概念模型中,通过改变计数器的值来读取下一条需要执行的字节码指令。
虚拟机栈:线程私有。方法执行时创建栈帧,存储局部变量表(只存储基本数据类型和对象引用类型)、操作栈、动态链接、方法出口等。
本地方法栈:线程私有。为虚拟机使用到的Native方法服务。
堆:线程共享。几乎所有的对象实例及数组都要在堆上分配。垃圾收集器管理的主要区域。
方法区:线程共享。存储已被虚拟机加载的类信息、常量、静态常量、即时编译器编译后的代码等数据。
运行时常量池(Runtime Constant Pool):方法区的一部分。存放编译期生成的各种字面量和符号引用。
直接内存(Direct Memory):不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域。NIO(New Input/Output)类,引入了基于通道(Channel)与缓冲区(Buffer)的I/O方式,可是有Native函数库直接分配堆外内存,通过存储在堆中的DirectByteBuffer对象作为该内存的引用进行操作。
永久区(Permanent):Permanet Generation(HotSpot8已移除该区域,新增MetaSpace) to store the JVM’s reflective data such as class and method objects
对象访问:句柄访问方式或直接指针访问方式。
GC内存回收
判断对象存活:
微软COM, squirrel等使用引用计数算法(Reference Counting);Java, C#使用根搜索算法(GC Roots Tracing)
GC Roots对象包括:虚拟机栈中引用的对象;方法区中的类静态属性引用的对象;方法区中的常量引用的对象;本地方法栈中JNI(Native方法)的引用的对象
强引用Strong Reference;软引用Soft Reference;弱引用Weak Reference;虚引用Phantom Reference;
回收方法区,判断无用的类:该类所以实例已回收;加载该类的ClassLoader已回收;该类对应的java.lang.Class类对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法;
垃圾收集算法
标记-清除算法Mark-Sweep:标记出需要回收的对象,标记完成后统一回收所有被标记的对象;
复制算法Copying:分配一个Eden两个Survivor,使用Eden和一块Survivor空间,回收时将两个区域还存活的对象一次拷贝到另一块Survivor空间;
标记-整理算法Mark-Compact:将存活对象向一端移动;
分代收集算法Generational Collection:根据对象存活时间划分几块,一般新生代、老年代,每块选用最合适算法。新生代复制算法,老年代对象存活率高选用标记-清除或标记-整理算法;
垃圾收集器
HotSpot JVM1.6的垃圾收集器
20150856_bMmR
Serial:简单高效,stop-the-world方式工作,虚拟机运行在client模式下的新生代默认收集器 ,mark-sweep算法
ParNew:Serial的多线程版本,虚拟机运行在server模式下的新生代首选收集器,mark-sweep算法
Parallel Scavenge:使用复制算法copying并行多线程收集器,目标是达到一个可控制的吞吐量
Serial Old:Serial收集器的老年代版本 ,mark-sweep算法
Parallel Old:Parallel Scavenge的老年代版本
CMS(Concurrent Mark Sweep):以获取最短回收停顿时间为目标的收集器 ,mark-sweep算法
G1(Garbage First):基于 Mark-Compact算法,可准确控制停顿,将整个堆划分多个大小固定的独立区域,自动跟踪各区域垃圾堆积情况,在后台维护一个优先列表,根据允许的收集时间优先回收垃圾最多的区域
堆结构:Young Generation(Eden Space, Survivor Space(from, to)), Tenured Generation(Virtual Space)
Minor GC:新生代GC,发生在新生代的垃圾收集动作,非常频繁,速度比较快
Major GC/Full GC:老年代GC,发生在老年代的GC,经常伴随Minor GC,速度慢
内存分配:(eclipse JVM内存分析工具MAT)
对象优先在新生代Eden区分配,空间不够时虚拟机发起一次Minor GC
大对象直接进入老年代
长期存活的对象将进入老年代,Eden出生,第一次Minor GC后存活且能被Survivor容纳进入Survivor,Survivor中Minor GC一次年龄增1,达到指定年限后晋升老年代
Survivor空间相同年龄所有对象大小总和大于Survivor空间一半,年龄大于等于该年龄的对象直接进入老年代
空间分配担保,发生Minor GC时检测之前每次晋升到老年代的平均大小是否大于老年代的剩余空间大小,如果大于会进行Full GC,如果小于则查看HandelPromotionFailure设置是否允许担保失败,如果不允许则进行Full GC
虚拟机性能监控与故障处理工具
jps: JVM Process Status Tool,查看正在运行的虚拟机进程,格式jps -option,参数-qlvm;
jstat: JVM Statistics Monitoring Tool,监视虚拟机运行状态,如类装载、内存、垃圾收集、JIT编译等运行数据;
jinfo: Configuration Info for Java,查看JVM参数配置信息;
jmap: Memory Map for Java,生成堆内存快照dump文件(后缀bin);
jhat: JVM Heap Dump Browser,分析生成的堆内存快照;(专业分析dump文件工具VisualVM, Eclipse Memory Analyzer, IBM HeapAnalyzer)
jstack: Stack Trace for Java,虚拟机堆栈跟踪(线程跟踪);
JDK可视化工具JConsole, VisualVM(All-in-One Java Troubleshooting Tool)
BTrace: 不停止虚拟机的前提下性能跟踪、代码诊断及动态修改代码(如日志语句)工具
对于用户交互性强、对停顿时间敏感的系统,可以给java虚拟机分配超大堆的前提是有把握把Full GC频率控制的足够低。
控制Full GC频率的关键是看应用中绝大多数对象是否符合朝生夕灭的原则,即大多数对象的生存空间不应该太长,尤其不能产生成批量的、长生存时间的大对象,这样才能保证老年代空间的稳定。
若干虚拟机建立逻辑集群:启动多个应用服务器进程,给每个服务器进程分配不同端口,在前端搭建一个负载均衡器,以反向代理的方式分配访问请求。
Class类文件结构
ClassFile {
u4             magic;
u2             minor_version;
u2             major_version;
u2             constant_pool_count;
cp_info        constant_pool[constant_pool_count-1];
u2             access_flags;
u2             this_class;
u2             super_class;
u2             interfaces_count;
u2             interfaces[interfaces_count];
u2             fields_count;
field_info     fields[fields_count];
u2             methods_count;
method_info    methods[methods_count];
u2             attributes_count;
attribute_info attributes[attributes_count];
}
一组以8位字节为基础单位的二进制流,class文件结构只有两种数据类型:无符号数和表。无符号数属于基本的数据类型,描述数字、索引引用、数据值、按UTF-8编码构成字符串值。表由多个无符号数或其他表作为数据项构成的符合数据类型
每个class文件头四个字节称为魔数(Magic Number),唯一作用是确定该文件是否为一个能被虚拟机接受的class文件。
第5,6字节是次版本号Minor Version,第7,8字节是主版本号Major Version。
主次版本号后是常量池(存放字面量Literal和符号引用Symbolic References)入口。
常量池之后是2个字节的访问标志(Access_flags)。
之后是类索引this_class、父类索引super_class、接口索引集合interfaces。
字段表用于描述接口或类中声明的变量。字段包括了类级变量或实例级变量,不包括方法内声明的变量。
方法表集合依次包括访问标志、名称索引、描述符索引、属性表集合几项。方法内的代码编译后存放在方法属性表中code属性中。
属性表attribute_info。方法的显示异常处理表实际是java代码的一部分,编译器使用异常表而不是简单的跳转命令来实现java异常及finally处理机制
对非static类型的变量(实例变量)赋值是在实例构造器<init>方法中进行的,对类变量有赋值在类构造器<clinit>方法中进行或使用ConstantValue属性赋值两种选择。
类加载机制
包含加载Loading、连接Linking(验证Verification、准备Preparation、解析Resolution)、初始化Initialization、使用Using、卸载Unloading7个阶段。
虚拟机规定需要立即初始化:1)遇到new, getstatic, putstatic, invokestatic时;2)使用java.lang.reflect包的方法对类进行反射调用时;3)初始化一个类时若父类未初始化须初始化其父类;4)虚拟机启动时会先启动父类(含main方法)初始化。其中1)主要是new实例化对象,读取/设置类静态字段,调用类静态方法。不包含被final修饰、已在编译期把结果放入常量池的静态字段。
加载:通过类全限定名来获取定义此类的二进制字节流;将该字节流所代表的静态存储结构转化为方法区的运行时数据结构;在Java堆中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口。
验证:确保Class文件的字节流中包含的信息符合当前虚拟机的要求并且不会危害虚拟机自身的安全。
准备:正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配。
解析:虚拟机将常量池内的符号引用替换为直接引用的过程。
符号引用(Symbolic References),直接引用(Direct References)
初始化:初始化阶段是执行类构造器<clinit>()方法的过程。
类加载器:类加载阶段中“通过一个类的全限定名来获取描述此类的二进制字节流”放到java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称为类加载器。
==============
reference:
java编程思想(第四版)
《Effective Java》、《Java编程思想》以及《重构:改善既有代码质量》是Java程序员必看书籍
《Understanding the JVM Advanced Features and Best Practices》(深入理解Java虚拟机JVM高级特性与最佳实践)
JDK源码:OpenJDK, Apache Harmony
《Java虚拟机规范》
Troubleshooting Guide for Java SE 6 with HotSpot VM http://www.oracle.com/technetwork/java/javase/toc-135973.html
This entry was posted in Java. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s