Java 基础(三)

🍵 Java是当今世界最重要、使用最广泛的计算机语言之一,全球每天有超百万的开发者在用Java进行开发。

1 封装

  • 封装
    • 封装将类的某些信息隐藏在类内部,不允许外部程序直接访问。
    • 只允许通过该类提供的方法,来实现对隐藏信息的操作和访问。
    • 实现封装步骤
      • 修改属性的可见性来限制对属性的访问,一般设为private。
      • 为每个属性创建一对赋值和取值方法,一般设为public,用于属性的读写。
      • 在赋值(setter)和取值(getter)中加入属性控制语句(判断属性值的合法性)。
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// 员工类的封装过程,主要属性有姓名、年龄、电话、住址
class Employee {
// 使用private关键字修饰属性
// 意味着除员工类本身外,其他任何类都不可以访问这些属性
private String name;
private int age;
private String phone;
private String address;

// 通过getName()方法访问属性
public String getName() {
return name;
}

// 通过属性的setName()方法赋值
public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
// 对年龄进行限制
if (age < 18 || age > 40) {
System.out.println("注意:年龄必须在18到40之间!");
this.age = 20;
} else {
this.age = age;
}
}

public String getPhone() {
return phone;
}

public void setPhone(String phone) {
this.phone = phone;
}

public String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}
}

// 图书类的封装过程,主要属性有图书名称、总页数
class Book {
private String bookName;
private int bookTotalNum;

public String getBookName() {
return bookName;
}

public void setBookName(String bookName) {
this.bookName = bookName;
}

public int getBookTotalNum() {
return bookTotalNum;
}

public void setBookTotalNum(int bookTotalNum) {
if (bookTotalNum < 200) {
System.out.println("注意:该书页数不能少于200页!");
this.bookTotalNum = 200;
} else {
this.bookTotalNum = bookTotalNum;
}
}

// 添加公有方法details(),输出图书名称、总页数
public void details() {
System.out.println("输出:" + this.bookName + "总页数" + this.bookTotalNum + "页。");
}
}

public class Test079 {
public static void main(String[] args) {
Employee p = new Employee();

// 调用Employee属性的setXXX()方法进行赋值
p.setName("张三");
p.setAge(15);
p.setPhone("18859745110");
p.setAddress("福建省厦门市");

// 调用getXXX()方法访问属性
System.out.println("姓名:" + p.getName());
System.out.println("年龄:" + p.getAge());
System.out.println("电话:" + p.getPhone());
System.out.println("住址:" + p.getAddress());

// 创建Book类的实例对象,为类中属性赋值
Book b = new Book();
b.setBookName("《红与黑》");
b.setBookTotalNum(156);
b.details();
}
}

2 继承

  • 继承
    • 继承是面向对象的三大特征之一,就是在已存在类的基础上进行扩展,从而产生新的类。
    • 已存在的类称为父类、基类或超类,而新产生的类称为子类或派生类。
    • 在子类中,不仅包含父类的属性和方法,还可以增加新的属性和方法。
    • 若父类成员是公有、被保护或默认的,则子类仍具相应特性且不能获得父类的构造方法。
    • 若父类存在有参构造方法而并没有重载无参构造方法,则子类中必须含有有参构造方法。
      • 如果在子类中不含有构造方法,默认会调用父类中无参的构造方法。
      • 而在父类中并没有无参的构造方法,因此继承父类时就会发生错误。

2-1 实例

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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// 定义动物类
class Animals {
// 4个公有属性,一个构造方法,一个toString()方法
public String name;
public int age;
public String sex;
public String color;

public Animals(String name, int age, String sex, String color) {
this.name = name;
this.age = age;
this.sex = sex;
this.color = color;
}

public String toString() {
return "名字:" + name + "\n年龄:" + age + "\n性别:" + sex + "\n颜色:" + color;
}
}

// 定义猫咪类,继承动物类
class Cat extends Animals {
// 夜视能力等级,0-1000分
private int nightVisionLevel;
// 是否擅长抓老鼠
private boolean isGoodAtCatchingMice;

public Cat(String name, int age, String sex, String color,
int nightVisionLevel, boolean isGoodAtCatchingMice) {
// 调用父类中的构造方法
super(name, age, sex, color);
this.nightVisionLevel = nightVisionLevel;
this.isGoodAtCatchingMice = isGoodAtCatchingMice;
}

public String toString() {
return "名字:" + name + "\n年龄:" + age +
"\n性别:" + sex + "\n颜色:" + color +
"\n夜视:" + nightVisionLevel +
"\n抓鼠:" + isGoodAtCatchingMice;
}
}

// 定义狗狗类,继承动物类
class Dog extends Animals {
// 品种
private String breed;
// 训练水平,0-1000分
private int trainingLevel;

public Dog(String name, int age, String sex, String color,
String breed, int trainingLevel) {
super(name, age, sex, color);
this.breed = breed;
this.trainingLevel = trainingLevel;
}

public String toString() {
return "名字:" + name + "\n年龄:" + age +
"\n性别:" + sex + "\n颜色:" + color +
"\n品种:" + breed + "\n训练:" + trainingLevel;
}
}

public class Test080 {
public static void main(String[] args) {
// 创建Cat类对象
Animals a = new Cat("阿花", 1, "公的", "白色", 1000, true);
System.out.println(a);
System.out.println("**********");

// 创建Dog类对象
Animals b = new Dog("大黄", 1, "母的", "黄色", "土狗", 1000);
System.out.println(b);
}
}

2-2 单继承

  • 单继承
    • Java只允许一个类直接继承另一类,即子类只能有一个直接父类。
    • extends关键字后面只能有一个类名,但是可以有多个间接的父类。
    • 定义Java类时并未显式指定该类的直接父类,则该类默认继承java.lang.Object类。
    • 因此java.lang.Object类是所有类的父类,要么是其直接父类,要么是其间接父类。

2-3 关键字

  • 关键字
    • 子类不能继承父类的构造方法,如果想要调用父类的构造方法,可使用super关键字。
    • super可在子类的构造方法中显式的调用父类构造方法,访问父类的成员方法和变量。

(1) 调用父类构造方法

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
// class TestDemo1 {
// public TestDemo1(String name) {
// }
// }

// TestDemo1类中不存在无参构造方法,而子类Test081中默认调用了super(),会编译报错
// public class Test081 extends TestDemo1 {
// }

class TestDemo1 {
public TestDemo1(String name, int age) {
}

public TestDemo1(String name, int age, String sex) {
}
}

// 子类Test081继承了父类Test,使用super语句定义父类的构造方法
public class Test081 extends TestDemo1 {
public Test081(String name, int age, String birth) {
super(birth, age);
}

public Test081(String name, int age, String sex, String birth) {
super(birth, age, sex);
}

public static void main(String[] args) {
System.out.println();
}
}

(2) 调用父类成员属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class TestDemo2 {
// 子类成员变量或方法与父类同名时,可使用super关键字访问
// 若子类重写父类的某个方法,即子类和父类有相同的方法定义
// 但方法体不同,此时,可以通过super调用父类里的这个方法
// 使用super访问父类中的成员与this使用相似,只不过引用的是子类的父类
int age = 29;
}

class T2 extends TestDemo2 {
int age = 30;

void display() {
// 使用super访问父类的age变量
System.out.println("年龄:" + super.age);
}
}

class Test082 {
public static void main(String[] args) {
T2 t = new T2();
t.display();
}
}

(3) 调用父类成员方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class TestDemo3 {
void message() {
System.out.println("父类");
}
}

class T3 extends TestDemo3 {
void message() {
System.out.println("子类");
}

void display() {
message();
super.message();
}
}

class Test083 {
public static void main(String[] args) {
T3 t = new T3();
t.display();
}
}

(4) super和this的区别

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
// 父类的定义
class TestDemo4 {
public String name;
}

public class Test084 extends TestDemo4 {
/**
* this指的是当前对象的引用,super是当前对象的父对象的引用
* 若构造方法的首行代码不是this()或super(),则默认添加super()
* ** super.父类属性名:调用父类中的属性
* ** super.父类方法名:调用父类中的方法
* ** super():调用父类的无参构造方法
* ** super(参数):调用父类的有参构造方法
* ** this.属性名:表示当前对象的属性
* ** this.方法名:表示调用当前对象的方法
* 当局部变量和成员变量发生冲突时,使用this进行区分
*/
private String name;

public Test084(String aname, String dname) {
// 通过super关键字来访问父类中的name属性
super.name = aname;
// 通过this关键字来访问本类中的name属性
this.name = dname;
}

public String toString() {
return super.name + "," + this.name;
}

public static void main(String[] args) {
TestDemo4 t = new Test084("张三", "李四");
System.out.println(t);
}
}

2-4 类型转换

  • 类型转换
    • 这里的对象类型转换,是指存在继承关系的对象,不是任意类型的对象。
    • 对不存在继承关系的对象进行强制类型转换时会抛出强制类型转换异常。
    • 向上转型:父类引用指向子类对象,fatherClass obj = new sonClass();
      • 向上转型就是把子类对象直接赋给父类引用,不用强制转换。
      • 可调用父类类型中所有成员,不能调用子类类型中特有成员。
    • 向下转型:子类对象指向父类引用,sonClass obj = (sonClass) fatherClass;
      • 可调用子类类型中所有成员,需注意若父类引用对象指向子类对象,向下转型编译正常。
      • 若父类引用对象是父类本身,向下转型过程不安全,编译不出错但运行时出现转换异常。
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
// 定义实例变量name、静态变量staticName、实例方法eat、静态方法staticEat
class TestDemo5 {
public String name = "父类:实例变量";
public static String staticName = "父类:静态变量";

public void eat() {
System.out.println("父类:实例方法");
}

public static void staticEat() {
System.out.println("父类:静态方法");
}
}

// 定义实例变量str、实例方法eatMethod
public class Test085 extends TestDemo5 {
public String name = "子类:实例变量";
public String str = "子类:实例变量";
public static String staticName = "子类:静态变量";

public void eat() {
System.out.println("子类:实例方法");
}

public static void staticEat() {
System.out.println("子类:静态方法");
}

public void eatMethod() {
System.out.println("子类:实例方法");
}

// 向下转型,必须强制类型转换,向上转型,不必使用强制类型转换
public static void main(String[] args) {
// 向上转型,将Test085类型转换为Test类型
TestDemo5 t1 = new Test085();

// 向下转型,将Test类型转换为Test085类型
Test085 t2 = (Test085) t1;

// 成员变量与引用变量所声明的类型成员变量绑定,属于静态绑定(编译阶段绑定)
// 成员变量包括静态变量和实例变量,如t1.name和t1.staticName都与Test类绑定
System.out.println(t1.name);
System.out.println(t1.staticName);

// 实例方法与引用变量实际引用的对象方法绑定,属于动态绑定
// 如t1.eat()是将eat()方法与Test085类进行绑定
t1.eat();

// 静态方法与引用变量所声明的类型方法绑定,属于静态绑定(编译阶段绑定)
// 如t1.staticEat()是将staticEat()方法与Test类进行绑定
t1.staticEat();

System.out.println(t2.str);
t2.eatMethod();
}
}

2-5 方法重载

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
public class Test086 {
/**
* 同个类中包含两个及以上相同的方法名,但形参列表不同,被称为方法重载
* 使用方法重载可避免出现繁多的方法名,方法名过多容易降低程序的可读性
*/
public void max(int a, int b) {
// 含有两个int类型参数的方法
System.out.println(a > b ? a : b);
}

public void max(double a, double b) {
// 含有两个double类型参数的方法
System.out.println(a > b ? a : b);
}

public void max(double a, double b, int c) {
// 含有两个double类型参数和一个int类型参数的方法
double max = (double) (a > b ? a : b);
System.out.println(c > max ? c : max);
}

public static void main(String[] args) {
Test086 t = new Test086();
t.max(1, 5);
t.max(2.30, 2.31);
t.max(3.14, 3.15, 3);
}
}

2-6 方法重写

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
class TestDemo6 {
public String name;
public int age;

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

public String getInfo() {
return "姓名:" + name + "\n年龄:" + age;
}
}

public class Test087 extends TestDemo6 {
/**
* 方法重载,overload,方法重写,override,又叫方法覆盖
* 子类中创建与父类相同名称、相同返回值类型、相同参数列表的方法
* 只是方法体的实现不同,以实现不同于父类的功能,该方式叫方法重写
* 遵循规则
*** 参数列表必须完全与被重写的方法参数列表相同
*** 返回的类型必须与被重写的方法的返回类型相同
*** 返回值类型必须小于或等于父类方法的返回值类型(版本1.5后放宽了限制)
*** 访问权限不能比父类中被重写方法的访问权限更低
*** 访问权限:public>protected>default>private
*** 重写方法一定不能抛出新的检査异常,或比被重写方法声明更加宽泛的检査型异常
* 注意事项
*** 重写方法可以使用@Override注解进行标识,父类的成员方法只能被其子类重写
*** 声明为final的方法不能被重写,声明为static的方法不能被重写但能再次声明
*** 构造方法不能被重写,如果不能继承一个方法则不能重写该方法
*** 子类和父类在同一个包中,子类可重写父类的所有方法,除声明为private和final的方法
*** 子类和父类不在同个包中,子类只能重写父类的声明为public和protected的非final方法
*/

// 若子类创建一个成员变量,其类型和名称都与父类中的同名成员变量相同,称其为变量隐藏
private String hobby;

public Test087(String name, int age, String hobby) {
super(name, age);
this.hobby = hobby;
}

// 重写了父类的getInfo()方法
public String getInfo() {
return "姓名:" + this.name + "\n年龄:" + this.age + "\n爱好:" + hobby;
}

public static void main(String[] args) {
TestDemo6 t = new Test087("张三", 23, "游戏");
System.out.println(t.getInfo());
}
}

3 多态

  • 多态
    • 多态指在父类中定义的属性和方法被子类继承后可具有不同的表现行为。
    • 多态性是面向对象编程的又一重要特征,分为编译时多态和运行时多态。
      • 编译时多态是静态的,主要是指方法的重载。
      • 运行时多态是动态的,通过动态绑定来实现。
    • 实现多态的必要条件
      • 继承:在多态中必须存在有继承关系的子类和父类。
      • 重写:子类对父类的某些方法重新定义,调用这些方法时就会调用子类的方法。
      • 向上转型:将子类的引用赋给父类对象,既能调用父类方法又能调用子类方法。
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
// 创建Figure类
class Figure {
// 定义存储二维对象的尺寸
double dim1;
double dim2;

Figure(double d1, double d2) {
// 有参构造方法
this.dim1 = d1;
this.dim2 = d2;
}

double area() {
// 计算对象的面积
System.out.println("父类计算无实际意义,需要在子类中重写!");
return 0;
}
}

// 创建Rectangle子类继承自Figure类
class Rectangle extends Figure {
// 调用父类的构造方法
Rectangle(double d1, double d2) {
super(d1, d2);
}

// 重写父类的area()方法
double area() {
System.out.println("长方形面积:");
return super.dim1 * super.dim2;
}
}

// 创建Triangle子类继承自Figure类
class Triangle extends Figure {
Triangle(double d1, double d2) {
super(d1, d2);
}

double area() {
System.out.println("三角形面积:");
return super.dim1 * super.dim2 / 2;
}
}

public class Test088 {
public static void main(String[] args) {
// 声明Figure类的变量
Figure f;
f = new Rectangle(9, 7);
System.out.println(f.area());
f = new Triangle(6, 9);
System.out.println(f.area());
f = new Figure(10, 10);
System.out.println(f.area());
}
}

3-1 双目

  • 双目
    • instanceof是Java中的双目运算符,也是保留关键字。
    • 使用instanceof来判断一个对象是否为一个类或接口。
    • 用法
      • 声明一个class类的对象,判断obj是否为class类的实例对象。
      • 声明一个class接口实现类的对象obj,判断obj是否为class接口实现类的实例对象。
      • obj是class类的直接或间接子类,obj必须为引用或空类型,class只能是类或接口。
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
class Animal {
}

class Cow extends Animal {
}

class Sheep extends Animal {
}

interface List {
}

class ArrayList implements List {
}

public class Test089 {
public Object animalCall(Animal a) {
String tip = "这个动物不是牛!";
// 判断参数a是不是Cow的对象,和三目运算符一起使用
return a instanceof Cow ? (Cow) a : tip;
}

// 创建类Sheep的对象作为形参传递到animalCall方法中
public static void main(String[] args) {
// 用法:声明一个class类的对象
// 判断obj是否为class类的实例对象
Animal a = new Animal();
System.out.println(a instanceof Animal);

// 用法:声明一个class接口实现类的对象obj
// 判断obj是否为class接口实现类的实例对象
ArrayList arrayList = new ArrayList();
System.out.println(arrayList instanceof List);
List list = new ArrayList();
System.out.println(list instanceof ArrayList);
System.out.println("****");

// 用法:obj是class类的直接或间接子类
// obj必须为引用或空类型,class只能是类或接口
Animal b = new Sheep();
Sheep c = new Sheep();
System.out.println(c instanceof Sheep);
System.out.println(b instanceof Sheep);
System.out.println(a instanceof Sheep);

Test089 t = new Test089();
// Sheep类的对象不能转换为Cow类型,所以输出结果
System.out.println(t.animalCall(c));
}
}

3-2 抽象类

  • 抽象类
    • Java语言提供了两种类,分别为具体类和抽象类,之前接触的都是具体类。
    • 若一个类没有包含足够的信息来描绘一个具体的对象,这样的类叫抽象类。
    • 抽象方法特征
      • 抽象方法没有方法体,并且必须存在于抽象类中。
      • 子类重写父类时,必须重写父类所有的抽象方法。
      • 使用abstract关键字修饰抽象方法时,不能使用private修饰。
      • abstract只能用于普通方法,不能用于static方法或构造方法。
    • 抽象类的定义和使用规则
      • 抽象类和抽象方法都要使用abstract关键字声明。
      • 如果一个方法被声明为抽象的,那么这个类也必须声明为抽象的。
      • 而一个抽象类中,可以有无限个抽象方法,以及无限个具体方法。
      • 抽象类不能实例化,也就是抽象类不能使用new关键字来创建对象。
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
// 创建一个抽象类Shape
abstract class Shape {
public int width;
public int height;

public Shape(int width, int height) {
this.width = width;
this.height = height;
}

// 定义抽象方法,计算面积
public abstract double area();
}

// 创建一个正方形类,继承自Shape
class Square extends Shape {
public Square(int width, int height) {
super(width, height);
}

// 重写父类中的抽象方法,实现计算正方形面积
@Override
public double area() {
return width * height;
}
}

// 创建一个三角形类,继承自Shape
class TrianGle extends Shape {
public TrianGle(int width, int height) {
super(width, height);
}

// 重写父类中的抽象方法,实现计算三角形面积
@Override
public double area() {
return width * height * 0.5;
}
}

public class Test090 {
public static void main(String[] args) {
Square s = new Square(5, 5);
System.out.println(s.area());
TrianGle t = new TrianGle(4, 5);
System.out.println(t.area());
}
}

3-3 特殊类

  • 特殊类
    • 接口是Java中最重要的概念之一,可被理解为一种特殊的类。
    • 接口成员没有执行体,由全局常量和公共的抽象方法所组成。
    • 定义方式与类基本相同,不过接口定义使用的关键字是interface。
    • 一个接口可以有多个直接父接口,但接口只能继承接口不能继承类。
    • 接口特征
      • public控制符的接口允许任何类使用,未指定public的接口访问将局限于所属包。
      • 方法声明不需其他修饰符,接口中声明的方法将隐式地声明为public和abstract。
      • 变量声明将隐式地声明为public、static和final,即常量,定义的变量必须初始化。
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
// 创建一个IMath接口
interface IMath {
public int sum();

public int maxNum(int a, int b);
}

// 定义一个MathClass类并实现IMath接口
class MathClass implements IMath {
private int num1;
private int num2;

public MathClass(int num1, int num2) {
this.num1 = num1;
this.num2 = num2;
}

public int sum() {
return num1 + num2;
}

public int maxNum(int a, int b) {
if (a >= b) {
return a;
} else {
return b;
}
}
}

public class Test091 {
public static void main(String[] args) {
// 创建实现类的对象
MathClass m = new MathClass(100, 300);
System.out.println(m.sum());
System.out.println(m.maxNum(100, 300));
}
}

3-4 内部类

  • 内部类
    • 如果在类Outer的内部定义一个类Inner,类Inner就称为内部类或嵌套类。
    • 而类Outer称为外部类或宿主类,内部类拥有外部类的所有元素访问权限。
    • 内部类可分为:成员内部类(包括实例内部类、静态内部类)、局部内部类。
    • 特点
      • 仍属于一个独立的类,会被编译成独立的class文件,前面冠以外部类的类名和$符号。
      • 不能用普通方式访问,内部类可以自由地访问外部类的成员变量,无论是否为private。
      • 内部类声明成静态的,就不能访问外部类的成员变量,只能访问外部类的静态成员变量。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Test092 {
/**
* 外部类有两种访问级别:public、默认
* 内部类有四种访问级别:public、protected、private、默认
* 内部类和外部类不能重名
* 可使程序结构变得紧凑,但在一定程度上破坏了Java面向对象的思想
*/
public class InnerClass {
// 在外部类中可直接通过内部类的类名访问内部类
// InnerClass i = new InnerClass();
public int getSum(int x, int y) {
return x + y;
}
}

public static void main(String[] args) {
// 在外部类以外的其他类中需要通过内部类的完整类名访问内部类
Test092.InnerClass t = new Test092().new InnerClass();
int s = t.getSum(3, 7);
System.out.println(s);
}
}

(1) 实例内部类

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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
public class Test093 {
/**
* 实例内部类是指没有用static修饰的内部类,也称为非静态内部类
*/

// 特点:在外部类的静态方法和外部类以外的其他类中
// 必须通过外部类实例,来创建内部类的实例
class Inner1 {
}

// 不需要创建外部类实例
Inner1 a = new Inner1();

public void method1() {
// 不需要创建外部类实例
Inner1 b = new Inner1();
}

public static void method2() {
// 需要创建外部类实例
Inner1 c = new Test093().new Inner1();
}

class Inner2 {
// 不需要创建外部类实例
Inner1 d = new Inner1();
}

// 特点:在实例内部类中,可以访问外部类的所有成员
// 如果多层嵌套,则内部类可以访问所有外部类的成员
public int x1 = 100;
static int x2 = 100;
final int x3 = 100;
private int x4 = 100;

public String method3() {
return "实例方法";
}

public static String method4() {
return "静态方法";
}

class Inner3 {
// 访问public的x1
int y1 = x1 + 1;
// 访问static的x2
int y2 = x2 + 1;
// 访问final的x3
int y3 = x3 + 1;
// 访问private的x4
int y4 = x4 + 1;
// 访问实例方法method3
String str3 = method3();
// 访问静态方法method4
String str4 = method4();
}

// 特点:在外部类中不能直接访问内部类的成员,而必须通过内部类的实例去访问
// 例如类A包含内部类B,类B包含内部类C,A不能直接访问C,要通过B的实例访问C
// 特点:外部类实例与内部类实例是一对多的关系
// 即一内部类实例只对应一外部类实例,而一个外部类实例可对应多个内部类实例
// 特点:在实例内部类中不能定义static成员,除非同时使用final和static修饰
int g = 105;

class Inner4 {
int g = 110;
int z1 = g;
int z2 = this.g;
int z3 = Test093.this.g;
}

public static void main(String[] args) {
// 创建内部类实例
Inner3 f = new Test093().new Inner3();
System.out.println(f.y1);
System.out.println(f.y2);
System.out.println(f.y3);
System.out.println(f.y4);
System.out.println(f.str3);
System.out.println(f.str4);

Inner4 h = new Test093().new Inner4();
System.out.println(h.z1);
System.out.println(h.z2);
System.out.println(h.z3);
}
}

class OtherDemo1 {
// 需要创建外部类实例
Test093.Inner1 e = new Test093().new Inner1();
}

(2) 静态内部类

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
public class Test094 {
/**
* 静态内部类是指使用static修饰的内部类
*/
static class Inner1 {
}

static class Inner2 {
// 特点:静态内部类中可以定义实例成员和静态成员
// 外部类以外的其他类需通过完整的类名访问静态内部类中的静态成员
// 如果要访问静态内部类中的实例成员,则需要通过静态内部类的实例
int a1 = 1;
static int b1 = 1;
}

// 特点:静态内部类可以直接访问外部类的静态成员
// 如果要访问外部类的实例成员,则需要通过外部类的实例去访问
int c1 = 0;
static int d1 = 0;

static class Inner3 {
Test094 t = new Test094();
// 访问实例变量和静态变量
int c2 = t.c1;
int d2 = d1;
}

public static void main(String[] args) {
System.out.println();
}
}

class OtherDemo2 {
// 特点:在创建静态内部类的实例时,不需要创建外部类的实例
Test094.Inner1 x1 = new Test094.Inner1();

Test094.Inner2 x2 = new Test094.Inner2();
// 访问实例成员和静态成员
int a2 = x2.a1;
int b2 = x2.b1;
}

(3) 局部内部类

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
public class Test095 {
/**
* 局部内部类是指在一个方法中定义的内部类
*/

// 特点:与局部变量一样,不能使用访问控制修饰符和static修饰符修饰
// 访问控制修饰符:public、private、protected
// 特点:局部内部类只在当前方法中有效,不能定义static成员

// 编译出错:局部内部类只在当前方法中有效
// Inner1 i1 = new Inner1();
// Test095.Inner1 i2 = new Test095.Inner1();
// Test095.Inner1 i3 = new Test095().new Inner1();
public void method1() {
// 特点:局部内部类中还可以包含内部类
// 但这些内部类也不能使用访问控制修饰符和static修饰符修饰
class Inner1 {
}
Inner1 i4 = new Inner1();
}

int a1 = 100;
int b1 = 100;

public void method2() {
int c1 = 100;
final int d1 = 100;
final int b1 = 200;
// 特点:局部内部类中可以访问外部类的所有成员
class Inner2 {
// 访问外部类中的成员
int a2 = a1;
// 访问方法中的成员
int d2 = d1;
int b2 = b1;
// 访问外部类中的成员
int b3 = Test095.this.b1;
}
// 特点:在局部内部类中只可以访问当前方法中final类型的参数与变量
// 如果方法中的成员与外部类中的成员同名
// 则可使用<OuterClassName>.this.<MemberName>形式访问外部类中的成员
Inner2 i5 = new Inner2();
System.out.println(i5.b2);
System.out.println(i5.b3);
}

public static void main(String[] args) {
Test095 t = new Test095();
t.method2();
}
}

(4) 匿名内部类

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
class Out {
void show() {
System.out.println("调用Out类的show()方法!");
}
}

public class Test096 {
/**
* 匿名类是指没有类名的内部类,必须在创建时使用new语句来声明类
* 实现方式:继承一个类,重写其方法,或实现一个接口再实现其方法
*/

// 在方法中构造一个匿名内部类
private void show() {
int a = 100;
final int b = 200;
Out anonyInter = new Out() {
// 获取匿名内部类的实例
void show() {
// 特点:匿名类和局部内部类一样,可以访问外部类的所有成员
System.out.println(a);
System.out.println(b);
System.out.println(i);
System.out.println("调用匿名类中的show()方法!");
}

// 特点:匿名类中允许使用非静态代码块进行成员初始化操作
// 特点:匿名类的非静态代码块会在父类的构造方法后被执行
int i;
{
i = 100;
}
};
anonyInter.show();
}

public static void main(String[] args) {
Test096 t = new Test096();
t.show();
}
}

3-5 新特性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Test097 {
/**
* 局部内部类和匿名内部类访问的局部变量必须由final修饰
* 从Java 8开始可以不加final修饰符,由系统默认进行添加
* Java将该功能称为Effectively final功能(Java 8新特性)
*/
public static void main(String[] args) {
String name = "张三";
Runnable r = new Runnable() {
@Override
public void run() {
// 一个非final的局部变量或方法参数
// 其值在初始化后就从未更改,那么该变量就是effectively final
System.out.println(name);
}
};
// Lambda表达式和匿名内部类访问的局部变量必须是final
// 只是不需要显式地声明变量为final,从而节省时间
r.run();
}
}

(1) Lambda

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
62
// 可计算接口,Lambda表达式实现的接口是函数式接口
// 函数式接口,有且只有一个抽象的方法(Object类中的方法不包括在内)
// 如果接口中声明多个抽象方法,那么Lambda表达式会发生编译错误
// Java 8提供了一个声明函数式接口的注解@FunctionalInterface
@FunctionalInterface
interface Calculable1 {
int calculateInt(int a, int b);
}

public class Test098 {
/**
* Lambda表达式是一个匿名函数,基于数学中的λ演算得名,也可称为闭包
* 是推动Java 8发布的重要新特性,允许函数作为参数传递进方法中
* ->被称为箭头操作符或Lambda操作符,->将Lambda表达式拆分成两部分
* 左侧是表达式的参数列表,右侧是Lambda体,即表达式中所需执行的功能
*/
public static Calculable1 calculable(char opr) {
Calculable1 result;
/*
if (opr == '+') {
// 匿名内部类实现Calculable1接口
result = new Calculable1() {
// 实现加法运算
@Override
public int calculateInt(int a, int b) {
return a + b;
}
};
} else {
// 匿名内部类实现Calculable1接口
result = new Calculable1() {
// 实现减法运算
@Override
public int calculateInt(int a, int b) {
return a - b;
}
};
}
*/
if (opr == '+') {
// Lambda表达式实现Calculable1接口
result = (int a, int b) -> {
return a + b;
};
} else {
// Lambda表达式实现Calculable1接口
result = (int a, int b) -> {
return a - b;
};
}
return result;
}

public static void main(String[] args) {
int n1 = 23;
int n2 = 11;
Calculable1 f1 = calculable('+');
Calculable1 f2 = calculable('-');
System.out.println(f1.calculateInt(n1, n2));
System.out.println(f2.calculateInt(n1, n2));
}
}

(2) 作为参数使用

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
@FunctionalInterface
interface Calculable2 {
int calculateInt(int a, int b);
}

public class Test099 {
/**
* Lambda表达式一种常见的用途就是作为参数传递给方法
* 这需要声明参数的类型声明为函数式接口类型
*/
public static void main(String[] args) {
int n1 = 7;
int n2 = 2;
// 打印加法计算结果
display((a, b) -> {
return a + b;
}, n1, n2);
// 打印减法计算结果
display((a, b) -> a - b, n1, n2);
}

public static void display(Calculable2 calc, int n1, int n2) {
System.out.println(calc.calculateInt(n1, n2));
}
}

(3) 访问成员变量

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
@FunctionalInterface
interface Calculable3 {
int calculateInt(int a, int b);
}

public class Test100 {
/**
* Lambda表达式可以访问所在外层作用域定义的变量,包括成员变量、局部变量
* 成员变量包括实例成员变量、静态成员变量,Lambda表达式可访问这些成员变量
* 此时的Lambda表达式与普通方法一样,可以读取成员变量,也可以修改成员变量
*/

// 实例成员变量
private int value = 8;
// 静态成员变量
private static int staticValue = 3;

// 静态方法,进行加法运算
public static Calculable3 add() {
Calculable3 result = (int a, int b) -> {
// 访问静态成员变量,不能访问实例成员变量
staticValue++;
// this.value;
int c = a + b + staticValue;
return c;
};
return result;
}

// 实例方法,进行减法运算
public Calculable3 sub() {
Calculable3 result = (int a, int b) -> {
// 访问静态成员变量和实例成员变量
staticValue++;
this.value++;
int c = a - b - staticValue - this.value;
return c;
};
return result;
}

public static void main(String[] args) {
Calculable3 addFunc = Test100.add();
int addResult = addFunc.calculateInt(5, 7);
System.out.println(staticValue);
System.out.println("调用add():5 + 7 + staticValue = " + addResult);

Test100 testInstance = new Test100();
Calculable3 subFunc = testInstance.sub();
int subResult = subFunc.calculateInt(9, 4);
System.out.println(staticValue);
System.out.println("调用sub(): 9 - 4 - staticValue - value = " + subResult);
}
}

(4) 访问局部变量

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
@FunctionalInterface
interface Calculable4 {
int calculateInt(int a, int b);
}

public class Test101 {
/**
* 对于成员变量的访问Lambda表达式与普通方法没有区别
* 但访问局部变量时,变量必须是final类型的(不可改变)
*/

// 实例成员变量
private int value = 8;
// 静态成员变量
private static int staticValue = 3;

// 静态方法,进行加法运算
public static Calculable4 add() {
// 局部变量
int localValue = 9;
Calculable4 result = (int a, int b) -> {
// 编译错误
// localValue++;
int c = a + b + staticValue;
return c;
};
return result;
}

// 实例方法,进行减法运算
public Calculable4 sub() {
// final局部变量
final int localValue = 9;
Calculable4 result = (int a, int b) -> {
int c = a - b - staticValue - this.value;
// 编译错误
// localValue = c;
return c;
};
return result;
}

// Lambda表达式只能访问局部变量而不能修改
// 否则编译错误,但对静态变量和成员变量可读可写
public static void main(String[] args) {
Calculable4 addFunc = Test101.add();
int addResult = addFunc.calculateInt(5, 7);
System.out.println(staticValue);
System.out.println("调用add():5 + 7 + staticValue = " + addResult);

Test101 testInstance = new Test101();
Calculable4 subFunc = testInstance.sub();
int subResult = subFunc.calculateInt(9, 4);
System.out.println(staticValue);
System.out.println("调用sub(): 9 - 4 - staticValue - value = " + subResult);
}
}

(5) 使用方法引用

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
@FunctionalInterface
interface Calculable5 {
int calculateInt(int a, int b);
}

// 被引用方法必须与函数式接口方法的参数列表和返回值类型一致
class LambdaTest {
// 静态方法,加法运算,参数列表与函数式接口方法calculateInt()兼容
public static int add(int a, int b) {
return a + b;
}

// 实例方法,减法运算,参数列表与函数式接口方法calculateInt()兼容
public int sub(int a, int b) {
return a - b;
}
}

public class Test102 {
/**
* 方法引用可理解为Lambda表达式的快捷写法,简洁,可读性更高
* 实现简单,复用不多推荐Lambda表达式,否则应该采用方法引用
* Java 8之后增加了双冒号::运算符,用于方法引用
* 方法引用与Lambda表达式有关,与函数式接口有关
*/
public static void main(String[] args) {
int n1 = 6;
int n2 = 3;
// LambdaTest::add,静态方法的方法引用
display(LambdaTest::add, n1, n2);
LambdaTest d = new LambdaTest();
// d::sub,实例方法的方法引用
display(d::sub, n1, n2);
}

public static void display(Calculable5 calc, int n1, int n2) {
System.out.println(calc.calculateInt(n1, n2));
}
}

4 集合

  • 集合
    • 为保存数量不确定或具映射关系的数据(关联数组),提供了集合类。
    • 集合类主要负责保存、盛装其他数据,因此集合类也被称为容器类。
    • Java所有集合类都位于java.util包下,提供了一个表示和操作对象集合的统一构架。
    • 集合类型分为Collection和Map,都是集合的根接口,又包含了一些子接口或者实现类。

集合

4-1 Collection

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
import java.util.Iterator;
import java.util.ArrayList;

public class Test103 {
/**
* Collection接口常用方法,可操作Set集合、List集合、Queue集合
* 方法可查:docs.oracle.com/javase/8/docs/api/help-doc.html
*/
public static void main(String[] args) {
ArrayList list1 = new ArrayList();
ArrayList list2 = new ArrayList();
list1.add("one");
list1.add("two");
System.out.println(list1.size());

// retainAll()方法的作用与removeAll()相反
// 即保留两个集合中相同的元素,其他全部删除
list2.addAll(list1);
list2.add("six");
// 删除第2个元素
list2.remove(1);
System.out.println(list2.size());

Iterator it1 = list1.iterator();
while (it1.hasNext()) {
System.out.print(it1.next() + " ");
}
System.out.println("");

Iterator it2 = list2.iterator();
while (it2.hasNext()) {
System.out.print(it2.next() + " ");
}
}
}

4-2 Java List集合

  • Java List集合
    • List是一个有序、可重复的集合,集合中每个元素都有其对应的顺序索引。
    • List集合允许使用重复的元素,可以通过索引来访问指定位置的集合元素。
    • List实现了Collection接口,主要有两个常用的实现类:ArrayList类和LinkedList类。
    • ArrayList是基于动态数组数据结构的实现,访问元素的速度相比于LinkedList较优。
    • LinkedList基于链表数据结构的实现,占用内存空间较大,批量插入或删除数据时优。

(1) 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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;

// 商品类
class Product {
private int id;
private String name;
private float price;

public Product(int id, String name, float price) {
this.id = id;
this.name = name;
this.price = price;
}

public void setId(int id) {
this.id = id;
}

public int getId() {
return id;
}

public void setName(String name) {
this.name = name;
}

public String getName() {
return name;
}

public void setPrice(float price) {
this.price = price;
}

public float getPrice() {
return price;
}

public String toString() {
return "编号:" + id + ",名称:" + name + ",价格:" + price;
}
}

public class Test104 {
/**
* ArrayList类实现了可变数组的大小,存储在内的数据称为元素
* 提供快速基于索引访问元素的方式,对尾部成员的增加和删除支持较好
* 构造方法的两种重载形式:ArrayList()用于构造一个初始容量为10的空列表
* ArrayList(Collection<? extends E>c)构造包含指定Collection元素的列表
*/
public static void main(String[] args) {
Product p1 = new Product(3, "洗发露", 59);
Product p2 = new Product(4, "沐浴露", 39);
Product p3 = new Product(5, "护发素", 39);
ArrayList list = new ArrayList();
list.add(p1);
list.add(p2);
list.add(p3);
for (int i = 0; i < list.size(); i++) {
// get():获取此集合中指定索引位置的元素
Product p = (Product) list.get(i);
System.out.println(p);
}

// 前提:指定的对象在List集合中有重复的对象
// indexOf():获得指定对象的最小索引位置
// lastIndexOf():获得指定对象的最大索引位置
ArrayList l = new ArrayList();
l.add("one");
l.add("|");
l.add("two");
l.add("|");
l.add("six");
l.add("|");
l.add("ten");
System.out.println(l.size());
Iterator it1 = l.iterator();
while (it1.hasNext()) {
System.out.print(it1.next() + " ");
}
System.out.println();
System.out.println("首次出现的位置:" + l.indexOf("|"));
System.out.println("最后出现的位置:" + l.lastIndexOf("|"));

// subList():截取List集合中部分元素,包括起始位置,不包括结束位置
List sublist = new ArrayList();
sublist = l.subList(2, 7);
System.out.println(sublist.size());
Iterator it2 = l.iterator();
while (it2.hasNext()) {
System.out.print(it2.next() + " ");
}
}
}

(2) LinkedList

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
import java.util.LinkedList;

public class Test105 {
/**
* LinkedList类采用链表结构保存对象,结构优点便于向集合中插入或删除元素
* 频繁向集合中插入和删除元素时,使用LinkedList类比ArrayList类效率高
* 但是LinkedList类随机访问元素(检索特定索引位置的元素)的速度相对较慢
*/
public static void main(String[] args) {
// 创建集合对象
LinkedList<String> products = new LinkedList<String>();
String p1 = new String("洗洁精");
String p2 = new String("晾衣架");
String p3 = new String("洗发露");
String p4 = new String("沐浴露");

// 将对象添加到集合中
products.add(p1);
products.add(p2);
products.add(p3);
products.add(p4);

String p5 = new String("面巾纸");

// 向集合的末尾添加对象
products.addLast(p5);
for (int i = 0; i < products.size(); i++) {
System.out.println(products.get(i) + "\t");
}
System.out.println("第一个商品:" + products.getFirst());
System.out.println("最后一商品:" + products.getLast());

// 删除最后一个元素
products.removeLast();
for (int i = 0; i < products.size(); i++) {
System.out.println(products.get(i) + "\t");
}
}
}

4-3 Java Set集合

  • Java Set集合
    • Set集合中的对象不按特定方式排序,不能包含重复对象,且最多只允许包含一null。
    • Set集合实现了Collection接口,主要有两个常用的实现类:HashSet类和TreeSet类。

(1) 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
import java.util.HashSet;
import java.util.Iterator;

public class Test106 {
/**
* 大多时候使用Set集合时就使用HashSet实现类
* 构造方法的两种重载形式:HashSet()用来构造一个新的空Set集合
* HashSet(Collection<? extends E>c)构造包含指定集合元素的新集合
* 若向Set集合中添加两个相同的元素,则后添加的会覆盖前面添加的元素
*/
public static void main(String[] args) {
// 创建一个空Set集合
HashSet<String> courseSet = new HashSet<String>();
String c1 = new String("香蕉");
String c2 = new String("苹果");
String c3 = new String("草莓");
String c4 = new String("芭乐");

// 将对象存储到集合中
courseSet.add(c1);
courseSet.add(c2);
courseSet.add(c3);
courseSet.add(c4);

Iterator<String> it = courseSet.iterator();
while (it.hasNext()) {
System.out.println((String) it.next());
}
System.out.println("个数:" + courseSet.size());
}
}

(2) 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
import java.util.Scanner;
import java.util.TreeSet;
import java.util.Iterator;
import java.util.SortedSet;

public class Test107 {
/**
* TreeSet类同时实现了Set接口和SortedSet接口
* SortedSet接口是Set接口的子接口,可实现对集合元素的自然排序(升序排序)
* TreeSet只能对实现了Comparable接口的类对象进行排序
* 使用自然排序时只能向TreeSet集合中添加相同数据类型的对象,否则抛出异常
*/
public static void main(String[] args) {
// 创建TreeSet集合
TreeSet<Double> scores = new TreeSet<Double>();
Scanner input = new Scanner(System.in);
for (int i = 0; i < 5; i++) {
System.out.print("第" + (i + 1) + "个学生成绩:");
double score = input.nextDouble();
// 将成绩转换为Double类型,再添加到TreeSet集合中
scores.add(Double.valueOf(score));
}

// 查询90分以上学生成绩
SortedSet<Double> s2 = scores.tailSet(90.0);
System.out.print("90以上成绩有:");
for (int i = 0; i < s2.toArray().length; i++) {
System.out.print(s2.toArray()[i] + "\t");
}

// 查询不及格学生成绩
SortedSet<Double> s1 = scores.headSet(60.0);
System.out.print("\n不及格成绩有:");
for (int i = 0; i < s1.toArray().length; i++) {
System.out.print(s1.toArray()[i] + "\t");
}

// 创建Iterator对象
Iterator<Double> it = scores.iterator();
System.out.print("\n学生成绩排序:");
while (it.hasNext()) {
System.out.print(it.next() + "\t");
}

System.out.print("\n输入查询成绩:");
double searchScore = input.nextDouble();
if (scores.contains(searchScore)) {
System.out.println("成绩为" + searchScore + "的学生存在!");
} else {
System.out.println("没有成绩为" + searchScore + "的学生!");
}
}
}

4-4 Java Map集合

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
import java.util.HashMap;
import java.util.Scanner;
import java.util.Iterator;

public class Test108 {
/**
* Map是一种键值对集合,用于保存具有映射关系的数据
* key和value可以是任何引用类型的数据,key不允许重复,value允许
* Map接口主要有两个实现类:HashMap类和TreeMap类
* HashMap类按哈希算法来存取键对象,TreeMap类可对键对象进行排序
*/
public static void main(String[] args) {
HashMap users = new HashMap();
users.put("11", "张三");
users.put("12", "李四");
users.put("13", "王五");
users.put("14", "赵六");
users.put("15", "孙七");
Iterator it = users.keySet().iterator();
while (it.hasNext()) {
Object key = it.next();
Object val = users.get(key);
System.out.println("学号:" + key + ",姓名:" + val);
}

Scanner input = new Scanner(System.in);
System.out.print("输入要删除的学号:");
int num = input.nextInt();
// 判断是否包含指定键
if (users.containsKey(String.valueOf(num))) {
// 包含就删除
users.remove(String.valueOf(num));
} else {
System.out.println("学号对应的姓名不存在!");
}
it = users.keySet().iterator();
while (it.hasNext()) {
Object key = it.next();
Object val = users.get(key);
System.out.println("学号:" + key + ",姓名:" + val);
}
}
}

4-5 遍历 Map集合

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
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;

public class Test109 {
/**
* Map有两组值,可只遍历值的集合,或只遍历键的集合,亦或同时遍历
* Map以及实现Map的接口类,都可以使用以下几种方式进行遍历
* 接口类,如HashMap、TreeMap、LinkedHashMap、Hashtable等
*/
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("11", "张三");
map.put("12", "李四");
map.put("13", "王五");

// for循环中使用entries实现Map的遍历(最常见)
for (Map.Entry<String, String> entry : map.entrySet()) {
String mapKey = entry.getKey();
String mapVal = entry.getValue();
System.out.println(mapKey + ": " + mapVal);
}
System.out.println("********");

// for-each循环遍历key或values,性能上比entrySet好
for (String key : map.keySet()) {
System.out.println(key);
}
System.out.println("********");

// 迭代器Iterator遍历
Iterator<Entry<String, String>> entries = map.entrySet().iterator();
while (entries.hasNext()) {
Entry<String, String> entry = entries.next();
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + ": " + value);
}
System.out.println("********");

// 通过键找值遍历,效率较低
for (String key : map.keySet()) {
String value = map.get(key);
System.out.println(key + ": " + value);
}
}
}

4-6 Collections类

  • Collections类
    • reverse(list):对指定List集合元素进行逆向排序。
    • shuffle(list):对List集合元素进行随机排序(洗牌)。
    • sort(list):依据元素的自然顺序对指定List集合的元素升序排序。
    • swap(list, i, j):将指定List集合中的元素i和元素j进行交换。
    • rotate(list, distance):该方法不会改变集合的长度。
      • distance为正数时,将List集合的后distance个元素整体移到前面。
      • distance为负数时,将List集合的前distance个元素整体移到后面。

(1) 升序

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
import java.util.List;
import java.util.Scanner;
import java.util.ArrayList;
import java.util.Collections;

public class Test110 {
/**
* Collections类是Java提供的一个操作Set、List和Map等集合的工具类
*/
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
List prices = new ArrayList();
for (int i = 0; i < 5; i++) {
System.out.print("请输入第" + (i + 1) + "个商品的价格:");
int p = input.nextInt();
prices.add(Integer.valueOf(p));
}

// 调用sort()方法对集合进行排序
Collections.sort(prices);
for (int i = 0; i < prices.size(); i++) {
System.out.print(prices.get(i) + "\t");
}
}
}

(2) 反转

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

public class Test111 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
List goods = new ArrayList();
for (int i = 0; i < 5; i++) {
System.out.print("请输入第" + (i + 1) + "个商品的名称:");
String name = input.next();
goods.add(name);
}

// 调用reverse()方法对集合进行反转排序,非降序排序
Collections.reverse(goods);
for (int i = 0; i < 5; i++) {
System.out.print(goods.get(i) + "\t");
}
}
}

(3) 替换

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.List;
import java.util.Scanner;
import java.util.ArrayList;
import java.util.Collections;

public class Test112 {
/**
* binarySearch(list, key):使用二分搜索法搜索指定的List集合
* max(coll):根据元素的自然顺序,返回给定集合中的最大元素
* min(coll):根据元素的自然顺序,返回给定集合中的最小元素
* fill(list, obj):使用指定元素obj替换指定List集合中的所有元素
* frequency(coll, o):返回指定集合中指定元素的出现次数
* indexOfSubList():返回子List对象在父List对象中第一次出现的位置索引
* lastIndexOfSubList():返回子List对象在父List对象最后出现的位置索引
* replaceAll():使用一个新值newVal替换List对象的所有旧值oldVal
*/
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
List products = new ArrayList();
for (int i = 0; i < 3; i++) {
System.out.print("请输入第" + (i + 1) + "个商品的名称:");
String name = input.next();
products.add(name);
}
System.out.println("将所有商品名称全更改为:未填写");
Collections.fill(products, "未填写");
System.out.print("重置后的商品名称信息为:");
for (int i = 0; i < products.size(); i++) {
System.out.print(products.get(i) + "\t");
}
}
}

(4) 查找

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
import java.util.ArrayList;
import java.util.Collections;

public class Test113 {
public static void main(String[] args) {
ArrayList nums = new ArrayList();
nums.add(2);
nums.add(3);
nums.add(7);
nums.add(5);
nums.add(-5);

System.out.println("最大值:" + Collections.max(nums));
System.out.println("最小值:" + Collections.min(nums));
System.out.println(nums);
Collections.replaceAll(nums, 2, 1);
System.out.println(nums);

// 排序
Collections.sort(nums);
System.out.println(nums);

// 排序后使用二分法查询3在集合中出现的位置
System.out.println("3在集合中出现的位置:" + Collections.binarySearch(nums, 3));

// 判断3在List集合中出现的次数,返回1
System.out.println("3在集合中出现的次数:" + Collections.frequency(nums, 3));
}
}

(5) 复制

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
import java.util.List;
import java.util.Scanner;
import java.util.ArrayList;
import java.util.Collections;

public class Test114 {
/**
* copy()静态方法用于将指定集合中的所有元素复制到另一个集合中
* 如果目标集合的长度更长,则不影响目标集合中的其余元素
* 如果目标集合长度不够而无法包含整个源集合元素,程序将抛出异常
*/
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
List srcList = new ArrayList();
List desList = new ArrayList();
desList.add("苹果");
desList.add("草莓");
desList.add("香蕉");
desList.add("芭乐");
desList.add("山竹");

System.out.println("原有水果名称如下:");
for (int i = 0; i < desList.size(); i++) {
System.out.print(desList.get(i) + "\t");
}

System.out.println("\n输入替换水果名称:");
for (int i = 0; i < desList.size(); i++) {
System.out.print("第" + (i + 1) + "个水果名称:");
String name = input.next();
srcList.add(name);
}

// 调用copy()方法将当前水果信息复制到原有水果信息集合中
Collections.copy(desList, srcList);
System.out.println("当前水果名称如下:");
for (int i = 0; i < desList.size(); i++) {
System.out.print(desList.get(i) + "\t");
}
}
}

4-7 Collection集合

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

public class Test115 {
/**
* 使用Lambda表达式来遍历集合元素
*/
public static void main(String[] args) {
// 创建一个集合
Collection objs = new HashSet();
objs.add("苹果");
objs.add("山楂");
objs.add("葡萄");

// 调用forEach()方法遍历集合
objs.forEach(obj -> System.out.print(obj + "\t"));
}
}

(1) Iterator遍历

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
import java.util.HashSet;
import java.util.Iterator;
import java.util.Collection;

public class Test116 {
/**
* Iterator(迭代器)是一个接口,作用是遍历容器的所有元素
*/
public static void main(String[] args) {
// 创建一个集合
Collection objs = new HashSet();
objs.add("苹果");
objs.add("山楂");
objs.add("葡萄");

// Iterator接口遍历集合元素
Iterator it = objs.iterator();
while (it.hasNext()) {
String obj = (String) it.next();
System.out.print(obj + "\t");
if (obj.equals("山楂")) {
objs.remove(obj);
// it.remove();
}
// obj = "香蕉";
}
// System.out.println(objs);
}
}

(2) Lambda遍历

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

public class Test117 {
public static void main(String[] args) {
// 创建一个集合
Collection objs = new HashSet();
objs.add("苹果");
objs.add("山楂");
objs.add("葡萄");

// Iterator接口遍历集合元素
Iterator it = objs.iterator();

// 使用Lambda表达式(目标类型是Comsumer)来遍历集合元素
it.forEachRemaining(obj -> System.out.print(obj + "\t"));
}
}

(3) foreach循环遍历

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

public class Test118 {
public static void main(String[] args) {
// 创建一个集合
Collection objs = new HashSet();
objs.add("苹果");
objs.add("山楂");
objs.add("葡萄");

for (Object obj : objs) {
String o = (String) obj;
System.out.print(o + "\t");
if (o.equals("山楂")) {
objs.remove(obj);
}
}
}
}

4-8 Java 8新增特性

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
import java.util.HashSet;
import java.util.Collection;
import java.util.function.Predicate;

public class Test119 {
/**
* Java 8新增removeIf(filter)方法,批量删除符合filter条件的所有元素
*/
public static void main(String[] args) {
// 创建一个集合
Collection objs = new HashSet();
objs.add(new String("梨"));
objs.add(new String("苹果"));
objs.add(new String("山楂"));
objs.add(new String("葡萄"));
objs.add(new String("哈密瓜"));
objs.add(new String("水蜜桃"));
objs.add(new String("火龙果"));

// 统计“果”字符串的数量
System.out.println(calAll(objs, ele -> ((String) ele).contains("果")));

// Lambda表达式(目标类型Predicate)过滤集合,所有长度小于3的都删除
objs.removeIf(ele -> ((String) ele).length() < 3);
System.out.println(objs);
}

public static int calAll(Collection fruits, Predicate p) {
int total = 0;
for (Object obj : fruits) {
// 使用Predicate的test()方法判断该对象是否满足Predicate指定的条件
if (p.test(obj)) {
total++;
}
}
return total;
}
}

4-9 Stream流式API

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
import java.util.HashSet;
import java.util.Collection;
import java.util.stream.IntStream;

public class Test120 {
/**
* Java 8新增Stream、IntStream、LongStream、DoubleStream等流式API
* Stream是一个通用的流接口,而IntStream等则代表元素类型为int等对应流
* Stream提供大量的方法进行聚焦操作,既可以是中间方法,也可以是末端方法
* 中间方法:中间操作允许流保持打开状态,并允许直接调用后续方法,例如map
* 末端方法:对某个流执行该方法后,该流将会被消耗且不再可用,例如count等
*/
public static void main(String[] args) {
IntStream is = IntStream.builder().add(20).add(13).add(-5).add(18).add(21).build();

// 以下调用聚集方法,每次只能执行一行
// Java流(Stream)只允许被消费一次,调用方法后流就会被关闭
// System.out.println("总 数:" + is.count());
// System.out.println("最大值:" + is.max().getAsInt());
// System.out.println("最小值:" + is.min().getAsInt());
// System.out.println("总 和:" + is.sum());
// System.out.println("平均值:" + is.average());
// System.out.println("判断元素平方是否大于20:" + is.allMatch(ele -> ele * ele > 20));
// System.out.println("任何元素平方是否大于20:" + is.anyMatch(ele -> ele * ele > 20));

// 将is映射成一个新Stream,每个元素是原Stream元素的2倍+1
IntStream newIs = is.map(ele -> ele * 2 + 1);
newIs.forEach(System.out::println);

// 创建一个集合
Collection objs = new HashSet();
objs.add(new String("梨"));
objs.add(new String("苹果"));
objs.add(new String("山楂"));
objs.add(new String("葡萄"));
objs.add(new String("哈密瓜"));
objs.add(new String("水蜜桃"));
objs.add(new String("火龙果"));

// 统计集合中出现字符串长度大于2的数量
// System.out.println("数量:" + objs.stream().filter(ele -> ((String) ele).length() > 2).count());
// 统计集合中出现“果”字符串的数量
// System.out.println("数量:" + objs.stream().filter(ele -> ((String) ele).contains("果")).count());
// 先调用Collection对象的stream()方法将集合转换为Stream
// 再调用Stream的mapToInt()方法获取原有Stream对应的IntStream
objs.stream().mapToInt(ele -> ((String) ele).length())
// 调用forEach()方法遍历InStream中的每个元素
.forEach(System.out::println);
}
}

5 泛型

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.Map;
import java.util.List;
import java.util.HashMap;
import java.util.ArrayList;

class Books {
private int Id;
private String Name;
private double Price;

// 构造方法
public Books(int id, String name, double price) {
this.Id = id;
this.Name = name;
this.Price = price;
}

public String toString() {
return this.Id + "," + this.Name + "," + this.Price;
}
}

public class Test121 {
/**
* Java 1.5开始提供泛型,可在编译时检查类型安全
* 并且所有的强制转换都是自动和隐式的,提高代码重用率
* 泛型本质上是提供类型的“类型参数”,也就是参数化类型
*/
public static void main(String[] args) {
// 创建3个Book对象
Books b1 = new Books(1, "《昆虫记》", 13.5);
Books b2 = new Books(2, "《西游记》", 24.5);
Books b3 = new Books(3, "《在人间》", 19.5);

// 定义泛型Map集合
Map<Integer, Books> books = new HashMap<Integer, Books>();
books.put(1001, b1);
books.put(1002, b2);
books.put(1003, b3);
for (Integer id : books.keySet()) {
// 遍历键
System.out.print(id + "—");
// 不需要类型转换
System.out.println(books.get(id));
}

// 定义泛型的List集合
List<Books> bookList = new ArrayList<Books>();
bookList.add(b1);
bookList.add(b2);
bookList.add(b3);
for (int i = 0; i < bookList.size(); i++) {
// 不需要类型转换
System.out.println(bookList.get(i));
}
}
}

5-1 泛型类

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
class Stu<N, A, S> {
private N name;
private A age;
private S sex;

// 创建类的构造函数
public Stu(N name, A age, S sex) {
this.name = name;
this.age = age;
this.sex = sex;
}

// 属性的setter、getter方法
public N getName() {
return name;
}

public void setName(N name) {
this.name = name;
}

public A getAge() {
return age;
}

public void setAge(A age) {
this.age = age;
}

public S getSex() {
return sex;
}

public void setSex(S sex) {
this.sex = sex;
}
}

public class Test122 {
/**
* 泛型类一般用于类中的属性类型不确定的情况下
*/
public static void main(String[] args) {
// 实例化泛型类时需指明泛型类中的类型参数,并赋予泛型类属性相应类型的值
Stu<String, Integer, Character> stu = new Stu("张三", 28, '男');
String name = stu.getName();
Integer age = stu.getAge();
Character sex = stu.getSex();
System.out.println("姓名:" + name + "\n年龄:" + age + "\n性别:" + sex);
}
}

5-2 泛型方法

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
class BookDemo {
private int Id;
private String Name;
private double Price;

// 构造方法
public BookDemo(int id, String name, double price) {
this.Id = id;
this.Name = name;
this.Price = price;
}

public String toString() {
return this.Id + "," + this.Name + "," + this.Price;
}
}

public class Test123 {
/**
* 是否拥有泛型方法,与其所在的类是不是泛型没有关系
* 若static方法需使用泛型能力,就必须使其成为泛型方法
*/
public static void main(String[] args) {
BookDemo stu = new BookDemo(1, "《红楼梦》", 15.9);
// 调用泛型方法
List(stu);
}

// 定义泛型方法,一般返回值类型至少有一个参数类型应是泛型且类型一致
// 只返回值类型或参数类型之一使用了泛型,则该泛型方法的使用就被限制了
public static <T> void List(T book) {
if (book != null) {
System.out.println(book);
}
}
}

5-3 高级用法

  • 高级用法
    • 泛型的用法非常灵活,可以在集合、类和方法中使用。
    • 高级用法包括限制泛型可用类型、使用类型通配符等。

(1) 限制可用类

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
import java.util.List;
import java.util.ArrayList;
import java.util.LinkedList;

// 限制Test124的泛型类型必须实现List接口
public class Test124<T extends List> {
/**
* 限制泛型可用类型:class 类名称<T extends anyClass>
* 使用泛型限制后,泛型类的类型必须实现或继承anyClass这个接口或类
* 未使用extends限制泛型类型时,实际默认使用Object类作为泛型类型
* 因此Object类下的所有子类都可以实例化泛型类对象
*/
public static void main(String[] args) {
// 实例化使用ArrayList的泛型类Test124,正确
Test124<ArrayList> lc1 = new Test124<ArrayList>();

// 实例化使用LinkedList的泛型类Test124,正确
Test124<LinkedList> lc2 = new Test124<LinkedList>();

// 实例化使用HashMap的泛型类Test124,错误,HashMap未实现List接口
// Test124<HashMap> lc3 = new Test124<HashMap>();

System.out.println("泛型限制测试完成!");
}
}

(2) 类型通配符

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
import java.util.List;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.LinkedList;

class A<T> {
// 泛型类中的一些方法和属性
private T value;

public A(T value) {
this.value = value;
}

public T get() {
return value;
}
}

public class Test125 {
/**
* 泛型类型通配符的语法格式:泛型类名称<? extends List>a = null;
* 作用是限定泛型类型必须是某个类或者接口的子类(子接口)或本身
*/
public static void main(String[] args) {
A<ArrayList> a1 = new A<>(new ArrayList());
A<LinkedList> a2 = new A<>(new LinkedList());
// 不能传HashMap,因为HashMap不是List子类
A<HashMap> a3 = new A<>(new HashMap());

// 传参数可以成功
processA(a1);
// processA(a2);
// processA(a3);

System.out.println("类型通配符测试完成!");
}

public static void processA(A<? extends List> a) {
// 此时只能调用get(),不能调用任何修改T的方法
List list = a.get();
System.out.println("List size: " + list.size());
}
}

(3) 继承泛型类

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
// 父类
class FatherClass<T1> {
private T1 value;

public FatherClass(T1 value) {
this.value = value;
}

public T1 getValue() {
return value;
}
}

// 若父类是泛型类,子类在继承时可保留父类的泛型参数
// 也可以用具体的类型来替代,还可以增加新的泛型参数
class SonClass<T1, T2, T3> extends FatherClass<T1> {
private T2 secondValue;
private T3 thirdValue;

public SonClass(T1 value, T2 secondValue, T3 thirdValue) {
super(value);
this.secondValue = secondValue;
this.thirdValue = thirdValue;
}

public T2 getSecondValue() {
return secondValue;
}

public T3 getThirdValue() {
return thirdValue;
}
}

public class Test126 {
public static void main(String[] args) {
SonClass<String, Integer, Double> son = new SonClass<>("父类", 123, 3.5);
System.out.println("父类泛型值: " + son.getValue());
System.out.println("第二个泛型值: " + son.getSecondValue());
System.out.println("第三个泛型值: " + son.getThirdValue());
}
}

(4) 实现泛型接口

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
// 泛型接口
interface Interface1<T1> {
void doSomething(T1 param);
}

// 泛型类实现接口,泛型指定为第二个泛型参数
class SubClass<T1, T2, T3> implements Interface1<T2> {
@Override
public void doSomething(T2 param) {
System.out.println("执行doSomething,参数为:" + param);
}

// 更多方法和逻辑
}

public class Test127 {
public static void main(String[] args) {
SubClass<String, Integer, Double> obj = new SubClass<>();
// T2对应的类型是Integer类型
obj.doSomething(100);

// 调用,如果传入非Integer类型,则编译报错
// obj.doSomething("abc");
}
}

5-4 图书查询

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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import java.util.Map;
import java.util.List;
import java.util.HashMap;
import java.util.ArrayList;

class Category {
private int id;
private String name;

public Category(int id, String name) {
this.id = id;
this.name = name;
}

public String toString() {
return "所属分类:" + this.name;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

class BookInfo {
private int id;
private String name;
private int price;
private String author;
private String startTime;

public BookInfo(int id, String name, int price, String author, String startTime) {
this.id = id;
this.name = name;
this.price = price;
this.author = author;
this.startTime = startTime;
}

public String toString() {
return this.id + "|" + this.name + "|" + this.price
+ "|" + this.author + "|" + this.startTime;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getPrice() {
return price;
}

public void setPrice(int price) {
this.id = price;
}

public String getAuthor() {
return author;
}

public void setAuthor(String author) {
this.author = author;
}

public String getStartTime() {
return startTime;
}

public void setStartTime(String startTime) {
this.startTime = startTime;
}
}

class CategoryDao {
// 定义泛型Map,存储图书信息
public static Map<Category, List<BookInfo>> categoryMap = new HashMap<Category, List<BookInfo>>();

public static void printDeptmentInfo() {
for (Category cate : categoryMap.keySet()) {
System.out.println("所属类别:" + cate.getName());
List<BookInfo> books = categoryMap.get(cate);
System.out.println("图书编号|图书名称|图书价格|图书作者|出版时间");
for (int i = 0; i < books.size(); i++) {
BookInfo b = books.get(i);
System.out.println(b.getId() + "\t"
+ b.getName() + " " + b.getPrice() + "\t"
+ b.getAuthor() + "\t" + b.getStartTime());
}
System.out.println();
}
}
}

public class Test128 {
public static void main(String[] args) {
Category category1 = new Category(1, "文学作品");
Category category2 = new Category(2, "程序设计");

BookInfo book1 = new BookInfo(1, "边城风云", 45, "沈从文", "1934-05-01");
BookInfo book2 = new BookInfo(2, "平凡世界", 50, "路 遥", "1986-01-01");
BookInfo book3 = new BookInfo(3, "围城故事", 42, "钱钟书", "1947-09-01");
BookInfo book4 = new BookInfo(4, "算法导论", 75, "韩家炜", "2006-08-01");
BookInfo book5 = new BookInfo(5, "代码大全", 65, "陈 皓", "2004-06-01");
BookInfo book6 = new BookInfo(6, "设计模式", 60, "李 刚", "2005-12-01");

List<BookInfo> pList1 = new ArrayList<BookInfo>();
pList1.add(book1);
pList1.add(book2);
pList1.add(book3);

List<BookInfo> pList2 = new ArrayList<BookInfo>();
pList2.add(book4);
pList2.add(book5);
pList2.add(book6);

CategoryDao.categoryMap.put(category1, pList1);
CategoryDao.categoryMap.put(category2, pList2);
CategoryDao.printDeptmentInfo();
}
}

6 枚举

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
enum Signal {
// 定义一个枚举类型
GREEN, YELLOW, RED
}

public class Test129 {
/**
* 枚举是一个被命名的整型常数集合,用于声明一组带标识符的常数
* 当一个变量有几种固定可能的取值时,就可以将其定义为枚举类型
* 任意两枚举成员不能同名,且常数值必须在其基础类型的范围之内
* 若没有显式地声明基础类型的枚举,意味着其所对应的基础类型是int
*/
Signal color = Signal.RED;

public void change() {
// 使用枚举可以使switch语句的可读性更强
switch (color) {
case RED:
color = Signal.GREEN;
break;
case YELLOW:
color = Signal.RED;
break;
case GREEN:
color = Signal.YELLOW;
break;
default:
break;
}
}

public static void main(String[] args) {
Test129 light = new Test129();

// 初始状态是RED
System.out.println("初始颜色:" + light.color);

// 按顺序改变颜色并打印结果
for (int i = 0; i < 5; i++) {
light.change();
System.out.println("第" + (i + 1) + "次change后颜色:" + light.color);
}
}
}

6-1 枚举类

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
public class Test130 {
/**
* Java中的每一个枚举都继承自java.lang.Enum类
* 当使用枚举类型成员时,直接使用枚举名称调用成员即可
* 所有枚举实例都可以调用Enum类的方法,例如valueOf等
*/
enum Signal {
// 定义一个枚举类型
RED, GREEN, YELLOW
}

public enum Sex {
male, female
}

public static void main(String[] args) {
for (int i = 0; i < Signal.values().length; i++) {
// values():以数组形式返回枚举类型的所有成员
System.out.println("枚举成员:" + Signal.values()[i]);

// ordinal():获取成员在枚举中的索引位置
System.out.println("索引" + Signal.values()[i].ordinal() + ",值:" + Signal.values()[i]);
}

// valueOf():将普通字符串转换为枚举实例
compare(Sex.valueOf("male"));
}

public static void compare(Sex s) {
for (int i = 0; i < Sex.values().length; i++) {
// compareTo():比较两个枚举成员在定义时的顺序
System.out.println(s + "与" + Sex.values()[i] + "的比较结果:" + s.compareTo(Sex.values()[i]));
}
}
}

6-2 添加方法

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
// 创建一个枚举类型WeekDay,并添加自定义方法
enum WeekDay {
// 枚举的成员必须先定义,并使用分号结束
Mon("星期一"), Tue("星期二"), Wed("星期三"), Thu("星期四"),
Fri("星期五"), Sat("星期六"), Sun("星期天");

private final String day;

private WeekDay(String day) {
this.day = day;
}

public static void printDay(int i) {
switch (i) {
case 1:
System.out.println(WeekDay.Mon);
break;
case 2:
System.out.println(WeekDay.Tue);
break;
case 3:
System.out.println(WeekDay.Wed);
break;
case 4:
System.out.println(WeekDay.Thu);
break;
case 5:
System.out.println(WeekDay.Fri);
break;
case 6:
System.out.println(WeekDay.Sat);
break;
case 7:
System.out.println(WeekDay.Sun);
break;
default:
System.out.println("错误数字!");
}
}

public String getDay() {
return day;
}
}

public class Test131 {
public static void main(String[] args) {
for (WeekDay day : WeekDay.values()) {
System.out.println(day + " —— " + day.getDay());
}
// WeekDay.printDay(5);
}
}

6-3 覆盖基类

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
public class Test132 {
// 创建Color枚举类型,覆盖toString()方法
public enum Color {
RED("红色", 1), GREEN("绿色", 2), WHITE("白色", 3);

// 成员变量
private String name;
private int index;

// 构造方法
private Color(String name, int index) {
this.name = name;
this.index = index;
}

// 覆盖方法
@Override
public String toString() {
return this.index + " - " + this.name;
}
}

public static void main(String[] args) {
System.out.println(Color.RED.toString());
}
}

6-4 EnumSet

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
import java.util.EnumSet;

public class Test133 {
/**
* EnumSet是枚举类型的高性能Set实现,要求放入其枚举常量必须属于同一枚举类型
*/

// 定义操作类型枚举
public enum Operation {
PLUS, MINUS, MULTIPLY, DIVIDE
}

public static void doSomeThing(Operation op) {
switch (op) {
case PLUS:
System.out.println("加法");
break;
case MINUS:
System.out.println("减法");
break;
case MULTIPLY:
System.out.println("乘法");
break;
case DIVIDE:
System.out.println("除法");
break;
}
}

public static void main(String[] args) {
// 创建从PLUS到MULTIPLY的操作范围EnumSet,包括PLUS, MINUS, MULTIPLY
EnumSet<Operation> ops = EnumSet.range(Operation.PLUS, Operation.MULTIPLY);

// 遍历EnumSet中的元素执行相应操作
for (Operation op : ops) {
doSomeThing(op);
}

// noneOf():创建指定枚举类型的空EnumSet对象
EnumSet<Operation> noOps = EnumSet.noneOf(Operation.class);
System.out.println("操作为空:" + noOps);

// of():创建包含指定枚举成员的EnumSet对象
EnumSet<Operation> plusAndDivide = EnumSet.of(Operation.PLUS, Operation.DIVIDE);
System.out.println("加法除法:" + plusAndDivide);

// complementOf():创建一个与指定EnumSet对象s相同的枚举类型EnumSet对象
EnumSet<Operation> complementOps = EnumSet.complementOf(plusAndDivide);
System.out.println("减法乘法:" + complementOps);

// allOf():创建一个包含指定枚举类型中所有枚举成员的EnumSet对象
EnumSet<Operation> allOps = EnumSet.allOf(Operation.class);
System.out.println("所有操作:" + allOps);
}
}

6-5 EnumMap

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
import java.util.EnumMap;

public class Test134 {
/**
* HashMap只能接收同一枚举类型的实例作为键值
* EnumMap使用数组来存放与枚举类型对应的值,效率更高
*/

// 定义数据库类型枚举
public enum DataBaseType {
MYSQL, ORACLE, DB2, SQLSERVER
}

// 存储每个数据库类型对应的URL
private EnumMap<DataBaseType, String> urls = new EnumMap<>(DataBaseType.class);

public Test134() {
// 初始化映射关系
urls.put(DataBaseType.MYSQL, "jdbc:mysql://localhost/mydb");
urls.put(DataBaseType.ORACLE, "jdbc:oracle:thin:@localhost:1521:sample");
urls.put(DataBaseType.DB2, "jdbc:db2://localhost:5000/sample");
urls.put(DataBaseType.SQLSERVER, "jdbc:microsoft:sqlserver://sql:1433;Database=mydb");
}

// 根据数据库类型返回对应的URL
public String getURL(DataBaseType type) {
return urls.get(type);
}

public static void main(String[] args) {
Test134 example = new Test134();
System.out.println("MySQL URL: " + example.getURL(DataBaseType.MYSQL));
System.out.println("Oracle URL: " + example.getURL(DataBaseType.ORACLE));
}
}

6-6 一对多关系

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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import java.util.List;
import java.util.Iterator;
import java.util.ArrayList;

// 一个学校包含多个学生,一个学生属于一个学校,属于一对多关系
class School {
private String name;
private List<Student> allStudents;

public School() {
this.allStudents = new ArrayList<Student>();
}

public School(String name) {
this();
this.setName(name);
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public List<Student> getAllStudents() {
return allStudents;
}

public void setAllStudents(List<Student> allStudents) {
this.allStudents = allStudents;
}

// 重写toString()方法
public String toString() {
return "学校:" + this.name;
}
}

class Student {
private String name;
private int age;
private School school;

// 通过构造方法设置内容
public Student(String name, int age) {
this.setName(name);
this.setAge(age);
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public School getSchool() {
return school;
}

public void setSchool(School school) {
this.school = school;
}

// 重写toString()方法
public String toString() {
return "姓名:" + this.name + ",年龄:" + this.age;
}
}

public class Test135 {
public static void main(String[] args) {
// 实例化学校对象
School sch = new School("厦大");

// 实例化学生对象
Student s1 = new Student("张三", 21);
Student s2 = new Student("李四", 22);
Student s3 = new Student("王五", 23);

// 在学校中加入学生
sch.getAllStudents().add(s1);
sch.getAllStudents().add(s2);
sch.getAllStudents().add(s3);

// 一个学生属于一个学校
s1.setSchool(sch);
s2.setSchool(sch);
s3.setSchool(sch);

// 输出学校信息
System.out.println(sch);

// 实例化Iterator对象,用于输出全部的学生信息
Iterator<Student> ite = sch.getAllStudents().iterator();
while (ite.hasNext()) {
System.out.println("\t" + ite.next());
}
}
}

6-7 多对多关系

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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import java.util.List;
import java.util.Iterator;
import java.util.ArrayList;

// 一个学生可以选多门课程,一门课程可以有多个学生参加,属于多对多关系
class Course {
private String name;
private int credit;

// 定义集合保存多个学生
private List<Students> allStudents;

private Course() {
// 实例化List集合
this.allStudents = new ArrayList<Students>();
}

public Course(String name, int credit) {
this();
this.setName(name);
this.setCredit(credit);
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getCredit() {
return credit;
}

public void setCredit(int credit) {
this.credit = credit;
}

public List<Students> getAllStudents() {
return allStudents;
}

public void setAllStudents(List<Students> allStudents) {
this.allStudents = allStudents;
}

// 重写toString()方法
public String toString() {
return "课程:" + this.name + ",学分:" + this.credit;
}
}

class Students {
private String name;
private int age;
private List<Course> allCourses;

private Students() {
this.allCourses = new ArrayList<Course>();
}

// 通过构造方法设置内容
public Students(String name, int age) {
// 调用无参构造
this();
this.setName(name);
this.setAge(age);
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public List<Course> getAllCourses() {
return allCourses;
}

public void setAllCourses(List<Course> allCourses) {
this.allCourses = allCourses;
}

// 重写toString()方法
public String toString() {
return "姓名:" + this.name + ",年龄:" + this.age;
}
}

public class Test136 {
public static void main(String[] args) {
// 实例化课程对象
Course c1 = new Course("英语", 3);
Course c2 = new Course("物理", 5);

// 实例化学生对象
Students s1 = new Students("张三", 20);
Students s2 = new Students("李四", 21);
Students s3 = new Students("王五", 22);
Students s4 = new Students("赵六", 23);
Students s5 = new Students("孙七", 24);
Students s6 = new Students("周八", 25);

// 第一门课程有3个人参加,向课程中增加3个学生信息,同时向学生中增加课程信息
c1.getAllStudents().add(s1);
c1.getAllStudents().add(s2);
c1.getAllStudents().add(s6);
s1.getAllCourses().add(c1);
s2.getAllCourses().add(c1);
s6.getAllCourses().add(c1);

// 第二门课程有6个人参加,向课程中增加6个学生信息,同时向学生中添加课程信息
// 向课程中增加学生信息
c2.getAllStudents().add(s1);
c2.getAllStudents().add(s2);
c2.getAllStudents().add(s3);
c2.getAllStudents().add(s4);
c2.getAllStudents().add(s5);
c2.getAllStudents().add(s6);
// 像学生中增加课程信息
s1.getAllCourses().add(c2);
s2.getAllCourses().add(c2);
s3.getAllCourses().add(c2);
s4.getAllCourses().add(c2);
s5.getAllCourses().add(c2);
s6.getAllCourses().add(c2);

// 输出一门课程的信息,观察一门课程有多少个学生参加
System.out.println(c1);
Iterator<Students> iter1 = c1.getAllStudents().iterator();
// 迭代输出
while (iter1.hasNext()) {
Students s = iter1.next();
System.out.println("\t" + s);
}

// 输出一个学生参加的课程信息,观察有多少门课程
System.out.println(s6);
Iterator<Course> iter2 = s6.getAllCourses().iterator();
while (iter2.hasNext()) {
// 取得所参加的课程
Course c = iter2.next();
// 输出课程信息
System.out.println("\t" + c);
}
}
}

Java 基础(三)
https://stitch-top.github.io/2025/07/08/java/java03-java-ji-chu-san/
作者
Dr.626
发布于
2025年7月8日 22:00:55
许可协议