疑问问题总结(一)

来源:互联网 发布:excel数据批量打印 编辑:程序博客网 时间:2024/05/22 17:40



1.关于构造函数的执行过程

运行以下代码片段,输出结果是?( )
class X {
Y b = new Y();
X() {
System.out.print("X");
}
}

class Y {
Y() {
System.out.print("Y");
}
}



public class Z extends X {
Y y = new Y();
Z() {
System.out.print("Z");
}

public static void main(String[] args) {
new Z();
}
}
A. Z
B. YZ
C. XYZ
D. YXYZ

new Z();之后不是应该执行Z的父类的构造函数,输出X,然后执行Z的构造函数输出Z么? 
那个Y在什么时候初始化,以及整个的初始化过程是怎样的?

 

--------------------------------------------------------------问题和答案分割线--------------------------------------------------------------

1.因为new用到了Z,并且子类继承了父类。所以会先找到父类X.class,再找到Z.class文件并加载到内存中。
2.在堆内存中开辟空间,分配内存地址
3.对 父类属性 进行初始化。也就是执行 Y b=new Y();//输出Y
4.运行父类构造函数//输出X
5.在堆内存中建立子类对象的特有属性,并进行初始化。也就是运行Y y = new Y();//输出Y
5.再执行子类构造函数//输出Z

另外分享下我的总结,看明白下面的初始化过程,所有关于对象创建先后顺序的题目一目了然
子类继承父类,子父类中都有 静态代码块(给类初始化用的,随着类的加载而加载),构造代码块(给对象初始化,优先于构造函数)带参构造函数。
Zi p=new Zi("lisi",20);
1、因为new用到了Zi.class,并且子类继承了父类。所以会先找到Fu.class,再找到Zi.class文件并加载到内存中
2、执行 父类静态代码块
3、执行子类静态代码块
4、在堆内存中开辟空间,分配内存地址
5、对 父类属性 默认初始化,显式初始化//默认初始化,既属性都有默认值,比如 int age=20;默认值就是0;显式初始化为20;
6、父类构造代码块
7、父类构造函数
8、在堆内存中建立子类对象的特有属性,并进行默认初始化
9、对子类属性显式初始化
10、子类构造代码块
11、子类构造函数
12、将内存地址付给栈内存中的p变量

 

----------------------------------------------------------------题目间分割线----------------------------------------------------------------



2.关于字符串在内存中存放位置的问题。

编译并运行以下代码的输出结果是?( )

String s1 = new String("amit");

System.out.println(s1.replace('m','r'));

System.out.println(s1);

String s3 = "arit";

String s4 = "arit"; //s3与s4的地址相同,那么 s3==s1 这个判断是否也为 ture呢?

String s2 = s1.replace('m','r'); //既然字符串池中不能有重复的元素,那么s2所代表的"arit"在池中已经存在,那么s2是不是应该与s3和s4是拥有同一地址值?

System.out.println(s2 == s3);

System.out.println(s3 == s4);

A. arit

amit

false

true

B. arit

arit

false

true

C. amit

amit

false

true

D. arit

amit

true

true

--------------------------------------------------------------问题和答案分割线--------------------------------------------------------------

答案一:

public String replace(char oldChar, char newChar) {
if (oldChar != newChar) {
int len = count;
int i = -1;
char[] val = value; /* avoid getfield opcode */
int off = offset; /* avoid getfield opcode */

while (++i < len) {
if (val[off + i] == oldChar) {
break;
}
}
if (i < len) {
char buf[] = new char[len];
for (int j = 0 ; j < i ; j++) {
buf[j] = val[off+j];
}
while (i < len) {
char c = val[off + i];
buf = (c == oldChar) ? newChar : c;
i++;
}
return new String(0, len,buf);
}
}
return this;
}
这是replace方法的源代码,从中可以看出,其实返回的是一个新new出来的String对象的引用,这样就很好理解了,说明Java虚拟机根本就没有对replace的返回结果进行缓存。

 

答案二:

首先,字符串对象是一个常量,存放于常量池中。什么是常量,就是不可变的。
String s1 = new String("amit");
System.out.println(s1.replace('m', 'r'));
//先说这个,其实这句话相当于String s2=s1.replace('m', 'r'); 再输出S2,这只是另外一个对象
//记得常量是不能改变的。所以不管你输出什么,s1没变

String s3 = "arit";
String s4 = "arit";
System.out.println(s3 == s4);
再说这个,既然是常量池,所以s3和s4指向同一对象,输出s3==s4,肯定是true。

System.out.println(s2 == s3);
再说困惑你的最后一个问题。
String s2=s1.replace('m', 'r'); //replace源码返回的其实是一个新new的字符串。
也就是相当于下面的比较:
String a="abc";
String b=new String("abc");
所以输出s2==s3是 false。

 

答案三:

答案选A
首先我们要知道对于String类
(1)Strings1="abc"; String s2="abc";
这种情况下,首先会在内存中查找看String池中有没有"abc"对象,如果有的话,就不会再创建新的对象,只是返回它的引用,如果没有的话才会创建该类对象
(2)String s=newString("abc");
这种情况下由于new了对象,所以不管String池中有没有该类对象都会再次创建该对象

String s1 = new String("amit"); //在内存中创建了一个String对象"amit"

System.out.println(s1.replace('m','r')); //用replace语句把amit中的m替换为r,所以打印"arit"

System.out.println(s1); //由于String类的对象不会被改变,所以s1对象不会改变还是"amit"

String s3 = "arit"; //创建s3对象"arit"

String s4 = "arit"; //创建s4对象"arit",上面总结的(1)可以看出,s3对象和s4对象其实都是一个对象,因为内存中只创建了一次,它们引用相同
String s2 = s1.replace('m','r'); //不是说线程池中不能有重复的元素,像(2),加入线程池中有"abc"对象,还是会创建的,其实这个替换的语句就
相当于重新创建对象,因为原有的对象不可能改变,只有创建新的对象
System.out.println(s2 == s3); //由于s2相当于创建了一个新的对象,所以两个对象地址值是不相同的,所以为false
System.out.println(s3 == s4); //s3和s4的引用指向了同一个对象,所以地址值相同,为true

所以答案为

A. arit

amit

false

true

----------------------------------------------------------------题目间分割线----------------------------------------------------------------

 

 

3.关于接口的问题。

尝试编译并运行以下代码,会发生什么? ( )

import java.util.*;

public abstract interface Test1

{

public void test();

}

public class Test2 implements Test1

{

public void test(){}

}

A. 编译错误

B. 运行期异常

C. 编译通过

D. 以上都不对

该题的答案是A,但是我不太清楚错误的点具体在哪里,是因为使用了两个public,还是接口不能用abstract修饰,还是其他的错误。

--------------------------------------------------------------问题和答案分割线--------------------------------------------------------------

此题是编译不通过,编译器会报The public typeTest1 must be defined in its own file这个错误。
意思是让你在Test1这个接口的自己的文件里定义。
其实也就是说 不能在同一个包中,定义两个public类或接口。

而没有主函数,是在运行时期的错误,运行时,找不到程序的入口。