2021-05-01

java错题集

1. 关于CGI和servlet的描述有问题的是? B

		A、servlet处于服务器进程中,它通过多线程方式运行其service方法
		B、CGI对每个请求都产生新的进程,服务完成后就销毁
		C、servlet在易用性上强于cgi,它提供了大量的实用工具例程,例如自动地解析和解码HTML表单数据、读取和设置HTTP头、处理Cookie、跟踪会话状态等
		D、cgi在移植性上高于servlet,几乎所有的主流服务器都直接或通过插件支持cgi

本题选D
CGI(Common Gateway Interface )又称为通用网关接口。通过请求信息动态产生回应内容的技术。总的来说CGI就是每进行一次请求都会产生一个进程来对请求进行处理,而且CGI每结束一个请求都会将这个进程销毁这样会占用很多的资源和时间、移植性低,因此现在该技术渐渐衰退。
servlet,一般都指的是java servlet,java servlet必须运行在web服务器,与web服务器形成一个互补的关系,就像一个程序运行在jvm中。java servlet是线程级的,每有一个请求产生都会生成一个线程,也因此降低的资源和时间的损耗。servlet处于服务器进程中,他通过多线程方法运行其service(),因此一个servlet实例会对应多个线程,服务完一个请求不会立即销毁。而且java servlet是在jvm中运行的,因此java servlet可以跨平台。
servlet是线程不安全的。通过以下三种方法可以达到线程安全
		1,实现 SingleThreadModel 接口的servlet是线程安全的
		2,用synchronized同步对共享数据的操作
		3,避免使用实例变量 

2. 下面有关servlet service描述错误的是?

		A、不管是post还是get方法提交过来的连接,都会在service中处理
		B、doGet/doPost 则是在 javax.servlet.GenericServlet 中实现的
		C、service()是在javax.servlet.Servlet接口中定义的
		D、service判断请求类型,决定是调用doGet还是doPost方法
本题选B
doget()/dopos()是在javax.servlet.httpservlet中实现的。javax.servlet.GenericServlet 的作用是对servlet中的生命周期、名字、配置、初始化方法参数,与底层协议无关。

3. 下列有关Servlet的生命周期,说法不正确的是?

		A、在创建自己的Servlet时候,应该在初始化方法init()方法中创建Servlet实例
		B、在Servlet生命周期的服务阶段,执行service()方法,根据用户请求的方法,执行相应的doGet()或是doPost()方法
 		C、在销毁阶段,执行destroy()方法后会释放Servlet 占用的资源
		D、destroy()方法仅执行一次,即在服务器停止且卸载Servlet时执行该方法
本题选A
servlet生命周期分为五个时期:加载、.创建、初始化、处理客户请求、销毁
	加载:容器通过类加载器使用servlet类对应的文件来加载servlet
	创建:通过使用servlet的构造函数来创建一个servlet实例
	初始化:使用init()来完成初始化
	处理客户请求:servlet创建后可以处理客户请求,当新客户发请求时,web容器会创建一个新的线程来处理新请求。servlet调用service()来响应客户的请求,并且servlet会根据客户的method方法来确定使用doget()还是dopost()。
	销毁:容器在卸载Servlet之前需要调用destroy()方法,让Servlet释放其占用的资源。

4. 下面哪一项不是加载驱动程序的方法?

		A、通过DriverManager.getConnection方法加载
		B、调用方法 Class.forName
		C、通过添加系统的jdbc.drivers属性
		D、通过registerDriver方法注册
本题选A
加载驱动程序的方法:
        1.Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
		2.DriverManager.registerDriver(new com.mysql.jdbc.Driver());
		3.System.setProperty("jdbc.drivers", "com.mysql.jdbc.Driver")
		
例子、创建一个以JDBC连接数据库的程序,包含7个步骤: 

(1)、加载JDBC驱动程序:

在连接数据库之前,首先要加载想要连接的数据库的驱动到JVM(Java虚拟机), 
这通过java.lang.Class类的静态方法forName(String className)实现。
例如:
    try{ //加载MySql的驱动类   
          Class.forName("com.mysql.jdbc.Driver");      } catch(ClassNotFoundException e)    {   
        System.out.println("找不到驱动程序类 ,加载驱动失败!");
        e.printStackTrace();    }
       
    	成功加载后,会将Driver类的实例注册到DriverManager类中。 

(2)、提供JDBC连接的URL

连接URL定义了连接数据库时的协议、子协议、数据源标识。
		 	1、 书写形式:协议:子协议:数据源标识
		 	2、 协议:在JDBC中总是以jdbc开始
		 	3、 子协议:是桥连接的驱动程序或是数据库管理系统名称。
		 	4、 数据源标识:标记找到数据库来源的地址与连接端口。
		 	

jdbc:mysql:   
    //localhost:3306/test?useUnicode=true&characterEncoding=gbk;

	useUnicode=true:表示使用Unicode字符集。 

如果characterEncoding设置为gb2312或GBK,本参数必须设置为true
&characterEncoding=gbk;字符编码方式。 

(3)、创建数据库的连接

	要连接数据库,需要向java.sql.DriverManager请求并获得Connection对象,该对象就代表一个数据库的连接。
	使用DriverManager的getConnectin(String url , String username ,
String password )方法传入指定的欲连接的数据库的路径、数据库的用户名和密码来获得。
	例如:

//连接MySql数据库,用户名和密码都是root    String url =
"jdbc:mysql://localhost:3306/test" ;     String username = "root" ; 
String password = "root" ;    try{   
    Connection con = DriverManager.getConnection(url , username , password ) ;   
     }catch(SQLException se){   
            System.out.println("数据库连接失败!");   
            se.printStackTrace() ;   
      }

(4)、创建一个Statement

		要执行SQL语句,必须获得java.sql.Statement实例,Statement实例分为以下3 

种类型: 

			<ol style="vertical-align: baseline;">
				<li style="vertical-align: baseline;">
					执行静态SQL语句。通常通过Statement实例实现。
				
				</li><li style="vertical-align: baseline;">
					执行动态SQL语句。通常通过PreparedStatement实例实现。
				
				</li><li style="vertical-align: baseline;">
					执行数据库存储过程。通常通过CallableStatement实例实现。
				
			
		
	


	具体的实现方式:

Statement stmt = con.createStatement() ;       PreparedStatement
pstmt = con.prepareStatement(sql) ;       CallableStatement cstmt =
con.prepareCall("{CALL demoSp(? , ?)}") ;

	(5)、执行SQL语句

Statement接口提供了三种执行SQL语句的方法:executeQuery、executeUpdate和execute 

			<ol style="vertical-align: baseline;">
				<li style="vertical-align: baseline;">
					ResultSet executeQuery(String sqlString):执行查询数据库的SQL语句,返回一个结果集(ResultSet)对象。
				
				</li><li style="vertical-align: baseline;">
					int executeUpdate(String sqlString):用于执行INSERT、UPDATE或 

DELETE语句以及SQL DDL语句,如:CREATE TABLE和DROP TABLE等
				
				</li><li style="vertical-align: baseline;">
					execute(sqlString):用于执行返回多个结果集、多个更新计数或二者组合的语句。	 	
ResultSet rs = stmt.executeQuery("SELECT * FROM ...") ;      int
rows = stmt.executeUpdate("INSERT INTO ...") ;      boolean flag =
stmt.execute(String sql) ; 	

(6)、处理结果

     两种情况: 
		执行更新返回的是本次操作影响到的记录数。

		执行查询返回的结果是一个ResultSet对象。ResultSet包含符合SQL语句中条件的所有行,
		并且它通过一套get方法提供了对这些 行中数据的访问。 使用结果集(ResultSet)对象的
		访问方法获取数据: 	

while(rs.next()){ String name = rs.getString("name") ; String pass =
rs.getString(1) ; // 此方法比较高效  }

( 7)、关闭JDBC对象

操作完成以后要把所有使用的JDBC对象全都关闭,以释放JDBC资源,关闭顺序和声 

明顺序相反: 

1、关闭记录集 

2、关闭声明

if(rs != null){   // 关闭记录集   
    try{   
        rs.close() ;   
    }catch(SQLException e){   
        e.printStackTrace() ;   
    }   
      }   
      if(stmt != null){   // 关闭声明   
    try{   
        stmt.close() ;   
    }catch(SQLException e){   
        e.printStackTrace() ;   
    }   
      }   
      if(conn != null){  // 关闭连接对象   
     try{   
        conn.close() ;   
     }catch(SQLException e){   
        e.printStackTrace() ;   
     }   
      } 

5.根据下面的程序代码,哪些选项的值返回true?

public class Square {  
    long width;  
    public Square(long l) {   
        width = l;  
    }  
    public static void main(String arg[]) {   
        Square a, b, c;   
        a = new Square(42L);   
        b = new Square(42L);   
        c = b;   
        long s = 42L;  
    } 
}

A、a == b
B、s == a
C、b == c
D、a.equals(s)
本题选C
这题考的是引用和内存。
//声明了3个Square类型的变量a, b, c
//在stack中分配3个内存,名字为a, b, c
Square a, b, c;
//在heap中分配了一块新内存,里边包含自己的成员变量width值为48L,然后stack中的a指向这块内存
a = new Square(42L);
//在heap中分配了一块新内存,其中包含自己的成员变量width值为48L,然后stack中的b指向这块内存
b = new Square(42L);
//stack中的c也指向b所指向的内存
c = b;
//在stack中分配了一块内存,值为42
long s = 42L;
如图所示:
在这里插入图片描述

来看4个选项:
A: a == b
由图可以看出a和b指向的不是同一个引用,故A错
B:s == a
一个Square类型不能与一个long型比较,编译就错误,故B错
c:b == c
由图可以看出b和c指向的是同一个引用,故C正确
d:a equal s
程序会把s封装成一个Long类型,由于Square没有重写Object的equals方法, 所以调用的是Object类的equals方法,源码如下
1
2
3
public boolean equals(Object obj) {
return (this == obj);
}
其实就是判断两个引用是否相等,故D也错误。

6. 关于struts项目中的类与MVC模式的对应关系,说法错误的是

A、Jsp文件实现视图View的功能
B、ActionServlet这一个类是整个struts项目的控制器
C、ActionForm、Action都属于Model部分
D、一个struts项目只能有一个Servlet

本题选c d
structs的工作原理:
structs是MVC的一种实现。他将servlet和jsp(标记部分)作为实现的一部分,Structs继承了MVC的各项特性,并让视图层更富于变化。
控制:有一个xml文件struct-config.xml,与之相关联的是controller,在structs中承担controller功能的是servlet,叫ActionServlet。ActionServlet是一个通用的控制组件,提供了所有http请求的入口点,他将各种请求截取后分发到各个相应的动作类中。另外控制组件还用于填充actionfrom,并传给动作类,动作类实现核心的业务逻辑,可以访问javabean或ejb。最后动作类将控制权传给相应的jsp文件,后者生成视图,所有控制逻辑用struct-config.xml来控制。
模型:模型以一个或多个java bean的形式存在。这些bean分为三类:Action Form、Action、JavaBean or EJB。Action Form通常称之为FormBean,封装了来自于Client的用户请求信息,如表单信息。Action通常称之为ActionBean,获取从 ActionSevlet传来的FormBean,取出FormBean中的相关信息,并做出相关的处理,一般是调用Java Bean或EJB等。Struts提供的ActionForm组件对象,它可以通过定义属性描述客户端表单数据。开发者可以从它派生子类对象,利用它和Struts提供的自定义标记库结合可以实现对客户端的表单数据的良好封装和支持,Action处理器对象可以直接对它进行读写,而不再需要和request、response对象进行数据交互。通过ActionForm组件对象实现了对View和Model之间交互的支持。
流程:在Struts中,用户的请求一般以*.do作为请求服务名,所有的*.do请求均被指向 ActionSevlet,ActionSevlet根据Struts-config.xml中的配置信息,将用户请求封装成一个指定名称的 FormBean,并将此FormBean传至指定名称的ActionBean,由ActionBean完成相应的业务操作,如文件操作,数据库操作等。 每一个*.do均有对应的FormBean名称和ActionBean名称,这些在Struts-config.xml中配置。
核心:Struts的核心是ActionSevlet,ActionSevlet的核心是Struts-config.xml。
在这里插入图片描述

7.下面有关jsp中静态include和动态include的区别,说法错误的是?

A、动态INCLUDE:用jsp:include动作实现
B、静态INCLUDE:用include伪码实现,定不会检查所含文件的变化,适用于包含静态页面<%@ include file=“included.htm” %>
C、静态include的结果是把其他jsp引入当前jsp,两者合为一体;动态include的结构是两者独立,直到输出时才合并
D、静态include和动态include都可以允许变量同名的冲突.页面设置也可以借用主文件的

本题选d
静态的include:是jsp的指令来实现的,<% @ include file=“xx.html”%> 特点是 共享request请求域,先包含再编译,不检查包含页面的变化。
动态的include:是jsp动作来实现的,<jsp:include page=“xx.jsp” flush=“true”/> 这个是不共享request请求域,先编译在包含,是要检查包含页面的变化的。

8. 给定以下JAVA代码,这段代码运行后输出的结果是()

public class Test
{
    public static int aMethod(int i)throws Exception
    {
        try{
            return i / 10;
        }
        catch (Exception ex)
        {
            throw new Exception("exception in a Method");
        } finally{
            System.out.printf("finally");
        }
    }
 
    public static void main(String [] args)
    {
        try
        {
            aMethod(0);
        }
        catch (Exception ex)
        {
            System.out.printf("exception in main");
        }
        System.out.printf("finished");
    }
}
 

A、exception in main finished
B、finally finished
C、exception in main finally
D、finally exception in main finished

本题选B
1、finally块一定会执行,无论是否try…catch。
2、finally前有return,会先执行return语句,并保存下来,再执行finally块,最后return。
3、finally前有return、finally块中也有return,先执行前面的return,保存下来,再执行finally的return,覆盖之前的结果,并返回。

9. 下面有关JVM内存,说法错误的是?

A、程序计数器是一个比较小的内存区域,用于指示当前线程所执行的字节码执行到了第几行,是线程隔离的
B、虚拟机栈描述的是Java方法执行的内存模型,用于存储局部变量,操作数栈,动态链接,方法出口等信息,是线程隔离的
C、方法区用于存储JVM加载的类信息、常量、静态变量、以及编译器编译后的代码等数据,是线程隔离的
D、原则上讲,所有的对象都在堆区上分配内存,是线程之间共享的

本题选C
大多数 JVM 将内存区域划分为 Method Area(Non-Heap)(方法区) ,Heap(堆) , Program Counter Register(程序计数器) , VM Stack(虚拟机栈,也有翻译成JAVA 方法栈的),Native Method Stack ( 本地方法栈 ),其中Method Area 和 Heap 是线程共享的 ,VM Stack,Native Method Stack 和Program Counter Register 是非线程共享的。为什么分为 线程共享和非线程共享的呢?请继续往下看。

首先我们熟悉一下一个一般性的 Java 程序的工作过程。一个 Java 源程序文件,会被编译为字节码文件(以 class 为扩展名),每个java程序都需要运行在自己的JVM上,然后告知 JVM 程序的运行入口,再被 JVM 通过字节码解释器加载运行。那么程序开始运行后,都是如何涉及到各内存区域的呢?

概括地说来,JVM初始运行的时候都会分配好 Method Area(方法区) 和Heap(堆) ,而JVM 每遇到一个线程,就为其分配一个 Program Counter Register(程序计数器) , VM Stack(虚拟机栈)和Native Method Stack (本地方法栈), 当线程终止时,三者(虚拟机栈,本地方法栈和程序计数器)所占用的内存空间也会被释放掉。这也是为什么我把内存区域分为线程共享和非线程共享的原因,非线程共享的那三个区域的生命周期与所属线程相同,而线程共享的区域与JAVA程序运行的生命周期相同,所以这也是系统垃圾回收的场所只发生在线程共享的区域(实际上对大部分虚拟机来说知发生在Heap上)的原因。
在这里插入图片描述
在这里插入图片描述

10. 下面有关jdbc statement的说法错误的是?

A、JDBC提供了Statement、PreparedStatement 和 CallableStatement三种方式来执行查询语句,其中 Statement 用于通用查询, PreparedStatement 用于执行参数化查询,而 CallableStatement则是用于存储过程
B、对于PreparedStatement来说,数据库可以使用已经编译过及定义好的执行计划,由于 PreparedStatement 对象已预编译过,所以其执行速度要快于 Statement 对象”
C、PreparedStatement中,“?” 叫做占位符,一个占位符可以有一个或者多个值
D、PreparedStatement可以阻止常见的SQL注入式攻击

本题选C

1.Statement、PreparedStatement和CallableStatement
都是接口(interface)。  
2.Statement继承自Wrapper、PreparedStatement继承自
Statement、CallableStatement继承自PreparedStatement。  
3.Statement接口提供了执行语句和获取结果的基本方法;  
PreparedStatement接口添加了处理 IN 参数的方法;  
CallableStatement接口添加了处理 OUT 参数的方法。  
4.  a.Statement:  普通的不带参的查询SQL;支持批量更新,批量删除;  
	b.PreparedStatement:  可变参数的SQL,编译一次,执行多次,效率高;  
	安全性好,有效防止Sql注入等问题;  支持批量更新,批量删除;  
	c.CallableStatement:  继承自PreparedStatement,支持带参数的SQL操		作;  
支持调用存储过程,提供了对输出和输入/输出参数(INOUT)的支持;  
Statement每次执行sql语句,数据库都要执行sql语句的编译 ,  
最好用于仅执行一次查询并返回结果的情形,效率高于PreparedStatement。  
PreparedStatement是预编译的,使用PreparedStatement有几个好处  
5. 在执行可变参数的一条SQL时,PreparedStatement比Statement的效率高,因为DBMS预编译一条SQL当然会比多次编译一条SQL的效率要高。  
6. 安全性好,有效防止Sql注入等问题。  
7.  对于多次重复执行的语句,使用PreparedStament效率会更高一点,并且在这种情况下也比较适合使用batch;  
8.  代码的可读性和可维护性。

11. 下面有关SPRING的事务传播特性,说法错误的是?

A、PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行
B、PROPAGATION_REQUIRED:支持当前事务,如果当前没有事务,就抛出异常
C、PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起
D、PROPAGATION_NESTED:支持当前事务,新增Savepoint点,与当前事务同步提交或回滚

本题选B
事务属性的种类: 传播行为、隔离级别、只读和事务超时

a) 传播行为定义了被调用方法的事务边界。

传播行为意义
PROPERGATION_MANDATORY表示方法必须运行在一个事务中,如果当前事务不存在,就抛出异常
PROPAGATION_NESTED表示如果当前事务存在,则方法应该运行在一个嵌套事务中。否则,它看起来和 PROPAGATION_REQUIRED 看起来没什么俩样
PROPAGATION_NEVER表示方法不能运行在一个事务中,否则抛出异常
PROPAGATION_NOT_SUPPORTED表示方法不能运行在一个事务中,如果当前存在一个事务,则该方法将被挂起
PROPAGATION_REQUIRED表示当前方法必须运行在一个事务中,如果当前存在一个事务,那么该方法运行在这个事务中,否则,将创建一个新的事务
PROPAGATION_REQUIRES_NEW表示当前方法必须运行在自己的事务中,如果当前存在一个事务,那么这个事务将在该方法运行期间被挂起
PROPAGATION_SUPPORTS表示当前方法不需要运行在一个是事务中,但如果有一个事务已经存在,该方法也可以运行在这个事务中

b) 隔离级别

在操作数据时可能带来 3 个副作用,分别是脏读、不可重复读、幻读。为了避免这 3 中副作用的发生,在标准的 SQL 语句中定义了 4 种隔离级别,分别是未提交读、已提交读、可重复读、可序列化。而在 spring 事务中提供了 5 种隔离级别来对应在 SQL 中定义的 4 种隔离级别,如下:

隔离级别意义
ISOLATION_DEFAULT使用后端数据库默认的隔离级别
ISOLATION_READ_UNCOMMITTED允许读取未提交的数据(对应未提交读),可能导致脏读、不可重复读、幻读
ISOLATION_READ_COMMITTED允许在一个事务中读取另一个已经提交的事务中的数据(对应已提交读)。可以避免脏读,但是无法避免不可重复读和幻读
ISOLATION_REPEATABLE_READ一个事务不可能更新由另一个事务修改但尚未提交(回滚)的数据(对应可重复读)。可以避免脏读和不可重复读,但无法避免幻读
ISOLATION_SERIALIZABLE这种隔离级别是所有的事务都在一个执行队列中,依次顺序执行,而不是并行(对应可序列化)。可以避免脏读、不可重复读、幻读。但是这种隔离级别效率很低,因此,除非必须,否则不建议使用。

c) 只读

如果在一个事务中所有关于数据库的操作都是只读的,也就是说,这些操作只读取数据库中的数据,而并不更新数据,那么应将事务设为只读模式( READ_ONLY_MARKER ) , 这样更有利于数据库进行优化 。

因为只读的优化措施是事务启动后由数据库实施的,因此,只有将那些具有可能启动新事务的传播行为 (PROPAGATION_NESTED 、 PROPAGATION_REQUIRED 、 PROPAGATION_REQUIRED_NEW) 的方法的事务标记成只读才有意义。

如果使用 Hibernate 作为持久化机制,那么将事务标记为只读后,会将 Hibernate 的 flush 模式设置为 FULSH_NEVER, 以告诉 Hibernate 避免和数据库之间进行不必要的同步,并将所有更新延迟到事务结束。

d) 事务超时

如果一个事务长时间运行,这时为了尽量避免浪费系统资源,应为这个事务设置一个有效时间,使其等待数秒后自动回滚。与设

置“只读”属性一样,事务有效属性也需要给那些具有可能启动新事物的传播行为的方法的事务标记成只读才有意义。

12. 如何获取ServletContext设置的参数值?

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

本题选B
getParameter()是获取POST/GET传递的参数值;
getInitParameter获取Tomcat的server.xml中设置Context的初始化参数
getAttribute()是获取对象容器中的数据值;
getRequestDispatcher是请求转发。

13. 单例模式中,两个基本要点是

A、构造函数私有
B、静态工厂方法
C、以上都不对
D、唯一实例

本题选A、D

什么是单例模式?
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

单例模式有分为饿汉式和懒汉式

特点:

1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
应用实例:

1、一个班级只有一个班主任。
2、Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。
3、一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。
优点:

1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例)。
2、避免对资源的多重占用(比如写文件操作)。
缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

使用场景:

1、要求生产唯一序列号。
2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
注意事项:getInstance() 方法中需要使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成 instance 被多次实例化。

实例
单例模式-饿汉式
代码

//单例模式-饿汉式

public class SingletonDemo {
    public static void main(String[] args) {
        //编译错误:无法实例化
        // Singleton singleton = new Singleton();

        //正确获取对象的方法
        Singleton singleton = Singleton.getINSTANCE();
        singleton.hello();
    }
}

class Singleton{
    //创建一个本身对象
    private static final Singleton INSTANCE = new Singleton();

    //让构造方法为private,这样该类就不会被实例化
    private Singleton(){}

    //创建一个获取对象的方法
    public static Singleton getINSTANCE() {
        return INSTANCE;
    }

    public void hello(){
        System.out.println("Hello World! ——单例模式-饿汉式");
    }
}

结果

Hello World! ——单例模式-饿汉式

单例模式-懒汉式(线程不安全版)
这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。

代码

//单例模式-懒汉式
public class SingletonDemo2 {
    public static void main(String[] args) {
        Singleton2 singleton = Singleton2.getInstance();
        singleton.hello();
 
    }
 
}
 
class Singleton2{
    private static Singleton2 instance;
    private Singleton2(){}
 
    public static Singleton2 getInstance() {
        if (instance == null){
            instance = new Singleton2();
        }
        return instance;
    }
 
    public void hello(){
        System.out.println("Hello World! ——单例模式-懒汉式");
    }
}

结果

Hello World! ——单例模式-懒汉式

单例模式-懒汉式(线程安全版)
描述:这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。
优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)。

代码

//单例模式-懒汉式
public class SingletonDemo3 {
    public static void main(String[] args) {
        Singleton3 singleton = Singleton3.getInstance();
        singleton.hello();
 
    }
 
}
 
class Singleton3{
    private static Singleton3 instance;
    private Singleton3(){}
 
    public synchronized static Singleton3 getInstance() {
        if (instance == null){
            instance = new Singleton3();
        }
        return instance;
    }
 
    public void hello(){
        System.out.println("Hello World! ——单例模式-懒汉式");
    }
}

结果

Hello World! ——单例模式-懒汉式

14. Java7特性中,abstract class和interface有什么区别。

A、抽象类可以有构造方法,接口中不能有构造方法
B、抽象类中可以有普通成员变量,接口中没有普通成员变量
C、抽象类中不可以包含静态方法,接口中可以包含静态方法
D、一个类可以实现多个接口,但只能继承一个抽象类。

本题选A、B、D

含有abstract修饰符的class即为抽象类,abstract类不能创建的实例对象。含有abstract方法的类必须定义为abstract class,abstract class类中的方法不必是抽象的。abstract class类中定义抽象方法必须在具体(Concrete)子类中实现,所以,不能有抽象构造方法或抽象静态方法。如果的子类没有实现抽象父类中的所有抽象方法,那么子类也必须定义为abstract类型。  
接口(interface)可以说成是抽象类的一种特例,接口中的所有方法都必须是抽象的。接口中的方法定义默认为public abstract类型,接口中的成员变量类型默认为public static final。  
下面比较一下两者的语法区别:  
   1.抽象类可以有构造方法,接口中不能有构造方法。  
   2.抽象类中可以有普通成员变量,接口中没有普通成员变量  
   3.抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。  
   4. 抽象类中的抽象方法的访问类型可以是public,protected和(默认类型,虽然  eclipse下不报错,但应该也不行),但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。  
   5. 抽象类中可以包含静态方法,接口中不能包含静态方法  
   6. 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。  
   7. 一个类可以实现多个接口,但只能继承一个抽象类。  

下面接着再说说两者在应用上的区别:
接口更多的是在系统架构设计方法发挥作用,主要用于定义模块之间的通信契约。而抽象类在代码实现方面发挥作用,可以实现代码的重用,
例如,模板方法设计模式是抽象类的一个典型应用,假设某个项目的所有Servlet类都要用相同的方式进行权限判断、记录访问日志和处理异常,那么就可以定义一个抽象的基类,让所有的Servlet都继承这个抽象基类,在抽象基类的service方法中完成权限判断、记录访问日志和处理异常的代码,在各个子类中只是完成各自的业务逻辑代码,伪代码如下:

package com.lei;
 
import java.io.IOException;
 
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
public abstract class BaseServlet extends HttpServlet {
 
 /**
 * serialVersionUID属性概述
 * 
 */
 private static final long serialVersionUID = 1L;
 
 public final void service(HttpServletRequest request,
 HttpServletResponse response) throws IOException, ServletException {
 // 记录访问日志
 // 进行权限判断
 if (true)// if条件里写的是“具有权限”
 {
 try {
 doService(request, response);
 } catch (IOException e) {
 // 记录异常信息
 }
 }
 
 }
 
 protected abstract void doService(HttpServletRequest request,
 HttpServletResponse response) throws IOException, ServletException;
}
 

实现类如下:

package com.lei;
 
import java.io.IOException;
 
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
public class MyServlet extends BaseServlet{
 
 /**
 * serialVersionUID属性概述
 * 
 */
 private static final long serialVersionUID = 1L;
 
 @Override
 protected void doService(HttpServletRequest request,
 HttpServletResponse response) throws IOException, ServletException {
 // TODO Auto-generated method stub
  
 }
 
}

父类方法中间的某段代码不确定,留给子类干,就用模板方法设计模式。
备注:这道题的思路是先从总体解释抽象类和接口的基本概念,然后再比较两者的语法细节,最后再说两者的应用区别。
比较两者语法细节区别的条理是:先从一个类中的构造方法、普通成员变量和方法(包括抽象方法),静态变量和方法,继承性等6个方面逐一去比较回答,接着从第三者继承的角度的回答,特别是最后用了一个典型的例子来展现自己深厚的技术功底。

15. 下列哪些情况下会导致线程中断或停止运行( )

A、InterruptedException异常被捕获
B、线程调用了wait方法
C、当前线程创建了一个新的线程
D、高优先级线程进入就绪状态

本题选A、B
A选项正确,Java中一般通过interrupt方法中断线程
B选项正确,线程使用了wait方法,会强行打断当前操作,进入阻塞(暂停)状态,然后需要notify方法或notifyAll方法才能进入就绪状态
C选项错误,新创建的线程不会抢占时间片,只有等当前线程把时间片用完,其他线程才有资格拿到时间片去执行。
D选项错误,调度算法未必是剥夺式的,而准备就绪但是还没有获得CPU,它的权限更高只能说明它获得CPU被执行的几率更大而已

16. true、false、null、sizeof、goto、synchronized 哪些是Java关键字?

A、true
B、false
C、null
D、sizeof
E、goto
F、synchronized

本题选E、F
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值