/Articles

ArrayList的动态数组扩容与收缩

最近在复习线性数据结构的时候,顺便拿了Java的ArrayList做了分析比较。ArrayList的本质是动态数组,当新加如的数据逼近数组容量时,自动扩容,删除数据后,自动收缩。这么做,虽然效率比较低,但是一定程度上能优化内存分配。 自动扩容 ArrayList内部有个elementData的Object数组,初始容量在Java 8中为10。当第11个数据需要添加时候,扩容1.5倍。这个1.5倍在ArrayList很巧妙,用了整数移位的方式。 int newCapacity = oldCapacity + (oldCapacity >> 1); 完整的扩容代码是:...

JDBC驱动的类加载-SPI新瓶装旧酒

今天偶尔看JDBC驱动的问题,发现了一个有意思的事情,就是Class.forName()和SPI动态注册,原理居然是一样的,新瓶装旧酒。 老派的手工注册 JDBC很早以来一直采用驱动管理显式的注册方法,就是驱动类的使用者,显式地使用Class.forName()加载驱动类。 Class.forName("oracle.jdbc.driver.OracleDriver"); Connection con=DriverManager.getConnection(...); Class.forName()的作用是初始化具体driver类的用static显式声明的静态初始化块。按照JDBC...

Java异常链与故障诊断

产品的故障诊断,都需要依靠日志文件中的异常栈来判断。很多时候,我不得不需要客户提供真实的数据库,一点点去调试。而客户提供的日志文件,很多时候都没有参考价值。原因就在于我们的代码中,Exception捕获的处理方式太随意,不注重后续跟踪。 Exception处理很容易被程序员忽略,大家都不注重,以为只要抛出来就对了,因为觉得它太简单了,too simple 所以 nobody cares。我把下面例子一讲,就会明白,道理如此简单,作用如此重要。 错误模拟 自定义一个AppException,再派生两个子类。 package cn.xwiz.test.exception; public...

Java加密套件强度限制引起的SSL handshake_failure

今天为客户解决了一个奇葩的SSL问题。通过Java代码使用HttpURLConnection去连接https系统时候总是报错handshake_failure。而使用浏览器访问一切正常。记录下诊断的过程。 HttpURLConnection的调用非常简单。 HttpURLConnection connection = (HttpURLConnection)m_url.openConnection(); connection.setRequestMethod("GET"); connection.setAllowUserInteraction(false);...

Java证书安装及多个Java应用间的根证书交叉信任

在一套Java产品环境中,常常会存在不同的Java应用,相互之间会通过HttpClient模拟HTTP访问对方,这时就涉及到浏览器所不会用到的特殊的过程:根证书的交叉信任。最后面会讲为什么浏览器和Java应用服务器的通信不需要交叉导入根证书(公钥)。 制作密钥库文件 假设有2个Java应用, app1和app2,用户访问的地址为https://app1.xwiz.cn 和 https://app2.xwiz.cn。首先分别为这两个主机应用创建密钥库JKS和证书请求CSR keytool -genkey -alias app01 -keyalg RSA -keysize 2048...

Java中static依赖导致的IncompatibleClassChangeError

处理客户的一个问题请求时,碰到了一个错误。 Expected static field com.agile.extract.server.text.TextOutputStreamWriter.formatter java.lang.IncompatibleClassChangeError: Expected static field com.agile.extract.server.text.TextOutputStreamWriter.formatter 根据官方的JDK...

try-with-resources和异常处理

Java 7开始有一个有意思的功能,就是结合了自动关闭资源和异常处理的try-with-resources语句。按照旧式的处理方式,关闭资源一般放在try-catch的finally块中,需要显式地关闭。比如关闭文件句柄、数据库中的连接、语句和记录集等。而try-with-resources不需要显式声明关闭,它会自动调用close()方法,防止程序员忘记关闭资源而引起内存可能的泄漏。因此这种方式必须要求资源具有统一性,即必须实现了java.lang.AutoCloseable接口和它的唯一方法close()。 实现AutoCloseable...

为什么对super的调用必须是类构造器中的第一个语句

仔细研究(类与成员变量的初始化),可以解释很多疑问。这里顺便分析一下Java界一直存在的疑惑。 为什么对super的调用必须是当前类构造器函数中的第一个语句? 先说明一下JLS语言规范对super调用的规则 如果父类存在默认构造函数(就是无参构造函数),子类的构造函数中,Java会隐式地在第一行语句前填入 super(); 用户也可以显式地调用 super(); 且必须保证是第一个语句。 如果父类不存在默认构造函数,但存在有参构造函数,那么在子类的构造函数中,程序员必须显式地调用父类的有参构造函数,如: super(xxx);...

类与成员变量的初始化

之前研究过变量(类变量与实例变量的内存存储),顺便这次分析一下类变量的初始化问题,也能从另外一个角度来证明类变量的内存分配的共享性质。 假设我定义一个ClassA,含有一个实例变量s,一个默认构造函数(无参构造器),一个有参构造函数,还有一个实例方法check(String)。 public class ClassA { String s; public ClassA(){ s = "hello"; System.out.println("ClassA.ClassA(): s=" + s); } public...

finalize与资源释放/对象销毁

在Oracle Agile的代码中有一段非常不好的设计,就是关闭FTP连接释放socket资源的过程被定义在封装类的finalize()方法中。比如下面的代码。 private void disconnect() throws IOException { // Disconnect from FTP site if (this.m_connected) { this.m_ftp.disconnect(); this.m_ftp = null; this.m_connected = false; } } @Override...

类变量与实例变量的内存存储

变量有类变量、实例变量和局部变量。局部变量比较好区别。类变量与实例变量的区别就在于前者在类中被定义时有static修饰符。 类变量属于类级别,因此在多个线程中引用时是共享的,因此只存在于一个内存区域。而实例变量仅仅属于类定义的具体实例,多个实例之间不共享实例变量,因此他们使用不同的内存地址。 这里就出现了一个问题。实例都是类的具体定义,都是保存在heap堆中,因为堆都是保存具体的对象,所以实例变量也一定会保存在heap堆中。heap堆中因此一定不会存储类变量,因为类变量不属于任何对象。废话那么多,那类变量储存在哪个内存区域呢? 通过具体的例子来看看就明白了。 package...

String对象常量池和内存分配

创建String变量的时候,对象的产生和内存分配会因为创建方式不同而有显著的差别。 直接赋值 String a1 = "hello"; String a2 = "hello"; String a3 = "hello"; System.out.println("a1=a2?" + (a1==a2)); System.out.println("a1=a3?" + (a1==a3));...

1 2 3