JAVA学习笔记_面向对象

文章目录

  • JAVA学习笔记_面向对象
    • 1. 面向对象
      • 1.1 面向对象的思想
      • 1.2 类和对象
      • 1.3 类的定义
      • 1.4 类的对象的创建和使用
      • 1.5成员变量和局部变量的区别
      • 1.6 内部类
        • 1.6.1 内部类的格式
        • 1.6.2 内部类使用
            • 成员内部类的使用
            • 局部内部类的使用
        • 1.6.3 * 匿名内部类
    • 2. 面向对象特征——封装
      • 2.1封装的概念
      • 2.2 封装的关键字——private
      • 2.3封装优化——this关键字
      • 2.4 封装优化——构造方法
      • 2.5 标准代码——JavaBean
      • 2.6 匿名对象
    • 3. 面向对象特征——继承
      • 3.1 继承的概念
      • 3.2 类的继承格式
      • 3.3 继承中的成员变量的关系
      • 3.4 重写(Override)
      • 3.5 构造方法的访问特点
      • 3.6 super关键字的三种用法:
      • 3.7 this关键字的三种用法:
      • 3.8 Java 继承的关系
      • 3.9 抽象类
      • 练习:红包随机分割。
    • 4. 面向对象特征——多态
      • 4.1 多态的概念
      • 4.2 多态的个格式
      • 4.3 多态中的成员变量和成员方法
      • 4.4 对象的转型
    • 5. Java中的接口
      • 4.1 定义接口的格式
      • 4.2 接口的使用步骤
      • 4.3 接口中抽象(abstract)方法
      • 4.4 接口中的默认(default)方法
      • 4.5 接口中的私有(private)方法
      • 4.6 接口中的静态(static)方法
      • 4.7 接口中的常量的定义和使用
      • 4.8 接口中方法的总结
      • 4.9 类的继承和实现接口的问题
      • 4.10 接口之间的多继承
    • 综合案例

JAVA学习笔记_面向对象

1. 面向对象

1.1 面向对象的思想

  • 概述

    Java语言是一种面向对象的程序设计语言,而面向对象思想是一种程序设计思想,我们在面向对象思想的指引下,使用Java语言去设计、开发计算机程序。 这里的对象泛指现实中一切事物,每种事物都具备自己的属性行为。面向对象思想就是在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为特征抽象出来,描述成计算机事件的设计思想。 它区别于面向过程思想,强调的是通过调用对象的行为来实现功能,而不是自己一步一步的去操作实现。

  • 面向过程:强调步骤。

  • 面向对象:强调对象,这里的对象就是洗衣机

  • 三大特征:

    • 继承
    • 封装
    • 多态

**实例:**输出数组

package cn.itcast.day06.demo01;

import java.util.Arrays; // 导入Arrays类

public class Demo01PrintArray {

    public static void main(String[] args) {
        int[] array = { 10, 20, 30, 40, 50, 60 };

        // 要求打印格式为:[10, 20, 30, 40, 50]
        // 使用面向过程,每一个步骤细节都要亲力亲为。
        System.out.print("[");
        for (int i = 0; i < array.length; i++) {
            if (i == array.length - 1) { // 如果是最后一个元素
                System.out.println(array[i] + "]");
            } else { // 如果不是最后一个元素
                System.out.print(array[i] + ", ");
            }
        }
        System.out. println("==============");
        // 使用面向对象
        // 找一个JDK给我们提供好的Arrays类,
        // 其中有一个toString方法,直接就能把数组变成想要的格式的字符串
        System.out.println(Arrays.toString(array));
    }
}
  • 特点: 特点面向对象思想是一种更符合我们思考习惯的思想,它可以将复杂的事情简单化,并将我们从执行者变成了指挥者。面向对象的语言中,包含了三大基本特征,即封装继承多态

1.2 类和对象

  • 什么是类

    • 类:是一组相关属性和行为的集合。可以看成是一类事物的模板,使用事物的属性特征和行为特征来描述该类事物。
    • 现实中,描述一类事物:
    • 属性:就是该事物的状态信息。
    • 行为:就是该事物能够做什么。
  • 什么是对象:

    ​ 一类事物的具体体现。对象是类的一个实例,必然具备该类事物的属性和行为。

  • 类与对象的关系

    • 类是对一类事物的描述,是抽象的
    • 对象是一类事物的实例,是具体的
    • 类是对象的模板,对象是类的实体。

1.3 类的定义

  • 属性:事物的状态信息。
  • 行为:事物能够做什么。
public class ClassName {
    //成员变量
    //成员方法
}
  • 定义类:就是定义类的成员,包括成员变量和成员方法。
  • 成员变量:和以前定义变量几乎是一样的。只不过位置发生了改变。在类中,方法外。
  • 成员方法:把static去掉

定义一个Student类:

public class Student {

    // 成员变量
    String name; // 姓名
    int age;     // 姓名

    // 成员方法
    public void eat() {
        System.out.println("吃饭饭!");
    }

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

    public void study() {
        System.out.println("学习!");
    }
}

成员变量(属性):
String name; // 姓名
int age; // 年龄
成员方法(行为):
public void eat() {} // 吃饭
public void sleep() {} // 睡觉
public void study() {} // 学习

注意事项:

  1. 成员变量是直接定义在类当中的,在方法外边。
  2. 成员方法不要写static关键字。

1.4 类的对象的创建和使用

  • 创建的一个Student的对象

public class Student {
    public static void main(String[] args) {
		// 1. 导包。
		import XXX.XXX.Student;
        // 2. 创建,格式:
        // 类名称 对象名 = new 类名称();

        Student stu = new Student();  // 根据Student类,创建了一个名为stu的对象

        // 3. 使用其中的成员变量,格式:
        // 对象名.成员变量名
        System.out.println(stu.name); // null
        System.out.println(stu.age);  // 0
        System.out.println("=============");

        // 改变对象当中的成员变量数值内容
        // 将右侧的字符串,赋值交给stu对象当中的name成员变量
        stu.name = "小明";
        stu.age = 18;
        System.out.println(stu.name); // 小明
        System.out.println(stu.age); // 18
        System.out.println("=============");

        // 4. 使用对象的成员方法,格式:
        // 对象名.成员方法名()
        stu.eat();
        stu.sleep();
        stu.study();
    }

}
  1. 导包:也就是指出需要使用的类,在什么位置。
    import 包名称.类名称;
    对于和当前类属于同一个包的情况,可以省略导包语句不写。
  2. 创建,格式:
    类名称 对象名 = new 类名称();
    Student stu = new Student();
  3. 使用,分为两种情况:
    使用成员变量:对象名.成员变量名
    使用成员方法:对象名.成员方法名(参数)

注意事项:
如果成员变量没有进行赋值,那么将会有一个默认值,规则和数组一样。

1.5成员变量和局部变量的区别

  • 在类中的位置不一样

  • 成员变量:类中,方法外

  • 局部变量:方法中或者方法声明上(形式参数)

  • 作用的范围不一样

    • 成员变量:类中
    • 局部变量:方法中
  • **初始化值的不同 **

    • 成员变量:有默认值
    • 局部变量:没有默认值。必须先定义,赋值,最后使用
  • **在内存中的位置不同 **

    • 成员变量:堆内存
    • 局部变量:栈内存
  • **生命周期不同 **

    • 成员变量:随着对象的创建而存在,随着对象的消失而消失
    • 局部变量:随着方法的调用而存在,随着方法的调用完毕而消失

1.6 内部类

内部类就是一个类里面还包含另一个类

分类:

  1. 成员内部类
  2. 局部内部类(包含匿名内部类)

1.6.1 内部类的格式

  • 成员内部类

    格式:

    修饰符 class 外部类名称 {
        修饰符 class 内部类名称 {
            // ...
        }
        // ...
    }
    

    注意:内用外,随意访问;外用内,需要内部类对象。

  • 局部内部类

    如果一个类是定义在一个方法内部的,那么这就是一个局部内部类。只有当前所属的方法才能使用它,出了这个方法外面就不能用了。

    格式:

    修饰符 class 外部类名称 {
        修饰符 返回值类型 外部类方法名称(参数列表) {
            class 局部内部类名称 {
                // ...
            }
        }
    }
    
  • 类的权限修饰符(能否使用)

    修饰符 外部类 成员内部类 局部内部类
    public Y Y N
    protected Y Y N
    default N Y N
    private N Y N

1.6.2 内部类使用

成员内部类的使用
  • 两种方法

    • 间接方式:

      在外部类的方法当中,使用内部类;然后main只是调用外部类的方法。

    • 直接方法:

      外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();
      
      //或 如果内部类使用Static修饰则可使用这种方式去创建内部类的对象
      外部类名称.内部类名称 对象名 = new 外部类名称.内部类名称();
      

    例如:

    public class Car {
        private String name;
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void methodCar() {
            System.out.println("外部Car的方法");
            // 调用内部类的方法
            Engine engine = new Engine();
            engine.methodEngine();
        }
    
        // 定义一个内部类
        public static class Engine {
    
            // 内部类的方法
            public void methodEngine() {
                System.out.println("发动机点火");
            }
    
        }
    }
    
局部内部类的使用

局部内部类,只有当前所属的方法才能使用它。

注意:

局部内部类,如果希望访问所在方法的局部变量,那么这个局部变量必须是【有效final的】。

备注:从Java 8+开始,只要局部变量事实不变,那么final关键字可以省略。

原因:

  1. new出来的对象在堆内存当中。
  2. 局部变量是跟着方法走的,在栈内存当中。
  3. 方法运行结束之后,立刻出栈,局部变量就会立刻消失。
  4. 但是new出来的对象会在堆当中持续存在,直到垃圾回收消失。

1.6.3 * 匿名内部类

匿名内部类是为了省略接口的实现类,直接使用new来重写接口的抽象方法。

格式:

接口名称 对象名 = new 接口名称() {
    // 覆盖重写所有抽象方法
};
public class Demo03AnonymityClass {
    public static void main(String[] args) {
        MyInterface myInterface = new MyInterface() {

            @Override
            public void method() {
                System.out.println("匿名类重写抽象方法");
            }
        };
        myInterface.method();
    }
}

注意事项:

  1. 匿名内部类,在【创建对象】的时候,只能使用唯一一次。
  2. 匿名对象,在【调用方法】的时候,只能调用唯一一次。
  3. 匿名内部类是省略了【实现类/子类名称】,但是匿名对象是省略了【对象名称】

2. 面向对象特征——封装

2.1封装的概念

面向对象编程语言是对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界无法直接操作和修改。封装可以被认为是一个保护屏障,防止该类的代码和数据被其他类随意访问。要访问该类的数据,必须通过指定的方式。适当的封装可以让代码更容易理解与维护,也加强了代码的安全性。

2.2 封装的关键字——private

  • private的含义:
  1. private是一个权限修饰符,代表最小权限。

  2. 可以修饰成 员变量和成员方法。

  3. 被private修饰后的成员变量和成员方法,只在本类中才能访问。

  • private的使用格式

    private 数据类型 变量名 ;
    
  • **使用 private 修饰成员变量,代码如下 **

    public class Student {
        private String name;
        private int age;
    }
    

    注: 使用了private本类可以直接访问,但是在类外无法直接进行访问

  • 使用setXxx方法、getXxx访问间接访问private的成员变量

    public class Student {
        private String name;
        private int age;
        public void setName(String n) {
            name = n;
        } 
        public String getName() {
        	return name;
        } 
        public void setAge(int a) {
            age = a;
        } 
        public int getAge() {
        	return age;
        }
    }
    

    注:必须叫setXxx或者是getXxx命名规则。
    对于Getter来说,不能有参数,返回值类型和成员变量对应;
    对于Setter来说,不能有返回值,参数类型和成员变量对应。

  • 对于boolean类型的private成员变量的Getter方法

    public class Person {
    
        private boolean male; //性别
        public void setMale(boolean male) {
            this.male = male;
        }
        public boolean isMale() {
            return male;
        }
    }
    

2.3封装优化——this关键字

  • this 的使用:
public class Person {

    String name; // 我自己的名字

    // 参数name是对方的名字
    // 成员变量name是自己的名字
    public void sayHello(String name) {
        System.out.println(name + ",你好。我是" + this.name);
        System.out.println(this);
    }
}

当方法的局部变量和类的成员变量重名的时候,根据“就近原则”,优先使用局部变量。
如果需要访问本类当中的成员变量,需要使用格式:
this.成员变量名
通过谁调用的方法,谁就是this

2.4 封装优化——构造方法

构造方法是专门用来创建对象的方法,当我们通过关键字new来创建对象时,其实就是在调用构造方法。

  • 格式:

    public 类名称(参数类型 参数名称) {
        方法体
    }
    
    package cn.fate.java_learn.basic.Class;
    
    import javax.xml.namespace.QName;
    
    public class Student {
    
        private String name;
        private int age;
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public Student() {
            System.out.println("无参数构造方法");
        }
    
        public Student(String name, int age) {
            System.out.println("带参数的构造方法");
            this.name = name;
            this.age = age;
        }
    
        public void show() {
            System.out.println("我叫:" + name + ",年龄:" + age);
        }
    
    }
    

    注:

    1. 构造方法的名称必须和所在的类名称完全一样,就连大小写也要一样。
    2. 构造方法不要写返回值类型、void都不写。
    3. 构造方法不能return一个具体的返回值。
    4. 如果没有编写任何构造方法,那么编译器将会默认赠送一个构造方法,没有参数、方法体什么事情都不做。public Student() {}
    5. 一旦编写了至少一个构造方法,那么编译器将不再赠送。
    6. 构造方法也是可以进行重载的。 重载:方法名称相同,参数列表不同。

2.5 标准代码——JavaBean

一个标准的类通常要拥有下面四个组成部分:

  1. 所有的成员变量都要使用private关键字修饰
  2. 为每一个成员变量编写一对儿Getter/Setter方法
  3. 编写一个无参数的构造方法
  4. 编写一个全参数的构造方法

这样标准的类也叫做Java Bean

  • 编写一个标准的类
package cn.fate.java_learn.basic.Class;

public class StandardStudent {
    private String name;
    private int age;
    private boolean Male;

    public StandardStudent() {
    }

    public StandardStudent(String name, int age, boolean male) {
        this.name = name;
        this.age = age;
        Male = male;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public boolean isMale() {
        return Male;
    }

    public void setMale(boolean male) {
        Male = male;
    }
}

注 : IDEA 可以使用快捷键Alt+Insert快速生成Getter、Setter 及构造方法

2.6 匿名对象

创建对象的标准格式:
类名称 对象名 = new 类名称();

匿名对象就是只有右边的对象,没有左边的名字和赋值运算符。
new 类名称();
注意事项:匿名对象只能使用唯一的一次,下次再用不得不再创建一个新对象。
使用建议:如果确定有一个对象只需要使用唯一的一次,就可以用匿名对象。

import java.util.Scanner;

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

        methodParam(new Scanner(System.in));
        Scanner sc = methodReturn();
        int num = sc.nextInt();  
        System.out.println("Input num is" + num);
        methodParam(new Scannner); // 匿名对象充当参数
    }
    public static void methodParam(Scanner sc){
        int num = sc.nextInt(); 
        System.out.printf("Input num is % d\n", num);
    }

    public static Scanner methodReturn(){
        return new Scanner(System.in); // 返回匿名对象
    }

}

3. 面向对象特征——继承

3.1 继承的概念

继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。

继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。

3.2 类的继承格式

在 Java 中通过 extends 关键字可以申明一个类是从另外一个类继承而来的,一般形式如下:

class 父类 {
}
 
class 子类 extends 父类 {
}

3.3 继承中的成员变量的关系

父子类的继承关系当中,如果成员变量重名,则创建子类对象时,访问有两种方式:

直接通过子类对象访问成员变量:
等号左边是谁,就优先用谁,没有则向上找。

间接通过成员方法访问成员变量:
该方法属于谁,就优先用谁,没有则向上找。

当出现三种情况时的调用关系:

  • 局部变量: 直接写成员变量名
  • 本类的成员变量: this.成员变量名
  • 父类的成员变量: super.成员变量名

注意事项:
无论是成员方法还是成员变量,如果没有都是向上找父类,绝对不会向下找子类的

// 父类
public class Fu {
    
    int num = 10;
    
}

// 子类
public class Zi extends Fu {

    int num = 20;

    public void method() {
        int num = 30;
        System.out.println(num);       // 30,局部变量
        System.out.println(this.num);  // 20,本类的成员变量
        System.out.println(super.num); // 10,父类的成员变量
    }
}


3.4 重写(Override)

重写(Override)

概念:在继承关系当中,方法的名称一样,参数列表也一样。

重写(Override):方法的名称一样,参数列表【也一样】。覆盖、覆写。
重载(Overload):方法的名称一样,参数列表【不一样】。

方法的覆盖重写特点:创建的是子类对象,则优先用子类方法。

方法覆盖重写的注意事项:

  1. 必须保证父子类之间方法的名称相同,参数列表也相同。
    @Override:写在方法前面,用来检测是不是有效的正确覆盖重写。
    这个注解就算不写,只要满足要求,也是正确的方法覆盖重写。

  2. 子类方法的返回值必须**【小于等于】**父类方法的返回值范围。
    小扩展提示:java.lang.Object类是所有类的公共最高父类(祖宗类),java.lang.String**就是Object的子类。

  3. 子类方法的权限必须**【大于等于】**父类方法的权限修饰符。
    小扩展提示:public > protected > (default) > private
    备注:(default)不是关键字default,而是什么都不写,留空。

3.5 构造方法的访问特点

继承关系中,父子类构造方法的访问特点:

  1. 子类构造方法当中有一个默认隐含的“super()”调用,所以一定是先调用的父类构造,后执行的子类构造。
  2. 子类构造可以通过super关键字来调用父类重载构造。
  3. super的父类构造调用,必须是子类构造方法的第一个语句。不能一个子类构造调用多次super构造

总结:
子类必须调用父类构造方法,不写则赠送super();写了则用写的指定的super调用,super只能有一个,还必须是第一个。

3.6 super关键字的三种用法:

  1. 在子类的成员方法中,访问父类的成员变量。
  2. 在子类的成员方法中,访问父类的成员方法。
  3. 在子类的构造方法中,访问父类的构造方法。

3.7 this关键字的三种用法:

  1. 在本类的成员方法中,访问本类的成员变量。
  2. 在本类的成员方法中,访问本类的另一个成员方法。
  3. 在本类的构造方法中,访问本类的另一个构造方法。

在第三种用法当中要注意:

  • this(…)调用也必须是构造方法的第一个语句,唯一一个。
  • superthis两种构造调用,不能同时使用。

总结:

super关键字用来访问父类内容,而this关键字用来访问本类内容

3.8 Java 继承的关系

JAVA学习笔记_面向对象_第1张图片

3.9 抽象类

抽象方法:就是加上abstract关键字,然后去掉大括号,直接分号结束。
抽象类:抽象方法所在的类,必须是抽象类才行。在class之前写上abstract即可。

如何使用抽象类和抽象方法:

  1. 不能直接创建new抽象类对象。
  2. 必须用一个子类来继承抽象父类。
  3. 子类必须覆盖重写抽象父类当中所有的抽象方法。
    覆盖重写(实现):子类去掉抽象方法的abstract关键字,然后补上方法体大括号。
  4. 创建子类对象进行使用。
// 父类
public abstract class Animal {
    // 定义一个抽象方法
    public abstract void eat();

    // 定义一个普通的方法
    public void method() {
        System.out.printf("普通方法");
    }
}


//子类
public class Cat extends Animal {
    public void eat(){
        System.out.println("Cat eats fish");
    }
}

// Main
public class DemoAnimal {
    public static void main(String[] args) {
        Cat cat = new Cat();
        cat.eat();
    }
}


注意事项:

  1. 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。

    • 理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。
  2. 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。

    • 理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。
  3. 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。

    • 理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,以满足特殊类的需求。
  4. 抽象类的子类(除子类也为抽象类),必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。

    • 理解:假设不重写所有抽象方法,则类中可能包含抽象方法没有意义,无法被使用。

练习:红包随机分割。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Random;

public class RandomSplit {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList();
        list = divived(100, 20);
        System.out.println(list);
    }


    public static ArrayList<Integer> divived(double money, int n) {
	 
        /*
         * 红包拆分方法
         * @param money  被拆分的总金额 (单位元)
         * @param n      被拆分的红包个数
         * @return 拆分后的每个红包金额数组
         */
        
        // 创建一个长度的红包数组
        ArrayList<Integer> redList = new ArrayList<>();

        int moneyFen = (int) (money * 100);
        // 判断红包的总金额
        if (money > 200) {
            System.out.println("单个红包不能超过200元");
            return redList; // 返回空的红包集合
        }
        if (moneyFen < n || moneyFen < 1) {
            System.out.println("被拆分的总金额不能小于0.01元");
            return redList; // 返回空的红包集合
        }
        // 创建一个n长的数组
        Integer[] array = new Integer[n];

        //1. 给每个包分0.01元,确保每个包有钱
        Arrays.fill(array, 1);
        moneyFen -= n;   //总金额减去已分配的0.01 * n 元

        //2. 进行随机分配
        Random rand = new Random();

        while (moneyFen > 1) {
            int moneyCut = rand.nextInt(moneyFen);  // 随机选择分配的包
            int i = rand.nextInt(n);  // 随机选择分配的包
            array[i] += moneyCut; // 分配给每一个包并且转换为元
            moneyFen -= (int) moneyCut;
        }
        //把最后的包放到最后一个包里面
        array[n - 1] += moneyFen ;
        // 把分好的包放到redList里面
        Collections.addAll(redList, array);

        return redList;
    }
}

4. 面向对象特征——多态

4.1 多态的概念

多态是同一个行为具有多个不同表现形式或形态的能力。

多态存在的三个必要条件:

  • 继承
  • 重写
  • 父类引用指向子类对象

4.2 多态的个格式

格式:

父类名称 对象名 = new 子类名称();
// 或者:
接口名称 对象名 = new 实现类名称();

4.3 多态中的成员变量和成员方法

  • 访问成员变量的两种方式:

    1. 直接通过对象名称访问成员变量:看等号左边是谁,优先用谁,没有则向上找。
    2. 间接通过成员方法访问成员变量:看该方法属于谁,优先用谁,没有则向上找。
  • 成员方法的访问规则是:
    new的是谁,就优先用谁,没有则向上找。

成员变量:编译看左边,运行还看左边。
成员方法:编译看左边,运行看右边。
口诀: 编译看左边,运行看右边。

4.4 对象的转型

  • 向上转型:多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的。当父类引用指向一个子类对象时,便是向上转型 。

    向上转型是安全的,等于从小范围转向大的范围。

    格式:

    父类类型 变量名 = new 子类类型();
    // 比如
    Animal a = new Cat();
    
  • 向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。

    注: 向下转型的对象,之前的必须是通过向上转型过来的,才能向下转回原来的对象。

    格式:

    子类类型 变量名 = (子类类型) 父类变量名;
    // 比如:
    Cat c =(Cat) a;
    

    向下转型的异常:

    ​ 如果错误的转换为其他对象,编译时会出现 ClassCastException的报错。为了确定向下转型的正确性,我们可以使用关键字 instanceof ,进行校验。

    校验格式:

    变量名 instanceof 数据类型
    如果变量属于该数据类型,返回true。
    如果变量不属于该数据类型,返回false

    实例:

    public class Test {
        public static void main(String[] args) {
            // 向上转型
            Animal a = new Cat();
            a.eat(); // 调用的是 Cat 的 eat
            // 向下转型
            if (a instanceof Cat) {
                Cat c = (Cat) a;
                c.catchMouse(); // 调用的是 Cat 的 catchMouse
            } else if (a instanceof Dog) {
                Dog d = (Dog) a;
                d.watchHouse(); // 调用的是 Dog 的 watchHouse
            }
        }
    }
    

5. Java中的接口

接口就是多个类的公共规范。

接口(Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。

接口是一种引用数据类型,最重要的内容就是其中的:抽象方法。

接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。

除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。

接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。

如果是Java 7,那么接口中可以包含的内容有:

  1. 常量
  2. 抽象方法

如果是Java 8,还可以额外包含有:
3. 默认方法
4. 静态方法

如果是Java 9,还可以额外包含有:

  1. 私有方法

4.1 定义接口的格式

public interface 接口名称 [extends 其他的接口名] {
       // 声明变量
       // 抽象方法
}

接口有以下特性:

  • 接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
  • 接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。
  • 接口中的方法都是公有的。

4.2 接口的使用步骤

  1. 接口不能直接使用,必须有一个“实现类”来“实现”该接口。
    格式:
public class 实现类名称 implements 接口名称 {
    // ...
}
  1. 接口的实现类必须覆盖重写(override)接口中所有的抽象方法。

  2. 创建实现类的对象,进行使用。

4.3 接口中抽象(abstract)方法

在任何版本的Java中,接口都能定义抽象方法。
格式:

public abstract 返回值类型 方法名称(参数列表)//方法体;

注意事项:

  1. 接口当中的抽象方法,修饰符必须是两个固定的关键字:public abstract
  2. public abstract关键字修饰符可以选择性地省略。
  3. 方法的三要素,可以随意定义。
  4. 实现类并没有覆盖重写接口中所有的抽象方法,那么这个实现类自己就必须是抽象类。
public interface MyInterfaceAbstract {

    // 这是一个抽象方法
    public abstract void methodAbs1();

    // 这也是抽象方法
    abstract void methodAbs2();

    // 这也是抽象方法
    public void methodAbs3();

    // 这也是抽象方法
    void methodAbs4();
}

4.4 接口中的默认(default)方法

从Java 7开始,接口当中允许定义Default方法。

接口中使用的默认方法,可以解决接口升级的问题。默认方法,实现类可以不重写。

格式:

public default 返回值类型 方法名称(参数列表) {
    方法体
}

注:

  1. 关键字public 修饰符可以选择性地省略。
  2. 接口的默认方法,可以通过接口实现类对象,直接调用。
  3. 接口的默认方法,也可以被接口实现类进行覆盖重写。
    public interface MyInterfaceDefault {
        // 抽象方法
        public abstract void methodAbstract();

        // 默认方法
        public default void methodDefault() {
            System.out.println("默认方法");
        }
}

4.5 接口中的私有(private)方法

从Java 9开始,接口当中允许定义私有方法。

  1. 普通私有方法,解决多个默认方法之间重复代码问题

格式:

private 返回值类型 方法名称(参数列表) {
 方法体
}
  1. 静态私有方法,解决多个静态方法之间重复代码问题
    格式:
private static 返回值类型 方法名称(参数列表) {
 方法体
}
public interface MyInterfacePrivate {
    public default void methodDefault1() {
        System.out.println("默认方法1");
        methodDefaultCommon();
    }

    public default void methodDefault2() {
        System.out.println("默认方法2");
        methodDefaultCommon();
    }

    public static void methodStatic1() {
        System.out.println("静态方法1");
        methodStaticCommon();
    }

   public static void methodStatic2() {
        System.out.println("静态方法2");
        methodStaticCommon();
    }

    private static void methodStaticCommon() {
        System.out.println("AAA");
        System.out.println("BBB");
        System.out.println("CCC");
    }

    private void methodDefaultCommon() {
        System.out.println("AAA");
        System.out.println("BBB");
        System.out.println("CCC");
    }
}

4.6 接口中的静态(static)方法

从Java 8开始,接口当中允许定义静态方法。
格式:

public static 返回值类型 方法名称(参数列表) {
    方法体
}

注 :

  1. 关键字public 修饰符可以选择性地省略。
  2. 静态方法的调用通过接口名称直接调用,而不是使用实现类的对象进行调用,因为静态和对象没关系。
public interface MyInterfaceStatic {
    public static void methodStatic() {
        System.out.println("这是接口的静态方法!");
    }
}

4.7 接口中的常量的定义和使用

接口当中也可以定义**“成员变量”**,但是必须使用public static final三个关键字进行修饰。
从效果上看,这其实就是接口的【常量】。
格式:

public static final 数据类型 常量名称 = 数据值;

// public static final 都可以省略

数据类型 常量名称 = 数据值;

备注:
一旦使用final关键字进行修饰,说明不可改变。

备注:
一旦使用final关键字进行修饰,说明不可改变。

注意事项:

  1. 接口当中的常量,可以省略public static final,注意:其实都可以省略。
  2. 接口当中的常量,必须进行赋值;不能不赋值。
  3. 接口中常量的名称,使用完全大写的字母,用下划线进行分隔。(推荐命名规则)
  4. 调用常量时使用接口名称去调用。

4.8 接口中方法的总结

  1. 成员变量其实是常量,格式:
[public] [static] [final] 数据类型 常量名称 = 数据值;

注意:
常量必须进行赋值,而且一旦赋值不能改变。
常量名称完全大写,用下划线进行分隔。

注意:
常量必须进行赋值,而且一旦赋值不能改变。
常量名称完全大写,用下划线进行分隔。

  1. 接口中最重要的就是抽象方法,格式:
[public] [abstract] 返回值类型 方法名称(参数列表);

注意:实现类必须覆盖重写接口所有的抽象方法,除非实现类是抽象类。

注意:实现类必须覆盖重写接口所有的抽象方法,除非实现类是抽象类。

  1. 从Java 8开始,接口里允许定义默认方法,格式:
[public] default 返回值类型 方法名称(参数列表) { 方法体 }

注意:默认方法也可以被覆盖重写

注意:默认方法也可以被覆盖重写

  1. 从Java 8开始,接口里允许定义静态方法,格式:
[public] static 返回值类型 方法名称(参数列表) { 方法体 }

注意:应该通过接口名称进行调用,不能通过实现类对象调用接口静态方法

注意:应该通过接口名称进行调用,不能通过实现类对象调用接口静态方法

  1. 从Java 9开始,接口里允许定义私有很乏,格式:
    普通私有方法:
private 返回值类型 方法名称(参数列表) { 方法体 }

静态私有方法:

private static 返回值类型 方法名称(参数列表) { 方法体 }

注意:private的方法只有接口自己才能调用,不能被实现类或别人使用。

4.9 类的继承和实现接口的问题

类的继承关系是单继承的,但类可同时实现多个接口。

使用接口时的注意事项:

  1. 接口是没有者构造方法的。
  2. 一个类的直接父类是唯一的,但是一个类可以同时实现多个接口。
  3. 实现类所实现的多个接口当中,存在重复的抽象方法,只需要覆盖重写一次即可。
  4. 实现类没有覆盖重写所有接口当中的所有抽象方法,实现类就必须是一个抽象类。
  5. 实现类实现的多个接口当中,存在重复的默认方法,实现类一定要对冲突的默认方法进行覆盖重写。
  6. 一个类如果直接父类当中的方法和接口当中的默认方法产生了冲突,优先用父类当中的方法。

4.10 接口之间的多继承

在Java中,类的多继承是不合法,但接口允许多继承。

在接口的多继承中extends关键字只需要使用一次,在其后跟着继承接口。

格式:

public class MyInterfaceImpl implements MyInterfaceA, MyInterfaceB {
    // 覆盖重写所有抽象方法
}

注意事项:

  1. 多个父接口当中的抽象方法如果重复,由于实现类中必重写抽象方法,说以没影响。
  2. 多个父接口当中的默认方法如果重复,那么子接口必须进行默认方法的覆盖重写。

综合案例

  • 问题:

    笔记本电脑(laptop)通常具备使用USB设备的功能。在生产时,笔记本都预留了可以插入USB设备的USB接口,但具体是什么USB设备,笔记本厂商并不关心,只要符合USB规格的设备都可以。定义USB接口,具备最基本的开启功能和关闭功能。鼠标和键盘要想能在电脑上使用,那么鼠标和键盘也必须遵守USB规范,实现USB接口,否则鼠标和键盘的生产出来也无法使用。

  • 分析:

    • USB接口,包含开启功能、关闭功能
    • 笔记本类,包含运行功能、关机功能、使用USB设备功能
    • 鼠标类,要实现USB接口,并具备点击的方法
    • 键盘类,要实现USB接口,具备敲击的方法
    // Usb接口
    
    public interface Usb {
    
        public abstract void on();
    
        public abstract void off();
    }
    
    
    // Mouse 鼠标类
    public class Mouse implements Usb {
        @Override
        public void on() {
            System.out.println("开启鼠标");
        }
    
        @Override
        public void off() {
            System.out.println("关闭鼠标");
        }
    
        public void click() {
            System.out.println("鼠标点击");
        }
    }
    
    
    
    // 键盘类
    public class Keboard implements Usb {
    
        @Override
        public void on() {
            System.out.println("开启键盘");
        }
    
        @Override
        public void off() {
            System.out.println("关闭键盘");
        }
    
        public void inputs() {
            System.out.println("键盘输出");
        }
    }
    
    // 电脑类
    public class Computer {
        public void powerOn() {
            System.out.println("开启电脑");
        }
    
        public void powerOff() {
            System.out.println("关闭电脑");
        }
    
        public void useDevice(Usb usb) {
            // 开启Usb设备
            usb.on();
    
            // 使用Usb设备
            if (usb instanceof Keboard) {
                // 向下转换为键盘
                Keboard keboard = (Keboard) usb;
                keboard.inputs();
            } else if (usb instanceof Mouse) {
                // 向下转换为鼠标
                Mouse mouse = (Mouse) usb;
                mouse.click();
            }
    
            // 关闭usb设备
            usb.off();
        }
    }
    
    // 测试
    
    public class DemoMain {
        public static void main(String[] args) {
            Computer computer = new Computer();
            // 开启电脑
            computer.powerOn();
    
            // 向上转换出一个use
            Usb usb = new Mouse();
            computer.useDevice(usb);
    
    
            // 直接传入一个usb设备
            Keboard keboard = new Keboard();
            computer.useDevice(keboard);  // 掉用过程: 参数传过去先向上转换, 再向下转换。
            // 关闭电脑
            computer.powerOff();
        }
    }
    
    

你可能感兴趣的:(Java,入门学习,面向对象)