文章目录
抽象类
概述&特点
描述 狗。吼叫。
描述 狼。吼叫。
两个事物具备共性,向上抽取。犬科,具备吼叫功能。
描述一个事物,却没有足够信息。这时就将这个事物称为抽象事物。
面向抽象的事物,虽然不具体,但是可以简单化。
不用面对具有的事物。
特点:
1.抽象方法一定定义在抽象类中,都需要用abstract来修饰。
2.抽象类不能实例化,不能用new关键字创建对象。
3.只有子类覆盖了所有的抽象方法后,子类具体化,子类就可以创建对象。
如果没有覆盖所有的抽象方法,那么子类还是一个抽象类。
抽象类也是不断地向上抽取而来的。抽取了方法的声明而不确定具体的方法内容。
由不同的子类来完成具体的方法内容。
abstract class 犬科//抽象类
//类也要标识为abctract
{
abstract void 吼叫(); //抽象函数
//抽象关键词abstract
}
class 狗 extends 犬科
{
void 吼叫()
{
System.out.println("汪汪");
}
}
class 狼 extends 犬科
{
void 吼叫()
{
System.out.println("嗷嗷");
}
}
class AbstractDemo
{
public static void main(String[] args)
{
狗 x=new 狗();
x.吼叫();
}
}
细节
问题:
1.抽象类中有构造函数吗?
有。抽象类的构造函数虽然不能给抽象类对象实例化。因为抽象类不能创建对象。
但是抽象类有子类,它的构造函数可以子类的对象实例化。
抽象类和一般类的异同点?
相同:都是用来描述事物的,都可以进行属性和行为的描述。
不同:抽象类描述事物的信息不具体,一般类描述事物的信息具体。
代码的不同:
抽象类中可以定义抽象方法,一般类不行。
抽象类不可以实例化,一般类可以。
2.抽象类一定是个父类么啊?
是的。必须需要子类覆盖抽象方法后,才可以实例化。使用这些方法。
abstract class Demo extends XXX
{
}
class sub extends Demo
{
}
3.抽象类中可以不定义抽象方法吗?
可以的。仅仅是让该类不能创建对象。(技巧应用)
4.抽象关键字abstract和哪些关键字不能共存呢?
(非法修饰符子符)
final;子类不可以覆盖
private;子类不可以直接访问。
static;跑到静态区,不需要创建对象,用类名就可以调用方法。抽象方法被调用没有意义。
练习
需求:
公司中程序猿有姓名,工号,薪水,工作内容。
项目经理有姓名,工号,薪水,工作内容。还有奖金。
对给出需求进行数据建模。
分析:问题领域中:
程序猿:
属性:姓名,工号,薪水
行为:工作内容
项目经理:
属性:姓名,工号,薪水,奖金
行为:工作内容。
两者不存在所属关系,但是有共性内容,可以向上抽取。
两者的共性类型是什么?雇员。
雇员:
属性:姓名,工号,薪水
行为:工作内容
abstract class Employee {
private String name;
private String id;
private double pay;
Employee(String name,String id,double pay){
this.name=name;
this.id=id;
this.pay=pay;
}
//工作内容
public abstract void work();
}
//描述程序猿
class Programmer extends Employee
{
Programmer(String name,String id,double pay){
super(name,id,pay);
}
public void work(){
System.out.print("code");
}
}
//描述项目经理
class Manager extends Employee
{
//特殊属性:奖金
private double bonus;
Manager(String name,String id,double pay,double bonus){
super(name,id,pay);
this.bonus=bonus;
}
public void work(){
System.out.print("manager");
}
}
接口
概述
抽象类中多有的方法都是抽象的。
这时,可以把抽象类用另一种形式来表示:接口。
初期可以理解为接口是特殊的抽象类。
abstract class AbsDemo{}
{
abstract void show1();
abstract void show2();
}
接口中的成员和class定义不同之处:
接口中常见的成员有两种:1.全局常量;2.抽象方法。
而且都有固定的修饰符。
共性:成员都是public修饰的。
所以上述代码可以改为:
//定义接口
interface Inter
{
public static final int NUM=4;
public abstract void show1();
public abstract void show2();
}
class InterfaceDemo
{
public static void main(String[] args){
System.out.println("Hello World");
}
}
特点
1.接口不可以实例化
2.需要覆盖了接口中的所有的抽象方法的子类,才可以实例化。否则,该子类还是一个抽象类。
3.接口是用来被实现的。
类与接口之间的关系是 实现关系。implements
实现方式如下:
interface Inter
{
public static final int NUM=4;
public abstract void show1();
public abstract void show2();
}
class Demo implements Inter {
public void show1() {};
public void show2() {};
}
class InterfaceDemo {
public static void main(String[] args) {
Demo d=new Demo();
}
好处&作用
接口的好处或者解决了什么问题?
多继承:好处:可以让子类具备更多的功能。弊端:调用的不确定性。
弊端举例如下:
class Fu1
{
void show1()
{
sop("fu1 show");
}
}
class Fu2
{
void show2()
{
sop("fu2 show");
}
}
class Zi extends Fu1,Fu2
{
}
Zi z=new Zi();
z.show1();
z.show2();
这时候Zi同时继承show1和show2,但如果将上述代码中的show1和show2均改为show,就会产生调用的不确定性。
原因在于方法主体内容不同。
Java中不直接支持多继承,而是对该机制进行改良。
通过接口来解决问题。将多继承转换成了多实现。
interface InterA
{
void show1();
}
interface InterB
{
void show2();
}
class SubInter implements InterA,InterB//多实现
{
public void show1()
{
sop("inter show1");
}
public void show2()
{
sop("inter show2");
}
}
class InterfaceDemo
{
public static void main(String[],args[])
{
subInter in=new subInter();
in.show1();
in.show2();
};
}
如果把上述代码中的show1和show2改为show呢?
interface InterA
{
void show();
}
interface InterB
{
void show();
}
class SubInter implements InterA,InterB//多实现
{
public void show()
{
sop("inter show");
}
}
class InterfaceDemo
{
public static void main(String[],args[])
{
subInter in=new subInter();
in.show();
};
}
直接实现有实体的部分,而不会产生不可调用的不确定性。
避免单继承的局限性
一个类继承一个类的同时,还可以实现多个接口。避免了单继承的局限性。
继承是为了获取体系的基本功能。
想要扩展功能就可以通过实现接口来完成。
class Fu
{
void show()
{
sop("fu show");
}
}
class Zi extends Fu//因为继承Zi具备所属的体系具备了该体系的基本功能
{
public void method()
{
sop("zi show");
}
}
class InterfaceDemo
{
public static void main(String[],args[])
{
Zi z=new Zi();
z.show();
z.method();
};
}
但如果还想扩展一些功能,具备show1功能。
class Fu
{
void show()
{
sop("fu show");
}
}
interface Inter
{
void show1();
}
class Zi extends Fu implements Inter
{
public void method()
{
sop("zi method");
}
public void show1()
{
sop("zi show1");
}
}
class InterfaceDemo
{
public static void main(String[],args[])
{
Zi z=new Zi();
z.show();
z.method();
z.show1();
};
}
接口多继承
类与类之间 继承关系. is a
类与接口之间 实现关系. like a
接口与接口之间关系:是继承关系,而且可以多继承。
interface Inter1
{
void show1();
}
interface InterA
{
void showA();
}
interface Inter2 extends Inter1,InterA//接口与接口之间可以多继承,类不可以多继承是因为主体调动不明确,而接口根本没有主体。
{
void show2();
}
class Demo implements Inter2//覆盖两个才能实例化
{
public void show1(){}
public void showA(){}
public void show2(){}
}
抽象类是否可以不定义抽象方法
interface Inter
{
void show1();
void show2);
void show3();
void show4();
}
//DemoA需要使用接口中的部分方法,比如使用show1.
class DemoA implements Inter
{
public void show1(){
sop("Demo show1");
}
void show2(){}
void show3(){}
void show4(){}
}
//DemoB需要使用接口中的部分方法,比如使用show3.
class DemoB implements Inter
{
public void show3(){
sop("Demo show1");
}
void show2(){}
void show1(){}
void show4(){}
}
class InterfaceDemo
{
public static void main(String[],args[])
{
DemoA a=new DemoA();
a.show1();
};
}
这个代码存在的问题就是:
只需要部分功能,但是为了实例化,必须要全部覆盖。代码的复用性很差。
所以上述代码改成:
interface Inter
{
void show1();
void show2();
void show3();
void show4();
}
为了方便创建Inter接口的子类对象。
可以用一个类先把接口中的所有方法都空实现。该类创建对象没有意义,所以可以将该类抽象。
这就是传说中的没有抽象方法的抽象类。
abstract class Demo implements Inter//Demo是空实现,那么就可以用抽象类。即为没有抽象方法的抽象类。方便创建接口的对象。
{
void show1(){}
void show2(){}
void show3(){}
void show4(){}
}
//DemoA需要使用接口中的部分方法,比如使用show1.
class DemoA extends Demo
{
public void show1(){
sop("Demo show1");
}
}
//DemoB需要使用接口中的部分方法,比如使用show3.
class DemoB extend Demo
{
public void show3(){
sop("Demo show1");
}
}
class InterfaceDemo
{
public static void main(String[],args[])
{
DemoA a=new DemoA();
a.show1();
};
}
接口的思想
笔记本电脑的USB接口
1.接口的出现扩展了功能。
2.接口其实就是暴露出来的规则。
3.接口的出现降低了耦合性。即解耦。
class Mouse
{
}
interface USB
{
}
class newMouse extends Mouse implements USB
{
}
接口的出现,一方在使用接口,一方在实现接口。
interface Inter
{
void show();
}
class Demo implements Inter
{
void show(){}
}
class Demoa implements Inter
{
void show(){}
}
抽象类和接口的区别
例子:
描述犬:里面有吃,叫,都是抽象的,具体由子类完成。
问题:定义成抽象类呢?还是定义成接口?
abstract class 犬
{
abstract void 吃();
abstract void 叫();
}
还是
interface 犬
{
abstract void 吃();
abstract void 叫();
}
添加一个功能,缉毒。单独描述一个缉毒功能的犬。
class 缉毒犬 extends 犬
{
void 吃();
void 叫();
void 缉毒();
}
还是
class 缉毒犬 inplements 犬
{
void 吃(){}
void 叫(){}
void 缉毒(){}
}
缉毒犬是犬中的一种。is a关系。
犬用于描述所有功能犬的基本功能。用class定义。父类。
所以犬不适合定义成接口。
具备缉毒功能的有很多,缉毒功能需要抽取。抽取到类中?还是抽取到接口中?
都试一下,先定义成类。
abstract class 缉毒
{
abstract void 缉毒();
}
不行,缉毒犬继承犬类,就不能继承其他类。因为类不能多继承。
定义成接口试试。
interface 缉毒
{
abstract void 缉毒();
}
class 缉毒犬 extends 犬 implements 缉毒
{
void 吃(){}
void 叫(){}
void 缉毒(){}
}
这是可行的。
类用于描述的是事物的共性基本功能。
接口用于定义的都是事物的额外功能。
抽象类和接口的区别?
1.类与类之间是继承关系,is a.
类与接口之间是实现关系,like a.
2.类中可以定义抽象和非抽象方法,子类可以直接使用,或者覆盖使用。
接口中定义都是抽象方法,必须实现才能用。
多态
概述
多态:多种形态。
重点说的是:对象的多态性。
class 动物
{
}
class 狗 extends 动物
{
}
//狗 x=new 狗();
动物 x=new 狗();//狗实例既是狗类型,又是动物类型。多态性。
多态在程序中的体现,父类的引用或者接口的引用指向了子类的对象。
多态的好处:提高了代码的扩展性。
多态的弊端:不能使用子类的特有方法。
多态的前提:
1.必须有关系,继承,实现。
2.通常有覆盖。
abstract class Animal
{
abstract void eat();
}
class Dog extends Animal {
void eat() {
System.out.println("骨头");
}
void lookHome() {
System.out.println("看家");
}
}
class Cat extends Animal
{
void eat()
{
System.out.println("鱼") ;
}
void catchMouse(){
System.out.println("抓老鼠");
}
}
class Pig extends Animal
{
void eat()
{
System.out.println("饲料") ;
}
void gongDi(){
System.out.println("拱地");
}
}
class DuoTaiDemo {
public static void main(String[] args) {
//Dog d=new Dog();
//d.eat();
//多态形式
Animal a=new Dog();
a.eat();
Animal a1=new Cat();//向上转型
a1.eat();
Dog d1=new Dog();
Dog d2=new Dog();
Dog d3=new Dog();
method(d1);
method(d2);
method(d3);
Cat c1=new Cat();
Cat c2=new Cat();
Cat c3=new Cat();
method(c1);
method(c2);
method(c3);
method (new pig());
}
//定义了狗和猫的调用方式。但是如果再出现一个子类比如猪,那么还要定义一个调用猪的方法。
//不利于扩展
//当面对共性类型时,所有的子类对象都可以接受。
//说明提高了代码的扩展性
public static void method( Animal a) //a=new Dog();a=new Cat();a=new Pig();
{
a.eat();
}
//既然调用的都是eat方法,而eat是动物共性行为,为什么不面对动物呢?
//直接定义共性类型的参数更合适。
/*
public static void method( d) {
d.eat();
}
public static void method(Cat c) {
c.eat();
}
}
*/
多态-向上向下转型
Animal a=new Cat();//向上转型
向上转型好处:隐藏了子类型,提高了代码的扩展性。
弊端:只能使用父类中的功能,不能使用子类特有功能,功能被限定住了。
如果不需要面对子类型,通过提高扩展性,或者使用父类的功能即可完成操作,就使用向上转型。
class DuoTaiDemo {
public static void main(String[] args) {
Animal a=new Dog();//向上转型
a.eat();
//如果想要使用子类的特有功能呢?比如看家。这时是不是就需要子类型。
if(!(a instanceof Dog))
{
System.out.println("类型不匹配");
return;
}
Dog d=(Dog)a;//向下转型
d.eat();
d.lookHome();
向下转型
好处:可以使用子类型的特有功能。
弊端:面对具体的子类型。向下转型有风险。容易发生ClassCastException.只要转换类型和对象类型不匹配就会发生。
想要安全,必须要进行判断。判断一个对象是否匹配某一类型,需要使用一个关键字 instanceof
如何使用:对象 instanceof 类型
什么时候用向下转型:需要子类型的特有方法时。但一定要判断。
练习
毕姥爷
讲课。
钓鱼。
毕老师 extends 毕姥爷
讲课。
看电影。
要求体现多态。
要看到向上转型,向下转型
//描述毕姥爷
class 毕姥爷
{
public void 讲课(){
System.out.println("管理");
}
public void 钓鱼(){
System.out.println("钓鱼");
}
}
//描述毕老师
class 毕老师 extends 毕姥爷
{
public void 讲课(){
System.out.println("技术");
}
public void 看电影(){
System.out.println("看电影");
}
}
Class DuoTaiTest
{
public static void main(String[],args){
//多态形式。
毕姥爷 x=new 毕老师();//向上转型
x.讲课();
x.钓鱼();
//要想使用毕姥爷的特有方法 看电影
毕老师 y=(毕老师)x;
y.看电影();//向下转型
y.钓鱼();
}
}
转型过程中,至始至终,之有子类对象在做着类型的变化。
多态-USB接口练习
阶段一:
笔记本电脑运行。笔记本中有一个运行功能。
阶段二:
想使用一个鼠标。又有一个功能使用鼠标。并多了一个鼠标对象。
阶段三:
还想使用一个键盘功能。又要多一个功能。
问题:每多一个功能就需要在笔记本对象中定义一个函数。不爽!扩展性差!
怎么解决?
降低鼠标,键盘等外围设备和笔记本电脑的耦合性。
阶段一
//描述笔记本电脑
class NoteBook
{
void run() {
System.out.println("book run");
}
}
class USBTest {
public static void main(String[] args) {
NoteBook book=new NoteBook();
book.run();
}
}
运行结果
book run
阶段二
需要一个鼠标。说明笔记本中多了一个使用鼠标的功能。
多了一个对象:鼠标。
class Mouse {
/**
* 开启功能。
*/
public void open() {
System.out.println("Mouse open");
}
/**
* 开启功能。
*/
public void close() {
System.out.println("Mouse close");
}
}
class NoteBook
{
public void run() {
System.out.println("book run");
}
/**
* 定义使用鼠标功能。鼠标不确定,定义成参数。
*/
public void useMouse(Mouse m) {
//判断一下,m必须指向鼠标对象才可以调用方法
if(m!=null) {
m.open();
m.close();
}
}
}
class USBTest {
public static void main(String[] args) {
NoteBook book=new NoteBook();
book.run();
book.useMouse(new Mouse());
}
}
结果
book run
Mouse open
Mouse close
阶段三:如果还想使用其他设备呢?比如键盘,外置硬盘。
可以通过再笔记本对象中继续定义useKey()等方法来完成。
但是出现问题:每增加一个设备都需要不断地改动笔记本类中的内容。扩展性太差,维护性也不好。
怎么办?
后期的设备是不确定的,每多加一个设备就加一个功能,就说明设备和笔记本的耦合性太强。
我不要每次都面对具体的类型,只要定义一个规则,让后期的设备都符合这个规则。这样只要面对规则就可以了。
java中可以通过接口的形式来完成规则的定义,进行解耦。
重新设计:
interface USB {
/**
* 开启功能。
*/
public void open();
/**
* 开启功能。
*/
public void close();
}
class NoteBook
{
/**
* 运行
*/
public void run() {
System.out.println("book run");
}
/**
* 使用符合规则的外围设备。
*/
public void useUSB(USB usb) {//定义了一个接口类型的引用。//USB usb=new MouseByUSB();多态,提高了笔记本的扩展性。
//判断一下,m必须指向鼠标对象才可以调用方法
if(usb!=null) {
usb.open();
usb.close();
}
}
}
//添加鼠标
class MouseByUSB implements USB
{
public void open()
{
System.out.println("mouse open") ;
}
public void close(){
System.out.println("mouse close");
}
}
//添加键盘
classKeyByUSB implements USB
{
public void open()
{
System.out.println("Key open") ;
}
public void close(){
System.out.println("Key close");
}
}
class ExtendsDemo3 {
public static void main(String[] args) {
NoteBook book=new NoteBook();
book.run();
book.useUSB(null);
book.useUSB(new MouseByUSB());
book.useUSB(new KeyByUSB());
}
}
结果:
book run
mouse open
mouse close
Key open
Key close
多态中对成员的调用
1.成员变量
当子父类中出现同名成员变量时,多态调用时,只看调用该成员变量的引用所属的类中的成员变量。
简单地说:无论编译或者运行,都看等号的左边。
class Fu {
int num=4;
}
class ExtendsDemo3 {
public static void main(String[] args) {
Fu f=new Fu();
f.num=8;
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/43692d94524957cda4fa769533cff3aa.png)
class Fu {
int num=4;
}
class Zi extends Fu
{
int num2=6;
}
class ExtendsDemo3 {
public static void main(String[] args) {
Zi z=new Zi();
z.num=9;
z.num2=10;
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/6c852acfc53a3311a2b2dccd74225615.png)
class Fu {
int num=4;
}
class Zi extends Fu
{
int num=6;
}
class ExtendsDemo3 {
public static void main(String[] args) {
Zi z=new Zi();
System.out.println(z.num)
}
}
结果
6
把上述代码中最后一个class改为如下:
class ExtendsDemo3 {
public static void main(String[] args) {
Fu f=new Zi();
System.out.println(f.num);
}
}
结果:
4
![](https://i-blog.csdnimg.cn/blog_migrate/1c49015128f412c941cb1f4d5734d7f2.png)
2.成员函数
出现一模一样函数时,
多态调用。
编译时,看的是引用变量所属的类中的方法。
运行时,看的是对象所属的类的方法。
简单说:编译看左边,运行右边。
成员方法动态绑定到当前对象上。
class Fu {
void show(){
System.out.println("fu show");
}
}
class Zi extends Fu
{
void show(){
System.out.println("zi show");
}
}
class ExtendsDemo3 {
public static void main(String[] args) {
Zi z=new zi();
z.show();
}
}
结果
zi show
把上述代码中最后一个class改为如下:
class ExtendsDemo3 {
public static void main(String[] args) {
Fu f=new Zi();
f.show();
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/d841d36c8a74a9d6e6c75900763c7d8e.png)
3.静态函数
出现一模一样函数时,
多态调用。
编译和运行时看引用变量所属的类中的方法。
简单说:编译运行看左边。
其实大家要知道,真正调用静态方法时不需要对象的。直接类名调用。因为静态方法绑定到类上。
所以上述情况,更多用于面试。
静态跟对象没有关系,只跟类有关。
class Fu {
static void staticMethod(){
System.out.println("fu static method");
}
}
class Zi extends Fu
{
static void staticMethod(){
System.out.println("zi static method");
}
}
class ExtendsDemo3 {
public static void main(String[] args) {
Fu f=new zi();
f.staticMethod();
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/1a9e578762ea1b6b61503c2f74edfed7.png)
练习
class Fu {
int num=5;
void show(){
System.out.println("num="+this.num);
}
}
class Zi extends Fu
{
int num=6;
}
class ExtendsDemo3 {
public static void main(String[] args) {
Fu f=new zi();
f.show();
}
}
结果
num=5
![](https://i-blog.csdnimg.cn/blog_migrate/1bafc535cb298d0a155ea8634b532849.png)