java基础笔记:抽象类 、多态、接口、内部类

问题导入:

在开发中,一个父类的子类里往往对于该父类的某一个方法实现,结果与过程都是不一样的,这时候,原先父类里定义的该方法就显得有些多余(不满足实现该方法的子类就必须强制重写该方法)

抽象方法&抽象类的引入

抽象类

概念 : 继承中,将多个共同的内容,提取到一个类中,但是有些共同方法,方法声明是一样的,具体的实现不一样(每个具体的对象在做具体的操作的时候,功能实现不同),如果有这种情况,这个方法就需要被定义为抽象方法

抽象方法就是具有方法名,但是没有具体的方法实现(没有大括号)

如果一个类中存在抽象方法,那么该类 就是抽象类

抽象类特点 :

写法 : 抽象方法或者抽象类通过abstract 关键字 修饰

特点 :

抽象类不能实例化

抽象类中,可以没有抽象方法,但是有抽象方法的类一定是抽象类

抽象类的子类,必须是一个抽象类 或者 是个普通类,普通类必须重写抽象类中的所有抽象方法

抽象类的成员特点:

成员属性 : 可以有变量,也可以有常量

成员方法 :有抽象方法,普通方法,静态方法

构造方法 :有构造方法,抽象类不能实例化,所以构造方法一般提供给子类访问使用

注意:abstract关键字和static关键字不可以同时作用于一个方法中,代码会报错

原因:静态方法一般都需要进行具体的实现,需要有方法体{实现代码},而抽象关键字的作用恰恰相反,不允许方法有具体的实现方法体(实现代码)两者是相违背的,因此不能同时写,写了代码会报错 即           修饰符 static abstract   返回值类型  方法名();      是错误写法

public abstract class Animal {
    private String name;
    final int age = 20;


    public Animal(String name) {
        this.name = name;
    }

    public Animal() {
    }

    //抽象方法,声明为抽象方法的类,必须是抽象类
    // 加上abstract关键字,完成方法和类的声明
    public abstract void eat();

    public void sleep(){
        System.out.println("动物会睡觉!");
    }

    public static void show(){
        System.out.println("动物会说话!");
    }

    
}

下面是具体案例,定义一个Person抽象类,其中有对应的抽象方法,定义它的子类,实现抽象方法的重写等等。

抽象类Person

package com.day06.AbstractDemo;

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 abstract void work();//抽象方法
    //父类作为抽象类,里面的抽象方法同样被子类继承,而且子类必须重写这些抽象方法

    public void sleep(){//普通方法
        System.out.println("人类会睡觉");
        //父类的普通方法也可以被子类继承并调用
    }
    public static void show(){//静态方法
        System.out.println("父类的show是静态方法");
    }
    
}

子类Student

package com.day06.AbstractDemo;

public class Student extends Person{

    @Override
    public void work() {//子类一但继承了抽象类,就强制要重写其所有的抽象方法
        System.out.println("学生的工作是学习");

    }

}

子类Teacher

package com.day06.AbstractDemo;

public class Teacher extends Person{

    @Override
    public void work() {
        System.out.println("老师的工作是教书");
    }
}

测试类

package com.day06.AbstractDemo;

public class PersonTest {
    public static void main(String[] args) {
        Student student1 = new Student();
        Teacher teacher1 = new Teacher();
            student1.work();
            teacher1.work();
            student1.sleep();//普通方法的实现
            Person.show();//抽象类的静态方法可以执行

    }
}

面向对象三大特征:封装、继承 、多态

多态特性必须要有 继承 extends 或者实现  implements  (实现接口 )

多态

image.png

父类的引用指向子类的对象

Person person =new Student();

父类  对象名 =new 子类();

public class Person {
    public void say(){
        System.out.println("人类会说话!");
    }

    public void show(){
        System.out.println("这是父类的show方法");
    }
}


public class Student extends Person {

        @Override
        public void show(){
            System.out.println("这是子类的show方法");
        }
        public void show1(){
            System.out.println("这是子类特有的show1方法");
        }
}


public class PersonTest {
    public static void main(String[] args) {
        //多态创建Person对象
        //父类的引用指向子类的对象
        Person person = new Student();
        person.say();   //作为父类形态,可以调用父类特有的方法
       // person.show1();   //不能调用子类特有的方法
        person.show(); // 作为子类形态,可以调用子类重写父类的方法
    }
}

多态的特点
前提条件:

1.存在继承关系或者实现关系,

2.必须有方法重写(多态体现)

3.有父类或者父接口的引用指向子类对象

多态的分类

1.具体类的多态

class Fu{}

class Zi extends Fu{}

Fu f =new Zi();

2.抽象类的多态

abstract  class Fu{}

class Zi extends Fu{}

Fu f =new Zi();

3.接口多态

interface Fu{}

class Zi implements Fu{}

Fu f =new Zi();

多态关系中成员访问特点 :

成员变量 : 直接看等号的左边,左边是谁,优先找谁,没有向上找,找不到就报错

编译看左边,运行看左边

成员方法 :先从等号的左边找,左边不存在,就报错,运行的时候看new的是谁,就找谁

编译看左边,运行看右边

构造方法 :子类的构造默认会访问父类构造

多态的好处:

多态可以提高代码的维护性(继承)

多态可以提高代码的扩展性(多态体现)

public abstract class Emp {
    public abstract void work();
}

public class Teacher extends Emp{

    @Override
    public void work() {
        System.out.println("老师在讲课!");
    }
}

public class Assistant extends Emp {

    @Override
    public void work() {
        System.out.println("助教在辅导");
    }
}

public class EmpTest {
    public static void main(String[] args) {
        //没有多态,创建对象,关心的是通过哪个类创建的对象调用的work方法
        Teacher emp1 = new Teacher();
        emp1.work();
        Assistant emp2 = new Assistant();
        emp2.work();

        //多态的方式创建,关注点是work方法的调用,其他的东西我不关心
        Emp emp3 = new Teacher();
        emp3.work();
        Emp emp4 = new Assistant();
        emp4.work();
    }
}

多态的缺点 :

父类引用不能使用子类特有功能,子类可以当做父类使用,父类不能当做子类使用

多态的转型:(面试问题)

多态中,父类引用不能使用子类特有功能,所以,要将父类对象做转型。

多态的转型,分为向上转型和向下转型

image.png

多态的分类 :(面试问题)

面向对象中,根据代码执行时刻的不同,将多态分为 编译时多态 和 运行时多态

编译时多态是静态的,一般指的是方法的重载,根据参数列表来区分不同的方法,通过编译之后就会变成不同的方法。

运行时多态是动态的,它是通过动态绑定来实现的,通过方法的重写来体现的,也就是之前讲解的面向对象的多态性

final关键字

概念:final表示最终的意思,可以用来修饰类,方法,变量

特点:

1.被final修饰的类不能被继承(String类就被final修饰,不能被继承)

2.final修饰的方法,不能被重写

3.被final修饰的变量,会变成常量,必须一开始就被赋予初始值(不会因为程序的运行而发生值的改变),如果被final修饰了还不赋值,代码会报错

public int num1 =10;//普通的变量

public final int num2=20;//常量20,无法被重新赋值(数值不能发生改变)

被final修饰的方法内的局部变量也无法再方法内被重新赋值(因为你执行方法时已经往局部变量里传了初值了,这个时候再进入到方法体内进行二次赋值会报错)

public class Father {
    public int num = 10;  //普通的变量
    public final int num2 = 20;  //常量

    public final void test01(){
        System.out.println("父类的final方法");
    }

}


public class Son extends Father {

    String name;

    public void show(){
        num = 100;
        //num2是常量,无法重新赋值
        //num2 = 1000;
        System.out.println(num);
        System.out.println(num2);
    }
    //子类不能重新父类的final方法
    //public void test01(){
    //    System.out.println("子类的方法");
    //}
}


public class Test {
    public static void main(String[] args) {
        Son son = new Son();
        son.show();

       final int a = 10; //final修饰的局部变量也是常量
       // a = 100;   //不能被重新赋值
        System.out.println(a);


        Son son1 = new Son();
        son1.name = "张三";
        System.out.println(son1.name);


        final Son son2 = new Son();
        son2.name = "李四";
        System.out.println(son2.name);
        //final修饰的引用类型变量,变量中的属性值是可以被改变的
        son2.name = "王五";
        System.out.println(son2.name);
        //final修饰的引用的类型的变量,引用地址无法重新被赋值
        //son2 = new Son();
    }

    //final能不能修饰局部变量?修饰基本类型和引用类型的区别
    //final修饰的局部变量也是常量
    //final修饰的引用类型变量,变量中的属性值是可以被改变的
    //final修饰的引用的类型的变量,引用地址无法重新被赋值
}

接口

接口里的方法都是抽象方法(不需要加abstract关键字,因为接口里的方法默认自己加上abstract)

实现类实现接口时需要实现父类接口里的所有的抽象方法(解决单继承的局限性)

接口的概念:接口在java中就是一种规则也是一种约束,实现类必须遵守接口里的规则。

接口和类属于统一级别(接口可以看作是特殊的类)

接口的特点:

写法:使用interface关键字,其实就是将类中的class换成interface

public interface 接口名()

子类实现接口

class 类名 implements 接口名{}

接口和接口之间可以继承

interface 子接口 extends 父接口{

}

其他特点:接口不能被实例化:不能new 接口名;

接口的子类(实现类)可以是一个具体的普通类(具体类中必须要实现接口内的全部方法)

也可以是一个抽象类,抽象类可以不用实现全部抽象方法

Java中的接口 :

概念 :接口就是一种规则,也是一种约束,实现类必须要遵守接口中定义的信息,并且还可以解决单继承的局限性

接口的特点:

写法 : 使用interface关键字,其实就是将类的class换成interface

public interface 接口名{}

子类实现接口:

class 类名 implements 接口名{}

interface 子接口 extends 父接口{}

其他特点 :

1,接口不能被实例化

2,接口的实现类 ,

可以是一个具体类,具体类中,必须要实现父接口的全部抽象方法

可以是一个抽象类,抽象类可以不用实现全部抽象方法

3,成员特点:

成员变量

成员变量只能是常量,默认的修饰符是 : public static final

构造方法

接口中没有构造方法

成员方法

默认只能存在抽象方法,默认修饰符就是 public abstract

jdk8针对接口的不容易扩展现象,增加了static 和 default方法,如果定义这两个方法,方法中必须要写方法体用来实现某些功能,主要用来增加接口的灵活性

jdk9之后,在接口中又增加了私有private修饰的方法和private修饰的静态方法,这些私有的方法,主要可以改善接口内部代码的可重用性

因为接口方法都是抽象方法,没有办法实例化new,因此都是通过多态写法,利用父类(接口)引用指向子类对象(实现类对象)

父类接口指定一个方法,子类(即实现类)去实现这个方法。

接口 接口名 =new 实现类();

运行的时候直接 

接口名.方法名();

注意:接口中不能定义属性(只能定义常量,没有意义)

接口与抽象类的区别

区别主要在设计区别上(日常使用中的区别不是很大)

接口和抽象类的区别 · 语雀 《接口和抽象类的区别》

内部类

概念:把类定义在其他类的内部,这个类就被成为内部类

特点:内部类作为外部类的一部分,它可以直接访问外部类的属性和方法(包括私有的)

相反,外部类如果要访问内部类的成员则需要创建内部类对象

分类:

定义在成员位置:成员内部类

定义在局部位置(一般指外部类的方法里):局部内部类

public class Outer {
    private int num = 100; //外部类的私有属性
    //成员内部类
    class Inner{
    }
    //外部类的成员方法
    public void method(){
        //局部内部类
        class Inner{
        }
    }
}

在同一个类中,成员内部类可以跟定义在方法里的局部内部类同名(类比成员变量和局部变量),它们的作用范围不一样

创建普通成员内部类对象的写法(固定写法)

public class Outer {
    private int num = 100; //外部类的私有属性
    private static int num1 = 200;
    //成员内部类
     class Inner{
        //内部类可以使用的修饰符
        //private  static
        //内部类的普通方法
        public void show(){
            //内部类加了static之后,只能访问外部类中加了static的属性
            System.out.println(num);
            System.out.println(num1);
        }
        //如果内部类没有static,内部不能定义static方法
        //public static void show2(){
        //    //System.out.println(num);
        //    System.out.println(num1);
        //}
    }
}

public class InnerDemo {
    public static void main(String[] args) {
        //创建内部类的对象的写法 :
        //外部类名.内部类名 对象名 = 外部类对象.内部类对象
        Outer.Inner  inner = new Outer().new Inner();
        inner.show();

    }
}

外部类名.内部类名  对象名  = 外部类对象.内部类对象

Outer.Inner  inner = new Outer().new Inner();

(外面多了一层外部类的创建)

内部类的调用:

inner.方法名();

内部类可以使用的修饰符

//private static

静态成员内部类

public class Outer {
    private int num = 100; //外部类的私有属性
    private static int num1 = 200;
    //成员内部类
    static class Inner{
        //内部类可以使用的修饰符
        //private  static
        //内部类的普通方法
        public void show(){
            //内部类加了static之后,只能访问外部类中加了static的属性
            //System.out.println(num);
            System.out.println(num1);
        }
        //如果内部类没有static,内部不能定义static方法
        public static void show2(){
            //System.out.println(num);
            System.out.println(num1);
        }
    }
}

public class InnerDemo {
    public static void main(String[] args) {

        //静态的内部类对象创建方式:
        //外部类名.内部类名 对象名 = new 外部类名.内部类名();
        Outer.Inner inner1 = new Outer.Inner();
        inner1.show();
        inner1.show2();
        //静态内部类中的静态方法,可以通过类名直接访问
        Outer.Inner.show2();
        //Outer.Inner.show();
    }
}

内部类加了static之后(变成静态内部类),就只能访问外部类中的静态成员变量

同样的如果内部类没有被static修饰,它的内部不能定义静态的方法与变量

局部内部类:

1,可以直接访问外部类的成员

2,可以在局部位置,创建内部类的对象,通过对象调用内部类的方法,从而来使用局部内部类的功能

public class Outer {
    private String str = "hello";

    public void method(String a){
    	  //a = "world";  //变量a重新赋值会报错
        //局部内部类
       class Inner{
           //内部类的成员方法
           public void show(){
               System.out.println(str); //直接访问外部类的成员属性
               System.out.println(a); //访问局部变量
           }
       }
       //在局部位置,直接创建内部类的对象
        Inner inner = new Inner();
        inner.show();
    }
}


public class InnerTest {
    public static void main(String[] args) {
        //创建外部类对象,通过外部类对象直接调用成员方法
        //从而调用到成员方法中的局部内部类
        Outer outer = new Outer();
        outer.method("你好");
    }
}

局部内部类访问局部变量的时候的问题

1,局部内部类访问的局部变量,必须是用final修饰的

为什么?

局部变量是随着方法的调用而存在,方法调用完毕而消失

局部内部类对象是在堆中,堆中的内容不会马上消失,所以如果不加final修饰,可能会出现一个没有消失的对象,在调用已经消失的局部变量,这种情况是不合理的。

加了final修饰后,变量就变为常量了,消失之后内存中的数据还是存在

jdk8之后,局部内部类的局部变量,即使没有加上final,系统也会默认变量是final的

匿名内部类

匿名内部类是内部类的简化写法

需要前提 :需要存在一个类或者接口,这个类可以是具体类,也可以是抽象类

new 类名或者接口名(){

重写方法;

}

public interface Inner {
    void show1();
    void show2();
}

public class Outer {

    //外部类的成员方法
    public void method(){
        //这个操作,相当于在创建接口的实现类
        //这个实现类,没有名字,称为匿名内部类
        //相当于把实现Inner接口的实现类,和实现类的对象都创建了
        //完成了4个动作 :
        // 1,Inner接口实现类的创建 2,接口方法的重写 3,实现类对象的创建 4,重写方法的调用
       /* new Inner(){
            //实现接口中的方法
            @Override
            public void show1() {
                System.out.println("匿名内部类重写的show1方法~");
            }

            @Override
            public void show2() {
                System.out.println("匿名内部类重写的show2方法~");
            }
        }.show1();
        //接口中有多个方法的话,写实现就得写多次,比较麻烦
        new Inner(){
            //实现接口中的方法
            @Override
            public void show1() {
                System.out.println("匿名内部类重写的show1方法~");
            }

            @Override
            public void show2() {
                System.out.println("匿名内部类重写的show2方法~");
            }
        }.show2();
        */

       //使用多态来改进写法,父接口的引用指向子类的对象
        Inner inner = new Inner(){
            //实现接口中的方法
            @Override
            public void show1() {
                System.out.println("匿名内部类重写的show1方法~");
            }

            @Override
            public void show2() {
                System.out.println("匿名内部类重写的show2方法~");
            }
        };
        inner.show1();
        inner.show2();
    }
}


public class OuterTest {
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.method();
    }
}

匿名内部类的笔试题:

public interface Inner {
    void show();
}


public class Outer {
        //等待补齐代码

}

public class OuterTest {
    public static void main(String[] args) {
        Outer.method().show();
        //在后台输出 :"HelloWord!"
    }
}

补充代码:


    //Outer.method()
    //说明存在静态的method()方法

    //Outer.method().show() 说明method方法应该返回Inner对象
    public static Inner method(){
        return new Inner() {
            @Override
            public void show() {
                System.out.println("HelloWorld!");
            }
        };
    }

通过设计模式的完成计算器的使用:

基本代码实现


/*
基本代码实现计算器
 */
public class Demo01 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入计算的第一个数字:");
        int a = scanner.nextInt();
        System.out.println("请输入运算符号:");
        String b = scanner.next();
        System.out.println("请输入计算的第二个数字:");
        int c = scanner.nextInt();
        int result = 0;
        switch (b){
            case "+":
              result = a + c;
              break;
            case "-":
                result = a - c;
                break;
            case "*":
                result = a * c;
                break;
            case "/":
                result = a / c;
                break;
            default:
                System.out.println("运算符输入错误!");
                return;
        }

        System.out.println( a + b + c + "=" +result);
    }
}

面向对象实现

//面向对象实现
public class Operation {

    public static int getResult(int a,int b,String operate){
        int result = 0;
        switch (operate){
            case "+":
                result = a + b;
                break;
            case "-":
                result = a - b;
                break;
            case "*":
                result = a * b;
                break;
            case "/":
                result = a / b;
                break;
            default:
                System.out.println("运算符输入错误!");
                break;
        }
        return result;
    }
}

public class Test {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入计算的第一个数字:");
        int a = scanner.nextInt();
        System.out.println("请输入运算符号:");
        String b = scanner.next();
        System.out.println("请输入计算的第二个数字:");
        int c = scanner.nextInt();

        int result = Operation.getResult(a, c, b);
        System.out.println(result);
    }
}

利用设计模式的工厂方法模式:

//计算类抽象类
public abstract class Operation {
    private double num1;
    private double num2;
    public abstract double getResult();

    public double getNum1() {
        return num1;
    }

    public void setNum1(double num1) {
        this.num1 = num1;
    }

    public double getNum2() {
        return num2;
    }

    public void setNum2(double num2) {
        this.num2 = num2;
    }
}

//加法类
public class Add extends Operation {
    @Override
    public double getResult() {
        return getNum1() + getNum2();
    }
}

//减法类
public class Minus extends Operation {

    @Override
    public double getResult() {
        return getNum1() - getNum2();
    }
}

利用设计模式的抽象工厂模式:

//计算类抽象类
public abstract class Operation {
    private double num1;
    private double num2;
    public abstract double getResult();

    public double getNum1() {
        return num1;
    }

    public void setNum1(double num1) {
        this.num1 = num1;
    }

    public double getNum2() {
        return num2;
    }

    public void setNum2(double num2) {
        this.num2 = num2;
    }
}

//加法类
public class Add extends Operation {
    @Override
    public double getResult() {
        return getNum1() + getNum2();
    }
}

//减法类
public class Minus extends Operation {

    @Override
    public double getResult() {
        return getNum1() - getNum2();
    }
}

//计算工厂抽象类
public abstract class OperationFactory {
    public abstract Operation createOperation();

}

//加法工厂
public class AddFactory extends OperationFactory {

    @Override
    public Operation createOperation() {
        return new Add();
    }
}
//减法工厂
public class MinusFactory extends OperationFactory {

    @Override
    public Operation createOperation() {
        return new Minus();
    }
}

//测试类
public class Test {
    public static void main(String[] args) {
        AddFactory addFactory = new AddFactory();
        Operation operation = addFactory.createOperation();
        operation.setNum1(10);
        operation.setNum2(20);
        System.out.println(operation.getResult());
    }
}

使用抽象工厂之后,添加一个运算,只要在写子类继承即可:

比如,加个乘法类:

//乘法类
public class ChengFa extends Operation {

    @Override
    public double getResult() {
        return getNum1() * getNum2();
    }
}

//乘法工厂
public class ChengFaFactory extends OperationFactory {

    @Override
    public Operation createOperation() {
      return new ChengFa();
    }
}


//测试类
		ChengFaFactory chengFaFactory = new ChengFaFactory();
        Operation operation1 = chengFaFactory.createOperation();
        operation1.setNum2(10);
        operation1.setNum1(20);
        System.out.println(operation1.getResult());

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值