五、抽象类
#抽象方法
抽象方法:将共性的行为(方法)抽取到父类之后。
由于每一个子类执行的内容是不一样
所以,在父类中不能确定具体的方法体
该方法就可以定义为抽象方法
抽象类:如果一个类中存在抽象方法,那么该类就必须声明为抽象类
#抽象类和抽象方法的定义格式
抽象方法的定义格式:
public abstract 返回值类型 方法名(参数列表);
抽象类的定义格式:
public abstract class 类名{}
#抽象类和抽象方法的意义
强制子类必须按照这种格式进行重写
#抽象类和抽象方法的注意事项
抽象类不能实例化
抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
可以有构造方法
抽象类的子类
要么重写抽象类中的所有抽象方法
要么是抽象类
练习、编写带有抽象类的标准Javabean类
青蛙frog 属性:名字,年龄 行为:吃虫子,喝水
狗Dog 属性:名字,年龄 行为:吃骨头,喝水
山羊Sheep 属性:名字,年龄 行为:吃草,喝水
Animal.Java
package cn.nwafu.abstractdemo2;
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 void drink(){
System.out.println("动物在喝水");
}
public abstract void eat();
}
frog.Java
package cn.nwafu.abstractdemo2;
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 cn.nwafu.abstractdemo2;
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 cn.nwafu.abstractdemo2;
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 cn.nwafu.abstractdemo2;
import java.sql.SQLOutput;
public class Test {
public static void main(String[] args) {
frog f = new frog("qingwa",2);
System.out.println(f.getName()+","+f.getAge());
f.drink();
f.eat();
Dog d = new Dog("taidi",1);
System.out.println(d.getName()+","+d.getAge());
d.drink();
d.eat();
Sheep s = new Sheep("shanyang",3);
System.out.println(s.getName()+","+s.getAge());
s.drink();
s.eat();
}
}
六、接口
#为什么有接口?
接口 :就是一种规则,是对行为的抽象
#接口的定义和使用
接口用关键字interface来定义
public interface 接口名{}
接口不能实例化
接口和类之间是实现关系,通过implements关键字表示
public class 类名 implements 接口名{}
接口的子类(实现类)
要么重写接口中的所有抽象方法
要么是抽象类
注意1:接口和类的实现关系,可以单实现,也可以多实现。
public class 类名 implements 接口名1,接口名2{}
注意2:实现类还可以在继承一个类的同时实现多个接口。
public class类名 extends 父类 implements 接口名1,接口名2{}
#接口中成员的特点
成员变量
只能是常量
默认修饰符:public static final
构造方法 没有
成员方法
只能是抽象方法
默认修饰符:public abstract
JDK7以前:接口中只能定义抽象方法。
JDK8的新特性:接口中可以定义有方法体的方法。
JDK9的新特性:接口中可以定义私有方法。
#接口和类之间的关系
1.类和类的关系
继承关系,只能单继承,不能多继承,但是可以多层继承
2.类和接口的关系
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
3.接口和接口的关系
继承关系,可以单继承,也可以多继承
练习、编写带有接口和抽象类的标准Javabean类
我们现在有乒乓球运动员和篮球运动员,乒乓球教练和篮球教练。
为了出国交流,跟乒乓球相关的人员都需要学习英语。
请用所有知识分析,在这个案例中,哪些是具体类,哪些是抽象类,哪些是接口?
乒乓球运动员:姓名,年龄,学打乒乓球,说英语
篮球运动员:姓名,年龄,学打篮球
乒乓球教练:姓名,年龄,教打乒乓球,说英语
篮球教练:姓名,年龄,教打篮球
思路一
思路二、
BasketballSporter.Java
package cn.nwafu.a01demo5;
public class BasketballSporter extends Sporter{
public BasketballSporter() {
}
public BasketballSporter(String name, int age) {
super(name, age);
}
@Override
public void study() {
System.out.println("篮球运动员在学习");
}
}
BasketCoach.Java
package cn.nwafu.a01demo5;
public class BasketCoach extends Coach{
public BasketCoach() {
}
public BasketCoach(String name, int age) {
super(name, age);
}
@Override
public void teach() {
System.out.println("篮球教练在教学");
}
}
Coach.Java
package cn.nwafu.a01demo5;
public abstract class Coach extends Person{
public Coach() {
}
public Coach(String name, int age) {
super(name, age);
}
public abstract void teach();
}
English.Java
package cn.nwafu.a01demo5;
public interface English {
public abstract void speakEnglish();
}
Person.Java
package cn.nwafu.a01demo5;
//因为现在我不想让外界去直接创建人的对象
//因为直接创建顶层父类人的对象此时是没有意义的
//所以我就把他写为抽象的。
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;
}
}
PingPangCoach.Java
package cn.nwafu.a01demo5;
public class PingPangCoach extends Coach
implements English{
public PingPangCoach() {
}
public PingPangCoach(String name, int age) {
super(name, age);
}
@Override
public void teach() {
System.out.println("乒乓球教练在教学");
}
@Override
public void speakEnglish() {
System.out.println("乒乓球教练在说英语");
}
}
PingPangSporter.Java
package cn.nwafu.a01demo5;
public class PingPangSporter extends Sporter implements English{
public PingPangSporter() {
}
public PingPangSporter(String name, int age) {
super(name, age);
}
@Override
public void study() {
System.out.println("乒乓球运动员在学习");
}
@Override
public void speakEnglish() {
System.out.println("乒乓球运动员在说英语");
}
}
Sporter.Java
package cn.nwafu.a01demo5;
public abstract class Sporter extends Person{
public Sporter() {
}
public Sporter(String name, int age) {
super(name, age);
}
public abstract void study();
}
Test.Java
package cn.nwafu.a01demo5;
public class Test {
public static void main(String[] args) {
// 练习、编写带有接口和抽象类的标准Javabean类
// 我们现在有乒乓球运动员和篮球运动员,乒乓球教练和篮球教练。
// 为了出国交流,跟乒乓球相关的人员都需要学习英语。
// 请用所有知识分析,在这个案例中,哪些是具体类,哪些是抽象类,哪些是接口?
// 乒乓球运动员:姓名,年龄,学打乒乓球,说英语
// 篮球运动员:姓名,年龄,学打篮球
// 乒乓球教练:姓名,年龄,教打乒乓球,说英语
// 篮球教练:姓名,年龄,教打篮球
PingPangSporter pps = new PingPangSporter("xuxin",27);
System.out.println(pps.getName()+","+pps.getAge());
pps.study();
pps.speakEnglish();
}
}
#补充知识点
一、JDK8与JDK9新增的方法
●JDK7以前:接口中只能定义抽象方法。
●JDK8的新特性:接口中可以定义有方法体的方法。(默认、静态)
●JDK9的新特性:接口中可以定义私有方法。
私有方法分为两种:普通的私有方法,静态的私有方法
1、JDK8开始接口中新增的方法
a.允许在接口中定义默认方法,需要使用关键字default修饰
作用:解决接口升级的问题
接口中默认方法的定义格式:
格式: public default 返回值类型 方法名(参数列表){}
范例: public default void show(){ }
接口中默认方法的注意事项:
默认方法不是抽象方法,所以不强制被重写。但是如果被重写,重写的时候去掉default关键字
public可以省略,default不能省略
如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写
Test.Java
package cn.nwafu.a01demo6;
public class Test {
public static void main(String[] args) {
//接口中默认方法的定义格式:
//格式: public default 返回值类型 方法名(参数列表){}
//范例: public default void show(){ }
//接口中默认方法的注意事项:
//默认方法不是抽象方法,所以不强制被重写。但是如果被重写,重写的时候去掉default关键字
//public可以省略,default不能省略
//如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写
InterImpl ii = new InterImpl();
ii.method1();
ii.show();
}
}
InterA.Java
package cn.nwafu.a01demo6;
public interface InterA {
// 接口中默认方法的定义格式:
// 格式: public default 返回值类型 方法名(参数列表){}
// 范例: public default void show(){ }
// 接口中默认方法的注意事项:
// 默认方法不是抽象方法,所以不强制被重写。但是如果被重写,重写的时候去掉default关键字
// public可以省略,default不能省略
// 如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写
public default void show(){
System.out.println("InterA接口中的默认方法-----show");
}
public abstract void method1();
}
InterB.Java
package cn.nwafu.a01demo6;
public interface InterB {
public default void show(){
System.out.println("InterB接口中的默认方法-----show");
}
}
InterImpl.Java
package cn.nwafu.a01demo6;
public class InterImpl implements InterA ,InterB{
@Override
public void show() {
System.out.println("重写接口中的show方法");
}
@Override
public void method1() {
System.out.println("实现类重写的抽象方法");
}
}
b.允许在接口中定义定义静态方法,需要用static修饰
接口中静态方法的定义格式:
格式: public static 返回值类型 方法名(参数列表){ }
范例: public static void show(){ }
接口中静态方法的注意事项:
●静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
public可以省略,static不能省略
Test.Java
package cn.nwafu.a01demo7;
public class Test {
public static void main(String[] args) {
// 接口中静态方法的定义格式:
//格式: public static 返回值类型 方法名(参数列表){ }
//范例: public static void show(){ }
//接口中静态方法的注意事项:
//静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
//public可以省略,static不能省略
//调用接口中的静态方法
Inter.show();
//调用实现类中的静态方法
//注意:静态发方法不是重写,只是刚好方法名一样而已
//子类把从父类继承下来的虚方法表里面的方法进行覆盖了,这才叫重写。
InterImpl.show();
}
}
InterImpl.Java
package cn.nwafu.a01demo7;
public class InterImpl implements Inter{
@Override
public void method() {
System.out.println("InterImpl重写的抽象方法");
}
public static void show(){
System.out.println("实现类中的静态方法");
}
}
Inter.Java
package cn.nwafu.a01demo7;
public interface Inter {
public abstract void method();
public static void show(){
System.out.println("接口中的静态方法");
}
}
2.JDK9新增的方法
私有方法分为两种:普通的私有方法,静态的私有方法
接口中私有方法的定义格式:
格式1: private返回值类型方法名(参数列表){}
范例1: private void show(){ }
格式2: private static返回值类型方法名(参数列表){ }
范例2: private static void method(){ }
InterA.Java
package cn.nwafu.a01demo8;
public interface InterA {
public default void show1(){
System.out.println("show1开始执行了");
//System.out.println("记录在运行过程中的各种细节,这里有100行代码");
show3();
}
public static void show2(){
System.out.println("show2开始执行了");
//System.out.println("记录在运行过程中的各种细节,这里有100行代码");
show4();
}
//普通的私有方法,给默认方法服务的
//注意:删掉default
private /* default */ void show3(){
System.out.println("记录在运行过程中的各种细节,这里有100行代码");
}
//静态的的私有方法,给静态方法服务的
private static void show4(){
System.out.println("记录在运行过程中的各种细节,这里有100行代码");
}
}
二、接口的应用
1.接口代表规则,是行为的抽象。想要让哪个类拥有一个行为,就让这个类实现对应的接口就可以了。
2. 当一个方法的参数是接口时,可以传递接口所有实现类的对象,这种方式称之为接口多态。
三、适配器设计模式
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。
简单理解:设计模式就是各种套路。
适配器设计模式:解决接口与接口实现类之间的矛盾问题
1.当一个接口中抽象方法过多,但是我只要使用其中一部分的时候,就可以适配器设计模式
2.书写步骤:
编写中间类XXXAdapter,实现对应的接口对接口中的抽象方法进行空实现
让真正的实现类继承中间类,并重写需要用的方法。
为了避免其他类创建适配器类的对象,中间的适配器类用abstract进行修饰
Inter.Java
package cn.nwafu.a01demo9;
public interface Inter {
//需求,只想用method5()
void menthod1();
void menthod2();
void menthod3();
void menthod4();
void menthod5();
void menthod6();
void menthod7();
void menthod8();
void menthod9();
void menthod10();
}
InterAdapter.Java
package cn.nwafu.a01demo9;
//设置成abstract是为了不让外界创建对象,没有意义
public abstract class InterAdapter implements Inter{
@Override
public void menthod1() {
}
@Override
public void menthod2() {
}
@Override
public void menthod3() {
}
@Override
public void menthod4() {
}
@Override
public void menthod5() {
}
@Override
public void menthod6() {
}
@Override
public void menthod7() {
}
@Override
public void menthod8() {
}
@Override
public void menthod9() {
}
@Override
public void menthod10() {
}
}
InterImpl.Java
package cn.nwafu.a01demo9;
public class InterImpl extends InterAdapter{
//我需要用到哪个方法,就重写哪个方法就可以了
@Override
public void menthod5() {
System.out.println("只用第五个方法");
}
}
七、内部类
#类的五大成员:
属性、方法、构造方法、代码块、内部类
#什么是内部类?
在一个类的里面,再定义一个类。
举例:在A类的内部定义B类,B类就被称为内部类
#为什么要学习内部类?
B类表示的事物是A类的一部分,且B单独存在没有意义。
#内部类的分类
成员内部类
静态内部类
局部内部类
匿名内部类
1.成员内部类
写在成员位置的,属于外部类的成员
*成员内部类的代码如何书写
成员内部类可以被一些修饰符所修饰,比如: private, 默认,protected, public, static等
在成员内部类里面,JDK16之前不能定义静态变量,JDK 16开始才可以定义静态变量。
*如何创建成员内部类的对象
方式一:
在外部类中编写方法,对外提供内部类的对象。
方式二:
直接创建格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
范例: Outer.Inner oi = new Outer( ).new Inner( );
*成员内部类如何获取外部类的成员变量
练习、
Test.Java
package cn.nwafu.a01innerclassdemo3;
public class Test {
public static void main(String[] args) {
//创建内部类的对象并且调用show方法
Outer.inner oi = new Outer().new inner();
oi.show();
}
}
Outer.Java
package cn.nwafu.a01innerclassdemo3;
public class Outer {
private int a = 10;
class inner{
private int a =20;
public void show(){
int a =30;
// Other.this 获取了外部类对象的地址值(见笔记)
System.out.println(Outer.this.a);//10
System.out.println(this.a);//20
System.out.println(a);//30
}
}
}
2.静态内部类
#什么是静态内部类?
一种特殊的成员内部类
静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问非静态的需要创建对象。
#创建静态内部类对象的格式:外部类名.内部类名 对象名 = new 外部类名.内部类名();
#调用非静态方法的格式:先创建对象,用对象调用
#调用静态方法的格式:外部类名.内部类名.方法名();
Outer.Java
package cn.nwafu.a01innerclassdemo4;
import java.io.OutputStream;
public class Outer {
int a =10;
static int b = 20;
//内部静态类
static class Inter{
public void show1(){
//System.out.println(a);报错!因为a为非静态
System.out.println(b);
Outer o =new Outer();
System.out.println(o.a);//调用需要创建对象
System.out.println("非静态类的方法被调用了");
}
public static void show2(){
//System.out.println(a);报错!因为a为非静态
System.out.println(b);
System.out.println("静态类的方法被调用了");
}
}
}
Test.Java
package cn.nwafu.a01innerclassdemo4;
public class Test {
public static void main(String[] args) {
//注意事项:
//1.静态内部类也是成员内部类中的一种
//2.静态内部类只能访问外部类中的静态变量和静态方法,
//如果想要访问非静态的需要创建外部类的对象。
//创建静态内部类对象的格式:
//外部类名.内部类名 对象名= new 外部类名.内部类名();
//调用静态方法的格式:外部类名.内部类名.方法名();
//创建静态内部类的对象
//只要是静态的东西,都可以用类名点直接获取
Outer.Inter oi = new Outer.Inter();
oi.show1();
//静态方法
Outer.Inter.show2();
}
}
3.局部内部类(了解)
1.将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量。
2.外界是无法直接使用,需要在方法内部创建对象并使用
3.该类可以直接访问外部类的成员,也可以访问方法内的局部变量。
4.匿名内部类(重要)
本质上是隐藏了名字的内部类,可以写在成员位置,也可局部位置
#格式:new 类名或者接口名(){
重写方法;
};
#格式的细节
包含了继承或实现,方法重写,创建对象。
整体就是一个类的子类对象或者接口的实现类对象
#使用场景
当方法的参数是接口或者类时,
以接口为例,可以传递这个接口的实现类对象,
如果实现类只要使用一次,就可以用匿名内部类简化代码。
Test.Java
package cn.nwafu.a01innerclassdemo6;
public class Test {
public static void main(String[] args) {
//匿名内部类(重要)
//本质上是隐藏了名字的内部类
//#格式:new 类名或者接口名(){
// 重写方法;
//};
new Swim() {
@Override
public void swim() {
System.out.println("重写swim方法");
}
public void method() {
System.out.println("method");
}
};
method1(
new Animal(){
@Override
public void eat() {
System.out.println("gouchigutou");
}
}
);
//在测试类中调用下面的method方法?
//以前的方式如何调用?
//要自己写一个子类继承Animal类
//再创建子类的对象,传递给method方法
// Dog d = new Dog();
// method1(d);
//如果Dog类我只要用一次,那么还需要单独定义一个类太麻烦了。
}
public static void method1(Animal d){// Animal a = 子类对象 多态
d.eat();//编译看左边,运行看右边
}
}
Animal.Java
package cn.nwafu.a01innerclassdemo6;
public abstract class Animal {
public abstract void eat();
}
Dog.Java
package cn.nwafu.a01innerclassdemo6;
public class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃骨头2");
}
}
Test2.Java
package cn.nwafu.a01innerclassdemo6;
public class Test2 {
public static void main(String[] args) {
//接口的实现类对象
//接口多态
Swim s = new Swim(){
@Override
public void swim() {
System.out.println("swim重写");
}
};
s.swim();
new Swim(){
@Override
public void swim() {
System.out.println("swim重写");
}
}.swim();//调用方法
}
}
Swim.Java
package cn.nwafu.a01innerclassdemo6;
public interface Swim {
public abstract void swim();
}
最后结尾附上我的源码,BiYing/Java学习12 - 码云 - 开源中国 (gitee.com)感兴趣的可以看看哈!