目录
抽象类
1.使用abstract修饰的方法称为抽象方法
2.使用abstract修饰的类称为抽象类
3.抽象类不可以被实例化 e.g.Shape shape = new Shape()//err
4.抽象类当中可以和普通类一样定义成员变量和方法
5.继承抽象类?当一个普通的类继承了抽象类,需要重写这个抽象类中所有的抽象方法!
所以抽象类不能和final共存,因为final这个关键字就是不让类重写的
6.抽象类的出现为了被继承:如果有些工作应由子类完成而非父类,那么父类是抽象类的话可以在实例化的时候报错,因为抽象类本身不能被实例化,要想使用,只能创建该抽象类的子类,然后重写抽象方法,这样可以让我们更容易发现问题。(作用)
7.不可以用private,static修饰抽象类方法
一开始没有重写方法,后面也需要重写方法
abstract class A extends Shape {
public abstract void testA();
}
class B extends A {
@Override
public void draw() {
}
@Override
public void testA() {
}
}
接口
基本概念
1.接口是使用interface修饰的
2.接口当中不能有被实现的方法,只能有抽象方法,但是被static和default方法修饰的方法除外
3.接口当中的抽象方法默认都是public abstract修饰的!
4.接口当中的成员变量默认都是public static final修饰的
5.接口不能进行实例化
6.类和接口之间的关系,可以使用implements来进行关联
7.系统给出两种选择,要么抽象要么重写
重写接口方法的时候不能使用默认的访问权限
因为子类的访问权限要大于等于父类的,父类默认是public abstract,子类有可能是private,就error了,子类必须是public
8.接口虽然不是类,但也是有对应的字节码文件.class的
private static void drawMap(Shape shape){
shape.draw();
}
public static void main(String[] args) {
//Shape shape = new Shape();//无法实例化,抽象的抽象类
Shape shape1 = new Rect();
Shape shape2 = new Flower();
drawMap(shape1);
drawMap(shape2);
System.out.println("===========");
Shape[] shapes = {new Rect(), new Rect(), new Flower(), new Flower()};//多态,接口也可以发生向上转型和动态绑定
for(Shape shape: shapes){
shape.draw();
}
}
9.接口中不能有代码块和构造方法
例子:
//接口USB
package demo1;
public interface USB {
void openDevice();
void closeDevice();
}
//类KeyBoard
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("疯狂敲打键盘.......");
}
}
//类Mouse
public class Mouse implements USB{
@Override
public void openDevice() {
System.out.println("鼠标开始工作");
}
public void click(){
System.out.println("疯狂点击鼠标......");
}
@Override
public void closeDevice() {
System.out.println("鼠标停止工作");
}
}
多接口使用
为什么接口解决了Java的多继承问题?
//抽象类Animal
abstract class Animal{
public String name;
public int age;
public Animal(String name, int age){
this.name=name;
this.age=age;
}
// public void fly(){}
// public void run(){}
// public void swim(){}
public abstract void eat();
}
在动物里面,有的会飞有的会游有的会跑
但我们不能在Animal类里面定义这三个方法因为不是每个动物都会飞或游或跑
因为Java不能同时继承多个类,所以这三个方法还不能直接写成不同的类
一个类可以实现多个接口,所以我们把他封装成接口如下:
//三个房卡其实可以看作个行为标准
//三个接口
interface IFly{
void fly();
}
interface IRun{
void run();
}
interface ISwim{
void swim();
}
想办法让三种动物执行多继承
//狗是一个动物,具备跑的功能
//鼠标放extends ALT+ENTER可以快速构造方法
//鼠标放Animal抽象类 ALT+ENTER可以快速重写方法
class Dog extends Animal implements IRun{
Dog(String name, int age){
super(name,age);
}
@Override
public void run() {
System.out.println(this.name+" 在用四条腿跑步");
}
@Override
public void eat() {
System.out.println(this.name+" 正在吃");
}
}
//蛙会跑会游
class Frog extends Animal implements IRun,ISwim{
public Frog(String name, int age){
super(name, age);
}
@Override
public void run() {
System.out.println(this.name+" 正在用两条腿跑");
}
@Override
public void swim() {
System.out.println(this.name+" 正在蛙泳");
}
@Override
public void eat() {
System.out.println(this.name+" 正在吃蛙粮");
}
}
//鸭会跑会游会飞
class Duck extends Animal implements IRun, ISwim, IFly{
public Duck(String name, int age) {
super(name, age);
}
@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+" 正在用两只脚划水");
}
@Override
public void eat() {
System.out.println(this.name+" 正在吃鸭粮");
}
}
public class Test {
public static void func1(Animal animal){
animal.eat();
}
public static void main1(String[] args) {
//多态
func1(new Duck("唐老鸭",10));
func1(new Dog("二狗子",10));
func1(new Frog("青蛙",10));
}
class Robot implements IRun{
@Override
public void run() {
System.out.println("机器人 正在用两只脚跑路");
}
}
public class Test {
public static void func1(Animal animal){
animal.eat();
}
public static void running(IRun iRun){
iRun.run();
}
public static void flying(IFly iFly){
iFly.fly();
}
public static void swimming(ISwim iSwim){
iSwim.swim();
}
//接口的意义:只要具备这个功能,我都能用
public static void main(String[] args) {
running(new Duck("唐老鸭",10));
running(new Dog("二狗子",10));
running(new Frog("青蛙",10));
running(new Robot());//不管是不是动物
System.out.println("================");
flying(new Duck("唐老鸭",10));
System.out.println("================");
swimming(new Duck("唐老鸭",10));
swimming(new Frog("青蛙",10));
}
接口的继承
interface A{
void testA();
}
interface B extends A{
void testB();
}
B接口不仅仅具备了B自己的功能,还具备了A接口的功能
用一个test来测试,发现既得重写A的方法,还得重写B的方法
关系:
1.类和接口:implements 实现
2.接口和接口:extends 拓展
接口使用实例
正常数字比较大小
public static void main1(String[] args) {
int a = 10;
int b = 20;
System.out.println(a>b);//输出false
}
但是到了类的引用,就比较不了了,因为地址无法比较
class Student{
public String name;
public int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
我们要解决这两个学生要根据什么比较?年龄?姓名?
我们需要实现一个接口
Java里面有一个Comparable接口,表示当前的类是可以比较的
重写compareTo方法
@Override
public int compareTo(Student o) {
if (this.age > o.age){
return 1;
} else if (this.age == o.age) {
return 0;
}else{
return -1;
}
}
调用
分析:
返回结果:
现在我想根据姓名比较,但如果要改变compareTo的逻辑得先复制后注释掉原来的方法再进行修改后单个比较,这样巨麻烦,(这也叫做对类的侵入性比较强,一旦写好了规定的比较方式,那么以后只能以这种方式进行比较),这不是我们想要的,那有没有另一种方法可以及能按照年龄比较又能按照姓名比较呢?
我们可以设计一个年龄比较器
comparator里面有一个方法
重写这个方法
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);
}
}
这里的compareTo我们点进去看是这样的
因为String类实现了Compare接口(用ASCII码来计算),所以我们调用的是String自己重写的compareTo方法
调用方法
这种方法好处就是比较灵活,只需要传入两个要比较的对象
数组的排序方法
/* int[] array = {1,2,31,14};
Arrays.sort(array);
System.out.println(Arrays.toString(array));
*/
定义五个学生并组成数组
Student[] students = new Student[] {
new Student("张三", 95),
new Student("李四", 96),
new Student("王五", 97),
new Student("赵六", 92),
};
我们是否可以沿用数组的排序思想给学生排序呢?
Arrays.sort(students);
System.out.println(Arrays.toString(students));
// 运行出错, 抛出异常.
Exception in thread "main" java.lang.ClassCastException: Student cannot be cast to java.lang.Comparable
//修改comparable的sort方法底层
public static void bubbleSort(Comparable[] comparables) {
for (int i = 0; i < comparables.length-1; i++) {
for (int j = 0; j < comparables.length-1-i; j++) {
if(comparables[j].compareTo(comparables[j+1]) > 0) {
Comparable tmp = comparables[j];
comparables[j] = comparables[j+1];
comparables[j+1] = tmp;
}
}
}
}
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("zhangsan",10);
students[1] = new Student("lisi",4);
students[2] = new Student("abc",5);
System.out.println("排序前: "+Arrays.toString(students));
//bubbleSort(students);
Arrays.sort(students);
System.out.println("排序后: "+Arrays.toString(students));
}
克隆
Clonable接口
Person person = new Person("张三",10);
Person person1 = person;//clone
现在想用第一个person克隆一个新的person1出来,Java的Obeject类里面有一个克隆方法
因为是protected修饰的,我们没办法直接在person后面.clone调用方法,所以需要super.clone()
//可能会抛出异常,这个异常叫做不知处克隆异常,是编译时期的异常
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
所以我们改一下main函数?
还报错?克隆方法的返回值是Object,person1的类是Person,父类Object给子类Person是不允许的,所以我们需要强转
Person person1 = (Person) person.clone();//clone
当我们来打印这两个person的时候,程序爆出问题
这说明我们自己写的类型要支持克隆的时候,要给它实现一个克隆接口Cloneable
但是点进去这个接口我们发现居然是空的,结果也没错,那实现这个接口意义在哪
这种接口叫做空接口或者标记接口,作用是表明当前类是可以被克隆的
拷贝
打印结果是什么呢?
分析:
目前这种情况叫做浅拷贝(只有一个对象的时候)就像两个人抢一个遥控器,遥控器变了(19.9->99.99),两个人抢的遥控器也跟着改变
深拷贝:把对象里面引用等信息拷贝出来一份
接下来我们要来实现深拷贝
Person类里面的super.clone()还在克隆person,还没有克隆money,我们可以借助一个中间变量来帮助克隆
Person tmp = (Person) super.clone();
再把Money类复制一份
tmp.money = (Money)this.money.clone();
这里的this就是person ,money的地址改成0x999(假设),再把这个0x999给到tmp
person1用来接收tmp的值
整个的深拷贝分析流程
深拷贝完成,打印结果如下
深拷贝相当于把一个遥控器复制一份分给另一个人,一个人拿到新的遥控器(99.99),另一个人还拿着老的遥控器(19.9)
Object类
红色🔒代表private修饰,绿色🔒代表public修饰,一把🔑代表protected修饰
equals方法
public static void main(String[] args) {
Person person = new Person("张三",10);
Person person1 = new Person("张三",10);
//System.out.println(person == person1);
System.out.println(person.equals(person1));//直接调用跟上一句没区别
}
两个学生名字年龄都一样,但是因为地址不一样所以比较结果false(equals默认比较对象的地址),这背离我们的初衷(这两个学生得相等)
我们就在Person类里面重写这个方法
//重写equals方法
@Override
public boolean equals(Object obj) {
Person tm = (Person) obj;
return this.name.equals(tm.name) && this.age == tm.age;
}
也可以再generate里面重写
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
hashcode方法可以看我以后的博客😀