抽象类和接口

1. 抽象类

1.1 抽象类概念

在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。


 1.2 抽象类语法

抽象类语法:

1. 抽象类使用abstract修饰类

2. 抽象类当中可以包含普通类所能包含的成员。

3. 抽象类和普通类所不一样的是抽象类能够包含抽象方法。 

4. 抽象方法是abstract修饰的,这个方法没有具体的实现。

5. 不能实例化抽象类

6. 抽象类存在的最大意义就是能够被继承

7. 如果一个普通类继承类抽象类,必须从写抽象类中的方法。

8. 如果一个抽象类A继承了抽象类B,此时抽象类A不用重写抽象类B,但如果抽象类A再被普通类继承,就需要重写。

9. 抽象方法不能是私有的。也就是要满足重写的规则。

10. final和abstract是矛盾的,static也不可以,因为不满足重写。

11. 抽象类当中可以有构造方法,目的是为了方便子类能够调用初始化抽象类中的成员。


1.3 抽象类的作用

程序的校验


2. 接口

2.1 接口的概念

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

接口是一种行为的标准/规范。


 2.2 语法规则

1. 使用interface来修饰接口。

2. 接口当中的成员方法,不能有具体的实现。(接口中的方法默认是public abstract)

3.  JDK1.8开始允许有可以实现的方法,但是这个方法只能由default修饰的。

4. 可以实现有一个静态方法

 5. 成员变量默认是public static修饰的

6. 接口无法实例化

 7. 类与接口之间利用implements来实现多个接口,并且需要重写这个接口里的抽象方法。

8. 子类重写抽象方法必须加上public。

9. 接口当中不能有构造代码块和静态代码块

 10. 如果不想在用implements连接的类中重写抽象方法,可以直接将类改为抽象类,如果这个类被其他类继承就必须重写。

 11. 用一个类可以实现多个接口,使用implements+用,隔开。(可以解决多继承问题)

 类似这样:(先继承再实现)

interface InSwim{
    void swim();
}
interface InRun{
    void run();
}
interface InFly{
    void fly();
}
class Animal{
    public String name;
    public int age;
    public Animal(String name,int age){
        this.name=name;
        this.age=age;
    }
}
class Dog extends Animal implements InSwim,InRun{
    public Dog(String name,int age){
        super(name,age);
    }
    public void swim(){
        System.out.println(name+"正在游泳");

    }
    public void run(){
        System.out.println(name+"正在跑");
        
    }

}
interface Animal{
    public void eat();

}
class Cat implements Animal{

    public void eat(){
        System.out.println("吃猫粮");
    }
}
class Dog implements Animal{
    public void eat(){
        System.out.println("吃狗粮");
    }
}
public class Test{
    public static void eat(Animal a){
        a.eat();
    }
    public static void main(String[] args) {
        Cat cat=new Cat();
        Dog dog=new Dog();
        eat(new Cat());
        eat(dog);
    }
}

接口也可以多态。

提示:
1. 创建接口时, 接口的命名一般以大写字母 I 开头.
2. 接口的命名一般使用 "形容词" 词性的单词.
3. 阿里编码规范中约定, 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性. 


2.3 接口间的继承

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


2.4 抽象类和接口的区别

核心区别: 抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中不能包含普通方法, 子类必须重写所有的抽象方法.
如之前写的 Animal 例子. 此处的 Animal 中包含一个 name 这样的属性, 这个属性在任何子类中都是存在的. 因此此处的 Animal 只能作为一个抽象类, 而不应该成为一个接口


3. Object类 

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

范例:使用Object接收所有类的对象


3.2 hashCode

我们看到了hashCode()这个方法,他帮我算了一个具体的对象位置,这里面涉及数据结构,但是我们还没学数据结构,没法讲述,所以我们只能说它是个内存地址。然后调Integer.toHexString()方法,将这个地址以16进制输出。

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        People people = (People) o;
        return age == people.age && Objects.equals(name, people.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

public class Test{
    public static void main(String[] args) {
        People people=new People("张三",15);
        People people1=new People("李四",19);

    }

}

结论:
1、hashcode方法用来确定对象在内存中存储的位置是否相同
2、事实上hashCode() 在散列表中才有用,在其它情况下没用。在散列表hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。


4. 比较两个对象内容的大小

一、Comparable<Student>

1. 当使用Arrays.sout来给实例化对象进行排序时程序将会报错。

2. 为了找到一个能够给实例化对象排序的方法我们可以仿照字符串排序来写

3. 首先写一个字符串排序,按住ctrl打开String类型。

4. 仿照String类型的排序找到字符串比较所必须的接口,并将其与类关联。

 

5. 再按住ctrl打开Comparable打开比较接口,发现这个接口通过compareTo方法进行比较,多以说我们也要重写这个方法。


小知识:按住选中所需方法按住alt+enter即可快速创建该方法所需的东西。

 

 


 6. 首先我们按照年龄进行比较,编写我们的比较标准。

 然后根据年龄比较就完成了

class Student implements Comparable<Student>{
    public String name;
    public int age;

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public int compareTo(Student o) {
        if(this.age>o.age) {
            return 1;
        }
            else if(this.age<o.age){
                return -1;
            }
            else{
                return 0;
        }
    }
}
public class Test {
//    public static void main(String[] args) {
//        String[] arr={"abc","skd","rjvo"};
//        Arrays.sort(arr);
//        System.out.println(Arrays.toString(arr));
//    }
    Student student1 = new Student("张三", 19);
        Student student2 = new Student("李四", 20);
        if (student1.compareTo(student2) < 0) {
            System.out.println("student1<student2");
        } else if (student1.compareTo(student2) > 0) {
            System.out.println("student1>student2");
        } else {
            System.out.println("student1=student2");
        }
    }
    }

总结:如果我们以后自定义的类型一定要记住如果比较大小必须要让这个类具备可以比较的功能,此时可以实现接口Comparable< >。

如果我们这是想要根据姓名排序,却又无妨修改compareTo方式怎么办呢?


二、 Comparator< >

1.创建一个类关联接口按住alt加回车重写compare方法。

 2.然后创建一个新建类的实例化对象,引出Arrays.sout的第二个用法

即可快速完成年龄排序 

3. 如果想要姓名排序仿照上面年龄排序修改compare返回内容即可


5.  Cloneable

要完成一个实例化对象的克隆我们需要使用到Cloneable接口

1. 首先编写一个类和需要克隆的对象,并来连接Cloneable。

 

 2. 现在只是拥有了克隆的资格还要要重写克隆方法。

3.之前讲过Object类中存在克隆方法,所以我们打开Object类查看其中的代码

 4.我们发现,Object是native类型,故clone()方法应该是跨包存在的所以要使用super(),接下来实例化一个新的对象来存储克隆。

 

5. 之后完成强制类型转换,和一些不知名操作即可完成克隆

class Person implements Cloneable{
    public int id;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "person{" +
                "id=" + id +
                '}';
    }

}
public class Test2 {
    public static void main(String[] args)throws  CloneNotSupportedException{
        Person person=new Person();
        Person person1=(Person) person.clone();

    }
}

5.2 深拷贝与浅拷贝

一、 浅拷贝

1. 首先我们创建一个类并在另一类中实例化这个类的对象并存入12.5.

此时再内存中的构造如下图;

 

 2. 先再我们克隆一个对象,再输出他们俩

 等于说现在我们的person1和person2同时指向同一个m

3. 如果我们该片其中一个里面m的值,则另一个的输出也会跟着变

 但是此时我们就无法完成拷贝的初衷,通过改变一个对象同时改变了两个对象。

以上的过程被我们称为浅拷贝。

但是我们的目的是为了将对象m也同时拷贝一遍。

 为此就有了深拷贝。


二、 深拷贝

1. 如果我们按照之前克隆的方法将克隆接口连接到meony类上面呢?

结果还是无法克隆,主要是因为:

protected Object clone() throws CloneNotSupportedException {
    return super.clone();
}

这串定义克隆对象的代码只要求克隆了父类的实例化对象而并没有调用并克隆父类中的m对象,所以着手点在于上面的那串代码。

2. 我们可以设置一个寄存器,将克隆的实例化对象放在里面,然后再将money克隆一份放到tmp.money中,就完成了一份完全的克隆。

 

 3.程序结束tmp被回收tmp中的量回到了person2中。

 4. 然后我们的深拷贝就完成了

class Money implements Cloneable{
    public double m=12.5;
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();

    }
}
class Person implements Cloneable{
    public int id;
    public Money money=new Money();

    @Override
    protected Object clone() throws CloneNotSupportedException {
        //return super.clone();
        Person tmp=(Person) super.clone();
        tmp.money=(Money) this.money.clone();
        return tmp;
    }

    @Override
    public String toString() {
        return "person{" +
                "id=" + id +
                '}';
    }

}
public class Test2 {
    public static void main(String[] args)throws  CloneNotSupportedException{
        Person person=new Person();

        Person person1=(Person) person.clone();
        person1.money.m=1999;
        System.out.println(person.money.m);
        System.out.println("person1:"+person1.money.m);

    }
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值