java错题记录(二)

1.下列说法正确的是

A 在类方法中可用this来调用本类的类方法

B 在类方法中调用本类的类方法可直接调用

C 在类方法中只能调用本类的类方法

D 在类方法中绝对不能调用实例方法

正确答案:B

本题总结:

类方法是静态方法,被static修饰;而对象方法是通过new创建实例调用的方法。

类方法是属于整个类的;而实例方法是属于某个类的对象的。因为 类方法是属于整个类的,而不是属于某个对象的,因此类方法中不能有与类的对象相关的内容。即

1)类方法体中不能引用对象变量

2)类方法中不能调用对象方法

3)类方法中不能使用super、this关键字

4)类方法不能被覆盖

而对象方法则没有这些限制,即

1)对象方法中可以引用对象变量,也可以引用类变量

2)对象方法中可以调用对象方法、也可以调用类方法

3)对象方法中可以使用super、this关键字

2.J2EE中,当把来自客户机的HTTP请求委托给servlet时,会调用HttpServlet的( )方法

A service

B doget

C dopost

D init

正确答案:A

本题总结:

BC项:doGet和doPost是创建HttpServlet时需要覆盖的方法

D项:Servlet生命周期分为三个阶段:

1)初始化阶段,调用init()方法

2)响应客户请求阶段,调用service方法

3)终止阶段,调用destroy()方法

HttpServlet容器响应web客户端请求流程如下:

1)Web客户端向servlet发送http请求

2)servlet容器解析web客户端的http请求

3)servlet容器创建一个HttpRequest对象,封装http请求消息

4)servlet容器创建一个HttpResponse对象

5)servlet容器调用HttpServlet的service方法,这个方法会根据request的Method来判断具体是执行doGet还是doPost(如果请求方式是Get则调用doGet方法;如果请求是POST则调用doPost方法),把HttpRequest和HttpResponse对象作为service方法的参数传给HttpServlet对象

6)HttpServlet容器调用HttpRequest的有关方法,获取Http请求信息

7)HttpServlet调用HttpRequest的有关方法,生成相应数据

8)Servlet容器把HttpResponse的相应结果传给Web客户端

3.以下哪种方式实现的单例是线程安全的

A 枚举

B 静态内部类

C 双检锁模式

D 饿汉式

正确答案:ABCD

本题总结:

单例模式的定义:确保一个类中只有一个实例,并提供该实例的全局访问点;这样做的好处是:有些实例,全局只需要一个就够了,使用单例模式可以避免一个全局使用的类被频繁的创造与销毁,耗费系统资源。

单例模式的设计要素:

1)一个私有的构造函数(确保只能单例类自己创建实例)

2)一个私有的静态变量(确保只有一个实例)

3)一个公有的静态函数(给使用者提供调用方法)

单例模式的六种实现方式及各实现方式的优缺点

一、 饿汉式(线程安全)

实现:

public class Singleton {
     private static Singleton  uniqueInstance = new Singleton();

     private Singleton() {

    }

    public static  Singleton getUniqueInstance() {
        
        return uniqueInstance;
    }
}

说明:先不管需不需要该实例,都会直接实例化好该实例,然后当需要时,直接调用方法就可以使用了

优点:提前实例化好了一个实例,避免了线程不安全的情况

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

二、懒汉式(线程不安全)

实现:

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();就会有多个实例;

三、懒汉式(线程安全)

public class Singleton {
     private static Singleton uniqueInstance;

     private Singleton() {

    }

    public static synchronized Singleton getUniqueInstance() {
        if (uniqueInstance == null) {
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
}

说明:实现和线程不安全的懒汉式几乎一致,唯一不同的是get方法上加了synchronized关键字,如此一来,当多线程访问时,每次只有拿到锁的线程才能进入该方法,避免了多线程环境下线程不安全的问题

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

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

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

实现:

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 Single();这段代码执行时分为三步:

1)为uniqueInstance分配内存空间

2)初始化uniqueInstance

3)将uniqueInstance指向分配的内存地址

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

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

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

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

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

实现:

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时,此时静态内部类才会被加载进内存,并且初始化INSTANCE实例,而且JVM会确保INSTANCE只被实例化一次。

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

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

实现:

public enum Singleton {

    INSTANCE;

    //添加自己需要的操作
    public void doSomeThing() {

    }

}

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

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

防止反序列化:

序列化:将java对象转换成字节序列的过程

反序列化:通过这些字节序列在内存中新建java对象的过程

说明:反序列化将一个单实例对象写到磁盘在读回来,从而获得了一个新的实例,要防止反序列化,避免得到多个实例;枚举类天然防止反序列化

其他单例模式可以通过重写readResolve()方法从而防止反序列化:

private Object readResolve() throws ObjectStreamException{
        return singleton;
}

单例模式的应用场景:

1)网站计数器

2)应用程序中的日志应用

3)数据库连接池

4)多线程池

5)Web项目中配置对象的读取

总结

频繁实例化然后又销毁的对象,使用单例模式可以提高性能。

经常使用的对象,但实例化时耗费时间或者资源多,如数据库连接池,使用单例模式,可以提高性能,降低资源损坏。

使用线程池之类的控制资源时,使用单例模式,可以方便资源之间的通信。

最后分析CR.作者主页

猜你喜欢

转载自blog.csdn.net/zssxcj/article/details/131236560