代码之旅

I love Coding !

Java多态-继承绑定

属性与静态方法

Java中属性与静态方法和类绑定(静态绑定)。静态方法和属性是属于类的,调用的时候直接通过类名.方法名完成对,不需要继承机制及可以调用。如果子类里面定义了同名静态方法和属性,那么这时候父类的静态方法或属性称之为"隐藏"。如果你想要调用父类的静态方法和属性,直接通过父类名.方法或变量名完成。

方法绑定

程序绑定的概念:

绑定指的是一个方法的调用与方法所在的类(方法主体)关联起来。对java来说,绑定分为静态绑定和动态绑定;或者叫做前期绑定和后期绑定。

静态绑定(早绑定 编译器绑定):

在程序执行前方法已经被绑定,此时由编译器或其它连接程序实现。针对java可以理解为程序编译期的绑定;特别说明一点,java当中的方法只有final,static,private和构造方法是前期绑定

动态绑定(迟绑定 运行期绑定):

后期绑定:在运行时根据具体对象的类型进行绑定。若一种语言实现了后期绑定,同时必须提供一些机制在运行期间判断对象的类型,并分别调用适当的方法。也就是说编译器此时依然不知道对象的类型,但方法调用机制能自己去调查,找到正确的方法主体。不同的语言对后期绑定的实现方法是有所区别的。可以这样认为:它们都要在对象中安插某些特殊类型的信息。

动态绑定的过程:

  • 虚拟机提取对象的实际类型的方法表
  • 虚拟机搜索方法签名
  • 调用方法

总结

了解三者的概念之后,我们发现java属于后期绑定。在java中,几乎所有的方法都是后期绑定,在运行时动态绑定方法属于子类还是基类。但也有特殊,针对static方法和final方法由于不能被继承,因此在编译时就可以确定他们的值,他们是属于前期绑定。特别说明的一点,private声明的方法和成员变量不能被子类继承,所有的private方法都被隐式的指定为final的(由此我们知道:将方法声明为final类型,一是为了防止方法被覆盖,二是为了有效的关闭java中的动态绑定)。java中的后期绑定是由JVM来实现的,我们不用去显式的声明它。

阅读全文 »

Java 进程

进程的概念主要有两点:

  • 第一,进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。
  • 第二,进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时(操作系统执行之),它才能成为一个活动的实体,我们称其为进程。
阅读全文 »

Java常见系统属性

获取property的方法

  • System.getProperty(String key)
  • System.getProperty(String key,String defaultValue)
  • System.getProperties()

每当使用getProperties()getProperty()方法,都会先调用
SecurityManager.checkPropertyAccess(key)方法来检查key对应的property是否为可读。

若属性不可读,会抛出异常

SecurityException - if a security manager exists and its checkPropertyAccess method doesn’t allow access to the specified system property。

阅读全文 »

Java应用如何使用shutdownhook

在线上Java程序中经常遇到进程程挂掉,一些状态没有正确的保存下来,这时候就需要在JVM关掉的时候执行一些清理现场的代码。JDK在1.3之后提供了Java Runtime.addShutdownHook(Thread hook)方法,可以注册一个JVM关闭的钩子,这个钩子可以在以下几种场景被调用:

  1. 程序正常退出
  2. 使用System.exit()
  3. 终端使用Ctrl+C触发的中断
  4. 系统关闭
  5. 使用Kill pid命令干掉进程(kill -9 除外)

关闭钩子在以下情景不会被调用:

  • 通过kill -9命令杀死进程——所以kill -9一定要慎用;
  • 程序中执行到了Runtime.getRuntime().halt()方法,该方法会强行关闭虚拟机;
  • 操作系统突然崩溃,或机器掉电。

一旦开始执行ShutdownHook时,无法再向JVM中addShutdownHook。

阅读全文 »

Java 处理系统信号

Linux信号量是一种比较原始的进程通信手段。有很多缺陷,可却是理解操作系统的基础概念。java 开放的api中涉及信号量的只有退出信号,即使用runtime.addShutdownHook()实现jvm退出钩子。但是我们可以通过 sun.misc 包来实现支持其他信号的处理。

阅读全文 »

Java SPI 机制

先描述下API(Application Programming Interface )。在java中,我们使用java提供的很多类、类的方法、数据结构来编写我们的应用程序,最终完成我们需求的程序功能,这里的类、方法、数据结构即是jdk提供的api。api的意义,其实就是这些提供给你完成某项功能的类、接口或者方法。

而SPI(Service Provider Interface)是指一些提供给你继承、扩展,完成自定义功能的类、接口或者方法。我们系统里抽象的各个模块,往往有很多不同的实现方案,比如日志模块的方案,xml解析模块、jdbc模块的方案等。面向的对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。 java spi就是提供这样的一个机制:为某个接口寻找服务实现的机制。有点类似IOC的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。

阅读全文 »

Java异常处理

  1. Java 的基本原理就是“形式错误的代码不会运行”。(编译错误=“形式错误的代码”)
  2. 捕获错误最理想的是在编译期间,最好在试图运行程序以前。然而,并非所有错误都能在编译期间侦测到。有些问题必须在运行期间解决,让错误的缔结者通过一些手续向接收者传递一些适当的信息,使其知道该如何正确地处理遇到的问题。

摘自 thinking in java 4th

阅读全文 »

Java lambda Function

如果匿名类的实现非常简单,比如只包含一个方法的接口,那么匿名类的语法可能看起来很笨重且不明确。使用Lambda表达式可以更简洁地表达单方法类的实例。

阅读全文 »

Java自增自减-中间变量缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Demo {
public static void main(String[] args){
method_1();
method_2();
}
private static void method_1(){
int j = 0;
for(int i=0;i<100;i++){
j = j++;
}
System.out.println("method_1---"+j);
}
private static void method_2(){
int j = 0;
for(int i=0;i<100;i++){
j = ++j;
}
System.out.println("method_2---"+j);
}
}

上面程序的输出结果是:

1
2
method_1---0  
method_2---100

为什么会是这样呢?

因为在计算过程中,使用了Java中间变量缓存机制。在java中,执行自增运算时,会为每一个自增操作分配一个临时变量,如果是前缀加(i),就会“先自加1后赋值(给临时变量)”;如果是后缀加(i),就会“先赋值(给临时变量)后自加1”。运算最终使用的,并不是变量本身,而是被赋了值的临时变量。

  • j = j++; ==> {int temp = j; j = j + 1; j = temp; }
  • j = ++j; ==> {j = j + 1; int temp = j; j = temp; }

Java不可变对象

可变类和不可变类(Mutable and Immutable Objects)的初步定义:

  1. 可变类:当你获得这个类的一个实例引用时,你可以改变这个实例的内容。
  2. 不可变类:当你获得这个类的一个实例引用时,你不可以改变这个实例的内容。不可变类的实例一但创建,其内在成员变量的值就不能被修改。
  • jdk的可变类和不可变类
    • 不可变类:jdk的java.lang包中 Boolean, Byte, Character, Double, Float, Integer, Long, Short,String
    • 可变类:StringBuffer,java.util.Date
阅读全文 »