快捷键

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
a.sout —> System.out.print(a);

sout —> System.out.print();

ctrl+alt+t 选择循环

alt+Enter 强制类型转换

右键generate,自动生成构造函数和get、set方法

a.fori+Enter 自动生成for循环

ctrl + alt + V 自动生成左边的接受变量

Alt+Shift+Enter 实现方法重写(抽象类尤甚)

static

定义

静态,可以修饰成员变量、成员方法。

成员变量

成员变量的执行原理

1
2
3
4
5
6
7
8
package com.itheima.staticdemo;

public class Student {
// 类变量
static String name;
// 实例变量(对象的变量)
int age;
}
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
package com.itheima.staticdemo;

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

// 1.类变量的用法
// 类名.变量名(推荐)
Student.name = "崔巉";

// 对象.类变量(不推荐)
Student s1 = new Student(); // new 出来的对象存在于堆内存中
s1.name = "左右";

Student s2 = new Student();
s2.name = "齐静春";

System.out.println(Student.name); // 齐静春
System.out.println(s1.name); // 齐静春
System.out.println(s2.name); // 齐静春
// 赋值的都是同一个地址里的name

// 2.实例变量的用法:属于每个对象的变量
// 对象.实例变量
s1.age = 18;
s2.age = 22;
System.out.println(s1.age); // 18
System.out.println(s2.age); // 22
//System.out.println(Student.age); // 报错,无法从 static 上下文引用非 static 字段 'age'
}
}

类变量的应用场景

1
2
3
4
5
6
7
8
9
10
11
12
package com.itheima.staticdemo;

public class User {
// 类变量
public static int number; // 类变量一般就用public,方便对外公开和共享

public User() {
// User.number++;
// 注意:在同一个类中,访问自己类的类变量,才可以省略类名不写
number++;
}
}
1
2
3
4
5
6
7
8
9
10
11
package com.itheima.staticdemo;

public class Test {
public static void main(String[] args) {
User u1 = new User();
User u2 = new User();
User u3 = new User();
User u4 = new User();
System.out.println(User.number); // 4
}
}

成员方法

成员方法的执行原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.itheima.staticmethod;

public class Student {
double score;

// 类方法(静态方法)
public static void printHelloWorld() {
System.out.println("Hello World!");
System.out.println("Hello World!");
}

// 实例方法(对象的方法)
public void PrintPass() {
System.out.println("成绩:" + (score >= 60 ? "及格" : "不及格"));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.itheima.staticmethod;

public class Test {
public static void main(String[] args) {
// 1.类方法的用法
// 类名.类方法(推荐)
Student.printHelloWorld();

// 对象.类方法(不推荐)
Student s1 = new Student();
s1.printHelloWorld();

// 2.实例方法的用法
s1.PrintPass();
// Student.printPass(); //报错,无法解析 'Student' 中的方法 'printPass'

}
}

类方法的常见应用场景

注:代码不作演示,关键在于工具类要用类方法以便给开发人员共同使用

好处:

  • 不需要再 new 一个对象,可以节省内存
  • 直接用 类名.方法名 来调用,代码更简洁
  • 提高代码复用性,提升了开发效率

在工具类中,可以将构造器私有化,防止用对象调用,多占内存。步骤:private XxxxUtil(){}

注意事项:

  • 类方法中可以直接访问类的成员,不可以直接访问实例成员(也就是非 static 的) ★★★★★

    若需要在类方法中访问实例成员,一种常见的做法是将实例作为参数传递给类方法。

  • 实例方法中既可以直接访问类成员,也可以直接访问实例成员

  • 实例方法中可以出现 this 关键字,类方法中不可以出现 this 关键字

main 方法

在 Java 中,main 方法也是一个类方法,java 虚拟机(即 JVM)是用 类名.main(…) 执行的,main 方法是程序的入口方法,它有固定的格式,所以String[] args 不可以省去

String[] args 这个字符串数组是保存运行 main 函数时输入的参数的,例如 main 函数所在的类名为 Test 那么你在 cmd 运行 java Test a b c 时,args[0] = a , args[1]=b, args[2]=c 你就可以在你的程序中调用你输入的这些变量了。

args的全称是 arguments,译为参数

代码块

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
package com.itheima.block;

public class Student {
static int number = 80;
static String schoolName;

// 静态代码块
static {
System.out.println("静态代码块执行了~~");
schoolName = "黑马";
}

// 实例代码块
{
System.out.println("实例代码块执行了~~");
System.out.println("有人实例对象了" + this);
}
// 实例代码块用得少,了解即可,以后开发的作用多是记录实例对象次数或者地址

public Student() {
System.out.println("无参构造器执行了~~");
}

public Student(String name) {
System.out.println("有参构造器执行了~~");
}
}
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
package com.itheima.block;

public class Test {
public static void main(String[] args) {
System.out.println(Student.number);
System.out.println(Student.number);
System.out.println(Student.number);
System.out.println(Student.schoolName);

System.out.println("--------------------");
Student s1 = new Student();
Student s2 = new Student("张三");
}
}
/*
静态代码块执行了~~
80
80
80
黑马
--------------------
实例代码块执行了~~
有人实例对象了com.itheima.block.Student@5f184fc6
无参构造器执行了~~
实例代码块执行了~~
有人实例对象了com.itheima.block.Student@6f496d9f
有参构造器执行了~~
*/

继承

定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.itheima.extend;

// 父类
public class A {
// 公开成员
public int i;

public void print1() {
System.out.println("===print1===");
}

// 私有成员
private int j;

private void print2() {
System.out.println("===print2===");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.itheima.extend;

// 子类
public class B extends A {
public int k;
private int u;

// 子类是可以继承父类的非私有成员
public void print3() {
System.out.println(i);
print1();

// 这些私有的东西是不可以继承的
// System.out.println(j);
// print2();
}

}
1
2
3
4
5
6
7
8
9
10
11
package com.itheima.extend;

public class Test {
public static void main(String[] args) {
B b = new B();// 此时对象b是由 类A和类B 共同设计出来的
System.out.println(b.i);
// System.out.println(b.j);
System.out.println(b.k);
// System.out.println(b.u);
}
}

好处:

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.itheima.extend2;

public class People {
private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.itheima.extend2;

public class Teacher extends People {
private String skill;

public String getSkill() {
return skill;
}

public void setSkill(String skill) {
this.skill = skill;
}

public void printInfo() {
System.out.println(this.getName() + "具备的技能:" + this.getSkill());
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.itheima.extend2;

public class Test {
public static void main(String[] args) {
Teacher t = new Teacher();
t.setName("张三");
t.setSkill("跆拳道");
System.out.println(t.getName());
System.out.println(t.getSkill());
t.printInfo();
}
}
/*
张三
跆拳道
张三具备的技能:跆拳道
*/

好处:减少了重复代码的编写,增加了代码的复用性。

权限修饰符

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
package com.itheima.modifier;

public class Father {
// 1.private:只能在本类中访问
private void privateMethod() {
System.out.println("=== Private ===");
}

// 2.缺省:本类、同一个包下的类
void Method() {
System.out.println("=== 缺省(package-private) ===");
}

// 3.protected:本类、同一个包下的类、任意包下的子类
protected void protectedMethod() {
System.out.println("=== Protected ===");
}

// 4.public:本类、同一个包下的类、任意包下的子类、任意包下的任意类
public void publicMethod() {
System.out.println("=== Public ===");
}

public void test() {
privateMethod();
Method();
protectedMethod();
publicMethod();
}
}
1
2
3
4
5
6
7
8
9
10
11
package com.itheima.modifier;

public class Demo {
public static void main(String[] args) {
Father f = new Father();
// f.privateMethod(); // 报错
f.Method();
f.protectedMethod();
f.publicMethod();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
package com.itheima.other_modifier;

import com.itheima.modifier.Father;

public class Son extends Father {
public void test() {
// privateMethod(); // 报错
// Method(); // 报错
protectedMethod();
publicMethod();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.itheima.other_modifier;

import com.itheima.modifier.Father;

public class Demo {
public static void main(String[] args) {
Father f = new Father();
// f.privateMethod(); // 报错
// f.Method(); // 报错
// f.protectedMethod(); // 报错
f.publicMethod();

Son s=new Son();
// s.protectedMethod(); // 报错,protected只能在任意包的子类里访问,不代表可以在子类对象里访问
s.publicMethod();
}
}

单继承

object

object 类是 java 所有类的祖宗类。我们写的任何一个类,其实都是 object 的子类或子孙类。

override

1
2
3
4
5
6
7
8
9
10
11
package com.itheima.override;

public class A {
public void print1() {
System.out.println("A1");
}

public void print2(int a) {
System.out.println("A2");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.itheima.override;

public class B extends A {
// 方法重写
@Override // 安全,可读性好
public void print1() {
System.out.println("B1");
}

@Override
public void print2(int a) {
System.out.println("B2");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.itheima.override;

public class Test {
public static void main(String[] args) {
B b = new B();
b.print1();
b.print2(2);
}
}
/*
B1
B2
*/

重写的应用场景

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
package com.itheima.override;

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

public Student() {
}

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

public int getAge() {
return age;
}

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

public String getName() {
return name;
}

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

@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
} // 右键可直接生成
}
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
package com.itheima.override;

import java.util.ArrayList;

public class Demo {
public static void main(String[] args) {
Student s = new Student("左右", 120);
System.out.println(s.toString());
System.out.println(s);
// toString()在object类中的含义是打印出地址,同打印s结果一样
/*
com.itheima.override.Student@5f184fc6
com.itheima.override.Student@5f184fc6
*/

// 现在重写tostring方法
/*
Student{name='左右', age=120}
Student{name='左右', age=120}
*/

// 同理,之前提到过的list,也是因为重写了toString方法,所以可以直接打印list
ArrayList list = new ArrayList();
list.add("java");
list.add("python");
System.out.println(list); // [java, python]
}
}

子类访问其他成员

1、在子类方法中访问其他成员(成员变量、成员方法),是依照就近原则的。

  • 先子类局部范围找。
  • 接着子类成员范围找。
  • 然后父类成员范围找,如果父类范围还没有找到则报错。

2、如果子父类中,出现了重名的成员会优先使用子类的,如果此时一定要在子类中使用父类的怎么办?

  • 可以通过super 关键字,指定访问父类的成员:super.父类成员变量/父类成员方法
1
2
3
4
5
6
7
8
9
package com.itheima.visit;

public class Father {
String name = "父类名称";

public void print1() {
System.out.println("===父类的print1执行===");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.itheima.visit;

public class Son extends Father {
String name = "子类名称";

public void printName() {
System.out.println(name); // 子类名称
String name = "局部名称";
System.out.println(name); // 局部名称
System.out.println(this.name); // 子类名称
System.out.println(super.name); // 父类名称
// 就近原则,自己品。
}

@Override
public void print1() {
System.out.println("===子类的print1执行===");
}

public void Method() {
print1(); // ===子类的print1执行===
super.print1(); //===父类的print1执行===
}
}
1
2
3
4
5
6
7
8
9
package com.itheima.visit;

public class Test {
public static void main(String[] args) {
Son s = new Son();
s.printName();
s.Method();
}
}

子类构造器

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
package com.itheima.constructor;

class Father {
public Father() {
System.out.println("===父类的 无参构造器 执行了===");
}

public Father(String name) {
System.out.println("===父类的 有参构造器 执行了===");
}
}

class Son extends Father {
public Son() {
// super(); // 默认存在这个super(); 写不写无所谓,但jvm都会自动调用父类的无参构造器。
System.out.println("===子类的 无参构造器 执行了===");
}

public Son(String name) {
// super(); 同样的,子类的有参构造器这边也是调用的父类的无参构造器。
System.out.println("===子类的 有参构造器 执行了===");
}
}

public class Test {
public static void main(String[] args) {
Son s = new Son();
System.out.println("============================");
Son s1 = new Son("白景");
}
}
/*
===父类的 无参构造器 执行了===
===子类的 无参构造器 执行了===
============================
===父类的 无参构造器 执行了===
===子类的 有参构造器 执行了===
*/
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
package com.itheima.constructor;

class Father {
// public Father() {
// System.out.println("===父类的 无参构造器 执行了===");
// }

public Father(String name) {
System.out.println("===父类的 有参构造器 执行了===");
}
}

class Son extends Father {
public Son() {
super( ); // 如果在父类中没有无参构造器,就需要在子类中用super定义有参,避免报错。
System.out.println("===子类的 无参构造器 执行了===");
}

public Son(String name) {
super(name);
System.out.println("===子类的 有参构造器 执行了===");
}
}

public class Test {
public static void main(String[] args) {
Son s = new Son();
System.out.println("============================");
Son s1 = new Son("白景");
}
}
/*
===父类的 有参构造器 执行了===
===子类的 无参构造器 执行了===
============================
===父类的 有参构造器 执行了===
===子类的 有参构造器 执行了===
*/

子类构造器调用父类构造器的常见应用场景

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
package com.itheima.constructor;

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

public People() {
}

public People(String name, int age) {
this.name = name;
this.age = 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;
}
}
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
package com.itheima.constructor;

public class Teacher extends People {
private String skill;

public Teacher() {
}

public Teacher(String name, int age, String skill) {
super(name, age); // 先调用父类有参构造器
this.skill = skill;
} // 这一步可以直接右键生成。

public Teacher(String skill) {
this.skill = skill;
}

public String getSkill() {
return skill;
}

public void setSkill(String skill) {
this.skill = skill;
}
}
1
2
3
4
5
6
7
8
9
10
package com.itheima.constructor;

public class Demo {
public static void main(String[] args) {
Teacher t = new Teacher("李宝瓶", 18, "写论文");
System.out.println(t.getName());
System.out.println(t.getAge());
System.out.println(t.getSkill());
}
}

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
package com.itheima.constructor;

public class Test1 {
public static void main(String[] args) {
Student s = new Student("李二", 99);
System.out.println(s.getName());
System.out.println(s.getAge());
System.out.println(s.getScore()); // 60
}
}

class Student {
private String name;
private int age;
private int score;

public Student() {
}

// public Student(String name, int age) {
// this.name = name;
// this.age = age;
// this.score = 60;
// }

public Student(String name, int age) {
this(name, age, 60);
// super(); // 不能再写super()了,这俩都得写在第一行,而且this()调用的有参构造器里面有super();
}

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

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 int getScore() {
return score;
}

public void setScore(int score) {
this.score = score;
}
}

多态

定义

1
2
3
4
5
6
7
8
9
package com.itheima.polymorphism;

public class People {
public String name = "父类的成员变量";

public void run() {
System.out.println("人可以跑~");
}
}
1
2
3
4
5
6
7
8
9
10
package com.itheima.polymorphism;

public class Teacher extends People {
public String name = "老师的成员变量";

@Override
public void run() {
System.out.println("老师跑得很慢~");
}
}
1
2
3
4
5
6
7
8
9
10
package com.itheima.polymorphism;

public class Student extends People {
public String name = "学生的成员变量";

@Override
public void run() {
System.out.println("学生跑得贼快~");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
package com.itheima.polymorphism;

public class Test {
public static void main(String[] args) {
People p1 = new Teacher();
p1.run(); // 识别技巧:编译看左边,运行看右边
System.out.println(p1.name); // 变量的编译和运行都是看左边
People p2 = new Student();
p2.run(); // 注解:编译看左边有没有这个方法,运行看右边要不要改方法里的内容
System.out.println(p2.name);
}
}

好处

1
2
3
4
5
6
7
8
9
package com.itheima.polymorphism;

public class People {
public String name = "父类的成员变量.";

public void run() {
System.out.println("人可以跑~");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.itheima.polymorphism;

public class Teacher extends People {
public String name = "老师的成员变量.";

@Override
public void run() {
System.out.println("老师跑得很慢~");
}

public void teach() {
System.out.println("老师会教知识~");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.itheima.polymorphism;

public class Student extends People {
public String name = "学生的成员变量.";

@Override
public void run() {
System.out.println("学生跑得贼快~");
}

public void study() {
System.out.println("学生要学习~");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.itheima.polymorphism;

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

// 好处1:可以实现解耦合,右边对象可以随时切换,后续业务随之改变
// 就是说只要改一下 Teacher()为Student()就行,后面的实例方法不用改了
People p1 = new Teacher();
p1.run(); // 识别技巧:编译看左边,运行看右边
// p1.teach(); // 因为编译看左边的people,不能调用子类独有功能,所以不可行

// 好处2:可以使用父类类型的变量作为形参,可以接收一切子类对象
Student s = new Student();
go(s);

Teacher t = new Teacher();
go(t);
}

public static void go(People p) {
} // 大家都要用的,就要加static
}

类型转换

注:People类、Teacher类、Student类同上,不再赘述。

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
package com.itheima.polymorphism;

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

People p1 = new Teacher();
p1.run();
// p1.study(); // 报错,无法解析 'People' 中的方法 'study'

// 强制类型转换
Teacher t1 = (Teacher) p1;
t1.teach();

// 编译阶段有继续或者实现关系就可以强制转换,但是运行时可能出现类型转换异常
// 说人话:就是一开始实例化的p1,本质上其实是Teacher类,所以没法强转为Student类
// Student s1 = (Student) p1; // 报错,java.lang.ClassCastException(类强制转换异常)
// s1.study();

// 此时可以通过instanceof语句判断原本的类型
if (p1 instanceof Teacher) {
Teacher t2 = (Teacher) p1;
t2.teach();
} else {
Student s1 = (Student) p1;
s1.study();
}

Student s = new Student();
go(s);
}

public static void go(People p) {
// 为什么一眼能看出来的内容要判断呢?因为有时候一眼看不出来(简直废话)
// 比如这个go方法,实例变量里接收的是People类型,People类型是既可以接收Teacher也可以接收Student,所以需要判断
if (p instanceof Teacher) {
Teacher t = (Teacher) p;
t.teach();
} else {
Student s = (Student) p;
s.study();
}
}
}

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
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.itheima.final1;

// 1.final修饰类,类不能被继承了
final class A {
};
//class B extends A{}; // 报错,无法从final 'com.itheima.final1.A' 继承

// 2.final修饰方法,方法不能被重写了
class C {
public final void print() {
System.out.println("C");
}
}

class D extends C {
// Override
// public void print(){ // 报错,'print()' 无法重写 'com.itheima.final1.C' 中的 'print()';重写的方法为 final
// }

}

// 3.final可以修饰变量,其规则是:有且仅能赋值一次
public class Test {
/*
常量:public static final修饰的成员变量,建议名称全部大写,多个单词用下划线连接
*/
public static final String ROLE_NAME = "陈平安";

public static void main(String[] args) {
/*
变量:
一、局部变量
二、成员变量
1、静态成员变量
2、实例成员变量
*/
final double pi = 3.14;
// pi = 3.141; // 报错,无法将值赋给 final 变量 'pi',因为二次赋值了

// final修饰引用类型的变量,地址改不了但是可以改存储的值,类似指针常量
final int[] arr = {11, 22, 33};
// arr = null; // 报错,无法将值赋给 final 变量 'arr'
arr[1] = 99;


}

public static void buy(final double money) {
// money =10000; // 报错,无法将值赋给 final 变量 'money'
}
}

回顾一下 C++中 const 的知识点。

1
2
3
4
5
6
7
8
9
// 常量指针  const右边是*,值不可改变 ,地址可改
const int *p1 = &a;
p1 = &b; // 正确
//*p1 = 100; //报错

// 指针常量 const右边是p,地址不可改变 ,值可改
int *const p2 = &a;
// p2 = &b; // 报错
*p2 = 100; // 正确

常量

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.itheima.final1;

public class Test1 {
public static final String SCHOOL_NAME = "北京大学";

public static void main(String[] args) {
System.out.println(SCHOOL_NAME); // 编译的时候这里的 SCHOOL_NAME 会被直接替换成 "北京大学",可以保持性能不变
System.out.println(SCHOOL_NAME);
System.out.println(SCHOOL_NAME);
System.out.println(SCHOOL_NAME);
System.out.println(SCHOOL_NAME);
}
}

编译后的.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
package com.itheima.abstract1;

public abstract class A { // 有抽象方法就要变成抽象类
private String name;
private static String schoolName;

// 抽象方法:必须要用abstract修饰,只有方法签名,不能有方法体,即{}。
public abstract void A();

public A() {
}

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

public String getName() {
return name;
}

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

public static String getSchoolName() {
return schoolName;
}

public static void setSchoolName(String schoolName) {
A.schoolName = schoolName;
}
}
1
2
3
4
5
6
7
8
9
package com.itheima.abstract1;

// 一个类继承了抽象类,必须重写抽象类的全部抽象方法,否则自己也要变成抽象类
public class B extends A {

@Override
public void A() {
} // 这个方法在A类中本是抽象类,但是现在重写了,这个B也就不用再变成抽象类了。
}
1
2
3
4
5
6
7
8
package com.itheima.abstract1;

public class Test {
public static void main(String[] args) {
// 注意:抽象类不能创建对象
// A a = new A();
}
}

应用场景和好处

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.itheima.abstract2;

public abstract class Animal {
private String name;

public abstract void shout();

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}
1
2
3
4
5
6
7
package com.itheima.abstract2;

public class Cat extends Animal {
public void shout() {
System.out.println(getName() + "会喵喵喵地叫~");
}
}
1
2
3
4
5
6
7
package com.itheima.abstract2;

public class Dog extends Animal {
public void shout() {
System.out.println(getName() + "会汪汪汪地叫~~");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
package com.itheima.abstract2;

public class Test {
public static void main(String[] args) {
Animal cat = new Cat();
cat.setName("眯眯");
cat.shout();
Animal dog = new Dog();
dog.setName("墩墩");
dog.shout();
}
}

注:不用抽象类也能实现这些功能,只是这样更专业,能更好地支持多态。

接口

定义

1
2
3
4
5
6
7
8
9
10
11
package com.itheima.interface1;

public interface A {
// 成员变量(常量)
String SCHOOL_NAME = "黑马程序员";
// public static final String SCHOOL_NAME = "黑马程序员";

// 成员方法(抽象方法)
void test();
// public abstract void test();
}
1
2
3
4
5
6
7
8
9
10
package com.itheima.interface1;

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

// A.SCHOOL_NAME= "BlackHorse"; // 无法将值赋给 final 变量 'SCHOOL_NAME'

// A a = new A(); // 'A' 为 abstract;无法实例化
}
}

1
2
3
4
5
6
7
package com.itheima.interface1;

public interface B {
void testb1();

void testb2();
}
1
2
3
4
5
6
7
package com.itheima.interface1;

public interface C {
void testc1();

void testc2();
}
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
package com.itheima.interface1;

public class D implements B, C { // 类“D”必须声明为抽象,或为实现“B”中的抽象方法“testb1()”
// Alt+Shift+Enter 实现方法重写
@Override
public void testb1() {

}

@Override
public void testb2() {

}

@Override
public void testc1() {

}

@Override
public void testc2() {

}

}
1
2
3
4
5
6
7
8
package com.itheima.interface1;

public class Test {
public static void main(String[] args) {
D d = new D();
d.testb1();
}
}

优点

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
package com.itheima.interface1;

public class Test1 {
public static void main(String[] args) {
Singer s = new Student();
s.sing();
System.out.println("---------");
Dancer d = new Student();
d.dance();
}
}

class Student implements Singer, Dancer { // 可以通过接口名知道对象有哪些方法、功能
@Override
public void sing() {
System.out.println("学习唱歌~");
}

@Override
public void dance() {
System.out.println("学习跳舞~");
}
}

interface Singer {
void sing();
}

interface Dancer {
void dance();
}

案例练习

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
package com.itheima.interface_demo;

public class Student {
private String name;
private String gender;
private double score;

public Student() {
}

public Student(String name, String gender, double score) {
this.name = name;
this.gender = gender;
this.score = score;
}

public String getName() {
return name;
}

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

public String getGender() {
return gender;
}

public void setGender(String gender) {
this.gender = gender;
}

public double getScore() {
return score;
}

public void setScore(double score) {
this.score = score;
}
}
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
package com.itheima.interface_demo;

import java.util.ArrayList;

public class ClassManager {
private ArrayList<Student> students = new ArrayList<>();
// 这一步挺关键的,要能想到实例化一个操作类。
//private StudentOperator studentOperator = new StudentOperatorImpl1();
private StudentOperator studentOperator = new StudentOperatorImpl2();

public ClassManager() {
students.add(new Student("陈清都", "男", 100));
students.add(new Student("阿良", "男", 92));
students.add(new Student("董三更", "男", 97));
students.add(new Student("陈曦", "男", 89));
students.add(new Student("左右", "男", 93));
students.add(new Student("陆芝", "女", 88));
}

public void printInfo() {
studentOperator.printAllInfo(students);
}

public void printAvgScores() {
studentOperator.printAvgScores(students);
}
}
1
2
3
4
5
6
7
8
9
package com.itheima.interface_demo;

import java.util.ArrayList;

public interface StudentOperator {
void printAllInfo(ArrayList<Student> students);

void printAvgScores(ArrayList<Student> students);
}
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
package com.itheima.interface_demo;

import java.util.ArrayList;

public class StudentOperatorImpl1 implements StudentOperator {
@Override
public void printAllInfo(ArrayList<Student> students) {
System.out.println("-------全班全部学生信息-------");
for (int i = 0; i < students.size(); i++) {
Student s = students.get(i); // 根据索引获取集合中某个索引位置处的值
System.out.println("姓名:" + s.getName() + "\t性别:" + s.getGender() + "\t成绩:" + s.getScore());
}
}

@Override
public void printAvgScores(ArrayList<Student> students) {
System.out.println("-------全班学生平均成绩-------");
double AllScore = 0;
for (int i = 0; i < students.size(); i++) {
Student s = students.get(i); // 根据索引获取集合中某个索引位置处的值
AllScore = AllScore + s.getScore();
}
System.out.println("平均成绩:" + AllScore / students.size());
}
}
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
package com.itheima.interface_demo;

import java.util.ArrayList;

public class StudentOperatorImpl2 implements StudentOperator {
@Override
public void printAllInfo(ArrayList<Student> students) {
System.out.println("-------全班男女学生信息-------");
int SumMale = 0;
for (int i = 0; i < students.size(); i++) {
Student s = students.get(i); // 根据索引获取集合中某个索引位置处的值
System.out.println("姓名:" + s.getName() + "\t性别:" + s.getGender() + "\t成绩:" + s.getScore());
if (s.getGender().equals("男")) {
SumMale++;
}
}
System.out.println("男生人数:" + SumMale + "\t女生人数:" + (students.size() - SumMale));
}

@Override
public void printAvgScores(ArrayList<Student> students) {
System.out.println("-------全班截尾平均成绩-------");
double[] avgScores = new double[students.size()];
for (int i = 0; i < students.size(); i++) {
Student s = students.get(i);
avgScores[i] = s.getScore();
}


for (int i = 1; i < avgScores.length; i++) {
boolean flag = false;
for (int j = i; j > 0; j--) {
if (avgScores[j] < avgScores[j - 1]) {
double tmp = avgScores[j];
avgScores[j] = avgScores[j - 1];
avgScores[j - 1] = tmp;
flag = true;
}
}
if (!flag) {
break;
}
}

/*
打印排序后的数组
for (int i = 0; i < avgScores.length; i++) {
System.out.print(avgScores[i] + "\t");
}
*/
// 也可以通过循环直接找出最大值和最小值,在算平均值的时候减去即可。

double AllScore = 0;
for (int i = 1; i < avgScores.length - 1; i++) {
AllScore = AllScore + avgScores[i];
}
System.out.println("\n最高分:" + avgScores[avgScores.length - 1]);
System.out.println("最低分:" + avgScores[0]);
System.out.println("平均成绩:" + AllScore / (avgScores.length - 2));
}
}
1
2
3
4
5
6
7
8
9
package com.itheima.interface_demo;

public class Test {
public static void main(String[] args) {
ClassManager classManager = new ClassManager();
classManager.printInfo();
classManager.printAvgScores();
}
}

JDK8 开始新增的方法

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
package com.itheima.interface_jdk8;

public interface A {
/**
* 1.默认方法:必须使用default修饰,默认会被public修饰
* 实例方法:就是用对象的方法,必须使用实现类的对象来访问,也就是后面的B.class
*/
default void test1() { // 前面默认会加一个public
System.out.println("===默认方法===");
test2(); // test2()在类外调用不了,只能通过类内的方法调用
}

/**
* 2.私有方法:必须使用private修饰。(JDK9开始才支持的)
* 实例方法:也是用对象的方法。
*/
private void test2() {
System.out.println("===私有方法===");
}

/**
* 3.静态方法:必须使用static修饰,默认会被public修饰
* 调用方法:用接口名来调用
*/
static void test3() { // 前面默认会加一个public
System.out.println("===静态方法===");
}

/**
* 作用:接口中增加功能之后,如果有很多实现类,都需要重写这个方法,现在定义默认的方法之后,
* 实现类里都能直接用,不用重写,扩展功能就很方便,维护成本就降低了。
*/
}
1
2
3
4
package com.itheima.interface_jdk8;

public class B implements A {
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.itheima.interface_jdk8;

public class Test {
public static void main(String[] args) {
B b = new B();
b.test1();
A.test3();
}
}
/*
===默认方法===
===私有方法===
===静态方法===
*/

接口的多继承

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
package com.itheima.interface_attention;

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

interface A {
void test1();
};

interface B {
void test2();
};

interface C {
void test3();
};

// 接口是可以多继承的
interface D extends A, B, C {
};


class E implements D {
// 类“E”必须声明为抽象,或为实现“A、B、C”中的抽象方法“test1、2、3()”
@Override
public void test1() {

}

@Override
public void test2() {

}

@Override
public void test3() {

}
}

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
package com.itheima.interface_attention;

public class Test1 {
public static void main(String[] args) {
Zi z = new Zi();
z.run(); // 我是亲爹~

M m = new M();
m.run(); // 我是X~
}
}

// 1、一个接口继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持多继承。
interface I {
void test1();
};

interface J {
String test1();
};

// interface K extends I, J {}
/*
'com.itheima.interface_attention.J' 中的 'test1()'
与 'com.itheima.interface_attention.I' 中的 'test1()' 冲突; 方法有不相关的返回值类型
*/

// 2、一个类实现多个接口,如果多个接口中存在方法签名冲突,则此时不支持多实现。
// class A implements I, J {}

// 3、一个类继承了父类,又同时实现了接口,父类中和接口中有同名的默认方法,实现类会优先用父类的。
class Fu {
public void run() {
System.out.println("我是亲爹~");
}
}

interface K {
default void run() {
System.out.println("我是干爹~");
}
}

class Zi extends Fu implements K {
}

// 4、一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。
interface X {
default void run() {
System.out.println("我是X~");
}
}

interface Y {
default void run() {
System.out.println("我是Y~");
}
}

class M implements X, Y {
@Override
public void run() {
X.super.run(); // 系统自动生成的,我本想重写的,emmm也行吧~
}
}

内部类

成员内部类

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
package com.itheima.inner_class;

public class Outer {
private double score = 99;
public static String subject; // 外部成员变量

public void demo() {
System.out.println(score);
System.out.println(subject); // 内部类的方法也是可以用外部成员变量的
}

public class Inner {
private String name;
private double grade = 88;

public static String schoolName; // JDK16才开始支持定义静态成员的。

public void test() { // 这种普通方法也是可以定义的
System.out.println(grade);
System.out.println(subject); // 内部类的方法也是可以用外部类成员的

double grade = 77;
System.out.println(grade); // 77
System.out.println(this.grade); // 88
System.out.println(Outer.this.score); // 99 // ★★★★★
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}
}
1
2
3
4
5
6
7
8
9
package com.itheima.inner_class;

public class Test {
public static void main(String[] args) {
Outer.Inner inner = new Outer().new Inner(); // ★★★★★ 类似:对象.实例方法
inner.setName("李宝瓶");
inner.test();
}
}

静态内部类

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
package com.itheima.inner_class2;

public class Outer {
private double score = 99;
public static String subject; // 外部成员变量

public static void demo() {
// System.out.println(score); // 静态方法(即类方法)不可以直接访问实例成员
System.out.println(subject);
}

public static class Inner {
private String name;
public static String schoolName;

public String getName() {
return name;
}

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

public static void test() { // 这种普通方法也是可以定义的
// System.out.println(score);
System.out.println(subject);
}
}
}
1
2
3
4
5
6
7
8
package com.itheima.inner_class2;

public class Test {
public static void main(String[] args) {
Outer.Inner inner = new Outer.Inner(); // 跟前面static篇内容对应上了,类似:类名.类方法
inner.test();
}
}

局部内部类

比较鸡肋,不作要求,无需了解。

匿名内部类(重点)

基础写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.itheima.inner_class3;

public class Test {
public static void main(String[] args) {
Animal a = new Cat();
a.shout();
}
}

class Cat extends Animal {
@Override
public void shout() {
System.out.println("眯眯喵喵叫~");
}
}

abstract class Animal {
public abstract void shout();
}

匿名内部类写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.itheima.inner_class3;

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

// 编译器会把这个匿名内部类编译成一个子类,然后会立即创建一个子类对象出来。
// 看下面的反编译文件
Animal a = new Animal() {
@Override
public void shout() {
System.out.println("眯眯喵喵叫~");
}
};
a.shout();
}
}


abstract class Animal {
public abstract void shout();
}

反编译 Test$1.class 文件

  • 所以这样就可以不用描述是怎样一个子类,Cat 或是 Dog,直接把它的属性给填进去(有实无名)。【体会一下什么叫匿名】
1
2
3
4
5
6
7
8
9
10
package com.itheima.inner_class3;

class Test$1 extends Animal {
Test$1() {
}

public void shout() {
System.out.println("眯眯喵喵叫~");
}
}

应用场景

  • 匿名内部类通常作为一个参数传递给方法

案例 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
package com.itheima.inner_class4;

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

// 写法一
// Swimming s1 = new Swimming() {
// @Override
// public void swim() {
// System.out.println("我要学游泳~");
// }
// };
// go(s1);

// 写法二
go(new Swimming() {
@Override
public void swim() {
System.out.println("我要学游泳~");
}
});

}

// 设计一个方法,可以接收Swimming接口的一切实现类对象过来参加游泳比赛。
public static void go(Swimming s) {
s.swim();
}
} // 这里的话,就是用go来封装一下,避免要写s.swim(),对于实现来说,知道的越少越好。

interface Swimming {
void swim();
}

案例 2

  • 部分代码涉及后续 GUI 内容,稍作了解即可,核心是了解匿名内部类的使用场景
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
package com.itheima.inner_class5;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

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

JFrame win = new JFrame("登陆界面");
JPanel panel = new JPanel();
win.add(panel);
JButton btn = new JButton("登录");
panel.add(btn);

// 给按钮绑定单击事件监听器
btn.addActionListener(new ActionListener() {
// 监听器对象是一个接口类型(见下图),要么用实现类(implements)来构建对象,要么用匿名内部类
// 所以一般是调用api时,发现需要的对象是接口类型,就被动地去使用匿名内部类了,而不是主动去用
@Override
public void actionPerformed(ActionEvent e) {
// 一点击登录就会触发这里面的事件
JOptionPane.showMessageDialog(win, "登录成功~");
}
}); // 匿名内部类的核心目的是 简化代码!

// 用lamda表达式还可以更简化
btn.addActionListener(e -> JOptionPane.showMessageDialog(win, "再点一下~"));


win.setSize(500, 500);
win.setLocationRelativeTo(null); // 居中
win.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); // 关闭窗口退出程序
win.setVisible(true); // 可见、显示出来
}
}

枚举

定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.itheima.enum1;

public enum A {
// 注意:枚举类的第一行需要罗列的是枚举对象的名字
X, Y, Z; // 这里都是常量,写法需要大写+下划线的组合

// 正常类的内容也是可以写进来的,如下
private String name;

A() {
} // 默认私有

A(String name) {
this.name = name;
} // 默认私有

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}
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
package com.itheima.enum1;

public class Test {
public static void main(String[] args) {
// 因为是常量,所以需要用类变量的用法:类名.变量名
A a1 = A.X;
System.out.println(a1); // X
System.out.println(A.Y); // Y

// 枚举类的构造都是私有的,不能对外构建对象
// A a2 = new A();

// 枚举类的第一行都是常量,记录的是枚举类的对象
A a2 = A.Z;

// 枚举类提供了一些额外的API供我们使用
A[] as = A.values(); // 获取全部对象
System.out.println(as[0]); // X

A a3 = A.valueOf("Z"); // 根据常量名得到这个枚举对象
System.out.println(a3.name()); // Z // 获取a3的名字
System.out.println(a3.ordinal()); // 2 // 获取a3的索引

A a4 = A.values()[1];
System.out.println(a4); // Y
}
}

javap: Java class 文件分解器,可以反编译,也可以查看 java 编译器生成的字节码。用于分解 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
package com.itheima.enum1;

public enum B {
X {
@Override
public void show() {
}
}, Y("2024") {
@Override
public void show() {
System.out.println("My CardID:" + getCardID());
}
};

private String CardID;

B() {
}

B(String cardID) {
CardID = cardID;
}

public String getCardID() {
return CardID;
}

public void setCardID(String cardID) {
CardID = cardID;
}

public abstract void show();

}
1
2
3
4
5
6
7
8
9
10
package com.itheima.enum1;

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

B b1 = B.Y;
b1.show(); // My CardID:2024

}
}

应用场景:做信息标志和分类

对比 1:

1
2
3
4
5
6
package com.itheima.enum2;

public class Constant {
public static final int BOY = 0;
public static final int GIRL = 1;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.itheima.enum2;

public class Test {
public static void main(String[] args) {
show(1);
// show(22); // 不合法
show(Constant.BOY);
}

public static void show(int gender) {
switch (gender) {
case Constant.BOY:
System.out.println("Boy");
break;
case Constant.GIRL:
System.out.println("Girl");
break;
}
}
}

对比 2:

1
2
3
4
5
package com.itheima.enum2;

public enum Constant {
BOY, GIRL;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.itheima.enum2;

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

show(Constant.BOY); // 这里无法再用数字来传参,更加严谨
}

public static void show(Constant gender) { // 注意这里的接收类型作了修改
switch (gender) {
case BOY:
System.out.println("Boy");
break;
case GIRL:
System.out.println("Girl");
break;
}
}
}

泛型

对比 1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.itheima.generics;

import java.util.ArrayList;

public class Test {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("a");
list.add("b");
list.add(2024);
list.add(new cat());

for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i)); // 直接打印是可以的
String e = (String) list.get(i); // 强转为String的话,实例对象强转不了,就出现报错了
System.out.println(e); // class java.lang.Integer cannot be cast to class java.lang.String
}
}
}

class cat {
}

对比 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
package com.itheima.generics;

import java.util.ArrayList;

public class Test {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>(); // 等号后面尖括号中的String可以省略
list.add("a");
list.add("b");
// list.add(2024); // 因为做了String限制,所以这俩都报错了
// list.add(new cat());

for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
String e = (String) list.get(i);
System.out.println(e);
}

// 同理
ArrayList<cat> list1 = new ArrayList<>();
// ArrayList<cat> list1 = new ArrayList(); // 等号后面有没有<>好像也没区别啊
list1.add(new cat());

for (int i = 0; i < list1.size(); i++) {
System.out.println(list1.get(i));
}
}
}

class cat {
}

泛型类

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
package com.itheima.generics_class;

public class MyArrayList<E> { // EFG都行,字母随便写
private Object[] arr = new Object[10]; // 用Object这个基类来接收一切类型
private int size;

// add函数
public boolean add(E e) {
arr[size++] = e;
/*
进行一系列添加操作(此处省略)
*/
return true;
}

// get函数
public E get(int index) { // 自定义返回类型

/*
进行一系列取值操作(此处省略)
*/
return (E) arr[index];
}

}
1
2
3
4
5
6
7
package com.itheima.generics_class;

public class MyClass2<E, T> {
public void put(E e, T t) {

}
}
1
2
3
4
5
package com.itheima.generics_class;

public class MyClass3<E extends Animal> {

}
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
package com.itheima.generics_class;

public class Test {
public static void main(String[] args) {
MyArrayList<String> list = new MyArrayList<>();
list.add("A");
list.add("B");
String e = list.get(0);
System.out.println(e);

MyClass2<Cat, String> c2 = new MyClass2<>();
// c2.put(); 这里面就只能放个Cat类型,再放个Sting类型的

MyClass3<Animal> c4 = new MyClass3<>(); // 要么放个Animal
MyClass3<Dog> c3 = new MyClass3<>(); // 要么就放继承Animal的子类
}
}

class Animal {
}

class Cat {
}

class Dog extends Animal {
}

泛型接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.itheima.generic_interface;

public class Test {
public static void main(String[] args) {
/*
...
*/
}
}

class Teacher {
}

class Student {
}

1
2
3
4
5
6
7
8
9
10
package com.itheima.generic_interface;

import java.util.ArrayList;

public interface Data<T> { // 同理,这里T 也可以 extends XXX.
// 用T可以更好地泛写各种类,在实现接口的时候进行重写,还能使用接口的功能,更方便
void addData(T data);

ArrayList<T> getByName(String name); // 这是个自定义的函数类型,后面需要重构
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.itheima.generic_interface;

import java.util.ArrayList;

public class TeacherData implements Data<Teacher> { // 一系列的老师信息填入这个类

@Override
public void addData(Teacher data) {
/*
具体实现不再编写
*/
}

@Override
public ArrayList<Teacher> getByName(String name) {
/*
具体实现不再编写
*/
return null;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.itheima.generic_interface;

import java.util.ArrayList;

public class StudentData implements Data<Student> {
@Override
public void addData(Student data) {
/*
具体实现不再编写
*/
}

@Override
public ArrayList<Student> getByName(String name) {
/*
具体实现不再编写
*/
return null;
}
}

泛型方法

注:自己定义的类型变量才是泛型方法,用现成的类型变量不算。

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
package com.itheima.generics_method;

import java.util.ArrayList;

public class Test {
public static void main(String[] args) {
String rs = test("Java"); // 这里test里跟的是String类型,那么返回的也是String类型
System.out.println(rs);

Dog d = test(new Dog()); // 按Ctrl,鼠标摇到test处,会显示public static <T> Dog test(Dog t)
System.out.println(d);

ArrayList<Car> cars = new ArrayList<>();
cars.add(new BMW());
cars.add(new Benz());
drive(cars);

ArrayList<BMW> bmws = new ArrayList<>();
bmws.add(new BMW());
bmws.add(new BMW());
drive(bmws);

ArrayList<Benz> benzs = new ArrayList<>();
benzs.add(new Benz());
benzs.add(new Benz());
drive(benzs);
System.out.println(benzs.size()); // 打印出长度 2

// ArrayList<Dog> dogs = new ArrayList<>();
// dogs.add(new Dog());
// dogs.add(new Dog());
// drive(dogs); // dogs不是car类也不是car类的子类
}

// 泛型方法
public static <T> T test(T t) { // test里跟的是T类型,那么返回类型也是T
// <T>:这个部分声明了一个类型参数 T。表示这个方法是泛型方法,它可以处理任何类型的对象,而不是固定在某一种类型。
return t;
}

public static <T extends Car> void drive(ArrayList<T> cars) {
// 这里的返回值类型改成了void
}

// 通配符 “?” ,在使用泛型的时候可以代表一切类型
// ? extends car (控制上限,即Car或者Car的子类)
// ? super car (控制下限,即Car或者Car的父类)
public static void drive1(ArrayList<?> cars) {
}

public static void drive2(ArrayList<? extends Car> cars) {
}

public static void drive3(ArrayList<? super Car> cars) {
}
}

class Dog {
}

class Car {
}

class BMW extends Car {
}

class Benz extends Car {
}

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
package com.itheima.gengrics_attention;

import java.util.ArrayList;

public class Test {
public static void main(String[] args) {
// 1.泛型是工作在编译阶段的,一旦程序编译成class文件,class文件中就不存在泛型了,这就是泛型擦除。
// 说人话:泛型是给予你在写代码的时候方便的,编译成class后就要变成规范的样子了。
ArrayList<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
String rs = list.get(0);
System.out.println(rs);

// 2.泛型不支持基本数据类型,只能支持对象类型(引用数据类型)
// ArrayList<int> list = new ArrayList<>();
// ArrayList<double> list = new ArrayList<>();
// 注意:String不是基本数据类型,是引用数据类型

// 但是int、double、float等,他们的对象类型是得到支持的
ArrayList<Integer> list1 = new ArrayList<>();
list1.add(1);
ArrayList<Double> list2 = new ArrayList<>();
list2.add(1.0);
ArrayList<Float> list3 = new ArrayList<>();
list3.add(1.0f);
}
}

反编译后的文件

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
package com.itheima.gengrics_attention;

import java.io.PrintStream;
import java.util.ArrayList;

public class Test
{

public Test()
{
}

public static void main(String args[])
{
ArrayList list = new ArrayList();
list.add("A");
list.add("B");
list.add("C");
String rs = (String)list.get(0);
System.out.println(rs);
ArrayList list1 = new ArrayList();
list1.add(Integer.valueOf(1));
ArrayList list2 = new ArrayList();
list2.add(Double.valueOf(1.0D));
ArrayList list3 = new ArrayList();
list3.add(Float.valueOf(1.0F));
}
}