Java序列化和反序列化

基础

序列化:将数据结构或对象转换成二进制串的过程。
反序列化:将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程。
调用writeObject()序列化一个对象,是将其写入磁盘,以后在程序再次调用readObject()时,根据wirteObject()磁盘的文件重新恢复那个对象。
Externalizable接口扩展Serializable,并增添两个方法:writeExternal()readExternal()。在序列化和重新装配的过程中,会自动调用这两个方法。
ExternalizableSerializable接口的子类,有时不希望序列化那么多,可以使用这个接口,这个接口的writeExternal()readExternal()可以指定序列化哪些属性。

算法

算法一般会按步骤做如下事情:

  1. 将对象实例相关的类元数据输出。
  2. 递归地输出类的超类描述直到不再有超类。
  3. 类元数据完以后,开始从最顶层的超类开始输出对象实例的实际数据值。
  4. 从上至下递归输出实例的数据。

线程中未捕获异常(UncaughtExceptionHandler)

Thread#run方法是不抛出任何检查型异常(Checked Exception),但是它自身却可能因为一个异常而被终止,导致这个线程的终结。
最麻烦的是,在线程中抛出的异常即使在主线程中使用try...catch也无法截获,因此可能导致一些问题出现,比如异常的时候无法回收一些系统资源,或者没有关闭当前的连接等等。
主线程之所以不处理子线程抛出的RuntimeException,是因为线程是异步的,子线程没结束,主线程可能已经结束了。
UncaughtExceptionHandler名字意味着处理未捕获的异常。更明确的说,它处理未捕获的运行时异常。

Java软引用、弱引用和虚引用

介绍

在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象。也就是说,只有对象处于可触及(reachable)状态,程序才能使用它。
从JDK 1.2版本开始,把对象的引用分为4种级别,从而使程序能更加灵活地控制对象的生命周期。
这4种级别由高到低依次为:强引用、软引用、弱引用和虚引用。


强引用(StrongReference)

强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。

软引用(SoftReference)

如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。
软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列(ReferenceQueue)中。软引用是在垃圾回收之后,才加入队列。

++i和i++简单分析

概念

++ii++如果用于自增这两个是没有区别的,都相当于i=i+1,但如果是通过这个来赋值如int a = i++;int b = ++j;那么a保存i原有值,b= j+1

Java异常机制

基本定义

异常的处理机制可以确保程序的健壮性,提高系统可用率。
在OO中提供的异常处理机制是提供代码健壮的强有力的方式。使用异常机制它能够降低错误处理代码的复杂度,如果不使用异常,那么就必须检查特定的错误,并在程序中的许多地方去处理它,而如果使用异常,那就不必在方法调用处进行检查,因为异常机制将保证能够捕获这个错误,并且,只需在一个地方处理错误,即所谓的异常处理程序中。这种方式不仅节约代码,而且把“概述在正常执行过程中做什么事”的代码和“出问题怎么办”的代码相分离。总之,与以前的错误处理方法相比,异常机制使代码的阅读、编写和调试工作更加井井有条。(摘自《Think in java 》)。
在《Think in java》中是这样定义异常的:异常情形是指阻止当前方法或者作用域继续执行的问题。在这里一定要明确一点,异常代码某种程度的错误,尽管Java有异常处理机制,但是我们不能以“正常”的眼光来看待异常,异常处理机制的原因就是告诉你:这里可能会或者已经产生错误,您的程序出现不正常的情况,可能会导致程序失败!
那么什么时候才会出现异常呢?只有在你当前的环境下程序无法正常运行下去,也就是说程序已经无法来正确解决问题,这时它所就会从当前环境中跳出,并抛出异常。
抛出异常后,它首先会做几件事。首先,它会使用new创建一个异常对象,然后在产生异常的位置终止程序,并且从当前环境中弹出对异常对象的引用。这时,异常处理机制就会接管程序,并开始寻找一个恰当的地方来继续执行程序,这个恰当的地方就是异常处理程序,它的任务就是将程序从错误状态恢复,以使程序要么换一种方法执行,要么继续执行下去。
总的来说异常处理机制就是当程序发生异常时,它强制终止程序运行,记录异常信息并将这些信息反馈给我们,由我们来确定是否处理异常。

异常体系

Java提供非常完美的异常处理机制,从下面这幅图可以看出,Throwable是Java语言中所有错误和异常的超类。它有两个子类:Error、Exception。

Java值传递

引用和对象在JVM内存中分布结构

在Java中,创建的实体对象在堆内存中开辟空间,而引用对象在栈内存中开辟空间。
基本数据类型、引用对象在栈内存中开辟空间。
引用对象存储的是实体对象的地址。

Thinking In Java 3版的第二章

Java里存放数据大都放在栈和堆里面。
引用和基本类型放在栈里面,因为这样会比较快。对象内容放在堆里面。
但是引用所在栈里面存放的是它对应的对象所在堆的地址,栈里的值,也就是堆的地址。
其实用引用作为参数说是值传递(堆中值的传递)和地址传递都可以。只是理解的角度不同。

Java递归生成XML

生成XML步骤:

  1. 生成根节点(root);
  2. 判断root节点,是否有子节点,生成root节点子节点(二级节点);
  3. 判断root节点的节点(二级节点)是否有子节点(三级节点);
  4. 3这2步骤就可以写成递归,在写一个判断是否有子节点的函数和中文格式化。

接口和抽象类的区别

概述

AbstractInterface是Java语言中对于抽象类定义进行支持的两种机制,正是由于这两种机制的存在,才赋予了Java强大的面向对象能力。AbstractInterface之间在对于抽象类定义的支持方面具有很大的相似性,甚至可以相互替换,因此很多开发者在进行抽象类定义时对于AbstractInterface的选择显得比较随意。
其实,两者之间还是有很大的区别的,对于它们的选择甚至反映出对于问题领域本质的理解、对于设计意图的理解是否正确、合理。

抽象类

把相同的东西提取出来, 就是为了重用。

接口

程序里面固化的契约, 是为了降低偶合。

理解抽象类(根据问题领域和设计意图)

AbstractInterface在Java语言中都是用来进行抽象类(本文中的抽象类并非从abstract class翻译而来,它表示的是一个抽象体,而是abstract class为Java语言中用于定义抽象类的一种方法,请读者注意区分)定义的,那么什么是抽象类,使用抽象类能带来什么好处呢?
在面向对象的概念中,知道所有的对象都是通过类来描绘的,但是反过来却不是这样。并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
抽象类往往用来表征在对问题领域进行分析(继承还是实现,继承是is a关系,表示是它的一个种类;实现是like a,像它,相当于功能的扩展)、设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象。
比如:进行一个图形编辑软件的开发,就会发现问题领域存在着圆、三角形这样一些具体概念,它们是不同的,但是它们又都属于形状这样一个概念,形状这个概念在问题领域是不存在的,它就是一个抽象概念。正是因为抽象的概念在问题领域没有对应的具体概念,所以用以表征抽象概念的抽象类是不能够实例化的,只能通过子类或者实现类去实例化。
在面向对象领域,抽象类主要用来进行类型隐藏。可以构造出一个固定的一组行为的抽象描述(抽象类或者接口),但是这组行为却能够有任意个可能的具体实现方式(子类继承或者实现)。这个抽象描述就是抽象类,而这一组任意个可能的具体实现则表现为所有可能的派生类。模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体(模块是子类),因此它可以是不允许修改的;同时,通过从这个抽象体派生,也可扩展此模块的行为功能。熟悉OCP的读者一定知道,为了能够实现面向对象设计的一个最核心的原则OCP(Open-Closed Principle),抽象类是其中的关键所在。

Java泛型

extend(上限)

上限用extends关键字声明,表示参数化的类型可能是所指定的类型,或者是此类型的子类。

1
2
3
4
5
6
7
public void upperBound(List<? extends Date> list, Date date)  
{
Date now = list.get(0);
System.out.println("now==>" + now);
//list.add(date); //这句话无法编译
list.add(null);//这句可以编译,因为null没有类型信息
}

为什么会无法编译呢,实际调用时传入的list可能是java.util.Date的某个子类的参数化类型。

1
2
3
4
5
6
public void testUpperBound()  
{
List<Timestamp> list = new ArrayList<Timestamp>();
Date date = new Date();
upperBound(list,date);
}

也就是说,现在upperBound方法中实际的listList<Timestamp>,向它添加一个Date类型,肯定是不行的。
相反,读取数据时,不管实际的list是什么类型,但可以知道它至少会返回一个Date类型,所以用foreach,get等没有问题。
可以使用泛型方法。

1
2
3
4
public <T extends Date> void upperBound2(List<T> list, T date)  //泛型的方法,需要泛型标识
{
list.add(date);
}

这里方法声明中的T作为一种泛型参数化信息,会存储在java字节码中,T的实际类型由调用时的参数决定的。

int,byte之间转换

基础知识

整形(int)

4个字节,占32位,1111,1111,1111,1111,1111,1111,1111,1111
整型int 其实不能说是去掉3个字节,只能说在读取数据的时候只读取了最低的一个字节里的数据而已,那另外的3个字节还在 ,只是没有读取里面的内容而已,因为高位都是0,可以不显示。
int转换成4个byte的时候,byte[4] b=b[0][1][2][3]需要移位,从1个字节开始移位。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×