第4章 对象与类

4.1 面向对象程序设计概述

  1. 实现封装的关键在于,绝对不能让类中的方法直接访问其他类的实例字段。程序只能通过对象的方法与对象数据进行交互。
  2. 对象的三个主要特性:
    • 对象的行为
    • 对象的状态
    • 对象的标识
  3. 识别类的一个简单经验是在分析问题的过程中寻找名词,而方法对应着动词。
  4. 类之间的关系:
    • 依赖:一个类的方法使用或操纵另一个类的对象
    • 聚合:包含关系意味着类A的对象包含类B的对象
    • 继承:表示一个更特殊的类与一个更一般的类之间的关系

4.2 使用预定义类

  1. Java中使用构造器来构造新实例。构造器的名字应该与类名相同。

    Date deadline = new Date();
    

    变量deadline不是一个对象,在它引用任何对象前,不能使用Date方法,否则将产生编译错误。

    注意:对象变量并没有实际包含一个对象,它只是引用一个对象。

  2. 所有的Java对象都存储在堆中。

  3. 不要使用构造器来构造LocalDate类的对象。实际上,应当使用静态工厂方法,它会代表你调用构造器。

    关于 Java 的静态工厂方法,看这一篇就够了!

    不通过 new,而是用一个静态方法来对外提供自身实例的方法,即为我们所说的静态工厂方法(Static factory method)

  4. 访问器方法:只访问对象而不修改对象的方法

    更改器方法:访问对象并且修改对象的方法

4.3 用户自定义类

  1. 文件名必须与public类的名字相匹配。在一个文件中,只能有一个公共类,但是可以有任意数目的非公共类。

  2. 构造器没有返回值。构造器总是伴随着new操作符一起调用。

  3. 不要在构造器中定义与实例字段同名的局部变量。

  4. Java 10中,如果可以从变量的初始值推导出它们的类型,那么可以用var关键字声明局部变量,而无须指定类型。注意var 关键字只能用于方法中的局部变量。

    Employee harry = new Employee("Harry Hacker", 50000, 1989, 10, 1);
    var harry = new Employee("Harry Hacker", 50000, 1989, 10, 1);
    
  5. 对象变量可以包含一个特殊值null,如果对null值应用一个方法,就会产生一个NullPointerException异常。

    定义一个类时,避免将某些字段定义为null的两种解决方法:

    • “宽容型”方法是把null参数转换为一个适当的非null

      if (n == null) name = "unknow" else name = n;
      
      // Objects类提供了一个便利方法
      public Employee(String n, double s, int year, int month, int day) {
          name = Objects.requireNonNullElse(n, "unknow");
      }
      
    • “严格型”方式则是拒绝null参数

      public Employee(String n, double s, int year, int month, int day) {
          name = Objects.requireNonNull(n, "The name cannot be null");
      }
      
  6. 方法的隐式参数和显式参数:

    • 隐式参数:调用方法的对象
    • 显式参数:传入方法的实参

    在每一个方法中,关键字this指示隐式参数。

  7. Java的所有方法都必须在类的内部定义,但并不一定是内联函数。是否为内联函数将由Java虚拟机决定。

  8. 对实例字段的封装:

    • 一个私有数据字段
    • 一个公共的字段访问器方法
    • 一个公共的字段更改器方法
  9. 注意不要编写返回可变对象引用的访问器方法,对于这种情况,应该对可变对象进行克隆(clone)。否则会破坏封装。

  10. 一个方法可以访问所属类的所有对象的私有数据。

  11. 实例字段如果定义为final,则该字段必须在构造对象时初始化,并且以后不能再修改,类似于C/C++中的const

    final修饰符对于类型为基本类型或者不可变类(如String类)的字段非常有用。

    final修复可变的类时,存储在变量中的对象引用不能再指向另一个不同的对象,但是指向的对象是可以改变的。

    // C/C++
    char hello[] = {"Hello"};
    char* const ptr = hello;	// final修复可变的类时与ptr变量类似。ptr为常量指针,初始化后,ptr指向的地址就不能再改变,不过指向的地址中的数据是可以改变的。
    

4.4 静态字段和静态方法

  1. 如果将类中的一个实例字段定义为static,这个字段就为静态字段。该字段属于类,不属于任何单个的对象。但是对每个对象来说,该字段是共享的。

  2. 静态常量

    public class Math{
        public static final double PI = 3.14159265358979323846;
        ...
    }
    

    对于上述代码。可以使用Math.PI来访问这个常量。如果去掉static,只能由某个对象来访问PI

  3. 静态方法:不在对象上执行,没有隐式参数,只能提供显式参数。不能访问实例字段,但是可以访问静态字段。可以用类名调用静态方法。

  4. 使用静态工厂方法来构造对象。

    • 静态工厂方法可以有不同的名字,不会局限在类名;
    • 可以返回构造对象类型的子类;
  5. 每一个类都可以有一个main方法,可以用于单元测试。

4.5 方法参数

  1. 参数传递可以分为:

    • 按值调用(call by value)
    • 按引用调用(call by reference)

    Java程序中总是按值调用。

  2. 有两种类型的方法参数:

    • 基本数据类型(数字、布尔值)
    • 对象引用

    Java中:

    • 方法不能修改基本数据类型的参数
    • 方法可以改变对象参数的状态
    • 方法不能让对象参数引用一个新的对象

4.6 对象构造

  1. Java中允许重载任何方法,包括构造器方法。方法名以及参数类型构成了方法的签名。

  2. 如果构造器中没有显式的为字段设置初值,那么字段会被自动赋为默认值:数值为0,布尔值为false,对象引用为null。注意方法中的局部变量必须要明确的初始化。

  3. 仅当类没有任何其他构造器时,才会得到一个默认的无参构造器。如果类中提供了一个构造器,但是没有提供无参构造器,那么构造对象时不提供参数就是不合法的。

    public ClassName() {
        
    }
    
  4. 显式字段初始化:

    public Employee() {
        private String name = "";
        ...
    }
    

    可以利用方法调用初始化一个字段。

  5. 参数名

    // 方法1
    public Employee(String aName, double aSalary){
        name = aName;
        salary = aSalary;
    }
    
    // 方法2
    public Employee(String name, double salary){
        this.name = name;
        this.salary = salary;
    }
    
  6. 调用另一个构造器

    public Employee(double s) {
        // calls Employee(String, double)
        this("Employee #" + nextId, s);
        nextId++;
    }
    

    this(...)语句必须是构造器的第一行语句。

  7. 初始化数据字段的方法:

    • 在构造器中设置值
    • 在声明中显式的赋值
    • 初始化块
  8. 调用构造器的具体处理步骤:

    1)如果构造器第一行调用了另一个构造器,则基于所提供的参数执行第二个构造器。

    2)否则,

    ​ a)所有数据字段初始化为其默认值(0、false或null)。

    ​ b)按照在类声明中出现的顺序,执行所有字段初始化方法和初始化块。

    3)执行构造器主体代码。

  9. 静态字段的初始化:

    • 在声明中赋值
    • 静态初始化块,只在类第一次加载时执行
  10. Java不支持析构器。

4.7 包

  1. 为了保证包名的唯一性,要用一个因特网域名(这显然是唯一的)以逆序的形式作为包名,然后对于不同的工程使用不同的子包。

  2. 一个类可以访问所属包中的所有类,以及其他包中的公共类。

  3. 两种方法访问另一个包中的公共类:

    • 使用完全限定名
    • 使用import语句
  4. import语句应该位于源文件的顶部,但位于package语句的后面

  5. 静态导入:允许导入静态方法和字段,还可以导入特定的方法和字段。

    import static java.lang.System.*;
    
    out.println("Hello, world!");	// 静态导入后,可以不加类名前缀
    
  6. 要想将类放入包中,就必须将包的名字放在源文件的开头。没有在源文件中放置package语句,这个源文件中的类就属于无名包。

  7. 应该将源文件放到与完整包名匹配的子目录中。如com.horstmann.corejava包中所有源文件应该放置在子目录com/horstmann/corejava中。

  8. 如果没有指定publicprivate,这个部分(类、方法或变量)可以被同一个包中的所有方法访问。变量必须显式的声明为private,否则的话将默认为包可访问,这会破坏封装性。

  9. 为了使类能被多个程序共享,需要做到下面几点:

    • 把类文件放到一个目录中。例如/home/user/classdir,注意这个目录是包树状结构的基目录。

    • JAR文件放在一个目录中。如/home/user/archives

    • 设置类路径(class path)。类路径是所有包含类文件的路径的集合。

  10. 类路径包括:

    • 基目录,/home/user/classdir
    • 当前目录(.)
    • JAR文件/home/user/archives/archives.jar

    Unix中:/home/user/classdir:.:/home/user/archives/'*'。注意*必须转义以防止shell扩展

    Windows中:c:\classdir;.;c:\archives\*

  11. 只可以从其他包中导入公共类。还可以从当前包中导入非公共类。

  12. 设置类路径:

    • Unixjava -classpath /home/user/classdir:.:/home/user/archives/archives.jar MyProg

      还可以使用-cp,或者Java 9中的--class-path

    • Windowsjava -classpath c:\classdir;.;c:\archives\archives.jar MyProg

    或者通过CLASSPATH环境变量设置:

    • bash shellexport CLASSPATH=/home/user/classdir:.:/home/user/archives/archives.jar
    • Windows shellset CLASSPATH=c:\classdir;.;c:\archives\archives.jar

    直到shell退出,类路径设置均有效。

4.8 JAR文件

  1. 一个JAR文件即可以包含类文件,也可以包含诸如图像和声音等其他类型的文件。JAR文件是ZIP压缩格式的。

  2. 使用jdk/bin文件下的jar工具制作JAR文件的常用语法:

    jar cvf jarFileName file1 file2 ...
    
  3. 清单文件,用于描述归档文件的特殊特性,被命名为MANIFEST.MF,位于JAR文件的一个特殊的META-INF子目录中。

4.9 文档注释

  1. javadoc工具从下面几项中抽取信息:
    • 模块
    • 公共类与接口
    • 公共的和受保护的字段
    • 公共的和受保护的构造器及方法
  2. 每个/**...*/文档注释包含标记以及之后紧跟着的自由格式文本。标记以@开始,如@since@param
  3. 在自由格式文本中可以使用HTML修饰符。
  4. 要想产生包注释,需要在每一个包目录中添加一个单独的文件。
  5. 注释抽取使用javadoc工具。

4.10 类设计技巧

  1. 一定要保证数据私有
  2. 一定要对数据进行初始化
  3. 不要在类中使用过多的基本类型
  4. 不是所有的字段都需要单独的字段访问器和字段更改器
  5. 分解有过多职责的类
  6. 类名和方法名要能够体现它们的职责
  7. 优先使用不可变的类
posted @ 2020-03-15 15:39  仰首望星辰  阅读(117)  评论(0)    收藏  举报