Java课程 第02周 预习、实验与作业:Java基础语法2:面向对象入门

1.3 课前问题列表

方法相关问题

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(String x) 的功能:接收一个字符串参数 x,并尝试将其重新赋值为 "xyz"。
changeArr(String[] strs) 的功能:该方法接收一个字符串数组,并遍历数组,将每个元素修改为原元素值加上其索引。

1.2 main方法的x有没有被改变?为什么?
没有被改变。
原因:
Java的参数传递是值传递(pass by value)。当调用 changeStr(x) 时,传递的是变量 x 的引用的副本(而不是引用本身)。
字符串是不可变对象,x = "xyz" 只是将方法内的局部参数 x(引用的副本)指向了新的字符串 "xyz",但原始变量 x 仍然指向原来的字符串 "abc"。

1.3 main方法的args数组的内容有没有被改变?为什么?
main方法的args数组的内容被改变。
原因:
Java的参数传递是值传递,但对于引用类型,传递的是引用的副本,即副本和原始引用指向同一个数组对象。
changeArr 方法通过这个引用副本遍历数组,并修改了每个元素的值。由于修改的是同一数组对象的内容,因此原始数组(即 args)的内容被改变。

1.4 args数组中的值是从哪里来的?要怎么才能给他赋值。
1)args 数组中的值来自命令行参数(command-line arguments),即在运行程序时通过命令行传入的参数。
2) 有以下几种方式可以给 args 赋值:
a)命令行直接传入(最常用):
例如,编译并运行程序:

      bash

       javac Main.java

       java Main arg1 arg2 arg3

则 args 数组为 ["arg1", "arg2", "arg3"]。
b)通过IDE配置:
在IDE(如IntelliJ IDEA、Eclipse)中运行程序时,可以在运行配置中设置命令行参数。

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 这段程序输出结果是什么?为什么?

  String[] strArr = {"aa","bb","cc"};

  strArr[1] = "xx";

  System.out.println(Arrays.toString(strArr));

输出结果:[aa, xx, cc]
原因:
1)字符串数组 strArr 初始化为 {"aa","bb","cc"}。
2)通过 strArr[1] = "xx"; 将索引1的元素(原为"bb")重新赋值为"xx"。
(数组是可变的数据结构,允许通过索引修改元素的值。)
3)修改后数组变为 ["aa", "xx", "cc"],Arrays.toString(strArr) 输出该内容。

2.2 字符串是不可变类,为什么可以对strArr[1]赋值"xx"。
字符串不可变指的是字符串对象本身的内容不可改变。即无法修改像"bb"这个字符串对象的内容,无法将它的某个字符改成其他字符。
但数组是可变的,即可以修改数组元素指向的对象。

3.使用int[5][]定义一个二维数组,其第二维到底有多长?尝试补全代码,然后使用foreach获其他循环方法遍历这个二维数组?
使用 int[5][] 定义的是一个第二维长度不确定的数组。第一维长度固定为5即5行,但第二维每个元素(每行)目前都是 null,需要手动初始化每行的长度。

补全代码:

    public class Main {

        public static void main(String[] args) {

  

    int[][] arr = new int[5][];

   

    arr[0] = new int[3];  // 第0行长度为3

    arr[1] = new int[2];  // 第1行长度为2

    arr[2] = new int[4];  // 第2行长度为4

    arr[3] = new int[1];  // 第3行长度为1

    arr[4] = new int[5];  // 第4行长度为5

    for (int i = 0; i < arr.length; i++) {

        for (int j = 0; j < arr[i].length; j++) {

            arr[i][j] = i * 10 + j;  // 任意赋值

        }

    }

    System.out.println("使用嵌套for循环:");

    for (int i = 0; i < arr.length; i++) {

        for (int j = 0; j < arr[i].length; j++) {

            System.out.print(arr[i][j] + " ");

        }

        System.out.println();

    }

    System.out.println("\n使用foreach循环:");

    for (int[] row : arr) {

        for (int num : row) {

            System.out.print(num + " ");

        }

        System.out.println();

    }

4.类与对象的区别是什么? Math类有对象吗?String类有什么属性是private的,有什么方法是public的,为什么这样设计(尝试举两例说明)?
1)类(Class):是一个模板或蓝图,定义了对象的结构和行为。
对象(Object):是类的一个具体实例,占用内存空间并存储实际数据。
2)Math类没有对象。Math 类被设计为工具类,所有方法都是static的,并且构造器是private的,因此无法创建Math对象。
3)String类的private属性:

    private final byte[] value

      private final int hash (缓存哈希值)

    private final byte coder (编码标识)

String类的public方法(部分):

public int length()

public char charAt

public String substring

4)例1:private final byte[] value(存储数据的数组被私有化)
为什么私有?
防止外部直接修改数组内容(字符串不可变的核心保障)。
如果数组是public,用户可能通过str.value[0] = 'x'修改字符串内容,破坏不可变性。
为什么提供public方法(如charAt())?
通过公共方法安全地访问数据(方法内部可做边界检查,避免数组越界)。
例如:charAt(int index) 方法会检查index是否合法,再返回对应字符。
例2:private final int hash(缓存哈希值)
为什么私有?
哈希值计算需要成本,缓存后可提高性能(如用于HashMap键值),但缓存过程对用户应该是透明的。
如果hash是public,用户可能错误地修改它,导致哈希不一致。
为什么提供public的hashCode()方法?
允许用户获取哈希值,但内部实现延迟计算(第一次调用时计算并缓存,后续直接返回缓存值)。
这样既保证了效率,又封装了实现细节。

5.将类的属性设置为public可以方便其他类访问,但为什么Java中普遍使用setter/getter模式对对象的属性进行访问呢?这与封装性又有什么关系?
1)封装性是面向对象的核心原则
封装性要求将对象的内部状态(属性)隐藏起来,只通过受控的接口进行访问。setter/getter模式正是实现封装的关键手段。
2)setter/getter模式 vs public属性的优势
a)控制访问权限(安全性)
public属性:任何类都可以直接修改属性,可能破坏对象状态的完整性。例如,设置非法值。
setter方法:可以在方法中添加验证逻辑,确保赋值合法。

    public void setAge(int age) {

        if (age < 0 || age > 150) {

        throw new IllegalArgumentException("年龄不合法");

      }

    this.age = age;

      }

b)实现只读或只写属性
如果属性是public,无法限制只读或只写。
通过只提供getter(无setter)实现只读属性:

    private String id;  // 只读属性

    public String getId() { return id; }  // 无setter

c)隐藏内部实现细节(灵活性)
内部存储方式改变时,不影响外部代码(例如,属性名或类型变化)。

  // 存储秒数

        private long timeInSeconds;

        public long getTime() { return timeInSeconds; }

d)支持延迟初始化
通过getter延迟加载资源,提高性能:

      private HeavyObject heavy;

      public HeavyObject getHeavy() {

      if (heavy == null) {

          heavy = new HeavyObject(); 

    }

      return heavy;

    }

6.对象的属性可在什么时候进行初始化?都有哪些进行初始化的办法?
1)声明时即编译时,直接初始化
2)构造器初始化或初始化块,即创建对象时
3)延迟初始化,即第一次使用时
初始化方法:
a)声明时直接初始化

        public class Person {

          private String name = "未知";          // 直接初始化

          private static int count = 0;         // 静态属性初始化

          private final int id = generateId();  // 方法调用初始化

          }   

  b)构造器初始化

        public class Person {

        private String name;

        private int age;

        public Person(String name, int age) {

            this.name = name;

            this.age = age;

        }

          }

  c)初始化块

        public class Person {

        private String name;

        private List<String> hobbies;

     

    // 实例初始化块(每次创建对象时执行)

    {

        name = "临时姓名";

        hobbies = new ArrayList<>();

        hobbies.add("阅读");

    }

    // 静态初始化块

      private static Map<String, Integer> config;

    static {

        config = new HashMap<>();

        config.put("max_age", 150);

        config.put("min_age", 0);

    }

}

d)延迟初始化

      public class Database {

      private Connection connection;

      public Connection getConnection() {

        if (connection == null) {

          connection = createConnection();

      }

          return connection;

      }

      private Connection createConnection() {

          // 创建数据库连接

          return new Connection();

      }

    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值