抽象类和接口

声明: 本文主要作为作者的复习笔记,由于作者水平有限,难免有错误和不准确之处,欢迎读者批评指正.

JDK三大特殊类之Object类

Object类Java所有类默认父类,不需要使用extends关键字明确写出继承Object; 只要使用class关键字声明的类,默认都有一个父类,就是Object!

  1. 由于Object类是所有类的父类,因此Object是参数的最高统一化! 所有类的对象,都可以通过Object引用进行接收;
Object obj = new Animal();
Object obj1 = new String(original:"123");
//包装类的自动装箱问题,等同于new Integer(123);
Object obj2 = 123;
  1. 由于Object类是所有类的父类,则Object类中的所有方法,子类中都有,而且我们可以选择性的覆写其中的方法; 将任意类的对象调用println方法,实际上就是调用的Object类提供的toString进行打印的!
    若想在自己类中输出属性值,而非地址,就可以覆写Object类的toString方法;
  2. JDK赋予了Object可以接收所有引用数据类型的权利,Object还可以接收数组和接口对象; 只剩下基本类型Object无法处理,这就有了包装类(就是将基本类型的数值封装到一个类中);

抽象类和抽象方法的定义与使用

在面向对象的世界中,一切都可以通过类来描述,一切皆对象; 但是某些类比较抽象的概念,没法对应到具体的实体; 若某些类的信息无法具体描绘一个对象,只是作为具体对象的父类,这种类就可以定义为抽象类!

使用关键字abstract定义抽象类; 抽象类是普通类的超集! 只是比普通类多了一些抽象方法([0…N])而已,普通类具备的内容,抽象类都有;

使用关键字abstract定义抽象方法,抽象方法没有方法体,只有方法声明;
例:

public abstract void print();

在Java中,没有方法体的方法一定是抽象方法吗? (错! 还要看关键字abstract!)

public final native Class<?> getClass();

在Java中,native方法也没有方法体,这种方法不是抽象方法; 本地方法是由C++实现的方法,Java只负责调用,方法体实现是C++代码,JVM本身就是C++实现的;

抽象类和抽象方法的使用原则

  1. 抽象类不能直接实例化对象,即便抽象类中没有任何抽象方法,也无法直接实例化对象! 只能通过向上转型进行引用赋值;
  2. 抽象类的子类若是普通类,则必须覆写抽象类中的所有抽象方法! 强制要求子类必须进行方法覆写,保证多态的正常运行; 若子类还是抽象类,可以一个抽象方法都不写;
  3. 抽象方法所在的类必须使用abstract声明为抽象类;
  4. abstract和private不能同时修饰一个方法,因为private子类无法覆写,而abstract必须被子类覆写; final和abstract不能同时出现,因为final没有子类不能覆写,而abstract必须有子类必须被覆写;
  5. abstract和static、final、private不能同时出现!
  6. 抽象方法没有加访问限定符时,默认就是包权限;
  7. 抽象类是普通类的超集,抽象类中仍然能定义构造方法和普通方法! 且仍然满足对象的实例化流程,先调用抽象类的构造方法而后调用子类的构造方法;
  8. 抽象类就是在普通类的基础上进一步提取,只是比普通类多了一些抽象方法而已; 抽象方法的存在要求子类必须进行方法覆写,从而保证运行时多态的正确执行!
  9. 在开发中,遵循接口优先原则,能同时使用抽象类和接口的,优先考虑使用接口来实现;

接口: 典型应用场景

Java中,接口可以看做是多个类共同规范,接口也是引用数据类型;
语法: 使用关键字interface定义接口,JDK8之前,接口中只有全局常量和抽象方法(更加纯粹的抽象类),这也是设计接口的主要内容,一般使用I开头表示接口的命名;

  • 子类使用implements实现接口,子类若是普通类,则必须覆写接口中所有的抽象方法;
  • 一般子类使用Impl结尾,表示是接口的实现子类; 若子类一眼看上去就知道是某个接口的子类,可以不加Impl结尾,例如鼠标、键盘等,知道是USB接口的子类;
  • 接口的子类之间并没有太多联系,接口只是多个不同类之间相同的规范,表示一种水平方向上的混合规范; 鼠标和键盘都是USB接口的子类,这两个子类之间没有明确的关联关系;
  • 接口的子类没有 is a/an 原则,相较于抽象类,使用更加灵活;
  • Dog、Cat、Duck都是Animal的子类,这些子类之间的关联还是很大的,这些子类基本都具有很多相同的属性和方法; 抽象类仍然是继承体系的内容,垂直结构,继承体系的子类之间是有很多共性的,Dog、Cat、Duck都具备很多的相同点,满足 is a/an 原则;
  • 接口是更加纯粹的抽象类,只有抽象方法和全局变量,没有构造方法,没有普通方法,因此接口仍然无法直接实例化对象; 只能通过具体子类向上转型被接口引用接收;
  • 接口的子类允许多实现! 一个类可以使用implements实现多个父接口(避免了抽象类的单继承局限);
  • 接口的子类不是 is a/an 关系,一个子类可以同时满足多个标准或具备多种能力; 例如手机类既满足5G标准,也满足USB标准; 所以手机这个类既实现5G接口,又要实现USB接口;
  • 正因为接口中只有全局常量和抽象方法,因此,在接口中这些关键字(public,abstract,static,final(常量))全部可以省略不写; Java中只有接口定义时可以省略这些关键字,类中不能省略,抽象类不能省略!
  • 子类若同时继承一个抽象类,实现多个接口; 请先使用extends继承一个类,而后使用implements实现多个接口;
  • 接口的多继承; 接口和接口之间可以使用extends继承多个父接口,但是接口不能使用extends继承类! 类中还有普通方法,构造方法等内容,接口中都没有;
  • JDK8之后,接口中也允许存在普通方法,接口中的普通方法使用default定义;

JDK内置的对象比较接口: java.lang.Comparable接口

自定义的类型要想让其具备可比较的能力,实现java.lang.Comparable接口,覆写compareTo方法;

@Override
public int compareTo(Object o){
	return 0;
}

表示当前对象和传入对象o做大小比较,根据返回值,能确定当前对象和传入对象o的大小关系:

> 0 的数当前对象 > 传入对象o
= 0 的数当前对象 == 传入对象o
< 0 的数当前对象 < 传入对象o

此处参数为何是Object类型?
JDK在定义Comparable接口的时候,设计者不知道具体子类是谁?也不知道到底哪些类要进行大小比较?而且也不关心,因此用参数的最高统一化类型,只要是进行比较的是类,都可以使用Object进行接收;

基本类型的大小关系是明确的,就是数值;例如:
int a = 10;
int b = 20;
所以基本类型可以根据具体的数值进行大小比较;

对象而言,内部可能包含很多属性,JDK怎么知道谁大谁小,因此通过Comparable接口的compareTo方法的返回值;
返回值是一个整型 => 将一个复杂的对象比较问题转换成了int的大小关系比较
默认负数 < 0 < 正数
返回0认为两对象相等!
站在JDK角度,不关心属性内部到底咋比较的,就看compareTo方法的返回值,返回负数JDK认为当前对象,返回正数JDK认为当前对象;

JDK内部的Cloneable接口

要想让一个类具备可复制的能力,实现Cloneable接口;
克隆 => 在克隆的过程中产生了新对象
新对象的属性值和被克隆的对象,完全一致;

Student stu = new Student("张三",20);
Student stu1 = stu;//stu1是引用,不是对象!!!此时只有一个对象,有两个引用;

在Java中,要让一个类具备可以复制的能力

  1. 实现Cloneable接口(java.lang.Cloneable);
public class Animal implements Cloneable{

}

Cloneable接口属于JDK的标记接口,类似的还有序列化接口(Serializable); 这种接口都属于JDK的标记接口,只要实现了标记接口的子类,JVM运行起来就会识别这种标记,赋予其相应的能力;

public interface Cloneable{

}
public interface Serializable{

}

例: 养猪场的猪在准备屠宰之前,工作人员会检查猪的健康状态,如果猪满足屠宰条件,会在猪的身上盖个章 => 标记; 这个章子本身没有什么特别的,但是屠宰场只会识别带有"合格"印章的猪并处理,不带这个章子,不做宰杀处理;

  1. 然后覆写Object类中的clone方法,JVM识别所有实现了Cloneable的类,赋予其可复制的能力,打上可克隆的标记;
public class Animal implements Cloneable{
	@Override
	protected Object clone() throws CloneNotSupportedException{  //只有实现了Cloneable接口的类才能覆写clone方法,否则就会被JVM抛出异常;
		return super.clone();
	}
}

深浅拷贝

浅拷贝:被克隆的对象内部若包含其他类型的引用,则克隆后的对象,仍然保留原引用,不会产生新的对象;
浅拷贝
浅拷贝只会将原对象内部的所有属性值复制一份; 引用就只会复制引用的值,即地址;

深拷贝: 克隆对象内部若包含其他类型的引用,其他类型的引用在克隆时也会调用克隆方法产生新对象;
深拷贝
要想实现深拷贝,两种方式:

  1. 递归进行clone的调用;
  2. 进行序列化操作,经过序列化得到的新对象一定是深拷贝对象;
    广义上的序列化: 将任意的对象转为字节流;
    广义上的反序列化: 将字节流转换为具体某个类的对象;
    现在的序列化: 将任意对象转为字符串,json字符串;
    现在的反序列化: 将特定的字符串转为某个具体类的对象,json的反序列化;

JDK8之前,抽象类和接口的区别,不包含default

区别抽象类(abstract)接口(interface)
结构组成普通类+抽象方法抽象方法+全局变量
权限各种权限public
子类使用使用extends关键字继承抽象类使用implements关键字实现接口
关系一个抽象类可以实现若干接口接口不能继承抽象类,但是接口可以使用extends关键字继承多个父接口
子类限制一个子类只能继承一个抽象类一个子类可以实现多个接口

"=="和equals

对于基本数据类型来说, “==” 比的是两个变量的数值是否相等;
例:
int a = 10;
int b = 10;
a == b; //true,a和b变量中保存的数值都是10

对于引用数据类型来说, “==” 仍然比较的是两个变量的数值,只不过引用变量保存的数值是一个地址;
“==” 比较的是两个对象的地址是否相等;

Person per1 = new Person(name:"张三", age:20);
Person per2 = new Person(name:"李四", age:22);
// false  "=="比较的是per1和per2变量保存的地址
System.out.println(per1 == per2);

对于引用数据类型来说, “==” 操作符比较的是引用类型保存的地址是否相等,若要进行对象内部属性值比较是否相等,使用equals方法(Object类提供的方法);
Object类默认的equals方法:

public boolean equals(Object obj) {
	return (this == obj);
}

Object默认的equals方法的效果: 当前对象和传入对象obj的地址是否相同,当前对象的引用this和传入的obj引用是否指向同一个对象 => per1 == per2没有什么区别!

因此,自定义的类之中,要想进行当前类的对象属性比较,必须要覆写equals方法!

@Override
public boolean equals(Object obj) {
	if(this == obj) {
		//当前对象和传入对象obj都指向的是同一个对象
		return true;
	}
	//若传入对象为空或传入对象obj压根就不是Person类型的引用
	if(obj == null || !(obj instanceof Person)){
		return false;
	}
	//代码如果走到这里,表示的情况是
	//传入对象obj和当前对象this确实是指向的两个不同对象,且obj一定是当前Person的引用
	//脱掉外衣,向下转型,将obj还原为Person引用进行具体属性值的比较
	Person per = (Person) obj;
	//只要是引用数据类型进行的对象相等比较,统一使用equals方法,JDK常用的类已经实现好了
	//String,包装类,集合类等等JDK实现好了equals方法;
	return this.age == per.age && this.name.equals(per.name);  //调用String类的equals方法
}	

数组存在equals方法,但是数组没法覆写! 默认就和 “==” 一样; 数组要进行内容的比较,循环取出元素比较即可;

int[] num1 = {1,3,5,7,9};
int[] num2 = {1,3,5,7,9};
System.out.println(num1.equals(num2));

Java中"=="和equals的区别

  1. "=="进行的是两个变量数值的比较,对于基本类型来说,比较的就是具体的数值是否相等; 对于引用类型来说, "=="比较的是两个引用是否指向同一个对象;
  2. "equals"方法进行的是两个类对象的属性值比较,若类的对象要具体比较属性值是否相等,需要覆写Object类提供的equals方法;
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值