No 3. 變量與數(shù)據(jù)類型
前情回顧
在之前的文章中,我們已經(jīng)介紹了如何搭建 Java 開發(fā)環(huán)境以及如何使用 IDEA,現(xiàn)在就開始正式學習 Java 語言的相關(guān)語法吧!??
本文內(nèi)容預告如下:
變量 數(shù)據(jù)類型 變量作用域 常量
變量
什么是變量?
所謂變量,就是用來命名一個數(shù)據(jù)的標識符,在 Java 中,變量又可以分為兩種:
基本類型的變量 引用類型的變量
// 基本類型的變量
int id = 1;
// 引用類型的變量
String name = "村雨遙";
其中 int 是基本數(shù)據(jù)類型,表示這是一個整型數(shù);而 String 則是引用類型,表示這是一個引用類型;
id 和 name 則是標識符,也就是我們所說的 變量;
= 則是賦值操作符,而 1 則是基本類型的值,村雨遙 則是引用類型的值;
變量的特點
變量的最重要的一個特點就是可以重新賦值;
public class Main {
public static void main(String[] args) {
// 定義int類型變量id,并賦予初始值1
int id = 1;
// 打印該變量的值,觀察是否為1
System.out.println(id);
// 重新賦值為2
id = 2;
// 打印該變量的值,觀察是否為2
System.out.println(id);
}
}
變量命名規(guī)則
變量命名也是一門學問,并不是我們想怎么命名就怎么命名,日常開發(fā)中最常見的變量命名規(guī)則主要有如下幾條:
強制:變量命名只能使用 字母(大小寫均可)、數(shù)字、$、_; 強制:變量名不能使用關(guān)鍵字(就是 Java 中內(nèi)置的一些關(guān)鍵字,如 int、for、long…);強制:變量第一個字符不能使用數(shù)字,只能用字母、 $、_;更多命名規(guī)則推薦參考阿里巴巴推出的《Java 開發(fā)手冊》,下載鏈接:https://github.com/cunyu1943/amazing-books
常見關(guān)鍵字
這是一種事先定義好的,有特定意義的標識符,也叫做保留字。對于 Java 編譯器有著特殊意義,用來表示一種數(shù)據(jù)類型,或者表示程序的結(jié)構(gòu)等。此外,關(guān)鍵字不能用作變量名、方法名、類名、包名和參數(shù)名。常見的關(guān)鍵字可以分為如下幾類,具體的關(guān)鍵字如下圖所示:
訪問控制類 類、方法及變量修飾符類 程序控制類 錯誤處理 包相關(guān) 基本類型 變量引用 保留字

數(shù)據(jù)類型
基本數(shù)據(jù)類型
Java 中,共有 8 中基本數(shù)據(jù)類型,由 Java 語言預定好的,每個數(shù)據(jù)類型都屬于關(guān)鍵字,而且每種基本變量都有其對應的封裝類,這 8 中基本數(shù)據(jù)類型分別是:
整型(4 種) 浮點型(2 種) 字符型(1 種) 布爾型(1 種)
| 數(shù)據(jù)類型 | bit | 字節(jié) | 封裝類 | 數(shù)據(jù)范圍 | 默認值 |
|---|---|---|---|---|---|
byte | 8 | 1 | Byte | ~ | 0 |
short | 16 | 2 | Short | ~ | 0 |
char | 16 | 2 | Character | \u0000 ~ \uffff( ~ ) | u0000 |
int | 32 | 4 | Integer | ~ | 0 |
long | 64 | 8 | Long | ~ | 0L |
float | 32 | 4 | Float | ~ | 0.0f |
double | 64 | 8 | Double | ~ | 0.0D |
boolean | 1 | 不確定 | Boolean | true 或 false | false |
注意:
boolean一般用 1bit來存儲,但是具體大小并未規(guī)定,JVM 在編譯期將boolean類型轉(zhuǎn)換為int,此時 1 代表true,0代表false。此外,JVM 還指出boolean數(shù)組,但底層是通過byte數(shù)組來實現(xiàn);使用 long類型時,需要在后邊加上L,否則將其作為整型解析,可能會導致越界;浮點數(shù)如果沒有明確指定 float還是double,統(tǒng)一按double處理;char是用 單引號''將內(nèi)容括起來,只能存放一個字符,相當于一個整型值(ASCII 值),能夠參加表達式運算;而String是用 雙引號""將內(nèi)容括起來,代表的是一個地址值;Java 在內(nèi)存中是采用 Unicode 表示,所以無論是一個中文字符還是英文字符,都能用 char來表示;
那么如何個一個基本類型變量賦值呢?
在 Java 中,基本數(shù)據(jù)類型屬于 Java 的一種內(nèi)置的特殊數(shù)據(jù)類型,不屬于任何類,所以可以直接對其進行賦值;給基本類型的變量賦值的方式就叫做 字面值;
float score = 89.0f;
int age = 26;
引用數(shù)據(jù)類型
常見引用數(shù)據(jù)類型
| 數(shù)據(jù)類型 | 默認值 |
|---|---|
| 數(shù)組 | null |
| 類 | null |
| 接口 | null |
而對于引用數(shù)據(jù)類型,我們經(jīng)常是需要 new 關(guān)鍵字來進行賦值,但是引用類型中的 接口是不能被實例化的,我們需要對其進行實現(xiàn);
// 初始化一個對象
Pet dog = new Pet();
// 初始化一個數(shù)組
int[] arr = new int[10];
String
對于引用數(shù)據(jù)類型中的 String,我們需要特別關(guān)注。
String 不同于 char,它屬于引用類型,而 char 屬于基本數(shù)據(jù)類型。用雙引號 "" 括起來表示字符串,一個字符串能夠保存 0 個到任意個字符,它一旦創(chuàng)建就不能被改變。
而針對字符串,如果我們要打印一些特殊的字符,比如字符串本來就包含 ",那么這個時候就需要借助于轉(zhuǎn)義字符 \,最常見的轉(zhuǎn)義字符主要有:
| 轉(zhuǎn)義字符 | 含義 |
|---|---|
\" | 字符 " |
\' | 字符 ' |
\\ | 字符 \ |
\n | 換行符 |
\t | 制表符 Tab |
\r | 回車符 |
那多個字符串之間或者字符串和其他類型數(shù)據(jù)之間,該如何進行連接呢?
Java 編譯器中,對于字符串和其他數(shù)據(jù)類型之間,可以使用 + 進行連接,編譯器會自動將其他數(shù)據(jù)類型自動轉(zhuǎn)換為字符串,然后再進行連接;
String既然是不可變,那有什么優(yōu)點呢?
用于緩存 hash值
由于 String 的 hash 值被頻繁使用,它的不可變性使得 hash 值也不可變,此時只需要進行一次計算;
字符串常量池(String Pool)的需要
如果一個 String 對象已經(jīng)被創(chuàng)建過,那么就會優(yōu)先從字符串常量池中獲取其引用,其不可變性確保了不同引用指向同一 String 對象;
安全性
我們經(jīng)常用 String 作為我們方法的參數(shù),其不變性能夠保證參數(shù)不可變;
線程安全
String 的不可變性讓它天生 具備線程安全,能夠在多個線程中方便使用而不用考慮線程安全問題。
String、StringBuilder、StringBuffer對比,該如何選擇?
| 可變性 | 線程安全 | 適用場景 | |
|---|---|---|---|
String | 不可變 | 安全 | 操作少量的數(shù)據(jù) |
StringBuffer | 可變 | 安全,內(nèi)部使用 synchronized 進行同步 | 多線程操作字符串緩沖區(qū)下操作大量數(shù)據(jù) |
StringBuilder | 可變 | 不安全 | 單線程操作字符串緩沖區(qū)下操作大量數(shù)據(jù),性能高于 StringBuffer |
通過
new String(“xxx”)創(chuàng)建字符串的兩種情況?
使用 new 的方式創(chuàng)建字符串對象,會有兩種不同的情況:
String Pool 中不存在 “xxx”
此時會創(chuàng)建兩個字符串對象,“xxx” 屬于字符串字面量,因此在編譯期會在 String Pool 中創(chuàng)建一個字符串對象,用于指向該字符串的字面量 “xxx”;然后 new 會在堆中創(chuàng)建一個字符串對象;

String Pool 中存在 “xxx”
此時只需要創(chuàng)建一個字符串對象,由于 String Pool 中已經(jīng)存在指向 “xxx” 的對象,所以直接在堆中創(chuàng)建一個字符串對象;

數(shù)據(jù)類型轉(zhuǎn)換
對于基本數(shù)據(jù)類型,不同類型之間是可以相互轉(zhuǎn)換的,但是需要滿足一定的條件;
從小到大自動轉(zhuǎn),從大到小強制轉(zhuǎn)。
即就是,對于低精度的數(shù)據(jù)類型,如果要轉(zhuǎn)換為高精度的數(shù)據(jù)類型,直接將低精度的值賦給高精度的值即可;
但對于高精度的數(shù)據(jù)類型,如果想要轉(zhuǎn)換為低精度的數(shù)據(jù)類型,則需要采用 強制轉(zhuǎn)換 的手段,但此時需要承擔精度丟失的風險,就像從一個大杯子往一個小杯子里倒水,你要做好小杯子可能裝不下溢出的情況;
int a = 110;
long b = 113;
// 低精度轉(zhuǎn)高精度,由于 long 的范圍比 int 大,所以可以自動轉(zhuǎn)
b = a;
// 高精度住哪低精度,由于 long 的范圍比 int 大,所以需要強制轉(zhuǎn)
a = (int)b;
隱式轉(zhuǎn)換(自動類型轉(zhuǎn)換)
當滿足如下條件時,如果將一種類型的數(shù)據(jù)賦值給另一種數(shù)據(jù)類型變量時,將執(zhí)行自動類型轉(zhuǎn)換:
兩種數(shù)據(jù)類型彼此兼容; 目標數(shù)據(jù)類型的取值范圍大于源數(shù)據(jù)類型;
一般而言,隱式轉(zhuǎn)換的規(guī)則是從低級類型數(shù)據(jù)轉(zhuǎn)換為高級類型數(shù)據(jù),對應規(guī)則如下:
數(shù)值類型: byte -> short -> int -> long -> float -> double字符類型轉(zhuǎn)整型: char -> int
顯式轉(zhuǎn)換(強制類型轉(zhuǎn)換)
那既然滿足上述兩個條件時會發(fā)生隱式轉(zhuǎn)換,那不滿足同時我們又想進行數(shù)據(jù)類型轉(zhuǎn)換時,我們該怎么辦呢?
這個時候就需要我們的 顯式轉(zhuǎn)換 登場了,其語法格式如下:
(type) variableName;
我們舉個 ?? 來說下:
int num = 3;
double ans = 5.0;
// 要將 double 類型的值賦值給 int,則需要強制轉(zhuǎn)換
num = (int)ans;
注意: 強制轉(zhuǎn)換可能會導致精度丟失,所以一般情況下盡量能不用就不用。
常見數(shù)據(jù)類型轉(zhuǎn)換方法
字符串與其他類型之間的轉(zhuǎn)換
其他類型 -> 字符串
調(diào)用類的串轉(zhuǎn)換方法: X.toString();自動轉(zhuǎn)換: "" + X;利用 String的方法:String.valueOf(X);
// 方法 1
String str1 = Integer.toString(int num);
String str2 = Long.toString(long num);
String str3 = Float.toString(flaot num);
String str4 = Double.toString(double num);
// 方法 2
String str = "" + num ; // num 是 int、long、float、double 類型
// 方法 3
String str1 = String.valueOf(int num);
String str2 = String.valueOf(long num);
String str3 = String.valueOf(float num);
String str4 = String.valueOf(double num);
字符串 - > 其他類型
調(diào)用 parseXXX方法,比如parseLong、parseFloat、parseDouble...;先調(diào)用 valueOf(),方法,然后再調(diào)用xxxValue()方法;
// 方法 1
int num1 = Integer.parseInt(String str);
Long num2 = Long.parseLong(String str);
Float num3 = Float.parseFloat(String str);
Double num4 = Double.parseDouble(String str);
// 方法 2
int num1 = Integer.valueOf(String str).intValue();
Long num2 = Long.valueOf(String str).longValue();
Float num1 = Float.valueOf(String str).floatValue();
Double num1 = Double.valueOf(String str).doubleValue();
int、float、double 之間的轉(zhuǎn)換
float -> double
float num = 1.0f;
Float num1 = new Float(num);
double num2 = num1.doubleValue();
double -> float
double num = 100.0;
float num1 = (float)num;
double -> int
double num = 100.0;
Double num1 = new Double(num);
int num2 = num1.intValue();
int -> double
int num = 200;
double num1 = num;
變量作用域
我們已經(jīng)學會了如何定義變量,也知道了使用各種數(shù)據(jù)類型來定義變量。但是還有一點不知道大家有沒有注意到,如果我們的定義變量在不同的位置,其作用是不是相同的呢?
這就涉及到變量的作用域,一般根據(jù)其作用域的不同,可以分為:
成員變量:定義在方法體和語句塊外,不屬于任何一個方法,能在整個類中起作用; 局部變量:定義在方法或方法體中的變量,作用域是其所在的代碼塊;
成員變量
成員變量又可以分為 全局變量(又叫實例變量) 和 靜態(tài)變量(也叫類變量),兩者的區(qū)別如下:
| 名稱 | 修飾符 | 訪問方式 | 生命周期 |
|---|---|---|---|
| 全局變量 | 無 | 對象名.變量名 | 一旦對象被引用,則實例變量就存在 |
| 靜態(tài)變量 | static | 類名.變量名 | 同類共生死,只有當類被 GC 回收時才會被銷毀 |
public class Person {
// 成員變量,全局變量
String name;
// 成員變量,全局變量
int age;
// 成員變量,靜態(tài)變量
public static final String wechatPublic = "公眾號:村雨遙";
// 成員變量,靜態(tài)變量
public static final String website = "http://cunyu1943.site";
}
局部變量
成員變量指定義在方法或方法體中的變量,作用域是其所在的代碼塊,可以分為如下三種:
形參
public class Main {
// 方法中的參數(shù)
public static void func(int num) {
System.out.println("num = " + num);
}
public static void main(String[] args) {
func(3);
}
}
方法內(nèi)定義
public class Main {
public static void main(String[] args) {
int num = 10;
if (num > 5) {
// 聲明一個 int 類型的局部變量
int tmp = 5;
System.out.println("tmp = " + tmp);
System.out.println("num = " + num);
}
System.out.println("num = " + num);
}
}
代碼塊定義
public class Main {
public static void func() {
try {
System.out.println("Hello!Exception!");
} catch (Exception e) { // 異常處理塊,參數(shù)為 Exception 類型
e.printStackTrace();
}
}
public static void main(String[] args) {
func();
}
}
常量
簡介
既然有變量,那就有與之相對的常量(也就是值是固定的,不能再變)。
常量又叫做字面常量,是通過數(shù)據(jù)直接來表示的,在程序運行過程中不能發(fā)生改變。通常我們把 Java 中用 final 關(guān)鍵字所修飾的成員變量叫做常量,它的值一旦給定就無法再進行改變!
分類
Java 中使用 final 關(guān)鍵字來聲明常量,其語法格式如下:
final 數(shù)據(jù)類型 常量名 = 常量初始值;
public class Main{
public static void main(String[] args){
// 聲明一個常量并賦值
final int num = 1024;
// 再次賦值,將導致編譯錯誤
num = 1943;
// 聲明一個常量但不賦值
final int id;
// 因為聲明時未賦值,所以可以進程初次賦值
id = 1;
// 常量已經(jīng)賦值過了,再次賦值將導致編譯錯誤
id = 2;
}
}
常量可以分為如下 3 種類型:
靜態(tài)常量: final之前用public staic修飾,表示該常量的作用域是全局的,我們不用創(chuàng)建對象就能夠訪問它。成員常量:類似于成員變量,但是最大的不同在于它不能被修改。 局部常量:作用類似于局部變量,不同之處也在于不能修改。
public class Main{
// 靜態(tài)變量
public static final dobule PI = 3.14;
// 成員常量
final int num = 1024;
public static void main(String[] args){
// 局部變量
final long count = 1000;
}
}
PS:final 修飾變量后,該變量則變?yōu)槌A?。?final 也還可以用來修飾類和方法,修飾方法時,表示這個方法不能被重寫(但可以重載);修飾類時,則表明該類無法被繼承。這些東西這時候你可能會覺得很陌生,不過等我們后續(xù)學習了面向?qū)ο笾?,你就會發(fā)現(xiàn)其實很簡單。
總結(jié)
碼字不易,如果覺得對您有所幫助,可以點贊關(guān)注一波哦!??
博主水平有限,對于文中可能出現(xiàn)的錯誤,還請各位批評指正,來評論區(qū)一起聊天吧!



