课前问题列表
1.方法相关问题
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的功能各是什么?
1.2 main方法的x有没有被改变?为什么?
1.3 main方法的args数组的内容有没有被改变?为什么?
1.4 args数组中的值是从哪里来的?要怎么才能给他赋值
1.1
- changeStr 方法的功能是尝试改变传入的字符串
x
的值为"xyz"
。然而,由于 Java 中字符串是不可变的(immutable),这个操作实际上不会改变原字符串x
的值,而是让局部变量x
指向了一个新的字符串对象"xyz"
。但这个改变不会影响main
中的x
的值。- changeArr 方法的功能是遍历传入的字符串数组
strs
,并将每个字符串元素与它的索引(转换为字符串)拼接起来。这个操作会修改数组中的每个元素,因此会影响传入的数组本身。
1.2
- main方法的x没有被改变。
changeStr(x)
方法试图将x
的值更改为"xyz"
,但由于 Java 字符串的不可变性,这个改变仅作用于方法内部的局部变量x
,而不影响main
方法中原始变量x
的值。
1.3
main
方法中的args
数组的内容被改变了。因为args
是一个对象(字符串数组),当它被传递给changeArr(args)
方法时,传递的是数组对象的引用。在changeArr
方法内部,通过数组引用对数组元素进行的修改会反映到原始数组上。
1.4
args数组中的值 是从命令行参数中来的。当你从命令行运行 Java 程序时,可以指定一系列的参数,这些参数会被 Java 运行时系统作为字符串数组传递给程序的
main
方法的args
参数。给args赋值:不能直接在 Java 代码中给
args
赋值,因为它是main
方法的参数,由 Java 运行时系统在程序启动时提供。但是可以在main
方法内部创建和修改新的字符串数组,并使用这些数组来模拟或处理args
数组的内容。例如,需要在程序中模拟命令行参数,可以这样:
public static void main(String[] args) { // 假设我们想模拟命令行参数 String[] simulatedArgs = {"arg1", "arg2", "arg3"}; // 可以使用 simulatedArgs 代替 args 进行操作 changeArr(simulatedArgs); System.out.println(Arrays.toString(simulatedArgs)); }
这样可以在不需要实际从命令行接收参数的情况下,测试和处理类似
args
的数组。
2. 数组相关问题
对于如下程序:
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];
创建了一个长度为3的整型数组,初始时所有元素被自动初始化为0。arr[0] = 1; arr[1] = 1;
将数组的第一个和第二个元素分别设置为1。int[] arrX = arr;
这行代码并没有创建数组的副本,而是让arrX
和arr
指向同一个数组对象。因此,arrX
和arr
是同一个数组的两个引用。arr[0] = 2;
修改了数组的第一个元素为2。由于arrX
和arr
指向同一个数组,这个修改也会反映在arrX
上。System.out.println(Arrays.toString(arr));
和System.out.println(Arrays.toString(arrX));
输出的是同一个数组的内容,所以两者输出的结果一样。数组的第三个元素没有被显式设置,所以它保持其初始值0。
String[] strArr = {"aa","bb","cc"}; strArr[1] = "xx"; System.out.println(Arrays.toString(strArr));
2.2 字符串是不可变类,为什么可以对strArr[1]赋值"xx"?
输出结果:
[aa, xx, cc]
为什么:
- 字符串在Java中是不可变的(immutable),这意味着一旦字符串对象被创建,其内容(字符序列)就不能被改变。然而这并不影响数组本身的可变性。在上面的例子中,
strArr
是一个字符串数组,不是字符串对象本身。- 当执行
strArr[1] = "xx";
时,并没有改变strArr[1]
原来的字符串对象(即"bb"
),而是让strArr[1]
引用了一个新的字符串对象"xx"
。数组strArr
仍然是一个可变对象,它可以持有对任何字符串对象的引用,包括在程序执行过程中新创建的字符串对象。- 字符串是不可变的,但字符串数组(或任何对象数组)是可变的,我们可以更改数组中元素的引用,即使这些元素本身是不可变的对象。
3.使用int[5][]
定义一个二维数组,其第二维到底有多长?尝试补全代码,然后使用foreach获其他循环方法遍历这个二维数组?
- 在Java中,当使用
int[5][]
定义一个二维数组时,实际上是在创建一个一维数组,其元素类型是int[]
(即整型数组)。这个一维数组的长度是5,意味着它可以存储5个整型数组(第二维)。但是,这些第二维数组(即每个int[]
)的长度在初始时是不确定的,并且每个第二维数组的长度可以独立设置。补全代码:
public class Main { public static void main(String[] args) { int[][] twoDArray = new int[5][]; twoDArray[0] = new int[2]; // 第一行长度为2 twoDArray[1] = new int[3]; // 第二行长度为3 twoDArray[2] = new int[1]; // 第三行长度为1 twoDArray[3] = new int[4]; // 第四行长度为4 twoDArray[4] = new int[5]; // 第五行长度为5 for (int i = 0; i < twoDArray.length; i++) { // 遍历第一维 for (int j = 0; j < twoDArray[i].length; j++) { // 遍历第二维 twoDArray[i][j] = i * j; // 给每个元素赋值 System.out.print(twoDArray[i][j] + " "); } System.out.println(); } // 使用foreach循环遍历二维数组 for (int[] row : twoDArray) { // 遍历第一维 for (int val : row) { // 遍历当前行的每个元素 System.out.print(val + " "); } System.out.println(); } } }
- 在这个例子中,我首先创建了一个
int[5][]
类型的二维数组twoDArray
,然后分别为它的每个第二维数组分配了不同的长度,并使用嵌套的for
循环来遍历和初始化这个二维数组。之后,我展示了两种遍历这个二维数组的方法:一种是使用嵌套的for
循环,另一种是使用嵌套的foreach
循环(注意foreach
不能直接用于二维数组的第二维,所以需要先用foreach
遍历第一维,然后在内部使用另一个foreach
遍历当前行的每个元素)。
4.类与对象的区别是什么? Math类有对象吗?String类有什么属性是private的,有什么方法是public的,为什么这样设计(尝试举两例说明)?
类与对象的区别:
- 类(Class) 是对一组具有相同属性和行为的对象的抽象描述。它定义了一个对象的模板或蓝图,指定了对象可以拥有的属性和行为(即方法和属性)。类是一种引用数据类型,用于创建对象。
- 对象(Object) 是根据类创建的实例。每个对象都有自己独立的属性(成员变量)和行为的特定值(成员方法)。对象可以看作是类的具体表现。
Math类有对象吗?
- Math类没有对象。Math类是一个包含用于执行基本数学运算的方法的类,如指数、对数、平方根、三角函数等。Math类中的方法都是静态的(static),这意味着可以直接通过类名调用它们,而无需创建Math类的对象。例如,
Math.abs(-10)
用于计算-10的绝对值,这里并没有创建Math类的实例。
String类的私有属性和公有方法 :
- String类的私有属性(通常包括用于存储字符序列的内部数据结构)对外部是不可见的。这些私有属性确保了String对象的封装性,即隐藏了对象的内部表示,仅通过公共接口与外界交互。
- String类的公有方法(例如
length()
,charAt(int index)
,substring(int beginIndex, int endIndex)
,equals(Object anObject)
,toLowerCase()
,toUpperCase()
等)允许我们访问和操作String对象。这些公有方法的设计目的是为了提供字符串处理的功能,同时保护String对象的内部状态不被随意修改(因为String对象是不可变的)。
为什么这样设计?
私有属性的设计原因:
- 封装:隐藏对象的内部状态,仅通过公共接口与外界交互,增强了数据的安全性和类的模块化。
- 灵活性:私有属性允许类内部实现细节的改变,而不需要修改使用类的代码。
公有方法的设计原因:
- 易用性:提供明确、一致的接口,使得其他类可以方便地调用String对象的功能。
- 安全性:通过控制对对象状态的访问,可以防止不恰当的修改,特别是在String对象为不可变类型时。
例子:
length()
方法:这是一个公有方法,用于获取字符串的长度。它是public的,因为这是一个常用的操作,而且字符串的长度是外部需要知道的信息。charAt(int index)
方法:这也是一个公有方法,用于获取字符串指定位置的字符。它同样是public的,因为访问字符串中的特定字符是常见的需求。这些公有方法的设计使得String类非常灵活和强大,同时保持了其内部状态的安全性和一致性。
5.将类的属性设置为public可以方便其他类访问,但为什么Java中普遍使用setter/getter模式对对象的属性进行访问呢?这与封装性又有什么关系?
- 在Java中,尽管将类的属性设置为public可以方便其他类直接访问,但这种做法并不被推荐,主要原因与封装性(Encapsulation)紧密相关。封装性是面向对象编程(OOP)的一个核心概念,它要求将对象的状态信息(即属性)隐藏在对象内部,不允许外部直接访问,而是通过公共的接口(即方法)来对对象的状态进行查询和修改。
- 使用setter/getter模式(也称为访问器(Accessor)和修改器(Mutator)方法)对对象的属性进行访问,正是为了实现封装性的一个重要手段。
主要原因:
隐藏内部实现:通过将属性设置为private,类的内部实现被隐藏起来,这样即使类的内部结构发生变化(例如,更换存储属性的数据结构),只要保持setter/getter方法的签名不变,就不会影响到使用这个类的其他代码。这提高了类的可维护性和可扩展性。
控制访问:setter/getter方法允许在属性被访问或修改时加入额外的逻辑。例如,可以在setter方法中加入验证逻辑,以确保属性值的有效性;在getter方法中,可以基于某些条件返回不同的结果或计算值。这种控制访问的能力是public属性所无法提供的。
灵活性:随着软件的发展,对类属性的访问需求可能会发生变化。如果属性是public的,那么改变访问方式可能会影响到很多使用这个属性的代码。而如果使用setter/getter方法,则可以通过修改方法实现来改变访问逻辑,而无需修改使用这个类的其他代码。
数据安全性:通过setter/getter方法可以保护对象的状态不被非法修改。例如,可以确保某些属性在对象创建后就不能被修改(只需不提供setter方法即可),或者确保属性值始终满足特定的约束条件(在setter方法中加入验证逻辑)。
综上所述,使用setter/getter模式对对象的属性进行访问,是Java中实现封装性的一个重要手段。它不仅提高了类的可维护性、可扩展性和灵活性,还增强了数据的安全性。因此,在Java中普遍推荐使用setter/getter模式来访问对象的属性。
6.对象的属性可在什么时候进行初始化?都有哪些进行初始化的办法?
声明时初始化
- 在类的成员变量声明时直接初始化。这种方式在对象创建之前就已经确定了属性的初始值,但这是在类加载到JVM时发生的,而不是在对象创建时。
public class MyClass { private int myNumber = 42; // 声明时初始化 }
构造方法中初始化
- 在构造方法(Constructor)中初始化对象的属性。这是最常见的初始化方式之一,因为它允许在创建对象时根据需要设置不同的初始值。
public class MyClass { private int myNumber; public MyClass() { myNumber = 0; // 无参构造方法初始化 } public MyClass(int number) { myNumber = number; // 带参构造方法初始化 } }
初始化块中初始化
- Java允许在类中定义初始化块(Initialization Blocks),这些块在构造方法执行之前执行,并且每次创建对象时都会执行。初始化块可以是静态的(static)或非静态的。静态初始化块用于初始化静态变量,而非静态初始化块用于初始化实例变量。
public class MyClass { private int myNumber; { // 非静态初始化块 myNumber = 10; } static { // 静态初始化块,用于静态变量 // 这里通常不用于实例变量的初始化 } }
通过setter方法初始化
- 在对象创建之后,通过调用对象的setter方法来设置属性的值。这种方式提供了更大的灵活性,允许在对象创建后的任何时间修改属性值。
public class MyClass { private int myNumber; // setter方法 public void setMyNumber(int number) { myNumber = number; } } // 使用 MyClass obj = new MyClass(); obj.setMyNumber(20); // 通过setter方法初始化
标签:02,初始化,Java,int,2024,对象,数组,字符串,属性 From: https://blog.csdn.net/2301_79959806/article/details/142089983