Java基础错题集(五)

1.Java中的集合类包括ArrayList、LinkedList、HashMap等类,下列关于集合类描述正确的是(AB)

A.ArrayList和LinkedList均实现了List接口
B.ArrayList的访问速度比LinkedList快
C.添加和删除元素时,ArrayList的表现更佳
D.HashMap实现Map接口,它允许任何类型的键和值对象,并允许将null用作键或值

解析:添加和删除元素时,LinkedList的表现更佳。ArrayList 底层使用的是 Object 数组LinkedList 底层使用的是 双向链表。HashMap实现Map接口,它允许任何类型的键和值对象,并允许将null用作键或值。但 null 作为键只能有一个,null 作为值可以有多个;Hashtable 不允许有 null 键和 null 值,否则会抛出 NullPointerException。HashSet 、ArraList、 LinkedList均可接受null值

2.以下会产生精度丢失的类型转换是(B)

A.float  a=10
B.int a=(int)8846.0
C.byte  a=10; int b=-a
D.double d=100

解析:小范围转化为大范围的数值型变量,jvm在进行编译的过程中将进行类型的自动提升

大范围到小范围会丢失精度。

范围大小依次是:byte、char、short、int、long、float、double。

B属于double转int,会丢失精度。对于c选项,只要int类型的数没有超出(byte,short,char)的表述范围也就是127,可以直接byte a=23

3.以下程序的运行结果是:(   C )

TreeSet set = new TreeSet();
TreeSet subSet = new TreeSet();
for(int i=606;i<613;i++){
    if(i%2==0){
     set.add(i);
     }
 }
subSet = (TreeSet)set.subSet(608,true,611,true);
set.add(609);
System.out.println(set+" "+subSet);
A.编译失败
B.发生运行时异常
C.[606, 608, 609,610, 612] [608, 609,610]
D.[606, 608, 609,610, 612] [608, 610]

解析:集合set用ts替换,集合subSet用ts2代替

TreeSet ts = new TreeSet();
TreeSet ts2 = new TreeSet();
for(int i=606;i<613;i++){
    if(i%2==0){
     ts.add(i);
     }
 }
//true表示包含边界值(608和611),同样false表示不包含
ts2 = (TreeSet)ts.subSet(608,true,611,true);


//609在608和611之间,所以可以同时添加到ts和ts2中
//ts.add(609);  //输出结果ts=[606, 608, 609,610, 612],ts2=[608, 609 , 610]

//700不在608和611之间,所以不能添加到ts2中,但可以添加到ts中
//ts.add(700);  //输出结果ts=[606, 608, 610, 612,700],ts2=[608, 610]

//609在608和611之间,所以可以同时添加到ts和ts2中
ts2.add(609);  //输出结果ts=[606, 608, 610, 609, 612],ts2=[608, 609, 610]

//700不在608和611之间,所以通过ts2添加时会报错
//ts2.add(609);  //IllegalArgumentException: key out of range


System.out.println(ts+" "+ts2);

总结:
若n是一个集合,m是通过n通过subSet()方法得到的集合,m在添加元素时:

  1. 通过n添加x元素,x可以是任意值
    • x 在 n 的范围内,n 和 m 都会添加x
    • x 不在 n 的范围内,只能 n 添加x, m 则不添加x
  2. 通过m自身添加x元素,x只能取值在n范围内
    否则,会报错(IllegalArgumentException: key out of range)

4.Math.floor(-8.5)=(D )

A.(float)-8.0
B.(long)-9
C.(long)-8
D.(double)-9.0

解析:

floor: 求小于参数的最大整数。返回double类型-----n. 地板,地面

         例如:Math.floor(-4.2) = -5.0

-----------------------------------------------------------

ceil:   求大于参数的最小整数。返回double类型-----vt. 装天花板;

         例如:Math.ceil(5.6) = 6.0

-----------------------------------------------------------

round: 对小数进行四舍五入后的结果。返回int类型

         例如:Math.round(-4.6) = -5

5.关于ASCII码和ANSI码,以下说法不的是(D)

A.标准ASCII只使用7个t
B.在简体中文的Windows系统中,ANSI就是GB22
C.ASCII码是ANSI码集
D.ASCII码都是可打印字符

Java基础错题集(五)_第1张图片

6.如何获取ServletContext设置的参值?(B)

A.context.getParameter()
B.context.getInitParameter()
C.context.getAttribute()
D.context.getRequestDispatcher()

解析:

1.getParameter()是获取POST/GET传递的参数值;
2.①Web容器在启动时为每个Web应用创建一个ServletContext对象,ServletConfig对象中维护了ServletContext的引用,开发人员在编写servlet时,可以通过

ServletConfig.getServletContext

方法获得ServletContext对象。由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。ServletContext对象通常也被称之为context域对象。因为存在ServletContext中的数据在服务器中会长时间,这样就会占用很多内存,因此在使用ServletContext时,建议不要往里面添加过大的数据! 

②  多个Servlet通过ServletContext对象实现数据共享。 在InitServlet的Service方法中利用ServletContext对象存入需要共享的数据

ServletContext context = this.getServletContext();    

context.setAttribute("name", "haha"); 
ServletContext context = this.getServletContext();  

 String name = context.getAttribute("name");   

③获取WEB应用的初始化参数。 在DemoServlet的doPost方法中测试获取初始化参数的步骤如下:   

ServletContext context = this.getServletContext();   
String url = context.getInitParameter("url"); 

详细资料:Servletcontext 对象_w3cschool

3.getAttribute()是获取对象容器中的数据值;
4.getRequestDispatcher是请求转发。 

7.以下哪种方式实现的单例是线程安全的(ABCD)

A.枚举
B.静态内部类
C.双检锁模式
D.饿汉式

一、单例模式的定义

定义: 确保一个类只有一个实例,并提供该实例的全局访问点。

这样做的好处是:有些实例,全局只需要一个就够了,使用单例模式就可以避免一个全局使用的类,频繁的创建与销毁,耗费系统资源。

二、单例模式的设计要素

  • 一个私有构造函数 (确保只能单例类自己创建实例)
  • 一个私有静态变量 (确保只有一个实例)
  • 一个公有静态函数 (给使用者提供调用方法)

简单来说就是,单例类的构造方法不让其他人修改和使用;并且单例类自己只创建一个实例,这个实例,其他人也无法修改和直接使用;然后单例类提供一个调用方法,想用这个实例,只能调用。这样就确保了全局只创建了一次实例。

三、单例模式的6种实现及各实现的优缺点

(一)懒汉式(线程不安全)

实现:

public class Singleton {

     private static Singleton uniqueInstance;



     private Singleton() {



    }



    public static Singleton getUniqueInstance() {

        if (uniqueInstance == null) {

            uniqueInstance = new Singleton();

        }

        return uniqueInstance;

    }

}

说明: 先不创建实例,当第一次被调用时,再创建实例,所以被称为懒汉式。

优点: 延迟了实例化,如果不需要使用该类,就不会被实例化,节约了系统资源。

缺点: 线程不安全,多线程环境下,如果多个线程同时进入了 if (uniqueInstance == null) ,若此时还未实例化,也就是uniqueInstance == null,那么就会有多个线程执行 uniqueInstance = new Singleton(); ,就会实例化多个实例;

(二)饿汉式(线程安全)

实现:

1

2

3

4

5

6

7

8

9

10

11

12

public class Singleton {

    private static Singleton uniqueInstance = new Singleton();

    private Singleton() {

    }

    public static Singleton getUniqueInstance() {

        return uniqueInstance;

    }

}

说明: 先不管需不需要使用这个实例,直接先实例化好实例 (饿死鬼一样,所以称为饿汉式),然后当需要使用的时候,直接调方法就可以使用了。

优点: 提前实例化好了一个实例,避免了线程不安全问题的出现。

缺点: 直接实例化好了实例,不再延迟实例化;若系统没有使用这个实例,或者系统运行很久之后才需要使用这个实例,都会操作系统的资源浪费。

(三)懒汉式(线程安全)

实现:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

public class Singleton {

    private static Singleton uniqueInstance;

    private static singleton() {

    }

    private static synchronized Singleton getUinqueInstance() {

        if (uniqueInstance == null) {

            uniqueInstance = new Singleton();

        }

        return uniqueInstance;

    }

}

说明: 实现和 线程不安全的懒汉式 几乎一样,唯一不同的点是,在get方法上 加了一把 锁。如此一来,多个线程访问,每次只有拿到锁的的线程能够进入该方法,避免了多线程不安全问题的出现。

优点: 延迟实例化,节约了资源,并且是线程安全的。

缺点: 虽然解决了线程安全问题,但是性能降低了。因为,即使实例已经实例化了,既后续不会再出现线程安全问题了,但是锁还在,每次还是只能拿到锁的线程进入该方法使线程阻塞,等待时间过长。

(四)双重检查锁实现(线程安全)

实现:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

public class Singleton {

    private volatile static Singleton uniqueInstance;

    private Singleton() {

    }

    public static Singleton getUniqueInstance() {

        if (uniqueInstance == null) {

            synchronized (Singleton.class) {

                if (uniqueInstance == null) {

                    uniqueInstance = new Singleton();

                }

            }

        }

        return uniqueInstance;

    

}

说明: 双重检查数相当于是改进了 线程安全的懒汉式。线程安全的懒汉式 的缺点是性能降低了,造成的原因是因为即使实例已经实例化,依然每次都会有锁。而现在,我们将锁的位置变了,并且多加了一个检查。 也就是,先判断实例是否已经存在,若已经存在了,则不会执行判断方法内的有锁方法了。 而如果,还没有实例化的时候,多个线程进去了,也没有事,因为里面的方法有锁,只会让一个线程进入最内层方法并实例化实例。如此一来,最多最多,也就是第一次实例化的时候,会有线程阻塞的情况,后续便不会再有线程阻塞的问题。

为什么使用 volatile 关键字修饰了 uniqueInstance 实例变量 ?

uniqueInstance = new Singleton(); 这段代码执行时分为三步:

  1. 为 uniqueInstance 分配内存空间
  2. 初始化 uniqueInstance
  3. 将 uniqueInstance 指向分配的内存地址

正常的执行顺序当然是 1>2>3 ,但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1>3>2。
单线程环境时,指令重排并没有什么问题;多线程环境时,会导致有些线程可能会获取到还没初始化的实例。
例如:线程A 只执行了 1 和 3 ,此时线程B来调用 getUniqueInstance(),发现 uniqueInstance 不为空,便获取 uniqueInstance 实例,但是其实此时的 uniqueInstance 还没有初始化。

解决办法就是加一个 volatile 关键字修饰 uniqueInstance ,volatile 会禁止 JVM 的指令重排,就可以保证多线程环境下的安全运行。

优点: 延迟实例化,节约了资源;线程安全;并且相对于 线程安全的懒汉式,性能提高了。

缺点: volatile 关键字,对性能也有一些影响。

(五)静态内部类实现(线程安全)

实现:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

public class Singleton {

    private Singleton() {

    }

    private static class SingletonHolder {

        private static final Singleton INSTANCE = new Singleton();

    }

    public static Singleton getUniqueInstance() {

        return SingletonHolder.INSTANCE;

    }

}

说明: 首先,当外部类 Singleton 被加载时,静态内部类 SingletonHolder 并没有被加载进内存。当调用 getUniqueInstance() 方法时,会运行 return SingletonHolder.INSTANCE; ,触发了 SingletonHolder.INSTANCE ,此时静态内部类 SingletonHolder 才会被加载进内存,并且初始化 INSTANCE 实例,而且 JVM 会确保 INSTANCE 只被实例化一次。

优点: 延迟实例化,节约了资源;且线程安全;性能也提高了。

(六)枚举类实现(线程安全)

实现:

1

2

3

4

5

6

7

8

9

10

public enum Singleton {

    INSTANCE;

    //添加自己需要的操作

    public void doSomeThing() {

    }

}

说明: 默认枚举实例的创建就是线程安全的,且在任何情况下都是单例。

优点: 写法简单,线程安全,天然防止反射和反序列化调用。

  • 防止反序列化
    序列化:把java对象转换为字节序列的过程;
    反序列化: 通过这些字节序列在内存中新建java对象的过程;
    说明: 反序列化 将一个单例实例对象写到磁盘再读回来,从而获得了一个新的实例。
    我们要防止反序列化,避免得到多个实例。
    枚举类天然防止反序列化。
    其他单例模式 可以通过 重写 readResolve() 方法,从而防止反序列化,使实例唯一重写 readResolve() :

1

2

3

private Object readResolve() throws ObjectStreamException{

        return singleton;

}

四、单例模式的应用场景

应用场景举例:

  • 网站计数器。
  • 应用程序的日志应用。
  • Web项目中的配置对象的读取。
  • 数据库连接池。
  • 多线程池。
  • ......

使用场景总结:

  • 频繁实例化然后又销毁的对象,使用单例模式可以提高性能。
  • 经常使用的对象,但实例化时耗费时间或者资源多,如数据库连接池,使用单例模式,可以提高性能,降低资源损坏。
  • 使用线程池之类的控制资源时,使用单例模式,可以方便资源之间的通信。

8.以下语句返回值为 true 的是(AB)

Java基础错题集(五)_第2张图片

A.a1==a2
B.d1==d2
C.b1==b2
D.c1==c2

 解析:

  • 选项A,a1、a2赋值给Integer类型,自动装箱。对于–128到127(默认是127)之间的值,Integer.valueOf(int i) 返回的是缓存的Integer对象(并不是新建对象),变量所指向的是同一个对象,所以a1==a2返回true。
  • 选项B,Integer和int比较会进行自动拆箱,比较的是数值大小,所以d1==d2返回true。
  • 选项C,由于超出自动装箱的范围,return返回的是新建的对象,所以对象内存地址不同,b1==b2返回false
  • 选项D,普通new创建对象,两个new创建两个地址不同的对像,所以c1==c2返回false。

你可能感兴趣的:(Java基础错题集,java,开发语言,jvm)