June 27, 2018

Command Pattern



Behavior Pattern အမျိုးအစားတစ်မျိုးဖြစ်ပါတယ်။ အရင်ဆုံး Command Pattern ကို အသုံးပြုပြီး ဖြေရှင်းနိုင်တဲ့ ပြဿနာတွေကနေ စပြီး လေ့လာကြည့်ကြရအောင်။

Problem

ကျွန်တော်တို့ GUI Application တစ်ခုကို ရေးသားနေပြီး၊ Button Class တစ်ခုကို ရေးသားဖို့လိုအပ်လာပြီဆိုကြပါစို့။ Button ကို Tools Bar မှာလဲ ထားမယ်။ ပြီးတော့ View Form တွေမှာလဲ ထားနိုင်မယ်။ ပြီးတော့ အဲ့ဒီ Button Object ကို အသုံးပြုပြီး copy လုပ်တာတွေ၊ cut လုပ်တာတွေပြီးတော့ Form ကို Submit လုပ်တာတွေ လုပ်ဆောင် စေလိုတဲ့အခါလဲရှိပါမယ်။ ကဲ ဒါဖြင့်ရင် ဘယ်လို ရေးကြမလဲ။ Button Class ထဲမှာ Label အတွက် Text လိုမယ်၊ ပြီးတော့ Icon ပါနိုင်တယ်၊ ပြီးတော့ အဲ့ဒီ Button ကို UI မှာ ဘယ်လို ဖေါ်ပြမလဲ ဆိုတဲ့ draw() ဆိုတဲ့ behavior လဲပါမယ်။ ပြီးတော့ အဲ့ဒီ button ကို Click နှိပ်ရင် ဘာလုပ်မယ်ဆိုတာကို ဖေါ်ပြနိုင်ရမယ်။ ပြီးတော့ Button အပေါ်မှာမူတည်ပြီး အလုပ်လုပ်ပုံတွေတူမှာမဟုတ်ဘူး။ ဒီနေရာမှာ စဉ်းစားမိမှာကတော့ “အမျိုးအစားတော့တူတယ်၊ အလုပ်လုပ်ပုံချင်းတော့မတူဘူး။ Polymorphism ကို သုံးနိုင်တယ်။”​ ဆိုတာပဲ ဖြစ်ပါမယ်။

ရေးကြည့်ကြရအောင်။ Polymorphism အတွက် Abstract Class ကို ရေးထားမယ်။ ပြီးမှ ဘုံအနေနဲ့ အသုံးပြုချင်တာတွေကို အဲ့ဒီ Class ထဲမှာရေးထားပြီး အလုပ်လုပ်ပုံချင်းမတူတဲ့ click() ကိုတော့ Abstract Method နဲ့ ရေးထားမယ်ပေါ့။ အိုကေ ဒါဆိုရင် သင်တန်းမှာ သင်ထားတဲ့ Abstraction ကော Polymorphism ပါ အဆင်ပြေသွားပြီ။
public abstract class Button {
 
 private String text;
 private String icon;

 public Button(String text, String icon) {

  this.text = text;
  this.icon = icon;
 }

 public void draw() {
  System.out.println("Button Text is " + text);
  System.out.println("Button Icon is " + icon);
 }

 public abstract void click();
}
Cut Button လိုချင်တယ်ဆိုရင်လဲ Button ကို Inheritance လုပ်ပြီး click method ကို Override လုပ်လိုက်ရုံပဲ။ ပြီးတော့ Past လဲ လာထား။ ဒါ့ထက်မကလို့ Send Button ဆိုလဲ နောက် Button Class တစ်ခုကို ရေးလိုက်ရုံပဲ။ နိပ်ဟ OOP! ဆိုပြီးတွေးပျော်နေမယ်ထင်ပါတယ်။
ဒါပေမဲ့ ဒီနေရာမှာ တွေးစရာ ၂ ချက်ရှိပါတယ်။ မတူညီတဲ့ Behavior တစ်ခုလိုတိုင်း Button တစ်ခုကို ရေးနေရရင် Button Class တွေအများကြီးဖြစ်လာနိုင်တယ်ဆိုတဲ့ အချက်နဲ့ တကယ်လို့ Cut Behavior ကို CTL + X နဲ့ ရေးချင်လာရင်လဲ Cut Behavior ကို Button မှာကော Short Cut Action မှာပါရေးနေရပါမယ်။ Don’t Repeat Yourself (DRY) ကို အကြီးအကျယ်ချိုးဖေါက်နေမိပါပြီ။ အဲ့ဒီလို ပြဿနာမျိုးကို Command Pattern ကို အသုံးပြုပြီး ဖြေရှင်းနိုင်မှာ ဖြစ်ပါတယ်။

Command Pattern

Command Pattern မှာ Command, Invoker, Receiver နဲ့ Client တို့ပါဝင်ကြပါတယ်။

ဒီနေရာမှာ အဓိက အရေးပါတာကတော့ Command Interface ပဲဖြစ်ပါတယ်။ ဘာလုပ်မှာလဲ ဆိုတာတဲ့ Business Logic ကို Wrap လုပ်ပေးနိုင်ပါတယ်။ အဲ့ဒီ Command Object ကို Invoker ကပိုင်ဆိုင်ပြီး ခိုင်းလိုတဲ့ အရာတွေကို အဲ့ဒီ Object ကို ခိုင်းမှာ ဖြစ်ပါတယ်။


အထက်ပါပုံအတိုင်း Client ကနေ Invoker ကို executeCommand() လို့ခိုင်းလိုက်တာနဲ့ Invoker ကနေ သူ့မှာရှိတဲ့ Command Object ရဲ့ execute() ကိုလုပ်ဆောင် ခိုင်းမှာ ဖြစ်ပါတယ်။ Command1 ကတော့ Command Interface ကို Implement လုပ်ထားတဲ့ Class ဖြစ်ပြီး Business Logic တွေကို ဆောင်ရွက်နိုင်တဲ့ Receiver Object နဲ့ အဲ့ဒီ Receiver Object ကို အလုပ်ခိုင်းရာမှာ အသုံးပြုနိုင်တဲ့ Parameter တွေကို State အနေနဲ့ ပိုင်ဆိုင်ထားပါတယ်။ တကယ်လို Invoker ကနေ execute() လို့ လုပ်ခိုင်းလိုက်ပြီဆိုတာနဲ့ Receiver Object ကို ပြန်ခိုင်းလိုက်ရုံပါပဲ။
How to Fix?


အိုကေ ဒီလို Pattern ကို အသုံးပြုလိုက်တာနဲ့ အထက်က ပြဿနာကို ဘယ်လို ဖြေရှင်းနိုင်မလဲ ဆိုတာကို လက်တွေ့ ကုဒ်လေးတွေရေးကြည့်ပြီး လက်တွေ့ ဖြေရှင်းကြည့်ကြရအောင်။

public interface Command {
 void execute();
}

ဒါကတော့ Button ကို click() နှိပ်တဲ့အခါမှာ အလုပ်လုပ်ပေးစေချင်တဲ့ Interface ဖြစ်တယ်။ ပြီးတော့ method တစ်ခုသာပါဝင်ပြီး Command အမျိုးမျိုးအတွက် တူညီတဲ့ ခေါ်ဆိုမှု့ကို သတ်မှတ်ပေးနိုင်ပါတယ်။ Copy Command ကို ရေးချင်တယ်ဆိုရင်လဲ ဒီ Interface ကို Inpmement လုပ်ပြီးရေးရမယ်။ Cut ဆိုရင်လဲ ဒီ Interface ကို Implement လုပ်ရမှာပါပဲ။ ဒါမှသာ Button Class ထဲကနေ ခိုင်းချင်တဲ့ အရာရှိရင် execute() method တစ်ခုကိုပဲ ခေါ်ပြီး ခိုင်းလိုက်ရုံပါပဲ။

public class Button {
 
 private String text;
 private Command command;

 public (String t, Command c) {
  text = t;
  command = c;
 }

 public void click() {
  command.execute();
 }
}

Button Class ကတော့ Command Pattern ရဲ့ Invoker နေရာမှာ တာဝန်ကျပါတယ်။ သူ့ဆီမှာတော့ Command ကို လုပ်ဆောင်ပေးနိုင်တဲ့ Command Object တစ်ခုရှိပါတယ်။ ဘာတွေလုပ်နိုင်တယ်ဆိုတာကိုတော့ Commannd Interface ရဲ့ နောက်ကွယ်မှာ Encapsulate လုပ်ပေးထားနိုင်ပါတယ်။ Button အနေနဲ့ သိစရာလိုတာကတော့ click() နှိပ်လာရင် Command ရဲ့ execute() ကို ခေါ်လိုက်ရုံပဲ ဆိုတာပါပဲ။

public class Editor {

 private String selectedText;
 
 public String getSelectedText() {
  return selectedText;
 }

 public void paste(String text) {
  selectedText = text;
 }
}

public class Clipboard {
 
 private String memo;

 public void save(String text) {
  memo = text;
 }

 public void getSaveData() {
  return memo;
 }
}

အထက်ပါ Editor နဲ့ ClickBoard တွေကတော့ Command Pattern ရဲ့ Receiver Class တွေပဲ ဖြစ်ကြပါတယ်။ သူတို့တွေကတော့ သက်ဆိုင်ရာ Command တွေကနေ ဆက်သွယ် အသုံးပြုကြမှာပါ။
public class CopyCommand implements Command {
 
 private Editor editor;
 private Clipboard clipboard;

 public CopyCommand(Editor e, Clipboard c) {
  editor = e;
  clipboard = c;
 }

 public void execute() {
  String text = editor.getSelectedText();
  clipboard.save(text);
 }
} 

ဆိုကြပါစို့။ Copy Command ကို ရေးပြီဆိုရင် Editor ထဲက Select လုပ်ထားတဲ့ Text ကို ယူပြီး Clipboard မှာ သွားပြီး Save လုပ်ပါမယ်။ တကယ်လို့ Clear Command မှာဆိုရင်လဲ Clipboard ထဲက memo ကို ဖျက်ပစ်ပါမယ်။ သက်ဆိုင်ရာ Command Concrete Object တွေက ဘယ် Receiver ကို သုံးမယ်ဆိုတာကို သိစရာလိုပါတယ်။

public class Application {
 
 private Button clearButton;
 private Button copyButton;

 public void init() {

  Editor editor = new Editor();
  Clipboard clipboard = new Clipboard();

  Command clear = new ClearCommand(clipboard);
  Command copy = new CopyCommand(editor, clipboard);

  clearButton = new Button("Clear", clear);
  copyButton = new Button("Copy", copy);

 }
}

အထက်ပါ Application Class ဟာ Command Pattern ရဲ့ Client နေရာမှာရှိပါတယ်။ Application Class ရဲ့ init method ထဲမှာ Receiver ဖြစ်တဲ့ Editor နဲ့ Clipboard Object တွေကို တည်ဆောက်ပြီး သက်ဆိုင်ရာ Command Object တွေကို တည်ဆောက်ပါတယ်။

Clear Button အတွက်ဆိုရင် ClearCommand Object ကိုပေးပြီတည်ဆောက်ပြီး Copy Button အတွက်ဆိုရင် CopyCommand Object တို့ကို ပေးပါတယ်။ ဒီလိုရေးသားထားတဲ့ အတွက် Short Cut တွေ Menu Item တွေမှာ အဲ့ဒီ Command တွေကို အသုံးပြုစေလိုတယ်ဆိုရင်လဲ ရေးထားပြီးသား Command တွေကို ပြန်ပြီး အသုံးပြုနိုင်ပါတယ်။ Button ထဲမှာသာ Business Logic တွေကို တိုက်ရိုက်ရေးသားထားခဲ့မယ်ဆိုရင် အဲ့ဒီ Logic တွေကို လိုချင်တဲ့ နေရာတိုင်းမှာ ရေးသားနေရမှာ ဖြစ်ပါတယ်။ အခုလို Command အနေနဲ့ ရေးသားလိုက်တဲ့အတွက် အဲ့ဒီ Command တွေကို အခြားနေရာမှာလဲ ပြန်ပြီး အသုံးချနိုင်တာ ဖြစ်ပါတယ်။ တဖန် Command ကိုသာ အသုံးမပြုထားခဲ့ရင် Invoker ဖြစ်တဲ့ Button က Receiver ဖြစ်တဲ့ Editor တို့ Clipboard တို့ကို တိုက်ရိုက်မှီခို အသုံးပြုနေရမှာ ဖြစ်တယ်။ ဤကဲ့သို့ Invoker နဲ့ Receiver တို့ကို Decouple လုပ်ပေးခြင်းအားဖြင့် လွတ်လွတ်လပ်လပ် ပြန်လည်အသုံးပြုနိုင်တဲ့ Component တွေကို ရေးသားစေနိုင်မှာ ဖြစ်ပါတယ်။

Because of Pattern

  • Operation တစ်ခုကို စတင်စေတဲ့ Object နဲ့ တကယ်အလုပ်လုပ်မည့် Object တို့ကို Decouple လုပ်ပေးနိုင်ပါတယ်
  • Open / Close Principle နဲ့ Single Responsibility Principle တို့ကို လိုက်နာစေပြီး Code တွေကို ပြန်လည် အသုံးချနိုင်အောင် ဆောင်ရွက်ပေးနိုင်ပါတယ်
  • ရှုပ်ထွေးတဲ့ လုပ်ဆောင်မှုတွေကို ရိုးရှင်းစွာ လုပ်ဆောင် စေနိုင်ပါတယ်

October 10, 2017

JShell - Quick Start

ဒီအခန်းမှာတော့ JShell ကို ဘယ်လို စပြီး အသုံးပြုသွားမလဲဆိုတာကို လေ့လာသွားကြပါမယ်။ JShell ရဲ့ အခြေခံအသုံးပြုပုံတွေ၊ ပြီးတော့ Help ကို အသုံးပြုပုံတွေနဲ့ Shortcuts တွေအကြောင်းကို ဖေါ်ပြသွားပါမယ်။


Basic Usages


JShell ဟာ JDK ရဲ့ Tool တစ်ခုဖြစ်တဲ့ အတွက် jdk 9 ကို Install လုပ်လိုက်တာနဲ့ JDK Install Directory ရဲ့ bin အောက်မှာ ပါဝင်ပြီး ဖြစ်နေပါတယ်။ JDK 9 ရဲ့ bin directory ကို Environment Variable Path အောက်မှာ ဖြည့်စွက်လိုက်တာနဲ့ JShell ကို စပြီး အသုံးပြုလို့ရမှာ ဖြစ်ပါတယ်။


Start JShell


JShell ကို စတင် အသုံးပြုဖို့ Terminal (Console) ကနေ jshell command ကို ရိုက်ထည့်ပြီး tools ကို invoke လုပ်ရမှာ ဖြစ်ပါတယ်။ ဒီလို Invoke လုပ်နိုင်အောင်လဲ jshell.exe file ရှိတဲ့ jdk installed Directory အောက်က bin directory ကို PATH environment variable ထဲမှာ သွားရောက်ဖြည့်စွက်ထားရင် ရပါပြီ။

PATH ထဲမှာ လမ်းကြောင်းထည့်ပုံကိုတော့ JDK Installation မှာကထဲက ဖေါ်ပြထားပြီး ဖြစ်တဲ့ အတွက် အဲ့ဒီအတိုင်း ဆောင်ရွက်လိုက်မယ်ဆိုရင်ရမှာ ဖြစ်ပါတယ်။

Mins-MacBook-Pro:~ minlwin$ jshell
|  Welcome to JShell -- Version 9
|  For an introduction type: /help intro

jshell> 

ဒီအတိုင်း jshell> လို့ပေါ်လာခဲ့ရင် JShell ရဲ့ REPL Mode ကို စတင် အသုံးပြုလို့ရမှာ ဖြစ်ပါတယ်။


Writing Variable


ဆက်လက်ပြီး Variable တွေကို အသုံးပြုကြည့်ပါမယ်။ အရင်ဆုံး Variable တွေကို Declare လုပ်ကြည့်ပါမယ်။ ပြီးရင် သတ်မှတ်ထားတဲ့ Variable ကို တန်ဖိုးတစ်ခု Assign လုပ်ကြည့်ပါမယ်။ ပြီးတော့ အဲ့ဒီ Variable ကို ပြန်ပြီး Access လုပ်ပြီး အသုံးပြုကြည့်ပါမယ်။

jshell> String school
school ==> null

jshell> school = "Java Developer Class"
school ==> "Java Developer Class"

jshell> System.out.println(school)
Java Developer Class


JShell ကို အသုံးပြုတဲ့ နေရာမှာ Statement တစ်ကြောင်းရေးပြီးတိုင်းမှာလဲ Semi Comma ကို ပိတ်စရာမလိုအပ်ပါဘူး။ Variable ကို Declare လုပ်တဲ့နေရာမှာလဲ အထက်ပါအတိုင်း String school လို့ ရေးသားလိုက်တာနဲ့ school ဆိုတဲ့ String Type Variable ကို Default တန်ဖိုးဖြစ်တဲ့ null နဲ့ သတ်မှတ်ပေးသွားမှာ ဖြစ်ပါတယ်။

တဖန် ရေးသားထားတဲ့ Variable နေရာကို တန်ဖိုးတစ်ခုခု Assign လုပ်လိုချင်တဲ့ အခါမှာလဲ school = “Java Developer Class” ဆိုပြီး ရေးသားနိုင်ပါတယ်။ အဲ့ဒီလို ရေးသားလိုက်တဲ့ အခါမှာလဲ တန်ဖိုးပြောင်းသွားတယ်ဆိုတာကို school ==> “Java Developer Class” ဆိုပြီး ဖေါ်ပြပေးမှာ ဖြစ်ပါတယ်။

အထက်ပါအတိုင်း မိမိရေးသားထားတဲ့ Variable ကို Method တစ်ခုခုရဲ့ Argument အနေနဲ့ အသုံးပြုလိုတဲ့ အခါမှာလဲ System.out.println(school) ဆိုပြီးရေးသား အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။


Writing Enum


Enum ဆိုတာဟာ Java 5 မှာ စတင်ပါဝင်ခဲ့တဲ့ Static Class အမျိုးအစားတစ်ခုဖြစ်ပါတယ်။ ဒီတစ်ခေါက်တော့ JShell ကို အသုံးပြုပြီး Enum တစ်ခုကို Declare လုပ်ကြည့်ပြီး ရေးသားထားတဲ့ Enum ကို ပြန်ပြီး အသုံးပြုကြည့်ပါမယ်။



Enum တွေကို Declare လုပ်မယ်ဆိုရင်လဲ အထက်ပါအတိုင်း enum Course {} ဆိုပြီး ရေးသားရပါမယ်။ တဖန် enum ရဲ့ Member တွေကိုလဲ {} အတွင်းမှာ ရေးသားနိုင်ပါတယ်။ အထက်ပါအတိုင်း ရေးသားပြီး Enter Key ကို Press လုပ်လိုက်ရင် create enum Course ဆိုပြီး enum တစ်ခုကို Create လုပ်ပြီးကြောင်းကို ဖေါ်ပြပေးမှာ ဖြစ်ပါတယ်။

တဖန် မိမိရေးသားထားတဲ့ Enum ကိုလဲ ပြန်ပြီး Course.values() ဆိုပြီး ပြန်ပြီး အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။ အထက်ဖေါ်ပြပါထဲကလို Line အများကြီးကို အသုံးပြုပြီး ရေးသားရတဲ့ for statememt လို နေရာမျိုးမှာလဲ Line တစ်ကြောင်းဆုံးရင် Enter ကို နှိပ်ပြီး ဆက်တိုက်ရေးသားနိုင်မှာ ဖြစ်ပါတယ်။

Line အများကြီးပါတဲ့ Statement တွေဆိုရင်တော့ Statement ပြီးဆုံးသွားမှသာ ရေးသားထားတဲ့ Code တွေကို Evaluate လုပ်ပြီး အဖြေကို ပြန်ပြပေးမှာ ဖြစ်ပါတယ်။ ဒါ့ကြောင့် for statement ရဲ့ အဆုံးဖြစ်တဲ့ } ကို ရိုက်နှိပ်ပြီးတော့မှသာ အဖြေတွေကို ပြန်ပြပေးနေတာဖြစ်ပါတယ်။


Writing Methods


ဆက်လက်ပြီး JShell နဲ့ Method တွေကို ဘယ်လိုရေးမယ်ဆိုတာကို လေ့လာသွားကြပါမယ်။ Method တွေကို ရေးတဲ့ နေရာမှာ Method တွေကို ဘယ်လို ရေးမလဲ၊ ပြီးတော့ Method တွေကို ဘယ်လို Invoke လုပ်မလဲဆိုတာကို လေ့လာသွားပါမယ်။ တဖန် Method တွေလို့ ပြောတဲ့ နေရာမှာလဲ Argument မပါတဲ့ Method တွေ ပြီးတော့ Argument ပါတဲ့ Method တွေ၊ ပြီးတော့ Return ပြန်တဲ့ Method တွေ၊ Return မပြန်တဲ့ Method တွေ ဆိုပြီး ရှိပါတယ်။

Method တွေကို Invoke လုပ်တဲ့ နေရာမှာလဲ Return ပြန်တဲ့ Method တွေဆိုရင် Return ပြန်လာတဲ့ တန်ဖိုးတွေကို Variable တစ်ခုမှာ ဘယ်လို Assign လုပ်မယ်ဆိုတာကို လက်တွေ့ ရေးသားလေ့လာကြည့်ပါမယ်။



အထက်ပါ နမူနာထဲမှာတော့ sayHello() ဆိုတဲ့ Argument လဲမပါ Return လဲ မပြန်တဲ့ Method တစ်ခုကို ရေးသားပါတယ်။ အထက်ပါအတိုင်း Method ကို ရေးသားပြီးတဲ့ အခါမှာ created method sayHello() ဆိုပြီး Method ကို ရေးသားပြီးပြီဖြစ်ကြောင်းကို ပြန်ပြီး ဖေါ်ပြပါတယ်။

ထိုနောက် sayHello() ဆိုပြီး ရေးသားခဲ့တဲ့ sayHello() method ကို Invoke လုပ်လိုက်တဲ့ အခါမှာ အဲ့ဒီ Method ထဲမှာ ရေးသားခဲ့တဲ့ လုပ်ဆောင်ချက်တွေကို ပြန်ပြီး ဖေါ်ပြပေးနိုင်မှာ ဖြစ်ပါတယ်။



အထက်ပါနမူနာကတော့ String name ဆိုတဲ့ Argument တစ်ခုကို ယူတဲ့ sayHello Method ကို ရေးသားကြည့်တာပါ။ အရှေ့မှာ ရေးခဲ့တဲ့ Method နဲ့ Method Name ချင်းတူပါတယ်။ ဒါပေမဲ့ Argument List တွေမတူကြပါဘူး။ ပထမ sayHello မှာက Argument မပါပေမဲ့ ဒုတိယ တစ်ကြိမ်ရေးတဲ့ sayHello Method မှာတော့ String တစ်ခုကို Argument အနေနဲ့ ရယူပါတယ်။

Java Compiler က ဒီလိုမျိုး နာမည်တူပြီး Argument မတူတဲ့ Method တွေကို မတူညီတဲ့ Method တွေလို့ သတ်မှတ်ပါတယ်။ ဒါ့ကြောင့် sayHello() ဆိုပြီး ဘာ Parameter မှ မပါပဲ ရေးသားခဲ့ရင် Argument မပါတဲ့ sayHello() method ကို Invoke လုပ်မှာ ဖြစ်ပြီး၊ sayHello(school) ဆိုပြီး String Type Variable school ကို Parameter အနေနဲ့ ပေးပြီး invoke လုပ်တဲ့ အခါမှာ Argument ပါတဲ့ sayHello(String name) method ကို Invoke လုပ်မှာ ဖြစ်ပါတယ်။

ဒီလိုမျိုး method name တူပြီး Argument List မတူတဲ့ Method တွေကို ရေးသားခြင်းကို Method Overload လုပ်တယ်လို့ ခေါ်ပါတယ်။



အထက်နမူနာမှာတော့ Argument နှစ်ခုကိုရယူပြီး၊ return ပြန်တဲ့ add(int a, int b) method ကို ရေးသားထားတာဖြစ်ပါတယ်။ အဲ့ဒီ Method ထဲမှာတော့ int variable နှစ်ခုကို Argument အနေနဲ့ ရယူပြီး a နဲ့ b ကို ပေါင်းပြီး ရလဒ်ကို Return ပြန်ပေးနေပါတယ်။ တဖန် add method ကို invoke လုပ်တဲ့ နေရာမှာလဲ int result = add(10, 3) ဆိုပြီး 10 နဲ့ 3 ကို a နဲ့ b နေရာကို ပေးပြီး ခေါ်ပါတယ်။ ရလာတဲ့ တန်ဖိုးကို int variable result မှာ အစားထိုးပါတယ်။ အဲ့ဒီ Statement ကို Evaluate လုပ်ပြီးတဲ့ အခါမှာ result ==> 13 ဆိုပြီး result ရဲ့ တန်ဖိုးဟာ 13 ဖြစ်သွားပါပြီဆိုပြီး ဖေါ်ပြနေတာ ဖြစ်ပါတယ်။


Writing Class


ကျွန်တော်တို့ ဒီတစ်ခေါက်တော့ Class တွေကို ရေးကြည့်ပါမယ်။ Class ထဲမှာ Instance Variable တွေပါမယ်။ ပြီးတော့ Method တွေ၊ ပြီးတော့ Constructor ပါပါမယ်။ ပြီးတော့ ရေးထားတဲ့ Class ကနေ Object တစ်ခုကို တည်ဆောက်ကြည့်ပြီး အဲ့ဒီ Object ရဲ့ Method တွေကို Invoke လုပ်ကြည့်ပါမယ်။



အထက်ပါ နမူနာထဲမှာတော့ name ဆိုတဲ့ Instance Variable တစ်ခုရယ်၊ Argument တစ်ခုယူတဲ့ Constructor တစ်ခုရယ်၊ greet() ဆိုတဲ့ Method တစ်ခုပါတဲ့ Student Class တစ်ခုကို ရေးသားထားပါတယ်။ အဲ့ဒီ အတိုင်းရေးပြီးရင် created class Student ဆိုပြီး Student Class ကို ရေးသားပြီးပြီဖြစ်ကြောင်း ဖေါ်ပြမှာ ဖြစ်ပါတယ်။

ဒါဆိုရင် ဒီ Student Class နဲ့ ဘာလုပ်လို့ရမလဲ။ Student အမျိုးအစား Object တွေကို တည်ဆောက်လို့ရမှာ ဖြစ်ပြီး၊ အဲ့ဒီ Student Object ကနေ greet() ဆိုတဲ့ လုပ်ဆောင်မှု့တွေကို ဆောင်ရွက်ပေးနိုင်မှာ ဖြစ်တယ်။



အထက်ပါ ကုဒ်တွေထဲမှာတော့ အရင်ဆုံး aung ဆိုတဲ့ Student Type Variable နေရာကို new Student(“Aung Aung”) ဆိုပြီး Object တစ်ခုကို တည်ဆောက်ပြီး အစားထိုးပါတယ်။ ဒါ့ကြောင့် aung ဆိုတဲ့ Variable နေရာရဲ့ တန်ဖိုးဟာ Student Object က အစားထိုးသွားပါပြီဆိုပြီး ဖေါ်ပြပေးနိုင်တာ ဖြစ်ပါတယ်။

အဲ့ဒီနောက်မှာ aung.greet() ဆိုပြီး Student Object ရဲ့ greet() method ကို လှမ်းပြီး Invoke လုပ်ခိုင်းပါတယ်။ ဒါ့ကြောင့် Hello! My name is Aung Aung ဆိုပြီး greet() method ထဲက လုပ်ဆောင်မှု့တွေကို ဆောင်ရွက်ပေးနိုင်တာ ဖြစ်ပါတယ်။


Writing Interface


ဒီနေရာမှာတော့ JShell ကို အသုံးပြုပြီး Interface ကို ရေးသားကြည့်ပါမယ်။ Interface ဆိုတာက 100% Abstract Class တွေဖြစ်ပြီး Object တစ်ခုကနေ အသုံးပြုနိုင်တဲ့ Method တွေကို စုစည်းဖေါ်ပြပေးနိုင်ပါတယ်။ Interface ကို Object တွေရဲ့ Type အနေနဲ့ အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။

ဒီနမူနာထဲမှာတော့ Interface တစ်ခုကို အရင် ရေးသားကြည့်ပါမယ်။ ထိုနောက် Interface ကို Implement လုပ်ထားတဲ့ Class တစ်ခုကို ရေးသားပါမယ်။ ပြီးကာမှ Interface Object ကို Implement လုပ်ထားတဲ့ Class ကနေ တဆင့် Object ဆောက်ကြည့်မယ်။ ဆက်လက်ပြီး Anonymous Class ကနေလဲ Object ဆောက်ကြည့်ပါမယ်။ နောက်ထပ် Lambda Expression ကို အသုံးပြုပြီးလဲ Interface Object ကို တည်ဆောက် အသုံးပြုကြည့်ပါမယ်။



အထက်ပါ နမူနာမှာတော့ add method တစ်ခုပါတဲ့ Addable Interface တစ်ခုကို တည်ဆောက်လိုက်ပါတယ်။ Abstract Method တစ်ခုသာပါတဲ့ Interface ဖြစ်တဲ့ အတွက် Addable ကို Lambda Expression နဲ့လဲ အစားထိုးရေးသားနိုင်မှာ ဖြစ်ပါတယ်။

ဆက်လက်ပြီး Adder Interface ရဲ့ Object တွေကို ပုံစံအမျိုးမျိုးနဲ့ တည်ဆောက် ရေးသားသွားပါမယ်။



ဒီနေရာမှာတော့ ရှေ့က ရေးခဲ့တဲ့ Addable Interface ကို implement လုပ်ထားတဲ့ Adder Class ကို အရင်ဆုံး ရေးသားပါတယ်။ Inerface ကို Implement လုပ်လိုက်ပြီဆိုတာနဲ့ Interface ထဲမှာ ရေးသားထားတဲ့ abstract method ဖြစ်တဲ့ add method ကို Override လုပ်ပြီးရေးသားရပါတော့မယ်။

Addable ထဲမှာ ရေးသားထားတဲ့ add method ကို ကြည့်ပါ။ public လဲ​မရေးထားသလို abstract လဲ မရေးထားပါဘူး။ ပြီးတော့ method body လဲမပါတဲ့ အတွက် Abstract Method ဖြစ်ပါတယ်။ Interface တွေဟာ default abstract နဲ့ public ဖြစ်တဲ့ အတွက် abstract နဲ့ public ကို မရေးထားလဲ public abstract ဖြစ်နေပါတယ်။

ဒါပေမဲ့ Implement လုပ်တဲ့ Adder Class ထဲမှာတော့ public ကိုမဖြစ်မနေ ရေးသားရမှာ ဖြစ်ပြီး၊ Method Body ကိုလဲ ရေးသားရမှာ ဖြစ်ပါတယ်။

ဆက်လက်ပြီး Addable Type ဖြစ်တဲ့ a1 နေရာကို Adder Class ကနေ တဆင် new Adder() ဆိုပြီး Object ကို ဆောက်ပြီး အစားထိုးပါတယ်။ Interface Type Variable တွေကနေ အဲ့ဒီ Interface ကို Implement လုပ်ပြီး ရေးသားထားတဲ့ Class  ရဲ့ Object တွေကို Reference လုပ်နိုင်တဲ့ အတွက် ဖြစ်ပါတယ်။

ဒီနေရာမှာ a1 ရဲ့ Type ဟာ Addable Interface ဖြစ်ပေမဲ့ နောက်ကွယ်မှာ ရှိတဲ့ Object ကတော့ Adder Class ရဲ့ Object ဖြစ်ပါတယ်။ ဆက်လက်ပြီး d1 နေရာကို a1.add(10, 11) ကို Invoke လုပ်ပြီး ရလာတဲ့ ရလဒ်နဲ့ အစားထိုးပါတယ်။ a1 ရဲ့ add(10,11) ကို ခေါ်လိုက်တာဖြစ်ပေမဲ့ တကယ် အလုပ်လုပ်သွားတာကတော့ Adder Class ကနေ တည်ဆောက်ထားတဲ့ Object ရဲ့ add method က အလုပ်လုပ်သွားတာဖြစ်ပါတယ်။ ဒါ့ကြောင့် d1 ရဲ့ တန်ဖိုးဟာ 21.0 ဖြစ်သွားပါပြီလို့ ပြန်ပြောပြနိုင်တာဖြစ်ပါတယ်။

ဒီတစ်ခေါက်ကတော့ Addable Interface ရဲ့ Object ကို Anonymous Class ကနေ တည်ဆောက် အသုံးပြုကြည့်ပါမယ်။



Interface Object တွေကို Anonymous Class တွေကနေလဲ ရေးသား နိုင်ပါတယ်။ အထက်ပါအတိုင်း new Addable() ဆိုပြီး Interface ကိုခေါ်ပြီး လိုအပ်တဲ့ Abstract တိုက်ရိုက်ဒီနေရာမှာပဲ Implement လုပ်ပြီး ရေးသားရပါတယ်။ ဒီနေရာမှာကြည့်မယ်ဆိုရင် a2 ရဲ့ တန်ဖိုးနေရာမှာ Class Name မပါပဲ ရှိနေတာကို တွေ့ရပါမယ်။ ဒါ့ကြောင့်ဒီလို Class မျိုးကို Anonymous Class လို့ခေါ်တာဖြစ်ပါတယ်။

a2.add(10, 11) လို့ Invoke လုပ်လိုက်ရင်လဲ တန်ဖိုးဟာ 21 ဖြစ်တယ်လို့ ဖေါ်ပြနိုင်တာဖြစ်ပါတယ်။



Addable Interface ဟာ Single Abstract Method Interface ဖြစ်တဲ့ အတွက် Lambda Expression  နဲ့လဲ ရေးသားနိုင်ပါတယ်။ ဒါ့ကြောင့် a3 Variable ကို (a,b) -> a + b ဆိုပြီး Lambda Expression နဲ့ အစားထိုးရေးသားနိုင်ခြင်း ဖြစ်ပါတယ်။

a3.add(10, 11) လို့ Invoke လုပ်လိုက်တဲ့အခါမှာလဲ တန်ဖိုးဟာ 21 ဖြစ်တယ်လို့ ဖေါ်ပြနိုင်တာဖြစ်ပါတယ်။



Lambda Expression နဲ့ ရေးသားလို့ရတဲ့ နေရာတိုင်းကို Method Reference နဲ့လဲ ရေးသားနိုင်ပါတယ်။ အထက်ပါ နမူနာကတော့ Method Reference ကို အသုံးပြုပြီး ရေးသားထားတာ ဖြစ်ပါတယ်။ a4 ရဲ့ add method ကို Invoke လုပ်ကြည့်တဲ့ အခါမှာလဲ မှန်ကန်တဲ့ အဖြေကို ဖေါ်ပြနိုင်တာကို တွေ့ရပါတယ်။


Using Helps & Commands


JShell ကို အသုံးပြုရင် Java Statement တွေကို Evaluate လုပ်ပြီး အဖြေကို ချက်ချင်းပြန်ပြပေးနိုင်တယ်ဆိုတာကို လက်တွေ့ လေ့လာခဲ့ပါတယ်။ ဒါပေမဲ့ JShell ကို အသုံးပြုတဲ့ နေရာမှာ ပိုပြီး အသုံးပြုရတာလွယ်ကူစေရန် ပြင်ဆင်ထားတဲ့ Commands တွေနဲ့ Help Feature တွေလဲပါဝင်ပါတယ်။

JShell မှာ အသုံးပြုနိုင်တဲ့ Function တွေကိုတော့ /help command ကို အသုံးပြုပြီး သိရှိစေနိုင်ပါတယ်။



တဖန် မိမိရှာဖွေလိုတဲ့ Command ရဲ့ အသုံးပြုပုံကို သိရှိလိုပါက /? ကို အသုံးပြုပြီး ရှာဖွေနိုင်ပါတယ်။



Listing Sources


ဆက်လက်ပြီး အသုံးများတဲ့ Command တွေကို လေ့လာသွားပါမယ်။ အရင်ဆုံးမိမိ ရေးသားထားခဲ့တဲ့ Source တွေကို ပြန်ကြည့်လိုပါက /list ဆိုတဲ့ Command ကို အသုံးပြုနိုင်ပါတယ်။



JShell ပေါ်မှာ မိမိရေးသားခဲ့တဲ့ Source တွေကို ID နံပါတ်နဲ့ အတူ ဖေါ်ပြပေးနိုင်မှာ ဖြစ်ပါတယ်။



တဖန် ID ကို ရွေးချယ်ပြီး Execute လုပ်ချင်တယ်ဆိုရင်လဲ /[ID] ကို အသုံးပြုပြီး ရေးသားနိုင်မှာ ဖြစ်ပါတယ်။


Listing Variables


မိမိရေးသားခဲ့တဲ့ Variables တွေကို ပြန်ပြီး ကြည့်လိုပါက /vars Command ကို အသုံးပြုနိုင်ပါတယ်။




Listing Methods


မိမိရေးသားခဲ့တဲ့ Methods တွေကို ပြန်ပြီး ကြည့်လိုပါက /methods Command ကို အသုံးပြုနိုင်ပါတယ်။




Listing Types


တဖန် မိမိရေးသားခဲ့တဲ့ Type အဖြစ် အသုံးပြုနိုင်တဲ့ Class, Interface နဲ့ Enum တို့ကို /types Command ကို အသုံးပြုပြီး ပြန်ကြည့်နိုင်ပါတယ်။




Edit with External Editor


တဖန် မိမိရေးသားထားခဲ့တဲ့ Type တွေကို External Editor ကို အသုံးပြုပြီး ပြုပြင်လိုတဲ့အခါမျိုးမှာ /edit [source] Command ကို အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။

/edit sayHello လို့ ရေးလိုက်တာနဲ့ sayHello Method တွေကို External Editor နဲ့ ဖွင့်ပေးမှာ ဖြစ်ပါတယ်။



ပြင်ဆင်လိုတဲ့ နေရာကို ပြင်ဆင်ပြီး Accept ကို နှိပ်လိုက်ပါက Source ထဲမှာလဲ ပြောင်းပေးမှာ ဖြစ်ပါတယ်။



အထက်ပါအတိုင်း Hello JShell ကို We Love JDC လို့ပြောင်းပြီး Accept ကို နှိပ်လိုက်ပါမယ်။


အထက်ပါအတိုင်း sayHello() ဆိုပြီး Method ကို Invoke လုပ်လိုက်ရင် We Love JDC ဆိုပြီး ပြောင်းသွားမှာ ကိုတွေ့ရပါမယ်။



Listing Imports


JShell ထဲမှာ Default အတိုင်းပါဝင်တဲ့ imports တွေနဲ့ မိမိရေးသားထားတဲ့ imports တွေကို သိရှိလိုပါက /imports Commands ကို အသုံးပြုနိုင်ပါတယ်။


အထက်မှာ မြင်ရတဲ့ အတိုင်း JShell ထဲမှာ အသုံးများတဲ့ Package တွေကိုပဲ Import လုပ်ထားတာ ဖြစ်ပါတယ်။ ဒါဟာ Java 9 မှာ စတင်ပါဝင်လာတဲ့ Modularity ကို အသုံးပြုပြီး Basic Module ကိုပဲ Import လုပ်ထားတဲ့ အတွက် ဖြစ်ပါတယ်။ နောက်အခန်းများမှာ Modularity နဲ့ ပတ်သက်ပြီး ဆက်လက် ဖေါ်ပြသွားပါမယ်။


Date And Time API ထဲက Class တွေကို အသုံးပြုလိုပါက ဒီအတိုင်းရေးမယ်ဆိုရင် import ထဲမှာ မပါဝင်တဲ့ အတွက် Error တက်မှာ ဖြစ်ပါတယ်။ အသုံးပြုချင်တယ်ဆိုရင်တော့ မိမိကိုယ်တိုင် import လုပ်ပြီး ရေးသားရမှာ ဖြစ်ပါတယ်။


အထက်ပါအတိုင်း java.time.LocalDate ကို import လုပ်ပြီး ရေးသားပါမှ​ အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။



Shortcuts


Console Program အတော်များများမှာ Shortcuts တွေဟာ မရှိမဖြစ် လိုအပ်တဲ့ Feature တစ်ခုဖြစ်ပါတယ်။ JShell မှာလဲ Code Snippest တွေကို အလွယ်တကူရေးသားနိုင်အောင် Shortcuts တွေကို ပြင်ဆင်ထားတာကို တွေ့ရပါတယ်။

ဘယ်လို Shortcuts တွေကို အသုံးပြုနိုင်တယ်ဆိုတာကို သိရှိလိုပါက /help shortcuts ဒါမှမဟုတ် /? shortcuts command ကို အသုံးပြုနိုင်ပါတယ်။



အထက်ပါအတိုင်း JShell မှာ အသုံးပြုနိုင်တဲ့ Shortcuts ၃ မျိုးရှိတာကို တွေ့ရပါတယ်။ OS အပေါ်မှာ မူတည်ပြီး အသုံးပြုနိုင်တဲ့ Key တွေ မတူညီနိုင်တာ ဖြစ်တဲ့ အတွက် အသုံးပြုခါနီးမှာ /help နဲ့ ပြန်ကြည့်သင့်ပါတယ်။


Tab Key


Tab Key ဟာ အမတန် အသုံးဝင်တဲ့ Shortcut တစ်မျိုးဖြစ်ပါတယ်။ Code Complete လို Function မျိုးကိုလဲရရှိနိုင်သလို၊ method တွေရဲ့ Argument တွေနဲ့ Constructor Overload တွေကိုလဲ သိရှိနိုင်ပါတယ်။


အထက်ပါအတိုင်း String လို့ရိုက်ပြီး Tab Key ကို နှိပ်လိုက်ပါက String နဲ့စတဲ့ Type တွေကို ဖေါ်ပြပေးနိုင်တာကို တွေ့ရပါမယ်။


တဖန် String. ဆိုပြီး Tab Key ကို နှိပ်လိုက်ပါက String Class နဲ့ အသုံးပြုနိုင်တဲ့ static Method တွေကို ဖေါ်ပြပေးနိုင်တာကို တွေ့ရပါတယ်။


အထက်ပါအတိုင်း new StringBuffer( လို့ရိုက်ပြီး Tab Key ကို နှိပ်လိုက်ပြန်ရင်လဲ StringBuffer ရဲ့ Override လုပ်ထားတဲ့ Constructor တွေကို ဖေါ်ပြပေးနိုင်တာကို တွေ့ရပါမယ်။


တဖန် sb.append( ဆိုပြီး Tab Key ကိုနှိပ်ရင်လဲ Overload လုပ်ထားတဲ့ Method တွေကို ဖေါ်ပြပေးမှာ ဖြစ်တဲ့ အတွက် မိမိရေးသားလိုတဲ့ Method ကို အသုံးပြုပြီး အလွယ်တကူရေးသားနိုင်မှာ ဖြစ်ပါတယ်။



Shift + Tab + i


အထက်ပါ Short Cut ကတော့ import တွေကို အကူအညီပေးနိုင်တဲ့ Shortcut ဖြစ်ပါတယ်။



အထက်ပါအတိုင်း import မလုပ်ရသေးတဲ့ LocalDate ကို ရိုက်ပြီးတာနဲမ Shft + Tab ကို တွဲပြီး နှိပ်ပါမယ်။ လွှတ်ပြီးတာနဲ့ i ကို ရိုက်လိုက်ရင် ဘာရွေးမလဲဆိုတာကို ဖေါ်ပြပေးမှာ ဖြစ်ပါတယ်။

အဲ့ဒီနေရာမှာ 0 ကို ရွေးခဲ့ရင် ဘာမှ လုပ်မှာ မဟုတ်ပဲ၊ 1 ကို ရွေးခဲ့ရင် java.time.LocalDate ကို import လုပ်မှာ ဖြစ်ပါတယ်။ ထိုကဲ့သို့ Shortcut ကို အသုံးပြုပြီးလဲ import တွေကို အလွယ်တကူ အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။


Shift + Tab + v


Variable တွေကို Create လုပ်ပေးနိုင်တဲ့ Shortcut ဖြစ်ပါတယ်။ Expression တစ်ခုကို ရေးသားပြီး အဖြေကို ကြည့်ပြီးမှ Variable တစ်ခုမှာ Assign လုပ်လိုတဲ့ အခါမျိုးမှာ အသုံးဝင်ပါတယ်။



အထက်ပါအတိုင်း LocalDate.now() ရဲ့ ရလဒ်ကိုစစ်ပြီးတော့မှ Up Arrow ကို အသုံးပြုပြီး LocalDate.now() ကို ပြန်ပြီး Console မှာ ဖေါ်ထားပါမယ်။ ပြီးတော့ ချက်ချင်းဆိုသလိုပဲ Shift + Tab ကိုနှိပ်ပါမယ်။ ချက်ချင်းပြန်လွှတ်ပြီး v ကို နှိပ်ပါမယ်။

အထက်ပါအတိုင်း LocalDate = LoalDate.now() ဆိုပြီး LocalDate နဲ့ = ရဲ့ကြား Variable Name နေရာမှာ Cursor ကို ချထားပေးမှာ ဖြစ်ပါတယ်။ အဲ့ဒီနေရာမှာ နှစ်သက်တဲ့ Variable Name ကိုပေးပြီး Variable အသစ်တစ်ခုကို ရေးသားနိုင်မှာ ဖြစ်ပါတယ်။

Expression ရဲ့ ရလဒ်ကို စစ်ဆေးပြီးမှ Variable အသစ်တစ်ခုကို ရေးသားလိုတဲ့ အခါမျိုးမှာ အသုံးဝင်မှာ ဖြစ်ပါတယ်။


/exit or Ctl + d


နောက်ဆုံး JShell ကို အဆုံးသတ်လိုတဲ့ အခါမျိုးမှာ /exit Command ဒါမှမဟုတ် CTL + d Shortcut ကို အသုံးပြုပြီး JShell REPL ကို အဆုံးသတ်စေနိုင်မှာ ဖြစ်ပါတယ်။

ဆက်ပါဦးမည်
မင်းလွင်

October 9, 2017

JShell

လူပြောများနေတဲ့ Java 9  ဟာ၂၀၁၇ ခု လပိုင်း ၂၁ ရက်နေ့မှာ တရားဝင် ထွက်ပေါ်ခဲ့ပါပြီ။ Modular Java, HTTP 2 Client, Security Update အစရှတဲ့ အထင်ကရ Feature တွေများလွန်းလို့ ဘယ်ကနေ ဘယ်လို စပြီး လေ့လာရမှန်းမသိခဲ့ပါဘူး။ 


ဒီလိုနဲ့ Java မှာ အခုမှပါလာတဲ့ REPL (Read - Eval - Print - Loop) ဖြစ်တဲ့ JShell ကနေ စပြီး လေ့လာသွားပါမယ်။ REPL ဆိုတာကတော့ နာမည်အတိုင်း ရေးသားလိုက်တဲ့ ကုဒ်တွေကို ဖတ်မယ်၊ ပြီးရင် ဘယ်လိုလုပ်ရမယ်ဆိုတာကို ဆုံးဖြတ်ပြီးတော့ အဖြေကို ပြန်ထုတ်ပြ၊ ဒါကို အကြိမ်ကြိမ်ဆောင်ရွက်နိုင်တဲ့ Interactive Tools တစ်မျိုးဖြစ်ပါတယ်။


အခြားသော Programming Language တွေမှာတော့ အတော်များများ REPL Tools တွေကို Support လုပ်ထားပေမဲ့ Java မှာတော့ ဒီတစ်ခေါက် Java 9 ရောက်မှာ ပါဝင်လာခဲ့တာဖြစ်ပါတယ်။ JShell ကို Open JDK ရဲ့ Sub Project တစ်ခုဖြစ်တဲ့ Project Kulla ကနေ တီထွင် ရေးသားခဲ့တာဖြစ်ပါတယ်။

JShell ကိုလေ့လာတဲ့ နေရာမှာ အောက်ပါအတိုင်း လေ့လာသွားမှာ ဖြစ်ပါတယ်။

  • Quick Start JShell
    JShell
    ကို အသုံးပြုနိုင်ဖို့ အခြေခံသိထားသင့်တဲ့ အကြောင်းအရာတွေကို ဒီနေရာမှာ ဖေါ်ပြသွားပါမယ်
  • Rules of JShell
    JShell
    ကို အသုံးပြုပြီး ဘာတွေလုပ်လို့ရတယ် ဘာတွေ လုပ်လို့မရဘူးဆိုတာတွေကို ဒီနေရာမှာ ဖေါ်ပြသွားပါမယ်
  • Usecases of JShell
    JShell
    ကို လက်တွေ့ ဘယ်လိုနေရာတွေမှာ အသုံးပြုသင့်တယ်ဆိုတယ်ဆိုတာတွေကို ဖေါ်ပြသွားပါမယ်

ဆက်ပါဦးမယ်။
မင်းလွင်

February 15, 2017

I/O Streams

အရင်တစ်ခေါက် File အကြောင်းရေးသားခဲ့စဉ်က File တွေ Directory တွေကို Create လုပ်နိုင်တယ်။ ပြီးတော့လဲ Delete လုပ်နိုင်တယ်။ အမည်ပြောင်းတာတို့လဲ လုပ်နိုင်ခဲ့ပါတယ်။ ဒါပေမဲ့ အဲ့ဒီ File တွေထဲက Data တွေကို Read လုပ်တာတို့ Write လုပ်တာတို့ကို မလုပ်နိုင်ခဲ့ပါဘူး။

File ထဲက Data တွေကို Input Output လုပ်နိုင်ဖို့အတွက် I/O Stream တွေကို အသုံးပြုရမှာ ဖြစ်ပါတယ်။ Java Language မှာ I/O Stream တွေကို အသုံးပြုမည့် Data Type အပေါ်မူတည်ပြီး အမျိုးမျိုးပြင်ဆင်ထားပါတယ်။ ပြီးတော့ I/O Stream တွေကို ရေးသားတဲ့နေရာမှာလဲ Design Pattern တွေကို အသုံးပြုရေးသားထားပါတယ်။ အရင်ဆုံး I/O Stream တွေကို လေ့လာတဲ့နေရာမှာ အသုံးပြုနိုင်တဲ့ I/O Stream တွေနဲ့ အသုံးပြုပုံတွေကို လေ့လာပြီး၊ နောက်ပိုင်းမှ I/O Stream တွေမှာ အသုံးပြုထားတဲ့ Design Patterns တွေကို လေ့လာသွားပါမယ်။


What are Streams?

Stream တွေဟာ Java Program ထဲမှာအသုံးပြုမည့် Input Source တွေနဲ့ Output Destination တွေကိုကိုယ်စားပြုတဲ့ Interface Object တွေဖြစ်ကြပါတယ်။ Source တွေကော Destination တွေပါ ပုံစံအမျိုးမျိုးရှိနိုင်ကြပြီး Input Output လုပ်ကြမည့် Data တွေလဲ​အမျိုးမျိုးဖြစ်နိုင်ပါတယ်။

Input Source လိုပြောတဲ့နေရာမှာ File တစ်ခုလဲဖြစ်နိုင်သလို System Console ကလာတဲ့ Input လဲ ဖြစ်နေနိုင်ပါတယ်။ ဒါမှမဟုတ် အခြားသော Program တစ်ခုခုလဲ ဖြစ်နိုင်ပါတယ်။ ဒီလိုပဲ Output Destination လို့ပြောတဲ့နေရာမှာလဲ File တစ်ခုခု၊ System Console ဒါမှမဟုတ် အခြားသော Program တစ်ခုခုလဲ ဖြစ်နေနိုင်ပါတယ်။ ဒါပေမဲ့ အခြေခံအားဖြင့်တော့ I/O Stream တွေကို Input Stream တွေနဲ့ Output Streams တွေဆိုပြီး သတ်မှတ်ထားပါတယ်။


Input Streams


Java Program ထဲကအသုံးပြုရန် Data များအား Source တစ်နေရာရာမှ ဖတ်ယူနိုင်ဖို့အတွက် Input Stream များကို အသုံးပြုရပါတယ်။ အသုံးပြုလိုတဲ့ Data Type အပေါ်မူတည်ပြီး Streams အမျိုးမျိုးကိုလဲ ပြင်ဆင်ထားပါတယ်။ အသုံးပြုနိုင်တဲ့ Data Type တွေကတော့ bytes, primitive type data, localize character များနဲ့ Object တွေပဲ ဖြစ်ကြပါတယ်။


Output Streams


Java Program ထဲကနေ Destination တစ်ခုဆီကို Data များကို Output လုပ်ချင်တဲ့အခါ Output Stream တွေကို အသုံးပြုနိုင်ပါတယ်။ Destination အနေနဲ့ File တစ်ခုခုဖြစ်နိုင်သလို၊ System Console လဲ ဖြစ်နိုင်သလို အခြားသော Program တစ်ခုခုလဲ ဖြစ်နိုင်ပါတယ်။ ပြီးတော့ byte အနေနဲ့သော်၎င်း၊ primitive data type အနေနဲ့သော်၎င်း၊ localize character များအနေနဲ့သော်၎င်း၊ Object များအနေနဲ့သော်၎င်း Outputလုပ်နိုင်ပါတယ်။

Input Stream တုန်းကလိုပဲ အသုံးပြုလိုတဲ့ Data Type တွေအလိုက် Stream တွေကို ရွေးချယ် အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။


Data Types and Streams


Java IO ရဲ့ Stream Class တွေအားလုံးဟာ InputStream နဲ့ OutputStream Class တွေကနေ ဆင်းသက်လာကြတဲ့ Class တွေဖြစ်ပါတယ်။ Input Output အနေနဲ့ အသုံးပြုလိုတဲ့ Data Type အလိုက် Stream တွေကို ခွဲခြားအသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။

Data Types Input Streams Output Streams
Byte FileInputStream FileOutputStream
Characters FileReader FileWriter
String Line BufferedReader PrintWriter
Primitive Data DataInputStream DataOutputStream
Objects ObjectInputStream ObjectOutputStream


Byte Streams


File တစ်ခုထဲက Data တွေကို Binary Data တွေဖြစ်တဲ့ 8-bit bytes ပုံစံနဲ့ Read Write လုပ်လိုတဲ့အခါ Byte Streams တွေကို အသုံးပြုရမှာ ဖြစ်ပါတယ်။ Byte Stream တွေအားလုံးဟာ InputStream နဲ့ OutputStream Class တွေရဲ့ Sub Class တွေဖြစ်ကြပြီး အသုံးများတာကတော့ FileInputStream နဲ့ FileOutputStream Class တို့ပဲ ဖြစ်ကြပါတယ်။

FileInputStream ကတော့ File တစ်ခုကနေ Byte တွေကို Read လုပ်ချင်တဲ့အခါမှာ အသုံးပြုနိုင်တဲ့ Stream တစ်ခုဖြစ်ပြီး၊ FileInputStream Object ကို တည်ဆောက်ရန် Constructor ၃ မျိုးပြင်ဆင်ထားပါတယ်။

Constructor Description
FileInputStream(File file) Input လုပ်ရမည့် File ကို ကိုယ်စားပြုတဲ့ File Object ကို Argument အနေနဲ့ အသုံးပြုရပါမယ်
FileInputStream(FileDescription fdObj) Input လုပ်မည့် File ကို ဖေါ်ပြပေးနိုင်တဲ့ FileDescriptor Object ကို Argument အနေနဲ့ အသုံးပြုရပါမယ်
FileInputStream(String name) Input လုပ်မည့် File ရဲ့ အမည် String Object ကို Argument အနေနဲ့ အသုံးပြုရပါမယ်

FileInputStream Object ကနေ byte တွေကို 1 byte ချင်း Read လုပ်လိုတဲ့အခါ read() ဆိုတဲ့ Method ကို အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။

FileOutputStream ကတော့ File တစ်ခုဆီကို Byte တွေ အနေနဲ့ Output လုပ်ပေးနိုင်တဲ့ Stream Object တစ်ခု ဖြစ်ပါတယ်။ FileOutputStream Object ကို တည်ဆောက်ဖို့အတွက် ပြင်ဆင်ထားတဲ့ Constructor တွေကတော့ အောက်ပါ အတိုင်းဖြစ်ကြပါတယ်။

Constructor Description
FileOutputStream(File file) Output လုပ်ရမည့် File ကို ကိုယ်စားပြုတဲ့ File Object ကို Argument အနေနဲ့ အသုံးပြုရပါမယ်
FileOutputStream(File file, boolean append) Output လုပ်ရမည့် File ကို ကိုယ်စားပြုတဲ့ File Object နဲ့ boolean တန်ဖိုးတစ်ခုကို Argument အနေနဲ့ ပေးရပါမယ်။
boolean ရဲ့ တန်ဖိုးဟာ true ဖြစ်မယ်ဆိုရင် လက်ရှိရှိပြီးသား File မှာ Output လုပ်လာတဲ့ Data တွေကို ဆက်ပြီးထည့်ပေးနိုင်မှာ ဖြစ်ပါတယ်
FileOutputStream(FileDescription fdObj) Output လုပ်မည့် File ကို ဖေါ်ပြပေးနိုင်တဲ့ FileDescriptor Object ကို Argument အနေနဲ့ အသုံးပြုရပါမယ်
FileOutputStream(String name) Output လုပ်မည့် File ရဲ့ အမည် String Object ကို Argument အနေနဲ့ အသုံးပြုရပါမယ်
FileOutputStream(String name, boolean append) Output လုပ်မည့် File ရဲ့ အမည်ကို String Object နဲ့ boolean တန်ဖိုးတစ်ခုကို Argument အနေနဲ့ ပေးရပါမယ်။
boolean ရဲ့ တန်ဖိုးဟာ true ဖြစ်မယ်ဆိုရင် လက်ရှိရှိပြီးသား File မှာ Output လုပ်လာတဲ့ Data တွေကို ဆက်ပြီးထည့်ပေးနိုင်မှာ ဖြစ်ပါတယ်

Output လုပ်လိုတဲ့ Byte Data တွေကို ရေးသားနိုင်ဖို့အတွက် write(int b), write(byte [] b) နဲ့ write(byte [] b, int off, int len) method တို့ကို ပြင်ဆင်ထားပါတယ်။ 1 byte ချင်း Write လုပ်ချင်ရင်တော့ write(int b) ကို အသုံးပြုရမှာ ဖြစ်ပါတယ်။

Stream Object တွေဟာ Resource တွေကို အသုံးပြုနေတဲ့ Object အမျိုးအစား ဖြစ်တဲ့အတွက် အသုံးပြုပြီးတဲ့အခါမှာ ပြန်ပြီး ပိတ်ပေးဖို့လိုအပ်တဲ့အတွက် close() ဆိုတဲ့ Method ကိုလဲ ပြင်ဆင်ပေးထားပါတယ်။ ဒါပေမဲ့ Java SE 7 ရဲ့ ရေးသားပုံအသစ်ဖြစ်တဲ့ try-with-resource ရေးသားပုံနဲ့ ရေးသားမယ်ဆိုရင်တော့ အလိုအလျောက် ပိတ်ပေးနိုင်မှာ ဖြစ်ပါတယ်။
package com.jdc.io.stream.ep1;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class CopyByte {

    public static void main(String[] args) {
        
        try (FileInputStream in = new FileInputStream("from.txt");
                FileOutputStream out = new FileOutputStream("to.txt")){
            
            int i = 0;
            
            while((i = in.read()) != -1) {
                out.write(i);
            }
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

အထက်ပါနမူနာကတော့ from.txt ဆိုတဲ့ File ထဲမှာရှိတဲ့ Data တွေကို 1 byte ချင်း read လုပ်ပြီး to.txt ဆိုတဲ့ဖိုင်ထဲကို 1 byte ချင်း write လုပ်နေတဲ့ နမူနာဖြစ်ပါတယ်။

FileInputStream နဲ့ FileOutputStream Object တွေကို try with resource နဲ့ ရေးသားထားတဲ့အတွက် နောက်ဆုံးမှာ အလိုအလျောက်ပိတ်ပေးသွားမှာ ဖြစ်တယ်။ အဲ့ဒီအတွက် တကူးတက close လုပ်ပေးစရာမလိုတော့ ပါဘူး။ တဖန် while clause ထဲမှာ i = in.read() ဆိုပြီး FileInputStream ကနေ 1 byte ချင်း Read လုပ်နေပါတယ်။ နောက်ဆုံး Read လုပ်စရာမရှိတော့တဲ့အခါ -1 ကို Return ပြန်မှာ ဖြစ်ပြီး While loop ထဲကနေ ထွက်သွားမှာ ဖြစ်ပါတယ်။

While Loop ထဲမှာတော့ Read လုပ်လာတဲ့ တန်ဖိုးကို 1 byte ချင်း FileOutputStream ကို သုံးပြီး write လုပ်ပေးနေပါတယ်။ ဤနည်းအားဖြင့် from.txt ဖိုင်ထဲက Data တွေကို to.txt ဖိုင်ထဲကို 1 byte ချင်း Write လုပ်ပေးသွားနိုင်မှာ ဖြစ်ပါတယ်။


Character Streams

Text File တစ်ခုအတွင်းရှိ Data များကို Character ပုံစံနဲ့ Read / Write လုပ်ချင်ရင် Character Streams များကို အသုံးပြုရမှာ ဖြစ်ပါတယ်။ Character Streams တွေဟာ InputStreamReader နဲ့ OutputStreamWriter တို့ရဲ့ Sub Class တွေ ဖြစ်ကြပြီး FileReader နဲ့ FileWriter တို့ဟာ အသုံးများပါတယ်။

FileReader နဲ့ FileWriter တို့ဟာ Default Encoding နဲ့ Character တွေကို အလွယ်တကူ Read / Write လုပ်နိုင်အောင်ပြင်ဆင်ထားတဲ့ Class တွေဖြစ်ကြပြီး၊ Character Set တို့ကို ကိုယ်တိုင် သတ်မှတ်ပြီး Read / Write လုပ်ချင်ရင်တော့ InputStreamReader နဲ့ OutputStreamWriter တို့ကို အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။

FileReader ကို Character များကို Read လုပ်တဲ့နေရာမှာ အသုံးပြုပြီး ရေးသားနိုင်တဲ့ Constructor များကတော့ အောက်ပါ အတိုင်းဖြစ်ကြပါတယ်။

Constructor Description
FileReader(File file) Input လုပ်ရမည့် File ကို ကိုယ်စားပြုတဲ့ File Object ကို Argument အနေနဲ့ အသုံးပြုရပါမယ်
FileReader(FileDescription fdObj) Input လုပ်မည့် File ကို ဖေါ်ပြပေးနိုင်တဲ့ FileDescriptor Object ကို Argument အနေနဲ့ အသုံးပြုရပါမယ်
FileReader(String name) Input လုပ်မည့် File ရဲ့ အမည် String Object ကို Argument အနေနဲ့ အသုံးပြုရပါမယ်

FileWriter ကို Character များကို Write လုပ်တဲ့နေရာမှာ အသုံးပြုပြီး ရေးသားနိုင်တဲ့ Constructor များကတော့ အောက်ပါ အတိုင်းဖြစ်ကြပါတယ်။

Constructor Description
FileWriter(File file) Output လုပ်ရမည့် File ကို ကိုယ်စားပြုတဲ့ File Object ကို Argument အနေနဲ့ အသုံးပြုရပါမယ်
FileWriter(File file, boolean append) Output လုပ်ရမည့် File ကို ကိုယ်စားပြုတဲ့ File Object နဲ့ boolean တန်ဖိုးတစ်ခုကို Argument အနေနဲ့ ပေးရပါမယ်။
boolean ရဲ့ တန်ဖိုးဟာ true ဖြစ်မယ်ဆိုရင် လက်ရှိရှိပြီးသား File မှာ Output လုပ်လာတဲ့ Data တွေကို ဆက်ပြီးထည့်ပေးနိုင်မှာ ဖြစ်ပါတယ်
FileWriter(FileDescription fdObj) Output လုပ်မည့် File ကို ဖေါ်ပြပေးနိုင်တဲ့ FileDescriptor Object ကို Argument အနေနဲ့ အသုံးပြုရပါမယ်
FileWriter(String name) Output လုပ်မည့် File ရဲ့ အမည် String Object ကို Argument အနေနဲ့ အသုံးပြုရပါမယ်
FileWriter(String name, boolean append) Output လုပ်မည့် File ရဲ့ အမည်ကို String Object နဲ့ boolean တန်ဖိုးတစ်ခုကို Argument အနေနဲ့ ပေးရပါမယ်။
boolean ရဲ့ တန်ဖိုးဟာ true ဖြစ်မယ်ဆိုရင် လက်ရှိရှိပြီးသား File မှာ Output လုပ်လာတဲ့ Data တွေကို ဆက်ပြီးထည့်ပေးနိုင်မှာ ဖြစ်ပါတယ်
FileReader နဲ့ FileWriter တို့ရဲ့ အသုံးပြုပုံဟာ အခြေခံအားဖြင့် FileInputStream နဲ့ FileOutputStream တို့နဲ့အတူတူပဲ ဖြစ်ပါတယ်။
package com.jdc.io.stream.ep2;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class CopyChar {
    
    public static void main(String[] args) {
        
        try(FileReader in = new FileReader("from.txt");
                FileWriter out = new FileWriter("out.txt")) {
            
            int c = 0;
            
            while((c = in.read()) != -1) {
                out.write(c);
            }
            
        } catch (IOException e) {
            e.printStackTrace();
        }
        
    }
}

FileReader ကို အသုံးပြုပြီး Text File အတွင်းမှ Character များကို Read လုပ်ရာမှာ read() method ကို အသုံးပြုနိုင်မှာဖြစ်ပါတယ်။ Return Type မှာ int ဖြစ်ပြီး Character တစ်လုံးကို ကိုယ်စားပြုမှာ ဖြစ်ပါတယ်။ နောက်ဆုံး Read လုပ်စရာမရှိတော့တဲ့အခါ -1 ကို Return ပြန်မှာဖြစ်ပါတယ်။

Character စာလုံးများကို Write လုပ်လိုတဲ့အခါ FileWriter Object ရဲ့ write(int char) method ကို အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။ Write Method ကို အသုံးပြုပြီး Character တစ်လုံးချင်းစီကို Output လုပ်ပေးနိုင်မှာ ဖြစ်ပါတယ်။

အထက်ပါနမူနာထဲံမှာတော့ from.txt file ထဲမှ Character များကို while statement ဖြင့် တစ်လုံးစီဖတ်ပြီး out.txt file ထဲကို Character တစ်လုံးစီ Output လုပ်ပေးနိုင်မှာ ဖြစ်ပါတယ်။


Line Oriented IO


Character I/O ကို အသုံးပြုတဲ့အခါမှာ Character တစ်လုံးခြင်း Read / Write လုပ်တာထက် ဒီထက်ပိုပြီး ကြီးမားတဲ့ Unit နဲ့ Read / Write လုပ်လေ့ရှိပါတယ်။ အသုံးများတာက စာကြောင်း တစ်ကြောင်းစီကို Read / Write လုပ်တဲ့ပုံစံပဲ ဖြစ်ပါတယ်။ Line တစ်ခုချင်းစီကို Read / Write လုပ်ဖို့အတွက် BufferedReader, BufferedWriter နဲ့ PrintWriter တို့ကို အသုံးပြုနိုင်ပါတယ်။


BufferedReader

BufferedReader တွေဟာ Text File တွေကနေ Character တွေကို သတ်မှတ်ထားတဲ့ Buffered Size အလိုက် Character Array အဖြစ်သော်၎င်း၊ စာကြောင်းတစ်ကြောင်းစီလိုက်သော်၎င်း Read လုပ်နိုင်အောင်ပြင်ဆင်ထားတဲ့ Class တွေ ဖြစ်ပါတယ်။

BufferedReader Class ကို Object ဆောက်နိုင်တဲ့ Constructor တွေကတော့ အောက်ပါ အတိုင်းဖြစ်ပါတယ်။

Constructor Description
BufferedReader(Reader in) Reader Object တစ်ခုကို Argument အနေနဲ့ပေးရပါမယ်။​ Buffer Size ကိုတော့ Default Size အတိုင်း အသုံးပြုသွားမှာ ဖြစ်ပါတယ်။
BufferedReader(Reader in, int size) Reader Object တစ်ခုနဲ့ Buffer Size ကို Argument အနေနဲ့ ပေးပြီး BufferedReader Object တစ်ခုကို တည်ဆောက်နိုင်မှာ ဖြစ်ပါတယ်။ Buffer Size ကို သတ်မှတ်ပြီး အသုံးပြုလိုပါက ဤ Constructor ကို အသုံးပြုရမှာ ဖြစ်ပါတယ်။

package com.jdc.io.stream.ep3;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class LineReader {
    
    public static void main(String[] args) {
        
        try(BufferedReader in = new BufferedReader(new FileReader("line.txt"))) {
            
            String line = null;
            
            while((line = in.readLine()) != null) {
                System.out.println(line);
            }
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

BufferedReader ကနေ စာကြောင်း တစ်ကြောင်းစီ Read လုပ်လိုပါက readLine() method ကို အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။ အထက်ပါ နမူနာမှာတော့ in.readLine() ကို အသုံးပြုပြီး line variable မှာ အစားထိုးနေပါတယ်။ ပြီးတော့ While Statement မှာ line ဟာ null မဖြစ်မခြင်း loop ပတ်နေမှာ ဖြစ်ပြီး စာကြောင်းတွေ ဖတ်စရာကုန်သွားရင် null ကို Return လုပ်မှာ ဖြစ်တဲ့အတွက် looping ထဲကနေ ထွက်သွားပါလိမ့်မယ်။

ဒါ့ကြောင့် line.txt ထဲက စာကြောင်းတွေကို အစ စာကြောင်းကနေ တစ်ကြောင်းချင်း ကုန်သွားသည် အထိ Read လုပ်သွားနိုင်မှာ ဖြစ်ပါတယ်။


PrintWriter


Java Program ထဲကနေ Character တွေကို String အနေနဲ့ Write လုပ်လိုတဲ့အခါမှာ PrintWriter ကို အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။ PrintWriter ကို အသုံးပြုပြီး String တွေလို ဒီအတိုင်း Write လုပ်နိုင်သလို၊ Format ချပြီးလဲ Write လုပ်နိုင်ပါတယ်။ ထို့အပြင် Locale အလိုက်သတ်မှတ်ပြီးလဲ Format လုပ်နိုင်မှာ ဖြစ်ပါတယ်။ PrintWriter Object ကို တည်ဆောက်နိုင်တဲ့ Constructor တွေကတော့ အောက်ပါ အတိုင်းဖြစ်ကြပါတယ်။

Constructor Description
PrintWriter(File file) Output File ကို သတ်မှတ်ပြီး PrintWriter Object ကို တည်ဆောက်နိုင်မှာ ဖြစ်ပါတယ်။ Auto Flush ကိုတော့ လုပ်ပေးနိုင်မှာ မဟုတ်ပါဘူး
PrintWriter(File file, String csn) Output File နဲ့ Character Set ကို သတ်မှတ်ပြီး PrintWriter Object ကို တည်ဆောက်နိုင်မှာ ဖြစ်ပါတယ်။ Auto Flush ကိုတော့ လုပ်ပေးနိုင်မှာ မဟုတ်ပါဘူး
PrintWriter(OutputStream out) OutputStream ကို သတ်မှတ်ပြီး PrintWriter Object ကို တည်ဆောက်နိုင်မှာ ဖြစ်ပါတယ်။ Auto Flush ကိုတော့ လုပ်ပေးနိုင်မှာ မဟုတ်ပါဘူး
PrintWriter(OutputStream out, boolean autoFlush) OutputStream နဲ့ Auto Flush Mode ကို သတ်မှတ်ပြီး PrintWriter Object ကို တည်ဆောက်နိုင်မှာ ဖြစ်ပါတယ်။
PrintWriter(String fileName) File Name ကို သတ်မှတ်ပြီး PrintWriter Object ကို တည်ဆောက်နိုင်မှာ ဖြစ်ပါတယ်။ Auto Flush ကိုတော့ လုပ်ပေးနိုင်မှာ မဟုတ်ပါဘူး
PrintWriter(String fileName, String csn) File Name နဲ့ Character Set ကို သတ်မှတ်ပြီး PrintWriter Object ကို တည်ဆောက်နိုင်မှာ ဖြစ်ပါတယ်။ Auto Flush ကိုတော့ လုပ်ပေးနိုင်မှာ မဟုတ်ပါဘူး
PrintWriter(Writer out) Writer ကို သတ်မှတ်ပြီး PrintWriter Object ကို တည်ဆောက်နိုင်မှာ ဖြစ်ပါတယ်။ Auto Flush ကိုတော့ လုပ်ပေးနိုင်မှာ မဟုတ်ပါဘူး
PrintWriter(Writer out, boolean autoFlush) Writer နဲ့ Auto Flush Mode ကို သတ်မှတ်ပြီး PrintWriter Object ကို တည်ဆောက်နိုင်မှာ ဖြစ်ပါတယ်။

package com.jdc.io.stream.ep3;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class LineWriter {

    private static final String [] ARRAY = {
            "Hello Java",
            "Hello Java IO",
            "Learn java Programming."
    };
    
    public static void main(String[] args) {
        
        try(PrintWriter out = new PrintWriter(new FileWriter("line.txt"))) {
            
            for (String line : ARRAY) {
                out.println(line);
            }
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

String တွေကို Write လုပ်ဖို့အတွက် print(String str) ဒါမှမဟုတ် write(String str) ကို အသုံးပြုနိုင်ပါတယ်။ ဒါအပြင် Line တွေကို Write လုပ်ဖို့အတွက် println(String str) ကို အသုံးပြုနိုင်ပါတယ်။ Format လုပ်ချင်ရင်တော့ printf(String format, String … param) ကို အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။


Data Streams

Primitive Data နဲ့ String တန်ဖိုး တွေကို Read / Write လုပ်ချင်တဲ့အခါ DataInputStream နဲ့  DataOutputStream တို့ကို အသုံးပြုနိုင်ပါတယ်။

Constructor Description
DataOutputStream(OutputStream out) OutputStream Object တစ်ခုကို အသုံးပြုပြီး DataOutputStream ကို တည်ဆောက်ရန် ပြင်ဆင်ထားတဲ့ Constructor ဖြစ်ပါတယ်။
DataInputStream(InputStream in) InputStream Object တစ်ခုကို အသုံးပြုပြီး DataInputStream ကို တည်ဆောက်ရန် ပြင်ဆင်ထားတဲ့ Constructor ဖြစ်ပါတယ်။

အသုံးပြုလိုတဲ့ Data Type အလိုက် Method တွေကိုလဲ ခွဲခြားပြင်ဆင်ထားပါတယ်။

Data Type DataInputStream DataOutputStream
boolean readBoolean() writeBoolean(boolean v)
char readChar() writeChar(int v)
byte readByte() writeByte(int v)
short readShort() writeShort(int v)
int readInt() writeInt(int v)
long readLong() writeLong(long v)
float readFloat() writeFloat(float v)
double readDouble() writeDouble(doble v)
String readUTF() writeUTF(String str)

မိမိအသုံးပြုလိုတဲ့  Data Type အလိုက် သက်ဆိုင်ရာ Method ကို အသုံးပြုပြီး ရေးသားရမှာ ဖြစ်ပါတယ်။


Write Data

package com.jdc.io.stream.ep4;

import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class DataOutput {
    
    private static final String name = "Aung Aung";
    private static final int age = 20;
    private static final boolean isMan = true;
    
    public static void main(String[] args) {
        
        System.out.println("Starting Output");

        try(DataOutputStream out = new DataOutputStream(new FileOutputStream("data.dat"))) {
            
            out.writeUTF(name);
            out.writeInt(age);
            out.writeBoolean(isMan);
            
            System.out.println("End Output");
        } catch (IOException e) {
            System.out.println("Output Error");
        }
        
    }

}

အထက်ပါနမူနာမှာတော့ name variable ဟာ String ဖြစ်တဲ့အတွက် writeUTF ကို အသုံးပြုပြီး၊ age ဟာ int ဖြစ်တဲ့အတွက် writeInt ကို အသုံးပြုပါတယ်။ တဖန် isMan ဆိုတဲ့ Variable ဟာ boolean type ဖြစ်တဲ့အတွက် writeBoolean ကို အသုံးပြရမှာ ဖြစ်ပါတယ်။


Reading Data

package com.jdc.io.stream.ep4;

import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;

public class DataInput {

    public static void main(String[] args) {
        
        try(DataInputStream in = new DataInputStream(
                new FileInputStream("data.dat"))) {
            
            System.out.println("Name : " + in.readUTF());
            System.out.println("Age  : " + in.readInt());
            System.out.println("Male : " + in.readBoolean());
            
        } catch (IOException e) {
            e.printStackTrace();
        }
        
    }
}

အထက်ပါ နမူနာကတော့ DataOutput ဖြင့် Write လုပ်ထားတဲ့ data.dat file ထဲက Data တွေကို ပြန်ပြီး Read လုပ်နေတာဖြစ်ပါတယ်။ String Data တွေကို readUTF ကိုအသုံးပြုပြီး၊ int ကိုတော့ readInt ကို အသုံးပြုပါတယ်။ တဖန် boolean ကိုတော့ readBoolean method ကို အသုံးပြုပါတယ်။


Serialization

အရှေ့တွင်ဖေါ်ပြခဲ့သလို Java ဘာသာရပ်တွင် Byte, Character, Primitive Data များနှင့် String များကို Read / Write လုပ်နိုင်တဲ့အပြင် Object တွေကိုလဲ Object အတိုင်း Read / Write လုပ်နိုင်ပါတယ်။

Java ဘာသာရပ်မှာ JVM Memory အပေါ်မှာရှိတဲ့ Object တွေကို အခြား တစ်နေနေရာကို ပို့ပေးလိုက်ခြင်းကို Serialize လုပ်တယ်လို့ခေါ်ပါတယ်။ တဖန် တစ်ခြားတစ်နေနေရာကနေလာတဲ့ Serialized လုပ်ထားတဲ့ Data တွေကနေ JVM Memory အပေါ်ကို ပြန်ပြီး Object အဖြစ်ပြန်ပြောင်းတာကိုတော့ Deserialize လုပ်တယ်လို့ခေါ်ပါတယ်။ Deseiralize လုပ်တယ်ဆိုတာကလဲ Serialize လုပ်ထားတဲ့ Data များကိုသာ ပြုလုပ်နိုင်မှာ ဖြစ်ပါတယ်။

နမူနာအားဖြင့် Program ထဲမှာ သုံးနေတဲ့ Java Object တွေကို File တစ်ခုခုမှာ သွားသိမ်းတာတို့၊ Internet ကိုဖြတ်ပြီး တစ်ခြားတစ်နေနေရာကိုပို့ပေးတာတို့ဟာ Serialize လုပ်တာဖြစ်ကြပါတယ်။ Server အပေါ်မှာရှိတဲ့ Object တွေကို Session ပေါ်မှာ သုံးနေတယ်ဆိုတာကလဲ Serialize လုပ်နေတာပဲ ဖြစ်ပါတယ်။

ဒါဖြင့် ဘယ်လို Object တွေကို Serialize လုပ်လို့ရတာလဲ။ Primitive Data Type တွေ၊ Serializable Interface ကို Implement လုပ်ထားတဲ့ Object တွေနဲ့ Serializable Object တွေရဲ့ Collection တွေ ဆိုရင် Serialize လုပ်လို့ရမှာ ဖြစ်ပါတယ်။

Object တစ်ခုကို Serialize လုပ်တဲ့အခါမှာ Object ရဲ့ State တွေဖြစ်ကြတဲ့ Instance Variable တွေကိုသာ Serialize လုပ်သွားမှာ ဖြစ်ပါတယ်။ ဒါ့ကြောင့် Serialize လုပ်မည့် Object ရဲ့ Instance Variable တွေဟာလဲ Serializable ဖြစ်ဖို့လိုအပ်ပါတယ်။ တကယ်လို့ Serialize မလုပ်ချင်တဲ့ Instance Variable တွေဆိုရင် transient modifier ကို ရေးသားပြီး ဖေါ်ပြနိုင်ပါတယ်။ Instance Variable တွေရှေ့မှာ transient ကို ရေးသားထားရင် အဲ့ဒီ Field ကို Serialize လုပ်မှာ မဟုတ်တော့ပါဘူး။
package com.jdc.io.stream.ep5;

import java.io.Serializable;

public class Employee implements Serializable{

    private static final long serialVersionUID = 1L;

    private int id;
    private String name;
    private String phone;
  
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }
}


Serial Version UID

Serializable Object တစ်ခုကို Serialize လုပ်ပြီး ပြန်ပြီး Deserialize လုပ်တဲ့အခါ Serial Version UID ကို အသုံးပြုပြီး Class ရဲ့ Version မှန်မမှန်ကို ပြန်ပြီး စစ်ဆေးလေ့ရှိပါတယ်။ Serial Version UID ကို ရေးသားရာမှာ static final long serialVersionUID ဆိုတဲ့ပုံစံနဲ့ ရေးသားပေးရပါတယ်။ Access Modifier ကိုတော့ နှစ်သက်ရာ ရေးသားအသုံးပြုနိုင်ပါတယ်။

Serialize လုပ်တဲ့အခါမှာ Serial Version UID ဟာ Static Variable ဖြစ်ပေမဲ့ ခြွင်းချက်အနေနဲ့ Serialize အလုပ်ခံရမှာ ဖြစ်ပါတယ်။ Deserialize လုပ်တဲ့အခါမှာ လက်ရှိ Class နဲ့ Deserialize လုပ်မည့် Object ရဲ့ Serial Version UID ကို တိုက်စစ်ပြီး မှန်ကန်မှသာ Serialize လုပ်ပေးမှာ ဖြစ်ပါတယ်။ တကယ်လို့ Version နှစ်ခုမတူညီခဲ့ပါက InvalidClassException ကို ဖြစ်ပေါ်စေမှာ ဖြစ်ပါတယ်။

တကယ်လို့ Serializable Class တစ်ခုမှာ Serial Version UID ကို မရေးထားဖူးဆိုရင် Serialize လုပ်တဲ့အခါမှာ JVM ကနေ နှစ်သက်ရာ ID တစ်ခုကို Generate လုပ်ပေးမှာ ဖြစ်ပါတယ်။

ဒါပေမဲ့ Serial Version UID Calculation ဟာ Compiler Implementation အပေါ်မူတည်ပြီး ပြောင်းလဲတတ်တဲ့အတွက် Serial Version UID ကို Class အတွင်းမှာ မဖြစ်မနေရေးသားသင့်ပါတယ်။


Writing Objects (Serialization)

Java Program ထဲကနေ Memory အပေါ်မှာရှိတဲ့ Serializable Object တွေနဲ့ Serializable Object ရဲ့ Collection တွေကို Serialize လုပ်လိုတဲ့အခါမှာ ObjectOutputStream ကို အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။ ObjectOutputStream ကို အသုံးပြုပြီး Primitive Data တွေနဲ့ Serializable Object Graphs တွေကို Serialize လုပ်ပေးနိုင်မှာ ဖြစ်ပါတယ်။

Object တွေကို Serialize လုပ်တဲ့နေရာမှာလဲ File တွေမှာလဲ Serialize လုပ်နိုင်သလို Network ကို အသုံးပြုတဲ့ Socket Stream တွေနဲ့လဲ တွဲဖက် အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။ ObjectOutputStream ရဲ့ Constructor တွေကတော့ အောက်ပါအတိုင်း ဖြစ်ကြပါတယ်။

Constructor Description
ObjectOutputStream() ObjectOutputStream ရဲ့ Sub Class တွေအတွက် ပြင်ဆင်ထားပေးတဲ့ Constructor ဖြစ်ပါတယ်။
ObjectOutputStream(OutputStream out) OutputStream Object တစ်ခုကို အသုံးပြုပြီး ObjectOuputStream ကို တည်ဆောက်ရန် ပြင်ဆင်ထားတဲ့ Constructor ဖြစ်ပါတယ်။

package com.jdc.io.stream.ep5;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class ObjectWriteTest {

    public static void main(String[] args) {
        Employee e = new Employee();
        e.setId(1);
        e.setName("Aung Aung");
        e.setPhone("09788986677");
        
        try(ObjectOutputStream out = new ObjectOutputStream(
                new FileOutputStream("employee.obj"))) {
            out.writeObject(e);
        } catch (IOException e1) {
            e1.printStackTrace();
        }
    }
}
အထက်ပါနမူနာအတိုင်း JVM Memory အပေါ်မှာရှိတဲ့ Object တွေကို Serialize လုပ်လိုပါက writeObject(Object obj) method ကို အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။


Reading Objects (Deserialization)

ObjectOutputStream ကို အသုံးပြုပြီး Serialize လုပ်ထားတဲ့ Primitive Data တွေနဲ့ Object တွေကို Deserialize လုပ်လိုတဲ့အခါ ObjectInputStream ကို အသုံးပြုရမှာ ဖြစ်ပါတယ်။

File ထဲမှာရှိတဲ့ Data တွေကို Deserialize လုပ်တဲ့အခါမျိုးမှာ ObjectInputStream ဟာ FileInputStream ကို အသုံးပြုပါတယ်။ ဒါကြောင့် File ထဲက Data တွေကို Read လုပ်လိုတဲ့အခါမျိုးမှာ InputStream Parameter နေရာမှာ FileInputStream Object ကို အသုံးပြုရမှာ ဖြစ်ပါတယ်။

တဖန် Remote System တစ်ခုကနေ Data တွေကို ရယူတဲ့အခါမျိုးမှာတော့ Socket Streams ကို အသုံးပြုပြီး Data တွေကို Deserialize လုပ်နိုင်ပါတယ်။

Constructor Description
ObjectInputStream() ObjectInputStream ရဲ့ Sub Class တွေအတွက် ပြင်ဆင်ထားပေးတဲ့ Constructor ဖြစ်ပါတယ်။
ObjectInputStream(InputStream in) OutputStream Object တစ်ခုကို အသုံးပြုပြီး ObjectOuputStream ကို တည်ဆောက်ရန် ပြင်ဆင်ထားတဲ့ Constructor ဖြစ်ပါတယ်။

package com.jdc.io.stream.ep5;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class ObjectReadTest {

    public static void main(String[] args) {
        
        try(ObjectInputStream in = new ObjectInputStream(
                new FileInputStream("employee.obj"))) {
            
            Employee e = (Employee) in.readObject();
            
            System.out.println(e.getName());
            
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

ယခင် နမူနာဖြင့် Serialize လုပ်ထားတဲ့ Employee Object ကို Deserialize လုပ်လိုတဲ့အခါ အထက်ပါအတိုင်း ObjectInputStream ကို အသုံးပြုပြီး ရေးသားနိုင်ပါတယ်။ အထက်ပါအတိုင်း in.readObject() method ရဲ့ Return Type ဟာ Object ဖြစ်နေတဲ့အတွက် Employee အဖြစ်ပြန်ပြီး Cast လုပ်ပေးဖို့လိုအပ်ပါတယ်။

ဆက်ပါဦးမယ်
မင်းလွင်

January 11, 2017

A Glance to Java 9

Java SE 9 အကြောင်း အတော်လေးပြောလာကြပြီ။ ဘာပဲပြောပြော ၂၀၁၇ နှစ်ထဲမှာပဲ Release လုပ်ဖြစ်မှာတော့ သေချာတယ်။ Java SE 9 ရဲ့ အဓိက Feature လို့ပြောရရင်တော့ Project Jigsaw လို့ပဲ ပြောရမှာ ဖြစ်ပေမဲ့ ပြောင်းလဲမှု့တွေကတော့ အတော်လေးကိုများပါတယ်။

စမ်းသပ်မယ်ဆိုရင်တော့ JDC 9 Early Release ကို Download လုပ်လို့ရနေပါပြီ။

JDK 9 Download
https://jdk9.java.net/download/

အဓိကအားဖြင့် Language, Tools နဲ့ Library Features တွေမှာ ပြောင်းလဲမှုတွေကို တွေ့ရမှာ ဖြစ်ပါတယ်။ JDK 9 ရဲ့ Early Release လဲ Download လုပ်လို့ရပြီဖြစ်တဲ့အတွက် ဒီတစ်ခေါက်တော့ Java SE 9 မှာ ဘာတွေပြောင်းသွားမလဲဆိုတာကို လေ့လာရင်းမိတ်ဆက်သွားပါမယ်။

JEP ဆိုတာက Java Enhancement Proposal ရဲ့ အတိုကောက် အခေါ်အဝါ်ဖြစ်ပါတယ်။ Java နဲ့ ပတ်သက်တဲ့ ပြုပြင်ပြောင်းလဲမှူ့ဆိုင်ရာ အကြံပြုလွှာတွေဖြစ်ပါတယ်။ Java SE 9 မှာ ပါဝင်တဲ့ JEP တွေကတော့ အားလုံးပေါင်း ၈၈ခုရှိပါတယ်။

102: Process API Updates
110: HTTP 2 Client
143: Improve Contended Locking
158: Unified JVM Logging
165: Compiler Control
193: Variable Handles
197: Segmented Code Cache
199: Smart Java Compilation, Phase Two
200: The Modular JDK
201: Modular Source Code
211: Elide Deprecation Warnings on Import Statements
212: Resolve Lint and Doclint Warnings
213: Milling Project Coin
214: Remove GC Combinations Deprecated in JDK 8
215: Tiered Attribution for javac
216: Process Import Statements Correctly
217: Annotations Pipeline 2.0
219: Datagram Transport Layer Security (DTLS)
220: Modular Run-Time Images
221: Simplified Doclet API
222: jshell: The Java Shell (Read-Eval-Print Loop)
223: New Version-String Scheme
224: HTML5 Javadoc
225: Javadoc Search
226: UTF-8 Property Files
227: Unicode 7.0
228: Add More Diagnostic Commands
229: Create PKCS12 Keystores by Default
231: Remove Launch-Time JRE Version Selection
232: Improve Secure Application Performance
233: Generate Run-Time Compiler Tests Automatically
235: Test Class-File Attributes Generated by javac
236: Parser API for Nashorn
237: Linux/AArch64 Port
238: Multi-Release JAR Files
240: Remove the JVM TI hprof Agent
241: Remove the jhat Tool
243: Java-Level JVM Compiler Interface
244: TLS Application-Layer Protocol Negotiation Extension
245: Validate JVM Command-Line Flag Arguments
246: Leverage CPU Instructions for GHASH and RSA
247: Compile for Older Platform Versions
248: Make G1 the Default Garbage Collector
249: OCSP Stapling for TLS
250: Store Interned Strings in CDS Archives
251: Multi-Resolution Images
252: Use CLDR Locale Data by Default
253: Prepare JavaFX UI Controls & CSS APIs for Modularization
254: Compact Strings
255: Merge Selected Xerces 2.11.0 Updates into JAXP
256: BeanInfo Annotations
257: Update JavaFX/Media to Newer Version of GStreamer
258: HarfBuzz Font-Layout Engine
259: Stack-Walking API
260: Encapsulate Most Internal APIs
261: Module System
262: TIFF Image I/O
263: HiDPI Graphics on Windows and Linux
264: Platform Logging API and Service
265: Marlin Graphics Renderer
266: More Concurrency Updates
267: Unicode 8.0
268: XML Catalogs
269: Convenience Factory Methods for Collections
270: Reserved Stack Areas for Critical Sections
271: Unified GC Logging
272: Platform-Specific Desktop Features
273: DRBG-Based SecureRandom Implementations
274: Enhanced Method Handles
275: Modular Java Application Packaging
276: Dynamic Linking of Language-Defined Object Models
277: Enhanced Deprecation
278: Additional Tests for Humongous Objects in G1
279: Improve Test-Failure Troubleshooting
280: Indify String Concatenation
281: HotSpot C++ Unit-Test Framework
282: jlink: The Java Linker
283: Enable GTK 3 on Linux
284: New HotSpot Build System
285: Spin-Wait Hints
287: SHA-3 Hash Algorithms
288: Disable SHA-1 Certificates
289: Deprecate the Applet API
290: Filter Incoming Serialization Data
292: Implement Selected ECMAScript 6 Features in Nashorn
294: Linux/s390x Port
295: Ahead-of-Time Compilation
297: Unified arm32/arm64 Port

ခေါင်းစဉ်တွေကြီး ဖတ်ကြည့်ရင်တောင် Language ပိုင်းဆိုင်ရာ ပြောင်းလဲမှူ့ထက် Platform ဘက်မှာ ပိုပြီး အားသာတာကို တွေ့ရပါမယ်။ Keywords ကတော့ Modularity ပါပဲ။ jshell ကတော့ Java ကို သင်ကြားရာမှာ Statement တွေကို အလွယ်တကူသင်ကြားနိုင်မှာ ဖြစ်ပါတယ်။

နောက်တစ်ခုစိတ်ဝင်စားမိတာက HTTP2 Client ဟာလဲ​စိတ်ဝင်စားစရာ ဖြစ်ပါတယ်။ Tools နဲ့ JVM အစရှိတဲ့ Platform ပိုင်းဆိုင်ရာ ပြောင်းလဲမှု့တွေကများတာကို တွေ့ရပါတယ်။

ဆက်ပြီးလေ့လာသွားပါဦးမယ်။
လေးစားစွာဖြင့်
မင်းလွင်