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中,可以访问私有方法
                          }
                      }
                  }