Java基础语法冷知识

程序员考拉

共 3868字,需浏览 8分钟

 ·

2020-11-01 01:59


概述


  • 本篇文章只罗列了我所知道的,基本不涉及类知识的一些不太容易被人知晓的冷知识。

    不过其实这些知识大多对生产没有太大用处。

    但是说不定哪天这些东西会帮助自己避坑呢?


在编程之前为什么需要配置环境变量?


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中填写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,左移之后的结果就很迷了。



浏览 34
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报