Java初阶(抽象类 + 接口)

一、抽象类

1.1 什么是抽象类

当无法准确地描述该方法/类的内容时,就可以用abstract修饰
没有实际工作的方法(无法准确描述), 我们可以把它设计成一个 抽象方法,包含抽象方法的类我们称为抽象类

1.2 抽象类的注意点

  • 抽象类和抽象方法都是依靠abstract进行修饰的
abstract class Father{   //抽象类
    abstract public void func();    //抽象方法
}
  • 抽象类不能进行实例化(因为抽象类自己本身无法准确描述一个类),但是普通类可以
abstract class Father{   //抽象类
    abstract public void func();    //抽象方法
}
public class Test1{
    public static void main(String[] args) {
        //Father father = new Father();     会报错
    }
}
  • 抽象类当中,不一样包含抽象方法,但是包含抽象方法的类,一定是抽象类
    • 抽象类当中可以存在构造方法,在子类实例化的时候,会帮助父类的成员进行初始化
    • 抽象类当中,可以定义成员变量和成员方法
  • 当一个普通类,继承我们的抽象类了,此时在普通类当中一定要重写抽象类的抽象方法(不重写会报错,所以抽象类最大的意义是为了被继承)
  • 当一个抽象类A继承一个抽象类B,此时抽象类A不需要重写抽象类B中的成员。但是当一个普通类C,继承了抽象类A,此时就需要重写所有没有被重写的抽象方法
  • 一定要满足重写的要求,所以final 关键字不可能同时作用在一个方法或者类上

1.3 抽象类的作用

  • 抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类. 然后让子类重写抽象类中的抽象方法,从而可以达到多态的效果
  • 使用抽象类的场景实际工作不应该由父类完成, 而应由子类完成. 那么此时如果不小心误用成父类了, 使用普通类编译器是不会报错的. 但是父类是抽象类就会在实例化的时候提示错误, 让我们尽早发现问题,即可以进行一次编译器的检验

二、接口

2.1 接口的基础概念

(1)什么是接口

生活中,我们有许许多多的接口,插座插头、USB接口……因为口径尺寸相同,是个标准的规范,我们可以很方便地去使用。

类比一下

接口其实就是一个对标准的规范,可以算抽象类的进一步抽象

(2)如何使用接口

语法格式:interface 接口名称;
      接口的定义格式与定义类的格式基本相同,将class关键字换成 interface 关键字,就定义了一个接口

写法上的注意点

interface Ishape{    //接口名推荐以I来开头
    public abstract void draw();    //public abstract这块是灰色,即接口中的方法默认是public abstract的
//阿里编码规范中约定, 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性.

    //接口当中有不能实现的方法,两种例外
    public static void eat(){          //静态方法可以有具体的实现
        System.out.println("中午吃什么?");
    }

    default public void drink(){     //这个方法被 default 关键词修饰,表示这个方法默认就是这样的
        System.out.println("中午喝什么?");
    }



    int a = 1;
    public static int b = 1;     //public static 是灰色
    public static final int c = 1;   //public static final 是灰色,
                                    // 即接口当中的成员变量默认是public static final修饰的
}

public class Demo {
    public static void main(String[] args) {
        //Ishape shape = new Ishape();       会报错,接口不能通过关键词new来初始化
    }
}

子类实现接口的方法的时候,这个方法一定要是public修饰的
接口当中,不能有构造方法和代码块

示例代码

interface IShape{
    void draw();
    public static void eat(){

    }
}


class Cycle implements IShape {    //类和接口之间,使用关键词implements进行关联
    //重写父类的方法
    //当一个类实现一个接口之后,这个类必须重写这个接口当中的抽象方法
    @Override
    public void draw() {
        System.out.println("○");
    }


    //当接口当中,存在default方法,可以选择重写,也可以选择不重写,具体看业务需求
    public void eat(){
        System.out.println("在重写这个default修饰的方法");
    }

}

class Rect implements IShape {
    @Override
    public void draw() {
        System.out.println("▭");
    }
}
public class Demo{

    //可以实现多态
    public static void drawMap(IShape iShape){
        iShape.draw();
    }
    public static void main(String[] args) {
        IShape iShape = new Cycle();    //接口虽然不能实例化自己,但可以实例化其子类,这个过程也是向上转型
        
        drawMap(new Cycle());
        drawMap(new Rect());     //ps.接口有自己独立的字节码文件
    }
}

在这里插入图片描述

2.2 实现多个接口

意义:可以实现多继承,不是所有的子类都可以

interface IFlying{
    void fly();
}

interface ISwimming{
    void swim();
}

interface IRunning{
    void run();
}

abstract class Animal{
    String name;
    int age;

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

    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 abstract void eat();
}

class Dog extends Animal implements ISwimming, IRunning{   //先继承类,再关联接口,不能改变顺序
    public Dog(String name, int age) {          //这里接口的意义,不是所有的动物都会飞
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println(this.name + "正在吃狗粮");
    }

    @Override
    public void swim() {
        System.out.println(this.name + "正在河里游泳");
    }

    @Override
    public void run() {
        System.out.println(this.name + "正在遛人");
    }
}

class Bird extends Animal implements IFlying, IRunning{
    public Bird(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println(this.name + "正在吃鸟粮");
    }

    @Override
    public void fly() {
        System.out.println(this.name + "正在吃天空飞翔");

    }

    @Override
    public void run() {
        System.out.println(this.name + "正在吃地上蹦蹦跳跳");
    }
}

public class Main {

    public static void testAnimal(Animal animal){
        animal.eat();
    }

    public static void testRun(IRunning run){
        run.run();
    }
    public static void main(String[] args) {
        testAnimal(new Bird("小鸟",1));
        testRun(new Dog("小狗",2));
    }
}

在这里插入图片描述

2.3 接口的继承

interface A {
    void testA();
}

interface B {
    void testB();
}

//接口和接口之间也存在关联,通过extends来进行关联,此时认为这个意思是扩展功能!
interface C extends A,B {   //此时的C接口,不仅仅有自己的功能,还有A接口和B接口的功能,
    void testC();  //可以使得代码的耦合更低,不会改变一个代码,会影响另一个代码,彼此之间相互独立
}
class D implements C {

    @Override
    public void testA() {

    }

    @Override
    public void testB() {

    }

    @Override
    public void testC() {

    }
}

2.4 常用接口实例

(1)Comparable 接口 ------- 排序

当我们对自定义类型进行比较的时候,一定要实现可比较的接口

  • Comparable 接口 里面有一个抽象方法是compareTo
  • 自定义类型中我们如果要排序,需要关联上这个接口,再重写compareTo方法
public class Person implements Comparable<Person>{
    String name;
    int age;

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

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

    @Override
    public int compareTo(Person o) {
        return o.age - this.age;
    }
}

public class Main{
    public static void main(String[] args) {
        Person[] person = new Person[3];
        person[0] = new Person("张三",21);
        person[1] = new Person("李四",8);
        person[2] = new Person("王五",76);
        System.out.println("排序前:"+ Arrays.toString(person));
        Arrays.sort(person);
        System.out.println("排序后:"+ Arrays.toString(person));
    }
}

在这里插入图片描述

自写排序

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

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

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

    @Override
    public int compareTo(Person o) {
        return this.age - o.age;
    }
}

public class Main{
    public static void main(String[] args) {
        Person[] person = new Person[3];
        person[0] = new Person("张三",21);
        person[1] = new Person("李四",8);
        person[2] = new Person("王五",76);
        System.out.println("排序前:"+ Arrays.toString(person));
        bubberSort(person);
        System.out.println("排序后:"+ Arrays.toString(person));
    }

    public static void bubberSort(Comparable[] comparables){
        for (int i = 0; i < comparables.length - 1; i++) {
            for (int j = 0; j < comparables.length - 1 - i; j++) {
                if (comparables[j].compareTo(comparables[j + 1]) > 0){
                    Comparable tmp =  comparables[j];
                    comparables[j] = comparables[j+1];
                    comparables[j+1] = tmp;
                }
            }
        }
    }
}

(2)Compactor 接口 ------- 排序

使用场景: Compactor 的 compareTo 把排序的对象写死了,即无法通过 name 排序,相比于 compareTo , Compactor 更加的灵活,可以从不同条件来进行排序

class Student {
    public String name;
    public int age;
    public double score;

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}
class AgeComparator implements Comparator<Student> {

    @Override
    public int compare(Student o1, Student o2) {
        return o1.age - o2.age;
    }
}
class ScoreComparator implements Comparator<Student> {

    public int compare(Student o1, Student o2) {
        return (int)(o1.score - o2.score);
    }
}
public class Test {
    public static void main(String[] args) {
        Student student1 = new Student("zhangsan",19,30.9);
        Student student2 = new Student("lisi",9,80.9);

        AgeComparator ageComparator = new AgeComparator();
        int ret = ageComparator.compare(student1,student2);
        System.out.println(ret);
        System.out.println("+++++++++++++++++++++");

        ScoreComparator scoreComparator = new ScoreComparator();
        int ret2 = scoreComparator.compare(student1,student2);
        System.out.println(ret2);
    }
}

在这里插入图片描述

(3)Cloneable 接口 ------- 拷贝

class Stu implements Cloneable{     //标记接口,里面什么都没有,关联了该接口,证明该类是可以被克隆的
    private String name;
    private int age;

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

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

    @Override
    protected Object clone() throws CloneNotSupportedException {   //处理异常问题
        return super.clone();
    }
}
public class Clone {                                  //处理异常问题
    public static void main(String[] args)  throws CloneNotSupportedException{
        Stu student1 = new Stu("aaa", 19);
        Stu student2 = (Stu)student1.clone();   //clone返回的方法是Object类的,所以要强转
        System.out.println(student1);
        System.out.println(student2);
    }
}

在这里插入图片描述

❤️浅拷贝 VS 深拷贝

拷贝的时候,能把这个对象里的所有内容都拷贝过来,则是深拷贝。否则,是浅拷贝

下面这个是浅拷贝

class Money{
    public int money;
}

class Stu implements Cloneable{
    private String name;
    private int age;
    Money m = new Money();

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

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class Clone {
    public static void main(String[] args)  throws CloneNotSupportedException{
        Stu student1 = new Stu("aaa", 19);
        Stu student2 = (Stu)student1.clone();
        student1.m.money = 19;
        System.out.println(student1.m.money);
        System.out.println(student2.m.money);
        System.out.println();
        student1.m.money = 29;
        System.out.println(student1.m.money);
        System.out.println(student2.m.money);
    }
}

在这里插入图片描述
在这里插入图片描述

下面是改成深拷贝的情况
核心思想:每个对象都要拷贝

class Money implements Cloneable{
    public int money;

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

class Stu implements Cloneable{
    private String name;
    private int age;
    Money m = new Money();

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

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Stu tmp = (Stu)super.clone();
        tmp.m = (Money) this.m.clone();
        return tmp;
    }
}
public class Clone {
    public static void main(String[] args)  throws CloneNotSupportedException{
        Stu student1 = new Stu("aaa", 19);
        Stu student2 = (Stu)student1.clone();
        student1.m.money = 19;
        System.out.println(student1.m.money);
        System.out.println(student2.m.money);
        System.out.println();
        student1.m.money = 29;
        System.out.println(student1.m.money);
        System.out.println(student2.m.money);
    }
}

在这里插入图片描述
在这里插入图片描述

三、Object类

1.概念

可以认为Object这个类是所有类的父类,那怕你没有显示地继承这个类
Java当中只能同时继承一个类

在 Java 中,只有基本类型 ( primitive types) 不是对象, 例如,数值、 字符和布尔类型的值都不是对象。
所有的数组类塱,不管是对象数组还是基本类型的数组都扩展了 Object 类

2.使用
Object 中的方法
在这里插入图片描述

1.引用任何实际的对象

Object obj = new EmployeeC’Harry Hacker", 35000);
当然, Object 类型的变量只能用于作为各种值的通用持有者。要想对其中的内容进行具体的操作, 还需要清楚对象的原始类型, 并进行相应的类型转换:
Employee e = (Employee) obj ;

3.1 toString() ---------- 返回表示对象值的字符串

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

//Object 类定义了 toString 方法, 用来打印输出对象所属的类名和散列码
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
  1. 绝大多数(但不是全部)的 toString方法都遵循这样的格式:类的名字,随后是一对方括号
  2. 随处可见 toString方法的主要原因是:只要对象与一个字符串通过操作符“
    +” 连接起来,Java 编译就会自动地调用 toString方法,以便获得这个对象的字符串描述
  3. 数组继承了Object中的toString()方法,数组类型将按照旧的格式打印。修正的方式是调用静态方法 Arrays.toString()。
    要想打印多维数组(即, 数组的数组)则需要调用 Arrays.deepToString 方法

3.2 对象比较equals方法

比较对象内容时,一定要根据实际,重写equals方法

// Object类中的equals()方法实现:
 public boolean equals(Object obj) {
     return (this == obj);
 }

3.3 hashcode方法 --------->算了一个具体的对象位置

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

// Object类中的hashcode()方法实现:
public native int hashCode();
//native表示这个方法,底层是由C/C++代码写的。我们看不到

假如我们认为两个名字相同,年龄相同的对象,将存储在同一个位置,并要实现这个效果

class Person {
    public String name;
    public int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
public class TestDemo4 {
    public static void main(String[] args) {
        Person per1 = new Person("gaobo", 20) ;
        Person per2 = new Person("gaobo", 20) ;
        System.out.println(per1.hashCode());
        System.out.println(per2.hashCode());     //执行结果一样
    }
}

equals方法 和 hashcode方法都可以用快捷键重写

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值