Aprendizaje de Java: orientado a objetos (2)

Cinco, orientado a objetos (dos)

5.1 Palabra clave: este

  • En Java, la palabra clave this no es difícil de entender y su función es muy cercana a su significado.

    • Se usa dentro de un método (hablando con precisión, un método de instancia o un método no estático) para representar el objeto que llama al método.
    • Se usa dentro de un constructor para representar el objeto que el constructor está inicializando.
  • La estructura a la que esto puede llamar: variables miembro, métodos y constructores.

5.1.1 este escenario de uso

① Los miembros del objeto actual se utilizan en el método de instancia o constructor

En el método o constructor de instancia, si usa la variable miembro o el método miembro de la clase actual, puede agregar esto delante para mejorar la legibilidad del programa. Sin embargo, generalmente estamos acostumbrados a omitir esto.

Sin embargo, cuando la variable miembro formal tiene el mismo nombre, si necesita usar la variable miembro en el método o constructor, debe agregar esto para indicar que la variable es una variable miembro de la clase. Es decir: podemos usar esto para distinguir 成员变量y 局部变量. Por ejemplo:
inserte la descripción de la imagen aquí

Además, al usar esto para acceder a propiedades y métodos, si no se encuentran en esta clase, se encontrarán en la clase padre.

Ejemplo 1:

class Person{
    
    		// 定义Person类
	private String name ;	
	private int age ;			
	public Person(String name,int age){
    
    	
		this.name = name ;   
		this.age = age ;  
    }
    public void setName(String name){
    
    
        this.name = name;
    }
    public void setAge(int age){
    
    
        this.age = age;
    }
	public void getInfo(){
    
    	
		System.out.println("姓名:" + name) ;
		this.speak();
	}
	public void speak(){
    
    
		System.out.println(“年龄:” + this.age);	
	}
}

Ejemplo 2:

public class Rectangle {
    
    
    int length;
    int width;

    public int area() {
    
    
        return this.length * this.width;
    }

    public int perimeter(){
    
    
        return 2 * (this.length + this.width);
    }

    public void print(char sign) {
    
    
        for (int i = 1; i <= this.width; i++) {
    
    
            for (int j = 1; j <= this.length; j++) {
    
    
                System.out.print(sign);
            }
            System.out.println();
        }
    }

    public String getInfo(){
    
    
        return "长:" + this.length + ",宽:" + this.width +",面积:" + this.area() +",周长:" + this.perimeter();
    }
}

Clase de prueba:

public class TestRectangle {
    
    
    public static void main(String[] args) {
    
    
        Rectangle r1 = new Rectangle();
        Rectangle r2 = new Rectangle();

        System.out.println("r1对象:" + r1.getInfo());
        System.out.println("r2对象:" + r2.getInfo());

        r1.length = 10;
        r1.width = 2;
        System.out.println("r1对象:" + r1.getInfo());
        System.out.println("r2对象:" + r2.getInfo());

        r1.print('#');
        System.out.println("---------------------");
        r1.print('&');

        System.out.println("---------------------");
        r2.print('#');
        System.out.println("---------------------");
        r2.print('%');
    }
}

② Los constructores de la misma clase se llaman entre sí

esto se puede usar como un formato especial para que los constructores de una clase se llamen entre sí.

  • this(): llama al constructor sin parámetros de esta clase
  • this (lista de parámetros reales): llame al constructor parametrizado de esta clase
public class Student {
    
    
    private String name;
    private int age;

    // 无参构造
    public Student() {
    
    
//        this("",18);//调用本类有参构造器
    }

    // 有参构造
    public Student(String name) {
    
    
        this();//调用本类无参构造器
        this.name = name;
    }
    // 有参构造
    public Student(String name,int age){
    
    
        this(name);//调用本类中有一个String参数的构造器
        this.age = age;
    }

    public String getName() {
    
    
        return name;
    }
    public void setName(String name) {
    
    
        this.name = name;
    }
    public int getAge() {
    
    
        return age;
    }
    public void setAge(int age) {
    
    
        this.age = age;
    }

    public String getInfo(){
    
    
        return "姓名:" + name +",年龄:" + age;
    }
}

Aviso:

  • Las llamadas recursivas no pueden ocurrir. Por ejemplo, llamando a su propio constructor.
    • Corolario: si se declaran n constructores en una clase, como máximo n - 1 constructores usan "esta (lista de parámetros)"
  • this() y this (lista de parámetros reales) solo se pueden declarar en la primera línea del constructor.
    • Corolario: en un constructor de una clase, como máximo se puede declarar "esta (lista de parámetros)"

5.2 Característica 2: Herencia

Beneficios Heredados

  • La aparición de la herencia reduce la redundancia del código y mejora la reutilización del código.

  • La aparición de la herencia es más propicia para la ampliación de funciones.

  • La aparición de la herencia crea is-auna relación entre clases y clases, lo que proporciona un requisito previo para el uso del polimorfismo.

    • La herencia describe la relación de propiedad entre las cosas, esta relación es: is-ala relación. Se puede ver que la clase padre es más general y general, y la subclase es más específica.

Nota: ¡No herede solo para obtener una función en otra clase!

Formato gramatical en herencia

A través extendsde la palabra clave se puede declarar que una clase B hereda otra clase A, y el formato de definición es el siguiente:

[修饰符] classA {
    
    
	...
}

[修饰符] classB extendsA {
    
    
	...
}

Clase B, llamada subclase, clase derivada (clase derivada), SubClass

Clase A, llamada clase padre, superclase, clase base (clase base), SuperClass

ejemplo de código

1. Clase de padres

/*
 * 定义动物类Animal,做为父类
 */
public class Animal {
    
    
    // 定义name属性
    String name;
    // 定义age属性
    int age;

    // 定义动物的吃东西方法
    public void eat() {
    
    
        System.out.println(age + "岁的"
                + name + "在吃东西");
    }
}

2. Subclases

/*
 * 定义猫类Cat 继承 动物类Animal
 */
public class Cat extends Animal {
    
    
    int count;//记录每只猫抓的老鼠数量

    // 定义一个猫抓老鼠的方法catchMouse
    public void catchMouse() {
    
    
        count++;
        System.out.println("抓老鼠,已经抓了"
                + count + "只老鼠");
    }
}

3. Clase de prueba

public class TestCat {
    
    
    public static void main(String[] args) {
    
    
        // 创建一个猫类对象
        Cat cat = new Cat();
        // 为该猫类对象的name属性进行赋值
        cat.name = "Tom";
        // 为该猫类对象的age属性进行赋值
        cat.age = 2;
        // 调用该猫继承来的eat()方法
        cat.eat();
        // 调用该猫的catchMouse()方法
        cat.catchMouse();
        cat.catchMouse();
        cat.catchMouse();
    }
}

Detalles de herencia

1. La subclase heredará todas las variables de instancia y los métodos de instancia de la clase principal
2. La subclase no puede acceder directamente a las variables y métodos de miembros privados (privados) en la clase principal
3. En Java, la palabra clave heredada usa "extiende" " , es decir, la subclase no es un subconjunto de la clase principal, sino una "extensión" de la clase principal
4. Java admite herencia multicapa (sistema de herencia)

class A{
    
    }
class B extends A{
    
    }
class C extends B{
    
    }

ilustrar:

  • La subclase y la clase padre son conceptos relativos.

  • La clase principal de nivel superior es la clase Object. Todas las clases heredan Object de forma predeterminada como clase principal.

5. Una clase principal puede tener varias subclases al mismo tiempo

class A{
    
    }
class B extends A{
    
    }
class D extends A{
    
    }
class E extends A{
    
    }

6. Java solo admite herencia única, no herencia múltiple

public class A{
    
    }
class B extends A{
    
    }

//一个类只能有一个父类,不可以有多个直接父类。
class C extends B{
    
    } 	//ok
class C extends A,B...	//error

práctica:

(1) Definir una clase ManKind, incluyendo

  • Variables integrantes int sexo y int salario;
  • Método void manOrWoman(): Mostrar "hombre" según el valor de sexo (sexo1) o "mujer" (sexo0);
  • Método anulado empleado (): muestra "sin trabajo" (salario == 0) o "trabajo" (salario! = 0) según el valor del salario.
public class ManKind {
    
    
    int sex; // 成员变量:性别
    int salary; // 成员变量:工资

    public void manOrWoman(){
    
     // 方法:判断男女性别并输出
        if(sex == 1){
    
     // 如果性别为 1,表示男性
            System.out.println("man"); // 输出 man
        }else if(sex == 0){
    
     // 如果性别为 0,表示女性
            System.out.println("woman"); // 输出 woman
        }
    }

    public void employeed(){
    
     // 方法:判断是否有工作并输出
        if(salary == 0){
    
     // 如果工资为 0,表示没有工作
            System.out.println("no job"); // 输出 no job
        }else{
    
     // 否则有工作
            System.out.println("job"); // 输出 job
        }
    }
}

(2) Definir la clase Kids para heredar ManKind e incluir

  • miembro variable int yearsOld;
  • El método printAge() imprime el valor de yearsOld.
package demo07;

public class Kids extends ManKind{
    
     // 继承自 ManKind
    int yearsOld; // 成员变量:年龄

    public void printAge(){
    
     // 方法:输出年龄
        System.out.println(yearsOld); // 输出成员变量 yearsOld 的值
    }
}

(3) Defina la clase KidsTest, cree una instancia del objeto someKid of Kids en el método principal de la clase y use este objeto para acceder a las variables miembro y métodos de su clase principal.

package demo07;

public class KidsTest {
    
    
    public static void main(String[] args) {
    
    
        Kids someKid = new Kids();// 实例化 Kids 类的对象 someKid
        someKid.sex = 1; // 调用继承自 ManKind 的成员变量 sex,赋值为 1
        someKid.salary = 1000; // 调用继承自 ManKind 的成员变量 salary,赋值为 1000
        someKid.manOrWoman(); // 调用继承自 ManKind 的方法 manOrWoman,输出 man
        someKid.employeed(); // 调用继承自 ManKind 的方法 employeed,输出 job
        someKid.yearsOld = 10; // 调用 Kids 自己的成员变量 yearsOld,赋值为 10
        someKid.printAge(); // 调用 Kids 自己的方法 printAge,输出 10
    }
}

5.3 Reescritura de métodos (override/overwrite)

La subclase heredará todos los métodos de la clase principal, pero cuando la subclase hereda un método, la subclase siente que la implementación original de la clase principal no es adecuada para su clase actual, ¿qué debe hacer? Las subclases pueden transformar los métodos heredados de la clase principal, a los que llamamos métodos 重写 (override、overwrite). También conocido como método 重置, 覆盖.

Cuando se ejecuta el programa, el método de la subclase anulará el método de la clase principal.

ejemplo

Por ejemplo, el nuevo teléfono móvil agrega la función de mostrar el avatar de la persona que llama, el código es el siguiente:

public class Phone {
    
    
    public void sendMessage(){
    
    
        System.out.println("发短信");
    }
    public void call(){
    
    
        System.out.println("打电话");
    }
    public void showNum(){
    
    
        System.out.println("来电显示号码");
    }
}

//SmartPhone:智能手机
public class SmartPhone extends Phone{
    
    
    //重写父类的来电显示功能的方法
	@Override
    public void showNum(){
    
    
        //来电显示姓名和图片功能
        System.out.println("显示来电姓名");
        System.out.println("显示头像");
    }
    //重写父类的通话功能的方法
    @Override
    public void call() {
    
    
        System.out.println("语音通话 或 视频通话");
    }
}
//TestOverride类
public class TestOverride {
    
    
    public static void main(String[] args) {
    
    
        // 创建子类对象
        SmartPhone sp = new SmartPhone();

        // 调用父类继承而来的方法
        sp.call();

        // 调用子类重写的方法
        sp.showNum();
    }
}

@Override Instrucciones de uso:

Escrito en el método, se utiliza para detectar si se cumplen los requisitos del método reescrito. Incluso si esta anotación no está escrita, siempre que se cumplan los requisitos, es la forma correcta de sobrescribir y reescribir. Se recomienda mantenerlo, para que el compilador pueda ayudarnos a verificar el formato, y también puede dejar en claro a los programadores que leen el código fuente que este es un método anulado.

Requisitos de anulación de métodos

  1. El método anulado por la subclase tiene el mismo 必须método anulado de la clase padre .方法名称参数列表

  2. El tipo de valor devuelto del método invalidado de la subclase. 不能大于El tipo de valor devuelto del método invalidado de la clase principal. (por ejemplo: Estudiante < Persona).

Nota: Si el tipo de valor devuelto es un tipo de datos básico y nulo, debe ser el mismo

  1. Los métodos anulados por subclases utilizan los derechos de acceso 不能小于de los métodos anulados de la clase principal. (público > protegido > predeterminado > privado)

Nota: ① El método privado de la clase principal no se puede anular; ② El método predeterminado de la clase principal de paquetes cruzados no se puede anular

  1. La excepción lanzada por el método de la subclase no puede ser mayor que la excepción del método anulado de la clase principal

Además, los métodos con el mismo nombre y parámetros en la subclase y la clase principal deben declararse como no estáticos (es decir, reescribiendo) o estáticos (no reescribiendo) al mismo tiempo. Debido a que el método estático pertenece a la clase, la subclase no puede anular el método de la clase principal.

Sobrecarga y reescritura de métodos

Java中的方法重载(Overloading): Significa que en una misma clase, puede haber múltiples métodos con el mismo nombre pero diferentes números o tipos de parámetros, es decir, el mismo nombre de método puede estar sobrecargado a través de diferentes listas de parámetros. Al llamar a estos métodos sobrecargados, el compilador elegirá el método más apropiado según el tipo, número y orden de los parámetros reales. La sobrecarga de métodos en Java generalmente se usa para lograr algunas funciones similares, mejorando así la reutilización del código.

Java中的方法重写(Overriding): significa que la subclase anula el método de la clase principal, también conocido como anulación. Defina un método en la subclase que tenga el mismo nombre, lista de parámetros y tipo de valor devuelto que el método de la clase principal y utilice el símbolo de anotación @Override para marcarlo, lo que indica que el método de la clase principal está anulado.

La sobrecarga y la reescritura de métodos son dos métodos comúnmente utilizados en Java para lograr la reutilización del código y mejorar la escalabilidad.

(1) En la misma clase

public class TestOverload {
    
    
    public int max(int a, int b){
    
    
        return a > b ? a : b;
    }
    public double max(double a, double b){
    
    
        return a > b ? a : b;
    }
    public int max(int a, int b,int c){
    
    
        return max(max(a,b),c);
    }
}

(2) En la clase padre-hijo

public class TestOverloadOverride {
    
    
    public static void main(String[] args) {
    
    
        Son s = new Son();
        s.method(1);//只有一个形式的method方法

        Daughter d = new Daughter();
        d.method(1);
        d.method(1,2);//有两个形式的method方法
    }
}

class Father{
    
    
    public void method(int i){
    
    
        System.out.println("Father.method");
    }
}
class Son extends Father{
    
    
    public void method(int i){
    
    //重写
        System.out.println("Son.method");
    }
}
class Daughter extends Father{
    
    
    public void method(int i,int j){
    
    //重载
        System.out.println("Daughter.method");
    }
}

5.4 Palabra clave: súper

Use super en una clase de Java para llamar a la operación especificada en la clase principal:

  • super se puede usar para acceder a las propiedades definidas en la clase principal
  • super se puede usar para llamar a métodos miembros definidos en la clase principal
  • super se puede usar para llamar al constructor de la clase principal en el constructor de la subclase

Aviso:

  • Especialmente cuando hay miembros con el mismo nombre en la clase principal secundaria, puede usar super para indicar que el miembro de la clase principal se llama
  • La trazabilidad de Super no se limita a la clase principal inmediata
  • El uso de super y this es similar, esto representa la referencia del objeto de esta clase y super representa la identidad del espacio de memoria de la clase principal

5.4.1 Escenarios de súper uso

①Llamar al método anulado de la clase padre en la subclase

  • Si la subclase no anula el método de la clase principal, siempre que el modificador de permiso lo permita, el método de la clase principal se puede llamar directamente en la subclase;
  • Si la subclase anula el método de la clase principal, debe pasarse a la subclase para super.llamar al método anulado de la clase principal; de lo contrario, el método reescrito por la subclase se llama de forma predeterminada.

Ejemplo:

public class Phone {
    
    
    public void sendMessage(){
    
    
        System.out.println("发短信");
    }
    public void call(){
    
    
        System.out.println("打电话");
    }
    public void showNum(){
    
    
        System.out.println("来电显示号码");
    }
}

//smartphone:智能手机
public class SmartPhone extends Phone{
    
    
    //重写父类的来电显示功能的方法
    public void showNum(){
    
    
        //来电显示姓名和图片功能
        System.out.println("显示来电姓名");
        System.out.println("显示头像");

        //保留父类来电显示号码的功能
        super.showNum();//此处必须加super.,否则就是无限递归,那么就会栈内存溢出
    }
}

Resumir:

  • No hay super y esto delante del método.

    • Primero encuentre el método coincidente de la subclase, si no, luego búsquelo de la clase principal directa, si no, continúe rastreando
  • El método está precedido por esto.

    • Primero encuentre el método coincidente de la subclase, si no, luego búsquelo de la clase principal directa, si no, continúe rastreando
  • El método está precedido por super.

    • Encuéntrelo desde la clase principal directa de la subclase actual, si no, continúe rastreando

②Llamar a la variable miembro con el mismo nombre en la clase principal en la subclase

  • Si la variable de instancia tiene el mismo nombre que la variable local, puede agregar esto delante de la variable de instancia para distinguirla.
  • Si la variable de instancia de la subclase y la variable de instancia de la clase principal tienen el mismo nombre, y la variable de instancia de la clase principal todavía está visible en la subclase, para acceder a la variable de instancia declarada por la clase principal en la subclase , debe agregar super antes de la variable de instancia de la clase principal, de lo contrario, el acceso predeterminado es la variable de instancia declarada por la propia subclase
  • Si las variables de instancia de las clases principal y secundaria no tienen el mismo nombre, siempre que lo permita el modificador de permiso, se puede acceder directamente a las variables de instancia declaradas en la clase principal en la subclase, y también se puede acceder mediante this. variable instancia o super.instancia

Ejemplo:

class Father{
    
    
	int a = 10;
	int b = 11;
}
class Son extends Father{
    
    
	int a = 20;
    
    public void test(){
    
    
		//子类与父类的属性同名,子类对象中就有两个a
		System.out.println("子类的a:" + a);//20  先找局部变量找,没有再从本类成员变量找
        System.out.println("子类的a:" + this.a);//20   先从本类成员变量找
        System.out.println("父类的a:" + super.a);//10    直接从父类成员变量找
		
		//子类与父类的属性不同名,是同一个b
		System.out.println("b = " + b);//11  先找局部变量找,没有再从本类成员变量找,没有再从父类找
		System.out.println("b = " + this.b);//11   先从本类成员变量找,没有再从父类找
		System.out.println("b = " + super.b);//11  直接从父类局部变量找
	}
	
	public void method(int a, int b){
    
    
		//子类与父类的属性同名,子类对象中就有两个成员变量a,此时方法中还有一个局部变量a		
		System.out.println("局部变量的a:" + a);//30  先找局部变量
        System.out.println("子类的a:" + this.a);//20  先从本类成员变量找
        System.out.println("父类的a:" + super.a);//10  直接从父类成员变量找

        System.out.println("b = " + b);//13  先找局部变量
		System.out.println("b = " + this.b);//11  先从本类成员变量找
		System.out.println("b = " + super.b);//11  直接从父类局部变量找
    }
}
class Test{
    
    
    public static void main(String[] args){
    
    
        Son son = new Son();
		son.test();
		son.method(30,13);  
    }
}

Resumen: diferentes puntos de partida (principio de proximidad)

  • Las variables no van precedidas de super y this.

    • Si una variable se usa en un constructor, bloque de código o método, primero verifique si está declarada por el bloque actual 局部变量,
    • Si no es una variable local, comience con el código que se está ejecutando actualmente本类去找成员变量
    • Si no se encuentra en la clase que actualmente ejecuta el código, buscará 父类声明的成员变量(los modificadores de permisos permiten el acceso en subclases)
  • Las variables van precedidas de esto.

    • Al buscar variables miembro a través de esto, primero comience desde el código que se está ejecutando actualmenteEsta clase encuentra variables miembro
    • Si no se encuentra en la clase que actualmente ejecuta el código, buscará == variables miembro declaradas por la clase principal (== los modificadores de permiso permiten el acceso en subclases)
  • Variables precedidas por super.

    • Encuentre variables miembro a través de super y busque directamente variables miembro de la clase principal directa del código que se está ejecutando actualmente (los modificadores de permisos permiten el acceso en subclases)
    • Si no hay una clase principal directa, vaya a la clase principal de la clase principal para encontrarla (los modificadores de permisos permiten el acceso en las subclases)

Nota especial: deben evitarse las declaraciones de subclase y las variables miembro con el mismo nombre que la clase principal

③Llamar al constructor de la clase principal en el constructor de la subclase

En Java, una subclase puede llamar al constructor de la clase principal para inicializar las variables miembro de la clase principal y usar el método super() para llamar explícitamente al constructor de la clase principal. Si el constructor de la subclase no llama explícitamente al constructor de la superclase, se llamará por defecto al constructor sin argumentos de la superclase. Utilice el método super() en la primera línea del constructor de la subclase para llamar al constructor de la superclase. Por ejemplo:

class Parent {
    
    
    int x;
    Parent(int x) {
    
    
        this.x = x;
    }
}

class Child extends Parent {
    
    
    int y;
    Child(int x, int y) {
    
    
        super(x); // 调用父类的构造器,初始化父类的 x 成员变量
        this.y = y;
    }
}

public class Test {
    
    
    public static void main(String[] args) {
    
    
        Child c = new Child(10, 20);
        System.out.println(c.x); // 输出 10,父类的 x 成员变量已经被初始化
        System.out.println(c.y); // 输出 20
    }
}

Utilice el método super() en el constructor de la subclase para llamar explícitamente al constructor de la clase principal, de modo que la subclase también pueda completar la inicialización de la clase principal durante la inicialización, lo que garantiza el correcto funcionamiento del programa. Al mismo tiempo, en la programación real, debe prestar atención a los siguientes puntos:

  1. Si el constructor de la clase principal no se llama explícitamente en el constructor de la subclase, el sistema llamará al constructor sin argumentos de la clase principal de forma predeterminada.Si la clase principal no tiene un constructor sin argumentos o el control de acceso del constructor sin argumentos es privado, compilará error. En este momento, debe llamar explícitamente al constructor de la clase principal en el constructor de la subclase, y la sintaxis es super (lista de parámetros).
  2. La llamada al constructor de la superclase debe colocarse en la primera línea del constructor de la subclase.
  3. Si el constructor de la clase principal tiene varios constructores, la subclase puede llamar explícitamente al constructor de la clase principal especificado a través de super(lista de parámetros). Un constructor de subclase solo puede llamar a un constructor de superclase y debe declararse en la primera línea.
  4. Cuando hay una instrucción de inicialización en el constructor de la clase principal, la subclase debe escribir su propio constructor y llamar al constructor de la clase principal.

5.4.2 este y super

1. El significado de esto y super

esto: el objeto actual

  • En constructores y bloques de código no estático, los objetos que están siendo nuevos
  • En un método de instancia, el objeto que llama al método actual

super: se refiere a los miembros declarados por la clase padre

2. El formato de uso de este y super

  • este
    • this.Member variable: representa una variable miembro del objeto actual, no una variable local
    • this.Member method: Indica un método miembro del objeto actual, puede omitirlo por completo.
    • this() o this (lista de parámetros real): llamar a otro constructor para ayudar en la creación de instancias del objeto actual, solo en la primera línea del constructor, solo se encontrará el constructor de esta clase y se informará un error si no se puede encontrar
  • súper
    • super.Variable miembro: indica una variable miembro del objeto actual, que se declara en la clase padre
    • super.método miembro: indica un método miembro del objeto actual, que se declara en la clase padre
    • super () o super (lista de parámetros reales): llame al constructor de la clase principal para ayudar en la creación de instancias del objeto actual, solo en la primera línea del constructor, solo se encontrará el constructor correspondiente de la clase principal directa , y se informará un error si no se encuentra

práctica

1. Escriba una cuenta de demostración de clase denominada Cuenta. Las propiedades y métodos de esta clase se muestran en la siguiente figura. Atributos incluidos en esta clase: id de cuenta, saldo de saldo, tasa de interés anual; métodos incluidos: método de acceso (métodos getter y setter), método getMonthlyInterest() que devuelve la tasa de interés mensual, método de retiro retiro(), método de depósito depósito() .

[Falló la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo anti-leeching, se recomienda guardar la imagen y cargarla directamente (img-ZPqLKGpD-1680682124909)(images/image-20220324003430464.png)]

package demo08;

public class Account {
    
    
    private int id; //账号id
    private double balance; //余额balance
    private double annualInterestRate; //年利率annualInterestRate
    public Account(int id, double balance, double annualInterestRate) {
    
    
        this.id = id;
        this.balance = balance;
        this.annualInterestRate = annualInterestRate;

    }
    public int getId() {
    
     // 获取账户
        return id;
    }

    public void setId(int id) {
    
     // 设置账户
        this.id = id;
    }

    public double getBalance() {
    
     // 获取余额
        return balance;
    }

    public void setBalance(double balance) {
    
     // 设置余额
        this.balance = balance;
    }

    public double getAnnualInterestRate() {
    
     //获取年利率
        return annualInterestRate;
    }

    public void setAnnualInterestRate(double annualInterestRate) {
    
     // 设置年利率
        this.annualInterestRate = annualInterestRate;
    }

    public double getMonthlyInterest() {
    
     // 获取月利率
        return annualInterestRate / 12;
    }

    public void withdraw(double amount) {
    
     // 取款
        if (amount > balance) {
    
    
            System.out.println("余额不足");
        } else {
    
    
            balance -= amount;
            System.out.println("成功取款" + amount + "元,账户余额为" + balance + "元");
        }
    }

    public void deposit(double amount) {
    
     // 存款
        balance += amount;
        System.out.println("成功存款" + amount + "元,账户余额为" + balance + "元");
    }
}

2. Cree una subclase CheckAccount de la clase Account para representar una cuenta que se pueda sobregirar. Se define un sobregiro de atributo en la cuenta para representar el límite de sobregiro. Anule el método de retiro en la clase CheckAccount, y su algoritmo es el siguiente:

如果(取款金额<账户余额),
	可直接取款
如果(取款金额>账户余额),
	计算需要透支的额度
	判断可透支额overdraft是否足够支付本次透支需要,如果可以
		将账户余额修改为0,冲减可透支金额
	如果不可以
		提示用户超过可透支额的限额
package demo08;

public class CheckAccount extends Account{
    
    
    private double overdraft;

    public CheckAccount(int id, double balance, double annualInterestRate, double overdraft) {
    
    
        super(id, balance, annualInterestRate);
        this.overdraft = overdraft;
    }

    public double getOverdraft() {
    
    
        return overdraft;
    }

    public void setOverdraft(double overdraft) {
    
    
        this.overdraft = overdraft;
    }
    @Override
    public void withdraw(double amount) {
    
     // 取款
        if (amount <= getBalance() + overdraft) {
    
    
            if (amount <= getBalance()) {
    
    
                setBalance(getBalance() - amount);
                System.out.println("成功取款" + amount + "元,账户余额为" + getBalance() + "元");
            } else {
    
    
                double overdraftAmount = amount - getBalance();
                setBalance(0);
                overdraft -= overdraftAmount;
                System.out.println("成功取款" + amount + "元,账户余额为0元,已透支" + overdraftAmount + "元,可透支额为" + overdraft + "元");
            }
        } else {
    
    
            System.out.println("余额不足,可透支额为" + overdraft + "元");
        }
    }
}

Requisitos:
Escriba un programa de usuario para probar la clase Cuenta. En el programa de usuario, cree un objeto Cuenta con un número de cuenta de 1122, un saldo de 20.000 y una tasa de interés anual del 4,5 %. Use el método de retiro para retirar 30,000 yuanes e imprimir el saldo.
Luego use el método de retiro para retirar 2500 yuanes, use el método de depósito para depositar 3000 yuanes y luego imprima el saldo y la tasa de interés mensual.
Sugerencia: En el método de retiro retirar, es necesario juzgar si el saldo del usuario puede cumplir con los requisitos del monto del retiro y, de no ser así, se debe dar un aviso.
Escriba un programa de usuario para probar la clase CheckAccount. En el programa de usuario, cree un objeto CheckAccount con un número de cuenta de 1122, un saldo de 20 000, una tasa de interés anual del 4,5 % y un límite de sobregiro de 5000 yuanes.
Use el método de retiro para retirar 5,000 yuanes e imprima el saldo de la cuenta y el monto del sobregiro.
Luego use el método de retiro para retirar 18,000 yuanes e imprima el saldo de la cuenta y el monto del sobregiro.
Luego use el método de retiro para retirar 3,000 yuanes e imprima el saldo de la cuenta y el monto del sobregiro.
Sugerencias:
(1) El método de construcción de la subclase CheckAccount necesita inicializar los 3 atributos heredados de la clase principal y los atributos de la propia subclase.
(2) El saldo del atributo de la clase principal Cuenta se establece en privado, pero su valor debe modificarse en el método de retiro de la subclase Cuenta de cheques, por lo que el atributo de saldo de la clase principal debe modificarse y definirse como protegido.

package demo08;
public class Test {
    
    
    public static void main(String[] args) {
    
    
        // 创建账号为1122、余额为20000、年利率4.5%的Account对象
        Account account = new Account(1122, 20000, 0.045);
        // 使用withdraw方法提款30000元,并打印余额
        account.withdraw(30000);
        // 使用withdraw方法提款2500元,使用deposit方法存款3000元,然后打印余额和月利率
        account.withdraw(2500);
        account.deposit(3000);
        System.out.println("账户余额为" + account.getBalance() + "元,月利率为" + account.getMonthlyInterest() * 100 + "%");

        // 创建账号为1122、余额为20000、年利率4.5%,可透支限额为5000元的CheckAccount对象
        CheckAccount checkAccount = new CheckAccount(1122, 20000, 0.045, 5000);
        // 使用withdraw方法提款5000元,并打印账户余额和可透支额
        checkAccount.withdraw(5000);
        System.out.println("账户余额为" + checkAccount.getBalance() + "元,可透支额为" + checkAccount.getOverdraft() + "元");
        // 使用withdraw方法提款18000元,并打印账户余额和可透支额
        checkAccount.withdraw(18000);
        System.out.println("账户余额为" + checkAccount.getBalance() + "元,可透支额为" + checkAccount.getOverdraft() + "元");
        // 使用withdraw方法提款3000元,并打印账户余额和可透支额
        checkAccount.withdraw(3000);
        System.out.println("账户余额为" + checkAccount.getBalance() + "元,可透支额为" + checkAccount.getOverdraft() + "元");
    }
}

>运行结果
余额不足
成功取款2500.0元,账户余额为17500.0元
成功存款3000.0元,账户余额为20500.0元
账户余额为20500.0元,月利率为0.375%
成功取款5000.0元,账户余额为15000.0元
账户余额为15000.0元,可透支额为5000.0元
成功取款18000.0元,账户余额为0元,已透支3000.0元,可透支额为2000.0元
账户余额为0.0元,可透支额为2000.0元
余额不足,可透支额为2000.0元
账户余额为0.0元,可透支额为2000.0

Todo el proceso de creación de instancias de objetos de subclase.

[Falló la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo anti-leeching, se recomienda guardar la imagen y subirla directamente (img-GiQ5vZpD-1680443622972)(images/image-20220324003713230.png)]

Ejemplo:

class Creature {
    
    
    public Creature() {
    
    
        System.out.println("Creature无参数的构造器");
	}
}
class Animal extends Creature {
    
    
    public Animal(String name) {
    
    
        System.out.println("Animal带一个参数的构造器,该动物的name为" + name);
    }
    public Animal(String name, int age) {
    
    
        this(name);
        System.out.println("Animal带两个参数的构造器,其age为" + age);
	}
}
public class Dog extends Animal {
    
    
    public Dog() {
    
    
        super("汪汪队阿奇", 3);
        System.out.println("Dog无参数的构造器");
    }
    public static void main(String[] args) {
    
    
        new Dog();
	}
}

5.5 Característica tres: polimorfismo

Hay mil Hamlets a los ojos de mil lectores.

5.5.1 Forma y realización del polimorfismo

polimorfismo de objetos

El polimorfismo es el concepto más importante en orientación a objetos, incorporado en Java: polimorfismo de objetos: la referencia de la clase padre apunta al objeto de la subclase

Formato: (tipo de clase principal: se refiere al tipo de clase principal heredado por la subclase o el tipo de interfaz implementado)

父类类型 变量名 = 子类对象;

Ejemplo:

Person p = new Student();

Object o = new Person();//Object类型的变量o,指向Person类型的对象

o = new Student(); //Object类型的变量o,指向Student类型的对象

Polimorfismo de objetos: en Java, se puede usar un objeto de una subclase en lugar de un objeto de la clase principal. Por lo tanto, una variable de tipo de referencia puede señalar (referir) muchos tipos diferentes de objetos

comprensión polimórfica

Las variables de referencia de Java tienen dos tipos: 编译时类型y 运行时类型. El tipo de tiempo de compilación 声明está determinado por el tipo usado por la variable y el tipo de tiempo de ejecución está 实际赋给该变量的对象determinado por la variable. Abreviatura: Al compilar, mire a la izquierda; al ejecutar, mire a la derecha.

  • Si el tipo de tiempo de compilación es inconsistente con el tipo de tiempo de ejecución, habrá polimorfismo del objeto (polimorfismo)
  • En el caso del polimorfismo, "mira a la izquierda": mira la referencia de la clase padre (la clase padre no tiene el método específico de la subclase) "mira a la derecha": mira el objeto de la subclase
    ( realmente ejecutar es la reescritura del método de subclase de la clase principal)

Requisitos previos para el polimorfismo: ① relación de herencia de clases ② reescritura de métodos

ejemplo

public class Pet {
    
    
    private String nickname; //昵称

    public String getNickname() {
    
    
        return nickname;
    }

    public void setNickname(String nickname) {
    
    
        this.nickname = nickname;
    }

    public void eat(){
    
    
        System.out.println(nickname + "吃东西");
    }
}
public class Cat extends Pet {
    
    
    //子类重写父类的方法
    @Override
    public void eat() {
    
    
        System.out.println("猫咪" + getNickname() + "吃鱼仔");
    }

    //子类扩展的方法
    public void catchMouse() {
    
    
        System.out.println("抓老鼠");
    }
}
public class Dog extends Pet {
    
    
    //子类重写父类的方法
    @Override
    public void eat() {
    
    
        System.out.println("狗子" + getNickname() + "啃骨头");
    }

    //子类扩展的方法
    public void watchHouse() {
    
    
        System.out.println("看家");
    }
}

1. La asignación de variables locales en el método refleja polimorfismo

public class TestPet {
    
    
    public static void main(String[] args) {
    
    
        //多态引用
        Pet pet = new Dog();
        pet.setNickname("小白");

        //多态的表现形式
        /*
        编译时看父类:只能调用父类声明的方法,不能调用子类扩展的方法;
        运行时,看“子类”,如果子类重写了方法,一定是执行子类重写的方法体;
         */
        pet.eat();//运行时执行子类Dog重写的方法
//      pet.watchHouse();//不能调用Dog子类扩展的方法

        pet = new Cat();
        pet.setNickname("雪球");
        pet.eat();//运行时执行子类Cat重写的方法
    }
}

2. La declaración de parámetros formales del método refleja polimorfismo

public class Person{
    
    
    private Pet pet;
    public void adopt(Pet pet) {
    
    //形参是父类类型,实参是子类对象
        this.pet = pet;
    }
    public void feed(){
    
    
        pet.eat();//pet实际引用的对象类型不同,执行的eat方法也不同
    }
}
public class TestPerson {
    
    
    public static void main(String[] args) {
    
    
        Person person = new Person();

        Dog dog = new Dog();
        dog.setNickname("小白");
        person.adopt(dog);//实参是dog子类对象,形参是父类Pet类型
        person.feed();

        Cat cat = new Cat();
        cat.setNickname("雪球");
        person.adopt(cat);//实参是cat子类对象,形参是父类Pet类型
        person.feed();
    }
}

3. El tipo de valor de retorno del método refleja polimorfismo

public class PetShop {
    
    
    //返回值类型是父类类型,实际返回的是子类对象
    public Pet sale(String type){
    
    
        switch (type){
    
    
            case "Dog":
                return new Dog();
            case "Cat":
                return new Cat();
        }
        return null;
    }
}
public class TestPetShop {
    
    
    public static void main(String[] args) {
    
    
        PetShop shop = new PetShop();

        Pet dog = shop.sale("Dog");
        dog.setNickname("小白");
        dog.eat();

        Pet cat = shop.sale("Cat");
        cat.setNickname("雪球");
        cat.eat();
    }
}

En desarrollo, a veces, cuando diseñamos una matriz, o una variable miembro, o el parámetro formal de un método y el tipo de valor de retorno, no podemos determinar su tipo específico, sino que solo podemos determinar que se trata de una determinada serie de tipos.

caso:

(1) Declare una clase Dog, incluido el método public void eat(), y genere "dog roe huesos"

(2) Declare una clase Cat, incluido el método public void eat(), y emita "el gato se come el pescado".

(3) Declarar una clase Person con las siguientes funciones:

  • Contiene propiedades para mascotas.
  • Contiene el método de adopción de una mascota anulación pública adoptar (tipo de mascota Mascota)
  • Contiene el método public void feed() para alimentar mascotas, implementado como una llamada al método pet object.eat()
public class Dog {
    
    
    public void eat(){
    
    
        System.out.println("狗啃骨头");
    }
}
public class Cat {
    
    
    public void eat(){
    
    
        System.out.println("猫吃鱼仔");
    }
}
public class Person {
    
    
    private Dog dog;

    //adopt:领养
    public void adopt(Dog dog){
    
    
        this.dog = dog;
    }

    //feed:喂食
    public void feed(){
    
    
        if(dog != null){
    
    
            dog.eat();
        }
    }
}

5.5.2 Ventajas y desventajas del polimorfismo

Beneficios : los diferentes objetos de subclase a los que hacen referencia las variables tienen diferentes métodos de ejecución, realizando un enlace dinámico. La escritura del código es más flexible, la función es más poderosa y la mantenibilidad y la escalabilidad son mejores.

Desventajas : si una variable de tipo de referencia se declara como el tipo de la clase principal, pero en realidad se refiere al objeto de la subclase, entonces la variable ya no puede acceder a las propiedades y métodos agregados en la subclase.

Student m = new Student();
m.school = "pku"; 	//合法,Student类有school成员变量
Person e = new Student(); 
e.school = "pku";	//非法,Person类没有school成员变量

// 属性是在编译时确定的,编译时e为Person类型,没有school成员变量,因而编译错误。

En desarrollo:

El uso de la clase padre como parámetro formal del método es la ocasión de polimorfismo más utilizada. Incluso si se agrega una nueva subclase, no es necesario cambiar el método, lo que mejora la escalabilidad y se ajusta al principio de apertura y cierre.

【Principio de apertura y cierre OCP】

  • Abierto para extensión, cerrado para modificación
  • Explicación popular: varios componentes en un sistema de software, como módulos (Módulos), clases (Clases) y funciones (Funciones), deben introducir nuevas funciones sin modificar los códigos existentes.

5.5.3 Invocación de métodos virtuales

En Java, un método virtual se refiere a un método cuya dirección de entrada de llamada no se puede determinar en la etapa de compilación, sino que solo se puede determinar en la etapa de tiempo de ejecución, es decir, un método que se puede reescribir.

Person e = new Student();
e.getInfo();	//调用Student类的getInfo()方法

El método con el mismo nombre y los mismos parámetros que la clase principal se define en la subclase. En el caso del polimorfismo, el método de la clase principal en este momento se denomina método virtual. La clase principal llama dinámicamente a los métodos que pertenecen a la subclase de acuerdo con los diferentes objetos de subclase asignados a ella.este método. Tales llamadas a métodos no se pueden determinar en tiempo de compilación.

静态链接(或早起绑定): cuando se carga un archivo de código de bytes en la JVM, si el método de destino llamado se conoce en tiempo de compilación y permanece sin cambios en tiempo de ejecución. En este caso, el proceso de convertir la referencia simbólica del método de llamada en una referencia directa se denomina enlace estático. Luego, llamar a dicho método se denomina llamada de método no virtual. Por ejemplo, llamar a métodos estáticos, métodos privados, métodos finales, constructores de clases principales y constructores sobrecargados de esta clase.

动态链接(或晚期绑定): si el método llamado no se puede determinar en tiempo de compilación, es decir, la referencia simbólica del método que llama solo se puede convertir en una referencia directa en el tiempo de ejecución del programa, porque este tipo de proceso de conversión de referencia es dinámico, también se llama It es un enlace dinámico. La llamada a dicho método se denomina llamada de método virtual. Por ejemplo, llame al método reescrito (para la clase principal), el método implementado (para la interfaz).

5.5.4 Las variables miembro no son polimórficas

  • Si la subclase reescribe el método de la clase principal, significa que el método definido en la subclase cubre completamente el método del mismo nombre en la clase principal, y el sistema no podrá transferir el método de la clase principal a la clase principal. subclase.

  • Para las variables de instancia, este fenómeno no existe Incluso si la variable de instancia que es exactamente igual a la clase principal está definida en la subclase, todavía es imposible que esta variable de instancia anule la variable de instancia definida en la clase principal.

public class TestVariable {
    
    
    public static void main(String[] args) {
    
    
        Base b = new Sub();
        System.out.println(b.a);
        System.out.println(((Sub)b).a);

        Sub s = new Sub();
        System.out.println(s.a);
        System.out.println(((Base)s).a);
    }
}
class Base{
    
    
    int a = 1;
}
class Sub extends Base{
    
    
    int a = 2;
}

5.5.5 Transformación ascendente y transformación descendente

En primer lugar, el tipo de objeto que crea un objeto cuando es nuevo no cambiará de principio a fin. Es decir, el tipo de tiempo de ejecución de este objeto y el tipo esencial se utilizan para no cambiar. Sin embargo, cuando este objeto se asigna a variables de diferentes tipos, los tipos de tiempo de compilación de estas variables son diferentes.
Polimorfismo, debe haber un momento en que el objeto de la subclase se asigna a la variable de la clase principal. En este momento, habrá 编译期间un fenómeno de conversión de tipos.
Sin embargo, después de usar la variable de la clase principal para recibir el objeto de la subclase, tenemos 不能调用métodos que tiene la subclase pero que la clase principal no tiene. Este es también un pequeño "pequeño problema" que nos trae el polimorfismo. Por lo tanto, si desea llamar a un método exclusivo de una subclase, debe realizar una conversión de tipo para que 编译通过.

  • Upcasting : cuando el tipo de la variable a la izquierda (clase principal) > el tipo de objeto/variable a la derecha (subclase), lo llamamos upcasting

    • En este momento, al compilar según el tipo de variable de la izquierda, solo puede llamar a las variables y métodos de la clase principal, y no puede llamar a las variables y métodos únicos de la subclase.
    • Sin embargo, en tiempo de ejecución, sigue siendo el tipo del objeto en sí , por lo que el método ejecutado es el cuerpo del método reescrito por la subclase.
    • En este momento, debe ser seguro, y también se hace automáticamente.
  • Downcasting : cuando el tipo (subclase) de la variable a la izquierda < el tipo de tiempo de compilación (clase principal) del objeto/variable a la derecha, lo llamamos downcasting

    • En este momento, según el tipo de variable de la izquierda al compilar, puede llamar a las variables y métodos únicos de la subclase
    • Sin embargo, en tiempo de ejecución, sigue siendo el tipo del objeto en sí.
    • No todas las transformaciones hacia abajo a través de la compilación son correctas y puede ocurrir ClassCastException. Por seguridad, puede usar la palabra clave isInstanceof para juzgar

Transformación ascendente: finalización automática

Transformación descendente: (tipo de subclase) variable de clase principal

public class ClassCastTest {
    
    
    public static void main(String[] args) {
    
    
        //没有类型转换
        Dog dog = new Dog();//dog的编译时类型和运行时类型都是Dog

        //向上转型
        Pet pet = new Dog();//pet的编译时类型是Pet,运行时类型是Dog
        pet.setNickname("小白");
        pet.eat();//可以调用父类Pet有声明的方法eat,但执行的是子类重写的eat方法体
//        pet.watchHouse();//不能调用父类没有的方法watchHouse

        Dog d = (Dog) pet;
        System.out.println("d.nickname = " + d.getNickname());
        d.eat();//可以调用eat方法
        d.watchHouse();//可以调用子类扩展的方法watchHouse

        Cat c = (Cat) pet;//编译通过,因为从语法检查来说,pet的编译时类型是Pet,Cat是Pet的子类,所以向下转型语法正确
        //这句代码运行报错ClassCastException,因为pet变量的运行时类型是Dog,Dog和Cat之间是没有继承关系的
    }
}

5.5.6 Palabra clave: instancia de

Para evitar la aparición de ClassCastException, Java proporciona instanceofla palabra clave para verificar el tipo de variables de referencia. El formato del código es el siguiente:

//检验对象a是否是数据类型A的对象,返回值为boolean型
对象a instanceof 数据类型A 
  • ilustrar:
    • Siempre que la instancia de devuelva verdadero, debe ser seguro convertir a este tipo y ClassCastException no se informará.
    • Si el objeto a pertenece a la subclase B de la clase A, el valor de una instancia de A también es verdadero.
    • Se requiere que la clase a la que pertenece el objeto a y la clase A deben estar en la relación entre una subclase y una clase padre, de lo contrario se produce un error de compilación.

código:

public class TestInstanceof {
    
    
    public static void main(String[] args) {
    
    
        Pet[] pets = new Pet[2];
        pets[0] = new Dog();//多态引用
        pets[0].setNickname("小白");
        pets[1] = new Cat();//多态引用
        pets[1].setNickname("雪球");

        for (int i = 0; i < pets.length; i++) {
    
    
            pets[i].eat();

            if(pets[i] instanceof Dog){
    
    
                Dog dog = (Dog) pets[i];
                dog.watchHouse();
            }else if(pets[i] instanceof Cat){
    
    
                Cat cat = (Cat) pets[i];
                cat.catchMouse();
            }
        }
    }
}

Práctica: prueba escrita y entrevista

Tema 1: La diferencia entre las variables miembro heredadas y los métodos heredados

class Base {
    
    
    int count = 10;
    public void display() {
    
    
        System.out.println(this.count);
    }
}

class Sub extends Base {
    
    
    int count = 20;
    public void display() {
    
    
        System.out.println(this.count);
    }
}

public class FieldMethodTest {
    
    
    public static void main(String[] args){
    
    
        Sub s = new Sub();
        System.out.println(s.count);
        s.display();
        Base b = s;
        System.out.println(b == s);
        System.out.println(b.count);
        b.display();
    }
}

Tema 2:

//考查多态的笔试题目:
public class InterviewTest1 {
    
    

	public static void main(String[] args) {
    
    
		Base base = new Sub();
		base.add(1, 2, 3);

//		Sub s = (Sub)base;
//		s.add(1,2,3);
	}
}

class Base {
    
    
	public void add(int a, int... arr) {
    
    
		System.out.println("base");
	}
}

class Sub extends Base {
    
    

	public void add(int a, int[] arr) {
    
    
		System.out.println("sub_1");
	}

//	public void add(int a, int b, int c) {
    
    
//		System.out.println("sub_2");
//	}

}

Tema 3:

//getXxx()和setXxx()声明在哪个类中,内部操作的属性就是哪个类里的。
public class InterviewTest2 {
    
    
	public static void main(String[] args) {
    
    
		Father f = new Father();
		Son s = new Son();
		System.out.println(f.getInfo());//atguigu
		System.out.println(s.getInfo());//尚硅谷
		s.test();//尚硅谷  atguigu
		System.out.println("-----------------");
		s.setInfo("大硅谷");
		System.out.println(f.getInfo());//atguigu
		System.out.println(s.getInfo());//大硅谷
		s.test();//大硅谷  atguigu
	}
}

class Father {
    
    
	private String info = "atguigu";

	public void setInfo(String info) {
    
    
		this.info = info;
	}

	public String getInfo() {
    
    
		return info;
	}
}

class Son extends Father {
    
    
	private String info = "尚硅谷";
	
	public void setInfo(String info) {
    
    
		this.info = info;
	}

	public String getInfo() {
    
    
		return info;
	}
	
	public void test() {
    
    
		System.out.println(this.getInfo());
		System.out.println(super.getInfo());
	}
}

Pregunta 4: ¿Es el polimorfismo un comportamiento en tiempo de compilación o un comportamiento en tiempo de ejecución?

//证明如下:
class Animal  {
    
    
	protected void eat() {
    
    
		System.out.println("animal eat food");
	}
}

class Cat  extends Animal  {
    
    
	protected void eat() {
    
    
		System.out.println("cat eat fish");
	}
}

class Dog  extends Animal  {
    
    
	public void eat() {
    
    
		System.out.println("Dog eat bone");
	}
}

class Sheep  extends Animal  {
    
    
	public void eat() {
    
    
		System.out.println("Sheep eat grass");
	}
}

public class InterviewTest {
    
    
	public static Animal  getInstance(int key) {
    
    
		switch (key) {
    
    
		case 0:
			return new Cat ();
		case 1:
			return new Dog ();
		default:
			return new Sheep ();
		}

	}

	public static void main(String[] args) {
    
    
		int key = new Random().nextInt(3);
		System.out.println(key);

		Animal  animal = getInstance(key);
		animal.eat(); 
	}
}

5.6 Uso de la clase Object

Una clase java.lang.Objectes la clase raíz de la jerarquía de clases, la clase principal de todas las demás clases. Cada clase se utiliza Objectcomo una superclase.

  • Hay referencias polimórficas entre variables de tipo Objeto y objetos de cualquier tipo de datos de referencia excepto Objeto

    method(Object obj){
          
          } //可以接收任何类作为其参数
    
    Person o = new Person();  
    method(o);
    
    
  • Todos los objetos (incluidos los arreglos) implementan los métodos de esta clase.

  • Si una clase no especifica una clase principal, hereda de la clase Object de forma predeterminada. Por ejemplo:

    public class Person {
          
          
    	...
    }
    //等价于:
    public class Person extends Object {
          
          
    	...
    }
    

5.6.1 Métodos de la clase Object

Según el código fuente de JDK y la documentación API de la clase Object, hay 11 métodos contenidos en la clase Object. Aquí nos centramos principalmente en 6 de ellos:

1. (Tecla) es igual a()

= =:

  • Los tipos primitivos comparan valores: verdadero siempre que los valores de dos variables sean iguales.

    int a=5; 
    if(a==6){
          
          }
    
  • Los tipos de referencia comparan referencias (ya sea que apunten al mismo objeto): == devuelve verdadero solo cuando apuntan al mismo objeto.

    Person p1=new Person();  	    
    Person p2=new Person();
    if (p1==p2){
          
          }
    
    • Al usar "==" para comparar, en ambos lados del símbolo 数据类型必须兼容(excepto para los tipos de datos básicos que se pueden convertir automáticamente), de lo contrario, el error de compilación

**equals():**Todas las clases heredan Object, por lo que obtienen el método equals(). También se puede anular.

  • Solo se pueden comparar tipos de referencia. La función de equals() en el código fuente de la clase Object es la misma que "==": comparar si apuntan al mismo objeto.

    imagen-20220503104750655
  • Formato: obj1.equals(obj2)

  • Caso especial: Al usar el método equals() para la comparación, para las clases File, String, Date y wrappers (Wrapper Class), el tipo y el contenido se comparan independientemente de si las referencias son el mismo objeto;

    • Motivo: el método equals() de la clase Object se anula en estas clases.
  • Al personalizar equals(), se puede anular. Se utiliza para comparar si los "contenidos" de dos objetos son iguales

  • El principio de anular el método equals()

    • 对称性: Si x.equals(y) devuelve "verdadero", entonces y.equals(x) también debería devolver "verdadero".

    • 自反性: x.equals(x) debe devolver "verdadero".

    • 传递性: Si x.equals(y) devuelve "verdadero" e y.equals(z) devuelve "verdadero", entonces z.equals(x) también debería devolver "verdadero".

    • 一致性: Si x.equals(y) devuelve "verdadero", siempre que los contenidos de x e y permanezcan sin cambios, no importa cuántas veces repita x.equals(y), el resultado es "verdadero".

    • En cualquier caso, x.equals(null) siempre devuelve "falso";

      x.equals (un objeto de un tipo diferente a x) siempre devuelve "falso".

  • Ejemplo de reescritura:

class User{
    
    
	private String host;
	private String username;
	private String password;
	public User(String host, String username, String password) {
    
    
		super();
		this.host = host;
		this.username = username;
		this.password = password;
	}
	public User() {
    
    
		super();
	}
	public String getHost() {
    
    
		return host;
	}
	public void setHost(String host) {
    
    
		this.host = host;
	}
	public String getUsername() {
    
    
		return username;
	}
	public void setUsername(String username) {
    
    
		this.username = username;
	}
	public String getPassword() {
    
    
		return password;
	}
	public void setPassword(String password) {
    
    
		this.password = password;
	}
	@Override
	public String toString() {
    
    
		return "User [host=" + host + ", username=" + username + ", password=" + password + "]";
	}
	@Override
	public boolean equals(Object obj) {
    
    
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		User other = (User) obj;
		if (host == null) {
    
    
			if (other.host != null)
				return false;
		} else if (!host.equals(other.host))
			return false;
		if (password == null) {
    
    
			if (other.password != null)
				return false;
		} else if (!password.equals(other.password))
			return false;
		if (username == null) {
    
    
			if (other.username != null)
				return false;
		} else if (!username.equals(other.username))
			return false;
		return true;
	}
	
}

Pregunta de la entrevista: La diferencia entre == e igual

  • == puede comparar tipos primitivos y tipos de referencia. Para tipos básicos, es para comparar valores, y para tipos de referencia, es para comparar direcciones de memoria.

  • Para equals, pertenece al método en la clase java.lang.Object, si el método no ha sido anulado, también es == por defecto, podemos ver que el método equals de String y otras clases ha sido anulado, y la clase String está en Se usa mucho en el desarrollo diario, y con el tiempo se ha formado la visión errónea de que igual es un valor comparativo.

  • Específicamente, depende de si el método equals de Object se reescribe en la clase personalizada para juzgar.

  • Normalmente, anular el método equals comparará si las propiedades correspondientes en la clase son iguales.

Ejercicio 1:

int it = 65;
float fl = 65.0f;
System.out.println("65和65.0f是否相等?" + (it == fl)); // 输出 false
//65 和 65.0f 不相等,因为它们虽然值相同,但数据类型不同

char ch1 = 'A'; char ch2 = 12;
System.out.println("65和'A'是否相等?" + (it == ch1)); // 输出 true
//65 和 'A' 相等,因为在 char 类型中,A 的 Unicode 码值是 65,也就是说 ch1 的值是 65
System.out.println("12和ch2是否相等?" + (12 == ch2)); // 输出 true
//12 和 ch2 相等,因为它们的值相同,都是 12

String str1 = new String("hello");
String str2 = new String("hello");
System.out.println("str1和str2是否相等?" + (str1 == str2)); // 输出 false
//str1 和 str2 不相等,因为它们虽然存储的字符串相同,但是是两个不同的对象,比较的是两者在内存中的引用地址

System.out.println("str1是否equals str2?" + (str1.equals(str2))); // 输出 true
//str1.equals(str2) 返回 true,说明两个对象存储的字符串相等
System.out.println("hello" == new java.util.Date()); // 抛出编译错误,因为无法比较两个不同类型的对象

Ejercicio 2:

Escriba la clase Order, que tiene orderId de tipo int, orderName de tipo String, los métodos getter() y setter() correspondientes, un constructor con dos parámetros y anula el método equals() de la clase principal: public boolean equals(Object obj) y juzgue si los dos objetos creados en la clase de prueba son iguales.

public class Order {
    
    
    private int orderId; // 订单编号
    private String orderName; // 订单名称

    /**
     * 两个参数的构造器
     * @param orderId 订单编号
     * @param orderName 订单名称
     */
    public Order(int orderId, String orderName) {
    
    
        this.orderId = orderId;
        this.orderName = orderName;
    }

    /**
     * 获取订单编号
     * @return 订单编号
     */
    public int getOrderId() {
    
    
        return orderId;
    }

    /**
     * 设置订单编号
     * @param orderId 订单编号
     */
    public void setOrderId(int orderId) {
    
    
        this.orderId = orderId;
    }

    /**
     * 获取订单名称
     * @return 订单名称
     */
    public String getOrderName() {
    
    
        return orderName;
    }

    /**
     * 设置订单名称
     * @param orderName 订单名称
     */
    public void setOrderName(String orderName) {
    
    
        this.orderName = orderName;
    }

    /**
     * 重写 equals 方法,判断两个对象是否相等
     * @param obj 待比较对象
     * @return true(相等)/false(不相等)
     */
    @Override
    public boolean equals(Object obj) {
    
    
        if (this == obj) //判断是否为同一个对象
            return true;
        if (obj instanceof Order) {
    
     //判断是否为同一类型
            Order order = (Order) obj;
            return this.orderId == order.orderId && this.orderName.equals(order.orderName); // 判断两个对象的 orderId 和 orderName 是否相等
        }
        return false;
    }

    public static void main(String[] args) {
    
    
        Order order1 = new Order(1, "order1"); // 创建 Order 对象 1
        Order order2 = new Order(2, "order2"); // 创建 Order 对象 2
        Order order3 = new Order(1, "order1"); // 创建 Order 对象 3
        System.out.println(order1.equals(order2)); // 判断 Order 对象 1 和 2 是否相等
        // 结果:false
        System.out.println(order1.equals(order3)); // 判断 Order 对象 1 和 3 是否相等
        // 结果:true
    }
}

2. (énfasis) toString()

Firma del método: public String toString()

① Por defecto, toString() devuelve "la forma hexadecimal del tiempo de ejecución del objeto type@object's hashCode value"

② Al conectar String y otros tipos de datos, el método toString() se llama automáticamente

Date now=new Date();
System.out.println(“now=+now);  //相当于
System.out.println(“now=+now.toString()); 

③ Si usamos directamente System.out.println (objeto), llamará automáticamente a String() de este objeto de forma predeterminada

Debido a que la dirección de memoria del objeto en realidad se almacena en la variable del tipo de datos de referencia de Java, pero Java oculta la información de la dirección de memoria del programador, por lo que la dirección de memoria no se puede mostrar directamente, por lo que cuando imprime el objeto, la JVM lo llama para su objeto toString().

④ El método toString() se puede reescribir en el tipo definido por el usuario según sea necesario.
Por ejemplo, la clase String reescribe el método toString() para devolver el valor de la cadena.

s1="hello";
System.out.println(s1);//相当于System.out.println(s1.toString());

Por ejemplo, una clase de persona personalizada:

public class Person {
    
      
    private String name;
    private int age;

    @Override
    public String toString() {
    
    
        return "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
    }
}

3, clonar ()

//Object类的clone()的使用
public class CloneTest {
    
    
	public static void main(String[] args) {
    
    
		Animal a1 = new Animal("花花");
		try {
    
    
			Animal a2 = (Animal) a1.clone();
			System.out.println("原始对象:" + a1);
			a2.setName("毛毛");
			System.out.println("clone之后的对象:" + a2);
		} catch (CloneNotSupportedException e) {
    
    
			e.printStackTrace();
		}
	}
}

class Animal implements Cloneable{
    
    
	private String name;

	public Animal() {
    
    
		super();
	}

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

	public String getName() {
    
    
		return name;
	}

	public void setName(String name) {
    
    
		this.name = name;
	}

	@Override
	public String toString() {
    
    
		return "Animal [name=" + name + "]";
	}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
    
    
		// TODO Auto-generated method stub
		return super.clone();
	}
	
}

4, finalizar ()

  • Cuando se recicla un objeto, el sistema llama automáticamente al método finalize() del objeto. (no llamado por el recolector de basura, pero llamado por el objeto de esta clase)
    • El método de finalización de un objeto siempre debe 不要主动调用ser llamado por el mecanismo de recolección de elementos no utilizados.
  • Cuándo se recicla: Cuando un objeto no tiene ninguna referencia, la JVM considera que el objeto es un objeto basura y utilizará el mecanismo de recolección de basura para destruir el objeto en un tiempo indeterminado más adelante. Antes de destruir el objeto, llamará finalizar ()método.
  • Las subclases pueden anular este método para realizar las operaciones de limpieza necesarias antes de que se limpie el objeto. Por ejemplo, desconecte los recursos de conexión relacionados dentro del método.
    • Si este método se anula para que una nueva variable de referencia vuelva a hacer referencia al objeto, el objeto se reactivará.
  • Este método ha quedado obsoleto en JDK 9 标记为过时.
public class FinalizeTest {
    
    
	public static void main(String[] args) {
    
    
		Person p = new Person("Peter", 12);
		System.out.println(p);
		p = null;//此时对象实体就是垃圾对象,等待被回收。但时间不确定。
		System.gc();//强制性释放空间
	}
}

class Person{
    
    
	private String name;
	private int age;

	public Person(String name, int age) {
    
    
		super();
		this.name = name;
		this.age = age;
	}
	public String getName() {
    
    
		return name;
	}
	public void setName(String name) {
    
    
		this.name = name;
	}
	public int getAge() {
    
    
		return age;
	}
	public void setAge(int age) {
    
    
		this.age = age;
	}
	//子类重写此方法,可在释放对象前进行某些操作
	@Override
	protected void finalize() throws Throwable {
    
    
		System.out.println("对象被释放--->" + this);
	}
	@Override
	public String toString() {
    
    
		return "Person [name=" + name + ", age=" + age + "]";
	}
	
}

5, obtenerClase()

public final Class<?> getClass(): Obtener el tipo de tiempo de ejecución del objeto

Debido a que Java tiene polimorfismo, el tipo de tiempo de compilación de una variable que se refiere a un tipo de datos puede no ser coherente con el tipo de tiempo de ejecución, por lo que si necesita verificar el tipo de objeto al que apunta la variable, debe usar el método getClass()

public static void main(String[] args) {
    
    
	Object obj = new Person();
	System.out.println(obj.getClass());//运行时类型
}

resultado:

class com.atguigu.java.Person

6, código hash()

public int hashCode(): devuelve el valor hash de cada objeto. (El seguimiento se centrará en el capítulo del marco de recopilación)

public static void main(String[] args) {
    
    
	System.out.println("AA".hashCode());//2080
    System.out.println("BB".hashCode());//2112
}

5.6.2 Palabra clave: nativo

Utilice la palabra clave native para indicar que este método es una función nativa, es decir, este método se implementa en un C/C++lenguaje que no es Java y 被编译成了DLLJava lo llama.

  • Los métodos nativos tienen cuerpos de métodos y están escritos en lenguaje C. Dado que el código fuente del cuerpo del método del método local no es de código abierto para nosotros, no podemos ver el cuerpo del método

  • Al definir un método nativo en Java, no se proporciona un cuerpo de implementación.

1. Por qué usar el método nativo

Java es muy conveniente de usar, pero no es fácil implementar algunas tareas en Java, o cuando nos preocupamos por la eficiencia del programa, por ejemplo: Java necesita intercambiar información con algunos sistemas operativos subyacentes o algún hardware. El método nativo es solo un mecanismo de comunicación de este tipo: nos proporciona una interfaz muy concisa y no necesitamos comprender los detalles engorrosos fuera de la aplicación Java.

2. El método declarado por native se puede usar como otros métodos de Java para la persona que llama

La existencia de métodos nativos no tiene ningún impacto en otras clases que llaman a estos métodos nativos, de hecho, otras clases que llaman a estos métodos ni siquiera saben que están llamando a un método nativo. La JVM controlará todos los detalles de la llamada a métodos nativos.

Supongo que te gusta

Origin blog.csdn.net/weixin_52357829/article/details/129909882
Recomendado
Clasificación