💢💢在Java中,一个类如果被 abstract 修饰称为抽象类,抽象类中被 abstract 修饰的方法称为抽象方法,抽象方法不用给出具体的实现体。
我们先来看一个简单的例子🌰
// 抽象类和抽象方法需要被 abstract 关键字修饰
abstract class Animal {
// 抽象类中的方法一般要求都是抽象方法,抽象方法没有方法体
abstract void eat();
// 抽象类也是类,也可以增加普通方法和属性
public double getArea(){
return area;
}
protected double area; // 面积
}
初看上面是不是没啥问题,但是当我们对这个类进行实例化的时候,就会发现:
像这样的类是不是就没有包含足够的信息来描绘一个具体的对象,因此也就不能直接去实例化对象了。那我们应该怎么解决这个实例化问题呢?
// 抽象类和抽象方法需要被 abstract 关键字修饰
abstract class Animal {
// 抽象类中的方法一般要求都是抽象方法,抽象方法没有方法体
abstract void eat();
// 抽象类也是类,也可以增加普通方法和属性
public double getArea(){
return area;
}
protected double area; // 面积
}
public class Test{
public static void main(String[] args) {
Animal animal = new Animal() {
@Override
void eat() {
System.out.println("重写");
}
};
}
}
只需要对 abstract类的抽象方法实例化之后进行重写即可。
注意:抽象类也是类,内部可以包含普通方法和属性,甚至构造方法
🍉抽象类不能直接实例化对象,无法创建对象,抽象类是被子类来继承的
Animal animal = new Animal();
// 编译出错
Error:(30, 23) java: Animal是抽象的; 无法实例化
🥑抽象方法不能是 private 的
abstract class Animal {
abstract private void eat(); //抽象方法不能是 private 的
}
// 编译出错
Error:(4, 27) java: 非法的修饰符组合: abstract和private
🥝抽象方法不能被final和static修饰,因为抽象方法要被子类重写
public abstract class Animal {
abstract final void methodA();
abstract public static void methodB();
}
// 编译报错:
// Error:(20, 25) java: 非法的修饰符组合: abstract和final
// Error:(21, 33) java: 非法的修饰符组合: abstract和static
🍋🟩抽象类必须被继承,并且继承后子类要重写父类中的抽象方法,否则子类也是抽象类,必须要使用 abstract 修饰
abstract class Animal {
abstract void eat();
// 抽象类也是类,也可以增加普通方法和属性
public double getArea(){
return area;
}
protected double area; // 面积
}
class Dog extends Animal{
@Override
void eat() {
// 重写
}
}
abstract class Cat extends Animal{
}
🥬其他特性:
🍅那既然一个类不能直接实例化,那这种抽象类存在的意义是什么呀🤔?我们接着往下看
🍅抽象类存在的一个最大意义就是被继承,当被继承后就可以利用抽象类实现多态。
代码示例如下:
class Dog extends Animal{
@Override
void eat() {
System.out.println("小狗吃东西");
}
}
public class Test {
public static void main(String[] args) {
//Animal animal = new Animal(); // 抽象类虽然无法直接实例化
// 但可以把一个普通类对象传给一个抽象类的引用呀,即父类引用指向子类对象
Animal animal = new Dog(); // 这称作:向上转型
/*Dog dog = new Dog();
Animal animal = dog; // 这是向上转型的另一种写法*/
animal.eat(); // 通过父类引用调用被子类重写的方法
}
}
向上转型的具体,我们之前在多态那篇博客【Java 基础】:三大特征之多态-CSDN博客那就已经讲过,就不过多讲解了,只需要知道向上转型是:父类引用指向子类对象
抽象类是类和类之间的共同特征,将这些共同特征进一步形成抽象类,由于类本身不存在,所以抽象类无法创建对象。 类到对象是实例化,对象到类是抽象 抽象方法不能被 final 修饰,因为抽象方法就是被子类实现的
抽象方法表示没有实现的方法,没有方法体的方法
但是不能说java语言中没有方法体的方法都是抽象方法。 因为Object类中就有很多方法都没有方法体,都是以“;”结尾的,但他们都不是抽象方法
🍑抽象类是从多个类中抽象出来的模板,如果将这种抽象进行的更彻底,则可以提炼出一种更加特殊的“抽象类”——接口(Interface)🤔。
📝接口是Java中最重要的概念之一,它可以被理解为一种特殊的类,不同的是接口的成员没有执行体,是由全局常量和公共的抽象方法所组成😎。
如何定义一个接口呢?下面我们来看一个栗子🌰
// 定义格式如下:
//[修饰符列表] interface 接口名{}
// 实例:
public interface Test{
// 定义变量
int a = 10; // 接口当中的成员变量默认都是public static final
// 抽象方法
public abstract void metho(); // public abstract 是固定搭配,可以不写
void method(); // 接口当中的成员方法默认都是public abstract, 更推荐用第二种来定义方法
}
提示:
接口不能直接使用,必须要有一个"实现类"来"实现"该接口,实现接口中的所有抽象方法。
/* 语法格式 */
/* class 类名称 implements 接口名称{
// ...
} */
//实例
interface USB {
void openDevice(); // 默认是public的
void closeDevice(); // 默认是public的
}
class Mouse implements USB {
@Override
public void openDevice() {
System.out.println("打开鼠标");
}
@Override
public void closeDevice() {
}
// ...
}
注:子类和父类之间是extends 继承关系,类与接口之间是 implements实现关系。
🍉接口类型是一种引用类型,但是不能直接new接口的对象
public class TestUSB {
public static void main(String[] args) {
USB usb = new USB();
}
}
// Error:(10, 19) java: USB是抽象的; 无法实例化
🥑接口中每一个方法都是public的抽象方法,所以不能有方法体。 即接口中的方法会被隐式的指定为 public abstract(只能是public abstract,其他修饰符都会报错)
//定义抽象方法的时候可以省略修饰符public abstract
public interface USB {
// Error:(4, 18) java: 此处不允许使用修饰符private
private void openDevice();
void closeDevice();
}
🥝接口中的方法是不能在接口中实现的,只能由实现接口的类来实现
public interface USB {
void openDevice();
// 编译失败:因为接口中的方式默认为抽象方法
// Error:(5, 23) java: 接口抽象方法不能带有主体
void closeDevice(){
System.out.println("关闭USB设备");
}
}
🍋🟩重写接口中方法时,不能使用默认的访问权限
/* 语法格式 */
/* public class 类名称 implements 接口名称{
// ...
} */
//实例
interface USB {
void openDevice(); // 默认是public的
}
class Mouse implements USB {
@Override
void openDevice() { //解决:函数前加个权限 public 即可
System.out.println("打开鼠标");
}
}
// 编译报错,重写USB中openDevice方法时,不能使用默认修饰符
// 正在尝试分配更低的访问权限; 以前为public
🍈接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量
interface USB {
double brand = 3.0; // 默认被:final public static修饰
void openDevice();
void closeDevice();
}
public class Test {
public static void main(String[] args) {
System.out.println(USB.brand); // 可以直接通过接口名访问,说明是静态的
// 编译报错:Error:(12, 12) java: 无法为最终变量brand分配值
USB.brand = 2.0; // 说明brand具有final属性,无法被再次赋值
}
}
🍀支持多继承,且一个接口可以继承多个接口,每一个interface 都会生成一个class后缀名的文件
interface a{
}
interface b extends a{
}
interface c extends a,b{
}
🥬其他特性:
通过接口实现多态
🍌刚才我们是用抽象类来实现多态,那么现在我们来尝试使用接口去实现多态😎
interface Animal{
int a = 10; //接口当中的成员变量默认都是public static final
int b = 23;
void eat(); //接口当中的成员方法一般只能是抽象方法,默认是public abstract(JDK1.8以前)
default void show() {
System.out.println("接口中的其他方法");//接口中的其他方法也可以实现,但要用default修饰
}
public static void test() {
System.out.println("这是接口当中的一个静态的方法");
}
}
// 一个普通的类要想实现接口,可以用implement,
//因为接口也是抽象方法的,所以实现接口的这个类也要重写抽象方法
class Dog implements Animal{
@Override
public void eat() {
System.out.println("小狗");
}
}
class Cat implements Animal{
@Override
public void eat() {
}
}
public class Test{
public static void main(String[] args) {
Animal[] animals = {new Dog(),new Cat()};
for(Animal animal: animals){
animal.eat();
}
}
}
假如有以下学生信息这样的代码,想根据学生年龄来进行排序
class Student {
public String name;
public int age;
public Student(String name,int age){
this.name=name;
this.age=age;
}
public String toString(){
return "["+this.name+":"+this.age+"]";
}
}
public class Test {
public static void main(String[] args) {
Student[] student=new Student[]{
new Student("zhangsan",21),
new Student("lisi",17),
new Student("wangwu",33),
};
System.out.println(Arrays.toString(student));
}
}
Arrays.sort(students);
System.out.println(Arrays.toString(students));
// 运行出错, 抛出异常.
Exception in thread "main" java.lang.ClassCastException: Student cannot be cast to java.lang.Comparable
为什么会出现问题呢:
class Student implements Comparable{
public String name;
public int age;
public Student(String name,int age){
this.name=name;
this.age=age;
}
public String toString(){
return "["+this.name+":"+this.age+"]";
}
@Override
public int compareTo(Object o) {
Student student=(Student) o;
return this.age-student.age;
}
}
实现comparable后要进行重写 compareTo 方法
在 sort 方法中会自动调用 compareTo 方法. compareTo 的参数是 Object , 其实传入的就是 Student 类型的对象.然后比较当前对象和参数对象的大小关系
注意事项: 对于 sort 方法来说, 需要传入的数组的每个对象都是 "可比较" 的, 需要具备 compareTo 这样的能力. 通过重写 compareTo 方法的方式, 就可以定义比较规则.
但是在上述代码中存在一个问题,就是无法通过名字进行比较,那么此时我们就要用到comparator接口
// 按照年龄排序
class Agecompare implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.age- o2.age;
}
}
// 按照名字排序
class Namecompare implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.name.compareTo(o2.name);
}
}
// 使用方法
Arrays.sort(student,new Agecompare());
Arrays.sort(student,new Namecompare());
两种写法都叫做比较器,但是后者更加灵活,可以根据所需进行编写,而且两者可以共同存在
Object类是Java中java.lang包下的核心类,Java里面除了Object类,所有的类都是存在继承关系的。默认会继承Object父类。即所有类的对象都可以使用Object的引用进行接收。
案例:(使用Object类型接收所有类的对象)
class A{};
class B{};
public class object_lei {
public static void main(String[] args) {
function(new A());
function(new B());
}
public static void function(Object obj) {
System.out.println(obj);
}
}
// 执行结果:
Test.A@3b07d329
Test.B@404b9385
Object 类属于java.lang包,此包下的所有类在使用时无需手动导入,系统会在程序编译期间自动导入
方法名 | 作用 |
---|---|
clone() | 保护方法,实现对象的浅复制,只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常。 |
getClass() | final方法,返回Class类型的对象,反射来获取对象。 |
toString() | 该方法用得比较多,一般子类都有覆盖,来获取对象的信息。 |
finalize() | 该方法用于释放资源。因为无法确定该方法什么时候被调用,很少使用。 |
equals() | 比较对象的内容是否相等 |
hashCode() | 该方法用于哈希查找,重写了equals方法一般都要重写hashCode方法。这个方法在一些具有哈希功能的Collection中用到。 |
wait() | 在后面线程那里会设计,使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait()方法一直等待,直到获得锁或者被中断。 wait (long timeout) 设定一个超时间隔,如果在规定时间内没有获得锁就返回。 |
notify() | 该方法唤醒在该对象上等待的某个线程。 |
notifyAll() | 该方法唤醒在该对象上等待的所有线程。 |
Clonable 和 深拷贝
对象克隆是一种创建对象的精确副本的方法。 Object类的clone()方法用于克隆对象java.langCloneable()接口必须由我们要创建其对象克隆的类实现。如果我们不实现Cloneable()接口,clone()方法将生成CloneNotSupportedExceptionequals。
为什么要使用clone()方法?
clone()方法保存用于创建对象的精确副本的额外处理任务。 如果我们使用new关键字执行它,它将需要执行大量的处理,这就是为什么我们使用对象克隆。
对象克隆的优点:
// clone 的基本语法:
protected Object clone() throws CloneNotSupportedException
// 案例使用
// 在 Object 的子类Person 中要完成重写
class P implements Cloneable{
public int age;
public P(int age){
this.age=age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public String toString() {
return "Preson="+this.age;
}
}
public class CloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
P p1=new P(10);
// 此时就要注意,object是父类,要完成向下转型,那么我们就要强转
P p2=(P) p1.clone();
System.out.println(p1.toString());
System.out.println(p2.toString());
}
}
// 结果输出
Preson=10
Preson=10
因此,clone()将对象的值复制到另一个对象。 因此在实际应用中我们不需要编写显式代码将对象的值复制到另一个对象。如果通过new关键字创建另一个对象并将另一个对象的值赋给这个对象,则需要对该对象进行大量处理。 所以为了节省额外的处理任务,我们使用clone()方法。
之前在这篇文章【Java 基础】类和对象(构造&this&封装&static&代码块已经讲过了,此处就不再过多讲解
// Object类中的toString()方法实现:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
在Java中,==进行比较时:
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.age = age ;
this.name = name;
}
}
// Object类中的equals方法
public boolean equals(Object obj) {
return (this == obj); // 使用引用中的地址直接来进行比较
}
public class Test {
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.equals(p2)); // 输出false
}
}
Person类重写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;
}
}
Java中接口和抽象类的定义语法分别为interface与abstract关键字。
相同点:
不同点:
【*★,°*:.☆( ̄▽ ̄)/$:*.°★* 】那么本篇到此就结束,希望我的这篇博客可以给你提供有益的参考和启示,感谢大家支持!!!祝大家天天开心💞 💞 💞