java面试题(一)
一、如何将字符串反转
//方法1 递归方法
public static String reverse1(String s) { int length = s.length(); if (length <= 1){ return s; } String left = s.substring(0, length / 2); String right = s.substring(length / 2, length); return reverse1(right) + reverse1(left); }
//方法2 通过 charAt(int index)返回char值进行字符串拼接
public static String reverse2(String s) { int length = s.length(); String reverse = ""; for (int i = 0; i < length; i++) reverse = s.charAt(i) + reverse; return reverse; }
//方法3 把字符串转换成字符数组倒叙拼接然后返回值
public static String reverse3(String s) { char[] array = s.toCharArray(); String reverse = ""; for (int i = array.length - 1; i >= 0; i--) reverse += array[i]; return reverse; }
//方法4 调用StringBuffer中的reverse方法
public static String reverse4(String s) { return new StringBuffer(s).reverse().toString(); }
二、接口和抽象类有什么区别?
-
实现:抽象类的子类使用 extends 来继承;接口必须使用 implements 来实现接口。一个接口可以继承多个接口
-
构造函数:抽象类可以有构造函数;接口不能有。
-
实现数量:类可以实现很多个接口;但是只能继承一个抽象类。
-
访问修饰符:接口中的方法默认使用 public 修饰;抽象类中的方法可以是任意访问修饰符。
三、Java 中 IO 流分为几种?
按功能来分:输入流(input)、输出流(output)。
按类型来分:字节流和字符流。
字节流和字符流的区别是:字节流按 8 位传输以字节为单位输入输出数据,字符流按 16 位传输以字符为单位输入输出数据。
四、HashMap 和 Hashtable 有什么区别?
-
存储:HashMap 运行 key 和 value 为 null,而 Hashtable 不允许。
-
线程安全:Hashtable 是线程安全的,而 HashMap 是非线程安全的。
-
推荐使用:在 Hashtable 的类注释可以看到,Hashtable 是保留类不建议使用,推荐在单线程环境下使用 HashMap 替代,如果需要多线程使用则用 ConcurrentHashMap 替代。
五、Array 和 ArrayList 有何区别?
-
Array 可以存储基本数据类型和对象,ArrayList 只能存储对象。
-
Array 是指定固定大小的,而 ArrayList 大小是自动扩展的。
-
Array 内置方法没有 ArrayList 多,比如 addAll、removeAll、iteration 等方法只有 ArrayList 有。
六、单例模式的设计
1、饿汉式(线程安全,调用效率高,但是不能延时加载):
public class ImageLoader{
private static ImageLoader instance = new ImageLoader();
private ImageLoader(){}
public static ImageLoader getInstance(){
return instance;
}
}
一上来就把单例对象创建出来了,要用的时候直接返回即可,这种可以说是单例模式中最简单的一种实现方式。但是问题也比较明显。单例在还没有使用到的时候,初始化就已经完成了。也就是说,如果程序从头到位都没用使用这个单例的话,单例的对象还是会创建。这就造成了不必要的资源浪费。所以不推荐这种实现方式。
2.懒汉式(线程安全,调用效率不高,但是能延时加载):
public class SingletonDemo2 {
//类初始化时,不初始化这个对象(延时加载,真正用的时候再创建)
private static SingletonDemo2 instance;
//构造器私有化
private SingletonDemo2(){}
//方法同步,调用效率低
public static synchronized SingletonDemo2 getInstance(){
if(instance==null){
instance=new SingletonDemo2();
}
return instance;
}
}
七、讲一讲int占几个字节
int 32位 4字节
boolean 1位
八、面向对象的特征
封装、多态、抽象、继承
多态:靠的是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象,而程序调用的方法在运行时才动态绑定,就是引用变量所指向的具体实例的对象的方法
Object ob=new xxx();
UserDao ud=new UserDaoImpl();
九、有了基本数据类型,为什么还需要包装类型
基本数据类型:java提供8种基本数据类型
包装类型:每一个基本数据类型都会一一对应一个包装类型
boolean -->Boolean
int -->Integer
十、拆箱和装箱
装箱:把基本数据类型转换成对应的包装类型
Integer i=1;
自动装箱:实际上在编译时会调用Integer.valueOf()方法
手动拆箱:Integer i=Integer.valueOf(1)
拆箱:就是把包装类型转换成基本数据类型
Integer i=1;
int j=i;//自动拆箱(实际上在编译时会调用intValue方法)
int j=i=intValueOf();//手动拆箱
java是一个面向对象的语言,而基本数据类型,不具备面向对象的特征
Integer ---->null int ---->0
举例:用Integer和int分别表示Person这个类的ID
问题:判断Person存不存在
缓存值:对象缓存
Integer i=1;Integer j=1;i==j
十一、"=="和equals方法究竟有什么区别
详见:https://www.cnblogs.com/www123----/p/7857298.html
十二、讲一讲String、StringBuilder、StringBuffer三者之间的区别
String:内容不可变的字符串。String底层使用了一个不可变的字符数组(final char[])
StringBuilder:内容可变的字符串,线程不安全,但效率较高
StringBuffer:内容可变的字符串,线程安全,效率较低
最经典的就是拼接字符串:
1.String进行拼接
String c="a"+“b”
2.StringBuilder或StringBuffer
StringBuilder sb=new StringBuilder();
sb.append("a").append("b")
拼接字符串不能使用String进行拼接(要创建许多中间对象),要是用StringBuilder或StringBuffer
十三、(1)讲一下java中的集合
java中的集合分为值和key--value(Collection Map)两种
存取值的集合Lis和Set
List:有序,不可重复
Set:无序,可重复
存取key--value的为Map
(2)ArrayList和LinkedList的区别?
ArrayList底层使用的数组,LinkedList使用的是链表
数组查询特定元素比较快,而插入和删除、修改比较慢(数组在内存中是一块连续的内存,如果插入和删除是需要移动内存的)
链表不要求内存是连续的,在当前元素中存放下一个或上一个元素的地址。查询时需要从头部开始,一个一个的找,所以查询效率较低,插入时不需要移动内存,只需改变引用指向
ArrayList使用在查询比较多,但是插入和删除比较少的情况,而LinkedList使用在查询比较少而插入和删除比较多的情况
(3)讲一下HashMap、HashTable、CurrentMap的区别
1.HashMap和HashTable都可以使用来存储key--value的数据
2. HashMap是可以把null作为key或value的,而HashTable是不可以的
3.HashMap是线程不安全的,效率较高,而HashTable是线程安全的,效率较低
十四、java转发和重定向的区别
forward(转发): 是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器.浏览器根本不知道服务器发送的内容从哪里来的,因为这个跳转过程实在服务器实现的,并不是在客户端实现的所以客户端并不知道这个跳转动作,所以它的地址栏还是原来的地址.
redirect(重定向):
是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是新的URL.
转发是服务器行为,重定向是客户端行为。
区别:
1. 从地址栏显示来说
forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器.浏览器根本不知道服务器发送的内容从哪里来的,所以它的地址栏还是原来的地址.
redirect是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是新的URL.
2. 从数据共享来说
forward:转发页面和转发到的页面可以共享request里面的数据.
redirect:不能共享数据.
3. 从运用地方来说
forward:一般用于用户登陆的时候,根据角色转发到相应的模块.
redirect:一般用于用户注销登陆时返回主页面和跳转到其它的网站等
4. 从效率来说
forward:高.
redirect:低.
十五、实现多线程的几种方式
1、继承Thread类创建线程
public class MyThread extends Thread {
public void run() {
System.out.println("MyThread.run()");
}
}
MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
myThread1.start();
myThread2.start();
2、实现Runnable接口创建线程
如果自己的类已经extends另一个类,就无法直接extends Thread,此时,可以实现一个Runnable接口,如下:
public class MyThread extends OtherClass implements Runnable {
public void run() {
System.out.println("MyThread.run()");
}
}
为了启动MyThread,需要首先实例化一个Thread,并传入自己的MyThread实例:
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start();
事实上,当传入一个Runnable target参数给Thread后,Thread的run()方法就会调用target.run(),参考JDK源代码:
public void run() {
if (target != null) {
target.run();
}
}
3、实现Callable接口通过FutureTask包装器来创建Thread线程
Callable接口(也只有一个方法)定义如下:
public interface Callable<V> {
V call() throws Exception;
}
public class SomeCallable<V> extends OtherClass implements Callable<V> {
@Override
public V call() throws Exception {
// TODO Auto-generated method stub
return null;
}
}
Callable<V> oneCallable = new SomeCallable<V>();
//由Callable<Integer>创建一个FutureTask<Integer>对象:
FutureTask<V> oneTask = new FutureTask<V>(oneCallable);
//注释:FutureTask<Integer>是一个包装器,它通过接受Callable<Integer>来创建,它同时实现了Future和Runnable接口。
//由FutureTask<Integer>创建一个Thread对象:
Thread oneThread = new Thread(oneTask);
oneThread.start();
//至此,一个线程就创建完成了。
十六、实现线程同步的几种方式
方法一:
使用synchronized关键字
package com.gcc.interview.synchro;
/**
* 创建线程
* @author gcc
*
* 2018年3月9日
*/
public class MybanRunnable implements Runnable{
private Bank bank;
public MybanRunnable(Bank bank) {
this.bank = bank;
}
@Override
public void run() {
for(int i=0;i<10;i++) {
bank.save1(100);
System.out.println("账户余额是---"+bank.getAccount());
}
}
}
package com.gcc.interview.synchro;
/**
* 银行存款实例
* @author gcc
*
* 2018年3月9日
*/
class Bank{
private int account = 100;
public int getAccount() {
return account;
}
//同步方法
public synchronized void save(int money) {
account+=money;
}
public void save1(int money) {
//同步代码块
synchronized(this) {
account+=money;
}
}
public void userThread() {
Bank bank = new Bank();
MybanRunnable my1 = new MybanRunnable(bank);
System.out.println("线程1");
Thread th1 = new Thread(my1);
th1.start();
System.out.println("线程2");
Thread th2 = new Thread(my1);
th2.start();
}
}
方法二:
使用特殊域变量volatile实现线程同步
a.volatile关键字为域变量的访问提供了一种免锁机制
b.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新
c.因此每次使用该域就要重新计算,而不是使用寄存器中的值
d.volatile不会提供任何原子操作,它也不能用来修饰final类型的变量
例如:
在上面的例子当中,只需在account前面加上volatile修饰,即可实现线程同步。
//只给出要修改的代码,其余代码与上同
class Bank {
//需要同步的变量加上volatile
private volatile int account = 100;
public int getAccount() {
return account;
}
//这里不再需要synchronized
public void save(int money) {
account += money;
}
}
注:多线程中的非同步问题主要出现在对域的读写上,如果让域自身避免这个问题,则就不需要修改操作该域的方法。
用final域,有锁保护的域和volatile域可以避免非同步的问题。
十七、jsp九大内置对象
JSP内置对象(9个内置对象):
1.PageContext javax.servlet.jsp.PageContext JSP的页面容器
2.request javax.servlet.http.HttpServletrequest 获取用户的请求信息
3.response javax.servlet.http.HttpServletResponse 服务器向客户端的回应信息
4.session javax.servlet.http.HttpSession 用来保存每一个用户的信息
5.application javax.servlet.ServletContext 表示所有用户的共享信息
6.config javax.servlet.ServletConfig 服务器配置信息,可以取得初始化参数
7.out javax.servlet.jsp.jspWriter 页面输出
8.page java.lang.object)
9.exception java.lang.Throwable
四种属性范围:
page(pageContext):只在一个页面中保存属性。 跳转之后无效。
request:只在一次请求中有效,服务器跳转之后有效。 客户端跳无效
session:再一次会话中有效。服务器跳转、客户端跳转都有效。 网页关闭重新打开无效
application:在整个服务器上保存,所有用户都可使用。 重启服务器后无效
注意:如果设置过多的application属性范围会影响服务器性能。
pageContext.PAGE_SCOPE
十八、同步和异步的区别
同步:
同步的思想是:所有的操作都做完,才返回给用户。这样用户在线等待的时间太长,给用户一种卡死了的感觉(就是系统迁移中,点击了迁移,界面就不动了,但是程序还在执行,卡死了的感觉)。这种情况下,用户不能关闭界面,如果关闭了,即迁移程序就中断了。
异步:
将用户请求放入消息队列,并反馈给用户,系统迁移程序已经启动,你可以关闭浏览器了。然后程序再慢慢地去写入数据库去。这就是异步。但是用户没有卡死的感觉,会告诉你,你的请求系统已经响应了。你可以关闭界面了。
同步,是所有的操作都做完,才返回给用户结果。即写完数据库之后,在响应用户,用户体验不好。
异步,不用等所有操作等做完,就响应用户请求。即先响应用户请求,然后慢慢去写数据库,用户体验较好。
异步操作例子:
为了避免短时间大量的数据库操作,就使用缓存机制,也就是消息队列。先将数据放入消息队列,然后再慢慢写入数据库。
引入消息队列机制,虽然可以保证用户请求的快速响应,但是并没有使得我数据迁移的时间变短(即80万条数据写入mysql需要1个小时,用了redis之后,还是需要1个小时,只是保证用户的请求的快速响应。用户输入完http url请求之后,就可以把浏览器关闭了,干别的去了。如果不用redis,浏览器不能关闭)。
同步就没有任何价值了吗?
银行的转账功能。
十九、java接口
详见:https://blog.csdn.net/Valentino112358/article/details/89017509