变量就是申请内存来存储值。也就是说,当创建变量的时候,需要在内存中申请空间。内存管理系统根据变量的类型为变量分配存储空间,分配的空间只能用来储存该类型数据。
Java 中有两种类型,原始类型(Primitive Type)会被直接映射到 CPU 的基础类型,引用类型(Reference Type)则指向了内存中的对象。
- 原始类型:boolean,char,byte,short,int,long,float,double。
- 包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double。
Object
Objects
所有其他类型都是对象,有两种特殊类型,String 和 arrays 是对象,但编译器认为它们是内置的类型。字符串的定义如下:
var text = "hello";
System.out.println("hello".length());
System.out.println("hello".toUpperCase());
System.out.println("hello".toLowerCase());
System.out.println("hello".charAt(0));
System.out.println("hello".indexOf('l'));
System.out.println("hello".indexOf('o'));Copy to clipboardErrorCopied
数组的定义如下:
var intArray = new int[] {2, 3};
System.out.println(intArray[0]);
intArray[0] = 42;
// intArray[-1] = 42; // throws IndexOutOfBoundsException
var clonedArray = intArray.clone();
var arrayLength = intArray.length;
System.out.println(arrayLength);
System.out.println(intArray);
System.out.println(intArray.equals(clonedArray));
var matrix = new double[][] { { 2.0, 3.0}, { 4.0, 5.0 } };Copy to clipboardErrorCopied
由于基本类型和数组几乎没有方法,因此,如果要使用它们,则必须使用静态方法。静态方法是在可以使用语法调用的某个类型上声明的函数 SomeWhere.methodName(arg0, arg1, arg2)
。
var resultAsInt = java.lang.Integer.parseInt("42");
System.out.println(resultAsInt);
var text = java.util.Arrays.toString(intArray);
System.out.println(text);
var intList = List.of(2, 3);
原始类型
Java 语言提供了八种原始类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。Java 的基础类型包含了如下几种:
// boolean (true|false)
var result = true;
var anotherResult = false;
// char (character)
var firstLetter = 'j';
// int (signed 32 bits integer)
var numberOfLegs = 2;
// double (64 bits floating point)
var cost = 3.78;
// long and float
// 一些更特殊的类型,它们需要后缀(L或f)长(64位整数)并浮点(32位浮点数)
var longValue = 123L;
var floatValue = 123.5f;
// byte and short
// 还有字节(带符号的8位整数)和短(带符号的16位短整数),它们仅在定义对象时占用较少的内存
record CompactHeader(byte tag, short version) {}
short value = 12;
var result = value + value;Copy to clipboardErrorCopied
实际上,Java 中还存在另外一种基本类型 void,它也有对应的包装类 java.lang.Void,不过我们无法直接对它们进行操作。
整型
Java 定义了四种整数类型:byte、short、int 和 long。所有这些类型都是有符号的、正值和负值。Java 不支持无符号、只支持正值的整数。许多其他计算机语言同时支持有符号和无符号整数。然而,Java 的设计者认为无符号整数是不必要的。具体来说,他们认为无符号的概念主要用于指定高阶位的行为,高阶位定义了整数值的符号。Java 通过添加一个特殊的无符号右移操作符,以不同的方式管理高阶位的含义。这样,就消除了对无符号整数类型的需求。
byte
byte 数据类型是 8 位、有符号的,以二进制补码表示的整数;最小值是 -128(-2^7) ,最大值是 127(2^7-1) ;默认值是 0 。byte 类型用在大型数组中节约空间,主要代替整数,因为 byte 变量占用的空间只有 int 类型的四分之一。
byte a = 100,byte b = -50。Copy to clipboardErrorCopied
计算机是用二进制来表示数据的,一个字节也就是 8 个比特位,其中最高位表示符号位(0 正 1 负),故 byte 的取值范围为 1000 0000 到 0111 1111。在 Java 中,是采用补码来表示数据的,正数的补码和原码相同,负数的补码是在原码的基础上各位取反然后加 1。1000 000 是补码,减一然后按位取反得到其原码 1000 0000。(减一得 0111 1111,再按位取反得 1000 0000)。因为是负数,所以最小的 byte 值为 -2^7=-128。0111 1111 的十进制为 2^7-1=127(等比序列求和)。byte 是一个字节,共有 2^8=256 种可能性,也就是 -128~127。
其他基本数据类型同理,char 没有负值,占两个字节,所以取值范围是 0~2^16-1(65535)。
short
short 数据类型是 16 位、有符号的以二进制补码表示的整数,最小值是 -32768(-2^15) ;最大值是 32767(2^15 - 1) 。short 数据类型也可以像 byte 那样节省空间。一个 short 变量是 int 型变量所占空间的二分之一;默认值是 0 。
short s = 1000,short r = -20000。Copy to clipboardErrorCopied
int
int 数据类型是 32 位、有符号的以二进制补码表示的整数;最小值是 -2,147,483,648(-2^31) ;最大值是 2,147,483,647(2^31 - 1) ;默认值是 0 。
一般地整型变量默认为 int 类型,除了其他用途之外,int 类型的变量通常被用来控制循环和索引数组。虽然您可能认为在不需要 int 的较大范围的情况下,使用字节或 short 会比使用 int 更有效,但事实可能并非如此。原因是,当在表达式中使用字节和短值时,当表达式执行时,它们会被提升为 int。因此,当需要使用整数时,int 通常是最佳选择。
int a = 100000, int b = -200000Copy to clipboardErrorCopied
long
long 数据类型是 64 位、有符号的以二进制补码表示的整数;最小值是 -9,223,372,036,854,775,808(-2^63) ;最大值是 9,223,372,036,854,775,807(2^63 -1) ;默认值是 0L 。
long a = 100000L,Long b = -200000LCopy to clipboardErrorCopied
"L"理论上不分大小写,但是若写成"l"容易与数字"1"混淆,不容易分辩。所以最好大写。这种类型主要使用在需要比较大整数的系统上;
public class Light {
public static void main(String[] args) {
int lightspeed;
long days;
long seconds;
long distance;
lightspeed = 186000;
days = 1000;
seconds = days * 24 * 60 * 60;
distance = lightspeed * seconds;
System.out.print("In " + days);
System.out.print(" days light will travel about ");
System.out.println(distance + " miles.");
}
}Copy to clipboardErrorCopied
浮点型
浮点数,也称为实数,用于评估需要分数精度的表达式。例如,诸如平方根,或正弦和余弦等超常数的计算,其值的精度需要使用浮点类型。Java 实现了标准的(IEEE-754)浮点类型和运算符集。浮点类型有两种,float 和 double,分别表示单精度和双精度的数字。
float
float 数据类型是单精度、32 位、符合 IEEE 754 标准的浮点数,默认值是 0.0f 。float 在储存大型浮点数组的时候可节省内存空间;单精度在某些处理器上速度更快,占用的空间是双精度的一半,但当值非常大或非常小时,会变得不精确。当你需要一个分数部分,但不需要很大程度的精度时,float 类型的变量是有用的。例如,当表示美元和美分时,float 可以很有用。
float f1 = 234.5fCopy to clipboardErrorCopied
double
double 数据类型是双精度、64 位、符合 IEEE 754 标准的浮点数;浮点数的默认类型为 double 类型;默认值是 0.0d 。双精度,正如双关键字所表示的那样,使用 64 位来存储一个值。在一些为高速数学计算而优化的现代处理器上,双精度实际上比单精度快。所有的超越数学函数,如 sin( )、cos( )和 sqrt( ),都会返回 double 类型。当你需要在多次迭代计算中保持精度,或者要处理大值数字时,double 是最佳选择。
double d1 = 123.4
public class Area {
public static void main(String[] args) {
double pi, r, a;
r = 10.8;
pi = 3.1416;
a = pi * r * r;
System.out.println("Area of circle is " + a);
}
}Copy to clipboardErrorCopied
char
在 Java 中,用于存储字符的数据类型是 char。需要理解的一个关键点是,Java 使用 Unicode 来表示字符。Unicode 定义了一个完全国际化的字符集,可以代表所有人类语言中的所有字符。它是拉丁文、希腊文、阿拉伯文、西里尔文、希伯来文、片假名、汉字等几十个字符集的统一。在 Java 创建的时候,Unicode 要求 16 位。因此,在 Java 中 char 是一个 16 位的类型。char 的范围是 0 到 65,536。没有负数的 char。被称为 ASCII 的标准字符集仍然一如既往地从 0 到 127,而扩展的 8 位字符集 ISO-Latin-1 的范围是 0 到 255。由于 Java 的设计是为了让程序能够被编写为全球使用,所以使用 Unicode 来表示字符是有道理的。当然,对于英语、德语、西班牙语或法语等语言来说,使用 Unicode 的效率有些低,因为这些语言的字符可以很容易地包含在 8 位之内。但这就是全球可移植性必须付出的代价。
char 类型是一个单一的 16 位 Unicode 字符;最小值是 \u0000 (即为 0);最大值是 \uffff (即为 65,535)。char 数据类型可以储存任何字符;
char letter = 'A';
public class CharDemo {
public static void main(String[] args) {
char ch1, ch2;
ch1 = 88;
ch2 = 'Y';
System.out.print("ch1 and ch2: ");
System.out.println(ch1 + " " + ch2);
}
}Copy to clipboardErrorCopied
请注意,ch1 被分配的值是 88,这是对应于字母 X 的 ASCII(和 Unicode)值,如前所述,ASCII 字符集占据了 Unicode 字符集的前 127 个值。出于这个原因,所有在其他语言中使用过的字符的老把戏在 Java 中也能用。虽然 char 被设计成用来存放 Unicode 字符,但它也可以被用作整数类型,你可以在上面进行算术运算。例如,您可以将两个字符相加,或者递增一个字符变量的值。
public class CharDemo2 {
public static void main(String[] args) {
char ch1;
ch1 = 'X';
System.out.println("ch1 contains " + ch1);
ch1++;
System.out.println("ch1 is now " + ch1);
}
}Copy to clipboardErrorCopied
在程序中,首先给 ch1 赋值 X,然后,ch1 被递增,结果 ch1 包含 Y,即 ASCII(和 Unicode)序列中的下一个字符。这将导致 ch1 包含 Y,即 ASCII(和 Unicode)序列中的下一个字符。
boolean
boolean 数据类型表示一位的信息;只有两个取值:true 和 false;默认值是 false 。
类型默认值
对于数值类型的基本类型的取值范围,我们无需强制去记忆,因为它们的值都已经以常量的形式定义在对应的包装类中了。请看下面的例子:
public class PrimitiveTypeTest {
public static void main(String[] args) {
// byte
System.out.println("基本类型:byte 二进制位数:" + Byte.SIZE);
System.out.println("包装类:java.lang.Byte");
System.out.println("最小值:Byte.MIN_VALUE=" + Byte.MIN_VALUE);
System.out.println("最大值:Byte.MAX_VALUE=" + Byte.MAX_VALUE);
System.out.println();
// short
System.out.println("基本类型:short 二进制位数:" + Short.SIZE);
System.out.println("包装类:java.lang.Short");
System.out.println("最小值:Short.MIN_VALUE=" + Short.MIN_VALUE);
System.out.println("最大值:Short.MAX_VALUE=" + Short.MAX_VALUE);
System.out.println();
// int
System.out.println("基本类型:int 二进制位数:" + Integer.SIZE);
System.out.println("包装类:java.lang.Integer");
System.out.println("最小值:Integer.MIN_VALUE=" + Integer.MIN_VALUE);
System.out.println("最大值:Integer.MAX_VALUE=" + Integer.MAX_VALUE);
System.out.println();
// long
System.out.println("基本类型:long 二进制位数:" + Long.SIZE);
System.out.println("包装类:java.lang.Long");
System.out.println("最小值:Long.MIN_VALUE=" + Long.MIN_VALUE);
System.out.println("最大值:Long.MAX_VALUE=" + Long.MAX_VALUE);
System.out.println();
// float
System.out.println("基本类型:float 二进制位数:" + Float.SIZE);
System.out.println("包装类:java.lang.Float");
System.out.println("最小值:Float.MIN_VALUE=" + Float.MIN_VALUE);
System.out.println("最大值:Float.MAX_VALUE=" + Float.MAX_VALUE);
System.out.println();
// double
System.out.println("基本类型:double 二进制位数:" + Double.SIZE);
System.out.println("包装类:java.lang.Double");
System.out.println("最小值:Double.MIN_VALUE=" + Double.MIN_VALUE);
System.out.println("最大值:Double.MAX_VALUE=" + Double.MAX_VALUE);
System.out.println();
// char
System.out.println("基本类型:char 二进制位数:" + Character.SIZE);
System.out.println("包装类:java.lang.Character");
// 以数值形式而不是字符形式将Character.MIN_VALUE输出到控制台
System.out.println("最小值:Character.MIN_VALUE="
+ (int) Character.MIN_VALUE);
// 以数值形式而不是字符形式将Character.MAX_VALUE输出到控制台
System.out.println("最大值:Character.MAX_VALUE="
+ (int) Character.MAX_VALUE);
}
}Copy to clipboardErrorCopied
下表列出了 Java 各个类型的默认值:
数据类型 | 默认值 |
---|---|
byte | 0 |
short | 0 |
int | 0 |
long | 0L |
float | 0.0f |
double | 0.0d |
char | 'u0000' |
String (or any object) | null |
boolean | false |
引用类型
在 Java 中,引用类型的变量非常类似于 C/C++的指针。引用类型指向一个对象,指向对象的变量是引用变量。这些变量在声明时被指定为一个特定的类型,比如 Employee、Puppy 等。变量一旦声明后,类型就不能被改变了。对象、数组都是引用数据类型,所有引用类型的默认值都是 null;一个引用变量可以用来引用任何与之兼容的类型。例子:Site site = new Site("JiangGuo")
。
枚举类型
枚举类型(Enumerated Type)很早就出现在编程语言中,它被用来将一组类似的值包含到一种类型当中。而这种枚举类型的名称则会被定义成独一无二的类型描述符,在这一点上和常量的定义相似。不过相比较常量类型,枚举类型可以为申明的变量提供更大的取值范围。为了改进 Java 语言在这方面的不足弥补缺陷,5.0 版本 SDK 发布时候,在语言层面上增加了枚举类型。枚举类型的定义也非常的简单,用 enum 关键字加上名称和大括号包含起来的枚举值体即可:
// 定义一周七天的枚举类型
public enum WeekDay { Mon, Tue, Wed, Thu, Fri, Sat, Sun }
// 读取当天的信息
WeekDay today = readToday();
// 根据日期来选择进行活动
switch(today) {
Mon: do something; break;
Tue: do something; break;
Wed: do something; break;
Thu: do something; break;
Fri: do something; break;
Sat: play sports game; break;
Sun: have a rest; break;
}Copy to clipboardErrorCopied
最直接的益处就是扩大 switch 语句使用范围。5.0 之前,Java 中 switch 的值只能够是简单类型,比如 int、byte、short、char, 有了枚举类型之后,就可以使用对象了。需要注意的是,Java 中的枚举类型实际上会被编译为类文件,值即是这个类型的成员变量,譬如我们丰富下前文的枚举类型:
public enum WeekDay {
Mon("Monday"),
Tue("Tuesday"),
Wed("Wednesday"),
Thu("Thursday"),
Fri("Friday"),
Sat("Saturday"),
Sun("Sunday");
private final String day;
private WeekDay(String day) {
this.day = day;
}
public static void printDay(int i) {
switch (i) {
case 1:
System.out.println(WeekDay.Mon);
break;
// ...
default:
System.out.println("wrong number!");
}
}
public String getDay() {
return day;
}
}Copy to clipboardErrorCopied
自定义枚举类型
在 JDK5 之前,Java 语言不支持枚举类型,只能用类(class)来模拟实现枚举类型。
/** 订单状态枚举 */
public final class OrderStatus {
/** 属性相关 */
/** 状态取值 */
private final int value;
/** 状态描述 */
private final String description;
/** 常量相关 */
/** 已创建(1) */
public static final OrderStatus CREATED = new OrderStatus(1, "已创建");
/** 进行中(2) */
public static final OrderStatus PROCESSING = new OrderStatus(2, "进行中");
/** 已完成(3) */
public static final OrderStatus FINISHED = new OrderStatus(3, "已完成");
/** 构造函数 */
private OrderStatus(int value, String description) {
this.value = value;
this.description = description;
}
/** 获取状态取值 */
public int getValue() {
return value;
}
/** 获取状态描述 */
public String getDescription() {
return description;
}
}