黑马程序员_Java基础_抽象类,模板设计模式,接口,内部类,多态

时间:2022-06-20 00:47:55
一,抽象类

        1,抽象类的特点:

                (1)抽象方法一定在抽象类中。

                (2)抽象方法和抽象类必须被abstract关键字修饰。

                (3)抽象类不可以用new创建对象。因为抽象方法没有意义。

                (4)抽象类中的抽象方法要被使用,必须由子类复写起所有的抽象方法后,建立子类对象调用。
                     如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类。

        2,抽象类和一般的类没有太大的不同,只不过是抽象类中多了一些不明确的东西,也就是抽象方法。所以导致抽象类不能实例化。

        3,抽象类中也可以不定义抽象方法,这样做仅仅是为了不让该类建立对象。

        4,抽象类和方法是使用abstract来表示的,abstract不能和final,private,static关键字共存。

            原因是:被final修饰的类不能有子类,而被acstract修饰的类一定要有子类,它一定是一个父类。抽象类中的抽象方法如果是私 有的,那么子类就不知道这些方法,就无法复写这些方法,而抽象方法是一定要被子类复写的。如果static修饰抽象方法,那么就可以不建立对象 ,直接通过抽象类的类名访问抽象方法,这样访问的方法是没有任何意义的。

        5,注意:抽象类中是有构造函数的,因为抽象类是一个父类,要给子类提供实例的初始化。

        示例:假设定义一个员工类,包括姓名,年龄,工资。经理也是员工,但是和普通员工的工作方法不一样,但是经理除了拥有工资还拥有奖金,bounds,这时候就可以将员工类定义成抽象的,经理和普通员工都继承这个抽象的员工类。 

//定义一个抽象员工类
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 Manager extends Employee {
// 经理除了拥有工资外还拥有额外的奖金
private int bonus;

Manager(String name, String id, double pay, int bonus) {
super(name, id, pay);
this.bonus = bonus;
}

// 重写经理的工作方式
public void work() {
System.out.println("manager work");
}
}

// 普通员工也继承自员工类
class Pro extends Employee {
Pro(String name, String id, double pay) {
super(name, id, pay);
}

// 普通员工重写普通员工的工作方法
public void work() {
System.out.println("pro work");
}
}

  6,抽象的一个小应用:模板方法设计模式

            模板方法设计模式是指:当在定义功能的时候,功能的一部分是确定的,一部分是不确定的,而确定的部分在调用不确定的部分,这时候我们可以将不确定的部分暴露出去,让他的子类来完成。

 示例:定义一个获取程序运行时间的模板,通过获取开时的时间和结束的时间,将两个事件相减就得到程序的运行时间。

abstract class GetTime {
// 定义获取时间的方法
public final void getTime() {
long start = System.currentTimeMillis();
runcode();
long end = System.currentTimeMillis();
System.out.println("程序运行了" + (end - start) + "毫秒");
}

public abstract void runcode();
}

class SubTime extends GetTime {
// 计算这段代码的运行时间
public void runcode() {

for (int x = 0; x < 4000; x++) {
System.out.print(x);
}
}
}

class Demo {
public static void main(String[] args) {
SubTime st = new SubTime();
st.getTime();
}
}abstract class GetTime {
// 定义获取时间的方法
public final void getTime() {
long start = System.currentTimeMillis();
runcode();
long end = System.currentTimeMillis();
System.out.println("程序运行了" + (end - start) + "毫秒");
}

public abstract void runcode();
}

class SubTime extends GetTime {
// 计算这段代码的运行时间
public void runcode() {

for (int x = 0; x < 4000; x++) {
System.out.print(x);
}
}
}

class Demo {
public static void main(String[] args) {
SubTime st = new SubTime();
st.getTime();
}
}

二,接口         1,如果一个抽象类中的所有方法都是抽象的,那么我们就可以使用接口的形式来表现这个类。         class用来定义类,而interface则用来定义接口。其实接口可以理解成是java中实现"多继承"的一体现形式。         2,接口中常见的定义有常量和抽象方法,接口中的常量的和抽象方法是由默认的修饰符的。             常量:public static final             方法:public abstract         3,接口也是不可以直接创建实例对象的,它的子类可以创建实例对象,前提是子类是一个非抽象类,只有子类实现了接口中的全部抽象方法才能是一个非抽象类,这样才能创建实例对象。如果子类中只实现了父类中的一部分方法,那么子类就是一个抽象类,也不可以创建对象。         4,接口可以被类多实现,也是对多继承不支持的转换形式。java支持多实现。         5,接口的特点:             (1)接口是对外暴露的规则             (2)接口是程序功能的扩展             (3)接口可以用来多实现             (4)类与接口之间是实现关系,类可以在继承类的同时实现多个接口             (5)接口之间是可以存在继承关系的         6,接口与抽象类的比较:         (1)相同点:都是不断向上抽取的         (2)不同点:a,抽象类需要被继承,而且只支持单继承。接口需要被实现,支持多实现。b,抽象类中可以定义抽象方法和非抽象方法,子类继承后可以直接使用父类的费抽象方法。 而接口中只能定义抽象方法,并且全部由子类去实现。c,抽象类是在定义该体系的共性内容。而接口是在定义体系额外的共能。
           示例:定义一个人的类,拥有年龄,姓名属性。学生和工人也是人,都继承自人这个类,那么学生和工人就拥有了姓名和年龄的属性。每个人都有自己的工作要做,学生工作是学习java,工人的工作是生产产品。学生还有学习外语的能力,并不是所有人都必须会说外语,比如工人就不一定了。

interface SpeakOther {
public abstract void speakEnglish();
}

abstract class Person {
String name;
int age;

Person(String name, int age) {
this.name = name;
this.age = age;
}

public abstract void work();
}

class Student extends Person implements SpeakOther {
Student(String name, int age) {
super(name, age);
}

public void work() {
System.out.println("我是学生,我的工作是学习java");
}

public void speakEnglish() {
System.out.println("我是学生,我还会说英语");
}
}

class Worker extends Person {
Worker(String name, int age) {
super(name, age);
}

public void work() {
System.out.println("我是工人,我的工作是生产商品");
}
}

三,面向对象之多态         1,定义:某一类事物的多种存在形态。         例如动物中的猫,狗都是属于动物类型的,同时它们也对应的有各自的猫类型,狗类型。         用java语言可以描述成:猫 cat = new 猫();  同时也可以描述成:动物 cat = new 猫();这时候猫就具备了动物和猫的特性。此时猫就具备了多种形态。         2,多态的体现:父类引用指向子类对象。父类的引用也可以接收自己的对象。         3,多态的前提:类与类之间必须要有关系,要么继承(extends),要么实现(implements)。还有一个前提就是存在覆盖。         4,多态的好处:大大提高了程序的扩展性。         5,多态的弊端:提高了扩展性,但是只能使用父类的引用访问父类的成员。         6,注意:多态自始至终都是子类对象在做着变化。在使用多态的时候如果要使用子类特有而父类没有的方法,则需要使用向上转型,也就是强制类型转换,将父类引用转换成子类类型。             注意:千万不要出现将父类对象转换成子类对象。
        示例:定义一个动物类,动物包括吃的功能,猫继承该类,狗也继承该类,同时猫具有捉老鼠的功能,狗具有看门的功能。通过多台调用各自的功能。

abstract class Animal {
public abstract void eat();
}

class Cat extends Animal {
public void eat() {
System.out.println("猫吃鱼");
}

public void catchMouse() {
System.out.println("猫捉老鼠");
}
}

class Dog extends Animal {
public void eat() {
System.out.println("狗吃骨头");
}

public void lookHouse() {
System.out.println("狗看房子");
}
}

class AnimalDo {
public void doEat(Animal a) {
a.eat();
/*
* if (a instanceof Cat) { Cat c2 = (Cat)a; c2.catchMouse(); } if (a
* instanceof Dog) { Dog d2 = (Dog)a; d2.lookHouse(); }
*/
}
}

class DuotaiDemon {
public static void main(String[] args) {
AnimalDo ad = new AnimalDo();
ad.doEat(new Cat());
ad.doEat(new Dog());
// Animal a = new Cat(); 这种方式叫做类型的提升,向上转型
// a.eat();
// Cat c = (Cat)a; 当要使用猫的特有方法时,使用强制类型转换
// c.catchMouse();
// function(new Cat());
// function(new Dog());
// 错误操作:Animal a = new Animal()
// Cat c = (Cat)a;
}
/*
* public static void function(Animal a1) { a1.eat(); if (a1 instanceof Cat)
* { Cat c2 = (Cat)a1; c2.catchMouse(); } if (a1 instanceof Dog) { Dog d2 =
* (Dog)a1; d2.lookHouse(); } }
*/
}

 7,多态中成员函数的特点         (1)编译时期,参阅引用类型变量所属类中是否有调用的方法,如果有编译通过,没有编译失败。                 在运行时期:参阅对象所属的类中是否有调用的方法。         (2)多态中成员变量的特点:无论编译和运行都参考变量所属的类。         (3)多态中静态成员函数的特点是:无论编译还是运行都参考引用类型变量所属的类。                 静态只找所属的类,覆盖不应用到静态上,不管是成员变量还是成员函数。     8,所谓的静态绑定就是指:只要类中存在静态成员(成员变量和成员函数),类一加载,这些静态成员就随着类的加载和他所属的类绑定了。           所谓的动态绑定就是指:非静态成员(成员变量和成员函数)是随着对象的创建而与他们所属的对象绑定了。     9,多态中静态的使用要注意一点:实际开发中不会有静态覆静态,但是面试中会经常被问到。
    举一个多态的实际应用:
/* 需求:定义一个主板,用户通过网卡上网,通过声卡听音乐,当然还可以添加其他卡实现其他功能。
* 多态增强程序的扩展性
*/
class MainBoard {
public void run() {
System.out.println("mainboard run...");
}

/*
* public void useNetCard(NetCard nd) { nd.open(); nd.close();
*
* 到此为止程序的扩展性并不好,因为当我还要加一个声卡听歌的时候,我必须还要
* 在增加一个useSongCard(SongCard()),如果在实际开发中这样的话,第二次增加功能时
* 代码还需要大篇幅修改,很麻烦。我们发现不管增加什么功能,他们的共性是open和close
* ,因此解决方法是增加接口,接口中只提供open和close方法,具体怎么实现则是子类的问题了。
*/
public void useFunction(Pci p) {
/*当把网卡传进来的时候,相当于Pci p = new NetCard();
* 父类引用指向子类对象,多态性;
*/
p.open();
p.close();
}
}
/*这样来实现的话在以后如果想增加什么新的功能的话,MainBoard类就不需要再做修改,相当于电脑主板如果
想增加什么硬件的话就在也不需要动主板,而直接把所要添加的硬件如声卡,网卡,直接插到主板所对外提供的
声卡插槽和网卡插槽就可以了,这样就减小了主板和网卡,声卡的耦合性了,既相互之间的依赖关系不在呢那么强了。
*/

interface Pci {
public abstract void open();

public abstract void close();
}

class NetCard implements Pci {
public void open() {
System.out.println("netcard open...");
}

public void close() {
System.out.println("netcard close...");
}
}

class SongCard implements Pci {
public void open() {
System.out.println("songcard open...");
}

public void close() {
System.out.println("songcard close...");
}
}

class MainBoardExap // 相当于客户端
{
public static void main(String[] args) {
MainBoard mb = new MainBoard();
mb.run();
// mb.useNetCard(new NetCard());
mb.useFunction(new NetCard());
mb.useFunction(new SongCard());
}
}

四,面向对象之内部类
    1,内部类顾名思义就定义在一个类的内部的类。
    2,内部类的使用规则是:(1)内部类可以直接访问外部类的成员,包括自有成员。(2)外部类如果想要访问内部类的成员,必须先建立内部类的对象。
      内部类定义在外部类的成员位置时而且非私有,在外部其他类中的访问格式是: 外部类名.内部类名  变量名 = 外部类对象.内部类对象;Outer.Inner in = new Outer().new Inner();
    3,当内部类出现在外部类的成员位置上时,就能被成员修饰符修饰,比如下面出现的Inner类可以被private修饰;
        当内部类用static修饰的时候,这个内部类就具有了static的特性————只能访问外部类中的static修饰的成员; 
    4,内部类可以不仅可以定义成私有,还可以定义成静态,这是外部类不能做到的。当把内部类定义成静态的时候要注意:(1)当内部类中定义了静态成员,那么该内部类必须是静态的。(2)当外部类的静态成员访内部类的时候,该内部类也要定义成静态的。
        在外部其他类中访问static内部类的非静态成员格式:new Outer.Inner().function();
        在外部其他类中访问static内部类的静态成员格式:Outer.Inner.function(); 
示例:
class Outer
{
static int num = 99;
static class Inner //内部类可以加private修饰符,外部类不可以;
{
//int num = 888;
public static void method() {
//int num = 7777; //当内部类中存在与外部类相同的成员变量时,会先从内部开始寻找
//System.out.println("Inner Class");
//内部类存在与外部类相同的成员变量时访问外部成员变量可以这么做, 再成员变量前加外部类的类名和this
//System.out.println(this.num);
//System.out.println(Outer.this.num);
System.out.println(num);
}
}
public void function() {
Inner in = new Inner();
in.method();
}
}
class InnerClassDemon
{
public static void main(String[] args) {
// Outer o = new Outer();
// o.function();
//内部类定义的格式如下:(格式记住就可以了)
//Outer.Inner oi = new Outer().new Inner();
//oi.method();
//new Outer.Inner().method();//当method方法为非静态时,有这种访问格式访问
//Outer.Inner.method(); //当method方法为静态时,用这种格式访问;
}
}

 5,内部类还可以定义在外部类的局部,也就是外部类的方法的内部。
注意:(1)局部内部类可以直接访问外部类的成员,因为持有外部类的引用。
           (2)局部内部类不可以直接访问局部的成员,也就是不能直接访问外部类的方法中的成员,除非局部成员是final的。
示例:关于局部内部类的使用举例
            (3)局部内部类是不可以定义成静态的,原因是static是成员修饰符,不可以修饰局部。
            (4)如果局部内部类内的成员是静态的,那么该内部类也必须是静态的,而static不能修饰局部,所以这样以来局部内部类中的成员也是不可以用static修饰的。

class Outer3
{
int x = 9;
public void show(final int n) {//这样才可以被Inner3类中的成员访问;
//n ++;错误,不能会n进行操作;
final int y = 88;
class Inner3
{
public void function3() {
System.out.println(n);
System.out.println("inner3 method___" + x +"___" + y);//这里的x相当于Outer3.this.x
//因为y在show方法内部,这里的y必须声明为final型,否则编译出错
}
}//注意:内部类在局部时,它里面的成员不能是静态,因为如果成员是静态则该内部类也必须是静态,而static
//是成员修饰符,static class Inner3不是成员,是show方法的局部,不能修饰局部;
new Inner3().function3();
}
}
class InnerClassDemon3
{
public static void main(String[] args) {
Outer3 o3 = new Outer3();
//o3.show();
o3.show(6);
o3.show(7);//进栈出栈的原理;
}
}

   6,匿名内部类:匿名内部类其实就是内部类的简写格式。定义匿名内部类的前提是:该类继承一个类或者一个接口 。匿名内部类的格式:new 父类名或接口名() {定义子类的内容}
匿名内部类可以理解成一个匿名的子类对象,也可以理解成带内容的对象。
        使用匿名内部类时要注意:匿名内部类本来就是为了简化代码量,当一个内部类继承的接口中含有三个以上的抽象方法的时候,不建议使用匿名内部类,因为在书写是你需要在匿名内部类中实现接口中所有的抽象方法,阅读性会很差,要实现功能的时候却只用到这些抽象方法中的一个。
下面提供一个匿名内部类使用的代码:

interface Inter
{
public abstract void show();
public abstract void show2(int n);
}
class Outer2
{
/*
class Inner2 implements Inter
{
public void show() {
System.out.println("show method");
}
}
*/
public void function2() {
//new Inner2().show();
Inner in = new Inter() { //这一句相当:Inner in = new Inner() --->多态性
public void show() {
System.out.println("show method2");
}
public void show2(int n) {
System.out.println("show2 method" + n);
}
public void show3() {
System.out.println(12345);
}//这就相当于子类的特有方法,如果在大括号外调用show3()就会出错,因为new Inner()具有多态性,
//父类中没有show3这个方法,所以不能调用子类特有的方法,当然这里也不能进行强制类型转换,因为
//该类跟部就没有名字,也就没有子类类型。
};
in.show(); //该部分就是上面注释掉的绿色部分
//in.show3();编译会出错,原因是42--44行;
new Inter() {
public void show() {
System.out.println("show method2");
}
public void show2(int n) {
System.out.println("show2 method___" + n);
}
}.show2(99);
}
}
class InnerClassTest
{
public static void main(String[] args) {
Outer2 o2 = new Outer2();
o2.function2();
}
}