文章目录
变量与常量
变量指程序运行时可变的量,相当开辟了一块内存空间来保存一些数据,类型则是对变量的种类进行划分,使不同的类型变量具有不同的特性
而变量与我们内存的硬件设施密切相关
- 输入设备,就是用相关的硬件把信息输入到电脑中,比如有键盘,鼠标,麦克风等,标准输入设备是键盘
- 输出设备,比如音响,打印机等,标准的输出设备是屏幕
- 内存,也称运行内存,比如我们买手机时的8+256G,其中8G就是内存,而程序中处理的信息都是要放在内存中的,它的容量较小,但是运行速度是很快的,而且它如果断电,那么数据就会消失,如果想做到关机之后数据不消失,就得看外存了
- 外存,比如硬盘,U盘这些都是外部存储器,手机的256G就是外存的容量,外存的大小比较大,但是运行速度比较慢,断电后数据不会消失,操作系统的文件都是在硬盘中存储
- CPU,是电脑的中央处理器,类似人的大脑,它是来控制计算机的,比如运行什么程序,和该如何运行,它是跟内存来打交道的,是从内存中去读取和输出数据的,不能直接从硬盘读取数据
JAVA是强类型语言
- 所谓强类型,就是定义一个变量时,必须要规定好他的类型,比如int a =10;a就是一个变量,他的类型是int,他的值是10,我们可以把a的值改为20,但是他的类型是不能变的,只能是int;
变量的命名规则
硬性指标 :
- 一个变量名只能包含数字, 字母, 下划线
- Java中的字母和数字的范围更大,字母包括AZ,az,下划线,$,或者在某种语言中表示字母的任何Unicode字符
- 数字不能开头.
- 变量名是大小写敏感的. 即 num 和 Num 是两个不同的变量.
- JAVA中不能使用关键字命名
注意: 虽然语法上也允许使用中文/美元符($)命名变量, 但是 强烈 不推荐这样做.
软性指标 :
- 变量命名要具有描述性, 见名知意.
- 变量名不宜使用拼音(但是不绝对).
- 变量名的词性推荐使用名词.
- 变量命名推荐 小驼峰命名法, 当一个变量名由多个单词构成的时候, 除了第一个单词之外, 其他单词首字母都大写.
常量
final 关键字修饰的常量
final int a = 10;
a = 20; // 编译出错. 提示 无法为最终变量a分配值 常量不能在程序运行过程中发生修改
- 虽然我们的Java用final来表明是一个常量,但是Java中的const是一个保留字
- 我们常量的命令方式是字母全大写,单词之间用_进行划分
字面常量
字面常量 ——程序中直接写出来的值
几种常数赋值方式
- 十六进制 在前面加上0x
- 八进制 在前面加上0 但是很容易弄混 比如010
- 从Java7开始,还支持了二进制 以0b开头
- 而且从Java7开始我们可以在数字中添加_,容易区分我们阅读,Java编译器会自动去除这些下划线
public class Candy3 {
public static void main(String[] args) {
int a=0x123;
int b=0b1001;
int c=010;
System.out.println(a);//291
System.out.println(b);//9
System.out.println(c);//8
}
}
基本类型
- Java是一种强类型的语言
- 而且我们的Java有一种能够表示任意精度的算数包,通常称为大数,但是他是对象
整型
基本整型变量 int
基本语法格式
int a=10;//初始化时并赋值
int a;
内存大小
int 大小为4个字节(byte),与操作系统的和JVM的版本无关,像C语言可能就会受到编译器的影响,是为了实现跨平台
整型的范围
- 也就是-231至231-1
- 因为4字节表示有32位,一位用来表示正负
数据溢出问题
为什么会溢出
因为2^32-1的数据的大小不够用,还不够表述出马云的个人资产,所以引出了long类型
长整型 long
long a=10L;
long a=10l;
long a=1000_000_000;//可以为数字字面量加下划线,这些下划线只是让人更易读,Java编译器会自动去除这些下划线
为什么要加L呢?
整型的字面量,默认是整型(int),浮点数的字面量,默认是double,所以要加一个L(l)表示它是long类型,否则会发生隐式转换
内存的大小
long类型的大小是8字节
long的数据范围
-263至263-1
短整型 short
short a=20;
内存大小
2个字节 数值范围是 -215至-215-1
基本没啥用
比特型 byte
byte a=1;
内存大小
一个字节 ,数值访问 -128到127
主要用于文件和网络的传输
浮点数 float和double
float f=1.2f;
float f=1.2F;
double d=1.2;
在JAVA中直接写出来的字母常量的浮点数都是double类型的,所以在flaot需要特别的声明一下
内存大小
float的大小为4个字节,double的字节是8个字节 在内存中采用指数的形式来模拟
浮点数存储规则
根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式:
(-1)^S * M * 2^E
-
(-1)^S S表示符号位,当s=0,V为正数;当s=1,V为负数。
-
M表示有效数字,大于等于1,小于2。
-
2^E E表示指数位。
举例来说:
十进制的5.0,写成二进制是 101.0 ,相当于 1.01×2^2 。
那么,按照上面V的格式,可以得出s=0,M=1.01,E=2。
十进制的-5.0,写成二进制是 -101.0 ,相当于 -1.01×2^2 。那么,s=1,M=1.01,E=2。
IEEE 754规定:
- 对于32位的浮点数,最高的1位是符号位s,接着的8位是指数E,剩下的23位为有效数字M。
- 对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。
IEEE 754对有效数字M和指数E,还有一些特别规定。
-
前面说过, 1≤M<2 ,也就是说,M必须写成 1.xxxxxx 的形式,其中xxxxxx表示小数部分。
-
IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的 xxxxxx部分。比如保存1.01的时 候,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。以32位 浮点数为例,留给M只有23位, 将第一位的1舍去以后,等于可以保存24位有效数字。
至于指数E,情况就比较复杂。
-
首先,E为一个无符号整数(unsigned int)
-
这意味着,如果E为8位,它的取值范围为0255;如果E为11位,它的取值范围为02047。但是,我们知道,科学计数法中的E是可以出现负数的,所以IEEE 754规定,存入内存时E的真实值必须再加上一个中间数,对于8位的E,这个中间数 是127;对于11位的E,这个中间 数是1023。比如,2^10的E是10,所以保存成32位浮点数时,必须保存成10+127=137,即 10001001。
然后,指数E从内存中取出还可以再分成三种情况:
E不全为0或不全为1
这时,浮点数就采用下面的规则表示,即指数E的计算值减去127(或1023),得到真实值,再将 有效数字M前加上第一位的1。
比如:0.5(1/2)的二进制形式为0.1,由于规定正数部分必须为1,即将小数点右移1位,则为1.0*2^(-1),其阶码为-1+127=126,表示为 01111110,而尾数1.0去掉整数部分为0,补齐0到23位00000000000000000000000,则其二进 制表示形式为:
E全为0
这时,浮点数的指数E等于1-127(或者1-1023)即为真实值, 有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字。
E全为1
这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s);
关于3*0.1==0.3
-
答案是不相同的
-
在上面,我们知道我们的浮点数在计算机以指数形式存储,其原理还是得用我们的二进制来表示
- 比如0.75 可以用 2-1+2-2来表示 0.11 1.1*2^-1 在计算机中变成 S=0 E=-1+127 M=1
- 但是这个0.1我们是不能用二进制位精确表达出来的,所以肯定会存在误差
如何解决
- 仍使用double类型,但是只看小数前6位,后面的忽略不计
- 使用BigDecimal来代替double使用
如果的基本整数和浮点数的精度不能满足需求,可以使用java.math包下两个很有用的类
BigInteger 实现任意精度的整数运算
- BigInteger a=new BigInteger(“123”); //第一种,参数是字符串
- BigInteger a=BigInteger.valueOf(123); //第二种,参数可以是int、long
BigDecimal 实现任意精度的浮点数运算
BigDecimal 常用的构造方法如下。
BigDecimal(double val):实例化时将双精度型转换为 BigDecimal 类型。
BigDecimal(String val):实例化时将字符串形式转换为 BigDecimal 类型。
BigDecimal add(BigDecimal augend) // 加法操作 BigDecimal subtract(BigDecimal subtrahend) // 减法操作 BigDecimal multiply(BigDecimal multiplieand) // 乘法操作 BigDecimal divide(BigDecimal divisor,int scale,int roundingMode ) // 除法操作
三种特殊的double
- 正无穷大 Double.POSITIVE_INFINITY 一个正整数/0就是正无穷大
- 负无穷大 Double.NEGATIVE_INFIITY 4e
- Double.NaN 表示不是一个数字 计算0/0或者负数的平方根
字符型 char
char a='a';
char b='A';
char c='哈';
内存大小
-
占两个字节,在C中是一个字节,而且它可以存储中文
-
这是C做不到的C言中使用 ASCII 表示字符, 而 Java 中使用 Unicode 表示字符. 因此一 个字符占用两个字节, 表示的字符种类更多, 包括中文
-
在Java中,char类型表示的是UTF-16的一个代码单元,而不是Uncoide的字符 字符集和char的关系
布尔类型 boolean
boolean b=true;
boolean b=false;
注意点
-
boolean只有两个值,真就是true,假就是false
-
Java 的 boolean 类型和 int 不能相互转换 , 不存在 1 表示 true, 0 表示 false 这样的用法
-
如何将boolean变成0或者1
-
boolean类型在我们的官方文档写的是 It’s virtual machine dependent. // 由虚拟机自己实现
- 我们都知道,Java语言中有个boolean类型。每个boolean类型的变量中存储的是一个true或者是false的逻辑值。那么存储这个逻辑值,需要多大的空间呢?从理论上来讲,存储这个逻辑值只需要1个位(bit)就可以了,所以很多教科书上谈到这个问题的时候,也说boolean类型的数据在内存中只占1个位。
- 但是稍微有点计算机常识的人都知道:计算机完成寻址操作的时候,是以字节为最小单位进行的。也就是说每次要读取内存中数据的时候,最小只能精确到1个字节,不能单独读取某个位上的信息。如果boolean类型的变量的值只占1个位,计算机每次读取到1个字节的信息,里面会包含8个boolean变量的值。计算机就不得不通过某种算法去确定这8个值中,哪一个才是我们要找的值。这样做显然非常不合理,因为要完成这个“8选1”的操作又会增加运算工作量。那么Java虚拟机到底是怎样存储boolean值呢?
- 在《虚拟机规范》中,对boolean类型的存储有专门的解释,文中说到:“虽然定义了boolean这种数据类型,但是只对它提供了非常有限的支持。在Java虚拟机中没有任何供boolean值专用的字节码指令,Java语言表达式所操作的boolean值,在编译之后都使用Java虚拟机中的int数据类型来代替,而boolean数组将会被编码成Java虚拟机的byte数组,(因此)每个boolean元素占8位”。
- 变成int类型,是因为我们很多的CPU都是32位,这样的存取效率是最高的
类型转换
- 图中的实线箭头,表示无信息丢失的转换
- 虚线箭头,表示可能存在精度的损失
隐式类型提升
对于这种转换但是无信息丢失的情况我们的隐式类型转换是会自动发生的
- 比如小类型的常量赋值给大类型的变量
- 小类型和大类型发生数学运算时,会先将小类型变成大类型,然后进行运算
int a=10;
long b=20;//发生了隐式类型提升
long c=a+b;//发生了隐式类型提升
强制类型转换
对于转换可能存储精度损失的情况我们必须采用强制类型转换
小类型 变量=(小类型) 大类型数值
int a = 0;
double b = 10.5;
a = (int)b;
int a = 10;
byte a=20;
byte b=256;//发生编译错误,因为byte的值位-128到127 所以需要强制类型转换
boolean b = false;
b = (boolean)a; // 编译出错, 提示不兼容的类型
- 不要用boolean类型和任何数值类型之间进行强制转换
关于一个byte和byte的现象
byte a=20;
byte b=30;
byte c=a+b;//会发生编译报错
- byte 和 byte 都是相同类型, 但是出现编译报错. 原因是, 虽然 a 和 b 都是 byte, 但是计算 a + b 会先将 a 和 b 都
提升成 int, 再进行计算, 得到的结果也是 int, 这是赋给 c, 就会出现上述错误.由于计算机的 CPU 通常是按照 4 个字节为单位从内存中读写数据. 为了硬件上实现方便, 诸如 byte 和 short 这种低于4 个字节的类型, 会先提升成 int, 再参与计算
可以写成
final byte a=20;
final byte b=20;
byte c=a+b;
- final可以保证我们的类型也不会发生变化
运算符
算数运算符
基本四则运算 + - * \ %
- 相同类型进行数学运算,得到的值仍然是本类型
- 小类型和大类型进行数学运算,先把小类型转换成大类型,再进行运算
- 对于我们的整型如果被0除那么会抛出一个异常,而浮点型被0除将会得到无穷大或者是NaN结果
- %运算为取余运算符,不仅可以用于整数,也可以用于小数(C语言中只能运用于整数)
增量赋值运算符 += - = *= /= %=
可以在赋值中使用二元运算符,是一种很方便的简写形式
int x=1;
x+=2;
x=x+2;
- 如果使用增量赋值运算符,得到的数与左侧操作数的类型不同,就会自动发生强制类型转换
int x=1;
x+=3.5;//相当于(int)(x+3.5)
自增/自减运算符 ++ –
int a = 10;
int b = ++a;
System.out.println(b);
int c = a++;
System.out.println(c);
结论:
- 如果不取自增运算的表达式的返回值, 则前置自增和后置自增没有区别.
- 如果取表达式的返回值, 则前置自增的返回值是自增之后的值, 后置自增的返回值是自增之前的值
关系运算符
关系运算符主要有六个:
== != < > <= >=
int a = 10;
int b = 20;
System.out.println(a == b);
System.out.println(a != b);
System.out.println(a < b);
System.out.println(a > b);
System.out.println(a <= b);
System.out.println(a >= b);
注意: 关系运算符的表达式返回值都是 boolean 类型
逻辑运算符
逻辑运算符主要有三个:
&& || !
注意: 逻辑运算符的操作数(操作数往往是关系运算符的结果)和返回值都是 boolean,如果操作数不是boolean,那么编译不通过
逻辑与 &&
规则: 两个操作数都为 true, 结果为 true, 否则结果为 false.
int a = 10;
int b = 20;
int c = 30;
System.out.println(a < b && b < c);
逻辑或 ||
规则: 两个操作数都为 false, 结果为 false, 否则结果为 true
int a = 10;
int b = 20;
int c = 30;
System.out.println(a < b || b < c)
逻辑非 !
规则: 操作数为 true, 结果为 false; 操作数为 false, 结果为 true(这是个单目运算符, 只有一个操作数).
int a = 10;
int b = 20;
System.out.println(!a < b);
&&与||都是短路运算符
&& 和 || 遵守短路求值的规则.
System.out.println(10 > 20 && 10 / 0 == 0); // 打印 false
System.out.println(10 < 20 || 10 / 0 == 0); // 打印 true
我们都知道, 计算 10 / 0 会导致程序抛出异常. 但是上面的代码却能正常运行, 说明 10 / 0 并没有真正被求值.
结论:
- 对于 && , 如果左侧表达式值为 false, 则表达式的整体的值一定是 false, 无需计算右侧表达式.
- 对于 ||, 如果左侧表达式值为 true, 则表达式的整体的值一定是 true, 无需计算右侧表达式.
& 和 |
& 和 | 如果操作数为 boolean 的时候, 也表示逻辑运算. 但是和 && 以及 || 相比, 它们不支持短路求值.
System.out.println(10 > 20 & 10 / 0 == 0); // 程序抛出异常
System.out.println(10 < 20 | 10 / 0 == 0); // 程序抛出异常
Java支持的三位运算符
condition ? expression1 : expression2
int max=a>b?a:b; //返回a 和 b中较大的那个值
位运算符
java 中对数据的操作的最小单位不是字节, 而是二进制位.
位运算符主要有四个:
& | ~ ^
位操作表示 按二进制位运算. 计算机中都是使用二进制来表示数据的(01构成的序列), 按位运算就是在按照二进制位的每一位依次进行计算
- & 按位与,只有两位都是1,结果是1,否则是0
- | 按位或,只有一位是1,结果为1,否则是0
- ~按位取反,如果是1返回0,如果是0返回1
- ^异或,如果两位相同则为0,两位不同为1
- 异或的重要用法 a^a=0 a^0=a
移位运算
移位运算符有三个:
<< >> >>>
都是按照二进制位来运算.
- <<算数左移 二进制位向左移动一位,最左侧不要(包括符号位),右边补0
- >>算数右移,二进制向右移动一位,最右边不要,(符合位正数补0,负数补1)
- >>>(C语言没有的),无符号右移,最右边不要了,最左边直接补0
- 左移 1 位 , 相当于原数字 * 2. 左移 N 位 , 相当于原数字 * 2 的 N 次方 .
- 右移 1 位 , 相当于原数字 / 2. 右移 N 位 , 相当于原数字 / 2 的 N 次方 .
- 由于计算机计算移位效率高于计算乘除, 当某个代码正好乘除 2 的N次方的时候可以用移位运算代替.
- 移动负数位或者移位位数过大都没有意义
int a = 0x10;
System.out.printf("%x\n", a << 1);
// 运行结果(注意, 是按十六进制打印的)
20
int a = 0x10;
System.out.printf("%x\n", a >> 1);
// 运行结果(注意, 是按十六进制打印的)
8
int b = 0xffff0000;
System.out.printf("%x\n", b >> 1);
// 运行结果(注意, 是按十六进制打印的)
ffff8000
int a = 0xffffffff;
System.out.printf("%x\n", a >>> 1);
// 运行结果(注意, 是按十六进制打印的)
7fffffff
关于移位大小的规定
- 移动运算符的右操作数要完成模32的运算(除非左操作数是long类型,在这种情况下需要对右操作数模64),例如1<<35的值等同于1<<3
运算符的优先级
先看一段代码
System.out.println(1 + 2 * 3);
结果为 7, 说明先计算了 2*3 , 再计算 1+
另外一个例子
System.out.println(10 < 20 && 20 < 30);
此时明显是先计算的 10 < 20 和 20 < 30, 再计算 &&. 否则 20 && 20 这样的操作是语法上有误的(&& 的操作数只能是boolean).
运算符之间是有优先级的. 具体的规则我们不必记忆. 在可能存在歧义的代码中加上括号即可.