java易错题锦集一

易错题

字符串

题一

原题链接

public class Example {
    
     
  String str = new String("good"); 
  char[] ch = {
    
    'a','b','c'}; 
  public static void main(String[] args) {
    
     
     Example ex = new Example(); 
     ex.change(ex.str, ex.ch); 
     System.out.print(ex.str +"and"); 
     System.out.print(ex.ch);  
  } 
    
  public void change(String str, char ch[]){
    
     
     str= "test ok"; 
     ch[0]= 'g'; 
  } 
}

A .test okandabc
B .test okandgbc
C .goodandabc
D .goodandgbc

解析:

链接:
final修饰的作用:

  • final修饰基本数据类型,表示值不可变
  • final修饰引用类型,表示引用的指向不可变

java 中String是final修饰的,是不可变,一旦初始化,其引用指向的内容是不可变的。

也就是说,String str = “aa”;str=“bb”;第二句不是改变“aa”所存储地址的内容,而是另外开辟了一个空间用来存储“bb”;同时由str指向

原来的“aa”,现在已经不可达,GC时会自动回收。

因此String作为参数传进来时候,str= “test ok”; 实际给副本引用str指向了新分配的地址,该地址存储“test ok”。

因此,原先的str仍然指向“good”

运行

public class Immutable {
    
    
    String str = new String("good");
    char[] ch = {
    
    'a','b','c'};
    public static void main(String[] args) {
    
    
        Immutable ex = new Immutable();
        System.out.println("main str hashcode-:"+ex.str.hashCode());
        ex.change(ex.str, ex.ch);
        System.out.println("main str hashcode-:"+ex.str.hashCode());
        System.out.print(ex.str +"and");
        System.out.print(ex.ch);
    }

    public void change(String str, char ch[]){
    
    
        System.out.println("func str hashcode-:"+str.hashCode());
        str= "test ok";
        System.out.println("func str hashcode-:"+str.hashCode());
        ch[0]= 'g';
    }
}

结果

main str hashcode-:3178685
func str hashcode-:3178685
func str hashcode-:-1422516182
main str hashcode-:3178685
goodandgbc
public static void main(String[] args) {
    
    
        String a = "aaa";
        System.out.println("str hashcode-:"+a.hashCode()+"  str value:"+a);
        a = "test";
        System.out.println("str hashcode-:"+a.hashCode()+"  str value:"+a);
    }

至于这一段代码为什么变了

str hashcode-:96321  str value:aaa
str hashcode-:3556498  str value:test

其实没有变

这里 a 是一个引用,他指向了一个具体的 对象,当他执行完a = "test"时又创建了一个新的对象,引用a 重新指向了这个新的对象,原来的对象还在,并没有改变

引用的源文链接:
Java中的String为什么是不可变的

补充

数组有length属性,字符串只有length()方法

	String a = "test";
        a.length();

        String[] s = {
    
    "a", "b"};
        int length = s.length;

Integer

下面三种情况各输出什么结果

    public static void main(String[] args) {
    
    
        Integer n1 = new Integer(47);
        Integer n2 = new Integer(47);
        System.out.print(n1 == n2);

        System.out.println("-------------------");
        Integer n3 = Integer.valueOf(48);
        Integer n4 = 48;
        System.out.println(n3 == n4);

        System.out.println("-------------------");
        Integer n5 = Integer.valueOf(129);
        Integer n6 = 129;
        System.out.println(n5 == n6);
    }

先直接说答案:ZmFsc2UsIGZhbHNlLCB0cnVl (base64解密后看结果可以用这个解密
解析:
使用Integer a = 1;或Integer a = Integer.valueOf(1); 在值介于-128至127直接时,作为基本类型。
使用Integer a = new Integer(1); 时,无论值是多少,都作为对象。
查看jdk源码
在-128到127的数字被缓存在cache中

    private static class IntegerCache {
    
    
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
    
    
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
    
    
                try {
    
    
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
    
    
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {
    
    }
    }
    public static Integer valueOf(int i) {
    
    
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

Integer第二道

public class Tester{
    
    
public static void main(String[] args){
    
    
   Integer var1=new Integer(1);
   Integer var2=var1;
   doSomething(var2);
   System.out.print(var1.intValue());
   System.out.print(var1==var2);
}
public static void doSomething(Integer integer){
    
    
    integer=new Integer(2);
    }
}

答案: MXRydWU= (base64解密后看结果可以用这个解密

数组 易错

下面哪几个可以成功创建多维数组
A. float f[][] = new float[6][6];
B. float []f[] = new float[6][6];
C. float f[][] = new float[][6];
D. float [][]f = new float[6][6];
E. float [][]f = new float[6][];

答案:QUJERQ== (base64解密后看结果可以用这个解密

try catch块 执行结果是什么

public class Test{
    
    	
    public int add(int a,int b){
    
    	
         try {
    
    	
             return a+b;		
         } 
        catch (Exception e) {
    
    	
            System.out.println("catch语句块");	
         }	
         finally{
    
    	
             System.out.println("finally语句块");	
         }	
         return 0;	
    } 
     public static void main(String argv[]){
    
     
         Test test =new Test(); 
         System.out.println("和是:"+test.add(9, 34)); 
     }
}

A: 和是:43 finally语句块
B: finally语句块  和是:43

答案: B
解析:

public abstract class Test {
    
    
    public static void main(String[] args) {
    
    
        System.out.println(beforeFinally());
    }
    
    public static int beforeFinally(){
    
    
        int a = 0;
        try{
    
    
            a = 1;
            return a;
        }finally{
    
    
            a = 2;
        }
    }
}
/**output:
1
*/

解答来源
从结果上看,貌似finally 里的语句是在return 之后执行的,其实不然,实际上finally 里的语句是在在return 之前执行的。那么问题来了,既然是在之前执行,那为什么a 的值没有被覆盖了?
实际过程是这样的:当程序执行到try{}语句中的return方法时,它会干这么一件事,将要返回的结果存储到一个临时栈中,然后程序不会立即返回,而是去执行finally{}中的程序, 在执行a = 2时,程序仅仅是覆盖了a的值,但不会去更新临时栈中的那个要返回的值 。执行完之后,就会通知主程序“finally的程序执行完毕,可以请求返回了”,这时,就会将临时栈中的值取出来返回。这下应该清楚了,要返回的值是保存至临时栈中的。
把上面代码稍微改一下,如果finally中也有return
那么在执行这个return时,就会更新临时栈中的值。同样,在执行完finally之后,就会通知主程序请求返回了,即将临时栈中的值取出来返回。故返回值是2.

public abstract class Test {
    
    
    public static void main(String[] args) {
    
    
        System.out.println(beforeFinally());
    }
    
    public static int beforeFinally(){
    
    
        int a = 0;
        try{
    
    
            a = 1;
            return a;
        }finally{
    
    
            a = 2;
            return a;
        }
    }
}
/**output:
2
*/

switch

jdk1.7之前byte,short ,int ,char
jdk1.7之后加入String
java8,switch支持10种类型
基本类型:byte char short int
包装类 :Byte,Short,Character,Integer String enum
实际只支持int类型 Java实际只能支持int类型的switch语句,那其他的类型时如何支持的
a、基本类型byte char short 原因:这些基本数字类型可自动向上转为int, 实际还是用的int。
b、基本类型包装类Byte,Short,Character,Integer 原因:java的自动拆箱机制 可看这些对象自动转为基本类型
c、String 类型 原因:实际switch比较的string.hashCode值,它是一个int类型
d、enum类型 原因 :实际比较的是enum的ordinal值(表示枚举值的顺序),它也是一个int类型 所以也可以说 switch语句只支持int类型

线程

通信

在Java中,常用的线程通信方式有两种,分别是利用Monitor实现线程通信、利用Condition实现线程通信。线程同步是线程通信的前提,所以究竟采用哪种方式实现通信,取决于线程同步的方式。

如果是采用synchronized关键字进行同步,则需要依赖Monitor(同步监视器)实现线程通信,Monitor就是锁对象。在synchronized同步模式下,锁对象可以是任意的类型,所以通信方法自然就被定义在Object类中了,这些方法包括:wait()、notify()、notifyAll()。一个线程通过Monitor调用wait()时,它就会释放锁并在此等待。当其他线程通过Monitor调用notify()时,则会唤醒在此等待的一个线程。当其他线程通过Monitor调用notifyAll()时,则会唤醒在此等待的所有线程。

JDK 1.5新增了Lock接口及其实现类,提供了更为灵活的同步方式。如果是采用Lock对象进行同步,则需要依赖Condition实现线程通信,33 Condition对象是由Lock对象创建出来的,它依赖于Lock对象。Condition对象中定义的通信方法,与Object类中的通信方法类似,它包括await()、signal()、signalAll()。通过名字就能看出它们的含义了,当通过Condition调用await()时当前线程释放锁并等待,当通过Condition调用signal()时唤醒一个等待的线程,当通过Condition调用signalAll()时则唤醒所有等待的线程。

同步

jvm中没有进程的概念 ,但是jvm中的线程映射为操作系统中的进程,对应关系为1:1。 在jvm中 是使用监视器锁来实现不同线程的异步执行, 在语法的表现就是synchronized 。

重写和重载

返回值不能作为重载的依据,有方法体的不能作为抽象函数

方法重写

参数列表, 返回类型 必须完全与被重写方法的相同;
访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为public,那么在子类中重写该方法就不能声明为protected。
父类的成员方法只能被它的子类重写。
声明为final的方法不能被重写。
声明为static的方法不能被重写,但是能够被再次声明。
子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为private和final的方法。
子类和父类不在同一个包中,那么子类只能够重写父类的声明为public和protected的非final方法。
重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
构造方法不能被重写。
如果不能继承一个方法,则不能重写这个方法。

总结为两同两小一大
两同:方法名和参数列表相同
两小:返回值或声明异常比父类小(或相同)
一大:访问修饰符比父类的大(或相同

方法重载

被重载的方法必须改变参数列表(参数个数或类型或顺序不一样);
被重载的方法可以改变返回类型;
被重载的方法可以改变访问修饰符;
被重载的方法可以声明新的或更广的检查异常;
方法能够在同一个类中或者在一个子类中被重载。
无法以返回值类型作为重载函数的区分标准。

servlet

生命周期

链接:https://www.nowcoder.com/questionTerminal/3dfd72f89070415185f09aebecd0e3f7
来源:牛客网

Servlet的生命周期一般可以用三个方法来表示:
init():仅执行一次,负责在装载Servlet时初始化Servlet对象
service() :核心方法,一般HttpServlet中会有get,post两种处理方式。在调用doGet和doPost方法时会构造servletRequest和servletResponse请求和响应对象作为参数。
destory():在停止并且卸载Servlet时执行,负责释放资源
初始化阶段:Servlet启动,会读取配置文件中的信息,构造指定的Servlet对象,创建ServletConfig对象,将ServletConfig作为参数来调用init()方法。

doGet/doPost

doGet/doPost 则是在 javax.servlet.http.HttpServlet 中实现的

import java.util.*

能访问java/util目录下的所有类,不能访问java/util子目录下的所有类
java.util.*,只能读取其目录下的类,不能读取其子目录下的类。
因为其根目录和子目录下可能有同名类,若都能读取,则会混淆

异常

运行异常,可以通过java虚拟机来自行处理。非运行异常,我们应该捕获或者抛出,
非运行异常=检查异常 需要try catch捕获或者throws抛出
Exception

treeSet

TreeSet自然排序,LinkedHashSet按添加顺序排序

前台线程 && 后台线程

main()函数即主函数,是一个前台线程,前台进程是程序中必须执行完成的,而后台线程则是java中所有前台结束后结束,不管有没有完成,后台线程主要用与内存分配等方面。

后台线程也称为守护线程。JVM的垃圾回收线程就是一个后台线程。
前台线程:是指接受后台线程服务的线程,其实前台后台线程是联系在一起,就像傀儡和幕后操纵者一样的关系。傀儡是前台线程、幕后操纵者是后台线程。由前台线程创建的线程默认也是前台线程。可以通过isDaemon()和setDaemon()方法来判断和设置一个线程是否为后台线程。

前台线程和后台线程的区别和联系:
1、后台线程不会阻止进程的终止。属于某个进程的所有前台线程都终止后,该进程就会被终止。所有剩余的后台线程都会停止且不会完成。
2、可以在任何时候将前台线程修改为后台线程,方式是设置Thread.IsBackground 属性。
3、不管是前台线程还是后台线程,如果线程内出现了异常,都会导致进程的终止。
4、托管线程池中的线程都是后台线程,使用new Thread方式创建的线程默认都是前台线程。
说明:
应用程序的主线程以及使用Thread构造的线程都默认为前台线程
使用Thread建立的线程默认情况下是前台线程,在进程中,只要有一个前台线程未退出,进程就不会终止。主线程就是一个前台线程。而后台线程不管线程是否结束,只要所有的前台线程都退出(包括正常退出和异常退出)后,进程就会自动终止。一般后台线程用于处理时间较短的任务,如在一个Web服务器中可以利用后台线程来处理客户端发过来的请求信息。而前台线程一般用于处理需要长时间等待的任务,如在Web服务器中的监听客户端请求的程序,或是定时对某些系统资源进行扫描的程序

线程共享,线程私有


私有:java虚拟机栈,程序计数器,本地方法栈
共享:java堆,方法区

java创建对象的方式

Java有5种方式来创建对象:
1、使用 new 关键字(最常用): ObjectName obj = new ObjectName();
2、使用反射的Class类的newInstance()方法: ObjectName obj = ObjectName.class.newInstance();
3、使用反射的Constructor类的newInstance()方法:ObjectName obj = ObjectName.class.getConstructor.newInstance();
4、使用对象克隆clone()方法: ObjectName obj = obj.clone();
5、使用反序列化(ObjectInputStream)readObject()方法: try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(FILE_NAME))) { ObjectName obj = ois.readObject(); }

猜你喜欢

转载自blog.csdn.net/weixin_44154094/article/details/128882457