java做题笔记
1、
初始化过程是这样的:
1.首先,初始化父类中的静态成员变量和静态代码块,按照在程序中出现的顺序初始化;
2.然后,初始化子类中的静态成员变量和静态代码块,按照在程序中出现的顺序初始化;
3.其次,初始化父类的普通成员变量和代码块,在执行父类的构造方法;
4.最后,初始化子类的普通成员变量和代码块,在执行子类的构造方法;
(1)初始化父类的普通成员变量和代码块,执行 C c = new C(); 输出C
(2)super("B"); 表示调用父类的构造方法,不调用父类的无参构造函数,输出B
(3) System.out.print("B");
所以输出CBB
2、
记住:无论如何finally语句都要执行就会这个题了
finally语句在try或catch中的return语句执行之后返回之前执行且finally里的修改语句不能影响try或catch中 return已经确定的返回值,若finally里也有return语句则覆盖try或catch中的return语句直接返回。(同学们可以踊跃探讨哦)
inally一定会执行的,有两种情况:
1.finally中有return语句,当try执行到return时会执行finally中的代码,其中有return 就直接返回了,如题,返回值为3.
2.finally中不含return语句,那么当执行到return时,它会被保存等待finally执行完毕后返回,这个时候无论finally内部如何改变这个值,都不会影响返回结果!如:
3、
语句:char foo='中',是否正确?(假设源文件以GB2312编码存储,并且以javac – encoding GB2312命令编译)
这在java中是正确的,在C语言中是错误的,java的char类型占两个字节,默认使用GBK编码存储。这种写法是正确的,此外java还可以用中文做变量名。
Java语言中,中文字符所占的字节数取决于字符的编码方式,一般情况下,采用ISO8859-1编码方式时,一个中文字符与一个英文字符一样只占1个字节;采用GB2312或GBK编码方式时,一个中文字符占2个字节;而采用UTF-8编码方式时,一个中文字符会占3个字节。
在C++中
在C++中,char是基础数据类型,8位,1个字节。byte不是基础数据类型,一般是typedef
unsigned char byte;这样子的,也就是说,byte其实是unsigned
char类型,那么也是8位,1个字节。不同的是,char可以表示的范围是-128-127,而byte可以表示的范围是0-255。
在Java中
在java中,char和byte都是基础数据类型,其中的byte和C++中的char类型是一样的,8位,1个字节,-128-127。但是,char类型,是16位,2个字节, '\u0000'-'\uFFFF'。
为什么java里的char是2个字节?
因为java内部都是用unicode的,所以java其实是支持中文变量名的,比如string
世界 = "我的世界";这样的语句是可以通过的。
综上,java中采用GB2312或GBK编码方式时,一个中文字符占2个字节,而char是2个字节,所以是对的
4、
检查程序,是否存在问题,如果存在指出问题所在,如果不存在,说明输出结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
package algorithms.com.guan.javajicu; public class Inc { public static void Inc inc = new int i = inc.fermin(i); i= i System.out.println(i); } void fermin(int i){ i++; } } |
Java使用了中间缓存变量机制:
i=i++;等同于:
temp=i; (等号右边的i)
i=i+1; (等号右边的i)
i=temp; (等号左边的i)
而i=++i;则等同于:
i=i+1;
temp=i;
i=temp;
但是那些说将i++表达式的结果赋给i的答案,本人表示不理解。
上面的回答都没说明白为什么会这样,首先运行这个程序,在c/c++和java会发现不一样,在c/c++中答案是1,在java中答案是0。为什么呢?
原因:jvm里面有两个存储区,一个是暂存区(是一个堆栈,以下称为堆栈),另一个是变量区。jvm会这样运行这条语句, JVM把count值(其值是0)拷贝到临时变量区。步骤2 count值加1,这时候count的值是1。步骤3 返回临时变量区的值,注意这个值是0,没修改过。步骤4 返回值赋值给count,此时count值被重置成0。 c/c++中没有另外设置一个临时变量或是临时空间来保存i,所有操作都是在一个内存空间中完成的,所以在c/c++中是1。
如果你理解JVM的内存模型,就不难理解为什么答案返回的是0,而不是1。
我们单独看问题中的这两句代码。
1 |
int i = 0; i = i++; |
Java虚拟机栈(JVM Stack)描述的是Java方法执行的内存模型,而JVM内存模型是基于“栈帧”的,每个栈帧中都有 局部变量表 和 操作数栈 (还有动态链接、return address等),那么JVM是如何执行这个语句的呢?通过javap大致可以将上面的两行代码翻译成如下的JVM指令执行代码。
0: iconst_0
1: istore_1
2: iload_1
3: iinc
1, 1
6: istore_1
7: iload_1
接下来分析一下JVM是如何执行的:
第0:将int类型的0入栈,就是放到操作数栈的栈顶
第1:将操作数栈栈顶的值0弹出,保存到局部变量表 index (索引)值为1的位置。(局部变量表也是从0开始的,0位置一般保存当前实例的this引用,当然静态方法例外,因为静态方法是类方法而不是实例方法)
第2:将局部变量表index 1位置的值的副本入栈。(这时局部变量表index为1的值是0,操作数栈顶的值也是0)
第3:iinc是对int类型的值进行自增操作,后面第一个数值1表示,局部变量表的index值,说明要对此值执行iinc操作,第二个数值1表示要增加的数值。(这时局部变量表index为1的值因为执行了自增操作变为1了,但是操作数栈中栈顶的值仍然是0)
第6:将操作数栈顶的值弹出(值0),放到局部变量表index为1的位置(旧值:1,新值:0),覆盖了上一步局部变量表的计算结果。
第7:将局部变量表index 1位置的值的副本入栈。(这时局部变量表index为1的值是0,操作数栈顶的值也是0)
总结:从执行顺序可以看到,这里第1和第6执行了2次将0赋值给变量i的操作(=号赋值),i++操作是在这两次操作之间执行的,自增操作是对局部变量表中的值进行自增,而栈顶的值没有发生变化,这里需要注意的是保存这个初始值的地方是操作数栈而不是局部变量表,最后再将栈顶的值覆盖到局部变量表i所在的索引位置中去。
有兴趣的同学可以去了解一下JVM的栈帧(Stack Frame)
关于第二个陷阱(为什么 fermin方法没有影响到i的值 )的解答看下面。
1 |
inc.fermin(i); |
1. java方法之间的参数传递是 值传递 而不是 引用传递
2. 每个方法都会有一个栈帧,栈帧是方法运行时的数据结构。这就是说每个方法都有自己独享的局部变量表。(更严谨的说法其实是每个线程在执行每个方法时都有自己的栈帧,或者叫当前栈帧 current stack
frame)
3. 被调用方法fermin()的形式参数int i 实际上是调用方法main()的实际参数 i 的一个副本。
4. 方法之间的参数传递是通过局部变量表实现的,main()方法调用fermin()方法时,传递了2个参数:
第0个隐式参数是当前实例(Inc inc = new Inc(); 就是inc引用的副本,引用/reference 是指向对象的一个地址,32位系统这个地址占用4个字节,也就是用一个Slot来保存对象reference,这里传递的实际上是reference的一个副本而不是 reference本身 );
第1个显示参数是 i 的一个副本。所以 fermin()方法对 i 执行的操作只限定在其方法独享或可见的局部变量表这个范围内,main()方法中局部变量表中的i不受它的影响;
如果main()方法和fermin()方法共享局部变量表的话,那答案的结果就会有所不同。 其实你自己思考一下,就会发现, JVM虚拟机团队这么设计是有道理的。
5、
都是Throwable的子类:
1.Exception(异常) :是程序本身可以处理的异常。
2.Error(错误): 是程序无法处理的错误。这些错误表示故障发生于虚拟机自身、或者发生在虚拟机试图执行应用时,一般不需要程序处理。
3.检查异常(编译器要求必须处置的异常) : 除了Error,RuntimeException及其子类以外,其他的Exception类及其子类都属于可查异常。这种异常的特点是Java编译器会检查它,也就是说,当程序中可能出现这类异常,要么用try-catch语句捕获它,要么用throws子句声明抛出它,否则编译不会通过。
4.非检查异常(编译器不要求处置的异常): 包括运行时异常(RuntimeException与其子类)和错误(Error)。