Home
Explore Uedu
Student Console
Register as Member/Login
Research Informed Consent Center
Survey Center
Educational Big Data Challenge
Claim or create a course
Try a Scenario Task
Support & Messages
Uptime Data

UeduGPTs

--

Jupyters

4

Local AI

--

Uedu Code

--

Taipei AQI 57 · Taichung AQI 74 · Tainan AQI 63 · Kaohsiung AQI 64
AI Reply Desktop Notifications

Show a desktop notification when the AI TA finishes replying

Chat Message Notifications

Notify me when classmates post messages in the forum

Sound notification

Play an alert sound whenever there is a new notification

Unit 02

變數與資料型態

八種基本型別、String、型別轉換與 var
Interaction實作 20 minute(s)

Learning Objectives

  • 說出八種基本型別的用途與大致範圍,依資料選擇合適的型別
  • 宣告變數並給初始值,遵守命名規則;知道區域變數使用前一定要先給值,final 常數只能給一次值
  • 分辨自動的寬化轉換與手動的強制轉型,預測小數被截掉與整數溢位的結果
  • 說明 char 與 int 的關係,會寫 long 與 float 的字面值(L、f)
  • 用 var 宣告區域變數,用 Scanner 讀入資料,避開 nextInt() 之後接 nextLine() 的陷阱

變數與型別

程式要記住資料,就要用變數(variable):替一塊記憶體取個名字,之後用名字存取裡面的值。

Java 的每個變數都有固定的型別(type),宣告時就要寫出來,之後只能放這種型別的值:

int age = 20;        // age 只能放整數
age = 21;            // 可以:換成另一個整數
age = "二十一";       // 編譯錯誤:String 不能放進 int

學過 Python 的人要特別注意:Python 的變數可以先放數字、再放字串;Java 不行。這個限制的好處是,型別放錯的錯誤在編譯時就會被抓出來, 不用等到程式跑到那一行才出事。型別也決定了一個值占多少記憶體、能表示多大的數字、可以做哪些運算。

八種基本型別

Java 內建八種基本型別(primitive type),變數裡直接存放值:

型別 大小 範圍或精確度 用途
byte 8 位元 -128~127 檔案、網路的原始資料
short 16 位元 -32,768~32,767 較少使用
int 32 位元 約 ±21 億(-2,147,483,648~2,147,483,647) 整數的預設選擇
long 64 位元 約 ±9.22 × 10¹⁸ 超過 21 億的整數,例如時間毫秒數、人口總數
float 32 位元 約 7 位有效數字 記憶體很吃緊時才用
double 64 位元 約 15~16 位有效數字 小數的預設選擇
char 16 位元 一個字元(Unicode 編號 0~65535) 單一字元,用單引號:'A'、'學'
boolean 不指定 只有 true、false 是非判斷

程式裡直接寫出來的值叫字面值(literal),它也有型別:

  • 整數字面值預設是 int。超過 int 範圍的數字要在後面加 L:long big = 3000000000L;,不加會編譯錯誤 integer number too large
  • 小數字面值預設是 double。要放進 float 必須加 f:float r = 0.75f;,寫成 float r = 0.75; 會編譯錯誤(double 轉 float 可能損失精確度)
  • 長數字可以用底線分隔,方便閱讀:int population = 23_400_000;

八種基本型別的變數

每執行一行宣告,左邊的框架就多一個變數。注意 long 的字面值後面有 L、float 的字面值後面有 f。最後兩行印出 int 與 long 能表示的最大值:Integer.MAX_VALUE、Long.MAX_VALUE。

宣告、初始化與命名

int score;            // 宣告:告訴編譯器有一個 int 變數叫 score
score = 80;           // 指派:把 80 放進去(第一次指派就是初始化)
int bonus = 10;       // 宣告的同時初始化(最常見)
int a = 1, b = 2;     // 一次宣告好幾個同型別的變數

區域變數使用前一定要先給值。 寫在方法裡的變數叫區域變數(local variable),它沒有預設值。 宣告了 int x; 就直接 System.out.println(x);,javac 會回報 variable x might not have been initialized,程式無法編譯。

命名規則(違反就無法編譯):

  • 只能用英文字母、數字、底線 _、錢字號 $,而且不能以數字開頭
  • 不能使用關鍵字,例如 int、class、public、new
  • 大小寫有別:score 和 Score 是兩個不同的變數

命名慣例(不會報錯,但大家都這樣寫):變數與方法用小駝峰 totalPrice,類別用大駝峰 Main,常數全大寫加底線 TAX_RATE。名稱要能看出用途,price 比 p 好懂。

final 常數:宣告時加上 final,這個變數就只能給一次值,之後再指派會編譯錯誤 cannot assign a value to final variable。 適合用在不該被改的值,例如稅率、每頁筆數。

String:文字用 String 型別,值用雙引號。String 不是基本型別,而是參照型別:變數存的是指向字串物件的參照(第 08 課詳細說明物件與參照)。 它有很多好用的方法,例如 name.length() 取得字數。

宣告、指派與常數

第 4 行只宣告、沒有給值,執行後 score 出現在框架裡,但還沒有值;第 5 行指派之後才能使用。第 12 行的 TAX_RATE 加了 final,之後任何指派都會被編譯器擋下。

想一想:宣告與型別

1.

下面的程式執行結果是?

public class Main {
    public static void main(String[] args) {
        int count;
        System.out.println("開始");
        System.out.println(count);
    }
}
2.

下面哪一個變數宣告可以通過編譯?

3.

下面的程式會印出什麼?

public class Main {
    public static void main(String[] args) {
        long n = 2147483647 + 1;
        System.out.println(n);
    }
}
第 1 次作答

型別轉換:寬化與強制轉型

把一種型別的值放進另一種型別的變數,分成兩種情況:

寬化轉換(widening):從範圍小的型別轉到範圍大的型別,不會失去資料,Java 自動進行。

byte → short → int → long → float → double
               char ↗
int count = 7;
long big = count;      // 自動轉成 long
double avg = count;    // 自動轉成 7.0

強制轉型(casting):反過來從大轉小,可能失去資料,Java 不會自動做,必須在值前面寫 (型別):

double price = 3.99;
int whole = (int) price;    // 3:小數部分直接截掉,不是四捨五入
int neg = (int) -2.7;       // -2:往 0 的方向截斷
byte b = (byte) 200;        // -56:超出 byte 範圍,只留下最低 8 位元

沒寫 (int) 就把 double 放進 int,javac 會回報 incompatible types: possible lossy conversion from double to int。 要四捨五入請用 Math.round(price)(第 03 課)。

整數溢位:int 運算的結果超過範圍時,Java 不會報錯,而是繞回另一端:Integer.MAX_VALUE + 1 等於 Integer.MIN_VALUE(-2147483648)。 數字可能很大時,一開始就用 long。

寬化、截斷與溢位

前三行是自動的寬化轉換。第 10、11 行用 (int) 強制轉型,注意 -2.7 變成 -2 而不是 -3。第 15 行加 1 之後,動畫會跳出 int 溢位的提醒。

char 與 int

char 存的其實是字元在 Unicode 裡的編號(16 位元的無號整數):'A' 是 65、'a' 是 97、'0' 是 48。所以:

  • char 可以自動轉成 int:int code = 'A'; 得到 65
  • char 參與運算時會先變成 int:'A' + 1 的結果是 int 66,要變回字元得寫 (char) ('A' + 1)
  • c++ 和 c += 1 會自動轉回 char,結果是下一個字元
  • 數字字元換成數值:'7' - '0' 等於 7

注意 'A'(char,單引號)和 "A"(String,雙引號)是不同的型別。"" + 'A' + 1 會得到字串 "A1",因為字串接任何東西都變成字串(第 03 課)。

字元的編號

比較第 14 行與第 15 行:同樣是 'A' 和 1,前面有沒有字串,結果完全不同。

var 與讀入資料

var(Java 10 起):宣告區域變數時,如果右邊的值已經清楚表明型別,可以用 var 讓編譯器推斷:

var count = 3;        // 推斷為 int
var unit = 12.5;      // 推斷為 double
var label = "杯";     // 推斷為 String

var 不是「什麼都能放」:推斷完型別就固定了,count = 2.5; 一樣會編譯錯誤。var 只能用在有初始值的區域變數,var x; 不行。 初學時建議先寫出完整型別,等熟悉了再用 var 省略冗長的型別名稱。

用 Scanner 讀入資料:Scanner 是 Java 標準函式庫的類別,放在 java.util 套件裡,所以檔案最上面要先 import:

import java.util.Scanner;              // 寫在 public class 之前

Scanner sc = new Scanner(System.in);   // System.in 是鍵盤輸入
int age = sc.nextInt();                // 讀一個整數
double h = sc.nextDouble();            // 讀一個小數
String word = sc.next();               // 讀一個詞(遇到空白就停)
String line = sc.nextLine();           // 讀一整行(含空白)

陷阱:nextInt() 只讀走數字,使用者按下的 Enter(換行)還留在輸入裡。緊接著呼叫 nextLine(),它讀到的是這個剩下的換行,結果是空字串。 解決方法是在 nextInt() 之後先多呼叫一次 sc.nextLine(); 把換行讀掉,再讀真正要的那一行。

var 與 Scanner 的陷阱

這支程式的輸入是兩行:20 和 Amy Chen。動畫中讀入的內容會顯示在輸出裡,就像在終端機打字。注意第 14 行:nextLine() 讀到的是第一行剩下的換行,rest 是空字串;第 16 行才讀到姓名。

想一想:轉型、字元與輸入

1.

下面的程式會印出什麼?

public class Main {
    public static void main(String[] args) {
        double d = 9.99;
        int i = (int) d;
        System.out.println(i + " " + (int) -9.99);
    }
}
2.

下面的程式會印出什麼?

public class Main {
    public static void main(String[] args) {
        char c = 'A';
        c += 2;
        System.out.println(c);
        System.out.println('A' + 2);
    }
}
3.

使用者輸入兩行:第一行 5,第二行 Bob。執行下面的程式碼之後,s 的值是什麼?

Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
String s = sc.nextLine();
第 1 次作答

動手寫:飲料訂單

程式要依序讀入商品名稱、單價、數量,算出總價,再用強制轉型得到去掉小數的金額。把五個 TODO 的 0 或 "" 換成正確的寫法。這次的輸入是 珍珠奶茶、55.5、3(讀入的內容會出現在輸出裡)。

你的輸出
預期輸出

重點回顧

  • 八種基本型別:byte、short、int、long、float、double、char、boolean;整數預設用 int、小數預設用 double
  • 超過 int 範圍的字面值加 L,float 字面值加 f
  • 區域變數使用前一定要先給值;final 變數只能給一次值;名稱不能以數字開頭、不能用關鍵字
  • String 是參照型別,存文字;'A' 是 char、"A" 是 String
  • 小轉大自動寬化;大轉小要寫 (型別) 強制轉型,小數會往 0 截斷,超出範圍只留下低位元
  • int 運算溢位不會報錯,結果繞回另一端;char 參與運算會先變成 int
  • var 讓編譯器推斷區域變數的型別,推斷後型別固定
  • Scanner 要 import java.util.Scanner;;nextInt() 之後接 nextLine() 會讀到空字串
這一課完成了!
Next lesson:運算子與運算式
你目前沒有登入,學習進度只存在這個瀏覽器。 Login 後,進度會跟著帳號,換電腦也看得到。