Java基础——基础语法冷知识
程序员文章站
2022-03-03 19:49:07
这篇文章不是面面俱到的基础知识集合,只是我个人的学习笔记。说人话就是:这篇文章是JavaSE基础知识全解的真子集,并不适合完全初学的小伙伴。且以下所有内容仅代表个人观点,不一定正确。欢迎辩证~版本说明发布日期1.0发布文章第一版2020-10-24文章目录概述在编程之前为什么需要配置环境变量?exe和路径的爱恨情仇环境变量path的作用自己定义环境变量并引用基础语法基本数据类型中的冷知识(基于64位系统)基本数据类型与内存的纠缠整数变量的赋值强转,不强转?1.0和1.1的爱...
这篇文章不是面面俱到的基础知识集合,只是我个人的学习笔记。说人话就是:这篇文章是JavaSE基础知识全解的真子集,并不适合完全初学的小伙伴。且以下所有内容仅代表个人观点,不一定正确。欢迎辩证~
版本 | 说明 | 发布日期 |
---|---|---|
1.0 | 发布文章第一版 | 2020-10-24 |
文章目录
概述
- 本篇文章只罗列了我所知道的,基本不涉及类知识的一些不太容易被人知晓的冷知识。不过其实这些知识大多对生产没有太大用处。但是说不定哪天这些东西会帮助自己避坑呢?
在编程之前
为什么需要配置环境变量?
exe和路径的爱恨情仇
- Windows执行可执行文件时,只能识别当前目录下的exe文件,而javac和java这两个可执行文件,都在jdk\bin中。我们当然不能把所有.java文件都扔到这个目录里面去执行,这时候就需要用到path环境变量。
环境变量path的作用
- path环境变量让Windows执行exe之前,先去path变量中从上到下(win7是从前到后)遍历,如果其中存在对应的exe,则可以直接执行。
- 正因为如此,我们才需要把jdk\bin目录加入到path环境变量中
自己定义环境变量并引用
- 而为了方便今后jdk路径变更以及Java EE的使用,我们又将jdk的路径单独设置一个环境变量JAVA_HOME,然后path中填写 J A V A H O M E JAVA_HOME JAVAHOME\bin就完事儿了。以后如果要变更路径,去改JAVA_HOME就行啦。
- PS:java11之后不需要配置classpath了。
基础语法
基本数据类型中的冷知识(基于64位系统)
基本数据类型与内存的纠缠
- 或许大家知道基本数据类型存储在栈区中,数组和引用数据类型存储在堆区中。但是大家可能容易忽略一个小细节。
int a = 1;
int b = 1;
- 上面这行代码,a和b使用的是同一块内存空间。什么意思呢?看下图:
- 像"1"这样的基本数据类型的值,我们称为直接量。int b = 1;时,会先查找栈中是否存在直接量"1",如果找到了,那么直接使用这个直接量。
- 所以真正存储在栈区中的其实是直接量,相同的直接量,不会重复占用多个内存空间,这就是上面代码a和b使用同一地址的原因。而不同的值会被分配到不同的地址去,所以如果a=1、b=2,那么他们的地址又是不同的。
- 不同类型的相同值,也是会被分配到不同地址,很好理解嘛,因为他们需要的内存大小都不一样嘛。比如1.0和1.0F,他们的在栈中的地址是不一样的。
- 尽管这个特性看起来很像是“引用”,但是我们需要避免这样去称呼他们。因为“引用”指的是利用栈区中的内存地址,指向堆区中的数据。
整数变量的赋值
- 我们都知道
byte a = 1000; short b = 1000000
之类的赋值会报错:不兼容的数据类型,从int到byte(short),因为byte和short太小了。 - 但你们知道
int a = 9999999998
会报什么错么?不兼容的数据类型?并不是,报错的内容是:整数太大。 - 这是为什么呢?因为在不加任何符号的情况下,1000、1000000、9999999998这些数字都是分配了4个字节的内存进行存储,并且类型为int。而某清华学子说过,我们小学二年级就学过int最大的表示范围大概是25亿,显然9999999998大于了这个数字。
- 所以我们才需要使用int a = 9999999998L来解决这个问题。
- 说到这儿,就像顺道说一下,浮点数(例如1.0)的默认类型是double,不是float哟~所以
float b = 1.0F
才能正确给float赋值哟~~~~
强转,不强转?
- 看了上面两小节之后,细的朋,哦不,细心的朋友们就要问了:
byte a = 1;
为什么不报错?float b = 1.0;
为什么报错? - 这个问题我也只知道一个很浅显的答案:对于byte范围内的值,JVM会自动将int(例如1)处理为byte,所以前者不报错。而JVM表示并不想把doule处理成float,即使这个值在float的表示范围内。
- 底层原因的话,猜测一下?可能是因为float和double是科学技术法表示的,转起会恶心到JVM?望有大佬能够明确一下答案。
1.0和1.1的爱恨情仇
- 我画你猜,a和b那个是true?那个是false?:
boolean a = 1.0F == 1.0;
boolean b = 1.1F == 1.1;
- 答案是,a真b假。
- 哦哟,搞啥子哦?为啥子喃?简单说一说:因为float和double都是近似值,并且精度不同,所以同样是1.1,他们真实的值都有误差,并且误差程度不同,自然值就不同啦~而1.0之所以相同,纯粹是巧合,因为刚好float和double都能精确表示1.0。
- 也就是说,99.9%的情况,float和double都是没法相等的。不信的话,给你们个网址,自己试一试浮点数的误差会有多大:https://www.h-schmidt.net/FloatConverter/IEEE754.html
==?
- 某清华学子说,我们小学2年级都学过:==比较的是地址,equals通常比较的是具体的内容(具体得看重写的方法是怎么写的)。
- 这句话莫得毛病,但是点小有问题:基本数据类型的==比较,比的是数值;而引用类型比的是地址。不然为什么1.0 == 1.0F是true呢?是吧,哈哈哈。
boolean类型有多大?
- 这个问题标准答案是:没有答案。对,就是这样,因为JAVA官方没有指明一个boolean占多大内存。但是大众普遍认为大小是1个字节,也不能算错吧。但更合理的猜测,不应该是一个bit么?哈哈。
自动类型转换的一个小知识
- 我们都知道,自动类型转换遵从小转大的原则。但是你知道么,这个小转大,并不是完全指内存的大小。比如最特殊的一个:long类型可以自动转换为float类型。
- 又有小老弟懵逼了,其实原因很简单。long类型表示的范围是
$-2^{63}$
至$2^{63}-1$
。而float的表示范围大概是$\pm10^{38} \approx \pm2^{114}$
。明显float能表示的数字更大,所以能够自动转换。当然,自动转换之后,也不可避免地会产生严重的误差。
强制类型转换如何取舍地?
-
byte a = 128;
的结果是多少呢?这就要涉及到强转的数值取舍了。小学二年级的时候都学过,128转换为二进制为:0000 0000 1000 0000。 - 因为byte类型就1个字节,所以强转之后,肯定需要舍弃一半。java在进行强制转换时,舍弃的是高位部分,所以a的值最终为:1000 0000。也就是-128。
变量初始值
- 以前一直以为基本数据类型始终会有一个初始值,后来试了试,结果试试就逝世。
- 直接上结论吧
- 类成员变量(包括静态的),无论是基础数据类型还是引用数据类型,在不初始化的情况下都有初始值。基本数据类型除了char,其他的初始值都是0(或者0.0)。char类型初始值是’’。而引用数据类型初始值统一为null。
- 而方法中的局部变量,不论是基本数据类型,还是引用类型,只要不初始化,直接编译就报错。
- 也就是说,下面这段代码,打印结果是0。如果把te.wa换成age,则直接编译报错。
public class VarTest {
int wa;
public static void main(String[] args) {
int age;
VarTest te = new VarTest();
System.out.println(te.wa);
}
}
运算符中的冷知识
除以一个0.0试试
- 小学二年级就学过,java中除以0会报arithmeticExcetption。
- 那大家知不知道java能否除以0.0呢?答案是可以说可以,也可以说不可以~
- 例如:
5 / 0.0
的结果是infinity;0 / 0.0
的结果是NaN。显然都不是一个正常的数字,所以我们肯定也不应该去除以0.0的哈。
赋值运算本身也会返回一个值
- 什么意思呢?其实很(没)简(有)单(用)。举个栗子:
int a, b;
a = b = 3;
- 这段代码执行完之后,a和b都被赋值为了3。因为
b = 3
这个表达式的结果是3。
运算结果的类型
- 小伙伴们觉得下面这段代码执行完后,a等于多少?
byte a = 10;
a = a + 2;
- 结果等于:报错~哈哈哈,想不到吧。其实这是JAVA编译器自动进行了优化处理。因为byte的运算(算术运算、移位运算等)的计算结果被编译器自动转换为了int。而int赋值给byte很显然是会报错的。
- 所以当我们真的想写这样一段逻辑的话,需要使用如下写法:
byte a = 10;
a = (byte)(a + 2);
- 或者!还可以如下:
byte a = 10;
a += 2;
- 这就很骚了,原来
a += 2;
等价的不是a = a + 2;
,而是a = (byte)(a + 2);
。 - 同理,不光是+、也不光是short会有这种优化。可以自行尝试一下其他地方是否也有如此特性。
如何高效(装逼)地运算i*4?
- 大家都知道java中的位运算效率是最高的。因为计算机底层就是二进制,如果直接对二进制进行操作,java不需要再费精力去将变量与二进制进行转换。
- 所以要高效地运算,最好的方法就是采用位运算。而移位运算符刚好就具备倍乘、倍除的特性。
- byte类型的9,二进制为0000 1001。左移一位,变为0001 0010,值为18。不用怀疑,就是二倍。同理,右移一位0000 0100,值为4。也不用怀疑,就是整数/2的结果。
- 所以的所以,如何高效运算i*4?答案是
i = i << 2;
。 - 当然,我劝大家可别在实际开发中这样写,坏处很明显:容易被打…好吧,其实是可读性太低。并且如果除符号位的最高位是1,左移之后的结果就很迷了。综上,无用知识+1~
流程控制中的冷知识
for( ; ; ){}
- for( ; ; ){}语句可以正常运行,结果是无限循环。
switch的default的穿透与短途效应(自己取的名字哈哈)
- 这个不算冷门吧,但switch确实用得太少了,导致这个问题之前并不知道。
- 众所周知,switch售价2000元…说岔了,众所周知,switch有一个default,用于匹配默认情况。但其实这句描述稍微有点问题,因为default匹配的其实不是默认情况,而是所有情况。什么意思呢?拿下面的例子说明:
int a = 1;
switch(a){
default:
System.out.print("default");
case 1:
System.out.print("1");
}
- 上面这行代码执行结果是打印"default1",也就是说如果default在前面,后面就算有真正匹配的case,也会匹配进入default。这一点我就姑且称之为短途效应吧~(名字取得好,逼格少不了)
- 至于穿透效应,其实就是带不带break;的问题。这里我就只是想说明一下,default语句块如果放到前面,也是需要考虑带不带break的。
- 不过,说了这么多,有谁会在default后面写case呢?(无用知识+1)
本文地址:https://blog.csdn.net/w764476876/article/details/109259887