抽象类和接口以及Object类

抽象类

在面对对象的概念中,所有对象的创建都是通过类来创建的。但是并不是所有的类都会创建对应的对象。如果一个类中没有足够的数据创建一个对象,这样的类就是抽象类。

如同经常使用的Animal动物类,Dog类,Cat类,Dog类和Cat类都是继承于Animal类,但是Animal类中没有足够的数据去创建Animal动物,所以我们就可以将Animal设置为抽象类。

在日常书写代码中,我们发现创建动物类时,会给里面定义Eat方法,并且这个方法中也不进行具体。我们将这种方法中不实现任何功能的方法设置为抽象方法,包含抽象方法的类称为抽象类。

抽象类的基本语法

抽象类也是类,内部可以包含普通方法和属性,构造方法

抽象类的特点

 不能实例化对象

 抽象方法不能设置为private

 

 抽象方法不能被final和static修饰,因为抽象类中的方法需要被重写

 抽象类必需被继承,并且继承后的子类一定要重写父类中的抽象方法,否则子类也是抽象方法。抽象类一定要用abstract修饰。

abstract class Animal {
    //抽象方法:被abstract修饰的方法,没有方法体
     abstract public void Eat();
    //抽象类也是类,也可以增加普通方法和属性
     abstract public void Run();
    protected int  age;//年龄
    }
  class Dog extends Animal{
      public void Eat(){
          System.out.println("吃骨头");
      }
      public  void  Run(){
          System.out.println("用腿跑");
      }
}

 抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类。

 抽象类中可以有构造方法,供子类创建对象时,初始化父类的成员变量。

抽象类的作用

使用抽象类相当于多了一重编译器的校验.
使用抽象类的场景就如上面的代码,实际工作都不通过父类完成,而通过子类完成.那么此时如果不小心误用成父类了,使用普通类编译器是不会报错的.但是父类是抽象类就会在实例化的时候提示错误,让我们尽早发现问题。
很多语法存在的意义都是为了"预防出错",例如我们曾经用过的final也是类似.创建的变量用户不去修改,不就相当于常量嘛?但是加上final能够在不小心误修改的时候,让编译器及时提醒我们。
充分利用编译器的校验,在实际开发中是非常有意义的.

接口

  接口的概念

  接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用。在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型。

  接口的基本语法

  接口的定义和类的定义格式基本相同,将class关键词换成interface关键字

    public interface接口名称{
                   //抽象方法
                   public abstract void method1(); // public abstract是固定搭配,可以不写
                   public void  method2();
                   abstract void method3();
                   void method4();
                   //在接口中上述写法都是抽象方法,跟推荐方式4,代码更简洁
}

创建接口时,接口的命名一般以大写字母开头
接口的命名一般使用"形容词"词性的单词
接口中的方法和属性不要加任何修饰符号,保持代码的简洁性

 接口的使用

接口不能直接使用,必须要有一个实现类来实现该接口,实现接口中的抽象方法。
public class  类名称  implements  接口名称{
}

子类和父类之间是extends继承关系,类与接口之间是implents实现关系。

如请实现

笔记本电脑使用USB鼠标、USB键盘的例子

        USB接口:包含打开设备、关闭设备功能
笔记本类:包含开机功能、关机功能、使用USB设备功能

鼠标类:实现USB接口,并具备点击功能
键盘类:实现USB接口,并具备输入功能


//USB接口
 interface USB {
    void openDevice();
    void closeDevice();
}

//鼠标类,实现USB接口
 class Mouse implements USB{
    @Override
    public void openDevice() {
        System.out.println("打开鼠标");
    }
    @Override
    public void closeDevice(){
        System.out.println("关闭鼠标");
    }
    public void click(){
        System.out.println("鼠标点击");
    }
}

//键盘类,实现USB接口
 class KeyBoard implements USB{
    @Override
    public void openDevice() {
    System.out.println("打开键盘");
    }
    @Override
    public void closeDevice() {
        System.out.println("关闭键盘");
    }
    public void inPut(){
        System.out.println("键盘输入");
    }
}

//笔记本类:使用USB设备
 class Computer {
    public void powerOn(){
    System.out.println("打开笔记本电脑");
    }
    public void powerOff(){
        System.out.println("关闭笔记本电脑");
    }
    public void useDevice(USB usb){
        usb.openDevice();
        if(usb instanceof Mouse){
            Mouse mouse =(Mouse)usb;
            mouse.click();
        }else if(usb instanceof KeyBoard){
            KeyBoard keyBoard = (KeyBoard)usb;
            keyBoard.inPut();
        }
        usb.closeDevice();
    }
}

public class Main {
    public static void main(String[] args) {
        Computer computer = new Computer();
        computer.powerOn();
        //使用鼠标设备
        computer.useDevice(new Mouse());
        //使用键盘设备
        computer.useDevice(new KeyBoard());
        computer.powerOff();
    }
}

类似上述的使用就实现了接口的用法。切记在继承接口时一定不用忘记重写他的所有方法。

  接口特性

  接口类型是一种引用类型,但是不能直接new接口的对象


public class Main {
    public static void main(String[] args) {
        USB usb = new USB();

    }
}

  会发生报错,因为接口是抽象的,所以无法实例化对象。

 接口中每一个方法都是public的抽象方法, 即接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)

 

 interface USB {

    priavte void openDevice();//会发生报错无法解析private
    protected pvoid closeDevice();//和上述情况一样
}

接口中的方法是不能在接口中实现的,只能由实现接口的类来实现

interface USB {

    void openDevice();
    void closeDevice(){//接口中的方法默认为抽象方法不能在其中实现
        System.out.println("...");
    };
}

重写接口中方法时,不能使用默认的访问权限

//USB接口
 interface USB {

    void openDevice();
    void closeDevice();
}

//鼠标类,实现USB接口
 class Mouse implements USB{
    @Override
    void openDevice() {//'Mouse'中的 ‘openDevice()'与'USB'中的 'openDevice()冲突,尝试分配较弱的访问权限(package-private');曾为'public'

        System.out.println("打开鼠标");
    }
    @Override
    public void closeDevice(){
        System.out.println("关闭鼠标");
    }
    public void click(){
        System.out.println("鼠标点击");
    }
}

接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量

interface USB {

    int n = 3;
    void openDevice();
    void closeDevice();
}
public class Main {
    public static void main(String[] args) {

        System.out.println(USB.n);//可以通过类名直接输出,其为静态变量
        USB.n = 2;//不能修改,其被final修饰
    }
}

.接口中不能有静态代码块和构造方法

接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是.class

如果类没有实现接口中的所有的抽象方法,则类必须设置为抽象类

jdk8中:接口中还可以包含default方法

实现多个接口

一个类可以实现多个接口

interface lFlying {
    void fly();
}
interface lRunning {
    void run();
}
interface lSwimming {
    void swim();
}

class Frog implements lRunning, lSwimming {青蛙这个类实现了游泳和跑这两个接口
    private String name ;
    public Frog(String name){
    this.name = name;
}
    @Override
    public void run() {
        System.out.println(this.name+"正在往前跳");
    }
    @Override
    public void swim(){
        System.out.println(this.name +"正在蹬腿游泳");
    }
}

  接口之间也可以继承

   在Java中,类和类之间是单继承的,一个类可以实现多个接口,接口与接口之间可以多继承。即:用接口可以达到 多继承的目的。 

   接口可以继承一个接口, 达到复用的效果. 使用 extends 关键字。

interface lRunning {
    void run();
}
interface lSwimming {
    void swim();
}
interface IAmphibious extends  lRunning,lSwimming{//接口与接口之间的继承,可以达到复用,达到了拼接的效果一个接口有几个接口的特性
    
}

class Frog implements IAmphibious{
    private String name ;
    public Frog(String name){
    this.name = name;
}
    @Override
    public void run() {
        System.out.println(this.name+"正在往前跳");
    }
    @Override
    public void swim(){
        System.out.println(this.name +"正在蹬腿游泳");
    }
}

   案例使用接口

   给对象数组排序

import java.util.Arrays;

class Student {
    private String name;
    private int score;
    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
    @Override
    public String toString(){
            return "[" + this.name + ":" + this.score + "]";
    }
}





    public class Main {

    public static void main(String[] args) {

        Student[] students = new Student[] {
                new Student("张三",95),
                new Student("李四",96),
                new Student("王五",97),
                new Student("赵六",92),};
        Arrays.sort(Student);

    }
}

建立一个学生数组想要通过sort方法来对学生数组进行排序发生报错,其只能对整数类型的数组进行排序,要想实现排序只能通过实现Comparable接口,并实现其中的compareTo方法.

import java.util.Arrays;

class Student implements Comparable{
    private String name;
    private int score;
    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
    @Override
    public String toString(){
            return "[" + this.name + ":" + this.score + "]";
    }

    public int compareTo(Object o){Student s = (Student)o;
        if (this.score > s.score){
            return -1;
        }else if (this.score < s.score) {
            return 1;
        }else {
            return 0;
        }
    }

}


    public class Main {

    public static void main(String[] args) {

        Student[] students = new Student[] {
                new Student("张三",95),
                new Student("李四",96),
                new Student("王五",97),
                new Student("赵六",92)
        };
        Arrays.sort(students);

    }
}

通过在Student类中重写comparTo方法会使Array.sort中会多出来一个这个为Student类的对象比较大小的sort方法。

注 对于 sort 方法来说, 需要传入的数组的每个对象都是 "可比较" 的, 需要具备 compareTo 这样的能力. 通过重写 compareTo 方法的方式, 就可以定义比较规则.

抽象类和接口的区别

抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中 不能包含普通方法, 子类必须重写所有的抽象方法

使用抽象类的意义是为了让编译器更好的校验, 像 Animal 这样的类我们并不会直接使用, 而是使用它的子类. 万一不小心创建了 Animal 的实例, 编译器会及时提醒我们.




 

Object类

Object是Java默认提供的一个类。Java里面除了Object类,所有的类都是存在继承关系的。默认会继承Object父 类。即所有类的对象都可以使用Object的引用进行接收。

案例

class Person{}
class Student{}
public class Main {
    public static void function(Object obj){
        System.out.println(obj);
    }
    
    
    public static void main(String[]args){
        function(new Person());
        function(new Student());
    }
    
}

所以在开发之中,Object类是参数的最高统一类型。但是Object类也存在有定义好的一些方法

toString()方法,equals()方法,hashcode()方法

toString方法

如果要打印对象中的内容,可以直接重写Object类中的toString()方法。

public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

对象比较equals方法

在Java中,==进行比较时:

如果==左右两侧是基本类型变量,比较的是变量中值是否相同

如果==左右两侧是引用类型变量,比较的是引用变量地址是否相同

如果要比较对象中内容,必须重写Object中的equals方法,因为equals方法默认也是按照地址比较的

//object类中的equals方法
public boolean equals(Object obj){
     return (this == obj);//使用引用中的地址直接来进行比较
}

  案例

class Person{
    private String name ;
    private int age;
    public Person(String name, int age){
        this.age = age ;
        this.name = name ;
    }
    
}
public class Main {
    public static void main(String[] args) {
        Person p1 = new Person("李四", 20);
        Person p2 = new Person("李四", 20);
        System.out.println(p1 == p2);
        // 输出false
        System.out.println(p1.equals(p2));
        //输出false
    }
}

因为Person是引用类型所以其比较的是引用变量地址是否相同,equals默认也是比较地址.改正这种错误只能进行重写equals.

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

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false ;
        }
        if(this == obj) {
            return true ;
        }
        //如果不是Person对象 instanceof判断左边是不是右边创建的对象是返回true否则是false
        if (!(obj instanceof Person)) {
            return false;
        }
        Person person = (Person) obj ; // 将传进来的参数重新变为要比较的形式向下转型,比较属性值
        return this.name.equals(person.name) && this.age==person.age ;

    }
}
public class Main {
    public static void main(String[] args) {
        Person p1 = new Person("gaobo", 20);
        Person p2 = new Person("gaobo", 20);
        int a = 10;
        int b = 10;
        System.out.println(a == b);
        //输出true
        System.out.println(p1 == p2);
        // 输出false
        System.out.println(p1.equals(p2));
        //输出false
    }
}

注:比较对象中内容是否相同的时候,一定要重写equals方法。

hashcode方法

我们认为两个名字相同,年龄相同的对象,将存储在同一个位置,如果不重写hashcode()方法,我们可以来看案例

class Person {
    public String name;
    public int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
public class Main{
    public static void main(String[] args) {
        Person per1 = new Person("李四", 20) ;
        Person per2 = new Person("李四", 20) ;
        System.out.println(per1.hashCode());
        System.out.println(per2.hashCode());
    }
}


460141958
1163157884

可以看出两个对象的hash值不一样重写hash方法

import java.util.Objects;

class Person {
    public String name;
    public int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
public class Main{
    public static void main(String[] args) {
        Person per1 = new Person("李四", 20) ;
        Person per2 = new Person("李四", 20) ;
        System.out.println(per1.hashCode());
        System.out.println(per2.hashCode());
    }
}
26104872
26104872

这一次两者的hash值相同因为这一次的hash的值是根据姓名和年龄来判断的。

hashcode方法用来确定对象在内存中存储的位置是否相同

事实上hashCode() 在散列表中才有用,在其它情况下没用。在散列表中hashCode() 的作用是获取对象的 散列码,进而确定该对象在散列表中的位置。

  • 35
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值