第02周 预习、实验与作业:Java基础语法2、面向对象入门
方法相关问题
1.1 changeStr与changeArr的功能各是什么?
changeStr(String x)
: 尝试将传入的字符串引用指向新的字符串"xyz"changeArr(String[] strs)
: 遍历数组并将每个元素修改为原值加上索引号
1.2 main方法的x有没有被改变?为什么?
没有改变。因为:
- Java中参数传递是值传递(传递的是引用的副本)
x = "xyz"
只是改变了方法内部参数x的指向,不影响外部的x变量- 字符串是不可变对象,无法在原地修改
1.3 main方法的args数组的内容有没有被改变?为什么?
有改变。因为:
- 数组是引用类型,传递的是数组引用的副本
- 两个引用(main中的args和changeArr中的strs)指向同一个数组对象
- 通过strs[i]修改的是共享的数组元素
1.4 args数组中的值是从哪里来的?要怎么才能给他赋值。
- args数组的值来自命令行参数
- 赋值方式:
- 命令行:
java Main arg1 arg2 arg3
- IDE中:在运行配置中设置程序参数
- 其他方式:可以通过其他数组赋值
args = new String[]{"a", "b"}
- 命令行:
2.1 这段程序输出结果是什么?为什么?
int[] arr = new int[3];
arr[0] = 1; arr[1] = 1;
int[] arrX = arr; // arrX和arr指向同一个数组
arr[0] = 2; // 修改的是共享的数组
System.out.println(Arrays.toString(arr));
System.out.println(Arrays.toString(arrX));
2,1,0,因为arrX和arr引用同一个数组对象。
2.2 字符串是不可变类,为什么可以对strArr[1]赋值"xx"。
- 字符串不可变指的是String对象内容不可变
strArr[1] = "xx"
是修改数组元素(引用)的指向,不是修改字符串内容- 原来的"bb"字符串对象没有被修改,只是数组第二个元素现在指向了新的"xx"字符串
3. 二维数组遍历
int[][] arr2D = new int[5][]; // 第二维长度不确定
// 初始化第二维
for (int i = 0; i < arr2D.length; i++) {
arr2D[i] = new int[i + 1]; // 每行长度不同
}
// 嵌套for循环
for (int i = 0; i < arr2D.length; i++) {
for (int j = 0; j < arr2D[i].length; j++) {
System.out.print(arr2D[i][j] + " ");
}
System.out.println();
}
// foreach
for (int[] row : arr2D) {
for (int num : row) {
System.out.print(num + " ");
}
System.out.println();
}
4.类与对象的区别
- 类:模板/蓝图,定义对象的属性和行为(如:String类)
- 对象:类的实例,具体存在的数据(如:"hello"字符串对象)
- 关系:类是抽象概念,对象是具体实现
Math类有对象吗?
没有。Math类是工具类:
- 所有方法和属性都是static的
- 构造函数是private的,无法实例化
- 设计为静态工具类,提供数学运算功能
String类的封装设计
private属性举例:
private final char value[]
:存储字符数据,防止外部直接修改private int hash
:缓存哈希值,避免重复计算
public方法举例:
public int length()
:提供安全的长度访问public char charAt(int index)
:提供安全的字符访问
为什么这样设计:
- 安全性:防止外部直接修改内部数据
- 不可变性:保证字符串内容不会被意外改变
- 封装实现细节:可以优化内部实现而不影响使用者
5.
为什么使用setter/getter:
- 控制访问:可以在setter中添加验证逻辑
- 保持兼容性:可以修改内部实现而不改变接口
- 延迟初始化:可以在getter中实现懒加载
- 添加副作用:可以在值改变时触发其他操作
与封装性的关系:
- 封装的核心是"隐藏实现细节,暴露必要接口"
- setter/getter提供了受控的访问方式,实现了信息隐藏
6. 对象的属性可在什么时候进行初始化?
- 声明时直接初始化
- 构造方法中初始化
- 初始化块中初始化
- 使用时延迟初始化
初始化方法:
public class Example {
// 1. 声明时初始化
private String name = "default";
// 2. 初始化块
{
count = 0;
}
// 3. 构造方法
public Example() {
this.name = "unknown";
}
// 4. 延迟初始化
private String lazyValue;
public String getLazyValue() {
if (lazyValue == null) {
lazyValue = "initialized";
}
return lazyValue;
}
}
进阶:作用域与封装性
从作用域角度理解封装性:
- 类内部作用域:private属性和方法只在类内部可见
- 包作用域:default访问级别在包内可见
- 全局作用域:public对所有类可见
封装性通过限制作用域来实现:
- 将实现细节限制在最小作用域(private)
- 只暴露必要的接口到更大作用域(public)
- 这样减少了代码的耦合度,提高了可维护性
例如String类的value
数组是private的,外部代码无法直接访问,只能通过public方法间接操作,这样就保证了字符串的不可变性和安全性。