一、抽象类
1、抽象方法
-
抽象方法的定义格式:
public abstract 返回值类型 方法名(参数列表);
-
抽象类的定义格式:
public abstract class 类名{}
2、抽象类和抽象方法的注意事项
-
抽象类不能实列化
-
抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
-
可以有构造方法
-
抽象类的子类
-
要么重写抽象类中的所有抽象方法
-
要么是抽象类
-
3、抽象类的作用是什么样的?
抽取共性时,无法确定方法体,就把方法定义为抽象的。
强制让子类按照某种格式重写。
抽象方法所在的类,必须时抽象类
二、接口
1、接口的定义和使用
-
接口用关键字
interface
来定义public interface 接口名{}
-
接口不能实例化
-
接口和类之间是实现关系,通过
implements
关键字表示
public class 类名 implements 接口名{}
-
接口的子类(实现类)
-
要么重写接口中的所有抽象方法
-
要么是抽象类
-
注意1:接口和类的实现关系,可以单实现,也可以多实现。
public class 类名 implements 接口名1,接口名2{}
注意2:实现类还可以在继承一个类的同时实现多个接口。
public class 类名 extends 父类 implements 接口名1,接口名2{}
2、接口中成员的特点
-
成员变量
-
只能是常量
-
默认修饰符:
public static final
-
-
构造方法
-
没有
-
-
成员方法
-
只能是抽象方法
-
默认修饰符:public abstract
-
-
JDK7以前:接口中只能定于抽象方法
-
JDK8的新特性:接口中可以定义有方法体的方法。
-
JDK9的新特性:接口中可以定义私有方法。
3、接口和类之间的关系
-
类和类的关系
-
继承关系,只能单继承,不能多继承,但是可以多层继承
-
-
类和接口的关系
-
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
-
-
接口与接口的关系
-
继承关系,可以单继承,也可以多继承
-
练习:编写带有接口和抽象类的标准Javabean类
我们现在有乒乓球运动员和篮球运动员,乒乓球教练和篮球教练
为了出国交流,跟乒乓球相关的人员都需要学习英语。
请用所有知识分析,在这个案例中,哪些是具体类,哪些是抽象类,哪些是接口?
乒乓球运动员:姓名,年龄,学打乒乓球,说英语
篮球运动员:姓名,年龄,学打篮球
乒乓球教练:姓名,年龄,教打乒乓球,说英语
篮球教练:姓名,年龄,教打篮球
人类
//人类
public abstract class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "Person{name = " + name + ", age = " + age + "}";
}
}
运动员类
//运动员类
public abstract class Sporter extends Person{
public Sporter() {
}
public Sporter(String name, int age) {
super(name, age);
}
public abstract void study();
}
教练类
//教练类
public abstract class Coach extends Person{
public Coach() {
}
public Coach(String name, int age) {
super(name, age);
}
public abstract void teach();
}
说英语的接口
//说英语接口
public interface English {
public abstract void speakEnglish();
}
乒乓球运动员
//乒乓球运动员
public class PingPangSporter extends Sporter implements English{
public PingPangSporter() {
}
public PingPangSporter(String name, int age) {
super(name, age);
}
@Override
public void speakEnglish() {
System.out.println("乒乓球运动员在说英语");
}
@Override
public void study() {
System.out.println("乒乓球运动员在学习如何打乒乓球");
}
}
篮球运动员
//篮球运动员
public class BasketballSporter extends Sporter{
public BasketballSporter() {
}
public BasketballSporter(String name, int age) {
super(name, age);
}
@Override
public void study() {
System.out.println("篮球运动员在学习如何打篮球");
}
}
乒乓球教练
//乒乓球教练
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("乒乓球在学习说英语");
}
}
//篮球教练
//篮球教练
public class BasketballCoach extends Coach{
public BasketballCoach() {
}
public BasketballCoach(String name, int age) {
super(name, age);
}
@Override
public void teach() {
System.out.println("篮球教练正在教如何打篮球");
}
}
测试类
//测试类
public class Test {
public static void main(String[] args) {
//创建运动员或教练的对象
PingPangSporter pingPangSporter = new PingPangSporter("刘诗雯",23);
System.out.println(pingPangSporter.getName() +","+pingPangSporter.getAge());
pingPangSporter.study();
pingPangSporter.speakEnglish();
}
}
JDK8
开始接口中新增的方法
-
JDK7
以前:接口中只能定义抽象方法。 -
JDK8
的新特性:接口中可以定义有方法体的方法。(默认 、静态) -
JDK9
的新特性:接口中可以定义私有方法
JDK8
以后接口中新增的方法
-
允许在接口中定义默认方法,需要使用关键字
default
修饰-
作用:解决接口升级的问题
-
接口中默认方法的定义格式:
-
格式:
public default 返回值类型 方法名(参数列表){}
-
范例:
public default void show(){}
接口中默认方法的注意事项:
-
默认方法不是抽象方法,所以不强制被重写。但是如果被重写,重写的时候去掉
default
关键字 -
public
可以省略,default
不能省略 -
如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写
-
允许在接口中定义定义静态方法,需要用static修饰
接口中静态方法的定义格式:
-
格式:
public static 返回值类型 方法名(参数列表){}
-
范例:
public static void show(){}
接口中静态方法的注意事项:
-
静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
-
public
可以省略,static
不能省略
JDK9
新增的方法
接口中私有方法的定义格式:
-
格式1:
private 返回值类型 方法名(参数列表){}
-
范例1:
private void show(){}
-
格式2:
private static 返回值类型 方法名(参数列表){}
-
范例2:
private static void method(){}
public interface Inter{
public default void show1(){
System.out.println("show1方法开始执行了");
}
public static void show2(){
System.out.println("show2方法开始执行了");
}
//普通的私有方法,给默认方法服务的
private void show3(){
System.out.println("记录程序在运行过程中的各种细节,这里有100行代码");
}
//静态的私有方法,给静态方法服务的
private static void show3(){
System.out.println("记录程序在运行过程中的各种细节,这里有100行代码");
}
}
接口的应用
-
接口代表规则,是行为的抽象。想要让那个类拥有一个行为,就让这个类实现对应的接口就可以了。
-
当一个方法的参数是接口时,可以传递接口所有实现类的对象,这种方式称之为接口多态。
适配器设计模式
-
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。
简单理解:设计模式就是各种套路
-
适配器设计模式:解决接口与接口实现类之间的矛盾问题
-
当一个接口中抽象方法过多,但是我只要使用其中一部分的时候,就可以适配器设计模式
-
书写步骤:
-
编写中间类
XXXAdapter
,实现对应的接口 -
对接口中的抽象方法进行空实现
-
让真正的实现类继承中间类,并重写需要用的方法
-
为了避免其他类创建适配器类的对象,中间的适配器类用
abstract
进行修饰
-
三、内部类
类的五大成员:属性、方法、构造方法、代码块、内部类
1、什么是内部类
在一个类的里面,再定义一个类。
举例:在A类的内部定义B类,B类就被称为内部类
public class Outer{ //外部类
public class Inner{ //内部类
}
}
2、为什么要学习内部类
需求:写一个
Javabean
类描述汽车。属性:汽车的品牌,车龄,颜色,发动机的品牌,使用年限。
public class Car{ //外部类
String carName;
int carAge;
int carColor;
//内部类表示的事物是外部类的一部分
//内部类单独出现没有任何意义
class Engine{ //内部类
String engineName;
int engineAge;
}
}
内部类的访问特点
-
内部类可以直接访问外部类的成员,包括私有
-
外部类要访问内部类的成员,必须创建对象
1、成员内部类(没多大用)
-
写在成员位置的,属于外部类的成员。
-
成员内部类可以被一些修饰符所修饰,比如:private,默认,protected,public,static等。
-
在成员内部类里面,
JDK16
之前不能定义静态变量,JDK16
开始才可以定义静态变量。
public class Car{ //外部类
String carName;
int carAge;
int carColor;
private class Engine{ //成员内部类
String engineName;
int engineAge;
}
}
获取内部类对象
方式一:
在外部类中编写方法,对外提供内部类的对象(private
)
方式二:直接创建
格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
范例:Outter.Inner oi = new Outer().new Inner();
public class Outer{
String name;
class Inner{
}
}
//当内部类没有被私有化时
public class Test{
public static void main(String[] args){
Outer.Inner oi = new Outer().new Inner();
}
}
public class Outer{
String name;
private class Inner{
}
public Inner getInstance(){
return new Inner();
}
}
//当内部类被私有化时,可以这样做
public class Test{
public static void main(String[] args){
Outer o = new.Outer();
//这样可以
Object Inner = o.getInstance();
System.out.println(Inner);
//这样也可以
System.out.println(o.getInstance());
}
}
外部类成员和内部类成员变量重名时,在内部类如何访问
public class Outer{
private int a = 10;
class Inner{
private int a = 20;
public void show(){
int a = 30;
System.out.println(a); //30
System.out.println(this.a); //20
System.out.println(Outer.this.a); //10
}
}
}
2、静态内部类(没多大用)
静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问非静态的需要创建对象
public class Car{ //外部类
String carName;
int carAge;
int carColor;
static private class Engine{ //静态内部类
String engineName;
int engineAge;
}
}
创建静态内部类对象的格式:外部类名.内部类名 对象名 = new 外部类名.内部类名();
调用非静态方法的格式:先创建对象,用对象调用
调用静态方法的格式:外部类名.内部类名.方法名();
3、局部内部类(没多大用)
-
将内部类定义在方法里面就叫做布局内部类,类似于方法里面的局部变量。
-
外界是无法直接使用,需要在方法内部创建对象并使用
-
该类可以直接访问外部类的成员,也可以访问方法内的局部变量。
public class Outer{
public void show(){
int a = 10;
//局部内部类
class Inner{
String name;
int age;
public void method1(){
System.out.println("局部内部类中的method1方法");
}
public static void method2(){
System.out.println("局部内部类中的method2静态方法");
}
}
//创建局部内部类的对象
Inner i = new Inner();
System.out.println(i.name);
System.out.println(i.age);
i.method2();
Inner.method2();
}
}
4、匿名内部类(重要)
匿名内部类本质上就是隐藏了名字的内部类。
1、什么是匿名内部类?
隐藏了名字的内部类,可以写在成员位置,也可以写在局部位置。
2、匿名内部类
格式:
new 类名或者接口名(){
重写方法;
};
(格式细节)
包含了继承或实现,方法重写,创建对象。
整体就是一个类的子类对象或者接口的实现类对象
举例:
new Inter(){
public void show(){
}
};
//接口
public interface Swin{
public abstract void swim();
}
public class Animal(){
public abstract void eat();
}
public class Test{
public static void main(String[] args){
//编写匿名内部类的代码
new Swim(){
@Override
public void swim(){
System.out.println("重写了游泳方法");
}
};
new Animal(){
@Override
public void eat(){
System.out.println("重写了吃饭方法");
}
};
method(
new Animal(){
@Override
public void eat(){
System.out.println("狗吃骨头");
}
}
);
}
public static void method(Animal a){
a.eat();
}
}
3、使用场景
当方法的参数是接口或者类时,以接口为例,可以传递这个接口的现实类,如果现实类只要使用一次,就可以用匿名内部类简化代码。
题外扩展
public interface Swin{
public abstract void swim();
}
public class Test {
public static void main(String[] args) {
/*
* 回顾一下匿名内部类的格式
* new 类/接口(){
* 重写的方法;
* }
*
* */
//整体我们可以理解为Swim接口的实现类对象
//接口多态
Swim s = new Swim(){
@Override
public void swim() {
System.out.println("重写之后游泳的方法");
}
};
//编译看左边,运行看右边
s.swim();
new Swim(){
@Override
public void swim() {
System.out.println("重写之后游泳的方法");
}
}.swim();
}
}