1. 对象和类

今天您将了解一些典型的 Java 程序是如何工作的。这是个大新闻:每个 Java 程序都由类和对象组成。

您已经知道什么是类,但什么是对象?

我将从一个类比开始。想象一下,你想造一艘小船。首先你需要创建一个蓝图,然后将它交给工厂,工厂将根据蓝图建造一艘船。或者也许打。或者你喜欢多少艘船。数十艘相同的船只是根据同一张蓝图建造的。这是这里的重要事情。

在 Java 编程中也是如此。

蓝图

程序员就像设计师。设计师创建蓝图,Java 程序员编写类。零件是根据蓝图创建的,对象是根据类创建的。

首先,我们编写类(制作蓝图),然后,随着程序的运行,Java 机器根据这些类创建对象。就像从蓝图创建船只一样。

只有一张蓝图,但可以有很多艘船。这些船是不同的——它们有不同的名称并装载不同的货物。但它们非常相似:它们都共享相同的设计并且可以执行相似的任务。

或者这是另一个类比......

蚁丘是物体如何相互作用的一个很好的例子。一个简单的蚁丘中有三类蚂蚁:蚁后、兵蚁和工蚁。

每个班级的蚂蚁数量不同。整个蚁丘只有一个蚁后,但有几十个士兵,还有数百只工蚁。三个类和数百个对象。蚂蚁根据严格的规则彼此互动——与同类别的蚂蚁以及与其他类别的蚂蚁。

这是一个完美的例子。在一个典型的程序中,一切都是这样的。有一个主要对象创建所有其他类的对象。这些对象开始相互交互并与程序的“外部世界”交互。对象的行为在内部是硬编码的。

这两个类比是同一枚硬币的两面。真相在中间。第一个示例(关于蓝图和船舶)显示了类和该类的对象之间的关系。这是一个很强的类比。第二个例子(关于蚁丘)显示了编写的类和程序运行时存在的对象之间的关系。

您必须首先为将存在于程序中的每个对象编写类,然后还要描述它们如何交互。是的,没错,但它比听起来容易。

在 Java 中,所有实体在运行时都是对象,编写程序就是描述对象交互的不同方式。对象只是简单地调用彼此的方法并将所需的数据传递给它们。

文档

你怎么知道要传递给方法的数据是什么?在你之前的人想到了一切。

每个类通常都有一个说明,说明创建它的目的。此外,每个公共方法通常都有一个说明,说明它的作用以及需要传递给它的数据。

要使用类,您需要大致了解它的作用。你需要确切地知道每种方法的作用。但你根本不需要知道它是如何做到的。这就像一根魔杖。

让我们看一下复制文件的代码:

将 c:\\data.txt 文件复制到 c:\\result.txt 文件
package com.codegym.lesson2;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileCopy
{
   public static void main(String[] args) throws IOException
   {
      FileInputStream fileInputStream = new FileInputStream("c:\\data.txt");
      FileOutputStream fileOutputStream = new FileOutputStream("c:\\result.txt");

      while (fileInputStream.available() > 0)
      {
         int data = fileInputStream.read();
         fileOutputStream.write(data);
      }

      fileInputStream.close();
      fileOutputStream.close();
   }
}

如果您逐行阅读这段代码,您可以大致猜出它的作用。尽管这需要经验和实践。一段时间后,您会觉得这段代码很熟悉并且可以理解。


2. 设计程序

程序设计是一门完整的艺术。它既简单又困难。很简单,因为没有严格的法律:任何不被禁止的事情都是允许的。好吧,这也是困难的原因:做某事的方法有很多,要找到最好的方法并不容易。

设计一个程序就像写一本书。一方面,你只是写字母、单词和句子。另一方面,情节、人物、内部矛盾、冲突、讲故事的风格、阴谋等等都很重要。

最主要的是了解你在为谁编写代码。而你为其他程序员编写代码。

产品开发不可避免地意味着进行更改:这里添加一些东西,那里删除一些东西,重新设计一些东西。这就是从小的迭代中诞生的巨大、巨大和巨大的项目。

对于代码来说最重要的是它必须是其他程序员可以理解的。 可以更正可理解的错误代码。正确但难以理解的代码无法改进。 你所能做的就是丢弃它。

那么如何编写好的、干净的代码呢?

这样做需要三件事:

  • 在方法中编写良好且易于理解的代码——这是最简单的要求
  • 决定哪些实体应该包含在程序中
  • 将程序正确拆分为逻辑部分

这些概念的背后是什么?

在方法中编写好的代码

如果你有基本的英语技能,你可能已经注意到有时将代码阅读为英语句子是多么容易:

  • class Cat extends Pet— 这意味着 Cat 类扩展了 Pet 类
  • while(stream.ready())——只要流准备好了……
  • if (a<b) return a; else return b— 如果a小于b,则返回a,否则返回b

这是故意的。Java 是使编写自文档化代码变得容易的几种语言之一,即无需注释即可理解的代码。在好的 Java 代码中,许多方法读起来就像英语句子。

编写代码时,您的任务是使其尽可能简单明了。只要考虑一下您的代码是否易于阅读,您就会开始朝着正确的方向前进。

在 Java 中,通常编写易于阅读的代码。最好是,一个方法的所有代码都适合一个屏幕(即 20-30 行)。这是整个 Java 社区的规范。如果代码可以改进,它应该被改进。

学习如何编写好的代码的最好方法是通过实践。多写代码,研究别人的代码,请更有经验的同事来review你的代码。

请记住,当您告诉自己“别管那么好”的那一刻,您的成长就会停止。

决定哪些实体应该包含在程序中

您需要编写其他程序员可以理解的代码。如果 10 个程序员中有 9 个会在程序设计中包含类 A、B 和 C,那么您也应该在程序中包含类 A、B 和 C。您必须编写其他人可以理解的代码。

伟大的,有效的,快速的,但非标准的代码是糟糕的代码。

你需要研究别人的项目:这是吸收 IT 行业几十年积累的所有智慧的最好、最快、最简单的方法。

顺便说一下,您已经可以访问一个出色、流行且文档齐全的项目 — Java SDK。从它开始。

分析课程及其组织方式。想想为什么有些方法是静态的,而另一些则不是。为什么这些方法具有它们所具有的特定参数而不是其他参数。为什么要使用这些方法,为什么这些类的名称是它们所命名的,以及为什么它们包含在它们的特定包中。

一旦您开始理解所有这些问题的答案,您将能够编写其他人可以理解的代码。

也就是说,我想警告您不要分析Java SDK方法中的代码。许多方法都被重写以最大限度地提高速度,但它们的可读性值得怀疑。

将程序正确拆分为逻辑部分

几乎每个程序都分为多个部分或模块。每个部分负责程序的自身方面。

计算机有主板、显示器和键盘——这些都是独立的、松散耦合的部件。更重要的是,它们以标准化方式交互:USB、HDMI 等。如果您将咖啡洒在键盘上,只需在水槽中将其清洗干净,晾干,然后继续使用。

但是笔记本电脑是整体架构的一个例子:我们似乎可以辨别出独立的逻辑部分,但它们的集成度要高得多。在 MacBookPro 上,您必须拆卸笔记本电脑的一半才能清洁键盘。将咖啡洒在笔记本电脑上是订购新笔记本电脑的一个原因。不是一杯新咖啡。


3. 创建自己的类

但由于您只是在学习编程,因此您应该从小事做起,学习创建自己的类。

当然,您已经创建了类,但是您需要学习了解程序中应该包含哪些类、它们应该如何命名以及它们应该具有哪些方法。以及他们应该如何互动。

实体清单

如果您不知道从哪里开始,请从头开始。

当你第一次开始设计一个程序时,你可以简单地拿一张纸写下程序中应该包含的实体(对象)列表。然后按照每个实体都是一个单独的类的原则编写代码。

例子

假设您想编写一个国际象棋游戏。您将需要以下实体:一个棋盘和 6 种棋子。这些棋子以不同的方式移动并具有不同的价值。它们是不同的类是有道理的。的确,刚开始的时候,课越多越好。

很少会遇到写十个类而不是两个类的新手程序员。初学者不喜欢写十个类,而是喜欢写两个类或者可能只写一个。所以请写更多的类,我的程序员朋友们。你的代码对每个人来说都会变得更加清晰,也许除了你 😛

假设我们决定为国际象棋编写课程:这些课程会是什么样子?

棋盘只是一个 8 x 8 的阵列吗?最好创建一个单独的类,在内部存储对数组的引用。然后,您可以向“棋盘”类添加许多有用的方法,例如,检查特定单元格是否为空或已占用

通常,在开始时,始终遵循以下原则:一个程序有各种实体,而一个实体有一个类型。这种类型就是类。


4.静态变量和方法

也不要忘记使用静态变量和方法。如果您有一个棋子与棋盘上的另一个棋子交互,那么您的代码需要一个方法来引用第一个和第二个棋子以及棋盘。

可以从程序中的任何地方访问的静态变量通常用于避免不断传递对“始终存在”的对象的引用。

例如,像这样:

代码 笔记
public class ChessBoard
{
   public static ChessBoard board = new ChessBoard();
   public ChessItem[][] cells = new ChessItem[8][8];
   ...
}

public class Game
{
   public static void main(String[] args)
   {
      var board = ChessBoard.board;
      board.cells[0][3] = new King(Color.WHITE);
      board.cells[0][4] = new Queen(Color.WHITE);
      ...
   }
}


对单个对象的引用ChessBoard
一个 8x8 的二维数组,不是静态变量。








将棋子添加到棋盘上。

或者,您可以创建一个返回单例对象的方法来代替静态变量。例如,像这样:

public class ChessBoard
{
   private static ChessBoard board = new ChessBoard();
   public static ChessBoard getBoard()
   {
      return board;
   }

   public ChessItem[][] cells = new ChessItem[8][8];
   ...
}

public class Game
{
   public static void main(String[] args)
   {
      var board = ChessBoard.getBoard();
      board.cells[0][3] = new King(Color.WHITE);
      board.cells[0][4] = new Queen(Color.WHITE);
      ...
   }
}