【干货】java面试之超全基础篇
- 和jre的区别以及各自作用
JRE是java runtime environment的简称,是java程序运行环境,核心内容是JVM和核心类库。 - java的编译和运行
java源码经过编译,生成中间字节码文件.class文件,需要将这些文件放在JVM中运行,JVM是跨平台的,所以java具有跨平台特性。
以下几个知识点相互之间是交融的:都是关于一段程序执行过程中真正发生了什么,包括jvm中内存分配过程
- java的垃圾回收机制
垃圾回收器自动进行,无需显示调用delete方法 - Java支持的数据类型有哪些?什么是自动拆装箱?
八种基本数据类型:
int, double, float, char, boolean, byte, long, short
java有两种数据类型:基本数据类型如上八种,基本类型在栈中占一块内存。
引用数据类型,例如string,其中引用数据类型,JVM中虚拟栈中存的是对象的地址,创建的对象实质在堆中,通过地址来找到堆中的对象的过程,即为引用类型。
引用类型:占两块内存,一块在栈,一块在堆。
自动装箱就是Java编译器在基本数据类型和对应的对象包装类型间的转化,即int转化为Integer,自动拆箱是Integer调用其方法将其转化为int的过程。 - 局部变量和成员变量的重要区别
局部变量没有初始化就打印时,会报错。例如函数中定义的变量
而成员变量,我们不用初始化,java会自动对它初始化。
当变量是引用的时候,java会赋给它null。 - 运行过程内存分析
- code:代码都是放在code segment里面。一旦找到main方法开始执行,其他开始起作用。类是静态的概念,它放在代码区。方法只有一份,执行的时候才占内存(平时它就是放在代码区中)。
- data: 基本数据类型变量,传递的方式是值传递。
值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量. - stack:局部变量分配在stack. 引用类型的地址。在栈中存储的是一个名值对,key是这个变量的名字,value值就是它在堆中的地址。
注意:在栈中的内存里的值不是堆中的地址,而是另一个地址。jvm通过这个地址可以找到堆中的相应对象。
传递方式是引用传递。传递的是该对象地址的一个副本, 并不是原对象本身 。 - heap: 是在程序运行时动态分配的,引用类型创建的对象实例
- 当方法调用结束时,栈内存中的局部变量就没有了。而堆中的,是等垃圾回收机制将其回收。
- Java中的方法覆盖(Overriding)和方法重载(Overloading)是什么意思?
Java中的方法重载发生在同一个类里面两个或者是多个方法的方法名相同但是参数不同的情况。构造方法重载也是这样,有其唯一的参数列表。
方法覆盖是说子类重新定义了父类的方法。方法覆盖必须有相同的方法名,参数列表和返回类型。覆盖者可能不会限制它所覆盖的方法的访问。
- 如何理解java中的类和对象?
- 类就像脑子中抽象的一种事物所具有的特点(一类事物的抽象,拥有静态属性,动态属性来下定义,定义这个类)其中静态属性对应成员变量,动态属性对应方法。
- 对象(也可以叫实例)这一类事物具体的某一种特征的具体的某个东西叫做对象。对象是new出来的class。
- 类可以衍生出多个对象,类可以认为是一个模板,其衍生出的对象应具有他的静态,动态属性。但由于对象的不同其在类中的特点都有,但取值不同,所以区别了每个对象,每个对象都有自己的属性,区别于其他对象不同的属性,静态属性通过构造函数传递。
- 类与类之间的关系
- 关联关系(最不紧密)常用:一个类的方法是另外一个类的对象
- 继承关系(强)
java不支持多继承,但是子接口可以通过implement继承多个父接口,通过实现父接口的方法变相实现多继承。使用extends继承父类的时候,必须重写父类的所有方法。 - 聚合关系(强)
- 实现关系
- 多态:三个必要条件
继承
重写
父类引用指向子类对象
- 面向对象的优点
可复用,可扩展 - 接口和抽象类的区别是什么?
- 接口中所有的方法隐含的都是抽象的。而抽象类则可以同时包含抽象和非抽象的方法。
- 类可以实现很多个接口,但是只能继承一个抽象类
- 进程和线程的区别:
进程是执行着的应用程序,比如电脑同时开着QQ和浏览器两个软件,而线程是进程内部的一个执行序列,比如在qq中你可以收信息,发语音,传文件等等。一个进程可以有多个线程。线程又叫做轻量级进程。 - java中创建线程的几种方式:
有4种方式可以用来创建线程:
- 继承Thread类
- 实现Runnable接口
- 应用程序可以使用Executor框架来创建线程池
- 还有一种方式是实现Callable接口
以上4种方法,实现Runnable接口这种方式更受欢迎,因为这不需要继承Thread类。在应用设计中已经继承了别的对象的情况下,这需要多继承(而Java不支持多继承),只能实现接口。同时,线程池也是非常高效的,很容易实现和使用。
-
线程的几种可用状态
线程5种状态
这里也可能会让你写一个死锁程序
//死锁
public class TestDeadLock implements Runnable {
public int flag = 1;
static Object o1 = new Object(), o2 = new Object();
public static void main(String[] args) {
TestDeadLock td1 = new TestDeadLock();
TestDeadLock td2 = new TestDeadLock();
td1.flag = 1;
td2.flag = 0;
Thread t1 = new Thread(td1);
Thread t2 = new Thread(td2);
t1.start();
t2.start();
}
@Override
public void run() {
System.out.println("flag=" + flag);
if (flag == 1) {
synchronized (o1) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2) {
System.out.println(1);
}
}
}
if (flag == 0) {
synchronized (o2) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1) {
System.out.println(2);
}
}
}
}
}
- 使用多线程的时候,一种非常简单的避免死锁的方式就是:指定获取锁的顺序,并强制线程按照指定的顺序获取锁。因此,如果所有的线程都是以同样的顺序加锁和释放锁,就不会出现死锁了。
- 关键字synchronized怎么使用?
- 同步方法使用关键字 synchronized修饰方法,而同步代码块主要是修饰需要进行同步的代码,用 synchronized(object){代码内容}进行修饰
- java的集合类
-
Collection:代表一组对象,每一个对象都是它的子元素。
-
Set:不包含重复元素的Collection。
HashSet是由一个hash表来实现的,因此,它的元素是无序的。add(),remove(),contains()方法的时间复杂度是O(1)。
另一方面,TreeSet是由一个树形的结构来实现的,它里面的元素是有序的。因此,add(),remove(),contains()方法的时间复杂度是O(logn)。 -
List:有顺序的collection,并且可以包含重复元素。linkedlist和arraylist两种实现
ArrayList和LinkedList都实现了List接口,他们有以下的不同点:
ArrayList是基于索引的数据接口,它的底层是数组。它可以以O(1)时间复杂度对元素进行随机访问。与此对应,LinkedList是以元素列表的形式存储它的数据,每一个元素都和它的前一个和后一个元素链接在一起,在这种情况下,查找某个元素的时间复杂度是O(n)。
相对于ArrayList,LinkedList的插入,添加,删除操作速度更快,因为当元素被添加到集合任意位置的时候,不需要像数组那样重新计算大小或者是更新索引。
LinkedList比ArrayList更占内存,因为LinkedList为每一个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素。 -
Map:可以把键(key)映射到值(value)的对象,键不能重复
HashMap工作原理:Java中的HashMap是以键值对(key-value)的形式存储元素的。HashMap需要一个hash函数,它使用hashCode()和equals()方法来向集合/从集合添加和检索元素。当调用put()方法的时候,HashMap会计算key的hash值,然后把键值对存储在集合中合适的索引上。如果key已经存在了,value会被更新成新值。HashMap的一些重要的特性是它的容量(capacity),负载因子(load factor)和扩容极限(threshold resizing)。 -
克隆(cloning)或者是序列化(serialization)的语义和含义是跟具体的实现相关的。因此,应该由集合类的具体实现来决定如何被克隆或者是序列化。
-
权衡使用无序数组或者有序数组的时候,主要看查找和插入的两种操作哪个用的多。如果大量查找,使用有序,反之无序。
容器API
-
迭代器
ListIterator实现了Iterator接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等。 -
Comparable和Comparator接口
Comparable & Comparator 都是用来实现集合中元素的比较、排序的,只是 Comparable 是在集合内部定义的方法实现的排序,Comparator 是在集合外部实现的排序。如想实现排序,就需要在集合外定义 Comparator 接口的方法或在集合内实现 Comparable 接口的方法。
可以将自己的类继承comparable接口,重写compareto方法,实现自定义的类的比较 -
Java中的两种异常类型是什么?他们有什么区别?
Exception和Error都是Throwable的子类。Exception用于用户程序可以捕获的异常情况。Error定义了不期望被用户程序捕获的异常。
Exception又包含了运行时异常(RuntimeException, 又叫非检查异常)和非运行时异常(又叫检查异常)
(1) Error是程序无法处理了, 如果OutOfMemoryError、OutOfMemoryError等等, 这些异常发生时, java虚拟机一般会终止线程 .
(2) 运行时异常都是RuntimeException类及其子类,如 NullPointerException、IndexOutOfBoundsException等, 这些异常是不检查的异常, 是在程序运行的时候可能会发生的, 所以程序可以捕捉, 也可以不捕捉. 这些错误一般是由程序的逻辑错误引起的, 程序应该从逻辑角度去尽量避免.
(3) 检查异常是运行时异常以外的异常, 也是Exception及其子类, 这些异常从程序的角度来说是必须经过捕捉检查处理的, 否则不能通过编译. 如IOException、SQLException等。异常处理过后,Exception对象会在下一个垃圾回收过程中被回收掉。 -
throw和throws有什么区别?
1、Throw用于方法内部,Throws用于方法声明上
2、Throw后跟异常对象,Throws后跟异常类型
3、Throw后只能跟一个异常对象,Throws后可以一次声明多种异常类型
- JDBC相关知识
- class.forName作用:初始化参数指定的类,并且返回此类对应的Class 对象
- Driver在JDBC中扮演的角色: JDBC驱动提供了特定厂商对JDBC API接口类的实现,驱动必须要提供java.sql包下面这些类的实现:Connection, Statement, PreparedStatement,CallableStatement, ResultSet和Driver。
- 数据库连接池作用:数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/bbs?characterEncoding=utf8&useSSL=false";
Connection connection = DriverManager.getConnection(url,"root","332999");
- java面向分布式的一些设计
- RMI
- DGC
- 序列化
Java提供了一种叫做对象序列化的机制,他把对象表示成一连串的字节,里面包含了对象的数据,对象的类型信息,对象内部的数据的类型信息等等。实现序列化和反序列化的对象必须实现serializable接口。序列化可以看成是为了把对象存储在磁盘上或者是从磁盘上读出来并重建对象而把对象扁平化的一种方式。反序列化是把对象从扁平状态转化成活动对象的相反的步骤。
这一块都是JAVAEE知识点
- Servlet
- Servlet是用来处理客户端请求并产生动态网页内容的Java类。Servlet主要是用来处理或者是存储HTML表单提交的数据,产生动态内容,在无状态的HTTP协议下管理状态信息。
- 所有的Servlet都必须要实现的核心的接口是javax.servlet.Servlet。每一个Servlet都必须要直接或者是间接实现这个接口,或者是继承javax.servlet.GenericServlet或者javax.servlet.http.HTTPServlet。最后,Servlet使用多线程可以并行的为多个请求服务。
- 生命周期
Servlet程序是由WEB服务器调用,web服务器收到客户端的Servlet访问请求后:
①Web服务器首先检查是否已经装载并创建了该Servlet的实例对象。如果是,则直接执行第④步,否则,执行第②步。
②装载并创建该Servlet的一个实例对象。(构造方法)
③调用Servlet实例对象的init()方法。
④创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。(为每个请求单独调用service方法)
⑤WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。 - doGet()方法和doPost()方法有什么区别?
doGet:GET方法会把名值对追加在请求的URL后面。因为URL对字符数目有限制,进而限制了用在客户端请求的参数值的数目。并且请求中的参数值是可见的,因此,敏感信息不能用这种方式传递。
doPOST:POST方法通过把请求参数值放在请求体中来克服GET方法的限制,因此,可以发送的参数的数目是没有限制的。最后,通过POST请求传递的敏感信息对外部客户端是不可见的。
- HTTP
- HTTP的请求报文的组成:
请求方法 + 请求的资源的URI + 协议版本 + 可选的请求首部字段 + 内容实体。 - HTTP的响应报文的组成:
协议版本 + 状态码 + 用于解释状态码的原因短语 + 可选的响应首部字段 + 实体主体。
状态码(Status Code):描述了响应的状态。可以用来检查是否成功的完成了请求。请求失败的情况下,状态码可用来找出失败的原因。如果Servlet没有返回状态码,默认会返回成功的状态码HttpServletResponse.SC_OK。
HTTP头部(HTTP Header):它们包含了更多关于响应的信息。比如:头部可以指定认为响应过期的过期日期,或者是指定用来给用户安全的传输实体内容的编码格式。如何在Serlet中检索HTTP的头部看这里。
主体(Body):它包含了响应的内容。它可以包含HTML代码,图片,等等。主体是由传输在HTTP消息中紧跟在头部后面的数据字节组成的。
-
Cookie和Session
cookie是Web服务器发送给浏览器的一块信息。浏览器会在本地文件中给每一个Web服务器存储cookie。以后浏览器在给特定的Web服务器发请求的时候,同时会发送所有为该服务器存储的cookie。下面列出了session和cookie的区别:
cookie是客户端的,session是服务端的。cookie存储于客户端,记录web服务器的信息,每次上网时都会先查看对应的cookie信息,比如购物时,使用cookie记录购物车信息。session是服务端记录客户机的信息,SessionID是session的唯一标识,使用session可以记录客户端的请求等。
无论客户端浏览器做怎么样的设置,session都应该能正常工作。因为客户端无法禁用服务端的session。
在存储的数据量方面session和cookies也是不一样的。session能够存储任意的Java对象,cookie只能存储String类型的对象。 -
sendRedirect()和forward()方法有什么区别?
forward是服务器内部的跳转,浏览器的地址栏不会发生变化,同时可以把request和response传递给后一个请求。sendRedirect()是浏览器方面的跳转,要发送两次请求,地址栏也会发生变化,同时request和response也会发生变化,重新生成新的对象。一般认为sendRedirect()比forward()要慢。 -
jsp
- 处理jsp请求
客户端通过浏览器发送jsp请求,服务器端接受到请求后,判断是否是第一次请求该页面,或者该页面是否改变,若是,服务器将jsp页面翻译为servlet,jvm将servlet编译为.class文件,字节码文件加载到服务器内存上执行,服务器将处理结果以.html页面的形式返回给客户端,若该页面不是第一次请求,则省略翻译和编译的步骤,直接执行。 - JSP页面中的隐含对象:
application
page
request
response
session
exception
out
config
pageContext - JSP表达式
表达式是在<%=和%>这两个标签之间定义,JSP表达式是Web服务器把脚本语言表达式的值转化成一个String对象,插入到返回给客户端的数据流中。 - JSP声明
声明(declaration):在jsp程序声明合法的全局变量和方法
语法:<%!declaration;[declaration]...%> - JSP动作(action)
JSP动作以XML语法的结构来控制Servlet引擎的行为。当JSP页面被请求的时候,JSP动作会被执行。它们可以被动态的插入到文件中,重用JavaBean组件,转发用户到其他的页面,或者是给Java插件产生HTML代码。下面列出了可用的动作:
jsp:include-当JSP页面被请求的时候包含一个文件。
jsp:useBean-找出或者是初始化Javabean。
jsp:setProperty-设置JavaBean的属性。
jsp:getProperty-获取JavaBean的属性。
jsp:forward-把请求转发到新的页面。
jsp:plugin-产生特定浏览器的代码。 - JSP指令
Directive是当JSP页面被编译成Servlet的时候,JSP引擎要处理的指令。Directive用来设置页面级别的指令,从外部文件插入数据,指定自定义的标签库。Directive是定义在 <%@ 和 %>之间的。下面列出了不同类型的Directive:
包含指令(Include directive):用来包含文件和合并文件内容到当前的页面。
页面指令(Page directive):用来定义JSP页面中特定的属性,比如错误页面和缓冲区。
Taglib指令: 用来声明页面中使用的自定义的标签库。
面向对象语言的特性
代码开发模块化,更易维护和修改。
代码复用。
增强代码的可靠性和灵活性。
增加代码的可理解性。
面向对象编程有很多重要的特性,比如:封装,继承,多态和抽象。