一、方法相关问题
public class Main {
static void changeStr(String x) {
x = "xyz";
}
static void changeArr(String[] strs) {
for (int i = 0; i < strs.length; i++) {
strs[i] = strs[i]+""+i;
}
}
public static void main(String[] args) {
String x = "abc";
changeStr(x);
System.out.println(x);
changeArr(args);
System.out.println(Arrays.toString(args));
}
}
对于如上代码:
1.1 changeStr与changeArr的功能各是什么?
答:changeStr方法的功能是将引用x指向了一个新的字符串对象"xyz"。;changeArr方法的功能是将传入的字符串数组中的每个元素修改为原字符串加上其索引值。
1.2 main方法的x有没有被改变?为什么?
答:main方法中的x没有被改变;因为字符串在Java中是不可变的,在changeStr方法中,x引用被重新赋值为指向一个新的字符串"xyz"。
1.3 main方法的args数组的内容有没有被改变?为什么?
答:main方法中的args数组的内容被改变了。因为数组是可变的对象,当args数组作为参数传递给changeArr方法时,传递的是数组对象的引用。在changeArr方法中,数组的内容被直接修改,因此main方法中的args数组的内容也会相应改变。
1.4 args数组中的值是从哪里来的?要怎么才能给他赋值。
答:args数组中的值来自程序运行时的命令行参数;要给args数组赋值,可以在运行Java程序时在命令行中传递参数。
二、数组相关问题
对于如下程序
int[] arr = new int[3];
arr[0] = 1; arr[1] = 1;
int[] arrX = arr;
arr[0] = 2;
System.out.println(Arrays.toString(arr));
System.out.println(Arrays.toString(arrX));
2.1 这段程序输出结果是什么?为什么?
答:输出的结果是[2,1,0] , [2,1,0] ;
当执行 int[] arr = new int[3];
时,创建了一个名为 arr
的整型数组,它有三个元素,默认初始化为0。
随后,arr[0] = 1;
和 arr[1] = 1;
将数组的第一个和第二个元素分别赋值为1。
int[] arrX = arr;
这行代码创建了一个新的引用 arrX
,它指向与 arr
相同的数组对象。因此,arrX
和 arr
引用的是同一个数组。
接下来,arr[0] = 2;
修改了数组的第一个元素的值为2。由于 arrX
和 arr
引用同一个数组,所以这个修改也会反映在 arrX
中。
所以arr[0] = 2,arr[1] = 1,arr[2] = 0.
因此,当打印 Arrays.toString(arr)
和 Arrays.toString(arrX)
时,两个数组的输出都是 [2, 1, 0]
,因为它们指向同一个数组对象,且数组已经被修改。
String[] strArr = {"aa","bb","cc"};
strArr[1] = "xx";
System.out.println(Arrays.toString(strArr));
2.2 字符串是不可变类,为什么可以对strArr[1]赋值"xx"。
答:虽然字符串不可以改变,但是String[] strArr = {"aa","bb","cc"};
中,strArr
是一个字符串数组,它包含了三个字符串对象 "aa"
, "bb"
, 和 "cc"
。所以字符串数组 strArr
本身是可变的,因为它是一个数组。可以改变数组中的元素引用,可以将bb改成xx.
三、使用int[5][]
定义一个二维数组,其第二维到底有多长?尝试补全代码,然后使用foreach或其他循环方法遍历这个二维数组?
答:int[5][]
定义了一个二维数组,其中第一维的长度是5,但是第二维的长度没有指定,因此它可以是任意长度。
补全代码:
public class Main {
public static void main(String[] args) {
// 定义一个二维数组,第一维长度为5,第二维长度不固定
int[][] twoDimArray = new int[5][];
// 为每个内部数组分配不同的长度
twoDimArray[0] = new int[2]; // 第二维长度为2
twoDimArray[1] = new int[3]; // 第二维长度为3
twoDimArray[2] = new int[1]; // 第二维长度为1
twoDimArray[3] = new int[4]; // 第二维长度为4
twoDimArray[4] = new int[5]; // 第二维长度为5
// 初始化二维数组中的元素
int value = 0;
for (int i = 0; i < twoDimArray.length; i++) {
for (int j = 0; j < twoDimArray[i].length; j++) {
twoDimArray[i][j] = value++;
}
}
// 使用foreach循环遍历二维数组
System.out.println("Using foreach loop:");
for (int[] innerArray : twoDimArray) {
for (int item : innerArray) {
System.out.print(item + " ");
}
System.out.println();
}
}
}
四、类与对象的区别是什么? Math类有对象吗?String类有什么属性是private的,有什么方法是public的,为什么这样设计(尝试举两例说明)?
答:类是对象的蓝图或模板,是一个静态的概念,对象是一个实例,是一个动态的概念,可以存储数据,具有类的属性和行为;Math类是一个工具类,包含了执行基本数学运算的方法,因为Math类中的所有方法都是静态的,所以不需要创建Math类的对象来使用这些方法。
五、将类的属性设置为public可以方便其他类访问,但为什么Java中普遍使用setter/getter模式对对象的属性进行访问呢?这与封装性又有什么关系?
答:虽然将类的属性设置为public确实可以方便其他类直接访问这些属性,但这种做法并不符合面向对象编程(OOP)的封装原则;使用setter/getter与封装性的关系:隐藏实现细节,保护对象状态,具有灵活性,修改内部代码表示不影响客户端代码,增加额外的逻辑,控制访问级别,调试方便,维护性增强。
六、对象的属性可在什么时候进行初始化?都有哪些进行初始化的办法?
答:可以在声明时初始化(在声明属性时直接赋初值);构造方法中初始化(在类的构造方法中设置属性的初始值);在初始化块中初始化(使用静态初始化块(仅适用于静态属性)和非静态初始化块(适用于实例属性)来初始化属性);工厂方法中初始化(在工厂方法中创建对象实例时设置属性的初始值);使用setter方法初始化(在创建对象后,通过调用setter方法来设置属性的初始值)。
七、进阶(可选):尝试使用作用域来说明封装性
答:
私有作用域(Private Scope):
在Java中,使用private
关键字定义的成员(属性或方法)的作用域仅限于其所属的类内部。这意味着,这些成员不能被类的外部直接访问或修改,从而隐藏了内部实现细节。
默认作用域(Package-private Scope):
如果没有指定访问修饰符,成员的作用域默认为包私有,即在同一包内的任何类都可以访问,但在包外不可见。这提供了一定程度的封装,限制了访问范围。
受保护作用域(Protected Scope):
使用protected
关键字定义的成员,除了在同一包内的类可以访问之外,还可以被不同包中的子类访问。这允许子类访问和修改部分封装的细节,同时仍然对外部世界隐藏。
公共作用域(Public Scope):
public
关键字定义的成员具有最大的作用域,它们可以从任何地方访问。尽管public
成员不是封装的一部分,但它们通常用于定义类的公共接口,这些接口是外界与对象交互的唯一途径。