【Java】面向对象期末复习-3.2w字全

题型

  • 判断题(5分,5道题)
  • 单项选择题(15分,9道题)
  • 填空题(5分,5空)
  • 综合题(55分,7道题,有名词解释题、简答题、读程序写结果等题型)
  • 编程题(20分,1道题)

考试范围

1 语言基础

1.1 了解Java语言的历史、Java程序的执行机制;

1.1.1 Java语言的历史:

Java是一种由Sun Microsystems(现在是Oracle Corporation)于1995年推出的编程语言。以下是Java语言的主要历史里程碑:

  1. 1991年: James Gosling、Mike Sheridan和Patrick Naughton等人在Sun Microsystems工作,开始了一个名为"Green"的项目,旨在开发一种适用于嵌入式系统的新语言。

  2. 1995年: Java正式发布。这一时期,Java的目标是创建一种能够在各种计算机平台上运行的、可移植的编程语言。

  3. 1996年: Java 1.0发布,标志着Java的广泛应用开始。

  4. 2004年: Java 5.0发布,引入了许多新特性,如泛型、枚举和注解。

  5. 2011年: Java 7发布,引入了新的语言特性和改进。

  6. 2014年: Java 8发布,引入了Lambda表达式和流API等功能。

  7. 2017年: Java 9发布,引入了模块系统等新特性。

  8. 之后: Java继续发展,每年都有新的版本发布,不断提供新的特性和改进。

1.1.2 Java程序的执行机制:

Java程序的执行过程主要包括以下步骤:

  1. 编写代码: 使用Java编程语言编写源代码文件,文件扩展名为 .java

  2. 编译代码: 使用Java编译器(javac)将源代码编译成字节码文件( .class文件),这个过程将Java源代码翻译成Java虚拟机(JVM)能够理解的中间代码。

  3. 字节码: 字节码是一种与平台无关的中间代码,它可以在任何安装了Java虚拟机的计算机上运行。

  4. 类加载: Java程序在运行时由Java虚拟机加载。类加载器将字节码文件加载到内存中,并创建一个对应的 Class对象。

  5. 运行程序: JVM执行加载的字节码。在运行时,Java虚拟机将字节码翻译成本地机器代码,这样就可以在特定的硬件上执行。

整个过程中,Java的“一次编译,到处运行”的特性使得Java程序具有高度的可移植性,因为字节码可以在任何支持Java虚拟机的平台上运行。这也是Java语言的一个重要设计目标之一。

1.2 掌握正确创建源文件、包声明、导入说明、所有形式的类声明、接口声明和实现、方法声明(包括用来开始执行一个程序的主方法)、变量声明和标识符等

1.2.1. Java源文件结构:

一个Java源文件通常包括以下几个部分:

  • 包声明(Package Declaration): 表示该文件所属的包,如果有的话,应该放在文件开头。

  • 导入说明(Import Statements): 用于引入其他包中的类,接口或静态成员。

  • 类声明(Class Declaration): Java程序中的基本组成单元,包含了字段(成员变量)和方法。

  • 接口声明(Interface Declaration): 一种抽象类型,包含了一组抽象方法。

  • 方法声明(Method Declaration): 包含了方法的名称、返回类型、参数列表和方法体。

  • 变量声明(Variable Declaration): 声明了变量的名称和类型。

  • 标识符(Identifiers): 用于命名类、方法、变量等的名称。

1.2.2. Java源文件示例:

下面是一个简单的Java源文件示例,展示了各种声明和结构:

java

// 包声明
package com.example.myapp;

// 导入说明
import java.util.ArrayList;
import java.util.List;

// 类声明
public class MyClass {
    
    // 字段声明
    private int myField;

    // 构造方法
    public MyClass(int initialValue) {
        this.myField = initialValue;
    }

    // 方法声明
    public void myMethod() {
        System.out.println("Hello from myMethod!");
    }

    // 主方法,程序入口
    public static void main(String[] args) {
        // 创建类实例
        MyClass myObject = new MyClass(42);

        // 调用方法
        myObject.myMethod();
    }
}

// 接口声明
interface MyInterface {
    // 抽象方法
    void interfaceMethod();
}

在这个例子中,我们有一个名为 MyClass的类,其中包含了字段、构造方法、普通方法,以及一个用于程序入口的主方法。此外,还有一个 MyInterface接口,包含一个抽象方法。这个文件所属的包是 com.example.myapp,并且导入了 java.util包中的一些类。

1.3 识别Java编程语言中的所有关键词并正确构建标识符

java

abstract  assert   boolean  break    byte     case
catch     char     class    const    continue default
do        double   else     enum     extends  final
finally  float    for      goto     if       implements
import    instanceof int      interface long     native
new      package  private  protected public   return
short    static   strictfp super    switch   synchronized
this     throw    throws   transient try      void
volatile while

这些关键字在Java中有特殊的含义,不能用作标识符。标识符则是由用户定义的名称,需要遵循一些规则:

  1. 标识符的命名规则:

    • 由字母、数字、下划线(_)和美元符号($)组成。
    • 必须以字母、下划线或美元符号开始。
    • 大小写敏感。
  2. Java的命名规范:

    • 类名以大写字母开头,采用驼峰命名法(例如: MyClass)。
    • 方法名以小写字母开头,采用驼峰命名法(例如: myMethod)。
    • 变量名以小写字母开头,采用驼峰命名法(例如: myVariable)。
    • 常量名通常采用全大写,用下划线分隔单词(例如: MAX_VALUE)。
示例:

java

// 合法的标识符
int myVariable;
String myString;
MyClass myObject;

// 非法的标识符,因为它是一个关键字
// 编译器将会报错
int class;

在这个例子中, class是一个关键字,因此不能用作标识符。如果尝试使用关键字作为标识符,编译器会产生错误。标识符的正确使用是编写清晰、易读的Java代码的关键之一。

1.4 掌握变量的初始化,字符串类型变量和数组类型变量特殊的初始化方式;

1.4.1. 基本数据类型变量的初始化:

基本数据类型包括整型、浮点型、字符型和布尔型。这些类型的变量可以通过直接赋值来初始化。

java

// 整型初始化
int intValue = 42;

// 浮点型初始化
double doubleValue = 3.14;

// 字符型初始化
char charValue = 'A';

// 布尔型初始化
boolean boolValue = true;
1.4.2. 引用数据类型变量的初始化:

引用数据类型包括类、接口、数组等。引用类型的变量初始化时,需要使用 new关键字来创建对象。

java

// 类初始化
MyClass myObject = new MyClass();

// 接口初始化(通常是实现接口的类的实例)
MyInterface myInterfaceObject = new MyInterfaceImpl();

// 数组初始化
int[] intArray = new int[5];
String[] stringArray = new String[]{"apple", "banana", "orange"};
1.4.3. 字符串类型变量的初始化:

字符串是Java中的引用数据类型,但它有一种特殊的初始化方式——使用双引号。

java

// 字符串初始化
String myString = "Hello, World!";

Java中的字符串是不可变的,一旦创建,就不能修改。字符串初始化后,可以使用各种字符串操作方法。

1.4.4. 数组类型变量的初始化:

数组是一种引用数据类型,可以通过 new关键字来初始化。

java

// 数组初始化
int[] intArray = new int[5];  // 初始化一个包含5个整数的数组
double[] doubleArray = {1.0, 2.0, 3.0};  // 初始化一个包含3个双精度浮点数的数组

在数组的初始化中,可以指定数组的长度,并且可以使用花括号直接给数组元素赋值。

注意: 对于数组类型,一旦确定了数组长度,就不能再改变。如果需要动态大小的集合,可以使用 ArrayList等集合类。

1.5 了解所有基本数据类型的取值范围、掌握基本数据类型的常量表示形式

1.5.1. 整型(int、long、short、byte):
  • int: 32位,范围为 -2^312^31 - 1

    java

    int myInt = 42;
    
  • long: 64位,范围为 -2^632^63 - 1

    java

    long myLong = 123456789L; // 注意:long类型的常量要加上"L"或"l"
    
  • short: 16位,范围为 -2^152^15 - 1

    java

    short myShort = 100;
    
  • byte: 8位,范围为 -2^72^7 - 1

    java

    byte myByte = 127;
    
1.5.2. 浮点型(float、double):
  • float: 32位,范围为 IEEE 754 规定的大约 -3.4e383.4e38

    java

    float myFloat = 3.14f; // 注意:float类型的常量要加上"F"或"f"
    
  • double: 64位,范围为 IEEE 754 规定的大约 -1.7e3081.7e308

    java

    double myDouble = 3.14;
    
1.5.3. 字符型(char):
  • char: 16位,表示Unicode字符,范围为 02^16 - 1

    java

    char myChar = 'A';
    
1.5.4. 布尔型(boolean):
  • boolean: 只有两个值, truefalse

    java

    boolean myBoolean = true;
    

在Java中,可以使用常量来表示特定的数值,例如 final 关键字定义的常量:

java

final int MAX_VALUE = 100;
final double PI = 3.14159;

这样的常量在程序中不能被修改,提高了代码的可读性和维护性。注意,约定上,常量的命名通常采用全大写,用下划线分隔单词。例如, MAX_VALUEPI

1.6 掌握String、StringBuilder类型的基本使用

在Java中, StringStringBuilder 是用于处理字符串的两个常用类,它们有不同的特性和适用场景。

1.6.1. String 类:

String 是不可变的类,一旦创建,它的值不能被更改。这意味着每次对字符串进行操作时,实际上都会创建一个新的字符串对象。

常见的 String 操作:

java

String str1 = "Hello";
String str2 = "World";

// 字符串拼接
String result = str1 + " " + str2; // "Hello World"

// 获取字符串长度
int length = result.length(); // 11

// 检查字符串是否为空
boolean isEmpty = result.isEmpty(); // false

// 获取子串
String subString = result.substring(6); // "World"

// 字符串比较
boolean isEqual = str1.equals(str2); // false

####1.6. 2. StringBuilder 类:

StringBuilder 是可变的类,它允许对字符串进行修改而不创建新的对象。因此,在大量字符串拼接或修改的情况下,使用 StringBuilder 效率更高。

常见的 StringBuilder 操作:

java

StringBuilder stringBuilder = new StringBuilder("Hello");

// 字符串拼接
stringBuilder.append(" World"); // "Hello World"

// 获取字符串长度
int length = stringBuilder.length(); // 11

// 插入字符串
stringBuilder.insert(5, ","); // "Hello, World"

// 删除子串
stringBuilder.delete(5, 6); // "Hello World"

// 反转字符串
stringBuilder.reverse(); // "dlroW olleH"

// 转换为 String 类型
String result = stringBuilder.toString();
总结:
  • 使用 String 当字符串内容不经常改变时,或者作为常量使用。
  • 使用 StringBuilder 当需要频繁对字符串进行修改时,以提高效率。

总的来说, String 适用于不经常变更的字符串,而 StringBuilder 适用于需要经常修改的字符串。选择合适的类取决于特定情况下的需求和性能考虑。

2 运算符和赋值

2.1 掌握运算符

在Java中,运算符是用来执行各种操作的特殊符号。运算符用于在表达式中执行算术、关系、逻辑等操作。以下是Java中常见的运算符:

2.1.1. 算术运算符:

java

int a = 5, b = 2;

int sum = a + b; // 加法
int difference = a - b; // 减法
int product = a * b; // 乘法
int quotient = a / b; // 除法
int remainder = a % b; // 求余
2.1.2. 关系运算符:

java

int x = 5, y = 8;

boolean isEqual = (x == y); // 等于
boolean notEqual = (x != y); // 不等于
boolean greaterThan = (x > y); // 大于
boolean lessThan = (x < y); // 小于
boolean greaterOrEqual = (x >= y); // 大于等于
boolean lessOrEqual = (x <= y); // 小于等于
2.1.3. 逻辑运算符:

java

boolean p = true, q = false;

boolean andResult = p && q; // 与
boolean orResult = p || q; // 或
boolean notResult = !p; // 非
2.1.4. 赋值运算符:

java

int a = 5;
a += 3; // 等效于 a = a + 3;
2.1.5. 自增和自减运算符:

java

int x = 5;

x++; // 自增,等效于 x = x + 1;
x--; // 自减,等效于 x = x - 1;
2.1.6. 位运算符:

java

int a = 5, b = 3;

int andResult = a & b; // 按位与
int orResult = a | b; // 按位或
int xorResult = a ^ b; // 按位异或
int leftShiftResult = a << 1; // 左移一位
int rightShiftResult = a >> 1; // 右移一位
2.1.7. 三元运算符:

java

int x = 5, y = 8;

int result = (x > y) ? x : y; // 如果 x 大于 y,则结果为 x,否则结果为 y

2.2 在包含运算符的表达式中,确定操作数的值、并确定表达式的值是什么(包括值和类型)

在Java中,表达式的值和类型是由运算符和操作数的值以及类型共同决定的。不同类型的操作数在表达式中的组合可能导致类型转换和规则的应用。以下是一些常见的运算符和它们在表达式中的行为:

2.2.1. 算术运算符:

java

int a = 5, b = 2;

int sum = a + b; // 7,整数相加
int difference = a - b; // 3,整数相减
int product = a * b; // 10,整数相乘
int quotient = a / b; // 2,整数相除,结果为整数
int remainder = a % b; // 1,取余

2.2.2. 关系运算符:

java

int x = 5, y = 8;

boolean isEqual = (x == y); // false,判断是否相等
boolean notEqual = (x != y); // true,判断是否不相等
boolean greaterThan = (x > y); // false,判断是否大于
boolean lessThan = (x < y); // true,判断是否小于
boolean greaterOrEqual = (x >= y); // false,判断是否大于等于
boolean lessOrEqual = (x <= y); // true,判断是否小于等于
2.2.3. 逻辑运算符:

java

boolean p = true, q = false;

boolean andResult = p && q; // false,逻辑与
boolean orResult = p || q; // true,逻辑或
boolean notResult = !p; // false,逻辑非
2.2.4. 赋值运算符:

java

int a = 5;
a += 3; // 8,相当于 a = a + 3;
2.2.5. 自增和自减运算符:

java

int x = 5;

x++; // 6,自增
x--; // 5,自减
2.2.6. 字符串连接:

java

String str1 = "Hello";
String str2 = " World";

String result = str1 + str2; // "Hello World",字符串连接
2.2.7. 类型转换:

在表达式中,如果操作数的类型不同,Java会进行类型转换。有两种类型转换:

  • 隐式类型转换: 小范围类型自动转换为大范围类型,例如,整数转换为浮点数。

java

int intValue = 42;
double doubleValue = intValue; // 隐式类型转换,int转换为double
  • 显式类型转换: 大范围类型转换为小范围类型,需要使用强制类型转换。

java

double doubleValue = 3.14;
int intValue = (int) doubleValue; // 显式类型转换,double转换为int

2.3 掌握函数参数的传递方式

在Java中,函数参数的传递方式有两种:值传递和引用传递。理解这两种传递方式对于编写Java程序是很重要的。

2.3.1. 值传递(Pass by Value):

在值传递中,实际参数(调用函数时传递的参数)的值被复制给形式参数(函数定义中声明的参数)。这意味着函数内对形式参数的修改不会影响实际参数的值。

java

public class PassByValueExample {
    public static void main(String[] args) {
        int x = 10;
        System.out.println("Before: " + x);
        modifyValue(x);
        System.out.println("After: " + x);
    }

    public static void modifyValue(int num) {
        num = 20; // 修改形式参数的值
    }
}

上述代码输出结果为:

makefile

Before: 10
After: 10

在这个例子中, modifyValue 方法接收一个值为 10 的参数 num,然后修改了 num 的值为 20。但是,这个修改并不影响 main 方法中的变量 x 的值。

2.3.2. 引用传递(Pass by Reference):

在Java中,虽然对象引用是通过值传递的,但是通过引用传递对象引用,实际上是传递了对象引用的副本。这样,在函数内对对象引用所指向的对象的修改会影响到实际参数。

java

public class PassByReferenceExample {
    public static void main(String[] args) {
        StringBuilder myStringBuilder = new StringBuilder("Hello");
        System.out.println("Before: " + myStringBuilder);
        modifyReference(myStringBuilder);
        System.out.println("After: " + myStringBuilder);
    }

    public static void modifyReference(StringBuilder strBuilder) {
        strBuilder.append(", World!"); // 修改对象引用所指向的对象
    }
}

上述代码输出结果为:

makefile

Before: Hello
After: Hello, World!

在这个例子中, modifyReference 方法接收了一个 StringBuilder 对象引用,然后在对象上进行了修改。这个修改反映在 main 方法中的 myStringBuilder 对象上。

总体来说,Java中的参数传递是值传递,但对于引用类型的参数,传递的是对象引用的副本,因此在函数内对对象引用所指向的对象的修改会影响到实际参数。

3 流控制和异常处理

3.1 if、switch语句

在Java中, ifswitch 是用于控制程序流程的条件语句。它们允许根据条件的满足与否来执行不同的代码块。

3.1.1. if 语句:

if 语句用于根据一个条件执行不同的代码块。语法如下:

java

if (condition) {
    // 如果条件为真,执行这里的代码块
} else if (anotherCondition) {
    // 如果上面的条件为假,且这个条件为真,执行这里的代码块
} else {
    // 如果上面的所有条件都为假,执行这里的代码块
}

示例:

java

int x = 10;

if (x > 0) {
    System.out.println("x is positive");
} else if (x < 0) {
    System.out.println("x is negative");
} else {
    System.out.println("x is zero");
}
3.1.2. switch 语句:

switch 语句用于根据表达式的值执行不同的代码块。它可以替代多个嵌套的 if-else 语句,提高代码的可读性。语法如下:

java

switch (expression) {
    case value1:
        // 如果 expression 的值等于 value1,执行这里的代码块
        break;
    case value2:
        // 如果 expression 的值等于 value2,执行这里的代码块
        break;
    // 可以有更多的 case
    default:
        // 如果 expression 的值不等于任何一个 case,执行这里的代码块
}

示例:

java

int dayOfWeek = 3;

switch (dayOfWeek) {
    case 1:
        System.out.println("Monday");
        break;
    case 2:
        System.out.println("Tuesday");
        break;
    case 3:
        System.out.println("Wednesday");
        break;
    // 可以有更多的 case
    default:
        System.out.println("Unknown day");
}

注意:

  • switch 语句中,每个 case 后面要有 break; 语句,以防止从当前 case 开始,一直执行到下一个 case
  • 如果没有匹配到任何 case,可以使用 default 分支执行默认的代码块。 default 分支是可选的。

选择使用 if 还是 switch 取决于具体的情况和代码的可读性。通常来说,当条件是一个具体的值时,使用 switch 更为清晰。而当条件是一个范围或更复杂的逻辑时,使用 if-else 更为灵活。

3.2 使用各种形式的循环语句编写代码,包括带标签的和不带标签的break和continue语句使用

在Java中,有三种主要的循环语句: forwhiledo-while。此外,Java还支持带标签的 breakcontinue语句,用于在嵌套循环中更灵活地控制循环的流程。下面是一些使用各种形式的循环语句和带标签的 breakcontinue语句的例子:

3.2.1. for 循环:

java

// 基本的 for 循环
for (int i = 0; i < 5; i++) {
    System.out.println(i);
}

// 增强型 for 循环(适用于数组和集合)
int[] numbers = {1, 2, 3, 4, 5};
for (int number : numbers) {
    System.out.println(number);
}
3.2.2. while 循环:

java

int i = 0;
while (i < 5) {
    System.out.println(i);
    i++;
}
3.2.3. do-while 循环:

java

int j = 0;
do {
    System.out.println(j);
    j++;
} while (j < 5);
3.2.4. 带标签的 break 和 continue:

java

outerLoop: // 标签
for (int i = 0; i < 5; i++) {
    innerLoop:
    for (int j = 0; j < 5; j++) {
        if (i == 2 && j == 2) {
            // 使用带标签的 break 跳出外层循环
            break outerLoop;
        }
        if (i == 1 && j == 1) {
            // 使用带标签的 continue 跳过本次内层循环,继续下一次外层循环
            continue outerLoop;
        }
        System.out.println(i + ", " + j);
    }
}

带标签的 breakcontinue 允许你在多层嵌套的循环中选择性地中断或继续特定的循环。

使用循环语句和带标签的 breakcontinue 可以使代码更具灵活性,适应不同的逻辑和需求。

3.3 正确编写使用异常和异常处理子句(try、catch、finally)的代码,以及编写带有异常处理的方法声明

在Java中,异常是一种在程序执行期间发生的不正常情况。异常处理是通过使用 trycatchfinally 关键字来捕获和处理异常的过程。以下是一些正确编写使用异常处理的代码的示例:

3.3.1. 使用 trycatchfinally 处理异常:

java

public class ExceptionHandlingExample {
    public static void main(String[] args) {
        try {
            // 可能引发异常的代码
            int result = divide(10, 0);
            System.out.println("Result: " + result);
        } catch (ArithmeticException ex) {
            // 处理特定类型的异常
            System.out.println("Error: " + ex.getMessage());
        } finally {
            // 无论是否发生异常,都会执行的代码块
            System.out.println("Finally block executed.");
        }
    }

    public static int divide(int num1, int num2) {
        // 可能引发异常的方法
        return num1 / num2;
    }
}

在这个例子中, divide 方法可能引发 ArithmeticException 异常(除零异常)。在 main 方法中,我们使用 try 块来包裹可能引发异常的代码,然后使用 catch 块来捕获并处理特定类型的异常。无论是否发生异常, finally 块中的代码都会执行。

3.3.2. 编写带有异常处理的方法声明:

java

public class MethodWithExceptionExample {
    public static void main(String[] args) {
        try {
            // 调用可能引发异常的方法
            performOperation(5);
        } catch (CustomException ex) {
            // 处理自定义异常
            System.out.println("Custom Exception: " + ex.getMessage());
        }
    }

    public static void performOperation(int value) throws CustomException {
        if (value < 0) {
            // 抛出自定义异常
            throw new CustomException("Negative value is not allowed.");
        } else {
            System.out.println("Operation performed successfully.");
        }
    }
}

// 自定义异常类
class CustomException extends Exception {
    public CustomException(String message) {
        super(message);
    }
}

在这个例子中, performOperation 方法声明了可能抛出 CustomException 异常。在调用该方法时,调用者必须使用 try- catch 块处理这个异常或者将异常声明抛给上层调用者。

通过合理使用异常处理机制,可以使代码更具鲁棒性,能够更好地应对不可预测的运行时问题。

3.4 掌握Java的异常类型的组织结构

在Java中,异常(Exception)是指程序在执行过程中发生的不正常情况。Java异常的组织结构是由 Throwable 类派生而来的。 Throwable 又分为两种主要的类型: ErrorException

3.4.1. Throwable 类:

Throwable 是所有异常类的根类。它有两个主要的子类: ErrorException

3.4.1.1 Error 类:

Error 类用于表示Java虚拟机无法处理的错误。通常,这些错误是严重的,程序无法恢复,因此不建议捕获 Error 类的实例。

3.4.1.2 Exception 类:

Exception 类是所有异常的父类,它分为两种:可检查异常(Checked Exception)和不可检查异常(Unchecked Exception)。

  • 可检查异常(Checked Exception): 是指在编译时必须进行处理的异常,否则程序无法通过编译。它是 Exception 类及其子类(除了 RuntimeException 及其子类)的实例。

  • 不可检查异常(Unchecked Exception): 是指在编译时可以不进行处理的异常。它是 RuntimeException 及其子类的实例。

3.4.2. Exception 类的常见子类:
3.4.2.1 RuntimeException 及其子类:

RuntimeException 及其子类是不可检查异常,程序在运行时可能抛出。常见的 RuntimeException 及其子类有:

  • ArithmeticException:算术异常,例如除零操作。
  • NullPointerException:空指针异常,当访问一个空对象的成员时抛出。
  • ArrayIndexOutOfBoundsException:数组下标越界异常。
  • IllegalArgumentException:非法参数异常,当传递给方法的参数不合法时抛出。
  • ClassCastException:类型转换异常,当进行对象类型转换时发生错误。
  • 等等…
3.4.2.2 IOException 及其子类:

IOException 及其子类是可检查异常,通常与输入输出操作相关。常见的 IOException 及其子类有:

  • FileNotFoundException:文件未找到异常。
  • IOException:输入输出异常的通用类。
  • EOFException:文件末尾异常,当尝试读取文件末尾时抛出。
  • SocketException:套接字异常,与网络编程相关。
  • 等等…
3.4.2.3 其他常见异常:
  • NullPointerException:空指针异常,访问空对象的成员。
  • ArrayIndexOutOfBoundsException:数组下标越界异常。
  • NumberFormatException:数字格式异常,当字符串转换为数字时发生错误。
  • RuntimeException:运行时异常的通用类。
  • Exception:所有异常的通用父类。
3.4.3. 自定义异常:

在需要处理特定业务逻辑的异常情况时,可以通过创建自定义异常类来满足特定需求。

java

public class CustomException extends Exception {
    public CustomException(String message) {
        super(message);
    }
}

自定义异常通常继承自 Exception 类或其子类,可以提供特定的构造函数和方法,以适应程序的需求。

理解Java异常的组织结构有助于有效地处理程序中可能出现的异常情况,并提高代码的可靠性和可维护性。

3.5 掌握自定义异常类类型

在Java中,自定义异常类是通过继承 Exception 或其子类来创建的。通过自定义异常类,你可以更好地适应你的应用程序的需求,使异常处理更具有可读性和灵活性。以下是如何定义和使用自定义异常类的基本步骤:

3.5.1. 创建自定义异常类:

java

public class CustomException extends Exception {
    // 构造函数
    public CustomException() {
        super("This is a custom exception.");
    }

    // 可以提供带有详细信息的构造函数
    public CustomException(String message) {
        super(message);
    }
}

在上面的例子中, CustomException 继承自 Exception 类。可以提供多个构造函数,其中一个包含异常信息,另一个使用默认的异常信息。

3.5.2. 在程序中使用自定义异常:

java

public class CustomExceptionExample {
    public static void main(String[] args) {
        try {
            performOperation(-5);
        } catch (CustomException ex) {
            System.out.println("Custom Exception Caught: " + ex.getMessage());
        }
    }

    // 示例方法,可能引发自定义异常
    public static void performOperation(int value) throws CustomException {
        if (value < 0) {
            throw new CustomException("Negative value is not allowed.");
        } else {
            System.out.println("Operation performed successfully.");
        }
    }
}

在上面的例子中, performOperation 方法可能抛出 CustomException。在调用该方法时,我们使用 try-catch 块来捕获并处理 CustomException

3.5.3. 捕获多个异常:

java

public class MultipleExceptionsExample {
    public static void main(String[] args) {
        try {
            performOperation(-5);
        } catch (CustomException ex) {
            System.out.println("Custom Exception Caught: " + ex.getMessage());
        } catch (AnotherCustomException ex) {
            System.out.println("Another Custom Exception Caught: " + ex.getMessage());
        }
    }

    public static void performOperation(int value) throws CustomException, AnotherCustomException {
        if (value < 0) {
            throw new CustomException("Negative value is not allowed.");
        } else if (value == 0) {
            throw new AnotherCustomException("Zero is not allowed.");
        } else {
            System.out.println("Operation performed successfully.");
        }
    }
}

// 另一个自定义异常类
class AnotherCustomException extends Exception {
    public AnotherCustomException(String message) {
        super(message);
    }
}

在上述例子中, performOperation 方法可能抛出两种不同的自定义异常。在捕获异常时,可以使用多个 catch 块分别处理不同类型的异常。

自定义异常类的使用可以提高代码的可读性,并使异常处理更具灵活性。确保自定义异常类能够清晰地传达有关异常的信息,有助于程序员更容易理解和维护代码。

4 类类型和接口类型

4.1 定义类、实例方法、实例变量、类方法、类变量,使用恰当的、语法允许的修饰符(比如public、final、static、abstract等等),能够清楚准确阐述单独修饰符和联合

当我们在Java中定义类时,可以使用各种修饰符来指定类的性质和访问级别。以下是关于类、实例方法、实例变量、类方法和类变量的一些基本概念以及可能使用的修饰符:

  1. 类定义

    java

    [修饰符] class ClassName {
        // 类体
    }
    
    • 修饰符:可以是 public、abstract、final 等。
  2. 实例方法

    java

    [修饰符] returnType methodName([参数列表]) {
        // 方法体
    }
    
    • 修饰符:可以是 public、private、protected、static、final 等。
    • 返回类型(returnType):方法返回的数据类型。
    • 方法名(methodName):方法的名称。
    • 参数列表:方法的输入参数。
  3. 实例变量

    java

    [修饰符] dataType variableName;
    
    • 修饰符:可以是 public、private、protected、static、final 等。
    • 数据类型(dataType):变量的数据类型。
    • 变量名(variableName):变量的名称。
  4. 类方法(静态方法)

    java

    [修饰符] static returnType methodName([参数列表]) {
        // 方法体
    }
    
    • 修饰符:可以是 public、private、protected、static、final 等。
    • 返回类型(returnType):方法返回的数据类型。
    • 方法名(methodName):方法的名称。
    • 参数列表:方法的输入参数。
  5. 类变量(静态变量)

    java

    [修饰符] static dataType variableName;
    
    • 修饰符:可以是 public、private、protected、static、final 等。
    • 数据类型(dataType):变量的数据类型。
    • 变量名(variableName):变量的名称。

修饰符的组合可以根据需要使用,例如:

  • 如果你希望一个类的实例变量对所有类都可见,可以使用 public 修饰符。
  • 如果你希望一个方法可以在没有创建对象的情况下被调用,可以使用 static 修饰符。
  • 如果你希望一个变量的值在被创建对象后不能被修改,可以使用 final 修饰符等。

需要注意的是,这些修饰符的使用要根据具体的需求和设计原则,灵活运用以达到合适的封装和访问控制。

4.2 修饰符的意义,以及在修饰符限定的声明条款上建立的包之间的关系所带来的影响

修饰符在Java中用于限定类、方法、变量等声明的属性和行为。它们控制了对这些声明的访问级别、性质和行为。以下是一些常见的修饰符及其意义:

  1. public: 可以被任何其他类访问。是最广泛的访问级别。
  2. private: 只能在声明它的类内部访问。用于实现封装。
  3. protected: 对于同一包内的类和所有子类可见。用于实现继承。
  4. default (package-private): 在同一包内可见,不使用任何修饰符。不同包的类无法访问。

对于方法,还有一些其他的修饰符:

  1. static: 静态方法或变量,属于类而不是实例。可以通过类名直接调用,而不需要实例化对象。
  2. final: 表示最终的、不可修改的。对于类,表示该类不能被继承;对于方法,表示方法不能被重写;对于变量,表示变量是一个常量。
  3. abstract: 用于声明抽象类或抽象方法。抽象类不能被实例化,而抽象方法必须在子类中被实现。

修饰符的选择影响了代码的可维护性、安全性和可读性。合适的修饰符能够提供适当的封装,防止未经授权的访问,同时也使得代码更容易理解和维护。

在建立包之间的关系方面,修饰符的选择直接影响了不同包中的类、方法和变量是否可以被访问。例如:

  • 如果一个类使用了 public 修饰符,那么它可以被其他包中的类直接访问。
  • 如果一个类使用了 default 修饰符,那么它只能被同一包中的类访问。
  • 如果一个变量使用了 private 修饰符,那么它只能在声明它的类内部访问。

在设计Java应用程序时,良好的包管理和修饰符的使用能够提高代码的模块化性、安全性和可维护性。开发者应该根据项目的需求和设计目标,选择合适的修饰符以及包的组织结构。

4.3 掌握访问控制修饰符

在Java中,访问控制修饰符用于控制类、方法、变量等成员的可见性和访问权限。Java提供了四种访问控制修饰符:

  1. public:

    • 意义: 公共访问级别,对所有类可见。
    • 应用场景: 适用于希望成员能够被所有类访问的情况。
  2. private:

    • 意义: 私有访问级别,只在声明它的类内部可见。
    • 应用场景: 适用于实现封装,限制对类的内部细节的直接访问,提高安全性。
  3. protected:

    • 意义: 受保护访问级别,对同一包内的类和所有子类可见。
    • 应用场景: 适用于希望在继承层次结构中提供对成员的访问权限,但不希望公开给所有类。
  4. default (package-private):

    • 意义: 包(包括子包)内可见,不使用任何修饰符时默认的访问级别。
    • 应用场景: 适用于将成员限制在同一包内,不对外公开。
如何掌握访问控制修饰符:
  1. 理解各个修饰符的含义:

    • 了解 public 表示公共访问,对所有类可见。
    • 了解 private 表示私有访问,只在声明类内部可见。
    • 了解 protected 表示受保护访问,对同一包内的类和所有子类可见。
    • 了解默认(无修饰符)表示包内可见。
  2. 应用场景:

    • 理解在不同场景下选择不同的访问修饰符的原因。
    • 了解如何使用修饰符来实现封装、继承和模块化。
  3. 掌握修饰符的组合:

    • 熟悉在类、方法、变量上可以组合使用多个修饰符,如 public staticprivate final等。
  4. 实践和案例分析:

    • 编写一些简单的类和方法,尝试使用不同的访问修饰符,理解其影响。
    • 分析现有的Java库和框架中的类和方法,看看它们是如何使用访问修饰符的。
  5. 深入理解面向对象概念:

    • 理解访问修饰符与面向对象编程中的封装、继承和多态的关系。

掌握访问控制修饰符是Java程序设计中的基础之一,有助于编写清晰、安全和易维护的代码。通过实际练习和深入理解,可以更好地运用这些概念来设计和构建Java应用程序。

4.4 掌握构造方法的创建,包括无参构造

构造方法是一种特殊类型的方法,用于在创建对象时执行必要的初始化操作。在Java中,构造方法的名称与类名相同,没有返回类型,并且可以有不同的参数列表。其中,无参构造方法是一种没有参数的构造方法。

4.4.1 创建有参构造方法:

有参构造方法通常用于接受参数,并根据这些参数对对象进行初始化。以下是创建有参构造方法的基本步骤:

java

public class MyClass {
    // 实例变量
    private int myField;

    // 有参构造方法
    public MyClass(int initialValue) {
        this.myField = initialValue;
    }

    // 其他方法和代码...
}

在这个例子中, MyClass 类有一个私有的实例变量 myField,并且有一个有参构造方法,该构造方法接受一个整数参数 initialValue,并将其赋值给实例变量 myField

4.4.2 创建无参构造方法:

如果没有为类显式定义构造方法,Java会提供一个默认的无参构造方法。但是,如果你显式定义了任何构造方法(无论是有参还是无参),默认的无参构造方法就不再被提供。因此,如果你需要无参构造方法,你需要显式定义它。

以下是创建无参构造方法的示例:

java

public class MyClass {
    // 实例变量
    private int myField;

    // 有参构造方法
    public MyClass(int initialValue) {
        this.myField = initialValue;
    }

    // 无参构造方法
    public MyClass() {
        // 可以在这里进行默认初始化,或者留空
    }

    // 其他方法和代码...
}

在这个例子中, MyClass 类有一个有参构造方法和一个无参构造方法。无参构造方法可以执行默认的初始化操作,或者留空,具体取决于你的需求。

通过提供不同的构造方法,你可以根据实际情况选择性地初始化对象的属性。这使得你的类更加灵活,能够适应不同的使用场景。

4.5 掌握this关键字、super关键字的使用

在Java中, thissuper 是两个关键字,用于在类的方法中引用当前对象或其父类对象。

4.5.1. this 关键字:
  • 用途: this 用于引用当前对象,即正在执行代码的对象。它可以用于解决实例变量和局部变量之间的命名冲突,也可以在构造方法中调用另一个构造方法。
a. 解决变量命名冲突:

java

public class MyClass {
    private int myField;

    public void setMyField(int myField) {
        this.myField = myField; // 使用this关键字区分实例变量和方法参数
    }
}
b. 在构造方法中调用另一个构造方法:

java

public class MyClass {
    private int myField;

    // 有参构造方法
    public MyClass(int myField) {
        this.myField = myField;
    }

    // 无参构造方法,调用有参构造方法进行初始化
    public MyClass() {
        this(0); // 调用有参构造方法
    }
}
4.5.2. super 关键字:
  • 用途: super 用于引用父类的对象或调用父类的方法。它主要用于解决子类和父类之间的成员变量或方法的命名冲突,以及在子类构造方法中调用父类的构造方法。
a. 调用父类方法:

java

public class ParentClass {
    public void display() {
        System.out.println("This is from the parent class.");
    }
}

public class ChildClass extends ParentClass {
    public void display() {
        super.display(); // 调用父类的display方法
        System.out.println("This is from the child class.");
    }
}
b. 在子类构造方法中调用父类构造方法:

java

public class ParentClass {
    private int parentField;

    public ParentClass(int parentField) {
        this.parentField = parentField;
    }
}

public class ChildClass extends ParentClass {
    private int childField;

    public ChildClass(int parentField, int childField) {
        super(parentField); // 调用父类的构造方法
        this.childField = childField;
    }
}

这样, superthis 关键字提供了一种清晰的方式来引用对象,解决了变量和方法的命名冲突,并且在构造方法中使用它们可以确保正确地初始化对象的状态。

4.6 定义类的继承,子类和父类之间的关系,数据成员的继承(可能包含隐藏),实例方法的继承(可能包含了覆盖)

继承是面向对象编程中的一个重要概念,它允许一个类(子类)继承另一个类(父类)的属性和方法。这建立了子类和父类之间的关系,使得代码的重用性和可维护性增强。

4.6.1. 定义类的继承:

在Java中,通过使用 extends 关键字可以实现类的继承。以下是一个简单的例子:

java

// 父类
class Animal {
    String name;

    public Animal(String name) {
        this.name = name;
    }

    public void makeSound() {
        System.out.println("Some generic sound");
    }
}

// 子类继承自父类
class Dog extends Animal {
    // 子类可以有自己的数据成员

    public Dog(String name) {
        super(name); // 调用父类的构造方法
    }

    // 子类可以覆盖父类的方法
    @Override
    public void makeSound() {
        System.out.println("Bark! Bark!");
    }

    // 子类可以有自己的方法
    public void wagTail() {
        System.out.println("Tail is wagging.");
    }
}

在这个例子中, Dog 类继承自 Animal 类。 Dog 类拥有自己的数据成员(例如, wagTail 方法),同时也继承了 Animal 类的数据成员和方法(例如, namemakeSound 方法)。子类可以根据需要添加新的数据成员和方法,也可以覆盖父类的方法。

4.6.2. 数据成员的继承:

子类会继承父类的数据成员,但有一点需要注意,即子类不能访问父类中声明为私有的成员(private)。子类可以访问父类的公有(public)和受保护(protected)成员。

java

class Animal {
    private String privateField = "I am private";
    public String publicField = "I am public";
    protected String protectedField = "I am protected";
}

class Dog extends Animal {
    public void printFields() {
        // 在子类中访问继承的成员
        // privateField 不可访问,会导致编译错误
        System.out.println(publicField);
        System.out.println(protectedField);
    }
}
4.6.3. 实例方法的继承(包含覆盖):

子类会继承父类的实例方法,可以选择性地覆盖这些方法。覆盖是指在子类中重新定义与父类中具有相同签名(方法名、参数列表和返回类型)的方法。

java

class Animal {
    public void makeSound() {
        System.out.println("Some generic sound");
    }
}

class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Bark! Bark!");
    }
}

在这个例子中, Dog 类覆盖了 Animal 类中的 makeSound 方法。当通过 Dog 类的对象调用 makeSound 方法时,将执行 Dog 类中的方法而不是父类中的方法。

总体而言,继承是面向对象编程中的重要概念,它提供了一种有效的代码复用和扩展的机制。然而,使用继承时需要小心,以避免滥用和深度继承链带来的复杂性。

4.7 类的toString方法的使用

在Java中, toStringObject类中的一个方法,它被设计用于返回对象的字符串表示。默认情况下, Object类的 toString 方法返回一个字符串,其中包含类的名称,紧跟着 “@” 符号,以及对象的哈希码。

java

public class MyClass {
    public static void main(String[] args) {
        MyClass obj = new MyClass();
        System.out.println(obj.toString()); // 默认的toString方法输出:类名@哈希码
    }
}

然而,通常情况下,我们会在自定义类中覆盖 toString 方法,以便提供有关对象内容更有用的信息。以下是一个示例:

java

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 覆盖toString方法,提供有用的对象信息
    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + '}';
    }

    public static void main(String[] args) {
        Person person = new Person("Alice", 25);
        System.out.println(person.toString()); // 输出:Person{name='Alice', age=25}
    }
}

在这个例子中,通过覆盖 toString 方法,我们定义了一个自定义的字符串表示形式,它包含了 Person 对象的关键信息。

使用 toString 方法的好处在于,在调试和日志记录中,可以更轻松地输出有关对象状态的信息,而无需手动构建字符串。此外,当你打印对象时(例如使用 System.out.println),实际上调用了对象的 toString 方法。

在实际开发中,如果你想要输出自定义对象的信息,覆盖 toString 方法是一个很好的实践,它能够提高代码的可读性和调试效率。

4.8 定义接口类型,类类型和接口类型之间的区别,以及接口类型的使用意义

在Java中,类类型和接口类型是两种不同的概念。让我们分别讨论它们之间的区别,并深入了解接口类型的定义和使用。

4.8.1 类类型(Class Type):
  • 定义: 类类型是指通过类定义的数据类型。类是面向对象编程中的基本概念,它可以包含数据成员和方法。对象是类的实例。

  • 示例:

    java

    public class Car {
        private String brand;
        private String model;
    
        public Car(String brand, String model) {
            this.brand = brand;
            this.model = model;
        }
    
        // 其他方法和代码...
    }
    
    // 创建Car类的对象
    Car myCar = new Car("Toyota", "Camry");
    
4.8.2 接口类型(Interface Type):
  • 定义: 接口类型是一种抽象类型,它定义了一组方法的签名,但不提供方法的实现。类通过实现接口来提供方法的具体实现。接口可以包含常量(静态常量)和默认方法(具有默认实现的方法)。

  • 示例:

    java

    public interface Vehicle {
        void start();
    
        void stop();
    }
    
    // 类实现接口
    public class Car implements Vehicle {
        @Override
        public void start() {
            System.out.println("Car is starting...");
        }
    
        @Override
        public void stop() {
            System.out.println("Car is stopping...");
        }
    
        // 其他方法和代码...
    }
    
4.8.3 类型之间的区别:
  1. 继承关系: 类型之间的主要区别在于继承。类通过继承关系构建,一个类可以继承另一个类,而接口通过实现关系构建,一个类可以实现多个接口。

  2. 多继承: 类在Java中是单继承的,即一个类只能直接继承一个父类。但是,一个类可以实现多个接口,实现了接口的类可以获得接口定义的多个方法。

4.8.4 接口类型的使用意义:
  1. 实现多态: 接口提供了一种实现多态的方式。不同的类可以实现相同的接口,从而允许它们以相似的方式被处理。

  2. 解耦合: 接口可以用于将抽象与实现分离,从而减少类之间的耦合度。一个类通过实现接口,而不是继承特定的类,使得代码更加灵活和可扩展。

  3. 组织代码: 接口可以用于组织代码结构,使得代码更加清晰和模块化。接口提供了一种约定,要求实现类必须提供指定的方法。

  4. 约定编程: 接口可以用于制定编程规范,让不同的类都遵循相同的接口,以确保一致性和可维护性。

总的来说,接口类型在Java中具有重要的作用,它提供了一种实现多态、解耦合、组织代码和约定编程的机制,使得代码更加灵活、清晰和易于维护。

5 重载、覆盖和面向对象

5.1 掌握面向对象的封装、继承和多态的概念,以及Java语言通过什么方式实现面向对象的这三大特征

当谈到Java中的面向对象编程时,封装、继承和多态是三个重要的概念。让我们分别来看这三个特征以及在Java中的实现方式:

  1. 封装(Encapsulation)

    • 封装是面向对象编程的基本原则之一,它指的是将数据和方法封装在一个类中,并对外部隐藏实现的细节。
    • 在Java中,封装通过访问修饰符来实现。通常使用 private修饰符来限制对类的成员的访问,而提供公共的方法(getter和setter)来访问或修改这些私有成员。

    java

    public class MyClass {
        private int myVariable;
    
        public int getMyVariable() {
            return myVariable;
        }
    
        public void setMyVariable(int value) {
            myVariable = value;
        }
    }
    
  2. 继承(Inheritance)

    • 继承允许一个类(子类)继承另一个类(父类)的属性和方法。
    • 在Java中,使用 extends关键字实现继承。子类可以继承父类的非私有属性和方法,并且可以添加自己的属性和方法。

    java

    public class ChildClass extends ParentClass {
        // 子类的属性和方法
    }
    
  3. 多态(Polymorphism)

    • 多态允许使用一个基类类型的引用来引用一个派生类的对象,从而实现同样的操作可以在不同的对象上有不同的行为。
    • 在Java中,主要通过方法重写(override)和接口来实现多态。

    java

    // 方法重写
    public class ParentClass {
        public void display() {
            System.out.println("ParentClass display");
        }
    }
    
    public class ChildClass extends ParentClass {
        @Override
        public void display() {
            System.out.println("ChildClass display");
        }
    }
    
    // 多态
    ParentClass obj = new ChildClass();
    obj.display(); // 输出:ChildClass display
    

总体而言,Java通过封装、继承和多态这三大特征来实现面向对象编程,这有助于提高代码的可维护性、可重用性和灵活性。

5.2 理解面向对象设计中封装的好处

封装是面向对象设计的重要概念,它带来了许多好处,其中一些主要好处包括:

  1. 隐藏实现细节: 封装允许将一个类的实现细节隐藏起来,只暴露必要的接口给外部。这样,其他部分的代码只需关心如何使用这个接口,而不需要关心实现的细节。这有助于降低代码的复杂性,提高代码的可维护性。

  2. 提高安全性: 将数据成员标记为私有的,只能通过类的公共方法进行访问,可以防止外部直接访问或修改内部状态。这提高了数据的安全性,减少了意外的错误和不必要的干扰。

  3. 简化代码和接口: 封装可以将复杂的实现细节封装在一个类中,从而简化了其他代码对该类的使用。用户只需要了解类的公共接口,而不必了解其内部实现,这有助于降低理解和使用类的难度。

  4. 提高代码的可维护性: 由于封装将实现细节隐藏,当需要修改类的内部实现时,只需确保不改变其公共接口。这种隔离性有助于减少对其他代码的影响,提高了代码的可维护性。

  5. 促进代码的重用: 封装有助于创建可重用的组件。其他部分的代码可以通过使用类的公共接口来实现功能,而不必重新实现相同的功能。

总体而言,封装是面向对象设计的基础,它通过隐藏实现细节、提高安全性、简化接口、提高可维护性和促进代码重用等方面为软件设计带来了诸多优势。

5.3 理解类类型之间可以发生的诸如“is a”和“has a”关系

在面向对象编程中,类之间的关系可以通过“is a”和“has a”来描述。这两种关系分别是继承(is a)和组合(has a)。

  1. “is a” 关系(继承):

    • "is a"关系表示一种类是另一种类的一种类型。这通常通过继承来实现,其中子类继承了父类的属性和方法,并且可以拥有自己的特定属性和方法。
    • 例如,如果有一个动物类(Animal),可以派生出狗类(Dog)和猫类(Cat)。在这种情况下,可以说“狗是一种动物”和“猫是一种动物”。

    java

    class Animal {
        // 公共属性和方法
    }
    
    class Dog extends Animal {
        // 独有的属性和方法
    }
    
    class Cat extends Animal {
        // 独有的属性和方法
    }
    
  2. “has a” 关系(组合):

    • "has a"关系表示一个类包含另一个类作为其一部分。这通常通过在一个类中创建另一个类的实例(成员对象)来实现。
    • 例如,一个汽车类(Car)可能包含一个引擎类(Engine),这样可以说“汽车有一个引擎”。

    java

    class Engine {
        // 引擎的属性和方法
    }
    
    class Car {
        Engine carEngine; // 汽车包含一个引擎
        // 其他汽车的属性和方法
    }
    

总体而言,"is a"关系用于描述继承关系,其中子类是父类的一种类型。而"has a"关系用于描述组合关系,其中一个类包含另一个类作为其一部分。这两种关系有助于构建灵活、可维护和可扩展的面向对象系统。

5.4 掌握重载和覆盖的概念,以及相对应的实现方式

在Java中,重载(Overloading)和覆盖(Overriding)是两个不同的概念,它们分别用于处理相同的方法名但具有不同行为的情况。

  1. 重载(Overloading):

    • 重载指的是在同一个类中可以定义多个方法,它们具有相同的名称但具有不同的参数列表。重载方法通常用于执行相似但不同的操作。
    • 参数列表包括参数的类型、个数或顺序。方法的返回类型和访问修饰符与重载无关。

    java

    public class MyClass {
        public int add(int a, int b) {
            return a + b;
        }
    
        public double add(double a, double b) {
            return a + b;
        }
    
        public String add(String a, String b) {
            return a + b;
        }
    }
    

    在上述例子中, add 方法被重载了三次,分别处理整数相加、浮点数相加和字符串连接。

  2. 覆盖(Overriding):

    • 覆盖指的是在子类中重新定义父类中已有的方法。覆盖方法要求子类方法的名称、参数列表和返回类型与父类方法完全相同。
    • 覆盖允许子类提供特定于子类的实现,而不改变父类的方法签名。

    java

    public class Animal {
        public void makeSound() {
            System.out.println("Animal makes a sound");
        }
    }
    
    public class Dog extends Animal {
        @Override
        public void makeSound() {
            System.out.println("Dog barks");
        }
    }
    

    在上述例子中, Dog 类覆盖了 Animal 类中的 makeSound 方法,提供了一个特定于狗的实现。

总体而言,重载和覆盖是Java中方法的两个重要概念。重载涉及同一类中的方法,其方法名相同但参数列表不同;而覆盖涉及继承关系中的两个类,子类重新定义了父类中的方法。

6 其他

6.1 绘制变量在内存中的布局图

在Java中,内存被划分为两个主要区域:堆内存和栈内存。这两者分别用于存储不同类型的数据。

  1. 堆内存(Heap Memory):

    • 堆内存用于存储对象和数组。它是在程序运行时动态分配的,用于存储通过 new 关键字创建的对象。
    • 所有在堆内存中创建的对象都共享一个堆,由Java的垃圾回收器负责管理。

    java

    public class MyClass {
        // 类的成员变量在堆内存中
        private int myVariable;
    
        public static void main(String[] args) {
            // 对象在堆内存中实例化
            MyClass obj = new MyClass();
            // obj 引用存储在栈内存中
        }
    }
    
  2. 栈内存(Stack Memory):

    • 栈内存用于存储基本数据类型的变量和对象的引用。它是一个相对较小的内存区域,存储方法调用和局部变量。
    • 当方法被调用时,会在栈上分配一块内存用于存储方法的参数和局部变量。当方法执行结束后,这些内存将被释放。

    java

    public class StackExample {
        public static void main(String[] args) {
            // 基本数据类型的变量在栈内存中
            int x = 10;
            int y = 20;
    
            // 对象的引用也存储在栈内存中
            MyClass obj = new MyClass();
        }
    }
    

总体而言,栈内存用于存储基本数据类型的变量和对象的引用,而堆内存用于存储通过 new 关键字创建的对象。在上述示例中,栈内存包含基本数据类型变量和对象引用,而堆内存包含对象的实际数据。Java的自动内存管理(垃圾回收)负责在不再需要的对象上释放堆内存。

而在Java中,变量的内存布局图主要取决于变量的类型和所处的位置。在Java中,主要有两种类型的变量:基本数据类型和引用类型。下面是简单讲解这两种类型变量在内存中的布局:

  1. 基本数据类型的内存布局:

    • 基本数据类型(例如int、float、char等)在内存中直接存储它们的值,而不是存储对值的引用。
    • 基本数据类型的内存布局是连续的,占用固定的内存空间。

    java

    int x = 42;
    

    内存布局图示例:

    diff

    +-----+
    |  42 |
    +-----+
    
  2. 引用类型的内存布局:

    • 引用类型(例如对象、数组)的变量存储的是对对象或数组在堆中内存地址的引用。
    • 对象或数组的实际内容存储在堆中,而变量本身在栈中存储引用。

    java

    MyClass obj = new MyClass();
    

    内存布局图示例:

    lua

    +----------+      +----------------------+
    | obj () | ---> | MyClass对象 (堆)     |
    +----------+      +----------------------+
    

    在这个示例中, obj 存储了指向堆中 MyClass 对象的引用。

需要注意的是,Java中的对象可能包含引用其他对象的字段,因此内存布局可能更为复杂。此外,Java的垃圾回收器负责管理堆中的对象的生命周期,回收不再使用的对象以释放内存。

总体而言,Java的内存布局取决于变量的类型和所处的位置(栈还是堆)。基本数据类型直接存储值,而引用类型存储对对象或数组的引用。这些概念对于理解Java中的内存管理和性能优化是很重要的。

6.2 使用UML图例绘制类之间的继承关系

UML(Unified Modeling Language)是一种用于软件系统设计和文档化的标准图形化语言。在UML中,继承关系可以通过类图来表示,其中用特定的箭头表示类之间的继承关系。以下是如何使用UML类图例绘制类之间的继承关系的基本步骤:

步骤:

  1. 确定类的层次结构: 确定哪个类是父类,哪个是子类。父类包含通用属性和方法,而子类继承这些属性和方法并可能添加自己特定的属性和方法。

  2. 绘制类图: 使用UML工具或纸和笔,在类图中绘制父类和子类的框。每个框表示一个类,框内包含类的名称、属性和方法。

  3. 添加继承关系: 使用带三角箭头的实线连接父类和子类。箭头指向子类,表示子类继承自父类。这个箭头表明了“is a”关系。

UML类图符号:

  • 类框: 包含类名、属性和方法。
  • 实线箭头: 从子类指向父类,表示继承关系。
  • {abstract}: 在类名旁边添加 {abstract} 表示抽象类,即不能直接实例化的类。

示例:

考虑一个简单的图形类继承关系,有一个基类 Shape,以及两个子类 CircleRectangle

plaintext

+-----------------------+
|        Shape          |
+-----------------------+
|                       |
| - color: String       |
|                       |
| + draw(): void        |
+-----------------------+
            |
            |
            v
+-----------------------+
|        Circle         |
+-----------------------+
|                       |
| - radius: double      |
|                       |
| + calculateArea(): double |
+-----------------------+

+-----------------------+
|      Rectangle        |
+-----------------------+
|                       |
| - length: double      |
| - width: double       |
|                       |
| + calculateArea(): double |
+-----------------------+

在这个示例中:

  • CircleRectangle 类都是 Shape 类的子类。
  • Shape 类有一个 color 属性和一个 draw 方法。
  • Circle 类有一个额外的 radius 属性和一个 calculateArea 方法。
  • Rectangle 类有额外的 lengthwidth 属性以及一个 calculateArea 方法。

这个UML类图清晰地展示了类之间的继承关系,帮助理解每个类的属性和方法。

部分考试题回忆

判断题:

try语句必须后跟 catch 语句,finally 语句可有可无 ( 错 )

选择题:

1.以下程序输出结果正确的是:

public class test {
    public static void main(String[] args) {
         StringBuilder a = new StringBuilder("-");
         StringBuilder b = a.append("-");
         System.out.print(a == b);
         System.out.print(" ");
         System.out.print(a.length());
     }
}

A. false 1

B. false 2

C. true 1

D. true 2

答案:D

2.考察了运算符的优先级。这个一定要背下来。

3.考察了for(i=0;ifor(i=0i在具体程序中的不同,结合了if语句、do while语句和while语句,选择它们分别打印出的数字。可以自己多写几个打印数字的示例程序理解它们之间的不同。

4.考察了字符的表示范围,例如char是0~65536。

综合题:

1.解释JVM
JVM(Java Virtual Machine,Java虚拟机)是Java平台的核心组件之一,它是一个在物理计算机上模拟运行Java字节码的虚拟机。JVM使得Java程序能够在不同的计算机上独立运行,实现了"一次编写,到处运行"的理念。

2.有一道画图题,给出了示例程序要求答题者画出此程序的变量等在内存中的存储结构。

{abstract} 表示抽象类,即不能直接实例化的类。

示例:

考虑一个简单的图形类继承关系,有一个基类 Shape,以及两个子类 CircleRectangle

plaintext

+-----------------------+
|        Shape          |
+-----------------------+
|                       |
| - color: String       |
|                       |
| + draw(): void        |
+-----------------------+
            |
            |
            v
+-----------------------+
|        Circle         |
+-----------------------+
|                       |
| - radius: double      |
|                       |
| + calculateArea(): double |
+-----------------------+

+-----------------------+
|      Rectangle        |
+-----------------------+
|                       |
| - length: double      |
| - width: double       |
|                       |
| + calculateArea(): double |
+-----------------------+

在这个示例中:

  • CircleRectangle 类都是 Shape 类的子类。
  • Shape 类有一个 color 属性和一个 draw 方法。
  • Circle 类有一个额外的 radius 属性和一个 calculateArea 方法。
  • Rectangle 类有额外的 lengthwidth 属性以及一个 calculateArea 方法。

这个UML类图清晰地展示了类之间的继承关系,帮助理解每个类的属性和方法。

部分考试题回忆

判断题:

try语句必须后跟 catch 语句,finally 语句可有可无 ( 错 )

选择题:

1.以下程序输出结果正确的是:

public class test {
    public static void main(String[] args) {
         StringBuilder a = new StringBuilder("-");
         StringBuilder b = a.append("-");
         System.out.print(a == b);
         System.out.print(" ");
         System.out.print(a.length());
     }
}

A. false 1

B. false 2

C. true 1

D. true 2

答案:D

2.考察了运算符的优先级。这个一定要背下来。

3.考察了for(i=0;ifor(i=0i在具体程序中的不同,结合了if语句、do while语句和while语句,选择它们分别打印出的数字。可以自己多写几个打印数字的示例程序理解它们之间的不同。

4.考察了字符的表示范围,例如char是0~65536。

综合题:

1.解释JVM
JVM(Java Virtual Machine,Java虚拟机)是Java平台的核心组件之一,它是一个在物理计算机上模拟运行Java字节码的虚拟机。JVM使得Java程序能够在不同的计算机上独立运行,实现了"一次编写,到处运行"的理念。

2.有一道画图题,给出了示例程序要求答题者画出此程序的变量等在内存中的存储结构。

编程题:平常作业好好做绝对会做的难度。

你可能感兴趣的:(面向对象,java,java,开发语言,经验分享,笔记)