java基礎_數組_數據類型_原碼反碼補碼_進制轉換_編碼_shell

數組

一:什麼是數組:
數組是相同類型的,用一個標識符名稱封裝到一起的一個對象序列或基本數據類型序列。
數組就是一個簡單的線性序列,這使得元素訪問非常快速,但是爲這種速度所付出的代價是數組對象的大小被固定,並且在其生命週期中不可改變。
在Java中數組是一種效率最高的存儲和隨機訪問對象引用序列的方式。

二:數組和其它容器有什麼區別:
1. 數組可以存儲基本數據類型,也可以存儲引用數據類型,集合只能存儲引用數據類型。
數組可以持有基本數據類型,而容器不能持有基本數據類型,但是JAVA SE5 泛型出來後,容器就可以指定並檢查它們所持有的對象類型,並且有了自動包裝機制,“容器看起來還能夠持有基本類型”(這裏只是看起來,實際上是持有的基本類型多對應的包裝器類型)。

2. 數組是固定長度的,集合的長度是可變的。
數組是一種內存結構,而容器是一種數據結構。數組創建就必須指定大小,指定了大小數組也就固定了。當數組空間不足的時候,只能創建一個新的數組,然以把數據拷貝到新數組中。
知道數組的長度,而且以後也不會再增加,那肯定就使用數組了;如果數組的長度不定或者說是長度會增加,爲了方便起見使用容器

三:數組的創建與初始化:

public class ArrayTest {
public static void main(String[] args) {
int[] a = new int[5]; //第一種初始化方法
System.out.println(Arrays.toString(a)); //[0, 0, 0, 0, 0]
int[] b = new int[]{1, 2, 3, 4, 5}; //第二種初始化方法
System.out.println(Arrays.toString(b)); //[1, 2, 3, 4, 5]
int[] c = {1, 2, 3, 4, 5}; //第三種初始化方式
System.out.println(Arrays.toString(c)); //[1, 2, 3, 4, 5]
}
}

 

四:多維數組:
對於一個二維數組來說:它是一個數組,它的每個元素都是一個一維數組。
對於n(n>1)維數組來說:它是一個數組,它的每個元素都是一個(n-1)維數組。

二維數組聲明和初始化:

public class ArrayTest {
public static void main(String[] args) {

int[][] a = new int[5][3]; //第一種初始化方法,表示有5個長度爲3的一維數組
System.out.println(Arrays.deepToString(a)); //[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
System.out.println(a.length); //5

int[][] b = new int[][]{{1, 2, 3, 4, 5}, {6, 7, 8, 9, 10}};//第二種初始化方法,表示有2個長度爲5的一維數組
System.out.println(Arrays.deepToString(b)); //[[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]
System.out.println(b.length); //2

int[][] c = {{1, 2, 3, 4, 5}, {6, 7, 8, 9, 10}};//第三種初始化方式
System.out.println(Arrays.deepToString(c)); //[[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]
System.out.println(c.length); //2

int[][] d = new int[2][]; //第四種創建方式,數組中構成矩陣的每個向量都可以具有任意長度,也就是說構成二維數組的每個元素的一維數組的長度可以任意長度。
for (int i = 0; i < d.length; i++) {
d[i] = new int[i + 1];
}
System.out.println(Arrays.deepToString(d)); //[[0], [0, 0]]
System.out.println(d.length); //2
}
}

五:Arrays實用功能:
Arrays.fill():填充數組
用同一個值來填充數組的各個位置,而針對對象而言,就是複製同一個引用進行填充。

public class ArrayTest {
public static void main(String[] args) {

int[] a = new int[5]; //創建數組,默認值爲0
System.out.println(Arrays.toString(a)); //[0, 0, 0, 0, 0]
Arrays.fill(a, 6); //將數據全部替換成6
System.out.println(Arrays.toString(a)); //[6, 6, 6, 6, 6]

boolean[] b = new boolean[6]; //創建數組,默認值爲false
System.out.println(Arrays.toString(b)); //[false, false, false, false, false, false]
Arrays.fill(b, true); //將數據全部替換成true
System.out.println(Arrays.toString(b)); //[true, true, true, true, true, true]

Animal[] c = new Animal[7]; //創建數組,默認值爲null
Animal animal = new Animal("animal"); //創建將要替換的對象
System.out.println(Arrays.toString(c)); //[null, null, null, null, null, null, null]
Arrays.fill(c, animal); //將數據全部替換成animal
System.out.println(Arrays.toString(c)); //[com.lonbon.mytest.Animal@1540e19d, com.lonbon.mytest.Animal@1540e19d, com.lonbon.mytest.Animal@1540e19d, com.lonbon.mytest.Animal@1540e19d, com.lonbon.mytest.Animal@1540e19d, com.lonbon.mytest.Animal@1540e19d, com.lonbon.mytest.Animal@1540e19d]
}
}

 

System.arraycopy():複製數組
Java標準類庫提供有static方法System.arraycopy(),用它複製數組比用for循環複製要快很多。
我們來看一下這個方法:public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
src:原數組
srcPos:原數組起始位置
dest:目標數組
destPos:目標數組的起始位置
length:要複製的數組元素的數目
下面看一個簡單例子:

public class ArrayTest {
public static void main(String[] args) {
int[] a = new int[5];
Arrays.fill(a, 6);
int[] b = new int[7];
System.arraycopy(a, 0, b, 0, a.length);
System.out.println(Arrays.toString(b)); //[6, 6, 6, 6, 6, 0, 0]
}
}

Arrays.equals():數組的比較
Arrays類提供了重載後的equals()方法,用來比較整個數組。數組相等的條件是元素個數必須相等並且對應的元素也相等。
看下面例子:

public class ArrayTest {
public static void main(String[] args) {

int[] a = new int[10];
int[] b = new int[10];
Arrays.fill(a, 6);
Arrays.fill(b, 6);
System.out.println(Arrays.equals(a, b)); //true

String[] c = new String[10];
String[] d = new String[10];
Arrays.fill(c, "q");
Arrays.fill(d, "w");
System.out.println(Arrays.equals(c, d)); //false

}
}
  • Arrays.sort   數組排序
  • Arrays.equals  判斷數組相等
  • Arrays.fill  填充數組
  • System.arraycopy   拷貝數組
  • Arrays.toString  把數組轉成字符串形式

Java基礎知識之數據類型

數據類型

  • java語言是一種強類型語言
  • 變量或常量必須有類型:聲明變量或常量必須聲明類型。
  • 賦值時類型必須一致:值的類型必須和變量或常量的類型完全一致。
  • 運算時類型必須一致:參與運算的數據類型必須一致才能運算。
  • 數據類型不一致時要進行類型轉換

java中的數據類型分爲基本數據類型和引用數據類型

  • 基本數據類型:基本數據類型共有8種,分別是:布爾型boolean, 字符型char和數值型byte/short/int/long/float/double。由於字符型char所表示的單個字符與Ascii碼中相應整形對應,因此,有時也將其劃分到數值型中
  • 引用數據類型:引用類型具體可分爲:數組、類和接口

基本數據類型:
數值類型在內存中直接存儲其本身的值,對於不同的數值類型,內存中會分配相應的大小去存儲。如:byte類型的變量佔用8位,int類型變量佔用32位等。相應的,不同的數值類型會有與其存儲空間相匹配的取值範圍

 

 

 


在java中整數的默認數據類型是int, 例如數字4, 小數的默認數字類型是double, 例如3.12. 當float a = 3.12時會報錯, 因爲3.12的默認數據類型是double

看下面例子

public class Test {
public static void main(String[] args) {
byte a = 128; //編譯出錯   (可以先賦值給int,再把int賦值給byte就不會報錯,此時byte就變成了負數)
byte b = 127; //編譯正確

float c = 3.12; //編譯出錯
float d = (float) 3.12; //編譯正確
}
}

 

將一個int型的128賦值給byte a編譯出錯,將127賦值給byte b編譯正確,爲什麼呢?
原因在於:jvm在編譯過程中,對於默認爲int類型的數值時,當賦給一個比int型數值範圍小的數值類型變量(在此統一稱爲數值類型k,k可以是byte/char/short類型),會進行判斷,如果此int型數值超過數值類型k,那麼會直接編譯出錯。因爲你將一個超過了範圍的數值賦給類型爲k的變量,k裝不下嘛,你有沒有進行強制類型轉換,當然報錯了。但是如果此int型數值尚在數值類型k範圍內,jvm會自定進行一次隱式類型轉換,將此int型數值轉換成類型k。這一點有點特別,需要稍微注意下。

在其他情況下,當將一個數值範圍小的類型賦給一個數值範圍大的數值型變量,jvm在編譯過程中俊將此數值的類型進行了自動提升。在數值類型的自動類型提升過程中,數值精度至少不應該降低(整型保持不變,float->double精度將變高)。

同時系統有一個自動轉換功能(也就是進行了底層轉換,效果和 byte test = (byte) 127 ; 是一樣的),只要賦予byte的值不超過byte的取值範圍,系統都會自動幫你轉換;這種情況特殊可以看做隱式高轉低,但是double不能隱式的轉爲float,只能顯示賦值、

public class Test {
public static void main(String[] args) {
long a = 11111111111; //編譯出錯
long b = 11111111111L; //編譯正確

int z = 10; //編譯正確
long q = z; //編譯正確
}
}

如上:定義long類型的a變量時,將編譯出錯,原因在於11111111111默認是int類型,同時int類型的數值範圍是-2^31 ~ 2^31-1,因此,11111111111已經超過此範圍內的最大值,故而其自身已經編譯出錯,更談不上賦值給long型變量a了。

此時,若想正確賦值,改變11111111111自身默認的類型即可,直接改成11111111111L即可將其自身類型定義爲long型。此時再賦值編譯正確。

將值爲10的int型變量 z 賦值給long型變量q,按照上文所述,此時直接發生了自動類型提升, 編譯正確。

接下來,還有一個地方需要注意的是:char型其本身是unsigned型,同時具有兩個字節,其數值範圍是0 ~ 2^16-1,因爲,這直接導致byte型不能自動類型提升到char,char和short直接也不會發生自動類型提升(因爲負數的問題),同時,byte當然可以直接提升到short型。

隱式類型轉換
隱式轉換也叫作自動類型轉換, 由系統自動完成.
從存儲範圍小的類型到存儲範圍大的類型.
byte ->short(char)->int->long->float->double

顯示類型轉換

顯示類型轉換也叫作強制類型轉換, 是從存儲範圍大的類型到存儲範圍小的類型.
當我們需要將數值範圍較大的數值類型賦給數值範圍較小的數值類型變量時,由於此時可能會丟失精度(1講到的從int到k型的隱式轉換除外),因此,需要人爲進行轉換。我們稱之爲強制類型轉換。

public class Test {
public static void main(String[] args) {
byte a = 3; //編譯正確
int b = 4; //編譯正確
byte c = b; //編譯錯誤
}
}

byte a =3;編譯正確在1中已經進行了解釋。接下來將一個值爲4的int型變量b賦值給byte型變量c,發生編譯錯誤。這兩種寫法之間有什麼區別呢?

區別在於前者3是直接量,編譯期間可以直接進行判定,後者b爲一變量,需要到運行期間才能確定,也就是說,編譯期間爲以防萬一,當然不可能編譯通過了。此時,需要進行強制類型轉換。

強制類型轉換所帶來的結果是可能會丟失精度,如果此數值尚在範圍較小的類型數值範圍內,對於整型變量精度不變,但如果超出範圍較小的類型數值範圍內,很可能出現一些意外情況。

public class Test {
public static void main(String[] args) {
int a = 233;
byte b = (byte) a;
System.out.print(b);
}
}

 


上面的例子中輸出值是 -23.
爲什麼結果是-23?需要從最根本的二進制存儲考慮。
233的二進制表示爲:24位0 + 11101001,byte型只有8位,於是從高位開始捨棄,截斷後剩下:11101001,由於二進制最高位1表示負數,0表示正數,其相應的負數爲-23。

進行數學運算時的數據類型自動提升與可能需要的強制類型轉換

當進行數學運算時,數據類型會自動發生提升到運算符左右之較大者,以此類推。當將最後的運算結果賦值給指定的數值類型時,可能需要進行強制類型轉換。例如:

public class Test {
public static void main(String[] args) {
int a = 9;
byte b = 1;
byte c = (byte) (a + b);
}
}


a+b會自動提升爲int, 因此在給c賦值的時候要強制轉換成byte.

計算機原碼、反碼、補碼詳解

一. 機器數和真值
機器數
一個數在計算機中的二進制表示形式, 叫做這個數的機器數。機器數是帶符號的,在計算機用一個數的最高位存放符號, 正數爲0, 負數爲1.比如,十進制中的數 +3 ,計算機字長爲8位,轉換成二進制就是00000011。如果是 -3 ,就是 10000011 。那麼,這裏的 00000011 和 10000011 就是機器數。

真值
因爲第一位是符號位,所以機器數的形式值就不等於真正的數值。例如上面的有符號數 10000011,其最高位1代表負,其真正數值是 -3 而不是形式值131(10000011轉換成十進制等於131)。所以,爲區別起見,將帶符號位的機器數對應的真正數值稱爲機器數的真值

例:0000 0001的真值 = +000 0001 = +1,1000 0001的真值 = –000 0001 = –1

二. 原碼, 反碼, 補碼的基礎概念和計算方法.
在探求爲何機器要使用補碼之前, 讓我們先了解原碼, 反碼和補碼的概念.對於一個數, 計算機要使用一定的編碼方式進行存儲. 原碼, 反碼, 補碼是機器存儲一個具體數字的編碼方式.

原碼
原碼就是符號位加上真值的絕對值, 即用第一位表示符號, 其餘位表示值. 比如如果是8位二進制:
[+1]原 = 0000 0001

[-1]原 = 1000 0001

第一位是符號位. 因爲第一位是符號位, 所以8位二進制數的取值範圍就是:

[1111 1111 , 0111 1111]

[-127 , 127]

原碼是人腦最容易理解和計算的表示方式.

反碼
反碼的表示方法是:

正數的反碼是其本身

負數的反碼是在其原碼的基礎上, 符號位不變,其餘各個位取反.

[+1] = [00000001]原 = [00000001]反

[-1] = [10000001]原 = [11111110]反

 

可見如果一個反碼錶示的是負數, 人腦無法直觀的看出來它的數值. 通常要將其轉換成原碼再計算.

補碼
補碼的表示方法是:

正數的補碼就是其本身

負數的補碼是在其原碼的基礎上, 符號位不變, 其餘各位取反, 最後+1. (即在反碼的基礎上+1)

[+1] = [00000001]原 = [00000001]反 = [00000001]補

[-1] = [10000001]原 = [11111110]反 = [11111111]補

 

對於負數, 補碼錶示方式也是人腦無法直觀看出其數值的. 通常也需要轉換成原碼在計算其數值.

原碼,反碼,補碼都是機器數,真值就是把符號位用正負號表示代表其真實值。

三. 爲何要使用原碼, 反碼和補碼
在開始深入學習前, 我的學習建議是先”死記硬背”上面的原碼, 反碼和補碼的表示方式以及計算方法.

現在我們知道了計算機可以有三種編碼方式表示一個數. 對於正數因爲三種編碼方式的結果都相同:

[+1] = [00000001]原 = [00000001]反 = [00000001]補
所以不需要過多解釋. 但是對於負數:

[-1] = [10000001]原 = [11111110]反 = [11111111]補
可見原碼, 反碼和補碼是完全不同的. 既然原碼纔是被人腦直接識別並用於計算表示方式, 爲何還會有反碼和補碼呢?

首先, 因爲人腦可以知道第一位是符號位, 在計算的時候我們會根據符號位, 選擇對真值區域的加減. (真值的概念在本文最開頭). 但是對於計算機, 加減乘數已經是最基礎的運算, 要設計的儘量簡單. 計算機辨別”符號位”顯然會讓計算機的基礎電路設計變得十分複雜! 於是人們想出了將符號位也參與運算的方法. 我們知道, 根據運算法則減去一個正數等於加上一個負數, 即: 1-1 = 1 + (-1) = 0 , 所以機器可以只有加法而沒有減法, 這樣計算機運算的設計就更簡單了.

於是人們開始探索 將符號位參與運算, 並且只保留加法的方法. 首先來看原碼:

計算十進制的表達式: 1-1=0

1 - 1 = 1 + (-1) = [00000001]原 + [10000001]原 = [10000010]原 = -2
如果用原碼錶示, 讓符號位也參與計算, 顯然對於減法來說, 結果是不正確的.這也就是爲何計算機內部不使用原碼錶示一個數.

爲了解決原碼做減法的問題, 出現了反碼:

計算十進制的表達式: 1-1=0

1 - 1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原= [0000 0001]反 + [1111 1110]反 = [1111 1111]反 = [1000 0000]原 = -0
發現用反碼計算減法, 結果的真值部分是正確的. 而唯一的問題其實就出現在”0”這個特殊的數值上. 雖然人們理解上+0和-0是一樣的, 但是0帶符號是沒有任何意義的. 而且會有[0000 0000]原和[1000 0000]原兩個編碼表示0.

於是補碼的出現, 解決了0的符號以及兩個編碼的問題:

1-1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]補 + [1111 1111]補 = [0000 0000]補=[0000 0000]原

這樣0用[0000 0000]表示, 而以前出現問題的-0則不存在了.而且可以用[1000 0000]表示-128:

(-1) + (-127) = [1000 0001]原 + [1111 1111]原 = [1111 1111]補 + [1000 0001]補 = [1000 0000]補

-1-127的結果應該是-128, 在用補碼運算的結果中, [1000 0000]補 就是-128. 但是注意因爲實際上是使用以前的-0的補碼來表示-128, 所以-128並沒有原碼和反碼錶示.(對-128的補碼錶示[1000 0000]補算出來的原碼是[0000 0000]原, 這是不正確的)

使用補碼, 不僅僅修復了0的符號以及存在兩個編碼的問題, 而且還能夠多表示一個最低數. 這就是爲什麼8位二進制, 使用原碼或反碼錶示的範圍爲[-127, +127], 而使用補碼錶示的範圍爲[-128, 127].

因爲機器使用補碼, 所以對於編程中常用到的32位int類型, 可以表示範圍是: [-2^31, 2^31-1] 因爲第一位表示的是符號位.而使用補碼錶示時又可以多保存一個最小值.

總結:原碼帶符號位加減計算結果是不正確的,反碼計算結果多了個-0,補碼消除了-0(10000000)並把-0表示爲了-128,因此使用補碼可以保證結果的正確性,同時讓計算結果多了一個值。

四 範圍溢出問題
下面是個人見解,如有不對,歡迎指正

由於計算機中的數字用補碼錶示,例如8bit的byte類型的表示範圍爲:[-128, 127]

0 = [0000 0000](補)

-128 = [1000 0000](補)

127 = [0111 1111](補)

當byte類型的變量超出界限時

128 = 127 + 1 = [0111 1111]補 + [0000 0001]補 = [1000 0000]補 = -128
129 = 127 + 2 = [0111 1111]補 + [0000 0010]補 = [1000 0001]補 = [1111 1111]原 = -127
...
...
-129 = -128 + (-1) = [1000 0000]補 + [1111 1111]補 = [0111 1111]補 = [0111 1111]原 = 127
-130 = -128 + (-2)= [1000 0000]補 + [1111 1110]補 = [0111 1110]補 = [0111 1110]原 = 126
public class Test {

public static void main(String[] args) {

byte a = (byte) 128;
byte b = (byte) 129;
byte c = (byte) -129;
byte d = (byte) -130;

System.out.print(a); //-128
System.out.print(b); //-127
System.out.print(c); //127
System.out.print(d); //126
}
}

可見,當越界時,會捨棄高位,並且在[-128,127]範圍內循環

進制轉換

十進制轉換

二進制–>十進制
方法:二進制數從低位到高位(即從右往左)計算,第0位的權值是2的0次方,第1位的權值是2的1次方,第2位的權值是2的2次方,依次遞增下去,把最後的結果相加的值就是十進制的值了。
1010(B)
1×2^3+0×2^2+1×2^1+0×2^0=10
八進制–>十進制
方法:八進制數從低位到高位(即從右往左)計算,第0位的權值是8的0次方,第1位的權值是8的1次方,第2位的權值是8的2次方,依次遞增下去,把最後的結果相加的值就是十進制的值了。
1010(O)
1×8^3+0×8^2+1×8^1+0×8^0=520
十六進制–>十進制
方法:十六進制數從低位到高位(即從右往左)計算,第0位的權值是16的0次方,第1位的權值是16的1次方,第2位的權值是16的2次方,依次遞增下去,把最後的結果相加的值就是十進制的值了。
1010(H)
1×16^3+0×16^2+1×16^1+0×16^0=4112

 

二進制轉換


八進制–>二進制
方法:取一分三法,即將一位八進制數分解成三位二進制數,用三位二進制按權相加去湊這位八進制數,小數點位置照舊。
106(O)
1拆成001
0拆成000
6拆成110
轉換後的二進制爲:001 000 110

十進制–>二進制
方法:除2取餘法,即每次將整數部分除以2,餘數爲該位權上的數,而商繼續除以2,餘數又爲上一個位權上的數,這個步驟一直持續下去,直到商爲0爲止,最後讀數時候,從最後一個餘數讀起,一直到最前面的一個餘數。
106(D)
1、106 ÷ 2 = 53 ……0
2、53 ÷ 2 = 26 ……1
3、26 ÷ 2 = 13 …….0
4、13 ÷ 2 = 6 ……1
5、6 ÷ 2 = 3 ……..0
6、3 ÷ 2 = 1 ……..1
7、1÷ 2 = 0 ……….1
所以轉換後的二進制數爲:1101010

十六進制–>二進制
方法:取一分四法,即將一位十六進制數分解成四位二進制數,用四位二進制按權相加去湊這位十六進制數,小數點位置照舊。
106(H)
1拆成0001
0拆成0000
6拆成0110
轉化成二進制爲:0001 0000 0110

八進制轉換

二進制–>八進制
方法:取三合一法,即從二進制的小數點爲分界點,向左(向右)每三位取成一位,接着將這三位二進制按權相加,然後,按順序進行排列,小數點的位置不變,得到的數字就是我們所求的八進制數。如果向左(向右)取三位後,取到最高(最低)位時候,如果無法湊足三位,可以在小數點最左邊(最右邊),即整數的最高位(最低位)添0,湊足三位。
11010111.0100111(B)
1、小數點前111 = 7;
2、010 = 2;
3、11補全爲011,011 = 3;
4、小數點後010 = 2;
5、011 = 3;
6、1補全爲100,100 = 4;
7、讀數,讀數從高位到低位,即(11010111.0100111)B=(327.234)O。

十進制–>八進制
方法1:除8取餘法,即每次將整數部分除以8,餘數爲該位權上的數,而商繼續除以8,餘數又爲上一個位權上的數,這個步驟一直持續下去,直到商爲0爲止,最後讀數時候,從最後一個餘數起,一直到最前面的一個餘數。
106(D)
1、106 ÷ 8 = 13 ……2
2、13 ÷ 8 = 1 ……5
3、1 ÷ 8 = 0 ……1
即轉化爲八進制爲:152(O)
方法2:使用間接法,先將十進制轉換成二進制,然後將二進制又轉換成八進制;

十六進制–>八進制
方法:將十六進制轉換爲二進制,然後再將二進制轉換爲八進制,小數點位置不變。
106(H)
先轉換爲二進制:上面已經講過,結果爲:
轉化成二進制爲:0001 0000 0110
二進制轉化爲八進制:上面已經講過,結果爲:
轉化爲八進制爲:406(O)

十六進制

 

二進制–>十六進制
方法:取四合一法,即從二進制的小數點爲分界點,向左(向右)每四位取成一位,接着將這四位二進制按權相加,然後,按順序進行排列,小數點的位置不變,得到的數字就是我們所求的十六進制數。如果向左(向右)取四位後,取到最高(最低)位時候,如果無法湊足四位,可以在小數點最左邊(最右邊),即整數的最高位(最低位)添0,湊足四位。
11010111(B)
1、0111 = 7
2、1101 = D
所以轉換結果爲:D7

八進制–>十六進制
方法:將八進制轉換爲二進制,然後再將二進制轉換爲十六進制,小數點位置不變。
106(O)
1、八進制–>二進制
上面已經講過,所以結果爲:001 000 110、
2、二進制–>十六進制
0110 = 6
1000 = 8
所以結果爲86(H)

十進制–>十六進制
方法1:除16取餘法,即每次將整數部分除以16,餘數爲該位權上的數,而商繼續除以16,餘數又爲上一個位權上的數,這個步驟一直持續下去,直到商爲0爲止,最後讀數時候,從最後一個餘數起,一直到最前面的一個餘數。
106(D)
1、106 ÷ 16 = 6 ……10(A)
2、6 ÷ 16 = 0 …….6
所以轉換結果爲6A(H)
方法2:使用間接法,先將十進制轉換成二進制,然後將二進制又轉換成十六進制;
8(三位二進制表示一位8進制)和16(四位二進制表示一位)都可以轉爲2進制,再2進制轉爲10進制,反之亦然

java編碼

爲什麼要編碼

由於人類的語言有太多,因而表示這些語言的符號太多,無法用計算機中一個基本的存儲單元—— byte 來表示,因而必須要經過拆分或一些翻譯工作,才能讓計算機能理解。我們可以把計算機能夠理解的語言假定爲英語,其它語言要能夠在計算機中使用必須經過一次翻譯,把它翻譯成英語。這個翻譯的過程就是編碼。所以可以想象只要不是說英語的國家要能夠使用計算機就必須要經過編碼。

所以總的來說,編碼的原因可以總結爲:

計算機中存儲信息的最小單元是一個字節即 8 個 bit,所以能表示的字符範圍是 0~255 個
人類要表示的符號太多,無法用一個字節來完全表示,要解決這個矛盾必須需要一個新的數據結構 char,從 char 到 byte 必須編碼,(char就是我們能識別的字符,byte就是二進制,計算機能識別的,所以我們要按一定規則進行編碼解碼)

Java 中需要編碼的場景

前面描述了常見的幾種編碼格式,下面將介紹 Java 中如何處理對編碼的支持,什麼場合中需要編碼。

I/O 操作中存在的編碼
我們知道涉及到編碼的地方一般都在字符到字節或者字節到字符的轉換上而需要這種轉換的場景主要是在 I/O 的時候,這個 I/O 包括磁盤 I/O 和網絡 I/O,關於網絡 I/O 部分在後面將主要以 Web 應用爲例介紹。下圖是 Java 中處理 I/O 問題的接口:

Reader 類是 Java 的 I/O 中讀字符的父類,而 InputStream 類是讀字節的父類,InputStreamReader 類就是關聯字節到字符的橋樑,它負責在 I/O 過程中處理讀取字節到字符的轉換,而具體字節到字符的解碼實現它由 StreamDecoder 去實現,在 StreamDecoder 解碼過程中必須由用戶指定 Charset 編碼格式。值得注意的是如果你沒有指定 Charset,將使用本地環境中的默認字符集,例如在中文環境中將使用 GBK 編碼。

寫的情況也是類似,字符的父類是 Writer,字節的父類是 OutputStream,通過 OutputStreamWriter 轉換字符到字節。如下圖所示:

同樣 StreamEncoder 類負責將字符編碼成字節,編碼格式和默認編碼規則與解碼是一致的。

如下面一段代碼,實現了文件的讀寫功能:

String file = "c:/stream.txt";
String charset = "UTF-8";
// 寫字符換轉成字節流
FileOutputStream outputStream = new FileOutputStream(file);
OutputStreamWriter writer = new OutputStreamWriter(
outputStream, charset);
try {
writer.write("這是要保存的中文字符");
} finally {
writer.close();
}

 


// 讀取字節轉換成字符

FileInputStream inputStream = new FileInputStream(file);
InputStreamReader reader = new InputStreamReader(
inputStream, charset);
StringBuffer buffer = new StringBuffer();
char[] buf = new char[64];
int count = 0;
try {
while ((count = reader.read(buf)) != -1) {
buffer.append(buffer, 0, count);
}
} finally {
reader.close();
}


在我們的應用程序中涉及到 I/O 操作時只要注意指定統一的編解碼 Charset 字符集,一般不會出現亂碼問題,有些應用程序如果不注意指定字符編碼,中文環境中取操作系統默認編碼,如果編解碼都在中文環境中,通常也沒問題,但是還是強烈的不建議使用操作系統的默認編碼,因爲這樣,你的應用程序的編碼格式就和運行環境綁定起來了,在跨環境下很可能出現亂碼問題。

內存中操作中的編碼

在 Java 開發中除了 I/O 涉及到編碼外,最常用的應該就是在內存中進行字符到字節的數據類型的轉換,Java 中用 String 表示字符串,所以 String 類就提供轉換到字節的方法,也支持將字節轉換爲字符串的構造函數。如下代碼示例:

String s = "這是一段中文字符串";
byte[] b = s.getBytes("UTF-8");
String n = new String(b,"UTF-8");

另外一個是已經被被廢棄的 ByteToCharConverter 和 CharToByteConverter 類,它們分別提供了 convertAll 方法可以實現 byte[] 和 char[] 的互轉。如下代碼所示:

ByteToCharConverter charConverter = ByteToCharConverter.getConverter("UTF-8");
char c[] = charConverter.convertAll(byteArray);
CharToByteConverter byteConverter = CharToByteConverter.getConverter("UTF-8");
byte[] b = byteConverter.convertAll(c);

 

這兩個類已經被 Charset 類取代,Charset 提供 encode 與 decode 分別對應 char[] 到 byte[] 的編碼和 byte[] 到 char[] 的解碼。如下代碼所示:

Charset charset = Charset.forName("UTF-8");
ByteBuffer byteBuffer = charset.encode(string);
CharBuffer charBuffer = charset.decode(byteBuffer);

編碼與解碼都在一個類中完成,通過 forName 設置編解碼字符集,這樣更容易統一編碼格式,比 ByteToCharConverter 和 CharToByteConverter 類更方便。

Java 中還有一個 ByteBuffer 類,它提供一種 char 和 byte 之間的軟轉換,它們之間轉換不需要編碼與解碼,只是把一個 16bit 的 char 格式,拆分成爲 2 個 8bit 的 byte 表示,它們的實際值並沒有被修改,僅僅是數據的類型做了轉換。如下代碼所以:

ByteBuffer heapByteBuffer = ByteBuffer.allocate(1024);
ByteBuffer byteBuffer = heapByteBuffer.putChar(c);

以上這些提供字符和字節之間的相互轉換隻要我們設置編解碼格式統一一般都不會出現問題。

幾種編碼格式的比較
對中文字符後面四種編碼格式都能處理,GB2312 與 GBK 編碼規則類似,但是 GBK 範圍更大,它能處理所有漢字字符,所以 GB2312 與 GBK 比較應該選擇 GBKUTF-16 與 UTF-8 都是處理 Unicode 編碼,它們的編碼規則不太相同,相對來說 UTF-16 編碼效率最高,字符到字節相互轉換更簡單,進行字符串操作也更好。它適合在本地磁盤和內存之間使用,可以進行字符和字節之間快速切換,如 Java 的內存編碼就是採用 UTF-16 編碼。但是它不適合在網絡之間傳輸,因爲網絡傳輸容易損壞字節流,一旦字節流損壞將很難恢復,想比較而言 UTF-8 更適合網絡傳輸,對 ASCII 字符采用單字節存儲,另外單個字符損壞也不會影響後面其它字符,在編碼效率上介於 GBK 和 UTF-16 之間,所以 UTF-8 在編碼效率上和編碼安全性上做了平衡,是理想的中文編碼方式。 所以utf-8效率介於GBK和utf-16之間,但是utf-8對字節損壞魯棒性強,所以utf-8更常用。
(8條消息) Shell 腳本詳解_喫透Java-CSDN博客

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章