《Java深入解析-渗透java本质的36个话题》总结

浮点类型的种种悬疑

浮点类型只是近似的存储,这很显而易见,因为在计算机内部是使用二进制来存储数值的,而对于0.3333之类的奇数小数就没办法存储,因此采用的是一种近似值的方式进行存储的,所以就会造成一个误差:下面代码将输出false,因为对于浮点数运算会存在误差:

double x = 0.1, y = 0.2, z = 0.3;
System.out.println(x+y==z);
//真正的0.1+0.2等于0.3000000004左右,后面会携带偏差值
System.out.println(x+y);
++i和i++解析(目前我看到的最好理解的讲解方式)

首先,我们知道i++和i++的一个明显的区别就是:前者先i先进行其他运算操作再自增,后者i先自增再进行其他运算操作。那么如果对于下面这种情况呢?

int i = 1;
int y = 2;
i = i++;
y = ++y;
System.out.println(i);
System.out.println(j);

首先说一下他们的结果分别为:i=1,j=3; 我们可以将它改变成一个容易看懂的操作:

int i = l;
int y = i++;

那么这个我们就能拆分成下面这个:

int i = 1;
int temp = i;
i++;
int y = temp;

最终y的值就是i,那么i也同样是这样:

int i = 1;
int temp = i;
i++;
i = temp;

因此i最终还是为i,我们再来看y:

int y = 2;
y += 1;
y = y;

因此最终y自增了,可以看出,i++和++j的区别就在于前者创建了一个临时的变量用于赋值操作,而其实两者的自增操作都是发生再进行运算操作之前。

i+++j的运算结果(贪心规则)

按照编译器的贪心规则来说,i最终匹配到自增运算,最终的结果为:

i++;
i+j;

贪心机制:编译器再运算分析符号的时候,会尽可能多的结合有效的符号。我们知道,在进行转义字符的时候,比如\431它为什么不匹配\43,就是因为编译器的贪心规则,不然就会永远都无法正确的转义\431

三元运算符的结果类型

三元运算符第一个为boolean类型,但是后面两个表达式的类型可能不同,那么当他们类型不同的时候,该运算符到底返回谁的类型呢?他们的具体判断顺序如下:

1.如果类型相同,则返回表达式1的类型
2.如果一个为基本类型,另一个为对应的包装类型,则返回基本类型
3.如果一个为null,一个为引用类型,则返回引用类型
4.如果一个为null,一个为基本类型,则返回基本类型对应的包装类型
+号连接字符串的性能

在进行+来连接字符串的时候,它会先创建一个临时的StringBuilder对象,进行String对象连接之后,在调用toString方法转化成String对象。

当使用+号连接的两边的字符串为编译时期就能确定的常量时,编译器不会创建临时StringBuilder对象,而会在编译期就计算出该字符串的值,不会造成性能开销

String类型常量都是保存在常量池里面的,我们也可以调用字符串的intern方法手动的将字符串保存到常量池中。这样,当String编译时期常量的值出现在常量池中 的时候,会直接返回常量池中的对象。intern方法返回值:如果常量池中有该对象,则返回常量池中 的该对象,否则返回自身

对于运行时创建的对象,会被分配到堆中,系统不会自动调用intern方法加入到常量池

特殊的main方法

我们通常都是通过run类来调用main方法,其实main方法也可以在其他方法中被调用:

public class Main_{
 public static int i = 0;
 public static void main(String[] args){
    if(i==0){
        main2();
        System.out.println(0);
    }
    System.out.println(1);
 }
 public static void main2(){
  i = 1;
  main();
 }
}

同样,main方法也是可以继承的,如果我们在Main2类中继承Main类,那么也可以直接启动Main2;

我们知道,静态方法是不能被重写的,那么是不是每次启动Main2类都只能调用Main的main方法呢?答案肯定不是的,静态方法虽然不能被重写,但是可以被隐藏,也就是说,我们在Main2类中在定义main方法,那么启动Main2将是调用自身定义的main方法,而父类的main方法将被隐藏

重载时的方法选择

方法重载时,类调用的方法在编译时期就已经确定了,如下代码:

public class Main{
 public static void a(String s){
  System.out.println("a1");
 }
 public static void a(Object o){
  System.out.println("a2");
 }
 public static void main(String[] args){
  Object o = new String();
  a(o);
 }
}

最终调用的方法为a(Object o);调用重载方法时,重载方法的参数类型是根据引用的静态类型来决定的,o的静态引用类型为Object,那么就调用a(Object o);

构造器

构造器其实并没有创建对象,它只是负责对象的一系列属性的初始化操作

当我们没有定义构造器时,编译器会自动生成一个空的默认无参构造器,这个构造器并不是一个空的方法,至少它调用了父类构造器进行父类初始化

this在哪里

我们知道,当我们调用一个实例方法的时候,我们可以直接使用一个this对象,那么该this对象是哪来的呢?其实,在构造器或者实例方法中,都含有一个隐藏的参数,该参数就是类的对象,当调用构造器或者实例方法时,就会将这个对象作为该参数传进去,比如:

obj.test();
//实质上里面包含一个隐含参数this,该this指向obj
obj.test(this);
Integer包装类型的缓存

Integer内部维护者一个缓存数组,默认的范围是-128到127,当数值在这个范围里面时,会从缓存里面取对象,否则就会新建一个对象。其中-128缓存下限是固定不能变得,而127上限可以通过修改系统属性来改变:

java -Djava.lang.Integer.IntegerCache.high=200 Main

使用上述语句来启动Main就会将Integer得内部缓存上限更改至200

java的多维数组

java的多维数组其实是采用数组的数组的形式实现的,也就是说高维数组的每一个元素都是一个低维数组

你可能感兴趣的:(java-web开发,java进阶,后端面试题,java面试宝典总结)