文章目录
- 字面常量
- Java数据类型
- 变量
- 定义变量的方式
- 整形变量
- 长整型变量
- 短整型变量
- 字节型变量
- 浮点型变量
- 双精度浮点型
- 单精度浮点型
- 字符型变量
- 布尔型变量
- 类型转换
- 自动类型转换(隐式)
- 强制类型转换(显式)
- 类型提升
- byte与byte的运算
- 字符串类型
- int转成String(字符串)
- String转成int
字面常量
在这段代码中,System.out.println("Hello world");
语句不论程序何时运行,输出的都是Hello world
,其实Hello world
就是字面常量。
常量:程序运行期间,固定不变(不能改变)的量称为常量。
我们再举个例子:
什么叫做变量呢
这个a
就是变量,它在函数内部定义,所以它也是个局部变量。
变量指的是在程序运行过程中可以修改,例如将a
的值改为9,再将a
的值改为8:
而上面的System.out.println(10);
中的10就改不了。
字面常量的分类:
- 字符串常量:由
" "
括起来的。如"12344"
、"hello"
、"你好"
- 整型常量:程序中直接写的数字(没有小数点)
- 浮点数常量:程序中直接写的小数
- 字符常量:由
' '
括起来的单个字符。如'A'
、'1'
- 布尔常量:只有两种:
true
和false
- 空常量:
null
我们在学C语言的时候学过const
这个关键字,被const
修饰后变得有了常属性(不能被修改),那么Java中有没有这种作用的关键字呢?
有的,叫final
。我们在变量a
前面加上final
:
加上final
后发现修改a
的值就报错了,所以:被final
修饰的变量叫做常量,是不可以被修改的。
Java数据类型
在Java中数据类型主要分为两类:基本数据类型和引用数据类型。
而基本数据类型又分为四类:整形、浮点型、字符型、布尔类型
四类里共包含八种数据类型:
我们发现,上表中每种数据类型所占用的内存大小都写死了。可我们学习C语言的时候,有些数据类型:如整形,在16位操作系统与32位操作系统中所占的内存大小是不一样的,为什么这里直接写死了呢?
答:因为在Java中,数据类型不关心是多少位的系统。不论是多少位系统,数据类型所占用的字节数是一定的。
好处:可移植性高(因为Java不关心是多少位的操作系统,不论是多少位的操作系统,所占用的字节都是一定的)
注意:
- 所以,不论是在16位系统还是32位系统,int都占用4个字节,long都占用8个字节
- 整形和浮点型都是带有符号的。
可以看到上表中没有 有符号整型与无符号整型,所以整型与浮点型既能表示正数,也能表示负数,没有无符号整型的概念。 - 整型默认为
int
,浮点型默认为double
- 字符串属于引用类型,这种类型以后介绍
什么是字节?
字节是计算机中表示空间大小的基本单位.
计算机使用二进制表示数据. 我们认为 8 个二进制位(8个bit位) 为一个字节(1Byte).
我们平时的计算机为 8GB 内存, 意思是 8G 个字节.
其中 1KB = 1024 Byte, 1MB = 1024 KB, 1GB = 1024 MB.
所以 8GB 相当于 80 多亿个字节.
内存单位最小的是bit,除了前两个进制是8,后面全是1024
变量
定义变量的方式
在程序中,除了有始终不变的常量外,有些内容可能会经常改变,比如:人的年龄、身高、成绩分数、数学函数的
计算结果等,对于这些经常改变的内容,在Java程序中,称为变量。
定义变量的语法格式为:
数据类型 变量名=初始值;
比如:
int a=10;
//定义整形变量a,a是变量名也被称为标识符
//该变量中放置的值为10
double d = 3.14;
char c = 'A';
boolean b = true;
System.Out.println(a);
System.Out.println(d);
System.Out.println(c);
System.Out.println(b);
a = 100;
//a是变量,a中的值是可以修改的
//注意:= 在java中表示赋值,即将100交给a,a中保存的值就是100
System.Out.println(a);
//注意:在一行可以定义多个相同类型的变量
int a1 = 10, a2 = 20, a3 = 30;
System.Out.println(a1);
System.Out.println(a2);
System.Out.println(a3);
这样写可以打印出int型变量所能表示的范围:
运行后的结果:
这里的Integer
是什么呢?
Integer
的术语是:包装类。
通俗理解就是int
的plus版本。
我们可以像int
一样用它定义变量:
运行程序:
没有任何错误。
Integer
与int
的区别是什么呢?
区别后面再仔细说明,现在只用理解Integer
也是int
的一种类型。
整形变量
//方式一:在定义时给出初始值
int a = 10;
System.Out.println(a);
//方式二:在定义时没有给初始值,但使用前必须设置初值
int b;
b = 10;
System.Out.println(b);
//使用方式二定义后,在使用前如果没有赋值,则编译期间会报错
int c;
System.Out.println(c);
//int型变量所能表示的范围:
System.Out.println(Integer.MIN_VALUE);
System.Out.println(Integer.MAX_VALUE);
//注意:在定义int型变量时,所赋值不能超过int的范围
int d = 12345678901234; //编译时报错,初值超过了int的范围
int
不论在何种系统下都是4个字节- 推荐使用方式一定义,如果没有合适的初始值,可以设置为0
- 在变量设置初始值时,值不能超过
int
的表示范围,否则会导致溢出 - 变量在使用之前必须要赋值,否则编译报错
int
的包装类型为Integer
长整型变量
int a = 10;
long b = 10;//long定义的长整型变量
long c = 10L;//+L是为了区分整形与长整型
//建议long类型变量的初始值之后加L或者l
long d = 10l; //一般用大写L,因为小写l与1不好区分
//long型变量所能表示的范围:
//这个数据范围远超过 int 的表示范围.
//足够绝大部分的工程场景使用.
System.Out.println(Long.MIN_VALUE);
System.Out.println(Long.MAX_VALUE);
每种包装类型不一样,int
叫Integer
,long
叫Long
.
我们打印出长整型变量的范围看看:
注意:
- 长整型变量的初始值后加
L
或者l
,推荐大写 - 长整型不论在哪个系统下都占8个字节
- 长整型表示范围为:-2^63 ~ 2^63-1
long
的包装类型为Long
(只是将首字母大写)
短整型变量
short a = 10;
System.Out.println(a);
// short型变量所能表示的范围:
System.Out.println(Short.MIN_VALUE);
System.Out.println(Short.MAX_VALUE);
注意:
- short在任何系统下都占2个字节
- short的表示范围为:-32768 ~ 32767(-2^15 ~ 2^15-1)
- 使用时注意不要超过范围(一般使用比较少)
- short的包装类型为
Short
(也只是将首字母大写)
字节型变量
比短整型更小的数据类型叫字节型
//用字节型定义变量并打印
byte b = 10;
System.Out.println(b);
// byte型变量所能表示的范围:
System.Out.println(Byte.MIN_VALUE);
System.Out.println(Byte.MAX_VALUE);
- byte在任何系统下都占1个字节
- byte的范围是:-128 ~ 127(-2^7~2^7-1)
- 字节的包装类型为
Byte
(数据类型首字母大写)
思考:byte、short、int、long都可以定义整形变量,为什么要给出4中不同类型呢?
因为它们字节大小是不一样的,不同需求下要的内存大小不一样
浮点型变量
双精度浮点型
//双精度浮点型变量的定义与输出
double d = 3.14;
System.Out.println(d);
代码1:
public class Test {
public static void main(String[] args) {
int a=1;
int b=2;
System.out.println(a/b);
}
}
思考:a是一个正数,b也是一个整数,a/b会得到小数吗
答:不可以,因为a,b都是int类型的,从数学角度是0.5,但在Java中, int 除以 int 的值仍然是 int。
所以运行后结果为0
如果想最后的结果是个小数,那么a,b中至少有一个(或者全部)变为double
类型,最后结果才会得出小数
public class Test {
public static void main(String[] args) {
double a=1.0;
int b=2;
System.out.println(a/b);
}
}
代码二:
public class Test {
public static void main(String[] args) {
double num=1.1;
System.out.println(num*num);
}
}
这个代码会输出1.21
吗?
运行:
最后的结果后面还有个2,为什么?
因为小数是不能精确表示每一位的,它只能说精确表示小数点后几位。若只能精确表示小数点的后6位,那么小数点后6位就是精确的,后6位以后的数字就不是精确的了。
所以对于小数来说,计算出这样的值不用奇怪。
注意:
- double在任何系统下都占8个字节
- 浮点数与整数在内存中的存储方式不同,不能单纯使用
2^n
的形式来计算 - double的包装类型为
Double
- double 类型的内存布局遵守 IEEE 754 标准(和C语言一样), 尝试使用有限的内存空间表示可能无限的小数, 势必会存在一定的精度误差,因此浮点数是个近似值,并不是精确值。
单精度浮点型
float num = 1.0f;//写作 1.0F 也可以
System.out.println(num);
为什么要加f或F
呢?
因为对于小数来说默认是double
类型的(8个字节),而给变量num
的定义是float
类型(4个字节)。当把8个字节给4个字节肯定是放不下的,所以后面加f或F
来标识1.0
是单精度浮点型。
float 类型在 Java 中占4个字节, 同样遵守 IEEE 754 标准. 由于表示的数据精度范围较小, 一般在工程上用到浮点数
都优先考虑 double, 不太推荐使用 float.
float的包装类型为Float
。
字符型变量
char c1='A';//大写字母
char c2='1';//数字字符
char c3='帅';
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
注意:
- Java 中使用
单引号 + 单个字母
的形式表示字符字面值 - 计算机中的字符本质上是一个整数(对应一个数字)。在 C 语言中使用的字符集表是 ASCII, 而在 Java 中是 Unicode。
一个字符占用2个字节, 所以可以表示的字符种类更多了,包括中文。例如上面的c3
- char的包装类型为
Character
可以通过Character
定义一个字符c
Character c='p';
注意:汉字没有对应的数字与它对应
布尔型变量
布尔类型常用来表示真假。
注意:与C语言不同,Java中布尔类型写做boolean
boolean b = true;
System.out.println(b);
注意:
- Java虚拟机规范中,并没有明确规定boolean占几个字节,也没有专门用来处理boolean的字节码指令。在Oracle公司的虚拟机实现中,boolean占1个字节
- boolean的包装类型为
Boolean
- boolean 类型的变量只有两种取值,
true
表示真,false
表示假 - 之前我们在学习C语言中会说:0是假,非0是真。
但是在Java 中的 boolean 类型和 int 是不能相互转换的, 不存在 1 表示 true、0 表示 false 这样的说法.
在Java中,布尔类型只有2个取值:true
或false
boolean b=true;
boolean b=1;//err
举例:以下两种类型不一样,所以不能进行运算
类型转换
Java作为一个强类型编程语言,当不同类型的变量相互赋值的时候,会有较严格的校验。
强类型: 指的是对类型的检查非常严格
int a=10;
long b=100L;
b=a;//可以通过编译
a=b;//编译失败
为什么将a
赋给b
就可以呢,而b
赋给a
不行呢?
因为a
是4个字节,b
是8个字节。把4个字节的数据给8个字节不会出现问题,放得下。
但是如果把8个字节的数据给4个字节时,会放不下,所以肯定会出现问题。
此时就涉及到了类型转换。
在Java中,参与运算数据类型不一致时,就会进行类型转换。
Java中类型转换主要分为两类:自动类型转换(隐式)和强制类型转换(显式)
自动类型转换(隐式)
自动类型转换:代码不需要经过任何处理,在代码编译时,编译器会自动进行处理。
特点:数据范围小的转为数据范围大的时会自动进行。
int a = 100;
long b = 10L;
b = a;
//a和b都是整形,a的范围小,b的范围大,当将a赋给b时
//编译器会自动将a提升为long类型,然后赋值(隐式)
a = b;
//编译报错,long的范围比int范围大,会有数据丢失,不安全
float f = 3.14F;
double d = 5.12;
d = f;
//编译器会自动将f转换为double,然后进行赋值(隐式)
f = d;
//double表示数据范围大
//直接将double交给float会有数据丢失,不安全
byte b1 = 100;
//编译通过,100没有超过byte的范围,编译器隐式将100转换为byte
byte b2 = 257;
//编译失败,257超过了byte的数据范围,有数据丢失
注意:在IDEA中将在byte范围中的整数赋值给byte类型的变量时,虽然整数默认是int类型(数据范围大的转为数据范围小的),但是只要整数在byte范围中,都是被隐式转换:将100转为byte。
当整数不在byte范围内时是需要显示转换的。
byte b1 = 100;
// 100默认为int,没有超过byte范围,隐式转换
byte b2 = (byte)257;
// 257默认为int,超过byte范围,需要显示转换,否则报错
强制类型转换(显式)
我们之前学C语言的时候已经学过强制类型转换了:将b
强制类型转换为int
类型,再将int
类型的b
赋值给a
:
int a=10;
long b=100L;
a=(int)b;
这种做法从编译层面上来讲不会报错,但是是一颗雷,可能会在某种情况下爆炸!
因为非要把8字节转换成4字节,那么一定会有4个字节存不下,有精度的丢失。
举个例子:
byte a=10;
int b=128;
a=(byte)b;
System.out.println(a);
这种情况就算是将b
强制类型转换成了byte
类型,a
也存不了128。
byte的范围是:-128 ~ 127(-2^7~2^7-1)
结果是-128
为什么呢?
我们知道byte
的取值范围为这个圈
int 类型的变量 b 值为 128 ,而 byte 类型的取值范围是 -128 到 127 。当执行 a = (byte)b; 将 int 类型的 128 强制转换为 byte 类型时,会发生数据截断。
在计算机中,数据以二进制补码的形式存储。 128 的32位二进制表示为 00000000 00000000 00000000 10000000 ,强制转换为 byte 类型后,只保留低8位,即 10000000 。在补码表示中, 10000000 代表的十进制值就是 -128 (补码 10000000 的原码计算:补码减1得到反码 01111111 ,反码按位取反得到原码 10000000 ,对应的十进制值为 -128 )。
所以,当执行 System.out.println(a); 时,输出的结果就是 -128 。
当把b
的值改为131的时候,结果为-125
这是因为:
int 类型的变量 b 值为 131 , byte 类型范围是 -128 到 127 。当把 int 类型的 131 强制转换为 byte 类型时,会发生数据截断。
在计算机中数据以二进制补码形式存储, 131 的二进制表示(32位, int 类型是32位)为 00000000 00000000 00000000 10000011 ,强制转换为 byte 类型后( byte 是8位),取低8位 10000011 ,这是一个补码形式,对于补码 10000011 ,其原码计算过程为:补码减1得到反码 10000010 ,反码按位取反得到原码 11111101 ,对应的十进制值就是 -125 。
所以执行 a=(byte)b; 后, byte 类型的变量 a 的值是 -125 , System.out.println(a); 输出的结果就是 -125 。
以上强制类型转换并不能得到我们预期的效果,所以强制类型转换是有隐患的,不建议使用强制类型转换。
强制类型转换:当进行操作时,代码需要经过一定的格式处理,不能自动完成。
特点:数据范围大的到数据范围小的。
int a = 10;
long b = 100L;
b = a;
//int-->long,数据范围由小到大,隐式转换
a = (int)b;
//long-->int, 数据范围由大到小,需要强转,否则编译失败
float f = 3.14F;
double d = 5.12;
d = f;
//float-->double,数据范围由小到大,隐式转换
f = (float)d;
//double-->float, 数据范围由大到小,需要强转,否则编译失败
a = d;// 报错,类型不兼容
a = (int)d;
//int没有double表示的数据范围大,需要强转,小数点之后全部丢弃
byte b1 = 100;
//100默认为int,没有超过byte范围,隐式转换
byte b2 = (byte)257;
//257默认为int,超过byte范围,需要显示转换,否则报错
boolean flag = true;
a = flag; // 编译失败:类型不兼容
flag = a; // 编译失败:类型不兼容
注意:
-
不同类型变量之间赋值, 表示范围更小的类型能隐式转换成范围较大的类型
-
如果需要把范围大的类型赋值给范围小的, 需要强制类型转换, 但是可能精度丢失
-
将一个字面值常量进行赋值的时候, Java 会自动针对数字范围进行检查
为什么第一种情况将整型128赋值给byte变量报错,而第二种情况没有报错呢?
第一种情况报错原因:
在Java里,所有“整数型字面量” 都会默认被当作 int 类型处理 。 byte 类型取值范围是 -128 到 127 ,当代码byte a = 128;
尝试把 int 类型的 128 直接赋值给 byte 类型变量 a 时,因为 128 超出了 byte 类型的取值范围,编译器会检测到这种不兼容,提示 “不兼容的类型:从 int 转换到 byte 可能会有损失” 错误 ,不允许这种赋值操作,以避免数据丢失或错误。
第二种情况不报错原因:
在byte a = 10; int b = 128; a = (byte)b;
这段代码中,使用了强制类型转换 (byte)b 。强制类型转换是显式告知编译器,程序员明确知道会有数据精度损失或溢出风险,但仍要进行类型转换 。编译器会按照指令执行转换操作,将 int 类型的 128 截断为 byte 类型。 128 的32位二进制表示为 00000000 00000000 00000000 10000000 ,强制转换为 byte 类型后,保留低8位 10000000 ,在补码表示中代表十进制的 -128 ,虽然发生了数据截断和值的改变,但因为有强制类型转换操作,编译器不会报错 。 -
强制类型转换不一定能成功,不相干的类型不能互相转换
就如上面的a=flag;
和flag = a
,布尔类型与整型,这两种类型完全不相干,强转都不行
为什么将整型127赋值给byte不会报错
编译期常量检查:若像 127 这样的整型字面量,在编译阶段,编译器能确定其值。此时,编译器会检查该值是否在目标类型(byte)的取值范围内。若在范围内,就允许将其赋值给 byte 变量,相当于自动在背后执行了隐式类型转换 ,无需显式的强制类型转换。
类型兼容性规则:从类型兼容性角度, byte 是小容量类型, int 是大容量类型 。Java允许在满足取值范围要求的前提下,小容量类型(如 byte )可以自动接收来自大容量类型(如 int )的赋值,只要值在小容量类型可表示范围内,这是语言设计时为方便编码设定的规则 。
但如果是将 int 类型变量赋值给 byte 变量,即使 int 变量的值在 byte 范围内,也必须进行强制类型转换,否则会报错,比如 int num = 127; byte b = num;
这样会编译报错,需写成 byte b = (byte)num;
。
这是为什么呢?
这是由Java的类型转换机制决定的:
自动类型转换规则:
Java中,小容量类型(取值范围小)向大容量类型(取值范围大)转换时,会自动进行隐式类型转换,无需额外操作 。例如 byte 转 int , byte 类型范围是-128到127 , int 类型范围是 -2147483648 到 2147483647 , byte 类型变量赋值给 int 变量时,编译器自动将 byte 值扩展为 int 类型 。
强制类型转换的必要性:
当大容量类型转小容量类型时,如 int 转 byte ,因为可能丢失数据或导致溢出,Java不会自动转换 。比如 int 类型值超出 byte 范围(-128到127 ) ,直接赋值会有数据丢失风险 。所以必须用强制类型转换,明确告知编译器开发者清楚风险并仍要进行转换 。即便 int 变量值在 byte 范围内,也需遵循该规则使用强制类型转换 ,这是语言设计为保证类型安全、防止意外数据丢失和错误 。
也就是,只要是大容量类型转小容量类型时,就都要遵循规则进行强制类型转换。
类型提升
不同类型的数据之间相互运算时,数据类型小的会被提升为数据类型大的
int与long之间:int会被提升为long
int a=10;
long b=20;
int c=a+b;
//编译出错:
//a+b -> int+long -> long+long -> long
//long赋值给int时会丢失数据
long d =a+b;
//编译成功:
//a+b -> int+long -> long+long -> long
//long赋值给long合适
总结:不同类型的数据混合运算, 范围小的会提升成范围大的
byte与byte的运算
byte a=10;
byte b=20;
byte c=a+b;
System.out.println(c);
运行:
byte 和 byte 明明是相同的类型, 为什么会出现编译报错呢?
由于计算机的 CPU 通常是按照 4 个字节为单位从内存中读写数据. 为了硬件上实现方便, 像 byte 和 short 这种低于 4
个字节的类型, 会先提升成 int, 再参与计算
所以虽然 a 和 b 都是 byte, 但是在计算 a+b 时会先将 a和b 都提升成 int, 再进行计算, 得到的结果必然也是 int, 这时将 int 赋给 c, 必然会有数据精度损失或溢出风险,所以就会出现上述报错.
所以我们要加上强制类型转换,告诉编译器程序员知道会有数据精度损失或溢出风险,仍要进行类型转换 。编译器会按照指令执行转换操作,将 int 类型强制转换为 byte 类型后,虽然发生了数据截断和值的改变,但因为有了强制类型转换操作,编译器不会报错 。(但是不建议强转)
我们也可以直接将c
的数据类型改为int
类型(不一定非得是int
,只要比int
大的都行)
总结:
对于 short, byte 这种比 4 个字节小的类型, 会先提升成 4 个字节的 int , 再运算
字符串类型
在C语言中是没有字符串类型的,在Java中是使用String
定义字符串类型,比如:
public static void main2(String[] args) {
String s1="hello";
String s2="world";
System.out.println(s1);
System.out.println(s2);
System.out.println(s1+s2);
//s1+s2表示:将s1和s2拼接
}
字符串相加就代表:拼接
注意:Java中的字符串没有所谓\0
结尾的说法!!
再写段代码:
可以发现System.out.println()
可以输出万物,可以放个字符串,也可以放整型…
我们也可以使用C语言的写法:
我们改变一下代码:
为什么会得出这种结果呢?
对于 System.out.println("a=="+a+c);
这行代码:
在 Java 中, + 运算符如果有一个操作数是字符串类型,那么就会进行字符串拼接操作。按照从左到右的顺序执行,首先 "a=="
是一个字符串, a
是一个变量值为 10 , "a==" + a
会将 a
的值转换为字符串并与 "a=="
拼接,得到字符串 "a==10"
。然后再与 c
(值为 20 )进行拼接, c
也会被转换为字符串,最终得到 "a==1020"
并输出。
对于 System.out.println("a=="+(a+c));
这行代码:
由于 a + c
被括号括起来了,根据运算符优先级,会先计算括号内的算术运算。 a
的值为 10 , c
的值为 20 , a + c
的结果是 30 。然后再将这个结果与字符串 "a=="
进行拼接,得到字符串 "a==30"
并输出。
再看个例子:
这个为什么会输出30=a+c
呢?
在 Java 中,
+
运算符有两种功能:算术加法和字符串拼接,具体使用哪种功能取决于操作数的类型。
对于 System.out.println(a+c+"=a+c");
输出 30=a+c 的原因,是按照 + 运算符的运算规则从左到右执行导致的,以下是详细解释:
代码中 a 的值是 10 , c 的值是 20 ,因为 a 和 c 都是 int 类型,根据 + 运算符的运算规则,会先进行算术加法运算, a + c 的结果是 30 。
计算出 a + c 的结果 30 后,接着要和字符串 "=a+c"
进行 + 运算,此时由于 "=a+c"
是字符串类型, + 运算符就变成了字符串拼接的功能,会把前面计算得到的整数 30 自动转换为字符串类型 30 ,然后和 “=a+c” 拼接在一起,最终得到字符串 30=a+c 并输出。
int转成String(字符串)
int num=10;
//方法1
String str1=num+"";
例子:
把整型123变成字符串123
这是因为+
运算符右边是个(空)字符串,所以 +
运算符就变成了字符串拼接功能,将左边的整型123
自动转换为字符串类型123
,最后与空字符串拼接,整体得出的结果就是个字符串,最后给变量str
,将它输出。
还有一个方法:
int num=10;
//方法2
String str2=String.valueOf(num);
这里需要用到一个方法叫做String.valueOf( )
,会将括号里的东西变成字符串
例子:
String转成int
字符串变成整数:
String str = "100";
int num = Integer.parseInt(str);
这里要用到int
的包装类Integer
(类里面包含方法)可以将str
变为整型
例子:
我们给a
加1验证a
是不是为整型了
结果为124
而不是1231
,所以此时的a
确实为整型