ทบทวน Java ก่อนเข้าเรื่อง Object-Oriented Programming

เนื่องจากมีหลายๆ คนที่ศึกษาการเขียนโปรแกรมแบบ OOP หรือ Object-Oriented Programming ซึ่งหนึ่งในภาษายอดนิยมที่ชอบยกมาสอนกันก็คือภาษา Java นั่นเอง (ส่วนภาษาอื่นๆ ก็เช่น C# Python และล่าสุดก็มีการเพิ่มภาษา Kotlin เข้ามาอีกภาษา)

แต่ปัญหาคือจำ syntax ภาษา Java ไม่ได้ ในบทความนี้เราก็เลยจะมาทบทวนกันหน่อย

หัวเรื่องที่จะทบทวนส่วนใหญ่จะเป็นส่วนที่เป็น Fundamental ทั่วๆ ไปนะ

  • คอนเซ็ปของภาษา Java
  • ตัวแปรชนิดต่างๆ
  • Input/Output
  • Flow Control (พวก if-else while for ต่างๆ)
  • Array & List

คอนเซ็ปของ Java: Write Once, Run anywhere

ปกติเวลาเราเขียนโปรแกรม เช่นขอสมมุติภาษา C ละกัน เราก็จะเขียนโค้ดด้วยภาษา C ซึ่งเป็นภาษาระดับสูง คนอ่านค่อนข้างง่าย ปัญหาคือภาษาพวกนี้คอมพิวเตอร์อ่านไม่รู้เรื่อง จึงต้องมีการสร้างโปรแกรมที่ทำการแปลงภาษาระดับสูงที่คนอ่านออกพวกนี้ ให้กลายเป็นภาษาเครื่องหรือ Machine Code (นึกถึงเลขฐาน2 0101110010 เยอะๆ อะไรแบบนั้น) ซึ่งเจ้าโปรแกรมพวกนี้เราจะเรียกมันว่า Compiler นั่นเอง

ทุกอย่างดูดี คนไม่จำเป็นต้องเขียนโค้ดเป็นภาษาเครื่องที่เขียนออกมาแล้วน่าปวดหัวเหลือเกิน แต่พวกเราโปรแกรมสามารถเขียนโค้ดด้วยภาษาระดับสูงที่คนพอจะอ่านรู้เรื่องได้ ชีวิตสบาย~

แต่ปัญหาต่อมาคือเจ้าภาษาเครื่องเนี่ย ในแต่ละเครื่องดันใช้ไม่เหมือนกัน ยกตัวอย่างเช่นในระบบ Windows ถ้าต้องการแสดงค่าออกมาทางหน้าจอจะใช้คำสั่ง A ส่วนถ้าอยู่ในระบบ Unix จะใช้คำสั่ง B แทน

นั่นหมายความว่า Compiler ของเราจะมีตัวเดียวไม่ได้แล้ว เพราะเราจะต้องแปลภาษาพวกนี้ให้ระบบหลายแบบ ถ้างงลองดูรูปข้างล่างนี้ประกอบนะ

ก็จะเห็นว่าถ้าเราใช้หลักการแบบนี้ Compiler จะต้องเก่ง (หรือมีหลายตัว) พอที่จะแปลโปรแกรมของเราให้ออกมาเป็น Machine Code ที่รันได้บนระบบทุกระบบเลย

แต่ในชีวิตจริง มันไม่ได้ง่ายขนาดนั้น!! เพราะว่าระบบปฏิบัติการแต่ละระบบก็มีคำสั่งเป็นของตัวเองที่ไม่เหมือนกันอีก! ทีนี้ก็จะเป็นเรื่องล่ะ เพราะมันจะทำให้บางครั้งอาจเกิดเหตุการแบบนี้

เราเขียนโปรแกรมบน Windows รันได้ ไม่มีปัญหาอะไรเลย แต่พอส่งไปเครื่องเพื่อนที่ใช้ Mac กันคอนไพล์ไม่ผ่านและรันไม่ได้ด้วย ... สาเหตุนี้เกิดขึ้นได้ ถ้าเราดันเขียนโปรแกรมโดยใช้คำสั่งที่มีบน Windows เท่านั้น

สรุปคือบางครั้ง เราอยากเขียนโปรแกรมของเราให้รันได้บนทั้ง Windows Mac Linux เราอาจจะต้องเขียนโปรแกรมเดียวกันทั้งหมด 3 รอบเลย

ตอนเขาสร้างภาษา Java เลยมีแนวคิดใหม่ขึ้นมา นั่นคือ

Write Once, Run Anywhere

หรือ เขียนครั้งเดียว รันได้ทุกระบบเลย ... ฟังแล้วดูดีมาก! ถ้าทำได้ ชีวิตโปรแกรมเมอร์จะสบายเลย ว่าแต่ จะทำยังไงล่ะ?

เขาก็เลยมีแนวคิดว่า ในเมื่อคอมพิวเตอร์มีหลายระบบ งั้นเราจะสร้างระบบตัวกลางสำหรับภาษา Java ขึ้นมา (ซึ่งเราจะเรียกระบบกลางที่สมมุติขึ้นมาตัวนี้ว่า JVM หรือ Java Virtual Machine) ซึ่งหน้าที่ของมันคือ

ทำตัวเป็นตัวแปลภาษาระหว่างโปรแกรมกับเครื่อง

หรือก็คือ แทนที่เราจะเอาโปรแกรมไปรันบนเครื่องต่างๆ ตรงๆ แบบเดิม เราก็จะรันบน JVM แทน ทีนี้พอรันบน JVM ก็ไม่ต้องมีปัญหาแล้ว ว่าคำสั่งนี้ใช้ได้บนเครื่องนี้รึเปล่า เพราะ JVM จะจัดการแทนเราทั้งหมดเลย

สรุป

  • ปกติเวลาเขียนโปรแกรมเสร็จ เราจะ compile ออกมาเป็นไฟล์ที่รันได้เฉพาะกับเครื่องแบบใดแบบหนึ่งเท่านั้น
  • ทำให้เวลาต้องการรันโปรแกรมในหลายระบบ ต้องมา compile ใหม่ทุกรอบ
  • Java แก้ไขโดยการสร้างการตั้งให้ไฟล์ที่ compile ออกมาเป็นไฟล์แบบพิเศษ (ไฟล์นี้จะรันบนเครื่องไหนก็ไม่ได้ทั้งนั้น แต่สามารถรันบน JVM ได้)
  • ดันนั้น เวลาเราจะรันโปรแกรม เราไม่ได้รันโปรแกรมตรงๆ แต่รันผ่านโปรแกรม Java อีกทีนึง
  • ซึ่งJava จะทำการแปลโปรแกรมของเราให้คุยกับเครื่องไหนก็ได้อีกทีนึงนั่นเอง

โครงสร้างภาษา Java

สำหรับ Java นั้นจะมองทุกอย่างเป็นคลาสทั้งหมด ดังนั้นการเขียนโค้ดในภาษานี้ จะต้องเขียนอยู่ในส่วนของคลาสทั้งหมด แบบนี้

class MyJava {
    //เขียนโค้ดในนี้นะ
}

ซึ่งชื่อคลาสที่เราจะตั้ง ไม่ใช่ตั้งยังไงก็ได้นะ เขามีกฎอยู่ นั่นคือชื่อคลาสจะต้องตรงกับชื่อไฟล์ แบบนี้

โดยแนวทางการตั้งชื่อคลาสในภาษา Java เรามักจะตั้งให้อยู่ในรูปแบบของ Camel-Case (ใครไม่รู้จักคาเมล อ่านได้ที่นี่) ไม่เขียนแบบนี้ก็ได้ แต่แต่ควรจะเขียนในรูปแบบนี้นะ

แต่แค่มีคลาส Java ก็ยังทำงานไม่ได้ เพราะมันจำเป็นต้องมีจุดเริ่มต้นของโปรแกรมด้วย (ถ้าใครเขียนภาษา C มาก่อนน่าจะคุ้นกับ int main() หรือ void main() นั่นแหละ) ภาษา Java ใช้ภาษา C เป็นต้นแบบ มันเลยลอกมาเลย

สรุปคือ จะเขียนโปรแกรมภาษา Java ต้องเริ่มด้วยโค้ดแบบนี้

class MyJava {
	public static void main(String[] args){
		//เขียนโค้ดในนี้
		System.out.println("hello world!");
	}
}
output

hello world!

คนเขียนภาษานี้ช่วงแรก มักจะจำโค้ดแพทเทิร์นบังคับนี้ไม่ค่อยได้ (แน่นอน ภาษาอะไร กว่าจะเริ่มเขียนโค้ดได้ ยากมากเลย!) แต่สำหรับผู้เริ่มเรียนภาษา Java แล้วก็พยายามจำกันให้ได้ละกัน

Variable หรือ ตัวแปร

ตัวแปรเป็นส่วนสำคัญในการเขียนโปรแกรม ทำหน้าที่เก็บข้อมูล (Value) ที่เปลี่ยนไปเรื่อยๆ ได้เอาไว้

ภาษา Java เป็นภาษาแบบ Type-Sensitive หรือซีเรียสเรื่องชนิดของข้อมูล นั่นหมายความว่าถ้าเราสร้างตัวแปรชนิดตัวเลขขึ้นมา เราก็จะเก็บข้อมูลได้แค่ตัวเลข ไม่สามารถเอาไปเก็บตัวอักษรหรือประโยคได้

โดยตัวแปรจะแบ่งออกเป็น 2 ประเภทหลักๆ คือ

Primitive Data-Type

หรือตัวแปรชนิดพื้นฐาน มีทั้งหมด 8 ตัวคือ

  • byte - เลขจำนวนเต็ม ขนาด 1 byte
  • short - เลขจำนวนเต็ม ขนาด 2 byte
  • int - เลขจำนวนเต็ม ขนาด 4 byte
  • long - เลขจำนวนเต็ม ขนาด 8 byte
  • float - เลขทศนิยม ขนาด 4 byte
  • double - เลขทศนิยม ขนาด 8 byte
  • char - ตัวอักษร 1 อักขระ (character 1 ตัวเท่านั้น)
  • boolean - ค่า logic มีแค่ 2 ค่าที่เป็นไปได้คือ true / false เท่านั้น

สังเกตว่าชนิดตัวแปรสำหรับตัวเลขจะมีหลายแบบ ต่างกันแค่ขนาดเท่านั้น แต่สำหรับมือใหม่ขอแนะนำให้ใช้แค่ int (สำหรับจำนวนเต็ม) กับ double (สำหรับทศนิยม) ไปก่อน ตัวอื่นอย่าเพิ่งไปใช้มัน

ตัวอย่างการใช้งาน

int num = 1;
double gravity = 9.8;
char classroom = 'A';
boolean pass = true;

และอย่างที่บอกว่า Java ซีเรียสเรื่องชนิดตัวแปรมาก ทำให้เราไม่สามารถ assign ค่าจากตัวแปรต่างชนิดกันได้ เช่น

int x = 1.2; //ทำไม่ได้เพราะ 1.2 เป็นทศนิยม
char c = 65; //ทำไม่ได้เพราะ 65 เป็นตัวเลข ในข้อนี้จะต่างจากภาษาตระกูลCที่ถือว่าเป็นการกำหนดค่า ASCII

สำหรับ Java ถ้าต้องการทำแบบนี้จะต้องทำการ Casting ชนิดตัวแปรเสมอ แบบนี้

int x = (int) 1.2; //แบบนี้ได้ เพราะบอกว่าขอแปล 1.2 ไปเป็น int ก่อน จะได้ 1 ถ้วนๆ
char c = (char) 65; //แบบนี้ก็ได้ คือขอแปล 65 เป็นตัวอักษรก่อน (ได้อักษรตัวไหน ดูตามตาราง ASCII)

Object-Type

ชนิดตัวแปรอีกแบบในโลก Java คือตัวแปรประเภทอ๊อบเจค (หรือภาษาไทยแปลออกมาว่าตัวแปรเชิงวัตถุ) ตัวแปรประเภทนี้จะเก็บข้อมูลในรูปแบบของ pointer ทั้งหมด ในภาษา Java ชนิดตัวแปรทั้งหมดนอกจาก primitive type ที่พูดถึงไปด้านบนเมื่อกี้ นับว่าเป็นตัวแปรแบบ Object type ทั้งหมดเลย สังเกตง่ายๆ คือส่วนใหญ่ตัวแปรพวกนี้จะขึ้นด้วยตัวอักษรตัวใหญ่ เช่น

  • String - ชนิดตัวแปรสำหรับเก็บประโยคยาวๆ (สายอักขระ)
  • Scanner - สแกนเนอร์สำหรับอ่านค่า เช่นรับข้อมูลจากคีย์บอร์ดหรือไฟล์
  • ArrayList - ข้อมูลชนิด List เก็บในรูปแบบ array

 

แต่การสร้างตัวแปร object จะยากกว่าตัวแปร primitive นิดหน่อยตรงที่เราต้องทำการ new ขึ้นมาก่อน โดยมีแพทเทิร์นแบบนี้

ClassName variable-name = new ClassName();

ตัวอย่างเช่น

String name = new String("Ta");
//หรือ
String name = "Ta";

ArrayList studentsName = new ArrayList();

แต่ก็มีข้อยกเว้น คือคลาสพิเศษบางตัวที่ Java อนุญาต ไม่ต้องเขียน new ก็ได้ (เดี๋ยวตอนคอมไพล์ ฉันใส่ให้เอง) เช่น String เป็นต้น

array

เราสามารถเอาตัวแปรชนิดต่างๆ มาสร้าง array ได้โดยการใช้ []

//การสร้างตัวแปรแบบธรรมดา
int x; 

//สร้างแบบ array 100 ช่อง
int[] x = new int[100];
//หรือ
int x[] = new int[100];

ใน Java ถือว่า array คล้ายกับตัวแปรแบบ object ดังนั้นจะต้อง new ก่อนใช้งานเสนอ

และเราสามารถกำหนดค่าเริ่มต้นให้ array ได้ด้วย แบบนี้

//การสร้างตัวแปรแบบธรรมดา
int x = 10; 

//สร้างแบบ array
int[] x = new int[]{10,20,30,40};
//หรือ
int[] x = {10,20,30,40};

String names = {"Ann", "Bob", "Civa", "Dota"};

Input / Output

การแสดงผล output

มาพูดถึง output หลักของ Java ก่อน เราใช้คำสั่ง System.out.println() ซึ่งปริ๊นค่าได้ทุกอย่างตั้งแต่ int double char boolean และ String

System.out.println("hello world!");
System.out.println(100);
System.out.println('A');
System.out.println(true);

int ans = 1 + 2;
System.out.println("answer is " + ans + ".");
output

hello world!
100
A
true
answer is 3.

ซึ่งสามารถใช้ + ในการต่อ string กับ variable ได้

ข้อควรจำสำหรับ println คือหลังจากปริ๊นค่าแล้ว มันจะขึ้นบรรทัดใหม่ให้อัตโนมัติ (ตามชื่อ "print line") แต่ถ้าไม่อยากให้มันขึ้นบรรทัดใหม่อัตโนมัติหลังจากปริ๊นค่าเสร็จ สามารถใช้คำสั่ง System.out.print() แทนได้

การรับค่า input

ใน Java มีวิธีรับค่าจากผู้ใช้งาน (จากการพิมพ์ผ่านคีย์บอร์ด) หลายวิธีมากตั้งแต่การใช้ BufferedReader InputStreamReader แต่วิธีที่ขอแนะนำคือใช้ Scanner

แต่อยู่ๆ เราจะใช้งาน Scanner เลยไม่ได้ เราจะต้องสร้าง Scanner ขึ้นมาก่อนด้วยโค้ดนี้

import java.util.Scanner;

class MyJava {
	public static void main(String[] args){
		Scanner sc = new Scanner(System.in);
	}
}

ในตัวอย่างนี้เราสร้าง Scanner ขึ้นมาหนึ่งตัวชื่อ sc และตั้งค่าให้มันสแกนค่าจาก System.in ซึ่งก็คือ คีย์บอร์ด (System.out ที่ใช้ในหัวข้อที่แล้วหมายถึง monitor)

*สำหรับการใช้ Scanner จะต้องทำการ import คลาสก่อนถึงจะใช้ได้ ด้วย import java.util.Scanner

ทีนี้เราก็มีสแกนเนอร์สำหรับอ่านค่าแล้ว ต่อไปก็คือสั่งให้สแกนเนอร์อ่านค่าจากคีย์บอร์ดเข้ามา โดยจะต้องบอกให้สแกนเนอร์รู้ว่าเราอยากได้ค่า (Value) เป็นตัวแปรชนิดไหน แบบนี้

Scanner sc = new Scanner(System.in);

int x = sc.nextInt();
System.out.print("input number: ");
double d = sc.nextDouble();
System.out.print("input your name: ");
String s = sc.nextLine();
output

input number: 3.1416 [←Enter]
input your name: nartra [←Enter]

*การใช้สแกนเนอร์สามารถใช้อ่านค่าได้เรื่อยๆ โดยไม่ต้องสร้างสแกนเนอร์ตัวใหม่ขึ้นมา

Flow Control

สำหรับใครที่เขียนภาษาตระกูล C มาก่อนน่าจะคุ้นกับ syntax พวกนี้ เพราะมันลอกกันมาเลย

if-else

if(x > 10){
    System.out.println("x more than ten");
}

if(x > 10){
    System.out.println("x more than ten");
}
else{
    System.out.println("x not more than ten");
}

while, do-while

int i = 1;
while(i<=10){
    System.out.println("i = " + i);
    i++;
}

int i = 1;
do{
    System.out.println("i = " + i);
    i++;
}
while(i<=10);

for

for(i=1; i<=10; i++){
    System.out.println("i = " + i);
}

break, continue

เหมือนกับภาษา C คือเราสามารถใช้คำสั่ง break และ continue ในการควบคุมการวนของลูปได้

//break at 3
for(i=1; i<=5; i++){
    if( i==3 ) break;
    System.out.print(i + " ");
}

//continue (skip) 3
for(i=1; i<=5; i++){
    if( i==3 ) continue;
    System.out.print(i + " ");
}
output

1 2
1 2 4 5

array และ List

Array

วิธีสร้าง array ใน Java มีหลายวิธีดังนี้

int[] arr = new int[10]; //สร้างarrayของintขนาด 10 ตัว

int[] arr = new int[]{1,2,3,4,5,6,7,8,9,10}; //สร้างarrayของint ตั้งแต่ 1-10 (ไม่ต้องบอกขนาดก็ได้ เพราะJavaจะนับให้เองเลย)

int[] arr = {1,2,3,4,5,6,7,8,9,10}; //หรือจะเขียนย่อๆ แบบนี้ก็ได้

//สำหรับตัวแปรประเภทอื่นก็สร้างคล้ายๆ กัน แต่เปลี่ยนชนิด
String[] nameList = new String[10];

//และถ้าอยากได้ขนาดของ array ว่ามีกี่ช่องจะใช้ .length (อย่าจำสับสนกับ .length() ของ String นะ ตัวนั้นจะมีวงเล็บต่อท้าย)
arr.length

ข้อสังเกตคือ array ในภาษานี้ถือว่ามีสภาพเป็น pointer ตัวหนึ่ง ดังนั้นก่อนจะใช้งานจะต้อง new ก่อนใช้งานเสมอ ถ้าลืมละก็ พังแน่นอนนะ

int[] arr;
arr[0] = 1;

แบบนี้พังแน่นอน จะขึ้นแจ้งเตือนว่า NullPointerException นะ

แต่นอกจากเขียนแบบนี้ เรามีวิธีเขียน array อีกแบบคือแบบผสม แบบนี้

int[] arr1, arr2, arr3;
//แน่นอนว่าอย่าลืม new ก่อนใช้ด้วยนะ
arr1 = new int[10];
arr2 = new int[10];
arr3 = new int[10];

//หรืออีกแบบนึง จะสลับข้าง [] แบบนี้ก็ได้นะ
int arr[] = new int[10]; 

//แต่ข้อควรระวังคือ...
int x[], y, z;
//แบบนี้ตัว x ถือว่าเป็น array แต่ y, z นั้นถือว่าเป็นแค่ int ธรรมดา

array ใน Java ถือว่ามีขนาดแบบฟิกตายตัว ถ้าประกาศ 10 ช่อง ก็ใช้ได้แค่ 10 ช่อง (index 0-9) ไม่สามารถขยายขนาดได้ ถ้าใช้เกินจะเจอ ArrayIndexOutOfBoundException นะ

และสำหรับ Array นั้นจะมีคลาสช่วยเหลือ (helper class) ชื่อว่า Arrays เอาไว้จัดการ Array ได้ เช่นการ sorting (เรียงลำดับข้อมูล) แบบนี้

int[] arr = {3,8,1,6,7,2,9,4,0,5};
Arrays.sort(arr);
//ตอนนี้ arr จะมีค่า {0,1,2,3,4,5,6,7,8,9}

List

เนื่องจาก array ไม่สามารถขยายขนาดได้ สำหรับข้อมูลชนิดที่เราไม่รูปขนาดตายตัวจะใช้ array ยากมาก ดังนั้นเลยมีการสร้างข้อมูลชนิด List ขึ้นมา โดยวิธีการใช้จะต้องเลือกว่าจะสร้างด้วย ArrayList หรือ LinkedList แบบนี้

List<Integer> data = new ArrayList<>();
//หรือ
List<Integer> data = new LinkedList<>();

ซึ่งวิธีการใช้จะต่างกับ array แบบปกติดังนี้

List<Integer> data = new ArrayList<>();

data.set(0,100);
//จะเหมือนกับ data[0] = 100

System.out.println(data.get(0));
//จะเหมือนกับ System.out.println(data[0]);

data.size();
//จะเหมือนกับ data.length

data.add(200);
//อันนี้ array ไม่มี ใช้สำหรับเพิ่มข้อมูลเข้าไปใน array ช่องสุดท้าย (ต่อท้าย) ตัว List จะขยายขนาดขึ้นเองอัตโนมัติ

แต่เนื่องจาก List นั้นไม่ใช่ array แท้ๆ (มันเป็น class ที่สร้างขึ้นมาเอง) เราเลยต้องมีการบอกด้วยว่าข้อมูลในลิสต์นี้เป็นชนิดอะไรด้วยการใส่ generic ลงไป (generic คือไอ้ <...> ที่อยู่ข้างหลังนะ อ่านเรื่อง generic เพิ่มเติมได้ที่นี่)

foreach

Java มี for แบบพิเศษที่เอาไว้วนลูป array (และ List ด้วย) แบบไม่ต้องนับเอง

ปกตินั้นถ้าเรามี array อยู่ ถ้าอยากวนลูปทุกตัวจะเขียนประมาณนี้

int[] arr = {1,2,3,4,5};
for(i=0; i<arr.length; i++){
    System.out.println(arr[i]);
}

//หรือแบบนี้

List<Integer> arr = new ArrayList<>();
for(i=0; i<arr.size(); i++){
    System.out.println(arr.get(i));
}

เราต้องเขียนเงื่อนไขและสร้างตัวนับ i ด้วยตัวเอง แต่ถ้าเราเขียนแบบ foreach จะเหลือแค่นี้

int[] arr = {1,2,3,4,5};
for(int e : arr){
    System.out.println(e);
}

//หรือแบบนี้

List<Integer> arr = new ArrayList<>();
for(int e : arr){
    System.out.println(e);
}

วิธีการใช้คือให้เราสร้างตัวแปรสำหรับเป็นตัวแทนข้อมูลขึ้นมา 1 ตัว แช่นในตัวอย่างเป็นการวนลูป array ของ int เลยสร้าง int e ขึ้นมา (ใช้ชื่ออะไรก็ได้นะ) ตัวแปรตัวนี้จะมีค่าเท่ากับการใช้ arr[i] แบบการวนลูป array ปกติ แค่เราไม่ต้องเขียนเงื่อนไขนับด้วยเองเท่านั้น การวนลูปแบบนี้เรียกว่าการวนแบบ foreach

802 Total Views 3 Views Today
Ta

Ta

สิ่งมีชีวิตตัวอ้วนๆ กลมๆ เคลื่อนที่ไปไหนโดยการกลิ้ง .. ถนัดการดำรงชีวิตโดยไม่โดนแสงแดด
ปัจจุบันเป็น Senior Software Engineer อยู่ที่ Centrillion Technology
งานอดิเรกคือ เขียนโปรแกรม อ่านหนังสือ เขียนบทความ วาดรูป และ เล่นแบดมินตัน

You may also like...

1 Response

  1. 5 เมษายน 2020

    […] คราวที่แล้วเขียนบทความรวม syntax พื้นฐานของภาษา Java ไปแล้ว คราวนี้จะมาเขียนของภาษายอดนิยมอีกตัวหนึ่งคือ Python กันบ้าง […]

ใส่ความเห็น

อีเมลของคุณจะไม่แสดงให้คนอื่นเห็น ช่องที่ต้องการถูกทำเครื่องหมาย *