├── .gitignore ├── 01 - Java Introduction ├── .ipynb_checkpoints │ └── Java Introduction-checkpoint.ipynb ├── DataTypeDemo.java ├── DialogTest.java ├── Java Introduction.ipynb ├── ScannerTest.java ├── Test.java └── Welcome.java ├── 02 - Control Structures ├── .ipynb_checkpoints │ └── Control Structures-checkpoint.ipynb ├── BlockDemo.java ├── Control Structures.ipynb └── DoWhileDemo.java ├── 03 - Collections ├── .ipynb_checkpoints │ └── Collections-checkpoint.ipynb └── Collections.ipynb ├── 04 - Classes and Objects ├── .ipynb_checkpoints │ └── Classes and Objects-checkpoint.ipynb ├── Classes and Objects.ipynb ├── Employee.java ├── Expense.java ├── OuterClass.java └── UMLClass.png ├── 05 - Inheritance ├── .ipynb_checkpoints │ └── Inheritance-checkpoint.ipynb ├── Inheritance.ipynb ├── UMLInheritance.png └── my │ ├── extended │ ├── ExchangeablePair.java │ ├── PairTest.java │ ├── PairUser.java │ └── SwappablePair.java │ └── pair │ ├── Pair.java │ └── ResettablePair.java ├── 06 - Polymorphism ├── .ipynb_checkpoints │ ├── Polymorphism-checkpoint.ipynb │ └── UMLPolymorphism-checkpoint.png ├── Polymorphism.ipynb ├── UMLComposition.png ├── UMLInterface.png └── UMLPolymorphism.png ├── 07 - Events and GUI Programming ├── .ipynb_checkpoints │ └── Events and GUI Programming-checkpoint.ipynb ├── BlankFrame.png ├── BorderLayoutExample.png ├── ButtonFrame.png ├── ButtonGrid.png ├── CardLayout01.png ├── CardLayout02.png ├── ComplexLayout.java ├── ComplexLayout.png ├── ComponentsEvents.png ├── Container.png ├── EventHierarchy.png ├── Events and GUI Programming.ipynb ├── FinalFrame.png ├── GUI.png ├── GUIClassHierarchy.png ├── MainApp01.png ├── MainApp02.png ├── MainApp03.png ├── MainApp04.png ├── PackedButtonFrame.png ├── Panel.png ├── ResizedPanel.png ├── idea-1.png └── idea-2.png ├── 08 - Graphics ├── .ipynb_checkpoints │ └── Graphics-checkpoint.ipynb ├── DrawPanel01.png ├── DrawPanel02.png ├── DrawingBoard.java ├── DrawingBoard.png ├── DrawingBoardTest.java ├── Graphics.ipynb ├── SpaceGame.png ├── SpaceGameUML.png ├── game │ ├── BasicGame.java │ ├── GameScene.java │ └── star.png └── spacegame │ ├── Alien.java │ ├── Bullet.java │ ├── GameObject.java │ ├── GameScene.java │ ├── ShootingScene.java │ ├── SpaceGame.java │ ├── SpaceShip.java │ └── images │ ├── alien.png │ ├── breakout │ ├── ball.png │ ├── brickie.png │ └── paddle.png │ ├── craft.png │ ├── minesweeper │ ├── 0.png │ ├── 1.png │ ├── 10.png │ ├── 11.png │ ├── 12.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ └── 9.png │ ├── missile.png │ ├── pacman │ ├── down1.png │ ├── down2.png │ ├── down3.png │ ├── ghost.png │ ├── left1.png │ ├── left2.png │ ├── left3.png │ ├── pacman.png │ ├── right1.png │ ├── right2.png │ ├── right3.png │ ├── up1.png │ ├── up2.png │ └── up3.png │ ├── snake │ ├── apple.png │ ├── dot.png │ └── head.png │ ├── sokoban │ ├── area.png │ ├── baggage.png │ ├── sokoban.png │ └── wall.png │ ├── spaceinvaders │ ├── alien.png │ ├── bomb.png │ ├── explosion.png │ ├── player.png │ └── shot.png │ └── star.png ├── 09 - Exceptions ├── .ipynb_checkpoints │ ├── ExceptionHierarchy-checkpoint.png │ └── Exceptions-checkpoint.ipynb ├── AssertTest.java ├── DivideByZeroNoExceptionHandling.java ├── DivideByZeroWithExceptionHandling.java ├── ExceptionHierarchy.png ├── Exceptions.ipynb ├── UsingChainedExceptions.java └── UsingExceptions.java ├── 10 - Generic Classes and Methods ├── .ipynb_checkpoints │ └── Generic Classes and Methods-checkpoint.ipynb └── Generic Classes and Methods.ipynb └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_Store 2 | **/*.class 3 | -------------------------------------------------------------------------------- /01 - Java Introduction/DataTypeDemo.java: -------------------------------------------------------------------------------- 1 | class DataTypeDemo { 2 | public static void main(String[] args) { 3 | int a, b; 4 | double x, y; 5 | boolean p, q; 6 | String s; 7 | 8 | a = 10; 9 | b = 20; 10 | x = 1.5; 11 | y = 2.0; 12 | 13 | p = a + b < x * b; 14 | System.out.printf("%d + %d = %d\n", a, b, a + b); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /01 - Java Introduction/DialogTest.java: -------------------------------------------------------------------------------- 1 | import javafx.application.Application; 2 | import javafx.stage.Stage; 3 | import javafx.scene.control.Alert; 4 | import javafx.scene.control.Alert.AlertType; 5 | 6 | public class DialogTest extends Application 7 | { 8 | @Override 9 | public void start(Stage stage) 10 | { 11 | Alert alert = new Alert(AlertType.INFORMATION); 12 | alert.setTitle("Information Dialog"); 13 | alert.setContentText("Here's a message for you: " + (byte)1234); 14 | alert.showAndWait(); 15 | 16 | //stage.show(); 17 | } 18 | 19 | public static void main(String[] args) 20 | { 21 | launch(args); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /01 - Java Introduction/ScannerTest.java: -------------------------------------------------------------------------------- 1 | import java.util.Scanner; 2 | 3 | public class ScannerTest { 4 | public static void main(String[] args) { 5 | Scanner input = new Scanner(System.in); 6 | System.out.print("Enter an integer: "); 7 | int n = input.nextInt(); 8 | System.out.print("Enter a floating-point number: "); 9 | double x = input.nextDouble(); 10 | System.out.print("Enter a few words: "); 11 | String firstWord = input.next(); 12 | String rest = input.nextLine(); 13 | 14 | System.out.println("n = " + n); 15 | System.out.println("x = " + x); 16 | System.out.println("The first word = " + firstWord); 17 | System.out.println("The rest of line = " + rest); 18 | System.out.println("That's all!"); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /01 - Java Introduction/Test.java: -------------------------------------------------------------------------------- 1 | void hello() { 2 | System.out.println("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /01 - Java Introduction/Welcome.java: -------------------------------------------------------------------------------- 1 | class Welcome { 2 | public static void main(String[] args) { 3 | System.out.println("Welcome to the Java World!"); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /02 - Control Structures/.ipynb_checkpoints/Control Structures-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# โครงสร้างควบคุม\n", 8 | "\n", 9 | "โปรแกรมในภาษา Java ประกอบขึ้นจากคำสั่ง (statement) ซึ่งโดยปกติจะทำงานตามลำดับไปจนสิ้นสุดส่วนของโปรแกรม เช่น เริ่มที่ต้นเมทอดไปจนสิ้นสุดเมทอด แต่โดยปกติแล้ว การทำงานใด ๆ ที่มีความซับซ้อนจะไม่ได้ทำตามลำดับขั้นตอนไปตลอด แต่มักจะต้องมีการตัดสินใจ การเลือกทำกรณีเฉพาะที่แตกต่างกัน การทำบางขั้นตอนซ้ำ ๆ การออกจากส่วนของโปรแกรมก่อนจะถึงจุดสิ้นสุด หรือลักษณะอื่น ๆ ที่ทำให้โปรแกรมไม่ทำงานไปตามลำดับปกติ\n", 10 | "\n", 11 | "กลไกที่มาสนับสนุนการเปลี่ยนแปลงลำดับการทำงานให้เป็นไปตามที่เราต้องการนั้นคือโครงสร้างควบคุม (control structure) ซึ่งเป็นรูปแบบคำสั่งที่กำหนดทิศทางการทำงานต่าง ๆ ได้ โครงสร้างควบคุมที่เราจะพิจารณากันในที่นี้คือ ทางเลือก การทำซ้ำ และการเรียกเมทอด\n", 12 | "\n", 13 | "> **หมายเหตุ** โปรแกรมตัวอย่างที่แสดงในบทนี้บางส่วนอาจจะเป็นเพียงส่วนของโปรแกรม การนำไปรันอาจต้องเพิ่มโครงของคลาสและเมทอด `main` ตามความเหมาะสม\n", 14 | "\n", 15 | "## การทำงานตามลำดับ\n", 16 | "\n", 17 | "ในสภาวะปกติที่ไม่มีคำสั่งควบคุมลำดับการทำงานเลย การทำงานของโปรแกรมจะเป็นไปตามลำดับ จากซ้ายไปขวาถ้ามีหลายคำสั่งในบรรทัดเดียวกัน และจากบนลงล่าง เราเรียกลักษณะการทำงานแบบนี้ว่าการควบคุมแบบตามลำดับ (sequential control) ซึ่งเป็นรูปแบบโดยปริยายเมื่อไม่มีการใช้คำสั่งควบคุมใด ๆ\n", 18 | "\n", 19 | "### คำสั่งแบบบล็อก\n", 20 | "\n", 21 | "Java มีโครงสร้างพิเศษที่ทำให้เราสามารถรวมหลาย ๆ คำสั่งไว้ด้วยกันแล้วมองเป็นคำสั่งเดียวได้ เรียกว่าคำสั่งแบบบล็อก (block statement) ซึ่งประกอบด้วยคำสั่งหลาย ๆ คำสั่งครอบด้วยเครื่องหมายวงเล็บปีกกา" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": 7, 27 | "metadata": {}, 28 | "outputs": [ 29 | { 30 | "name": "stdout", 31 | "output_type": "stream", 32 | "text": [ 33 | "Outside of the block\n", 34 | "Inside the block\n", 35 | "... where a + c = 25.4\n", 36 | "Outside again\n", 37 | "... where a + b = 15.25\n", 38 | "... and where there is no c\n" 39 | ] 40 | } 41 | ], 42 | "source": [ 43 | "public class BlockDemo {\n", 44 | " public static void main(String[] args) {\n", 45 | " int a = 10;\n", 46 | " double b = 5.25;\n", 47 | " \n", 48 | " System.out.println(\"Outside of the block\");\n", 49 | " \n", 50 | " {\n", 51 | " double c = 15.4;\n", 52 | " \n", 53 | " System.out.println(\"Inside the block\");\n", 54 | " System.out.println(\"... where a + c = \" + (a + c));\n", 55 | " }\n", 56 | " \n", 57 | " System.out.println(\"Outside again\");\n", 58 | " System.out.println(\"... where a + b = \" + (a + b));\n", 59 | " System.out.println(\"... and where there is no c\");\n", 60 | " }\n", 61 | "}\n", 62 | "\n", 63 | "BlockDemo.main(new String[0]);" 64 | ] 65 | }, 66 | { 67 | "cell_type": "markdown", 68 | "metadata": {}, 69 | "source": [ 70 | "การทำงานภายในบล็อกก็ยังคงเป็นลำดับต่อเนื่องจากภายนอกบล็อกตามปกติ แต่จากภายนอกจะมองเสมือนคำสั่งภายในบล็อกรวมกันเป็นคำสั่งเดียว การประกาศตัวแปรใด ๆ ภายในบล็อกจะมีขอบเขตเพียงภายในบล็อก ในตัวอย่างนี้ ส่วนที่อยู่นอกบล็อกจะไม่สามารถอ้างอิงตัวแปร `c` ได้เนื่องจากประกาศอยู่ภายในบล็อก\n", 71 | "\n", 72 | "## การทำงานแบบมีทางเลือก\n", 73 | "\n", 74 | "โครงสร้างควบคุมที่ทำให้เราสามารถเลือกการทำงานได้ตามเงื่อนไขที่ระบุในภาษา Java มีอยู่ 2 แบบ ได้แก่ `if`/`else` และ `switch`\n", 75 | "\n", 76 | "คำสั่ง `if`/`else` จะเลือกทำงานระหว่าง 2 ทางเลือกตามเงื่อนไข ถ้าเงื่อนไขเป็นจริงก็จะทำทางเลือกแรก แต่ถ้าเงื่อนไขไม่จริง ในกรณีที่ไม่มี `else` ก็จะไม่ทำอะไร แต่ทำคำสั่งถัดไปต่อไปเลย ในกรณีที่มี `else` ก็จะทำทางเลือกของ `else` ก่อนจะทำคำสั่งถัดไปต่อไป\n", 77 | "\n", 78 | "รูปแบบวากยสัมพันธ์ของ `if` เป็นดังนี้\n", 79 | "\n", 80 | "```\n", 81 | "if (condition)\n", 82 | " true-branch\n", 83 | "```\n", 84 | "\n", 85 | "แบบที่มี `else` มีลักษณะดังนี้\n", 86 | "\n", 87 | "```\n", 88 | "if (condition)\n", 89 | " true-branch\n", 90 | "else\n", 91 | " false-branch\n", 92 | "```\n", 93 | "\n", 94 | "_condition_ คือเงื่อนไขที่จะทดสอบ มีค่าเป็นชนิด `boolean` คือเป็นค่า `true` หรือ `false`\n", 95 | "\n", 96 | "_true-branch_ คือคำสั่งที่จะให้ทำถ้าเงื่อนไขเป็นจริง และจะทำ _false-branch_ สำหรับกรณีที่มี `else` และเงื่อนไขเป็นเท็จ คำสั่งที่ให้ทำในแต่ละทางเลือกจะเป็นคำสั่งเดี่ยว ๆ คำสั่งเดียว หรือจะเป็นหลายคำสั่งรวมกันในรูปของคำสั่งแบบบล็อกก็ได้\n", 97 | "\n", 98 | "### รูปแบบของเงื่อนไข\n", 99 | "\n", 100 | "เงื่อนไขที่ใช้กับ `if` จะต้องมีค่าเป็น `true` หรือ `false` หมายความว่าต้องเป็นค่าชนิด `boolean` ซึ่งจะได้จาก\n", 101 | "\n", 102 | "1. ค่าชนิด `boolean` โดยตรง ได้แก่ ค่า `true` และ `false`\n", 103 | "2. ตัวแปรชนิด `boolean`\n", 104 | "3. ผลจากตัวดำเนินการเปรียบเทียบหรือทดสอบ เช่น `==`, `>=`, `<` หรือ `instanceof`\n", 105 | "4. ผลจากตัวดำเนินการตรรกะ เช่น `&&`, `||` หรือ `!`\n", 106 | "\n", 107 | "ตัวดำเนินการเปรียบเทียบมีทั้งหมด 6 แบบ ได้แก่ น้อยกว่า (`<`) น้อยกว่าหรือเท่ากัน (`<=`) มากกว่าหรือเท่ากัน (`>=`) มากกว่า (`>`) เท่ากัน (`==`) และไม่เท่ากัน (`!=`) ส่วนตัวดำเนินการทดสอบมีแบบเดียวคือ `instanceof` ซึ่งใช้ทดสอบความสัมพันธ์ระหว่างอ็อบเจกต์และคลาส\n", 108 | "\n", 109 | "ตัวดำเนินการตรรกะมีทั้งหมด 6 แบบ ได้แก่ นิเสธหรือ not (`!`), and (`&&`), or, (`||`), strict exclusive-or (`^`), strict and (`&`) และ strict or (`|`)\n", 110 | "\n", 111 | "โดยปกติแล้ว ตัวดำเนินการตรรกะที่เราใช้บ่อยคือ `!`, `&&` และ `||` ส่วนอีก 3 ตัวที่เหลือมักจะพบการใช้เป็นการดำเนินการระดับบิตกับข้อมูลชนิดตัวเลขมากกว่า\n", 112 | "\n", 113 | "ตัวดำเนินการตรรกะมีลำดับความสำคัญต่ำกว่าตัวดำเนินการเปรียบเทียบ เพราะฉะนั้นถ้าอยู่ร่วมกันในนิพจน์เดียวกัน ตัวดำเนินการเปรียบเทียบจะถูกทำก่อน\n", 114 | "\n", 115 | "ตัวอย่างต่อไปนี้แสดงการใช้งาน `if` และเงื่อนไขในรูปแบบต่าง ๆ" 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": 9, 121 | "metadata": {}, 122 | "outputs": [ 123 | { 124 | "name": "stdout", 125 | "output_type": "stream", 126 | "text": [ 127 | "Second case is true\n", 128 | "Nothing is true\n" 129 | ] 130 | } 131 | ], 132 | "source": [ 133 | "int x = 10;\n", 134 | "int y = 20;\n", 135 | "boolean b = true;\n", 136 | "boolean c = b && x < y; // true\n", 137 | "\n", 138 | "if (x < y - 10)\n", 139 | " System.out.println(\"First case is true\");\n", 140 | "\n", 141 | "if (c) {\n", 142 | " // Using block statement here\n", 143 | " System.out.println(\"Second case is true\");\n", 144 | "} else {\n", 145 | " // Also block statement here\n", 146 | " System.out.println(\"Second case if false\");\n", 147 | "}\n", 148 | "\n", 149 | "// Nested if's, all using block statements\n", 150 | "if (x + 1 > y) {\n", 151 | " System.out.println(\"Third case is true\");\n", 152 | "} else if (x + 5 > y && y < 30) {\n", 153 | " System.out.println(\"Fourth case is true\");\n", 154 | "} else if (x + 10 > y && y < 40) {\n", 155 | " System.out.println(\"Fifth case is true\");\n", 156 | "} else {\n", 157 | " System.out.println(\"Nothing is true\");\n", 158 | "}" 159 | ] 160 | }, 161 | { 162 | "cell_type": "markdown", 163 | "metadata": {}, 164 | "source": [ 165 | "### นิพจน์เงื่อนไข\n", 166 | "\n", 167 | "เราสามารถใช้ทางเลือกในนิพจน์ได้ในรูปของนิพจน์เงื่อนไข ซึ่งมีรูปแบบเป็น `condition ? value1 : value2` ซึ่งจะให้ผลลัพธ์เป็นค่าของ _value1_ ถ้า _condition_ เป็นจริงหรือ _value2_ ถ้า _condition_ เป็นเท็จ" 168 | ] 169 | }, 170 | { 171 | "cell_type": "code", 172 | "execution_count": 10, 173 | "metadata": {}, 174 | "outputs": [ 175 | { 176 | "name": "stdout", 177 | "output_type": "stream", 178 | "text": [ 179 | "10\n" 180 | ] 181 | } 182 | ], 183 | "source": [ 184 | "int x = -10;\n", 185 | "int y = x < 0 ? -x : x;\n", 186 | "\n", 187 | "System.out.println(y);" 188 | ] 189 | }, 190 | { 191 | "cell_type": "markdown", 192 | "metadata": {}, 193 | "source": [ 194 | "ในตัวอย่างนี้ค่าของ `y` จะขึ้นอยู่กับเงื่อนไข `x < 0` ซึ่งถ้าจริง `y` ก็จะเท่ากับ `-x` และถ้าไม่จริง `y` ก็จะเท่ากับ `x` ในกรณีนี้ค่าของ `x` เป็นลบ จึงเข้าเงื่อนไขที่เป็นค่า `-x` ค่าของ `y` จึงได้เป็น 10\n", 195 | "\n", 196 | "### คำสั่งทางเลือกแบบหลายทาง\n", 197 | "\n", 198 | "ในบางกรณีเราต้องการเลือกทางเลือกจากค่าของตัวแปรหรือค่าผลลัพธ์ของนิพจน์ โดยผลลัพธ์แต่ละค่าจะเป็นทางเลือกที่ต่างกัน และมีหลายทางเลือก ในกรณีนี่เราอาจจะใช้คำสั่ง `switch` ในการจัดการ\n", 199 | "\n", 200 | "คำสั่ง `switch` จะอยู่ในรูป\n", 201 | "\n", 202 | "```\n", 203 | "switch (expression) {\n", 204 | " case constant1: branch1\n", 205 | " case constant2: branch2\n", 206 | " ...\n", 207 | " default: branchn\n", 208 | "}\n", 209 | "```\n", 210 | "\n", 211 | "ส่วนของ _expression_ เป็นค่าที่ `switch` จะพิจารณาเพื่อเลือกทางเลือก โดยต้องมีชนิดเป็น `int`, `short`, `char`, `byte`, `enum`, `String` หรือเป็นคลาสหีบห่อ (wrapper class) ของ `int`, `short`, `char` และ `byte`\n", 212 | "\n", 213 | "`switch` จะเทียบค่าของ _expression_ กับค่า _constant_ ของแต่ละ `case` ว่าตรงกับอันไหน แล้วก็จะเริ่มทำคำสั่งตาม _branch_ ที่ตรงกับ `case` นั้น โดยจะทำไปเรื่อย ๆ จนกว่าจะสิ้นสุดคำสั่ง `switch` หรือจนกว่าจะเจอคำสั่ง `break`\n", 214 | "\n", 215 | "ในกรณีที่ค่าของ _expression_ ไม่ตรงกับ `case` ใดเลย จะทำคำสั่งที่กำหนดที่ `default` ยกเว้นจะไม่ได้กำหนดส่วนของ `default` เอาไว้\n", 216 | "\n", 217 | "ตัวอย่างต่อไปนี้เป็นการใช้ `switch` เบื้องต้น" 218 | ] 219 | }, 220 | { 221 | "cell_type": "code", 222 | "execution_count": 11, 223 | "metadata": {}, 224 | "outputs": [ 225 | { 226 | "name": "stdout", 227 | "output_type": "stream", 228 | "text": [ 229 | "Wednesday\n" 230 | ] 231 | } 232 | ], 233 | "source": [ 234 | "int day = 4;\n", 235 | "String dayName;\n", 236 | "\n", 237 | "switch (day) {\n", 238 | " case 1:\n", 239 | " dayName = \"Sunday\";\n", 240 | " break;\n", 241 | " case 2:\n", 242 | " dayName = \"Monday\";\n", 243 | " break;\n", 244 | " case 3:\n", 245 | " dayName = \"Tuesday\";\n", 246 | " break;\n", 247 | " case 4:\n", 248 | " dayName = \"Wednesday\";\n", 249 | " break;\n", 250 | " case 5:\n", 251 | " dayName = \"Thursday\";\n", 252 | " break;\n", 253 | " case 6:\n", 254 | " dayName = \"Friday\";\n", 255 | " break;\n", 256 | " case 7:\n", 257 | " dayName = \"Saturday\";\n", 258 | " break;\n", 259 | " default:\n", 260 | " dayName = \"No such day\";\n", 261 | "}\n", 262 | "\n", 263 | "System.out.println(dayName);" 264 | ] 265 | }, 266 | { 267 | "cell_type": "markdown", 268 | "metadata": {}, 269 | "source": [ 270 | "จากตัวอย่างนี้ ค่าของ `day` คือ 4 ซึ่งจะตรงกับ `case` ที่กำหนดให้ `dayName` มีค่าเป็น `\"Wednesday\"` แล้วก็ `break` เลย\n", 271 | "\n", 272 | "ในบางครั้งเราอาจไม่ต้องการ `break` ในบาง `case` เพื่อให้ทำงานต่อไปยัง `case` ต่อไปเลยโดยไม่ออกจาก `switch` ลักษณะแบบนี้เรียกว่า fall through คือปล่อยให้ไหลต่อไป ลองดูตัวอย่างต่อไปนี้" 273 | ] 274 | }, 275 | { 276 | "cell_type": "code", 277 | "execution_count": 27, 278 | "metadata": {}, 279 | "outputs": [ 280 | { 281 | "name": "stdout", 282 | "output_type": "stream", 283 | "text": [ 284 | "You've got a board game, a lollipop, a fancy mask, and a lot of fun.\n" 285 | ] 286 | } 287 | ], 288 | "source": [ 289 | "Random dice = new Random();\n", 290 | "\n", 291 | "String prizes = \"You've got \";\n", 292 | "\n", 293 | "switch (dice.nextInt(6) + 1) {\n", 294 | " case 1:\n", 295 | " prizes += \"a teddy bear.\";\n", 296 | " break;\n", 297 | " case 2:\n", 298 | " prizes += \"a model robot, \";\n", 299 | " case 3:\n", 300 | " prizes += \"a board game, \";\n", 301 | " case 4: case 5:\n", 302 | " prizes += \"a lollipop, \";\n", 303 | " case 6:\n", 304 | " prizes += \"a fancy mask, \";\n", 305 | " default:\n", 306 | " prizes += \"and a lot of fun.\";\n", 307 | "}\n", 308 | "\n", 309 | "System.out.println(prizes);" 310 | ] 311 | }, 312 | { 313 | "cell_type": "markdown", 314 | "metadata": {}, 315 | "source": [ 316 | "ตัวอย่างนี้เป็นการทอดลูกเต๋าชิงรางวัล โดยสุ่มค่า 1-6 ด้วยเมทอด `nextInt` (ซึ่งจะได้ค่าในช่วง 0-5 จึงบวกเพิ่มอีก 1) แล้วนำค่าที่สุ่มได้ไปกำหนดรางวัลที่ได้\n", 317 | "\n", 318 | "ในตัวอย่างเราจะเห็นว่า `case 1` จะให้ teddy bear แล้ว `break` เลย แต่ตั้งแต่ `case 2` เป็นต้นไปจะไม่มีการ `break` นอกจากนี้ `case 4` และ `case 5` ยังอยู่รวมกัน ซึ่งหมายความว่าถ้าได้ค่า 4 หรือ 5 ก็จะมาตกกรณีเดียวกัน\n", 319 | "\n", 320 | "ในการรันตัวอย่าง พบว่าทอดลูกเต๋าได้ค่า 3 สตริง `prizes` จึงได้ต่อข้อความมาตั้งแต่ `\"a board game, \"` ของ `case 3` ไหลต่อเนื่องไปยัง `\"a lollipop, \"` ของ `case 4` และ `case 5` ไปถึง `\"a fancy mask, \"` ของ `case 6` และไปจบที่ `\"and a lot of fun.\"` ของ `default` เนื่องจากไม่มีการ `break` เลย\n", 321 | "\n", 322 | "กรณีตัวอย่างนี้แสดงให้เห็นการใช้งาน `switch` ในการสะสมค่าต่อเนื่องตั้งแต่กรณีที่ค่าตรงกันไปจนจบ ซึ่งในการใช้งานจริงอาจพบกรณีลักษณะนี้อยู่บ้าง\n", 323 | "\n", 324 | "> **หมายเหตุ** การใช้ `Random` เพื่อสุ่มค่าจะต้องมีการ `import java.util.Random` ก่อน\n", 325 | "\n", 326 | "## การวนซ้ำ\n", 327 | "\n", 328 | "คำสั่งสำหรับวนซ้ำในภาษา Java มีอยู่ 3 รูปแบบด้วยกัน ได้แก่ `while`, `do` ... `while` และ `for` โดย 2 แบบแรกนิยมใช้กับการวนซ้ำตามเงื่อนไข ส่วนแบบหลังนิยมใช้กับการวนซ้ำแบบรู้จำนวนรอบล่วงหน้า ทั้งนี้ ในความเป็นจริงแล้วคำสั่งวนซ้ำทั้ง 3 แบบสามารถใช้แทนกันได้ทั้งหมด\n", 329 | "\n", 330 | "### คำสั่ง while\n", 331 | "\n", 332 | "คำสั่ง `while` หรือลูป (loop) `while` ใช้สำหรับการวนซ้ำโดยพิจารณาตามเงื่อนไข ถ้าเงื่อนไขที่กำหนดเป็นจริงก็ทำซ้ำต่อไปแล้วกลับมาพิจารณาเงื่อนไขใหม่ เมื่อใดที่เงื่อนไขไม่จริงก็จะสิ้นสุดการทำงาน\n", 333 | "\n", 334 | "รูปแบบของคำสั่ง `while` เป็นดังนี้\n", 335 | "\n", 336 | "```\n", 337 | "while (condition)\n", 338 | " body\n", 339 | "```\n", 340 | "\n", 341 | "_condition_ คือเงื่อนไขที่พิจารณาในการวนซ้ำ โดยจะทำส่วนของ _body_ ตราบเท่าที่เงื่อนไขยังเป็นจริงอยู่ ดังตัวอย่างนี้" 342 | ] 343 | }, 344 | { 345 | "cell_type": "code", 346 | "execution_count": 33, 347 | "metadata": {}, 348 | "outputs": [ 349 | { 350 | "name": "stdout", 351 | "output_type": "stream", 352 | "text": [ 353 | "Total: 45\n" 354 | ] 355 | } 356 | ], 357 | "source": [ 358 | "int x = 10;\n", 359 | "int y = 5;\n", 360 | "int sum = 0;\n", 361 | "\n", 362 | "while (x > y) {\n", 363 | " sum += x + y;\n", 364 | " x--;\n", 365 | " y++;\n", 366 | "}\n", 367 | "\n", 368 | "System.out.println(\"Total: \" + sum);" 369 | ] 370 | }, 371 | { 372 | "cell_type": "markdown", 373 | "metadata": {}, 374 | "source": [ 375 | "ลูปนี้จะทำงานไปเรื่อย ๆ ตราบเท่าที่ค่าของ `x` ยังมากกว่า `y` โดยในแต่ละรอบจะลดค่าของ `x` และเพิ่มค่าของ `y` ด้วย\n", 376 | "\n", 377 | "ในกรณีที่เงื่อนไขไม่เป็นจริงตั้งแต่แรก ลูป `while` จะไม่ทำงานเลย\n", 378 | "\n", 379 | "#### ตัวดำเนินการเพิ่มค่าและลดค่า\n", 380 | "\n", 381 | "ตัวดำเนินการ `++` และ `--` ที่เห็นในตัวอย่างคือตัวดำเนินการเพิ่มค่าและลดค่าซึ่งใช้ได้กับทั้งตัวแปรชนิดจำนวนเต็มและทศนิยม `++` คือการเพิ่มค่าไปอีก 1 ค่าให้กับตัวแปรนั้น และ `--` คือการลดค่าลงไป 1 ค่า เราสามารถใช้ตัวดำเนินการนี้ร่วมกับตัวแปรเป็นส่วนหนึ่งของนิพจน์อื่น ๆ ได้\n", 382 | "\n", 383 | "ตัวดำเนินการ `++` และ `--` มีลักษณะการใช้งาน 2 แบบ คือการเพิ่ม/ลดค่าก่อน และการเพิ่ม/ลดค่าทีหลัง แบบแรกเราจะใช้ตัวดำเนินการนี้หน้าชื่อตัวแปร ตัวแปรจะเปลี่ยนค่าทันที แต่แบบที่สองเราจะใช้ตัวดำเนินการนี้หลังชื่อตัวแปร ค่าเดิมของตัวแปรจะถูกนำไปใช้ในนิพจน์ ตัวแปรจะเปลี่ยนค่าทีหลัง\n", 384 | "\n", 385 | "ลองดูตัวอย่างต่อไปนี้" 386 | ] 387 | }, 388 | { 389 | "cell_type": "code", 390 | "execution_count": 30, 391 | "metadata": {}, 392 | "outputs": [ 393 | { 394 | "name": "stdout", 395 | "output_type": "stream", 396 | "text": [ 397 | "n = 6, m = 6, x = 3.5, y = 2.5\n", 398 | "n = 7, m = 5, x = 2.5, y = 14.5\n" 399 | ] 400 | } 401 | ], 402 | "source": [ 403 | "int n = 5;\n", 404 | "int m = ++n;\n", 405 | "double x = 2.5;\n", 406 | "double y = x++;\n", 407 | "\n", 408 | "System.out.printf(\"n = %d, m = %d, x = %.1f, y = %.1f%n\", n, m, x, y);\n", 409 | "\n", 410 | "y = n++ + --m + x--;\n", 411 | "\n", 412 | "System.out.printf(\"n = %d, m = %d, x = %.1f, y = %.1f%n\", n, m, x, y);" 413 | ] 414 | }, 415 | { 416 | "cell_type": "markdown", 417 | "metadata": {}, 418 | "source": [ 419 | "ค่าของ `m` ถูกกำหนดให้เท่ากับ `++n` ซึ่งก็คือเพิ่มค่าให้กับ `n` เป็น 6 ก่อนแล้วจึงกำหนดให้กับ `m` ค่าของ `m` จึงเป็น 6 เช่นกัน ในทางกลับกัน ค่าของ `y` ถูกกำหนดให้เท่ากับ `x++` ซึ่งจะนำค่าของ `x` เดิมคือ 2.5 ไปกำหนดให้กับ `y` ก่อนแล้วจึงเพิ่มค่าให้กับ `x` เป็น 3.5\n", 420 | "\n", 421 | "อีกส่วนหนึ่งคือ `y = n++ + --m + x--` ซึ่งจะมีการเพิ่มค่าให้ `n` และลดค่าของ `m` และ `x` แต่จะใช้ค่าของ `n` และ `x` ก่อนการเปลี่ยนค่าในการคำนวณค่าที่จะกำหนดให้ `y`\n", 422 | "\n", 423 | "บ่อยครั้งที่เรามักจะใช้ตัวดำเนินการ `++` และ `--` เพื่อเพิ่มหรือลดค่าอย่างเดียวโดยไม่ได้นำค่าไปใช้ในนิพจน์อื่นหรือไปกำหนดให้กับตัวแปรอื่นต่อ เช่น ในตัวอย่าง `while` ที่เราใช้ `x--` และ `y++` โดด ๆ โดยไม่ได้นำค่าไปกำหนดให้ตัวแปรอื่นต่อ การใช้ในลักษณะนี้เราจะใช้ตัวดำเนินการก่อนหรือหลังชื่อตัวแปรก็ได้ผลลัพธ์เหมือนกัน\n", 424 | "\n", 425 | "#### ตัวดำเนินการรวมค่า\n", 426 | "\n", 427 | "อีกตัวดำเนินการหนึ่งที่เราเห็นในตัวอย่าง `while` คือ `+=` ซึ่งเราสามารถเรียกได้ว่าเป็นตัวดำเนินการรวมค่า การเขียนว่า `x += n` เทียบเท่าได้กับการเขียนว่า `x = x + n` คือการบวกค่ากับตัวแปรหนึ่งแล้วกำหนดค่านั้นคืนให้กับตัวแปรนั้น มองอีกอย่างได้ว่าเป็นการเอาค่าทางฝั่งขวาของตัวดำเนินการไปเพิ่มให้กับตัวแปรทางฝั่งซ้ายของตัวดำเนินการนั่นเอง ในกรณีของตัวอย่าง `sum += x + y` ก็เหมือนกับการเขียนว่า `sum = sum + (x + y)` ซึ่งก็คือการเอาค่าของ `x + y` ไปเพิ่มให้กับ `sum`\n", 428 | "\n", 429 | "นอกจาก `+=` แล้วยังมีตัวดำเนินการในลักษณะเดียวกันตัวอื่นด้วย ได้แก่ `-=`, `*=`, `/=`, `%=`, `&=`, `|=` และ `^=`\n", 430 | "\n", 431 | "### คำสั่ง do ... while\n", 432 | "\n", 433 | "ลูปที่วนซ้ำตามเงื่อนไขอีกแบบคือลูป `do` ... `while` ซึ่งมีรูปแบบดังนี้\n", 434 | "\n", 435 | "```\n", 436 | "do\n", 437 | " body\n", 438 | "while (condition);\n", 439 | "```\n", 440 | "\n", 441 | "การทำงานของ `do` ... `while` จะต่างจาก `while` ตรงที่การทำงานในส่วนของ _body_ จะเกิดขึ้นก่อนการตรวจสอบ _condition_ ซึ่งหมายความว่าไม่ว่าเงื่อนไขจะเป็นอย่างไร `do` ... `while` จะต้องทำส่วนของ _body_ อย่างน้อย 1 รอบก่อน\n", 442 | "\n", 443 | "หลังจากรอบแรกแล้ว พฤติกรรมของ `do` ... `while` จะไม่ต่างจาก `while`\n", 444 | "\n", 445 | "ตัวอย่างหนึ่งที่เราจะพบการใช้ `do` ... `while` ได้บ่อยคือการรับข้อมูลจากผู้ใช้ เราจะรับค่ามาก่อนแล้วค่อยตรวจสอบว่าค่าที่รับมาถูกต้องหรือไม่ ถ้าไม่ถูกต้องก็กลับไปรับใหม่จนกว่าจะถูกต้อง ดังตัวอย่างนี้" 446 | ] 447 | }, 448 | { 449 | "cell_type": "code", 450 | "execution_count": 36, 451 | "metadata": {}, 452 | "outputs": [], 453 | "source": [ 454 | "import java.util.Scanner;\n", 455 | "import java.util.Random;\n", 456 | "\n", 457 | "public class DoWhileDemo {\n", 458 | " public static void main(String[] args) {\n", 459 | " Scanner input = new Scanner(System.in);\n", 460 | " Random random = new Random();\n", 461 | " int secretValue = random.nextInt(9) + 1;\n", 462 | " int guess;\n", 463 | " \n", 464 | " do {\n", 465 | " System.out.print(\"Enter a number between 1 to 9: \");\n", 466 | " guess = input.nextInt();\n", 467 | " if (guess < secretValue) {\n", 468 | " System.out.println(\"Too low!\");\n", 469 | " } else if (guess > secretValue) {\n", 470 | " System.out.println(\"Too high!\");\n", 471 | " }\n", 472 | " } while (guess != secretValue);\n", 473 | " \n", 474 | " System.out.println(\"You've got it!\");\n", 475 | " }\n", 476 | "}" 477 | ] 478 | }, 479 | { 480 | "cell_type": "markdown", 481 | "metadata": {}, 482 | "source": [ 483 | "โปรแกรมนี้จะสุ่มค่าระหว่าง 1-9 แล้วให้ผู้ใช้ทายเลข โดยจะให้ทายซ้ำไปเรื่อย ๆ จนกว่าจะทายถูก ถ้าทายไม่ถูกก็จะแจ้งว่าค่าที่ทายสูงไปหรือต่ำไป เราใช้ `do` ... `while` เพราะเราต้องการรับค่าจากผู้ใช้ก่อนแล้วจึงตรวจสอบค่า\n", 484 | "\n", 485 | "ตัวอย่างผลการรันเป็นดังนี้\n", 486 | "\n", 487 | "```\n", 488 | "Enter a number between 1 to 9: 5\n", 489 | "Too low!\n", 490 | "Enter a number between 1 to 9: 7\n", 491 | "Too high!\n", 492 | "Enter a number between 1 to 9: 6\n", 493 | "You've got it!\n", 494 | "```\n", 495 | "\n", 496 | "### คำสั่ง for\n", 497 | "\n", 498 | "คำสั่งวนซ้ำตัวสุดท้ายคือคำสั่ง `for` ซึ่งมีรูปแบบดังนี้\n", 499 | "\n", 500 | "```\n", 501 | "for (initialization; condition; step)\n", 502 | " body\n", 503 | "```\n", 504 | "\n", 505 | "โครงสร้างของคำสั่ง `for` จะมีลำดับการทำงานเป็นดังนี้\n", 506 | "\n", 507 | "1. ทำส่วน _initialization_\n", 508 | "2. ทดสอบเงื่อนไขในส่วน _condition_\n", 509 | " - ถ้าเงื่อนไขเป็นจริงให้ทำต่อข้อ 3\n", 510 | " - ถ้าเงื่อนไขเป็นเท็จให้จบการทำงานของคำสั่ง `for`\n", 511 | "3. ทำส่วน _body_\n", 512 | "4. ทำส่วน _step_\n", 513 | "5. กลับไปข้อ 2\n", 514 | "\n", 515 | "จากลำดับนี้จะเห็นได้ว่าส่วน _initialization_ จะทำเพียงครั้งเดียวในตอนเริ่มต้น ส่วนนี้เราจะใช้ในการกำหนดค่าตั้งต้นที่จำเป็นสำหรับการทำซ้ำซึ่งมักจะเป็นการประกาศและกำหนดค่าตั้งต้นให้กับตัวแปรที่ใช้นับจำนวนรอบของการวนซ้ำ แต่ไม่จำเป็นต้องเป็นแบบนั้นเสมอไป\n", 516 | "\n", 517 | "ก่อนการวนซ้ำแต่ละรอบจะมีการทดสอบ _condition_ ซึ่งถ้าเป็นจริงก็จะไปทำส่วน _body_ ซึ่งเป็นโค้ดหลักที่ต้องการให้วนซ้ำ แล้วจึงทำส่วนของ _step_ ซึ่งมักจะเป็นคำสั่งเปลี่ยนค่าของตัวแปรที่ใช้นับรอบ แล้วจึงกลับไปตรวจสอบ _condition_ ใหม่ ดังตัวอย่างนี้" 518 | ] 519 | }, 520 | { 521 | "cell_type": "code", 522 | "execution_count": 38, 523 | "metadata": {}, 524 | "outputs": [ 525 | { 526 | "name": "stdout", 527 | "output_type": "stream", 528 | "text": [ 529 | "Adding 1\n", 530 | "Adding 2\n", 531 | "Adding 3\n", 532 | "Adding 4\n", 533 | "Adding 5\n", 534 | "Sum = 15\n" 535 | ] 536 | } 537 | ], 538 | "source": [ 539 | "int sum = 0;\n", 540 | "\n", 541 | "for (int i = 0; i < 5; i++) {\n", 542 | " System.out.printf(\"Adding %d%n\", i + 1);\n", 543 | " sum += i + 1;\n", 544 | "}\n", 545 | "\n", 546 | "System.out.println(\"Sum = \" + sum);" 547 | ] 548 | }, 549 | { 550 | "cell_type": "markdown", 551 | "metadata": {}, 552 | "source": [ 553 | "ลูป `for` จะเริ่มต้นโดยการประกาศตัวแปร `i` และกำหนดค่าให้เท่ากับ 0 และทุกรอบจะมีการตรวจสอบค่าของ `i` ว่ายังน้อยกว่า 5 อยู่ และทำส่วนของ _body_ คือการแสดงข้อความและบวกค่าตัวนับให้กับตัวแปร `sum` แล้วจึงทำส่วนของ _step_ ซึ่งก็คือการเพิ่มค่าให้กับตัวนับซึ่งก็คือตัวแปร `i` นั่นเอง ส่วน _step_ จึงถือเป็นส่วนที่มีความจำเป็นมากในการทำให้การวนซ้ำมีการเดินหน้าไปถึงจุดสิ้นสุด\n", 554 | "\n", 555 | "ถ้าเราลองพิจารณากันอย่างละเอียดจะพบว่าเราสามารถใช้คำสั่ง `while` แทนคำสั่ง `for` ได้เช่นกัน โดยถ้าต้องการเลียนแบบการทำงานทุกอย่างของ `for` เราสามารถเขียนเป็นคำสั่ง `while` ได้ในรูปนี้\n", 556 | "\n", 557 | "```\n", 558 | "initialization\n", 559 | "while (condition) {\n", 560 | " body\n", 561 | " step\n", 562 | "}\n", 563 | "```\n", 564 | "\n", 565 | "ซึ่งถ้าใช้กับโปรแกรมตัวอย่างข้างต้นก็จะได้เป็นดังนี้" 566 | ] 567 | }, 568 | { 569 | "cell_type": "code", 570 | "execution_count": 39, 571 | "metadata": {}, 572 | "outputs": [ 573 | { 574 | "name": "stdout", 575 | "output_type": "stream", 576 | "text": [ 577 | "Adding 1\n", 578 | "Adding 2\n", 579 | "Adding 3\n", 580 | "Adding 4\n", 581 | "Adding 5\n", 582 | "Sum = 15\n" 583 | ] 584 | } 585 | ], 586 | "source": [ 587 | "int sum = 0;\n", 588 | "int i = 0;\n", 589 | "\n", 590 | "while (i < 5) {\n", 591 | " System.out.printf(\"Adding %d%n\", i + 1);\n", 592 | " sum += i + 1;\n", 593 | " i++;\n", 594 | "}\n", 595 | "\n", 596 | "System.out.println(\"Sum = \" + sum);" 597 | ] 598 | }, 599 | { 600 | "cell_type": "markdown", 601 | "metadata": {}, 602 | "source": [ 603 | "ข้อแตกต่างของคำสั่ง `for` กับการใช้คำสั่ง `while` แทนในรูปแบบนี้ นอกจากเรื่องความกระชับแล้ว ยังมีความแตกต่างในรายละเอียดเล็กน้อยอีกประเด็น ได้แก่ ขอบเขตของตัวแปรที่ประกาศในส่วน _initialization_ ซึ่งในกรณีของ `for` จะมีขอบเขตแค่ภายในลูป `for` เท่านั้น แต่ในกรณีของ `while` ตัวแปรที่ประกาศขึ้นจะมีขอบเขตในระดับเดียวกับบล็อกที่ครอบคำสั่ง `while` นั้นอยู่ ซึ่งถ้าเป็นเมทอดก็จะมีขอบเขตเป็นทั้งเมทอดนั้น\n", 604 | "\n", 605 | "เนื่องจากในความเป็นจริงแล้วทั้ง `for` และ `while` ก็มีลักษณะที่ทดแทนกันได้ เราจึงสามารถใช้ `for` แทน `while` และใช้กับการวนซ้ำที่มีเงื่อนไขซับซ้อนได้เช่นกัน แต่ในทางปฏิบัติแล้วเราจะนิยมใช้ `for` ในการวนซ้ำที่มีจำนวนรอบแน่นอนเป็นส่วนใหญ่ และใช้ `while` หรือ `do` ... `while` สำหรับกรณีอื่น ๆ\n", 606 | "\n", 607 | "> **หมายเหตุ** ยังมีรูปแบบของคำสั่ง `for` อีกรูปแบบหนึ่งซึ่งใช้กับข้อมูลชนิด collection ซึ่งจะกล่าวถึงในบทต่อไป\n", 608 | "\n", 609 | "### คำสั่ง break\n", 610 | "\n", 611 | "ในการวนซ้ำ บางกรณีเราต้องการยุติการวนซ้ำก่อนที่จะไปถึงการตรวจสอบเงื่อนไขรอบต่อไป ในกรณีนี้เราสามารถใช้คำสั่ง `break` เพื่อยุติการทำงานของลูปได้\n", 612 | "\n", 613 | "พิจารณาตัวอย่างต่อไปนี้" 614 | ] 615 | }, 616 | { 617 | "cell_type": "code", 618 | "execution_count": 42, 619 | "metadata": {}, 620 | "outputs": [ 621 | { 622 | "name": "stdout", 623 | "output_type": "stream", 624 | "text": [ 625 | "Found 10098 in 14 steps.\n" 626 | ] 627 | } 628 | ], 629 | "source": [ 630 | "int start = 10000;\n", 631 | "int end = 20000;\n", 632 | "int step = 7;\n", 633 | "int divider = 17;\n", 634 | "int stepCount = 0;\n", 635 | "int i;\n", 636 | "\n", 637 | "for (i = start; i <= end; i += step) {\n", 638 | " if (i % divider == 0)\n", 639 | " break;\n", 640 | " \n", 641 | " stepCount++;\n", 642 | "}\n", 643 | "\n", 644 | "System.out.printf(\"Found %d in %d steps.%n\", i, stepCount);" 645 | ] 646 | }, 647 | { 648 | "cell_type": "markdown", 649 | "metadata": {}, 650 | "source": [ 651 | "ตัวอย่างนี้เราต้องการหาค่าที่สามารถหารด้วย 17 ลงตัวจากชุดตัวเลขที่เริ่มต้นที่ 10,000 และเพิ่มทีละ 7 ไปจนถึง 20,000\n", 652 | "\n", 653 | "ลูป `for` ในที่นี้จะเป็นการนับตามปกติโดยเพิ่มค่าของ `i` รอบละ 7 แต่ภายในลูปเรามีการตรวจสอบเงื่อนไขเพิ่มเติมว่าค่าของ `i` หาร 17 ลงตัวหรือไม่ ถ้าลงตัวถือว่าเราพบค่าแล้ว ไม่ต้องวนซ้ำต่อ จึง `break` ออกมาจากลูป\n", 654 | "\n", 655 | "คำสั่ง `break` มักจะใช้ร่วมกับคำสั่ง `if` เพื่อตรวจสอบเงื่อนไขในการสิ้นสุดการวนซ้ำเพิ่มเติมก่อนสั่งยุติการวนซ้ำ\n", 656 | "\n", 657 | "### คำสั่ง continue\n", 658 | "\n", 659 | "นอกเหนือจากการยุติการทำงานของลูปด้วยคำสั่ง `break` แล้ว เรายังสามารถควบคุมการทำงานภายในลูปได้อีกลักษณะหนึ่ง คือการสั่งให้ขึ้นรอบการวนซ้ำรอบต่อไปทันทีโดยไม่ทำคำสั่งภายในลูปส่วนที่เหลือ การขึ้นรอบต่อไปก็จะมีการตรวจสอบเงื่อนไขก่อนการทำซ้ำตามปกติ\n", 660 | "\n", 661 | "พิจารณาตัวอย่างต่อไปนี้" 662 | ] 663 | }, 664 | { 665 | "cell_type": "code", 666 | "execution_count": 43, 667 | "metadata": {}, 668 | "outputs": [ 669 | { 670 | "name": "stdout", 671 | "output_type": "stream", 672 | "text": [ 673 | "Sum = 4780\n" 674 | ] 675 | } 676 | ], 677 | "source": [ 678 | "// Sum of all except those that are divisible by both 6 and 9\n", 679 | "int sum = 0;\n", 680 | "\n", 681 | "for (int i = 1; i <= 100; i++) {\n", 682 | " if (i % 6 == 0 && i % 9 == 0)\n", 683 | " continue;\n", 684 | " \n", 685 | " sum += i;\n", 686 | "}\n", 687 | "\n", 688 | "System.out.println(\"Sum = \" + sum);" 689 | ] 690 | }, 691 | { 692 | "cell_type": "markdown", 693 | "metadata": {}, 694 | "source": [ 695 | "ตัวอย่างนี้เป็นการหาผลรวมของจำนวนเต็มจาก 1-100 โดยยกเว้นตัวเลขที่ทั้ง 6 และ 9 สามารถหารลงตัว เราตรวจสอบเงื่อนไขการหารลงตัวนี้แล้วสั่งให้ขึ้นรอบต่อไปเลยโดยไม่เอาค่ามารวมกับ `sum` ถ้าเลขนั้นหารได้ลงตัว\n", 696 | "\n", 697 | "## เมทอดของคลาส\n", 698 | "\n", 699 | "เมทอดของคลาส (class method) หรือเมทอดสถิต (static method) คือเมทอดที่ประกาศขึ้นโดยมีการระบุตัวกำหนด `static` ซึ่งที่ผ่านมาเราจะเห็นได้ในเมทอด `main`\n", 700 | "\n", 701 | "เมทอดคือโครงสร้างที่รวมคำสั่งจำนวนหนึ่งเอาไว้ด้วยกันเป็นชุด แล้วตั้งชื่อขึ้นมาใหม่เสมือนเป็นคำสั่งใหม่ เปรียบเทียบกับภาษาอื่นอาจจะเปรียบเทียบได้กับฟังก์ชัน แต่ข้อแตกต่างจากฟังก์ชันก็คือ ฟังก์ชันจะเป็นอิสระ แต่เมทอดจะผูกติดอยู่กับคลาสหรืออ็อบเจกต์ ซึ่งหมายความว่าไม่สามารถเรียกใช้โดด ๆ ได้ ต้องเรียกใช้ผ่านคลาสหรือผ่านอ็อบเจกต์ที่ผูกติดอยู่\n", 702 | "\n", 703 | "เมทอดของคลาสเป็นเมทอดที่ถูกผูกติดกับคลาส การเรียกใช้ต้องเรียกผ่านคลาสหรือผ่านอ็อบเจกต์ของคลาสนั้น ยกเว้นจะเป็นการเรียกภายในคลาสเดียวกัน ซึ่งไม่ต้องระบุคลาสอีก\n", 704 | "\n", 705 | "รูปแบบการประกาศเมทอดของคลาสเบื้องต้นเป็นดังนี้\n", 706 | "\n", 707 | "```\n", 708 | "access-modifier static return-type methodName(parameter-list)\n", 709 | " method-body\n", 710 | "```\n", 711 | "\n", 712 | "- _access-modifiers_ เป็นตัวกำหนดการเข้าถึงไม่เกินหนึ่งตัวระหว่าง `private` กับ `public` หรือไม่ระบุก็ได้ ในที่นี้เราจะใช้ `public` ซึ่งหมายถึงเรียกใช้ได้จากทุกที่ไปก่อน\n", 713 | "- _return-type_ เป็นตัวระบุชนิดของข้อมูลที่จะส่งกลับจากเมทอด โดยที่ `void` จะหมายถึงเมทอดนี้ไม่ส่งค่ากลับ\n", 714 | "- _parameter-list_ เป็นรายการพารามิเตอร์ที่เมทอดนี้จะรับ\n", 715 | "- _method-body_ เป็นตัวโค้ดของเมทอดในรูปของบล็อก\n", 716 | "\n", 717 | "### คำสั่ง return\n", 718 | "\n", 719 | "เมทอดสามารถที่จะคืนค่าผลลัพธ์กลับไปยังผู้เรียกได้ ชนิดของข้อมูลที่คืนค่าจะระบุที่ _return-type_ ถ้า _return-type_ ไม่ใช่ `void` ในตัวโค้ดของเมทอดจะต้องมีคำสั่ง `return` เพื่อคืนค่ากลับ เมื่อพบคำสั่ง `return` เมทอดจะสิ้นสุดการทำงานพร้อมกับส่งค่าที่ระบุในคำสั่ง `return` กลับเป็นผลลัพธ์\n", 720 | "\n", 721 | "ในกรณีที่ระบุ _return-type_ เป็น `void` ซึ่งหมายความว่าเมทอดจะไม่มีการคืนค่ากลับ เมทอดนี้จะมีคำสั่ง `return` หรือไม่มีก็ได้ ถ้ามีก็จะเป็นคำสั่ง `return` แบบไม่ระบุค่า แต่ถ้าไม่มี เมทอดก็จะทำงานจนถึงคำสั่งสุดท้ายในตัวเมทอดแล้วจึงคืนการทำงานกลับไปยังผู้เรียก\n", 722 | "\n", 723 | "ตัวอย่างนี้แสดงการประกาศและใช้เมทอดของคลาส" 724 | ] 725 | }, 726 | { 727 | "cell_type": "code", 728 | "execution_count": 49, 729 | "metadata": {}, 730 | "outputs": [ 731 | { 732 | "name": "stdout", 733 | "output_type": "stream", 734 | "text": [ 735 | "Hello, my name is John.\n", 736 | "Do you know that absolute of -17 is 17?\n", 737 | "Hello, my name is Jim.\n", 738 | "Do you know that I like sushi?\n" 739 | ] 740 | } 741 | ], 742 | "source": [ 743 | "public class StaticMethodDemo {\n", 744 | " public static int abs(int value) {\n", 745 | " if (value < 0) {\n", 746 | " return -value;\n", 747 | " } else {\n", 748 | " return value;\n", 749 | " }\n", 750 | " }\n", 751 | " \n", 752 | " public static void printGreeting(String name, String message) {\n", 753 | " System.out.printf(\"Hello, my name is %s.%n\", name);\n", 754 | " System.out.printf(\"Do you know that %s?%n\", message);\n", 755 | " }\n", 756 | " \n", 757 | " public static void main(String[] args) {\n", 758 | " int value = -17;\n", 759 | " \n", 760 | " String message = String.format(\"absolute of %d is %d\", value, abs(value));\n", 761 | " \n", 762 | " printGreeting(\"John\", message);\n", 763 | " \n", 764 | " StaticMethodDemo.printGreeting(\"Jim\", \"I like sushi\");\n", 765 | " }\n", 766 | "}\n", 767 | "\n", 768 | "StaticMethodDemo.main(new String[0]);" 769 | ] 770 | }, 771 | { 772 | "cell_type": "markdown", 773 | "metadata": {}, 774 | "source": [ 775 | "เมทอด `abs` รับพารามิเตอร์ 1 ตัว และคืนค่ากลับเป็นชนิด `int` ในขณะที่เมทอด `printGreeting` รับพารามิเตอร์ 2 ตัว และไม่คืนค่ากลับ จึงไม่ต้องมีคำสั่ง `return` ในตัวเมทอด\n", 776 | "\n", 777 | "ในเมทอด `main` มีการเรียกใช้เมทอด `printGreeting` 2 ครั้ง ครั้งแรกเรียกชื่อตรง ๆ ครั้งที่สองเรียกผ่านชื่อคลาส ในกรณีที่เมทอดที่เรียกกับเมทอดที่ถูกเรียกอยู่ในคลาสเดียวกัน เราไม่จำเป็นต้องระบุชื่อคลาส แต่ถ้าอยู่ต่างคลาสกัน เราต้องระบุชื่อคลาสด้วยเสมอ\n", 778 | "\n", 779 | "### เมทอด main\n", 780 | "\n", 781 | "เมทอด `main` เป็นเมทอดของคลาสที่เราใช้มาตั้งแต่ต้น เมทอดนี้มีความสำคัญเป็นพิเศษเนื่องจากโปรแกรมจะเริ่มต้นทำงานที่เมทอดนี้ เมทอด `main` จึงเป็นเสมือนตัวตั้งต้นที่ต้องจัดเตรียมสภาวะการทำงานของโปรแกรมให้พร้อมแล้วดำเนินการต่อไป\n", 782 | "\n", 783 | "เมทอด `main` จะเป็นต้องเป็นเมทอดของคลาสซึ่งประกาศด้วยตัวกำหนด `static` เพราะเมทอดของคลาสสามารถเรียกใช้ได้เลยโดยไม่ต้องมีอ็อบเจกต์มาอ้างอิง ขณะที่โปรแกรมเริ่มทำงานระบบจะไม่มีอ็อบเจกต์ใด ๆ อยู่ ดังนั้นเมทอดแรกที่จะเรียกได้ต้องสามารถเรียกได้โดยไม่ต้องอ้างอิงอ็อบเจกต์ เมทอด `main` จึงต้องมีสถานะเป็นเมทอดของคลาส\n", 784 | "\n", 785 | "`main` จะรับพารามิเตอร์เป็นอาร์เรย์ของสตริง ซึ่งเราสามารถส่งเข้ามาให้ได้โดยการกำหนดตอนที่สั่งรันโปรแกรม เช่น กำหนดจาก Command Prompt หรือ Terminal แต่ในวิชานี้เราไม่ได้ใช้คุณสมบัตินั้น จึงไม่กล่าวถึงในที่นี้\n", 786 | "\n", 787 | "ในบทเรื่องคลาสและอ็อบเจกต์ เราจะศึกษาเมทอดอีกประเภท เรียกว่าเมทอดของอินสแตนซ์ (instance method) ซึ่งผูกติดกับอ็อบเจกต์ ไม่สามารถเรียกใช้ผ่านคลาสได้ ต้องเรียกผ่านอ็อบเจกต์เท่านั้น\n", 788 | "\n", 789 | "### ขอบเขตของตัวแปร\n", 790 | "\n", 791 | "ขอบเขต (scope) ของตัวแปรหมายถึงส่วนของโปรแกรมที่สามารถมองเห็นและอ้างอิงตัวแปรเหล่านั้นได้ ตัวแปรที่ประกาศขึ้นในบล็อกใด ๆ จะมีขอบเขตตั้งแต่จุดที่ประกาศไปจนถึงจุดสิ้นสุดบล็อกนั้น ๆ ภายในบล็อกจะไม่สามารถประกาศตัวแปรชื่อซ้ำกันได้\n", 792 | "\n", 793 | "ตัวแปรที่ประกาศเป็นพารามิเตอร์ของเมทอดหรือคอนสตรักเตอร์จะมีขอบเขตทั้งเมทอดหรือคอนสตรักเตอร์นั้น ๆ\n", 794 | "\n", 795 | "ตัวแปรที่ประกาศในส่วนกำหนดค่าตั้งต้นของคำสั่ง `for` จะมีขอบเขตตลอดทั้งคำสั่ง `for` นั้น\n", 796 | "\n", 797 | "ตัวแปรที่ประกาศกับคลาส (ตัวแปรของคลาสและตัวแปรของอินสแตนซ์) จะมีขอบเขตตลอดทั้งคลาส\n", 798 | "\n", 799 | "### การบดบัง\n", 800 | "\n", 801 | "โดยปกติแล้ว เราไม่สามารถประกาศตัวแปรชื่อซ้ำกันภายในบล็อกได้ แม้ว่าตัวแปรหนึ่งจะประกาศที่บล็อกที่ครอบอยู่หรือเป็นพารามิเตอร์และอีกตัวประกาศในบล็อกที่ซ้อนอยู่ด้านในก็ไม่สามารถทำได้ แต่มีข้อยกเว้นอยู่กรณีหนึ่ง คือการที่ตัวแปรตัวหนึ่งประกาศในส่วนของคลาส และอีกตัวหนึ่งประกาศในส่วนของพารามิเตอร์ของเมทอดหรือภายในเมทอด\n", 802 | "\n", 803 | "การประกาศแบบนี้สามารถทำได้ แต่จะทำให้เกิดการบดบัง (shadowing) ขึ้น ซึ่งหมายความว่าเราจะไม่สามารถอ้างอิงถึงตัวแปรของคลาสหรืออ็อบเจกต์ตรง ๆ ในเมทอดนั้นได้ เนื่องจากถูกตัวแปรภายในบดบังอยู่" 804 | ] 805 | }, 806 | { 807 | "cell_type": "code", 808 | "execution_count": 4, 809 | "metadata": {}, 810 | "outputs": [ 811 | { 812 | "name": "stdout", 813 | "output_type": "stream", 814 | "text": [ 815 | "In shadowedByParameter: parameter x = 12.5\n", 816 | "In shadowedByLocalVariable: local varaible x = 2.5\n", 817 | "In shadowedByLocalVariable: class variable x = 1.5\n", 818 | "In blockScoping, inside block: x = 1234.5\n", 819 | "In blockScoping, outside block: x = 1.5\n" 820 | ] 821 | } 822 | ], 823 | "source": [ 824 | "public class ScopeDemo {\n", 825 | " private static double x = 1.5;\n", 826 | " \n", 827 | " public static void shadowedByParameter(double x) {\n", 828 | " System.out.printf(\"In shadowedByParameter: parameter x = %.1f%n\", x);\n", 829 | " }\n", 830 | " \n", 831 | " public static void shadowedByLocalVariable() {\n", 832 | " double x = 2.5;\n", 833 | "\n", 834 | " System.out.printf(\"In shadowedByLocalVariable: local varaible x = %.1f%n\", x);\n", 835 | " System.out.printf(\"In shadowedByLocalVariable: class variable x = %.1f%n\", ScopeDemo.x);\n", 836 | " }\n", 837 | " \n", 838 | " public static void blockScoping() {\n", 839 | " do {\n", 840 | " double x = 1234.5;\n", 841 | " System.out.printf(\"In blockScoping, inside block: x = %.1f%n\", x);\n", 842 | " } while (false);\n", 843 | " \n", 844 | " System.out.printf(\"In blockScoping, outside block: x = %.1f%n\", x);\n", 845 | " }\n", 846 | " \n", 847 | " public static void main(String[] args) {\n", 848 | " shadowedByParameter(12.5);\n", 849 | " shadowedByLocalVariable();\n", 850 | " blockScoping();\n", 851 | " }\n", 852 | "}\n", 853 | "\n", 854 | "ScopeDemo.main(new String[0]);" 855 | ] 856 | }, 857 | { 858 | "cell_type": "markdown", 859 | "metadata": {}, 860 | "source": [ 861 | "ตัวอย่างนี้แสดงให้เห็นขอบเขตของ `x` ในบริบทต่าง ๆ กัน\n", 862 | "\n", 863 | "`x` ที่เป็นตัวแปรชั้นนอกสุดประกาศเป็นตัวแปรของคลาส มีค่าเป็น 1.5 แต่ในเมทอด `shadowedByParameter` มีการประกาศพารามิเตอร์ `x` ซึ่งจะไปบดบัง `x` ที่เป็นตัวแปรของคลาส ทำให้เวลาที่แสดงค่าออกมาจะเป็นค่าของอาร์กิวเมนต์ที่ผ่านเข้ามาให้พารามิเตอร์ (12.5)\n", 864 | "\n", 865 | "ในเมทอด `shadowedByLocalVariable` มีการประกาศตัวแปร `x` ภายในเมทอดโดยกำหนดให้มีค่าเท่ากับ 2.5 เมื่อแสดงค่าออกมาก็จะเป็นค่านี้เนื่องจาก `x` ตัวนี้ไปบดบัง `x` ที่เป็นตัวแปรของคลาสเช่นกัน แต่ในเมทอดนี้ยังมีการอ้างอิงถึง `x` ที่เป็นตัวแปรของคลาสด้วยโดยอ้างผ่านชื่อคลาสเป็น `ScopeDemo.x` ซึ่งนี่เป็นวิธีหนึ่งในการอ้างตัวแปรของคลาสที่ถูกบดบัง\n", 866 | "\n", 867 | "ในเมทอด `blockScoping` มีการประกาศตัวแปร `x` ในคำสั่งแบบบล็อกที่เป็นส่วนหนึ่งของ `do` ... `while` ซึ่ง `x` ตัวนี้จะมีขอบเขตเฉพาะภายในบล็อกนี้เท่านั้น ดังนั้นเมื่อสั่งให้แสดงค่าของ `x` ภายในบล็อกจึงได้ค่า 1234.5 แต่เมื่อสั่งให้แสดงค่าของ `x` ภายนอกบล็อกจึงได้ค่า 1.5 เนื่องจากภายนอกบล็อกไม่มี `x` อื่นมาบดบัง `x` ที่เป็นตัวแปรของคลาสแล้วนั่นเอง\n", 868 | "\n", 869 | "> **หมายเหตุ** ตัวแปรที่ประกาศขึ้นภายในเมทอดหรือคอนสตรักเตอร์โดยไม่กำหนดค่าตั้งต้นให้จะไม่มีค่าตั้งต้นโดยปริยายเหมือนตัวแปรของคลาสหรืออ็อบเจกต์ ภาษา Java บังคับให้ตัวแปรภายในเหล่านี้ต้องมีค่าก่อนนำไปใช้อ้างอิง ถ้าไม่ได้กำหนดค่าตั้งต้นให้ก็จะต้องมีคำสั่งกำหนดค่าให้ในภายหลังก่อนจะถึงคำสั่งที่จะนำค่าไปใช้ มิฉะนั้นโปรแกรมก็จะคอมไพล์ไม่ผ่าน" 870 | ] 871 | } 872 | ], 873 | "metadata": { 874 | "kernelspec": { 875 | "display_name": "Java", 876 | "language": "java", 877 | "name": "java" 878 | }, 879 | "language_info": { 880 | "codemirror_mode": "java", 881 | "file_extension": ".java", 882 | "mimetype": "text/x-java-source", 883 | "name": "Java", 884 | "pygments_lexer": "java", 885 | "version": "9.0.4+11" 886 | } 887 | }, 888 | "nbformat": 4, 889 | "nbformat_minor": 2 890 | } 891 | -------------------------------------------------------------------------------- /02 - Control Structures/BlockDemo.java: -------------------------------------------------------------------------------- 1 | public class BlockDemo { 2 | public static void main(String[] args) { 3 | int a = 10; 4 | double b = 5.25; 5 | 6 | System.out.println("Outside of the block"); 7 | 8 | { 9 | double c = 15.4; 10 | 11 | System.out.println("Inside the block"); 12 | System.out.println("... where a + b = " + (a + b)); 13 | } 14 | 15 | for (int a = 0; a < 10; a++) { 16 | System.out.println("Hey, a is redefined"); 17 | } 18 | 19 | System.out.println("Outside again"); 20 | System.out.println("... where a + b = " + (a + b)); 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /02 - Control Structures/Control Structures.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# โครงสร้างควบคุม\n", 8 | "\n", 9 | "โปรแกรมในภาษา Java ประกอบขึ้นจากคำสั่ง (statement) ซึ่งโดยปกติจะทำงานตามลำดับไปจนสิ้นสุดส่วนของโปรแกรม เช่น เริ่มที่ต้นเมทอดไปจนสิ้นสุดเมทอด แต่โดยปกติแล้ว การทำงานใด ๆ ที่มีความซับซ้อนจะไม่ได้ทำตามลำดับขั้นตอนไปตลอด แต่มักจะต้องมีการตัดสินใจ การเลือกทำกรณีเฉพาะที่แตกต่างกัน การทำบางขั้นตอนซ้ำ ๆ การออกจากส่วนของโปรแกรมก่อนจะถึงจุดสิ้นสุด หรือลักษณะอื่น ๆ ที่ทำให้โปรแกรมไม่ทำงานไปตามลำดับปกติ\n", 10 | "\n", 11 | "กลไกที่มาสนับสนุนการเปลี่ยนแปลงลำดับการทำงานให้เป็นไปตามที่เราต้องการนั้นคือโครงสร้างควบคุม (control structure) ซึ่งเป็นรูปแบบคำสั่งที่กำหนดทิศทางการทำงานต่าง ๆ ได้ โครงสร้างควบคุมที่เราจะพิจารณากันในที่นี้คือ ทางเลือก การทำซ้ำ และการเรียกเมทอด\n", 12 | "\n", 13 | "> **หมายเหตุ** โปรแกรมตัวอย่างที่แสดงในบทนี้บางส่วนอาจจะเป็นเพียงส่วนของโปรแกรม การนำไปรันอาจต้องเพิ่มโครงของคลาสและเมทอด `main` ตามความเหมาะสม\n", 14 | "\n", 15 | "## การทำงานตามลำดับ\n", 16 | "\n", 17 | "ในสภาวะปกติที่ไม่มีคำสั่งควบคุมลำดับการทำงานเลย การทำงานของโปรแกรมจะเป็นไปตามลำดับ จากซ้ายไปขวาถ้ามีหลายคำสั่งในบรรทัดเดียวกัน และจากบนลงล่าง เราเรียกลักษณะการทำงานแบบนี้ว่าการควบคุมแบบตามลำดับ (sequential control) ซึ่งเป็นรูปแบบโดยปริยายเมื่อไม่มีการใช้คำสั่งควบคุมใด ๆ\n", 18 | "\n", 19 | "### คำสั่งแบบบล็อก\n", 20 | "\n", 21 | "Java มีโครงสร้างพิเศษที่ทำให้เราสามารถรวมหลาย ๆ คำสั่งไว้ด้วยกันแล้วมองเป็นคำสั่งเดียวได้ เรียกว่าคำสั่งแบบบล็อก (block statement) ซึ่งประกอบด้วยคำสั่งหลาย ๆ คำสั่งครอบด้วยเครื่องหมายวงเล็บปีกกา" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": 7, 27 | "metadata": {}, 28 | "outputs": [ 29 | { 30 | "name": "stdout", 31 | "output_type": "stream", 32 | "text": [ 33 | "Outside of the block\n", 34 | "Inside the block\n", 35 | "... where a + c = 25.4\n", 36 | "Outside again\n", 37 | "... where a + b = 15.25\n", 38 | "... and where there is no c\n" 39 | ] 40 | } 41 | ], 42 | "source": [ 43 | "public class BlockDemo {\n", 44 | " public static void main(String[] args) {\n", 45 | " int a = 10;\n", 46 | " double b = 5.25;\n", 47 | " \n", 48 | " System.out.println(\"Outside of the block\");\n", 49 | " \n", 50 | " {\n", 51 | " double c = 15.4;\n", 52 | " \n", 53 | " System.out.println(\"Inside the block\");\n", 54 | " System.out.println(\"... where a + c = \" + (a + c));\n", 55 | " }\n", 56 | " \n", 57 | " System.out.println(\"Outside again\");\n", 58 | " System.out.println(\"... where a + b = \" + (a + b));\n", 59 | " System.out.println(\"... and where there is no c\");\n", 60 | " }\n", 61 | "}\n", 62 | "\n", 63 | "BlockDemo.main(new String[0]);" 64 | ] 65 | }, 66 | { 67 | "cell_type": "markdown", 68 | "metadata": {}, 69 | "source": [ 70 | "การทำงานภายในบล็อกก็ยังคงเป็นลำดับต่อเนื่องจากภายนอกบล็อกตามปกติ แต่จากภายนอกจะมองเสมือนคำสั่งภายในบล็อกรวมกันเป็นคำสั่งเดียว การประกาศตัวแปรใด ๆ ภายในบล็อกจะมีขอบเขตเพียงภายในบล็อก ในตัวอย่างนี้ ส่วนที่อยู่นอกบล็อกจะไม่สามารถอ้างอิงตัวแปร `c` ได้เนื่องจากประกาศอยู่ภายในบล็อก\n", 71 | "\n", 72 | "## การทำงานแบบมีทางเลือก\n", 73 | "\n", 74 | "โครงสร้างควบคุมที่ทำให้เราสามารถเลือกการทำงานได้ตามเงื่อนไขที่ระบุในภาษา Java มีอยู่ 2 แบบ ได้แก่ `if`/`else` และ `switch`\n", 75 | "\n", 76 | "คำสั่ง `if`/`else` จะเลือกทำงานระหว่าง 2 ทางเลือกตามเงื่อนไข ถ้าเงื่อนไขเป็นจริงก็จะทำทางเลือกแรก แต่ถ้าเงื่อนไขไม่จริง ในกรณีที่ไม่มี `else` ก็จะไม่ทำอะไร แต่ทำคำสั่งถัดไปต่อไปเลย ในกรณีที่มี `else` ก็จะทำทางเลือกของ `else` ก่อนจะทำคำสั่งถัดไปต่อไป\n", 77 | "\n", 78 | "รูปแบบวากยสัมพันธ์ของ `if` เป็นดังนี้\n", 79 | "\n", 80 | "```\n", 81 | "if (condition)\n", 82 | " true-branch\n", 83 | "```\n", 84 | "\n", 85 | "แบบที่มี `else` มีลักษณะดังนี้\n", 86 | "\n", 87 | "```\n", 88 | "if (condition)\n", 89 | " true-branch\n", 90 | "else\n", 91 | " false-branch\n", 92 | "```\n", 93 | "\n", 94 | "_condition_ คือเงื่อนไขที่จะทดสอบ มีค่าเป็นชนิด `boolean` คือเป็นค่า `true` หรือ `false`\n", 95 | "\n", 96 | "_true-branch_ คือคำสั่งที่จะให้ทำถ้าเงื่อนไขเป็นจริง และจะทำ _false-branch_ สำหรับกรณีที่มี `else` และเงื่อนไขเป็นเท็จ คำสั่งที่ให้ทำในแต่ละทางเลือกจะเป็นคำสั่งเดี่ยว ๆ คำสั่งเดียว หรือจะเป็นหลายคำสั่งรวมกันในรูปของคำสั่งแบบบล็อกก็ได้\n", 97 | "\n", 98 | "### รูปแบบของเงื่อนไข\n", 99 | "\n", 100 | "เงื่อนไขที่ใช้กับ `if` จะต้องมีค่าเป็น `true` หรือ `false` หมายความว่าต้องเป็นค่าชนิด `boolean` ซึ่งจะได้จาก\n", 101 | "\n", 102 | "1. ค่าชนิด `boolean` โดยตรง ได้แก่ ค่า `true` และ `false`\n", 103 | "2. ตัวแปรชนิด `boolean`\n", 104 | "3. ผลจากตัวดำเนินการเปรียบเทียบหรือทดสอบ เช่น `==`, `>=`, `<` หรือ `instanceof`\n", 105 | "4. ผลจากตัวดำเนินการตรรกะ เช่น `&&`, `||` หรือ `!`\n", 106 | "\n", 107 | "ตัวดำเนินการเปรียบเทียบมีทั้งหมด 6 แบบ ได้แก่ น้อยกว่า (`<`) น้อยกว่าหรือเท่ากัน (`<=`) มากกว่าหรือเท่ากัน (`>=`) มากกว่า (`>`) เท่ากัน (`==`) และไม่เท่ากัน (`!=`) ส่วนตัวดำเนินการทดสอบมีแบบเดียวคือ `instanceof` ซึ่งใช้ทดสอบความสัมพันธ์ระหว่างอ็อบเจกต์และคลาส\n", 108 | "\n", 109 | "ตัวดำเนินการตรรกะมีทั้งหมด 6 แบบ ได้แก่ นิเสธหรือ not (`!`), and (`&&`), or, (`||`), strict exclusive-or (`^`), strict and (`&`) และ strict or (`|`)\n", 110 | "\n", 111 | "โดยปกติแล้ว ตัวดำเนินการตรรกะที่เราใช้บ่อยคือ `!`, `&&` และ `||` ส่วนอีก 3 ตัวที่เหลือมักจะพบการใช้เป็นการดำเนินการระดับบิตกับข้อมูลชนิดตัวเลขมากกว่า\n", 112 | "\n", 113 | "ตัวดำเนินการตรรกะมีลำดับความสำคัญต่ำกว่าตัวดำเนินการเปรียบเทียบ เพราะฉะนั้นถ้าอยู่ร่วมกันในนิพจน์เดียวกัน ตัวดำเนินการเปรียบเทียบจะถูกทำก่อน\n", 114 | "\n", 115 | "ตัวอย่างต่อไปนี้แสดงการใช้งาน `if` และเงื่อนไขในรูปแบบต่าง ๆ" 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": 9, 121 | "metadata": {}, 122 | "outputs": [ 123 | { 124 | "name": "stdout", 125 | "output_type": "stream", 126 | "text": [ 127 | "Second case is true\n", 128 | "Nothing is true\n" 129 | ] 130 | } 131 | ], 132 | "source": [ 133 | "int x = 10;\n", 134 | "int y = 20;\n", 135 | "boolean b = true;\n", 136 | "boolean c = b && x < y; // true\n", 137 | "\n", 138 | "if (x < y - 10)\n", 139 | " System.out.println(\"First case is true\");\n", 140 | "\n", 141 | "if (c) {\n", 142 | " // Using block statement here\n", 143 | " System.out.println(\"Second case is true\");\n", 144 | "} else {\n", 145 | " // Also block statement here\n", 146 | " System.out.println(\"Second case is false\");\n", 147 | "}\n", 148 | "\n", 149 | "// Nested if's, all using block statements\n", 150 | "if (x + 1 > y) {\n", 151 | " System.out.println(\"Third case is true\");\n", 152 | "} else if (x + 5 > y && y < 30) {\n", 153 | " System.out.println(\"Fourth case is true\");\n", 154 | "} else if (x + 10 > y && y < 40) {\n", 155 | " System.out.println(\"Fifth case is true\");\n", 156 | "} else {\n", 157 | " System.out.println(\"Nothing is true\");\n", 158 | "}" 159 | ] 160 | }, 161 | { 162 | "cell_type": "markdown", 163 | "metadata": {}, 164 | "source": [ 165 | "### นิพจน์เงื่อนไข\n", 166 | "\n", 167 | "เราสามารถใช้ทางเลือกในนิพจน์ได้ในรูปของนิพจน์เงื่อนไข ซึ่งมีรูปแบบเป็น `condition ? value1 : value2` ซึ่งจะให้ผลลัพธ์เป็นค่าของ _value1_ ถ้า _condition_ เป็นจริงหรือ _value2_ ถ้า _condition_ เป็นเท็จ" 168 | ] 169 | }, 170 | { 171 | "cell_type": "code", 172 | "execution_count": 10, 173 | "metadata": {}, 174 | "outputs": [ 175 | { 176 | "name": "stdout", 177 | "output_type": "stream", 178 | "text": [ 179 | "10\n" 180 | ] 181 | } 182 | ], 183 | "source": [ 184 | "int x = -10;\n", 185 | "int y = x < 0 ? -x : x;\n", 186 | "\n", 187 | "System.out.println(y);" 188 | ] 189 | }, 190 | { 191 | "cell_type": "markdown", 192 | "metadata": {}, 193 | "source": [ 194 | "ในตัวอย่างนี้ค่าของ `y` จะขึ้นอยู่กับเงื่อนไข `x < 0` ซึ่งถ้าจริง `y` ก็จะเท่ากับ `-x` และถ้าไม่จริง `y` ก็จะเท่ากับ `x` ในกรณีนี้ค่าของ `x` เป็นลบ จึงเข้าเงื่อนไขที่เป็นค่า `-x` ค่าของ `y` จึงได้เป็น 10\n", 195 | "\n", 196 | "### คำสั่งทางเลือกแบบหลายทาง\n", 197 | "\n", 198 | "ในบางกรณีเราต้องการเลือกทางเลือกจากค่าของตัวแปรหรือค่าผลลัพธ์ของนิพจน์ โดยผลลัพธ์แต่ละค่าจะเป็นทางเลือกที่ต่างกัน และมีหลายทางเลือก ในกรณีนี่เราอาจจะใช้คำสั่ง `switch` ในการจัดการ\n", 199 | "\n", 200 | "คำสั่ง `switch` จะอยู่ในรูป\n", 201 | "\n", 202 | "```\n", 203 | "switch (expression) {\n", 204 | " case constant1: branch1\n", 205 | " case constant2: branch2\n", 206 | " ...\n", 207 | " default: branchn\n", 208 | "}\n", 209 | "```\n", 210 | "\n", 211 | "ส่วนของ _expression_ เป็นค่าที่ `switch` จะพิจารณาเพื่อเลือกทางเลือก โดยต้องมีชนิดเป็น `int`, `short`, `char`, `byte`, `enum`, `String` หรือเป็นคลาสหีบห่อ (wrapper class) ของ `int`, `short`, `char` และ `byte`\n", 212 | "\n", 213 | "`switch` จะเทียบค่าของ _expression_ กับค่า _constant_ ของแต่ละ `case` ว่าตรงกับอันไหน แล้วก็จะเริ่มทำคำสั่งตาม _branch_ ที่ตรงกับ `case` นั้น โดยจะทำไปเรื่อย ๆ จนกว่าจะสิ้นสุดคำสั่ง `switch` หรือจนกว่าจะเจอคำสั่ง `break`\n", 214 | "\n", 215 | "ในกรณีที่ค่าของ _expression_ ไม่ตรงกับ `case` ใดเลย จะทำคำสั่งที่กำหนดที่ `default` ยกเว้นจะไม่ได้กำหนดส่วนของ `default` เอาไว้\n", 216 | "\n", 217 | "ตัวอย่างต่อไปนี้เป็นการใช้ `switch` เบื้องต้น" 218 | ] 219 | }, 220 | { 221 | "cell_type": "code", 222 | "execution_count": 11, 223 | "metadata": {}, 224 | "outputs": [ 225 | { 226 | "name": "stdout", 227 | "output_type": "stream", 228 | "text": [ 229 | "Wednesday\n" 230 | ] 231 | } 232 | ], 233 | "source": [ 234 | "int day = 4;\n", 235 | "String dayName;\n", 236 | "\n", 237 | "switch (day) {\n", 238 | " case 1:\n", 239 | " dayName = \"Sunday\";\n", 240 | " break;\n", 241 | " case 2:\n", 242 | " dayName = \"Monday\";\n", 243 | " break;\n", 244 | " case 3:\n", 245 | " dayName = \"Tuesday\";\n", 246 | " break;\n", 247 | " case 4:\n", 248 | " dayName = \"Wednesday\";\n", 249 | " break;\n", 250 | " case 5:\n", 251 | " dayName = \"Thursday\";\n", 252 | " break;\n", 253 | " case 6:\n", 254 | " dayName = \"Friday\";\n", 255 | " break;\n", 256 | " case 7:\n", 257 | " dayName = \"Saturday\";\n", 258 | " break;\n", 259 | " default:\n", 260 | " dayName = \"No such day\";\n", 261 | "}\n", 262 | "\n", 263 | "System.out.println(dayName);" 264 | ] 265 | }, 266 | { 267 | "cell_type": "markdown", 268 | "metadata": {}, 269 | "source": [ 270 | "จากตัวอย่างนี้ ค่าของ `day` คือ 4 ซึ่งจะตรงกับ `case` ที่กำหนดให้ `dayName` มีค่าเป็น `\"Wednesday\"` แล้วก็ `break` เลย\n", 271 | "\n", 272 | "ในบางครั้งเราอาจไม่ต้องการ `break` ในบาง `case` เพื่อให้ทำงานต่อไปยัง `case` ต่อไปเลยโดยไม่ออกจาก `switch` ลักษณะแบบนี้เรียกว่า fall through คือปล่อยให้ไหลต่อไป ลองดูตัวอย่างต่อไปนี้" 273 | ] 274 | }, 275 | { 276 | "cell_type": "code", 277 | "execution_count": 27, 278 | "metadata": {}, 279 | "outputs": [ 280 | { 281 | "name": "stdout", 282 | "output_type": "stream", 283 | "text": [ 284 | "You've got a board game, a lollipop, a fancy mask, and a lot of fun.\n" 285 | ] 286 | } 287 | ], 288 | "source": [ 289 | "Random dice = new Random();\n", 290 | "\n", 291 | "String prizes = \"You've got \";\n", 292 | "\n", 293 | "switch (dice.nextInt(6) + 1) {\n", 294 | " case 1:\n", 295 | " prizes += \"a teddy bear.\";\n", 296 | " break;\n", 297 | " case 2:\n", 298 | " prizes += \"a model robot, \";\n", 299 | " case 3:\n", 300 | " prizes += \"a board game, \";\n", 301 | " case 4: case 5:\n", 302 | " prizes += \"a lollipop, \";\n", 303 | " case 6:\n", 304 | " prizes += \"a fancy mask, \";\n", 305 | " default:\n", 306 | " prizes += \"and a lot of fun.\";\n", 307 | "}\n", 308 | "\n", 309 | "System.out.println(prizes);" 310 | ] 311 | }, 312 | { 313 | "cell_type": "markdown", 314 | "metadata": {}, 315 | "source": [ 316 | "ตัวอย่างนี้เป็นการทอดลูกเต๋าชิงรางวัล โดยสุ่มค่า 1-6 ด้วยเมทอด `nextInt` (ซึ่งจะได้ค่าในช่วง 0-5 จึงบวกเพิ่มอีก 1) แล้วนำค่าที่สุ่มได้ไปกำหนดรางวัลที่ได้\n", 317 | "\n", 318 | "ในตัวอย่างเราจะเห็นว่า `case 1` จะให้ teddy bear แล้ว `break` เลย แต่ตั้งแต่ `case 2` เป็นต้นไปจะไม่มีการ `break` นอกจากนี้ `case 4` และ `case 5` ยังอยู่รวมกัน ซึ่งหมายความว่าถ้าได้ค่า 4 หรือ 5 ก็จะมาตกกรณีเดียวกัน\n", 319 | "\n", 320 | "ในการรันตัวอย่าง พบว่าทอดลูกเต๋าได้ค่า 3 สตริง `prizes` จึงได้ต่อข้อความมาตั้งแต่ `\"a board game, \"` ของ `case 3` ไหลต่อเนื่องไปยัง `\"a lollipop, \"` ของ `case 4` และ `case 5` ไปถึง `\"a fancy mask, \"` ของ `case 6` และไปจบที่ `\"and a lot of fun.\"` ของ `default` เนื่องจากไม่มีการ `break` เลย\n", 321 | "\n", 322 | "กรณีตัวอย่างนี้แสดงให้เห็นการใช้งาน `switch` ในการสะสมค่าต่อเนื่องตั้งแต่กรณีที่ค่าตรงกันไปจนจบ ซึ่งในการใช้งานจริงอาจพบกรณีลักษณะนี้อยู่บ้าง\n", 323 | "\n", 324 | "> **หมายเหตุ** การใช้ `Random` เพื่อสุ่มค่าจะต้องมีการ `import java.util.Random` ก่อน\n", 325 | "\n", 326 | "## การวนซ้ำ\n", 327 | "\n", 328 | "คำสั่งสำหรับวนซ้ำในภาษา Java มีอยู่ 3 รูปแบบด้วยกัน ได้แก่ `while`, `do` ... `while` และ `for` โดย 2 แบบแรกนิยมใช้กับการวนซ้ำตามเงื่อนไข ส่วนแบบหลังนิยมใช้กับการวนซ้ำแบบรู้จำนวนรอบล่วงหน้า ทั้งนี้ ในความเป็นจริงแล้วคำสั่งวนซ้ำทั้ง 3 แบบสามารถใช้แทนกันได้ทั้งหมด\n", 329 | "\n", 330 | "### คำสั่ง while\n", 331 | "\n", 332 | "คำสั่ง `while` หรือลูป (loop) `while` ใช้สำหรับการวนซ้ำโดยพิจารณาตามเงื่อนไข ถ้าเงื่อนไขที่กำหนดเป็นจริงก็ทำซ้ำต่อไปแล้วกลับมาพิจารณาเงื่อนไขใหม่ เมื่อใดที่เงื่อนไขไม่จริงก็จะสิ้นสุดการทำงาน\n", 333 | "\n", 334 | "รูปแบบของคำสั่ง `while` เป็นดังนี้\n", 335 | "\n", 336 | "```\n", 337 | "while (condition)\n", 338 | " body\n", 339 | "```\n", 340 | "\n", 341 | "_condition_ คือเงื่อนไขที่พิจารณาในการวนซ้ำ โดยจะทำส่วนของ _body_ ตราบเท่าที่เงื่อนไขยังเป็นจริงอยู่ ดังตัวอย่างนี้" 342 | ] 343 | }, 344 | { 345 | "cell_type": "code", 346 | "execution_count": 33, 347 | "metadata": {}, 348 | "outputs": [ 349 | { 350 | "name": "stdout", 351 | "output_type": "stream", 352 | "text": [ 353 | "Total: 45\n" 354 | ] 355 | } 356 | ], 357 | "source": [ 358 | "int x = 10;\n", 359 | "int y = 5;\n", 360 | "int sum = 0;\n", 361 | "\n", 362 | "while (x > y) {\n", 363 | " sum += x + y;\n", 364 | " x--;\n", 365 | " y++;\n", 366 | "}\n", 367 | "\n", 368 | "System.out.println(\"Total: \" + sum);" 369 | ] 370 | }, 371 | { 372 | "cell_type": "markdown", 373 | "metadata": {}, 374 | "source": [ 375 | "ลูปนี้จะทำงานไปเรื่อย ๆ ตราบเท่าที่ค่าของ `x` ยังมากกว่า `y` โดยในแต่ละรอบจะลดค่าของ `x` และเพิ่มค่าของ `y` ด้วย\n", 376 | "\n", 377 | "ในกรณีที่เงื่อนไขไม่เป็นจริงตั้งแต่แรก ลูป `while` จะไม่ทำงานเลย\n", 378 | "\n", 379 | "#### ตัวดำเนินการเพิ่มค่าและลดค่า\n", 380 | "\n", 381 | "ตัวดำเนินการ `++` และ `--` ที่เห็นในตัวอย่างคือตัวดำเนินการเพิ่มค่าและลดค่าซึ่งใช้ได้กับทั้งตัวแปรชนิดจำนวนเต็มและทศนิยม `++` คือการเพิ่มค่าไปอีก 1 ค่าให้กับตัวแปรนั้น และ `--` คือการลดค่าลงไป 1 ค่า เราสามารถใช้ตัวดำเนินการนี้ร่วมกับตัวแปรเป็นส่วนหนึ่งของนิพจน์อื่น ๆ ได้\n", 382 | "\n", 383 | "ตัวดำเนินการ `++` และ `--` มีลักษณะการใช้งาน 2 แบบ คือการเพิ่ม/ลดค่าก่อน และการเพิ่ม/ลดค่าทีหลัง แบบแรกเราจะใช้ตัวดำเนินการนี้หน้าชื่อตัวแปร ตัวแปรจะเปลี่ยนค่าทันที แต่แบบที่สองเราจะใช้ตัวดำเนินการนี้หลังชื่อตัวแปร ค่าเดิมของตัวแปรจะถูกนำไปใช้ในนิพจน์ ตัวแปรจะเปลี่ยนค่าทีหลัง\n", 384 | "\n", 385 | "ลองดูตัวอย่างต่อไปนี้" 386 | ] 387 | }, 388 | { 389 | "cell_type": "code", 390 | "execution_count": 30, 391 | "metadata": {}, 392 | "outputs": [ 393 | { 394 | "name": "stdout", 395 | "output_type": "stream", 396 | "text": [ 397 | "n = 6, m = 6, x = 3.5, y = 2.5\n", 398 | "n = 7, m = 5, x = 2.5, y = 14.5\n" 399 | ] 400 | } 401 | ], 402 | "source": [ 403 | "int n = 5;\n", 404 | "int m = ++n;\n", 405 | "double x = 2.5;\n", 406 | "double y = x++;\n", 407 | "\n", 408 | "System.out.printf(\"n = %d, m = %d, x = %.1f, y = %.1f%n\", n, m, x, y);\n", 409 | "\n", 410 | "y = n++ + --m + x--;\n", 411 | "\n", 412 | "System.out.printf(\"n = %d, m = %d, x = %.1f, y = %.1f%n\", n, m, x, y);" 413 | ] 414 | }, 415 | { 416 | "cell_type": "markdown", 417 | "metadata": {}, 418 | "source": [ 419 | "ค่าของ `m` ถูกกำหนดให้เท่ากับ `++n` ซึ่งก็คือเพิ่มค่าให้กับ `n` เป็น 6 ก่อนแล้วจึงกำหนดให้กับ `m` ค่าของ `m` จึงเป็น 6 เช่นกัน ในทางกลับกัน ค่าของ `y` ถูกกำหนดให้เท่ากับ `x++` ซึ่งจะนำค่าของ `x` เดิมคือ 2.5 ไปกำหนดให้กับ `y` ก่อนแล้วจึงเพิ่มค่าให้กับ `x` เป็น 3.5\n", 420 | "\n", 421 | "อีกส่วนหนึ่งคือ `y = n++ + --m + x--` ซึ่งจะมีการเพิ่มค่าให้ `n` และลดค่าของ `m` และ `x` แต่จะใช้ค่าของ `n` และ `x` ก่อนการเปลี่ยนค่าในการคำนวณค่าที่จะกำหนดให้ `y`\n", 422 | "\n", 423 | "บ่อยครั้งที่เรามักจะใช้ตัวดำเนินการ `++` และ `--` เพื่อเพิ่มหรือลดค่าอย่างเดียวโดยไม่ได้นำค่าไปใช้ในนิพจน์อื่นหรือไปกำหนดให้กับตัวแปรอื่นต่อ เช่น ในตัวอย่าง `while` ที่เราใช้ `x--` และ `y++` โดด ๆ โดยไม่ได้นำค่าไปกำหนดให้ตัวแปรอื่นต่อ การใช้ในลักษณะนี้เราจะใช้ตัวดำเนินการก่อนหรือหลังชื่อตัวแปรก็ได้ผลลัพธ์เหมือนกัน\n", 424 | "\n", 425 | "#### ตัวดำเนินการรวมค่า\n", 426 | "\n", 427 | "อีกตัวดำเนินการหนึ่งที่เราเห็นในตัวอย่าง `while` คือ `+=` ซึ่งเราสามารถเรียกได้ว่าเป็นตัวดำเนินการรวมค่า การเขียนว่า `x += n` เทียบเท่าได้กับการเขียนว่า `x = x + n` คือการบวกค่ากับตัวแปรหนึ่งแล้วกำหนดค่านั้นคืนให้กับตัวแปรนั้น มองอีกอย่างได้ว่าเป็นการเอาค่าทางฝั่งขวาของตัวดำเนินการไปเพิ่มให้กับตัวแปรทางฝั่งซ้ายของตัวดำเนินการนั่นเอง ในกรณีของตัวอย่าง `sum += x + y` ก็เหมือนกับการเขียนว่า `sum = sum + (x + y)` ซึ่งก็คือการเอาค่าของ `x + y` ไปเพิ่มให้กับ `sum`\n", 428 | "\n", 429 | "นอกจาก `+=` แล้วยังมีตัวดำเนินการในลักษณะเดียวกันตัวอื่นด้วย ได้แก่ `-=`, `*=`, `/=`, `%=`, `&=`, `|=` และ `^=`\n", 430 | "\n", 431 | "### คำสั่ง do ... while\n", 432 | "\n", 433 | "ลูปที่วนซ้ำตามเงื่อนไขอีกแบบคือลูป `do` ... `while` ซึ่งมีรูปแบบดังนี้\n", 434 | "\n", 435 | "```\n", 436 | "do\n", 437 | " body\n", 438 | "while (condition);\n", 439 | "```\n", 440 | "\n", 441 | "การทำงานของ `do` ... `while` จะต่างจาก `while` ตรงที่การทำงานในส่วนของ _body_ จะเกิดขึ้นก่อนการตรวจสอบ _condition_ ซึ่งหมายความว่าไม่ว่าเงื่อนไขจะเป็นอย่างไร `do` ... `while` จะต้องทำส่วนของ _body_ อย่างน้อย 1 รอบก่อน\n", 442 | "\n", 443 | "หลังจากรอบแรกแล้ว พฤติกรรมของ `do` ... `while` จะไม่ต่างจาก `while`\n", 444 | "\n", 445 | "ตัวอย่างหนึ่งที่เราจะพบการใช้ `do` ... `while` ได้บ่อยคือการรับข้อมูลจากผู้ใช้ เราจะรับค่ามาก่อนแล้วค่อยตรวจสอบว่าค่าที่รับมาถูกต้องหรือไม่ ถ้าไม่ถูกต้องก็กลับไปรับใหม่จนกว่าจะถูกต้อง ดังตัวอย่างนี้" 446 | ] 447 | }, 448 | { 449 | "cell_type": "code", 450 | "execution_count": 36, 451 | "metadata": {}, 452 | "outputs": [], 453 | "source": [ 454 | "import java.util.Scanner;\n", 455 | "import java.util.Random;\n", 456 | "\n", 457 | "public class DoWhileDemo {\n", 458 | " public static void main(String[] args) {\n", 459 | " Scanner input = new Scanner(System.in);\n", 460 | " Random random = new Random();\n", 461 | " int secretValue = random.nextInt(9) + 1;\n", 462 | " int guess;\n", 463 | " \n", 464 | " do {\n", 465 | " System.out.print(\"Enter a number between 1 to 9: \");\n", 466 | " guess = input.nextInt();\n", 467 | " if (guess < secretValue) {\n", 468 | " System.out.println(\"Too low!\");\n", 469 | " } else if (guess > secretValue) {\n", 470 | " System.out.println(\"Too high!\");\n", 471 | " }\n", 472 | " } while (guess != secretValue);\n", 473 | " \n", 474 | " System.out.println(\"You've got it!\");\n", 475 | " }\n", 476 | "}" 477 | ] 478 | }, 479 | { 480 | "cell_type": "markdown", 481 | "metadata": {}, 482 | "source": [ 483 | "โปรแกรมนี้จะสุ่มค่าระหว่าง 1-9 แล้วให้ผู้ใช้ทายเลข โดยจะให้ทายซ้ำไปเรื่อย ๆ จนกว่าจะทายถูก ถ้าทายไม่ถูกก็จะแจ้งว่าค่าที่ทายสูงไปหรือต่ำไป เราใช้ `do` ... `while` เพราะเราต้องการรับค่าจากผู้ใช้ก่อนแล้วจึงตรวจสอบค่า\n", 484 | "\n", 485 | "ตัวอย่างผลการรันเป็นดังนี้\n", 486 | "\n", 487 | "```\n", 488 | "Enter a number between 1 to 9: 5\n", 489 | "Too low!\n", 490 | "Enter a number between 1 to 9: 7\n", 491 | "Too high!\n", 492 | "Enter a number between 1 to 9: 6\n", 493 | "You've got it!\n", 494 | "```\n", 495 | "\n", 496 | "### คำสั่ง for\n", 497 | "\n", 498 | "คำสั่งวนซ้ำตัวสุดท้ายคือคำสั่ง `for` ซึ่งมีรูปแบบดังนี้\n", 499 | "\n", 500 | "```\n", 501 | "for (initialization; condition; step)\n", 502 | " body\n", 503 | "```\n", 504 | "\n", 505 | "โครงสร้างของคำสั่ง `for` จะมีลำดับการทำงานเป็นดังนี้\n", 506 | "\n", 507 | "1. ทำส่วน _initialization_\n", 508 | "2. ทดสอบเงื่อนไขในส่วน _condition_\n", 509 | " - ถ้าเงื่อนไขเป็นจริงให้ทำต่อข้อ 3\n", 510 | " - ถ้าเงื่อนไขเป็นเท็จให้จบการทำงานของคำสั่ง `for`\n", 511 | "3. ทำส่วน _body_\n", 512 | "4. ทำส่วน _step_\n", 513 | "5. กลับไปข้อ 2\n", 514 | "\n", 515 | "จากลำดับนี้จะเห็นได้ว่าส่วน _initialization_ จะทำเพียงครั้งเดียวในตอนเริ่มต้น ส่วนนี้เราจะใช้ในการกำหนดค่าตั้งต้นที่จำเป็นสำหรับการทำซ้ำซึ่งมักจะเป็นการประกาศและกำหนดค่าตั้งต้นให้กับตัวแปรที่ใช้นับจำนวนรอบของการวนซ้ำ แต่ไม่จำเป็นต้องเป็นแบบนั้นเสมอไป\n", 516 | "\n", 517 | "ก่อนการวนซ้ำแต่ละรอบจะมีการทดสอบ _condition_ ซึ่งถ้าเป็นจริงก็จะไปทำส่วน _body_ ซึ่งเป็นโค้ดหลักที่ต้องการให้วนซ้ำ แล้วจึงทำส่วนของ _step_ ซึ่งมักจะเป็นคำสั่งเปลี่ยนค่าของตัวแปรที่ใช้นับรอบ แล้วจึงกลับไปตรวจสอบ _condition_ ใหม่ ดังตัวอย่างนี้" 518 | ] 519 | }, 520 | { 521 | "cell_type": "code", 522 | "execution_count": 38, 523 | "metadata": {}, 524 | "outputs": [ 525 | { 526 | "name": "stdout", 527 | "output_type": "stream", 528 | "text": [ 529 | "Adding 1\n", 530 | "Adding 2\n", 531 | "Adding 3\n", 532 | "Adding 4\n", 533 | "Adding 5\n", 534 | "Sum = 15\n" 535 | ] 536 | } 537 | ], 538 | "source": [ 539 | "int sum = 0;\n", 540 | "\n", 541 | "for (int i = 0; i < 5; i++) {\n", 542 | " System.out.printf(\"Adding %d%n\", i + 1);\n", 543 | " sum += i + 1;\n", 544 | "}\n", 545 | "\n", 546 | "System.out.println(\"Sum = \" + sum);" 547 | ] 548 | }, 549 | { 550 | "cell_type": "markdown", 551 | "metadata": {}, 552 | "source": [ 553 | "ลูป `for` จะเริ่มต้นโดยการประกาศตัวแปร `i` และกำหนดค่าให้เท่ากับ 0 และทุกรอบจะมีการตรวจสอบค่าของ `i` ว่ายังน้อยกว่า 5 อยู่ และทำส่วนของ _body_ คือการแสดงข้อความและบวกค่าตัวนับให้กับตัวแปร `sum` แล้วจึงทำส่วนของ _step_ ซึ่งก็คือการเพิ่มค่าให้กับตัวนับซึ่งก็คือตัวแปร `i` นั่นเอง ส่วน _step_ จึงถือเป็นส่วนที่มีความจำเป็นมากในการทำให้การวนซ้ำมีการเดินหน้าไปถึงจุดสิ้นสุด\n", 554 | "\n", 555 | "ถ้าเราลองพิจารณากันอย่างละเอียดจะพบว่าเราสามารถใช้คำสั่ง `while` แทนคำสั่ง `for` ได้เช่นกัน โดยถ้าต้องการเลียนแบบการทำงานทุกอย่างของ `for` เราสามารถเขียนเป็นคำสั่ง `while` ได้ในรูปนี้\n", 556 | "\n", 557 | "```\n", 558 | "initialization\n", 559 | "while (condition) {\n", 560 | " body\n", 561 | " step\n", 562 | "}\n", 563 | "```\n", 564 | "\n", 565 | "ซึ่งถ้าใช้กับโปรแกรมตัวอย่างข้างต้นก็จะได้เป็นดังนี้" 566 | ] 567 | }, 568 | { 569 | "cell_type": "code", 570 | "execution_count": 39, 571 | "metadata": {}, 572 | "outputs": [ 573 | { 574 | "name": "stdout", 575 | "output_type": "stream", 576 | "text": [ 577 | "Adding 1\n", 578 | "Adding 2\n", 579 | "Adding 3\n", 580 | "Adding 4\n", 581 | "Adding 5\n", 582 | "Sum = 15\n" 583 | ] 584 | } 585 | ], 586 | "source": [ 587 | "int sum = 0;\n", 588 | "int i = 0;\n", 589 | "\n", 590 | "while (i < 5) {\n", 591 | " System.out.printf(\"Adding %d%n\", i + 1);\n", 592 | " sum += i + 1;\n", 593 | " i++;\n", 594 | "}\n", 595 | "\n", 596 | "System.out.println(\"Sum = \" + sum);" 597 | ] 598 | }, 599 | { 600 | "cell_type": "markdown", 601 | "metadata": {}, 602 | "source": [ 603 | "ข้อแตกต่างของคำสั่ง `for` กับการใช้คำสั่ง `while` แทนในรูปแบบนี้ นอกจากเรื่องความกระชับแล้ว ยังมีความแตกต่างในรายละเอียดเล็กน้อยอีกประเด็น ได้แก่ ขอบเขตของตัวแปรที่ประกาศในส่วน _initialization_ ซึ่งในกรณีของ `for` จะมีขอบเขตแค่ภายในลูป `for` เท่านั้น แต่ในกรณีของ `while` ตัวแปรที่ประกาศขึ้นจะมีขอบเขตในระดับเดียวกับบล็อกที่ครอบคำสั่ง `while` นั้นอยู่ ซึ่งถ้าเป็นเมทอดก็จะมีขอบเขตเป็นทั้งเมทอดนั้น\n", 604 | "\n", 605 | "เนื่องจากในความเป็นจริงแล้วทั้ง `for` และ `while` ก็มีลักษณะที่ทดแทนกันได้ เราจึงสามารถใช้ `for` แทน `while` และใช้กับการวนซ้ำที่มีเงื่อนไขซับซ้อนได้เช่นกัน แต่ในทางปฏิบัติแล้วเราจะนิยมใช้ `for` ในการวนซ้ำที่มีจำนวนรอบแน่นอนเป็นส่วนใหญ่ และใช้ `while` หรือ `do` ... `while` สำหรับกรณีอื่น ๆ\n", 606 | "\n", 607 | "> **หมายเหตุ** ยังมีรูปแบบของคำสั่ง `for` อีกรูปแบบหนึ่งซึ่งใช้กับข้อมูลชนิด collection ซึ่งจะกล่าวถึงในบทต่อไป\n", 608 | "\n", 609 | "### คำสั่ง break\n", 610 | "\n", 611 | "ในการวนซ้ำ บางกรณีเราต้องการยุติการวนซ้ำก่อนที่จะไปถึงการตรวจสอบเงื่อนไขรอบต่อไป ในกรณีนี้เราสามารถใช้คำสั่ง `break` เพื่อยุติการทำงานของลูปได้\n", 612 | "\n", 613 | "พิจารณาตัวอย่างต่อไปนี้" 614 | ] 615 | }, 616 | { 617 | "cell_type": "code", 618 | "execution_count": 42, 619 | "metadata": {}, 620 | "outputs": [ 621 | { 622 | "name": "stdout", 623 | "output_type": "stream", 624 | "text": [ 625 | "Found 10098 in 14 steps.\n" 626 | ] 627 | } 628 | ], 629 | "source": [ 630 | "int start = 10000;\n", 631 | "int end = 20000;\n", 632 | "int step = 7;\n", 633 | "int divider = 17;\n", 634 | "int stepCount = 0;\n", 635 | "int i;\n", 636 | "\n", 637 | "for (i = start; i <= end; i += step) {\n", 638 | " if (i % divider == 0)\n", 639 | " break;\n", 640 | " \n", 641 | " stepCount++;\n", 642 | "}\n", 643 | "\n", 644 | "System.out.printf(\"Found %d in %d steps.%n\", i, stepCount);" 645 | ] 646 | }, 647 | { 648 | "cell_type": "markdown", 649 | "metadata": {}, 650 | "source": [ 651 | "ตัวอย่างนี้เราต้องการหาค่าที่สามารถหารด้วย 17 ลงตัวจากชุดตัวเลขที่เริ่มต้นที่ 10,000 และเพิ่มทีละ 7 ไปจนถึง 20,000\n", 652 | "\n", 653 | "ลูป `for` ในที่นี้จะเป็นการนับตามปกติโดยเพิ่มค่าของ `i` รอบละ 7 แต่ภายในลูปเรามีการตรวจสอบเงื่อนไขเพิ่มเติมว่าค่าของ `i` หาร 17 ลงตัวหรือไม่ ถ้าลงตัวถือว่าเราพบค่าแล้ว ไม่ต้องวนซ้ำต่อ จึง `break` ออกมาจากลูป\n", 654 | "\n", 655 | "คำสั่ง `break` มักจะใช้ร่วมกับคำสั่ง `if` เพื่อตรวจสอบเงื่อนไขในการสิ้นสุดการวนซ้ำเพิ่มเติมก่อนสั่งยุติการวนซ้ำ\n", 656 | "\n", 657 | "### คำสั่ง continue\n", 658 | "\n", 659 | "นอกเหนือจากการยุติการทำงานของลูปด้วยคำสั่ง `break` แล้ว เรายังสามารถควบคุมการทำงานภายในลูปได้อีกลักษณะหนึ่ง คือการสั่งให้ขึ้นรอบการวนซ้ำรอบต่อไปทันทีโดยไม่ทำคำสั่งภายในลูปส่วนที่เหลือ การขึ้นรอบต่อไปก็จะมีการตรวจสอบเงื่อนไขก่อนการทำซ้ำตามปกติ\n", 660 | "\n", 661 | "พิจารณาตัวอย่างต่อไปนี้" 662 | ] 663 | }, 664 | { 665 | "cell_type": "code", 666 | "execution_count": 43, 667 | "metadata": {}, 668 | "outputs": [ 669 | { 670 | "name": "stdout", 671 | "output_type": "stream", 672 | "text": [ 673 | "Sum = 4780\n" 674 | ] 675 | } 676 | ], 677 | "source": [ 678 | "// Sum of all except those that are divisible by both 6 and 9\n", 679 | "int sum = 0;\n", 680 | "\n", 681 | "for (int i = 1; i <= 100; i++) {\n", 682 | " if (i % 6 == 0 && i % 9 == 0)\n", 683 | " continue;\n", 684 | " \n", 685 | " sum += i;\n", 686 | "}\n", 687 | "\n", 688 | "System.out.println(\"Sum = \" + sum);" 689 | ] 690 | }, 691 | { 692 | "cell_type": "markdown", 693 | "metadata": {}, 694 | "source": [ 695 | "ตัวอย่างนี้เป็นการหาผลรวมของจำนวนเต็มจาก 1-100 โดยยกเว้นตัวเลขที่ทั้ง 6 และ 9 สามารถหารลงตัว เราตรวจสอบเงื่อนไขการหารลงตัวนี้แล้วสั่งให้ขึ้นรอบต่อไปเลยโดยไม่เอาค่ามารวมกับ `sum` ถ้าเลขนั้นหารได้ลงตัว\n", 696 | "\n", 697 | "## เมทอดของคลาส\n", 698 | "\n", 699 | "เมทอดของคลาส (class method) หรือเมทอดสถิต (static method) คือเมทอดที่ประกาศขึ้นโดยมีการระบุตัวกำหนด `static` ซึ่งที่ผ่านมาเราจะเห็นได้ในเมทอด `main`\n", 700 | "\n", 701 | "เมทอดคือโครงสร้างที่รวมคำสั่งจำนวนหนึ่งเอาไว้ด้วยกันเป็นชุด แล้วตั้งชื่อขึ้นมาใหม่เสมือนเป็นคำสั่งใหม่ เปรียบเทียบกับภาษาอื่นอาจจะเปรียบเทียบได้กับฟังก์ชัน แต่ข้อแตกต่างจากฟังก์ชันก็คือ ฟังก์ชันจะเป็นอิสระ แต่เมทอดจะผูกติดอยู่กับคลาสหรืออ็อบเจกต์ ซึ่งหมายความว่าไม่สามารถเรียกใช้โดด ๆ ได้ ต้องเรียกใช้ผ่านคลาสหรือผ่านอ็อบเจกต์ที่ผูกติดอยู่\n", 702 | "\n", 703 | "เมทอดของคลาสเป็นเมทอดที่ถูกผูกติดกับคลาส การเรียกใช้ต้องเรียกผ่านคลาสหรือผ่านอ็อบเจกต์ของคลาสนั้น ยกเว้นจะเป็นการเรียกภายในคลาสเดียวกัน ซึ่งไม่ต้องระบุคลาสอีก\n", 704 | "\n", 705 | "รูปแบบการประกาศเมทอดของคลาสเบื้องต้นเป็นดังนี้\n", 706 | "\n", 707 | "```\n", 708 | "access-modifier static return-type methodName(parameter-list)\n", 709 | " method-body\n", 710 | "```\n", 711 | "\n", 712 | "- _access-modifiers_ เป็นตัวกำหนดการเข้าถึงไม่เกินหนึ่งตัวระหว่าง `private` กับ `public` หรือไม่ระบุก็ได้ ในที่นี้เราจะใช้ `public` ซึ่งหมายถึงเรียกใช้ได้จากทุกที่ไปก่อน\n", 713 | "- _return-type_ เป็นตัวระบุชนิดของข้อมูลที่จะส่งกลับจากเมทอด โดยที่ `void` จะหมายถึงเมทอดนี้ไม่ส่งค่ากลับ\n", 714 | "- _parameter-list_ เป็นรายการพารามิเตอร์ที่เมทอดนี้จะรับ\n", 715 | "- _method-body_ เป็นตัวโค้ดของเมทอดในรูปของบล็อก\n", 716 | "\n", 717 | "### คำสั่ง return\n", 718 | "\n", 719 | "เมทอดสามารถที่จะคืนค่าผลลัพธ์กลับไปยังผู้เรียกได้ ชนิดของข้อมูลที่คืนค่าจะระบุที่ _return-type_ ถ้า _return-type_ ไม่ใช่ `void` ในตัวโค้ดของเมทอดจะต้องมีคำสั่ง `return` เพื่อคืนค่ากลับ เมื่อพบคำสั่ง `return` เมทอดจะสิ้นสุดการทำงานพร้อมกับส่งค่าที่ระบุในคำสั่ง `return` กลับเป็นผลลัพธ์\n", 720 | "\n", 721 | "ในกรณีที่ระบุ _return-type_ เป็น `void` ซึ่งหมายความว่าเมทอดจะไม่มีการคืนค่ากลับ เมทอดนี้จะมีคำสั่ง `return` หรือไม่มีก็ได้ ถ้ามีก็จะเป็นคำสั่ง `return` แบบไม่ระบุค่า แต่ถ้าไม่มี เมทอดก็จะทำงานจนถึงคำสั่งสุดท้ายในตัวเมทอดแล้วจึงคืนการทำงานกลับไปยังผู้เรียก\n", 722 | "\n", 723 | "ตัวอย่างนี้แสดงการประกาศและใช้เมทอดของคลาส" 724 | ] 725 | }, 726 | { 727 | "cell_type": "code", 728 | "execution_count": 49, 729 | "metadata": {}, 730 | "outputs": [ 731 | { 732 | "name": "stdout", 733 | "output_type": "stream", 734 | "text": [ 735 | "Hello, my name is John.\n", 736 | "Do you know that absolute of -17 is 17?\n", 737 | "Hello, my name is Jim.\n", 738 | "Do you know that I like sushi?\n" 739 | ] 740 | } 741 | ], 742 | "source": [ 743 | "public class StaticMethodDemo {\n", 744 | " public static int abs(int value) {\n", 745 | " if (value < 0) {\n", 746 | " return -value;\n", 747 | " } else {\n", 748 | " return value;\n", 749 | " }\n", 750 | " }\n", 751 | " \n", 752 | " public static void printGreeting(String name, String message) {\n", 753 | " System.out.printf(\"Hello, my name is %s.%n\", name);\n", 754 | " System.out.printf(\"Do you know that %s?%n\", message);\n", 755 | " }\n", 756 | " \n", 757 | " public static void main(String[] args) {\n", 758 | " int value = -17;\n", 759 | " \n", 760 | " String message = String.format(\"absolute of %d is %d\", value, abs(value));\n", 761 | " \n", 762 | " printGreeting(\"John\", message);\n", 763 | " \n", 764 | " StaticMethodDemo.printGreeting(\"Jim\", \"I like sushi\");\n", 765 | " }\n", 766 | "}\n", 767 | "\n", 768 | "StaticMethodDemo.main(new String[0]);" 769 | ] 770 | }, 771 | { 772 | "cell_type": "markdown", 773 | "metadata": {}, 774 | "source": [ 775 | "เมทอด `abs` รับพารามิเตอร์ 1 ตัว และคืนค่ากลับเป็นชนิด `int` ในขณะที่เมทอด `printGreeting` รับพารามิเตอร์ 2 ตัว และไม่คืนค่ากลับ จึงไม่ต้องมีคำสั่ง `return` ในตัวเมทอด\n", 776 | "\n", 777 | "ในเมทอด `main` มีการเรียกใช้เมทอด `printGreeting` 2 ครั้ง ครั้งแรกเรียกชื่อตรง ๆ ครั้งที่สองเรียกผ่านชื่อคลาส ในกรณีที่เมทอดที่เรียกกับเมทอดที่ถูกเรียกอยู่ในคลาสเดียวกัน เราไม่จำเป็นต้องระบุชื่อคลาส แต่ถ้าอยู่ต่างคลาสกัน เราต้องระบุชื่อคลาสด้วยเสมอ\n", 778 | "\n", 779 | "### เมทอด main\n", 780 | "\n", 781 | "เมทอด `main` เป็นเมทอดของคลาสที่เราใช้มาตั้งแต่ต้น เมทอดนี้มีความสำคัญเป็นพิเศษเนื่องจากโปรแกรมจะเริ่มต้นทำงานที่เมทอดนี้ เมทอด `main` จึงเป็นเสมือนตัวตั้งต้นที่ต้องจัดเตรียมสภาวะการทำงานของโปรแกรมให้พร้อมแล้วดำเนินการต่อไป\n", 782 | "\n", 783 | "เมทอด `main` จะเป็นต้องเป็นเมทอดของคลาสซึ่งประกาศด้วยตัวกำหนด `static` เพราะเมทอดของคลาสสามารถเรียกใช้ได้เลยโดยไม่ต้องมีอ็อบเจกต์มาอ้างอิง ขณะที่โปรแกรมเริ่มทำงานระบบจะไม่มีอ็อบเจกต์ใด ๆ อยู่ ดังนั้นเมทอดแรกที่จะเรียกได้ต้องสามารถเรียกได้โดยไม่ต้องอ้างอิงอ็อบเจกต์ เมทอด `main` จึงต้องมีสถานะเป็นเมทอดของคลาส\n", 784 | "\n", 785 | "`main` จะรับพารามิเตอร์เป็นอาร์เรย์ของสตริง ซึ่งเราสามารถส่งเข้ามาให้ได้โดยการกำหนดตอนที่สั่งรันโปรแกรม เช่น กำหนดจาก Command Prompt หรือ Terminal แต่ในวิชานี้เราไม่ได้ใช้คุณสมบัตินั้น จึงไม่กล่าวถึงในที่นี้\n", 786 | "\n", 787 | "ในบทเรื่องคลาสและอ็อบเจกต์ เราจะศึกษาเมทอดอีกประเภท เรียกว่าเมทอดของอินสแตนซ์ (instance method) ซึ่งผูกติดกับอ็อบเจกต์ ไม่สามารถเรียกใช้ผ่านคลาสได้ ต้องเรียกผ่านอ็อบเจกต์เท่านั้น\n", 788 | "\n", 789 | "### ขอบเขตของตัวแปร\n", 790 | "\n", 791 | "ขอบเขต (scope) ของตัวแปรหมายถึงส่วนของโปรแกรมที่สามารถมองเห็นและอ้างอิงตัวแปรเหล่านั้นได้ ตัวแปรที่ประกาศขึ้นในบล็อกใด ๆ จะมีขอบเขตตั้งแต่จุดที่ประกาศไปจนถึงจุดสิ้นสุดบล็อกนั้น ๆ ภายในบล็อกจะไม่สามารถประกาศตัวแปรชื่อซ้ำกันได้\n", 792 | "\n", 793 | "ตัวแปรที่ประกาศเป็นพารามิเตอร์ของเมทอดหรือคอนสตรักเตอร์จะมีขอบเขตทั้งเมทอดหรือคอนสตรักเตอร์นั้น ๆ\n", 794 | "\n", 795 | "ตัวแปรที่ประกาศในส่วนกำหนดค่าตั้งต้นของคำสั่ง `for` จะมีขอบเขตตลอดทั้งคำสั่ง `for` นั้น\n", 796 | "\n", 797 | "ตัวแปรที่ประกาศกับคลาส (ตัวแปรของคลาสและตัวแปรของอินสแตนซ์) จะมีขอบเขตตลอดทั้งคลาส\n", 798 | "\n", 799 | "### การบดบัง\n", 800 | "\n", 801 | "โดยปกติแล้ว เราไม่สามารถประกาศตัวแปรชื่อซ้ำกันภายในบล็อกได้ แม้ว่าตัวแปรหนึ่งจะประกาศที่บล็อกที่ครอบอยู่หรือเป็นพารามิเตอร์และอีกตัวประกาศในบล็อกที่ซ้อนอยู่ด้านในก็ไม่สามารถทำได้ แต่มีข้อยกเว้นอยู่กรณีหนึ่ง คือการที่ตัวแปรตัวหนึ่งประกาศในส่วนของคลาส และอีกตัวหนึ่งประกาศในส่วนของพารามิเตอร์ของเมทอดหรือภายในเมทอด\n", 802 | "\n", 803 | "การประกาศแบบนี้สามารถทำได้ แต่จะทำให้เกิดการบดบัง (shadowing) ขึ้น ซึ่งหมายความว่าเราจะไม่สามารถอ้างอิงถึงตัวแปรของคลาสหรืออ็อบเจกต์ตรง ๆ ในเมทอดนั้นได้ เนื่องจากถูกตัวแปรภายในบดบังอยู่" 804 | ] 805 | }, 806 | { 807 | "cell_type": "code", 808 | "execution_count": 4, 809 | "metadata": {}, 810 | "outputs": [ 811 | { 812 | "name": "stdout", 813 | "output_type": "stream", 814 | "text": [ 815 | "In shadowedByParameter: parameter x = 12.5\n", 816 | "In shadowedByLocalVariable: local varaible x = 2.5\n", 817 | "In shadowedByLocalVariable: class variable x = 1.5\n", 818 | "In blockScoping, inside block: x = 1234.5\n", 819 | "In blockScoping, outside block: x = 1.5\n" 820 | ] 821 | } 822 | ], 823 | "source": [ 824 | "public class ScopeDemo {\n", 825 | " private static double x = 1.5;\n", 826 | " \n", 827 | " public static void shadowedByParameter(double x) {\n", 828 | " System.out.printf(\"In shadowedByParameter: parameter x = %.1f%n\", x);\n", 829 | " }\n", 830 | " \n", 831 | " public static void shadowedByLocalVariable() {\n", 832 | " double x = 2.5;\n", 833 | "\n", 834 | " System.out.printf(\"In shadowedByLocalVariable: local varaible x = %.1f%n\", x);\n", 835 | " System.out.printf(\"In shadowedByLocalVariable: class variable x = %.1f%n\", ScopeDemo.x);\n", 836 | " }\n", 837 | " \n", 838 | " public static void blockScoping() {\n", 839 | " do {\n", 840 | " double x = 1234.5;\n", 841 | " System.out.printf(\"In blockScoping, inside block: x = %.1f%n\", x);\n", 842 | " } while (false);\n", 843 | " \n", 844 | " System.out.printf(\"In blockScoping, outside block: x = %.1f%n\", x);\n", 845 | " }\n", 846 | " \n", 847 | " public static void main(String[] args) {\n", 848 | " shadowedByParameter(12.5);\n", 849 | " shadowedByLocalVariable();\n", 850 | " blockScoping();\n", 851 | " }\n", 852 | "}\n", 853 | "\n", 854 | "ScopeDemo.main(new String[0]);" 855 | ] 856 | }, 857 | { 858 | "cell_type": "markdown", 859 | "metadata": {}, 860 | "source": [ 861 | "ตัวอย่างนี้แสดงให้เห็นขอบเขตของ `x` ในบริบทต่าง ๆ กัน\n", 862 | "\n", 863 | "`x` ที่เป็นตัวแปรชั้นนอกสุดประกาศเป็นตัวแปรของคลาส มีค่าเป็น 1.5 แต่ในเมทอด `shadowedByParameter` มีการประกาศพารามิเตอร์ `x` ซึ่งจะไปบดบัง `x` ที่เป็นตัวแปรของคลาส ทำให้เวลาที่แสดงค่าออกมาจะเป็นค่าของอาร์กิวเมนต์ที่ผ่านเข้ามาให้พารามิเตอร์ (12.5)\n", 864 | "\n", 865 | "ในเมทอด `shadowedByLocalVariable` มีการประกาศตัวแปร `x` ภายในเมทอดโดยกำหนดให้มีค่าเท่ากับ 2.5 เมื่อแสดงค่าออกมาก็จะเป็นค่านี้เนื่องจาก `x` ตัวนี้ไปบดบัง `x` ที่เป็นตัวแปรของคลาสเช่นกัน แต่ในเมทอดนี้ยังมีการอ้างอิงถึง `x` ที่เป็นตัวแปรของคลาสด้วยโดยอ้างผ่านชื่อคลาสเป็น `ScopeDemo.x` ซึ่งนี่เป็นวิธีหนึ่งในการอ้างตัวแปรของคลาสที่ถูกบดบัง\n", 866 | "\n", 867 | "ในเมทอด `blockScoping` มีการประกาศตัวแปร `x` ในคำสั่งแบบบล็อกที่เป็นส่วนหนึ่งของ `do` ... `while` ซึ่ง `x` ตัวนี้จะมีขอบเขตเฉพาะภายในบล็อกนี้เท่านั้น ดังนั้นเมื่อสั่งให้แสดงค่าของ `x` ภายในบล็อกจึงได้ค่า 1234.5 แต่เมื่อสั่งให้แสดงค่าของ `x` ภายนอกบล็อกจึงได้ค่า 1.5 เนื่องจากภายนอกบล็อกไม่มี `x` อื่นมาบดบัง `x` ที่เป็นตัวแปรของคลาสแล้วนั่นเอง\n", 868 | "\n", 869 | "> **หมายเหตุ** ตัวแปรที่ประกาศขึ้นภายในเมทอดหรือคอนสตรักเตอร์โดยไม่กำหนดค่าตั้งต้นให้จะไม่มีค่าตั้งต้นโดยปริยายเหมือนตัวแปรของคลาสหรืออ็อบเจกต์ ภาษา Java บังคับให้ตัวแปรภายในเหล่านี้ต้องมีค่าก่อนนำไปใช้อ้างอิง ถ้าไม่ได้กำหนดค่าตั้งต้นให้ก็จะต้องมีคำสั่งกำหนดค่าให้ในภายหลังก่อนจะถึงคำสั่งที่จะนำค่าไปใช้ มิฉะนั้นโปรแกรมก็จะคอมไพล์ไม่ผ่าน" 870 | ] 871 | } 872 | ], 873 | "metadata": { 874 | "kernelspec": { 875 | "display_name": "Java", 876 | "language": "java", 877 | "name": "java" 878 | }, 879 | "language_info": { 880 | "codemirror_mode": "java", 881 | "file_extension": ".java", 882 | "mimetype": "text/x-java-source", 883 | "name": "Java", 884 | "pygments_lexer": "java", 885 | "version": "9.0.4+11" 886 | } 887 | }, 888 | "nbformat": 4, 889 | "nbformat_minor": 2 890 | } 891 | -------------------------------------------------------------------------------- /02 - Control Structures/DoWhileDemo.java: -------------------------------------------------------------------------------- 1 | import java.util.Scanner; 2 | import java.util.Random; 3 | 4 | public class DoWhileDemo { 5 | public static void main(String[] args) { 6 | Scanner input = new Scanner(System.in); 7 | Random random = new Random(); 8 | int secretValue = random.nextInt(9) + 1; 9 | int guess; 10 | 11 | do { 12 | System.out.print("Enter a number between 1 to 9: "); 13 | guess = input.nextInt(); 14 | if (guess < secretValue) { 15 | System.out.println("Too low!"); 16 | } else if (guess > secretValue) { 17 | System.out.println("Too high!"); 18 | } 19 | } while (guess != secretValue); 20 | 21 | System.out.println("You've got it!"); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /03 - Collections/.ipynb_checkpoints/Collections-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# คอลเลกชัน\n", 8 | "\n", 9 | "คอลเลกชัน (collection) หรือข้อมูลชนิดรวมหมู่เป็นชนิดของข้อมูลประเภทหนึ่งที่ใช้เพื่อเก็บข้อมูลชนิดอื่น ๆ หลาย ๆ จำนวนไว้ด้วยกัน ในบทนี้เราจะมาศึกษาข้อมูลชนิดอาร์เรย์ คลาส `ArrayList` สตริง อีนัม และคลาส `HashMap` นอกจากนี้เราจะศึกษาการวนซ้ำบนคอลเลกชันด้วย\n", 10 | "\n", 11 | "## อาร์เรย์\n", 12 | "\n", 13 | "อาร์เรย์ (array) หรือแถวลำดับเป็นชนิดข้อมูลที่ใช้จัดเก็บข้อมูลชนิดอื่นแบบเป็นลำดับ โดยสามารถใช้ดัชนี (index) เป็นตัวระบุตำแหน่งเพื่อเข้าถึงข้อมูลตำแหน่งใด ๆ ก็ได้ในทันที\n", 14 | "\n", 15 | "### การประกาศและสร้างอาร์เรย์\n", 16 | "\n", 17 | "การประกาศตัวแปรให้เป็นชนิดอาร์เรย์จะต้องระบุว่าเป็นอาร์เรย์ของข้อมูลชนิดใด และใช้รูปแบบการประกาศดังนี้\n", 18 | "\n", 19 | "```\n", 20 | "type[] varName;\n", 21 | "```\n", 22 | "\n", 23 | "วงเล็บก้ามปูสามารถไว้ท้ายชื่อชนิดหรือท้ายชื่อตัวแปรก็ได้ เพราะฉะนั้นรูปแบบการประกาศแบบนี้ก็ถือว่าถูกต้องเช่นกัน\n", 24 | "\n", 25 | "```\n", 26 | "type varName[];\n", 27 | "```\n", 28 | "\n", 29 | "เช่น" 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": null, 35 | "metadata": {}, 36 | "outputs": [], 37 | "source": [ 38 | "int[] ranks;\n", 39 | "String[] messages;\n", 40 | "double scores[];" 41 | ] 42 | }, 43 | { 44 | "cell_type": "markdown", 45 | "metadata": {}, 46 | "source": [ 47 | "รูปแบบหลังนี้เป็นแบบที่รับมาจากภาษา C แต่เป็นรูปแบบที่ไม่นิยมและไม่แนะนำให้ใช้\n", 48 | "\n", 49 | "ตัวแปรอาร์เรย์ที่เราประกาศแล้วแต่ยังไม่กำหนดค่าตั้งต้นให้จะมีค่าเป็น `null` ซึ่งหมายถึงตัวแปรอาร์เรย์นี้ยังไม่ได้อ้างอิงอาร์เรย์ใด ๆ เป็นเพียงตัวแปร แต่ยังไม่มีตัวอาร์เรย์\n", 50 | "\n", 51 | "เราสามารถสร้างอาร์เรย์และกำหนดค่าตั้งต้นให้กับตัวแปรอาร์เรย์ได้หลายวิธีดังนี้" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": 10, 57 | "metadata": {}, 58 | "outputs": [], 59 | "source": [ 60 | "int[] ranks = new int[10];\n", 61 | "double[] scores = { 1.25, 2.5, 5.0, 6.25, 7.5 };\n", 62 | "String[] messages = new String[] { \"Hello\", \"this\", \"is\", \"a\", \"message\" };\n", 63 | "double[] distances;\n", 64 | "distances = new double[] { 425.23, 771.94, 23.56, 1_038.77 };" 65 | ] 66 | }, 67 | { 68 | "cell_type": "markdown", 69 | "metadata": {}, 70 | "source": [ 71 | "แบบแรกเป็นการประกาศตัวแปรอาร์เรย์ `ranks` แล้วกำหนดค่าตั้งต้นให้โดยการใช้คำสั่ง `new` สร้างอาร์เรย์ของชนิด `int` ขนาด 10 สมาชิกโดยไม่ระบุค่าตั้งต้นให้กับสมาชิกแต่ละตัว\n", 72 | "\n", 73 | "การไม่กำหนดค่าตั้งต้นให้กับสมาชิกของอาร์เรย์จะทำให้สมาชิกแต่ละตัวมีค่าตั้งต้นโดยปริยายโดยอัตโนมัติ ซึ่งถ้ามีชนิดเป็นตัวเลขหรือ `char` ก็จะมีค่าเป็น 0 หรือ 0.0 ถ้าเป็น `boolean` ก็จะมีค่าเป็น false และถ้าเป็นข้อมูลชนิดตัวอ้างอิงก็จะมีค่าเป็น `null`\n", 74 | "\n", 75 | "แบบถัดมา `scores` ถูกประกาศพร้อมกับกำหนดให้เป็นอาร์เรย์ของ `double` ที่มีข้อมูลเป็น 1.25, 2.5, 5.0, 6.25, 7.5 ตามลำดับ ขนาดของอาร์เรย์จะเป็น 5 สมาชิกโดยอัตโนมัติ\n", 76 | "\n", 77 | "ตัวแปรอาร์เรย์ `messages` ก็เช่นกัน ถูกกำหนดให้มีค่าตั้งต้นเป็น `\"Hello\"`, `\"this\"`, `\"is\"`, `\"a\"`, `\"message\"` ซึ่งมีขนาดเป็น 5 สมาชิก การกำหนดค่าเริ่มต้นแบบนี้คล้ายกับแบบของ `scores` แต่เราใช้คำสั่ง `new` ในการสร้างอาร์เรย์\n", 78 | "\n", 79 | "รูปแบบที่ใช้กับการกำหนดค่าเริ่มต้นของ `messages` อาจจะดูยาวกว่าแบบที่ใช้กับ `scores` แต่มีข้อดีคือสามารถใช้สร้างอาร์เรย์พร้อมกำหนดค่าตั้งต้นให้กับสมาชิกอาร์เรย์ที่ใดก็ได้ ไม่จำเป็นต้องใช้เฉพาะเวลาประกาศตัวแปรเหมือนอย่างแบบที่ใช้กับ `scores`\n", 80 | "\n", 81 | "เราเห็นตัวอย่างได้จาก `distances` ซึ่งตอนประกาศเราไม่ได้สร้างอาร์เรย์ขึ้นมาด้วย แต่มาสร้างและกำหนดให้ทีหลังในบรรทัดต่อไปโดยใช้รูปแบบเดียวกับที่กับ `messages` การกำหนดค่าเริ่มต้นแบบที่ใช้กับ `scores` ไม่สามารถใช้ในกรณีนี้ได้\n", 82 | "\n", 83 | "อาร์เรย์ที่สร้างขึ้นมาแล้วไม่สามารถเปลี่ยนขนาดได้นอกจากจะสร้างอาร์เรย์ใหม่ให้มีขนาดใหม่ที่ต้องการแล้วย้ายข้อมูลจากอาร์เรย์เดิมไป\n", 84 | "\n", 85 | "### การเข้าถึงอาร์เรย์\n", 86 | "\n", 87 | "เราสามารถเข้าถึงสมาชิกแต่ละตัวในอาร์เรย์ได้โดยใช้รูปแบบ `varName[i]` โดยจะเป็นตัวระบุตำแหน่งสมาชิก สมาชิกตัวแรกจะเริ่มต้นที่ตำแหน่ง 0\n", 88 | "\n", 89 | "เราสามารถรู้ขนาดของอาร์เรย์ได้โดยการอ้างอิงคุณสมบัติ `length` ของอาร์เรย์นั้นในรูป `varName.length` ตำแหน่งของสมาชิกจะเริ่มจาก 0 ไปสิ้นสุดที่ `length` - 1\n", 90 | "\n", 91 | "ตัวอย่างต่อไปนี้เป็นการหาผลบวกสะสมของลำดับตัวเลข โดยเก็บค่าผลลัพธ์กลับลงไปในอาร์เรย์ตัวเดิมเลย" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": 15, 97 | "metadata": {}, 98 | "outputs": [ 99 | { 100 | "name": "stdout", 101 | "output_type": "stream", 102 | "text": [ 103 | "[1.0, 4.5, 6.5, 8.0, 13.5, 16.5]\n" 104 | ] 105 | } 106 | ], 107 | "source": [ 108 | "// In-place cumulative sum\n", 109 | "double[] values = { 1.0, 3.5, 2.0, 1.5, 5.5, 3.0 };\n", 110 | "double sum = 0.0;\n", 111 | "\n", 112 | "for (int i = 0; i < values.length; i++) {\n", 113 | " sum += values[i];\n", 114 | " values[i] = sum;\n", 115 | "}\n", 116 | "\n", 117 | "System.out.println(Arrays.toString(values));" 118 | ] 119 | }, 120 | { 121 | "cell_type": "markdown", 122 | "metadata": {}, 123 | "source": [ 124 | "เราจะเห็นว่าทั้งการอ้างอิงอาร์เรย์เพื่อนำค่ามาใช้หรือการกำหนดค่าให้กับสมาชิกในอาร์เรย์ต่างก็ใช้รูปแบบ `values[i]` เหมือนกัน\n", 125 | "\n", 126 | "นอกจากนี้สิ่งที่เราเห็นจากตัวอย่างนี้อีกอย่างคือการแสดงค่าอาร์เรย์ซึ่งเราใช้ `Arrays.toString`\n", 127 | "\n", 128 | "## คลาส Arrays\n", 129 | "\n", 130 | "การจัดการกับข้อมูลชนิดอาร์เรย์โดยเบื้องต้นแล้วทำได้เพียงการสร้างอาร์เรย์และการอ้างอิงสมาชิกของอาร์เรย์ แต่งานบางอย่างที่ควรจะทำได้ง่ายก็ค่อนข้างยุ่งยากถ้าจะจัดการโดยตรง เช่น การเปรียบเทียบว่าอาร์เรย์ 2 ตัวมีค่าเท่ากัน การกำหนดค่าเดียวกันให้กับสมาชิกทุกตัวในอาร์เรย์ หรือการแสดงค่าอาร์เรย์ทั้งหมดออกทางจอภาพ\n", 131 | "\n", 132 | "แต่ JDK ก็ให้เครื่องมือจัดการกับอาร์เรย์เพิ่มเติมมาในรูปของคลาสอำนวยความสะดวกชื่อว่า `Arrays`\n", 133 | "\n", 134 | "คลาสนี้ชื่อว่า `Arrays` แต่โดยตัวมันเองไม่ได้เป็นอาร์เรย์ เป็นเพียงคลาสที่รวบรวมเอาเมทอดที่เกี่ยวข้องกับการจัดการกับอาร์เรย์เอาไว้ในตัว เมทอดที่น่าสนใจมีดังนี้\n", 135 | "\n", 136 | "- `sort` ใช้เรียงลำดับข้อมูลในอาร์เรย์จากน้อยไปมาก\n", 137 | "- `fill` ใช้เพื่อใส่ค่าที่กำหนดเข้าไปในสมาชิกของอาร์เรย์ทีละหลาย ๆ ตัว\n", 138 | "- `copyOf` ใช้เพื่อสร้างอาร์เรย์ใหม่ที่มีข้อมูลเหมือนอาร์เรย์เดิม\n", 139 | "- `equals` ใช้เปรียบเทียบของสมาชิกระหว่างอาร์เรย์ 2 ตัวว่าเหมือนกันทั้งหมดหรือไม่\n", 140 | "- `toString` ใช้เมื่อต้องการสตริงที่แสดงค่าของสมาชิกแต่ละตัวในอาร์เรย์ ถ้าเป็นอาร์เรย์ซ้อนกันหรืออาร์เรย์หลายมิติควรใช้ `deepToString`\n", 141 | "- `binarySearch` ใช้ค้นหาข้อมูลที่ต้องการในอาร์เรย์ มีเงื่อนไขว่าข้อมูลในอาร์เรย์ต้องเรียงลำดับมาก่อนจึงจะเรียกใช้ได้\n", 142 | "\n", 143 | "เมทอดอื่น ๆ ของคลาส Arrays ที่น่าสนใจสามารถศึกษาเพิ่มเติมได้ที่นี่ https://docs.oracle.com/javase/9/docs/api/java/util/Arrays.html\n", 144 | "\n", 145 | "ตัวอย่างการใช้งานคลาส `Arrays`" 146 | ] 147 | }, 148 | { 149 | "cell_type": "code", 150 | "execution_count": 28, 151 | "metadata": {}, 152 | "outputs": [ 153 | { 154 | "name": "stdout", 155 | "output_type": "stream", 156 | "text": [ 157 | "Original values: [9.1, 6.25, 2.5, 5.0, 1.25, 7.5, 0.25]\n", 158 | "Copied values: [9.1, 6.25, 2.5, 5.0, 1.25, 7.5, 0.25]\n", 159 | "Original and copied arrays are equal.\n", 160 | "Sorted values: [0.25, 1.25, 2.5, 5.0, 6.25, 7.5, 9.1]\n", 161 | "Found 6.25 at position 4.\n", 162 | "Original and copied arrays are now different.\n", 163 | "Copied array after fill operation: [1.23, 1.23, 1.23, 1.23, 1.23, 1.23, 1.23]\n" 164 | ] 165 | } 166 | ], 167 | "source": [ 168 | "double[] scores = { 9.1, 6.25, 2.5, 5.0, 1.25, 7.5, 0.25 };\n", 169 | "\n", 170 | "System.out.printf(\"Original values: %s%n\", Arrays.toString(scores));\n", 171 | "\n", 172 | "double[] copiedScores = Arrays.copyOf(scores, scores.length);\n", 173 | "System.out.printf(\"Copied values: %s%n\", Arrays.toString(copiedScores));\n", 174 | "\n", 175 | "if (Arrays.equals(scores, copiedScores)) {\n", 176 | " System.out.println(\"Original and copied arrays are equal.\");\n", 177 | "} else {\n", 178 | " System.out.println(\"Original and copied arrays are now different.\");\n", 179 | "}\n", 180 | "\n", 181 | "Arrays.sort(scores);\n", 182 | "System.out.printf(\"Sorted values: %s%n\", Arrays.toString(scores));\n", 183 | "\n", 184 | "System.out.printf(\"Found 6.25 at position %d.%n\", Arrays.binarySearch(scores, 6.25));\n", 185 | "\n", 186 | "if (Arrays.equals(scores, copiedScores)) {\n", 187 | " System.out.println(\"Original and copied arrays are equal.\");\n", 188 | "} else {\n", 189 | " System.out.println(\"Original and copied arrays are now different.\");\n", 190 | "}\n", 191 | "\n", 192 | "Arrays.fill(copiedScores, 1.23);\n", 193 | "System.out.printf(\"Copied array after fill operation: %s%n\", Arrays.toString(copiedScores));" 194 | ] 195 | }, 196 | { 197 | "cell_type": "markdown", 198 | "metadata": {}, 199 | "source": [ 200 | "ในตัวอย่างนี้เราเริ่มต้นด้วยการสร้างอาร์เรย์ `scores` ซึ่งมีข้อมูลไม่เรียงลำดับอยู่จำนวนหนึ่ง และแสดงข้อมูลนั้นโดยใช้ `Arrays.toString` เพื่อเรียกเอาสตริงที่แสดงข้อมูลในอาร์เรย์ออกมาแสดงผล จากนั้นเราก็ทดสอบการสร้างอาร์เรย์ใหม่ที่มีข้อมูลเหมือนอาร์เรย์เดิมโดยใช้ `Arrays.copyOf` และทดสอบว่าอาร์เรย์ใหม่และอาร์เรย์เดิมมีข้อมูลเหมือนกันด้วย `Arrays.equals`\n", 201 | "\n", 202 | "ต่อจากนั้นจึงเอา `scores` มาเรียงลำดับด้วย `Arrays.sort` และใช้ `Arrays.binarySearch` ค้นหาตำแหน่งของข้อมูลที่ต้องการและแสดงออกมา การทดสอบว่าอาร์เรย์ `scores` และ `copiedScores` เท่ากันหรือไม่อีกครั้งแสดงให้เห็นว่าไม่เท่ากันแล้วเนื่องจาก `scores` ถูกนำไปเรียงลำดับ สุดท้ายเป็นการใช้ `Arrays.fill` เพื่อใส่ค่าที่ต้องการในทุกสมาชิกของอาร์เรย์ `copiedScores`\n", 203 | "\n", 204 | "เรารู้ว่าการขยายขนาดอาร์เรย์ไม่สามารถทำได้โดยตรง แต่เราสามารถสร้างอาร์เรย์ใหม่มาแทนที่โดยกำหนดให้มีขนาดใหม่ที่ต้องการแล้วย้ายข้อมูลจากอาร์เรย์เดิมไปโดยใช้ `Arrays.copyOf` ได้ ดังตัวอย่างนี้" 205 | ] 206 | }, 207 | { 208 | "cell_type": "code", 209 | "execution_count": 29, 210 | "metadata": {}, 211 | "outputs": [ 212 | { 213 | "name": "stdout", 214 | "output_type": "stream", 215 | "text": [ 216 | "Scores after resizing: [9.1, 6.25, 2.5, 5.0, 1.25, 7.5, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]\n" 217 | ] 218 | } 219 | ], 220 | "source": [ 221 | "double[] scores = { 9.1, 6.25, 2.5, 5.0, 1.25, 7.5, 0.25 };\n", 222 | "int newSize = 2 * scores.length;\n", 223 | "\n", 224 | "scores = Arrays.copyOf(scores, newSize);\n", 225 | "System.out.printf(\"Scores after resizing: %s%n\", Arrays.toString(scores));" 226 | ] 227 | }, 228 | { 229 | "cell_type": "markdown", 230 | "metadata": {}, 231 | "source": [ 232 | "## อาร์เรย์หลายมิติ\n", 233 | "\n", 234 | "ปัญหาบางประเภทจำเป็นต้องใช้โครงสร้างหลายมิติ เช่น การคำนวณเมทริกซ์ การจำลองโมเดลเชิงปริมาตรต่าง ๆ เป็นต้น เราสามารถกำหนดอาร์เรย์ให้มีหลายมิติเพื่อรองรับปัญหาเหล่านี้ได้\n", 235 | "\n", 236 | "อาร์เรย์หลายมิติในความเป็นจริงแล้วคืออาร์เรย์ที่ซ้อนกันอยู่ หรือเรียกว่าอาร์เรย์ของอาร์เรย์ เราสามารถประกาศอาร์เรย์หลายมิติและกำหนดค่าตั้งต้นให้ได้หลายรูปแบบดังตัวอย่างต่อไปนี้" 237 | ] 238 | }, 239 | { 240 | "cell_type": "code", 241 | "execution_count": 40, 242 | "metadata": {}, 243 | "outputs": [ 244 | { 245 | "name": "stdout", 246 | "output_type": "stream", 247 | "text": [ 248 | "matrixA:\n", 249 | "[[D@52525845, [D@3b94d659]\n", 250 | "[[1.2, 2.4, 3.6], [1.3, 2.6, 3.9]]\n", 251 | "zerosTable:\n", 252 | "[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]\n", 253 | "matrixB:\n", 254 | "[[1.2, 2.4], [3.6, 1.3], [2.6, 3.9]]\n", 255 | "jaggedData:\n", 256 | "[[1, 2, 3], [1, 2], [1, 2], [1]]\n", 257 | "anotherJagger:\n", 258 | "[[3, 2, 1], [0, 0, 0, 0, 0], [4, 2]]\n" 259 | ] 260 | } 261 | ], 262 | "source": [ 263 | "double[][] matrixA = new double[][] {{ 1.2, 2.4, 3.6 }, { 1.3, 2.6, 3.9 }};\n", 264 | "System.out.println(\"matrixA:\");\n", 265 | "System.out.printf(\"%s%n\", Arrays.toString(matrixA));\n", 266 | "System.out.printf(\"%s%n\", Arrays.deepToString(matrixA));\n", 267 | "\n", 268 | "int[][] zerosTable = new int[3][5];\n", 269 | "System.out.println(\"zerosTable:\");\n", 270 | "System.out.printf(\"%s%n\", Arrays.deepToString(zerosTable));\n", 271 | "\n", 272 | "double[][] matrixB = {{ 1.2, 2.4 }, { 3.6, 1.3 }, { 2.6, 3.9 }};\n", 273 | "System.out.println(\"matrixB:\");\n", 274 | "System.out.printf(\"%s%n\", Arrays.deepToString(matrixB));\n", 275 | "\n", 276 | "int[][] jaggedData = {{ 1, 2, 3 }, { 1, 2 }, { 1, 2 }, { 1 }};\n", 277 | "System.out.println(\"jaggedData:\");\n", 278 | "System.out.printf(\"%s%n\", Arrays.deepToString(jaggedData));\n", 279 | "\n", 280 | "int[][] anotherJagger = new int[3][];\n", 281 | "anotherJagger[0] = new int[] { 3, 2, 1 };\n", 282 | "anotherJagger[1] = new int[5];\n", 283 | "anotherJagger[2] = new int[] { 4, 2 };\n", 284 | "System.out.println(\"anotherJagger:\");\n", 285 | "System.out.printf(\"%s%n\", Arrays.deepToString(anotherJagger));" 286 | ] 287 | }, 288 | { 289 | "cell_type": "markdown", 290 | "metadata": {}, 291 | "source": [ 292 | "`matrixA` ถูกประกาศเป็นอาร์เรย์ 2 มิติของ `double` โดยขนาดของแต่ละมิติถูกกำหนดโดยตัวข้อมูลที่ใช้เป็นค่าตั้งต้น ซึ่งในที่นี้จะได้เป็นเมทริกซ์ขนาด 2×3 ในขณะที่ `zerosTable` ถูกกำหนดขนาดด้วยการระบุเป็นมิติลงไปโดยตรง ค่าตั้งต้นของสมาชิกแต่ละตัวให้เป็นค่าโดยปริยายซึ่งในที่นี้คือ 0\n", 293 | "\n", 294 | "ในส่วนการแสดงค่าของอาร์เรย์ เราทดลองใช้ทั้ง `Arrays.toString` และ `Arrays.deepToString` ซึ่งจะเห็นได้ว่าในกรณีที่เป็นอาร์เรย์มากกว่ามิติเดียว เราจำเป็นต้องใช้ `Arrays.deepToString`\n", 295 | "\n", 296 | "`jaggedData` แสดงตัวอย่างให้เห็นอาร์เรย์ที่มีขนาดแต่ละแถวไม่เท่ากัน (jagged array) ในตัวอย่างนี้จะมีขนาดสมาชิกในแต่ละแถวเป็น 3, 2, 2 และ 1 สมาชิกตามลำดับ ซึ่งบ่งชี้ว่าอาร์เรย์ที่ซ้อนอยู่แต่ละแถวเป็นอิสระต่อกัน ไม่จำเป็นต้องมีขนาดเท่ากัน\n", 297 | "\n", 298 | "`anotherJagger` เป็นอาร์เรย์ที่มีขนาดแต่ละแถวไม่เท่ากันอีกตัวหนึ่ง แต่ในตัวอย่างนี้เราสร้างอาร์เรย์มิติแรกขึ้นมาก่อน แล้วค่อยทยอยสร้างมิติที่สองทีละแถวในภายหลัง\n", 299 | "\n", 300 | "เราใช้ `Arrays.deepToString` เพื่อช่วยในการแสดงผลอาร์เรย์หลายมิติ แต่คลาส `Arrays` ก็ไม่มีเมทอดอำนวยความสะดวกสำหรับอาร์เรย์หลายมิติมากนัก เช่น `Arrays.copyOf` ก็ไม่สามารถใช้กับอาร์เรย์ซ้อนได้\n", 301 | "\n", 302 | "## คลาส ArrayList\n", 303 | "\n", 304 | "อาร์เรย์เป็นคอลเลกชันพื้นฐานที่มีให้มาในตัวภาษา Java เองเลย แต่อาร์เรย์ก็มีข้อจำกัดในแง่ของความยืดหยุ่น เราไม่สามารถเพิ่มหรือลดสมาชิกหรือปรับขนาดของอาร์เรย์ตามความต้องการโดยไม่ต้องสร้างใหม่ได้ เราไม่สามารถแทรกหรือลบข้อมูลจากตรงกลางของอาร์เรย์ได้ ซึ่งทำให้การใช้งานอาร์เรย์ในการแก้ปัญหาบางอย่างไม่สะดวกมากนัก\n", 305 | "\n", 306 | "ใน JDK มีคลาสคอลเลกชันที่มีลักษณะการใช้งานคล้ายอาร์เรย์ แต่มีคุณสมบัติในการเพิ่มหรือลดขนาดและแทรกหรือลบข้อมูลได้ตามต้องการ คลาสนั้นคือ `ArrayList`\n", 307 | "\n", 308 | "การประกาศตัวแปรที่มีชนิดเป็น `ArrayList` ทำได้ 2 รูปแบบ แบบแรกเป็นรูปแบบเจเนอริกซึ่งจะมีการระบุชนิดของข้อมูลที่ `ArrayList` จะจัดเก็บ แบบที่สองเป็นรูปแบบดั้งเดิมซึ่งจะไม่ระบุชนิดข้อมูล (raw type)" 309 | ] 310 | }, 311 | { 312 | "cell_type": "code", 313 | "execution_count": 63, 314 | "metadata": {}, 315 | "outputs": [ 316 | { 317 | "name": "stdout", 318 | "output_type": "stream", 319 | "text": [ 320 | "ranks: [1, 5, 3]\n", 321 | "messages: [Hello, I, am, a, robot]\n", 322 | "scores: [28.5, 25.0, 19.2, 22.7]\n" 323 | ] 324 | } 325 | ], 326 | "source": [ 327 | "// Generic form - full initialization\n", 328 | "ArrayList ranks = new ArrayList();\n", 329 | "ranks.add(1);\n", 330 | "ranks.add(5);\n", 331 | "ranks.add(3);\n", 332 | "System.out.printf(\"ranks: %s%n\", ranks);\n", 333 | "\n", 334 | "// Generic form - with diamond operator\n", 335 | "ArrayList messages = new ArrayList<>();\n", 336 | "Collections.addAll(messages, \"Hello\", \"I\", \"am\", \"a\", \"robot\");\n", 337 | "System.out.printf(\"messages: %s%n\", messages);\n", 338 | "\n", 339 | "// Raw form\n", 340 | "ArrayList scores = new ArrayList();\n", 341 | "scores.add(28.5);\n", 342 | "scores.add(19.2);\n", 343 | "scores.add(22.7);\n", 344 | "scores.add(1, 25.0);\n", 345 | "System.out.printf(\"scores: %s%n\", scores);" 346 | ] 347 | }, 348 | { 349 | "cell_type": "markdown", 350 | "metadata": {}, 351 | "source": [ 352 | "ในรูปแบบเจเนอริก เราจะต้องระบุชนิดของข้อมูลที่ `ArrayList` จะจัดเก็บในส่วนประกาศตัวแปร ซึ่งเห็นในตัวอย่างนี้ในรูปของ `ArrayList` โดยที่ `T` คือชนิดของข้อมูลที่ต้องการจัดเก็บ\n", 353 | "\n", 354 | "ในส่วนของการสร้าง `ArrayList` เพื่อกำหนดเป็นค่าตั้งต้นให้กับตัวแปร `ranks` และ `messages` นั้น เราสามารถเลือกได้ว่าจะระบุชนิดอีกครั้งเหมือนในตัวอย่างของ `ranks` ซึ่งใช้คำสั่ง `new ArrayList()` เพื่อสร้าง `ArrayList` หรือจะไม่ระบุชนิดก็ได้ แต่ต้องใส่เครื่องหมาย diamond operator (`<>`) เอาไว้เหมือนในตัวอย่างของ `messages` ซึ่งในรูปแบบนี้ คอมไพเลอร์จะกำหนดชนิดให้เองตามชนิดของตัวแปรที่ประกาศเอาไว้\n", 355 | "\n", 356 | "นอกจากการประกาศตัวแปรและการสร้าง `ArrayList` แล้ว เรายังแสดงตัวอย่างการใส่ข้อมูลเข้าไปใน `ArrayList` ในตัวอย่างนี้ด้วย แบบแรกคือใช้เมทอด `add` ซึ่งจะใส่ข้อมูลเข้าไปต่อท้ายข้อมูลเดิมที่มีอยู่ หรือเราอาจจะแทรกได้โดยการระบุตำแหน่งเป็นอาร์กิวเมนต์เพิ่มอีกตัวเหมือนในตัวอย่างของ `scores` และอีกแบบเป็นการใส่ข้อมูลทีละหลายตัวโดยการใช้เมทอด `Collections.addAll` จากคลาส `Collections`\n", 357 | "\n", 358 | "อีกรูปแบบของการประกาศและสร้าง `ArrayList` คือรูปแบบที่ไม่ระบุชนิดข้อมูล ซึ่งเราเรียกว่าการประกาศแบบ raw type การประกาศแบบนี้เป็นรูปแบบดั้งเดิมของ Java ตั้งแต่รุ่นแรก โดย `ArrayList` ที่ไม่ระบุชนิดข้อมูลนี้จะสามารถใช้กับอ็อบเจกต์ชนิดใดก็ได้\n", 359 | "\n", 360 | "เราลองมาดูการประกาศแบบ raw type กันอีกครั้ง" 361 | ] 362 | }, 363 | { 364 | "cell_type": "code", 365 | "execution_count": 73, 366 | "metadata": {}, 367 | "outputs": [ 368 | { 369 | "name": "stdout", 370 | "output_type": "stream", 371 | "text": [ 372 | "scores: [28.5, 15, John, false]\n" 373 | ] 374 | } 375 | ], 376 | "source": [ 377 | "ArrayList scores = new ArrayList();\n", 378 | "\n", 379 | "scores.add(28.5);\n", 380 | "scores.add(15);\n", 381 | "scores.add(\"John\");\n", 382 | "scores.add(false);\n", 383 | "System.out.printf(\"scores: %s%n\", scores);\n", 384 | "\n", 385 | "double first = (double)scores.get(0);\n", 386 | "int second = (int)scores.get(1);\n", 387 | "String third = (String)scores.get(2);\n", 388 | "boolean fourth = (boolean)scores.get(3);" 389 | ] 390 | }, 391 | { 392 | "cell_type": "markdown", 393 | "metadata": {}, 394 | "source": [ 395 | "จะเห็นได้ว่าเราสามารถใส่ข้อมูลชนิดใดก็ได้ถ้าเราประกาศเป็นแบบ raw type ซึ่งดูเหมือนจะยืดหยุ่นดี แต่ปัญหาที่ตามมาใหญ่ ๆ มี 2 ประการ\n", 396 | "\n", 397 | "1. คอมไพเลอร์ไม่สามารถตรวจสอบความถูกต้องในการใช้งานได้เนื่องจากไม่มีข้อกำหนดว่าจะใช้กับข้อมูลชนิดใด ซึ่งทำให้เราสามารถใส่ข้อมูลที่ไม่ถูกชนิดโดยไม่ได้ตั้งใจได้\n", 398 | "2. เวลาที่เราจะเอาข้อมูลออกมาใช้ เราต้องมีการแปลงชนิดข้อมูลก่อนด้วยการ cast เช่น `(double)scores.get(0)` เพื่อบอกว่าข้อมูลที่นำออกมามีชนิดเป็น `double` ไม่อย่างนั้นแล้วโค้ดนี้จะไม่สามารถคอมไพล์ได้ โดยมีความผิดพลาดในรูปแบบ `incompatible types` และถ้าเราระบุชนิดผิดพลาด ก็จะเกิดความผิดพลาดชนิด `ClassCastException` ขึ้นได้\n", 399 | "\n", 400 | "นอกจากนี้การไม่ระบุชนิดในโค้ดอาจทำให้ความชัดเจนของโค้ดลดลงด้วย เพราะฉะนั้นการใช้ `ArrayList` หรือคลาสคอลเลกชันแบบเจเนอริกตัวอื่น ๆ ในแบบ raw type จึงเป็นสิ่งที่ควรหลีกเลี่ยง\n", 401 | "\n", 402 | "### คลาสหีบห่อ\n", 403 | "\n", 404 | "ในทั้ง 2 ตัวอย่างที่ผ่านมาเราจะมีข้อสังเกตอย่างหนึ่ง คือ เราไม่สามารถใช้ข้อมูลชนิดพื้นฐาน (primitive type) กับ `ArrayList` ได้โดยตรง อย่างเช่นตอนที่เราประกาศชนิดของ `ranks` เราประกาศว่า `ArrayList ranks` เราระบุชนิดของข้อมูลที่จะจัดเก็บเป็น `Integer` ซึ่งเป็นคลาสไม่ใช่ `int` ซึ่งเป็นชนิดพื้นฐาน\n", 405 | "\n", 406 | "ประเด็นนี้เป็นข้อจำกัดของชนิดเจเนอริกของ Java ซึ่งอนุญาตให้ใช้เจเนอริกกับข้อมูลชนิดตัวอ้างอิงเท่านั้น ไม่อนุญาตให้ใช้ข้อมูลชนิดพื้นฐาน ดังนั้น ถ้าเราต้องการใช้ `ArrayList` ซึ่งเป็นคลาสเจเนอริกในการจัดเก็บข้อมูลชนิด `int` เราจำเป็นต้องทำให้ข้อมูลนั้นอยู่ในรูปของอ็อบเจกต์ก่อนจึงจะจัดเก็บได้\n", 407 | "\n", 408 | "และนี่เป็นที่มาของกลุ่มคลาสพิเศษที่เรียกว่าคลาสหีบห่อ (wrapper class) ได้แก่ `Boolean`, `Character`, `Byte`, `Short`, `Integer`, `Long`, `Float` และ `Double` ซึ่งเราจะเห็นได้ว่าทุกชนิดพื้นฐานจะมีคลาสหีบห่อที่คู่กัน\n", 409 | "\n", 410 | "หน้าที่ของคลาสหีบห่อคือใช้สร้างอ็อบเจกต์เพื่อเก็บข้อมูลชนิดพื้นฐานเหล่านั้น หนึ่งอ็อบเจกต์ต่อหนึ่งข้อมูล เพื่อให้สามารถนำไปใช้กับคลาสที่ต้องการอ็อบเจกต์ได้ เช่น ใช้กับ `ArrayList` นั่นเอง เมื่อเราต้องการเอาค่า `int` ไปเก็บใน `ArrayList` เราต้องสร้างอ็อบเจกต์ `Integer` ขึ้นแล้วใส่ค่า `int` นี้ไว้ในอ็อบเจกต์ก่อนแล้วจึงนำไปเก็บ `ArrayList` การสร้างอ็อบเจกต์นี้ขึ้นมาแล้วเอาค่าไปเก็บไว้ในอ็อบเจกต์เรียกว่าการห่อ (boxing) และเมื่อเราต้องการเอาค่าออกมาใช้ในรูปของ `int` อีกครั้ง ก็จะต้องมีการนำค่าออกมาจากอ็อบเจกต์นั้น เรานำค่าออกมาเราเรียกว่าการแกะ (unboxing)\n", 411 | "\n", 412 | "Java มีคุณสมบัติการห่อและการแกะโดยอัตโนมัติ (autoboxing/unboxing) ทำให้กระบวนการที่กล่าวมานั้นเป็นไปเองโดยที่เราไม่ต้องระบุการห่อและการแกะโดยตรง ดังตัวอย่างนี้" 413 | ] 414 | }, 415 | { 416 | "cell_type": "code", 417 | "execution_count": 77, 418 | "metadata": {}, 419 | "outputs": [], 420 | "source": [ 421 | "ArrayList ranks = new ArrayList();\n", 422 | "\n", 423 | "// Without autoboxing/unboxing\n", 424 | "ranks.add(new Integer(10));\n", 425 | "int rankOne = ranks.get(0).intValue();\n", 426 | "\n", 427 | "// With autoboxing/unboxing\n", 428 | "ranks.add(20);\n", 429 | "int rankTwo = ranks.get(1);" 430 | ] 431 | }, 432 | { 433 | "cell_type": "markdown", 434 | "metadata": {}, 435 | "source": [ 436 | "รูปแบบแรกเราต้องสร้างอ็อบเจกต์จากคลาสหีบห่อขึ้นมาเองด้วยคำสั่ง `new` และเวลาที่เอาค่าออกมาใช้ก็ต้องมีการเรียกเมทอด `intValue` เพื่อขอค่าจากตัวอ็อบเจกต์ แต่ตั้งแต่ Java รุ่น 1.5 เป็นต้นมาเราสามารถละกระบวนการเหล่านี้ได้โดยใช้คุณสมบัติ autoboxing/unboxing เช่นในรูปแบบที่สองในตัวอย่าง อย่างไรก็ดี ในรูปแบบนี้ถึงแม้เราจะละการเขียนโค้ดเพื่อห่อและแกะโดยตรงซึ่งทำให้เราใช้งานได้สะดวกมากขึ้น แต่ในการทำงานเบื้องหลังก็ยังเกิดขั้นตอนการห่อและการแกะเหล่านั้นอยู่\n", 437 | "\n", 438 | "ในบางกรณี เราอาจจะยังจำเป็นต้องเขียนโค้ดเพื่อแกะหรือห่อโดยตรงอยู่บ้าง เช่น ในกรณีที่เราใช้ `ArrayList` ในรูปแบบ raw type ซึ่งคอมไพเลอร์จะไม่สามารถคาดเดาชนิดของข้อมูลที่อยู่ใน `ArrayList` ได้ ในกรณีนี้คอมไพเลอร์อาจจะไม่ทำกระบวนการห่อและแกะโดยอัตโนมัติให้ได้\n", 439 | "\n", 440 | "> **หมายเหตุ** คลาสหีบห่อมีเมทอดที่น่าสนใจสำหรับการแปลงชนิดข้อมูลจากสตริงมาเป็นชนิดพื้นฐานแต่ละชนิด เมทอดแรกคือ `valueOf` เช่น `Integer.valueOf(\"123\")` จะได้อ็อบเจกต์ของคลาสหีบห่อ `Integer` ที่มีค่าเป็น 123 และอีกเมทอดคือเมทอดกลุ่ม `parse` ซึ่งจะมีชื่อขึ้นต้นด้วย `parse` ตามด้วยชื่อชนิดพื้นฐาน เมทอดกลุ่มนี้ใช้แปลงข้อมูลจากสตริงมาเป็นชนิดพื้นฐานโดยตรงโดยไม่ต้องแปลงเป็นคลาสหีบห่อก่อน เช่น `Integer.parseInt(\"123\")` จะได้ค่า 123 เป็นชนิด `int` เลยโดยตรง\n", 441 | "\n", 442 | "### เมทอดที่น่าสนใจของ ArrayList\n", 443 | "\n", 444 | "ในตัวอย่างที่ผ่านมาเราเห็นการใช้งานเมทอดของ `ArrayList` ซึ่งใช้ในการใส่ข้อมูล (`add`) และเรียกข้อมูลมาดู (`get`) แล้ว ยังมีเมทอดอื่น ๆ ที่น่าสนใจอีก เช่น\n", 445 | "\n", 446 | "- `int indexOf(Object o)` สำหรับหาตำแหน่งที่มีข้อมูลนี้อยู่เป็นตำแหน่งแรก ถ้าไม่พบข้อมูลก็จะคืนค่ากลับเป็น -1\n", 447 | "- `boolean isEmpty()` คืนค่าเป็น true ถ้า `ArrayList` นั้นไม่มีข้อมูลอยู่\n", 448 | "- `E remove(int index)` ลบข้อมูล ณ ตำแหน่งที่ระบุ และคืนค่าเป็นข้อมูลที่ถูกลบ\n", 449 | "- `boolean remove(Object o)` ลบข้อมูลตัวแรกที่ตรงกับข้อมูลที่ระบุถ้ามีข้อมูลนั้นอยู่ และคืนค่าเป็น true ถ้าพบข้อมูล\n", 450 | "- `E set(int index, E element)` แทนค่า ณ ตำแหน่ง `index` ด้วยค่าใหม่ `element` และคืนค่าเดิมกลับไป\n", 451 | "- `int size()` คืนค่าเป็นจำนวนข้อมูลที่มีอยู่\n", 452 | "- `List subList(int fromIndex, int toIndex)` คืนค่าเป็น `List` ย่อยของตัวเดิม โดยเริ่มตั้งแต่สมาชิกตัวที่ `fromIndex` ไปจนถึงสมาชิกตัวก่อน `toIndex` ซึ่ง `List` ย่อยที่คืนค่าไปจะใช้ข้อมูลร่วมกับ `ArrayList` ตัวเดิม ดังนั้นการเปลี่ยนแปลงใด ๆ กับตัวย่อยจาก `subList` จะส่งผลถึงตัวเดิมด้วย\n", 453 | "\n", 454 | "ตัวอย่างการใช้งานเมทอดต่าง ๆ" 455 | ] 456 | }, 457 | { 458 | "cell_type": "code", 459 | "execution_count": 93, 460 | "metadata": {}, 461 | "outputs": [ 462 | { 463 | "name": "stdout", 464 | "output_type": "stream", 465 | "text": [ 466 | "Original ranks: [5, 12, 1, 1, 13, 5, 11, 12, 1]\n", 467 | "Removing 5\n", 468 | "Removing 12\n", 469 | "Removing 1\n", 470 | "Removing 1\n", 471 | "After pruning: [5, 12, 1, 13, 11]\n" 472 | ] 473 | } 474 | ], 475 | "source": [ 476 | "ArrayList ranks = new ArrayList();\n", 477 | "Collections.addAll(ranks, 5, 12, 1, 1, 10, 5, 11, 12, 1);\n", 478 | "\n", 479 | "ranks.set(4, 13);\n", 480 | "System.out.printf(\"Original ranks: %s%n\", ranks);\n", 481 | "\n", 482 | "int index = 0;\n", 483 | "while (!ranks.isEmpty() && index < ranks.size()) {\n", 484 | " int key = ranks.get(index);\n", 485 | " do {\n", 486 | " // Get a sub-list starting right after the key object\n", 487 | " List subList = ranks.subList(index+1, ranks.size());\n", 488 | " \n", 489 | " // Find the first duplicate in the sub-list and remove it\n", 490 | " int duplicate = subList.indexOf(key);\n", 491 | " if (duplicate < 0)\n", 492 | " break;\n", 493 | " \n", 494 | " System.out.println(\"Removing \" + key);\n", 495 | " subList.remove(duplicate);\n", 496 | " } while (true);\n", 497 | " index++;\n", 498 | "}\n", 499 | "\n", 500 | "System.out.printf(\"After pruning: %s%n\", ranks);" 501 | ] 502 | }, 503 | { 504 | "cell_type": "markdown", 505 | "metadata": {}, 506 | "source": [ 507 | "ตัวอย่างนี้เป็นการหาค่าซ้ำแล้วลบออกให้เหลือเพียงค่าเดียว โดยจะหยิบค่าแรกมา แล้ววนหาตำแหน่งของค่าที่ซ้ำแล้วลบออกทั้งหมด แล้วจึงหยิบค่าถัดไป ทำไปเรื่อย ๆ จนจบข้อมูล\n", 508 | "\n", 509 | "`ArrayList` มีความยืดหยุ่นสูงกว่าอาร์เรย์และมีประสิทธิภาพในระดับใกล้เคียงกับอาร์เรย์ เพราะการทำงานภายในของ `ArrayList` ก็ใช้อาร์เรย์อยู่เบื้องหลัง แต่ถ้าเราต้องการใช้งานกับข้อมูลชนิดพื้นฐาน (primitive type) ซึ่ง `ArrayList` ไม่สามารถทำได้โดยตรง ต้องมีการสร้างอ็อบเจกต์จาก wrapper class ขึ้นมาเก็บข้อมูลแต่ละตัว และอาจจะมีกระบวนการ boxing/unboxing เกิดขึ้นระหว่างที่เราอ้างอิงข้อมูลด้วย ซึ่งส่งผลกระทบต่อประสิทธิภาพได้ ในกรณีนี้อาร์เรย์ก็อาจจะเป็นตัวเลือกที่ดีกว่า\n", 510 | "\n", 511 | "> **หมายเหตุ** `ArrayList` เป็นส่วนหนึ่งของ Java Collection Framework และมีรูปแบบเป็นคลาสเจเนอริก (generic class) ซึ่งสามารถกำหนดให้จัดเก็บข้อมูลเป็นชนิดคลาสใดก็ได้ เราจะพูดถึงชนิดเจเนอริกโดยละเอียดอีกครั้งในภายหลัง\n", 512 | "\n", 513 | "## การวนซ้ำบนคอลเลกชัน\n", 514 | "\n", 515 | "การวนซ้ำบนข้อมูลในคอลเลกชันเป็นสิ่งที่เกิดขึ้นบ่อย เราจึงมีโครงสร้างการวนซ้ำแบบที่ใช้กับคอลเลกชันหรืออาร์เรย์โดยเฉพาะ ซึ่งมีรูปแบบดังนี้\n", 516 | "\n", 517 | "```\n", 518 | "for (tx x : expression)\n", 519 | " body\n", 520 | "```\n", 521 | "\n", 522 | "ลูป `for` นี้จะวนซ้ำจำนวนรอบเท่ากับจำนวนสมาชิกในคอลเลกชันหรืออาร์เรย์ที่ระบุในส่วน _expression_ โดยตัวแปร `x` ชนิด _tx_ ซึ่งต้องสอดคล้องกับชนิดของข้อมูลในคอลเลกชัน จะเปลี่ยนค่าไปทุกรอบ แต่ละรอบจะมีค่าเท่ากับสมาชิกแต่ละตัวของคอลเลกชันตามลำดับ (ยกเว้นคอลเลกชันชนิดที่ไม่มีลำดับแน่นอน)\n", 523 | "\n", 524 | "ในการวนซ้ำแบบนี้ เราจะไม่มีตัวนับ จึงอาจไม่รู้ว่าการวนอยู่ในรอบที่เท่าใดหรือกำลังจัดการกับสมาชิกลำดับที่เท่าใด ยกเว้นจะกำหนดตัวแปรนับขึ้นมาต่างหาก แต่ในการใช้งานจริงบ่อยครั้งที่เราไม่จำเป็นต้องรู้ว่าเรากำลังดำเนินการกับสมาชิกตัวที่เท่าใด สำคัญเพียงว่าสมาชิกนั้นมีค่าเป็นอะไรเท่านั้น\n", 525 | "\n", 526 | "ตัวอย่างต่อไปนี้แสดงการวนซ้ำเพื่อหาค่าผลรวมของอาร์เรย์" 527 | ] 528 | }, 529 | { 530 | "cell_type": "code", 531 | "execution_count": 113, 532 | "metadata": {}, 533 | "outputs": [ 534 | { 535 | "name": "stdout", 536 | "output_type": "stream", 537 | "text": [ 538 | "Sum = 31.85, Average = 4.55\n" 539 | ] 540 | } 541 | ], 542 | "source": [ 543 | "double[] scores = { 9.1, 6.25, 2.5, 5.0, 1.25, 7.5, 0.25 };\n", 544 | "\n", 545 | "double sum = 0;\n", 546 | "for (double score : scores) {\n", 547 | " sum += score;\n", 548 | "}\n", 549 | "\n", 550 | "System.out.printf(\"Sum = %.2f, Average = %.2f%n\", sum, sum / scores.length);" 551 | ] 552 | }, 553 | { 554 | "cell_type": "markdown", 555 | "metadata": {}, 556 | "source": [ 557 | "## สตริง\n", 558 | "\n", 559 | "สตริงเป็นข้อมูลชนิดข้อความ ซึ่งเป็นการเรียงต่อกันของข้อมูลชนิดตัวตัวอักษร (`char`) เราอาจมองสตริงคล้ายกับอาร์เรย์ของตัวอักษร แต่สตริงไม่สามารถใช้งานอย่างอาร์เรย์โดยตรงได้ และไม่สามารถเปลี่ยนแปลงค่าได้ หากต้องการสตริงที่มีค่าบางตัวอักษรแตกต่างจากเดิมต้องสร้างสตริงใหม่ หรือใช้คลาส `StringBuilder` หรือ `StringBuffer` ในการจัดการ\n", 560 | "\n", 561 | "สตริงมีเมทอดที่สำคัญดังนี้\n", 562 | "\n", 563 | "- `equals` ใช้ทดสอบว่าสตริง 2 ตัวมีค่าเท่ากันหรือไม่\n", 564 | "- `compareTo` สำหรับเปรียบเทียบค่าของสตริง 2 ตัวว่าตัวใดมาก่อนหรือหลังตามลำดับตัวอักษร ถ้าต้องการเปรียบเทียบโดยไม่สนใจว่าเป็นตัวพิมพ์ใหญ่หรือตัวพิมพ์เล็กให้ใช้ `compareToIgnoreCase`\n", 565 | "- `length` คืนค่าเป็นความยาวของสตริง\n", 566 | "- `format` คืนค่าเป็นสตริงที่จัดรูปแบบ การใช้งานเหมือนกับเมทอด `printf`\n", 567 | "- `indexOf` ใช้ค้นหาสตริงย่อยภายในสตริง คืนค่าเป็นตำแหน่งที่พบ หรือเป็น -1 หากไม่พบ\n", 568 | "- `split` ใช้แยกสตริงออกเป็นอาร์เรย์ของสตริง โดยระบุตัวสัญลักษณ์ที่ใช้แบ่งได้\n", 569 | "- `substring` คืนค่าเป็นสตริงย่อยของสตริงเดิม โดยระบุตำแหน่งเริ่มต้นและตำแหน่งสิ้นสุด\n", 570 | "- `toCharArray` คืนค่าเป็นอาร์เรย์ของ `char` เพื่อนำไปใช้งานต่อในรูปแบบอาร์เรย์ได้\n", 571 | "- `toLowerCase` คืนค่าเป็นสตริงใหม่ที่เปลี่ยนตัวอักษรเป็นตัวพิมพ์เล็กทั้งหมด\n", 572 | "- `toUpperCase` คืนค่าเป็นสตริงใหม่ที่เปลี่ยนตัวอักษรเป็นตัวพิมพ์ใหญ่ทั้งหมด\n", 573 | "- `trim` คืนค่าเป็นสตริงใหม่ที่ตัดช่องว่างด้านหน้าและด้านหลังออกแล้ว\n", 574 | "\n", 575 | "ตัวอย่างต่อไปนี้แสดงการใช้งานเมทอดของสตริงในหลาย ๆ รูปแบบ" 576 | ] 577 | }, 578 | { 579 | "cell_type": "code", 580 | "execution_count": 111, 581 | "metadata": {}, 582 | "outputs": [ 583 | { 584 | "name": "stdout", 585 | "output_type": "stream", 586 | "text": [ 587 | "Split: [This, is, the, real, world.]\n", 588 | "Upper: THISISTHEREALWORLD.\n", 589 | "Substitute: This is the REAL world.\n", 590 | "Char Array: ~W~e~l~c~o~m~e~\n" 591 | ] 592 | } 593 | ], 594 | "source": [ 595 | "String sentence = \"This is the real world.\";\n", 596 | "\n", 597 | "// Split to array\n", 598 | "String[] words = s1.split(\" \");\n", 599 | "System.out.printf(\"Split: %s%n\", Arrays.toString(words));\n", 600 | "\n", 601 | "// Make upper-case and join all words\n", 602 | "String upperCase = \"\";\n", 603 | "for (String word : words) {\n", 604 | " upperCase += word.toUpperCase();\n", 605 | "}\n", 606 | "System.out.println(\"Upper: \" + upperCase);\n", 607 | "\n", 608 | "// Substituting a word by rebuilding a new string\n", 609 | "int realPosition = sentence.indexOf(\"real\");\n", 610 | "String realSentence = sentence.substring(0, realPosition)\n", 611 | " + \"REAL\" + sentence.substring(realPosition + 4);\n", 612 | "System.out.println(\"Substitute: \" + realSentence);\n", 613 | "\n", 614 | "// Convert to char array and process each character individually\n", 615 | "System.out.print(\"Char Array: \");\n", 616 | "for (char c : \"Welcome\".toCharArray()) {\n", 617 | " System.out.print(\"~\" + c);\n", 618 | "}\n", 619 | "System.out.println(\"~\");" 620 | ] 621 | }, 622 | { 623 | "cell_type": "markdown", 624 | "metadata": {}, 625 | "source": [ 626 | "## การแจงนับ\n", 627 | "\n", 628 | "เราสามารถกำหนดชนิดข้อมูลใหม่ที่มีค่าเป็นไปตามที่เราระบุได้ในรูปของข้อมูลชนิด `enum` หรือข้อมูลชนิดแจงนับ\n", 629 | "\n", 630 | "ข้อมูลชนิด `enum` ทำให้เราสามารถกำหนดค่าที่เป็นไปได้ทั้งหมดของข้อมูลชนิดนั้นได้ เช่น กำหนดข้อมูลชนิดวันในสัปดาห์ โดยมีค่าที่เป็นไปได้คือชื่อวันทั้ง 7 วัน การกำหนดแบบนี้ทำให้เราสามารถอ้างอิงข้อมูลในรูปแบบที่เข้าใจง่ายขึ้นได้ และยังเป็นการกำหนดด้วยว่าค่าใดที่ใช้ได้บ้าง\n", 631 | "\n", 632 | "การตั้งชื่อของค่า `enum` เป็นไปตามกฎการตั้งชื่อของ Java แต่โดยธรรมเนียมแล้วเราจะตั้งชื่อแบบเดียวกับค่าคงที่ คือใช้ตัวพิมพ์ใหญ่ทั้งหมดและแบ่งคำด้วย `_` (underscore)\n", 633 | "\n", 634 | "โค้ดต่อไปนี้แสดงตัวอย่างของการใช้ `enum`" 635 | ] 636 | }, 637 | { 638 | "cell_type": "code", 639 | "execution_count": 118, 640 | "metadata": {}, 641 | "outputs": [ 642 | { 643 | "name": "stdout", 644 | "output_type": "stream", 645 | "text": [ 646 | "It's just another work day.\n" 647 | ] 648 | } 649 | ], 650 | "source": [ 651 | "enum DayOfWeek {\n", 652 | " SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY\n", 653 | "}\n", 654 | "\n", 655 | "DayOfWeek day = DayOfWeek.TUESDAY;\n", 656 | "\n", 657 | "switch (day) {\n", 658 | " case SUNDAY:\n", 659 | " System.out.println(\"I'll just sleep.\");\n", 660 | " break;\n", 661 | " case MONDAY:\n", 662 | " System.out.println(\"I hate Monday.\");\n", 663 | " break;\n", 664 | " case FRIDAY:\n", 665 | " System.out.println(\"Hey, it's Friday.\");\n", 666 | " break;\n", 667 | " case SATURDAY:\n", 668 | " System.out.println(\"Yay! I'm free.\");\n", 669 | " break;\n", 670 | " default:\n", 671 | " System.out.println(\"It's just another work day.\");\n", 672 | "}" 673 | ] 674 | }, 675 | { 676 | "cell_type": "markdown", 677 | "metadata": {}, 678 | "source": [ 679 | "ตัวอย่างนี้มีจุดที่น่าสนใจคือ ในการใช้งาน `enum` โดยทั่วไป เมื่อเราต้องการอ้างอิงถึงค่าของ `enum` เรามักจะต้องระบุชื่อของ `enum` ด้วย เช่น `DayOfWeek.TUESDAY` แต่ในคำสั่ง `switch` เราจะไม่ระบุชื่อของ `enum` แต่จะระบุเพียงค่าของมันเท่านั้น เพราะ `switch` รู้ชนิดของ `enum` จากค่าที่นำมาทดสอบอยู่แล้ว\n", 680 | "\n", 681 | "> **หมายเหตุ** `enum` เป็นข้อมูลชนิดตัวอ้างอิงแบบหนึ่งซึ่งมีลักษณะคล้ายกับคลาส (ในทางเทคนิคแล้ว `enum` เป็นคลาสแบบหนึ่ง) การใช้งาน `enum` มีรายละเอียดที่น่าสนใจอื่น ๆ อีกมาก\n", 682 | "\n", 683 | "## คลาส HashMap\n", 684 | "\n", 685 | "ข้อมูลชนิดอาร์เรย์หรือ `ArrayList` ใช้ตัวเลขตำแหน่งในการอ้างอิงข้อมูล แต่ในงานบางอย่าง เราอาจต้องการใช้ค่าชนิดอื่นในการอ้างอิงข้อมูล เช่น ข้อความ เป็นต้น คอลเลกชันชนิด `HashMap` ทำให้เราสามารถกำหนดชนิดของดัชนีหรือตัวอ้างอิงตำแหน่งข้อมูลได้\n", 686 | "\n", 687 | "ตัวอย่างการใช้งาน `HashMap`" 688 | ] 689 | }, 690 | { 691 | "cell_type": "code", 692 | "execution_count": 122, 693 | "metadata": {}, 694 | "outputs": [ 695 | { 696 | "name": "stdout", 697 | "output_type": "stream", 698 | "text": [ 699 | "March has 31 days.\n" 700 | ] 701 | } 702 | ], 703 | "source": [ 704 | "HashMap daysInMonth = new HashMap<>();\n", 705 | "\n", 706 | "daysInMonth.put(\"Jan\", 31);\n", 707 | "daysInMonth.put(\"Feb\", 28);\n", 708 | "daysInMonth.put(\"Mar\", 31);\n", 709 | "\n", 710 | "// ... code omitted\n", 711 | "\n", 712 | "daysInMonth.put(\"Dec\", 31);\n", 713 | "\n", 714 | "System.out.printf(\"%s has %d days.%n\", \"March\", daysInMonth.get(\"Mar\"));" 715 | ] 716 | }, 717 | { 718 | "cell_type": "markdown", 719 | "metadata": {}, 720 | "source": [ 721 | "> **หมายเหตุ** เรายังไม่เน้นการใช้งานคลาส `HashMap` ในที่นี้ เป็นเพียงการแนะนำเบื้องต้นเพื่อเปรียบเทียบกับข้อมูลชนิด dictionary ของ Python เท่านั้น" 722 | ] 723 | } 724 | ], 725 | "metadata": { 726 | "kernelspec": { 727 | "display_name": "Java", 728 | "language": "java", 729 | "name": "java" 730 | }, 731 | "language_info": { 732 | "codemirror_mode": "java", 733 | "file_extension": ".java", 734 | "mimetype": "text/x-java-source", 735 | "name": "Java", 736 | "pygments_lexer": "java", 737 | "version": "9.0.4+11" 738 | } 739 | }, 740 | "nbformat": 4, 741 | "nbformat_minor": 2 742 | } 743 | -------------------------------------------------------------------------------- /03 - Collections/Collections.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# คอลเลกชัน\n", 8 | "\n", 9 | "คอลเลกชัน (collection) หรือข้อมูลชนิดรวมหมู่เป็นชนิดของข้อมูลประเภทหนึ่งที่ใช้เพื่อเก็บข้อมูลชนิดอื่น ๆ หลาย ๆ จำนวนไว้ด้วยกัน ในบทนี้เราจะมาศึกษาข้อมูลชนิดอาร์เรย์ คลาส `ArrayList` สตริง อีนัม และคลาส `HashMap` นอกจากนี้เราจะศึกษาการวนซ้ำบนคอลเลกชันด้วย\n", 10 | "\n", 11 | "## อาร์เรย์\n", 12 | "\n", 13 | "อาร์เรย์ (array) หรือแถวลำดับเป็นชนิดข้อมูลที่ใช้จัดเก็บข้อมูลชนิดอื่นแบบเป็นลำดับ โดยสามารถใช้ดัชนี (index) เป็นตัวระบุตำแหน่งเพื่อเข้าถึงข้อมูลตำแหน่งใด ๆ ก็ได้ในทันที\n", 14 | "\n", 15 | "### การประกาศและสร้างอาร์เรย์\n", 16 | "\n", 17 | "การประกาศตัวแปรให้เป็นชนิดอาร์เรย์จะต้องระบุว่าเป็นอาร์เรย์ของข้อมูลชนิดใด และใช้รูปแบบการประกาศดังนี้\n", 18 | "\n", 19 | "```\n", 20 | "type[] varName;\n", 21 | "```\n", 22 | "\n", 23 | "วงเล็บก้ามปูสามารถไว้ท้ายชื่อชนิดหรือท้ายชื่อตัวแปรก็ได้ เพราะฉะนั้นรูปแบบการประกาศแบบนี้ก็ถือว่าถูกต้องเช่นกัน\n", 24 | "\n", 25 | "```\n", 26 | "type varName[];\n", 27 | "```\n", 28 | "\n", 29 | "เช่น" 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": null, 35 | "metadata": {}, 36 | "outputs": [], 37 | "source": [ 38 | "int[] ranks;\n", 39 | "String[] messages;\n", 40 | "double scores[];" 41 | ] 42 | }, 43 | { 44 | "cell_type": "markdown", 45 | "metadata": {}, 46 | "source": [ 47 | "รูปแบบหลังนี้เป็นแบบที่รับมาจากภาษา C แต่เป็นรูปแบบที่ไม่นิยมและไม่แนะนำให้ใช้\n", 48 | "\n", 49 | "ตัวแปรอาร์เรย์ที่เราประกาศแล้วแต่ยังไม่กำหนดค่าตั้งต้นให้จะมีค่าเป็น `null` ซึ่งหมายถึงตัวแปรอาร์เรย์นี้ยังไม่ได้อ้างอิงอาร์เรย์ใด ๆ เป็นเพียงตัวแปร แต่ยังไม่มีตัวอาร์เรย์\n", 50 | "\n", 51 | "เราสามารถสร้างอาร์เรย์และกำหนดค่าตั้งต้นให้กับตัวแปรอาร์เรย์ได้หลายวิธีดังนี้" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": 10, 57 | "metadata": {}, 58 | "outputs": [], 59 | "source": [ 60 | "int[] ranks = new int[10];\n", 61 | "double[] scores = { 1.25, 2.5, 5.0, 6.25, 7.5 };\n", 62 | "String[] messages = new String[] { \"Hello\", \"this\", \"is\", \"a\", \"message\" };\n", 63 | "double[] distances;\n", 64 | "distances = new double[] { 425.23, 771.94, 23.56, 1_038.77 };" 65 | ] 66 | }, 67 | { 68 | "cell_type": "markdown", 69 | "metadata": {}, 70 | "source": [ 71 | "แบบแรกเป็นการประกาศตัวแปรอาร์เรย์ `ranks` แล้วกำหนดค่าตั้งต้นให้โดยการใช้คำสั่ง `new` สร้างอาร์เรย์ของชนิด `int` ขนาด 10 สมาชิกโดยไม่ระบุค่าตั้งต้นให้กับสมาชิกแต่ละตัว\n", 72 | "\n", 73 | "การไม่กำหนดค่าตั้งต้นให้กับสมาชิกของอาร์เรย์จะทำให้สมาชิกแต่ละตัวมีค่าตั้งต้นโดยปริยายโดยอัตโนมัติ ซึ่งถ้ามีชนิดเป็นตัวเลขหรือ `char` ก็จะมีค่าเป็น 0 หรือ 0.0 ถ้าเป็น `boolean` ก็จะมีค่าเป็น false และถ้าเป็นข้อมูลชนิดตัวอ้างอิงก็จะมีค่าเป็น `null`\n", 74 | "\n", 75 | "แบบถัดมา `scores` ถูกประกาศพร้อมกับกำหนดให้เป็นอาร์เรย์ของ `double` ที่มีข้อมูลเป็น 1.25, 2.5, 5.0, 6.25, 7.5 ตามลำดับ ขนาดของอาร์เรย์จะเป็น 5 สมาชิกโดยอัตโนมัติ\n", 76 | "\n", 77 | "ตัวแปรอาร์เรย์ `messages` ก็เช่นกัน ถูกกำหนดให้มีค่าตั้งต้นเป็น `\"Hello\"`, `\"this\"`, `\"is\"`, `\"a\"`, `\"message\"` ซึ่งมีขนาดเป็น 5 สมาชิก การกำหนดค่าเริ่มต้นแบบนี้คล้ายกับแบบของ `scores` แต่เราใช้คำสั่ง `new` ในการสร้างอาร์เรย์\n", 78 | "\n", 79 | "รูปแบบที่ใช้กับการกำหนดค่าเริ่มต้นของ `messages` อาจจะดูยาวกว่าแบบที่ใช้กับ `scores` แต่มีข้อดีคือสามารถใช้สร้างอาร์เรย์พร้อมกำหนดค่าตั้งต้นให้กับสมาชิกอาร์เรย์ที่ใดก็ได้ ไม่จำเป็นต้องใช้เฉพาะเวลาประกาศตัวแปรเหมือนอย่างแบบที่ใช้กับ `scores`\n", 80 | "\n", 81 | "เราเห็นตัวอย่างได้จาก `distances` ซึ่งตอนประกาศเราไม่ได้สร้างอาร์เรย์ขึ้นมาด้วย แต่มาสร้างและกำหนดให้ทีหลังในบรรทัดต่อไปโดยใช้รูปแบบเดียวกับที่กับ `messages` การกำหนดค่าเริ่มต้นแบบที่ใช้กับ `scores` ไม่สามารถใช้ในกรณีนี้ได้\n", 82 | "\n", 83 | "อาร์เรย์ที่สร้างขึ้นมาแล้วไม่สามารถเปลี่ยนขนาดได้นอกจากจะสร้างอาร์เรย์ใหม่ให้มีขนาดใหม่ที่ต้องการแล้วย้ายข้อมูลจากอาร์เรย์เดิมไป\n", 84 | "\n", 85 | "### การเข้าถึงอาร์เรย์\n", 86 | "\n", 87 | "เราสามารถเข้าถึงสมาชิกแต่ละตัวในอาร์เรย์ได้โดยใช้รูปแบบ `varName[i]` โดยจะเป็นตัวระบุตำแหน่งสมาชิก สมาชิกตัวแรกจะเริ่มต้นที่ตำแหน่ง 0\n", 88 | "\n", 89 | "เราสามารถรู้ขนาดของอาร์เรย์ได้โดยการอ้างอิงคุณสมบัติ `length` ของอาร์เรย์นั้นในรูป `varName.length` ตำแหน่งของสมาชิกจะเริ่มจาก 0 ไปสิ้นสุดที่ `length` - 1\n", 90 | "\n", 91 | "ตัวอย่างต่อไปนี้เป็นการหาผลบวกสะสมของลำดับตัวเลข โดยเก็บค่าผลลัพธ์กลับลงไปในอาร์เรย์ตัวเดิมเลย" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": 15, 97 | "metadata": {}, 98 | "outputs": [ 99 | { 100 | "name": "stdout", 101 | "output_type": "stream", 102 | "text": [ 103 | "[1.0, 4.5, 6.5, 8.0, 13.5, 16.5]\n" 104 | ] 105 | } 106 | ], 107 | "source": [ 108 | "// In-place cumulative sum\n", 109 | "double[] values = { 1.0, 3.5, 2.0, 1.5, 5.5, 3.0 };\n", 110 | "double sum = 0.0;\n", 111 | "\n", 112 | "for (int i = 0; i < values.length; i++) {\n", 113 | " sum += values[i];\n", 114 | " values[i] = sum;\n", 115 | "}\n", 116 | "\n", 117 | "System.out.println(Arrays.toString(values));" 118 | ] 119 | }, 120 | { 121 | "cell_type": "markdown", 122 | "metadata": {}, 123 | "source": [ 124 | "เราจะเห็นว่าทั้งการอ้างอิงอาร์เรย์เพื่อนำค่ามาใช้หรือการกำหนดค่าให้กับสมาชิกในอาร์เรย์ต่างก็ใช้รูปแบบ `values[i]` เหมือนกัน\n", 125 | "\n", 126 | "นอกจากนี้สิ่งที่เราเห็นจากตัวอย่างนี้อีกอย่างคือการแสดงค่าอาร์เรย์ซึ่งเราใช้ `Arrays.toString`\n", 127 | "\n", 128 | "## คลาส Arrays\n", 129 | "\n", 130 | "การจัดการกับข้อมูลชนิดอาร์เรย์โดยเบื้องต้นแล้วทำได้เพียงการสร้างอาร์เรย์และการอ้างอิงสมาชิกของอาร์เรย์ แต่งานบางอย่างที่ควรจะทำได้ง่ายก็ค่อนข้างยุ่งยากถ้าจะจัดการโดยตรง เช่น การเปรียบเทียบว่าอาร์เรย์ 2 ตัวมีค่าเท่ากัน การกำหนดค่าเดียวกันให้กับสมาชิกทุกตัวในอาร์เรย์ หรือการแสดงค่าอาร์เรย์ทั้งหมดออกทางจอภาพ\n", 131 | "\n", 132 | "แต่ JDK ก็ให้เครื่องมือจัดการกับอาร์เรย์เพิ่มเติมมาในรูปของคลาสอำนวยความสะดวกชื่อว่า `Arrays`\n", 133 | "\n", 134 | "คลาสนี้ชื่อว่า `Arrays` แต่โดยตัวมันเองไม่ได้เป็นอาร์เรย์ เป็นเพียงคลาสที่รวบรวมเอาเมทอดที่เกี่ยวข้องกับการจัดการกับอาร์เรย์เอาไว้ในตัว เมทอดที่น่าสนใจมีดังนี้\n", 135 | "\n", 136 | "- `sort` ใช้เรียงลำดับข้อมูลในอาร์เรย์จากน้อยไปมาก\n", 137 | "- `fill` ใช้เพื่อใส่ค่าที่กำหนดเข้าไปในสมาชิกของอาร์เรย์ทีละหลาย ๆ ตัว\n", 138 | "- `copyOf` ใช้เพื่อสร้างอาร์เรย์ใหม่ที่มีข้อมูลเหมือนอาร์เรย์เดิม\n", 139 | "- `equals` ใช้เปรียบเทียบของสมาชิกระหว่างอาร์เรย์ 2 ตัวว่าเหมือนกันทั้งหมดหรือไม่\n", 140 | "- `toString` ใช้เมื่อต้องการสตริงที่แสดงค่าของสมาชิกแต่ละตัวในอาร์เรย์ ถ้าเป็นอาร์เรย์ซ้อนกันหรืออาร์เรย์หลายมิติควรใช้ `deepToString`\n", 141 | "- `binarySearch` ใช้ค้นหาข้อมูลที่ต้องการในอาร์เรย์ มีเงื่อนไขว่าข้อมูลในอาร์เรย์ต้องเรียงลำดับมาก่อนจึงจะเรียกใช้ได้\n", 142 | "\n", 143 | "เมทอดอื่น ๆ ของคลาส Arrays ที่น่าสนใจสามารถศึกษาเพิ่มเติมได้ที่นี่ https://docs.oracle.com/javase/9/docs/api/java/util/Arrays.html\n", 144 | "\n", 145 | "ตัวอย่างการใช้งานคลาส `Arrays`" 146 | ] 147 | }, 148 | { 149 | "cell_type": "code", 150 | "execution_count": 28, 151 | "metadata": {}, 152 | "outputs": [ 153 | { 154 | "name": "stdout", 155 | "output_type": "stream", 156 | "text": [ 157 | "Original values: [9.1, 6.25, 2.5, 5.0, 1.25, 7.5, 0.25]\n", 158 | "Copied values: [9.1, 6.25, 2.5, 5.0, 1.25, 7.5, 0.25]\n", 159 | "Original and copied arrays are equal.\n", 160 | "Sorted values: [0.25, 1.25, 2.5, 5.0, 6.25, 7.5, 9.1]\n", 161 | "Found 6.25 at position 4.\n", 162 | "Original and copied arrays are now different.\n", 163 | "Copied array after fill operation: [1.23, 1.23, 1.23, 1.23, 1.23, 1.23, 1.23]\n" 164 | ] 165 | } 166 | ], 167 | "source": [ 168 | "double[] scores = { 9.1, 6.25, 2.5, 5.0, 1.25, 7.5, 0.25 };\n", 169 | "\n", 170 | "System.out.printf(\"Original values: %s%n\", Arrays.toString(scores));\n", 171 | "\n", 172 | "double[] copiedScores = Arrays.copyOf(scores, scores.length);\n", 173 | "System.out.printf(\"Copied values: %s%n\", Arrays.toString(copiedScores));\n", 174 | "\n", 175 | "if (Arrays.equals(scores, copiedScores)) {\n", 176 | " System.out.println(\"Original and copied arrays are equal.\");\n", 177 | "} else {\n", 178 | " System.out.println(\"Original and copied arrays are now different.\");\n", 179 | "}\n", 180 | "\n", 181 | "Arrays.sort(scores);\n", 182 | "System.out.printf(\"Sorted values: %s%n\", Arrays.toString(scores));\n", 183 | "\n", 184 | "System.out.printf(\"Found 6.25 at position %d.%n\", Arrays.binarySearch(scores, 6.25));\n", 185 | "\n", 186 | "if (Arrays.equals(scores, copiedScores)) {\n", 187 | " System.out.println(\"Original and copied arrays are equal.\");\n", 188 | "} else {\n", 189 | " System.out.println(\"Original and copied arrays are now different.\");\n", 190 | "}\n", 191 | "\n", 192 | "Arrays.fill(copiedScores, 1.23);\n", 193 | "System.out.printf(\"Copied array after fill operation: %s%n\", Arrays.toString(copiedScores));" 194 | ] 195 | }, 196 | { 197 | "cell_type": "markdown", 198 | "metadata": {}, 199 | "source": [ 200 | "ในตัวอย่างนี้เราเริ่มต้นด้วยการสร้างอาร์เรย์ `scores` ซึ่งมีข้อมูลไม่เรียงลำดับอยู่จำนวนหนึ่ง และแสดงข้อมูลนั้นโดยใช้ `Arrays.toString` เพื่อเรียกเอาสตริงที่แสดงข้อมูลในอาร์เรย์ออกมาแสดงผล จากนั้นเราก็ทดสอบการสร้างอาร์เรย์ใหม่ที่มีข้อมูลเหมือนอาร์เรย์เดิมโดยใช้ `Arrays.copyOf` และทดสอบว่าอาร์เรย์ใหม่และอาร์เรย์เดิมมีข้อมูลเหมือนกันด้วย `Arrays.equals`\n", 201 | "\n", 202 | "ต่อจากนั้นจึงเอา `scores` มาเรียงลำดับด้วย `Arrays.sort` และใช้ `Arrays.binarySearch` ค้นหาตำแหน่งของข้อมูลที่ต้องการและแสดงออกมา การทดสอบว่าอาร์เรย์ `scores` และ `copiedScores` เท่ากันหรือไม่อีกครั้งแสดงให้เห็นว่าไม่เท่ากันแล้วเนื่องจาก `scores` ถูกนำไปเรียงลำดับ สุดท้ายเป็นการใช้ `Arrays.fill` เพื่อใส่ค่าที่ต้องการในทุกสมาชิกของอาร์เรย์ `copiedScores`\n", 203 | "\n", 204 | "เรารู้ว่าการขยายขนาดอาร์เรย์ไม่สามารถทำได้โดยตรง แต่เราสามารถสร้างอาร์เรย์ใหม่มาแทนที่โดยกำหนดให้มีขนาดใหม่ที่ต้องการแล้วย้ายข้อมูลจากอาร์เรย์เดิมไปโดยใช้ `Arrays.copyOf` ได้ ดังตัวอย่างนี้" 205 | ] 206 | }, 207 | { 208 | "cell_type": "code", 209 | "execution_count": 29, 210 | "metadata": {}, 211 | "outputs": [ 212 | { 213 | "name": "stdout", 214 | "output_type": "stream", 215 | "text": [ 216 | "Scores after resizing: [9.1, 6.25, 2.5, 5.0, 1.25, 7.5, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]\n" 217 | ] 218 | } 219 | ], 220 | "source": [ 221 | "double[] scores = { 9.1, 6.25, 2.5, 5.0, 1.25, 7.5, 0.25 };\n", 222 | "int newSize = 2 * scores.length;\n", 223 | "\n", 224 | "scores = Arrays.copyOf(scores, newSize);\n", 225 | "System.out.printf(\"Scores after resizing: %s%n\", Arrays.toString(scores));" 226 | ] 227 | }, 228 | { 229 | "cell_type": "markdown", 230 | "metadata": {}, 231 | "source": [ 232 | "## อาร์เรย์หลายมิติ\n", 233 | "\n", 234 | "ปัญหาบางประเภทจำเป็นต้องใช้โครงสร้างหลายมิติ เช่น การคำนวณเมทริกซ์ การจำลองโมเดลเชิงปริมาตรต่าง ๆ เป็นต้น เราสามารถกำหนดอาร์เรย์ให้มีหลายมิติเพื่อรองรับปัญหาเหล่านี้ได้\n", 235 | "\n", 236 | "อาร์เรย์หลายมิติในความเป็นจริงแล้วคืออาร์เรย์ที่ซ้อนกันอยู่ หรือเรียกว่าอาร์เรย์ของอาร์เรย์ เราสามารถประกาศอาร์เรย์หลายมิติและกำหนดค่าตั้งต้นให้ได้หลายรูปแบบดังตัวอย่างต่อไปนี้" 237 | ] 238 | }, 239 | { 240 | "cell_type": "code", 241 | "execution_count": 40, 242 | "metadata": {}, 243 | "outputs": [ 244 | { 245 | "name": "stdout", 246 | "output_type": "stream", 247 | "text": [ 248 | "matrixA:\n", 249 | "[[D@52525845, [D@3b94d659]\n", 250 | "[[1.2, 2.4, 3.6], [1.3, 2.6, 3.9]]\n", 251 | "zerosTable:\n", 252 | "[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]\n", 253 | "matrixB:\n", 254 | "[[1.2, 2.4], [3.6, 1.3], [2.6, 3.9]]\n", 255 | "jaggedData:\n", 256 | "[[1, 2, 3], [1, 2], [1, 2], [1]]\n", 257 | "anotherJagger:\n", 258 | "[[3, 2, 1], [0, 0, 0, 0, 0], [4, 2]]\n" 259 | ] 260 | } 261 | ], 262 | "source": [ 263 | "double[][] matrixA = new double[][] {{ 1.2, 2.4, 3.6 }, { 1.3, 2.6, 3.9 }};\n", 264 | "System.out.println(\"matrixA:\");\n", 265 | "System.out.printf(\"%s%n\", Arrays.toString(matrixA));\n", 266 | "System.out.printf(\"%s%n\", Arrays.deepToString(matrixA));\n", 267 | "\n", 268 | "int[][] zerosTable = new int[3][5];\n", 269 | "System.out.println(\"zerosTable:\");\n", 270 | "System.out.printf(\"%s%n\", Arrays.deepToString(zerosTable));\n", 271 | "\n", 272 | "double[][] matrixB = {{ 1.2, 2.4 }, { 3.6, 1.3 }, { 2.6, 3.9 }};\n", 273 | "System.out.println(\"matrixB:\");\n", 274 | "System.out.printf(\"%s%n\", Arrays.deepToString(matrixB));\n", 275 | "\n", 276 | "int[][] jaggedData = {{ 1, 2, 3 }, { 1, 2 }, { 1, 2 }, { 1 }};\n", 277 | "System.out.println(\"jaggedData:\");\n", 278 | "System.out.printf(\"%s%n\", Arrays.deepToString(jaggedData));\n", 279 | "\n", 280 | "int[][] anotherJagger = new int[3][];\n", 281 | "anotherJagger[0] = new int[] { 3, 2, 1 };\n", 282 | "anotherJagger[1] = new int[5];\n", 283 | "anotherJagger[2] = new int[] { 4, 2 };\n", 284 | "System.out.println(\"anotherJagger:\");\n", 285 | "System.out.printf(\"%s%n\", Arrays.deepToString(anotherJagger));" 286 | ] 287 | }, 288 | { 289 | "cell_type": "markdown", 290 | "metadata": {}, 291 | "source": [ 292 | "`matrixA` ถูกประกาศเป็นอาร์เรย์ 2 มิติของ `double` โดยขนาดของแต่ละมิติถูกกำหนดโดยตัวข้อมูลที่ใช้เป็นค่าตั้งต้น ซึ่งในที่นี้จะได้เป็นเมทริกซ์ขนาด 2×3 ในขณะที่ `zerosTable` ถูกกำหนดขนาดด้วยการระบุเป็นมิติลงไปโดยตรง ค่าตั้งต้นของสมาชิกแต่ละตัวให้เป็นค่าโดยปริยายซึ่งในที่นี้คือ 0\n", 293 | "\n", 294 | "ในส่วนการแสดงค่าของอาร์เรย์ เราทดลองใช้ทั้ง `Arrays.toString` และ `Arrays.deepToString` ซึ่งจะเห็นได้ว่าในกรณีที่เป็นอาร์เรย์มากกว่ามิติเดียว เราจำเป็นต้องใช้ `Arrays.deepToString`\n", 295 | "\n", 296 | "`jaggedData` แสดงตัวอย่างให้เห็นอาร์เรย์ที่มีขนาดแต่ละแถวไม่เท่ากัน (jagged array) ในตัวอย่างนี้จะมีขนาดสมาชิกในแต่ละแถวเป็น 3, 2, 2 และ 1 สมาชิกตามลำดับ ซึ่งบ่งชี้ว่าอาร์เรย์ที่ซ้อนอยู่แต่ละแถวเป็นอิสระต่อกัน ไม่จำเป็นต้องมีขนาดเท่ากัน\n", 297 | "\n", 298 | "`anotherJagger` เป็นอาร์เรย์ที่มีขนาดแต่ละแถวไม่เท่ากันอีกตัวหนึ่ง แต่ในตัวอย่างนี้เราสร้างอาร์เรย์มิติแรกขึ้นมาก่อน แล้วค่อยทยอยสร้างมิติที่สองทีละแถวในภายหลัง\n", 299 | "\n", 300 | "เราใช้ `Arrays.deepToString` เพื่อช่วยในการแสดงผลอาร์เรย์หลายมิติ แต่คลาส `Arrays` ก็ไม่มีเมทอดอำนวยความสะดวกสำหรับอาร์เรย์หลายมิติมากนัก เช่น `Arrays.copyOf` ก็ไม่สามารถใช้กับอาร์เรย์ซ้อนได้\n", 301 | "\n", 302 | "## คลาส ArrayList\n", 303 | "\n", 304 | "อาร์เรย์เป็นคอลเลกชันพื้นฐานที่มีให้มาในตัวภาษา Java เองเลย แต่อาร์เรย์ก็มีข้อจำกัดในแง่ของความยืดหยุ่น เราไม่สามารถเพิ่มหรือลดสมาชิกหรือปรับขนาดของอาร์เรย์ตามความต้องการโดยไม่ต้องสร้างใหม่ได้ เราไม่สามารถแทรกหรือลบข้อมูลจากตรงกลางของอาร์เรย์ได้ ซึ่งทำให้การใช้งานอาร์เรย์ในการแก้ปัญหาบางอย่างไม่สะดวกมากนัก\n", 305 | "\n", 306 | "ใน JDK มีคลาสคอลเลกชันที่มีลักษณะการใช้งานคล้ายอาร์เรย์ แต่มีคุณสมบัติในการเพิ่มหรือลดขนาดและแทรกหรือลบข้อมูลได้ตามต้องการ คลาสนั้นคือ `ArrayList`\n", 307 | "\n", 308 | "การประกาศตัวแปรที่มีชนิดเป็น `ArrayList` ทำได้ 2 รูปแบบ แบบแรกเป็นรูปแบบเจเนอริกซึ่งจะมีการระบุชนิดของข้อมูลที่ `ArrayList` จะจัดเก็บ แบบที่สองเป็นรูปแบบดั้งเดิมซึ่งจะไม่ระบุชนิดข้อมูล (raw type)" 309 | ] 310 | }, 311 | { 312 | "cell_type": "code", 313 | "execution_count": 63, 314 | "metadata": {}, 315 | "outputs": [ 316 | { 317 | "name": "stdout", 318 | "output_type": "stream", 319 | "text": [ 320 | "ranks: [1, 5, 3]\n", 321 | "messages: [Hello, I, am, a, robot]\n", 322 | "scores: [28.5, 25.0, 19.2, 22.7]\n" 323 | ] 324 | } 325 | ], 326 | "source": [ 327 | "// Generic form - full initialization\n", 328 | "ArrayList ranks = new ArrayList();\n", 329 | "ranks.add(1);\n", 330 | "ranks.add(5);\n", 331 | "ranks.add(3);\n", 332 | "System.out.printf(\"ranks: %s%n\", ranks);\n", 333 | "\n", 334 | "// Generic form - with diamond operator\n", 335 | "ArrayList messages = new ArrayList<>();\n", 336 | "Collections.addAll(messages, \"Hello\", \"I\", \"am\", \"a\", \"robot\");\n", 337 | "System.out.printf(\"messages: %s%n\", messages);\n", 338 | "\n", 339 | "// Raw form\n", 340 | "ArrayList scores = new ArrayList();\n", 341 | "scores.add(28.5);\n", 342 | "scores.add(19.2);\n", 343 | "scores.add(22.7);\n", 344 | "scores.add(1, 25.0);\n", 345 | "System.out.printf(\"scores: %s%n\", scores);" 346 | ] 347 | }, 348 | { 349 | "cell_type": "markdown", 350 | "metadata": {}, 351 | "source": [ 352 | "ในรูปแบบเจเนอริก เราจะต้องระบุชนิดของข้อมูลที่ `ArrayList` จะจัดเก็บในส่วนประกาศตัวแปร ซึ่งเห็นในตัวอย่างนี้ในรูปของ `ArrayList` โดยที่ `T` คือชนิดของข้อมูลที่ต้องการจัดเก็บ\n", 353 | "\n", 354 | "ในส่วนของการสร้าง `ArrayList` เพื่อกำหนดเป็นค่าตั้งต้นให้กับตัวแปร `ranks` และ `messages` นั้น เราสามารถเลือกได้ว่าจะระบุชนิดอีกครั้งเหมือนในตัวอย่างของ `ranks` ซึ่งใช้คำสั่ง `new ArrayList()` เพื่อสร้าง `ArrayList` หรือจะไม่ระบุชนิดก็ได้ แต่ต้องใส่เครื่องหมาย diamond operator (`<>`) เอาไว้เหมือนในตัวอย่างของ `messages` ซึ่งในรูปแบบนี้ คอมไพเลอร์จะกำหนดชนิดให้เองตามชนิดของตัวแปรที่ประกาศเอาไว้\n", 355 | "\n", 356 | "นอกจากการประกาศตัวแปรและการสร้าง `ArrayList` แล้ว เรายังแสดงตัวอย่างการใส่ข้อมูลเข้าไปใน `ArrayList` ในตัวอย่างนี้ด้วย แบบแรกคือใช้เมทอด `add` ซึ่งจะใส่ข้อมูลเข้าไปต่อท้ายข้อมูลเดิมที่มีอยู่ หรือเราอาจจะแทรกได้โดยการระบุตำแหน่งเป็นอาร์กิวเมนต์เพิ่มอีกตัวเหมือนในตัวอย่างของ `scores` และอีกแบบเป็นการใส่ข้อมูลทีละหลายตัวโดยการใช้เมทอด `Collections.addAll` จากคลาส `Collections`\n", 357 | "\n", 358 | "อีกรูปแบบของการประกาศและสร้าง `ArrayList` คือรูปแบบที่ไม่ระบุชนิดข้อมูล ซึ่งเราเรียกว่าการประกาศแบบ raw type การประกาศแบบนี้เป็นรูปแบบดั้งเดิมของ Java ตั้งแต่รุ่นแรก โดย `ArrayList` ที่ไม่ระบุชนิดข้อมูลนี้จะสามารถใช้กับอ็อบเจกต์ชนิดใดก็ได้\n", 359 | "\n", 360 | "เราลองมาดูการประกาศแบบ raw type กันอีกครั้ง" 361 | ] 362 | }, 363 | { 364 | "cell_type": "code", 365 | "execution_count": 73, 366 | "metadata": {}, 367 | "outputs": [ 368 | { 369 | "name": "stdout", 370 | "output_type": "stream", 371 | "text": [ 372 | "scores: [28.5, 15, John, false]\n" 373 | ] 374 | } 375 | ], 376 | "source": [ 377 | "ArrayList scores = new ArrayList();\n", 378 | "\n", 379 | "scores.add(28.5);\n", 380 | "scores.add(15);\n", 381 | "scores.add(\"John\");\n", 382 | "scores.add(false);\n", 383 | "System.out.printf(\"scores: %s%n\", scores);\n", 384 | "\n", 385 | "double first = (double)scores.get(0);\n", 386 | "int second = (int)scores.get(1);\n", 387 | "String third = (String)scores.get(2);\n", 388 | "boolean fourth = (boolean)scores.get(3);" 389 | ] 390 | }, 391 | { 392 | "cell_type": "markdown", 393 | "metadata": {}, 394 | "source": [ 395 | "จะเห็นได้ว่าเราสามารถใส่ข้อมูลชนิดใดก็ได้ถ้าเราประกาศเป็นแบบ raw type ซึ่งดูเหมือนจะยืดหยุ่นดี แต่ปัญหาที่ตามมาใหญ่ ๆ มี 2 ประการ\n", 396 | "\n", 397 | "1. คอมไพเลอร์ไม่สามารถตรวจสอบความถูกต้องในการใช้งานได้เนื่องจากไม่มีข้อกำหนดว่าจะใช้กับข้อมูลชนิดใด ซึ่งทำให้เราสามารถใส่ข้อมูลที่ไม่ถูกชนิดโดยไม่ได้ตั้งใจได้\n", 398 | "2. เวลาที่เราจะเอาข้อมูลออกมาใช้ เราต้องมีการแปลงชนิดข้อมูลก่อนด้วยการ cast เช่น `(double)scores.get(0)` เพื่อบอกว่าข้อมูลที่นำออกมามีชนิดเป็น `double` ไม่อย่างนั้นแล้วโค้ดนี้จะไม่สามารถคอมไพล์ได้ โดยมีความผิดพลาดในรูปแบบ `incompatible types` และถ้าเราระบุชนิดผิดพลาด ก็จะเกิดความผิดพลาดชนิด `ClassCastException` ขึ้นได้\n", 399 | "\n", 400 | "นอกจากนี้การไม่ระบุชนิดในโค้ดอาจทำให้ความชัดเจนของโค้ดลดลงด้วย เพราะฉะนั้นการใช้ `ArrayList` หรือคลาสคอลเลกชันแบบเจเนอริกตัวอื่น ๆ ในแบบ raw type จึงเป็นสิ่งที่ควรหลีกเลี่ยง\n", 401 | "\n", 402 | "### คลาสหีบห่อ\n", 403 | "\n", 404 | "ในทั้ง 2 ตัวอย่างที่ผ่านมาเราจะมีข้อสังเกตอย่างหนึ่ง คือ เราไม่สามารถใช้ข้อมูลชนิดพื้นฐาน (primitive type) กับ `ArrayList` ได้โดยตรง อย่างเช่นตอนที่เราประกาศชนิดของ `ranks` เราประกาศว่า `ArrayList ranks` เราระบุชนิดของข้อมูลที่จะจัดเก็บเป็น `Integer` ซึ่งเป็นคลาสไม่ใช่ `int` ซึ่งเป็นชนิดพื้นฐาน\n", 405 | "\n", 406 | "ประเด็นนี้เป็นข้อจำกัดของชนิดเจเนอริกของ Java ซึ่งอนุญาตให้ใช้เจเนอริกกับข้อมูลชนิดตัวอ้างอิงเท่านั้น ไม่อนุญาตให้ใช้ข้อมูลชนิดพื้นฐาน ดังนั้น ถ้าเราต้องการใช้ `ArrayList` ซึ่งเป็นคลาสเจเนอริกในการจัดเก็บข้อมูลชนิด `int` เราจำเป็นต้องทำให้ข้อมูลนั้นอยู่ในรูปของอ็อบเจกต์ก่อนจึงจะจัดเก็บได้\n", 407 | "\n", 408 | "และนี่เป็นที่มาของกลุ่มคลาสพิเศษที่เรียกว่าคลาสหีบห่อ (wrapper class) ได้แก่ `Boolean`, `Character`, `Byte`, `Short`, `Integer`, `Long`, `Float` และ `Double` ซึ่งเราจะเห็นได้ว่าทุกชนิดพื้นฐานจะมีคลาสหีบห่อที่คู่กัน\n", 409 | "\n", 410 | "หน้าที่ของคลาสหีบห่อคือใช้สร้างอ็อบเจกต์เพื่อเก็บข้อมูลชนิดพื้นฐานเหล่านั้น หนึ่งอ็อบเจกต์ต่อหนึ่งข้อมูล เพื่อให้สามารถนำไปใช้กับคลาสที่ต้องการอ็อบเจกต์ได้ เช่น ใช้กับ `ArrayList` นั่นเอง เมื่อเราต้องการเอาค่า `int` ไปเก็บใน `ArrayList` เราต้องสร้างอ็อบเจกต์ `Integer` ขึ้นแล้วใส่ค่า `int` นี้ไว้ในอ็อบเจกต์ก่อนแล้วจึงนำไปเก็บ `ArrayList` การสร้างอ็อบเจกต์นี้ขึ้นมาแล้วเอาค่าไปเก็บไว้ในอ็อบเจกต์เรียกว่าการห่อ (boxing) และเมื่อเราต้องการเอาค่าออกมาใช้ในรูปของ `int` อีกครั้ง ก็จะต้องมีการนำค่าออกมาจากอ็อบเจกต์นั้น เรานำค่าออกมาเราเรียกว่าการแกะ (unboxing)\n", 411 | "\n", 412 | "Java มีคุณสมบัติการห่อและการแกะโดยอัตโนมัติ (autoboxing/unboxing) ทำให้กระบวนการที่กล่าวมานั้นเป็นไปเองโดยที่เราไม่ต้องระบุการห่อและการแกะโดยตรง ดังตัวอย่างนี้" 413 | ] 414 | }, 415 | { 416 | "cell_type": "code", 417 | "execution_count": 77, 418 | "metadata": {}, 419 | "outputs": [], 420 | "source": [ 421 | "ArrayList ranks = new ArrayList();\n", 422 | "\n", 423 | "// Without autoboxing/unboxing\n", 424 | "ranks.add(new Integer(10));\n", 425 | "int rankOne = ranks.get(0).intValue();\n", 426 | "\n", 427 | "// With autoboxing/unboxing\n", 428 | "ranks.add(20);\n", 429 | "int rankTwo = ranks.get(1);" 430 | ] 431 | }, 432 | { 433 | "cell_type": "markdown", 434 | "metadata": {}, 435 | "source": [ 436 | "รูปแบบแรกเราต้องสร้างอ็อบเจกต์จากคลาสหีบห่อขึ้นมาเองด้วยคำสั่ง `new` และเวลาที่เอาค่าออกมาใช้ก็ต้องมีการเรียกเมทอด `intValue` เพื่อขอค่าจากตัวอ็อบเจกต์ แต่ตั้งแต่ Java รุ่น 1.5 เป็นต้นมาเราสามารถละกระบวนการเหล่านี้ได้โดยใช้คุณสมบัติ autoboxing/unboxing เช่นในรูปแบบที่สองในตัวอย่าง อย่างไรก็ดี ในรูปแบบนี้ถึงแม้เราจะละการเขียนโค้ดเพื่อห่อและแกะโดยตรงซึ่งทำให้เราใช้งานได้สะดวกมากขึ้น แต่ในการทำงานเบื้องหลังก็ยังเกิดขั้นตอนการห่อและการแกะเหล่านั้นอยู่\n", 437 | "\n", 438 | "ในบางกรณี เราอาจจะยังจำเป็นต้องเขียนโค้ดเพื่อแกะหรือห่อโดยตรงอยู่บ้าง เช่น ในกรณีที่เราใช้ `ArrayList` ในรูปแบบ raw type ซึ่งคอมไพเลอร์จะไม่สามารถคาดเดาชนิดของข้อมูลที่อยู่ใน `ArrayList` ได้ ในกรณีนี้คอมไพเลอร์อาจจะไม่ทำกระบวนการห่อและแกะโดยอัตโนมัติให้ได้\n", 439 | "\n", 440 | "> **หมายเหตุ** คลาสหีบห่อมีเมทอดที่น่าสนใจสำหรับการแปลงชนิดข้อมูลจากสตริงมาเป็นชนิดพื้นฐานแต่ละชนิด เมทอดแรกคือ `valueOf` เช่น `Integer.valueOf(\"123\")` จะได้อ็อบเจกต์ของคลาสหีบห่อ `Integer` ที่มีค่าเป็น 123 และอีกเมทอดคือเมทอดกลุ่ม `parse` ซึ่งจะมีชื่อขึ้นต้นด้วย `parse` ตามด้วยชื่อชนิดพื้นฐาน เมทอดกลุ่มนี้ใช้แปลงข้อมูลจากสตริงมาเป็นชนิดพื้นฐานโดยตรงโดยไม่ต้องแปลงเป็นคลาสหีบห่อก่อน เช่น `Integer.parseInt(\"123\")` จะได้ค่า 123 เป็นชนิด `int` เลยโดยตรง\n", 441 | "\n", 442 | "### เมทอดที่น่าสนใจของ ArrayList\n", 443 | "\n", 444 | "ในตัวอย่างที่ผ่านมาเราเห็นการใช้งานเมทอดของ `ArrayList` ซึ่งใช้ในการใส่ข้อมูล (`add`) และเรียกข้อมูลมาดู (`get`) แล้ว ยังมีเมทอดอื่น ๆ ที่น่าสนใจอีก เช่น\n", 445 | "\n", 446 | "- `int indexOf(Object o)` สำหรับหาตำแหน่งที่มีข้อมูลนี้อยู่เป็นตำแหน่งแรก ถ้าไม่พบข้อมูลก็จะคืนค่ากลับเป็น -1\n", 447 | "- `boolean isEmpty()` คืนค่าเป็น true ถ้า `ArrayList` นั้นไม่มีข้อมูลอยู่\n", 448 | "- `E remove(int index)` ลบข้อมูล ณ ตำแหน่งที่ระบุ และคืนค่าเป็นข้อมูลที่ถูกลบ\n", 449 | "- `boolean remove(Object o)` ลบข้อมูลตัวแรกที่ตรงกับข้อมูลที่ระบุถ้ามีข้อมูลนั้นอยู่ และคืนค่าเป็น true ถ้าพบข้อมูล\n", 450 | "- `E set(int index, E element)` แทนค่า ณ ตำแหน่ง `index` ด้วยค่าใหม่ `element` และคืนค่าเดิมกลับไป\n", 451 | "- `int size()` คืนค่าเป็นจำนวนข้อมูลที่มีอยู่\n", 452 | "- `List subList(int fromIndex, int toIndex)` คืนค่าเป็น `List` ย่อยของตัวเดิม โดยเริ่มตั้งแต่สมาชิกตัวที่ `fromIndex` ไปจนถึงสมาชิกตัวก่อน `toIndex` ซึ่ง `List` ย่อยที่คืนค่าไปจะใช้ข้อมูลร่วมกับ `ArrayList` ตัวเดิม ดังนั้นการเปลี่ยนแปลงใด ๆ กับตัวย่อยจาก `subList` จะส่งผลถึงตัวเดิมด้วย\n", 453 | "\n", 454 | "ตัวอย่างการใช้งานเมทอดต่าง ๆ" 455 | ] 456 | }, 457 | { 458 | "cell_type": "code", 459 | "execution_count": 93, 460 | "metadata": {}, 461 | "outputs": [ 462 | { 463 | "name": "stdout", 464 | "output_type": "stream", 465 | "text": [ 466 | "Original ranks: [5, 12, 1, 1, 13, 5, 11, 12, 1]\n", 467 | "Removing 5\n", 468 | "Removing 12\n", 469 | "Removing 1\n", 470 | "Removing 1\n", 471 | "After pruning: [5, 12, 1, 13, 11]\n" 472 | ] 473 | } 474 | ], 475 | "source": [ 476 | "ArrayList ranks = new ArrayList();\n", 477 | "Collections.addAll(ranks, 5, 12, 1, 1, 10, 5, 11, 12, 1);\n", 478 | "\n", 479 | "ranks.set(4, 13);\n", 480 | "System.out.printf(\"Original ranks: %s%n\", ranks);\n", 481 | "\n", 482 | "int index = 0;\n", 483 | "while (!ranks.isEmpty() && index < ranks.size()) {\n", 484 | " int key = ranks.get(index);\n", 485 | " do {\n", 486 | " // Get a sub-list starting right after the key object\n", 487 | " List subList = ranks.subList(index+1, ranks.size());\n", 488 | " \n", 489 | " // Find the first duplicate in the sub-list and remove it\n", 490 | " int duplicate = subList.indexOf(key);\n", 491 | " if (duplicate < 0)\n", 492 | " break;\n", 493 | " \n", 494 | " System.out.println(\"Removing \" + key);\n", 495 | " subList.remove(duplicate);\n", 496 | " } while (true);\n", 497 | " index++;\n", 498 | "}\n", 499 | "\n", 500 | "System.out.printf(\"After pruning: %s%n\", ranks);" 501 | ] 502 | }, 503 | { 504 | "cell_type": "markdown", 505 | "metadata": {}, 506 | "source": [ 507 | "ตัวอย่างนี้เป็นการหาค่าซ้ำแล้วลบออกให้เหลือเพียงค่าเดียว โดยจะหยิบค่าแรกมา แล้ววนหาตำแหน่งของค่าที่ซ้ำแล้วลบออกทั้งหมด แล้วจึงหยิบค่าถัดไป ทำไปเรื่อย ๆ จนจบข้อมูล\n", 508 | "\n", 509 | "`ArrayList` มีความยืดหยุ่นสูงกว่าอาร์เรย์และมีประสิทธิภาพในระดับใกล้เคียงกับอาร์เรย์ เพราะการทำงานภายในของ `ArrayList` ก็ใช้อาร์เรย์อยู่เบื้องหลัง แต่ถ้าเราต้องการใช้งานกับข้อมูลชนิดพื้นฐาน (primitive type) ซึ่ง `ArrayList` ไม่สามารถทำได้โดยตรง ต้องมีการสร้างอ็อบเจกต์จาก wrapper class ขึ้นมาเก็บข้อมูลแต่ละตัว และอาจจะมีกระบวนการ boxing/unboxing เกิดขึ้นระหว่างที่เราอ้างอิงข้อมูลด้วย ซึ่งส่งผลกระทบต่อประสิทธิภาพได้ ในกรณีนี้อาร์เรย์ก็อาจจะเป็นตัวเลือกที่ดีกว่า\n", 510 | "\n", 511 | "> **หมายเหตุ** `ArrayList` เป็นส่วนหนึ่งของ Java Collection Framework และมีรูปแบบเป็นคลาสเจเนอริก (generic class) ซึ่งสามารถกำหนดให้จัดเก็บข้อมูลเป็นชนิดคลาสใดก็ได้ เราจะพูดถึงชนิดเจเนอริกโดยละเอียดอีกครั้งในภายหลัง\n", 512 | "\n", 513 | "## การวนซ้ำบนคอลเลกชัน\n", 514 | "\n", 515 | "การวนซ้ำบนข้อมูลในคอลเลกชันเป็นสิ่งที่เกิดขึ้นบ่อย เราจึงมีโครงสร้างการวนซ้ำแบบที่ใช้กับคอลเลกชันหรืออาร์เรย์โดยเฉพาะ ซึ่งมีรูปแบบดังนี้\n", 516 | "\n", 517 | "```\n", 518 | "for (tx x : expression)\n", 519 | " body\n", 520 | "```\n", 521 | "\n", 522 | "ลูป `for` นี้จะวนซ้ำจำนวนรอบเท่ากับจำนวนสมาชิกในคอลเลกชันหรืออาร์เรย์ที่ระบุในส่วน _expression_ โดยตัวแปร `x` ชนิด _tx_ ซึ่งต้องสอดคล้องกับชนิดของข้อมูลในคอลเลกชัน จะเปลี่ยนค่าไปทุกรอบ แต่ละรอบจะมีค่าเท่ากับสมาชิกแต่ละตัวของคอลเลกชันตามลำดับ (ยกเว้นคอลเลกชันชนิดที่ไม่มีลำดับแน่นอน)\n", 523 | "\n", 524 | "ในการวนซ้ำแบบนี้ เราจะไม่มีตัวนับ จึงอาจไม่รู้ว่าการวนอยู่ในรอบที่เท่าใดหรือกำลังจัดการกับสมาชิกลำดับที่เท่าใด ยกเว้นจะกำหนดตัวแปรนับขึ้นมาต่างหาก แต่ในการใช้งานจริงบ่อยครั้งที่เราไม่จำเป็นต้องรู้ว่าเรากำลังดำเนินการกับสมาชิกตัวที่เท่าใด สำคัญเพียงว่าสมาชิกนั้นมีค่าเป็นอะไรเท่านั้น\n", 525 | "\n", 526 | "ตัวอย่างต่อไปนี้แสดงการวนซ้ำเพื่อหาค่าผลรวมของอาร์เรย์" 527 | ] 528 | }, 529 | { 530 | "cell_type": "code", 531 | "execution_count": 113, 532 | "metadata": {}, 533 | "outputs": [ 534 | { 535 | "name": "stdout", 536 | "output_type": "stream", 537 | "text": [ 538 | "Sum = 31.85, Average = 4.55\n" 539 | ] 540 | } 541 | ], 542 | "source": [ 543 | "double[] scores = { 9.1, 6.25, 2.5, 5.0, 1.25, 7.5, 0.25 };\n", 544 | "\n", 545 | "double sum = 0;\n", 546 | "for (double score : scores) {\n", 547 | " sum += score;\n", 548 | "}\n", 549 | "\n", 550 | "System.out.printf(\"Sum = %.2f, Average = %.2f%n\", sum, sum / scores.length);" 551 | ] 552 | }, 553 | { 554 | "cell_type": "markdown", 555 | "metadata": {}, 556 | "source": [ 557 | "## สตริง\n", 558 | "\n", 559 | "สตริงเป็นข้อมูลชนิดข้อความ ซึ่งเป็นการเรียงต่อกันของข้อมูลชนิดตัวตัวอักษร (`char`) เราอาจมองสตริงคล้ายกับอาร์เรย์ของตัวอักษร แต่สตริงไม่สามารถใช้งานอย่างอาร์เรย์โดยตรงได้ และไม่สามารถเปลี่ยนแปลงค่าได้ หากต้องการสตริงที่มีค่าบางตัวอักษรแตกต่างจากเดิมต้องสร้างสตริงใหม่ หรือใช้คลาส `StringBuilder` หรือ `StringBuffer` ในการจัดการ\n", 560 | "\n", 561 | "สตริงมีเมทอดที่สำคัญดังนี้\n", 562 | "\n", 563 | "- `equals` ใช้ทดสอบว่าสตริง 2 ตัวมีค่าเท่ากันหรือไม่\n", 564 | "- `compareTo` สำหรับเปรียบเทียบค่าของสตริง 2 ตัวว่าตัวใดมาก่อนหรือหลังตามลำดับตัวอักษร ถ้าต้องการเปรียบเทียบโดยไม่สนใจว่าเป็นตัวพิมพ์ใหญ่หรือตัวพิมพ์เล็กให้ใช้ `compareToIgnoreCase`\n", 565 | "- `length` คืนค่าเป็นความยาวของสตริง\n", 566 | "- `format` คืนค่าเป็นสตริงที่จัดรูปแบบ การใช้งานเหมือนกับเมทอด `printf`\n", 567 | "- `indexOf` ใช้ค้นหาสตริงย่อยภายในสตริง คืนค่าเป็นตำแหน่งที่พบ หรือเป็น -1 หากไม่พบ\n", 568 | "- `split` ใช้แยกสตริงออกเป็นอาร์เรย์ของสตริง โดยระบุตัวสัญลักษณ์ที่ใช้แบ่งได้\n", 569 | "- `substring` คืนค่าเป็นสตริงย่อยของสตริงเดิม โดยระบุตำแหน่งเริ่มต้นและตำแหน่งสิ้นสุด\n", 570 | "- `toCharArray` คืนค่าเป็นอาร์เรย์ของ `char` เพื่อนำไปใช้งานต่อในรูปแบบอาร์เรย์ได้\n", 571 | "- `toLowerCase` คืนค่าเป็นสตริงใหม่ที่เปลี่ยนตัวอักษรเป็นตัวพิมพ์เล็กทั้งหมด\n", 572 | "- `toUpperCase` คืนค่าเป็นสตริงใหม่ที่เปลี่ยนตัวอักษรเป็นตัวพิมพ์ใหญ่ทั้งหมด\n", 573 | "- `trim` คืนค่าเป็นสตริงใหม่ที่ตัดช่องว่างด้านหน้าและด้านหลังออกแล้ว\n", 574 | "\n", 575 | "ตัวอย่างต่อไปนี้แสดงการใช้งานเมทอดของสตริงในหลาย ๆ รูปแบบ" 576 | ] 577 | }, 578 | { 579 | "cell_type": "code", 580 | "execution_count": 111, 581 | "metadata": {}, 582 | "outputs": [ 583 | { 584 | "name": "stdout", 585 | "output_type": "stream", 586 | "text": [ 587 | "Split: [This, is, the, real, world.]\n", 588 | "Upper: THISISTHEREALWORLD.\n", 589 | "Substitute: This is the REAL world.\n", 590 | "Char Array: ~W~e~l~c~o~m~e~\n" 591 | ] 592 | } 593 | ], 594 | "source": [ 595 | "String sentence = \"This is the real world.\";\n", 596 | "\n", 597 | "// Split to array\n", 598 | "String[] words = s1.split(\" \");\n", 599 | "System.out.printf(\"Split: %s%n\", Arrays.toString(words));\n", 600 | "\n", 601 | "// Make upper-case and join all words\n", 602 | "String upperCase = \"\";\n", 603 | "for (String word : words) {\n", 604 | " upperCase += word.toUpperCase();\n", 605 | "}\n", 606 | "System.out.println(\"Upper: \" + upperCase);\n", 607 | "\n", 608 | "// Substituting a word by rebuilding a new string\n", 609 | "int realPosition = sentence.indexOf(\"real\");\n", 610 | "String realSentence = sentence.substring(0, realPosition)\n", 611 | " + \"REAL\" + sentence.substring(realPosition + 4);\n", 612 | "System.out.println(\"Substitute: \" + realSentence);\n", 613 | "\n", 614 | "// Convert to char array and process each character individually\n", 615 | "System.out.print(\"Char Array: \");\n", 616 | "for (char c : \"Welcome\".toCharArray()) {\n", 617 | " System.out.print(\"~\" + c);\n", 618 | "}\n", 619 | "System.out.println(\"~\");" 620 | ] 621 | }, 622 | { 623 | "cell_type": "markdown", 624 | "metadata": {}, 625 | "source": [ 626 | "## การแจงนับ\n", 627 | "\n", 628 | "เราสามารถกำหนดชนิดข้อมูลใหม่ที่มีค่าเป็นไปตามที่เราระบุได้ในรูปของข้อมูลชนิด `enum` หรือข้อมูลชนิดแจงนับ\n", 629 | "\n", 630 | "ข้อมูลชนิด `enum` ทำให้เราสามารถกำหนดค่าที่เป็นไปได้ทั้งหมดของข้อมูลชนิดนั้นได้ เช่น กำหนดข้อมูลชนิดวันในสัปดาห์ โดยมีค่าที่เป็นไปได้คือชื่อวันทั้ง 7 วัน การกำหนดแบบนี้ทำให้เราสามารถอ้างอิงข้อมูลในรูปแบบที่เข้าใจง่ายขึ้นได้ และยังเป็นการกำหนดด้วยว่าค่าใดที่ใช้ได้บ้าง\n", 631 | "\n", 632 | "การตั้งชื่อของค่า `enum` เป็นไปตามกฎการตั้งชื่อของ Java แต่โดยธรรมเนียมแล้วเราจะตั้งชื่อแบบเดียวกับค่าคงที่ คือใช้ตัวพิมพ์ใหญ่ทั้งหมดและแบ่งคำด้วย `_` (underscore)\n", 633 | "\n", 634 | "โค้ดต่อไปนี้แสดงตัวอย่างของการใช้ `enum`" 635 | ] 636 | }, 637 | { 638 | "cell_type": "code", 639 | "execution_count": 118, 640 | "metadata": {}, 641 | "outputs": [ 642 | { 643 | "name": "stdout", 644 | "output_type": "stream", 645 | "text": [ 646 | "It's just another work day.\n" 647 | ] 648 | } 649 | ], 650 | "source": [ 651 | "enum DayOfWeek {\n", 652 | " SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY\n", 653 | "}\n", 654 | "\n", 655 | "DayOfWeek day = DayOfWeek.TUESDAY;\n", 656 | "\n", 657 | "switch (day) {\n", 658 | " case SUNDAY:\n", 659 | " System.out.println(\"I'll just sleep.\");\n", 660 | " break;\n", 661 | " case MONDAY:\n", 662 | " System.out.println(\"I hate Monday.\");\n", 663 | " break;\n", 664 | " case FRIDAY:\n", 665 | " System.out.println(\"Hey, it's Friday.\");\n", 666 | " break;\n", 667 | " case SATURDAY:\n", 668 | " System.out.println(\"Yay! I'm free.\");\n", 669 | " break;\n", 670 | " default:\n", 671 | " System.out.println(\"It's just another work day.\");\n", 672 | "}" 673 | ] 674 | }, 675 | { 676 | "cell_type": "markdown", 677 | "metadata": {}, 678 | "source": [ 679 | "ตัวอย่างนี้มีจุดที่น่าสนใจคือ ในการใช้งาน `enum` โดยทั่วไป เมื่อเราต้องการอ้างอิงถึงค่าของ `enum` เรามักจะต้องระบุชื่อของ `enum` ด้วย เช่น `DayOfWeek.TUESDAY` แต่ในคำสั่ง `switch` เราจะไม่ระบุชื่อของ `enum` แต่จะระบุเพียงค่าของมันเท่านั้น เพราะ `switch` รู้ชนิดของ `enum` จากค่าที่นำมาทดสอบอยู่แล้ว\n", 680 | "\n", 681 | "> **หมายเหตุ** `enum` เป็นข้อมูลชนิดตัวอ้างอิงแบบหนึ่งซึ่งมีลักษณะคล้ายกับคลาส (ในทางเทคนิคแล้ว `enum` เป็นคลาสแบบหนึ่ง) การใช้งาน `enum` มีรายละเอียดที่น่าสนใจอื่น ๆ อีกมาก\n", 682 | "\n", 683 | "## คลาส HashMap\n", 684 | "\n", 685 | "ข้อมูลชนิดอาร์เรย์หรือ `ArrayList` ใช้ตัวเลขตำแหน่งในการอ้างอิงข้อมูล แต่ในงานบางอย่าง เราอาจต้องการใช้ค่าชนิดอื่นในการอ้างอิงข้อมูล เช่น ข้อความ เป็นต้น คอลเลกชันชนิด `HashMap` ทำให้เราสามารถกำหนดชนิดของดัชนีหรือตัวอ้างอิงตำแหน่งข้อมูลได้\n", 686 | "\n", 687 | "ตัวอย่างการใช้งาน `HashMap`" 688 | ] 689 | }, 690 | { 691 | "cell_type": "code", 692 | "execution_count": 122, 693 | "metadata": {}, 694 | "outputs": [ 695 | { 696 | "name": "stdout", 697 | "output_type": "stream", 698 | "text": [ 699 | "March has 31 days.\n" 700 | ] 701 | } 702 | ], 703 | "source": [ 704 | "HashMap daysInMonth = new HashMap<>();\n", 705 | "\n", 706 | "daysInMonth.put(\"Jan\", 31);\n", 707 | "daysInMonth.put(\"Feb\", 28);\n", 708 | "daysInMonth.put(\"Mar\", 31);\n", 709 | "\n", 710 | "// ... code omitted\n", 711 | "\n", 712 | "daysInMonth.put(\"Dec\", 31);\n", 713 | "\n", 714 | "System.out.printf(\"%s has %d days.%n\", \"March\", daysInMonth.get(\"Mar\"));" 715 | ] 716 | }, 717 | { 718 | "cell_type": "markdown", 719 | "metadata": {}, 720 | "source": [ 721 | "> **หมายเหตุ** เรายังไม่เน้นการใช้งานคลาส `HashMap` ในที่นี้ เป็นเพียงการแนะนำเบื้องต้นเพื่อเปรียบเทียบกับข้อมูลชนิด dictionary ของ Python เท่านั้น" 722 | ] 723 | } 724 | ], 725 | "metadata": { 726 | "kernelspec": { 727 | "display_name": "Java", 728 | "language": "java", 729 | "name": "java" 730 | }, 731 | "language_info": { 732 | "codemirror_mode": "java", 733 | "file_extension": ".java", 734 | "mimetype": "text/x-java-source", 735 | "name": "Java", 736 | "pygments_lexer": "java", 737 | "version": "9.0.4+11" 738 | } 739 | }, 740 | "nbformat": 4, 741 | "nbformat_minor": 2 742 | } 743 | -------------------------------------------------------------------------------- /04 - Classes and Objects/Employee.java: -------------------------------------------------------------------------------- 1 | public class Employee { 2 | private String name; 3 | private double salary; 4 | private final int id; 5 | private static int lastId = 1000; 6 | 7 | public static final double SALARY_STEP_SIZE = 10.0; 8 | 9 | public Employee(String name, double salary) { 10 | this.name = name; 11 | this.salary = computeNextSalaryStep(salary); 12 | id = ++lastId; 13 | } 14 | 15 | public static double computeNextSalaryStep(double salary) { 16 | // Round to the next salary step 17 | double steps = Math.ceil(salary / SALARY_STEP_SIZE); 18 | return steps * SALARY_STEP_SIZE; 19 | } 20 | 21 | public void raiseSalary(double percent) { 22 | double raise = salary * percent / 100.0; 23 | salary = computeNextSalaryStep(salary + raise); 24 | } 25 | 26 | public void showProfile() { 27 | System.out.printf("Name: %s\n", name); 28 | System.out.printf("ID: %d\n", id); 29 | System.out.printf("Salary: %.2f\n", salary); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /04 - Classes and Objects/Expense.java: -------------------------------------------------------------------------------- 1 | import java.time.LocalDateTime; 2 | 3 | public class Expense { 4 | private String item; 5 | private double amount; 6 | private LocalDateTime time; 7 | 8 | public Expense(String item, double amount) { 9 | this.item = item; 10 | this.amount = amount; 11 | time = LocalDateTime.now(); 12 | } 13 | 14 | public String getItem() { 15 | return item; 16 | } 17 | 18 | public double getAmount() { 19 | return amount; 20 | } 21 | 22 | public void print() { 23 | System.out.printf("%s: %.2f", item, amount); 24 | } 25 | 26 | public static void main(String[] args) { 27 | Expense item1 = new Expense("Phone bill", 799.00); 28 | item1.print(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /04 - Classes and Objects/OuterClass.java: -------------------------------------------------------------------------------- 1 | public class OuterClass { 2 | public int publicVar = 0; 3 | private int privateVar = 1; 4 | 5 | private class InnerClass { 6 | public int publicVar = 3; 7 | private int privateVar = 4; 8 | 9 | public void innerClassTest() { 10 | System.out.println(publicVar); 11 | System.out.println(privateVar); 12 | System.out.println(OuterClass.this.privateVar); 13 | System.out.println(StaticNestedClass.staticClassVar); 14 | } 15 | } 16 | 17 | public static class StaticNestedClass { 18 | public static int staticClassVar = 6; 19 | } 20 | 21 | public void outerClassTest() { 22 | System.out.println(publicVar); 23 | System.out.println(privateVar); 24 | 25 | InnerClass inner = new InnerClass(); 26 | inner.innerClassTest(); 27 | } 28 | 29 | public static void main(String[] args) { 30 | OuterClass top = new OuterClass(); 31 | 32 | System.out.println(top.publicVar); 33 | System.out.println(top.privateVar); 34 | 35 | top.outerClassTest(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /04 - Classes and Objects/UMLClass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/04 - Classes and Objects/UMLClass.png -------------------------------------------------------------------------------- /05 - Inheritance/UMLInheritance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/05 - Inheritance/UMLInheritance.png -------------------------------------------------------------------------------- /05 - Inheritance/my/extended/ExchangeablePair.java: -------------------------------------------------------------------------------- 1 | package my.extended; 2 | 3 | import my.pair.Pair; 4 | 5 | public class ExchangeablePair extends Pair { 6 | public ExchangeablePair(int first, int second) { 7 | super(first, second); 8 | } 9 | 10 | public void exchangeWith(ExchangeablePair another) { 11 | int temp; 12 | 13 | // Swap values of first 14 | temp = another.first; 15 | another.first = first; 16 | first = temp; 17 | 18 | // Swap values of second 19 | temp = another.second; 20 | another.second = second; 21 | second = temp; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /05 - Inheritance/my/extended/PairTest.java: -------------------------------------------------------------------------------- 1 | package my.extended; 2 | 3 | class PairTest { 4 | public static void main(String[] args) { 5 | ExchangeablePair a = new ExchangeablePair(1, 2); 6 | ExchangeablePair b = new ExchangeablePair(3, 4); 7 | 8 | a.exchangeWith(b); 9 | a.print(); 10 | b.print(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /05 - Inheritance/my/extended/PairUser.java: -------------------------------------------------------------------------------- 1 | package extended; 2 | 3 | import pair.Pair; 4 | 5 | class PairUser { 6 | public static void main(String[] args) { 7 | Pair a = new Pair(1, 2); 8 | SwappablePair b = new SwappablePair(3, 4); 9 | 10 | System.out.println(a.first + a.second); // OK if in the same package, FAILED otherwise 11 | System.out.println(b.first + b.second); // OK if in the same package, FAILED otherwise 12 | 13 | b.swap(); // OK 14 | b.print(); // OK 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /05 - Inheritance/my/extended/SwappablePair.java: -------------------------------------------------------------------------------- 1 | package extended; 2 | 3 | import pair.Pair; 4 | 5 | public class SwappablePair extends Pair { 6 | public SwappablePair(int first, int second) { 7 | super(first, second); 8 | } 9 | 10 | public void swap() { 11 | int temp = first; 12 | first = second; 13 | second = temp; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /05 - Inheritance/my/pair/Pair.java: -------------------------------------------------------------------------------- 1 | package my.pair; 2 | 3 | public class Pair { 4 | protected int first; 5 | protected int second; 6 | 7 | public Pair(int first, int second) { 8 | this.first = first; 9 | this.second = second; 10 | } 11 | 12 | public int getFirst() { 13 | return first; 14 | } 15 | 16 | public int getSecond() { 17 | return second; 18 | } 19 | 20 | public void print() { 21 | System.out.println("(" + first + ", " + second + ")"); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /05 - Inheritance/my/pair/ResettablePair.java: -------------------------------------------------------------------------------- 1 | package my.pair; 2 | 3 | public class ResettablePair extends Pair { 4 | public void reset() { 5 | first = 0; 6 | second = 0; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /06 - Polymorphism/.ipynb_checkpoints/UMLPolymorphism-checkpoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/06 - Polymorphism/.ipynb_checkpoints/UMLPolymorphism-checkpoint.png -------------------------------------------------------------------------------- /06 - Polymorphism/UMLComposition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/06 - Polymorphism/UMLComposition.png -------------------------------------------------------------------------------- /06 - Polymorphism/UMLInterface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/06 - Polymorphism/UMLInterface.png -------------------------------------------------------------------------------- /06 - Polymorphism/UMLPolymorphism.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/06 - Polymorphism/UMLPolymorphism.png -------------------------------------------------------------------------------- /07 - Events and GUI Programming/BlankFrame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/07 - Events and GUI Programming/BlankFrame.png -------------------------------------------------------------------------------- /07 - Events and GUI Programming/BorderLayoutExample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/07 - Events and GUI Programming/BorderLayoutExample.png -------------------------------------------------------------------------------- /07 - Events and GUI Programming/ButtonFrame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/07 - Events and GUI Programming/ButtonFrame.png -------------------------------------------------------------------------------- /07 - Events and GUI Programming/ButtonGrid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/07 - Events and GUI Programming/ButtonGrid.png -------------------------------------------------------------------------------- /07 - Events and GUI Programming/CardLayout01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/07 - Events and GUI Programming/CardLayout01.png -------------------------------------------------------------------------------- /07 - Events and GUI Programming/CardLayout02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/07 - Events and GUI Programming/CardLayout02.png -------------------------------------------------------------------------------- /07 - Events and GUI Programming/ComplexLayout.java: -------------------------------------------------------------------------------- 1 | import javax.swing.JFrame; 2 | import javax.swing.JPanel; 3 | import javax.swing.JTextField; 4 | import javax.swing.JButton; 5 | import javax.swing.JLabel; 6 | import javax.swing.ImageIcon; 7 | import java.awt.GridLayout; 8 | import java.awt.BorderLayout; 9 | 10 | public class ComplexLayout extends JFrame { 11 | public ComplexLayout() { 12 | super("Complex Layout"); 13 | 14 | JPanel mainPanel = new JPanel(); 15 | mainPanel.setLayout(new BorderLayout()); 16 | 17 | JPanel keypad = new JPanel(); 18 | keypad.setLayout(new GridLayout(3, 3)); 19 | keypad.add(new JButton("7")); 20 | keypad.add(new JButton("8")); 21 | keypad.add(new JButton("9")); 22 | keypad.add(new JButton("4")); 23 | keypad.add(new JButton("5")); 24 | keypad.add(new JButton("6")); 25 | keypad.add(new JButton("1")); 26 | keypad.add(new JButton("2")); 27 | keypad.add(new JButton("3")); 28 | 29 | JPanel allKeys = new JPanel(); 30 | allKeys.add(keypad); 31 | 32 | ImageIcon ideaIcon = new ImageIcon("idea-1.png"); 33 | allKeys.add(new JButton("OK", ideaIcon)); 34 | 35 | mainPanel.add(new JTextField(), BorderLayout.NORTH); 36 | mainPanel.add(allKeys, BorderLayout.CENTER); 37 | mainPanel.add(new JLabel("Version 1.0.0"), BorderLayout.SOUTH); 38 | 39 | add(mainPanel); 40 | setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 41 | pack(); 42 | } 43 | 44 | public static void main(String[] args) { 45 | ComplexLayout app = new ComplexLayout(); 46 | app.setVisible(true); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /07 - Events and GUI Programming/ComplexLayout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/07 - Events and GUI Programming/ComplexLayout.png -------------------------------------------------------------------------------- /07 - Events and GUI Programming/ComponentsEvents.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/07 - Events and GUI Programming/ComponentsEvents.png -------------------------------------------------------------------------------- /07 - Events and GUI Programming/Container.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/07 - Events and GUI Programming/Container.png -------------------------------------------------------------------------------- /07 - Events and GUI Programming/EventHierarchy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/07 - Events and GUI Programming/EventHierarchy.png -------------------------------------------------------------------------------- /07 - Events and GUI Programming/FinalFrame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/07 - Events and GUI Programming/FinalFrame.png -------------------------------------------------------------------------------- /07 - Events and GUI Programming/GUI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/07 - Events and GUI Programming/GUI.png -------------------------------------------------------------------------------- /07 - Events and GUI Programming/GUIClassHierarchy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/07 - Events and GUI Programming/GUIClassHierarchy.png -------------------------------------------------------------------------------- /07 - Events and GUI Programming/MainApp01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/07 - Events and GUI Programming/MainApp01.png -------------------------------------------------------------------------------- /07 - Events and GUI Programming/MainApp02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/07 - Events and GUI Programming/MainApp02.png -------------------------------------------------------------------------------- /07 - Events and GUI Programming/MainApp03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/07 - Events and GUI Programming/MainApp03.png -------------------------------------------------------------------------------- /07 - Events and GUI Programming/MainApp04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/07 - Events and GUI Programming/MainApp04.png -------------------------------------------------------------------------------- /07 - Events and GUI Programming/PackedButtonFrame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/07 - Events and GUI Programming/PackedButtonFrame.png -------------------------------------------------------------------------------- /07 - Events and GUI Programming/Panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/07 - Events and GUI Programming/Panel.png -------------------------------------------------------------------------------- /07 - Events and GUI Programming/ResizedPanel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/07 - Events and GUI Programming/ResizedPanel.png -------------------------------------------------------------------------------- /07 - Events and GUI Programming/idea-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/07 - Events and GUI Programming/idea-1.png -------------------------------------------------------------------------------- /07 - Events and GUI Programming/idea-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/07 - Events and GUI Programming/idea-2.png -------------------------------------------------------------------------------- /08 - Graphics/DrawPanel01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/DrawPanel01.png -------------------------------------------------------------------------------- /08 - Graphics/DrawPanel02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/DrawPanel02.png -------------------------------------------------------------------------------- /08 - Graphics/DrawingBoard.java: -------------------------------------------------------------------------------- 1 | import java.awt.Graphics; 2 | import java.awt.Color; 3 | import java.awt.event.MouseEvent; 4 | import java.awt.event.MouseListener; 5 | import java.awt.event.MouseMotionListener; 6 | import javax.swing.JPanel; 7 | import java.util.ArrayList; 8 | 9 | public class DrawingBoard extends JPanel { 10 | private ArrayList xPoints = new ArrayList<>(); 11 | private ArrayList yPoints = new ArrayList<>(); 12 | private ArrayList inkLevels = new ArrayList<>(); 13 | 14 | private float inkLevel; 15 | 16 | private static final float INK_DEPLETION_RATE = 0.005f; 17 | private static final float INK_STOP_CONCENTRATION = 1.5f; 18 | private static final int INK_WIDTH = 12; 19 | 20 | public DrawingBoard() { 21 | super(); 22 | 23 | setBackground(Color.WHITE); 24 | inkLevel = 0.0f; 25 | 26 | addMouseListener(new MouseListener() { 27 | @Override 28 | public void mouseClicked(MouseEvent e) { 29 | // Do nothing 30 | } 31 | 32 | @Override 33 | public void mousePressed(MouseEvent e) { 34 | xPoints.add(e.getX()); 35 | yPoints.add(e.getY()); 36 | inkLevel = 0.0f; 37 | inkLevels.add(inkLevel); 38 | repaint(); 39 | } 40 | 41 | @Override 42 | public void mouseReleased(MouseEvent e) { 43 | xPoints.add(e.getX()); 44 | yPoints.add(e.getY()); 45 | inkLevel /= INK_STOP_CONCENTRATION; 46 | inkLevels.add(inkLevel); 47 | repaint(); 48 | } 49 | 50 | @Override 51 | public void mouseEntered(MouseEvent e) { 52 | // Do nothing 53 | } 54 | 55 | @Override 56 | public void mouseExited(MouseEvent e) { 57 | // Do nothing 58 | } 59 | }); 60 | 61 | addMouseMotionListener(new MouseMotionListener() { 62 | @Override 63 | public void mouseMoved(MouseEvent e) { 64 | // Do nothing 65 | } 66 | 67 | @Override 68 | public void mouseDragged(MouseEvent e) { 69 | xPoints.add(e.getX()); 70 | yPoints.add(e.getY()); 71 | inkLevel += INK_DEPLETION_RATE; 72 | if (inkLevel > 1.0f) 73 | inkLevel = 1.0f; 74 | inkLevels.add(inkLevel); 75 | repaint(); 76 | } 77 | }); 78 | } 79 | 80 | @Override 81 | public void paintComponent(Graphics g) { 82 | super.paintComponent(g); 83 | 84 | for (int i = 0; i < xPoints.size(); i++) { 85 | float inkLevel = inkLevels.get(i); 86 | int width = (int)(INK_WIDTH * (0.2f + (1.0f - inkLevel) * 0.8f)); 87 | g.setColor(new Color(inkLevel, inkLevel, inkLevel)); 88 | g.fillOval(xPoints.get(i), yPoints.get(i), width, width); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /08 - Graphics/DrawingBoard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/DrawingBoard.png -------------------------------------------------------------------------------- /08 - Graphics/DrawingBoardTest.java: -------------------------------------------------------------------------------- 1 | import javax.swing.JFrame; 2 | import javax.swing.SwingUtilities; 3 | 4 | public class DrawingBoardTest { 5 | public DrawingBoardTest() { 6 | DrawingBoard panel = new DrawingBoard(); 7 | 8 | JFrame application = new JFrame(); 9 | 10 | application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 11 | 12 | application.add(panel); 13 | application.setSize(500, 500); 14 | application.setVisible(true); 15 | } 16 | 17 | public static void main(String[] args) { 18 | SwingUtilities.invokeLater(new Runnable() { 19 | @Override 20 | public void run() { 21 | new DrawingBoardTest(); 22 | } 23 | }); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /08 - Graphics/SpaceGame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/SpaceGame.png -------------------------------------------------------------------------------- /08 - Graphics/SpaceGameUML.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/SpaceGameUML.png -------------------------------------------------------------------------------- /08 - Graphics/game/BasicGame.java: -------------------------------------------------------------------------------- 1 | import javax.swing.SwingUtilities; 2 | import javax.swing.JFrame; 3 | 4 | public class BasicGame { 5 | public static void main(String[] args) { 6 | SwingUtilities.invokeLater(new Runnable() { 7 | @Override 8 | public void run() { 9 | JFrame game = new JFrame(); 10 | game.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 11 | game.setSize(400, 300); 12 | game.add(new GameScene()); 13 | game.setVisible(true); 14 | } 15 | }); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /08 - Graphics/game/GameScene.java: -------------------------------------------------------------------------------- 1 | import java.awt.Graphics; 2 | import java.awt.Color; 3 | import java.awt.event.KeyEvent; 4 | import java.awt.event.KeyListener; 5 | import java.awt.image.BufferedImage; 6 | import java.io.File; 7 | import java.io.IOException; 8 | import javax.imageio.ImageIO; 9 | import javax.swing.JPanel; 10 | 11 | public class GameScene extends JPanel { 12 | private BufferedImage star; 13 | private int positionX = 180; 14 | private int positionY = 120; 15 | 16 | public GameScene() { 17 | setBackground(Color.BLACK); 18 | setFocusable(true); 19 | 20 | // Loading star image 21 | try { 22 | star = ImageIO.read(new File("star.png")); 23 | if (star == null) { 24 | System.err.println("Unrecognized image type."); 25 | System.exit(1); 26 | } 27 | } catch (IOException e) { 28 | System.err.println("Error while loading image."); 29 | System.exit(1); 30 | } 31 | 32 | addKeyListener(new KeyListener() { 33 | @Override 34 | public void keyTyped(KeyEvent e) { 35 | // Do nothing 36 | } 37 | 38 | @Override 39 | public void keyPressed(KeyEvent e) { 40 | switch (e.getKeyCode()) { 41 | case KeyEvent.VK_LEFT: 42 | positionX -= 10; 43 | repaint(); 44 | break; 45 | case KeyEvent.VK_RIGHT: 46 | positionX += 10; 47 | repaint(); 48 | break; 49 | } 50 | } 51 | 52 | @Override 53 | public void keyReleased(KeyEvent e) { 54 | // Do nothing 55 | } 56 | }); 57 | } 58 | 59 | @Override 60 | public void paintComponent(Graphics g) { 61 | super.paintComponent(g); 62 | 63 | g.drawImage(star, positionX, positionY, null); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /08 - Graphics/game/star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/game/star.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/Alien.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by poonna on 2/27/18 AD. 3 | */ 4 | 5 | import java.awt.*; 6 | 7 | public class Alien extends GameObject { 8 | private GameScene scene; 9 | private int x, y; 10 | private int speedX = 10; 11 | private int speedY = 10; 12 | 13 | public Alien(GameScene scene, int x, int y) { 14 | this.scene = scene; 15 | this.x = x; 16 | this.y = y; 17 | } 18 | 19 | @Override 20 | public void update(double deltaTime) { 21 | if (x < 100) { 22 | speedX = 10; 23 | } else if (x > 700) { 24 | speedX = -10; 25 | } 26 | if (y < 100) { 27 | speedY = 10; 28 | } else if (y > 300) { 29 | speedY = -10; 30 | } 31 | x += speedX; 32 | y += speedY; 33 | } 34 | 35 | @Override 36 | public void draw(Graphics g) { 37 | g.setColor(Color.BLUE); 38 | g.fillRect(x, y, 50, 50); 39 | } 40 | 41 | @Override 42 | public Rectangle getBounds() { 43 | return new Rectangle(x, y, 50, 50); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /08 - Graphics/spacegame/Bullet.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by poonna on 2/27/18 AD. 3 | */ 4 | 5 | import java.awt.*; 6 | 7 | public class Bullet extends GameObject { 8 | private GameScene scene; 9 | private int x, y; 10 | private int speed = 20; 11 | 12 | public Bullet(GameScene scene, int x, int y) { 13 | this.scene = scene; 14 | this.x = x; 15 | this.y = y; 16 | } 17 | 18 | @Override 19 | public void update(double deltaTime) { 20 | y -= speed; 21 | if (y < 0) { 22 | destroy(); 23 | } 24 | } 25 | 26 | @Override 27 | public void draw(Graphics g) { 28 | g.setColor(Color.WHITE); 29 | g.fillRect(x, y, 5, 10); 30 | } 31 | 32 | @Override 33 | public Rectangle getBounds() { 34 | return new Rectangle(x, y, 5, 10); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /08 - Graphics/spacegame/GameObject.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by poonna on 2/27/18 AD. 3 | */ 4 | 5 | import java.awt.Graphics; 6 | import java.awt.Rectangle; 7 | 8 | public abstract class GameObject { 9 | private boolean destroyed = false; 10 | 11 | public GameObject() { 12 | destroyed = false; 13 | } 14 | 15 | public abstract void update(double deltaTime); 16 | 17 | public abstract void draw(Graphics g); 18 | 19 | public abstract Rectangle getBounds(); 20 | 21 | public boolean collides(GameObject other) { 22 | if (isDestroyed() || other.isDestroyed()) { 23 | return false; 24 | } else { 25 | return getBounds().intersects(other.getBounds()); 26 | } 27 | } 28 | 29 | public void destroy() { 30 | destroyed = true; 31 | } 32 | 33 | public boolean isDestroyed() { 34 | return destroyed; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /08 - Graphics/spacegame/GameScene.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by poonna on 2/27/18 AD. 3 | */ 4 | 5 | import java.awt.Graphics; 6 | import java.awt.Color; 7 | import java.awt.event.ActionEvent; 8 | import java.awt.event.ActionListener; 9 | import java.awt.event.KeyEvent; 10 | import java.awt.event.KeyAdapter; 11 | import javax.swing.JPanel; 12 | import javax.swing.Timer; 13 | import java.util.ArrayList; 14 | import java.util.Iterator; 15 | 16 | public abstract class GameScene extends JPanel { 17 | public static final int KEY_UP = 0; 18 | public static final int KEY_DOWN = 1; 19 | public static final int KEY_LEFT = 2; 20 | public static final int KEY_RIGHT = 3; 21 | public static final int KEY_FIRE = 4; 22 | 23 | public enum KeyState { UP, DOWN } 24 | 25 | private KeyState[] keyStates = new KeyState[5]; 26 | 27 | private static final int DEFAULT_FRAME_RATE = 50; 28 | private long previousTime; 29 | 30 | private ArrayList gameObjects = new ArrayList<>(); 31 | private ArrayList waitList = new ArrayList<>(); 32 | 33 | public GameScene() { 34 | this(DEFAULT_FRAME_RATE); 35 | } 36 | 37 | public GameScene(int frameRate) { 38 | setBackground(Color.BLACK); 39 | setFocusable(true); 40 | setDoubleBuffered(true); 41 | 42 | addKeyListener(new KeyAdapter() { 43 | @Override 44 | public void keyPressed(KeyEvent e) { 45 | switch (e.getKeyCode()) { 46 | case KeyEvent.VK_UP: 47 | keyStates[KEY_UP] = KeyState.DOWN; 48 | break; 49 | case KeyEvent.VK_DOWN: 50 | keyStates[KEY_DOWN] = KeyState.DOWN; 51 | break; 52 | case KeyEvent.VK_LEFT: 53 | keyStates[KEY_LEFT] = KeyState.DOWN; 54 | break; 55 | case KeyEvent.VK_RIGHT: 56 | keyStates[KEY_RIGHT] = KeyState.DOWN; 57 | break; 58 | case KeyEvent.VK_SPACE: 59 | keyStates[KEY_FIRE] = KeyState.DOWN; 60 | break; 61 | } 62 | } 63 | 64 | @Override 65 | public void keyReleased(KeyEvent e) { 66 | switch (e.getKeyCode()) { 67 | case KeyEvent.VK_UP: 68 | keyStates[KEY_UP] = KeyState.UP; 69 | break; 70 | case KeyEvent.VK_DOWN: 71 | keyStates[KEY_DOWN] = KeyState.UP; 72 | break; 73 | case KeyEvent.VK_LEFT: 74 | keyStates[KEY_LEFT] = KeyState.UP; 75 | break; 76 | case KeyEvent.VK_RIGHT: 77 | keyStates[KEY_RIGHT] = KeyState.UP; 78 | break; 79 | case KeyEvent.VK_SPACE: 80 | keyStates[KEY_FIRE] = KeyState.UP; 81 | break; 82 | } 83 | } 84 | }); 85 | 86 | previousTime = System.currentTimeMillis(); 87 | 88 | Timer timer = new Timer(1000/frameRate, new ActionListener() { 89 | @Override 90 | public void actionPerformed(ActionEvent e) { 91 | updateAll(); 92 | } 93 | }); 94 | 95 | timer.start(); 96 | } 97 | 98 | public void addGameObject(GameObject object) { 99 | waitList.add(object); 100 | } 101 | 102 | public KeyState getKeyState(int key) { 103 | return keyStates[key]; 104 | } 105 | 106 | private void updateAll() { 107 | long currentTime = System.currentTimeMillis(); 108 | double deltaTime = (currentTime - previousTime) / 1000.0f; 109 | previousTime = currentTime; 110 | 111 | // Call update() on game scene and all game objects 112 | update(deltaTime); 113 | for (GameObject gameObject : gameObjects) { 114 | gameObject.update(deltaTime); 115 | } 116 | 117 | // Prune dead game objects 118 | Iterator it = gameObjects.iterator(); 119 | while (it.hasNext()) { 120 | GameObject gameObject = it.next(); 121 | if (gameObject.isDestroyed()) { 122 | it.remove(); 123 | } 124 | } 125 | 126 | // Add newly queued game objects 127 | for (GameObject object : waitList) { 128 | gameObjects.add(object); 129 | } 130 | waitList.clear(); 131 | 132 | repaint(); 133 | } 134 | 135 | @Override 136 | public void paintComponent(Graphics g) { 137 | super.paintComponent(g); 138 | render(g); 139 | for (GameObject gameObject : gameObjects) { 140 | gameObject.draw(g); 141 | } 142 | } 143 | 144 | public abstract void update(double deltaTime); 145 | public abstract void render(Graphics g); 146 | } 147 | -------------------------------------------------------------------------------- /08 - Graphics/spacegame/ShootingScene.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by poonna on 2/27/18 AD. 3 | */ 4 | 5 | import java.awt.Graphics; 6 | import java.awt.Color; 7 | import java.util.ArrayList; 8 | import java.util.Random; 9 | 10 | public class ShootingScene extends GameScene { 11 | private SpaceShip player; 12 | private ArrayList enemies = new ArrayList(); 13 | private ArrayList bullets = new ArrayList(); 14 | 15 | private Random random; 16 | private int score; 17 | private int respawnCount = 3; 18 | 19 | public ShootingScene() { 20 | player = new SpaceShip(this, 400, 500); 21 | addGameObject(player); 22 | random = new Random(); 23 | } 24 | 25 | @Override 26 | public void update(double deltaTime) { 27 | for (GameObject bullet : bullets) { 28 | for (GameObject enemy : enemies) { 29 | if (bullet.collides(enemy)) { 30 | bullet.destroy(); 31 | enemy.destroy(); 32 | 33 | addScore(10); 34 | respawnCount++; 35 | } 36 | } 37 | } 38 | 39 | while (respawnCount > 0) { 40 | Alien alien = new Alien(this, 100 + random.nextInt(700), 100 + random.nextInt(200)); 41 | enemies.add(alien); 42 | addGameObject(alien); 43 | respawnCount--; 44 | } 45 | } 46 | 47 | @Override 48 | public void render(Graphics g) { 49 | g.setColor(Color.WHITE); 50 | g.drawString("Score: " + score, 10, 20); 51 | } 52 | 53 | public void fire(Bullet bullet) { 54 | bullets.add(bullet); 55 | addGameObject(bullet); 56 | } 57 | 58 | public void addScore(int points) { 59 | score += points; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /08 - Graphics/spacegame/SpaceGame.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by poonna on 2/27/18 AD. 3 | */ 4 | 5 | import javax.swing.SwingUtilities; 6 | import javax.swing.JFrame; 7 | 8 | public class SpaceGame { 9 | public static void main(String[] args) { 10 | SwingUtilities.invokeLater(new Runnable() { 11 | @Override 12 | public void run() { 13 | JFrame game = new JFrame(); 14 | game.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 15 | game.setSize(800, 600); 16 | game.add(new ShootingScene()); 17 | game.setVisible(true); 18 | } 19 | }); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /08 - Graphics/spacegame/SpaceShip.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by poonna on 27/2/18 AD. 3 | */ 4 | 5 | import java.awt.*; 6 | import java.io.File; 7 | import java.io.IOException; 8 | import javax.imageio.ImageIO; 9 | 10 | public class SpaceShip extends GameObject { 11 | private ShootingScene scene; 12 | private Image image; 13 | private int x, y; 14 | private int speed = 10; 15 | 16 | private static final double FIRING_INTERVAL = 0.5; 17 | private double lastFiringElapsed = 0.0; 18 | 19 | public SpaceShip(ShootingScene scene, int x, int y) { 20 | this.scene = scene; 21 | 22 | // Load player image 23 | try { 24 | image = ImageIO.read(new File("images/star.png")); 25 | if (image == null) { 26 | System.err.println("Unrecognized image type."); 27 | System.exit(1); 28 | } 29 | } catch (IOException e) { 30 | System.err.println("Error while loading image."); 31 | System.exit(1); 32 | } 33 | 34 | this.x = x - image.getWidth(null) / 2; 35 | this.y = y - image.getHeight(null) / 2; 36 | } 37 | 38 | @Override 39 | public void update(double deltaTime) { 40 | if (scene.getKeyState(GameScene.KEY_UP) == GameScene.KeyState.DOWN) { 41 | y -= speed; 42 | } 43 | if (scene.getKeyState(GameScene.KEY_DOWN) == GameScene.KeyState.DOWN) { 44 | y += speed; 45 | } 46 | if (scene.getKeyState(GameScene.KEY_LEFT) == GameScene.KeyState.DOWN) { 47 | x -= speed; 48 | } 49 | if (scene.getKeyState(GameScene.KEY_RIGHT) == GameScene.KeyState.DOWN) { 50 | x += speed; 51 | } 52 | if (scene.getKeyState(GameScene.KEY_FIRE) == GameScene.KeyState.DOWN) { 53 | if (lastFiringElapsed >= FIRING_INTERVAL) { 54 | scene.fire(new Bullet(scene, x, y)); 55 | lastFiringElapsed = 0.0; 56 | } 57 | } 58 | 59 | lastFiringElapsed += deltaTime; 60 | } 61 | 62 | @Override 63 | public void draw(Graphics g) { 64 | g.drawImage(image, x, y, scene); 65 | } 66 | 67 | @Override 68 | public Rectangle getBounds() { 69 | return new Rectangle(x, y, image.getWidth(null), image.getHeight(null)); 70 | } 71 | 72 | public int getX() { 73 | return x; 74 | } 75 | 76 | public int getY() { 77 | return y; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/alien.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/alien.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/breakout/ball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/breakout/ball.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/breakout/brickie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/breakout/brickie.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/breakout/paddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/breakout/paddle.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/craft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/craft.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/minesweeper/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/minesweeper/0.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/minesweeper/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/minesweeper/1.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/minesweeper/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/minesweeper/10.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/minesweeper/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/minesweeper/11.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/minesweeper/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/minesweeper/12.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/minesweeper/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/minesweeper/2.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/minesweeper/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/minesweeper/3.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/minesweeper/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/minesweeper/4.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/minesweeper/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/minesweeper/5.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/minesweeper/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/minesweeper/6.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/minesweeper/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/minesweeper/7.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/minesweeper/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/minesweeper/8.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/minesweeper/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/minesweeper/9.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/missile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/missile.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/pacman/down1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/pacman/down1.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/pacman/down2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/pacman/down2.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/pacman/down3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/pacman/down3.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/pacman/ghost.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/pacman/ghost.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/pacman/left1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/pacman/left1.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/pacman/left2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/pacman/left2.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/pacman/left3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/pacman/left3.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/pacman/pacman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/pacman/pacman.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/pacman/right1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/pacman/right1.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/pacman/right2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/pacman/right2.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/pacman/right3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/pacman/right3.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/pacman/up1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/pacman/up1.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/pacman/up2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/pacman/up2.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/pacman/up3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/pacman/up3.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/snake/apple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/snake/apple.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/snake/dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/snake/dot.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/snake/head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/snake/head.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/sokoban/area.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/sokoban/area.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/sokoban/baggage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/sokoban/baggage.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/sokoban/sokoban.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/sokoban/sokoban.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/sokoban/wall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/sokoban/wall.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/spaceinvaders/alien.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/spaceinvaders/alien.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/spaceinvaders/bomb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/spaceinvaders/bomb.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/spaceinvaders/explosion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/spaceinvaders/explosion.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/spaceinvaders/player.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/spaceinvaders/player.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/spaceinvaders/shot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/spaceinvaders/shot.png -------------------------------------------------------------------------------- /08 - Graphics/spacegame/images/star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/08 - Graphics/spacegame/images/star.png -------------------------------------------------------------------------------- /09 - Exceptions/.ipynb_checkpoints/ExceptionHierarchy-checkpoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/09 - Exceptions/.ipynb_checkpoints/ExceptionHierarchy-checkpoint.png -------------------------------------------------------------------------------- /09 - Exceptions/AssertTest.java: -------------------------------------------------------------------------------- 1 | import java.util.Scanner; 2 | 3 | public class AssertTest { 4 | public static void main(String[] args) { 5 | Scanner input = new Scanner(System.in); 6 | 7 | System.out.println("Enter a number between 0 and 10: "); 8 | int number = input.nextInt(); 9 | 10 | assert (number >= 0 && number <= 10) : "bad number: " + number; 11 | 12 | System.out.printf("You entered %d%n", number); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /09 - Exceptions/DivideByZeroNoExceptionHandling.java: -------------------------------------------------------------------------------- 1 | import java.util.Scanner; 2 | 3 | public class DivideByZeroNoExceptionHandling { 4 | public static int quotient(int numerator, int denominator) { 5 | return numerator / denominator; 6 | } 7 | 8 | public static void main(String[] args) { 9 | Scanner input = new Scanner(System.in); 10 | System.out.print("Enter an integer numerator: "); 11 | int numerator = input.nextInt(); 12 | System.out.print("Enter an integer denominator: "); 13 | int denominator = input.nextInt(); 14 | 15 | int result = quotient(numerator, denominator); 16 | System.out.printf("%d/%d = %d%n", numerator, denominator, result); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /09 - Exceptions/DivideByZeroWithExceptionHandling.java: -------------------------------------------------------------------------------- 1 | import java.util.Scanner; 2 | import java.util.InputMismatchException; 3 | 4 | public class DivideByZeroWithExceptionHandling { 5 | public static int quotient(int numerator, int denominator) { 6 | return numerator / denominator; 7 | } 8 | 9 | public static void main(String[] args) { 10 | Scanner input = new Scanner(System.in); 11 | boolean keepRetrying = true; 12 | 13 | do { 14 | try { 15 | System.out.print("Enter an integer numerator: "); 16 | int numerator = input.nextInt(); 17 | System.out.print("Enter an integer denominator: "); 18 | int denominator = input.nextInt(); 19 | 20 | int result = quotient(numerator, denominator); 21 | System.out.printf("%d/%d = %d%n", numerator, denominator, result); 22 | 23 | keepRetrying = false; 24 | } catch (InputMismatchException e) { 25 | System.err.printf("Exception: %s%n", e); 26 | System.out.println("You must enter an integer. Please try again."); 27 | input.nextLine(); // discard the input and try again 28 | } catch (ArithmeticException e) { 29 | System.err.printf("Exception: %s%n", e); 30 | System.out.println("Denominator cannot be zero. Please try again."); 31 | } 32 | } while (keepRetrying); 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /09 - Exceptions/ExceptionHierarchy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poonna/java-book/66d46b96135b2247844b36175f6398593d4a0cfb/09 - Exceptions/ExceptionHierarchy.png -------------------------------------------------------------------------------- /09 - Exceptions/UsingChainedExceptions.java: -------------------------------------------------------------------------------- 1 | public class UsingChainedExceptions { 2 | public static void main(String[] args) { 3 | try { 4 | method1(); 5 | } catch (Exception e) { 6 | e.printStackTrace(); 7 | } 8 | } 9 | 10 | public static void method1() throws Exception { 11 | try { 12 | method2(); 13 | } catch (Exception e) { 14 | throw new Exception("Exception thrown in method1()", e); 15 | } 16 | } 17 | 18 | public static void method2() throws Exception { 19 | try { 20 | method3(); 21 | } catch (Exception e) { 22 | throw new Exception("Exception thrown in method2()", e); 23 | } 24 | } 25 | 26 | public static void method3() throws Exception { 27 | throw new Exception("Exception thrown in method3()"); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /09 - Exceptions/UsingExceptions.java: -------------------------------------------------------------------------------- 1 | public class UsingExceptions { 2 | public static void main(String[] args) { 3 | try { 4 | method1(); 5 | } catch (Exception e) { 6 | System.err.printf("%s%n%n", e.getMessage()); 7 | e.printStackTrace(); 8 | 9 | // Obtain stack-trace information 10 | StackTraceElement[] traceElements = e.getStackTrace(); 11 | 12 | System.out.printf("%nStack trace from getStackTrace():%n"); 13 | System.out.println("Class\t\tFile\t\t\tLine\tMethod"); 14 | for (StackTraceElement element : traceElements) { 15 | System.out.printf("%s\t", element.getClassName()); 16 | System.out.printf("%s\t", element.getFileName()); 17 | System.out.printf("%s\t", element.getLineNumber()); 18 | System.out.printf("%s%n", element.getMethodName()); 19 | } 20 | } 21 | } 22 | 23 | public static void method1() throws Exception { 24 | method2(); 25 | } 26 | 27 | public static void method2() throws Exception { 28 | method3(); 29 | } 30 | 31 | public static void method3() throws Exception { 32 | throw new Exception("Exception thrown in method3()"); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # java-book 2 | My ongoing drafts of Java lecture notes in Thai. The notes might initially contain some copyrighted materials. They will gradually be replaced with other suitable non-copyrighted materials. 3 | 4 | เอกสารแต่ละบทจะอยู่ในไฟล์ชื่อ `*.ipynb` สามารถคลิกดูจาก browser ได้เลยโดยตรง หรือคลิกจากสารบัญนี้ 5 | 6 | 1. [ภาษา Java](https://github.com/Poonna/java-book/blob/master/01%20-%20Java%20Introduction/Java%20Introduction.ipynb) 7 | 2. [โครงสร้างควบคุม](https://github.com/Poonna/java-book/blob/master/02%20-%20Control%20Structures/Control%20Structures.ipynb) 8 | 3. [คอลเลกชัน](https://github.com/Poonna/java-book/blob/master/03%20-%20Collections/Collections.ipynb) 9 | 4. [คลาสและอ็อบเจกต์](https://github.com/Poonna/java-book/blob/master/04%20-%20Classes%20and%20Objects/Classes%20and%20Objects.ipynb) 10 | 5. [การสืบทอด](https://github.com/Poonna/java-book/blob/master/05%20-%20Inheritance/Inheritance.ipynb) 11 | 6. [การมีหลายรูปแบบ (พหุสัณฐาน)](https://github.com/Poonna/java-book/blob/master/06%20-%20Polymorphism/Polymorphism.ipynb) 12 | 7. [อีเวนต์และการโปรแกรมส่วนต่อประสานกราฟิกกับผู้ใช้](https://github.com/Poonna/java-book/blob/master/07%20-%20Events%20and%20GUI%20Programming/Events%20and%20GUI%20Programming.ipynb) 13 | 8. [กราฟิกส์](https://github.com/Poonna/java-book/blob/master/08%20-%20Graphics/Graphics.ipynb) 14 | 9. [เอ็กเซปชัน](https://github.com/Poonna/java-book/blob/master/09%20-%20Exceptions/Exceptions.ipynb) 15 | 10. [คลาสและเมทอดเจเนอริก](https://github.com/Poonna/java-book/blob/master/10%20-%20Generic%20Classes%20and%20Methods/Generic%20Classes%20and%20Methods.ipynb) 16 | --------------------------------------------------------------------------------