3.1 Java语法深入讲解
本节对Java语法中一些高级主题进行讲解。
3.1.1 static
1.static变量
本质上讲,static关键字声明了一个全局变量。
尽管Java是一门纯面向对象的语言,语言规范中没有全局变量这种说法,但static声明的变量,从本质上就等同于C或C++语言中的全局变量。整个应用程序只有一个变量实例,任何对static变量进行的更改,在程序的其他地方都可见。
举个例子如下:
public class StaticExample {
public static int counter;
}
counter是个整型变量,前面用static关键字修饰后,就变成了一个全局变量,在程序的代码执行之前,这个counter变量就已经存在于内存中了,而且是唯一的实例。
counter静态变量是在StaticExample类中声明的,但实际上counter变量是独立于StaticExample的任何实例的。也就是说,程序没有创建任何StaticExample实例时, counter已经存在。程序创建100个StaticExample实例时,couner在内存中仍然是一个,而不是100个。当程序中创建的StaticExample类的实例都被虚拟机垃圾回收了, counter还存在。因此静态变量的生命周期,可以认为程序的第一行代码执行之前,就已经在内存中准备好,在程序的最后一行代码执行完毕,静态变量还存在于内存中。静态变量独立于任何对象,包括声明静态变量的类实例对象。
对简单类型是这样,对复杂类型(对象类型)也是这样。
静态变量的这种特性,经常被用在单实例的类实现中,例子如下:
public class Logger { // 构造函数声明为private, 这样就不能在类的外部用new 来创建对象 private Logger() {} private static Logger logger=null; // 单实例 // 暴露给外界调用的方法 public static Logger getLogger() { if(null==logger){ // 判断静态实例是否初始化,如没有就创建 logger=new Logger(); } return logger; // 返回全局唯一的实例 } //…… 其他方法声明 }
2.static方法
如果一个方法,仅依赖方法的传入参数、其他static变量,则此方法不依赖于声明方法的类实例,应该声明为static。表示此方法是类方法,而非实例方法。例子如下:
public class ConvertUtil {
public static int toInt(String s) { return Integer.parseInt(s); } }
toint方法的实现,仅依赖于方法传入的参数s,不依赖ConvertUtil对象的成员变量,因此toint方法是个类方法,不是个实例方法,因此用static来修饰toint方法的声明。这样,调用者调用时的代码就像下面这样:
int iValue = ConvertUtil.toInt(str);
如果不用static修饰,则调用者的代码就需要修改为如下:
ConvertUtil util = new ConvertUtil(); int iValue = util.toInt(str);
但实际上,toint方法根本就不需要创建一个ConvertUtil类的实例,创建这个对象是个浪费。
组件设计中,常用的工具类方法,基本都是static方式声明的。
3.static类
一个普通的Java类声明,用static修饰,是非法的,编译器会提示出错。这一点,与C++的静态类的概念是完全不同的。在C++中,一个类的声明用static修饰,则这个类中的所有成员变量和成员函数都是静态方法,独立于对象存在。
对于嵌套类的声明,可以用static修饰,但这有另外的含义,在后续的嵌套类中进行讲解。
3.1.2 嵌套类
一个类的声明,是在另外一个类中,这种在类中声明的类叫嵌套类,也叫类中类、内部类。例子如下:
public class Outer { private String outerId;
private Inner inner = new Inner(); public String getId() { // 访问内部类的私有成员 return outerId + "-" + inner.innerId; } public void setId(String id) { String[] strArray = id.split("-"); outerId = strArray[0]; inner.setId(strArray[1]); // 调用内部类的私有方法 } private void printStr(String str) { System.out.println(str); } // 内部类定义 public class Inner { private String innerId; private String getId() { return innerId; } private void setId(String id) { innerId = id; } protected void printId() { String str = "outerId=" + outerId + ", innerId=" + innerId; // 访问外部类的私有成员 printStr(str); // 访问外部类的私有方法 } } }
总结如下:
(1)内部类可以访问外部类的任何成员变量,包括外部类实例的私有成员变量。
(2)内部类可以调用外部类的任何成员函数,包括外部类实例的私有成员函数。
(3)外部类可以访问内部类的任何成员变量,包括内部类实例的私有成员变量。
(4)外部类可以调用内部类的任何成员函数,包括内部类实例的私有成员函数。
因此,对于内部类的成员变量和成员函数,用private, protected, public, package修饰,就如同没有这些修饰词一样。
另外,内部类是不能独立于外部类而单独存在的,对象构造的顺序是由外向内,先生成外部类,然后生成内部类。
3.1.3 静态嵌套类
嵌套类,在类声明的时候用static修饰,就成了静态嵌套类。静态嵌套类与普通嵌套类是完全不同的。这里,static的唯一作用,就相当于一个分隔符,将内部类和外部类隔离开来,使内部类独立于外部类单独存在。也就是说,内部类对外部类没有任何的依赖关系。而普通的内部类,是必须依赖于外部类而存在的。
因此,外部类与静态内部类之间的关系,就和两个独立的类之间的关系相同,二者之间互相访问,与两个独立类之间的访问规则相同。因此,用private, protected, public, package修饰,将直接影响可访问性。示例如下:
public class OuterClass { private String outerId; private StaticInner inner = new StaticInner(); public String getId() { //return outerId+"-"+inner.innerId(); // 私有成员,不允许访问 return outerId+"-"+inner.getInnerId();// 公有方法,可以访问 } public void setId(String id) { String[] strArray = id.split("-"); outerId = strArray[0]; inner.setInnerId(strArray[1]); // 公有方法,可以访问
} private void printStr(String str) { System.out.println(str); } public static class StaticInner { private String innerId; public String getInnerId() { return innerId; } public void setInnerId(String innerId) { this.innerId = innerId; } public void printId() { // 无法访问外部类的私有成员 // String str = "outerId=" + outerId + ", innerId=" + innerId; //printStr(str); // 无法访问外部类的私有方法 OuterClass outer = new OuterClass(); outer.printStr(innerId); // 同一package中,可以访问私有方法 } } }