文章目录
抽象类
概念
注:
- 抽象类不能实例化对象,其余的属性和方法和普通的类相同。
- 由于其不能实例化对象,所以抽象类必须被继承;因父类本身是抽象的,所以其不能使用其内部的方法。
- 在 Java 中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。
例2:
我们发现父类的draw()方法无法实现,但可以交付其子类完成;像这种没有实际工作的方法, 我们可以把它设计成一个抽象方法(abstractmethod), 包含抽象方法的类我们称为 抽象类(abstract class).
语法
public abstract class Shape {
//抽象方法:被abstract修饰的方法,没有方法体
//面积
abstract public double area();
//周长
abstract public double perimeter();
//形状
abstract public void draw();
}
注:
- 抽象类也是类可以添加属性和普通方法,以及构造方法。
特性
- 成员变量不可以被abstract修饰,
public abstract class Shape {
abstract private int Q;
abstract public int v;
abstract int s;
}
运行结果:
- 抽象类中方法不可以被private修饰
public abstract class Shape {
abstract private int aa();
}
运行结果:
注:抽象方法没有加访问限定符时,默认是public。
- 抽象方法不能被final和static修饰,因为抽象方法要被子类重写
public abstract class Shape {
abstract final void methodA();
abstract public static void methodB();
}
运行结果:
4. 抽象类必须被继承,并且继承后子类要重写父类中的抽象方法,否则子类也是抽象类,必须要使用 abstract 修饰
- Shape类
package package2;
/**
* Created with Intellij IDEA.
* Description:
* User: a
* Date: 2022-03-31
* Time: 22:50
*/
public abstract class Shape {
//面积
abstract public double area();
//周长
abstract public double perimeter();
//形状
abstract public void draw();
}
- 矩形类
package package2;
/**
* Created with Intellij IDEA.
* Description:矩形类
* User: a
* Date: 2022-03-31
* Time: 23:14
*/
public class Rectangle extends Shape{
private double length;
private double width;
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
@Override
public double area() {
return getWidth() * getLength();
}
@Override
public double perimeter() {
return 2 * (getLength() + getWidth());
}
@Override
public void draw() {
System.out.println("矩形");
}
}
- 三角形类
package package2;
/**
* Created with Intellij IDEA.
* Description:三角形
* User: a
* Date: 2022-03-31
* Time: 23:27
*/
public class Triangle extends Shape{
private double a, b, c;
public double getA() {
return a;
}
public void setA(double a) {
this.a = a;
}
public double getB() {
return b;
}
public void setB(double b) {
this.b = b;
}
public double getC() {
return c;
}
public void setC(double c) {
this.c = c;
}
@Override
public double area() {
double p = (getA() + getB() + getC()) / 2;
return Math.sqrt(p * (p - getA()) * (p - getB()) * (p - getC()));
}
@Override
public double perimeter() {
return getA() + getB() + getC();
}
@Override
public void draw() {
System.out.println("三角形");
}
}
- 圆形类
package package2;
/**
* Created with Intellij IDEA.
* Description:园类
* User: a
* Date: 2022-03-31
* Time: 23:30
*/
public class Circle extends Shape{
private double diameter;
public double getDiameter() {
return diameter;
}
public void setDiameter(double diameter) {
this.diameter = diameter;
}
@Override
public double area() {
return Math.PI * Math.pow(getDiameter() / 2, 2);
}
@Override
public double perimeter() {
return Math.PI * getDiameter();
}
@Override
public void draw() {
System.out.println("圆形");
}
}
- 抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类
- 抽象类中可以有构造方法,供子类创建对象时,初始化父类的成员变量
作用
接口
概念
注意事项:
- 接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
- 接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。
语法
[可见度] interface 接口名称 [extends 其他的接口名] {
// 声明变量
// 抽象方法
}
使用
public class 类名称 implements 接口名称{
// ...
}
注意:子类和父类之间是extends 继承关系,类与接口之间是 implements 实现关系。
- USB接口
package package2;
public interface USB {
void openDevice();
void closeDevice();
}
- 笔记本类
package package2;
/**
* Created with Intellij IDEA.
* Description:电脑类
* User: a
* Date: 2022-04-01
* Time: 15:59
*/
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 = new Mouse();
mouse.click();
} else if(usb instanceof keyBoard) {
keyBoard keyBoard = new keyBoard();
keyBoard.inPut();
}
usb.closeDevice();
}
}
- 鼠标类
package package2;
import package2.USB;
/**
* Created with Intellij IDEA.
* Description:鼠标类,实现USB接口
* User: a
* Date: 2022-04-01
* Time: 15:54
*/
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("鼠标点击");
}
}
- 键盘类
package package2;
import package2.USB;
/**
* Created with Intellij IDEA.
* Description:键盘类,实现USB接口
* User: a
* Date: 2022-04-01
* Time: 15:56
*/
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("键盘输入");
}
}
- TestUSB类
package package2;
/**
* Created with Intellij IDEA.
* Description:
* User: a
* Date: 2022-04-01
* Time: 15:40
*/
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();
}
}
运行结果:
特性
- 接口中每一个方法是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。所以我们建议接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性.
public interface USB {
public abstract void method1();
public void method2();
abstract void method3();
void method();
}
- 接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
public interface USB {
int price = 20;
void openDevice();
void closeDevice();
}
public class TestUSB {
public static void main(String[] args) {
System.out.println(USB.price);//可以直接通过接口名访问,说明是静态的
USB.price = 20;//不可以被修改,说明其具有final属性
}
}
Error:(13, 12) java: 无法为最终变量price分配值
- 接口类型是一种引用类型,但是不能直接new接口的对象
public static void main(String[] args) {
USB usb = new USB();
}
Error:(12, 19) java: package2.USB是抽象的; 无法实例化
- 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
public interface USB {
void openDevice() {
System.out.println("打开USB设备");
}
void closeDevice();
Error:(12, 23) java: 接口抽象方法不能带有主体
-
接口,接口方法都是抽象的,所以不必使用abstract关键字,且接口中方法都是共有的。
-
重写接口中方法时,不能使用default访问权限修饰
package package2;
import package2.USB;
public class Mouse implements USB {
@Override
void openDevice() {
System.out.println("打开鼠标");
}
}
Error:(14, 11) java: package2.Mouse中的openDevice()无法实现package2.USB中的openDevice()正在尝试分配更低的访问权限; 以前为public
- JDK 1.8 以后,接口里可以有静态方法和方法体了
- JDK 1.9 以后,允许将方法定义为 private,使得某些复用的代码不会把方法暴露出去。
- JDK 1.8 以后,接口允许包含具体实现的方法,该方法称为"默认方法",默认方法使用 default 关键字修饰
- 接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是.class
- 如果类没有实现接口中的所有的抽象方法,则类必须设置为抽象类。
接口的继承
public interface Person {
void setSex(String sex);
void setAge(int age);
void setBirthDay(int year, int month, int day);
}
public interface Teacher extends Person{
void assignHomework(String homework);
void solveTheProblem(String problem);
}
public interface MathematicsTeacher extends Teacher{
void teachingMath();
}
public interface Psychologist extends Person{
void teachingPsychology();
}
public interface Doctor extends Person{
void treatPatient();
}
- 在Java中,类的多继承是不合法,但接口允许多继承。
public interface Students extends Person,Study{
}
实现多个接口
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 Cat extends Animal implements IRunning{
public Cat(String name) {
super(name);
}
@Override
public void run() {
System.out.println(this.name + "正在用四条腿跑");
}
}
- 青蛙
public class Frog extends Animal implements ISwimming,IRunning{
public Frog(String name) {
super(name);
}
@Override
public void run() {
System.out.println(this.name + "正在用两条腿奔跑");
}
@Override
public void swim() {
System.out.println(this.name + "正在用两条腿游泳");
}
}
- 鱼
public class Fish extends Animal implements ISwimming{
public Fish(String name) {
super(name);
}
@Override
public void swim() {
System.out.println(this.name + "正在用尾巴游泳");
}
}
- 鸭子
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 + "正在用两只脚蹼游泳");
}
}
实现了接口的类,因为它具有这种特性,所以即使忘记类型,但我们知道它具备这种能力,例如,我们实现一个”游泳“方法
public class TestSwimming {
public static void swim (ISwimming swimming) {
System.out.println("我和小动物去游泳");
swimming.swim();
}
public static void main(String[] args) {
Cat cat = new Cat("小小猫");
//swim(cat);
Duck duck = new Duck("小鸭子");
swim(duck);
Fish fish = new Fish("小鱼");
swim(fish);
}
}
即使我们不知道它是否有这种特性,编译器会告诉我们;这里假如不知道猫会游泳
Error:(17, 14) java: 不兼容的类型: package3.Cat无法转换为package3.ISwimming
运行结果:
甚至参数不是“动物”,只有这个类实现了ISwimming接口,比如:航空母舰
public class AircraftCarrier implements ISwimming{
private String name;
public AircraftCarrier(String name) {
this.name = name;
}
@Override
public void swim() {
System.out.println(this.name + "正在海上航行");
}
}
public static void main(String[] args) {
AircraftCarrier aircraftCarrier = new AircraftCarrier("航空母舰");
swim(aircraftCarrier);
}
接口使用实例
public class Student implements Comparable {
private String name;
private double score;
public Student(String name, double score) {
this.name = name;
this.score = score;
}
@Override
public String toString() {
return "[" + this.name + ":" + this.score + "]";
}
@Override
public int compareTo(Object o) {
Student s = (Student)o;
if(this.score > s.score) {
return -1;
} else if (this.score < s.score) {
return 1;
} else {
return 0;
}
}
}
public class Test {
public static void main(String[] args) {
Student[] students = new Student[] {
new Student("张三",92),
new Student("李四",96),
new Student("王五",97),
new Student("赵六",95)
};
Arrays.sort(students);
System.out.println(Arrays.toString(students));
}
}
对象克隆
我们克隆基本类型(boolean,char,byte,short,float,double.long)非常简单,例如
int a = 10;
int b = a;
并且我们修改b的值并不会影响a的值。但对象的克隆并不是这么简单,如果我们按照这种方法来克隆:
public class Money {
public double m = 100;
}
public class Person {
public Money money = new Money();
}
public class Test {
public static void main(String[] args) {
Person person1 = new Person();
Person person2 = person1;
System.out.println("通过Person2修改前的结果");
System.out.println(person1.money.m);
System.out.println(person2.money.m);
System.out.println("通过Person2修改后的结果");
person2.money.m = 200;
System.out.println(person1.money.m);
System.out.println(person2.money.m);
}
}
运行结果:
结果很明显,这里的 Person person2 = person1;
让person2的引用指向person1所引用的对象;所以,person1和person2指向堆的同一个对象。这里的克隆就是浅拷贝。
Object类中的clone()
但在Object类中提供的clone()方法;
在这个文件夹下找到clone()的声明
protected native Object clone() throws CloneNotSupportedException;
我们来看官方文档对其解释:
为什么要克隆??
浅克隆
在浅拷贝中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。
-
被复制的类需要实现Clonenable接口(不实现的话在调用clone方法会抛出CloneNotSupportedException异常), 该接口为标记接口(不含任何方法)
-
覆盖clone()方法,访问修饰符设为public。方法中调用super.clone()方法得到需要的复制对象。
package package5;
/**
* Created with Intellij IDEA.
* Description:
* User: a
* Date: 2022-04-03
* Time: 18:52
*/
public class Student implements Cloneable{
private String name;//引用类型
private int age;//基本类型
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public Object clone() {
Student stu = null;
try{
stu = (Student)super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return stu;
}
}
package package5;
/**
* Created with Intellij IDEA.
* Description:
* User: a
* Date: 2022-04-03
* Time: 18:55
*/
public class Test {
public static void main(String[] args) {
Student stu1 = new Student("张三",20);
Student stu2 = (Student)stu1.clone();
System.out.println("通过stu2修改前");
System.out.println("学生1" + stu1.getName() + " " + stu1.getAge());
System.out.println("学生2" + stu2.getName() + " " + stu2.getAge());
System.out.println("通过stu2修改前");
stu2.setAge(30);
stu2.setName("李四");
System.out.println("学生1" + stu1.getName() + " " + stu1.getAge());
System.out.println("学生2" + stu2.getName() + " " + stu2.getAge());
}
}
运行结果:
可以看出其sut2被修改了,如果还不相信它们是不是同一对象,我们可以加上这一句;
System.out.println(stu1 == stu2);
结果:false
上面的克隆叫做浅克隆。下面我们解释深克隆
深克隆
现在我们新增一个School类
- Student类
package package5;
/**
* Created with Intellij IDEA.
* Description:
* User: a
* Date: 2022-04-03
* Time: 18:52
*/
public class Student implements Cloneable{
private String name;//引用类型
private School school;//类类型
public School getSchool() {
return school;
}
public void setSchool(School school) {
this.school = school;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public Object clone() {
Student stu = null;
try{
stu = (Student)super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return stu;
}
}
- School类
public class School {
private String school;
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
}
public static void main(String[] args) {
School school = new School();
school.setSchool("AAAA");
Student stu1 = new Student();
stu1.setName("0.0");
stu1.setSchool(school);
Student stu2 = (Student)stu1.clone();
System.out.println("修改前");
System.out.println("学生1:"+ stu1.getName() +" " + stu1.getSchool().getSchool());
System.out.println("学生2:"+ stu2.getName() +" " + stu2.getSchool().getSchool());
System.out.println("修改后");
school.setSchool("BBBB");
System.out.println("学生1:"+ stu1.getName() +" " + stu1.getSchool().getSchool());
System.out.println("学生2:"+ stu2.getName() +" " + stu2.getSchool().getSchool());
}
结果:
所以,我们需要将School类可复制化,并且修改clone方法;
- 在School类增加实现接口
@Override
public Object clone() {
School sch = null;
try{
sch = (School) super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return sch;
}
- 修改Student类的clone()
@Override
public Object clone() {
Student stu = null;
try{
stu = (Student)super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
stu.school = (School)school.clone();
return stu;
}
运行结果: