JavaWeb 常见的一些必备知识
基础概念
Java基本数据类型:
注意:如果整型字面量的值在-128 到 127 之间,那么不会 new 新的 Integer 对象,而是直接引用常量池中的 Integer 对象。
String是引用类型,底层用char数组实现的。
Java为每一个基本数据类型都引入了对应的包装类型(wrapper class),int 的包装类就是 Integer,从 Java 5 开始引入了自动装箱/拆箱机制,使得二者可以相互转换。
不同的权限修饰符的区别:
new一个对象的过程:
首先看new操作符后面的类型,知道类型后,才能知道要分配多大的内存空间。分配完内存之后,再调用构造函数,填充对象的各个域,这一步叫做对象的初始化,构造方法返回后,一个对象创建完毕,可以把它的引用地址发布到外部,在外部就可以使用这个引用操纵这个对象。
基本运算符(& &&):
https://www.jianshu.com/p/8cf5af30f245
重写overrite 和 重载overload 区别
(1) 重写一般是实现某个接口,并且方法名 参数列表 返回值必须完全相同,声明为final类的方法不能被重写,static的也不能,但能重新声明。
(2) 重载定义为 方法名相同 参数列表不同 与返回值无关
error和exception 的区别
Error和Exception都是Throwable类,区别如下:
(1) Error类一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢出等。遇到这样的错误,建议程序中止
(2) Exception 分为运行时异常(Runtime Exception) 和 受检查的异常(Checked Exception),运行时异常如(NullPointerException、ClassNotFoundException、NumberFormatException、IndexOutOfBoundsException、IllegalArgumentException、ClassCastException、SQLException)等编译能通过,但是一运行就终止了,程序不会处理运行时异常,出现这类异常,程序会终止。而受检查的异常,要么用try catch捕获,要么用throws声明抛出,交给它的父类处理,否则编译不会通过。
throw 和 throws 的区别
值传递和引用传递(址)
(1) 值传递是指对象被值传递,意味着传递了对象的一个副本,即使副本被改变,也不会影响源对象。
(2) 引用传递是指对象被引用传递,意味着传递的并不是实际的对象,而是对象的引用。
(3) 特殊:
new StringBuffer, new String也相当于值传递。因为String 创建之后是不可变的
String,StringBuffer, StringBuilder 的区别是什么?String为什么是不可变的?
1、String是字符串常量,StringBuffer和StringBuilder都是字符串变量。后两者的字符内容可变,而前者创建后内容不可变。
2、String不可变是因为在JDK中String类被声明为一个final类。
3、StringBuffer是线程安全的,而StringBuilder是非线程安全的。
ps:线程安全会带来额外的系统开销,所以StringBuilder的效率比StringBuffer高。如果对系统中的线程是否安全很掌握,可用StringBuffer,在线程不安全处加上关键字Synchronize。
string 用 + 连接起来的语句其实跟底层就是用的StringBuilder。
为什么重写equals时必须重写hashCode方法?
hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode() 函数。
散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)
(1) 如果两个对象相等,则hashcode一定也是相同的
(2) 两个对象相等,对两个对象分别调用equals方法都返回true
(3) 两个对象有相同的hashcode值,它们也不一定是相等的
(4) 因此,equals方法被覆盖过,则hashCode方法也必须被覆盖
(5) hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)
sleep() 和 wait() 的区别:
sleep 方法:是 Thread 类的静态方法,当前线程将睡眠 n 毫秒,线程进入阻塞状态。当睡眠时间到了,会解除阻塞,进行可运行状态,等待 CPU 的到来。睡眠不释放锁(如果有的话);
wait 方法:是 Object 的方法,必须与 synchronized 关键字一起使用,线程进入阻塞状态,当 notify 或者 notifyall 被调用后,会解除阻塞。但是,只有重新占用互斥锁之后才会进入可运行状态。睡眠时,释放互斥锁。
常见的关键字
static
方便在没有创建对象的情况下来进行调用(方法/变量)。
final
JVM和Java应用都会缓存final变量。
被final声明的类不可变,不可被继承;被final修饰的方法不可被重写;final变量经常和static关键字一起使用,作为常量。
transient
java 的transient关键字为我们提供了便利,你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性(比如说password)就不会序列化到指定的目的地中。
volatile
Java 语言中的 volatile 变量可以被看作是一种 “程度较轻的 synchronized”;与 synchronized 块相比,volatile 变量所需的编码较少,并且运行时开销也较少,但是它所能实现的功能也仅是 synchronized 的一部分。
synchronized
底层实现:进入时,执行 monitorenter,将计数器 +1,释放锁 monitorexit 时,计数器-1;当一个线程判断到计数器为 0 时,则当前锁空闲,可以占用;反之,当前线程进入等待状态。
由NaN延伸的问题 (可以通过Double.isFinite() 来判断 这是一个有限的数字)
Double类型的难点:
Infinity 无穷大
-Infinity 无穷小
NaN 不是一个数字
集合
ConcurrentHashMap
ConcurrentHashMap 结合了 HashMap 和 HashTable 二者的优势,迭代器是弱一致性的。HashMap 没有考虑同步,hashtable 考虑了同步的问题。但是 hashtable 在每次同步执行时都要锁住整个结构。 ConcurrentHashMap 锁的方式是微细粒度的。 ConcurrentHashMap将 hash 表分为 16 个桶(默认值),诸如 get,put,remove 等常用操作只锁当前需要用到的桶。
具体实现:
该类包含两个静态内部类 HashEntry 和 Segment;前者用来封装映射表的键值对,后者用来充当锁的角色;
Segment 是一种可重入的锁 ReentrantLock,每个 Segment 守护一个 HashEntry 数组里得元素,当对 HashEntry 数组的数据进行修改时,必须首先获得对应的 Segment 锁。
Vector,ArrayList, LinkedList的区别是什么?
1、Vector、ArrayList都是以类似数组的形式存储在内存中,LinkedList则以链表的形式进行存储。
2、List中的元素有序、允许有重复的元素,Set中的元素无序、不允许有重复元素。
3、Vector线程同步,ArrayList、LinkedList线程不同步。
4、LinkedList适合指定位置插入、删除操作,不适合查找;ArrayList、Vector适合查找,不适合指定位置的插入、删除操作。
5、ArrayList在元素填满容器时会自动扩充容器大小的50%,而Vector则是100%,因此ArrayList更节省空间。
HashTable, HashMap,TreeMap区别?
答: 1、HashTable使用了synchronized关键字,是线程安全的; HashMap非线程安全。
2、HashTable不允许<键,值>有空值,HashMap允许<键,值>有空值。
3、HashTable使用Enumeration,HashMap使用Iterator。
4、HashTable中hash数组的默认大小是11,增加方式的old*2+1,HashMap中hash数组的默认大小是16,增长方式一定是2的指数倍。
5、TreeMap能够把它保存的记录根据键排序,默认是按升序排序。
详见:http://www.zlmind.com/?p=679
HashMap的底层实现:Java8 之前,其底层实现是数组 + 链表实现,Java8 使用了数组 + 链表 + 红黑树实现。
List、Set 和 Map 的初始容量和加载因子, 以及特性?
(1)List
ArrayList 的初始容量是 10;加载因子为 0.5; 扩容增量:原容量的 0.5 倍 +1;一次扩容后长度为 16。
Vector 初始容量为 10,加载因子是 1。扩容增量:原容量的 1 倍,如 Vector 的容量为 10,一次扩容后是容量为 20。
LinkedList
(2)Set
HashSet,初始容量为 16,加载因子为 0.75; 扩容增量:原容量的 1 倍; 如 HashSet 的容量为 16,一次扩容后容量为 32
(3)Map
HashMap,初始容量 16,加载因子为 0.75; 扩容增量:原容量的 1 倍; 如 HashMap 的容量为 16,一次扩容后容量为 32
线程
如何指定多个线程的执行顺序,让10个线程按照顺序打印0123456789?
(1)设定一个 orderNum,每个线程执行结束之后,更新 orderNum,指明下一个要执行的线程。并且唤醒所有的等待线程。
(2)在每一个线程的开始,要 while 判断 orderNum 是否等于自己的要求值!!不是,则 wait,是则执行本线程。
也可以用加锁的方式实现,方法有很多。
线程池有了解吗?
java.util.concurrent.ThreadPoolExecutor 类就是一个线程池。客户端调用 ThreadPoolExecutor.submit(Runnable task) 提交任务,线程池内部维护的工作者线程的数量就是该线程池的线程池大小,有 3 种形态:
当前线程池大小 :表示线程池中实际工作者线程的数量;
最大线程池大小 (maxinumPoolSize):表示线程池中允许存在的工作者线程的数量上限;
核心线程大小 (corePoolSize ):表示一个不大于最大线程池大小的工作者线程数量上限。
如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队;
如果运行的线程等于或者多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不是添加新线程;
如果无法将请求加入队列,即队列已经满了,则创建新的线程,除非创建此线程超出 maxinumPoolSize, 在这种情况下,任务将被拒绝。
IO
JVM
关于JAVA内存模型,一个对象(两个属性,四个方法)实例化100次,现在内存中的存储状态,几个对象,几个属性,几个方法。
答: 由于JAVA中new出来的对象都是放在堆中,所以如果要实例化100次,将在堆中产生100个对象,一般对象与其中的属性、方法都属于一个整体,但如果 属性和方法是静态的,就是用static关键字声明的,那么属于类的属性和方法永远只在内存中存在一份。
内存泄漏和内存溢出
概念:
内存溢出指的是内存不够用了;
内存泄漏是指对象可达,但是没用了。即本该被 GC 回收的对象并没有被回收;
内存泄露是导致内存溢出的原因之一;内存泄露积累起来将导致内存溢出。
内存泄漏的原因分析:
长生命周期的对象引用短生命周期的对象;
没有将无用对象置为 null。
JVM 内存划分?
方法区 :常量、静态变量、JIT(即时编译器) 编译后的代码也都在方法区;
堆(heap)内存 :垃圾回收的主要场所;
程序计数器 : 当前线程执行的字节码的位置指示器;
虚拟机栈 :保存局部变量、基本数据类型变量以及堆内存中某个对象的引用变量;
本地方法栈 :为 JVM 提供使用 native 方法的服务。
注意:以下三个是每个线程私有的内存空间,随线程而生而亡,因此这三个区域的内存分配和回收都是确定的,无需考虑内存回收的问题。
heap和stack有什么区别?
1.申请方式
stack:由系统自动分配
heap:需要程序员自己申请,并指明大小,new Object()形式开辟
2.申请后系统的响应
stack: 只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
heap:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时, 会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。
3.申请大小的限制
stack: 从栈获得的空间较小
heap: 堆是向高地址扩展的数据结构,是不连续的内存区域,堆获得的空间比较灵活。
4.申请效率的比较
stack: 由系统自动分配,速度较快。
heap: 由new分配的内存,一般速度比较慢,而且容易产生碎片,不过用起来最方便。
5. heap和stack中的存储内容
stack: 在函数调用时
heap: 一般是在堆的头部用一个字节存放堆的大小,堆中的具体内容有程序员安排。
类什么时候被初始化?
1. 创建类的实例,也就是new一个对象
2. 访问某个类或接口的静态变量,或者对该静态变量赋值
3. 调用类的静态方法
4. 反射
5. 初始化一个类的子类(会首先初始化子类的父类)
6.JVM启动时标明的启动类,即文件名和类名相同的那个类
垃圾回收算法有哪些?
1. 引用计数(废弃) :原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数。垃圾回收时,只用收集计数为 0 的对象。此算法最致命的是无法处理循环引用的问题;
2. 根搜索算法(使用):
根搜索算法是通过一些“GC Roots”对象作为起点,从这些节点开始往下搜索,搜索通过的路径成为引用链(Reference Chain),当一个对象没有被GC Roots的引用链连接的时候,说明这个对象是不可用的。
(1)标记-清除 :此算法执行分两阶段。第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除;
此算法需要暂停整个应用,同时,会产生内存碎片;
(2)复制算法 :此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中;
此算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,不会出现 “碎片” 问题。当然,此算法的缺点也是很明显的,就是需要两倍内存空间;
(3)标记-整理 :此算法结合了 “标记-清除” 和 “复制” 两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并且把存活对象 “压缩” 到堆的其中一块,按顺序排放。
此算法避免了 “标记-清除” 的碎片问题,同时也避免了 “复制” 算法的空间问题。
(4)分代收集:分代收集是根据对象的存活时间把内存分为新生代和老年代,根据各个代的对象存活时间,每个代采用不同的垃圾回收算法。
root 搜索算法中,哪些可以作为 root?
被启动类(bootstrap 加载器)加载的类和创建的对象;
JavaStack 中的引用的对象 (栈内存中引用的对象);
方法区中静态引用指向的对象;
方法区中常量引用指向的对象;
Native 方法中 JNI 引用的对象。
为什么会有GC机制呢?
1. 安全性考虑
2. 减少内存泄漏
3. 减少程序员工作量
对于Java的GC那些内存需要回收?
GC主要进行回收的内存时JVM重的方法区和堆
GC 什么时候开始?
1.比较主流的判定一个对象已死的方法是:可达性分析,所有生成的对象都是一个称为“GC Roots”的根的子树。
当内存空间还够时,能够保存在内存中;如果进行了垃圾回收之后内存空间仍旧非常紧张,则可以抛弃这些对象。所以根据不同的需求,给出如下四种引用,根据引用类型的不同,GC回收时也会有不同的操作:
强引用 (只要强引用还存在,GC永远不会回收掉被引用的对象)
软引用 (描述止血还有用但非必需的对象,在系统将要发生内存溢出之前,会把这些对象列入回收范围进行二次回收)
弱引用 (每次gc之前都会回收)
虚引用 (不会对其生存时间构成影响)
2.GC 经常发生的区域是堆区,堆区还可以细分为新生代、老年代,新生代还分为一个 Eden 区和两个 Survivor 区。
对象优先在 Eden 中分配,当 Eden 中没有足够空间时,虚拟机将发生一次 Minor GC,因为 Java 大多数对象都是朝生夕灭,所以 Minor GC 非常频繁,而且速度也很快;
Full GC,发生在老年代的 GC,当老年代没有足够的空间时即发生 Full GC,发生 Full GC 一般都会有一次 Minor GC。
大对象直接进入老年代,如很长的字符串数组,虚拟机提供一个;XX:PretenureSizeThreadhold 参数,令大于这个参数值的对象直接在老年代中分配,避免在 Eden 区和两个 Survivor 区发生大量的内存拷贝;
发生 Minor GC 时,虚拟机会检测之前每次晋升到老年代的平均大小是否大于老年代的剩余空间大小,如果大于,则进行一次 Full GC,如果小于,则查看 HandlePromotionFailure 设置是否允许担保失败,如果允许,那只会进行一次 Minor GC,如果不允许,则改为进行一次 Full GC。
类似-Xms、-Xmn 这些参数的含义:
堆内存分配:
JVM 初始分配的内存由-Xms 指定,默认是物理内存的 1/64;
JVM 最大分配的内存由-Xmx 指定,默认是物理内存的 1/4;
默认空余堆内存小于 40% 时,JVM 就会增大堆直到-Xmx 的最大限制;空余堆内存大于 70% 时,JVM 会减少堆直到 -Xms 的最小限制;
因此服务器一般设置-Xms、-Xmx 相等以避免在每次 GC 后调整堆的大小。对象的堆内存由称为垃圾回收器的自动内存管理系统回收。
非堆内存分配:
JVM 使用-XX:PermSize 设置非堆内存初始值,默认是物理内存的 1/64;
由 XX:MaxPermSize 设置最大非堆内存的大小,默认是物理内存的 1/4;
-Xmn2G:设置年轻代大小为 2G;
-XX:SurvivorRatio,设置年轻代中 Eden 区与 Survivor 区的比值。
网络
http和https的区别:
三次握手和四次挥手
设计模式
Java中一般认为有23种设计模式,分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
单例设计模式:
分为懒汉式和饿汉式
观察者模式:
当一个对象变化时,其他依赖该对象的对象都会收到通知,并且随着变化。
其他
JDBC操作数据库流程?
1. Class.forName()加载数据库连接驱动
2.DriverManager.getConnection() 获取数据连接对象
3.根据Sql获取sql会话对象,有2种方式Statement,PreparedStatement
4.执行sql处理的结果集,执行sql前如果有参数值就设置参数值setXXX()
5.关闭结果集,关闭会话,关闭连接
Tomcat,Apache,JBoss的区别?
答: 1、Apache是Http服务器,Tomcat是web服务器,JBoss是应用服务器。
2、Apache解析静态的html文件;Tomcat可解析jsp动态页面、也可充当servlet容器。
详见:http://foohsinglong.iteye.com/blog/1195780
GET,POST区别?
答: 基础知识:Http的请求格式如下。
<request line> 主要包含三个信息:1、请求的类型(GET或POST),2、要访问的资源(如\res\img\a.jif),3、Http版本(http/1.1)
<header> 用来说明服务器要使用的附加信息
<blank line> 这是Http的规定,必须空一行
[<request-body>] 请求的内容数据
区别:
1、Get是从服务器端获取数据,Post则是向服务器端发送数据。
2、在客户端,Get方式通过URL提交数据,在URL地址栏可以看到请求消息,该消息被编码过;Post数据则是放在Html header内提交。
3、对于Get方式,服务器端用Request.QueryString获取变量的值;对用Post方式,服务器端用Request.Form获取提交的数据值。
4、Get方式提交的数据最多1024字节,而Post则没有限制。
5、Get方式提交的参数及参数值会在地址栏显示,不安全,而Post不会,比较安全。
详见:http://www.cnblogs.com/wxf0701/archive/2008/08/17/1269798.html
Session, Cookie区别
答: 1、Session由应用服务器维护的一个服务器端的存储空间;Cookie是客户端的存储空间,由浏览器维护。
2、用户可以通过浏览器设置决定是否保存Cookie,而不能决定是否保存Session,因为Session是由服务器端维护的。
3、Session中保存的是对象,Cookie中保存的是字符串。
4、Session和Cookie不能跨窗口使用,每打开一个浏览器系统会赋予一个SessionID,此时的SessionID不同,若要完成跨浏览器访问数据,可以使用 Application。
5、Session、Cookie都有失效时间,过期后会自动删除,减少系统开销。
Servlet的生命周期
答: 大致分为4部:Servlet类加载-->实例化-->服务-->销毁
1、Web Client向Servlet容器(Tomcat)发出Http请求。
2、Servlet容器接收Client端的请求。
3、Servlet容器创建一个HttpRequest对象,将Client的请求信息封装到这个对象中。
4、Servlet创建一个HttpResponse对象。
5、Servlet调用HttpServlet对象的service方法,把HttpRequest对象和HttpResponse对象作为参数传递给HttpServlet对象中。
6、HttpServlet调用HttpRequest对象的方法,获取Http请求,并进行相应处理。
7、处理完成HttpServlet调用HttpResponse对象的方法,返回响应数据。
8、Servlet容器把HttpServlet的响应结果传回客户端。
其中的3个方法说明了Servlet的生命周期:
1、init():负责初始化Servlet对象。
2、service():负责响应客户端请求。
3、destroy():当Servlet对象推出时,负责释放占用资源。
HTTP 报文包含内容
答: 主要包含四部分:
1、request line
2、header line
3、blank line
4、request body
sendRedirect, foward区别
答: 1、foward是服务器端控制页面转向,在客户端的浏览器地址中不会显示转向后的地址;sendRedirect则是完全的跳转,浏览器中会显示跳转的地址并重 新发送请求链接。
原理:forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后再将这些内容返回给浏览器,浏览器根本不知 道服务器发送的这些内容是从哪来的,所以地址栏还是原来的地址。
redirect是服务器端根据逻辑,发送一个状态码,告诉浏览器重新去请求的那个地址,浏览器会用刚才的所有参数重新发送新的请求。