Java基础面试题附答案 61-80(四)

面试题 61-80(四)

61、编写多线程程序有几种实现方式?

62、synchronized 关键字的用法?

63、举例说明同步和异步。

64、启动一个线程是调用 run()还是 start()方法?

65、什么是线程池(thread pool)?

66、线程的基本状态以及状态之间的关系?

67、简述 synchronized 和 java.util.concurrent.locks.Lock 的异同?

68、Java 中如何实现序列化,有什么意义?

69、Java 中有几种类型的流?

70、写一个方法,输入一个文件名和一个字符串,统计这个字符串在这个文件中出现的次数。

71、如何用 Java 代码列出一个目录下所有的文件?

72、Class.forName的作用?为什么要用?

73、XML 文档定义有几种形式?它们之间有何本质区别?解析 XML文档有哪几种方式?

74、你在项目中哪些地方用到了 XML?

75、阐述 JDBC 操作数据库的步骤。

76、Statement 和 PreparedStatement 有什么区别?哪个性能更好?

77、使用 JDBC 操作数据库时,如何提升读取数据的性能?如何提升更新数据的性能?

78、在进行数据库编程时,连接池有什么作用?

79、什么是 DAO 模式?

80、事务的 ACID 是指什么?


61、编写多线程程序有几种实现方式?

Java 5 以前实现多线程有两种实现方法:一种是继承 Thread 类;另一种是实现Runnable 接口。两种方式都要通过重写 run()方法来定义线程的行为,推荐使用后者,因为 Java 中的继承是单继承,一个类有一个父类,如果继承了 Thread 类就无法再继承其他类了,显然使用 Runnable 接口更为灵活。

补充:Java 5 以后创建线程还有第三种方式:实现 Callable 接口,该接口中的 call 方法可以在线程执行结束时产生一个返回值。

62、synchronized 关键字的用法?

synchronized 关键字可以将对象或者方法标记为同步,以实现对对象和方法的互斥访问,可以用 synchronized(对象) { … }定义同步代码块,或者在声明方法时将 synchronized 作为方法的修饰符。

63、举例说明同步和异步。

如果系统中存在临界资源(资源数量少于竞争资源的线程数量的资源),例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就必须进行同步存取(数据库操作中的排他锁就是最好的例子)。当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。事实上,所谓的同步就是指阻塞式操作,而异步就是非阻塞式操作。

64、启动一个线程是调用 run()还是 start()方法?

启动一个线程是调用 start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由 JVM 调度并执行,这并不意味着线程就会立即运行。run()方法是线程启动后要进行回调(callback)的方法。

65、什么是线程池(thread pool)?

在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。在 Java 中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁,这就是”池化资源”技术产生的原因。线程池顾名思义就是事先创建若干个可执行的线程放入一个池(容器)中,需要的时候从池中获取线程不用自行创建,使用完毕不需要销毁线程而是放回池中,从而减少创建和销毁线程对象的开销。Java 5+中的 Executor 接口定义一个执行线程的工具。它的子类型即线程池接口是 ExecutorService。要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,因此在工具类 Executors 面提供了一些静态工厂方法,生成一些常用的线程池,如下所示:
(1)newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

(2)newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

(3) newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60 秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说 JVM)能够创建的最大线程大小。

(4)newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

如果希望在服务器上使用线程池,强烈建议使用 newFixedThreadPool方法来创建线程池,这样能获得更好的性能。

66、线程的基本状态以及状态之间的关系?

说明:其中 Running 表示运行状态,Runnable 表示就绪状态(万事俱备,只欠CPU),Blocked 表示阻塞状态,阻塞状态又有多种情况,可能是因为调用 wait()方法进入等待池,也可能是执行同步方法或同步代码块进入等锁池,或者是调用了 sleep()方法或 join()方法等待休眠或其他线程结束,或是因为发生了 I/O 中断。

67、简述 synchronized 和 java.util.concurrent.locks.Lock 的异同?

Lock 是 Java 5 以后引入的新的 API,和关键字 synchronized 相比主要相同点:Lock 能完成 synchronized 所实现的所有功能;主要不同点:Lock 有比synchronized 更精确的线程语义和更好的性能,而且不强制性的要求一定要获得锁。synchronized 会自动释放锁,而 Lock 一定要求程序员手工释放,并且最好在 finally 块中释放(这是释放外部资源的最好的地方)。

68、Java 中如何实现序列化,有什么意义?

序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决对象流读写操作时可能引发的问题(如果不进行序列化可能会存在数据乱序的问题)。要实现序列化,需要让一个类实现 Serializable 接口,该接口是一个标识性接口,标注该类对象是可被序列化的,然后使用一个输出流来构造一个对象输出流并通过 writeObject(Object)方法就可以将实现对象写出(即保存其状态);如果需要反序列化则可以用一个输入流建立对象输入流,然后通过 readObject 方法从流中读取对象。序列化除了能够实现对象的持久化之外,还能够用于对象的深度克隆

69、Java 中有几种类型的流?

字节流和字符流。字节流继承于 InputStream、OutputStream,字符流继承于Reader、Writer。在 java.io 包中还有许多其他的流,主要是为了提高性能和使用方便。关于 Java 的 I/O 需要注意的有两点:一是两种对称性(输入和输出的对称性,字节和字符的对称性);二是两种设计模式(适配器模式和装潢模式)。

70、写一个方法,输入一个文件名和一个字符串,统计这个字符串在这个文件中出现的次数。

import java.io.BufferedReader;
import java.io.FileReader;
public final class MyUtil {
    // 工具类中的方法都是静态方式访问的因此将构造器私有不允许创建对象(绝对好习惯)
    private MyUtil() {
        throw new AssertionError();
    }
    /**
    * 统计给定文件中给定字符串的出现次数
    *
    * @param filename 文件名
    * @param word 字符串
    * @return 字符串在文件中出现的次数
    */
    public static int countWordInFile(String filename, String word) {
        int counter = 0;
        try (FileReader fr = new FileReader(filename)) {
            try (BufferedReader br = new BufferedReader(fr)) {
                String line = null;
                while ((line = br.readLine()) != null) {
                    int index = -1;
                    while (line.length() >= word.length() && (index = line.indexOf(word)) >= 0) {
                        counter++;
                        line = line.substring(index + word.length());
                    }
                }
            }
        }catch (Exception ex) {
            ex.printStackTrace();
        }
        return counter;
    }
}

71、如何用 Java 代码列出一个目录下所有的文件?

如果只要求列出当前文件夹下的文件,代码如下所示:

import java.io.File;
class Test12 {
    public static void main(String[] args) {
        File f = new File("/Users/Hao/Downloads");
        for (File temp : f.listFiles()) {
            if(temp.isFile()) {
                System.out.println(temp.getName());
            }
        }
    }
}

如果需要对文件夹继续展开,代码如下所示:

import java.io.File;
class Test12 {
    public static void main(String[] args) {
        showDirectory(new File("/Users/Hao/Downloads"));
    }
    public static void showDirectory(File f) {
        _walkDirectory(f, 0);
    }
    private static void _walkDirectory(File f, int level) {
        if(f.isDirectory()) {
            for (File temp : f.listFiles()) {
                _walkDirectory(temp, level + 1);
            }
        } else {
            for (int i = 0; i < level - 1; i++) {
                System.out.print("t");
            }
            System.out.println(f.getName());
        }
    }
}

72、 Class.forName的作用?为什么要用?

Class.forName常见的场景是在数据库驱动初始化的时候调用。

Class.forName本身的意义是加载类到JVM中。 一旦一个类被加载到JVM中,它的静态属性就会被初始化,在初始化的过程中就会执行相关代码,从而达到"加载驱动的效果"

73、XML 文档定义有几种形式?它们之间有何本质区别?解析 XML文档有哪几种方式?

XML 文档定义分为 DTD 和 Schema 两种形式,二者都是对 XML 语法的约束,其本质区别在于 Schema 本身也是一个 XML 文件,可以被 XML 解析器解析,而且可以为 XML 承载的数据定义类型,约束能力较之 DTD 更强大。

对 XML 的解析主要有 DOM(文档对象模型,Document Object Model)、SAX(Simple API forXML)和 StAX(Java 6 中引入的新的解析 XML 的方式,Streaming API for XML),其中 DOM 处理大型文件时其性能下降的非常厉害,这个问题是由 DOM 树结构占用的内存较多造成的,而且 DOM 解析方式必须在解析文件之前把整个文档装入内存,适合对 XML 的随机访问(典型的用空间换取时间的策略);SAX 是事件驱动型的 XML 解析方式,它顺序读取 XML 文件,不需要一次全部装载整个文件。当遇到像文件开头,文档结束,或者标签开头与标签结束时,它会触发一个事件,用户通过事件回调代码来处理 XML 文件,适合对 XML 的顺序访问;顾名思义,StAX 把重点放在流上,实际上 StAX 与其他解析方式的本质区别就在于应用程序能够把 XML 作为一个事件流来处理。将 XML 作为一组事件来处理的想法并不新颖( SAX 就是这样做的),但不同之处在于 StAX 允许应用程序代码把这些事件逐个拉出来,而不用提供在解析器方便时从解析器中接收事件的处理程序。

74、你在项目中哪些地方用到了 XML?

XML 的主要作用有两个方面:数据交换和信息配置。

在做数据交换时,XML 将数据用标签组装成起来,然后压缩打包加密后通过网络传送给接收者,接收解密与解压缩后再从 XML 文件中还原相关信息进行处理,XML 曾经是异构系统间交换数据的事实标准,但此项功能几乎已经被被JSON(JavaScript Object Notation)取而代之。当然,目前很多软件仍然使用 XML 来存储配置信息,我们在很多项目中通常也会将作为配置信息的硬代码写在 XML 文件中,Java 的很多框架也是这么做的,而且这些框架都选择了 dom4j 作为处理 XML 的工具,因为 Sun 公司的官方API 实在不怎么好用。补充:现在有很多时髦的软件(如 Sublime)已经开始将配置文件书写成 JSON格式,我们已经强烈的感受到 XML 的另一项功能也将逐渐被业界抛弃。

75、阐述 JDBC 操作数据库的步骤。

下面的代码以连接本机的 Oracle 数据库为例,演示 JDBC 操作数据库的步骤。
(1) 加载驱动。

Class.forName("oracle.jdbc.driver.OracleDriver");

(2) 创建连接。

Connection con = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl",
"scott", "tiger");

(3) 创建语句。

PreparedStatement ps = con.prepareStatement("select * from emp
where sal between ? and ?");
ps.setint(1, 1000);
ps.setint(2, 3000);

(4)执行语句。

ResultSet rs = ps.executeQuery();

(5)处理结果。

while(rs.next()) {
    System.out.println(rs.getint("empno") + " - " +
    rs.getString("ename"));
}

(6) 关闭资源。

finally {
    if(con != null) {
        try {
            con.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

提示:关闭外部资源的顺序应该和打开的顺序相反,也就是说先关闭 ResultSet、再关闭 Statement、在关闭 Connection。上面的代码只关闭了 Connection(连接),虽然通常情况下在关闭连接时,连接上创建的语句和打开的游标也会关闭,但不能保证总是如此,因此应该按照刚才说的顺序分别关闭。此外,第一步加载驱动在 JDBC 4.0 中是可以省略的(自动从类路径中加载驱动),但是我们建议保留。

76、Statement 和 PreparedStatement 有什么区别?哪个性能更好?

与 Statement 相比,①PreparedStatement 接口代表预编译的语句,它主要的优势在于可以减少 SQL 的编译错误并增加 SQL 的安全性(减少 SQL 注射攻击的可能性);②PreparedStatement 中的 SQL 语句是可以带参数的,避免了用字符串连接拼接 SQL 语句的麻烦和不安全;③当批量处理 SQL 或频繁执行相同的查询时,PreparedStatement 有明显的性能上的优势,由于数据库可以将编译优化后的SQL 语句缓存起来,下次执行相同结构的语句时就会很快(不用再次编译和生成执行计划)。

补充:为了提供对存储过程的调用,JDBC API 中还提供了 CallableStatement 接口。存储过程(Stored Procedure)是数据库中一组为了完成特定功能的 SQL 语句的集合,经编译后存储在数据库中,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。虽然调用存储过程会在网络开销、安全性、性能上获得很多好处,但是存在如果底层数据库发生迁移时就会有很多麻烦,因为每种数据库的存储过程在书写上存在不少的差别。

77、使用 JDBC 操作数据库时,如何提升读取数据的性能?如何提升更新数据的性能?

要提升读取数据的性能,可以指定通过结果集(ResultSet)对象的 setFetchSize()方法指定每次抓取的记录数(典型的空间换时间策略);要提升更新数据的性能可以使用 PreparedStatement 语句构建批处理,将若干 SQL 语句置于一个批处理中执行。

78、在进行数据库编程时,连接池有什么作用?

由于创建连接和释放连接都有很大的开销(尤其是数据库服务器不在本地时,每次建立连接都需要进行 TCP 的三次握手,释放连接需要进行 TCP 四次握手,造成的开销是不可忽视的),为了提升系统访问数据库的性能,可以事先创建若干连接置于连接池中,需要时直接从连接池获取,使用结束时归还连接池而不必关闭连接,从而避免频繁创建和释放连接所造成的开销,这是典型的用空间换取时间的策略(浪费了空间存储连接,但节省了创建和释放连接的时间)。池化技术在Java 开发中是很常见的,在使用线程时创建线程池的道理与此相同。基于 Java 的开源数据库连接池主要有:C3P0、Proxool、DBCP、BoneCP、Druid 等。

补充:在计算机系统中时间和空间是不可调和的矛盾,理解这一点对设计满足性能要求的算法是至关重要的。大型网站性能优化的一个关键就是使用缓存,而缓存跟上面讲的连接池道理非常类似,也是使用空间换时间的策略。可以将热点数据置于缓存中,当用户查询这些数据时可以直接从缓存中得到,这无论如何也快过去数据库中查询。当然,缓存的置换策略等也会对系统性能产生重要影响,对于这个问题的讨论已经超出了这里要阐述的范围。

79、什么是 DAO 模式?

DAO(Data Access Object)顾名思义是一个为数据库或其他持久化机制提供了抽象接口的对象,在不暴露底层持久化方案实现细节的前提下提供了各种数据访问操作。在实际的开发中,应该将所有对数据源的访问操作进行抽象化后封装在一个公共 API 中。用程序设计语言来说,就是建立一个接口,接口中定义了此应用程序中将会用到的所有事务方法。在这个应用程序中,当需要和数据源进行交互的时候则使用这个接口,并且编写一个单独的类来实现这个接口,在逻辑上该类对应一个特定的数据存储。DAO 模式实际上包含了两个模式,一是 DataAccessor(数据访问器),二是 Data Object(数据对象),前者要解决如何访问数据的问题,而后者要解决的是如何用对象封装数据。

80、事务的 ACID 是指什么?

(1)原子性(Atomic):事务中各项操作,要么全做要么全不做,任何一项操作的失败都会导致整个事务的失败;
(2)一致性(Consistent):事务结束后系统状态是一致的;
(3)隔离性(Isolated):并发执行的事务彼此无法看到对方的中间状态;
(4)持久性(Durable):事务完成后所做的改动都会被持久化,即使发生灾难性的失败。通过日志和同步备份可以在故障发生后重建数据。

补充:关于事务,在面试中被问到的概率是很高的,可以问的问题也是很多的。首先需要知道的是,只有存在并发数据访问时才需要事务。当多个事务访问同一数据时,可能会存在 5 类问题,包括 3 类数据读取问题(脏读、不可重复读和幻读)和 2 类数据更新问题(第 1 类丢失更新和第 2 类丢失更新)。

  • 脏读(Dirty Read):A 事务读取 B 事务尚未提交的数据并在此基础上操作,而 B事务执行回滚,那么 A 读取到的数据就是脏数据。

  • 不可重复读(Unrepeatable Read):事务 A 重新读取前面读取过的数据,发现该数据已经被另一个已提交的事务 B 修改过了。

  • 幻读(Phantom Read):事务 A 重新执行一个查询,返回一系列符合查询条件的行,发现其中插入了被事务 B 提交的行。

  • 第 1 类丢失更新:事务 A 撤销时,把已经提交的事务 B 的更新数据覆盖了。

  • 第 2 类丢失更新:事务 A 覆盖事务 B 已经提交的数据,造成事务 B 所做的操作丢失。

数据并发访问所产生的问题,在有些场景下可能是允许的,但是有些场景下可能就是致命的,数据库通常会通过锁机制来解决数据并发访问问题,按锁定对象不同可以分为表级锁和行级锁;按并发事务锁定关系可以分为共享锁和独占锁,具体的内容大家可以自行查阅资料进行了解。直接使用锁是非常麻烦的,为此数据库为用户提供了自动锁机制,只要用户指定会话的事务隔离级别,数据库就会通过分析 SQL 语句然后为事务访问的资源加上合适的锁,此外,数据库还会维护这些锁通过各种手段提高系统的性能,这些对用户来说都是透明的(就是说你不用理解,事实上我确实也不知道)。ANSI/ISOSQL 92 标准定义了 4 个等级的事务隔离级别,如下表所示:

需要说明的是,事务隔离级别和数据访问的并发性是对立的,事务隔离级别越高并发性就越差。所以要根据具体的应用来确定合适的事务隔离级别,这个地方没有万能的原则。


上一篇:Java基础面试题附答案 41-60(三)

猜你喜欢

转载自www.cnblogs.com/newRyan/p/12599654.html