文章目录
一 数据类型与运算符
1.1 数据类型
java
是一种强类型语言,我们必须为每一个变量声明一种类型。数据类型限制了变量可以持有的值和表达式产生的值,以及在这些值上可以进行的操作。强类型机制有助于在编译是探测错误。
在java中,数据类型分为两类:基本数据类型(Primitive Type)和引用数据类型(Reference Type)。基本数据类型包括整数类型(byte、short、int和long)、浮点类型(float和double)、字符类型(char)和布尔类型(boolean)。引用类型包括类类型、接口类型、数组类型和一个特殊的空类型(null空指针)。
有两种数据类型,相应的就有两种数据值。它们可以被存储在变量中。基本数据类型定义的变量存储的是值;而引用类型定义的变量存储的则是对象的引用(引用值)。
1.1.1 基本数据类型
java 是一门面向对象的语言。一切都可以视为对象。但基于工程上的考虑,java引入了8中的基本类型,它们能够在执行效率以及内存使用上提升软件的性能。
8种基本数据类型包括:
- 整数类型:
byte
、short
、int
、long
- 浮点类型:
float
、double
- 字符类型:
char
- 布尔类型:
boolean
数据类型 | 代表含义 | 长度 | 默认值 | 取值范围 | 包装类 |
---|---|---|---|---|---|
byte | 字节型 | 8 | (byte)0 | 128~217 | Byte |
short | 短整数型 | 16 | (short)0 | -215~215-1 | Short |
int | 整数型 | 32 | 0 | -231~231-1 | Integer |
long | 长整数型 | 64 | OL | -263~-263-1 | Long |
float | 单精度浮点型 | 32 | 0.0f | 1.4e-45 ~ 3.4e+38 | Float |
doule | 双精度浮点型 | 64 | 0.0d | 4.9e-324 ~ 1.789e+308 | Double |
char | 字符型 | 16 | ‘\u000’ | ‘\u0000’ ~ ‘\uFFFF’ | Character |
boolean | 布尔型 | - | false | false,true | Boolean |
void | 空 | - | - | - | Void |
整数类型
整数
用于表示没有小数部分的数值,允许负数。4 种整数类型:byte
、short
、int
、long
,仅存储空间和表示的数据范围不同。- 长整型数值有一个后缀 L,如:5000000000L
- 十六进制数值有一个前缀 0x,如:0x0F 表示15
- 八进制数值有一个前缀0,如:010 表示8
- 从 Java7 开始,使用0b前缀表示二进制数,而且还可以为数字加下划线,如:0b1111_0100_0010_0100_0000
// 下边赋值语句超过 short 的最大值 32767,程序在编译时就会报错
// short s1 = 32768;
// 数值计算时如果计算结果超长了,程序不会报任何错误,会给你一个错误的结果
Integer n1 = 2 * Integer.MAX_VALUE;
System.out.println(n1);
long n2 = 1000L;
System.out.println(n2);
int n3 = 0x0F;
System.out.println(n3);
int n4 = 010;
System.out.println(n4);
int n5 = 0b1_0001;
System.out.println(n5);
1.1.2 引用数据类型
在 Java 语言中,除了8种基本数据类型外,其他的都是引用类型,指向各种不同的对象(类类型、接口类型、数组类型),理解了引用可以更好的帮助掌握 Java 对象生命周期和 JVM 内部机制。
Java 引用类型包含以下四类,详细的内容可以在 JVM 章节时去学习,这里只提前列举一下。
- 强引用
- 软引用
- 弱引用
- 幻象引用
1.2 变量/常量/字面量
int n1;
final int n2 = 10;
String str1 = "hello world";
在上述代码中:
n1
、str1
是变量n2
是常量10
、hello world
是字面量
1.2.1 变量
在 Java 中,每一个变量属于一种类型。在声明变量时,变量所属的类型位于变量名之前。变量必须先初始化,然后再进行使用,可以在声明时进行初始化,也可以先声明后进行初始化。
从本质上来讲:一个变量就是一个存储位置,并且具有相关联的类型,只能存储对应类型的数据。
int n1;
// n1 在使用前(打印)未进行初始化,下边语句报错
// System.out.println(n1);
int n2 = 100;
System.out.println(n2);
int n3;
n3 = 100;
System.out.println(n3);
变量名
必须是一个以字母开头的由字母或数字构成的序列,大小写敏感。变量名的长度没有限制,但是不能是 Java 语言保留关键字。
在 Java 中字母和数字范围要比很多其他语言要大:
- 字母包括 ‘A’ ~ ‘Z’、‘a’ ~ ‘z’、’_’、’$’ 或在某种语言中代表字母的任何 Unicode 字符,是作为参数调用
Character.isJavaIdentifierStart( int )
方法时返回 true 的字符。 - 数字包括 ‘0’ ~ ‘9’ 和在某种语言中代表数字的任何 Unicode 字符。
- 字母或数字是作为参数调用
Character.isJavaIdentifierPart( int )
方法时返回 true 的字符。
建议:
- 变量的声明尽可能地靠近变量第一次使用的地方。
- 虽然
$
是一个合法字符,但不要在代码中使用。 - 逐一声明每一个变量以提高程序可读性,不使用类似
int i, j;
这样同时声明多个变量。 - 变量使用驼峰命名,首字母小写,其余单词首字母大写,例如:int userCount;。
- 定义变量时使用有意义的名称,不要怕变量过长;不要使用不规则的简写,比如拼音首字母;不要中英文混合。
1.2.2常量
在 Java 中,使用关键字 final 指示常量,表示这个变量只能被赋值一次。一旦被赋值就不能够再被修改了。常量名一般使用全大写。
final int MAX_AGE = 200;
1.2.3字面量
字面量是类型为基本类型、String类型和空类型的值在源程序中的表示。
1.3 运算符
Java 中的数据是通过使用运算符来操作的,运算符接受一个或多个参数,并生成一个新值。除了以下两种情况,所有的运算符都只能操作“基本类型”。
- =、==、!= 运算符可以用于所有对象
- String 类支持 + 和 +=
运算符的运算结果类型有时会根据被计算的值而变化。比如:两个 int 相加,结果类型是 int,两个 byte 或者 short 相加,返回值的类型就是 int。
除了赋值运算符外,运算符本身不会更改变量的值。
Object obj1 = new Object();
Object obj2 = new Object();
System.out.println(obj1 == obj2);
System.out.println(obj1 != obj2);
String str1 = "hello" + " ";
str1 += "world";
System.out.println(str1);
int n1 = 1;
int n2 = 1;
int n3 = n1 + n2;
System.out.println(n3);
byte b1 = 1;
byte b2 = 1;
// 两个 byte 相加,返回值的类型是 int,因此下边语句报错
// byte b3 = b1 + b2;
short s1 = 1;
short s2 = 2;
// // 两个 short 相加,返回值的类型是 int,因此下边语句报错
// short s3 = s1 + s2;
Set<Short> set = new HashSet<>();
set.add(s1);
set.add(s2);
System.out.println(set);
set.remove(s1 + 1);
System.out.println(set);
set.remove((short) (s1 + 1));
System.out.println(set);
算法运算符
算术运算符包括加号(+)、减号(-)、除号(/)、乘号(*)和取模(%)。整数除法会直接舍掉计算结果的小数部分,而不是四舍五入。
Java 也支持同时进行运算和赋值操作,用运算符后紧跟一个等号来表示,它对于 Java 中所有运算符都适用,只要其有实际意义就行。
System.out.println(3 + 2);
System.out.println(3 - 2);
System.out.println(3 * 2);
System.out.println(3 / 2);
System.out.println(3 % 2);
System.out.println(3 / 2.0);
System.out.println("===============================");
int n1 = 3;
n1 += 2;
System.out.println(n1);
n1 = 3;
n1 -= 2;
System.out.println(n1);
n1 = 3;
n1 *= 2;
System.out.println(n1);
n1 = 3;
n1 /= 2;
System.out.println(n1);
n1 = 3;
n1 %= 2;
System.out.println(n1);
n1 = 3;
n1 /= 2.0;
System.out.println(n1);
一元加、一元减
一元加(+)和一元减(-)前边加法和减法使用来相同的符号。
一元减号用于转变数据的符号,而一元加号只是为了与一元减号相对应,它唯一的作用仅仅是将较小类型的操作数提升为 int。
int n1 = 2;
int n2 = -n1;
int n3 = -n2;
int n4 = +n3;
System.out.println(n2);
System.out.println(n3);
System.out.println(n4);
short s1 = 2;
// 一元加会将小类型操作数提升为 int,所以下一句报错
// short s2 = +s1;
int s3 = +s1;
自动递增和递减
自动递增(++)和自动递减(–),各有两种使用方式,“前缀式”和“后缀式”:
- 前缀式:先执行运算,再生成值。例如:
++a
,--a
- 后缀式:先生成值,再执行运算。例如:
a++
,a--
int n1 = 5;
int r1 = 1 + ++n1;
System.out.println(r1);
System.out.println(n1);
int n2 = 5;
int r2 = n2++;
System.out.println(r2);
System.out.println(n2);
int n3 = 5;
int r3 = --n1;
System.out.println(r3);
System.out.println(n3);
int n4 = 5;
int r4 = n4--;
System.out.println(r4);
System.out.println(n4);
关系运算符
关系运算符生成的是一个 boolean 结果,它们计算的是操作数的值之间的关系。如果关系成立返回 true(真);如果关系不成立,则返回 false(假)。
关系运算符包括:
- < 小于
- >大于
- <= 小于等于
- >= 大于等于
- == 等于
- != 不等于
System.out.println(5 > 3);
System.out.println(5 > 3.0);
System.out.println(5 < 3);
System.out.println(5 == 3);
System.out.println(5 != 3);
逻辑运算符
逻辑运算符包括:与(&&)、或(||)、非(!),根据参数的逻辑关系,生成一个布尔值 true 或者 false。
System.out.println(5 == 5 && 3 == 5);
System.out.println(5 == 5 && 3 == 3);
System.out.println(5 == 5 || 3 == 5);
System.out.println(5 == 3 || 5 == 5);
System.out.println(!(5 == 3 || 5 == 5));
1.4 数据类型转换
在程序运行时,Java 会在适当的时候,将一种数据类型自动转换成另一种。例如:将一个整数值赋值给浮点型,编译器会将 int 自动转换成 float。
Java 允许我们把任何基本数据类型转换成别的基本数据类型,但布尔类型除外,布尔类型不允许进行任何类型的转换处理。
自动类型转换
- 不会出现问题的类型转换,Java 编译器会自动转换,比如低精度的数字向高精度的数字转换。
- 自动类型转换可以发生在算数运算,也可以发生在赋值。
- 数值精度顺序:
double
>float
>long
>int
>short
>byte
double d1 = 1F;
double d2 = 1L;
short s1 = 1;
short s2 = 1000000;
强制类型转换
- 可能出现问题的类型转换,需要使用强制类型转换,比如高精度数值向低精度数值转换。
- 强制类型转换也是运算符:
小括号
括起来的目标类型放在被转换的值前面。 - 强制转换会造成数据精度丢失。
short s1 = 1;
int n1 = s1;
// int 到 short,精度下降,不能直接转换,报错
// short s2 = n1;
short s3 = (short) n1;
int n2 = 100000;
short s4 = (short) n2;
// s4 结果是 -31072,强制转换会造成精度丢失,结果不准确
System.out.println(s4);
1.5 包装类的装箱拆箱
Java 语言虽然视一切都是对象,但是8种基本数据类型除外。Java 为8种基本数据类型提供了对应的包装类。包装类相对于基本类型,有哪些特性:
- 对象
包装类是一个对象,包含 hashCode、getClass、max、min 等属性和方法。 - 可用于定义泛型参数
包装类可以用于定义泛型,而基本数据类型不行。
List<Integer> list1 = new ArrayList<>();
// 基本数据类型不能用于定义泛型,所以下边语句报错
// List<int> list2 = new ArrayList<>();
- 序列化
所有包装类都实现了java.io.Serializable
接口,所以包装类都支持序列化和反序列化。
public final class Integer extends Number implements Comparable<Integer> {
......
}
public abstract class Number implements java.io.Serializable {
......
}
- 类型转换
包装类提供类型转换方法。
int n1 = Integer.parseInt("100");
int n2 = Integer.parseInt("100");
System.out.println(n1 == n2); // true 因为 n1 和 n2为基本数据类型 而基本数据类型存储的是值 而不是引用地址
- equals 只能对引用类型进行比较,不能对基本数据类型进行比较
- 基本数据类型的比较方式,只能用 == 比较
- 高频区间进行了数据缓存
在实践中,大部分数据操作都是集中在较小的数值范围,因此从 Java 5 开始新增了高频区间数据缓存,例如调用
Integer.valueOf
静态方法时,会利用缓存机制,如果缓存中已经存在,则直接返回缓存的对象,而不会重新创建。
各个包装类的缓存范围:
- Byte:全部缓存,缓存范围 -128~127
- Short:缓存范围 -128~127
- Integer:缓存范围 -128~127
- Long:缓存范围 -128~127
- Float/Double:未缓存
- Character:缓存范围 0~127
- Boolean:缓存了 true/false 实例,Boolean.TRUE/Boolean.FALSE
注:Integer 是唯一可以通 JVM 参数
-XX:AutoBoxCacheMax=N
来设置缓存上限的。
装箱/拆箱
在 Java 5 中,引入了自动装箱/自动拆箱功能(boxing/unboxing),Java 可以根据上下文,自动进行转换,从而简化了编程。
自动装箱/自动拆箱发生在编译阶段,例如:对于整数,javac 会把装箱转换成 Integer.valueOf(),而把拆箱替换未 Integer.intValue()。