[Java SE] Abstract classes and interfaces

Table of contents

【1】Abstract class

【1.1】Abstract class concept

【1.2】Abstract class syntax

【1.3】Abstract class characteristics

【1.4】The role of abstract classes

【2】Interface

【2.1】The concept of interface

【2.2】Grammar rules

【2.3】Interface usage

【2.4】Interface characteristics

【2.5】Implement multiple interfaces

【2.6】Inheritance between interfaces

【2.7】Interface usage examples

【2.8】Clonable interface and deep copy

【2.9】The difference between abstract class and interface

【3】Object class

【3.1】Print object information toString method

【3.2】Object comparison equals method

【3.3】hashcode method


【1】Abstract class

【1.1】Abstract class concept

        In the object-oriented concept, all objects are described by classes, but conversely, not all classes are used to describe objects. If a class does not contain enough information to describe a specific object , such a class is an abstract class .

【for example】

        In the example of printing graphics, we found that the draw method in the parent class Shape does not seem to have any actual work. The main drawing of graphics is done by the draw methods of various subclasses of Shape. Things like this have no actual work. method, we can design it as an abstract method, and the class containing the abstract method is called an abstract class .

【1.2】Abstract class syntax

        In Java, if a class is modified by abstract, it is called an abstract class. The method modified by abstract in the abstract class is called an abstract method. Abstract methods do not need to give a specific implementation body.

// 抽象类:被abstract访问修饰符修饰。
abstract class Shape {
    // 抽象方法:被abstract访问修饰符修饰,没有方法体。
    public abstract void draw();
    abstract void calArea();

    // 抽象类也是类,也可以增加普通方法和属性。
    public double getArea() {
        return _arga;
    }
   
    protected double _arga; // 字段:面积
}

[Note] Abstract classes are also classes, which can contain ordinary methods, properties, and even constructors.

【1.3】Abstract class characteristics

  1. Abstract classes cannot instantiate objects directly

Shape shape = new Shape();

// 编译出错
Error:(30, 23) java: Shape是抽象的; 无法实例化
  1. Abstract methods cannot be private

abstract class Shape {
	abstract private void draw();
}

// 编译出错
Error:(4, 27) java: 非法的修饰符组合: abstract和private

[Note] When the abstract method does not add an access qualifier, the default is public.

  1. Abstract methods cannot be modified by final and static, because abstract methods must be overridden by subclasses

public abstract class Shape {
	abstract final void methodA();
	abstract public static void methodB();
}

// 编译报错:
// Error:(20, 25) java: 非法的修饰符组合: abstract和final
// Error:(21, 33) java: 非法的修饰符组合: abstract和static
  1. Abstract classes must be inherited, and after inheritance, the subclass must override the abstract method in the parent class. Otherwise, the subclass is also an abstract class and must be modified with abstract.

// 抽象类:被abstract访问修饰符修饰。
abstract class Shape {
    // 抽象方法:被abstract访问修饰符修饰,没有方法体。
    public abstract void draw();

    protected double _area; // 字段:面积
}

// 矩形类
class Rect extends Shape {
    private double length;
    private double width;
    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;
    }
}

// 圆类:
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;
    }
}

// 三角形类:
abstract 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
    //double calcArea(); // 编译失败:要么实现该抽象方法,要么将三角形设计为抽象类
}
  1. An abstract class does not necessarily contain abstract methods, but a class with abstract methods must be an abstract class.

  2. An abstract class can have a constructor method for the subclass to initialize the member variables of the parent class when creating an object.

【1.4】The role of abstract classes

        The abstract class itself cannot be instantiated. To use it, you can only create a subclass of the abstract class. Then let the subclass override the abstract method in the abstract class.

        Some people may say, ordinary classes can also be inherited, ordinary methods can also be overridden, why do we have to use abstract classes and abstract methods?

That's true. But using abstract classes is equivalent to an extra layer of compiler verification.

        The scenario of using an abstract class is like the above code. The actual work should not be done by the parent class, but by the subclass. If you accidentally misuse it as a parent class at this time, you will not get an error using the ordinary class compiler. . But if the parent class is an abstract class, an error will be prompted during instantiation. Let us find the problem as early as possible.

The purpose of many syntaxes is to "prevent errors." For example, the final we have used is similar. If the created variable is not modified by the user, isn't it equivalent to a constant? But adding final can prevent it from being accidentally modified. , let the compiler remind us in time.

Making full use of the compiler's verification is very meaningful in actual development.

【2】Interface

【2.1】The concept of interface

        In real life, examples of interfaces abound, such as USB ports on laptops, power sockets, etc.

        The computer's USB port can be plugged into: U disk, mouse, keyboard...all devices that comply with the USB protocol. The power socket can be plugged into: computers, TVs, rice cookers...all the equipment that meets the specifications can be plugged into the above example. It can be seen that interfaces are public behavioral standards. When everyone implements them, as long as they comply with the standards, they can be used universally. In Java, an interface can be thought of as: a common specification for multiple classes, which is a reference data type.

【2.2】Grammar rules

        The definition format of an interface is basically the same as that of a class. Replace the class keyword with the interface keyword to define an interface.

interface ISharp {
    int size = 10;  // 默认是public static final
    void dorw();    // 默认是public abstract

    // 接口中的方法不能实现,如果非要实现需要加default
    public default void func01() {
        System.out.println("默认方法");
    }

    // 接口中可以有静态方法
    public static void func02() {
        System.out.println("静态方法");
    }
}

【hint】

  1. When creating an interface, the name of the interface generally starts with a capital letter I.

  2. Interfaces are generally named using "adjective" words.

  3. Alibaba Coding Standards stipulate that methods and properties in interfaces should not be modified with any modification symbols to keep the code concise.

【2.3】Interface usage

The interface cannot be used directly. There must be an "implementation class" to "implement" the interface and implement all abstract methods in the interface.

public class 类名称 implements 接口名称{
	// ...
}	

[Note] The inheritance relationship between subclasses and parent classes is extends, and the implementation relationship between classes and interfaces is implements.

[Interface usage code example]

/** - 定义接口
 */
interface ISharp {
    public abstract void drow();
}
/** - 继承接口
 */
class Rect implements ISharp {
    @Override
    public void drow() {
        System.out.println("矩形!");
    }
}
/** - 继承接口
 */
class Flower implements  ISharp {
    @Override
    public void drow() {
        System.out.println("花!");
    }
}

/** - 继承接口
 */
class Cyclic implements  ISharp {
    @Override
    public void drow() {
        System.out.println("圆!");
    }
}

public class Main {
    public static void drowMap(ISharp iSharp) {
        iSharp.drow();
    }
    
    public static void main(String[] args) {
        // 接口实现多态:向上转型
        drowMap(new Rect());
        drowMap(new Flower());
        drowMap(new Cyclic());
    }
}

[Interface example code]

/** - (基础方法)定义接口
 */
interface USB {
    public abstract void openDevice();
    public abstract void closeDevice();
}

/** - (继承接口)鼠标
 */
class Mouse implements USB {

    @Override
    public void openDevice() {
        System.out.println("打开鼠标");
    }

    @Override
    public void closeDevice() {
        System.out.println("关闭鼠标");
    }

    public void click() {
        System.out.println("点击鼠标");
    }
}

/** - (继承接口)键盘
 */
class KeyBoard implements USB {

    @Override
    public void openDevice() {
        System.out.println("打开键盘");
    }

    @Override
    public void closeDevice() {
        System.out.println("关闭键盘");
    }

    public void inPut() {
        System.out.println("键盘输入");
    }
}

/** - 电脑
 */
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 Main {
    public static void main(String[] args) {
        Computer computer = new Computer();
        computer.powerOn();

        /* 使用设备(类里面的函数处理类型判断) */
        computer.useDevice(new Mouse());
        computer.useDevice(new KeyBoard());

        computer.powerOff();
    }
}

【2.4】Interface characteristics

  1. The interface type is a reference type, but the object of the interface cannot be directly new.

public class TestUSB {
	public static void main(String[] args) {
		USB usb = new USB();
	}
} 
// Error:(10, 19) java: day20210915.USB是抽象的; 无法实例化
  1. Every method in the interface is a public abstract method, that is, the method in the interface will be implicitly designated as public abstract (it can only be public abstract, other modifiers will report an error)

public interface USB {
	// Error:(4, 18) java: 此处不允许使用修饰符private
	private void openDevice();
	void closeDevice();
}
  1. Methods in an interface cannot be implemented in the interface and can only be implemented by the class that implements the interface.

public interface USB {
	void openDevice();
	// 编译失败:因为接口中的方式默认为抽象方法
	// Error:(5, 23) java: 接口抽象方法不能带有主体
	void closeDevice(){
		System.out.println("关闭USB设备");
	}
}
  1. When overriding methods in an interface, you cannot use the default access permissions.

public interface USB {
    void openDevice(); // 默认是public的
    void closeDevice(); // 默认是public的
}
public class Mouse implements USB {
    @Override
    void openDevice() {
        System.out.println("打开鼠标");
    }
    // ...
}
// 编译报错,重写USB中openDevice方法时,不能使用默认修饰符
// 正在尝试分配更低的访问权限; 以前为public
  1. Interfaces can contain variables, but variables in the interface are implicitly designated as public static final  variables .

public interface USB {
    double brand = 3.0; // 默认被:final public static修饰
    void openDevice();
    void closeDevice();
}

public class TestUSB {
    public static void main(String[] args) {
        System.out.println(USB.brand); // 可以直接通过接口名访问,说明是静态的
        // 编译报错:Error:(12, 12) java: 无法为最终变量brand分配值
        USB.brand = 2.0; // 说明brand具有final属性
    }
}
  1. There cannot be static code blocks and constructors in the interface.

public interface USB {
    // 编译失败
    public USB(){
    } 
    {} // 编译失败
    void openDevice();
    void closeDevice();
}
  1. Although the interface is not a class, the suffix format of the bytecode file after the interface is compiled is also .class

  2. If the class does not implement all the abstract methods in the interface, the class must be set as an abstract class

  3. In JDL 1.8: Interfaces can also contain default methods.

  4. If you don't want to override the interface methods and add abstract to the class, then define the class as an abstract class. However, if this class is inherited by other classes, then it must be rewritten.

【Summarize】

  • Use interface to modify the interface.

  • Member methods in interfaces cannot have specific implementations and are public by default.

    • Abstract method: The default is public abstract method.

    • Starting from JDK 1.8, there are methods that can be implemented, but this method can only be modified by default.

    • Interfaces can have static methods.

  • Member variables are public static final by default.

  • Interface cannot be instantiated.

  • Implementations are used to implement relationships between classes and interfaces.

【2.5】Implement multiple interfaces

        In Java, there is single inheritance between classes. A class can only have one parent class. That is, multiple inheritance is not supported in Java , but a class can implement multiple interfaces . The following uses classes to represent a group of animals

/** - 基类(动物)
 */
class Animal {
    protected String _name;
    protected int _age;

    /* 构造函数 */
    public Animal(String name, int age) {
        this._name = name;
        this._age = age;
    }
}

In addition, we provide a set of interfaces, representing "flying", "running", and "swimming" respectively.

/** - 接口(会飞)
 */
interface IFlying {
    void flyint();
}

/** - 接口(会跑)
 */
interface IRunning {
    void running();
}

/** - 接口(会游泳)
 */
interface ISwimming {
    void swimming();
}

Next we create several specific animal cats, which can run

/** - 猫的行为
 */
class Cat extends Animal implements IRunning {
    public Cat(String name, int age) {
        super(name, age);
    }
    @Override
    public void running() {
        System.out.println("我的名字叫:" + _name + "->我会跑!");
    }
}

Fish can swim

/** - 鱼的行为
 */
class Fish extends Animal implements ISwimming {
    public Fish(String name, int age) {
        super(name, age);
    }
    @Override
    public void swimming() {
        System.out.println("我的名字叫:" + _name + "->我会游泳!");
    }
}

Ducks can swim, run and fly

/** - 鸭子行为
 */
class Duck extends Animal implements IRunning, IFlying, ISwimming {
    public Duck(String name, int age) {
        super(name, age);
    }

    @Override
    public void flyint() {
        System.out.println("我的名字叫:" + _name + "->我会飞!");
    }

    @Override
    public void running() {
        System.out.println("我的名字叫:" + _name + "->我会飞游跑!");
    }

    @Override
    public void swimming() {
        System.out.println("我的名字叫:" + _name + "->我会游泳!!");
    }
}

[Note] When a class implements multiple interfaces, the abstract methods in each interface must be implemented, otherwise the class must be set as an abstract class.

Tip, use ctrl + i in IDEA to quickly implement the interface

public static void main(String[] args) {
    Cat cat = new Cat("猫", 20);
    cat.running();
    
    Fish fish = new Fish("鱼", 21);
    fish.swimming();
    
    Duck duck = new Duck("鸭子", 32);
    duck.swimming();
    duck.flyint();
    duck.running();
}

The above code shows the most common usage in Java object-oriented programming: a class inherits a parent class and implements multiple interfaces at the same time. The meaning expressed by inheritance is is-a semantics, while the meaning expressed by interface is that it has xxx characteristics.

  • Cat is an animal with the characteristic of running.

  • Frog is also an animal that can run and swim.

  • Ducks are also animals that can run, swim, and fly.

What are the benefits of this design? Always keep the benefits of polymorphism in mind and let programmers forget about types . With interfaces, users of classes do not have to pay attention to specific types, but only whether a certain class has certain capabilities.

For example: Anyone who can run can call standard methods:

// 此方法:拥有会跑的接口的对象都可以进行传参,并且会自动调用。
public static void run(IRunning runing) {
    runing.running();
}

public static void main(String[] args) {   
    // 自动调用行为,对比
    run(cat);
    // run(fish); // 鱼不会跑,所以不可以使用run方法
    run(duck);
}

【2.6】Inheritance between interfaces

        In Java, there is single inheritance between classes. A class can implement multiple interfaces, and interfaces can have multiple inheritance. That is: using interfaces can achieve the purpose of multiple inheritance.

        An interface can inherit an interface to achieve reuse. Use the extends keyword.

【Example code】

/** - 接口(会飞)
 */
interface IFlying {
    void flyint();
}

/** - 接口(会跑)
 */
interface IRunning {
    void running();
}

/** - 接口(会游泳)
 */
interface ISwimming {
    void swimming();
}

/** - 接口的继承
 */
interface ExtendInterface extends IFlying, IRunning, ISwimming {

}

/** - TestInter这个类需要实现所有的接口
 */
class TestInter implements ExtendInterface {

    @Override
    public void flyint() {

    }

    @Override
    public void running() {

    }

    @Override
    public void swimming() {

    }
}

        Create a new interface IAmphibious through interface inheritance, which means "amphibious". At this time, the Frog class created to implement the interface continues to implement the run method and also needs to implement the swim method.

        Create a new interface IAmphibious through interface inheritance, which means "amphibious". At this time, the Frog class created to implement the interface continues to implement the run method and also needs to implement the swim method.

        Inheritance between interfaces is equivalent to merging multiple interfaces together

【2.7】Interface usage examples

When learning arrays, I tried sorting the array:

public static void main(String[] args) {
    int[] array = {1,3,65,7,8,9,0,3,6};
    Arrays.sort(array);
    System.out.println(Arrays.toString(array)); // 打印结果[0, 1, 3, 3, 6, 7, 8, 9, 65]
}

[Method 1 for sorting object 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 +
                '}';
    }
}

Given an array of student objects, sort the elements in this object array (sorted by age).

public static void main(String[] args) {
    Student[] students = new Student[]{
            new Student("张三", 12),
            new Student("李四", 23),
            new Student("王五", 42),
            new Student("赵六", 13),
    };
}

According to our previous understanding, we have a ready-made sort method for arrays. Can we use this method directly?

Arrays.sort(students);
System.out.println(Arrays.toString(students));

// 报错:
"C:\Program Files\Microsoft\jdk-11.0.16.101-hotspot\bin\java.exe" "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2022.3.2\lib\idea_rt.jar=53241:D:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2022.3.2\bin" -Dfile.encoding=UTF-8 -classpath E:\Code\Java\L20230720_Java\out\production\L20230720_Java Test.Test
Exception in thread "main" java.lang.ClassCastException: class Test.Student cannot be cast to class java.lang.Comparable (Test.Student is in unnamed module of loader 'app'; java.lang.Comparable is in module java.base of loader 'bootstrap')
	at java.base/java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:320)
	at java.base/java.util.ComparableTimSort.sort(ComparableTimSort.java:188)
	at java.base/java.util.Arrays.sort(Arrays.java:1249)
	at Test.Test.main(Test.java:39)

If you think about it carefully, it is not difficult to find that, unlike ordinary integers, two integers can be directly compared, and the size relationship is clear. How to determine the size relationship between two student objects? We need to specify additionally.

Let our Student class implement the Comparable interface and implement the compareTo method in it

class Student implements Comparable<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 +
                '}';
    }

    @Override
    public int compareTo(Student s) {
        if (this._age > s._age) {
            return 1;
        } else if (this._age < s._age) {
            return -1;
        } else {
            return 0;
        }

    }
}

public static void main(String[] args) {
    Student[] students = new Student[]{
            new Student("张三", 12),
            new Student("李四", 23),
            new Student("王五", 42),
            new Student("赵六", 13),
    };
    Arrays.sort(students);
    System.out.println(Arrays.toString(students));
    // 打印结果:[Student{_name='张三', _age=12}, Student{_name='赵六', _age=13}, Student{_name='李四', _age=23}, Student{_name='王五', _age=42}]
}

The compareTo method is automatically called in the sort method. The parameter of compareTo is Object. In fact, the object passed in is the Student type, and then the size relationship between the current object and the parameter object is compared (calculated as a fraction).

  • If the current object should be sorted before the parameter object, return a number less than 0;

  • If the current object should be ranked after the parameter object, return a number greater than 0;

  • If the current object and the parameter object are in no particular order, return 0;

Execute the program again and the result will be as expected.

[Student{_name='张三', _age=12}, Student{_name='赵六', _age=13}, Student{_name='李四', _age=23}, Student{_name='王五', _age=42}]

[Method 2 for sorting object 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 +
                '}';
    }
}

/* 类似仿函数一样的年龄排序(比较器) */
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);
    }
}

// 这样也是可以排序的,更灵活一些
public static void main(String[] args) {
    Student[] students = new Student[]{
            new Student("张三", 12),
            new Student("李四", 23),
            new Student("王五", 42),
            new Student("赵六", 13),
    };
    AgeComparator comparator1 = new AgeComparator();
    NameComparator comparator2 = new NameComparator();
    Arrays.sort(students, comparator2);
    System.out.println(Arrays.toString(students));
}

[Notes]: For the sort method, each object of the passed array needs to be "comparable" and needs to have the ability to compareTo. By overriding the compareTo method, comparison rules can be defined.

In order to further deepen our understanding of the interface, we can try to implement a sort method ourselves to complete the sorting process just now (using bubble sort).

public static void bubbleSort(Comparable[] students) {
    for(int i = 0; i < students.length - 1; i++) {
        for(int j = 0; j < students.length - 1 - i; j++) {
            if(students[j].compareTo(students[j + 1]) > 0) {
                // 交换
                Comparable temp = students[j];
                students[j] = students[j + 1];
                students[j + 1] = temp;
            }
        }
    }
}

public static void main(String[] args) {
    Student[] students = new Student[]{
            new Student("张三", 12),
            new Student("李四", 23),
            new Student("王五", 42),
            new Student("赵六", 13),
    };

    bubbleSort(students);
    System.out.println(Arrays.toString(students));
}

【2.8】Clonable interface and deep copy

        There are some useful interfaces built into Java, and Clonable is one of them.

        There is a clone method in the Object class. Calling this method can create a "copy" of the object. However, in order to legally call the clone method, you must first implement the Clonable interface, otherwise a CloneNotSupportedException exception will be thrown.

【Clonable instance】

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 Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person = new Person();
        person._id = 99;

        // 克隆
        Person personClone = (Person)person.clone();
        // 查看克隆结果
        System.out.println(person);
        System.out.println(personClone);
    }
}

[Shallow copy VS deep copy]

The object copied by Cloneable is a "shallow copy". Observe the following code:

class Money {
    public double _m;
}

class Person implements Cloneable {
    public int _id;
    public Money _money = new Money();

    // 重写克隆方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    // 重写打印方法

    @Override
    public String toString() {
        return "Person{" +
                "_id=" + _id +
                ", _money=" + _money +
                '}';
    }
}

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person = new Person();
        person._money._m = 199;

        // 克隆
        Person personClone = (Person)person.clone();
        // 查看克隆结果
        System.out.println("person:" + person._money._m);
        System.out.println("personClone:" + personClone._money._m);
    }
}

// 打印结果:
person:199.0
personClone:199.0

【view】

[The desired result of deep copy is to copy _money as well]

class Money implements Cloneable {
    public double _m;

    @Override
    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 temp = (Person)super.clone();
        temp._money = (Money)this._money.clone();
        return temp;
    }

    // 重写打印方法

    @Override
    public String toString() {
        return "Person{" +
                "_id=" + _id +
                ", _money=" + _money +
                '}';
    }
}

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person = new Person();
        person._money._m = 199;

        // 克隆
        Person personClone = (Person)person.clone();
        personClone._money._m = 1000;
        // 查看克隆结果
        System.out.println("person:" + person._money._m);
        System.out.println("personClone:" + personClone._money._m);
    }
}

【view】

【2.9】The difference between abstract class and interface

        Abstract classes and interfaces are common ways of using polymorphism in Java. They both need to be mastered. At the same time, you must understand the difference between the two (important!!! Common interview questions).

        Core difference : Abstract classes can contain ordinary methods and ordinary fields, and such ordinary methods and fields can be used directly by subclasses (without rewriting), while interfaces cannot contain ordinary methods, and subclasses must rewrite all abstract methods.

        Take the Animal example written before. The Animal here contains an attribute like name, which exists in any subclass. Therefore, the Animal here can only be used as an abstract class and should not become an interface.

class Animal {
	protected String name;
	public Animal(String name) {
		this.name = name;
	}
}

[Remind again] The purpose of abstract classes is to allow the compiler to better verify. We will not use classes like Animal directly, but use its subclasses. In case an instance of Animal is accidentally created, The compiler will remind us in time.

【3】Object class

        Object is a class provided by Java by default. Except for the Object class, all classes in Java have inheritance relationships. By default, it will inherit the Object parent class. That is, objects of all classes can be received using the reference of Object.

[Example] Use Object to receive objects of all classes.

class Person{}
class Student{}
public class Test {
    public static void main(String[] args) {
        function(new Person());
        function(new Student());
    }
    public static void function(Object obj) {
        System.out.println(obj);
    }
} 
//执行结果:
Person@1b6d3586
Student@4554617c

        Therefore, in development, the Object class is the highest unified type of parameters. But the Object class also has some well-defined methods. as follows:

All methods in the entire Object class need to be mastered.

In this section, we are mainly familiar with these methods: toString() method, equals() method, hashcode() method.

【3.1】Print object information toString method

If you want to print the contents of the object, you can directly override the toString() method in the Object class. This has been mentioned before and will not be repeated here.

// Object类中的toString()方法实现:
public String toString() {
	return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

【3.2】Object comparison equals method

In Java, when == is compared: a. If the left and right sides of == are basic type variables, the comparison is whether the values ​​in the variables are the same. b. If the left and right sides of == are reference type variables, the comparison is the reference variable address. Whether they are the same c. If you want to compare the contents of the object, you must rewrite the equals method in Object, because the equals method also compares based on the address by default:

[Example code demonstration]

class Person {
    public String _name;
    public int _age;

    public Person(String name, int age) {
        this._name = name;
        this._age = age;
    }
}
class Student {
    public String _name;
    public int _age;

    public Student(String name, int age) {
        this._name = name;
        this._age = age;
    }
}

public class Test {
    public static void main(String[] args) {
        Person person1 = new Person("张三", 18);
        Person person2 = new Person("张三", 18);
        // 调用Object原本的equals()方法进行比较观察现象。
        System.out.println(person1.equals(person2));
        // 打印结果为false,为什么呢?
    }
}

You need to override the equals() method:

class Person {
    public String _name;
    public int _age;

    public Person(String name, int age) {
        this._name = name;
        this._age = age;
    }
	
    // 重写equals()方法
    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false ;
        }

        if(this == obj) {
            return true ;
        }

        // 不是Person类对象
        if (!(obj instanceof Person)) {
            return false ;
        }

        Person per = (Person)obj;
        if(this._name.equals(per._name) && this._age == per._age) {
            return true;
        }

        return false;
    }
}
class Student {
    public String _name;
    public int _age;

    public Student(String name, int age) {
        this._name = name;
        this._age = age;
    }
}


public class Test {
    public static void main(String[] args) {
        Person person1 = new Person("张三", 18);
        Person person2 = new Person("张三", 18);
        System.out.println(person1.equals(person2));

    }
}

[Summary] When comparing whether the contents of objects are the same, you must override the equals method.

【3.3】hashcode method

Recall the source code of the toString method just now:

public String toString() {
	return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

        We saw the hashCode() method, which helped me calculate a specific object location , which involves a data structure, but we haven't learned the data structure yet and can't explain it, so we can only say that it is a memory address. Then call the Integer.toHexString() method to output this address in hexadecimal.

[hashcode method source code]

public native int hashCode();

        This method is a native method, and the bottom layer is written in C/C++ code. We can't see it. We think that two objects with the same name and the same age will be stored in the same location. If we do not override the hashcode() method, we can look at the sample code:

class Person {
    public String _name;
    public int _age;

    public Person(String name, int age) {
        this._name = name;
        this._age = age;
    }
}
class Student {
    public String _name;
    public int _age;

    public Student(String name, int age) {
        this._name = name;
        this._age = age;
    }
}


public class Test {
    public static void main(String[] args) {
        Person person1 = new Person("张三", 18);
        Person person2 = new Person("张三", 18);
        System.out.println(person1.hashCode());
        System.out.println(person2.hashCode());

    }
}
// 打印结果:
2083562754
1239731077

[Note] The hash values ​​of the two objects are different.

Like overriding the equals method, we can also override the hashcode() method. Let’s take a look again at this point.

class Person {
    public String _name;
    public int _age;

    public Person(String name, int age) {
        this._name = name;
        this._age = age;
    }
	
    // 重写hashCode()方法
    @Override
    public int hashCode() {
        return Objects.hash(_name, _age);
    }
}
class Student {
    public String _name;
    public int _age;

    public Student(String name, int age) {
        this._name = name;
        this._age = age;
    }
}


public class Test {
    public static void main(String[] args) {
        Person person1 = new Person("张三", 18);
        Person person2 = new Person("张三", 18);
        System.out.println(person1.hashCode());
        System.out.println(person2.hashCode());

    }
}
// 打印结果:
24022538
24022538

[Note] The hash values ​​are the same.

【in conclusion】

  1. The hashcode method is used to determine whether objects are stored in the same location in memory.

  2. In fact hashCode() is only useful in hash tables, not in other situations. The function of hashCode() in the hash table is to obtain the hash code of the object and then determine the position of the object in the hash table.

Guess you like

Origin blog.csdn.net/lx473774000/article/details/132726977