一、抽象类
在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(); }}////抽象类abstracttracttact 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关键字代替class关键字 interface 定义一个接口的关键字。
- 接口的使用
///界面定义interface USB { void openDevice(); void closeDevice();}////实现接口classss Mouse implements USB { @Override public void openDevice() { System.out.println(打开鼠标); } @Override public void closeDevice() { System.out.println(关闭鼠标); } public void click(){ System.out.println(鼠标点击); }}////实现接口classss 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默认提供的一个类别。除了Object,Java中的所有类别都有继承关系。默认情况下,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() 只有在散列表中才有用,在其他情况下才无用。 结论: 1、hashcode方法用于确定对象在内存中的存储位置是否相同 2、事实上,hashCode() 只有在散列表中才有用,在其他情况下才无用。 hashCode()在散列表中 其功能是获取对象的散列码,然后确定对象在散列表中的位置。 */ System.out.println(per2.hashCode()); }}
3.3 equals()方法
- 比较地址
// objectequals方法publict 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()方法由编译器自动生成重写
@Overridepublic 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); }
四、常用接口4.1 Comparable接口(比较)object类,hashcode()方法是本地方法,返回对象的地址值,object类中的equals()方法也是两个对象的地址值。**如果equals()相等,说明两个对象的地址值相等,当然hashcode()相等。.**但是hashcode() 相同时,equals()不一定相同
✅✅还必须重写hashcode()方法才能重写equals方法吗?
答:必须,hashCode 和 equals 这两种方法用于协同判断两个对象是否相等。这样做的原因是当重写时,可以提高程序插入和查询的速度
equals
有必要在方法结束后进行hashCode
该方法也被重写,以确保它不会违反它hashCode
“相同的对象必须具有相同的哈希值”的协议。✅✅ == 和 equals 有什么区别?
答:
基本类型和引用类型 == 效果不同,如下所示:
- 基本类型:比较值是否相同;
- 引用类型:比较引用是否相同
String x = "string";String y = "string";String z = new String("string");System.out.println(x==y); // trueSystem.out.println(x==z); // falseSystem.out.println(x.equals(y)); // trueSystem.out.println(x.equals(z)); // true
equals() 根据源码可以知道方法 : equals() 本质上是truee
public boolean equals(Object obj) {return (this == obj);}
因此,equals()方法 默认情况下是引用比较,但重写的类别很多 equals 方法,比如 String、Integer 等它变成值比较,所以一般情况下 equals 比较值是否相等。
在学习数组时,Arrays类中的sort方法可以对对对象数组进行排序 , 下面的对象数组能用Arrays吗?.sort排序呢?
class Student { String name; int age; public Student(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; }}public class test4 { public static void main(String[] args) { Student[] students = new Student[] { new Student("zhangsan", 13), new Student("lisi", 23), new Student("able", 17), }; Arrays.sort(students); System.out.println(Arrays.toString(students)); }}
此时,编译器不知道它是按姓名还是年龄排序的。当sort方法对对象的类别进行排序时,对象的类别必须实现comparable接口。根据参考文件,comparable接口中只有一种抽象方法。
然后我们可以实现comparable接口,实现和重写compareto方法
class Student implements Comparable<Student>{ public int age; public String name; public Student(int age, String name) { this.age = age; this.name = name; } @Override public String toString() { return "Student{" + "age=" + age + ", name='" + name + '\'' + '}'; } ///重写compareto方法 @Override public int compareTo(Student o) { if (this.age - o.age > 0) return 1; else if (this.age - o.age < 0) return -1; else return 0; } public static void main1(String[] args) { Student student = new Student(16, "liba"); Student student1 = new Student(13, "zhangsan"); System.out.println(student.toString()); System.out.println(student1.toString()); if (student.compareTo(student1) > 0) { System.out.println("student > student1”; } else { System.out.println("student < student1”; } }}
此时可以得到按年龄排序的结果:
我们知道在Arrays.sort(students); 其中有一个学生对象数组。在调用Arays对象数组进行排序时,我们实际上在Comparable接口中调用CompareTo方法对数组的每个元素进行排序和比较,并在Java中对引用数据类型进行比较或排序,Comparable接口中的compareto()通常用于使用 方法
按姓名排序时,重写compareto方法
@Override public int compareTo(Student o) { // this.代表引用当前对象,o.代表参数对的引用 if (this.name.compareTo(o.name) > compareto方法在/String类中重写,可直接使用 return 1; else if (this.name.compareTo(o.name) < 0) return -1; else return 0; } //如果当前对象应排在参数对象之前, 返回小于 0 的数字; //如果当前对象应排在参数对象之后, 返回大于 0 的数字; //如果当前对象和参数对象不分顺序, 返回 0;