剑指Offer——J2EE基础知识点储备_j2ee实用基础

	}
}

}


在上面的代码中,循环申请object对象,并添加到`Vector`中,然后设置`object=null`(就是清除栈中引用变量object),但是这些对象被`vector`引用着,必然不能被`GC`回收,造成内存泄露。因此要释放这些对象,还需要将它们从`vector`中删除,最简单的方法就是将`vector=null`,清空集合类中的引用。


* **监听器**:在`java`编程中,我们都需要和监听器打交道,通常一个应用中会用到很多监听器,我们会调用一个控件,诸如`addxxxListener()`等方法来增加监听器,但往往释放的时候却没有去删除这些监听器,从而增加内存泄露的机会。  
 ![这里写图片描述](https://img-blog.csdnimg.cn/img_convert/ab7fd6f6d532ebff8d7b8f765953e95e.png)  
 注意⚠️:对于物理连接,一般把这些代码放在`spring`容器中,避免内存泄露。


#### 2.3 匿名内部类是什么?如何访问在其外面定义的变量?


**匿名内部类是什么?**


* 匿名内部类没有访问修饰符;
* 当所在方法的形参需要被匿名内部类使用,那么这个形参类型就必须为`final`;
* 匿名内部类没有构造方法,因为它连名字都没有何来构造方法;


**如何访问在其外面定义的变量?**  
 当所在方法的形参需要被匿名内部类使用,那么这个形参类型就必须为`final`。


### 三、J2EE 基础


#### 3.1 servlet 生命周期


`Servlet`接口定义了5个方法,其中前三个方法与`servlet`生命周期有关:



> 
> 1. `void init(ServletConfig config);`
> 2. `Void service(ServletRequest req, ServletResponse resp);`
> 3. `Void destory();`
> 4. `java.lang.String getServletInfo();`
> 5. `ServletConfig getServletConfig();`
> 
> 
> 


Web容器加载`servlet`并将其实例化后,servlet生命周期开始,容器运行其`init()`方法进行`servlet`的初始化;请求到达时调用`servlet`的`service()`方法,`service()`方法会根据需要调用与请求对应的`doGet`或`doPost`等方法;当服务器关闭或项目被卸载时,服务器会将`servlet`实例销毁,此时会调用`servlet`的`destroy()`方法。


Servlet的生命周期分为5个阶段:**加载、创建、初始化、处理客户请求、卸载**。



> 
> 1. **加载**:容器通过类加载器使用`servlet`类对应的文件加载`servlet`;
> 2. **创建**:通过调用`servlet`构造函数创建一个`servlet`对象;
> 3. **初始化**:调用`init`方法初始化;
> 4. **处理客户请求**:每当有一个客户请求,容器会创建一个线程来处理客户请求;
> 5. **卸载**:调用`destroy()`方法让`servlet`自己释放其占用的资源;
> 
> 
> 


#### 3.2 保存会话状态方式,有哪些区别?


由于`http`协议本身是无状态的,服务器为了区分不同的用户,就需要对用户会话进行跟踪,简单的说就是为用户进行登记,为用户分配唯一的id,下一次用户在请求中包含此id,服务器据此判断到底是哪一个用户。


1. **URL重写**:在`url`中添加用户会话信息作为请求参数,或者将唯一的会话id添加到url结尾以标识一个会话。
2. **设置表单隐藏域**:将和会话跟踪相关的字段添加到隐藏表单域中,这些信息不会在浏览器中显示但是提交表单时会提交给服务器。


这两种方式很难处理跨越多个页面的信息传递,因为如果每次都要修改`url`或则在页面中添加隐式表单域来存储用户会话相关信息,事情将变得非常麻烦。


`html5`中可以使用`web storage`技术通过`javaScript`来保存数据,例如可以使用`localStorage`和`sessionStroage`来保存用户会话的信息,也能够实现会话跟踪。


#### 3.3 cookie 和 session 的区别


* `session`在服务器端,`cookie`在客户端(浏览器);
* `Session`的运行依赖`session id`, 而`session id`是存在`cookie`中的,也就是说,如果浏览器禁止了`cookie`,同时`session`也会失效(但是可以通过其它方式实现,比如在`url`中传递`session id`);
* `Session`可以放在文件、数据库或内存中都可以;
* 用户验证一般会用`session`;
* `Cookie`不是很安全,别人可以分析存在本地的`cookie`并进行`cookie`欺骗,考虑到安全应当使用`session`;
* `Session`会在一定时间内保存在服务器上,当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用`cookie`;
* 单个`cookie`保存的数据不能超过4k,很多浏览器都限制一个站点最多保存20个`cookie`。


#### 3.4 web.xml中标签加载顺序


加载顺序:**context-param -> listener -> filter -> servlet**


(1)`tomcat`加载应用程序都是从读取`web.xml`文件开始的。读`web.xml`的核心就是从读节点开始`listener>filter>servlet`。其实还有一个`<context-param>`这个标签是提供应用程序上下文信息(可以写在任意位置)。因此加载的顺序是:**context-param---->listener---->filter----->servlet**


##### 3.4.1 各个标签的简单说明


1. `context-param` 加载上下文的配置文件(主要是`java bean`)


![这里写图片描述](https://img-blog.csdnimg.cn/img_convert/80bae4e53423d3e895f270592990c97f.png)


2. `listener`监听器  
 ![这里写图片描述](https://img-blog.csdnimg.cn/img_convert/0430d1f1149f66a06f63435985dbf870.png)  
 通过监听器的通配符,将配置信息加载到`spring`容器中。还有一般事务写在`spring`(业务逻辑层),**事务**的启动也是靠`listener`。
3. `filter`过滤器  
 ![这里写图片描述](https://img-blog.csdnimg.cn/img_convert/49f00f0c2fdd5a1141e088ce8549a172.png)  
 **`Struts`就是依靠过滤器来实现`action`的访问。**
4. `servlet`是我们的后台程序`java`类。


#### 3.5 页面传值方法



> 
> 1. 表单传值,通过`request`根据参数取值;
> 2. 直接在地址栏输入页面的地址,在后面加`?`,然后把要传的参数及值写在后面,若有多个用`&`隔开;
> 3. 在`form`中还可以用`hidden`(隐藏);
> 
> 
> 


### 四、SSH三大框架


#### 4.1 Struts


##### 4.1.1 struts2 工作流程:



> 
> 1. 用户发出`http`请求;
> 2. `tomcat`读取`web.xml`配置文件;
> 3. 通过过滤器读取`Struts.xml`配置文件至内存;
> 4. 根据`web.xml`配置,该请求被核心控制器`FilterDispatcher`接收(拦截这些请求交由`Struts.xml`处理);
> 5. `struts.xml`配置接收到这些请求,找到需要调用的`action`类和方法,并通过**依赖注入**方式,将值注入`action`(到对应的`servlet`中进行处理);
> 6. `action`调用业务逻辑组件处理业务逻辑,比如基本的数据增删改查、其他的业务处理;
> 7. `action`执行完毕,根据`struts.xml`中的配置找到对应的返回结果`result`,并跳转到相应页面;
> 
> 
> 


![这里写图片描述](https://img-blog.csdnimg.cn/img_convert/627bf59f35ce544d72c4f151ef72b7a6.png)


##### 4.1.2 struts2 与 struts1 的区别


**从`action`类上分析**:



> 
> * 1.`Struts1`要求`Action`类继承一个抽象基类。`Struts1`的一个普遍问题是使用抽象类编程而不是接口。
> * `Struts 2` `Action`类可以实现一个`Action`接口,也可实现其他接口,使可选和定制的服务成为可能。`Struts2`提供一个`ActionSupport`基类去实现常用的接口。`Action`接口不是必须的,任何有`execute`标识的`POJO`对象(就是简单的`javabean`)都可以用作`Struts2`的`Action`对象。
> 
> 
> 


**从`Servlet`依赖分析**:



> 
> * `Struts1 Action` 依赖于`Servlet API` ,因为当一个`Action`被调用时,`HttpServletRequest` 和`HttpServletResponse` 被传递给`execute`方法。
> * `Struts 2 Action`不依赖于容器,允许`Action`脱离容器单独被测试。如果需要,`Struts2 Action`仍然可以访问初始的`request`和`response`。但是,其他的元素减少或者消除了直接访问`HttpServetRequest` 和 `HttpServletResponse`的必要性。
> 
> 
> 


**从`action`线程模式分析**:



> 
> * `Struts1 Action`是单例模式并且必须是线程安全的,因为仅有`Action`的一个实例来处理所有的请求。单例策略限制了`Struts1 Action`能做的事,并且要在开发时特别小心。`Action`资源必须是线程安全的或同步的。
> * `Struts2 Action`对象为每一个请求产生一个实例,因此不存在线程安全问题。(实际上,`servlet`容器给每个请求产生许多可丢弃的对象,并且不会导致性能和垃圾回收问题)
> 
> 
> 


#### 4.2 Spring


**IOC容器的加载过程?**


1. 创建`IOC`配置文件的抽象资源;
2. 创建一个`BeanFactory`;
3. 把读取配置信息的`BeanDefinitionReader`,这里是`XmlBeanDefinitionReader`配置给`BeanFactory`;
4. 从定义好的资源位置读入配置信息,具体的解析过程由`XmlBeanDefinitionReade`来完成,这样完成整个载入`bean`定义的过程;


**说明**


1. `XmlBeanFactory`,`ClasspathXmlApplicationContext`是`IOC`容器实现的两种方式,`XmlBeanFactory`是基本的`IOC`容器的实现,`ApplicationContext`实现的`IOC`容器可以提供很多个高级特性(`IOC`容器的加载主要以`ApplicationContext`实现。)
2. `SpringIOC`容器管理了我们定义的各种`Bean`对象及其相互关系,`Bean`对象在`Spring`实现中是以`BeanDefinition`来描述;


##### 4.2.1 IOC 容器的实现过程


1. `IOC` 容器的初始化过程(定义各种`bean`对象以及`bean`对象间的关系),整个过程分成`resource`的定位、载入和解析。
2. `IOC`容器的依赖注入,`Bean`的名字通过`ApplicationContext`容器,`getbean`获得对应的`bean`。


##### 4.2.2 Spring IOC的理解


**传统创建对象的缺陷**:  
 传统创建对象,通过关键字`new`获得。但是如果在不同的地方创建很多相同的对象,不仅占用很大的内存,同时影响性能。


**改进思路**:  
 仿造工厂模式,需要什么对象,直接拿过来,按照用户需求,组装相应的产品。以后不再通过`new`获取对象,而是需要什么对象,就在`spring`容器中取就行了。这就是将创建对象这个行为,进行**控制反转**,交由容器去完成。什么时候需要这些对象,再通过**依赖注入**的方式去获取这些对象。


**依赖注入的几种方式**


1. **注解方式注入**

 
<?xml version="1.0" encoding="UTF-8"?>

context:annotation-config/

<context:component-scan base-package=“org.zttc.itat.spring”/>

 

// 等价于
// 公共创建bean的annototion
@Component(“userDAO”)
// 一般用于DAO的注入,Spring3新增注解方式
@Repository(“userDAO”)
@Scope(“prototype”)
public class UserDAO implements IUserDAO {

}

@Component(“userService”)
//@Service一般用于业务层
@Service(“userService”)
public class UserService implements IUserService {
private IUserDAO userDAO;

public IUserDAO getUserDAO() {
	return userDAO;
}

// 默认通过名称进行注入,在JSR330中提供了@Inject来进行注入,需导入相应的包
@Resource
public void setUserDAO(IUserDAO userDAO) {
	this.userDAO = userDAO;
}


}

@Component(“userAction”)
//MVC的控制层一般使用@Controller
@Controller(“userAction”)
@Scope(“prototype”)
public class UserAction extends ActionSupport {

}



**注:项目较大时,按照模块进行划分,模块中再按层划分,而非按层(MVC)进行划分;中小型项目按层划分。**


**2. Setter方法注入**


**applicationContext.xml**



<?xml version="1.0" encoding="UTF-8"?>

<!-- 

创建如下bean等于完成了:HelloWorld helloWorld = new HelloWorld()
–>


aaa bbb ccc


public class UserAction{
private UserService userService;
public String login(){
userService.valifyUser(xxx);
}
public void setUserService(UserService userService){
this.userService = userService;
}
}


**3. 构造方法注入**



public class UserAction{
private final UserService userService;
public String login(){
userService.valifyUser(xxx);
}
public UserAction(UserService userService){
this.userService = userService;
}
}


**三种注入方式对比**



> 
> * **注解方式注入**:适用于中小型项目,不适合大型项目。因为大型项目中存在很多的包、类,书写较复杂,且不易明白项目的整体结构。
> * **Setter 注入**:对于习惯了传统 `javabean` 开发的程序员,通过 `setter`方法设定依赖关系更加直观。如果依赖关系较为复杂,那么构造子注入模式的构造函数也会相当庞大,而此时注入模式则更为简洁。
> * **构造器注入**:在构造期间完成一个完整的、合法的对象。 所有依赖关系在构造函数中集中呈现。依赖关系在构造时由容器一次性设定,组件被创建之后一直处于相对“不变”的稳定状态。只有组件的创建者关心其内部依赖关系,对调用者而言,该依赖关系处于“黑盒”之中。如果用到了第三方类库,可能要求我们的组件提供一个默认的构造函数,此时构造器注入模式也不适用。
> 
> 
> 


##### 4.2.3 AOP 切面编程


很多项目上相应的业务处理上上需要事务和日志的管理,然而许多业务上的事务、日志代码又都是一样的,这样就造成了代码重复。


**改进思路:**  
 让这些重复的代码抽取出来,让专门的一个类进行管理。


**AOP的实现:**  
 在需要的地方加上通知(可以在目标代码前后执行),将相同的业务交由**代理类**去执行(比如日志、事务),然后再执行**目标类**,实现了代码复用。


**静态代理的缺陷:**  
 相同的业务交由代理类去处理,那么需要日志管理,就要创建一个日志代理类,需要事务管理,就要创建事务代理类……,这样会造成代理类的无限膨胀。


**改进措施:**  
 根据这些类,动态创建代理类。所以在这个过程,要实现两个步骤:



> 
> 1.通过反射机制产生代理类;  
>  2. 代理类要实现一个统一的接口,该接口可对目标类实现额外功能的附加,如在目标类前面加日志、事务等。
> 
> 
> 


基于注解的`AOP`实现逻辑如下:



package org.zttc.itat.spring.proxy;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component(“logAspect”)//让这个切面类被Spring所管理
@Aspect//申明这个类是一个切面类
public class LogAspect {

/\*\*

* execution(* org.zttc.itat.spring.dao.*.add*(…))
* 第一个*表示任意返回值
* 第二个*表示 org.zttc.itat.spring.dao包中的所有类
* 第三个*表示以add开头的所有方法
* (…)表示任意参数
*/
@Before(“execution(* org.zttc.itat.spring.dao.*.add*(…))||” +
“execution(* org.zttc.itat.spring.dao.*.delete*(…))||” +
“execution(* org.zttc.itat.spring.dao.*.update*(…))”)
public void logStart(JoinPoint jp) {
//得到执行的对象
System.out.println(jp.getTarget());
//得到执行的方法
System.out.println(jp.getSignature().getName());
Logger.info(“加入日志”);
}
/**
* 函数调用完成之后执行
* @param jp
*/
@After(“execution(* org.zttc.itat.spring.dao.*.add*(…))||” +
“execution(* org.zttc.itat.spring.dao.*.delete*(…))||” +
“execution(* org.zttc.itat.spring.dao.*.update*(…))”)
public void logEnd(JoinPoint jp) {
Logger.info(“方法调用结束加入日志”);
}

/\*\*

* 函数调用中执行
* @param pjp
* @throws Throwable
*/
@Around(“execution(* org.zttc.itat.spring.dao.*.add*(…))||” +
“execution(* org.zttc.itat.spring.dao.*.delete*(…))||” +
“execution(* org.zttc.itat.spring.dao.*.update*(…))”)
public void logAround(ProceedingJoinPoint pjp) throws Throwable {
Logger.info(“开始在Around中加入日志”);
pjp.proceed();//执行程序
Logger.info(“结束Around”);
}
}


##### 4.2.4 动态代理的实现


动态代理主要实现了两个功能:



> 
> * 实现`InvocationHandler`接口中的`invoke`方法(主要是被代理类实例、方法、方法参数),该方法就是对被代理对象加的额外操作,如添加日志、权限等。
> * 在运行时产生一个代理实例(通过反射)。
> 
> 
> 


代码实现如下:



Proxy.newProxyInstance(ClassLoad loader, Class[] interfaces,InvocationHandler h)
// 要代理的真实对象
RealSubject rs = new RealSubject();
前端资料汇总

我一直觉得技术面试不是考试,考前背背题,发给你一张考卷,答完交卷等通知。

首先,技术面试是一个 认识自己 的过程,知道自己和外面世界的差距。

更重要的是,技术面试是一个双向了解的过程,要让对方发现你的闪光点,同时也要 试图去找到对方的闪光点,因为他以后可能就是你的同事或者领导,所以,面试官问你有什么问题的时候,不要说没有了,要去试图了解他的工作内容、了解这个团队的氛围。
找工作无非就是看三点:和什么人、做什么事、给多少钱,要给这三者在自己的心里划分一个比例。
最后,祝愿大家在这并不友好的环境下都能找到自己心仪的归宿。

  • 23
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值