四时宝库

程序员的知识宝库

Java基础总结,超级全的面试题(二)

29. List、Map、Set 三个接口存取元素时,各有什么特点?

List以特定索引来存取元素,可以有重复元素。 Set 不能存放重复元素(用对象的**equals()*方法来区分元素是否重复)。 **Map**保存*键值对( key-value)映射,映射关系可以是一对一或多对一。(Map不支持一对多,但是可以用Map<Integer, List>这种格式来达到一对多的系,多对多类似)

Set和Map容器都有基于哈希存储和排序树的两种实现版本,基于哈希存储的版本理论存取时间复杂度为O(1),而基于排序树版本的实现在插入或删除元素时会按照元素或元素的键( key)构成排序树从而达到排序和去重的效果。

30. ArrayList、Vector、LinkedList 的存储性能和特性【***】

ArrayList和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引查询数据快而插入数据慢, Vector 中的方法由于添加了synchronized 修饰,因此Vector 是线程安全的容器,但性能上较ArrayList 差,因此已经是Java 中的遗留容器。 LinkedList 使用双向链表实现存储( 将内存中零散的内存单元通过附加的引用关联起来,形成一个可以按序号索引的线性结构,这种链式存储方式与数组的连续存储方式相比, 内存的利用率更高) ,按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。 Vector 属于遗留容器(Java 早期的版本中提供的容器, 除此之外,Hashtable、Dictionary、BitSet、Stack、Properties都是遗留容器),已经不推荐使用, 但是由于**ArrayList和LinkedListed都是非线程安全的**, 如果遇到多个线程操作同一个容器的场景,则可以通过工具Collections 中的synchronizedList 方法将其转换成线程安全的容器后再使用( 这是对装潢模式的应用, 将已有对象传入另一个类的构造器中创建新的对象来增强实现)。

31. Collection 和Collections 的区别,Collections 工具类中的sort()方法如何比较元素,TreeMap和TreeSet 在排序时如何比较元素

Collection是一个接口, 它是Set、List 等容器的父接口。 Collections是个一个工具类,提供了一系列的静态方法来辅助容器操作,这些方法包括对容器的搜索、排序、线程安全化等等。

Collections工具类的sort方法有两种重载的形式,第一种要求传入 的待排序容器中存放的对象比较实现Comparable接口以实现元素的比较;第二种不强制性的要求容器中的元素必须可比较, 但是要求传入第二个参数, 参数是Comparator接口的子类型(需要重写compare 方法实现元素的比较),相当于一个临时定义的排序规则,其实就是通过接口注入比较元素大小的算法, 也是对回调模式的应用( Java 中对函数式编程的支持)。

TreeSet要求存放的对象所属的类必须实现Comparable接口,该接口提供了比较元素的compareTo()方法,当插入元素时会回调该方法比较元素的大小。 TreeMap要求存放的键值对映射的键必须实现Comparable 接口从而根据键对元素进行排序。

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

1)XML文档定义分为DTD和Schema两种形式,二者都是对XML语法的约束。 2)DTD和Schema两种形式的本质区别在于Schema本身也是一个XML文件,可以被XML 解析器解析,而且可以为XML 承载的数据定义类型,约束能力较之DTD更强大。 3)对XML的解析主要有DOM(文档对象模型,Document Object Model)、SAX( Simple API forXML)和StAX(JDK1.6 中引入的新的解析XML的方式,Streaming API for XML)。

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

33. XML的主要作用

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

34. JDBC操作数据库的步骤(MySQL)

//1. 加载JDBC驱动程序(加载MySQL驱动类)
Class.forName("com.mysql.jdbc.Driver");
//2. 提供JDBC连接的URL来创建连接
 //databaseName数据库名称,useUnicode=true:表示使用Unicode字符集,characterEncoding=UF-8字符编码方式utf-8, udrtnsmr和password是mysql连接用户名和密码
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/databaseName?useUnicode=true&characterEncoding=UF-8;",username,password);
 // 也可以将mysql驱动写到DriverManager.getConnection()中

//3. PreparedStatement预编译sql
String sql = "select * from dept where id =  ? and name = ?";
PreparedStatement ps = con.prepareStatement(sql);
ps.setInt(1, 10);
ps.setInt(2, "研究员");
//4. 执行SQL语句
ResultSet rs = ps.executeQuery();
//5. 处理结果
while(rs.next()) {
 System.out.println(rs.getInt("id"));
 System.out.println(rs.getInt("name"));
}
//6. 在finally里面进行释放资源(关闭外部资源的顺序应该和打开的顺序相反)
if(rs !=null){
 rs.close;
}
if(ps != null){
 ps.close();
}
if(con != null) {
 con.close();
}
1234567891011121314151617181920212223242526272829

35. Statement和PreparedStatement哪个性能更好?

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

36. JDBC 能否处理Blob 和Clob

Blob是指二进制大对象(Binary Large Object) Clob是指大字符对象(Character Large Objec) Blob是为存储大的二进制数据而设计的,而Clob 是为存储大的文本数据而设计的。JDBC 的PreparedStatement和 ResultSet都提供了相应的方法来支持Blob和Clob操作(流的操作)。

37. 数据库编程时,连接池的优势

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

38. Java分层,Dao模式是什么?

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

39. 事务的特性ACID,事务隔离级别,事务的并发问题【****】

  1. 事务具有ACID四个特性:

1)原子性(Atomicity):事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生 2)一致性(Consistency):事务在完成后数据的完整性必须保持一致 3)隔离性(Isolation):多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之间的数据要相互隔离 4)持久性(Durability):一个事务一旦被提交,它对数据库中数据的改变应该是永久性的,即使数据库发生故障也不应该对其有任何影响

如果整个事务执行过程中,有任何一个地方出现异常/错误,那么都会进行事务回滚,回滚之后数据的状态将和事务执行之前完全一致。

  1. 事务的隔离级别:

数据库为用户提供了自动锁机制,只要用户指定会话的事务隔离级别, 数据库就会通过分析SQL语句然后为事务访问的资源加上合适的锁。 隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义有五个表示隔离级别的常量(用于解决并发问题)一般情况下使用中间两种就行。

TransactionDefinition 接口事务隔离级别描述READ_UNCOMMITTED该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读,不可重复读和幻读,因此很少使用该隔离级别。READ_COMMITTED系统默认值,该隔离级别表示一个事务只能读取另一个事务已经提交的数据。可以防止脏读,是大多数情况下的推荐值。REPEATABLE_READ该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。该级别可以防止脏读和不可重复读SERIALIZABLE所有的事务依次逐个执行,事务之间就完全不可能产生干扰。该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

隔离级别(√:允许出现 ×:不允许出现)脏读不可重复读幻读第一类丢失更新第二类丢失更新READ_UNCOMMITTED√√√×√READ_COMMITTED×√√×√REPEATABLE_READ××√××SERIALIZABLE×××××

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

  1. 事务的并发问题

首先要知道,只有存在并发数据访问时才需要事务(提交/回滚就结束事务),当多个事务访问同一数据时,可能会存在5类并发问题,包括3 类数据读取问题( 脏读、不可重复读和幻 读和2类数据更新问题(第1 类丢失更新和第2 类丢失更新) 3类数据读取问题

1)脏读(Dirty Read): 一个事务读到了另一个事务的还没有提交数据. (比如A事务读取B事务尚未提交的数据并在此基础上操作,而B事务执行回滚,那么A 读取到的数据就是脏数据。)(很严重的行为,必须处理,不然可能有很大的影响,比如转账事务) 2)不可重复读(Unrepeatable Read): 一个事务中多次读到的数据不一致,一个事务读到了另一个事务修改后的数据。(不可重复读,保证数据修改后,不会出现两次一样的数据) 3) 幻读(虚读Phantom Read):一个事务读到了另一个事务insert提交的数据。(比如事务A 重新执行一个查询,返回一系列符合查询条件的行,发现其中插入了被事务B 提交的行)(不可能出现在MySQL中,只会出现在Oracle中)

两类丢失更新问题

1)第一类丢失更新: ??事务A撤销时, 把已经提交的事务B的更新数据覆盖了(比如 取款事务A开启事务查询余额1000元,转账事务B开启事务转账100给A,A取出100,提交事务之后,再查询余额还是1000元) 2)事务A覆盖事务 已经提交的数据,造成事务B 所做的操作丢失(比如:取款事务A和转账B先后开启事务,先后查询余额都是1000元,取款事务A取出100,余额变成900,提交事务,但是此时转账事务B存入100,将余额修改为1100元,提交事务,然后再查询帐户余额就是1100,取款事务A的操作丢失)

JDBC如何进行事务处理:

Connection提供了事务处理的方法,通过调用setAutoCommit(false)可以设置手动提交事务。当事务完成后用commit()显式提交事务;如果在事务处理过程中发生异常则通过rollback()进行事务回滚。除此之外, 从JDBC 3.0 中还引入了Savepoint( 保存点)的概念,允许通过代码设置保存点并让事务回滚到指定的保存点。

40. 正则表达式是什么,Java中如何支持正则表达式。

在编写处理字符串的程序时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说, 正则表达式就是记录文本规则的代码。正则表达式就是在进行字符串匹配和处理的时候最为强大的工具,绝大多数语言都提供了对正则表达式的支。 ??Java中的String类提供了支持正则表达式操作的方法,包括: matches()、replaceAll()、replaceFirst()、split() 此外,Java 中可以用Pattern类表示正则表达式对象, 它提供了丰富的API 进行各种正则表达式操作。

41. 获取Class对象的三种方法,通过反射创建对象的方法

获取class对象的三种方法:

1)每个类通过class属性获取。【类名.class】 2) 每个对象通过getClass()方法获取。【对象.getClass()】 3)通过**Class.forName(“类的完整名”)**获取,需要捕获异常。【Class.forName("类的完整名")】

第一种方式,类名.class 不会将类加载到内存,第三种Class.forName()会将类加载到内存,对象.getClass()(创建对象了)会加载到内存中

通过反射创建对象的两种方法:

1)通过类对象(class)调用1newInstance()1方法,例如创建String对象: String.class.newInstance() 2)通过类对象(class)的getConstructor()getDeclaredConstructor()方法获得构造器(Constructor)对象并调用其newInstance()方法创建对象,例如: ·String.class.getConstructor(String.class).newInstance(“Hello”);·

42. 23种经典设计模式

类型设计模式创建型工厂方法模式(FactoryMethod)、抽象工厂模式(AbstractFactory)、建造者模式(Builder)、原型模式(Prototype)、单例模式(Singleton)结构型适配器模式(Adapter)、桥接模式(Bridge)、组合模式(Composite)、装饰器模式(Decorator)、门面模式(Facade)、享元模式(Flyweight)、代理模式(Proxy)行为型解释器模式(Interpreter)、模板方法模式(TemplateMethod)、责任链模式(ChainofResponsibility)、命令模式(Command)、迭代器模式(Iterator)、调解者模式(Mediator)、备忘录模式(Memento)、观察者模式(Observer)、状态模式(State)、策略模式(Strategy)、访问者模式(Visitor)

单例模式:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。(懒汉式、饿汉式写法) 原型模式:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。 策略模式:定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。 工厂模式:定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。工厂类可以根据条件生成不同的子类实例,这些子类有一个公共的抽象父类并且实现了相同的方法,但是这些方法针对不同的数据进行了不同的操作(多态方法)。当得到子类的实例后,开发人员可以调用基类中的方法而不必考虑到底返回的是哪一个子类的实例。 代理模式:给一个对象提供一个代理对象,并由代理对象控制原对象的引用。实际开发中,按照使用目的的不同,代理可以分为:远程代理、虚拟代理、保护代理、Cache 代理、防火墙代理、同步化代理、智能引用代理。 适配器模式:把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起使用的类能够一起工作。 模板方法模式:提供一个抽象类,将部分逻辑以具体方法或构造器的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法(多态实现),从而实现不同的业务逻辑。 状态模式:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。 装饰者模式:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。 了解其他的可以从这里面看:23种设计模式详解

43. 面向对象的七大设计原则

原则描述开闭原则(OCP)软件实体应当对扩展开放,对修改关闭里氏替换原则(LSP)阐述了有关继承的一些原则(什么时候使用继承),里氏替换原是继承复用的基础,反映了基类与子类之间的关系,是对开闭原则的补充,是对实现抽象化的具体步骤的规范。可以这么理解:子类可以扩展父类的功能,但不能改变父类原有的功能,也就是子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。依赖倒置原则(DIP)核心思想是:要面向接口编程,不要面向实现编程。(抽象层相对稳定,因此以抽象为基础搭建起来的架构要比以细节(实现类)为基础搭建起来的架构要稳定得多)单一职责原则(SRP)提出对象不应该承担太多职责。单一职责同样也适用于方法。一个方法应该尽可能做好一件事情。如果一个方法处理的事情太多,其颗粒度会变得很粗,不利于重用。接口隔离原则(ISP)要求程序员尽量将臃肿庞大的接口拆分成更小的和更具体的接口,让接口中只包含客户感兴趣的方法。接口要小而专,绝不能大而全。迪米特法则(LoD)又叫作最少知识原则(LKP),一个对象应当对其他对象有尽可能少的了解。如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。合成复用原则(CRP)叫组合/聚合复用原则(CARP)要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。

概述: 开闭原则是总纲,它告诉我们要对扩展开放,对修改关闭; 里氏替换原则告诉我们不要破坏继承体系; 依赖倒置原则告诉我们要面向接口编程; 单一职责原则告诉我们实现类要职责单一; 接口隔离原则告诉我们在设计接口的时候要精简单一; 迪米特法则告诉我们要降低耦合度; 合成复用原则告诉我们要优先使用组合或者聚合关系复用,少用继承关系复用。

44. UML概念,常用UML图

UML是统一建模语言( Unified Modeling Language)的缩写,它发表于1997年, 综合了当时已经存在的面向对象的建模语言、方法和过程, 是一个支持模型化和软件系统开发的图形化语言,为软件开发的所有阶段提供模型化和可视化支持。使用UML 可以帮助沟通与交流,辅助应用设计和文档的生成,还能够阐释系统的结构和行为。 常用UML图 用例图(use case diagram)、类图(class diagram)、时序图(sequencediagram)、协作图(collaboration diagram)、状态图(statechart diagram)、活动图(activity diagram)、构件图(component diagram)、部署图(deploymentdiagram) 用例图:用来捕获需求,描述系统的功能,通过该图可以迅速的了解系统的功能模块及其关系 类图:描述类以及类与类之间的关系,通过该图可以快速了解系统 时序图:描述执行特定任务时对象之间的交互关系以及执行顺序, 通过该图可以了解对象能接收的消息也就是说对象能够向外界提供的服务。

45. HashMap实现原理

JDK1.8中,HashMap采用**位桶+链表+红黑树**实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。 HashCode是jdk根据对象的地址或字符串或者数字利用hash算法计算出的int类型的数值。 HashMap实现原理:

首先有一个每个元素都是链表的数组,当添加一个元素(key-value)时,就首先计算元素key的hash值,以此确定插入数组中的位置,但是可能存在同一hash值的元素已经被放在数组同一位置了,这时就添加到同一hash值的元素的后面,他们在数组的同一位置,但是形成了链表,同一各链表上的Hash值是相同的,所以说数组存放的是链表。而当链表长度太长时,链表就转换为红黑树,这样大大提高了查找的效率。 在jdk8中,HashMap处理“碰撞”增加了红黑树这种数据结构,当碰撞结点较少时,采用链表存储,当较大时(>8个),采用红黑树(特点是查询时间是O(logn))存储(有一个阀值控制,大于阀值(8个),将链表存储转换成红黑树存储)

红黑树是一种自平衡二叉查找树

红黑树是每个节点都带有颜色属性的二叉查找树,颜色或红色或黑色。 对于任何有效的红黑树都有以下要求: 1)节点是红色或黑色。 2)根节点是黑色。 3)每个叶节点是黑色的。 4)每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点) 5从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

46. 同步和异步

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

47. Servlet的生命周期

Web容器加载Servlet并将其实例化后,Servlet生命周期开始,容器运行其init()方法进行Servlet的初始化;请求到达时调用Servlet的service方法,service方法会调用与请求对应的doGet或doPost等方法;当服务器关闭会项目被卸载时服务器会将Servlet实例销毁,此时会调用Servlet的destroy方法。

48. get和post请求的区别

1)get请求用来从服务器上获得资源,而post是用来向服务器提交数据 2)get将表单中数据按照name=value的形式,添加到action 所指向的URL 后面,并且两者使用“?”连接,而各个变量之间使用“&”连接;post是将表单中的数据放在HTML头部(header),传递到action所指向URL 3)get传输的数据要受到URL长度限制(1024字节);而post可以传输大量的数据,上传文件只能使用post方式 4)使用get时参数会显示在地址栏上,如果这些数据不是敏感数据,那么可以使用get;对于敏感数据还是应用使用post

49. 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,把HttpRequest和HttpResponse对象作为service方法的参数传给HttpServlet对象; 6)HttpServlet调用HttpRequest的有关方法,获取HTTP请求信息; 7)HttpServlet调用HttpResponse的有关方法,生成响应数据; 8)Servlet容器把HttpServlet的响应结果传给Web客户

50. 什么是Callable 和Future?

Callable 接口类似于Runnable,但是Runnable 不会返回结果,并且无法抛出返回结果的异常,而Callable 功能更强大一些,被线程执行后,可以返回值,这个返回值可以被Future 拿到,也就是说,Future 可以拿到异步执行任务的返回值。可以认为是带有回调的Runnable。 Future 接口表示异步任务,是还没有完成的任务给出的未来结果。所以说Callable用于产生结果,Future 用于获取结果。

JAVA一些题目和Java的一些方法

  1. Math.round(11.5) 等于多少?Math.round(-11.5)等于多少?

Math.round(11.5)的返回值是12, Math.round(-11.5)的返回值是-11。四舍五 入的原理是在参数上加0.5 然后进行下取整。

  1. short s1 = 1; s1 = s1 + 1;有错吗? short s1 = 1; s1 += 1;有错吗?

1)short s1 = 1; s1 = s1 + 1; ??编译错误。由于1 是int 类型,因此s1+1运算结果也是int型, 需要强制转换类型才能赋值给short 型。 2)short s1 = 1; s1 += 1; ??编译正确,因为s1+= 1;相当于s1 = (short)(s1 + 1);其中有隐含的强制类型转换(自增长自动转换,除非越界)

  1. 用最有效率的方法计算2乘以8(运算符<<和>>的概念)

2 << 3(左移3 位相当于乘以2 的3 次方,右移3 位相当于除以2 的3 次方。

关于运算符<<和>> x >> n就是 先将x转成二进制,不读后面的n位 x << n就是 先将x转成二进制,往二进制数据后面加n个0

案例:int x = 16; x >> 1 输出x=8(先将x转成二进制 10000, 不读最后一位0, 输出 1000, 转为10进制即为8) x << 1 输出x=32(先将x转成二进制 10000,,往最后再读取一位0( 或根据是否已经有移位), 输出 100000, 转为10进制即为32)

  1. 数组有没有length()方法?String 有没有length()方法?

数组没有length()方法,只有有length 的属性。String 有length()方法。但是在JavaScript中 ,获得字符串的长度是通过length 属性得到的(非常容易和Java搞混)

  1. 当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?

是值传递。Java 语言的方法调用只支持参数的值传递。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性可以在被调 用过程中被改变,但对对象引用的改变是不会影响到调用者的。C++和C#中可以 通过传引用或传输出参数来改变传入的参数的值。在C#中可以编写如下所示的代 码, 但是在Java 中却做不到。

  1. String str = new String(“xyz”);创建了几个字符串对象?

两个对象,一个是静态区(方法区常量池)的”xyz“字符串,一个是用new 创建在堆上的对象str。

  1. Java 中有几种类型的流,它们的关系

Java中主要是字节流和字符流。字节流继承于InputStream、OutputStream,字符流继承于Reader、Writer。 在java.io 包中还有许多其他的流,主要是为了提高性能和使用方便 关于Java 的I/O 需要注意的有两点:

1)两种对称性( 输入和输出的对称性,字节和字符的对称性) 2)两种设计模式(适配器模式和装潢模式)。

  1. 编程实现文件拷贝的两种方式
public static void fileCopy(String source, String target) throws
IOException {
 try (InputStream in = new FileInputStream(source)) {
  try (OutputStream out = new FileOutputStream(target)) {
   byte[] buffer = new byte[4096];
   int bytesToRead;
   while((bytesToRead = in.read(buffer)) != -1) {
    out.write(buffer, 0, bytesToRead);
   }
  }
 }
}
123456789101112

NIO方式

public static void fileCopyNIO(String source, String target) throws
IOException {
 try (FileInputStream in = new FileInputStream(source)) {
  try (FileOutputStream out = new FileOutputStream(target)) {
   FileChannel inChannel = in.getChannel();
   FileChannel outChannel = out.getChannel();
   ByteBuffer buffer = ByteBuffer.allocate(4096);
   while(inChannel.read(buffer) != -1) {
    buffer.flip();
    outChannel.write(buffer);
    buffer.clear();
   }
  }
 }
}

12345678910111213141516
  1. 写一个方法,输入一个文件名和一个字符串,统计这个字符串在这个文件中出现的次数。
//统计这个字符串在这个文件中出现的次数
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;
}
12345678910111213141516171819
  1. 如何用Java 代码列出一个目录下所有的文件? 1)只列出当前文件夹下的文件
public static void main(String[] args) {
 File f = new File("/Users/Downloads");
 for(File temp : f.listFiles()) {
  if(temp.isFile()) {
   System.out.println(temp.getName());
  }
 }
}
12345678

2)对于当前文件夹下的文件夹继续展开显示所有文件

private static void queryDirectory(File f, int level) {
 if(f.isDirectory()) {
  for(File temp : f.listFiles()) {
   queryDirectory(temp, level + 1); //递归
  }
 }else {
  for(int i = 0; i < level - 1; i++) {
   System.out.print("\t"); //\t四个空格
  }
  System.out.println(f.getName());  //输出文件名
 }
}

public static void main(String[] args) {
 queryDirectory(new File("/Users/Downloads"),0);
}
12345678910111213141516

JDK1.7之后可以使用NIO.2的API实现

public static void main(String[] args) throws IOException {
 Path initPath = Paths.get("/Users/Downloads");
 Files.walkFileTree(initPath, new SimpleFileVisitor<Path>() {
  @Override
  public FileVisitResult visitFile(Path file, BasicFileAttributesattrs) throws IOException {
   System.out.println(file.getFileName().toString());
   return FileVisitResult.CONTINUE;
  }
 });
}
12345678910
  1. TCP编程通过Socket套接字编程实现服务端向客户端发送信息 服务端:
import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

//socket,TCP编程,服务端程序
public class Server {

    public static void serverInfo(){
        ServerSocket server = null;
        Socket client = null;
        PrintStream out = null;

        try {
            //在服务器8000端口等待客户连接
            server = new ServerSocket(8000);
            System.out.println("服务器正在等待客户端的连接......");
            //程序阻塞,等待客户端的连接
            client = server.accept();

            System.out.println("连接客户端成功!!");
            //实例化打印流对象,用于向客户端发送输出信息
            out = new PrintStream(client.getOutputStream());

            System.out.println("请输入您要向客户端发送的信息:");
            Scanner scan = new Scanner(System.in); //获取输入流
            //准备向客户端发送的信息
            String info = "服务端向客户端发送信息:" + scan.nextLine();

            //输出信息
            out.println(info);

   scan.close();   //关闭输入流
            out.close();    //关闭输出打印流
            client.close(); //关闭客户端
            server.close(); //关闭服务器端的练级
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        serverInfo();//开启服务端
    }
}
12345678910111213141516171819202122232425262728293031323334353637383940414243444546

客户端:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

//socket编程,客户端,要与服务端的端口号一致
public class Client {

    public static void clientInfo(){
        //声明socket对象
        Socket client = null;

        try {
            //实例化socket对象,指定连接的主机名称和端口号
            client = new Socket("localhost",8000);
            System.out.println("客户端连接成功");
            //声明缓存字符流,用来接收信息
            BufferedReader buf = new BufferedReader(new InputStreamReader(client.getInputStream()));
            //读取信息
            String info = buf.readLine();
            //输出读取的信息
           System.out.println("客户端收到服务器("+ client.getInetAddress() +")端发来的信息:【" + info + "】");
            client.close();
            buf.close();

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {
        clientInfo();
    }
}

123456789101112131415161718192021222324252627282930313233343536

运行:

1、 开启服务端Server

Server:服务器正在等待客户端的连接…

2.、开启客户端Client

Server:(输出内容) ??服务器正在等待客户端的连接… ??连接客户端成功!! ??请输入您要向客户端发送的信息: Client:(输出内容) ??客户端连接成功

3、Server输入内容HelloWorld

客户端Client接收到信息: ??客户端收到服务器(localhost/127.0.0.1)端发来的信息:【服务端向客户端发送信息:Hello World!!!】

  1. 饿汉式单例模式和懒汉式单例模式

1) 将构造器私有,不允许外界通过构造器 创建对象; 2) 通过公开的静态方法向外界返回类的唯一实例

饿汉式单例模式:

//饿汉式  不延迟实例化的做法,直接静态实例化创建 保证线程安全
public class HungarySingleton {
    private static HungarySingleton instance=new HungarySingleton();
    private HungarySingleton(){   
    }
    public static HungarySingleton getInstance(){
        return instance;//返回唯一实例
    }
}
123456789

懒汉式单例模式:

public class LazySingleton {
 private static LazySingleton instance = null;
 private Singleton() {}
 public static synchronized LazySingleton getInstance(){
  if (instance == null) instance = new LazySingleton ();
  return instance;
 }
}
12345678

volatile关键字,DCL机制实现懒汉式,提高性能

//懒汉式 DCL机制
//T1先从主存拷贝数据到自己的线程内存,进行处理数据,处理完数据更新到主存
//T2从主存拷贝数据到自己的线程内存
public class LazySingleton {
    private static volatile LazySingleton instance = null;
    private LazySingleton(){
    }
    //unlock happen-before lock 语义
    //T1,T2 ---可见性  T1 happen-before T2

    /**
     * A->B,B->C===A->C
     * T2调用getInstance()和调用getName()
     * T1调用getInstance()
     * a++
     * T1 ->T2
     * 构造器内存操作->T1 ->T2
     */
    public static  LazySingleton getInstance(){
      //T2
       if(instance==null){
            //T1  同步化 所有为null的只能进入一个,等待执行
            synchronized(LazySingleton.class){
                if(instance==null){
                    instance=new LazySingleton();
                    //JSR-133
                    //1.开辟空间
                    //2.初始化对象
                    //3.把地址给isntance变量---CPU
                }
            }
        }
        return instance;
    }
}
1234567891011121314151617181920212223242526272829303132333435
  1. 能将int强制转换为byte类型的变量吗?如果该值 大于byte 类型的范围,将会出现什么现象?

可以做强制转换,但是Java中int 是32位(4byte)的,而byte 是8位(1byte)的,所以,如果强制转化是,int 类型的高24 位将会被丢弃,byte 类型的范围是从-128 到128。

  1. 哪个类包含clone 方法?是Cloneable 还是Object?

java.lang.Cloneable 是一个标示性接口,不包含任何方法, clone 方法在object 类中定义。并且clone() 方法是一个本地方法,这意味着它是由c 或c++ 或其他本地语言实现。

  1. Java 中++ (- -)操作符是线程安全的吗?

不是线程安全的操作。它涉及到多个指令,如读取变量值,增加/减少,然后存储回内存,这个过程可能会出现多个线程交差。

  1. a = a + b 与a += b 的区别

+=隐式的将加操作的结果类型强制转换为持有结果的类型(比如a += b 会将运算结果a+b转换为需要a对应的类型)。如果两这个整型相加,如byte、short 或者int,首先会将它们提升到int 类型,然后在执行加法操作,最后再转换为接收的数据的类型。 而 a = a + b不会进行类型转换,如果越界就抛出异常

// byte取值 -128到128,超过就越界要转换类型
byte a = 120;
byte b = 120;
a = a + b; //编译报错:cannot convert from int to byte,因为不会将结果(a+b)自动强制类型转换
a += b;  //编译正确,b = -16
//隐式的将加操作的结果类型强制转换为持有结果的类型(a+b的时候隐式转换为int相加
//结果强制转换为接收结果a的类型byte,a+b=240(int类型)转换为a的类型byte=-16)
1234567
  1. int 和Integer 哪个会占用更多的内存

Integer包装类会占用更多的内存。Integer 是一个对象,需要存储对象的元数据。但是int 是一个原始类型的数据,所以占用的空间更少

  1. “a==b”和”a.equals(b)”有什么区别?

如果a和b都是对象,则a==b 是比较两个对象的引用,只有当a和b指向的是堆中的同一个对象才会返回true,a.equals(b) 是进行逻辑比较,所以通常需要重写该方法来提供逻辑一致性的比较。例如,String 类重写equals() 方法, 所以可以用于两个不同对象,但是包含的字母相同的比较

  1. a.hashCode() 有什么用?与a.equals(b) 有什么关系?

hashCode()方法是相应对象整型的hash值。它常用于基于hash 的集合类,如Hashtable、HashMap、LinkedHashMap 等等。它与equals() 方法关系特别紧密。根据Java 规范,两个使用equal() 方法来判断相等的对象,必须具有相同的hash code。

  1. Java集合框架

poll()方法和remove()方法的区别

poll() 和remove() 都是从队列中取出一个元素,但是poll() 在获取元素失败的时候会返回空,但是remove() 失败的时候会抛出异常。

LinkedHashMap 和PriorityQueue 的区别

PriorityQueue保证最高或者最低优先级的的元素总是在队列头部,但是LinkedHashMap 维持的顺序是元素插入的顺序。当遍历一个PriorityQueue时,没有任何顺序保证,但是LinkedHashMap 课保证遍历顺序是元素插入的顺序。

ArrayList 与LinkedList 的主要区别

ArrrayList 底层的数据结构是数组,支持随机访问,而 LinkedList的底层数据结构是链表(双向链表),不支持随机访问(但是优化了更新操作)。使用下标访问一个元素,ArrayList 的时间复杂度是O(1),而LinkedList 是O(n)。

Hashtable 与HashMap 有什么不同

1)Hashtable 是JDK 1 遗留下来的类,而HashMap 是后来增加的。 2)Hashtable 是同步的,比较慢,但HashMap 没有同步策略,所以会更快。 3)Hashtable不允许有个空的key,但是HashMap允许出现一个null key。

Java中的HashSet,内部是如何工作

HashSet 的内部采用HashMap 来实现。由于Map 需要key 和value,所以所有key 的都有一个默认value。类似于HashMap, HashSet 不允许重复的key,只允许有一个null key,意思就是HashSet 中只允许存储一个null 对象。

ArrayList 和HashMap 的默认大小

ArrayList 的默认大小是10 个元素, HashMap的默认大小是16 个元素(必须是2的幂次)

两个相同的对象的hash code一定相同,两个对象有相同的hash Code,但两个对象不一定相同。

两个不相等的对象可能会有相同的hashcode 值, 这就是为什么在hashmap 中会有冲突。相等hashcode 值的规定只是说如果两个对象相等, 必须有相同的hashcode 值, 但是没有关于不相等对象的任何规定

Java中的TreeMap是使用红黑树实现的。

本文作者:strive_day
本文链接:https://blog.csdn.net/qq_40542534/article/details/109241330

更多信息请关注@软件老王,关注不迷路,软件老王和他的IT朋友们,分享一些他们的技术见解和生活故事。

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接