Noções básicas de Java - quanto você não sabe?
1. && e ||
1. Adivinhe o que o seguinte programa irá imprimir?
O resultado é o seguinte:
2. Explique
o símbolo &&. Quando o lado esquerdo for falso, o julgamento à direita não será mais executado, porque o programa já pode ser determinado como falso, então b++ não é executado
Quando o símbolo || é verdadeiro, o julgamento à esquerda não é executado, porque o programa pode ser determinado como verdadeiro, então d++ é executado
3. O uso deste mecanismo de julgamento
pode resolver o problema de julgamento sequencial. Por exemplo, o seguinte programa
primeiro julga se não está vazio e depois julga o comprimento da string. Não está errado, porque quando s está vazio, ele não executará s.length()== 5
Por outro lado, se o comprimento da string for julgado primeiro, uma exceção de ponteiro nulo será lançada imediatamente
Dois, inteiro e int
1. Você consegue fazer as seguintes perguntas corretamente? Qual é a saída?
resultado:
2. Explique que
Integer é a classe wrapper de int, Integer refere-se ao objeto recém-criado na memória heap e int refere-se aos dados no conjunto de constantes java
Integer e int terão um mecanismo de unboxing e boxing
. Ao comparar int e Integer, Integer será automaticamente unboxed para int
. Ao atribuir dados Integer a int
, Integer será automaticamente unboxed
. Ao comparar int com ==, a comparação é o tamanho do valor.
Ao comparar int e Integer com ==, Integer será automaticamente desmarcado para int, e a comparação é o tamanho do valor.
Ao comparar Integer e Integer com ==, a comparação é dois Se os objetos são referências a o mesmo
Os pequenos detalhes do int autoboxing: para os dados de -128~127, int julgará se existe um objeto Integer com o valor correspondente no cache, se houver, retorne o objeto diretamente, caso contrário, crie um novo objeto e coloque o objeto no meio do cache. Isso é feito para salvar apenas uma cópia dos dados usados com frequência e reduzir o desperdício de memória
package com.wu.hello.main;
public class Main {
public static void main(String[] args) {
int a=0;
int b=0;
// true,int和Integer用==比较时,Integer会自动拆箱为int,比较的是值的大小
System.out.println(a==b);
Integer c=new Integer(1);
Integer d=new Integer(1);
// false,Integer和Integer用==比较时,比较的是两个对象是否是同一个的引用
System.out.println(c==d);
Integer e=2;
Integer f=2;
// true,int自动装箱的小细节:对于-128~127的数据,int会判断缓存里有没有相对应值的Integer对象,
// 如果有,直接返回该对象,否则新创建一个对象,并把对象放到缓存中。
System.out.println(e==f);
Integer g=200;
Integer h=200;
// false,int自动装箱的小细节:对于-128~127的数据,int会判断缓存里有没有相对应值的Integer对象,
// 如果有,直接返回该对象,否则新创建一个对象,并把对象放到缓存中。
System.out.println(g==h);
Integer i=new Integer(200);
int j=new Integer(200);
// true,int和Integer用\=\=比较时,Integer会自动拆箱为int,比较的是值的大小
System.out.println(i==j);
}
}
3. A diferença entre String, StringBuffer e StringBuilder
1. Diferença
Corda | StringBuffer | StringBuilder | |
---|---|---|---|
local de memória | java pool constante/espaço de heap | espaço de pilha | espaço de pilha |
problemas de segurança de thread | segurança do fio | segurança do fio | tópico inseguro |
é variável | Imutável | variável | variável |
eficiência | mais lento | geralmente | o mais rápido |
cenas a serem usadas | Use quando nenhuma alteração for necessária | Multithreading | fio único |
2. O que significa que String é imutável?
Por exemplo, a string s="abc", se você quiser s="adc", você não pode alterar diretamente o caractere 'b' para 'd', mas crie um novo objeto "adc" e vamos nos referir a ele
3. Por que o String é o mais lento?
Como o objeto String é imutável, um novo objeto precisa ser gerado toda vez que ele é modificado,
como emenda de string: "abc"+"d", um novo objeto string "abcd" precisa ser criado e StringBuffer e StringBuilder podem modificar diretamente o objeto atual
4. Qual thread não é seguro?
No caso de multi-threading, ocorrerão erros, por exemplo: vários threads modificam o objeto StringBuilder ao mesmo tempo
package com.wu.hello.main;
public class Main {
public static void main(String[] args) throws InterruptedException {
StringBuilder s=new StringBuilder();
for (int i = 0; i < 10; i++){
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 1000; j++){
s.append("a");
}
}
}).start();
}
Thread.sleep(1000);
System.out.println(s.length());
}
}
Uma exceção foi relatada e deve gerar logicamente 10000. Devido ao problema de multiencadeamento, o resultado estava errado, o que significa que o encadeamento não é seguro.
5. A razão pela qual o thread StringBuilder não é seguro
Como vários threads operam no mesmo objeto StringBuilder e o método de StringBuilder não está bloqueado, vários threads inserem o método ao mesmo tempo, resultando em inconsistências. Por exemplo, o thread 1 obtém dados após inserir o método e o thread 2 modifica o string , então o que o thread 1 obtém são dados sujos
E cada método de StringBuffer é sincronizado, então multi-threading não é problema
6. Da mesma forma, HashMap e HashSet não são thread-safe. Em um ambiente multi-threaded, ConcurrentHashMap e ConcurrentHashSet podem ser usados, é claro, a eficiência será reduzida
4. i+1<i é realmente verdadeiro?
1. Questão da prova escrita: Existe um número i+1<i? existir?
Existe no computador, o número do tipo Int ocupa 32 bits, quando o int for o valor máximo, adicione 1, então ele se tornará o valor mínimo
package com.wu.hello.main;
public class Main {
public static void main(String[] args) throws InterruptedException {
int maxValue = Integer.MAX_VALUE;
System.out.println(maxValue);
System.out.println(maxValue+1<maxValue);
}
}
5. O problema nulo ignorante
1. Veja o programa primeiro
package com.wu.hello.main;
public class NULL {
public static void haha(){
System.out.println("haha");
}
public static void main(String[] args) throws InterruptedException {
((NULL)null).haha();
}
}
2. O que é isso? Este programa pode realmente executar? real!
3. Explicação
Na verdade, NULL é o nome de uma classe.Em java, null pode ser convertido em qualquer objeto. Após a transferência, é equivalente ao seguinte programa:
package com.wu.hello.main;
public class NULL {
public static void haha(){
System.out.println("haha");
}
public static void main(String[] args) throws InterruptedException {
NULL n=null;
n.haha();
}
}
Então, você provavelmente entendeu! haha() é um método estático, e objetos nulos também podem ser chamados! Obviamente, se não for um método estático, uma exceção de ponteiro nulo será relatada.
6. Quantas rodadas você conhece sobre divisão inteira?
package com.wu.hello.main;
public class Main {
public static void main(String[] args) {
int a=20;
int b=6;
//方法1
System.out.println((int)Math.ceil((double) a/b));
//方法2
System.out.println(a/b+(((a%b)!=0)?1:0));
//方法3
System.out.println((a+b-1)/b);
}
}
O arredondamento para cima mais comumente usado é o método 3.
7. Isso também funcionará?
1. Veja o programa primeiro
package com.wu.hello.main;
public class Main {
public static void main(String[] args) {
https://www.baidu.com/;
System.out.println("haha");
}
}
2. Você acha que este programa pode ser executado?
A resposta é sim
3. Explicação
O programa atual é o seguinte:
package com.wu.hello.main;
public class Main {
public static void main(String[] args) {
https:
System.out.println("haha");
}
}
Na verdade, https: é a maneira de escrever a instrução goto. Essa forma de escrever está quase abandonada, porque a instrução goto tornará o programa confuso e difícil de manter.
Você pode ver o seguinte programa para entender o uso de goto:
package com.wu.hello.main;
public class Main {
public static void main(String[] args) {
https:
while (true){
break https;
}
System.out.println("haha");
}
}
Java otimizou a sintaxe goto de C++, que só pode ser seguida por break e continue para sair do loop ou reexecutar o loop
Oito, bug multiencadeado?
1. Observe atentamente o seguinte programa. Tanto a thread 1 quanto a thread 3 podem provar que a parada é verdadeira. Por que a thread 2 não pode parar?
package com.wu.hello.main;
public class Main {
static boolean stop = false;
public static void main(String[] args) {
// 线程1
new Thread(()->{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
stop=true;
System.out.println("线程1执行完毕,stop="+stop);
}).start();
// 线程2
new Thread(()->{
int i=0;
while (!stop){
i++;
}
System.out.println("线程2执行完毕,i="+i);
}).start();
// 线程3
new Thread(()->{
try {
Thread.sleep(200);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("线程3执行完毕,stop="+stop);
}).start();
}
}
2. Explique
Primeiro, vejamos a explicação do Baidu:
(1), JIT
A compilação just-in-time (JIT) é uma técnica que melhora o desempenho de linguagens compiladas por bytecode, traduzindo bytecode em código de máquina em tempo de execução.
(2), código quente
No sistema de compilação Java, no processo de transformar um arquivo de código-fonte Java em uma instrução de máquina executável por computador, são necessários dois estágios de compilação: o primeiro estágio é converter o arquivo .java em um arquivo .class. A segunda etapa da compilação é o processo de conversão de .class em instruções de máquina.
Quando a JVM descobre que um determinado método ou bloco de código é executado com frequência, ela o considera um "código de ponto de acesso". O JIT traduzirá algumas classes de "código quente" em códigos de máquina relacionados à máquina local, os otimizará e, em seguida, armazenará em cache os códigos de máquina traduzidos para o próximo uso.
É justamente por causa do problema do jit que o loop while é otimizado pelo jit após ser executado várias vezes, e o programa ter sido modificado
3. Prova
Adicionando o parâmetro -Xint à VM, usando o significado de interpretar a execução,
podemos ver que o programa executou 22428641 vezes e finalmente parou, o que resolveu esse problema, mas não usar o jit reduzirá muito o desempenho do programa , porque a máquina virtual não vai otimizar para nós, então o programa otimizado sem erro não será otimizado
4. Uma boa solução
Para recursos compartilhados multiencadeados, você pode usar volátil para modificar variáveis. Volátil pode nos ajudar a garantir a visibilidade e ordem do programa, mas ainda não pode garantir atomicidade
package com.wu.hello.main;
public class Main {
static volatile boolean stop = false;
public static void main(String[] args) {
// 线程1
new Thread(()->{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
stop=true;
System.out.println("线程1执行完毕,stop="+stop);
}).start();
// 线程2
new Thread(()->{
int i=0;
while (!stop){
i++;
}
System.out.println("线程2执行完毕,i="+i);
}).start();
// 线程3
new Thread(()->{
try {
Thread.sleep(200);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("线程3执行完毕,stop="+stop);
}).start();
}
}
5. Em detalhes, a segurança de thread volátil
deve considerar três aspectos: visibilidade, ordem e atomicidade
Visibilidade: um thread modifica variáveis compartilhadas e outro thread pode ver os resultados mais recentes
Ordenação: o código em uma thread é executado na ordem em que foi escrito
Atomicidade: Múltiplas linhas de código em um thread executado como um todo, durante o qual nenhum outro código de thread pode pular na fila
A visibilidade do programa é como este exemplo, que resolve o problema de que a thread 2 não consegue ler o valor mais recente de stop
Atomicity é, por exemplo, o seguinte programa, um par de threads a+=5, outro thread a-=5, então o resultado de a deve ser 0? A resposta é não, 5 e -5 também podem aparecer, porque embora a+=5 seja uma linha de código, são várias instruções após a compilação, incluindo busca de dados, cálculo, armazenamento de volta, etc. Essas operações não podem ser garantidas como atômicas Sim, depois que o thread 1 obtém os dados, o thread 2 modifica os dados e o thread 1 lê os dados sujos
package com.wu.hello.main;
import java.util.concurrent.CountDownLatch;
public class Main {
static int a=0;
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(2);
// 线程1
new Thread(() -> {
a += 5;
latch.countDown();
}).start();
// 线程2
new Thread(() -> {
a -= 5;
latch.countDown();
}).start();
latch.await();
System.out.println("a=" + a);
}
}
Ordenação significa que no programa a seguir, é óbvio que pode haver três situações de impressão:
x=0
y=0
x=1
y=1
x=0
y=1
Então, é possível que x=1 e y=0? A resposta é possível, porque o programa otimizará o programa ao compilar, e a ordem de execução do código pode ser alterada sem alterar o resultado de um único thread, o que significa que o thread 1 pode primeiro executar x=1; e depois executar y =1;, resultando em x=1 e y=0
package com.wu.hello.main;
import java.util.concurrent.CountDownLatch;
public class Main {
static int x=0;
static int y=0;
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(2);
// 线程1
new Thread(() -> {
y=1;
x=1;
}).start();
// 线程2
new Thread(() -> {
System.out.println("x="+x);
System.out.println("y="+y);
}).start();
}
}
variáveis modificadas voláteis podem resolver este problema
package com.wu.hello.main;
import java.util.concurrent.CountDownLatch;
public class Main {
static volatile int x=0;
static volatile int y=0;
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(2);
// 线程1
new Thread(() -> {
y=1;
x=1;
}).start();
// 线程2
new Thread(() -> {
System.out.println("x="+x);
System.out.println("y="+y);
}).start();
}
}
Então, em resumo, o volátil pode garantir visibilidade e ordem, enquanto a atomicidade precisa ser resolvida pelo mecanismo de bloqueio
Se você acha que a escrita é boa, dê um joinha!