透析java本质的36个话题-第一章基本概念笔记

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/uotail/article/details/83449055

记得大三时在图书馆看过这本书,当时一口气就看完了,参加工作后又回过头来再看,还是收益很多,我是先看的这本书,然后再看了深入理解jvm虚拟机这本经典之作,必须反复看。现在又回过头来看 透析java本质的36个话题 这本书,全书一共5章,我谨以5篇博文纪念。

1、开门见山—测试你的java水平

 当时赶脚自己连java新手都不是,大哭o(╥﹏╥)o

上面的问题都会在我的这5篇博文中找到答案 

2、世外隐者—隐居深山的关键字 

 

2.1 、 goto与const

Java中取消了goto的使用,取而代之的是使用循环标签 outer

需要注意的是continue 和break 只针对第一次循环有效 ,对于多次循环只能用循环标签

下面是使用outer的例子  

public class TestOuter {
    public static void main(String[] args) {
        for (int i = 0; i < 2; i++) {
            for (int j = 0; j < 2; j++) {
                System.out.println("i=" + i + ", j=" + j);
            }
        }
    }
}

 这是一个双层循环

我们使用outer跳出第二层循环

public class TestOuter {
    public static void main(String[] args) {
        //在外层循环处添加outer标签 outer标签使用格式为      字母: break 字母
        //所一冒号前面只要是合法标识符就好了
        outer:for (int i = 0; i < 2; i++) {
                    for (int j = 0; j < 2; j++) {
                        if (i==0&&j==1){
                            break outer;//如果只是break 它只跳出内层循环
                        }
                        System.out.println("i=" + i + ", j=" + j);
                    }
                }
            }
}

 

 

 2.2、true、false与null

 

运行报错

 2.3、关键字列表

 

 

3、疑团满腹—标识符更深层次的的思考

3.1、标识符定义规则

 

这个好多面试题的答案就是这个,嗯嗯嗯呃呃

 

 合法的标识符的个数

public static void main(String[] args) {
    int    start=0;
    int    part=0;
    for(int i=0x0000;i<0x10ffff;i++) {
        if(Character.isJavaIdentifierStart(i)) {
            //判断int数据对应的字符是否可以作为java标识符的首字母
            start++;
        }
        if(Character.isJavaIdentifierPart(i)) {
            //判断int数据对应的字符是否可以作为java标识符的一部分
            part++;
        }
    }
    System.out.println("Unicode字符集个数"+(0x10ffff+1));//1114112
    System.out.println("可作为标识符首字母个数"+start);//101296
    System.out.println("可作为标识符一部分的个数"+part);//103584
    System.out.println("二者只差"+(part-start));//2288
}

 

 3.2、“$” 惹的祸

public class Test${
    public static void main(String[] args) {
        System.out.println("我的类名有 $ ");
    }
}
这个可以正常运行

 

再看一个 

public class Test$User{
    public static void main(String[] args) {
        System.out.println("我的类名为 Test$User ");
    }
}
class Test{
    class User{
        void print(){
            System.out.println("我是Test中内部类User类中的print方法 !");
        }
    }
}

这下报错了

3.3、标识符的最大长度 

这里class中常量字符串的存储在 深入理解jvm虚拟机 这本书里有介绍 

 

4、鞭长莫及---我的字符串你不能用 

 

4.1、转义字符介绍

public class Test{
    public static void main(String[] args) {

        char c1    ='\u0027'; //等价于  char c11 ="'"; 单引号
        char c2    ='\u005c'; //等价于 char c22 ="/";  反斜杠
        String s ="\u0022";    //等价于 String ss ="""; 双引号
        //上述三行Unicode转义不正确,经过转义以后,单引号和双引号都没有合理的匹配。
        //而\是转义字符需要与其他字符结合使用
      //\u代表Unicode转义
        char c3    ='\400';
        char c4    ='\28';
        //这两行时八进制转义,自然只能出现0-7;
        //而且他的合理范围是0-255
      //\400的十进制是256.超过了这个范围
    }
}

 

 

对于Unicode字符集及其编码的了解可以看看这个

http://www.cnblogs.com/wangduo/p/6225538.html 

https://www.zhihu.com/question/23374078

https://www.cnblogs.com/pureEve/p/6542809.html 

4.2、三种转义的联系

public class Test{
    public static void main(String[] args) {
        //字符A的三种表现形式
        char c1    ='A';
        char c2    ='\u0041';
        char c3    ='\101';
        //字符串双引号 “ 的三种现形式
        char e1    ='\"';
        char e2    ='\u0022';
        char e3    ='\42';

        System.out.println(c1==c2&&c2==c3);
        System.out.println(e1==e2&&e2==e3);
    }
}

 

可见,就本例而言,三种方式是等价的

 4.3、三种转义的区别

 先看这个例子

public class Test{

    public  static char c1 = '\u00a';
    public  static char c2 = '\u00d';

    public static void main(String[] args) {
        System.out.println(c1);
        System.out.println(c2);
    }
}

 

编译报错了

注释掉字段并删掉main函数

public class Test{

    //public  static char c1 = '\u00a';
    //public  static char c2 = '\u00d';

}

继续报错

书上还有个小问题题
其实第一个代码应该如下
public class Test{

//    char c1 = '\u00a';
//    char c2 = '\u00d';
//上面两行代码是书上的,如果换成下面两行仍不能通过编译,
//会报这个错误  Error:(8, 28) java: 无法从静态上下文中引用非静态 变量 c1
    char c1 = 'a';
    char c2 = 'd';
    public static void main(String[] args) {
        System.out.println(c1);
        System.out.println(c2);
    }
}

 所有我给测试的类的字段加了静态公有

再回到上面编译报错的问题

 4.4、增补字符串

这个就先给个定义,很繁琐我也很懵懂

5、 移星换斗—从byte b=1谈类型转换的神秘

 

5.1、无形的转换

 

public class Test{

    public static void print(short value){
        System.out.println(value);
    }

    /*public static void print(long value){
        System.out.println(value);
    }*/

    public static void main(String[] args) {
        print(60);//13行
    }
}

报错

 

 5.2、整型的转换

 

public class Test{
    public static void main(String[] args) {
        //这三行为隐式转换,编译期可以自行处理
        byte b = -23;
        short s = 60;
        char c = '中';

        //下面的需要转换运算符
        b= (byte) c;
        c= (char) b;

        s= (short) c;
        c= (char) s;

        b= (byte) -b;
        s= (short) (s+b);
        b= (byte) (b+1);
        b=+1;//正确 等价于b= (byte) (b+1);
        //符合运算符在赋值时可以自动将运算结果转换为左侧的操作类型
    }
}

 

此处 代码省略

原码, 反码, 补码 详解 

6、扑朔迷离-浮点类型的种种悬疑 

 

6.1、浮点类型只是近似的存储 

 

public class Test{
    public static void main(String[] args) {
        double d1 = 0.1;
        double d2 = 0.2;
        double d3 = d1+d2;
        System.out.println(d3);
    }
}
运行

 

 下面在看看浮点类型存储的值

public class Test{
    public static void main(String[] args) {
        System.out.println("使用BigDecimal存储的浮点类型值,它能更精确的输出浮点数的值");
        for(int i=1;i<=9;i++){
            double d = Double.parseDouble("0." + i);
            System.out.println(d);
            BigDecimal bd=new BigDecimal(d);
            System.out.println(bd);
        }
    }
}

 

6.2、数量级差很大的浮点数 

 看程序

public class Test{
    public static void main(String[] args) {
        float f1 =30000;
        float f2 =f1+1;
        System.out.println(f1);
        System.out.println(f2);
        System.out.println("f2>f1为"+(f2>f1));

        float f3= 30000000;
        float f4 = f3+1;
        System.out.println(f3);
        System.out.println(f4);
        System.out.println("f4>f3为"+(f4>f3));

    }
}

运行

 

6.3、整形到浮点类型的转换  

 

 6.4、从浮点类型到整形的转换

 

--1 如果浮点值为NaN,结果为0L,
--2 如果浮点值为+Infinity,则为long类型的最大(小)值
--3 如果浮点值不是±Infinity,则将浮点值向0舍入为整型值
----    如果该整型值再long类型的取值范围内,结果就是long类型的整型值。
----    如果该整型值不在long类型的取值范围内,则为long类型的最大(小)值

--1 如果浮点值为NaN,结果为0(int类型),
--2 如果浮点值为+Infinity,则为int类型的最大(小)值
--3 如果浮点值不是±Infinity,则将浮点值向0舍入为整型值
----    如果该整型值再int类型的取值范围内,结果就是int类型的整型值。
----    如果该整型值不在int类型的取值范围内,则为int类型的最大(小)值

下面的程序来验证上面的规则 

public class Test{
    public static void main(String[] args) {
       //public static final double NaN = 0.0d / 0.0
        double d = Double.NaN;
        System.out.println("(long)Double.NaN="+(long)d);
        System.out.println("(int)Double.NaN="+(int)d);
        System.out.println();

        d=3e30;//很大的浮点正数值
        System.out.println("(long)3e30="+(long)d);
        System.out.println("(int)3e30="+(int)d);
        System.out.println();

        d=-8e28;//很小的浮点负数值
        System.out.println("(long)-8e28="+(long)d);
        System.out.println("(int)-8e28="+(int)d);
        System.out.println();

        d=Double.POSITIVE_INFINITY;//正无穷
        System.out.println("(long)infinity="+(long)d);
        System.out.println("(int)infinity="+(int)d);
        System.out.println();

        d=Double.NEGATIVE_INFINITY;//负无穷
        System.out.println("(long)-infinity="+(long)d);
        System.out.println("(int)-infinity="+(int)d);
        System.out.println();

        d=-12345678.6;//在int类型的取值范围内
        System.out.println("(long)-12345678.6="+(long)d);
        System.out.println("(int)-12345678.6="+(int)d);
        System.out.println("(byte)-12475678.6="+(byte)d);
        System.out.println("(int)(char)-12345678.6="+(int)(char)d);
        System.out.println("(short)-12345678.6="+(short)d);
        System.out.println();
    }
}

运行

 在第三点  浮点类型的收缩转换中

浮点类型先收缩转换为int类型再转换为目标类型(byte short char)

 对应程序中的最后几句

 浮点类型先收缩转换为int类型如果不是特殊值需要向0取整,那什么是向0取整呢,写个例子看看

public class Test{
    public static void main(String[] args) {
        double d = 12345678.6;
        System.out.println("(int)-12345678.6="+(int)d);
        d=-1.123456789;
        System.out.println("(int)-1.123456789="+(int)d);
        d=-1.923456789;
        System.out.println("(int)-1.923456789="+(int)d);
        d=1234567899.87654321;
        System.out.println("(int)1234567899.87654321="+(int)d);
    }
}

可见本文提到的收缩转换中的向0取整仅仅是把小数点后面的数字截掉了 

 

 注意,计算机中是使用补码的运算的

7、水落石出-浮点结构的最终解密 

7.1、浮点类型的存储 

这里先说说十进制的浮点数与二进制的转换

https://www.cnblogs.com/xkfz007/articles/2590472.html

 下来再了解下 浮点类型中什么是指数

https://blog.csdn.net/hdlover/article/details/4424547

对于指数本来以为很简单,实际上概念也很混淆,就用一段代码来验证下

float f1 =8.1f;
System.out.println(f1);//8.1
f1 =8.123456f;
System.out.println(f1); //8.123456
f1 =8000000.123456f;//整数部分8后面有6个零
System.out.println(f1); //8000000.0
f1 =80000000.123456f;//整数部分8后面有7个零
System.out.println(f1);//8.0E7
f1 =800000000.123456f;//整数部分8后面有8个零
System.out.println(f1);//8.0E8,有的书上说E后面的数字就是他的指数
f1 =812345678.123456f;//整数部分8后面还有8位数字
System.out.println(f1);//8.1234566E8

f1 =0.11223344556677888f;
System.out.println(f1);//0.112233445


/**
 * floa转换为2进制
 */

//我们知道7的二进制为 4+2+1 即 0111
//我们知道8的二进制为 1000
System.out.println(Integer.toBinaryString(7));//111
System.out.println(Integer.toBinaryString(8));//1000
//即Integer.toBinaryString就是一个数的二进制表现形式

f1=8.1f;
System.out.println("8.1f的二进制为"+ Integer.toBinaryString(Float.floatToIntBits(f1)));
f1=99.5f;
int fi = Float.floatToIntBits(f1);
System.out.println("99.5f的二进制为"+ Integer.toBinaryString(fi));
//100 0010 1100 0111 0000 0000 0000 0000
//float是32位的前面可以补一个0
//0100 0010 1100 0111 0000 0000 0000 0000


继续往下看

 

 

 

 

 

 7.2、近似存储

 

 

 7.3、浮点数值之间的间隙

 

 

 

 回过头在讨论下指数域

一个数表示成 x.……*2^n,这个n就数指数域要存的值

7.4、最近舍入模式

 

 

 

 

 

 8、龙虎争霸-基本for循环和增强for循环

 8.1、语法对比

先看一下二者在操作数组和集合之间的差别 

 

public static void main(String[] args) {
        int[] array = new int[] {1,2,3};
        List<String> list = new ArrayList<String>();
        list.add("top");
        list.add("middle");
        list.add("bottom");
        //基本for循环
        for(int i = 0;i<array.length;i++) {
            System.out.println(array[i]);
        }
        Iterator<String> iterator = list.iterator();
        //相当于while(iterator.hasNext())
        for(;iterator.hasNext();) {
            System.out.println(iterator.next());
        }
        
        //增强for循环,for each
        for (int i : array) {
            System.out.println(i);
        }
        for (String string : list) {
            System.out.println(string);
        }
    } 

 

 

 8.2、加强for循环的极限

 

 

8.3、加强for循环的处理实现 

 

 

 

 

 

 

 

猜你喜欢

转载自blog.csdn.net/uotail/article/details/83449055