2012年12月16日 星期日

JVM記憶體圖-類別的載入與執行

寫好的程式編譯成class之後
透過class loader啟動放到記憶體
class 類別檔就好比是一個設計藍圖
藍圖內有static的method或者field都配置在Method area內
可以整個程式使用
物件(Object)則配置在Heap區
JVM stack則是放變數的起始位置

檔案的讀取

寫一個程式,把文件檔的內容讀取出來

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

class Print{
    public static void main(String[] args) throws IOException{
      
        FileReader f =new FileReader("d:\\MyWriter.txt");
        FileReader f1 =new FileReader("d:\\MyWriter.txt");
        BufferedReader b = new BufferedReader(f);
        BufferedReader b1 = new BufferedReader(f1);

        String s;
        int i=0;
        int j=0;
        while((s = b.readLine())!=null){
            ++i;
        }
        b.close();
//用變數i來統計總共幾筆資料

        String[][] t =new String[10][];
        while((s = b1.readLine())!=null){
            t[j]=s.split(":");
            ++j;
        }
        b1.close();
//逐行讀取,切割分開後用陣列方式存放

        for(int k=0;k<i;k++){
            System.out.println(t[k][0]+":"+t[k][1]);
        }
    }
}

執行結果:
jack:10
mary:20
tom:30
mark:40


透過把檔案丟到陣列再進行讀取,但實際上不用用到這麼麻煩

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

class Print{
    public static void main(String[] args) throws IOException{
      
        FileReader f =new FileReader("d:\\MyWriter.txt");
        BufferedReader b = new BufferedReader(f);
        String s;

        while((s = b.readLine())!=null){
            System.out.println(s);
        }
        b.close();
//這邊改為判斷檔案非空值就列印,如此也可以做到印出整個文件
    }
}

執行結果:
jack:10
mary:20
tom:30
mark:40



檔案的寫入

寫一個程式,可以儲存人名和成績

import java.util.Scanner;
import java.io.FileWriter;
import java.io.IOException;

class Write{
    public static void main(String[] args)throws IOException{
   
        FileWriter f = new FileWriter("d:\\MyWriter.txt", true);
//透過FileWriter來指定位置跟存入一個檔案
   
        Scanner s = new Scanner(System.in);

        System.out.println("請輸入性名");
        String name = s.nextLine();
        System.out.println("請輸入分數");
        int sc = s.nextInt();
       
        f.write(name+":"+sc+"\n");
        f.close();
    }
}

執行後生成一個檔案
檔案內容:
jack:10
mary:20
tom:30
mark:40


多載(Overload)

多載的方式可以用在類別和建構式內,讓程式依照輸入參數的不同去選擇適當的處理方式執行
例如:買蘋果

class apple{
    int app1;
    int app2;
    int sum;
   
    apple(){
        app1 = 0;
        app2 = 0;
    }
//沒有給參數的建構式

    apple(int a, int b){
        app1 = a;
        app2 = b;
    }
//有給參數的建構式

    public void show(){
        sum=app1*15+app2*12;
        System.out.println("共買富士蘋果"+app1+"顆,梨山蘋果"+app2+"顆");
        System.out.println("一共是"+sum+"元");       
    }
   
}

public class Buy{
    public static void main(String[] args){
        apple x = new apple();
        x.show();
       
        apple y = new apple(10,20);
        y.show();
    }
}

執行結果:
共買富士蘋果0顆,梨山蘋果0顆
一共是0元
共買富士蘋果10顆,梨山蘋果20顆
一共是390元


可以觀察到程式自動根據有無參數去呼叫適當的建構式使用了


建構式(Constructor)

建構式的用法,是在建立物件的起始值
例如:寫一個程式做簡單計算第幾次執行

class X{
    int num;
   
    public X(){
        num = 0;
        System.out.println("開始執行");
    }

//Constructor做為初始值
//一定使用class的名稱做為Constructor的名稱
//建構式不能有傳回值
    public void show(){
        num++;
        System.out.println("第"+num+"次執行");
    }
}

public class Constructor{
    public static void main(String[] args){
        X a=new X();
//建立一個新的物件a
        a.show();
        a.show();
    }
}

執行結果:
開始執行
第1次執行
第2次執行


但若是沒有定義Constructor,在java程式會自動準備一個沒有參數的建構式來使用
像是改成:
    public X(){

            }

也是可以被執行

結果如下:
第1次執行
第2次執行

方法的類型(method)

method分為兩種類型

instance method
class test{
    void ln(){
        System.out.println("Hello");
    }
}

public class Imethod{
    public static void main(String[] args){
        test a=new test();
        a.ln();

//要先建立新物件後才能使用
    }
}
class method
class test{
    static void ln(){
        System.out.println("Hello");
    }
}

public class Cmethod{
    public static void main(String[] args){
        test.ln();
//不需要建立物件就可以使用
    }
}

兩者差別在於,class method有用static宣告
instance method再使用前要先建立物件,class method則不用,可以立即被使用
並且class method只能使用class field

變數的類型(Field)

基本上分三類
Class Field (Static)
class vclass {
  static int a=100;
// class field
  static void va(){
     System.out.println(++a);
  }
}

不在method內,前面有static宣告,置於method area
classfield可以被整個運作中的java程式使用,所以又稱為全域變數
再使用上也可以不用給初值

Instance Field (Heap)
public class vinstance {
    int a=100; 
}
 
沒有寫在method內,也沒有static宣告,再JVM記憶體內配置在heap
有效的範圍只在整個class內

Local Field (Stack)  
class vlocal {
  void z(){
     int  b; 
// b 沒有設定初值, 這行單獨編譯是會成功
     System.out.println(b); 
// 編譯時出現 vlocal.java:4: variable b might not have been initialized
  }
}
 
在method內宣告的變數,再JVM記憶體內配置在stack 
在使用上,一定要有初值
而且不能有static宣告

2012年12月15日 星期六

作業練習-字串切割

題目:將 "2008-10-31" 這字串轉換成 "2008 年 10 月 31 日"

class HomeWork2{
    public static void main(String[] args){
   
      String d = "2008-10-31";
      String[] t; 
//字串物件陣列變數
     
      t=d.split("-");
//把字串切割後用陣列存放
     
      System.out.println(t[0] +"年" + t[1] +"月"+ t[2] +"日");
   
    }
}

執行結果
2008年10月31日

作業練習-數字排列

寫出一個程式,限定數字1-9,讓數字可以做到下面的排列
/*
輸入數字 : 3  
1
 2
333

輸入數字 : 5
1
 2
  3
   4
55555
*/

程式如下

import java.util.Scanner;
import java.io.IOException;
public class HomeWork1{

    public static void main(String[] args) throws IOException{
   
        Scanner k = new Scanner(System.in);
       
         int c;
         do{
              System.out.println("請輸入數字:");
             c = k.nextInt();

         }while (c<1 || c>9);
//判斷數字範圍再1-9
        
         String str="", sp="";
         for(int i=1;i<c;i++){
           System.out.println(sp+i);
           sp += ' ';
//透過sp來延伸空格的長度,然後在加上數字
           str=str+c;        
//str用來累計最後要的數字的長度
         }
         System.out.println(str+c);
            
    }
}

這樣就可以求得想要的結果

作業練習-根據老師給的程式寫出比較用的程式

作業程式如下:
class StudentDO {
   String name;
   int chinese;
   int english;
   int math;

   // 建構子 (無參數)
   StudentDO () {
     this("Nobody",0,0,0);
   }
   // 建構子 (四個參數)
   StudentDO (String n, int c, int e, int m){
     name=n;
     chinese=c;
     english=e;
     math=m;
   }
}

public class TestStudentDB {
   public static void main(String[] argv) {
      StudentDO x = new StudentDO("Tom",12,23,33);
      StudentDO y = new StudentDO("Mary",55,44,33);

      // 在此加入程式, 比較 x 與 y 物件大小 (根據物件中數字總合來比較),
      // 將大的物件總和顯示出來
   }
}

下面是作業的答案

class StudentDO {
   String name;
   int chinese;
   int english;
   int math;

   // 建構子 (無參數)
   StudentDO () {
     this("Nobody",0,0,0);
   }
   // 建構子 (四個參數)
   StudentDO (String n, int c, int e, int m){
     name=n;
     chinese=c;
     english=e;
     math=m;
   }
}

public class TestStudentDB {
   public static void main(String[] argv) {
      StudentDO x = new StudentDO("Tom",12,23,33);
      StudentDO y = new StudentDO("Mary",55,44,33);
    
      if((x.chinese+x.english+x.math)>(y.chinese+y.english+y.math))
         System.out.print(x.chinese+x.english+x.math);
      if((x.chinese+x.english+x.math)<(y.chinese+y.english+y.math))
         System.out.print(y.chinese+y.english+y.math);
      if((x.chinese+x.english+x.math)==(y.chinese+y.english+y.math))
         System.out.print("相等");     

   }
}

執行結果
132

但是這樣寫是把事先準備好的instancefield呼叫下來使用

也可以準備一個method去處理


class StudentDO {
   String name;
   int chinese;
   int english;
   int math;

   // 建構子 (無參數)
   StudentDO () {
     this("Nobody",0,0,0);
   }
   // 建構子 (四個參數)
   StudentDO (String n, int c, int e, int m){
     name=n;
     chinese=c;
     english=e;
     math=m;
   }

   public int getSum() {
      return chinese + english + math;

//用這個method去做總和再來回傳
   }
}

public class StudentDB01 {
   public static void main(String[] argv) {
      StudentDO x = new StudentDO("Tom",12,23,33);
      StudentDO y = new StudentDO("Mary",55,44,33);

      if ( x.getSum() > y.getSum() )
         System.out.println(x.getSum());
      else
         System.out.println(y.getSum());

//直接用method做總和以後,再來做比較
   }
}

這樣子可以讓程式用method做完運算再來取得想要得值,可以讓整個程式看起來比較乾淨,method也可以重複被使用

作業練習-讀取log

課程作業:撈取log檔中的IP

import java.io.*;
import java.net.*;
import java.util.*;

public class  myLog01{
    public static void main(String[] args) throws IOException{
        URL fileURL = myLog01.class.getResource("/wiki.myruby.net-May-2012");
        FileReader fileReader = new FileReader(fileURL.getFile());
        BufferedReader bReader = new BufferedReader(fileReader);
       
   
        String y;
        int x=0;
        while((y = bReader.readLine()) != null ){
            ++x;
        }
        System.out.println("此文件總共"+x+"個IP");

//用來逐行讀取,先求出ip總數(含重複)
       
        String[] t;
        String[] ip=new String[x];
        int i=0;
       
        FileReader f1=new FileReader(fileURL.getFile());
        BufferedReader b1 =new BufferedReader(f1);
        while((y = b1.readLine()) != null ) {
               t=y.split(" ");
               ip[i]=t[0];
               ++i;
        }
//再從頭讀取一次,然後用空格做分割,再取出第一個位置的值直接放到ip陣列內

        Arrays.sort(ip);
//把ip按照順序排列一次

        int s=0;
        int m = 0;
        int j;
        for(j=0; j<x-1 ; j++) {
            if(ip[j].equals(ip[j+1])!=true){
                s=j-s;
                System.out.println(ip[j]+" 造訪 "+s+" 次");
                s=j;
                m++;
            }       
        }
        System.out.println(m+"個ip造訪");
    }
}
//把陣列ip值做比較,後一個等於前一個的時候就做累計,用來求出同個ip有幾次的造訪
//最後再統計扣掉重複以後,總共是幾個ip造訪

檔案 wiki.myruby.net-May-2012 內容大概都像下面這樣

1.202.218.8 - - [30/Apr/2012:07:04:47 -0600] "GET /robots.txt HTTP/1.0" 404 2554 "-" "\"Mozilla/5.0"

最後運算的結果如下(取一行)

1.202.218.8 造訪 206 次

 這個程式是用來練習撈取ip記錄檔,像大型網站要看有多少訪客的時候就可能用上這樣的程式撈資料


類別與物件

類別
java內的class檔
可以想像為一個東西的"規格"
因此在大量作相同屬性物件的時候
可以透過使用類別方式提升效率

物件
實際上,物件當中可以包含了資料和方法
也可以透過類別來產生物件

有夠模糊的說明
講白了就是
java將很多類型相同的功能都集合在同一個類別檔內
這些類型相同的功能就形成了類別

物件則就好比一個物體
這個東西可以純粹的只是物體
也可以是包含產生物體的機器
當然這台機器就有生產物體的方法