一、抽象类
1.概念:如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象,这样的类就是抽象类。
像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法,包含抽象⽅法的类我们称为抽象类。
2.语法
在Java中,⼀个类如果被 abstract 修饰称为抽象类,抽象类中被abstract 修饰的⽅法称为抽象⽅法,抽象⽅法不⽤给出具体的实现体。
public abstract class Shape {
//抽象方法:被abstract修饰的方法,没有方法体
abstract public void draw();
abstract void calcArea();
//抽象类也是类,也可以增加普通方法和属性
public double getArea(){
return area;
}
protected double area;//面积
}
#注:抽象类也是类,内部可以包含普通方法和属性,甚至构造方法
3.抽象类的特性
(1)抽象类不能直接实例化对象
Shape sahpe = new Shape();
//编译错误
Error:(30, 23) java: Shape是抽象的; ⽆法实例化
(2)抽象方法不能是private的,也不能被final和static修饰(抽象类注定要被继承,内部的抽象方法要被重写)
(3)抽象类必须被继承,并且继承后子类要重写父类中的抽象方法,否则子类也是抽象类,必须要使用abstract修饰(如果子类也是抽象类,那么它的子类要重写其父类和父类的父类的抽象方法)
//抽象类:被abstract修饰的类
public abstract class Shape {
//抽象方法:被abstract修饰的方法,没有方法体
abstract public void draw();
abstract void calcArea();
//抽象类也是类,也可以增加普通方法和属性
public double getArea(){
return area;
}
protected double area;//面积
}
public class Rect extends Shape{
private double length;
private double width;
public Rect(double length, double width) {
this.length = length;
this.width = width;
}
public void draw(){
System.out.println("矩形:length="+length+"width="+width);
}
public void calcArea(){
area = length*width;
}
}
public class Circle extends Shape{
private double r;
final private static double PI = 3.14;
public Circle(double r){
this.r = r;
}
public void draw(){
System.out.println("圆:r = "+r);
}
public void calcArea(){
area = PI*r*r;
}
}
public class Triangle extends Shape{
private double a;
private double b;
private double c;
@Override
public void draw() {
System.out.println("三⻆形: a = "+a + " b = "+b+" c = "+c);
}
@Override
void calcArea() {
}
}
(4).抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类
(5).抽象方法中可以有构造方法,供子类创建对象时初始化父类的成员变量
4.抽象类的作用
抽象类的使用相当于是多了一重编译器的校验。
在上述代码中,实际工作是由子类完成的,那么如果误用成了父类,普通的类编译器不会报错,但是父类是抽象类就会在实例化的时候提示错误,尽早发现问题。
二、接口
1.概念
在Java中,接口可以看成是多个类的公共规范,是一种引用数据类型
2.语法
public interface 接口名称{
//抽象方法
public abstract void method1();// public abstract是固定搭配,可以不写
public void method2();
abstract void method3();
void method4();
//注意:在接口中上述写法都是抽象方法,但是更推荐方式4,代码更加简洁
}
#注:
(1)创建接口时,接口的命名一般是以大写的字母I开头
(2)接口的命名一般使用形容词性的单词(后面+able)
(3)阿里编码规范中约定,接口中的方法和属性不要加任何修饰符号,保持代码的简洁性。
3.接口的使用
接口不能直接使用,必须要有一个“实现类”来“实现”该接口,实现接口中所有的抽象方法。
public class 类名 implements 接口名称{
//...
}
#注:子类和父类之间是extends继承关系,类与接口之间是implements实现关系
//鼠标类,实现USB接口
public 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接口
public 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设备
public 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();
} else if (usb instanceof KeyBoard) {
KeyBoard keyBoard = (KeyBoard) usb;
keyBoard.inPut();
}
usb.closeDevice();
}
}
//测试类
public class TestUSB {
public static void main(String[] args) {
Computer computer = new Computer();
computer.powerOn();
//使用鼠标设备
computer.useDevice(new Mouse());
//使用键盘设备
computer.useDevice(new KeyBoard());
computer.powerOff();
}
}
instanceof是用于检查比较的,避免出现ClassCastException异常
4.特性
(1)接口是一种引用类型,但是不能直接new接口的对象
(2)即接⼝中的⽅法会被隐式的指定为public abstract
(3)接口中的方法不能在接口中实现,只能由实现接口的类来实现
(4)重写接口中的方法时,不能使用默认的访问权限
(5)接口中可以含有变量,但是接口中的变量会被隐式的指定为public static final变量
(6)接口中不能有静态代码块和构造方法
(7)接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是.class
(8)如果类没有实现接口中的所有抽象方法,则类必须设置为抽象类
5.实现多个接口
Java中不⽀持多继承,但是⼀个类可以实现多个接⼝。
public class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
}
public interface IFlying {
void fly();
}
public interface IRunning {
void run();
}
public interface ISwimming {
void swim();
}
public class Duck extends Animal implements IRunning,ISwimming,IFlying{
public Duck(String name) {
super(name);
}
@Override
public void fly() {
System.out.println(this.name+"正在用翅膀飞");
}
@Override
public void run() {
System.out.println(this.name+"正在用两条腿跑");
}
@Override
public void swim() {
System.out.println(this.name+"正在漂在水上");
}
}
这样设计可以让程序员不必关注具体类型,而是只关注某个类是否具备某种能力
6.接口之间的继承
在Java中,类和类之间是单继承的,⼀个类可以实现多个接⼝,接⼝与接⼝之间可以多继承。即:⽤ 接⼝可以达到多继承的⽬的。
接⼝可以继承⼀个接⼝,达到复⽤的效果. 使⽤extends关键字.
7.接⼝使⽤实例
例:对象之间进⾏⼤⼩关系⽐较
(1)使⽤Comparable接⼝
public class Student implements Comparable<Student>{
private String name;
private int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
@Override
public int compareTo(Student o) {
// Student s = (Student) o;
// if(this.score > s.score){
// return -1;
// } else if (this.score > s.score) {
// return 1;
// }else {
// return 0;
// }
return this.score-o.score;
//return this.name.compareTo(o.name);
}
}
public class Test {
public static void main(String[] args) {
Student s1 = new Student("zhangsan",10);
Student s2 = new Student("lisi",20);
System.out.println(s1.compareTo(s2));
}
}
(2)使⽤Comparator接⼝
public class ScoreComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.getScore()-o2.getScore();
}
}
public class NameComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.getName().compareTo(o2.getName());
}
}
public class Test {
public static void main(String[] args) {
Student s1 = new Student("zhangsan",10);
Student s2 = new Student("lisi",20);
//根据分数进行比较
ScoreComparator scoreComparator = new ScoreComparator();
System.out.println(scoreComparator.compare(s1,s2));
//根据姓名比较
NameComparator nameComparator = new NameComparator();
System.out.println(nameComparator.compare(s1,s2));
}
}
8.Clonable 接⼝和深拷⻉
Java 中内置了⼀些很有⽤的接⼝,Clonable就是其中之⼀.
Object 类中存在⼀个clone⽅法,调⽤这个⽅法可以创建⼀个对象的"拷⻉".但是要想合法调⽤clone ⽅法,必须要先实现Clonable接⼝,否则就会抛出CloneNotSupportedException异常.
public class Animal implements Cloneable{
private String name;
public Animal clone(){
Animal o = null;
try{
o = (Animal) super.clone();
}catch(CloneNotSupportedException e){
e.printStackTrace();
}
return o;
}
}
public class Test {
public static void main(String[] args) {
Animal animal = new Animal();
Animal animal2 = animal.clone();
System.out.println(animal == animal2);
}
}
#注:try-catch用来处理克隆过程中可能出现的异常的(try尝试调用并执行父类Object的clone方法,catch则是捕获异常,是一种校验)
(1)浅拷贝
public class Person implements Cloneable{
public Money money = new Money();
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Money {
public double m = 99.99;
}
public class TestDemo3 {
public static void main(String[] args) throws
CloneNotSupportedException {
Person person1 = new Person();
Person person2 = (Person) person1.clone();
System.out.println(" 通过 person2 修改前的结果 ");
System.out.println(person1.money.m);
System.out.println(person2.money.m);
person2.money.m = 13.6;
System.out.println("通过person2 修改后的结果 ");
System.out.println(person1.money.m);
System.out.println(person2.money.m);
}
}
就像是上面的代码,只是拷贝了Person,它的Money没有被拷贝,这就叫做浅拷贝
(2)深拷贝(在浅拷贝的基础上连同Money一起拷贝)
protected Object clone()
throws CloneNotSupportedException {
//return super.clone();
Person tmp = (Person)super.clone();
tmp.m = (Money) this.m.clone();
return tmp;
}
}
9.抽象类和接口的区别
核⼼区别:抽象类中可以包含普通⽅法和普通字段,这样的普通⽅法和字段可以被⼦类直接使⽤(不必重写), ⽽接⼝中不能包含普通⽅法,⼦类必须重写所有的抽象⽅法.
三、Object类
Object是Java默认提供的⼀个类。Java⾥⾯除了Object类,所有的类都是存在继承关系的。默认会继 承Object⽗类。即所有类的对象都可以使⽤Object的引⽤进⾏接收
Object类也存在有定义好的⼀些⽅法,我们主要来熟悉这⼏个⽅法:equals()⽅法,hashcode()⽅法。
1.equals⽅法
equals⽅法默认是按照地址⽐较,如果要⽐较对象中内容,必须重写Object中的equals⽅法
class Person{
...
@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 ; // 向下转型,⽐较属性值
return this.name.equals(person.name) && this.age==person.age ;
}
}
2.hashcode⽅法
了hashCode()这个⽅法,他帮我算了⼀个具体的对象位置,这⾥⾯涉及数据结构,但是还没学数据结构,没法讲述,所以只能说它是个内存地址。然后调⽤Integer.toHexString()⽅法, 将这个地址以16进制输出。
像重写equals⽅法⼀样,我们也可以重写hashcode()⽅法。
class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
public class TestDemo4 {
public static void main(String[] args) {
Person per1 = new Person("gaobo", 20) ;
Person per2 = new Person("gaobo", 20) ;
System.out.println(per1.hashCode());
System.out.println(per2.hashCode());
}
}
//执⾏结果
460141958
460141958