接口
接口是一种引用数据类型,语法和抽象类类似,只不过需要把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 {
}
}
- 在实例内部类中可以访问外部类中的任意访问权限的成员
- 在内部类中访问的时候,如果外部类和内部类中有相同名称的成员,则会优先访问内部类自己的
- 在内部类中访问的时候,如果要访问外部类同名成员时候,必须:外部类名称.this.同名成员名字
- 要想访问实例类中的成员,必须先实例化内部类,实例化的语法如下:
OutClass.InnerClass innerClass1 = new OutClass().new InnerClass();
- 实例内部类的非静态方法中包含了外部类的引用。
- 实例内部类有两个引用:this表示内部类自己的,OuterClass.this表示外部类的
- 创建内部类对象时,必须先创建外部类对象。
- 实例内部类也受访问修饰符的限定。
静态内部类
被static修饰的成员内部类称为静态内部类
public class JavaSE731 {
static class Student {
}
}
- 在静态内部类中只可以访问外部类的静态成员。
- 创建内部类对象时不需要创建外部类
- 静态内部类对象的创建:
OutClass.InnerClass innerClass = new OutClass.InnerClass();
局部内部类
定义在外部类的方法内部或者{}内部的类叫做局部内部类。这个类只可以在定义位置使用。(很少使用)
public class JavaSE731 {
public void func() {
class A {
}
}
}
- 局部内部类不可以被public,static修饰
- 只可以在方法内部使用
匿名内部类
创建匿名内部类缩减了我们子类继承父类,再重写父类方法,再创建子类对象的过程,直接一步到位了。
父类 对象 = 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();
}
};
}
}