面对对象进阶- 下(九)


面对对象进阶- 下(九)

抽象方法和抽象类

抽象方法:将共性的行为(方法)抽取到父类之后,由于每个子类的执行的内容不一样,所以在父类中不能确定具体的方法体。该方法就可以定义为抽象方法。
抽象类:若一个类中存在抽象方法,那么该类就必须声明为抽象类。

定义格式

抽象方法的定义格式
public abstract 返回值类型 方法名(参数列表);
抽象类的定义格式
public abstract class 类名{}

注意事项

  • 抽象类不能实例化(不能创造对象)
  • 抽象类中不一定有抽象方法,但有抽象方法的类一定是抽象类
  • 可以有构造方法
  • 抽象类的子类
    • 要么重写抽象类中的所有抽象方法
    • 要么是抽象类

范例

1
2
3
4
//需求
青蛙frog 属性:名字,年龄 行为:吃虫子,喝水
狗dog 属性:名字,年龄 行为:吃骨头,喝水
山羊sheep 属性:名字,年龄 行为:吃草,喝水
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
//Animal.java
package com.ljsblog.domain2;
public abstract class Animal{
private String name;
private int age;
public Animal(){}
public Animal(String name,int age){
this.name=name;
this.age=age;
}
public void setName(String name){
this.name=name;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age=age;
}
public int getAge(){
return age;
}
public abstract void eat();
public void drink(){
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
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
//Frog.java
package com.ljsblog.domain2;
public class Frog extends Animal {
public Frog(){}
public Frog(String name,int age){
super(name,age);
}
@Override
public void eat(){
System.out.println("吃虫子");
}
}

//Dog.java
package com.ljsblog.domain2;
public class Dog extends Animal{
public Dog(){}
public Dog(String name,int age){
super(name,age);
}
@Override
public void eat(){
System.out.println("吃骨头");
}
}

//Sheep.java
package com.ljsblog.domain2;
public class Sheep extends Animal{
public Sheep(){}
public Sheep(String name,int age){
super(name,age);
}
@Override
public void eat(){
System.out.println("吃草");
}
}

//Test.java
package com.ljsblog.domain2;
public class test {
public static void main(String[] args) {
Dog g = new Dog("傻狗", 18);
Frog f = new Frog("癞蛤蟆", 20);
Sheep s = new Sheep("喜洋洋", 3);
g.eat();
g.drink();
f.eat();
f.drink();
s.eat();
s.drink();
}
}
/*
吃骨头
喝水
吃虫子
喝水
吃草
喝水
*/

接口

接口就是一种规则,是对行为的抽象。

定义和使用

  • 接口用关键字interface定义:
    • public interface 接口名{}
  • 接口不能实例化。
  • 接口与类之间是实现关系,通过implements关键字表示:
    • public class 类名 implements 接口名{}
  • 接口的子类(实现类):
    • 要么重写接口类中的所有抽象方法
    • 要么是抽象类

注意事项

  1. 接口与类可以单实现,也可以多实现
    • public class 类名 implements 接口名1,接口名2…{}
  2. 实现类可以在继承一个类的同时实现多个接口
    • public class 类名 extends 父类名 implements 接口名1,接口名2…{}

范例

1
2
3
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
//父类
//Animal.java
package com.ljsblog.domain2;
public abstract class Animal{
private String name;
private int age;
public Animal(){}
public Animal(String name,int age){
this.name=name;
this.age=age;
}
public void setName(String name){
this.name=name;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age=age;
}
public int getAge(){
return age;
}
public abstract void eat();
}
1
2
3
4
5
6
//接口
//Regularity
package com.ljsblog.domain2;
public interface Regularity{
public abstract void swim();
}
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
//子类
//Frog.java
package com.ljsblog.domain2;
public class Frog extends Animal implements Regularity {
public Frog(){}
public Frog(String name,int age){
super(name,age);
}
@Override
public void eat(){
System.out.println("吃虫子");
}
@Override
public void swim(){
System.out.println("蛙泳");
}
}

//Dog.java
package com.ljsblog.domain2;
public class Dog extends Animal implements Regularity{
public Dog(){}
public Dog(String name,int age){
super(name,age);
}
@Override
public void eat(){
System.out.println("吃骨头");
}
@Override
public void swim(){
System.out.println("狗刨");
}
}

//Rabbit.java
package com.ljsblog.domain2;
public class Rabbit extends Animal{
public Rabbit(){}
public Rabbit(String name,int age){
super(name,age);
}
@Override
public void eat(){
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
//测试类
//Test.java
package com.ljsblog.domain2;
public class Test{
public static void main(String[] args){
Frog f=new Frog();
f.eat();
f.swim();
Dog d=new Dog();
d.eat();
d.swim();
Rabbit r=new Rabbit();
r.eat();
}
}
/*
吃虫子
蛙泳
吃骨头
狗刨
吃胡萝卜
*/

接口中成员的特点

  • 成员变量
    • 只能是常量,默认修饰符:public static final
  • 构造方法-无
  • 成员方法
    • 只能是抽象方法,默认修饰符:public abstract

接口与类的关系

  • 类与类的关系
    • 继承关系。只能单继承,不能多继承,但可多层继承
  • 类与接口的关系
    • 实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
    • 若多个接口中有重名的方法,只需重写一次
  • 接口与接口的关系
    • 继承关系,可以单继承,也可以多继承
    • 若实现最下方的子接口,则需要重写所有的抽象方法

范例

1
2
3
4
5
//需求
乒乓球运动员: 姓名,年龄,学打乒乓球,说英语
篮球运动员: 姓名,年龄,学打篮球
乒乓球教练: 姓名,年龄,教打乒乓球,说英语
篮球教练: 姓名,年龄,教打篮球
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//Person.java
package com.ljsblog.domain3;
public class Test {
public static void main(String[] args){
PingAthlete pa=new PingAthlete("张三",16);
pa.study();
pa.speakEnglish();
BasketballAthele ba=new BasketballAthele("李四",16);
ba.study();
PingTeacher pt=new PingTeacher("王五一",26);
pt.teach();
pt.speakEnglish();
BasketBallTeacher bt=new BasketBallTeacher("王五二",26);
bt.teach();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//Athlete.java
package com.ljsblog.domain3;
public abstract class Athlete extends Person{
public Athlete(){}
public Athlete(String name,int age){
super(name,age);
}
public abstract void study();
}

//Teacher.java
package com.ljsblog.domain3;
public abstract class Teacher extends Person{
public Teacher(){}
public Teacher(String name,int age){
super(name,age);
}
public abstract void teach();
}

//Speak.java
package com.ljsblog.domain3;
public interface Speak{
public abstract void speakEnglish();
}

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
//BasketballAthele.java
package com.ljsblog.domain3;
public class BasketballAthele extends Athlete{
public BasketballAthele(){}
public BasketballAthele(String name,int age){
super(name,age);
}
@Override
public void study(){
System.out.println("年龄为"+getAge()+"的篮球运动员"+getName()+"学打篮球");
}
}

//BasketBallTeacher.java
package com.ljsblog.domain3;
public class BasketBallTeacher extends Teacher{
public BasketBallTeacher(){}
public BasketBallTeacher(String name,int age){
super(name,age);
}
@Override
public void teach(){
System.out.println("年龄为"+getAge()+"的篮球教练"+getName()+"教打篮球");
}
}

//PingAthlete.java
package com.ljsblog.domain3;
public class PingAthlete extends Athlete implements Speak{
public PingAthlete(){}
public PingAthlete(String name,int age){
super(name,age);
}
@Override
public void study(){
System.out.println("年龄为"+getAge()+"的乒乓球运动员"+getName()+"学打乒乓球");
}
@Override
public void speakEnglish(){
System.out.println("乒乓球运动员在说英语");
}
}

//PingTeacher.java
package com.ljsblog.domain3;
public class PingTeacher extends Teacher implements Speak{
public PingTeacher(){}
public PingTeacher(String name,int age){
super(name,age);
}
@Override
public void teach(){
System.out.println("年龄为"+getAge()+"的乒乓球教练"+getName()+"教打乒乓球");
}
@Override
public void speakEnglish(){
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
//测试类
package com.ljsblog.domain3;
public class Test {
public static void main(String[] args){
PingAthlete pa=new PingAthlete("张三",16);
pa.study();
pa.speakEnglish();
BasketballAthele ba=new BasketballAthele("李四",16);
ba.study();
PingTeacher pt=new PingTeacher("王五一",26);
pt.teach();
pt.speakEnglish();
BasketBallTeacher bt=new BasketBallTeacher("王五二",26);
bt.teach();
}
}
/*
年龄为16的乒乓球运动员张三学打乒乓球
乒乓球运动员在说英语
年龄为16的篮球运动员李四学打篮球
年龄为26的乒乓球教练王五一教打乒乓球
乒乓球教练在说英语
年龄为26的篮球教练王五二教打篮球
*/

JDK8后接口新增方法

默认方法

允许在接口中定义默认方法,需使用关键字default修饰,以此解决接口升级的问题。
接口中默认方法定义格式
public default 返回值类型 方法名(参数列表){}
注意事项

  • 默认方法不是抽象方法,不强制被重写;若要重写,重写时去掉default关键字。
  • public可省,default不可省
  • 若实现多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写。

范例

1
2
3
4
5
6
7
//接口
public interface Inter{
public abstract void show1();
public default void show2(){
System.out.println("默认方法");
}
}
1
2
3
4
5
6
public class A implements Inter{
@Override
public void show1(){
System.out.println("抽象方法");
}
}
1
2
3
4
5
6
7
8
9
10
11
public class Test{
public static void main(String[] args){
A a=new A();
a.show1();
a.show2();
}
}
/*
抽象方法
默认方法
*/

静态方法

允许在接口中定义静态方法,需要用static修饰。
定义格式
public static 返回值类型 方法名(参数列表){}
注意事项

  • 静态方法只能用接口名调用,不能通过实现类名或者对象名调用
  • public可省,static不能省
  • 静态方法不能被重写

范例

1
2
3
4
5
6
//接口
public interface Inter{
public static void show(){
System.out.println("静态方法");
}
}
1
2
3
4
5
6
7
//测试类
//Test.java
public class Test{
public static void main(String[] args){
Inter.show();//静态方法
}
}

应用

  1. 接口代表规则,是行为的抽象,想让哪个类拥有一个行为,就让这个类实现对应的接口就可以了。
  2. 当一个方法参数是接口时,可传递接口所有的实现类的对象,这种方式称为接口多态。

适配器设计模式

设计模式就是各种套路,当一个接口中抽象方法过多,但只要使用其中一部分时,就可以使用适配器使用模式。
书写步骤

  • 编写中间类XXXAdapter,实现对应的接口
  • 对接口中的抽象方法进行空实现,即方法体为空
  • 让真正的实现类继承中间类,并重写需要用的方法
  • 为避免其他类创建适配器类的对象,适配器类用abstract修饰。

范例

1
2
3
4
5
6
7
8
9
//接口
public interface Inter{
public abstract void show1();
public abstract void show2();
public abstract void show3();
public abstract void show4();
public abstract void show5();
public abstract void show6();
}
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
//适配器类
public class InterAdapter implements Inter{

@Override
public void show1() {

}

@Override
public void show2() {

}

@Override
public void show3() {

}

@Override
public void show4() {

}

@Override
public void show5() {

}

@Override
public void show6() {

}
}
1
2
3
4
5
6
7
//实现类
public class Im extends InterAdapter{
@Override
public void show3(){
System.out.println("方法3");
}
}
1
2
3
4
5
6
7
8
//测试类
package com.ljsblog.domain5;
public class Test {
public static void main(String[] args){
Im i=new Im();
i.show3();//方法3
}
}

内部类

类的五大成员

  1. 属性
  2. 方法
  3. 构造方法
  4. 代码块
  5. 内部类

内部类定义:在一个类中,再定义一个类,例如,在A类内部定义B类,B类称为内部类。
内部类表示的事物是外部类的一部分,内部类单独出现没有任何意义。
访问特点

  • 内部类可以直接访问外部类的成员,包括私有
  • 外部类要访问内部类的成员,必须创建对象

应用场景:B类表示的事物是A类的一步,且B类存在没有任何意义。例如:ArrayList迭代器,汽车的发动机,人的心脏。
分类

  • 成员内部类
  • 静态内部类
  • 局部内部类
  • 匿名内部类

成员内部类

写在成员位置,即类中方法外,属于外部类的成员。
成员内部类可以被一些修饰符修饰,比如:private,默认,protected,public,static等。
在成员内部类中,JDK16之前不能定义静态变量,JDK16开始才可以定义静态变量。
获取内部类对象方式

  1. 在外部类中编写方法,对外提供内部类的对象。(private)
  2. 直接创建,格式:外部类名.内部类名 对象名=外部类对象.内部类对象;

范例
方法一

1
2
3
4
5
6
7
8
9
10
package com.ljsblog.domain5;
public class Outer{
public Inner getInner(){
return new Inner();
}
private class Inner{
public void show(){
}
}
}
1
2
3
4
5
6
7
package com.ljsblog.domain5;
public class Test{
public static void main(String[] args){
Outer o=new Outer();
System.out.println(o.getInner());
}
}

方法二

1
2
3
4
5
6
7
8
9
10
package com.ljsblog.domain5;
public class Outer{
public Inner getInner(){
return new Inner();
}
public class Inner{
public void show(){
}
}
}
1
2
3
4
5
6
7
package com.ljsblog.domain5;
public class Test{
public static void main(String[] args){
Outer.Inner oi=new Outer().new Inner();
System.out.println(oi);
}
}

当外部类成员变量和内部类成员变量重名时,内部类访问外部类成员变量:
System.out.println(Outer.this.变量名);
范例:

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.ljsblog.domain5;
public class Outer{
private int a=1;
class Inner{
private int a=2;
public void show(){
int a=3;
System.out.println(Outer.this.a);//1
System.out.println(this.a);//2
System.out.println(a);//3
}
}
}
1
2
3
4
5
6
7
package com.ljsblog.domain5;
public class Test{
public static void main(String[] args){
Outer.Inner oi=new Outer().new Inner();
oi.show();
}
}

静态内部类

静态内部类只能访问外部类中的静态变量和静态方法,若要访问非静态的需要创建对象。
创建静态内部类对象的格式
外部类名.内部类名 对象名=new 外部类名.内部类名();
调用非静态方法的格式:先创建对象,用对象调用。
调用静态方法的格式
外部类名.内部类名.方法名();
范例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.ljsblog.domain5;
public class Outer{
int a=10;
static int b=20;
static class Inner{
public static void show1(){
System.out.println("静态方法");
Outer o=new Outer();
System.out.println(o.a);
System.out.println(b);
}
public void show2(){
System.out.println("非静态方法");
Outer o=new Outer();
System.out.println(o.a);
System.out.println(b);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.ljsblog.domain5;
public class Test{
public static void main(String[] args){
Outer.Inner oi=new Outer.Inner();
Outer.Inner.show1();
oi.show2();
}
}
/*
静态方法
10
20
非静态方法
10
20
*/

局部内部类

  1. 将内部类定义在方法中就叫局部内部类,类似于方法中的局部变量
  2. 外界是无法直接使用,需要在方法内部创建对象并使用
  3. 该类可以直接访问外部类的成员,也可方法方法内部的局部变量
  4. 因为局部内部类没啥用,所以就不举例了。

匿名内部类

匿名内部类本质上就是隐藏了名字的内部类,并非真的没有名字。
格式

1
2
3
4
new 类名/接口名(){
重写方法;
}
//其中包含了继承或实现,方法重写,创建对象,整体就是一个类的子类或者接口的实现类对象

使用场景:当方法的参数是接口或类时,以接口为例,可传递该接口的实现类对象,若实现类仅使用一次,可用匿名内部类简化代码。
范例

1
2
3
4
//接口
public abstract interface Inter{
public abstract void speakEnglish();
}
1
2
3
4
//父类
public abstract class Animal{
public abstract void eat();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//测试类
public class Test{
public static void main(String[] args){
Inter i=new Inter(){
@Override
public void speakEnglish(){
System.out.println("讲英语");
}
};
i.speakEnglish();
Animal a=new Animal(){
@Override
public void eat(){
System.out.println("狗吃骨头");
}
};
a.eat();
}
}
/*
讲英语
狗吃骨头
*/

Author: ljs
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source ljs !
评论
  TOC