Java面试题(3)
1、一个“.java”源文件中是否可以包含多个类(不是内部类)?有什么限制?
可以包含多个类。只允许有一个公共类,且公共类名与Java源文件名相同。
2、switch能否作用在byte上,能否作用在long上,能否作用在String上?
基本类型的包装类(如:Character、Byte、Short、Integer)
switch可作用于char byte short int
switch可作用于char byte short int对应的包装类
switch不可作用于long double float boolean,包括他们的包装类
switch中可以是字符串类型,String(jdk1.7之后才可以作用在string上)
switch中可以是枚举类型
3、说说final、finally、finalize三者的区别?
答:
1.final:如果一个类被final修饰,意味着该类不能派生出新的子类,不能作为父类被继承。因此一个类不能被声明为abstract,又被声明为final。将变量或方法声明为final。可以保证他们在使用的时候不被改变。其初始化可以在两个地方:一是其定义的地方,也就是在final变量在定义的时候就对其赋值;二是在构造函数中。这两个地方只能选其中的一个,要么在定义的时候给值,要么在构造函数中给值。被声明为final的方法也只能使用,不能重写。
2.finally:在异常处理的时候,提供finally块来执行任何的清除操作。如果抛出一个异常,那么相匹配的catch字句就会执行,然后控制就会进入finally块,前提是有finally块。
3.finalize:finalize是方法名,java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是在垃圾收集器确认一个对象没有被引用时对这个对象调用的。它是在Object类中定义的,因此,所有的类都继承了它。子类覆盖finalize()方法已整理系统资源或者执行其他清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。
4、Math.round(11.5)和Math.round(-11.5)分别等于多少?
答案:12 和 -11
01、round()方法可以这样理解:
将括号内的数+0.5之后,向下取值,
比如:round(3.4)就是3.4+0.5=3.9,向下取值是3,所以round(3.4)=3;
round(-10.5)就是-10.5+0.5=-10,向下取值就是-10,所以round(-10.5)=-10
所以,Math.round(11.5)=12; Math.round(-11.5)=-11;
02、扩展:
001、Math.ceil求最小的整数,但不小于本身.
ceil的英文意义是天花板,该方法就表示向上取整,
例子:
所以,Math.ceil(11.3)的结果为12,Math.ceil(-11.3)的结果是-11;
002、Math.floor求最大的整数,但不大于本身.
floor的英文意义是地板,该方法就表示向下取整,
例子:
floor的英文意义是地板,该方法就表示向下取整,
所以,Math.floor(11.6)的结果为11,Math.floor(-11.6)的结果是-12;
5、说说String、StringBuffer、StringBuilder的区别。
区别:String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象,因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,那速度是一定会相当慢的,
执行速度:三者在执行速度方面的比较:
StringBuilder > StringBuffer > String
使用场景:
01.如果要操作少量的数据用 = String
02.单线程操作字符串缓冲区 下操作大量数据 = StringBuilder
03.多线程操作字符串缓冲区 下操作大量数据 = StringBuffe
6、为什么不建议记录日志时使用System.out.println()。
-
Log4j就是帮助开发人员进行日志输出管理的API类库。它最重要的特点就可以配置文件灵活的设置日志信息的优先级、日志信息的输出目的地以及日志信息的输出格式。
-
Log4j除了可以记录程序运行日志信息外还有一重要的功能就是用来显示调试信息。
-
程序员经常会遇到脱离java ide环境调试程序的情况,这时大多数人会选择使用System.out.println语句输出某个变量值的方法进行调试。这样会带来一个非常麻烦的问题:一旦哪天程序员决定不要显示这些System.out.println的东西了就只能一行行的把这些垃圾语句注释掉。若哪天又需调试变量值,则只能再一行行去掉这些注释恢复System.out.println语句。
使用log4j可以很好的处理类似情况。
7、运行时异常和普通异常有什么区别?
Java提供了两种错误的异常类,分别为Error和Exception,他们拥有共同的父类—Throwable。
Error表示程序在运行期间出了非常严重的错误,并且错误不可恢复,如OutOfMemoryError、ThreadDeath等
Exception表示可以恢复的异常,是编译器可以捕捉到的。包含两种类型:
检查异常和运行时异常。
1)检查异常。Java编译器强制程序去捕获此类异常,如IO异常和SQL异常。
2)运行时异常。编译器不会对其强制进行捕获并处理。如果不进行处理,出现异常时,JVM会来处理。
出现运行时异常,系统会把异常一直往上抛,知道遇到处理代码为止。若没有处理代码,则抛到最上层。
多线程又Thread.run()方法抛出,单线程用main()方法抛出。如果不处理异常,一旦发生,要么线程终止,要么主程序终止。
运行时异常包括:NullPointException(空指针异常)
ClassCastException(类型转换异常)
ArrayIndexOutOfBoundsException(数组越界异常)
ArrayStoreException(数组存储异常)
BufferOverflowException(缓冲区溢出异常)
ArithmeticException(算输异常)
8、Thread.Sleep和Object.wait()有什么区别?
1、这两个方法来自不同的类分别是,sleep来自Thread类,和wait来自Object类。
sleep是Thread的静态类方法,谁调用的谁去睡觉,即使在a线程里调用了b的sleep方法,实际上还是a去睡觉,要让b线程睡觉要在b的代码中调用sleep。
2、最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
sleep不出让系统资源;wait是进入线程等待池等待,出让系统资源,其他线程可以占用CPU。一般wait不会加时间限制,因为如果wait线程的运行资源不够,再出来也没用,要等待其他线程调用notify/notifyAll唤醒等待池中的所有线程,才会进入就绪队列等待OS分配系统资源。sleep(milliseconds)可以用时间指定使它自动唤醒过来,如果时间不到只能调用interrupt()强行打断。
Thread.Sleep(0)的作用是“触发操作系统立刻重新进行一次CPU竞争”。
3、使用范围:wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
synchronized(x){
x.notify()
//或者wait()
}
4、sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常
9、当一个线程进入一个对象的一个synchronized方法后,其它线程是否可以进入此对象的其他synchronized方法和普通方法?
1.其他方法前是否加了synchronized关键字,如果没加,则能。
2.如果这个方法内部调用了wait,则可以进入其他synchronized方法。
3.如果其他个方法都加了synchronized关键字,并且内部没有调用wait,则不能。
4.如果其他方法是static,它用的同步锁是当前类的字节码,与非静态的方法不能同步,因为非静态的方法用的是this。
10、如何遍历HashMap。
public static void main(String[] args) {
Map<String,String> map=new HashMap<String,String>();
map.put("1", "value1");
map.put("2", "value2");
map.put("3", "value3");
map.put("4", "value4");
//第一种:普通使用,二次取值
System.out.println("\n通过Map.keySet遍历key和value:");
for(String key:map.keySet())
{
System.out.println("Key: "+key+" Value: "+map.get(key));
}
//第二种
System.out.println("\n通过Map.entrySet使用iterator遍历key和value: ");
Iterator map1it=map.entrySet().iterator();
while(map1it.hasNext())
{
Map.Entry<String, String> entry=(Entry<String, String>) map1it.next();
System.out.println("Key: "+entry.getKey()+" Value: "+entry.getValue());
}
//第三种:推荐,尤其是容量大时
System.out.println("\n通过Map.entrySet遍历key和value");
for(Map.Entry<String, String> entry: map.entrySet())
{
System.out.println("Key: "+ entry.getKey()+ " Value: "+entry.getValue());
}
//第四种
System.out.println("\n通过Map.values()遍历所有的value,但不能遍历key");
for(String v:map.values())
{
System.out.println("The value is "+v);
}
}
输出结果:
通过Map.keySet遍历key和value:
Key: 1 Value: value1
Key: 2 Value: value2
Key: 3 Value: value3
Key: 4 Value: value4
通过Map.entrySet使用iterator遍历key和value:
Key: 1 Value: value1
Key: 2 Value: value2
Key: 3 Value: value3
Key: 4 Value: value4
通过Map.entrySet遍历key和value
Key: 1 Value: value1
Key: 2 Value: value2
Key: 3 Value: value3
Key: 4 Value: value4
通过Map.values()遍历所有的value,但不能遍历key
The value is value1
The value is value2
The value is value3
The value is value4
11、Set中和的元素不能重复,用什么方法来区分是否重复呢?使用==还是用equals?他们有什么区别?
Set是Collection容器的一个子接口,它不允许出现重复元素,当然也只允许有一个null对象。
如何来区分重复与否呢?
“ 用 iterator() 方法来区分重复与否 ”,这是在网上流传的答案,个人认为这是个错误的答案。API中写的很明白:“set 不包含满足
e1.equals(e2) 的元素对 e1 和 e2 ”,由此可见回答使用equals()区分更合适。
为什么用equals()而不用==来区分?
应该从它俩的区别谈起,==是用来判断两者是否是同一对象(同一事物),而equals是用来判断是否引用同一个对象。再看一下Set里面存的是
对象,还是对象的引用。根据java的存储机制可知,set里面存放的是对象的引用,所以当两个元素只要满足了equals()时就已经指向同一个对象,
也就出现了重复元素。所以应该用equals()来判断。
12、写一个方法,计算菲波那切数列(1,1,2,3,5,8,…)第100项的值,方法 参数为第N项,返回值为第N项的值(考虑程序运行效率、异常处理)。
第一百项的值:354224848179261915075
public static BigDecimal getNum(Integer n){
boolean flag=true;//声明一个标识符
List<BigDecimal> lists = new ArrayList<BigDecimal>();//声明一个集合存储斐波那契数列的值
try {
for(int i=1;i<=n;i++){
if (i<=2) {//斐波那契数列前两项值
lists.add(new BigDecimal(1));
}else{//大于两项后的值计算
lists.add((lists.get(i-3).add(lists.get(i-2))));
}
}
for (BigDecimal bigDecimal : lists) {//便利斐波那契数列
System.out.println(bigDecimal);
}
} catch (Exception e) {
flag=false;
System.err.print("出错了");
e.printStackTrace();
}
if (flag) {
return lists.get(n-1);//返回第n象的值
}else{
return new BigDecimal(0);
}
13、谈谈你对java.math.BigDecimal类的认识。
java.math.BigDecimal 类提供用于算术,刻度操作,舍入,比较,哈希算法和格式转换操作。
对于不需要任何准确计算精度的数字可以直接使用float或double,但是如果需要精确计算的结果,则必须使用BigDecimal类,而且使用BigDecimal类也可以进行大数的操作。
BigDecimal是Java中用来表示任意精确浮点数运算的类,在BigDecimal中,使用unscaledValue × 10-scale来表示一个浮点数。其中,unscaledValue是一个BigInteger,scale是一个int。从这个表示方法来看,BigDecimal只能标识有限小数,不过可以表示的数据范围远远大于double,在实际应用中基本足够了。
toString()方法提供BigDecimal的规范表示。它使用户可以完全控制舍入行为。
提供用于操作BigDecimal规模两种类型的操作:
缩放/舍入操作
小数点移动操作。
此类及其迭代器实现Comparable接口的所有可选方法。
14、Java进程间通信的方式有哪些。
(1)管道(Pipe):管道可用于具有亲缘关系进程间的通信,允许一个进程和另一个与它有共同祖先的进程之间进行通信。
(2)命名管道(named pipe):命名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关 系 进程间的通信。命名管道在文件系统中有对应的文件名。命名管道通过命令mkfifo或系统调用mkfifo来创建。
(3)信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送 信号给进程本身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数)。
(4)消息(Message)队列:消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺
(5)共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。
(6)内存映射(mapped memory):内存映射允许任何多个进程间通信,每一个使用该机制的进程通过把一个共享的文件映射到自己的进程地址空间来实现它。
(7)信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。
(8)套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。
15、CSS规则style=”padding:0 0 3px 3px”设置的元素内边距分别是多少。
上:0px 右:0px 下:3px 左:3px
16、说出HTTP请求的GET和POST方式的区别。
(一)、原理区别
一般我们在浏览器输入一个网址访问网站都是GET请求;再FORM表单中,可以通过设置Method指定提交方式为GET或者POST提交方式,默认为GET提交方式。
HTTP定义了与服务器交互的不同方法,其中最基本的四种:GET,POST,PUT,DELETE,HEAD,其中GET和HEAD被称为安全方法,因为使用GET和HEAD的HTTP请求不会产生什么动作。不会产生动作意味着GET和HEAD的HTTP请求不会在服务器上产生任何结果。但是安全方法并不是什么动作都不产生,这里的安全方法仅仅指不会修改信息。
根据HTTP规范,POST可能会修改服务器上的资源的请求。比如CSDN的博客,用户提交一篇文章或者一个读者提交评论是通过POST请求来实现的,因为再提交文章或者评论提交后资源(即某个页面)不同了,或者说资源被修改了,这些便是“不安全方法”。
(二)、表现形式区别
在HTTP请求中,奇异行必须是一个请求行,包括请求方法,请求URL,报文所用HTTP版本信息。紧接着是一个herders小节,可以有零个或一个首部,用来说明服务器要使用的附加信息。在首部之后就是一个空行,最后就是报文实体的主体部分,包含一个由任意数据组成的数据块。但是并不是所有的报文都包含实体的主体部分。
请求方式的区别:
1)、GET请求,请求的数据会附加在URL之后,以?分割URL和传输数据,多个参数用&连接。URL的编码格式采用的是ASCII编码,而不是uniclde,即是说所有的非ASCII字符都要编码之后再传输。
POST请求:POST请求会把请求的数据放置在HTTP请求包的包体中。上面的item=bandsaw就是实际的传输数据。
因此,GET请求的数据会暴露在地址栏中,而POST请求则不会。
2)、传输数据的大小
在HTTP规范中,没有对URL的长度和传输的数据大小进行限制。但是在实际开发过程中,对于GET,特定的浏览器和服务器对URL的长度有限制。因此,在使用GET请求时,传输数据会受到URL长度的限制。
对于POST,由于不是URL传值,理论上是不会受限制的,但是实际上各个服务器会规定对POST提交数据大小进行限制,Apache、IIS都有各自的配置。
3)、安全性
POST的安全性比GET的高。这里的安全是指真正的安全,而不同于上面GET提到的安全方法中的安全,上面提到的安全仅仅是不修改服务器的数据。比如,在进行登录操作,通过GET请求,用户名和密码都会暴露再URL上,因为登录页面有可能被浏览器缓存以及其他人查看浏览器的历史记录的原因,此时的用户名和密码就很容易被他人拿到了。除此之外,GET请求提交的数据还可能会造成Cross-site request frogery攻击
4)、HTTP中的GET,POST,SOAP协议都是在HTTP上运行的
17、Servlet的forword和redirect的区别。
使用forward的时候浏览器不知道它所请求的具体资源来源,所以地址栏不会变;
使用redirect,服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址。所以地址栏显示的是新的URL。
forward,转发页面和转发到的页面可以共享request里面的数据.redirect,不能共享数据。
18、写一段JDBC查询oracle数据的代码。
package test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class JDBCDemo {
public static void main(String[] args){
Connection conn = null;
ResultSet rs = null;
try {
//加载驱动
Clas敏感词orName("oracle.jdbc.OracleDriver");
//获得连接
conn = DriverManager.getConnection(
"jdbc:oracle:thin:@localhost:1521:orcl",
"scott",
"tiger");
Statement stat = conn.createStatement();
//sql语句
String sql = "SELECT * FROM emp";
//执行语句获得结果集
rs = stat.executeQuery(sql);
//遍历结果集
while(rs.next()){
String name = rs.getString("name");
System.out.println(name);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭连接
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
19、JDBC中PreparedStatement相比Statement的好处。
(1)极大提高了安全性能,可以防止sql注入
(2)代码的可读性和可维护性相比Statement要好.
(3)有预编译功能,相同操作批量数据效率较高
因为预编译语句有可能被重复调用.所以语句在被DB的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中就会得到执行
上一篇: java 面试题----集合相关
下一篇: 对于java中多态的简单理解