JAVA 基础
1、Java中有三大变量
实例变量:在堆中。
静态变量:在方法区。
局部变量:在栈中。
以上三大变量中:
局部变量永远都不会存在线程安全问题。
因为局部变量不共享。(一个线程一个栈。)
局部变量在栈中。所以局部变量永远都不会共享。
实例变量在堆中,堆只有1个。
静态变量在方法区中,方法区只有1个。
堆和方法区都是多线程共享的,所以可能存在线程安全问题。
局部变量+常量:不会有线程安全问题。
成员变量:可能会有线程安全问题。
如果使用局部变量的话:建议使用:StringBuilder。因为局部变量不存在线程安全问题。
选择StringBuilder。StringBuffer效率比较低。
JVM
1、关于JDK中自带的类加载器
什么是类加载器?
专门负责加载类的命令/工具。
ClassLoader
JDK中自带了3个类加载器
启动类加载器:rt.jar
扩展类加载器:ext/*.jar
应用类加载器:classpath
假设有这样一段代码:
String s = "abc";
代码在开始执行之前,会将所需要类全部加载到JVM当中。
通过类加载器加载,看到以上代码类加载器会找String.class
文件,找到就加载,那么是怎么进行加载的呢?
首先通过“启动类加载器”加载。
注意:启动类加载器专门加载:C:\Program Files\Java\jdk1.8.0_101\jre\lib\rt.jar
rt.jar中都是JDK最核心的类库。
如果通过“启动类加载器”加载不到的时候,
会通过"扩展类加载器"加载。
注意:扩展类加载器专门加载:C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\*.jar
如果“扩展类加载器”没有加载到,那么
会通过“应用类加载器”加载。
注意:应用类加载器专门加载:classpath中的类。
java中为了保证类加载的安全,使用了双亲委派机制。
优先从启动类加载器中加载,这个称为“父”
“父”无法加载到,再从扩展类加载器中加载,这个称为“母”。双亲委派。
如果都加载不到,才会考虑从应用类加载器中加载。直到加载到为止。
集合
Set 接口
1、java中什么时候需要重写equals和hashCode方法,以及为什么重写?
/*因为Object中默认的equals方法,内部还是使用==来比较对象在内存中的地址,所以结果位false*/
/*如果重写了equals方法,那么如果两个对象的属性值相同,那么程序会在第三步判断中返回true,
* hashCode()方法,它是一个本地方法,底层是运用对象的内存地址通过哈希函数来计算的。
* 问题:为什么重写equals时,一定需要重写hashCode()方法?
* 因为重写了equals()之后,判断两个属性值相同的对象时,会返回true,如果没有重写hashCode(),
* 那么程序还是按照默认的使用内存地址的方法去计算,那么一定会返回false,
* java中规定:两个对象的equals()相同,hashCode一定相同。hashCode相同,但equals不一定相同
* 所以在重写equals时,一定需要重写hashCode。*/
/*什么时候需要重写类的equals()和hashCode()方法?
* 1.当我们需要重新定义两个对象是否相等的条件时,需要进行重写。比如通常情况下,我们认为两个不同对象的某些属性值相同时,
* 就认为这两个对象是相同的。
* 例如:我们在HashMap中添加元素时,我们认为当key相同时,两个元素就相同,但是默认的Object中的equals(),只是单纯的比较两个元素 的内存地址是否相同,不能满足我们的要求,所以需要重写。
* 2.当我们自定义一个类时,想要把它的实例保存在集合时,就需要重写equals()和hashCode()方法。*/
美团JAVA一面面经
2、Comparable 和 Comparator 的区别?
//Comparable 是默认的比较接口, Comparable 和需要比较的对象紧密结合到一起
//Comparator 可以分离比较规则,所以它更具灵活性
一个类实现了 Camparable 接口则表明这个类的对象之间是可以相互比较的,
这个类对象组成的集合就可以直接使用 sort 方法排序。
Comparator 可以看成一种算法的实现,将算法和数据分离, Comparator 也可以在下面两种环境下使用:
1、类的没有考虑到比较问题而没有实现 Comparable,可以通过 Comparator 来实现排序而不必改变对象本身
2、可以使用多种排序标准,比如升序、降序等
详解Java中Comparable和Comparator接口的区别
3、什么是序列化和反序列化?
对象流可以将 Java 对象转换成二进制写入磁盘,这个过程通常叫做序列化,
并且还可以从磁盘读出完整的 Java 对象,而这个过程叫做反序列化。
对象流主要包括: ObjectInputStream 和 ObjectOutputStream
多线程并发
1、什么时候数据在多线程并发的环境下会存在安全问题呢?
三个条件:
条件1:多线程并发。
条件2:有共享数据。
条件3:共享数据有修改的行为。
满足以上3个条件之后,就会存在线程安全问题。
2、怎么解决线程安全问题呢?
使用“线程同步机制”
当多线程并发的环境下,有共享数据,并且这个数据还会被修改,
此时就存在线程安全问题,怎么解决这个问题?
线程排队执行。(不能并发)用排队执行解决线程安全问题。
这种机制被称为:线程同步机制。
专业术语叫做:线程同步,实际上就是线程不能并发了,线程必须排队执行。
线程同步就是线程排队了,线程排队了就会牺牲一部分效率,没办法,数据安全
第一位,只有数据安全了,我们才可以谈效率。
线程同步这块,涉及到这两个专业术语:
异步编程模型:
线程t1和线程t2,各自执行各自的,t1不管t2,t2不管t1,谁也不需要等谁,
这种编程模型叫做:异步编程模型。
其实就是:多线程并发(效率较高。)
异步就是并发。
同步编程模型:
线程t1和线程t2,在线程t1执行的时候,必须等待t2线程执行结束,
或者说在t2线程执行的时候,必须等待t1线程执行结束,
两个线程之间发生了等待关系,这就是同步编程模型。
效率较低。线程排队执行。
同步就是排队。
聊一聊,我们以后开发中应该怎么解决线程安全问题?
是一上来就选择线程同步吗?synchronized
不是,synchronized会让程序的执行效率降低,用户体验不好。
系统的用户吞吐量降低。用户体验差。在不得已的情况下再选择
线程同步机制。
第一种方案:尽量使用局部变量代替“实例变量和静态变量”。
第二种方案:如果必须是实例变量,那么可以考虑创建多个对象,这样
实例变量的内存就不共享了。(一个线程对应1个对象,100个线程对应100个对象,
对象不共享,就没有数据安全问题了。)
第三种方案:如果不能使用局部变量,对象也不能创建多个,这个时候
就只能选择synchronized了。线程同步机制。
JAVA WEB
1、多个Servlet之间调用规则:
1.前提条件:
某些来自于浏览器发送请求,往往需要服务端中多个Servlet协同处理。 但是浏览器一次只能访问一个Servlet,
导致用户需要手动通过浏览器发起多次请求才能得到服务。这样增加用户获得服务难度,导致用户放弃访问当前网站。
2.提高用户使用感受规则:
无论本次请求涉及到多少个Servlet,用户只需要【手动】通知浏览器发起 一次请求即可
3.多个Servlet之间调用规则:
1)重定向解决方案
2)请求转发解决方案
2、多个Servlet之间数据共享实现方案
数据共享:OneServlet工作完毕后,将产生数据交给TwoServlet来使用
Servlet规范中提供四种数据共享方案
1.ServletContext接口 【全局作用域对象】
介绍:如果两个Servlet来自于同一个网站。彼此之间通过网站的 ServletContext 实例对象实现数据共享
原理:每一个网站都存在一个全局作用域对象。这个全局作用域对象【相当于】一个Map.
在这个网站中OneServlet可以将一个数据存入到全局作用域对象,当前网站中其他
Servlet此时都可以从全局作用域对象得到这个数据进行使用。
生命周期:*****全局作用域对象生命周期贯穿网站整个运行期间*****
1)在Http服务器启动过程中,自动为当前网站在内存中创建一个全局作用域对象
2)在Http服务器运行期间时,一个网站只有一个全局作用域对象
3)在Http服务器运行期间,全局作用域对象一直处于存活状态
4)在Http服务器准备关闭时,负责将当前网站中全局作用域对象进行销毁处理
OneServlet{
public void doGet(HttpServletRequest request,HttpServletResponse response){
//1.通过【请求对象】向Tomcat索要当前网站中【全局作用域对象】
ServletContext application = request.getServletContext();
//2.将数据添加到全局作用域对象作为【共享数据】
application.setAttribute("key1",数据)
}
}
TwoServlet{
public void doGet(HttpServletRequest request,HttpServletResponse response){
//1.通过【请求对象】向Tomcat索要当前网站中【全局作用域对象】
ServletContext application = request.getServletContext();
//2.从全局作用域对象得到指定关键字对应数据
Object 数据 = application.getAttribute("key1");
}
}
2.Cookie类
介绍: 如果两个Servlet来自于同一个网站,并且为同一个浏览器/用户提供服务,此时借助于Cookie对象进行数据共享
Cookie存放当前用户的私人数据,在共享数据过程中提高服务质量
在现实生活场景中,Cookie相当于用户在服务端得到【会员卡】
原理:用户通过浏览器第一次向MyWeb网站发送请求申请OneServlet。
OneServlet在运行期间创建一个Cookie存储与当前用户相关数据
OneServlet工作完毕后,【将Cookie写入到响应头】交还给当前浏览器。
浏览器收到响应响应包之后,将cookie存储在浏览器的缓存
一段时间之后,用户通过【同一个浏览器】再次向【myWeb网站】发送请求申请TwoServlet时。
【浏览器需要无条件的将myWeb网站之前推送过来的Cookie,写入到请求头】发送过去
此时TwoServlet在运行时,就可以通过读取请求头中cookie中信息,得到OneServlet提供的共享数据
生命周期:在默认情况下,Cookie对象存放在浏览器的缓存中。因此只要浏览器关闭,Cookie对象就被销毁掉
在手动设置情况下,可以要求浏览器将接收的Cookie存放在客户端计算机上硬盘上,同时需要指定Cookie在硬盘上存活时间。
在存活时间范围内,关闭浏览器关闭客户端计算机,关闭服务器,都不会导致Cookie被销毁。
在存活时间到达时,Cookie自动从硬盘上被删除
OneServlet{
public void doGet(HttpServletRequest request,HttpServletResponse resp){
//1.创建一个cookie对象,保存共享数据(当前用户数据)
Cookie card = new Cookie("key1","abc");
Cookie card1= new Cookie("key2","efg");
****cookie相当于一个map
****一个cookie中只能存放一个键值对
****这个键值对的key与value只能是String
****键值对中key不能是中文
//2.【发卡】将cookie写入到响应头,交给浏览器
resp.addCookie(card);
resp.addCookie(card1)
}
}
浏览器/用户 <---------响应包 【200】
【cookie: key1=abc; key2=eft】
【】
【处理结果】
浏览器向myWeb网站发送请求访问TwoServlet---->请求包 【url:/myWeb/two method:get】
【
请求参数:xxxx
Cookie key1=abc;key2=efg
】
【】
【】
TwoServlet{
public void doGet(HttpServletRequest request,HttpServletResponse resp){
//1.调用请求对象从请求头得到浏览器返回的Cookie
Cookie cookieArray[] = request.getCookies();
//2.循环遍历数据得到每一个cookie的key 与 value
for(Cookie card:cookieArray){
String key = card.getName(); 读取key "key1"
Strign value = card.getValue();读取value "abc"
提供较好的服务。。。。。。。。
}
}
}
3.HttpSession接口【会话作用域对象】
介绍:如果两个Servlet来自于同一个网站,并且为同一个浏览器/用户提供服务
此时借助于HttpSession对象进行数据共享
Http服务器如何将用户与HttpSession关联起来:cookie
与Cookie的区别:下一题
生命周期: 用户与HttpSession关联时使用的Cookie只能存放在浏览器缓存中.
在浏览器关闭时,意味着用户与他的HttpSession关系被切断
由于Tomcat无法检测浏览器何时关闭,因此在浏览器关闭时并不会导致Tomcat将浏览器关联的HttpSession进行销毁
为了解决这个问题,Tomcat为每一个HttpSession对象设置【空闲时间】
这个空闲时间默认30分钟,如果当前HttpSession对象空闲时间达到30分钟
此时Tomcat认为用户已经放弃了自己的HttpSession,此时Tomcat就会销毁掉这个HttpSession
OneServlet{
public void doGet(HttpServletRequest request,HttpServletResponse response){
//1.调用请求对象向Tomcat索要当前用户在服务端的私人储物柜
HttpSession session = request.getSession();
//2.将数据添加到用户私人储物柜
session.setAttribute("key1",共享数据)
}
}
浏览器访问/myWeb中TwoServlet
TwoServlet{
public void doGet(HttpServletRequest request,HttpServletResponse response){
//1.调用请求对象向Tomcat索要当前用户在服务端的私人储物柜
HttpSession session = request.getSession();
//2.从会话作用域对象得到OneServlet提供的共享数据
Object 共享数据 = session.getAttribute("key1");
}
}
4.HttpServletRequest接口【请求作用域对象】
介绍:在同一个网站中,如果两个Servlet之间通过【请求转发】方式进行调用,
彼此之间共享同一个请求协议包。而一个请求协议包只对应一个请求对象
因此servlet之间共享同一个请求对象,此时可以利用这个请求对象在两个Servlet之间实现数据共享
OneServlet{
public void doGet(HttpServletRequest req,HttpServletResponse response){
//1.将数据添加到【请求作用域对象】中attribute属性
req.setAttribute("key1",数据); //数据类型可以任意类型Object
//2.向Tomcat申请调用TwoServlet
req.getRequestDispatcher("/two").forward(req,response)
}
}
TwoServlet{
public void doGet(HttpServletRequest req,HttpServletResponse response){
//从当前请求对象得到OneServlet写入到共享数据
Object 数据 = req.getAttribute("key1");
}
}
3、HttpSession 与 Cookie 区别
1)存储位置: 一个在天上,一个在地下
Cookie:存放在客户端计算机(浏览器内存/硬盘)
HttpSession:存放在服务端计算机内存
2)数据类型:
Cookie对象存储共享数据类型只能是String
HttpSession对象可以存储任意类型的共享数据Object
3) 数据数量:
一个Cookie对象只能存储一个共享数据
HttpSession使用map集合存储共享数据,所以可以存储任意数量共享数据
4)参照物:
Cookie相当于客户在服务端【会员卡】
HttpSession相当于客户在服务端【私人保险柜】