java基礎之java程序基礎(二)--變量和數據類型


前言....

本節我們將介紹Java程序的基礎知識,包括:

  • Java程序基本結構

  • 變量和數據類型

  • 整數運算

  • 浮點數運算

  • 布爾運算

  • 字符和字符串

  • 數組類型

java-basic

Java程序基本結構

我們先剖析一個完整的Java程序,它的基本結構是什么:

**
 * 可以用來自動創建文檔的注釋
 */
public class Hello {
    public static void main(String[] args) {
        // 向屏幕輸出文本:
        System.out.println("Hello, world!");
        /* 多行注釋開始
        注釋內容
        注釋結束 */
    }
} // class定義結束

因為Java是面向對象的語言,一個程序的基本單位就classclass是關鍵字,這里定義的class名字就是Hello

public class Hello { // 類名是Hello
    // ...
} // class定義結束

類名要求

  • 類名必須以英文字母開頭,后接字母,數字和下划線的組合
  • 習慣以大寫字母開頭

要注意遵守命名習慣,好的類命名:

  • Hello
  • NoteBook
  • VRPlayer

不好的類命名

  • hello
  • Good123
  • Note_Book
  • _World

注意到public是訪問修飾符,表示該class是公開的。

不寫public,也能正確編譯,但是這個類將無法從命令行執行

class內部,可以定義若干方法(method):

public class Hello {
    public static void main(String[] args) { // 方法名是main
        // 方法代碼...
    } // 方法定義結束
}

這里的方法名是main,返回值是void,表示沒有任何返回值。

我們注意到public除了可以修飾class外,也可以修飾方法。而關鍵字static是另一個修飾符,它表示靜態方法,后面我們會講解方法的類型,目前,我們只需要知道,Java入口程序規定的方法必須是靜態方法,方法名必須為main,括號內的參數必須是String數組。

方法名也有命名規則,命名和class一樣,但是首字母小寫:

在方法內部,語句才是真正的執行代碼。Java的每一行語句必須以分號結束:

public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello, world!"); // 語句
    }
}

在Java程序中,注釋是一種給人閱讀的文本,不是程序的一部分,所以編譯器會自動忽略注釋。

Java有3種注釋,第一種是單行注釋,以雙斜線開頭,直到這一行的結尾結束:

// 這是注釋...

而多行注釋以/*星號開頭,以*/結束,可以有多行:

/*
這是注釋
blablabla...
這也是注釋
*/

還有一種特殊的多行注釋,以/**開頭,以*/結束,如果有多行,每行通常以星號開頭:

/**
 * 可以用來自動創建文檔的注釋
 * 
 * @auther liaoxuefeng
 */
public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello, world!");
    }
}

這種特殊的多行注釋需要寫在類和方法的定義處,可以用於自動創建文檔。

變量和數據類型

變量

什么是變量?

變量就是初中數學的代數的概念,例如一個簡單的方程,x,y都是變量:

y=x^2+1y=x2+1

在Java中,變量分為兩種:基本類型的變量和引用類型的變量。

我們先討論基本類型的變量。

在Java中,變量必須先定義后使用,在定義變量的時候,可以給它一個初始值。例如:

int x = 1;

上述語句定義了一個整型int類型的變量,名稱為x,初始值為1

來看一個完整的定義變量,然后打印變量值的例子:

// 定義並打印變量
public class Main {
    public static void main(String[] args) {
        int x = 100; // 定義int類型變量x,並賦予初始值100
        System.out.println(x); // 打印該變量的值
    }
}
100

變量的一個重要特點是可以重新賦值。例如,對變量x,先賦值100,再賦值200,觀察兩次打印的結果:

public class Main {
    public static void main(String[] args) {
        int x = 100; // 定義int類型變量x,並賦予初始值100
        System.out.println(x); // 打印該變量的值,觀察是否為100
        x = 200; // 重新賦值為200
        System.out.println(x); // 打印該變量的值,觀察是否為200
    }
}
100
200

注意到第一次定義變量x的時候,需要指定變量類型int,因此使用語句int x = 100;。而第二次重新賦值的時候,變量x已經存在了,不能再重復定義,因此不能指定變量類型int,必須使用語句x = 200;

變量不但可以重新賦值,還可以賦值給其他變量。讓我們來看一個例子:

//變量之間賦值
public
class Main { public static void main(String[] args) { int n = 100; // 定義變量n,同時賦值為100 System.out.println("n = " + n); // 打印n的值 n = 200; // 變量n賦值為200 System.out.println("n = " + n); // 打印n的值 int x = n; // 變量x賦值為n(n的值為200,因此賦值后x的值也是200) System.out.println("x = " + x); // 打印x的值 x = x + 100; // 變量x賦值為x+100(x的值為200,因此賦值后x的值是200+100=300) System.out.println("x = " + x); // 打印x的值 System.out.println("n = " + n); // 再次打印n的值,n應該是200還是300? } }

我們一行一行地分析代碼執行流程:

執行int n = 100;,該語句定義了變量n,同時賦值為100,因此,JVM在內存中為變量n分配一個“存儲單元”,填入值100

      n
      │
      ▼
┌───┬───┬───┬───┬───┬───┬───┐
│   │100│   │   │   │   │   │
└───┴───┴───┴───┴───┴───┴───┘

執行n = 200;時,JVM把200寫入變量n的存儲單元,因此,原有的值被覆蓋,現在n的值為200

      n
      │
      ▼
┌───┬───┬───┬───┬───┬───┬───┐
│   │200│   │   │   │   │   │
└───┴───┴───┴───┴───┴───┴───┘

執行int x = n;時,定義了一個新的變量x,同時對x賦值,因此,JVM需要新分配一個存儲單元給變量x,並寫入和變量n一樣的值,結果是變量x的值也變為200

      n           x
      │           │
      ▼           ▼
┌───┬───┬───┬───┬───┬───┬───┐
│   │200│   │   │200│   │   │
└───┴───┴───┴───┴───┴───┴───┘

執行x = x + 100;時,JVM首先計算等式右邊的值x + 100,結果為300(因為此刻x的值為200),然后,將結果300寫入x的存儲單元,因此,變量x最終的值變為300


      n           x
      │           │
      ▼           ▼
┌───┬───┬───┬───┬───┬───┬───┐
│   │200│   │   │300│   │   │
└───┴───┴───┴───┴───┴───┴───┘

可見,變量可以反復賦值。注意,等號=是賦值語句,不是數學意義上的相等,否則無法解釋x = x + 100

基本數據類型

基本數據類型是CPU可以直接進行運算的類型。Java定義了以下幾種基本數據類型:

  • 整數類型:byte,short,int,long

  • 浮點數類型:float,double

  • 字符類型:char

  • 布爾類型:boolean

Java定義的這些基本數據類型有什么區別呢?要了解這些區別,我們就必須簡單了解一下計算機內存的基本結構。

計算機內存的最小存儲單元是字節(byte),一個字節就是一個8位二進制數,即8個bit。它的二進制表示范圍從00000000~11111111,換算成十進制是0~255,換算成十六進制是00~ff

內存單元從0開始編號,稱為內存地址。每個內存單元可以看作一間房間,內存地址就是門牌號。

  0   1   2   3   4   5   6  ...
┌───┬───┬───┬───┬───┬───┬───┐
│   │   │   │   │   │   │   │...
└───┴───┴───┴───┴───┴───┴───┘

一個字節是1byte,1024字節是1K,1024K是1M,1024M是1G,1024G是1T。一個擁有4T內存的計算機的字節數量就是:

4T = 4 x 1024G
   = 4 x 1024 x 1024M
   = 4 x 1024 x 1024 x 1024K
   = 4 x 1024 x 1024 x 1024 x 1024
   = 4398046511104

不同的數據類型占用的字節數不一樣。我們看一下Java基本數據類型占用的字節數:

       ┌───┐
  byte │   │
       └───┘
       ┌───┬───┐
 short │   │   │
       └───┴───┘
       ┌───┬───┬───┬───┐
   int │   │   │   │   │
       └───┴───┴───┴───┘
       ┌───┬───┬───┬───┬───┬───┬───┬───┐
  long │   │   │   │   │   │   │   │   │
       └───┴───┴───┴───┴───┴───┴───┴───┘
       ┌───┬───┬───┬───┐
 float │   │   │   │   │
       └───┴───┴───┴───┘
       ┌───┬───┬───┬───┬───┬───┬───┬───┐
double │   │   │   │   │   │   │   │   │
       └───┴───┴───┴───┴───┴───┴───┴───┘
       ┌───┬───┐
  char │   │   │
       └───┴───┘

byte恰好就是一個字節,而longdouble需要8個字節。

整型

對於整型類型,Java只定義了帶符號的整型,因此,最高位的bit表示符號位(0表示正數,1表示負數)。各種整型能表示的最大范圍如下:

byte:-128 ~ 127
short: -32768 ~ 32767
int: -2147483648 ~ 2147483647
long: -9223372036854775808 ~ 9223372036854775807
//定義整形
public
class Main { public static void main(String[] args) { int i = 2147483647; int i2 = -2147483648; int i3 = 2_000_000_000; // 加下划線更容易識別 int i4 = 0xff0000; // 十六進制表示的16711680 int i5 = 0b1000000000; // 二進制表示的512 long l = 9000000000000000000L; // long型的結尾需要加L } }

特別注意:同一個數的不同進制的表示是完全相同的,例如15=0xf0b1111

浮點型

浮點類型的數就是小數,因為小數用科學計數法表示的時候,小數點是可以“浮動”的,如1234.5可以表示成12.345x102,也可以表示成1.2345x103,所以稱為浮點數。

下面是定義浮點數的例子:

float f1 = 3.14f;
float f2 = 3.14e38f; // 科學計數法表示的3.14x10^38
double d = 1.79e308;
double d2 = -1.79e308;
double d3 = 4.9e-324; // 科學計數法表示的4.9x10^-324

對於float類型,需要加上f后綴。

浮點數可表示的范圍非常大,float類型可最大表示3.4x1038,而double類型可最大表示1.79x10308。

布爾類型

布爾類型boolean只有truefalse兩個值,布爾類型總是關系運算的計算結果:

boolean b1 = true;
boolean b2 = false;
boolean isGreater = 5 > 3; // 計算結果為true
int age = 12;
boolean isAdult = age >= 18; // 計算結果為false

Java語言對布爾類型的存儲並沒有做規定,因為理論上存儲布爾類型只需要1 bit,但是通常JVM內部會把boolean表示為4字節整數

字符類型

字符類型char表示一個字符。Java的char類型除了可表示標准的ASCII外,還可以表示一個Unicode字符:

//字符類型
public class Main {
    public static void main(String[] args) {
        char a = 'A';
        char zh = '中';
        System.out.println(a);
        System.out.println(zh);
    }
}
A
中

注意char類型使用單引號',且僅有一個字符,要和雙引號"的字符串類型區分開。

常量

定義變量的時候,如果加上final修飾符,這個變量就變成了常量:

inal double PI = 3.14; // PI是一個常量
double r = 5.0;
double area = PI * r * r;
PI = 300; // compile error!

常量在定義時進行初始化后就不可再次賦值,再次賦值會導致編譯錯誤。

常量的作用是用有意義的變量名來避免魔術數字(Magic number),例如,不要在代碼中到處寫3.14,而是定義一個常量。如果將來需要提高計算精度,我們只需要在常量的定義處修改,例如,改成3.1416,而不必在所有地方替換3.14

根據習慣,常量名通常全部大寫。

定義變量時,要遵循作用域最小化原則,盡量將變量定義在盡可能小的作用域,並且,不要重復使用變量名。

小結

Java提供了兩種變量類型:基本類型和引用類型

基本類型包括整型,浮點型,布爾型,字符型。

變量可重新賦值,等號是賦值語句,不是數學意義的等號。

常量在初始化后不可重新賦值,使用常量便於理解程序意圖

 

 

整數運算

 Java的整數運算遵循四則運算規則,可以使用任意嵌套的小括號。四則運算規則和初等數學一致。例如:

 

public class Main {
    public static void main(String[] args) {
        int i = (100 + 200) * (99 - 88); // 3300
        int n = 7 * (5 + (i - 9)); // 23072
        System.out.println(i);
        System.out.println(n);
    }
}

整數的數值表示不但是精確的,而且整數運算永遠是精確的,即使是除法也是精確的,因為兩個整數相除只能得到結果的整數部分:

int x = 12345 / 67; // 184

求余運算使用%

int y = 12345 % 67; // 12345÷67的余數是17

特別注意:整數的除法對於除數為0時運行時將報錯,但編譯不會報錯。

溢出

要特別注意,整數由於存在范圍限制,如果計算結果超出了范圍,就會產生溢出,而溢出不會出錯,卻會得到一個奇怪的結果

public class Main {
    public static void main(String[] args) {
        int x = 2147483640;
        int y = 15;
        int sum = x + y;
        System.out.println(sum); // -2147483641
    }
}

要解釋上述結果,我們把整數214748364015換成二進制做加法:

  0111 1111 1111 1111 1111 1111 1111 1000
+ 0000 0000 0000 0000 0000 0000 0000 1111
-----------------------------------------
  1000 0000 0000 0000 0000 0000 0000 0111

由於最高位計算結果為1,因此,加法結果變成了一個負數。

要解決上面的問題,可以把int換成long類型,由於long可表示的整型范圍更大,所以結果就不會溢出:

long x = 2147483640;
long y = 15;
long sum = x + y;
System.out.println(sum); // 2147483655

還有一種簡寫的運算符,即+=-=*=/=,它們的使用方法如下:

n += 100; // 3409, 相當於 n = n + 100;
n -= 100; // 3309, 相當於 n = n - 100;

自增/自減

Java還提供了++運算和--運算,它們可以對一個整數進行加1和減1的操作:

public class Main {
    public static void main(String[] args) {
        int n = 3300;
        n++; // 3301, 相當於 n = n + 1;
        n--; // 3300, 相當於 n = n - 1;
        int y = 100 + (++n); // 不要這么寫
        System.out.println(y);
    }
}

注意++寫在前面和后面計算結果是不同的,++n表示先加1再引用n,n++表示先引用n再加1。不建議把++運算混入到常規運算中,容易自己把自己搞懵了。

 

移位運算

在計算機中,整數總是以二進制的形式表示。例如,int類型的整數7使用4字節表示的二進制如下

00000000 0000000 0000000 00000111

可以對整數進行移位運算。對整數7左移1位將得到整數14,左移兩位將得到整數28

int n = 7;       // 00000000 00000000 00000000 00000111 = 7
int a = n << 1;  // 00000000 00000000 00000000 00001110 = 14
int b = n << 2;  // 00000000 00000000 00000000 00011100 = 28
int c = n << 28; // 01110000 00000000 00000000 00000000 = 1879048192
int d = n << 29; // 11100000 00000000 00000000 00000000 = -536870912

左移29位時,由於最高位變成1,因此結果變成了負數

類似的,對整數28進行右移,結果如下:

int n = 7;       // 00000000 00000000 00000000 00000111 = 7
int a = n >> 1;  // 00000000 00000000 00000000 00000011 = 3
int b = n >> 2;  // 00000000 00000000 00000000 00000001 = 1
int c = n >> 3;  // 00000000 00000000 00000000 00000000 = 0

如果對一個負數進行右移,最高位的1不動,結果仍然是一個負數

int n = -536870912;
int a = n >> 1;  // 11110000 00000000 00000000 00000000 = -268435456
int b = n >> 2;  // 10111000 00000000 00000000 00000000 = -134217728
int c = n >> 28; // 11111111 11111111 11111111 11111110 = -2
int d = n >> 29; // 11111111 11111111 11111111 11111111 = -1

還有一種不帶符號的右移運算,使用>>>,它的特點是符號位跟着動,因此,對一個負數進行>>>右移,它會變成正數,原因是最高位的1變成了0

int n = -536870912;
int a = n >>> 1;  // 01110000 00000000 00000000 00000000 = 1879048192
int b = n >>> 2;  // 00111000 00000000 00000000 00000000 = 939524096
int c = n >>> 29; // 00000000 00000000 00000000 00000111 = 7
int d = n >>> 31; // 00000000 00000000 00000000 00000001 = 1

byteshort類型進行移位時,會首先轉換為int再進行位移。

仔細觀察可發現,左移實際上就是不斷地×2,右移實際上就是不斷地÷2。

 

位運算

位運算是按位進行與、或、非和異或的運算。

與運算的規則是,必須兩個數同時為1,結果才為1

n = 0 & 0; // 0
n = 0 & 1; // 0
n = 1 & 0; // 0
n = 1 & 1; // 1

或運算的規則是,只要任意一個為1,結果就為1

n = 0 | 0; // 0
n = 0 | 1; // 1
n = 1 | 0; // 1
n = 1 | 1; // 1

非運算的規則是,01互換

n = ~0; // 1
n = ~1; // 0

異或運算的規則是,如果兩個數不同,結果為1,否則為0

n = 0 ^ 0; // 0
n = 0 ^ 1; // 1
n = 1 ^ 0; // 1
n = 1 ^ 1; // 0

對兩個整數進行位運算,實際上就是按位對齊,然后依次對每一位進行運算。例如:

public class Main {
    public static void main(String[] args) {
        int i = 167776589; // 00001010 00000000 00010001 01001101
        int n = 167776512; // 00001010 00000000 00010001 00000000
        System.out.println(i & n); // 167776512
    }
}

上述按位與運算實際上可以看作兩個整數表示的IP地址10.0.17.7710.0.17.0,通過與運算,可以快速判斷一個IP是否在給定的網段內。

運算優先級

在Java的計算表達式中,運算優先級從高到低依次是:

  • ()
  • ! ~ ++ --
  • * / %
  • + -
  • << >> >>>
  • &
  • |
  • += -= *= /=

 

類型自動提升與強制轉型

在運算過程中,如果參與運算的兩個數類型不一致,那么計算結果為較大類型的整型例如,shortint計算,結果總是int,原因是short首先自動被轉型為int

// 類型自動提升與強制轉型

 

public class Main {
    public static void main(String[] args) {
        short s = 1234;
        int i = 123456;
        int x = s + i; // s自動轉型為int
        short y = s + i; // 編譯錯誤!
    }
}

也可以將結果強制轉型,即將大范圍的整數轉型為小范圍的整數。強制轉型使用(類型)例如,將int強制轉型為short

int i = 12345;
short s = (short) i; // 12345

要注意,超出范圍的強制轉型會得到錯誤的結果,原因是轉型時,int的兩個高位字節直接被扔掉,僅保留了低位的兩個字節

public class Main {
    public static void main(String[] args) {
        int i1 = 1234567;
        short s1 = (short) i1; // -10617
        System.out.println(s1);
        int i2 = 12345678;
        short s2 = (short) i2; // 24910
        System.out.println(s2);
    }
}

因此,強制轉型的結果很可能是錯的。

 整數運算的結果永遠是精確的;

運算結果會自動提升;

可以強制轉型,但超出范圍的強制轉型會得到錯誤的結果;

應該選擇合適范圍的整型(intlong),沒有必要為了節省內存而使用byteshort進行整數運算。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM