持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第 16 天,点击查看活动详情
一、抽象类
在Java中,一个类如果被 abstract 修饰称为抽象类,抽象类中被 abstract 修饰的方法称为抽象方法,抽象方法不用给出具体的实现体。
public class TestDemo {
public static void main(String[] args){
Circle c = new Circle();
c.setR(5);
c.getArea();
Squre s = new Squre();
s.setLength(10);
s.getArea();
}
}
//抽象类
abstract class Shape{
private int size;
//抽象方法
abstract public void getArea();
}
class Circle extends Shape{
private int r;
public int getR() {
return r;
}
public void setR(int r) {
this.r = r;
}
//重写抽象方法
@Override
public void getArea() {
double area = r*r*r*4.0/3;
System.out.println("此圆形的面积是: "+area);
}
}
class Squre extends Shape{
private int length;
//重写抽象方法
@Override
public void getArea() {
double area = length*length;
System.out.println("此正方形的面积是: "+area);
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
}
-
抽象类的特性
- 抽象类中使用abstract修饰类和抽象方法,这个方法没有具体的实现,抽象类中可以包含普通类所能包含的成员,抽象类所存在的最大意义就是被继承。
- 抽象类方法不能是私有的,如果一个普通类继承了抽象类,那么必须重写抽象类中的抽象方法,不能被static和final修饰,因为抽象方法要被子类继承。
- 抽象类中不一定包含抽象方法,但是包含抽象方法的一定是抽象类,抽象类之间的相互继承不需要重写抽象方法。
二、接口
- 接口的定义
接口的定义格式与定义类的格式基本相同,将class关键字换成 interface 关键字,就定义了一个接口。
- 接口的使用
//接口的定义
interface USB {
void openDevice();
void closeDevice();
}
//实现接口
class Mouse implements USB {
@Override
public void openDevice() {
System.out.println("打开鼠标");
}
@Override
public void closeDevice() {
System.out.println("关闭鼠标");
}
public void click(){
System.out.println("鼠标点击");
}
}
//实现接口
class KeyBoard implements USB{
//实现接口中的抽象类
@Override
public void openDevice() {
System.out.println("打开键盘");
}
@Override
public void closeDevice() {
System.out.println("关闭键盘");
}
public void inPut(){
System.out.println("键盘输入");
}
}
-
注意事项
- ❗ 接口不能够直接使用,必须有一个类来实现接口,并实现接口中的所有抽象方法
- ❗ 子类和父类之间是extends 继承关系,类与接口之间是 implements 实现关系
- 接口中每一个方法都是public的抽象方法, 即接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错。
-
❗ 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现 ,JDK1.8 开始允许有可以实现的方法,但这个方法只能是default 修饰的,类在实现该接口时,不需要重写该默认方法。
具体作用: 当我们进行业务扩展时,需要在接口中新增方法。如果新增的这个方法写成普通方法的话,那么需要在该接口所有的实现类中都重写这个方法。如果新增的方法定义为default类型,就不需要在所有的实现类中全部重写该default方法,哪个实现类需要新增该方法,就在哪个类中进行实现
- 重写接口中方法时,不能使用default访问权限修饰
- 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量
- 接口中不能有静态代码块和构造方法
- 接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是.class
- 如果类没有实现接口中的所有的抽象方法,则类必须设置为抽象类
-
实现多个接口
- 一个类实现多个接口
interface USB { void openDevice(); void closeDevice(); } interface ULine{ void lineInsert(); } class Mouse implements USB,ULine{ @Override public void openDevice() { System.out.println("打开鼠标"); } @Override public void closeDevice() { System.out.println("关闭鼠标"); } @Override public void lineInsert() { System.out.println("插入鼠标线"); } }一个类实现多个接口时,每个接口中的抽象方法都要实现,否则类必须设置为抽象类
- 一个类继承一个父类,同时实现多个接口
public class TestDemo3 { public static void main(String[] args) { Duck duck = new Duck("yaya"); walk(duck); Brid brid = new Brid("gugu"); walk(brid); } public static void walk(IRunning running) { System.out.println("去散步"); running.run(); } } class Animal { protected String name; public Animal(String name) { this.name = name; } } interface IFlying { void fly(); } interface IRunning { void run(); } interface ISwimming { void swim(); } class Duck extends Animal implements IFlying,IRunning,ISwimming{ public Duck(String name) { super(name); } @Override public void fly() { System.out.println("飞飞飞"); } @Override public void run() { System.out.println("鸭子嘎嘎跑"); } @Override public void swim() { System.out.println("游游游"); } } class Brid extends Animal implements IRunning,ISwimming,IFlying{ public Brid(String name) { super(name); } @Override public void fly() { System.out.println("鸟儿飞"); } @Override public void run() { System.out.println("鸟儿跑"); } @Override public void swim() { System.out.println("鸟儿游"); } }- 接口中的多态
public class TestDemo3 { public static void main(String[] args) { Duck duck = new Duck("yaya"); walk(duck); Brid brid = new Brid("gugu"); walk(brid); } public static void walk(IRunning running) { System.out.println("去散步"); running.run(); } }有了接口之后, 类的使用者就不必关注具体类型,而只关注某个类是否具备某种能力.
-
接口之间的继承
一个类可以实现多个接口,接口与接口之间可以多继承。即:用接口可以达到多继承的目的。接口可以继承一个接口, 达到复用的效果. 使用 extends 关键字
interface IRing {
void run();
}
interface ISing {
void swim();
}
interface IAmphibious extends IRunning, ISwimming {}
class Frog implements IAmphibious {
@Override
public void run() {
System.out.println("跑啊跑");
}
@Override
public void swim() {
System.out.println("游啊游");
}
}
接口间的继承相当于把多个接口合并在一起.
✅抽象类和接口的区别??
| 区别 | 抽象类(abstract) | 接口(interface) | |
|---|---|---|---|
| 1 | 结构组成 | 普通类+抽象方法 | 抽象方法+全局变量 |
| 2 | 权限 | 各种权限 | public |
| 3 | 子类使用 | 使用extends关键字继承抽象类 | 使用implements关键字实现接口 |
| 4 | 关系 | 一个抽象类可以实现若干接口 | 接口不能继承抽象类,但是可以使用extends关键字继承多个接口 |
| 5 | 子类限制 | 一个子类只能继承一个抽象类 | 一个子类可以实现多个接口 |
三、Object类
Object是Java默认提供的一个类。Java里面除了Object类,所有的类都是存在继承关系的。默认会继承Object父类。即所有类的对象都可以使用Object的引用进行接收。
public class TestDemo5 {
public static void main(String[] args) {
function(new Person());
function(new Student());
}
public static void function(Object obj){
System.out.println(obj);
}
}
class Person{
private int age;
private String name;
}
class Student{
private int grade;
private String sno;
}
Object类中提供的一些默认方法
3.1 toString()方法
//Object类中的toString()方法实现:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
toString()方法一般需要通过重写之后进行使用。
3.2 hashcode()方法
- 返回对象的hash代码值
源码:
public native int hashCode();
重写hashCode() 方法
class Per{
public String name;
public int age;
public Per(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
public class TestDemo6 {
public static void main(String[] args) {
Per per1 = new Per("gaobo",20);
Per per2 = new Per("gaobo", 20);
System.out.println(per1.hashCode());
/*
注意事项:哈希值一样。
结论:
1、hashcode方法用来确定对象在内存中存储的位置是否相同
2、事实上hashCode() 在散列表中才有用,在其它情况下没用。
在散列表中hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。
*/
System.out.println(per2.hashCode());
}
}
3.3 equals()方法
- 比较的是地址
// Object类中的equals方法
public boolean equals(Object obj){
return (this == obj);
// 使用引用中的地址直接来进行比较
}
如果要比较对象中内容,必须重写Object中的equals方法,因为equals方法默认也是按照地址比较的
重写equals()方法
@Override
public boolean equals(Object obj) {
//判断是否为空
if (obj == null) {
return false ;
} if(this == obj) {
return true ;
}
// 不是Person类对象
if (!(obj instanceof Per)) {
return false ;
}
Per per = (Per) obj ; // 向下转型,比较属性值
return this.name.equals(per.name) && this.age==per.age ;
}
/* @Override
public boolean equals(Object obj) {
Per per = (Per)obj;
//String类调用的是自身的equals,
// s1跟s2两者比较的规则则是按照String类重写后的equals方法来比较,
//很显然,String类的比较规则是按照值来比较的,因此结果会输出true。
if(this.name.equals(per.name)&&this.age == per.age){
return true;
}
return false;
}
}
*/
编译器自动生成重写的hashcode()和equals()方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Per per = (Per) o;
return age == per.age &&
Objects.equals(name, per.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
在object类中,hashcode()方法是本地方法,返回的是对象的地址值,而object类中的equals()方法比较的也是两个对象的地址值,如果equals()相等,说明两个对象地址值也相等,当然hashcode()也就相等了.* 但是hashcode() 相同时,equals()不一定相同*
✅✅重写equals方法时,也必须重写hashcode()方法吗?
答:必须,hashCode 和 equals 两个方法是用来协同判断两个对象是否相等的,采用这种方式的原因是可以提高程序插入和查询的速度,当重写
equals方法后有必要将hashCode方法也重写,这样做才能保证不违背hashCode方法中“相同对象必须有相同哈希值”的约定。
✅✅ == 和 equals 的区别是什么?
答:
对于基本类型和引用类型 == 的作用效果是不同的,如下所示:
- 基本类型:比较的是值是否相同;
- 引用类型:比较的是引用是否相同
String x = "string"; String y = "string"; String z = new String("string"); System.out.println(x==y); // true System.out.println(x==z); // false System.out.println(x.equals(y)); // true System.out.println(x.equals(z)); // true对于equals() 方法,根据源码可以得知 : equals() 的本质上就是true
public boolean equals(Object obj) { return (this == obj); }所以equals()方法 默认情况下是引用比较,只是很多类重写了 equals 方法,比如 String、Integer 等把它变成了值比较,所以一般情况下 equals 比较的是值是否相等。