La descarga está en progreso. Por favor, espere

La descarga está en progreso. Por favor, espere

1. Historia. Características de la tecnología. Especificaciones del lenguaje. Maquina virtual de Java. Recolector de basura en HotSpot 2.

Presentaciones similares


Presentación del tema: "1. Historia. Características de la tecnología. Especificaciones del lenguaje. Maquina virtual de Java. Recolector de basura en HotSpot 2."— Transcripción de la presentación:

1 1

2 Historia. Características de la tecnología. Especificaciones del lenguaje. Maquina virtual de Java. Recolector de basura en HotSpot 2

3 El lenguaje Java así como la máquina virtual, comenzaron como un proyecto interno de Sun Microsystems en Los ingenieros de Sun no estaban satisfechos con el rendimiento del lenguaje C++, por lo que James Gosling, Mike Sheridan y Patrick Naughton, junto con otros más, comenzaron a desarrollar un nuevo lenguaje, que en principio pensaron dedicar a la programación de todo tipo de aparatos, tales como microondas, heladeras, teléfonos móviles, etc.. Ellos pensaban que éstos generarían muchas e importantes aplicaciones para la tecnología del futuro. 3

4 El lenguaje tendría que obviar problemas que presenta C++, en campos tales como la programación distribuída, las aplicaciones multihilo, el manejo de la memoria y ser más sencillo de manejar que C++. Finalmente se deseaba que los programas fueran portables a todo tipo de aparatos. Inicialmente el lenguaje se llamó Oak (en español 'roble'), en honor de un roble que había frente a la oficina. 4

5 En 1992, se presentó como demostración una PDA con interface gráfica y un asistente inteligente representado mediante un muñeco llamado Duke. Oak fue presentado a concurso, como solución tecnológica, en varios proyectos para la industria del cine y la televisión, pero no fue elegido. En 1994 John Gage, James Gosling, Bill Joy, Patrick Naughton, Wayne Rosing, y Eric Schmidt se se reunieron para reorientar Oak. Decidieron orientarlo hacia la tecnología de la Web, pues se pensaba que tras la aparición del navegador Mosaic, ésta evolucionaría hacia la misma clase de interactividad, que la televisión por cable, para la cual habían estado preparando Oak. 5

6 Fue asimismo en 1994 cuando se cambió el nombre de Oak a Java. Poco después, aún en 1994, la plataforma Java 1.0, estaba disponible para descarga en la Web. En 1995 Netscape anunció que incluiría soporte para Java en sus navegadores, dando con esto un buen apoyo a Java. 6

7 Simple: Elimina la complejidad de los lenguajes como "C" y da paso al contexto de los lenguajes modernos orientados a objetos. La filosofía de programación orientada a objetos es diferente a la programación convencional. 7

8 Familiar: Como la mayoría de los programadores están acostumbrados a programar en C o en C++, la sintaxis de Java es muy similar a la de estos. 8

9 Robusto: El sistema de Java maneja la memoria de la computadora por uno. No te tienes que preocupar por punteros, memoria que no se esté utilizando, etc. Java realiza todo esto sin necesidad de que uno se lo indique. 9

10 Seguro: El sistema de Java tiene ciertas políticas que evitan se puedan codificar virus con este lenguaje. Existen muchas restricciones, especialmente para los applets, que limitan lo que se puede y no puede hacer con los recursos críticos de una computadora. 10

11 Portable: Como el código compilado de Java (conocido como byte code) es interpretado, un programa compilado de Java puede ser utilizado por cualquier computadora que tenga implementado el interprete de Java. 11

12 Independiente de la arquitectura: Al compilar un programa en Java, el código resultante un tipo de código binario conocido como byte code. Este códido es interpretado por diferentes computadoras de igual manera, solamente hay que implementar un intérprete para cada plataforma. De esa manera Java logra ser un lenguaje que no depende de una arquitectura computacional definida. 12

13 Multithreaded: Un lenguaje que soporta múltiples threads es un lenguaje que puede ejecutar diferentes líneas de código al mismo tiempo. 13

14 Interpretado: Java corre en máquina virtual, por lo tanto es interpretado. 14

15 Dinámico: Java no requiere que compiles todas las clases de un programa para que este funcione. Si realizas una modificación a una clase Java se encarga de realizar un Dynamic Bynding o un Dynamic Loading para encontrar las clases. 15

16 Actualmente Java ha sido clasificada en tres ediciones distintas, cada una orientada hacia distintas áreas de desarrollo: J2SE o Java Standar Edition – Orientada al desarrollo de aplicaciones independientes de la plataforma. J2EE o Java Esterprise Edition – orientada para desarrollo de aplicaciones de entorno empresarial. J2ME o Java Micro Edition – Orientada a dispositivos con capacidades restringidas. 16

17 Mantiene la esencia del la idea original de Java. Sus características primordiales son: Basado en C++ desde su orígen, agregando componentes de alto nivel, (strings y recolector de basura). Código independiente de la plataforma, precompilado a bytecodes, ejecutado en el cliente por una JVM (Java Virtual Machine). Abstracción del sistema operativo subyacente mediante un completo de APIs de programación. Esta versión de Java contiene el conjunto básico de herramientas usadas para desarrollar Java Applets, así cómo las APIs orientadas a la programación de aplicaciones de usuario final. 17

18 Versión orientada a entornos empresariales. Tiene características propias: Pensado para ser ejecutado sobre una red de computadoras de forma distribuída y remota mediante EJBs (Enterprise Java Beans). Orientada especialmente al desarrollo de servicios web, servicios de nombres, persistencia de objetos, XML, autenticación, APIs para la gestión de transacciones, etc. 18

19 Versión de Java enfocada a la aplicación de la tecnología Java en dispositivos electrónicos con capacidades muy reducidas, por ejemplo teléfonos móviles, PDAs o electrodomésticos inteligentes. Tiene unos componentes básicos que la diferencian de las otras versiones. Uso de una máquina virtual denominada KVM (Kilo Virtual Machine un pequeño y rápido recolector de basura). 19

20 Fase I: Editor Se crea un programa con la ayuda de un editor Se almacena en un fichero con extensión.java 20

21 Fase II: Compilador El compilador lee el código Java (fichero.java) Si se detectan errores sintácticos, el compilador nos informa de ello. Se generan los bytecodes, que se almacenan en ficheros.class 21

22 Fase III: Cargador de clases El cargador de clases lee los bytecodes (ficheros.class): Los bytecodes pasan de disco a memoria principal. 22

23 Fase IV: Verificador de bytecodes El verificador de bytecodes comprueba que los bytecodes son válidos y no violan las restricciones de seguridad de la máquina virtual Java. 23

24 Fase V: Intérprete de bytecodes o compilador JIT La máquina virtual Java (JVM) lee los bytecodes y los traduce al lenguaje que el ordenador entiende (código máquina). 24

25 NOTA: Conforme se ejecuta el programa, se hace uso de la memoria principal para almacenar los datos con los que trabaja la aplicación. 25

26 Primero: Java es un lenguaje de programación orientado a objetos, y tiene todos los beneficios que ofrece esta metodología de programación. 26

27 Segundo: No debes volver a escribir el código si quieres ejecutar el programa en otra máquina. Un solo código funciona para todos los browsers compatibles con Java o donde se tenga una Máquina Virtual de Java (Mac's, PC's, Sun's, etc). Un browser compatible con Java deberá ejecutar cualquier programa hecho en Java, esto ahorra a los usuarios tener que estar insertando "plug-ins" y demás programas que a veces nos quitan tiempo y espacio en disco. Se encuentra instalado en mas de millones de dispositivos 27

28 Segundo: No debes volver a escribir el código si quieres ejecutar el programa en otra máquina. Un solo código funciona para todos los browsers compatibles con Java o donde se tenga una Máquina Virtual de Java (Mac's, PC's, Sun's, etc). Un browser compatible con Java deberá ejecutar cualquier programa hecho en Java, esto ahorra a los usuarios tener que estar insertando "plug-ins" y demás programas que a veces nos quitan tiempo y espacio en disco. 28

29 Tercero: Si lo que interesa son las páginas Web, ya no tienen que ser estáticas, se le pueden poner toda clase de elementos multimedia y permiten un alto nivel de interactividad, sin tener que gastar en paquetes carísimos de multimedia. 29

30 Cuarto: Existen dentro de su librería clases gráficas como awt y swing, las cuales permiten crear objetos gráficos comunes altamente configurables y con una arquitectura independiente de la plataforma. 30

31 Quinto: Se puede acceder a bases de datos fácilmente con JDBC, independientemente de la plataforma utilizada. El manejo de las bases de datos es uniforme, es decir transparente y simple. Existen herramientas como Crystal Reports o herramientas libres como iText que los genera en formato pdf. La API que utilizan estas herramientas en Java, es la más recomendable para generar reportes en Web. 31

32 Primero: La velocidad. Los programas hechos en Java no tienden a ser muy rápidos, supuestamente se está trabajando en mejorar esto. Como los programas de Java son interpretados nunca alcanzan la velocidad de un verdadero ejecutable. 32

33 Segundo: Java es un lenguaje de programación. Esta es otra gran limitante, por más que digan que es orientado a objetos y que es muy fácil de aprender sigue siendo un lenguaje y por lo tanto aprenderlo no es cosa fácil. Especialmente para los no programadores. 33

34 Tercero: Hay diferentes tipos de soporte técnico para la misma herramienta, por lo que el análisis de la mejor opción se dificulta. 34

35 Cuarto: El diseño de interfaces gráficas con awt y swing no es simple. Existen herramientas como el JBuilder que permiten generar interfaces gráficas de manera sencilla, pero tienen un costo adicional. 35

36 Quinto: Puede ser que no haya JDBC para bases de datos poco comerciales. 36

37 Clasificación de modificadores: 1)Modificadores de acceso: public, default 2)Modificadores que no son de acceso: strictfp, final y abstract Tipo de acceso a clases: Cuando decimos que el código de una clase (clase A) tiene acceso a otra clase (clase B), significa que la clase A puede hacer una de las 3 cosas: Crear una instancia de la clase B. Heredar la clase B (la clase B es una subclase de B). Acceso a ciertos métodos y variables en la clase B, dependiendo del control de acceso de esos métodos y variables. 37

38 Acceso default: package cert; class Beverage { } package exam.stuff; import cert.Beverage; class Tea extends Beverage { } /* La clase Tea no compilará pues su superclase, Beverage, tiene acceso default y esta en un paquete diferente.*/ Acceso público: La declaración de clase public le acceso a todas las clases de todos los paquetes a una clase publica. Nota: Si la clase publica esta en otro paquete, igual habrá que importarla. 38

39 Strictfp: Hacer que una clase sea strictfp significa que el código de sus métodos respetaran el estándar IEEE 754 para puntos flotantes. Final: package cert; public final class Beverage { public void importantMethod() { } } package exam.stuff; import cert.Beverage; class Tea extends Beverage { } // Obtendremos un error de compilación pues la clase Beverage es final. Abstract: abstract class Car { private double price; private String model; private String year; public abstract void goFast(); public abstract void slowDown(); public double getPrice(){ return price; } 39

40 // Ejemplo correcto: public interface Printable{ void print(); } public class Persona implements Printable{ private String nombre; public void print(){ System.out.print(nombre); } public class Recta implements Printable{ private double largo; public void print(){ System.out.print(largo); } // El siguiente ejemplo no compilará interface Foo { int BAR = 42; void go(); } class Zap implements Foo { public void go() { BAR = 27; } 40

41 Modificadores de acceso: Los modificadores de acceso permiten: Que el código de un método de una clase pueda acceder a miembros de otra clase Que una subclase pueda heredar miembros de su superclase. Miembros públicos: Cuando un método o variable es declarado como publico, todas las otras clases, sin importar si pertenecen a otro paquete o no, puede acceder al miembro (asumiendo que la propia clase es visible). Miembros privados: public class Roo { private String do() { return "do"; } class UseARoo { public void testIt() { Roo r = new Roo(); System.out.println(r.do ()); } // No compilará pues el metodo do() de la clase Roo fue declarado como private. 41

42 Miembros default: package certification; public class OtherClass { void testIt() { // Si no hay modficadore de acceso, siginifica que el miembro tendrá acceso default. System.out.println("OtherClass"); } package somethingElse; import certification.OtherClass; class AccessClass { static public void main(String[] args) { OtherClass o = new OtherClass(); o.testIt(); } // Producirá un error de compilación pues las clases pertenecen a distintos paquetes. Miembros protegidos: package certification; public class Parent { protected int x = 9; // acceso protected (protegido). } package other; // es otro paquete. import certification.Parent; class Child extends Parent { public void testIt() { System.out.println("x is " + x); /* No hay problema, Child hereda a x.*/ Parent p = new Parent(); System.out.println("X in parent is " + p.x); /* Error de compilación: el atributo x puede ser accedido desde otro paquete a través de la herencia */ } 42

43 MODIFICADORES QUE NO SON DE ACCESO Métodos: Métodos Final: public class ClaseA { public final void metodoFinal(){ System.out.println("es el final de la ClaseA"); } public class ClaseB extends ClaseA { public static void main(String[] args) { ClaseB miClaseB = new ClaseB(); miClaseB.metodoFinal(); /* no hay problema en invocar al método, no lo estamos redefiniendo*/ } Argumentos final: Un argumento final mantendrá el mismo valor que el parámetro tenía cuando fue pasado en el método. public Record getRecord(int fileNumber, final int recordNumber) {} 43

44 MODIFICADORES QUE NO SON DE ACCESO Métodos: Métodos abstractos: Un método abstracto es un método que fue declarado (como abstracto) pero no implementado. En otras palabras, el método no contiene código funcional. Uno marca a un método como abstracto cuando se quiere forzar a una subclase a que le dé implementación. Es ilegal tener un método declarado como abstracto en una clase que no haya sido declarada como abstracta. El siguiente código no compilará: public class IllegalClass{ public abstract void doIt(); } Métodos synchronized: Un método sincronizado (synchronized) podrá ser accedido por un solo hilo de ejecución (thread) a la vez. public synchronized Record retrieveUserInfo(int id) { } Métodos native: El modificador native indica que un método es implementado en un código que depende de alguna plataforma especifica, por lo general C. Métodos strictfp: El modificador strictfp fuerza a que el código dentro de un método se adhiera a las normas del estándar IEEE 754 para los puntos flotantes. 44

45 MODIFICADORES QUE NO SON DE ACCESO Métodos: Métodos con listas de argumentos (var-args): A partir de la versión 1.5, Java permite crear métodos que puedan tomar un número variable(no fijo) de argumentos. Para definir un método var-args debemos tener en cuenta: Tipo del var-arg: Cuando declaramos un parámetro var-arg, debemos especificar el tipo del el(los) argumento(s) del parámetro que este método puede recibir (puede ser un tipo primitivo o un objeto). Sintaxis: Cuando se define un método como var-arg, se debe declarar el tipo seguido de tres puntos suspensivos(…), un espacio en blanco y luego el nombre del arreglo que recibirá los parámetros. No se puede tener más de un parámetro var-arg por método. El parámetro var-arg debe ser el último de los parámetros de un método. Declaraciones legales de métodos con parámetros var-args: void doStuff(int... x) { } void doStuff2(char c, int... x) { } void doStuff3(Animal... animal) { } Declaraciones ilegales de métodos con parámetros var-args: void doStuff4(int x...) { } void doStuff5(int... x, char... y) { } void doStuff6(String... s, byte b) { } 45

46 MODIFICADORES QUE NO SON DE ACCESO Variables Final: Al declarar una variable como final será imposible reinicializarla una vez que se la inicializo con un valor explicito. Efecto de final en las variables, clases y métodos: Las clases marcadas como final no podrán tener subclases. Un método marcado como final no podrá ser redefinido. A una variable marcada como final no se le puede asignar un nuevo valor 46

47 Si bien los constructores pueden verse similares a los métodos, estos no devuelven valores de ningún tipo. Sin embargo, los constructores si pueden tener modificadores de acceso y pueden tomar parámetros (incluidos los var-args) como los métodos. Otra regla importante es que deben tener el mismo nombre con el que fue declarada la clase. Por último, los constructores no pueden ser marcados como static, final o abstract. class Foo2 { // constructores legales Foo2() { } private Foo2(byte b) { } Foo2(int x) { } Foo2(int x, int... y) { } // constructores ilegales void Foo2() { } // es un método no un constructor. Foo() { } // ni método ni constructor Foo2(short s); // método abstracto static Foo2(float f) { } // no puede ser static final Foo2(long x) { } // no puede ser final abstract Foo2(char c) { } // no puede ser abstract Foo2(int... x, int t) { } // mala sintaxis del parametro var-arg } 47

48 Tipos de variables en Java: Primitivas: Pueden ser una de los siguientes tipos: char, boolean, byte, short, int, long, double, or float. Una vez que una variable primitiva ha sido declarada, su tipo primitivo nunca puede cambiar, sin embargo, su valor si puede cambiar. Variables referenciadas: Se usan para referenciar (o acceder) a un objeto. Una variable referenciada es declarada para ser de un tipo dado y ese tipo no se puede cambiar. Se pueden usar para referir a un objeto de cualquier objeto del tipo declarado, o un subtipo del tipo declaro. class Foo2 { Declaración de primitivas y sus rangos byte b; boolean myBooleanPrimitive; int x, y, z; Para el tipo booleano de datos, no existe un rango de valores; un booleano solo puede ser true o false. El tamaño en bits del booleano depende de la versión de la java virtual machine. El tipo primitivo char contiene un solo carácter Unicode de 16 bit. 48

49 Variables referenciadas: Las variables referenciadas pueden ser declaradas como variables de clase (estáticas), variables de instancia, parámetros de métodos, o como variables locales. Se pueden declarar una o más variables primitivas del mismo tipo en la misma línea. Object o; Dog myNewDogReferenceVariable; String s1, s2, s3; Variables de instancia: Se definen dentro de una clase, pero fuera de cualquier método, y solo son inicializadas cuando se instancia la clase. Las variables de instancia son los atributos de cada objeto único. Se las pueden marcar como: final, transient, abstract, synchronized, strictfp, native y static (variables de clase); también pueden tener cualquiera de los modificadores de acceso (default, public, private o protected). Variables locales: Son declaradas e inicializadas dentro de un método. Como su ciclo de vida se inicia dentro del método, también serán destruidas (colectadas por el recolector de basura de la JVM) cuando este método ha sido completado. Las variables locales siempre están en el stack, no en el heap. Sin embargo, no hay que olvidar que a pesar de que la variable local este en el stack, si la misma es la referencia a un objeto, el objeto será creado en el heap. class TestServer { public void logIn() { int count = 10; } public void doSomething(int i) { count = i; // no compilará pues count es una variable local de otro método. } 49

50 Los arrays se declaran definiendo el tipo de elementos que albergaran (primitivos u objetos), seguido de corchetes a cualquier lado del identificador: int[] key; // Corchetes antes del nombre (recomendado) int key []; // Corchetes después del nombre (legal pero menos legible) La misma declaración se aplica para los arrays de objetos. Tambien se pueden declarar arrays de muchas dimensiones, que en difinitiva son arrays de arrays: String[][][] occupantName; // Arrays de 3 dimensiones (array de arrays de arrays) String[] managerName []; // Array de 2 dimensiones (declaración poco recomendada) String[3] names; // No es legal definir el tamaño del array cuando se lo declara, sino cuando se lo instancia Names = new String[3]; // Legal int[][] scores = {{5,2,4,7}, {9,2}, {3,4}}; /*Legal, tendremos un array de 3 arrays, donde el primero tendrá 4 elementos, y el segundo y el tercero tendrán 2*/ Dog puppy = new Dog("Frodo"); Dog[] myDogs = {puppy, new Dog("Clover"), new Dog("Aiko")}; Objetos creados después de ejecutar las dos últimas líneas: 1 objecto Dog referenciado por puppy y por myDogs[0] 1 array Dog[] referenciado por myDogs 2 objetos Dog referenciados por myDogs[1] y myDogs[2] 50

51 A partir de la versión 5.0, Java permite restringir una variable a tener un número pre-definido de valores posibles, es decir, una lista enumerada. A los ítems en una lista enumerada se los conoce como enums. A partir de la versión 5.0, Java permite restringir una variable a tener un número pre-definido de valores posibles, es decir, una lista enumerada. A los ítems en una lista enumerada se los conoce como enums. enum TamanioCafe { NORMAL, GRANDE, ENORME}; Luego la única manera de obtener el tamaño de un café será asi: TamanioCafe tc = TamanioCafe. GRANDE; public class CafeTest{ public static void main(String[] args) { enum TamanioCafe { NORMAL, GRANDE, ENORME} // es opcional poner ; Cafe cafe= new Cafe(); cafe.size() = TamanioCafe. GRANDE; } 51

52 Con los Enums podemos hacer mucho más que agregarles valores fijos a ellos, como definirle constructores, métodos, variables, etc: public enum TamanioCafes { NORMAL(8),GRANDE(10),ENORME(14); /* se le pasa el valor al constructor */ private TamanioCafes(int tamanio) { this.tamanio = tamanio; } private int tamanio; public int getTamanio() { return tamanio; } public class Cafe { TamanioCafes tamanioCafe; public static void main(String[] args) { Cafe cafe1 = new Cafe(); cafe1.tamanioCafe = TamanioCafes.NORMAL; Cafe cafe2 = new Cafe(); cafe2.tamanioCafe=TamanioCafes.ENORME; System.out.println(cafe1.tamanioCafe.getTamanio()); for (TamanioCafes tc : TamanioCafes.values()) { System.out.println(tc+" "+tc.getTamanio()); } Nota: el método values() de Enum devuelve un ArrayList con sus enums. 52

53 Herencia: Toda clase en Java es una subclase de Object (excepto la propia clase Object, por supuesto). Cada vez que creemos una clase, estamos heredando todos los métodos provenientes de la clase Object. En Java, se podrán crear relaciones de herencia extendiendo de otra clase (usando la palabra reservada extends). Pero Java no soporta la herencia multiple, es decir, que cada clase solo podrá heredar de una única clase (además de la clase Object). Motivos para usar herencia: Permite la reusabilidad de código. Permite el uso de polimorfismo. Polimorfismo: El uso de la herencia permite que una clase pueda ser accedida polimórficamente. De este manera, se puede tratar a cualquier sub clase de una super clase como la propia super clase. Es decir, las sub clases pueden recibir los mismos mensajes que recibirían las super clases. Con lo cual podremos tener una variable de instancia definida para una super clase que hace referencia a una sub clase. Sin embargo, esta variable podrá recibir mensajes que solo podría entender la super clase: public class Figura { public void mostratFigura(){ } } class Cuadrilatero extends Figura{ private double area; public double getArea(){ return area; } public static void main(String[] args) { Figura figura = new Cuadrilatero(); figura.mostratFigura(); figura.getArea(); } } // no compilara debido a que el método getArea() no fue definido para la clase Figura. 53

54 En Java, cualquier objeto que pueda pasar la prueba es-un puede ser considerado como polimórfico. Exceptuando a los objetos del tipo Object, todas los objetos son polimórficos en el sentido de que pasan la prueba es-un para su propio tipo y para la clase Object. Ya que Java no soporta la herencia multiple, existe una forma para que clases con distintas super clases puedan recibir el mismo mensaje. Eso se logra a través de las interfaces, haciendo que clases que no pasan la prueba de es-un (por ejemplo, la clase Recta con la clase Foto) tengan algo en común. public class Figura implements Imprimible{ public void mostratFigura(){} public void imprimir() { System.out.println("Imprimiendo Figura"); } class Cuadrilatero extends Figura{ private double area; public double getArea(){return area; } public void imprimir() { System.out.println("Imprimiendo Cuadrilatero"); } class Dibujante implements Imprimible{ public void dibujar(Figura figura){ figura.mostratFigura(); } public void imprimir() { System.out.println("Imprimiendo Dibujante"); } interface Imprimible{ void imprimir(); } class Impresora{ public static void main(String[] args) { List impressiones = new ArrayList (); Figura unaFigura = new Cuadrilatero(); Imprimible aImprimir = new Figura(); Dibujante unDibujante = new Dibujante(); impressiones.add(unaFigura); impressiones.add(aImprimir); impressiones.add(unDibujante); for (Imprimible imprimible : impressiones) { imprimible.imprimir(); } Obtendremos el siguiente output Imprimiendo Cuadrilatero Imprimiendo Figura Imprimiendo Dibujante 54

55 Redefinición de métodos Cada vez que tenemos a una clase que hereda métodos de una super clase, tenemos la oportunidad de redefinir el método (a menos que ese método allá sido marcado como final en la super clase). El beneficio de hacerlo es que se puede establecer un comportamiento mas especifico para la subclase. class Animal { public void comer(){ System.out.println("Animal generico comiendo genericamente"); } } class Caballo extends Animal{ public void comer() { System.out.println("Caballo comiendo"); } } Reglas para redefinir un método: La lista de argumentos debe ser la misma que el método heredado (de lo contrario se estaría haciendo una sobrecarga) El tipo del return debe ser del mismo que del método heredado o una subclase. El nivel de acceso debe ser el mismo o menos restrictivo. Una sub clase que esta en un paquete diferente solo podrá re definir aquellos métodos que estén marcados como public o protected. Los métodos re definidos pueden arrojar excepciones no checkeadas, sin importar si el método original las arroja, pero no puede arrojar excepciones checkeadas que sean nuevas o mas abarcativas que las arrojadas origrinalmente por la heredada. No se pueden re definir métodos marcados como final o abstract. Si un método no se hereda, no se puede redefinir. 55

56 Sobre carga de métodos Los métodos sobrecargados te dejan usar el mismo nombre de un método en una clase, pero con argumentos distintos ( y opcionalmente, con un return distinto). Reglas para la sobrecarga de métodos: Los métodos sobrecargados DEBEN cambiar el listado de argumentos. Los métodos sobrecargados PUEDEN cambiar el tipo del return. Los métodos sobrecargados PUEDEN cambiar los modificadores de acceso. Los métodos sobrecargados PUEDEN declarar nuevas excepciones checked o mas abarcativas. Un método puede ser sobrecargado en la propia clase o en una subclase. Sobrecargas legales: Supongamos q en una clase tenemos el siguiente método: public void changeSize(int size, String name, float pattern) { } Entonces las siguientes son sobrecargas legales: public void changeSize(int size, String name) { } public int changeSize(int size, float pattern) { } public void changeSize(float pattern, String name)throws IOException { } 56

57 57

58 Casteos hacia abajo: Permite usar variables de referencia genéricas para referirnos a un objeto mas especifico. Lo que se hace es con una variable de una superclase hacer referencia a una subclase: Animal animal = new Dog(); La variable en tiempo de compilación solo podrá recibir mensajes que entienda la superclase, es decir, métodos que estén definidos solo en la superclase. En tiempo de ejecución, a través de los métodos virtuales, se llamara dinámicamente al método de la subclase que estemos haciendo referencia. public class Animal { public void comer(){ System.out.println("Animal generico comiendo genericamente"); } public static void main(String[] args) { Animal animal = new Caballo(); animal.comer(); } class Caballo extends Animal{ public void comer() { System.out.println("Caballo comiendo"); } Y obtendremos el siguiente output: "Caballo comiendo 58

59 Casteos hacia arriba: Lo hacemos subiendo por el árbol de herencia a un tipo más general. Este tipo de casteo trabaja en forma implícita, porque estamos restringiendo el número de métodos que podemos invocar, a diferencia del casteo hacia abajo que implica que más tarde (en tiempo de ejecución) queramos invocar un método mas especifico. Caballo caballo = new Caballo(); Animal animal1 = caballo; Animal animal2 = (Animal)animal1; animal2.comer(); 59

60 Horse h = new Horse(); Suponiendo que la clase hereda de Animal (y este ultimo de Object), ¿qué es lo que realmente sucede cuando decimos new Hore() ? 1)El constructor de Horse es invocado. Cada constructor llama al constructor de su superclase con un llamado (implícito) a super(), a menos que el constructor invoque al a un constructor sobrecargado de la misma clase. 2)El constructor de Animal es invocado. 3)El constructor de Object es invocado. A esta altura nos encontramos en la cima del stack. 4)Las variables de instancia de objetos obtienen sus valores explícitos. Con valores explícitos, queremos decir aquellos valores que son asignados en el momento en que las variables son declaradas, como int x = 27, donde 27 es el valor explícito (como oposición al valor por default que en este caso por ser un int seria 0) de la variable de instancia. 5)Fin del constructor de Object. 6)Las variables de instancia de Animal reciben valores explícitos (si aplica). 7)Fin del constructor de Animal. 8)Las variables de instancia de Horse reciben valores explícitos (si aplica). 9)Fin del constructor de Horse. 60

61 El modificador static se usa para crear métodos y variables que existirán independientemente de cualquier instancia de una clase. Todos los miembros existen antes de de hacer una nueva instancia de la clase, y solo habrá una copia de un miembro estático sin importar el numero de instancias de esa clase. En otras palabras, todas las instancias de una clase dada compartirán el mismo valor de una variable marcada como static. ¿Qué se puede marcar como static? Métodos Variable Clases anidadas dentro de otras clases (pero no de otros métodos) Bloques de inicialización 61

62 class Frog { static int frogCount = 0; /* Se declara e inicializa una variable estática */ public Frog() { frogCount += 1; // se modifica su valor en el constructor } public static void main (String [] args) { new Frog(); System.out.println("Frog count is now " + frogCount); } En el código anterior, la variable estática frogCount se inicializa en cero cuando la clase Frog es cargada por primera vez por la JVM, antes de que ninguna instancia de Frog haya sido creada. class Frog { int frogCount = 0; /* Se declara e inicializa una variable de instancia */ public Frog() { frogCount += 1; // se modifica su valor en el constructor } public static void main (String [] args) { new Frog(); System.out.println("Frog count is now " + frogCount); } El código anterior no compilará ya que la JVM no sabe a qué instancia de la clase Frog queremos acceder cuando llamamos a frogCount en el método main. 62

63 Efectos de los métodos y variables estáticos: Los métodos estáticos no pueden acceder a variables de instancia no estáticas. Los métodos estáticos no pueden acceder a métodos no estáticas. Los métodos estáticos si pueden acceder a métodos y variables estáticas. 63

64 Literales del tipo integer: int length = 343; // en base 10 int nine = 011; // en base 8 int z = 0xDeadCafe; // en base 16 Literales de punto flotante: float g = F; Literales booleanos: boolean t = true; // Legal boolean f = 0; // Error de compilación Literales del tipo char: char b = char letterN = '\u004E'; // La letra 'N' char a = 0x892; // literal hexadecimal char b = 982; // literal int char c = (char)70000; // el casteo es necesario, el esta fuera de rango Asignacion de primitivas: El signo igual (=) es usado para asignar un valor a una variable. Se puede asignar una variable primitiva usando un literal o el resultado de una expresión. int x = 7; // asignación con un literal int y = x + 2; // asignación con una expresión (que incluye un literal) int z = x * y; // asignación con una expresión 64

65 Casteo implícito: ocurre cuando estamos haciendo conversiones de ensanchamiento, es decir, cuando queremos poner algo mas chico (como un byte) en algo más grande (como un int). int a = 100; long b = a; // el valor de un int entra en un long Casteo explicito: Tiene lugar cuando queremos hacer asignaciones en donde podemos tener perdida de precisión, es decir, cuando queremos poner algo mas grande. int x = (int) ; /* Se truncará la parte decimal para ajustarse al tamaño del int y obtendremos por pantalla 3957*/ long l = 56L; byte b = (byte)l; // compilará y ejecutará sin problemas long l = 130L; byte b = (byte)l; // compilará y ejecutará sin problemas, mostrando por pantalla

66 class Layout { // clase static int s = 343; // variable estática int x; // variable de instancia { x = 7; int x2 = 5; } // bloque de inicialización Layout() { x += 8; int x3 = 6;} // constructor void doStuff() { // method int y = 0; // variable local for(int z = 0; z < 4; z++) { // código dentro de un ciclo 'for' y += z + x; } Las variables estáticas tienen el más largo de todos los scopes; son creadas cuando la clase es cargada, y sobreviven tanto como la clase siga cargada en la JVM. Las variables de instancia son las próximas con mas vida; son creadas cuando una nueva instancia es creada, y permanecen vivas siempre y cuando no se remueva a la instancia. Luego vienen las variables locales, que viven tanto tiempo como el método siga en el stack. Las variables locales deben estar inicializadas para poder ser usadas, de lo contrario el compilador no compilará. Las variables que se encuentran en el bloque de un ciclo vivirán tanto tiempo como se ejecute dicho ciclo. 66

67 Pasando variables que hacen referencia a objetos: Lo que se esta pasando es la referencia del objeto, y no el objeto concreto. La variable de referencia guarda bits que representan (dependiendo de la JVM) una manera para obtener un objeto especifico en la memoria (heap). Entonces estamos pasando una copia de esa variable de referencia (la copia es de los bits que almacena dicha variable). Tanto el emisor del mensaje como el método receptor tendrán una copia idéntica de la referencia,. import java.awt.Dimension; class ReferenceTest { public static void main (String [] args) { Dimension d = new Dimension(5,10); ReferenceTest rt = new ReferenceTest(); System.out.println("Before modify() d.height = " + d.height); rt.modify(d); System.out.println("After modify() d.height = " + d.height); } void modify(Dimension dim) { dim.height = dim.height + 1; System.out.println("dim = " + dim.height); } Y obtendremos por pantalla: Before modify() d.height = 10 dim = 11 After modify() d.height = 11 Pasando primitivas a los métodos: Lo que estamos pasando es una copia de los bits de su valor. class ReferenceTest { public static void main (String [] args) { int a = 1; ReferenceTest rt = new ReferenceTest(); System.out.println("Before modify() a = " + a); rt.modify(a); System.out.println("After modify() a = " + a); } void modify(int number) { number = number + 1; System.out.println("number = " + number); } Y obtendremos por pantalla: Before modify() a = 1 number = 2 After modify() a = 1 Notemos que a no cambió después de que fue pasada al método. Recordemos que fue una copia de la variable lo que se pasó. 67

68 Función de las clases envolventes en Java: Proveen un mecanismo para envolver valores primitivos en objetos para que las primitivas puedan incluirse en actividades reservadas para los objetos, como ser adheridas en una Collection, o ser devueltas por un método cuyo tipo de retorno sea el de un objeto. Proveen muchas funciones utilitarias para las variables primitivas. La mayoría de estas funciones tienen que ver con conversiones, como por ejemplo convertir Strings en tipos numéricos primitivos. Construyendo una clase envolvente usando su constructor: Integer i1 = new Integer(42); Integer i2 = new Integer("42"); Float f1 = new Float(3.14f); Float f2 = new Float("3.14f"); Character c1 = new Character('c'); 68

69 Metodos utilitarios de las clases envolventes: valueOf(): recibe un String y devuelve una clase envolvente con el valor del String pasado como parámetro. Float f2 = Float.valueOf("3.14f"); xxxValue(): devuelve el tipo de dato primitivo de la clase envolvente. Integer i2 = new Integer(42); short s = i2.shortValue(); parseXxx(): recibe los mismo parámetros que valueOf(), pero devuelve el tipo primitivo. double d4 = Double.parseDouble("3.14"); Auto Boxing Antes de Java 5: Integer y = new Integer(567); // creamos la variable envolvente y le damos un valor int x = y.intValue(); // la desenvolvemos x++; // la usamos y = new Integer(x); // la envolvemos otra vez System.out.println("y = " + y); // la imprimos A partit de Java 5: Integer y = new Integer(567); y++; System.out.println("y = " + y); En los dos casos obtenemos el mismo resultado, y=568. Boxing, ==, and equals() Los creadores de la API de Java decidieron que para todas las clases envolventes, dos objetos son iguales si son del mismo tipo y tienen el mismo valor. Integer i1 = 1000; Integer i2 = 1000; if(i1 != i2) System.out.println("objetos diferentes"); if(i1.equals(i2)) System.out.println("iguales significativamente"); Que produce el siguiente ouput: "objetos diferentes" "iguales significativamente" 69

70 class EasyOver { static void go(int x) { System.out.print("int "); } static void go(long x) { System.out.print("long "); } static void go(double x) { System.out.print("double "); } public static void main(String [] args) { byte b = 5; short s = 5; long l = 5; float f = 5.0f; go(b); go(s); go(l); go(f); } Que produce el siguiente output: int int long double Ensanchamiento: cuando no existe un valor que se ajuste exactamente, la JVM usa el método el método con el argumento más chico que sea más grande que el parámetro. Esta regla se conoce como widening o ensanchamiento. class AddBoxing { static void go(Integer x) { System.out.println("Integer"); } static void go(long x) { System.out.println("long"); } public static void main(String [] args) { int i = 5; go(i); } Imprimirá por pantalla: long class AddVarargs { static void go(int x, int y) { System.out.println("int,int");} static void go(byte... x) { System.out.println("byte... "); } public static void main(String[] args) { byte b = 5; go(b,b); } Imprimirá por pantalla: int,int Ensanchamiento vence a Boxing. Ensanchamiento vence a var-args. 70

71 El termino excepción significa condición excepcional, y es un suceso que altera el flujo normal de un programa. Cuando ocurre un evento excepcional en Java, se dice que se arroja una excepción. El código responsable de hacer algo con la excepción se conoce como exception handler, y atrapa a la excepción que fue arrojada. De este manera se transfiera la ejecución del programa a dicho bloque. En Java trabajamos con los bloques try/catch, en donde el bloque try se usa para definir el código que pueda originar la excepción, y el catch para trabajar con la excepción. Podemos tener más de un bloque catch que se ajuste a una excepción mas especifica. Se puede incluir un bloque finally, que se ejecutará sin importar si se arrojó o no la excepción. Es muy útil para manejos de I/O, como por ejemplo, cerrar un archivo o cerrar una conexión remota. try { } catch (Exception e1) { } catch (Exception e2) { } finally{ } 71

72 Si un método no tiene un bloque catch para alguna excepción en particular, el método arrojara a la excepción a una instancia superior. Esta excepción irá subiendo por los métodos que se encuentran en el stack hasta que alguno lo atrape, en el orden inverso en que fueron invocados. class TestEx { public static void main (String [] args) { doStuff(); } static void doStuff() { doMoreStuff(); } static void doMoreStuff() { int x = 5/0; /* no se puede dividir por cero, una excepción del tipo ArithmeticException se arrojara aquí*/ } Y obtendremos por pantalla el nombre de la excepción con un seguimiento del stack: Exception in thread "main" java.lang.ArithmeticException: / by zero at TestEx.doMoreStuff(TestEx.java:10) at TestEx.doStuff(TestEx.java:7) at TestEx.main(TestEx.java:3) 72

73 Jerarquía de herencias de las excepciones: Todas las excepciones son un subtipo de Exception, y a su vez esta ultima hereda de Throwable. Hay dos subclases de Throwable: Exception y Error. Las clases que derivan de Error representan situaciones inusuales que son causadas por errores de programa, por ejemplo que la JVM se quede sin memoria. Como generalmente no será posible que la aplicación se recupere de un Error, asi que no es necesario manejarlas. Manejo de jerarquía de herencia de las excepciones: Se puede atrapar mas de una excepción en un solo bloque catch. Si la clase de la excepción especificada en el bloque catch no tiene subclases, entonces solo la clase de excepción especificada será atrapada. Pero si tuviera subclases, cualquier subclase puede ser atrapada en el bloque catch. 73

74 Correspondencia de excepciones: Cuando una excepción es arrojada, Java tratará de encontrar (teniendo en cuenta los bloque catch disponibles empezando de arriba hacia abajo) un bloque catch para el tipo de la excepción. Si no lo hace, buscará un bloque catch para que atrape al supertipo de la excepción. Y si no lo hace la excepción seguirá siendo arrojado hacia abajo entre los bloques catch. import java.io.*; public class ReadData { public static void main(String args[]) { try { RandomAccessFile raf = new RandomAccessFile("myfile.txt", "r"); byte b[] = new byte[1000]; raf.readFully(b, 0, 1000); 9: } catch(FileNotFoundException e) { System.err.println("File not found"); System.err.println(e.getMessage()); e.printStackTrace(); } catch(IOException e) { System.err.println("IO Error"); System.err.println(e.toString()); e.printStackTrace(); } Si hubieramos tenido: try { // hacer cosas riesgosas referidas al IO } catch (IOException e) { // manejo de excepciones generales del tipo (o subtipo) IOException } catch (FileNotFoundException ex) { // manejo de excepciones únicamente del tipo FileNotFoundException } Obtendremos un error de compilación al comienzo del segundo bloque catch. 74

75 Interfaz pública de excepción: Se debe declarar las excepciones que arroja un método (a menos que estas excepciones sean una subclase de RuntimeException). void myFunction() throws MyException1, MyException2 { // code for the method here } Importante: Cada método debe manejar TODAS las excepciones registradas (checked, son aquellas que heredan de Exception y no de RuntimeException) suministrando un bloque catch, o listar cada excepción registrada que no se maneje como thrown exception. Un método que arroje una excepción que derive de RuntimeException, no se ve en la obligación de incluir en su declaración pública. Más aun, quien llame a dicho método tampoco está obligado a rodearlo en un bloque try/catch. 75

76 Equals() Si queremos saber si dos referencias son idénticas, usamos el operador ==. Pero si queremos saber si los objetos mismos (no las referencias) son iguales, usamos el método equals(). Para cada clase, el programador debe decidir cómo es que se consideran iguales a dos objetos. Si no redefinimos a este método, dos objetos serán considerados iguales si sus referencias lo son. public class EqualsTest { public static void main (String [] args) { Moof one = new Moof(8); Moof two = new Moof(8); if (one.equals(two)) { System.out.println("one and two are equal"); } } class Moof { private int moofValue; Moof(int val) { moofValue = val; } public int getMoofValue() { return moofValue; } public boolean equals(Object o) { if ((o instanceof Moof) && (((Moof)o).getMoofValue() == this.moofValue)) { return true; } else { return false; } 76

77 HashCode() Los Hashcodes se usan típicamente para mejorar la performance de colecciones que albergan mucha información. Si bien podemos pensar en los hashcodes como una especie de ID de los objetos, no necesariamente son únicos. Tanto HashMap como HashSet usan el hascode de un objeto para determinar cómo debe guardarse y recuperarse en la colección. class HasHash { public int x; HasHash(int xVal) { x = xVal; } public boolean equals(Object o) { boolean bResult = false; if (o instanceof HasHash) { HasHash h = (HasHash) o; if (h.x == this.x) { bResult = true; } return bResult; } public int hashCode() { return (x * 17); } } 77

78 ¿Qué podemos hacer con ellas? Adherir objetos a una colección Remover objetos de una colección Saber si un objeto (o grupo de objetos) pertenece a una colección Obtener un objeto de una colección Iterar a través de una colección, trabajando con cada elemento (objeto) uno detrás del otro. Permiten autoboxing, pudiéndole pasar a las colecciones valores primitivos. 78

79 Jerarquía de clases e interfaces de las colecciones: 79

80 Clasificación de las colecciones: 80

81 compareTo() Para usarlo nuestra clase debe implementar a la interfaz Comparable e implementar dicho método para que compare a los objetos como queremos. La postcondición que se debe cumplir es que debe devolver: Negativo -> if thisObject < anotherObject Cero -> if thisObject == anotherObject Postivo -> if thisObject > anotherObject class DVDInfo implements Comparable { String title, genre, leadActor; DVDInfo(String t, String g, String a) { title = t; genre = g; leadActor = a; } public String toString() { return title + " " + genre + " " + leadActor + "\n"; } public int compareTo(DVDInfo d) { return title.compareTo(d.getTitle()); } public String getTitle() { return title; } // other getters and setters } Luego, en un main deberíamos tener algo asi ArrayList dvdList = new ArrayList (); populateList(); // llena la lista con las películas Collections.sort(dvdList); Compare() Se usa para ordenar a una colección que no implemente a comparable, o cuando queremos ordenar la colección de distintas maneras. Debemos armarnos una clase que implemente a la interfaz Comparator, y esta nueva clase será la responsable de saber cómo ordenar a la colección. import java.util.*; class GenreSort implements Comparator { public int compare(DVDInfo one, DVDInfo two) { return one.getGenre().compareTo(two.getGenre()); } import java.util.*; public class TestDVD { ArrayList dvdlist = new ArrayList (); public static void main(String[] args) { new TestDVD().go(); } public void go() { populateList(); GenreSort gs = new GenreSort(); Collections.sort(dvdlist, gs); System.out.println(dvdlist); } public void populateList() { // read the file, create DVDInfo instances, and // populate the ArrayList dvdlist with these instances } 81

82 Iteradores Un iterador no es mas que un objeto asociado con una colección especifica, que nos permite recorrerla. La clase en cuestión debe implementar a la interfaz Iterator, e implementar los siguientes métodos: Boolean hasNext(): Devuelve true si por lo menos queda un elemento por recorrer en la colección. Invocando este método NO nos estamos moviendo al próximo elemento en la colección Object next(): Devuelve el próximo elemento en la colección, y además mueve una posición en la colección. import java.util.*; class Dog { public String name; Dog(String n) { name = n; } } class ItTest { public static void main(String[] args) { List d = new ArrayList (); Dog dog = new Dog("aiko"); d.add(dog); d.add(new Dog("clover")); d.add(new Dog("magnolia")); Iterator i3 = d.iterator(); // construimos un iterador while (i3.hasNext()) { Dog d2 = i3.next(); // no hace falta casteo alguno por el uso de generics System.out.println(d2.name); } System.out.println("size " + d.size()); System.out.println("get1 " + d.get(1).name); System.out.println("aiko " + d.indexOf(dog)); d.remove(2); Object[] oa = d.toArray(); 82

83 Iteradores Un iterador no es mas que un objeto asociado con una colección especifica, que nos permite recorrerla. La clase en cuestión debe implementar a la interfaz Iterator, e implementar los siguientes métodos: Boolean hasNext(): Devuelve true si por lo menos queda un elemento por recorrer en la colección. Invocando este método NO nos estamos moviendo al próximo elemento en la colección Object next(): Devuelve el próximo elemento en la colección, y además mueve una posición en la colección. import java.util.*; class Dog { public String name; Dog(String n) { name = n; } } class ItTest { public static void main(String[] args) { List d = new ArrayList (); Dog dog = new Dog("aiko"); d.add(dog); d.add(new Dog("clover")); d.add(new Dog("magnolia")); Iterator i3 = d.iterator(); // construimos un iterador while (i3.hasNext()) { Dog d2 = i3.next(); // no hace falta casteo alguno por el uso de generics System.out.println(d2.name); } for(Object o : oa) { // igual que recorrer con un iterador Dog d2 = (Dog)o; System.out.println("oa " + d2.name); } 83

84 GENERICS Los Arrays en Java siempre fueron seguros de tipado, ya que un Array de Strings solo va a aceptar Strings y no Integers. Pero hasta antes del Java 5, no había sintaxis para declarar colecciones seguras de tipado. Con la llegada del Java 5, esto se solucionó con el concepto de generics. Hasta antes de Java 5: List myList = new ArrayList(); // mp se declara el tipo de elementos de la colección myList.add("Fred"); myList.add(new Dog()); myList.add(new Integer(42)); Como vemos podiamos ingresar a una coleccion cualquier tipo de objeto, y por este motivo no sabemos de qué tipo son los objetos que almacena, por lo tanto estamos forzados a realizar un casteo de los objetos que saquemos para poder trabajar con ellos (es recomendable que antes de hacer el casteo hagamos una validación del tipo del objeto usando instanceOf). String s = (String) myList.get(0); Apartir de Java 5: El uso de generics se encarga de los dos problemas presentados arriba, es decir, sacar y meter objetos de una colección al forzar el tipado de la misma. List myList = new ArrayList (); myList.add("Fred"); // OK, puede tener Strings myList.add(new Dog()); // error de compilacion String s = myList.get(0); // no tendrá errors de compilacion 84

85 En Java un thread puede ser: Una instancia de la clase java.lang.Thread Un hilo en ejecución Una instancia de la clase Thread es un objeto, que tiene sus métodos y vive en el heap. Pero un hilo en ejecución es un proceso individual que tiene su propio stack. En Java, hay un thread (hilo) por cada stack. El método main() hace que se inicie nuestra aplicación y corre sobre el thread main. La JVM, que obtiene su turno en la CPU por el mecanismo de scheduling que tenga el SO, opera como un mini SO que maneja sus propios threads sin importar bajo que sistema operativo este corriendo. Haciendonos un Thread : Un thread en Java comienza como una instancia de java.lang.Thread. La acción que queremos que se ejecute en el thread tiene lugar en el método run() de esta clase. Pensemos en el código que queremos que se ejecute por sepadaro (en otro thread) como el trabajo a realizar. Podemos definir un thread de dos maneras: Heredando de la clase java.lang.Thread Implementando a la interfaz Runnable Heredando de java.lang.Thread class MyThread extends Thread { public void run() { System.out.println("Tarea importante corriendo en MyThread"); } Implementando a la interfaz java.lang.Runnable class MyRunnable implements Runnable { public void run() { System.out.println("Tarea importante corriendo en MyRunnable"); } 85

86 Instanciando un thread Si heredamos de java.lang.Thread lo único que deberemos hacer es lo siguiente: MyThread t = new MyThread(); En cambio si implementamos a la interfaz java.lang.Thread: MyRunnable r = new MyRunnable(); // representa la tarea a realizar Thread t = new Thread(r); // es quien va a realizar la tarea La misma tarea se le puede dar a varios hilos: public class TestThreads { public static void main (String [] args) { MyRunnable r = new MyRunnable(); Thread foo = new Thread(r); Thread bar = new Thread(r); } Iniciando un Thread Lo único que debemos hacer (una vez que inicializamos la instancia de Thread) es invocar al método start() t.start(); Antes de hacerlo, lo que teníamos era un objeto cuyo tipo era java.lang.Thread, pero ahora contamos con un hilo de ejecución. ¿Qué sucede luego de que se llama a este método? Se inicia un nuevo hilo de ejecución con su propio stack. El thread pasa del estado new a runnable. Cuando el thread tenga alguna chance de ser ejecutado, su método run() objetivo se ejecutará. 86

87 Inciando y corriendo multiples threads class NameRunnable implements Runnable { public void run() { for (int x = 1; x <= 400; x++) { System.out.println("Run by " + Thread.currentThread().getName() + ", x is " + x); } public class ManyNames { public static void main(String[] args) { NameRunnable nr = new NameRunnable(); Thread one = new Thread(nr); Thread two = new Thread(nr); Thread three = new Thread(nr); one.setName("Fred"); two.setName("Lucy"); three.setName("Ricky"); one.start(); two.start(); three.start(); } Al correrlo tendremos el siguiente ouput: Run by Ricky, x is 313 Run by Lucy, x is 341 Run by Ricky, x is 314 Run by Lucy, x is 342 Run by Ricky, x is 315 Run by Fred, x is 346 Run by Lucy, x is 343 Run by Fred, x is 347 Run by Lucy, x is 344 El cual irá cambiando cada vez que ejecutemos a la aplicacion. Esto es asi, porque no se garantiza en que orden los threads se elijiran para ser ejecutados. 87

88 Estados de un thread: ¿Cómo prevenir la ejecución de n Thread? Sleep(): try { Thread.sleep(5*60*1000); // Sleep for 5 minutes } catch (InterruptedException ex) { } Prioridades y yield(): FooRunnable r = new FooRunnable(); Thread t = new Thread(r); t.setPriority(8); t.start(); t.yield(); Join():Thread t = new Thread(); Thread a = new Thread(); Thread b = new Thread(); a.start(); b.start(); b.join(); 88

89 Ejemplo no concurrente: public class CuentaPeligrosa implements Runnable { private Cuenta acct = new Cuenta(); public static void main(String[] args) { CuentaPeligrosa r = new CuentaPeligrosa(); Thread one = new Thread(r); Thread two = new Thread(r); one.setName("Lucas"); two.setName("Lucia"); one.start(); two.start(); } public void run() { for (int x = 0; x < 5; x++) { realizarExtraccion(10); if (acct.getBalance() < 0) { System.out.println("La cuenta no tiene fondos"); } private void realizarExtraccion(int amt) { if (acct.getBalance() >= amt) { System.out.println(Thread.currentThread().g etName() + " va a realizar una extraccion"); try { Thread.sleep(500); } catch (InterruptedException ex) { } acct.extraccion(amt); System.out.println(Thread.currentThread().getName()+ " completó la extracción"); } else { System.out.println("No hay suficientes fondos para que + Thread.currentThread().getName() + " haga una extraccion + acct.getBalance()); } class Cuenta { private int balance = 50; public int getBalance() {return balance;} public void extraccion(int amount) { balance = balance – amount; } } 89

90 Output: Lucas va a realizar una extraccion Lucia va a realizar una extraccion Lucas completó la extracción Lucas va a realizar una extraccion Lucia completó la extracción Lucia va a realizar una extraccion Lucas completó la extracción Lucas va a realizar una extraccion Lucia completó la extracción Lucia va a realizar una extraccion Lucia completó la extracción No hay suficientes fondos para que Lucia haga una extraccion 0 Lucas completó la extracción La cuenta no tiene fondos No hay suficientes fondos para que Lucas haga una extraccion -10 La cuenta no tiene fondos No hay suficientes fondos para que Lucas haga una extraccion -10 La cuenta no tiene fondos Solución: private synchronized void realizarExtraccion(int amt) { if (acct.getBalance() >= amt) { System.out.println(Thread.currentThread().getName() + " va a realizar una extraccion"); try { Thread.sleep(500); } catch (InterruptedException ex) { } acct.extraccion(amt); System.out.println(Thread.currentThread().getName() + " completó la extracción"); } else { System.out.println("No hay suficientes fondos para que " + Thread.currentThread().getName() + " haga una extraccion " + acct.getBalance()); } 90

91 Dreadlock: abrazo de la muerte Un dreadlock ocurre cuando dos threads están bloqueados, cada uno esperando la llave del otro. Ningun puede ejecutarse hasta que el otro renuncie a su llave, por lo tanto se quedarán esperando indefinidamente. public class DeadlockRisk { private static class Resource { public int value; } private Resource resourceA = new Resource(); private Resource resourceB = new Resource(); public int read() { synchronized(resourceA) { // May deadlock here synchronized(resourceB) { return resourceB.value + resourceA.value; } public void write(int a, int b) { synchronized(resourceB) { // May deadlock here synchronized(resourceA) { resourceA.value = a; resourceB.value = b; } 91

92 Comunicación entre threads: métodos wait(),notify() y notifyAll() public class ThreadA { public static void main(String[] args) { ThreadB b = new ThreadB(); b.start(); synchronized (b) { try { System.out.println("Waiting for b to complete..."); b.wait(); /* el thread en ejecución (es el thread main que se originó en la clase ThreadA) esperará a q finalice la ejecución del ThreadB, evitando de esta manera que el ThreadA pueda saltearse la línea System.out.println("Total is: " + b.total); antes de que ThreadB termine de realizar la sumatoria*/ } catch (InterruptedException e) { } System.out.println("Total is: " + b.total); } class ThreadB extends Thread { int total; public void run() { synchronized (this) { for (int i = 0; i < 100; i++) { total += i; } notify(); } 92

93 La arquitectura de Java surge de cuatro tecnologías diferentes pero interrelacionados: El lenguaje de programación Java El formato de archivo de clase Java (.class file) La Interfaz de programación de aplicaciones Java (Java´s API) La máquina virtual Java (JVM) Se escribe el código fuente en el lenguaje de programación Java. Se compilan los fuentes para generar los archivos de clase (.class) y Las llamadas al sistema se realizan a través de la API Los programas se ejecutan los mismos en la JVM. 93

94 94

95 Es un computador abstracto. Implementada en software o hardware. Su especificación define ciertas características que cada JVM debe tener, pero deja muchas opciones a los diseñadores de cada implementación. soporta las tres aristas de la arquitectura orientada a red de Java: Independencia de plataforma. Seguridad. movilidad sobre redes. JVM 95

96 Carga los archivos de clase (.class). Carga los archivos de clase de la API de Java. Ejecuta los bytecodes que contienen en un motor de ejecución. Existen dos formas de ejecución: Por Interpretación Por Compilacion Just in time. La interaccion con el SO se realiza a través de metodos almacenados en dlls. JVM 96

97 En la especificación de la Máquina virtual de Java se describe la siguiente arquitectura abstracta. JVM 97

98 Almacena la información que necesita el programa al ejecutarse (bytecodes, información de tipos, objetos instanciados,etc). Zonas compartidas por los hilos del programa : El Area de métodos: Aloja información sobre las clases cargadas, como ser el tipo, etc. El heap: Aloja los objetos instanciados. JVM 98

99 Zonas no compartidas por los hilos. contador de programa (PC register) : Si el hilo está ejecutando un método de Java (no un método nativo), el valor del PC indica la siguiente instrucción a ejecutar. Pilas de Java (Java stacks). Almacena el estado de los métodos invocados por hilo. Dicho estado incluye sus variables locales, los parámetros con los que se invocó, su valor de retorno (si lo hubiere), y los cálculos intermedios. La pila de Java se compone de los marcos de pila (stack frames). Un marco de la pila contiene el estado de la invocación de método de Java. Cuando un hilo invoca un método, se realiza un push de un stack frame en el stack del hilo. Cuando el método se completa, se realiza un pop del marco y éste se desecha. JVM 99

100 JVM 100

101 Contiene 3 partes: Variables locales Pila de operandos (operand stack) Datos del frame (frame data) Esta sección del SF esta organizada como un vector de words con acceso por indice El compilador pone los argumentos en el vector con el orden en que se declaran JVM 101

102 JVM 102 class Example3a { public static int runClassMethod(int i, long l, float f, double d, Object o, byte b) { return 0; } public int runInstanceMethod(char c, double d, short s,boolean b) { return 0; }

103 JVM 103 También organizada con un vector de words con acceso tipo pila (push,pop). Se utiliza para hacer las operaciones aritméticas. Es un espacio de trabajo Ejemplo: Supongase los sig bytecodes: begin iload_0 // push the int in local variable 0 onto the stack iload_1 // push the int in local variable 1 onto the stack iadd // pop two ints, add them, push result istore_2 // pop int, store into local variable 2 end

104 JVM 104 Adicionalmente a las variables locales y a la pila de operandos la Java stack frame incluye soporte para: la resolución de constantes retorno de los métodos envío de excepciones. Todo esto es almacenado en el stack frame

105 La especificación de la JVM no detalla el mecanismo de recoleccion de basura. "The Java Virtual Machine assumes no particular type of automatic storage management system, and the storage management technique may be chosen according to the implementor's system requirements." - Java Virtual Machine Specification, Section [JVMS2 1999] Características deseables: Seguro Abarcativo Frecuente Eficiente Limite la fragmentación JVM 105

106 Equilibrio entre : Velocidad Tamaño (heap) Frecuencia. TamañoVelocidadFrecuencia TamañoVelocidadFrecuencia JVM 106

107 JVM Se basa en dos principios La mayoría de los objetos vivos no están referenciados por mucho tiempo, es decir, que mueren jóvenes. (inexistencia de objetos stack) Existe pocas referencias de los objetos viejos a los jóvenes. (retorno de punteros). Los objetos inicialmente se alocan el la generación joven y son promovidos después de un numero de recolecciones a la generación antigua. 107

108 Dispone de 4 recolectores: Recolector Serie Recolector paralelo Recolector paralelo con compactacion Marcado y barrido concurrente Todos disponen la misma arquitectura generacional Posibilidad de elección del recolector por medio de parámetros en la JVM. JVM 108

109 Se divide el heap en 3 generaciones: Jóvenes: Es donde se albergan todos los objetos inicialmente (excepto los de gran tamaño). Dispone de un tamaño reducido Antiguos: Es donde se albergan los objetos que han supervivido algúnos ciclos de recolección. Dispone de gran tamaño Permanentes: Es donde se albergan las definiciones y descripciones de las clases y métodos. Dispone de gran tamaño Cuando la generación Jóvenes se llena, se dispara una recolección menor o recolección joven. Cuando la generación Viejos o la generación Permanente se llenan, se dispara una recolección mayor o completa. JVM 109

110 Se encuentra dividida en una gran parte denominada Edén y dos pequeñas partes denominadas To y From que forman el área llamada espacio de supervivientes. Los objetos inicialmente se alocan en el Edén. Si sobreviven a mas de un ciclo de recolección pasan al área de supervivientes. El área From y To intercambian roles. JVM 110

111 Se dispara cuando se llena el Edén. Tanto la recolección de jovenes como de antiguos se realizan en serie en forma paren el mundo. Los objetos vivos del Edén se copian en el área denominada To del espacio de supervivientes. Los objetos que ocupan el espacio denominado From que son relativamente jóvenes, también son copiados al espacio To. Los objetos, los que tienen una edad suficiente son promovidos a la generación antigua. JVM 111

112 Luego de los movimientos el Edén y From solo contienen objetos muertos que son dealocados. El área From y To intercambian roles. JVM 112

113 Se utiliza el algoritmo de marcado-barrido-compactado. Fase de marcado: se identifica los que todavía viven. Fase de barrido: se libera la memoria de los objetos no marcados que son considerados basura. Fase de compactado: Se colocan los objetos contiguamente desde el inicio del área. Permite alocacion rápida (técnica bump-the-pointer). Apropiado para PCs tipo cliente que no requieren tiempos de pausa cortos. JVM 113

114 El recolector paralelo realiza la recolección en paralelo utilizando varios CPUs, sigue utilizando la forma paren el mundo pero ésta es mucho mas breve. La recolección de la generación antigua utiliza el mismo algoritmo marcar-barrer-compactar que la recolección serie. Apropiado para PCs tipo servers y aplicaciones sin requerimientos de pausas cortas (procesamiento batch, calculo científico) JVM 114

115 La generación Jóvenes se recolecta con la versión paralela de antes. Para la generación Viejos se utilizan tres fases: La generación se divide lógicamente en regiones. Fase 1 (Marcado): Todos los objetos alcanzables desde el código se marcan en paralelo.Cada vez que se encuentra un objeto vivo, se agrega la información detamaño y ubicación al resumen de su región. Fase 2 (Resumen): Se revisa la densidad de cada región. Las zonas densas no se tocan. Para las zonas ralas se calculan las nuevas direcciones que ocuparán los objetos. (En serie). Fase 3 (Compactación): Los hilos utilizan la información de resumen para mover los objetos (en paralelo). Cuando hay varias CPUs elimina las largas pausas. No se lo debe usar cuando un programa no debe monopolizar la máquina por largo rato. JVM 115

116 La generación Jóvenes se recolecta con la versión paralela de antes. Para la generación Viejos se utilizan tres fases: La generación se divide lógicamente en regiones. Fase 1 (Marcado): Todos los objetos alcanzables desde el código se marcan en paralelo.Cada vez que se encuentra un objeto vivo, se agrega la información detamaño y ubicación al resumen de su región. Fase 2 (Resumen): Se revisa la densidad de cada región. Las zonas densas no se tocan. Para las zonas ralas se calculan las nuevas direcciones que ocuparán los objetos. (En serie). Fase 3 (Compactación): Los hilos utilizan la información de resumen para mover los objetos (en paralelo). Cuando hay varias CPUs elimina las largas pausas. No se lo debe usar cuando un programa no debe monopolizar la máquina por largo rato. JVM 116

117 La generación Jóvenes se recolecta con la versión paralela de antes. Para la generación antigua la recolección se hace concurrentemente con la ejecución del programa: Marcado inicial: Breve pausa que identifica los objetos vivos directamente accesibles desde el código del programa (I). Marcado concurrente: marca lo alcanzable transitivamente desde I. Pueden quedar objetos nuevos sin marcar. Remarcado: Nueva pausa para revisar los objetos modificados (en paralelo). Barrido concurrente. JVM 117

118 Es un recolector de baja latencia. Es el único que no compacta para ahorrar tiempo. Al no compactar no se puede usar la técnica bump-the-pointer y debe utilizarse listas para los espacios libres JVM 118

119 119


Descargar ppt "1. Historia. Características de la tecnología. Especificaciones del lenguaje. Maquina virtual de Java. Recolector de basura en HotSpot 2."

Presentaciones similares


Anuncios Google