目录
抽象
某个父类知道其所有子类要完成某功能,但是每个子类完成情况都不一样,父类就只定义该功能的基本要求,具体实现由子类完成,这个类就可以是一个抽象类,抽象类其实就是一种不完全的设计图。
抽象类必须使用abstract修饰:
修饰符 abstract class 类名{ }
Animal
父类,相当于子类的模板
package ab;
public abstract class Animal {
public abstract void run();
//{
// System.out.println("动物可以跑");
//}
}
Tiger
package ab;
public class Tiger extends Animal{
@Override
public void run() {
System.out.println("老虎可以跑");
}
}
Dog
package ab;
public class Dog extends Animal{
@Override
public void run() {
System.out.println("狗跑的很快");
}
}
测试输出:
package ab;
public class Test {
public static void main(String[] args) {
Animal animal=new Tiger();
animal.run();
Animal animal1=new Dog();
animal1.run();
}
}
抽象类的使用总结和注意事项
1.抽象类用来被继承的,充当模板的,同时也可以提高代码的复用。抽象方法是交给子类重写实现的。
2.一个类如果继承了抽象类,那么这个类必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
3.抽象方法必须在抽象类里
4.抽象类不能被实例化:不能创建对象[ A a=new A();]
5.抽象类构造器存在的意义:为了约定子类的构造器必须和父类要匹配
6.abstract能修饰什么:类、方法。不能修饰:变量、代码块、构造器(构造器没有抽象的概念)
7.抽象方法必须是public或者protected(因为如果是private,不能被子类继承,子类便无法实现该方法),默认情况下是可以的。(默认情况其实就是public)
8.抽象类不能被直接实例化,需要依靠子类采用向上转型的方式处理
9.抽象类必须有子类,使用extends继承,一个类只能继承一个抽象类。
10.子类(如果不是抽象类)则必须重写抽象类中的所有抽象方法 (如果子类没有实现父类的抽象方法,必须将子类也定义为abstract)
11.抽象方法,没有大括号,没有方法体。
12.约定大于配置,配置大于编码
13.抽象类不一定有抽象方法,有抽象方法的类一定是抽象类
思考
Q1:当我们定义一个抽象类,有那些结构?
A1:属性、普通的成员方法、构造器、抽象方法、常量
Q2:抽象类中能不能没有抽象方法?
A2:可以!(如果没有抽象方法,尽量不要写抽象类,非要写也可以)
Q3:抽象方法能否用private修饰?
A3:不能!默认可以,开发中抽象方法基本上是public
Q4:抽象方法能不能用final修饰?
A4:不能、用final修饰的方法时不允许重写
Q5:抽象类能不能用final修饰?
A5:不能、用final修饰的类不允许重写
Q6:抽象方法,存在的意义是什么?
A6:根本意义:约定。自定义的规则。
Q7:抽象方法到底约定了什么,规定了什么,定了哪些规则?
A7:约定了返回值,访问权限,参数列表,需要在重写的时候去定义方法体
抽象类的作用是什么样的?什么时候定义抽象类?
1.可以被子类继承、充当模板的、同时也可以提高代码的复用。
2.如果父类知道子类要完成某个功能,实现要交给子类。
抽象方法是什么样的?
只有方法签名,没有方法体,使用了abstract修饰。
抽象类案例
(本案例参考)https://www.zhihu.com/education/video-course/1483149993460109313?section_id=1483150079171747840
Card(父类)
package ab.anli;
/**
* 抽象类的父类
*/
public abstract class Card {
private String name;//主人的名称
private double money;
/**
* 支付功能:子类一定要支付,但是每个子类支付的情况不一样,所以父类把支付定义成抽象方法,交给具体子类实现
*/
public abstract void pay(double money);
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
}
金卡(子类)
package ab.anli;
public class GoldCard extends Card {
/*金卡*/
@Override
public void pay(double money) {
//把优惠后的金额算出来
double rs = money * 0.8;
double lastMoney = getMoney() - rs;//剩余的钱=一开始注入的钱10000-打完折后的钱
System.out.println(getName() + "当前账户总金额:" +
getMoney() + " 当前消费了:" + rs + " 剩余余额:" + lastMoney);
setMoney(lastMoney);
}//先获得银行卡主人的名字,以及当前注入的10000元(getMoney()),然后,当前消费是8折,剩余钱数是一开始注入的钱10000-打完折后的钱
}
银卡(子类)
package ab.anli;
public class SilverCard extends Card {
@Override
public void pay(double money) {
double rs1 = money * 0.85;
double lastMoney1 = getMoney() - rs1;
System.out.println(getName() + "当前账户余额为:" + getMoney() + "当前消费了:" + rs1 +
"余额为:" + lastMoney1);
setMoney(lastMoney1);
}
}
测试
package ab.anli;
public class Test {
public static void main(String[] args) {
GoldCard goldCard=new GoldCard();
goldCard.setMoney(10000);//父类
goldCard.setName("菠萝");
goldCard.pay(6000);
System.out.println("---------------");
SilverCard silverCard=new SilverCard();
silverCard.setName("吹雪");
silverCard.setMoney(5000);
silverCard.pay(300);
}
}
输出
特点与注意事项
有得有失:得到了抽象方法,失去了创建对象的能力。
public abstract void run();
Why?
抽象类不能创建对象不是因为没有构造器,其实是有构造器的,子类需要调用父类构造器,如果没有子类就会报错。那为什么有构造器也不能创建对象呢?
反证法:假如能创建,animal.run();用animal对象调用run方法【参考本文第一个案例】,但是run方法不能跑,因为根本连方法体都没有,它是让子类去重写的,因此假如抽象类能创建对象,万一拿对象去调用从抽象方法,抽象方法跑不了。它不是一个完整的方法,因此抽象类不能创建对象。另外,抽象,本身就是不可以实例化的。
final和abstract的关系?
互斥关系
1.final修饰类,类不能被继承,abstract修饰类,类是抽象类(迫不及待地希望被继承,用它的模板)
abstract定义的抽象类作为模板让子类继承,final定义的类不能被继承。
2.抽象方法定义通用功能让子类重写,final定义的方法子类不能重写。
接口
接口就是体现规范的,其中用抽象方法定义的一组行为规范,接口是更加彻底的抽象。
体现了现实世界中“如果你是这类事物...则必须完成某些行为...”的思想。
接口的关键字:用interface来定义
接口不能创建对象。
定义与特点
格式
public interface 接口名{
//常量
//抽象方法
}
JDK1.7之前:接口中只能定义抽象方法,不能有属性,不能有方法。(静态的常量)
JDK8之前:接口中只能是抽象方法和常量。
JDK8及以后:接口中只能定义抽象方法,不能有属性,可以有默认方法和静态方法,静态常量。
JDK7---JDK8:默认方法,静态方法。
接口不能实例化。
接口中的成员都是public修饰的,写不写都是,因为规范的目的是为了公开化。
接口中的抽象方法默认是public abstract,推荐省略,接口默认会为你加上:void run();
接口中的常量默认是public static final,推荐省略。接口默认会为你加上。
接口的基本使用
接口是用来被类实现(implements)的,实现接口的类称为实现类。实现类可以理解成所谓的子类。(人话:接口就是干爹
修饰符 class 实现类 implements 接口1,接口2, 接口3,....{ }
实现的关键字:implements
接口可以被单实现,也可以被多实现。
创建SportMan、Law接口
package interface_26;
/**
* 接口*/
public interface SportMan {
void run();
void eat();
}
package interface_26;
public interface Law {
void rule();
}
创建PingPangMen、Test类
package interface_26;
/*实现类*/
public class PingPangMen implements SportMan, Law {//要实现两个接口,implements后面放的就是干爹类
private String name;
public PingPangMen(String name) {//通过有参构造器进行初始化
this.name = name;
}
@Override
public void rule() {
System.out.println(name+"这个运动员遵守规则");
}
@Override
public void run() {
System.out.println(name+"必须训练");
}
@Override
public void eat() {
System.out.println(name+"按时吃饭");
}
}
package interface_26;
public class Test {
public static void main(String[] args) {
PingPangMen pingPangMen=new PingPangMen("张继科");
pingPangMen.run();
pingPangMen.eat();
pingPangMen.rule();
}
}
输出结果
接口实现的注意事项
一个类实现接口,必须重写完全部接口的全部抽象方法,否则这个类需要定义成抽象类。
接口与接口的关系:多继承
类和类的关系:单继承。
类和接口的关系:多实现。
接口和接口的关系:多继承,一个接口可以同时继承多个接口。
创建SportMan、Person接口
package interface_extends;
public interface SportMen {
void run();
void drink();
}
package interface_extends;
public interface Person {
void eat();
}
创建Basketball类,让它实现三个接口多继承
package interface_extends;
import interface_26.Law;
/*实现类*/
public class Basketball implements SportMen,Law,Person {
}
//现在是报错的!!要重写方法
重写之后
package interface_extends;
import interface_26.Law;
/*实现类*/
public class Basketball implements SportMen,Law,Person {
@Override
public void rule() {
}
@Override
public void eat() {
}
@Override
public void run() {
}
@Override
public void drink() {
}
}
由于后面实现的接口太多啦!能不能把这些接口规范合并呢?
SportMen接口
让SportMen同时继承Law,Person这两个接口,SportMen把这两个接口合并起来了,Basketball就不用再实现三个接口了,直接实现SportMen就可以了。
package interface_extends;
import interface_26.Law;
public interface SportMen extends Law,Person {
void run();
void drink();
}
package interface_extends;
/*实现类*/
public class Basketball implements SportMen{
@Override
public void rule() {
}
@Override
public void eat() {
}
@Override
public void run() {
}
@Override
public void drink() {
}
}
接口多继承的作用
规范合并,整合多个接口为同一个接口,便于子类实现。
栈&队列
栈和队列是两种操作受限的线性表。
栈:只能在一端进行插入和删除操作的特殊线性表
允许进行插入和删除操作的一端称为栈顶,另一端称为栈底。
栈的特点
先进后出(FILO)
队列
只允许在表位插入元素,在表头删除元素,FIFO First In First Out
栈与队列的相同点
1.都是线性结构
2.插入操作都是在表尾进行
3.都可以通过顺序结构和链式结构实现。
栈与队列的不同点
队列:先进先出,栈:先进后出
案例1
创建队列Queue类
定义成员变量:private SuperLinked superLinked = new SuperLinked();
入队(新增)
public void add(Integer item){
superLinked.add(item);
}
如果调用add方法,只传数据,不传位置的话,默认在单链表的表尾。
出队(删除)
public Integer poll(){
// 1.判断这个队列是不是空
if(empty()){
return null;//如果是空,直接返回空
}
// 2.找到队列的头
Integer integer = superLinked.get(0);
// 3.把队伍的头删掉
superLinked.removeFirst();
// 4.返回删除的值
return integer;
}
判断这个队列是不是空:其实就是判断链表是不是空。只需要判断它的size是不是0,如果是0,就是空的,return true;其他情况就是return false;
if(superLink.size()==0){
return true;
}
return false;
}
代码优化:
return superLink.size()==0;
返回队首,不出队
public Integer peek() {//一般获取队列首部的元素都叫peek
if(empty()) {//判断是否为空
return null;
}
return superLinked.get(0);
}
测试:
返回队首
public static void main(String[] args) {
Queue queue = new Queue();
queue.add(1);
queue.add(120);
queue.add(50);
queue.add(999);
System.out.println(queue.peek());
}
出队
public static void main(String[] args) {
Queue queue = new Queue();
queue.add(1);
queue.add(120);
queue.add(50);
queue.add(999);
queue.poll();
System.out.println(queue.peek());
}
案例2
创建栈Stack
定义成员变量:private SuperLinked superLinked = new SuperLinked();
入栈/压栈
public void push(Integer item){
superLinked.add(item);
}
判断是否为空
private boolean empty() {
return superLinked.size() == 0;
}
返回栈顶元素,不出栈
public Integer peek(){
if(empty()) {
return null;
}
return superLinked.get(superLinked.size() - 1);//栈顶元素是底部最后一个
}
删除/出栈,从栈尾出去
public Integer pop(){
if(empty()) {
return null;
}
//找到尾部
Integer integer = superLinked.get(superLinked.get(superLinked.size() - 1));
superLinked.remove(superLinked.size() - 1);
return integer;
}
测试 添加
public static void main(String[] args) {
// 测试
Stack stack = new Stack();
stack.push(1);
stack.push(2);
stack.push(3);
stack.push(4);
System.out.println(stack.peek());
}
删除
public static void main(String[] args) {
// 测试
Stack stack = new Stack();
stack.push(1);
stack.push(2);
stack.push(3);
stack.push(4);
stack.pop();
System.out.println(stack.peek());
}
总结
今天学了抽象、接口、栈和队列。其中抽象和接口理解的相对来说比较好,栈和队列只能说能听白老师讲的意思,但是自己写不出来。还是要配合前面讲的链表多加练习。