Existe um destruidor para Java?


594

Existe um destruidor para Java? Parece que não consigo encontrar nenhuma documentação sobre isso. Se não houver, como posso obter o mesmo efeito?

Para tornar minha pergunta mais específica, estou escrevendo um aplicativo que lida com dados e as especificações dizem que deve haver um botão 'redefinir' que traga o aplicativo de volta ao seu estado original recém-lançado. No entanto, todos os dados precisam estar "ativos", a menos que o aplicativo seja fechado ou o botão de redefinição seja pressionado.

Sendo geralmente um programador de C / C ++, achei que isso seria trivial de implementar. (E, portanto, planejei implementá-lo por último.) Estruturei meu programa de forma que todos os objetos 'redefiníveis' estivessem na mesma classe para que eu pudesse destruir todos os objetos 'ativos' quando um botão de redefinição foi pressionado.

Eu estava pensando se tudo o que fiz foi apenas desreferenciar os dados e aguardar o coletor de lixo coletá-los. Não haveria vazamento de memória se meu usuário digitasse repetidamente os dados e pressionasse o botão de reset? Eu também estava pensando que, como o Java é bastante maduro como linguagem, deve haver uma maneira de impedir que isso aconteça ou resolver isso com graça.


7
Há apenas um vazamento de memória se você mantiver referências a objetos de que não precisa. ou seja, há um erro no seu programa. O GC irá executar como ele precisa (às vezes mais cedo)
Peter Lawrey

17
A máquina virtual não executará o GC em breve, se você estiver processando dados rapidamente através de objetos. A idéia de que o GC sempre possa acompanhar ou tomar as decisões certas é uma falácia.
Kieveli 13/10/11

1
@Kieveli A JVM não executaria o GC antes de dar um erro?
WVrock #

4
Sim, seria bom se houvesse destruidor para Java que o destruiria de uma vez por todas.
Tomáš Zato - Restabelece Monica

Respostas:


526

Como Java é uma linguagem de coleta de lixo, não é possível prever quando (ou mesmo se) um objeto será destruído. Portanto, não há equivalente direto de um destruidor.

Existe um método herdado chamado finalize, mas é chamado inteiramente a critério do coletor de lixo. Portanto, para as classes que precisam explicitamente arrumar, a convenção é definir um método close e usar finalize apenas para verificação de integridade (ou seja, se close não tiver sido chamado, faça isso agora e registre um erro).

Houve uma pergunta que gerou uma discussão aprofundada sobre finalizar recentemente, para fornecer mais profundidade, se necessário ...


5
"Close ()" neste contexto se refere ao método em java.lang.Autocloseable?
Sridhar Sarnobat

21
Não, o AutoCloseable foi introduzido no Java 7, mas a convenção 'close ()' existe há muito mais tempo.
Jon Onstott

por que você não pode prever quando (ou mesmo se) um objeto será destruído. o que aproxima outra maneira de prever isso?
dctremblay

A destruição do objeto @dctremblay é feita pelo coletor de lixo e o coletor de lixo pode nunca ser executado durante a vida útil do aplicativo.
Piro diz Reinstate Monica

4
Observe que o finalizemétodo foi descontinuado no Java 9. #
317

124

Veja a instrução try-with-resources . Por exemplo:

try (BufferedReader br = new BufferedReader(new FileReader(path))) {
  System.out.println(br.readLine());
} catch (Exception e) {
  ...
} finally {
  ...
}

Aqui, o recurso que não é mais necessário é liberado no BufferedReader.close()método Você pode criar sua própria classe que implementa AutoCloseablee usa-a de maneira semelhante.

Essa declaração é mais limitada do que finalizeem termos de estrutura de código, mas ao mesmo tempo, torna o código mais simples de entender e manter. Além disso, não há garantia de que um finalizemétodo seja chamado durante o tempo de vida do aplicativo.


12
Estou surpreso que isso tenha tão poucos votos. É a resposta real.
Nurettin

20
Eu discordo que é a resposta real. Se uma instância tiver um recurso que lide por um período maior de tempo, em várias chamadas de método, a tentativa com recursos não ajudará. A menos que seja correto fechar e reabrir o recurso na proporção em que os métodos são chamados - não é um fato geral.
Eric

14
De fato, essa não é a resposta real. É impossível usar essa estrutura para gerenciar a destruição de um objeto, a menos que a construção e o uso do objeto sejam totalmente encapsulados pelo trye finallyusados ​​para forçar uma chamada para obj.finalize(). E mesmo essa configuração não resolve o problema do OP: destruição de objeto no meio do programa, acionada por um botão "redefinir".
#

1
Outros usuários mostraram que isso está sendo feito no ponto de entrada do aplicativo. Defina sua variável globalmente. Inicialize-o na função de entrada, com try. Desinicialize o finalmente (quando o aplicativo for fechado). É inteiramente possível.
TamusJRoyce

1
@ nurettin O Java 7 estava fora apenas por três meses quando a pergunta foi feita, se isso ajuda a entender melhor.
CorsiKa 01/04/19

110

Não, não há destruidores aqui. O motivo é que todos os objetos Java são alocados no heap e coletados como lixo. Sem desalocação explícita (ou seja, operador de exclusão do C ++), não há maneira sensata de implementar destruidores reais.

Java suporta finalizadores, mas eles devem ser usados ​​apenas como uma proteção para objetos que mantêm um identificador de recursos nativos, como soquetes, identificadores de arquivo, identificadores de janela etc. Quando o coletor de lixo coleta um objeto sem um finalizador, simplesmente marca a memória. região tão livre e é isso. Quando o objeto tem um finalizador, ele é copiado primeiro para um local temporário (lembre-se, estamos coletando lixo aqui), depois é enfileirado em uma fila de espera para finalização e, em seguida, um encadeamento do Finalizador controla a fila com prioridade muito baixa e executa o finalizador.

Quando o aplicativo sai, a JVM para sem aguardar a finalização dos objetos pendentes, portanto, praticamente não há garantias de que seus finalizadores serão executados.


4
Obrigado por mencionar os recursos nativos - esta é uma área em que um método "destrutivo" é útil.
Nathan Osman

Sim, estou enfrentando o mesmo problema agora com a liberação de recursos / identificadores alocados por meio de chamadas nativas para C ++.
Nikk

@ddimitrov, em teoria, o java poderia implementar uma desalocação explícita? Ou isso é uma contradição lógica?
mils

1
A implementação inativa de desalocação explícita do @mils quebraria a suposição de Java de que qualquer referência aponta para um objeto ativo. Você pode iterar sobre todos os ponteiros e anular os aliases, mas isso é mais caro que o GC. Ou você pode tentar usar algum sistema de tipos lineares (consulte "propriedade" no Rust), mas essa é uma grande mudança de idioma. Também existem outras opções (consulte Memória com escopo do JavaRT, etc), mas em geral a desalocação explícita não se encaixa bem na linguagem Java.
precisa saber é o seguinte

31

Uso de finalize () métodos deve ser evitado. Eles não são um mecanismo confiável para a limpeza de recursos e é possível causar problemas no coletor de lixo abusando deles.

Se você precisar de uma chamada de desalocação em seu objeto, diga para liberar recursos, use uma chamada de método explícita. Essa convenção pode ser vista nas APIs existentes (por exemplo , Closeable , Graphics.dispose () , Widget.dispose () ) e geralmente é chamada via try / finalmente.

Resource r = new Resource();
try {
    //work
} finally {
    r.dispose();
}

As tentativas de usar um objeto descartado devem gerar uma exceção de tempo de execução (consulte IllegalStateException ).


EDITAR:

Eu estava pensando, se tudo o que fiz foi apenas desreferenciar os dados e aguardar o coletor de lixo coletá-los, não haveria vazamento de memória se meu usuário digitasse repetidamente os dados e pressionasse o botão de reset?

Geralmente, tudo que você precisa fazer é desreferenciar os objetos - pelo menos, é assim que deve funcionar. Se você estiver preocupado com a coleta de lixo, consulte o Ajuste de coleta de lixo da máquina virtual Java SE 6 HotSpot [tm] (ou o documento equivalente para sua versão da JVM).


1
Não é isso que significa desreferência. Não é "definir a última referência de um objeto como nula", mas sim obter (ler) o valor de uma referência para que você possa usá-lo para operações subseqüentes.

1
O try..finally ainda é uma abordagem válida e recomendada? Suponha que, anteriormente, eu estava chamando um método nativo em finalize (), posso mover a chamada para a cláusula finally? class Resource { finalize() { destroy(); } protected native void destroy(); } class Alt_Resource { try (Resource r = new Resource()) { // use r } finalize { r.destroy(); }
aashima 24/09/19

r não teria como escopo o bloco final. Portanto, você não pode chamar de destruir nesse ponto. Agora, se você corrigir o escopo para que o objeto seja criado antes do bloco try, você terminará com o feio "antes de tentar com recursos".
usar o seguinte comando

21

Com o Java 1.7 lançado, agora você tem a opção adicional de usar o try-with-resourcesbloco. Por exemplo,

public class Closeable implements AutoCloseable {
    @Override
    public void close() {
        System.out.println("closing..."); 
    }
    public static void main(String[] args) {
        try (Closeable c = new Closeable()) {
            System.out.println("trying..."); 
            throw new Exception("throwing..."); 
        }
        catch (Exception e) {
            System.out.println("catching..."); 
        }
        finally {
            System.out.println("finalizing..."); 
        } 
    }
}

Se você executar esta classe, c.close()será executado quando o trybloco for deixado e antes que os blocos catche finallysejam executados. Ao contrário do caso do finalize()método, close()é garantido que seja executado. No entanto, não há necessidade de executá-lo explicitamente na finallycláusula.


e se não usássemos o bloco try-with-resources? Eu acho que podemos chamar close em finalize () apenas para garantir que o close tenha sido chamado.
shintoZ

3
@shintoZ como eu li em cima respostas não há garantia de finalize()execução
Asif Mushtaq

14

Concordo plenamente com outras respostas, dizendo para não confiar na execução de finalize.

Além dos blocos try-catch-finalmente, você pode usar o tempo de execução # addShutdownHook (introduzido no Java 1.3) para executar as limpezas finais no seu programa.

Isso não é o mesmo que os destruidores , mas pode-se implementar um gancho de desligamento com objetos ouvintes registrados nos quais métodos de limpeza (fechar conexões persistentes com o banco de dados, remover bloqueios de arquivos etc.) podem ser chamados - coisas que normalmente seriam feitas em destruidores . Novamente - isso não substitui os destruidores, mas em alguns casos, você pode abordar a funcionalidade desejada com isso.

A vantagem disso é ter um comportamento de desconstrução fracamente acoplado ao restante do seu programa.


Aparentemente, o addShutdownHook foi introduzido no Java 1.3. De qualquer forma, está disponível para mim no 1.5. :) Veja esta: stackoverflow.com/questions/727151/...
skiphoppy

1
Para sua informação, na minha experiência, os ganchos de desligamento não serão chamados se você usar o botão vermelho "encerrar" no Eclipse - a JVM inteira será destruída imediatamente, os ganchos de desligamento não serão chamados normalmente. Ou seja, você pode ver um comportamento diferente durante o desenvolvimento e produção se desenvolver usando eclipse
Hamy

11

Não, java.lang.Object#finalizeé o mais próximo que você pode chegar.

No entanto, quando (e se) é chamado, não é garantido.
Vejo:java.lang.Runtime#runFinalizersOnExit(boolean)


5
Um método que pode ou não ser chamado é essencialmente inútil no meu livro. Teria sido melhor não poluir a linguagem com um método especial inútil que, na melhor das hipóteses, fornece uma falsa sensação de segurança. Nunca entenderei por que os desenvolvedores da linguagem Java pensaram que finalizar era uma boa idéia.
antred

@antred Os desenvolvedores da linguagem Java concordam . Acho que, na época, para alguns deles, foi a primeira vez que eles criaram uma linguagem de programação e um ambiente de tempo de execução com coleta de lixo. O que é menos compreensível, é o motivo pelo qual essa outra linguagem gerenciada copiou esse conceito de uma vez, quando já era entendido que esse conceito é uma má ideia.
Holger

7

Primeiro, observe que, como o Java é coletado pelo lixo, é raro precisar fazer algo sobre a destruição de objetos. Em primeiro lugar, porque você normalmente não possui recursos gerenciados para liberar e, em segundo lugar, porque não pode prever quando ou se isso acontecerá, por isso é inapropriado para coisas que você precisa ocorrer "assim que ninguém mais estiver usando meu objeto "

Você pode ser notificado após a destruição de um objeto usando java.lang.ref.PhantomReference (na verdade, dizer que foi destruído pode ser um pouco impreciso, mas se uma referência fantasma a ele estiver na fila, ela não será mais recuperável, o que geralmente equivale a a mesma coisa). Um uso comum é:

  • Separe os recursos em sua classe que precisam ser destruídos em outro objeto auxiliar (observe que, se tudo o que você está fazendo é fechar uma conexão, que é um caso comum, não é necessário escrever uma nova classe: a conexão a ser fechada seria o "objeto auxiliar" nesse caso).
  • Ao criar seu objeto principal, crie também uma PhantomReference para ele. Faça isso referir-se ao novo objeto auxiliar ou configure um mapa a partir de objetos PhantomReference para seus objetos auxiliares correspondentes.
  • Depois que o objeto principal é coletado, o PhantomReference fica na fila (ou melhor, pode estar na fila - como finalizadores, não há garantia de que alguma vez existirá, por exemplo, se a VM sair, ela não esperará). Verifique se está processando sua fila (em um encadeamento especial ou de tempos em tempos). Devido à referência rígida ao objeto auxiliar, o objeto auxiliar ainda não foi coletado. Portanto, faça a limpeza que você quiser no objeto auxiliar, depois descarte o PhantomReference e o auxiliar também será coletado.

Também existe finalize (), que parece um destruidor, mas não se comporta como um. Geralmente não é uma boa opção.


Por que um PhantomReference em vez de um WeakReference?
Uckelman

2
@uckelman: se tudo o que você quer é notificação, o PhantomReference faz o trabalho, é para isso que ele foi projetado. A semântica adicional do WeakReference não é necessária aqui e, no momento em que o ReferenceQueue é notificado, você não pode mais recuperar o objeto por meio do WeakReference, portanto, o único motivo para usá-lo é economizar, lembrando que o PhantomReference existe. Qualquer trabalho extra que o WeakReference faz é provavelmente insignificante, mas por que se preocupar com isso?
21711 Steve Jobs

Obrigado por sugerir o PhantomReference. Não é perfeito, mas ainda é melhor que nada.
foo

@SteveJessop que "trabalho extra", você acha, tem uma referência fraca em comparação com uma referência fantasma?
21919 Holger

6

o finalize() função é o destruidor.

No entanto, ele não deve ser usado normalmente porque é invocado após o GC e você não pode dizer quando isso acontecerá (se é que alguma vez).

Além disso, são necessários mais de um GC para desalocar objetos que tenham finalize() .

Você deve tentar limpar nos locais lógicos do seu código usando as try{...} finally{...}instruções!


5

Eu concordo com a maioria das respostas.

Você não deve depender totalmente de ambos finalizeouShutdownHook

finalizar

  1. A JVM não garante quando este finalize()método será chamado.

  2. finalize()é chamado apenas uma vez pelo encadeamento do GC. Se um objeto se recuperar do método de finalização, finalizenão será chamado novamente.

  3. No seu aplicativo, você pode ter alguns objetos ativos, nos quais a coleta de lixo nunca é chamada.

  4. Qualquer coisa Exceptionlançada pelo método de finalização é ignorada pelo encadeamento do GC

  5. System.runFinalization(true)e Runtime.getRuntime().runFinalization(true)métodos aumentam a probabilidade de chamar o finalize()método, mas agora esses dois métodos foram preteridos. Esses métodos são muito perigosos devido à falta de segurança do encadeamento e possível criação de deadlock.

shutdownHooks

public void addShutdownHook(Thread hook)

Registra um novo gancho de desligamento de máquina virtual.

A máquina virtual Java é encerrada em resposta a dois tipos de eventos:

  1. O programa sai normalmente, quando o último encadeamento não daemon sai ou quando a saída (equivalente, System.exit método ) é invocado, ou
  2. A máquina virtual é encerrada em resposta a uma interrupção do usuário, como digitar ^ C, ou a um evento em todo o sistema, como logoff do usuário ou encerramento do sistema.
  3. Um gancho de desligamento é simplesmente um encadeamento inicializado, mas não iniciado. Quando a máquina virtual inicia sua sequência de desligamento, inicia todos os ganchos de desligamento registrados em uma ordem não especificada e os deixa executar simultaneamente. Quando todos os ganchos terminarem, ele executará todos os finalizadores não solicitados se a finalização na saída estiver ativada.
  4. Finalmente, a máquina virtual será interrompida. Observe que os encadeamentos do daemon continuarão em execução durante a sequência de desligamento, assim como os encadeamentos que não sejam do daemon se o desligamento foi iniciado invocando o método exit
  5. Os ganchos de desligamento também devem terminar seu trabalho rapidamente. Quando um programa chama sair, a expectativa é que a máquina virtual seja desligada e saia prontamente.

    Mas mesmo a documentação da Oracle citou que

Em raras circunstâncias, a máquina virtual pode abortar, ou seja, parar de executar sem desligar corretamente

Isso ocorre quando a máquina virtual é finalizada externamente, por exemplo, com o SIGKILLsinal no Unix ou a TerminateProcesschamada no Microsoft Windows. A máquina virtual também pode abortar se um método nativo der errado, por exemplo, corrompendo estruturas de dados internas ou tentando acessar memória inexistente. Se a máquina virtual abortar, não será possível garantir se algum gancho de desligamento será executado ou não.

Conclusão : use os try{} catch{} finally{}blocos adequadamente e libere recursos críticos em finally(}bloco. Durante a liberação de recursos em finally{}bloco, pegue Exceptione Throwable.


4

Se é apenas com a memória que você está preocupado, não. Apenas confie no GC, ele faz um trabalho decente. Na verdade, vi algo tão eficiente que poderia ser melhor para o desempenho criar montes de objetos minúsculos do que utilizar matrizes grandes em alguns casos.


3

Talvez você possa usar um bloco try ... finalmente para finalizar o objeto no fluxo de controle no qual você está usando o objeto. Obviamente, isso não acontece automaticamente, mas a destruição em C ++ também não. Você costuma ver o fechamento de recursos no bloco final.


1
Essa é a resposta certa quando o recurso em questão possui um único proprietário e nunca tem referências a ele "roubadas" por outro código.
Steve Jessop

3

Há uma anotação @Cleanup no Lombok que se assemelha principalmente aos destruidores de C ++:

@Cleanup
ResourceClass resource = new ResourceClass();

Ao processá-lo (no momento da compilação), o Lombok insere um try-finallybloco apropriado para que resource.close()seja chamado, quando a execução sair do escopo da variável. Você também pode especificar explicitamente outro método para liberar o recurso, por exemplo resource.dispose():

@Cleanup("dispose")
ResourceClass resource = new ResourceClass();

2
A vantagem que vejo é que haverá menos aninhamento (o que pode ser significativo se você tiver muitos objetos que exigem "destruição").
Alexey #

Um bloco try-with-resource pode ter vários recursos de uma só vez
Alexander - Reinstate Monica

1
Mas não pode ter instruções entre eles.
Alexey

Justo. Suponho que a recomendação é preferir tentar com recurso, mesmo para vários recursos, a menos que seja necessário que haja instruções entre eles que o forcem a criar novos blocos de tentativa com recurso (aumento do aninhamento) e use@Cleanup
Alexander - Reinstate Monica

2

O equivalente mais próximo de um destruidor em Java é o método finalize () . A grande diferença para um destruidor tradicional é que você não pode ter certeza de quando será chamado, pois essa é a responsabilidade do coletor de lixo. É altamente recomendável ler atentamente isso antes de usá-lo, pois seus padrões típicos de RAIA para identificadores de arquivos e assim por diante não funcionarão de maneira confiável com finalize ().


2

Muitas ótimas respostas aqui, mas há algumas informações adicionais sobre por que você deve evitar o uso de finalize () .

Se a JVM sair devido a System.exit()ou Runtime.getRuntime().exit(), os finalizadores não serão executados por padrão. No Javadoc for Runtime.exit () :

A sequência de desligamento da máquina virtual consiste em duas fases. Na primeira fase, todos os ganchos de desligamento registrados, se houver, são iniciados em uma ordem não especificada e podem ser executados simultaneamente até que terminem. Na segunda fase, todos os finalizadores não solicitados serão executados se a finalização na saída estiver ativada. Feito isso, a máquina virtual pára.

Você pode ligar, System.runFinalization()mas apenas faz "o melhor esforço para concluir todas as finalizações pendentes" - não uma garantia.

Existe um System.runFinalizersOnExit()método, mas não o use - é inseguro, obsoleto há muito tempo.


1

Se você estiver escrevendo um Java Applet, poderá substituir o método Applet "destroy ()". Isto é...

 * Called by the browser or applet viewer to inform
 * this applet that it is being reclaimed and that it should destroy
 * any resources that it has allocated. The stop() method
 * will always be called before destroy().

Obviamente, não é o que você deseja, mas pode ser o que as outras pessoas estão procurando.


1

Pensando apenas na pergunta original ... que, acho que podemos concluir com todas as outras respostas aprendidas, e também com o Java eficaz eficaz de Bloch , o item 7, "Evite finalizadores", procura a solução para uma pergunta legítima de uma maneira que é inadequado para a linguagem Java ...:

... não seria uma solução óbvia fazer o que o OP realmente deseja ser para manter todos os seus objetos que precisam ser redefinidos em uma espécie de "cercadinho", para o qual todos os outros objetos não redefiníveis têm referências apenas através de algum tipo do objeto acessador ...

E então, quando você precisa "redefinir", desconecta o cercadinho existente e cria um novo: toda a teia de objetos no cercadinho é lançada à deriva, para nunca mais retornar e um dia para ser coletada pelo GC.

Se algum desses objetos for Closeable(ou não, mas tiver um closemétodo), você poderá colocá-los em um Bagno cercadinho conforme eles forem criados (e possivelmente abertos), e o último ato do acessador antes de cortá-lo seria: através de todo o Closeablesfechamento deles ...?

O código provavelmente seria algo como isto:

accessor.getPlaypen().closeCloseables();
accessor.setPlaypen( new Playpen() );

closeCloseablesprovavelmente seria um método de bloqueio, provavelmente envolvendo uma trava (por exemplo CountdownLatch), para lidar com (e esperar conforme apropriado) qualquer Runnables/ Callablesem qualquer encadeamento específico para o Playpenfinal, conforme apropriado, em particular no encadeamento JavaFX.


0

Embora tenha havido avanços consideráveis ​​na tecnologia GC do Java, você ainda precisa estar atento às suas referências. Muitos casos de padrões de referência aparentemente triviais que são realmente ninhos de ratos sob o capô vêm à mente.

Na sua postagem, não parece que você está tentando implementar um método de redefinição para fins de reutilização de objetos (verdadeiro?). Seus objetos estão mantendo outro tipo de recurso que precisa ser limpo (por exemplo, fluxos que devem ser fechados, objetos agrupados ou emprestados que devem ser devolvidos)? Se a única coisa com a qual você está preocupado é com a desalocação de memória, eu reconsideraria minha estrutura de objetos e tentaria verificar se meus objetos são estruturas independentes que serão limpas no momento da GC.



0

Nenhum Java não possui destruidores. O principal motivo por trás dele em Java é o Garbage Collectors, que sempre trabalha passivamente em segundo plano e todos os objetos são criados na memória heap, que é o local em que o GC trabalha. É necessário chamar explicitamente a função de exclusão, pois não existe um coletor de lixo como esse.


-3

Eu costumava lidar principalmente com C ++ e foi isso que me levou à busca de um destruidor também. Estou usando muito JAVA agora. O que eu fiz e pode não ser o melhor para todos, mas implementei meu próprio destruidor, redefinindo todos os valores para 0 ou para o padrão por meio de uma função.

Exemplo:

public myDestructor() {

variableA = 0; //INT
variableB = 0.0; //DOUBLE & FLOAT
variableC = "NO NAME ENTERED"; //TEXT & STRING
variableD = false; //BOOL

}

Idealmente, isso não funcionará para todas as situações, mas onde existem variáveis ​​globais, ele funcionará desde que você não tenha uma tonelada delas.

Eu sei que não sou o melhor programador Java, mas parece estar funcionando para mim.


Tente usar objetos imutáveis mais, depois de 'entrar' tudo fará mais sentido :)
R. van Twisk

5
Isso não é tão errado quanto inútil - ou seja, não alcança nada. Se o seu programa exige que os tipos brutos sejam redefinidos para funcionar corretamente, as instâncias de suas classes têm escopo incorreto, o que significa que você provavelmente está reatribuindo as propriedades de um objeto existente às propriedades de um novo objeto, sem criar um novo Object ().
precisa saber é o seguinte

1
Exceto que há muitas necessidades para redefinir variáveis. Eu escolhi o nome destruidor porque ele se encaixa no que estou fazendo. Ele faz alcançar algo, apenas nada que você entenda
ChristianGLong
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.