Java--接口和内部类

接口

接口是一种引用数据类型,语法和抽象类类似,只不过需要把abstract换成interface
接口的命名一般以I开头

//语法格式
public interface 接口名称 {
}

接口的使用

接口不可以直接使用,需要有一个类来实现这个接口,并把接口内的抽象方法全部重写。

//接口
public interface IRun {
    void run();
}

//类(实现接口)
public class Dog implements IRun{
    @Override
    public void run() {
        System.out.println("狗在跑");
    }
}

类和类之间是继承关系(extends),类和接口之间是实现(implements)

接口的特性

  • 接口是引用类型,但并不可以直接进行实例化(new 接口)。
  • 接口中的每一个方法都是被public abstract修饰,(我们实际编写代码的时候可以不用加上,接口方法会被隐式指定为public abstract,这样也就保持了我们代码的简洁性)
  • 接口中的方法并不会在接口中有实现,而在实现接口的类中实现。
  • 接口中不可以有被实现的方法,等同于只可以有抽象方法,但是两个除外(static和default修饰的)
  • 接口中可以有成员变量,只不过要被public static final修饰,编写代码的时候可以省略修饰,变量也会被隐式指定为public static final。
  • 重写接口方法的时候,不可以用default来修饰。(子类的访问权限要大于等于父类)
  • 接口中不可以有代码块和构造方法。
  • 如果类没有实现接口的抽象方法,那么类也要设置为抽象类。

多继承问题

  • 在Java中没有多继承,但通过接口却可以解决这个问题。我们来进一步理解一下extends和implements。
//意思为,狗是一个动物具有跑的功能
//!!!当extends和implements同时出现的时候,extends必须在前面
public class Dog extends Animal implements IRun
  • 类不可以多继承,但一个类可以实现多个接口
public class Dog extends Animal implements IRun,IEat
  • 类和接口之间为Implements,接口和接口之间要用extends,并且接口之间支持多继承
interface IAmphibious extends IRunning, ISwimming {
}

对象类型进行比较

对于一般类型我们都可以比较出来大小,但我们如何对一个对象进行比较呢?下面我将来讲述两种方法。

1.如果对象对应的类可以进行比较,它需要实现接口Comparable(用来告诉我们这个类可以进行比较),然后我们需要重写实现的接口中的compareTo方法,然后在main方法中通过调用compareTo方法进行比较。

//Comparable<Student>这个里面尖括号括起来的部分称为泛型,表示可以进行比较的类
class Student implements Comparable<Student>{
    String name;
    int age;
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    //这重写了compareTo,这里我编写的逻辑是根据年龄
    //s.compareTo(s1)===>s调用了方法,s就代表this,s1作为参数传过来,所以s1等同于下面的o
    @Override
    public int compareTo(Student o) {
        //根据年龄比较
        if (this.age > o.age) {
            return this.age - o.age;
        } else {
            return o.age - this.age;
        }
    }
}
public class JavaSE731 {
    public static void main(String[] args) {
        Student student = new Student("lisi",18);
        Student student1 = new Student("zhangsan",22);
        System.out.println(student.compareTo(student1));
    }
}

通过方法一我们可以对对象进行比较,但是有个不好的地方是,我们这次想根据年龄比较,然后把代码写死在了compareTo中,无论什么时候调用,他都是根据年龄去比较,但我们想根据姓名比较的时候呢?我们还需要再去compareTo代码中进行修改,很不方便。所以更推荐方法二。
2.通过比较器来比较。

import java.util.Comparator;

class Student {
    String name;
    int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
class AgeCompare implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.age - o2.age;
    }
}
public class JavaSE731 {
    public static void main(String[] args) {
        Student student = new Student("zs",14);
        Student student1 = new Student("ws",22);
        AgeCompare ageCompare = new AgeCompare();
        System.out.println(ageCompare.compare(student,student1));
    }
}

我们通过年龄比较,就可以创建一个类实现接口Comparator接口,后面的泛型填写要比较的那个类。然后重写compare方法,在里面写上比较方法,在main函数中创建创建这个类的对象,将要比较的两个对象传过去。这种方法很灵活。

深拷贝和浅拷贝

Java提供了一个方法clone实现对象的克隆,但是要想调用这个方法必须得实现接口Clonable,否则就会报不支持克隆的异常。那么什么是深拷贝?什么是浅拷贝?

浅拷贝

class Ban {
    int num = 19;

    @Override
    public String toString() {
        return "Ban{" +
                "num=" + num +
                '}';
    }
}
//浅拷贝
class Student implements Cloneable{
    String name;
    int age;
    Ban ban = new Ban();

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

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                "," + ban +
                '}';
    }
}
public class JavaSE731 {
    public static void main(String[] args) throws CloneNotSupportedException{
        Student student = new Student("zs",14);
        Student student1 = (Student) student.clone();
        student.ban.num = 99;
        System.out.println(student);
        System.out.println(student1);
    }
}

这里需要注意很多易错点:
1.对于要克隆的类必须得实现Cloneable接口,否则会报错(不支持克隆的错误)。
2.我们实现接口后,要将接口中方法clone重写,我们直接输入clone,Idea会给我们提供。此时我们并不需要对Idea写好的clone进行修改。
3.上面我在main中调用的clone方法,所以我们需要在main方法上抛出异常(这部分先照做,后面异常的时候再说为啥)。
4.调用完克隆方法后,我们需要将类型转为我们对应的类,因为clone()返回值为Object类型。
为了方便理解,请欣赏下图。
在这里插入图片描述
上面代码中可以看出是一个对象中有另外一个对象,但是浅拷贝就只会拷贝一层,比方说上面部分,并不会再创建一个ban对象,而是将地址直接拷贝过去了,这也正是浅拷贝。

深拷贝

深拷贝则与上面不同代码如下:

class Ban implements Cloneable{
    int num = 19;

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

    @Override
    public String toString() {
        return "Ban{" +
                "num=" + num +
                '}';
    }
}
//深拷贝
class Student implements Cloneable{
    String name;
    int age;
    Ban ban = new Ban();

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Student student = (Student) super.clone();
        student.ban = (Ban) this.ban.clone();
        return student;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                "," + ban +
                '}';
    }
}
public class JavaSE731 {
    public static void main(String[] args) throws CloneNotSupportedException{
        Student student = new Student("zs",14);
        Student student1 = (Student) student.clone();
        student.ban.num = 99;
        System.out.println(student);
        System.out.println(student1);
    }
}

根据上面代码我们可以看出深拷贝的时候我们对Student的克隆方法进行了修改,并且将Ban类也实现了Cloneable的接口,表示可以进行克隆,并且重写了clone方法。
在这里插入图片描述

接口和抽象类区别

核心区别在于抽象类允许存在普通成员方法和成员变量,但是接口只允许存在抽象方法和常量。
在这里插入图片描述

Object类

Object类是最高的类,所有类的父类,任何对象都可以用Object接收。同时Object提供了很多方法。比方说toString,equals,hascode方法。当然提供的方法可能我们并不适用,这就需要我们重写这个方法,比方说提供的equals方法,对于引用类型而言,比较的就是地址。这就需要我们根据需要去更改。String类就重写了这个方法,方便字符串之间进行内容的比较。

内部类

内部类包括:实例内部类,静态内部类,局部内部类,匿名内部类

实例内部类

没有被static修饰的成员内部类

public class JavaSE731 {
    class Student {
        
    }
}
  1. 在实例内部类中可以访问外部类中的任意访问权限的成员
  2. 在内部类中访问的时候,如果外部类和内部类中有相同名称的成员,则会优先访问内部类自己的
  3. 在内部类中访问的时候,如果要访问外部类同名成员时候,必须:外部类名称.this.同名成员名字
  4. 要想访问实例类中的成员,必须先实例化内部类,实例化的语法如下:
OutClass.InnerClass innerClass1 = new OutClass().new InnerClass();
  1. 实例内部类的非静态方法中包含了外部类的引用。
  2. 实例内部类有两个引用:this表示内部类自己的,OuterClass.this表示外部类的
  3. 创建内部类对象时,必须先创建外部类对象。
  4. 实例内部类也受访问修饰符的限定。

静态内部类

被static修饰的成员内部类称为静态内部类

public class JavaSE731 {
    static class Student {

    }
}
  1. 在静态内部类中只可以访问外部类的静态成员。
  2. 创建内部类对象时不需要创建外部类
  3. 静态内部类对象的创建:
OutClass.InnerClass innerClass = new OutClass.InnerClass();

局部内部类

定义在外部类的方法内部或者{}内部的类叫做局部内部类。这个类只可以在定义位置使用。(很少使用)

public class JavaSE731 {
    public void func() {
        class A {
            
        }
    }
}
  1. 局部内部类不可以被public,static修饰
  2. 只可以在方法内部使用

匿名内部类

创建匿名内部类缩减了我们子类继承父类,再重写父类方法,再创建子类对象的过程,直接一步到位了。

父类 对象 = new 父类(){   重写父类中的方法   }class Father {
    public void test() {
        System.out.println("父类");
    }
}
public class JavaSE731 {
    public static void main(String[] args) {
        Father father = new Father(){
            @Override
            public void test() {
                super.test();
            }
        };
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值