¿Qué? ¡Al escuchar estos cuatro conceptos, muchos veteranos de Java no tienen idea!

¿Qué? ¡Al escuchar estos cuatro conceptos, muchos veteranos de Java no tienen idea!

Java es un lenguaje de programación que mucha gente ha estado usando, pero algunos conceptos de Java son muy difíciles de entender. Incluso algunos veteranos durante muchos años tienen cierta confusión y confusión acerca de algunos conceptos de Java.
Por lo tanto, en este artículo, presentaremos los cuatro conceptos más difíciles en Java para ayudar a los desarrolladores a comprender estos conceptos con mayor claridad:
1. El uso de clases internas anónimas
2. Multithreading
3. Cómo lograr la sincronización
4 .Publicación por entregas

Clase interna anónima

Una clase interna anónima también se llama clase anónima. Es un poco como una clase local o una clase interna, excepto que la clase interna anónima no tiene nombre. Podemos declarar e instanciar una clase interna anónima al mismo tiempo.
Una clase interna anónima solo es adecuada para escenarios en los que desea usar una clase parcial y solo usar esta clase parcial una vez.
Una clase interna anónima no tiene un constructor que deba declararse explícitamente, pero habrá un constructor oculto y declarado automáticamente.
Hay dos formas de crear una clase interna anónima:
1. Crear una clase interna anónima heredando una clase (ya sea concreta o abstracta)
2. Crear una clase interna anónima implementando una interfaz
Veamos el siguiente ejemplo:


interface Programmer {
    void develop();
}

public class TestAnonymousClass {
    public static Programmer programmer = new Programmer() {
        @Override
        public void develop() {
            System.out.println("我是在类中实现了接口的匿名内部类");
        }
    };

    public static void main(String[] args) {
        Programmer anotherProgrammer = new Programmer() {
            @Override
            public void develop() {
                System.out.println("我是在方法中实现了接口的匿名内部类");
            }
        };

        TestAnonymousClass.programmer.develop();
        anotherProgrammer.develop();
    }
}

Como se puede ver en el ejemplo anterior, las clases anónimas se pueden crear en clases o en métodos.
Anteriormente también mencionamos que las clases anónimas pueden heredar una clase concreta o una clase abstracta, o implementar una interfaz. Entonces, en el código anterior, creé una interfaz llamada Programmer e implementé la interfaz en la clase TestAnonymousClass y el método main () respectivamente.
Además de la interfaz, el programador puede ser una clase abstracta o una clase concreta.
Clase abstracta, como el siguiente código:

public abstract class Programmer {
    public abstract void develop();
}

El código de clase específico es el siguiente:

public class Programmer {
    public void develop() {
        System.out.println("我是一个具体类");
    }
}

Bien, vaya más allá, entonces, ¿qué pasa si no hay un constructor sin parámetros para la clase Programmer? ¿Podemos acceder a variables de clase en clases anónimas? Si heredamos una clase, ¿necesitamos implementar todos los métodos en la clase anónima?

public class Programmer {
    protected int age;

    public Programmer(int age) {
        this.age = age;
    }

    public void showAge() {
        System.out.println("年龄:" + age);
    }

    public void develop() {
        System.out.println("开发中……除了异性,他人勿扰");
    }

    public static void main(String[] args) {
        Programmer programmer = new Programmer(38) {
            @Override
            public void showAge() {
                System.out.println("在匿名类中的showAge方法:" + age);
            }
        };
        programmer.showAge();
    }
}

1. Al construir una clase anónima, podemos usar cualquier constructor. En el código anterior, podemos ver que hemos utilizado un constructor con parámetros.
2. Las clases anónimas pueden heredar clases concretas o abstractas y también pueden implementar interfaces. Entonces, las reglas del modificador de acceso son las mismas que las de las clases ordinarias. La clase secundaria puede acceder a las propiedades restringidas protegidas en la clase principal, pero no puede acceder a las propiedades restringidas privadas.
3. Si la clase anónima hereda una clase concreta, como la clase Programmer en el código anterior, no es necesario volver a escribir todos los métodos. Pero si una clase anónima hereda una clase abstracta o implementa una interfaz, entonces la clase anónima debe implementar todos los métodos abstractos que no estén implementados.
4. No puede usar la inicialización estática en una clase interna anónima, ni puede agregar variables estáticas.
5. La clase interna anónima puede tener constantes estáticas modificadas por final.

Escenarios de uso típicos de clases anónimas

Uso temporal: a veces necesitamos agregar implementaciones temporales de algunas clases para solucionar algunos problemas o agregar algunas funciones. Para evitar agregar archivos java al proyecto, especialmente cuando usamos esta clase solo una vez, usaremos clases anónimas.
Oyentes de eventos de la interfaz de usuario: en la programación de interfaz gráfica de Java, el escenario más utilizado para las clases anónimas es crear un detector de eventos. Por ejemplo:
button.setOnClickListener (new View.OnClickListener () {
public void onClick (View v) {
}
}); En el
código anterior, hemos implementado la interfaz setOnClickListener a través de una clase anónima, que nos activará cuando el usuario haga clic en el botón El método onClick implementado.

Multihilo

El subproceso múltiple en Java es el uso de varios subprocesos para completar el proceso de ejecución de una tarea grande. El uso de subprocesos múltiples puede maximizar el uso de la CPU.
El uso de subprocesos múltiples usa subprocesos en lugar de procesos para realizar el procesamiento de tareas porque los subprocesos son más livianos que los procesos. Los subprocesos son un proceso liviano, la unidad más pequeña de ejecución del programa y la memoria principal se comparte entre subprocesos Y el proceso no lo es.

Ciclo de vida del hilo

¿Qué?  ¡Al escuchar estos cuatro conceptos, muchos veteranos de Java no tienen idea!

Como se muestra en la figura anterior, hay seis estados en el ciclo de vida del hilo. Ahora presentaremos estos estados uno por uno.
1. Nuevo: cuando construimos una instancia de hilo, el hilo tiene el estado Nuevo. Este estado es el primer estado del hilo. En este punto, el hilo no está listo para ejecutarse.
2. Ejecutable: cuando se llama al método start () de la clase de hilo, el hilo pasará del estado Nuevo al estado Ejecutable. Esto significa que este hilo está listo para ejecutarse. Sin embargo, si el hilo realmente quiere ejecutarse, necesita un programador de hilos para programar la ejecución de este hilo. Sin embargo, el programador de subprocesos puede estar ocupado ejecutando otros subprocesos y no puede programar la ejecución de este subproceso a tiempo. El programador de subprocesos se basa en la estrategia FIFO para seleccionar un subproceso del grupo de subprocesos para su ejecución.
3. Bloqueado: el hilo puede cambiar automáticamente al estado Bloqueado debido a diferentes situaciones. Por ejemplo, esperar operaciones de E / S, esperar conexiones de red, etc. Además, cualquier subproceso con una prioridad más alta que el subproceso actualmente en ejecución puede hacer que el subproceso en ejecución cambie al estado Bloqueado.
4. En espera: llame al método de espera del objeto sincronizado en el bloque de sincronización y el hilo actual entrará en el estado de espera. Si se llama anotificar () / notificarAll () en un bloque sincronizado donde el mismo objeto en otro hilo está sincronizado, puede hacer que el hilo en espera entre en el estado Ejecutable.
5.Timed_Waiting: Igual que el estado de espera, pero habrá un límite de tiempo. Cuando expire el tiempo de espera, el hilo entrará automáticamente en el estado de ejecución.
6. Terminado: el subproceso entrará en el estado Terminado después de que se ejecute el método run () del subproceso o después de que el método run () salga de forma anormal.

Por que usar multihilo

En la lengua vernácula, es utilizar varios subprocesos para hacer varias cosas al mismo tiempo para que las aplicaciones Java se ejecuten más rápido, utilizando subprocesos para implementar el paralelismo y la concurrencia. Las CPU de hoy son de varios núcleos y tienen una alta frecuencia. Si se utiliza un solo hilo, las ventajas de las CPU de varios núcleos no se aprovechan por completo.
Ventaja importante

  • Puede hacer un mejor uso de la CPU
  • Puede mejorar mejor la experiencia del usuario relacionada con la capacidad de respuesta
  • Puede reducir el tiempo de respuesta
  • Puede atender a varios clientes al mismo tiempo

    Hay dos formas de crear hilos

1. Cree un hilo heredando la clase Thread.
Esta clase heredada anulará el método run () de la clase Thread. La ejecución real de un hilo comienza desde dentro del método run (), y el método run () de este hilo se llama a través del método start ().


public class MultithreadDemo extends Thread {
    @Override
    public void run() {
        try {
            System.out.println("线程 " +  Thread.currentThread().getName() + " 现在正在运行");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            MultithreadDemo multithreadDemo = new MultithreadDemo();
            multithreadDemo.start();
        }
    }
}

2. Crea un hilo implementando la interfaz Runnable
Creamos una nueva clase que implementa la interfaz java.lang.Runnable e implementamos su método run (). Luego crearemos una instancia de un objeto Thread y llamaremos al método start () de este objeto.


public class MultithreadDemo implements Runnable {

    @Override
    public void run() {
        try {
            System.out.println("线程 " + Thread.currentThread().getName() + " 现在正在运行");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(new MultithreadDemo());
            thread.start();
        }
    }
}

Comparación de dos métodos de creación

  • Si una clase hereda la clase Thread, esta clase no puede heredar ninguna otra clase. Debido a que Java es una herencia única, no está permitido heredar varias clases al mismo tiempo. La herencia múltiple solo puede usar interfaces y una clase puede implementar múltiples interfaces. Por lo tanto, utilizar la interfaz Runnable es mejor en la práctica que heredar la clase Thread.
  • El primer método de creación, puede anular yield (), interrupt () y otros métodos que pueden no ser de uso común. Pero si utilizamos el segundo método para crear subprocesos, los métodos como yield () no se pueden reescribir.

    Sincronizar

La sincronización es significativa solo en condiciones de subprocesos múltiples, y solo un subproceso puede ejecutar un bloque sincronizado a la vez.
En Java, el concepto de sincronización es muy importante, porque Java en sí mismo es un lenguaje multiproceso En un entorno multiproceso, la sincronización adecuada es extremadamente importante.

Por que usar la sincronización

Para ejecutar código en un entorno multiproceso, si varios subprocesos pueden acceder a un objeto, es muy necesario utilizar la sincronización para este objeto para evitar errores en el estado del objeto o la ejecución del programa.
Antes de sumergirnos en el concepto de sincronización, echemos un vistazo a los problemas relacionados con la sincronización.

class Production {

    //没有做方法同步
    void printProduction(int n) {
        for (int i = 1; i <= 5; i++) {
            System.out.print(n * i+" ");
            try {
                Thread.sleep(400);
            } catch (Exception e) {
                System.out.println(e);
            }
        }

    }
}

class MyThread1 extends Thread {

    Production p;

    MyThread1(Production p) {
        this.p = p;
    }

    public void run() {
        p.printProduction(5);
    }

}

class MyThread2 extends Thread {

    Production p;

    MyThread2(Production p) {
        this.p = p;
    }

    public void run() {
        p.printProduction(100);
    }
}

public class SynchronizationTest {
    public static void main(String args[]) {
        Production obj = new Production(); //多线程共享同一个对象
        MyThread1 t1 = new MyThread1(obj);
        MyThread2 t2 = new MyThread2(obj);
        t1.start();
        t2.start();
    }
}

Después de ejecutar el código anterior, dado que no agregamos sincronización, podemos ver que el resultado de la ejecución es muy confuso.
Salida: 100 5
10200 15300 20400 25500 A continuación, agregamos sincronización a la impresión Método de producción:

class Production {

    //做了方法同步
    synchronized void printProduction(int n) {
        for (int i = 1; i <= 5; i++) {
            System.out.print(n * i+" ");
            try {
                Thread.sleep(400);
            } catch (Exception e) {
                System.out.println(e);
            }
        }

    }
}

Cuando agregamos sincronización a printProduction (), si ya hay un hilo en ejecución, no habrá ningún hilo que pueda ejecutar este método nuevamente. Esta vez, el resultado de salida después de la sincronización se agrega en orden.
Salida: 5 10 15 20 25 100 200 300 400 500
Similar a los métodos de sincronización, también puede sincronizar clases y objetos Java.
Nota: De hecho, a veces no tenemos que sincronizar todo el método. Por razones de rendimiento, en realidad podemos sincronizar solo la parte del código en el método que necesitamos sincronizar. La parte del código que se sincroniza es el bloque de sincronización del método.

Publicación por entregas

La serialización de Java es un mecanismo para convertir un objeto Java en un flujo de bytes. La conversión del flujo de bytes de nuevo al objeto Java se denomina deserialización, que es la operación inversa de la serialización.
La serialización y deserialización son independientes de la plataforma, lo que significa que puede serializar en el sistema Linux y luego deserializar en el sistema operativo Windows.
Si desea serializar un objeto, debe usar el método writeObject () de la clase ObjectOutputStream. Si desea deserializar, debe usar el método readObject () de la clase ObjectOutputStream.
Como se muestra en la figura siguiente, los objetos se convierten en flujos de bytes y se almacenan en diferentes medios. Este proceso es la serialización. En el lado derecho de la figura, también puede ver que los flujos de bytes se obtienen de diferentes medios, como la memoria, y se convierten en objetos, lo que se denomina deserialización.
¿Qué?  ¡Al escuchar estos cuatro conceptos, muchos veteranos de Java no tienen idea!

Por que utilizar la serialización

Si creamos un objeto Java, el estado de este objeto desaparecerá después de que se ejecute o salga del programa, y ​​no se guardará.
Por tanto, para solucionar este tipo de problemas, Java proporciona un mecanismo de serialización. De esta forma, podemos almacenar temporalmente o conservar el estado del objeto, de modo que cuando necesitemos este objeto más adelante, podamos restaurarlo mediante deserialización.
Aquí hay algunos códigos para ver cómo hacemos la serialización.

import java.io.Serializable;

public class Player implements Serializable {

    private static final long serialVersionUID = 1L;

    private String serializeValueName;
    private transient String nonSerializeValuePos;

    public String getSerializeValueName() {
        return serializeValueName;
    }

    public void setSerializeValueName(String serializeValueName) {
        this.serializeValueName = serializeValueName;
    }

    public String getNonSerializeValueSalary() {
        return nonSerializeValuePos;
    }

    public void setNonSerializeValuePos(String nonSerializeValuePos) {
        this.nonSerializeValuePos = nonSerializeValuePos;
    }

    @Override
    public String toString() {
        return "Player [serializeValueName=" + serializeValueName + "]";
    }
}
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class SerializingObject {
    public static void main(String[] args) {

        Player playerOutput = null;
        FileOutputStream fos = null;
        ObjectOutputStream oos = null;

        playerOutput = new Player();
        playerOutput.setSerializeValueName("niubi");
        playerOutput.setNonSerializeValuePos("x:1000,y:1000");

        try {
            fos = new FileOutputStream("Player.ser");
            oos = new ObjectOutputStream(fos);
            oos.writeObject(playerOutput);

            System.out.println("序列化数据被存放至Player.ser文件");

            oos.close();
            fos.close();

        } catch (IOException e) {

            e.printStackTrace();
        }
    }
}

Salida: los datos serializados se almacenan en el archivo Player.ser

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class DeSerializingObject {

    public static void main(String[] args) {

        Player playerInput = null;
        FileInputStream fis = null;
        ObjectInputStream ois = null;

        try {
            fis = new FileInputStream("Player.ser");
            ois = new ObjectInputStream(fis);
            playerInput = (Player) ois.readObject();

            System.out.println("从Player.ser文件中恢复");

            ois.close();
            fis.close();

        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }

        System.out.println("player名字为 : " + playerInput.getSerializeValueName());
        System.out.println("player位置为 : " + playerInput.getNonSerializeValuePos());
    }
}

Salida:

从Player.ser文件中恢复
player名字为 : niubi
player位置为 : null

Características clave

1. Si la clase padre implementa la interfaz serializable, entonces la subclase no necesita implementar la interfaz serializable. Pero lo contrario no funcionará.
2. La serialización solo admite variables miembro no estáticas.
3. Las variables y constantes modificadas estáticas y las variables modificadas transitorias no se serializarán. Entonces, si no queremos serializar algunas variables miembro no estáticas, simplemente use transient para modificarlas.
4. Al deserializar un objeto, no se llamará al constructor del objeto.
5. Si un objeto es referenciado por un objeto a serializar, el objeto también será serializado y el objeto también debe implementar la interfaz serializable.

para resumir

Primero, presentamos la definición, escenarios de uso y métodos de uso de clases anónimas.
En segundo lugar, discutimos el multi-threading y su ciclo de vida, así como los escenarios de uso de multi-threading.
Una vez más, entendemos la sincronización, después de conocer la sincronización, solo un hilo puede ejecutar el método sincronizado o el bloque de código al mismo tiempo. Cuando un subproceso está ejecutando código sincronizado, otros subprocesos solo pueden esperar en la cola hasta que el subproceso que ejecuta el código sincronizado libere recursos.
Finalmente, sabemos que la serialización consiste en almacenar el estado de un objeto para su uso posterior.

¿Qué?  ¡Al escuchar estos cuatro conceptos, muchos veteranos de Java no tienen idea!

Supongo que te gusta

Origin blog.51cto.com/15006939/2551691
Recomendado
Clasificación