当前位置: 首页 > news >正文

山东大学高级程序设计期末复习

Java

不要像笔者一样,大一不好好学习考的烂,大三又在找补

  • 在Java中,% 是取余运算符:

    System.out.println(-7 % 3);  // 输出 -1
    System.out.println(7 % -3);  // 输出 1
    

    这是因为余数的符号与被除数相同。

  • 静态方法中不能有 thissuper

    静态方法属于类,而不是实例,因此不能使用 thissuper,它们只能用于实例方法:

    public static void staticMethod() {// this 和 super 在这里不可用
    }
    
  • 权限修饰符只能修饰成员变量和成员方法,不能修饰局部变量

    权限修饰符(如 public, private)只适用于类的成员,不能用于局部变量:

    public void method() {private int a = 5;  // 错误,局部变量不能有访问修饰符
    }
    
  • 接口中可以有具体的成员方法

    在Java 8之后,接口可以有默认方法和静态方法,这些方法可以有实现:

    interface MyInterface {default void defaultMethod() {System.out.println("默认方法");}
    }
    
  • Java中只允许父类引用指向子类对象

    是Java的多态机制,父类引用可以指向子类对象,但反过来不行:

    class Parent {}
    class Child extends Parent {}Parent p = new Child();  // 合法
    Child c = new Parent();  // 错误,子类不能指向父类对象
    
  • 将子类对象强转为父类对象

    编译可以通过,但父类引用只能调用父类中的方法:

    Child c = new Child();
    Parent p = (Parent) c;  // 强制转换
    
  • 将父类对象强转为子类对象时会报错

    因为父类对象没有子类的额外成员,强转会失败:

    Parent p = new Parent();
    Child c = (Child) p;  // 错误,父类对象不能转为子类对象
    
  • 静态方法的重写和多态

    静态方法属于类,而不是类的实例,因此静态方法不能像实例方法那样被重写。即使使用多态,静态方法仍然根据引用类型来调用,而不是对象的实际类型。

    class Parent {static void show() {System.out.println("Parent show()");}
    }class Child extends Parent {static void show() {System.out.println("Child show()");}
    }public class Test {public static void main(String[] args) {Parent p = new Child();p.show();  // 调用 Parent 类的 show(),静态方法是根据引用类型调用的}
    }
    

    尽管 p 引用的是 Child 类型,但因为是静态方法,实际上调用的是父类 Parentshow() 方法。

  • 静态方法可以重载

    静态方法是可以重载的,因为方法重载是基于方法签名(即方法名和参数类型)来区分的,而与是否是静态方法无关。

  • final 修饰符的使用

    • final 修饰类:表示该类不能被继承。例如,final class MyClass { }
    • final 修饰方法:表示该方法不能被重写。
    • final 修饰变量:表示该变量一旦被赋值就不能更改。

    final 不能用于修饰抽象类和接口,也不能用于修饰抽象方法。

  • 多维数组的初始化

    多维数组的第一维必须初始化,而后续维度可以不初始化,但使用时需要初始化。

    int[][][] arr = new int[3][][];
    arr[0] = new int[2][];
    arr[0][0] = new int[5];
    
  • String str = "CHASD5SH";
    int doub = str.charAt(5);
    System.out.println(doub);
    

    str.charAt(5) 获取的是字符串 str 中索引为 5 的字符。

    • str = "CHASD5SH",所以 str.charAt(5) 返回的是字符 '5'

    在 Java 中,字符是基于 Unicode 编码的。字符 '5' 的 Unicode 编码是 53

    char 类型实际上是一个整数,表示该字符的 Unicode 编码。因此,charAt(5) 返回的是字符 '5',将其赋值给 int doub,结果是字符 '5' 的整数值 53

  • String 类的 compareTo() 方法返回的值是基于字符的 Unicode 编码值的差异。返回值的具体含义取决于字符串的字典顺序。

    • 返回值为 0
      • 表示两个字符串相等
      • 比较过程会依次检查两个字符串的每一个字符,直到找到不同的字符或字符串结束。如果没有发现不同,且两个字符串的长度也相同,compareTo() 返回 0
    • 返回负数(< 0)
      • 表示当前字符串小于另一个字符串
      • 如果第一个不同的字符的 Unicode 值在当前字符串中较小,那么返回一个负数。该负数的大小是当前字符与另一个字符串对应字符的 Unicode 值之差。
    • 返回正数(> 0)
      • 表示当前字符串大于另一个字符串
      • 如果第一个不同的字符的 Unicode 值在当前字符串中较大,那么返回一个正数。该正数的大小是当前字符与另一个字符串对应字符的 Unicode 值之差。
  • 自动包装(自动装箱与拆箱)

    自动包装是指Java语言中的基本数据类型与对应的包装类之间的自动转换。例如,int 类型自动转换为 Integer 类,double 自动转换为 Double 类,等等。

    • 自动装箱(Autoboxing):将基本数据类型转换为对应的包装类对象。

      例如:

      int a = 5;
      Integer b = a;  // 自动装箱:int 转为 Integer
      
    • 自动拆箱(Unboxing):将包装类对象转换为对应的基本数据类型。

      例如:

      Integer b = 10;
      int a = b;  // 自动拆箱:Integer 转为 int
      

    Java 会自动完成这些转换,因此我们不需要显式地调用构造函数来进行转换。这个机制使得基本数据类型和包装类之间的转换更加简便。

  • 包的基本概念

    包是类的组织方式,用来组织类文件,以避免类名冲突,并可以实现访问控制。

    • 包不是文件的组织方式
      • 在 Java 中,包主要是用来组织类和接口的逻辑分组,并不直接决定文件如何存储或组织。即使类在同一个包中,它们的源文件可能位于不同的目录。
    • 包里的类相互使用
      • 同一个包中的类可以直接相互访问,通常不需要导入(import)。如果不同包之间的类需要相互访问,就需要使用 import 来导入。
    • 包的最大好处
      • 避免类名冲突:不同的包中可以有同名的类而不冲突。
      • 访问控制:包内的类可以通过访问修饰符(如 publicprivateprotected)控制访问权限。
  • 阅读下面异常处理程序,写出执行结果(6分)

    public class ExceptionDemo {public static void main(String[] args)  {ExceptionTest  et=new ExceptionTest();  et.m1();  }
    }
    class ExceptionTest{ int i;int[] a = new int[5];	void m1() {try { while (true) {m2();System.out.println(); }}catch (Exception e) {System.out.println(" m1 runs ");  }}void m2() throws Exception {try{ System.out.println(10/i);System.out.println(a[i]);}catch (ArithmeticException e) {i = 10;System.out.println("handle ArithmeticException");  }Finally {System.out.println("finally");  }System.out.println("m2 ends");}	
    }
    
    • m1 方法包含一个无限循环 while (true),每次循环都会调用 m2 方法。
    • 如果 m2 抛出异常,m1 会捕获到该异常并输出 " m1 runs "
    • m2 方法首先尝试执行 10 / i,然后尝试打印数组 a[i] 的值。
    • 如果发生 ArithmeticException(例如,i 为 0 时),就会进入 catch 块,打印 "handle ArithmeticException",并将 i 设置为 10。
    • 无论是否发生异常,finally 块都会执行,打印 "finally"
    • finally 块之后,m2 会输出 "m2 ends"

    第一次调用 m2()

    • i 默认值是 0(因为它是类的实例变量)。
    • 10 / i 会导致 ArithmeticException,因为除以 0 会抛出异常。
    • 进入 catch 块,打印 "handle ArithmeticException",并将 i 设置为 10
    • finally 块执行,打印 "finally"
    • m2 ends 被打印。

    第二次调用 m2()

    • 由于 i 已经被修改为 10,执行 10 / i 会输出 1
    • 然后尝试访问 a[i],即 a[10],但是数组 a 只有 5 个元素,因此会发生 ArrayIndexOutOfBoundsException 异常。
    • 因为 m2 中没有捕获 ArrayIndexOutOfBoundsException,这个异常会传递给 m1m1 会捕获到这个异常并输出 " m1 runs "

    m1 捕获异常

    • m2 抛出异常时,m1 捕获到异常并输出 " m1 runs ",然后退出。

    执行结果:

    handle ArithmeticException
    finally
    m2 endsm1 runs
    

    对于类的实例变量(如 int i 和 int[] a),它们会自动初始化为默认值:0 和 null。

  • Java 编译过程从源代码(.java)开始,通过编译器生成字节码(.class 文件),然后通过 JVM 来运行。

  • 错误分为语法错误、运行时错误和逻辑错误,每种错误都有不同的表现和调试方式。

  • 要运行 Java 程序,系统需要安装 JDK(用于开发)或者 JRE(仅用于运行)。

  • 在 Java 中,变量可以分为 原型类型(基本类型)和 类类型(引用类型):

    原型类型(基本类型):

    这些类型是 Java 中最基础的类型,它们直接存储值,而不涉及对象的引用。原型类型有 8 种:

    • byte:8 位,范围从 -128 到 127
    • short:16 位,范围从 -32,768 到 32,767
    • int:32 位,范围从 -2^31 到 2^31-1
    • long:64 位,范围从 -2^63 到 2^63-1
    • float:32 位单精度浮点数
    • double:64 位双精度浮点数
    • char:16 位,表示一个字符,范围从 ‘\u0000’ 到 ‘\uffff’
    • boolean:表示布尔值,只有两个值:truefalse

    类类型(引用类型):

    类类型是通过引用来指向对象的,它们可以是任何类类型(如 StringObject)以及数组。类类型的变量保存的是对象在内存中的地址,而不是对象本身。

  • 标识符的命名规则

    标识符是 Java 中用来命名变量、方法、类等的名称,命名时需要遵守一定的规则:

    1. 首字符:标识符必须以字母(A-Z,a-z)、下划线(_)或美元符号($)开头。
      • 例如:_variable$valuemyVar
    2. 后续字符:标识符可以包含字母、数字(0-9)、下划线(_)和美元符号($)。
      • 例如:my_var123value$num_1
    3. 不能使用 Java 关键字
      • Java 保留了一些关键字(如 intclassifelse 等),这些关键字不能作为标识符。
    4. 可以包含 $
      • 标识符中可以使用美元符号($),例如:$value$testVar。但是它通常用于自动生成的代码,不建议在自定义标识符中使用 $
    5. 符号“-”:不可以作为标识符的一部分。
      • 例如:my-variable 是非法的,my_variable 才是合法的。
    6. 大小写敏感
      • Java 是区分大小写的,即 myVarmyvar 是两个不同的标识符。

    Java 区分大小写:

    • Return 不是关键字,但是因为它首字母大写,可以作为变量名,虽然这不符合 Java 命名习惯。
    • return 是关键字,表示方法的返回语句,不能作为标识符使用。
  • 字符串常用方法:

    • length():返回字符串的长度(字符数)。

      String str = "Hello";
      System.out.println(str.length());  // 输出 5
      
    • toLowerCase():将字符串转换为小写字母。

      String str = "HELLO";
      System.out.println(str.toLowerCase());  // 输出 "hello"
      
    • substring():提取字符串的子字符串。可以传入起始和结束索引。

      String str = "Hello";
      System.out.println(str.substring(1, 4));  // 输出 "ell" (从索引1到3的字符)
      
    • replace():替换字符串中的字符或子字符串。

      String str = "Hello World";
      System.out.println(str.replace("World", "Java"));  // 输出 "Hello Java"
      
    • charAt():返回指定索引位置的字符。

      String str = "Hello";
      System.out.println(str.charAt(1));  // 输出 "e" (索引从 0 开始)
      
    • toCharArray():将字符串转换为字符数组。

      String str = "Hello";
      char[] chars = str.toCharArray();
      System.out.println(chars[0]);  // 输出 'H'
      
  • 比较两个字符串的长度

    在 Java 中,比较字符串长度时,需要使用 length() 方法。length() 方法返回字符串的长度(即字符的数量),而不是 equals() 方法,后者是用来比较两个字符串的内容是否相等的。

  • String 在 Java 中是 不可变的(immutable),这意味着一旦创建了一个 String 对象,它的内容不能被更改。如果你尝试修改字符串,实际上是创建了一个新的字符串对象。

  • import 语句用于引入外部类库或包中的类,以便在程序中使用它们。可以引入整个包,或者只引入某个具体的类。

    import java.util.Scanner;  // 引入 Scanner 类
    import java.util.*;         // 引入整个 java.util 包
    
  • intInteger 的转换:

    • int 转为 Integer:使用 Integer.valueOf() 或自动装箱。
    • Integer 转为 int:使用 intValue() 或自动拆箱。
  • Math 类提供了许多常用的数学方法,包括求绝对值、平方根、最大值、最小值等。

    常用方法:

    • Math.abs():返回绝对值。

      System.out.println(Math.abs(-10));  // 输出 10
      
    • Math.sqrt():返回平方根。

      System.out.println(Math.sqrt(16));  // 输出 4.0
      
    • Math.max()Math.min():返回两个数中的最大值或最小值。

      System.out.println(Math.max(5, 10));  // 输出 10
      
    • Math.pow():返回一个数的指数幂。

      System.out.println(Math.pow(2, 3));  // 输出 8.0
      
  • 静态变量(Static Variables)

    • 定义:静态变量是属于类的,而不是属于类的某个实例。无论创建多少对象,静态变量只有一个副本。
    • 初始化:静态变量由 Java 自动初始化,默认值与实例变量相同。
    • 访问方式:可以通过类名直接访问静态变量,也可以通过对象引用访问(不推荐)。
    class MyClass {static int count = 0;  // 静态变量,初始值为 0
    }public class Main {public static void main(String[] args) {System.out.println(MyClass.count);  // 通过类名访问静态变量MyClass obj1 = new MyClass();MyClass obj2 = new MyClass();System.out.println(obj1.count);  // 通过对象引用访问静态变量obj1.count = 5;  // 修改静态变量的值System.out.println(obj2.count);  // 由于静态变量是类共享的,obj2.count 会输出 5}
    }
  • 方法中的参数:传值与传址

    在 Java 中,方法的参数传递有两种方式:传值(Pass-by-Value)传址(Pass-by-Reference)

    原型类型(基本类型)参数:传值

    • 定义:原始类型(如 int, char, double 等)传递的是值的副本,即在方法调用时,实参的值会复制给形参。方法内部对形参的修改不会影响到实参。
    • 特点:Java 中基本类型变量是传值的,传递的是值的副本。

    示例:

    class Test {public static void changeValue(int x) {x = 100;  // 仅修改了 x 的副本}public static void main(String[] args) {int num = 10;changeValue(num);System.out.println(num);  // 输出 10,原始变量没有改变}
    }
    
    • 解释:在 changeValue 方法中,xnum 的副本。对 x 的修改不会影响原始的 num

    对象参数:传址

    • 定义:对于对象参数,Java 是通过传递对象的引用来实现的。这意味着方法中操作的是原始对象的引用,因此如果在方法内修改对象的字段(属性),这些更改会影响到原始对象。
    • 特点:Java 传递的是对象引用的副本,即传址。但需要注意的是,传递的引用本身是传值的,不能在方法内修改引用指向的对象。

    示例:

    class Person {String name;Person(String name) {this.name = name;}
    }class Test {public static void changeName(Person p) {p.name = "John";  // 修改了 p 对象的属性}public static void main(String[] args) {Person person = new Person("Alice");changeName(person);System.out.println(person.name);  // 输出 John,原始对象的属性被修改}
    }
    
    • 解释pperson 对象的引用副本。通过 p 修改了对象的属性,影响到了原始的 person 对象。
  • 矩阵乘法(嵌套循环)

    for (int i = 0; i < A; i++)for (int j = 0; j < B; j++)for (int k = 0; k < A; k++)C[i][j] += A[i][k] * B[k][j];
    
  • 满足条件的数筛选(如个位+十位和等于某值)

    for (int i = 1000; i <= 1800; i++) {int ge = i % 10;int shi = (i / 10) % 10;if (ge + shi == 9) System.out.println(i);
    }
  • 重载(Overloading)

    同一类中允许多个方法同名但参数不同

    参数不同指的是:数量不同、类型不同、顺序不同

    ❌ 不能仅通过返回值不同来区分重载!

  • 构造函数的访问修饰符不一定必须是 public,但一般这么写是为了让类可以被外部实例化。

  • 选择排序:

    public void selectionSort(int[] a) {for (int i = 0; i < a.length - 1; i++) {int minIndex = i;for (int j = i + 1; j < a.length; j++) {if (a[j] < a[minIndex]) {minIndex = j;}}int temp = a[i];a[i] = a[minIndex];a[minIndex] = temp;}
    }
  • 二分查找(数组需先排序):

    public int binarySearch(int[] number, int searchValue) {int low = 0, high = number.length - 1;while (low <= high) {int mid = (low + high) / 2;if (number[mid] == searchValue)return mid;else if (number[mid] < searchValue)low = mid + 1;elsehigh = mid - 1;}return -1;
    }
  • 可以写递归形式的二分查找:

    public int recursiveBinarySearch(int[] arr, int low, int high, int key) {if (low > high) return -1;int mid = (low + high) / 2;if (arr[mid] == key) return mid;else if (arr[mid] < key) return recursiveBinarySearch(arr, mid + 1, high, key);else return recursiveBinarySearch(arr, low, mid - 1, key);
    }
  • static + final 的变量是否必须赋初值?

    是的,必须。

    final static int MAX = 100; // ✅ 必须立刻赋初值
    

    因为 static final 相当于一个 常量(compile-time constant),一旦定义必须马上确定其值。

  • 抽象类与抽象方法(abstract)

    抽象方法:

    • 仅包含方法签名(如:abstract void draw();
    • 不包含方法体
    • 不能是 private、final 或 staticstatic 方法属于类本身,而不是类的实例。抽象方法需要被子类实例实现,因此不能是静态的。)

    抽象类:

    • 可以没有抽象方法

    • 如果至少包含一个抽象方法,必须被声明为 abstract

    • 可以包含非抽象方法,即普通方法、构造方法、属性等

    子类中必须实现所有抽象方法,否则该子类也必须声明为 abstract

    abstract 可以修饰:

    • 方法(不能修饰属性和构造方法)
  • 在动态绑定和多态中,Java 运行时会根据对象的实际类型来决定调用哪个方法

    示例代码:

    class Z {void f(int x) { System.out.println("Z int"); }void f(double x) { System.out.println("Z double"); }
    }class P extends Z {void f(int x) { System.out.println("P int"); }// void f(double x) { System.out.println("P double"); } // 可能有也可能没有
    }public class TestOverride {public static void main(String[] args) {P t = new P(); t.f(10);    // P intt.f(10L);   // 10L是long,会优先匹配 f(long) → 没有 → 看 f(double)t.f(2.5);   // P 或 Z 的 f(double)}
    }
    
    1. 正常情况下(P t = new P()
    • t.f(10)
      10int 类型,所以会调用 P 中的 f(int x) 方法。输出:

      P int
      
    • t.f(10L)
      10Llong 类型,会先检查 P 类中是否有 f(long x),但是没有找到该方法,所以会去查找 Z 类中的 f(double x)。由于 long 可以隐式转换为 double,会调用 Z 中的 f(double x)。输出:

      Z double
      
    • t.f(2.5)
      2.5double 类型,会首先调用 P 类中的 f(double x),如果 P 中没有这个方法,则会调用 Z 中的 f(double x)。在当前代码中,P 类没有 f(double x),所以会调用 Z 中的 f(double x)。输出:

      Z double
      
    1. 删除 P 中的 f(double x) 方法后

    如果删除 P 中的 f(double x) 方法,则调用 t.f(10L)t.f(2.5) 时都会调用 Z 中的 f(double x),因为 P 类没有匹配的方法,且 longdouble 可以互相隐式转换。

    • t.f(10L)
      long 类型会隐式转换为 double,所以调用 Z 中的 f(double x) 方法。输出:

      Z double
      
    • t.f(2.5)
      直接调用 Z 中的 f(double x) 方法。输出:

      Z double
      
    1. 修改 main 方法为 Z t = new P();

    在这种情况下,变量 tZ 类型的引用,指向一个 P 类型的对象。此时,方法调用仍然是根据实际对象的类型来决定的(即 P 类型的对象),但是 Z 类中的方法是最先被查找的。

    • t.f(10L)
      由于 t 的类型是 Z,它会首先查找 Z 类中的方法。如果 Z 中没有 f(long x),就会向上查找 f(double x)。由于 Z 中有 f(double x),它会调用 Z 中的 f(double x)。输出:

      Z double
      
    • t.f(2.5)
      同样地,t 会首先查找 Z 中的 f(double x),然后调用它。输出:

      Z double
      
    1. 如果 Z 中没有 f(double x) 方法

    如果 Z 中删除了 f(double x) 方法,且 P 中也没有 f(double x) 方法,那么编译时会报错,因为没有找到匹配的方法。

    • t.f(10L)t.f(2.5)
      如果 ZP 都没有 f(double x),则会发生编译错误,因为 Java 无法找到一个合适的方法来匹配 longdouble 类型的参数。

      编译错误:方法 f(double) 在类 Z 中不存在
      
    1. 如果 Z 中添加了 f(long x) 方法

    如果在 Z 中添加了 f(long x) 方法,则当调用 t.f(10L) 时会调用 Z 中的 f(long x) 方法。

    • t.f(10L)
      会执行 Z.f(long x),因为 long 类型会直接匹配 Z 中的 f(long x)。输出:

      Z long
      
    • t.f(2.5)
      仍然会调用 Z.f(double x),因为 double 类型直接匹配 Z 中的 f(double x) 方法。输出:

      Z double
      
  • Java支持单继承 + 多接口实现

    class Parent {}
    interface A {}
    interface B {}class Child extends Parent implements A, B {}
  • 即使 trycatchreturn 了,也会先执行 finally 再返回值

  • Java 中的异常处理有两种主要方式:

    一种是捕获并处理异常(try-catch-finally):

    这是最常见的方式,如果你知道如何处理某种异常,就在方法中使用 try-catch

    try {int a = 10 / 0; // 可能抛出 ArithmeticException
    } catch (ArithmeticException e) {System.out.println("发生算术异常:" + e.getMessage());
    } finally {System.out.println("无论是否发生异常,我都会执行");
    }
    

    另一种是声明异常并抛出(throws / throw):

    如果你不打算在当前方法中处理异常,就使用 throws 把异常交给调用者去处理(声明),或用 throw 主动抛出异常对象:

    声明异常:

    public void readFile(String path) throws IOException {FileReader reader = new FileReader(path); // 可能抛出IOException
    }
    

    主动抛出异常:

    public void checkAge(int age) {if (age < 18) {throw new IllegalArgumentException("未满18岁不允许注册");}
    }
    

    ❗注意:

    • try/catch 是处理异常:你知道这个异常可能发生,并且你有办法应对。
    • throws 是把异常交给别人处理:你暂时无能为力,就把锅甩给上层调用者。
    • 两者可以结合使用:一个方法内部 try/catch 处理部分异常,剩下的用 throws 向外声明。
  • 链表基本结构回顾

    • ListNode 类(结点类)

      public class ListNode {private String data;private ListNode link;public ListNode(String data, ListNode link) {this.data = data;this.link = link;}public String getData() { return data; }public ListNode getLink() { return link; }public void setData(String data) { this.data = data; }public void setLink(ListNode link) { this.link = link; }
      }
      
    • StringLinkedList 类中的常用方法实现

      1. length()
      public int length() {int count = 0;ListNode position = head;while (position != null) {count++;position = position.getLink();}return count;
      }
      
      1. addANodeToStart(String addData)
      public void addANodeToStart(String addData) {head = new ListNode(addData, head);
      }
      
      1. deleteHeadNode()
      public void deleteHeadNode() {if (head != null)head = head.getLink();
      }
      
      1. find(String target)
      public boolean find(String target) {ListNode position = head;while (position != null) {if (position.getData().equals(target))return true;position = position.getLink();}return false;
      }
      
      1. showList()
      public void showList() {ListNode position = head;while (position != null) {System.out.println(position.getData());position = position.getLink();}
      }
      
    • 将链表所有数据转入 ArrayList

      ArrayList<String> list = new ArrayList<>();
      ListNode position = head;
      while (position != null) {list.add(position.getData());position = position.getLink();
      }
    • 插入新节点:

      public void insertAfter(String target, String newData) {ListNode position = head;while (position != null) {if (position.getData().equals(target)) {ListNode newNode = new ListNode(newData, position.getLink());position.setLink(newNode);return;}position = position.getLink();}
      }
    • 删除目标节点后面的节点:

      public void deleteAfter(String target) {ListNode position = head;while (position != null) {if (position.getData().equals(target) && position.getLink() != null) {position.setLink(position.getLink().getLink());return;}position = position.getLink();}
      }
  • 基类方法为public int a(int x, int y); 派生类方法为public void a(int x, int y); 可以这样实现 覆盖方法吗?

    答: 不可以. 编译将报错,因为这既不是方法的重载(译文版教材: 无法根据返回值的类型来实现重载), 也不是方法的覆盖(方法名之前的修饰符要完全一致, 方法名之后的参数数目, 类型要完全一样 )

  • 若在派生类中自己编写缺省构造函数, 而在里面又只初始化了部分的实例变量, 那么另外的实例变量的值是否还被自动初始化?

    答: 现在版本的java仍将对缺省构造函数中未能涉及到的实例变量进行初始化

  • 构造函数与一般的方法有何区别?

    • 专门用来在创建对象时进行初始化.
    • 构造函数与所属的类同名.
    • 没有返回值类型, 函数头不带关键字void.
    • 构造函数不可以有继承关系.
  • 表达式“i=4; i+++i+++i++; ”的值是多少 ?

    答:结果为15; 上式可以理解为(i++)+(i++)+(i++), 即: 4+5+6=15; 因为++的优先级比+高,java编译器将首先组织优先级较高的运算符组成表达式.

2021-2022学年一学期高级程序设计课程试卷A

  • Java源文件命名时大小写不敏感

  • 一个Java程序可以编译成多个class文件

  • 抽象类不一定**非得有抽象方法,例如为了防止创建对象,也可定义成 abstract。

  • Java 不允许在类型声明中 指定数组的大小。也就是说:

    int[3] a = {1, 2, 3}; // ❌ 错误:不能写 int[3]

    • 正确的 Java 数组声明方式是:

      方式 1:自动推断长度

      int[] a = {1, 2, 3}; // 正确,长度自动为 3
      

      方式 2:使用 new 显式声明并初始化

      int[] a = new int[]{1, 2, 3}; // 正确
      

      方式 3:只声明长度,稍后赋值

      int[] a = new int[3];  // 创建长度为 3 的数组,初始值全为 0
      a[0] = 1;
      a[1] = 2;
      a[2] = 3;
      
  • 重写(override)时,返回类型必须相同(或协变返回类型)重载是通过参数列表的不同(个数、顺序或类型)来区分的返回值类型不同不能作为重载的唯一依据

  • 输出结果是什么?

    public class Test {int a[] = new int[10];public static void main ( String arg[] ) {System.out.println ( a[6] );}
    }

    正确答案:C. 会出现编译错误

    解析:

    • 变量 a 是一个非静态成员变量,但 main 方法是 static 的。

    • 在静态方法中不能直接访问非静态成员,所以 a[6] 会导致编译错误。

    • 正确写法应该是:

      public static void main(String[] args) {Test t = new Test();System.out.println(t.a[6]);  // 输出 0
      }
      
  • this 表示当前对象的引用。this 只能在 非 static 方法或构造方法中使用,不能在 static 方法中使用this 是和具体对象绑定的,不是和类绑定。

  • 关于 throws 和 throw 的说法错误的是?

    选项:

    • A. 可以使用 throw 抛出自定义异常
    • B. throw 用于方法中手动抛异常,throws 用于声明可能发生的异常
    • C. throw 抛出的异常,可以不进行处理
    • D. 用 throw 时是说明知道方法中可能有异常但不想马上处理,而希望抛出给上级处理

    原因分析:

    • A ✅ 正确:你可以定义一个异常类,比如 MyException extends Exception,然后用 throw new MyException(); 抛出。
    • B ✅ 正确:这是基本定义,没问题。
    • C ❌ 错误:如果你用 throw 抛出的是受检异常(checked exception),必须处理(try-catch)或声明(throws)。否则编译报错。这条语句是不准确的。
    • D ❌ 错误:因为:
      • throw 是用来“抛出”异常的,它不能“声明”你不想处理,而是你“现在就要抛”!
      • 如果你想表达“我不想处理这个异常,扔给别人”,那应该用的是 throws,而不是 throw

    深入解析 Java 中的 throw 和 throws:异常处理机制的核心_java throw-CSDN博客

  • public class T22A {public static void main(String\[] args) {String\[] codes = { "2021", "202b","202" };try {for (int i = 0; i <= codes.length; i++) {try {System.out.println("Analyzing: " + codes\[i]);analyzeCode(codes\[i]);} catch (NumberFormatException e) {System.out.println("Not a Number: " + codes\[i]);} finally{System.out.println("\*\*\*\*\*\*\*\*");}}} catch (Exception e) {System.out.println("Unknown Exception ");}System.out.println("Analyze Completed ");}public static void analyzeCode(String code) throws Exception {if (code.length() != 4) throw new Exception();System.out.println("successful : " + Integer.parseInt(code));}
    }

    运行结果:

    Analyzing: 2021
    successful : 2021
    ********
    Analyzing: 202b
    Not a Number: 202b
    ********
    Analyzing: 202
    Unknown Exception 
    Analyze Completed
    
    1. 第一次循环 (i=0):
      • 处理 codes[0] = "2021"
      • analyzeCode 检查长度合法(4),并成功解析为整数,输出 successful : 2021
      • finally 块执行,输出 *********
    2. 第二次循环 (i=1):
      • 处理 codes[1] = "202b"
      • analyzeCode 检查长度合法(4),但解析时遇到非数字字符 b,抛出 NumberFormatException
      • 内部 catch 捕获异常,输出 Not a Number: 202b
      • finally 块执行,输出 *********
    3. 第三次循环 (i=2):
      • 处理 codes[2] = "202"
      • analyzeCode 检查长度不合法(3),直接抛出 Exception
      • 内部 try 块没有匹配的 catch,异常传递到外层。
      • 执行 finally,输出 *********
      • 外层 catch 捕获通用 Exception,输出 Unknown Exception
    4. 循环终止与后续流程:
      • 外层异常处理后,循环提前终止(不会执行 i=3,避免数组越界)。
      • 最后输出 Analyze Completed

    第三次循环(i=2)发生了什么?

    • 处理 codes[2] = "202",调用 analyzeCode("202")
    • analyzeCode 检查 code.length() != 4,发现长度为 3,直接抛出 Exception
    • 内部 try-catch 没有捕获 Exception(它只捕获 NumberFormatException),所以异常会向外层传播。
    • 异常传播到外层的 try-catchmain 方法的 try 块),被 catch (Exception e) 捕获,输出 Unknown Exception
    • 此时,整个外层 try 块会立即终止,包括其中的 for 循环。(因为try只按顺序执行一次,外面的try执行完了,里面的for循环自然就不执行了)

山东大学 2021-2022 学年 一 学期 高级程序设计语言 课程试卷B

  • 在一个 Java 源文件中,只能有一个 public class。如果尝试在一个源文件中定义多个 public class,编译器会报错。因为 Java 的设计规范规定,每个源文件最多只能有一个公共类,并且源文件的名称必须与该公共类的名称完全相同。不过,可以在一个源文件中定义多个非 public class,这些类可以用 privateprotected 或默认的访问修饰符。

  • stu&sdu

    • 标识符中不能包含 & 这样的符号。& 用于位运算或表示参数化类型的交界类型等,不是用于组成标识符的有效字符。
  • 在 Java 中,字符串的长度是通过 length() 方法来获取的,而数组的长度是通过直接访问 length 属性(而不是方法)来获取的。例如,对于字符串 String str = "hello";,可以用 str.length() 获取长度;对于数组 int[] arr = new int[5];,则通过 arr.length 获取长度

  • 在 Java 中,二维数组的声明格式为 int a[][]int[] a[]int[][] a。正确的初始化方式是 int a[][] = { {1,2}, {3,4}, {5,6} }; 或者 int a[][] = new int[3][2]; 然后给数组元素赋值。选项 A 中的 [3][2] 放在数组名后面是不正确的,应该将 new 关键字放在数组声明前面,例如 int a[][] = new int[3][2]; 或者直接初始化为具体的值。

  • static 不能用于修饰局部变量(即在方法内部定义的变量)。

  • public class T23B {public static void main(String[] args){new B().m(2022);new A(2021).m(2020);}
    }
    class B{public B(){System.out.println("Init B: ");}public void m(int x){  f(g(x));    }public void f(int x){System.out.println("\tf() in B");g(x);}private int g(int x){System.out.println("\tg(int) in B");return x;}
    }
    class A extends B {public A(int x){System.out.println("Init A: " + x);}public void f(int x){System.out.println("\tf() in A");g(x);}public int g(int d){System.out.println("\tg(int) in A");return (int)d;}
    }

    运行结果

    Init B: g(int) in Bf() in Bg(int) in B
    Init B: 
    Init A: 2021g(int) in B//这个很奇怪f() in Ag(int) in A
    
    • new A(2021).m(2020)。这里创建了一个A类的实例,构造函数的参数是2021。A类继承自B类,所以首先要调用父类B的构造函数。B的构造函数会打印"Init B: “。然后执行A类的构造函数,打印"Init A: 2021”。接着,调用m方法,参数是2020。注意,A类并没有覆盖m方法,所以调用的是从B类继承来的m方法。在m方法中,同样执行f(g(x))。这里的x是2020,先调用g(x)。但是,在A类中是否存在g方法?因为B类的g方法是private的,A类中的g方法是public的,并且参数类型是int,所以这是一个新的方法,而不是覆盖。但是,在B类的m方法中调用g(x),由于m方法是定义在B类中的,而g是private的,所以即使是在A类中调用m方法,这里的g(x)仍然是指B类的g方法吗?或者因为A类继承了B类的m方法,而在m方法中的g(x)调用是否会被动态绑定到A类的g方法?
    • 这里可能存在理解上的误区。因为B类的g方法是private的,所以它只能在B类内部访问,子类A是无法继承或覆盖这个方法的。因此,在A类中定义的g方法是一个全新的方法,与B类的g方法无关。当在B类的m方法中调用g(x)时,由于m方法属于B类,所以这里的g(x)指的是B类的private方法,而不是A类的g方法。因此,当执行new A(2021).m(2020)时,m方法中的g(x)仍然是调用B类的g方法,而之后调用的f方法则是根据实际对象类型来决定的,因为f方法是public的,且A类覆盖了f方法。
  • 如果一个数等于它的因子之和,则称该数为“完数”(或“完全数”)。例如,28的因子为1、2、4、7、14,而 28=1+2+4+7+14,因此28是“完数”。写一个程序输出10000以内的所有完数。(8分)

    public class PerfectNumber {public static void main(String[] args) {// 输出10000以内的完数for (int i = 1; i <= 10000; i++) {if (isPerfect(i)) {System.out.println(i);}}}// 判断一个数是否为完数public static boolean isPerfect(int number) {int sumOfDivisors = 0;// 计算该数的所有真因子之和for (int i = 1; i <= number / 2; i++) {if (number % i == 0) {sumOfDivisors += i;}}// 如果真因子之和等于该数,则返回truereturn sumOfDivisors == number;}
    }
  • 输入两个小于1~1000位的超大整数,输出它们的和(注意:因为位数很大,会超过java的整数范围,直接用整数输入是不合适的)。(10分)样例: 输入: 917654321454365454445435 87654321987654321987654321 输出:87746087419799758532099756(以上两数相加)

    import java.math.BigInteger;
    import java.util.Scanner;public class LargeNumberAddition {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);// 读取两个大整数System.out.println("请输入第一个超大整数:");BigInteger num1 = new BigInteger(scanner.nextLine());  // 读取第一个数字System.out.println("请输入第二个超大整数:");BigInteger num2 = new BigInteger(scanner.nextLine());  // 读取第二个数字// 计算它们的和BigInteger sum = num1.add(num2);// 输出结果System.out.println("它们的和是:" + sum);}
    }
  • 返回链表中最大值出现的次数

    public int maxCount() {if (head == null) return 0;  // 如果链表为空,直接返回 0int count = 1;  // 初始化计数器,最大值出现次数ListNode maxNode = head;  // 设置最大节点为头节点int maxValue = maxNode.data;  // 获取头节点的数据作为初始最大值maxNode = head.link;  // 移动到下一个节点// 遍历链表while (maxNode != null) {if (maxNode.data > maxValue) {  // 如果发现更大的值maxValue = maxNode.data;  // 更新最大值count = 1;  // 重置计数器为 1,因为找到了新的最大值} else if (maxNode.data == maxValue) {  // 如果找到了相等的最大值count++;  // 计数器加 1}maxNode = maxNode.link;  // 继续遍历下一个节点}return count;  // 返回最大值出现的次数
    }
  • 返回链表倒数第 k 个节点

    public ListNode findKthToTail(int k) {// 快慢指针法ListNode fast = head;ListNode slow = head;// 移动 fast 指针,前进 k 步for (int i = 0; i < k; i++) {if (fast == null) {return null;  // 如果 k 大于链表长度,返回 null}fast = fast.link;}// 快指针和慢指针同时移动,直到快指针到达末尾while (fast != null) {fast = fast.link;slow = slow.link;}return slow;  // slow 即为倒数第 k 个节点
    }

山东大学 2023-2024 学年 一 学期 高级程序设计语言 课程试卷A

  • 方法的返回值可以是基本数据类型,也可以是对象类型,包括自定义类的实例。

  • int 是基本数据类型,不能直接调用 equals() 方法

  • continue 语句用于终止当前循环的迭代,跳过本次循环中剩余的代码,然后继续下一次循环迭代。

  • 并非程序中所有可能出现的异常都必须在 catch 块中捕获。非检查异常(如 RuntimeException 及其子类)不需要显式捕获,而检查异常需要要么被捕获,要么用 throws 声明。

  • 在方法定义中使用 throws 关键字声明可能出现的异常,即使在方法调用时异常没有实际抛出,也不会影响代码的编译。throws 声明用于告知调用者该方法可能会抛出的异常类型。

  • class Animal{public String name;public Animal(String s){name = s;        }public void move(){System.out.println(this + "在运动");}public String toString(){return  "动物" + name;}
    }
    class Bird extends Animal{public Bird(String s){super(s);      }public void move(){System.out.println(this + "在飞行");}public boolean equals(Object other){System.out.print(name + " Object 判断相等 ");return super.equals(other);}public boolean equals(Bird other){System.out.print(name + " Bird 判断相等 ");return true;}public String toString(){return  "小鸟" + name;}
    }
    public class T24A {public static void main(String\[] args){Bird\[] blist = {new Bird("麻雀"),new Bird("鸿鹄")};Animal\[] alist = {new Animal("大黄"), blist\[0],blist\[1]};for (Animal a : alist) a.move();System.out.println(alist\[0].equals(alist\[2]));;System.out.println(alist\[1].equals(blist\[0]));System.out.println(blist\[1].equals(blist\[0]));}
    }
    答案:
    动物大黄在运动
    小鸟麻雀在飞行
    小鸟鸿鹄在飞行
    false
    麻雀 Object 判断相等 true
    鸿鹄 Bird 判断相等 true
    

    编译时,根据引用类型,去对应的类中找参数列表符合的方法,锁定该方法;运行时,根据实际类型,去对应的子类中找刚刚锁定的方法的重写方法,没有就用刚刚的。

  • n个猴子围成一圈选大王,依次1-7循环报数,报到7的猴子被淘汰,直到最后一只猴子称为大王,写一个方法public static int monkeyKing(int n),返回第几只猴子会成为大王?(10分)

    public static int monkeyKing(int n) {boolean[] b=new boolean[n];for(int i=0;i<b.length;i++){b[i]=true;}int num=0;int monkeyLeft=n;int index=0;while(monkeyLeft>1){if(b[index]){num++;if(num==7){b[index]=false;monkeyLeft--;num=0;}}index++;if(index==n){index=0;}}//遍历数组,找到最后活着的那个猴子王for(int i=0;i<b.length;i++){if(b[i]){return i+1;}}return 0;}
  • public void removeRep ( ){
    // 删除链表中值重复出现的节点
    // 如假设当前链表为1->2->4->4->3->3->2->1->1,
    // 删除值重复的节点之后为1->2->4->3}
    
    public void removeRep ( ){ListNode cur=head;ListNode pre=null,next=null;while(cur!=null){pre=cur;next=cur.link;while(next!=null){if (cur.data==next.data)pre.link=next.link;elsepre=next;next=next.link;}cur=cur.link;}}
    
    public void removeRep() {Set<Integer> seen = new HashSet<>();  // 用于记录已经遇到过的值ListNode current = head;ListNode previous = null;while (current != null) {if (seen.contains(current.data)) {  // 如果值已出现过,删除节点previous.link = current.link;  // 跳过当前节点} else {seen.add(current.data);  // 记录当前节点的值previous = current;  // 继续前进}current = current.link;  // 移动到下一个节点}
    }
  • public void reversePart ( int from , int to ) {
    //在链表中把第from个节点到第to个节点这一部分进行反转,假设 0<from < to < length// 如假设目前链表为1->2->3->4->5->null,from=2,to=4// 调整结果为1->4->3->2->5
    //注意不使用额外的链表、堆栈等结构,不能填写到数组中,操作后再创建链表 
    
    public void reversePart(int from, int to) {if (from >= to) return;  // 输入参数非法(from < to)ListNode dummy = new ListNode();  // 创建一个虚拟头节点,简化边界情况的处理dummy.link = head;ListNode preFrom = dummy;  // preFrom 指向 from 前一个节点// 找到 from 的前一个节点for (int i = 0; i < from - 1; i++) {preFrom = preFrom.link;}ListNode fromNode = preFrom.link;  // 从第 from 个节点开始ListNode toNode = fromNode;for (int i = from; i < to; i++) {toNode = toNode.link;}// 保存 toNode 后面的节点ListNode nextToNode = toNode.link;// 将从 from 到 to 的部分反转ListNode prev = null;ListNode current = fromNode;while (current != nextToNode) {ListNode next = current.link;current.link = prev;prev = current;current = next;}// 将反转后的链表重新连接preFrom.link = prev;fromNode.link = nextToNode;if (from == 1) {head = prev;  // 如果反转的是从头开始的部分,更新头节点}
    }

山东大学 2023-2024 学年 一 学期 高级程序设计语言 课程试卷B

  • package 语句定义的包名必须与文件的目录结构一致。例如,如果代码中使用 package com.example;,那么该文件必须位于 com/example 目录下。

  • this 关键字只能在实例方法中使用,因为它指向当前对象。而 main 方法是静态的,静态方法是与类相关的,而不是与某个特定的对象相关,因此不能在 main 方法中使用 this

  • abstract 方法不能被声明为 static,因为 abstract 方法需要被子类实现,而 static 方法属于类本身,不能被子类重写。

  • static 方法不能直接访问非 static 属性。因为 static 方法属于类而不是对象,非 static 属性属于对象实例,static 方法无法直接访问对象实例的非静态属性。

  • Object 类定义了许多通用方法,如 toString()equals() 等。继承自 Object 的类不需要强制重写 toString()equals() 方法,但通常建议重写以提供更有用的实现。

  • 使用 final 定义的变量必须在声明时或者在构造方法中初始化

  • abstract class QAnimal{public int distance = 0;int id;static int total = 0;public QAnimal(){id = total++; }public void move(int d){        System.out.println(this + "方式:运动");}public String toString(){return  "运动距离" + distance+",";}
    }
    class QBird extends QAnimal{public void move(int d){distance = d;System.out.println(this + "方式:飞行");}public String toString(){return  id + "号小鸟" + super.toString();}
    }
    class QTurtle extends QAnimal{public void move(double s){distance += (int)s;System.out.println(this);}public String toString(){return  id + "号海龟" + super.toString();}
    }
    public class T24B {public static void main(String[] args){QBird[] blist = {new QBird(),new QBird()};QTurtle t = new QTurtle();QAnimal[] alist = {blist[0],t,blist[1]}; for (QAnimal a : alist) a.move(100);t.move(15.0);        
    }
    }
    

    答案:(打印this会调用tostring)

    0号小鸟运动距离100,方式:飞行
    2号海龟运动距离0,方式:运动
    1号小鸟运动距离100,方式:飞行
    2号海龟运动距离15,
    
    • 循环中每个元素调用move(100)。这里要注意,QAnimal的move方法参数是int,而QTurtle的move方法参数是double,所以在多态调用时,如果实际类型是QTurtle,会调用哪个方法?
    • 因为QAnimal的move是public void move(int d),而QTurtle有一个public void move(double s),这并没有覆盖父类的方法,而是重载。因此,当通过QAnimal引用调用move(100)时,由于编译时类型是QAnimal,会调用QAnimal的move方法,而不是QTurtle的move(double)。因此,当处理到alist中的t(QTurtle实例)时,实际上调用的是QAnimal中的move方法,即打印“运动方式”,而distance没有被修改,保持0。而QBird实例覆盖了move(int),所以它们的distance被设置为100,并打印飞行方式。
    • 接下来,执行t.move(15.0),这里参数是double,所以调用的是QTurtle自己的move方法,distance增加15,变成15,并调用toString打印。

山东大学 2024-2025 学年 一 学期 高级程序设计语言 课程试卷A

递归的某些读程序题关键点在于读懂方法的作用,层层递归太多的话很容易弄错,也容易弄不完

  • public staticstatic public 是等价的,但通常习惯写成 public static

  • main 方法必须声明为 public static void main,并且接受一个字符串数组作为参数。

  • 接口不继承 Object 类,但实现接口的类继承 Object 类。

  • 如果类没有重写 toString() 方法,则使用 Object 类的 toString() 方法,默认返回对象的内存地址。

  • 写一个高精度减法,方法头如下:public static String sub(String a, String b) ,其中a,b分别为被减数和减数,要求返回一个字符串表示的结果。如:sub(“123456789”,“1”) 返回为“123456788”;sub(“123456788”,“123456789”)返回为“-1”。需要注意:数字位数可能比较多。不能使用现成的如BigInteger、BigDecimal类实现(10分)

    public class HighPrecisionSubtraction {public static String sub(String a, String b) {// 判断a, b哪个大,若a < b, 结果为负数boolean isNegative = false;if (compare(a, b) < 0) {// 交换a和b,保证a >= bString temp = a;a = b;b = temp;isNegative = true;}// 将a和b填充至相同的长度int lenA = a.length();int lenB = b.length();int maxLen = Math.max(lenA, lenB);a = padLeft(a, maxLen);b = padLeft(b, maxLen);// 结果的存储,最大可能需要lenA+1位StringBuilder result = new StringBuilder();int borrow = 0;// 从低位到高位进行逐位减法for (int i = maxLen - 1; i >= 0; i--) {int digitA = a.charAt(i) - '0'; // 被减数的当前位int digitB = b.charAt(i) - '0'; // 减数的当前位// 进行减法,考虑借位int diff = digitA - digitB - borrow;if (diff < 0) {diff += 10;borrow = 1; // 发生借位} else {borrow = 0;}result.append(diff);}// 反转结果,去掉前导零result.reverse();// 去掉可能出现的前导零while (result.length() > 1 && result.charAt(0) == '0') {result.deleteCharAt(0);}// 如果结果是负数,添加负号if (isNegative) {result.insert(0, '-');}return result.toString();}// 比较两个字符串表示的数字,返回负数(a < b)、零(a == b)或正数(a > b)private static int compare(String a, String b) {// 先比较长度,长度较大的数字较大if (a.length() > b.length()) return 1;if (a.length() < b.length()) return -1;// 长度相同,逐位比较for (int i = 0; i < a.length(); i++) {if (a.charAt(i) > b.charAt(i)) return 1;if (a.charAt(i) < b.charAt(i)) return -1;}return 0; // 相等}// 将数字字符串填充至指定长度,前面补0private static String padLeft(String str, int length) {StringBuilder sb = new StringBuilder();for (int i = 0; i < length - str.length(); i++) {sb.append('0');}sb.append(str);return sb.toString();}// 测试public static void main(String[] args) {System.out.println(sub("123456789", "1")); // 123456788System.out.println(sub("123456788", "123456789")); // -1System.out.println(sub("500", "200")); // 300System.out.println(sub("1000", "999")); // 1System.out.println(sub("5000", "10000")); // -5000}
    }
  • deleteDuplicates 方法

    此方法删除链表中所有重复的节点,使得每个节点的值只出现一次。

    public ListNode deleteDuplicates(ListNode head) {if (head == null) {return null; // 如果链表为空,直接返回}ListNode current = head;while (current != null) {ListNode runner = current;// 删除当前节点之后所有重复的节点while (runner.next != null) {if (runner.next.data == current.data) {runner.next = runner.next.next; // 删除重复节点} else {runner = runner.next; // 继续查找}}current = current.next; // 继续处理下一个节点}return head;
    }
  • hasCycle 方法

    此方法判断链表中是否存在环。如果链表中存在环,返回 true;否则,返回 false

    public boolean hasCycle(ListNode head) {if (head == null || head.next == null) {return false; // 链表为空或只有一个节点,肯定没有环}ListNode slow = head;ListNode fast = head;while (fast != null && fast.next != null) {slow = slow.next; // 慢指针每次走一步fast = fast.next.next; // 快指针每次走两步if (slow == fast) {return true; // 快慢指针相遇,说明链表有环}}return false; // 快指针走到链表末尾,没有环
    }

山东大学 2024-2025 学年 一 学期 高级程序设计语言 课程试卷B

class Element {private String s;public Element(String text) { s = text; }public String toString() { return s; }
}
class B extends Element {protected final Element sub;public B(Element e) { super(""); sub = e; }public String toString() {return "<B>" + sub.toString() + "</B>";}
}
class I extends B {public I(Element e) { super(e); }public String toString() {return "<I>" + sub.toString() + "</I>";}
}
public class T24 {public static void main(String[] args) {
Element e = new Element("OK"), i = new I(e), b = new B(i);System.out.println( e +"\n" + i  +"\n"  + b  +"\n"  + new I(b));  }
}

答案:

OK
<I>OK</I>
<B><I>OK</I></B>
<I><B><I>OK</I></B></I>
  • 注意看,把前面的对象放进去了,别粗心

山东大学 2018-2019 学年 一 学期 高级程序设计语言 课程试卷

  • 在 Java 中,小数默认是 double 类型而不是 float 类型
  • “r” 是字符串(String),而 char 类型应该用单引号,如 char ca = 'r';

相关文章:

  • 【C++】从零认识C++的“继承”
  • Flink-Yarn运行模式
  • C++ 中,派生类什么时候可以不定义构造函数,什么时候必须定义构造函数?
  • Flink 核心概念解析:流数据、并行处理与状态
  • TDengine 运维—容量规划
  • leetcode 25. Reverse Nodes in k-Group
  • 鸿蒙HarmonyOS最新的组件间通信的装饰器与状态组件详解
  • SpringMVC 通过ajax 实现文件的上传
  • 关于光谱相机的灵敏度
  • naive-ui切换主题
  • 实验分享|基于千眼狼sCMOS科学相机的流式细胞仪细胞核成像实验
  • 【marked与katex结合】渲染公式
  • Vue3 Element Plus el-table-column Sortable 排序失效
  • 2025最新obs31.0.x版本编译办法,windows系统
  • 数据湖和数据仓库的区别
  • ES的倒排索引和正排索引的区别及适用场景?为什么倒排索引适合全文搜索?
  • vue3 threejs 物体发光描边
  • 电力设备制造企业数字化转型路径研究:从生产优化到生态重构
  • WordPress_Madara 本地文件包含漏洞复现(CVE-2025-4524)
  • k8s-ServiceAccount 配置
  • 福州网站建设网络公司排名/谷歌seo靠谱吗
  • 金华网络公司网站建设/企业整站seo
  • 企业信息的网站/推广关键词
  • 怎么选择顺德网站建设/武汉最新今天的消息
  • 南京做网站seo的/百度下载安装app
  • 重庆南岸营销型网站建设公司哪家专业/网站建站公司