抽象类

抽象类是一种不能被实例化的类,它的主要目的是为其子类提供公共的模板和基础实现。抽象类之所以能够提供这些共享方法和字段,是因为它定义了一些基本的结构和行为,而具体实现则由子类去完成。

当我们需要将一些有共同特征的对象进行抽象时,我们可以使用抽象类

例如我们需要定义一系列图形类,比如矩形和圆形等,每个图形都具有计算面积和计算周长的方法,但具体实现方法可能不一样。我们可以定义一个抽象类 Shape 来表示这些图形的共同特征,并且提供一些方法的基本实现,具体的类可以继承这个抽象类并提供自己的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public abstract class Shape {
public abstract double getArea(); //抽象方法,具体实现由子类定义
public abstract double getPerimeter(); //抽象方法,具体实现由子类定义

public void displaySize() { //基本实现方法
System.out.println("The area of this shape is: " + getArea());
System.out.println("The perimeter of this shape is: " + getPerimeter());
}
}

public class Circle extends Shape {
private double radius;

public Circle(double radius) {
this.radius = radius;
}

@Override
public double getArea() {
return Math.PI * radius * radius;
}

@Override
public double getPerimeter() {
return 2 * Math.PI * radius;
}
}

public class Rectangle extends Shape {
private double length;
private double width;

public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}

@Override
public double getArea() {
return length * width;
}

@Override
public double getPerimeter() {
return 2 * (length + width);
}
}

在上面的代码中,Shape 类是一个抽象类,它定义了两个抽象方法 getArea()getPerimeter(),以及一个基本实现方法 displaySize()。而在 CircleRectangle 类中则是实现了这两个抽象方法并提供了它们自己的实现。这样我们就可以在后续使用这些图形时,通过继承 Shape 类来实现方法的复用和代码结构的统一性。

容器

Iterator接口

Iterator接口是用于遍历集合类中的元素的接口。通过Iterator,我们可以在集合中遍历元素,判断集合中是否还有元素,获取当前位置元素并将游标向下移动等操作。

Iterator接口提供了4个方法,分别是:

  1. boolean hasNext():判断集合中是否还有元素,若有元素则返回 true,否则返回 false;
  2. E next():返回集合中的下一个元素;
  3. void remove():删除集合中上一次返回的元素;
  4. default void forEachRemaining(Consumer<? super E> action):默认方法,遍历剩下的所有元素,并对每个元素执行指定操作。

在使用Iterator接口遍历集合时,需要先使用集合对象的 iterator()方法来获取一个迭代器对象,然后使用该迭代器对象对集合进行遍历:

1
2
3
4
5
6
7
8
9
10
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("C++");

Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
String item = iter.next();
System.out.println(item);
}

这里我们对list集合进行了遍历,首先使用 list.iterator() 方法获取一个迭代器对象,然后使用 while 循环和 hasNext() 方法判断集合中是否还有元素,如果有则使用 next() 方法获取下一个元素并输出。最终输出的结果为:

1
2
3
Java
Python
C++

Collection接口

List

List列表是按照一定次序排列的对象集,对象之间有次序关系,可以重复,主要实现类为ArrayList和LinkedList:

1
2
3
4
5
6
7
8
9
10
11
import java.util.*;

public class Main{
public static void main(String[] args) {
Collection c = new ArrayList<>();
c.add(1);
c.add(2);
c.add(3);
System.out.println(c);
}
}

c的类型定义为Collection:面向接口,便于修改实现类。

ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。

泛型:使用类的时候给类提供类型参数,表示只接受特定类型,如下只接受String类型:

1
ArrayList<String> list = new ArrayList<>();

一些简单操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import java.util.ArrayList;

public class Main {
public static void main(String[] args) {
// 创建一个空的ArrayList
ArrayList<String> list = new ArrayList<>();

// 添加元素
list.add("apple");
list.add("banana");
list.add("orange");

// 访问元素
System.out.println(list.get(0)); // 输出"apple"
System.out.println(list.indexOf("orange")); // 判断是否存在并返回下标,不存在返回-1

// 遍历ArrayList
for (String s : list) {
System.out.println(s);
}

// 修改元素
list.set(0, "grape");

// 删除元素
list.remove(0); // 删除第一个元素

// 判断ArrayList是否为空
if (list.isEmpty()) {
System.out.println("ArrayList is empty");
}

// 判断ArrayList是否有元素orange
if (list.contains("orange")) {
System.out.println("The string 'orange' is in the ArrayList");
} else {
System.out.println("The string 'orange' is not in the ArrayList");
}

// ArrayList元素数量
int size = list.size();
System.out.println("The size of the list is " + size);
}
}
基本类型 引用类型
boolean Boolean
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character

Set

Set集合继承于java.util.Collection接口,表示不允许出现重复元素的集合。Set中的元素是没有顺序的。

Set的常用实现类有HashSet、TreeSet和LinkedHashSet。其中,HashSet底层是基于哈希表实现的;TreeSet底层是基于红黑树结构实现的,可以对元素进行排序;LinkedHashSet底层是基于双向链表和哈希表实现的,它可以按照元素插入的顺序进行排序。

HashSet是最常用的Set实现类之一,内部使用哈希表来实现,是无序的,它内部实现是基于哈希表,不会保证元素添加的顺序和输出的顺序一致。HashSet没有保证元素的顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import java.util.HashSet;

public class Main {
public static void main(String[] args) {
HashSet<String> set = new HashSet<String>();

// 添加元素到 HashSet
set.add("apple");
set.add("banana");
set.add("orange");

// 打印 HashSet 中的元素
System.out.println("HashSet: " + set);

// 判断 HashSet 是否包含某个元素
if (set.contains("banana")) {
System.out.println("HashSet contains banana");
}

// 获取 HashSet 中元素的数量
System.out.println("HashSet size: " + set.size());

// 移除 HashSet 中的一个元素
set.remove("orange");
System.out.println("HashSet after removing orange: " + set);

// 清空 HashSet 中的所有元素
set.clear();
System.out.println("HashSet after clearing all elements: " + set);

// 遍历 HashSet 中的所有元素
HashSet<Integer> numbers = new HashSet<Integer>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);

for(int a : numbers){
System.out.println(a);
}

// 求两个 HashSet 的交集
HashSet<String> set1 = new HashSet<String>();
set1.add("apple");
set1.add("banana");
set1.add("orange");

HashSet<String> set2 = new HashSet<String>();
set2.add("grape");
set2.add("banana");
set2.add("pear");

set1.retainAll(set2); // 获取 set1 和 set2 的交集
System.out.println("HashSet intersection of set1 and set2: " + set1);
}
}

需要排序可以考虑使用TreeSet,TreeSet使用红黑树作为存储结构,可以确保元素被排序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import java.util.TreeSet;

public class Main {
public static void main(String[] args) {
// 创建一个 TreeSet 对象,存储字符串类型的元素
TreeSet<String> treeSet = new TreeSet<String>();

// 添加元素到 TreeSet
treeSet.add("apple");
treeSet.add("banana");
treeSet.add("orange");
treeSet.add("mango");

// 打印 TreeSet 中的元素
System.out.println("TreeSet: " + treeSet);

// 获取 TreeSet 中元素的数量
System.out.println("TreeSet size: " + treeSet.size());

// 判断 TreeSet 是否包含某个元素
if (treeSet.contains("banana")) {
System.out.println("TreeSet contains banana");
}

// 获取 TreeSet 中的第一个元素和最后一个元素
String firstElement = treeSet.first();
String lastElement = treeSet.last();
System.out.println("TreeSet first element: " + firstElement);
System.out.println("TreeSet last element: " + lastElement);

// 获取 TreeSet 中小于等于某个元素的最大元素和大于等于某个元素的最小元素
String lowerOrEqualElement = treeSet.floor("orange");
String higherOrEqualElement = treeSet.ceiling("orange");
System.out.println("TreeSet lowerOrEqual orange element: " + lowerOrEqualElement);
System.out.println("TreeSet higherOrEqual orange element: " + higherOrEqualElement);

// 移除 TreeSet 中的一个元素
treeSet.remove("mango");
System.out.println("TreeSet after removing mango: " + treeSet);

// 清空 TreeSet 中的所有元素
treeSet.clear();
System.out.println("TreeSet after clearing all elements: " + treeSet);

// 遍历 TreeSet 中的所有元素
TreeSet<Integer> numbers = new TreeSet<Integer>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);

for(int a:numbers){
System.out.println(a);
}
}
}

LinkedHashSet 是通过哈希表+链表实现的,既可以快速访问元素,又保证了插入顺序。因此,在需要元素有序的情况下,推荐使用 LinkedHashSet。当然,在不需要元素有序的情况下,HashSet 的效率是最高的。

Queue

Queue根据排队规则来确定对象的顺序,规则为先进先出,主要实现类为LinkedList:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.*;

public class Main {
public static void main(String[] args) {
Queue queue = new LinkedList<>();
queue.add(1);
queue.add(2);
queue.add(3);
queue.add(4);
System.out.println(queue);//[1, 2, 3, 4]
System.out.println(queue.peek());//返回队头:1
System.out.println(queue.poll());//队头出队:1
System.out.println(queue);//[2, 3, 4]
}
}

vector是list接口的实现类,栈是vector的延伸,对栈进行操作规则为先进后出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.util.*;

public class Main {
public static void main(String[] args) {
Stack stack = new Stack<>();
stack.push(1);
stack.push(2);
stack.push(3);
stack.push(4);
System.out.println(stack); //[1, 2, 3, 4]
System.out.println(stack.pop()); //出栈:4
System.out.println(stack); //[1, 2, 3]
System.out.println(stack.peek()); //查看栈顶:3
while(!stack.isEmpty()){
System.out.println(stack.pop());
}
}
}

Stack也可以当做普通列表用,但是就不具备Stack的性质了。

Map接口

Map 接口中键和值一一映射,可以通过键来获取值。

给定一个键和一个值,你可以将该值存储在一个 Map 对象。之后,你可以通过键key来访问对应的值value。

一些常用的 Map 接口的实现类有 HashMap、TreeMap 和 LinkedHashMap

  • HashMap:HashMap 是基于哈希表实现的 Map,是最常用的 Map 实现之一。
  • TreeMap:TreeMap 是基于红黑树实现的 Map,它可以保证元素的顺序,通常用于需要自定义排序的场景。
  • LinkedHashMap:LinkedHashMap 是哈希表和双向链表的组合实现,可以保证元素的插入顺序。

对于HashMap的简单操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.util.HashMap;
import java.util.Map;

public class Main {
public static void main(String[] args) {
// 创建一个 HashMap
Map<String, Integer> hashMap = new HashMap<>();

// 在 HashMap 中添加元素
hashMap.put("apple", 1);
hashMap.put("banana", 2);

// 获取 HashMap 中的元素
int appleCount = hashMap.get("apple");
int bananaCount = hashMap.get("banana");
System.out.println("appleCount: " + appleCount); // 输出:1
System.out.println("bananaCount: " + bananaCount); // 输出:2

// 遍历 HashMap 的 key 和 value
for (Map.Entry<String, Integer> entry : hashMap.entrySet()) {
System.out.println("key: " + entry.getKey() + ", value: " + entry.getValue());
}
}
}

TreeMap 可以保持元素有序,在后面的操作中会方便排序,下面是一个简单实现排序的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.util.*;

public class Main {
public static void main(String[] args) {
String str = "hello world, hello java, hello world, hello python, hello cplusplus";
String[] words = str.split("\\s+");

Map<String, Integer> wordCount = new TreeMap<String, Integer>();

for (String word : words) {
if (wordCount.containsKey(word)) {
wordCount.put(word, wordCount.get(word) + 1);
} else {
wordCount.put(word, 1);
}
}

// 对 TreeMap 进行排序
List<Map.Entry<String, Integer>> list = new ArrayList<Map.Entry<String, Integer>>(wordCount.entrySet());
Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
@Override
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
return o1.getValue().compareTo(o2.getValue());
}
});

// 输出按照 value 排序后的结果
for (Map.Entry<String, Integer> entry : list) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
}

下面是LinkedHashMap:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.util.LinkedHashMap;
import java.util.Map;

public class Main {
public static void main(String[] args) {

// 创建一个容量为10的新LinkedHashMap对象
Map<String, String> linkedHashMap = new LinkedHashMap<>(10);

// 往LinkedHashMap中添加一些元素
linkedHashMap.put("apple", "red"); // 苹果 红色
linkedHashMap.put("banana", "yellow"); // 香蕉 黄色
linkedHashMap.put("orange", "orange"); // 橙子 橙色

// 打印LinkedHashMap中的元素
System.out.println("LinkedHashMap: " + linkedHashMap);
// 输出: LinkedHashMap: {apple=red, banana=yellow, orange=orange}

// 根据键访问LinkedHashMap中的一个元素
String color = linkedHashMap.get("apple");
System.out.println("The color of apple is: " + color);
// 输出: 苹果的颜色是:红色

// 遍历LinkedHashMap中的值并打印它们
System.out.print("The values in the LinkedHashMap are: ");
for (String value : linkedHashMap.values()) {
System.out.print(value + " ");
}
System.out.println();
// 输出: LinkedHashMap中的值为:红色 黄色 橙色
}
}

LinkedHashMap和HashMap在实现上最主要的区别在于 LinkedHashMap 它可以保证元素的插入顺序和访问顺序一致,即按照元素插入的顺序或者最近访问的顺序(根据构造方法中的参数设置而定)进行访问。

异常处理

所有的异常类是从 java.lang.Exception 类继承的子类。

捕获异常

使用 try 和 catch 关键字可以捕获异常。try/catch 代码块放在异常可能发生的地方。

try/catch代码块中的代码称为保护代码,使用 try/catch 的语法如下:

1
2
3
4
5
6
7
try
{
// 程序代码
}catch(ExceptionName e1)
{
//Catch 块
}

一个 try 代码块后面可以跟随多个 catch ,跟随多个 catch 代码块的情况就叫多重捕获。

实例:调用数组超出范围:

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.io.*;
public class ExcepTest{

public static void main(String args[]){
try{
int a[] = new int[2];
System.out.println("Access element three :" + a[3]);
}catch(ArrayIndexOutOfBoundsException e){
System.out.println("Exception thrown :" + e);
}
System.out.println("Out of the block");
}
}

throw和throws关键字

当在Java中处理异常时,可以使用throw关键字抛出一个具体的异常对象,或者使用throws关键字声明函数可能抛出的异常类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Example {
public static void main(String[] args) {
try {
divide(10, 0);
} catch (ArithmeticException e) {
System.out.println("发生了除以零的异常!");
System.out.println(e.getMessage());
}
}

public static int divide(int dividend, int divisor) throws ArithmeticException {
if (divisor == 0) {
throw new ArithmeticException("除数不能为零");
}
return dividend / divisor;
}
}

一个方法可以声明抛出多个异常,多个异常之间用逗号隔开。

自定义异常类

当我们希望在Java中使用自定义的异常类时,我们需要创建一个继承自Exception或其子类的新异常类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class CustomException extends Exception {

public CustomException(String message) {
super(message);
}
}

public class Example {
public static void main(String[] args) {
try {
process();
} catch (CustomException e) {
System.out.println("捕获到自定义异常!");
System.out.println(e.getMessage());
}
}

public static void process() throws CustomException {
// 假设在这里发生了某种错误
throw new CustomException("发生了自定义异常");
}
}

在上面的示例中,我们首先定义了一个名为CustomException的自定义异常类,它继承自Exception类。构造函数接受一个字符串参数用于设置异常信息,并通过super关键字调用父类Exception的构造函数来设置异常信息。

finally关键字

finally 关键字用来创建在 try 代码块后面执行的代码块。

无论是否发生异常,finally 代码块中的代码总会被执行。

finally 代码块出现在 catch 代码块最后,语法如下:

1
2
3
4
5
6
7
8
9
try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}finally{
// 程序代码
}

测试题目

员工的工资

假定要为某个公司编写雇员工资支付程序,这个公司有各种类型的雇员(Employee),不同类型的雇员按不同的方式支付工资(都是整数):

(1)经理(Manager)——每月获得一份固定的工资

(2)销售人员(Salesman)——在基本工资的基础上每月还有销售提成

(3)一般工人(Worker)——则按他每月工作的天数计算工资 在Employee中提供方法getSalary(),用于计算每个雇员一个月的工资,并在子类中重写。

Post_AppendCode的main方法中已经构造Employee的三个变量,分别指向Manager、Salesman、Worker的对象,调用getSalary方法,输出三个对象的工资。 要求:编码实现经理、销售人员、一般工人三个类。

方法接口定义

1
public int getSalary();    //计算每个雇员一个月的工资

裁判测试程序样例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.util.Scanner;

public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int managerSalary = scan.nextInt();
int salemanSalary = scan.nextInt();
int salemanRaise = scan.nextInt();
int workerEveryday = scan.nextInt();
int workerDays = scan.nextInt();

Employee e1 = new Manager(managerSalary);
Employee e2 = new Salesman(salemanSalary, salemanRaise);
Employee e3 = new Worker(workerEveryday, workerDays);

System.out.println(e1.getSalary());
System.out.println(e2.getSalary());
System.out.println(e3.getSalary());

scan.close();
}
}

/* 请在这里填写答案 */

输入描述

经理的月工资
销售人员的基本工资 销售人员的提成
工人的工作天数 工人每天的工资

输出描述

经理的工资
销售人员的工资
工人的工资

输入样例

1
2
3
12000
3000 5000
22 200

输出样例

1
2
3
12000
8000
4400

给出代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import java.util.Scanner;

public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int managerSalary = scan.nextInt();
int salemanSalary = scan.nextInt();
int salemanRaise = scan.nextInt();
int workerEveryday = scan.nextInt();
int workerDays = scan.nextInt();

Employee e1 = new Manager(managerSalary);
Employee e2 = new Salesman(salemanSalary, salemanRaise);
Employee e3 = new Worker(workerEveryday, workerDays);

System.out.println(e1.getSalary());
System.out.println(e2.getSalary());
System.out.println(e3.getSalary());

scan.close();
}
}

//定义抽象类
abstract class Employee{
abstract public int getSalary();
}

class Manager extends Employee{
private int Salary;
public Manager(int Salary) {
this.Salary=Salary;
}
public int getSalary() {
return this.Salary;
}
}

class Salesman extends Employee{
private int Salary;
private int Salarymore;
public Salesman(int Salary,int Salarymore) {
this.Salary=Salary;
this.Salarymore=Salarymore;
}
public int getSalary() {
return this.Salary+Salarymore;
}
}

class Worker extends Employee{
private int days;
private int Salary;
public Worker(int days,int Salary) {
this.days=days;
this.Salary=Salary;
}
public int getSalary() {
return this.Salary*this.days;
}
}

异常代码填空

下面程序抛出了一个“异常”并捕捉它。请在横线处填入适当内容完成程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Main {
static void procedure() throws IllegalAccessException {
System.out.println("inside procedure");
___ ___ IllegalAccessException("demo");
}

public static void main(String args[]) {
try {
procedure();
} catch (___) {
System.out.println("捕获:" + e);
}
}
}

根据异常的知识点直接填空:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Main {
static void procedure() throws IllegalAccessException {
System.out.println("inside procedure");
throw new IllegalAccessException("demo");
}

public static void main(String args[]) {
try {
procedure();
} catch (IllegalAccessException e) {
System.out.println("捕获:" + e);
}
}
}

处理异常输入

使用异常处理输入机制,让程序变得更健壮。

main方法

  1. 输入n,创建大小为n的int数组。
  2. 输入n个整数,放入数组。输入时,有可能输入的是非整型字符串,这时候需要输出异常信息,然后重新输入。
  3. 使用Arrays.toString输出数组中的内容。

输入样例

1
2
3
4
5
6
7
8
5
1
2
a
b
4
5
3

输出样例

1
2
3
java.lang.NumberFormatException: For input string: "a"
java.lang.NumberFormatException: For input string: "b"
[1, 2, 4, 5, 3]

这里需要知道捕获的异常信息输出即可得到题目所给的输出,捕获了异常之后输出异常信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.util.Arrays;
import java.util.Scanner;

public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int[] array = new int[n];
for (int i = 0; i < n; i++) {
String next = scanner.next();
try {
array[i] = Integer.parseInt(next);
} catch (NumberFormatException e) {
System.out.println(e);
i--;
}
}
System.out.println(Arrays.toString(array));
scanner.close();
}
}