抽象类
抽象方法
- 抽象方法:将共性的行为(方法)抽取到父类之后,由于每一个子类执行的内容是不一样的。所以,在父类中不能确定具体的方法体。该方法就可以定义为抽象方法。
- 抽象类:如果一个类中存在抽象方法,那么该类就必须声明为抽象类
抽象类和抽象方法的定义格式
- 抽象方法的定义格式:
public abstract 返回值类型 方法名(参数列表);
- 抽象类的定义格式:
public abstract class 类名 {}
示例:
package com.itheima.test39;
public abstract class Person {
public abstract void work();
}
抽象类和抽象方法的注意事项
- 抽象类不能实例化
- 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
-
package com.itheima.test39; public abstract class Person { // public abstract void work(); public void sleep() { System.out.println("睡觉"); } }
-
- 可以有构造方法
-
package com.itheima.test39; public abstract class Person { private String name; private int age; // 创建子类对象时,给属性进行赋值 public Person() { } public Person(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; } public abstract void work(); public void sleep() { System.out.println("睡觉"); } }
-
- 抽象类的子类
- 要么重写抽象类的所有抽象方法(更多使用)
-
package com.itheima.test39; public class Student extends Person{ public Student() { } public Student(String name, int age) { super(name, age); } @Override public void work() { System.out.println("学生正在学习"); } }
-
- 要么是抽象类
-
package com.itheima.test39; public abstract class Teacher extends Person{ }
-
- 要么重写抽象类的所有抽象方法(更多使用)
package com.itheima.test39;
public class Test {
public static void main(String[] args) {
Student s = new Student("zhangsan", 20);
System.out.println(s.getName() + "," + s.getAge());
}
}
运行结果:
因此,可知,如果想创建一个对象,就必须定义一个类继承抽象类,并重写抽象类中所有的抽象方法,才可以对其进行实例化。抽象类中的构造方法起到创建子类对象时,给属性进行赋值的作用。
抽象类和抽象方法的意义
抽取共性时,如果无法确定方法体,就将方法定义为抽象的,并且强制让子类按照这种格式进行重写
编写带有抽象类的标准Javabean类
接口
接口的定义和使用
- 接口用关键字interface来定义
-
public interface 接口名 {}
-
- 接口不能实例化
- 接口与类之间是实现关系,通过implements关键字表示
-
public class 类名 implements 接口名 {}
-
- 接口的子类(实现类)
- 要么重写接口中所有的抽象方法
- 要么是抽象类
- 注意:
- 接口和类的实现关系,可以单实现,也可以多实现。
-
public class 类名 implements 接口名1,接口名2 {}
-
- 实现类可以在继承一个类的同时实现多个接口
-
public class 类名 extends 父类 implements 接口名1,接口名2 {}
-
- 接口和类的实现关系,可以单实现,也可以多实现。
编写带有接口和抽象类的标准Javabean类
package com.itheima.test40;
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 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 abstract void eat();
}
package com.itheima.test40;
public interface Swim {
public abstract void swim();
}
package com.itheima.test40;
public class Rabbit extends Animal{
public Rabbit() {
}
public Rabbit(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("吃胡萝卜");
}
}
package com.itheima.test40;
public class Dog extends Animal implements Swim{
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("狗刨");
}
}
package com.itheima.test40;
public class Frog extends Animal implements Swim{
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("蛙泳");
}
}
package com.itheima.test40;
public class Test {
public static void main(String[] args) {
Rabbit r = new Rabbit("lhj", 20);
System.out.println(r.getName() + "," + r.getAge());
r.eat();
Frog f = new Frog("fjq", 21);
System.out.println(f.getName() + "," + f.getAge());
f.eat();
f.swim();
}
}
运行结果:
接口中成员的特点
- 成员变量
- 只能是常量
- 默认修饰符:public static final
- 构造方法
- 没有
- 成员方法
- 只能是抽象方法
- 默认修饰符:public abstract
- JDK7以前:接口中只能定义抽象方法
- JDK8的新特性:接口中可以定义有方法体的方法
- JDK9的新特性:接口中可以定义私有方法
接口和类之间的关系
- 类和类的关系:
- 继承关系,只能单继承,不能多继承,但是可以多层继承
- 类和接口的关系:
- 实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
- 如果实现多个接口的时候,发现不同接口里面有同名方法,只用重写一次抽象方法即可,无需重复重写
示例:Inter1中有抽象方法method1、method2、method3;Inter2中有抽象方法method1、method2、method3、method4,Test实现Inter1、Inter2时,只需要重写四个抽象方法即可
package com.itheima.test42;
public interface Inter1 {
public abstract void method1();
public abstract void method2();
public abstract void method3();
}
package com.itheima.test42;
public interface Inter2 {
public abstract void method1();
public abstract void method2();
public abstract void method3();
public abstract void method4();
}
package com.itheima.test42;
public class Test implements Inter1, Inter2 {
@Override
public void method1() {
}
@Override
public void method2() {
}
@Override
public void method3() {
}
@Override
public void method4() {
}
}
- 接口和接口的关系:
- 继承关系,可以单继承,也可以多继承
- 但是如果一个实现类实现类最下面的子接口,那么就需要重写子接口所继承的所有的接口的抽象方法(包含子接口自身的抽象方法)
示例:Inter3继承Inter1、Inter2,Test实现Inter3的同时,不仅需要将Inter3中的抽象方法重写,还需要将Inter1、Inter2中的抽象方法重写
package com.itheima.test41;
public interface Inter1 {
public abstract void method1();
}
package com.itheima.test41;
public interface Inter2 {
public abstract void method2();
}
package com.itheima.test41;
public interface Inter3 extends Inter1, Inter2 {
public abstract void method3();
}
package com.itheima.test41;
public class Test implements Inter1, Inter2, Inter3 {
@Override
public void method1() {
}
@Override
public void method2() {
}
@Override
public void method3() {
}
}
编写带有接口和抽象类的标准Javabean类
package com.itheima.test43;
// 之所以定义Person类为抽象类
// 是因为直接创建Person类的对象没有意义
public abstract class Person {
private String name;
private int age;
public Person() {
}
public Person(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;
}
}
package com.itheima.test43;
public abstract class Sporter extends Person{
public Sporter() {
}
public Sporter(String name, int age) {
super(name, age);
}
public abstract void study();
}
package com.itheima.test43;
public abstract class Coach extends Person{
public Coach() {
}
public Coach(String name, int age) {
super(name, age);
}
public abstract void teach();
}
package com.itheima.test43;
public interface SpeakEnglish {
public abstract void speakEnglish();
}
package com.itheima.test43;
public class PingPongPlayer extends Sporter implements SpeakEnglish{
public PingPongPlayer() {
}
public PingPongPlayer(String name, int age) {
super(name, age);
}
@Override
public void study() {
System.out.println("乒乓球运动员正在学打乒乓");
}
@Override
public void speakEnglish() {
System.out.println("乒乓球运动员正在学习英语");
}
}
package com.itheima.test43;
public class BasketballPlayer extends Sporter{
public BasketballPlayer() {
}
public BasketballPlayer(String name, int age) {
super(name, age);
}
@Override
public void study() {
System.out.println("篮球运动员正在学打篮球");
}
}
package com.itheima.test43;
public class PingPongCoach extends Coach implements SpeakEnglish{
public PingPongCoach() {
}
public PingPongCoach(String name, int age) {
super(name, age);
}
@Override
public void teach() {
System.out.println("乒乓球教练正在教打乒乓");
}
@Override
public void speakEnglish() {
System.out.println("乒乓球教练正在学习英语");
}
}
package com.itheima.test43;
public class PingPongCoach extends Coach implements SpeakEnglish{
public PingPongCoach() {
}
public PingPongCoach(String name, int age) {
super(name, age);
}
@Override
public void teach() {
System.out.println("乒乓球教练正在教打乒乓");
}
@Override
public void speakEnglish() {
System.out.println("乒乓球教练正在学习英语");
}
}
package com.itheima.test43;
public class Test {
public static void main(String[] args) {
PingPongPlayer pp = new PingPongPlayer("zhangsan", 20);
System.out.println(pp.getName() + "," + pp.getAge());
pp.study();
pp.speakEnglish();
BasketballCoach bc = new BasketballCoach("lisi", 41);
System.out.println(bc.getName() + "," + bc.getAge());
bc.teach();
}
}
运行结果:
JDK8开始接口中新增的方法
- JDK7以前:接口只能定义抽象方法
- JDK8的新特性:接口中可以定义有方法体的方法。(默认、静态)
- JDK9的新特性:接口中可以定义私有方法
默认方法
允许在接口中定义默认方法,需要使用关键字default修饰
- 作用:解决接口升级的问题
接口中默认方法的定义格式:
- 格式:public default 返回值类型 方法名(参数列表) { }
- 范例:public default void show() { }
接口中默认方法的注意事项:
- 默认方法不是抽象方法,所以不强制被重写。但是如果被重写,重写的时候去掉default关键字
-
package com.itheima.test44; public interface Inter1 { public abstract void method(); public default void show() { System.out.println("接口里面的默认方法————show"); } }
package com.itheima.test44; public class InterImp implements Inter1{ @Override public void method() { } }
可以看到default关键字修饰的默认方法并不强制重写
-
修改一下InterImp里面的代码
-
package com.itheima.test44; public class InterImp implements Inter1{ @Override public void method() { } @Override public void show() { Inter1.super.show(); System.out.println("接口实现类的show方法"); } }
可以看到重写默认方法的时候default关键字去掉了
-
package com.itheima.test44; public class Test { public static void main(String[] args) { InterImp i = new InterImp(); i.show(); } }
运行结果:
-
- public可以省略,但default不能省略
- 如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写
可以看到InterImp同时实现的Inter1、Inter2接口中存在同名的默认方法show,InterImp就必须对默认方法show进行重写
静态方法
允许在接口中定义静态方法,需要用static修饰
接口中静态方法的定义格式:
- 格式:public static 返回值类型 方法名(参数列表) { }
- 范例:public static void show() { }
接口中静态方法的注意事项:
- 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
- public可以省略,但static不能省略
可以看到哪怕是在Inter的实现类InterImp中写了一个show方法,也不是对Inter内静态方法show的重写,顶多算是有同名的方法,并且接口中的静态方法show只能用接口名调用
运行结果:
JDK9新增的方法
- 之所以会有私有方法的出现,就是在一个接口当中,定义的多个默认方法的方法体中存在相同的代码,就会将这行代码提取出来,定义成一个私有方法,再以”方法名()“的形式进行调用
-
package com.itheima.test47; public interface Inter { public default void show1() { System.out.println("show1"); // System.out.println("这是一行代码"); log(); } public default void show2() { System.out.println("show2"); // System.out.println("这是一行代码"); log(); } private void log() { System.out.println("这是一行代码"); } }
-
- 若是定义的多个静态方法的方法体中存在相同的代码,也会将这行代码提取出来,定义成一个私有方法,再以”方法名()“的形式进行调用
-
package com.itheima.test47; public interface Inter { public static void show3() { System.out.println("show3"); // System.out.println("这是一行代码"); log1(); } public static void show4() { System.out.println("show4"); // System.out.println("这是一行代码"); log1(); } private static void log1() { System.out.println("这是一行代码"); } }
-
接口中私有方法的定义格式:
为默认方法服务
- 格式1:private 返回值类型 方法名(参数列表) { }
- 范例1:private void show() { }
为静态方法服务
- 格式2:private static 返回值类型 方法名(参数列表) { }
- 范例2:private static void method() { }
接口总结
- 接口代表规则,是行为的抽象。想让哪个类拥有一个行为,就让这个类实现对应的接口就可以了
- 当一个方法的参数是接口时,可以传递接口所有实现类的对象,这种方式称之为接口多态
适配器设计模式
- 当一个接口中抽象方法过多,但是我一直要使用其中一部分的时候,就可以使用适配器设计模式
- 书写步骤:
- 编写中间类XXXAdapter,实现对应的接口
- 对接口中的抽象方法进行空实现
- 让真正的实现类继承中间类,并重写需要用的方法
- 为了避免其他类创建适配器类的对象,中间的适配器类用abstract进行修饰
内部类
内部类概述
在一个类的里面,再定义一个类
举例:在A类的内部定义B类,B类就被称为内部类
package com.itheima.test49;
public class Car {
private String carName;
private int carAge;
private String carColor;
// 但是在外部类中并不能直接访问内部类中的成员
// 必须先在外部类当中创建内部类的对象
public void show() {
Engine e = new Engine();
System.out.println(carAge + "," + e.engineAge);
}
class Engine {
String engineName;
int engineAge;
// 内部类可以直接访问外部类中的成员,包括私有的成员
public void show() {
System.out.println(carName + "," + engineName);
}
}
}
内部类的分类
成员内部类
获取成员内部类的两种方式
- 方式一:当成员内部类被private修饰时。在外部类中编写方法,对外提供内部类对象
package com.itheima.test51;
public class Outer {
private int a;
private class Inner {
public void show() {
System.out.println("Inner的show");
}
}
public Inner getInnerImp() {
return new Inner();
}
}
package com.itheima.test51;
public class Test {
public static void main(String[] args) {
Outer o = new Outer();
Object i = o.getInnerImp();
System.out.println(i);
}
}
- 方式二:当成员内部类被非私有修饰时,直接创建对象。
-
Outer.Inner oi = new Outer().new Inner();
-
package com.itheima.test50;
public class Outer {
private int a;
class Inner {
public void show() {
System.out.println("Inner的show");
}
}
}
package com.itheima.test50;
public class Test {
public static void main(String[] args) {
Outer.Inner oi = new Outer().new Inner();
oi.show();
}
}
外部类成员变量和内部类成员变量重名时,在内部类如何访问?
System.out.println(Outer.this.变量名);
package com.itheima.test52;
public class Outer {
private int a = 10;
class Inner {
int a = 20;
public void show(){
int a = 30;
System.out.println(Outer.this.a);
System.out.println(this.a);
System.out.println(a);
}
}
}
package com.itheima.test52;
public class Test {
public static void main(String[] args) {
Outer.Inner oi = new Outer().new Inner();
oi.show();
}
}
静态内部类
- 静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问外部类当中非静态的成员需要创建对象
- 静态内部类和成员内部类的区别就在于,静态内部类是以static关键词修饰的成员内部类
- 创建静态内部类对象的格式:外部类名.内部类名 对象名 = new 外部类名.内部类名();
- 相比于创建成员内部类,静态内部类的创建无需再创建外部类的对象
如何调用静态内部类中的方法
- 调用非静态方法的格式:先创建对象,用对象调用
- 调用静态方法的格式:外部类名.内部类名.方法名();
package com.itheima.test53;
public class Outer {
private static int a = 10;
static class Inner {
public static void show1() {
System.out.println("静态内部类中的静态方法");
}
public void show2() {
System.out.println("静态内部类中的非静态方法");
}
}
}
package com.itheima.test53;
public class Test {
public static void main(String[] args) {
Outer.Inner.show1();
Outer.Inner oi = new Outer.Inner();
oi.show2();
}
}
运行结果:
局部内部类
- 将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量
- 外界无法直接使用,需要在方法内部创建对象并使用
- 该类可以直接访问外部类的对象,也可以访问方法内的局部变量
package com.itheima.test54;
public class Outer {
private int a = 10;
public void show() {
class Inner {
int b = 20;
public void method1() {
System.out.println(a);
System.out.println(b);
System.out.println("局部内部类中的非静态方法");
}
private static void method2() {
System.out.println("局部内部类中的静态方法");
}
}
// 如果需要调用局部内部类,就需要在方法中对其实例化
Inner i = new Inner();
i.method1();
Inner.method2();
}
}
package com.itheima.test54;
public class Test {
public static void main(String[] args) {
Outer o =new Outer();
o.show();
}
}
匿名内部类
什么是匿名内部类
匿名内部类本质上就是隐藏了名字的内部类,可以写在成员位置,也可以写在局部位置。
匿名内部类的格式
格式的细节
- 包含了继承或实现,方法重写,创建对象。
- 整体就是一个类的子类对象或者接口的实现类对象
使用场景
- 当方法的参数是接口或者类时,以接口为例,可以传递这个接口的实现类对象,如果实现类是要使用一次,就可以用匿名内部类简化代码
package com.itheima.test55;
public abstract class Animal {
public abstract void eat();
}
package com.itheima.test55;
public interface Swim {
public void swim();
}
package com.itheima.test55;
public class Test {
public static void main(String[] args) {
new Animal(){
@Override
public void eat() {
System.out.println("狗吃骨头");
}
};
new Swim(){
@Override
public void swim() {
System.out.println("游泳");
}
};
method(
new Animal() {
@Override
public void eat() {
System.out.println("狗吃骨头");
}
}
);
}
public static void method(Animal a) {
a.eat();
}
}
运行结果:
package com.itheima.test55;
public class Test2 {
public static void main(String[] args) {
Swim s = new Swim(){
@Override
public void swim() {
System.out.println("游泳");
}
};
new Animal(){
@Override
public void eat() {
System.out.println("吃饭");
}
}.eat();
}
}
运行结果: