抽象类
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果
一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。 比如:动物类,形状类
- 抽象类 使用
abstract
修饰类。 - 抽象类当中,可以包含普通类所能包含的成员。
- 抽象类当中可以包含抽象方法,也可以不包含。
- 抽象方法是使用
abstract
修饰的,这个方法没有具体的实现 - 抽象类不能实例化。
- 抽象类存在的最大意义:被继承
- 如果一个普通类继承了抽象类,必须重写抽象方法。
- 如果一个抽象类A继承一个抽象类B,此时A当中不需要重写B当中的抽象方法,但如果A在被普通类继承 ,就需要重写抽象方法。(如果想要类B继承抽象类A,但是又不想在类B中重写A中的抽象类方法,方法:将类B写成抽象类)
- 抽象方法不能是私有的【抽象方法的出现就是为了重写】。要满足重写的规则。
final
和abstract
是矛盾的,不可共存 ;static
和abstract
是矛盾的,不可共存 。【抽象方法不能被final
和static
修饰,因为抽象方法要被子类重写,而final
修饰的类称为静态类,不能被继承】- 抽象类当中可以有构造方法,为了方便子类能够调用,来初始化抽象类当中的成员。【因为抽象类不能实例化,提供了构造方法后,相当于子类可以帮助抽象类来初始化成员。
比如说,我们定义了一个Shape类,用来描述形状,
定义了三角形类,正方形类等形状,让他们继承shape类,重新抽象类的抽象方法,也就是draw()方法。
abstract class Shape {
public static int a = 10;
// 抽象方法,没有具体的实现
public abstract void draw();
public void func() {
}
}
class Rect extends Shape {
@Override
public void draw() {
System.out.println("this is a rect.");
}
}
接 口
接口概念
在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型。
接口规则
- 1.使用 interface 修饰接口
- 2.接口当中的成员方法不能有具体的实现 默认都是【public】
-
- 抽象方法:默认是public abstract,这个写不写都一样
-
- JDK1.8开始,允许有可以实现的方法,但是只能由default修饰
-
- 可以实现静态方法
- 3.成员变量默认是public static final修饰,写不写都一样
- 4.接口不能被实例化
- 5.类和接口之间采用implements实现多个接口
- 6.子类重写抽象方法,必须使用public
- 7.接口中不能含有静态代码块和构造方法。
- 8.如果一个类继承了接口,但是不想实现接口的方法,那么就把这个类定义为抽象类。但是如果这个类被其他类继承,那么必须重写。
- 9.一个类可以实现多个接口。使用implements,用逗号隔开。【可以解决多继承问题】
接口使用
提示:
- 创建接口时,接口的命名一般以大写字母
I
开头 - 接口命名一般使用形容词的词性
- 阿里编程规范中约定,接口中的方法和属性不要加任何的修饰符号【不用写public abstract】,保持代码的简洁。
- 一个接口就回生成一个.class文件。
- 一般一个接口写在一个Java文件中。
// USB接口
interface USB {
void openDevice();
void closeDevice();
}
// 鼠标类,实现USB接口
class Mouse implements USB {
@Override
public void openDevice() {
System.out.println("打开鼠标");
}
@Override
public void closeDevice() {
System.out.println("关闭鼠标");
}
public void click(){
System.out.println("鼠标点击");
}
}
// 键盘类,实现USB接口
class KeyBoard implements USB {
@Override
public void openDevice() {
System.out.println("打开键盘");
}
@Override
public void closeDevice() {
System.out.println("关闭键盘");
}
public void inPut(){
System.out.println("键盘输入");
}
}
// 笔记本类:使用USB设备
class Computer {
public void powerOn(){
System.out.println("打开笔记本电脑");
}
public void powerOff(){
System.out.println("关闭笔记本电脑");
}
public void useDevice(USB usb){
usb.openDevice();
if(usb instanceof Mouse){
Mouse mouse = (Mouse)usb; //强制类型转换 (向下转型
mouse.click(); //方法只在子类中定义,通过usb无法调用它
}else if(usb instanceof KeyBoard){
KeyBoard keyBoard = (KeyBoard)usb;
keyBoard.inPut();
}
usb.closeDevice();
}
}
public class test3 {
public static void main(String[] args) {
Computer computer = new Computer();
computer.powerOn();
computer.useDevice(new KeyBoard());
computer.useDevice(new Mouse());
computer.powerOff();
}
}
接口特性
- 接口类型是一种引用类型,但是不能直接new接口的对象
USB usb = new USB(); //错误
- 接口中每一个方法都是
public
的抽象方法, 即接口中的方法会被隐式的指定为public abstract
(只能是
public abstract
,其他修饰符都会报错,public abstract
可不写,因为默认就是public abstract
)
interface USB {
void openDevice();
public abstract void closeDevice();
}
- 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现。
interface USB {
void openDevice();
// 编译失败:因为接口中的方法默认为抽象方法
//接口中的抽象方法不能带有主体
void closeDevice() {
System.out.println("test");
}
}
- 重写接口中的方法的时候,不能使用默认的访问修饰权限。
interface USB {
void openDevice();
// 编译失败:因为接口中的方法默认为抽象方法
//接口中的抽象方法不能带有主体
void closeDevice();
}
class Mouse implements USB {
@Override
public void openDevice() { //重写时, 需要加上public,子类的访问修饰权限必须大于等于父类是权限
System.out.println("打开鼠标");
}
}
- 接口中可以含有变量,会被隐式的指定为
public static final
- 接口中不能含有静态代码块和构造方法。【因为接口本身不能被实例化】
- 接口虽然不是类,但是接口编译完成后的字节码文件后缀格式也是.class
- 如果类没有实现接口中的所有抽象方法,则类必须设置为抽象类
- jdk8中,接口可以包含default方法。
实现多个接口
一个类可以实现多个接口。使用implements,用逗号隔开。【可以解决多继承问题】
Java当中只能继承一个类,但是可以实现多个接口。接口解决了Java中无法多继承的问题。
interface A1 {
void func1();
}
interface B1 {
void func2();
}
interface D extends A1,B1 { //接口D继承了接口A1,B1 【接口和接口之间】 把多个接口合并在一起,扩展的意思。
void func3();
}
class E implements A1,B1 {
@Override
public void func1() {
}
@Override
public void func2() {
}
}
class C implements D { //类 C 使用接口 D 【类和接口之间】
@Override
public void func1() {
}
@Override
public void func2() {
}
@Override
public void func3() {
}
}
class Animal {
public String name;
public int age;
public void eat() {
System.out.println("eat");
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
}
//这里没有写成类的原因是,Java当中只能继承一个类,不能继承多个类。 但是可以实现多个接口。接口解决了Java无法多继承的问题
interface IFlying {
void flying();
}
interface ISwimming {
void swimming();
}
interface IRunning{
void running();
}
class Dog extends Animal implements IRunning,ISwimming{
public Dog(String name, int age) { //构造方法
super(name, age);
}
@Override
public void swimming() {
System.out.println(name + "dog swimming");
}
@Override
public void running() {
System.out.println(name + "dog running");
}
@Override
public void eat() {
System.out.println(name + "eat dog food");
}
}
class Bird extends Animal implements IFlying { //一定是先继承再实现
public Bird(String name, int age) {
super(name, age);
}
@Override
public void flying() {
}
}
class Duck extends Animal implements IFlying,ISwimming,IRunning {
public Duck(String name, int age) {
super(name, age);
}
@Override
public void flying() {
System.out.println(name + "Duck flying");
}
@Override
public void swimming() {
System.out.println(name + "Duck swimming");
}
@Override
public void running() {
System.out.println(name + "Duck running");
}
@Override
public void eat() {
System.out.println(name + "eat Duck food");
}
}
public class Test5 {
//有了接口之后就不关注类型了,只要实现了接口 重写了方法,表现的行为就不一样了,就可以实现多态
public static void walk(IRunning iRunning) { //多态
iRunning.running();
}
public static void func(Animal animal){
animal.eat();
}
public static void main(String[] args) {
walk(new Dog("aa",12));
walk(new Duck("bb",34));
func(new Dog("aa",12));
func(new Duck("bb",34));
}
}
接口间的继承
interface A1 {
void func1();
}
interface B1 {
void func2();
}
interface D extends A1,B1 { //接口D继承了接口A1,B1
void func3();
}
抽象类和接口的区别
抽象类和接口都是Java中多态的常见使用方式。
核心区别:抽象类中可以包含普通方法和普通字段,这样的普通方法和字段可以被子类直接使用(不用重写),而接口中不能包含普通方法,子类必须重写所有的抽象方法。
区别 | 抽象类 | 接口 |
---|---|---|
结构组成 | 普通类+抽象方法 | 抽象方法+全局变量 |
权限 | 各种权限 | public |
子类使用 | 使用extends关键字继承抽象类 | 使用implements关键字实现抽象类 |
关系 | 一个抽象类可以实现若干接口 | 接口不能继承抽象类,但是接口可以使用extends关键字继承多个父接口 |
子类限制 | 一个子类只能继承一个抽象类 | 一个子类可以实现多个接口 |
Object类
Object类是所有类的父类,我们自己写的类,就算没有显示extends Object ,都是默认继承的
equals
Object 的equals判断的是引用变量的里面存储的地址是否一样。如果有自己的判断相等的规则,需要重写equals方法。
如果写了自定义类型,要注意重写equals方法。
import java.util.Objects;
class Person{
private String name ;
private int age ;
public Person(String name, int age) {
this.age = age ;
this.name = name ;
}
//重写equals方法
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if(this == obj) {
return true ;
}
// 不是Person类对象
if (!(obj instanceof Person)) {
return false ;
}
Person person = (Person) obj ; // 向下转型,比较属性值
if (this.name.equals(person.name) && this.age==person.age) {
return true;
} else {
return false;
}
}
}
public class Test6 {
public static void main(String[] args) {
Person p1 = new Person("gaobo", 20) ;
Person p2 = new Person("gaobo", 20) ;
int a = 10;
int b = 10;
System.out.println(a == b); // 输出true
System.out.println(p1 == p2); // 输出false
System.out.println(p1);
System.out.println(p2);
System.out.println(p1.equals(p2)); // 输出false
}
}
hashcode
重写hashcode
import java.util.Objects;
class Person{
private String name ;
private int age ;
public Person(String name, int age) {
this.age = age ;
this.name = name ;
}
@Override
public int hashCode() {
//计算对象的位置
return Objects.hash(name, age);
}
}
public class Test6 {
public static void main(String[] args) {
Person p1 = new Person("gaobo", 20) ;
Person p2 = new Person("gaobo", 20) ;
//重写hashcode前,不在同一个地方
//重写后,一样的数据生成的hashcode一样
System.out.println(p1.hashCode());
System.out.println(p2.hashCode());
}
}
comparable接口
对类的侵入性很强,定好了以后的比较都是用什么比较
总结:如果以后自定义的类型,一定要记得,如果要比较大小,必须要让这个类具备可以比较的功能。此时可以选择实现接口comparable
import java.util.Arrays;
class Student implements Comparable<Student>{
public String name;
public int age;
public Student(String name, int age) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
@Override
public int compareTo(Student o) { //谁调用compareTo方法谁就是this,
if(this.age - o.age > 0) {
return 1;
} else if (this.age - o.age < 0){
return -1;
} else {
return 0;
}
}
}
public class Test {
//总结:如果以后自定义的类型,一定要记得,如果要比较大小,必须要让这个类具备可以比较的功能。
//此时可以选择实现接口comparable
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("bit1",8);
students[1] = new Student("bit2",2);
students[2] = new Student("bit3",3);
//如何对学生类数组进行排序 向平常数组一样Arrays.sort(students) 是不可行的
//自定义的学生类,需要具备可以比较的功能【让student类实现一个Comparable接口,重写compareTo方法】
Arrays.sort(students);
System.out.println(Arrays.toString(students));
Student student1 = new Student("a", 11);
Student student2 = new Student("b", 12);
if(student1.compareTo(student2) > 0) { //谁调用compareTo方法谁就是this,在这里student1就是this,student2就是o
System.out.println("student1 > student2");
}
if(student1.compareTo(student2) < 0) {
System.out.println("student1 < student2");
}
}
}
Comparator接口
对类的侵入性较弱
import java.util.Arrays;
import java.util.Comparator;
class Student {
public String name;
public int age;
public Student(String name, int age) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
//TODO:Comparator接口当中,不止一个抽象方法,那为什么只需要实现一个compare方法就好了
class AgeComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.age - o2.age;
}
}
class NameComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.name.compareTo(o2.name);
}
}
public class Test {
public static void main(String[] args) {
Student student1 = new Student("a", 11);
Student student2 = new Student("b", 12);
AgeComparator ageComparator = new AgeComparator();
if(ageComparator.compare(student1, student2) > 0) {
System.out.println("student1 > student2");
}
if(ageComparator.compare(student1, student2) < 0) {
System.out.println("student1 < student2");
}
}
public static void main1(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("bit1",8);
students[1] = new Student("bit2",2);
students[2] = new Student("bit3",3);
// AgeComparator ageComparator = new AgeComparator();
// Arrays.sort(students, ageComparator); //ageComparator的第二个参数是实现了Comparator接口的类的对象
// System.out.println(Arrays.toString(students));
NameComparator nameComparator = new NameComparator();
Arrays.sort(students, nameComparator);
System.out.println(Arrays.toString(students));
}
}
compareTo 和equals
equals返回布尔类型,判断是否相等。
compareTo返回整数,比较的是两个之间的大小关系。
clone
一个对象 想被克隆:
- 添加克隆接口
class Person implements Cloneable
- 重写克隆方法
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
- 弹出异常,强转类型
public static void main(String[] args) throws CloneNotSupportedException {
Person person2 = (Person) person.clone();
//Cloneable (空接口 / 标记接口) 表示当前类可以被克隆
//implements Cloneable 让Person具备可克隆的功能
class Person implements Cloneable{
public int id;
//重写 Object 克隆方法
//不同包中,需要通过super去访问。
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
'}';
}
}
public class Test2 {
public static void main(String[] args) throws CloneNotSupportedException {
Person person = new Person();
person.id = 1;
Person person2 = (Person) person.clone();
System.out.println(person);
System.out.println(person2); //得到的结果都是Person{id=1}
}
}
Clonable 接口和深拷贝
浅拷贝
深拷贝
浅拷贝
class Money{
public double m = 12.5;
}
//Cloneable (空接口 / 标记接口) 表示当前类可以被克隆
//implements Cloneable 让Person具备可克隆的功能
class Person implements Cloneable{
public int id;
public Money money = new Money();
//重写 Object 克隆方法
//不同包中,需要通过super去访问。
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); //原来只是克隆出一个对象,在money中,没有克隆出money指向的值 浅拷贝
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", money=" + money +
'}';
}
}
public class Test2 {
public static void main(String[] args) throws CloneNotSupportedException {
Person person = new Person();
person.id = 1;
Person person2 = (Person) person.clone();
//问题:person.money.m 和 person2.money.m 指向了同一个值
//浅拷贝
//改变person2 之后 pearson也变了
person2.money.m = 100;
System.out.println(person.money.m);
System.out.println(person2.money.m);
}
}
深拷贝
class Money implements Cloneable{
public double m = 12.5;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
//Cloneable (空接口 / 标记接口) 表示当前类可以被克隆
//implements Cloneable 让Person具备可克隆的功能
class Person implements Cloneable{
public int id;
public Money money = new Money();
@Override
protected Object clone() throws CloneNotSupportedException {
// return super.clone(); //原来只是克隆出一个对象,在money中,没有克隆出money指向的值 浅拷贝
//深拷贝
Person tmp = (Person) super.clone(); //克隆出了对象。但是他money指向的还是原来的对象的money值 【克隆person】
tmp.money = (Money) this.money.clone(); //克隆当前对象的money 谁调用clone 谁就是当前对象 【克隆money】
return tmp; //
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", money=" + money +
'}';
}
}
public class Test2 {
public static void main(String[] args) throws CloneNotSupportedException {
Person person = new Person();
person.id = 1;
Person person2 = (Person) person.clone();
//问题:person.money.m 和 person2.money.m 指向了不同值
person2.money.m = 100;
System.out.println(person.money.m);
System.out.println(person2.money.m);
}
}