在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
像这种没有实际工作的方法, 我们可以把它设计成一个 抽象方法(abstract method), 包含抽象方法的类我们称为 抽象类(abstract class).
在Java中,一个类如果被 abstract 修饰称为抽象类,抽象类中被 abstract 修饰的方法称为抽象方法,抽象方法不用给出具体的实现体。
注意:抽象类也是类,内部可以包含普通方法和属性,甚至构造方法。
我们可以先来看一段代码:
public abstract class Animal {
public String name;
public int age;
abstract void eat();
}
class Dog extends Animal{
Dog(String name,int age){
this.name = name;
this.age = age;
}
public void eat(){
System.out.println(name +"吃狗粮!");
}
}
class Cat extends Animal{
Cat(String name,int age){
this.name = name;
this.age = age;
}
public void eat(){
System.out.println(name +"吃猫粮!");
}
}
class Test{
public static void main(String[] args) {
Dog dog1 = new Dog("旺旺",10);
Cat cat1 = new Cat("喵喵",5);
dog1.eat();
cat1.eat();
}
}
很多语法的出现,都是为了能够让程序员更早的发现错误。
接口的定义格式与定义类的格式基本相同,将class关键字换成 interface 关键字,就定义了一个接口。 接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用。在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型。
接口的定义格式与定义类的格式基本相同,将class关键字换成 interface 关键字,就定义了一个接口。
public interface 接口名称{
// 抽象方法
public abstract void method1(); // public abstract 是固定搭配,可以不写
public void method2();
abstract void method3();
void method4();
// 注意:在接口中上述写法都是抽象方法
}
提示:
接口不能直接使用,必须要有一个"实现类"来"实现"该接口,实现接口中的所有抽象方法。
public class 类名称 implements 接口名称{
// ...
}
注意:子类和父类之间是extends 继承关系,类与接口之间是 implements 实现关系。
package demo1;
import java.util.Arrays;
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 +
'}';
}
}
public class Test {
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("zhang",10);
students[1] = new Student("huang",20);
students[2] = new Student("li",30);
Arrays.sort(students);
System.out.println(Arrays.toString(students));
}
}
可以看到代码报错了,在比较时要规定一下用什么来比较。姓名,年龄。 自定义的学生类需要具备可以比较的功能。
方法一:(根据年龄比较大小)
总结:如果我们以后自定义的类型,一定要记住,如果要比较大小,那么必须要让这个类具备可以比较的功能,此时可以选择实现接口Compara。
方法二:(根据姓名比较大小)
也可以这样写:
方法三:函数冒泡排序法
Java 中内置了一些很有用的接口, Clonable 就是其中之一. Object 类中存在一个 clone 方法, 调用这个方法可以创建一个对象的 “拷贝”. 但是要想合法调用 clone 方法, 必须要先实现 Clonable 接口, 否则就会抛出 CloneNotSupportedException 异常.
空接口、标记接口:当前类可以被克隆
以下以克隆为例再举一个接口的实例:
class Person implements Cloneable{
public int id;
@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 person2 = (Person)person.clone();
}
}
这就是浅拷贝。
class Money implements Cloneable{
public double m = 66.66;
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Person implements Cloneable{
public int id;
public Money money = new Money();
@Override
protected Object clone() throws CloneNotSupportedException {
Person tmp = (Person)super.clone();
tmp.money = (Money)this.money.clone();
return tmp;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
'}';
}
}
public class Test2 {
public static void main(String[] args) throws CloneNotSupportedException{
Person person = new Person();
Person person2 = (Person)person.clone();
person2.money.m =8888;
System.out.println("person:"+person.money.m);
System.out.println("person2 :"+person2.money.m);
}
}
图解代码:
核心区别: 抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中不能包含普通方法, 子类必须重写所有的抽象方法. 抽象类存在的意义是为了让编译器更好的校验
Object是Java默认提供的一个类。Java里面除了Object类,所有的类都是存在继承关系的。默认会继承Object父类。即所有类的对象都可以使用Object的引用进行接收。
如果要打印对象中的内容,可以直接重写Object类中的toString()方法。
在Java中,== 进行比较时: a.如果左右两侧是基本类型变量,比较的是变量中值是否相同 b.如果左右两侧是引用类型变量,比较的是引用变量地址是否相同 c.如果要比较对象中内容,必须重写Object中的equals方法,因为equals方法默认也是按照地址比较的
涉及数据结构,后面我们再深入学习
hashcode方法源码:
public native int hashCode();
该方法是一个native方法,底层是由C/C++代码写的。我们看不到。
结论: 1、hashcode方法用来确定对象在内存中存储的位置是否相同 2、事实上hashCode() 在散列表中才有用,在其它情况下没用。在散列表中hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。