面试宝典总结:
java 基础
Java 中的八种数据类型
四类 | 八种 | 字节数 | 数据范围 |
---|---|---|---|
整型 | byte | 1 | -128··127 |
整型 | short | 2 | -32768··32767 |
整型 | int | 4 | |
整型 | long | 8 | -2^63 ·· 2^63-1 |
浮点型 | float | 4 | |
浮点型 | double | 8 | |
字符型 | char | 2 | |
布尔型 | boolean | 1 | true,false |
面向对象的四大特征:
- 继承:继承是从已有类得到继承信息,创建新类的过程。提供继承信息的类叫做父类,得到继承信息的类叫做子类。
- 封装:隐藏一切可隐藏的东西,只向外界提供最简单的编程接口。
- 多态:多态性指允许不同子类型的对象对同一消息做出不同的响应。(同样的对象引用调用同样的方法但是做了不同的事)多态必须满足的两个条件:1. 方法重写,2. 父类引用指向子类
- 抽象:抽象就是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象,抽象只关注对象有哪些属性和行为,不关心具体实现。
访问权限修饰符的区别
修饰符 | 当前类 | 同包 | 子类 | 其他包 |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
default | √ | √ | × | × |
private | √ | × | × | × |
对象的克隆
克隆出来的对象是一个新的对象。
深拷贝和浅拷贝的区别:
当对象的中的对象是引用数据类型时,浅拷贝指向的时对象的引用,深拷贝才是新的创建。(Clone方法是浅拷贝)
如何实现深拷贝?
如果想深拷贝一个对象(对象中含有引用对象),这个对象非必须要实现Cloneable 接口,实现Clone方法,并且在clone方法内部,把该对象的引用的其他对象也要clone一份,这也意味者这个其他对象内部也要实现Cloneable接口,重写clone方法。
深克隆的方法验证
static class Body implements Cloneable {
public Head head;
public Body() {
}
public Body(Head head) {
this.head = head;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Body newBody = (Body) super.clone();
newBody.head = (Head) head.clone();
return newBody;
}
}
static class Head implements Cloneable {
public Face face;
public Head() {
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public static void main(String[] args) throws CloneNotSupportedException {
Body body = new Body(new Head(new Face()));
Body body1 = (Body) body.clone();
System.out.println("body == body1 : " + (body == body1));
System.out.println("body.head == body1.head : " + (body.head == body1.head));
}
/**
* 程序执行结果:
* body == body1 : false
* body.head == body1.head : false
*/
& 和 && 的区别:
& :
- 位运算符 (可用于基本类型的数据运算(仅限byte,short,int,char,boolean))
- 逻辑与
&&:短路与
虽然& 和 && 运算符都要求左右两端的布尔值都是 true,整个表达式的结果才为true,但是短路与&& 运算符只有当左边的表达式为true时才会继续进行有右边等式的运算,右边为false时,会直接短路掉,右边的表达式不会执行。
java 中如何跳出当前的多重嵌套循环?
break关键字,在最外层循环加一个标记A,通过breakA;可以跳出多重循环
两对象的HashCode值比较。
如果两个对象equals方法返回true,那么它们的 hashCode 值一定相同。
两个对象的 hashCode 值相同,它们的equals方法不一定返回 true。(hash冲突)
A==B判断的两个对象的地址值是否相同,A.equals(B) 比较的是两个对象的值是否相同。
instanceOf 判断的是两个对象是否是同一个类型。
String 是否可以被继承?(final 修饰的类)
由于String 是被final修饰的类,所以不能被继承。
当一个对象被当作参数传递到一个方法后,方法可以改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?
//基础类型作为值传递
public class test {
public static void main(String[] args) {
int i = 1;
System.out.println("before change, i = "+i);
change(i);
System.out.println("after change, i = "+i);
}
public static void change(int i){
i = 5;
}
}
/**
* 输出的结果是 before change i = 1;
* 输出的结果是 after change i = 1;
* 当基本数据类型作为参数传递时,传递的是实参值的副本,即传的是值,无论在函数中怎么操作这个副本,
* 实参的值是不会被改变的。
*/
//对象(引用类型)作为值传递
public class test {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer("Hello ");
System.out.println("before change, sb is "+sb.toString());
change(sb);
System.out.println("after change, sb is "+sb.toString());
}
public static void change(StringBuffer stringBuffer){
stringBuffer.append("world !");
}
}
/**
* before change, sb is Hello
* after change, sb is Hello world !
*/
/**
* 从输出结果中我们可以发现,sb所指向的对象的值被改变了,那么是否我们可以推论出,在Java中,当
* 对象作为参数传递时,传递的是该对象的引用呢?我们再来看下面这个例子
*/
public class test {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer("Hello ");
System.out.println("before change, sb is "+sb.toString());
change(sb);
System.out.println("after change, sb is "+sb.toString());
}
public static void change(StringBuffer stringBuffer){
stringBuffer = new StringBuffer("Hi ");
stringBuffer.append("world !");
}
}
/**
* 如果上面的推论是正确的,即Java中对象作为参数传递,实际传递的是该对象的引用,那么在调用change
* 函数之后,原对象的值应该是会改变的,变为“Hi world !”,但是,当我们运行程序后,结果却是如下所示:
* before change, sb is Hello
* after change, sb is Hello
*
* 原对象的值并没有被改变,这与上面的推论相矛盾!
*/
/**
* 总结就是基本数据类型传递的是形参,形参不影响实参;引用数据类型传递的是地址,形参在方法内被改变,实
* 参也会改变,若在方法内实例化了同名对象,即产生了新的地址,对这个同名对象的改变,由于地址不一样,
* 所以不影响原来的对象
*/
方法重载和方法重写的区别
方法的重载和方法的重写都是实现多态的方式。方法的重载是编译时的多态性,而方法的重写是运行时的多态性。
**方法重载的规则**
- 方法名一致
- 请求参数不同(个数不同,参数类型不同)
- 和方法的返回值无关。
- 可以抛出不同的异常,可以有不同的修饰符
**方法的重写**
- 方法名,请求参数,返回值相同
- 构造方法不能被重写,static 和 final 修饰的方法不能被重写
- 重写的方法不能抛出被重写方法更广泛的异常。
抽象类 Abstract 和 interface 的区别?
相同: 都不能被实例化。
不同:抽象类中可以定义构造器,接口中不能定义构造器。接口中的成员全是public 的。抽象类中的成员可以是任何。抽象类中可以有成员变量,接口中没有。抽象类中可以有抽象方法和具体方法。接口中只有抽象方法。有抽象方法的类必须被声明成抽象类,但是抽象类中可以没有抽象方法 ,接口中的成员变量就是常量。抽象类中可以有静态方法。抽象类只能够单继承。接口可以多继承。
== 和 equal 的区别
一个是方法一个是运算符, == 如果比较的是基本类型的变量,则比较的是数值是否相等。如果比较的是引用类型,那么比较的是引用类型的地址值是否相等。 equal是用来比较两个对象的内容是否相等。equal是不能用来比较基本类型的。如果对象没有对equal 方法进行重写,那么equal方法比较的是对象的地址值;
String x = "string";
String y = "string";
String z = new String("string");
System.out.println(x==y); // true
System.out.println(x==z); // false
System.out.println(x.equals(y)); // true
System.out.println(x.equals(z)); // true
// new String() 方法是重写开辟了空间,所以 == 的结果为 false
equal 默认情况下是引用的比较(也就是 == 比较),不过很多的类都重写了 equal 方法,比如 String,Integer 等内部将equal 方法变成了 值 得比较。
break 和 continue 的区别
都是用来控制循环语句的。break 是用来结束一个循环的。continue 是用来跳过此次循环,执行下次循环。
throw 和 throws 的区别
throw 是用在方法体中的,用来抛出异常。执行throw 一定是抛出了某种异常。
throws 是用在方法生命后面的,表示可能会跑出某种异常。表示了某种异常的可能性,不一定会抛出异常。
final ,finally,finalize 的区别
final 修饰类,方法,属性,表示为最终态不可变。finally 异常处理语句结构的一部分,表示最终一定会被执行。finalize : Object 的一个方法,在垃圾回收器执行的时候会调用被回收对象的这个方法。更像是一个对象生命周期的临终方法,当此方法被调用代表着该对象的死亡。
final 修饰的类叫最终类,该类不能被继承
final 修饰的方法不能被重写
final 修饰的变量叫常量,常量必须初始化,初始化后不能再被修改
Math.round()方法
数据轴向右取整。 -11.5==》-11 11.5 ==》 12
switch 中可使用的类型
java 5 以前只能使用 byte,short,int ,char 。 java5 开始 引入了 enum 。 java7 后,引入了 String ,但是 Long 类型一直都是不可以的。
String,StringBuffer ,Stringbuild 的区别
String 是被final 修饰的。不能被修改,Stringbuild 的值是可以被修改的。 StringBuffer 是线程安全的类。被 Synchronized 修饰。
String 声明的是不可变的变量,每次操作都会生成新的String对象,而Stringbuild 和 Stringbuffer 都是在原字符串上进行操作的。StringBuild 是线程不安全的,推荐在单线程下使用,而StringBuffer 是线程安全的推荐在多线程下使用。
String str="i"与 String str=new String(“i”)一样吗?
不一样,因为内存的分配方式不一样。String str="i"的方式,java 虚拟机会将其分配到常量池中;而 String str=new String(“i”) 则会被分到堆内存中。
BIO,AIO,NIO 有什么区别?
BIO:同步阻塞式IO,简单方便,并发处理效率低
NIO:同步非阻塞IO,客户端和服务器通过channel 信道,实现了多路复用
AIO:NIO的升级,也叫NIO2。异步非阻塞IO
泛型
泛型类型用于类的定义中,被称为泛型类。通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种容器类,如:List、Set、Map。
public interface List<E> extends Collection<E> {
........
}
//泛型的最基本写法:
classs 类名称<泛型标识:可以随便写任意标识符,标识指定的泛型的类型>{
private 泛型标识 /* (成员变量类型) */ var ;
}
public void showKeyValue1(Generic<?> obj){
Log.d("泛型测试","key value is " + obj.getKey());
}
JAVA 中的容器
常见的集合类: ArrayList,LinkedList,Vector,HashSet, TreeSet,HashMap , HashTable
list 中的元素可重复,set ,map 中的元素不可重复。
HashSet 和 HashMap
hashSet 的 底层是由 hashMap 实现的,HashSet 的值存放在 HashMap 的key 上,HashMap 的 value 统一是一个常量PRESENT。
ArrayList 和 LinkedList 的区别
ArrayList 的底层是数组,支持随机访问,LinkedList 的底层是双向循环链表,不支持随机访问。
Array 和 ArrayList 的区别
Array 只能存纳 基本数据类型的数据, 而ArrayList 只能容纳对象。
Sleep 和 wait 方法的区别
sleep():方法是线程类 Thread 的静态方法,让调用线程进入睡眠状态。 wait():是Object 类。 并且wait方法会释放锁。Sleep 方法不会释放锁。
线程池中的 submit ,和 execute 方法的区别?
接收到的参数不一样
submit 有返回值,而execute 没有
submit 方便 exception 处理
序列化,什么情况下需要序列化
当你想把内存中的对象状态保存到一个文件中或者数据库中的时候
当你想套接字在网络上传输对象时
当你想通过RMI传输对象时
什么是动态代理?
当想要给实现了某个接口的类中的方法,加一些额外处理时,比如说加日志,加事务。顾名思意就是创建一个新的类,这个类不仅包含原来类方法的功能,而且还在原来的基础上添加了额外处理的新类。这个代理类不是定义好的,是动态生成的。具有解耦意义。灵活拓展性强。
JAVAWEB
jsp 和 servlet 有什么区别?
jsp 经编译就成了 servlet 。(JSP 的本质就是 servlet )
servlet 中没有内置对象,JSP 中的内置对象都是必须通过 HttpServletRequset对象,HttpServletResponse对象以及HttpServlet 对象得到。
JSP 中有哪些内置对象?作用分别是什么?
- request: 封装客户端的请求,其中包含来自get或post请求的参数
- response:封装服务器对客户端的响应
- pageContext:通过该对象获取其他对象
- session:封装用户会话的对象
- application:封装服务器运行环境的对象
- out:输出服务器响应的输出流对象
- config:Web应用的配置对象
- page:JSP页面本身(相当于java 中的this)
- exception:封装页面抛出异常的对象
JSP 的四种作用域对象?
page :代表与页面相关的对象和属性
request :代表web 客户机发出的一个请求相关的对象和属性,一个请求可以跨越多个页面,涉及多个web 组件,需要在页面显示的临时数据可以置于此作用域
session :代表与整个用户和服务器建立的一次会话相关的对象和属性,跟某个用户相关的数据应该放在用户自己的session 中。
application :代表与整个 web 应用程序相关的 对象和属性,它实质上是 跨越整个web 应用程序,包括多个页面,请求和会话的一个全局作用域。
Session 和 Cookie 的区别
由于http 是无状态的协议,所以服务器需要记录用户的状态时,就需要某种机制来识别具体用户,这个机制就是session,
典型的场景比如购物车,当你点击下单按钮时,由于HTTP协议无状态,所以并不知道是哪个用户操作的,所以服务端要为特定的用户创建了特定的session,用来标识这个用户,并且跟踪用户。 这个session 是保存在服务端的,有一个唯一标识,在服务端保存session的方法有很多,内存,数据库,文件都有。集群的时候也要考虑session的转移,在大型的网站,一般都有专门的session服务器集群,用来保存用户的会话信息。
思考一下服务端如何识别特定的客户?cookie 的作用就是用来识别客户。每次http 请求的时候,客户端都会发送相应的cookie信息到服务端,(实际上大多数的应用都是利用cookie 来实现 session 跟踪的),第一次创建session 的时候,服务端会在http 协议中告诉客户端,需要在cookie 里面记录一个sessionID,以后每次请求都把这个会话ID 发送到服务端。这样我就能知道你是谁了,(如果cookie 被禁用了怎么办?一般这种情况下,会使用一种叫做URL重写的技术来实现会话跟踪,即每次http交互,URL后面都会被附加上一个诸如sid=xxx这样的参数,服务端据此来识别用户)
session 是在服务器保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群数据库,文件中。Cookie 是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现session的一种方式。
session 的工作原理
session 是存在服务器上的类似于一个散列表格的文件。里面存有我们需要的信息,在我们需要的时候可以从里面取出来,类似于一个大号的map。里面的键存储的是用户的sessionId,用户向服务器请求就会带上这个key。
如果客户端禁用cookie ,session 还能用么?
不能,sessionID 是通过 cookie 来传递的,禁用cookie 相当于失去了 sessionId。
其他实现途径:手动通过URL传值,隐藏表单传递sessionID, 用文件、数据库等形式保存 sessionID,在跨页过程中手动调用。