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);
      ...
   }
}