面试题(一)

操作系统中 heap 和 stack 的区别


堆栈是两种数据结构。堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除。在单片机应用中,堆栈是个特殊的存储区,主要功能是暂时存放数据和地址,通常用来保护断点和现场。要点:堆,队列优先,先进先出(FIFO—first in first out)。栈,先进后出(FILO—First-In/Last-Out)。

堆和栈的区别:

一、堆栈空间分配区别:

1、栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈;

2、堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。

二、堆栈缓存方式区别:

1、栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放;

2、堆是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。

三、堆栈数据结构区别:

堆(数据结构):堆可以被看成是一棵树,如:堆排序;

栈(数据结构):一种先进后出的数据结构。

Java中栈和堆的区别:

栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。

在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配。当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。

堆内存用来存放由new创建的对象和数组,在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。在堆中产生了一个数组或对象后,还可以在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。引用变量就相当于是为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象。

Java中变量在内存中的分配:

1、类变量(static修饰的变量):在程序加载时系统就为它在堆中开辟了内存,堆中的内存地址存放于栈以便于高速访问。静态变量的生命周期–一直持续到整个”系统”关闭。

2、实例变量:当你使用java关键字new的时候,系统在堆中开辟并不一定是连续的空间分配给变量(比如说类实例),然后根据零散的堆内存地址,通过哈希算法换算为一长串数字以表征这个变量在堆中的”物理位置”。 实例变量的生命周期–当实例变量的引用丢失后,将被GC(垃圾回收器)列入可回收“名单”中,但并不是马上就释放堆中内存。

3、局部变量:局部变量,由声明在某方法,或某代码段里(比如for循环),执行到它的时候在栈中开辟内存,当局部变量一但脱离作用域,内存立即释放。

这里要涉及到Java内存问题,可以参考:Java的内存机制


Java 内存机制:

Java 把内存划分成两种:一种是栈内存,另一种是堆内存。

在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时,Java 就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java 会自动释放掉为该变量分配的内存空间,该内存空间可以立即被另作它用。

堆内存用来存放由 new 创建的对象和数组,在堆中分配的内存,由 Java 虚拟机的自动垃圾回收器来管理。在堆中产生了一个数组或者对象之后,还可以在栈中定义一个特殊的变量,让栈中的这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或者对象,引用变量就相当于是为数组或者对象起的一个名称。引用变量是普通的变量,定义时在栈中分配,引用变量在程序运行到其作用域之外后被释放。而数组和对象本身在堆中分配,即使程序运行到使用 new 产生数组或者对象的语句所在的代码块之外,数组和对象本身占据的内存不会被释放,数组和对象在没有引用变量指向它的时候,才变为垃圾,不能在被使用,但仍然占据内存空间不放,在随后的一个不确定的时间被垃圾回收器收走(释放掉)。

java中常用的内存区域

在java中主要存在4块内存空间,这些内存的名称及作用如下:

1.栈内存空间:保存所有的对象名称(更准确地说是保存了引用的堆内存空间的地址)

2.堆内存空间:保存每个对象的具体属性内容。

3.全局数据区:保存static类型的属性。

4.全局代码区:保存所有的方法定义。


什么是基于注解的切面实现

我们使用切面来非侵入式操作程序方法,常用的场景如日志记录、权限判断等。

下面我实现权限判断的切面。

分析:

要实现基于注解的切面,我们要定义“注解”,定义切面,定义权限验证,定义权限返回。

定义注解:PermissionCheck.java

@Target({ElementType.TYPE, ElementType.METHOD}) // 注解类型, 级别

@Retention(RetentionPolicy.RUNTIME) // 运行时注解

public @interface PermissionCheck {

String value() default "";

}

定义权限校验方法,这里定义 AuthService 和它的实现。

public interface AuthService {

boolean checkAccess();

}

@Service

@Transactional(readOnly = true, rollbackFor = Exception.class)

public class AuthServiceImpl implements AuthService {

@Override

public boolean checkAccess() {

    return true;

}

}

根据需要可改写 checkAccess 方法。

定义一个advice 来处理校验结果:

@ControllerAdvice

public class PermissionAdvice {

@ExceptionHandler(value = PermissionCheckException.class)

@ResponseStatus(HttpStatus.OK)

@ResponseBody

public String dealWithPermissionCheckException(PermissionCheckException exception) {

    System.out.println(exception.getMessage());

    return exception.getMessage();

}

public String dealWithPermissionCheckException() {

    return null;

}

}

接下来就是组合进切面了

@Aspect // 切面标识

@Component // 交给spring容器管理

public class PermissionAspect {

@Autowired

private AuthService authService;

/**

 * 选取切入点为自定义注解

 */

@Pointcut("@annotation(com.honeywen.credit.annotation.PermissionCheck)")

public void permissionCheck(){}

@Before(value = "permissionCheck()")

public void before(JoinPoint joinPoint) throws NoSuchMethodException {

    // 获取连接点的方法签名对象

    Signature signature = joinPoint.getSignature();

    if (!(signature instanceof MethodSignature)) {

        throw new PermissionCheckException("user permission check failed , stop the request!");

    }

    MethodSignature methodSignature = (MethodSignature) signature;

    Object target = joinPoint.getTarget();

    // 获取到当前执行的方法

    Method method = target.getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes());

    // 获取方法的注解

    PermissionCheck annotation = method.getAnnotation(PermissionCheck.class);

    System.out.println(annotation);

    System.out.println("我是在执行业务逻辑之前");

    // 权限检查

    authService.checkAccess();

}

@After(value = "permissionCheck()")

public void after(JoinPoint joinPoint) {

    System.out.println("我是在执行业务逻辑之后");

}

}

总结:

主要过程在定义切面, 然后就是切面在哪里执行:

@Pointcut("@annotation(com.honeywen.credit.annotation.PermissionCheck)")

public void permissionCheck(){}

这里声明的是标注 @PermissionCheck 注解的方法执行


什么是 对象/关系 映射集成模块

Spring 通过提供ORM模块,支持我们在直接JDBC之上使用一个对象/关系映射映射(ORM)工具,Spring 支持集成主流的ORM框架,如Hiberate,JDO和 iBATIS SQL Maps。Spring的事务管理同样支持以上所有ORM框架及JDBC。


什么是 Java 的反射机制

一,先看一下反射的概念:

         主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。

         反射是java中一种强大的工具,能够使我们很方便的创建灵活的代码,这些代码可以再运行时装配,无需在组件之间进行源代码链接。但是反射使用不当会成本很高!  

二,反射机制的作用:

          1,反编译:.class-->.java

          2,通过反射机制访问java对象的属性,方法,构造方法等;

         这样好像更容易理解一些,下边我们具体看怎么实现这些功能。

  三,在这里先看一下sun为我们提供了那些反射机制中的类:

java.lang.Class;

java.lang.reflect.Constructor; java.lang.reflect.Field;

java.lang.reflect.Method;

java.lang.reflect.Modifier;

  1. //第一种方式:

  2. Classc1 = Class.forName("Employee");

  3. //第二种方式:

  4. //java中每个类型都有class 属性.

  5. Classc2 = Employee.class;

  6. //第三种方式:

  7. //java语言中任何一个java对象都有getClass 方法

  8. Employeee = new Employee();

  9. Classc3 = e.getClass(); //c3是运行时类 (e的运行时类是Employee)


什么是 A(原子性)C(一致性)I(隔离性)D(持久性)

原子性

原子性任务是一个独立的操作单元,是一种要么全部是,要么全部不是的原子单位性的操作。

一致性

一个事务可以封装状态改变(除非它是一个只读的)。事务必须始终保持系统处于一致的状态,不管在任何给定的时间并发事务有多少。

一致性有下面特点:

如果一个操作触发辅助操作(级联,触发器),这些也必须成功,否则交易失败。

如果系统是由多个节点组成,一致性规定所有的变化必须传播到所有节点(多主复制)。如果从站节点是异步更新,那么我们打破一致性规则,系统成为“最终一致性”。

一个事务是数据状态的切换,因此,如果事务是并发多个,系统也必须如同串行事务一样操作。

隔离性

事务是并发控制机制,他们交错使用时也能提供一致性。隔离让我们隐藏来自外部世界未提交的状态变化,一个失败的事务不应该破坏系统的状态。隔离是通过用悲观或乐观锁机制实现的。

持久性

一个成功的事务将永久性地改变系统的状态,所以在它结束之前,所有导致状态的变化都记录在一个持久的事务日志中。如果我们的系统突然受到系统崩溃或断电,那么所有未完成已提交的事务可能会重演。

SQL标准规定了四个隔离水平:

  • READ_UNCOMMITTED
  • READ_COMMITTED
  • REPETABLE_READ
  • SERIALIZABLE

BS与CS的联系与区别


Cookie 和 Session的区别

cookie存在客户端,session存在服务端


fail-fast 与 fail-safe 机制有什么区别

1.什么是同步修改?

当一个或多个线程正在遍历一个集合Collection,此时另一个线程修改了这个集合的内容(添加,删除或者修改)。这就是并发修改

2.什么是 fail-fast 机制?

fail-fast机制在遍历一个集合时,当集合结构被修改,会抛出Concurrent Modification Exception。

fail-fast会在以下两种情况下抛出ConcurrentModificationException

(1)单线程环境

集合被创建后,在遍历它的过程中修改了结构。

注意 remove()方法会让expectModcount和modcount 相等,所以是不会抛出这个异常。

(2)多线程环境

当一个线程在遍历这个集合,而另一个线程对这个集合的结构进行了修改。

3. fail-fast机制是如何检测的?

迭代器在遍历过程中是直接访问内部数据的,因此内部的数据在遍历的过程中无法被修改。为了保证不被修改,迭代器内部维护了一个标记 “mode” ,当集合结构改变(添加删除或者修改),标记"mode"会被修改,而迭代器每次的hasNext()和next()方法都会检查该"mode"是否被改变,当检测到被修改时,抛出Concurrent Modification Exception

4. fail-safe机制

fail-safe任何对集合结构的修改都会在一个复制的集合上进行修改,因此不会抛出ConcurrentModificationException

fail-safe机制有两个问题

(1)需要复制集合,产生大量的无效对象,开销大

(2)无法保证读取的数据是目前原始数据结构中的数据。


get 和 post请求的区别

2.缓存

GET 请求能够被缓存,默认的请求方式也是有缓存的

POST请求默认不会缓存

缓存是针对URL来进行缓存的,GET请求由于其参数是直接加在URL上-的,一种参数组合就有一种URL的缓存,可以根据参数来进行一一对应,重复请求是幂等的(不论请求多少次,结果都一样);

而POST请求的URL没有参数,每次请求的URL都相同,数据体(HTTPBody)可能不同,无法一一对应,所以缓存没有意义

3.安全性

GET的所有参数全部包装在URL中,明文显示,且服务器的访问日志会记录,非常不安全

POST的URL中只有资源路径,不包含参数,参数封装在二进制的数据体中,服务器也不会记录参数,相对安全。所有涉及用户隐私的数据都要用POST传输

POST的安全是相对的,对于普通用户来说他们看不到明文,数据封装对他们来说就是屏障。但是对于专业人士,它们会抓包会分析,没有加密的数据包对他们来说也是小case。所以POST仅仅是相对安全,唯有对数据进行加密才会更安全。当然加密也有被破解的可能性,理论上所有的加密方式都可以破解,只是时间长短的问题。而加密算法要做的就是使得破解需要的时间尽量长,越长越安全。由于我们也需要解密,加密算法太过复杂也并非好事,这就要结合使用情况进行折中或者足够实际使用即可。绕的有点远,具体的话,我将在后续的文章之中介提及,并介绍一些常用的加密算法。

4.数据量

HTTP协议中均没有对GET和POST请求的数据大小进行限制,但是实际应用中它们通常受限于软硬件平台的设计和性能。

GET:不同的浏览器和服务器不同,一般限制在2~8K之间,更加常见的是1k以内

POST方法提交的数据比较大,大小靠服务器的设定值限制,PHP默认是2M(具体的话大家以后看后端给的开发文档就行了)


Interface 与 abstract 类的区别


| |

Abstract class

|

Interface

|
|

实例化

|

不能

|

不能

|
|

|

一种继承关系,一个类只能使用一次继承关系。可以通过继承多个接口实现多重继承

|

一个类可以实现多个interface

|
|

数据成员

|

可有自己的

|

静态的不能被修改即必须是static final,一般不在此定义

|
|

方法

|

可以私有的,非abstract方法,必须实现

|

不可有私有的,默认是public,abstract 类型

|
|

变量

|

可有私有的,默认是friendly 型,其值可以在子类中重新定义,也可以重新赋值

|

不可有私有的,默认是public static final 型,且必须给其初值,实现类中不能重新定义,不能改变其值。

|
|

设计理念

|

表示的是“is-a”关系

|

表示的是“like-a”关系

|
|

实现

|

需要继承,要用extends

|

要用implements

|


IOC的优点是什么

IOC(Inversion of Control)控制反转,将控制权(创建对象和对象之间的依赖关系的权利)交给spring容器。

接口驱动设计(Interface Driven Design)的好处,可以灵活提供不同的子类实现(其实就是解耦),提高程序的灵活性、可扩展性和可维护性。

IOC模式将耦合代码从程序中移出,放到统一的XML文件中管理。

由IOC容器通过配置文件来管理对象的生命周期、依赖关系等,这样就不用重新修改并编译具体的代码,从而实现组件之间的解耦。

轻量级IOC容器:Pico Container、Avalon、Spring、HiveMind等。

超重量级IOC容器:EJB。

半轻半重IOC容器:JBoss,Jdon等。

反射:就是将Java类中的各种成分映射成相应的Java类。

通俗的说,反射就是根据给出的字符串(类名等)来生成对象。

反射的好处就是可以临时决定要生成哪一种对象,缺点就是生成对象较慢。

IOC的优点:实现组件之间的解耦,提高程序的灵活性和可维护性。

IOC的缺点:

1、创建对象的步骤变复杂了,不直观,当然这是对不习惯这种方式的人来说的。

2、因为使用反射来创建对象,所以在效率上会有些损耗。但相对于程序的灵活性和可维护性来说,这点损耗是微不足道的。

3、缺少IDE重构的支持,如果修改了类名,还需到XML文件中手动修改,这似乎是所有XML方式的缺憾所在。


IO 和 NIO的区别,NIO优点

IO NIO

面向流 面向缓冲

阻塞IO 非阻塞IO

无 选择器

传统的socket IO中,需要为每个连接创建一个线程,当并发的连接数量非常巨大时,线程所占用的栈内存和CPU线程切换的开销将非常巨大。使用NIO,不再需要为每个线程创建单独的线程,可以用一个含有限数量线程的线程池,甚至一个线程来为任意数量的连接服务。由于线程数量小于连接数量,所以每个线程进行IO操作时就不能阻塞,如果阻塞的话,有些连接就得不到处理,NIO提供了这种非阻塞的能力。

小量的线程如何同时为大量连接服务呢,答案就是就绪选择。这就好比到餐厅吃饭,每来一桌客人,都有一个服务员专门为你服务,从你到餐厅到结帐走人,这样方式的好处是服务质量好,一对一的服务,VIP啊,可是缺点也很明显,成本高,如果餐厅生意好,同时来100桌客人,就需要100个服务员,那老板发工资的时候得心痛死了,这就是传统的一个连接一个线程的方式。

老板是什么人啊,精着呢。这老板就得捉摸怎么能用10个服务员同时为100桌客人服务呢,老板就发现,服务员在为客人服务的过程中并不是一直都忙着,客人点完菜,上完菜,吃着的这段时间,服务员就闲下来了,可是这个服务员还是被这桌客人占用着,不能为别的客人服务,用华为领导的话说,就是工作不饱满。那怎么把这段闲着的时间利用起来呢。这餐厅老板就想了一个办法,让一个服务员(前台)专门负责收集客人的需求,登记下来,比如有客人进来了、客人点菜了,客人要结帐了,都先记录下来按顺序排好。每个服务员到这里领一个需求,比如点菜,就拿着菜单帮客人点菜去了。点好菜以后,服务员马上回来,领取下一个需求,继续为别人客人服务去了。这种方式服务质量就不如一对一的服务了,当客人数据很多的时候可能需要等待。但好处也很明显,由于在客人正吃饭着的时候服务员不用闲着了,服务员这个时间内可以为其他客人服务了,原来10个服务员最多同时为10桌客人服务,现在可能为50桌,60客人服务了。

这种服务方式跟传统的区别有两个:

1、增加了一个角色,要有一个专门负责收集客人需求的人。NIO里对应的就是Selector。

2、由阻塞服务方式改为非阻塞服务了,客人吃着的时候服务员不用一直侯在客人旁边了。传统的IO操作,比如read(),当没有数据可读的时候,线程一直阻塞被占用,直到数据到来。NIO中没有数据可读时,read()会立即返回0,线程不会阻塞。

NIO中,客户端创建一个连接后,先要将连接注册到Selector,相当于客人进入餐厅后,告诉前台你要用餐,前台会告诉你你的桌号是几号,然后你就可能到那张桌子坐下了,SelectionKey就是桌号。当某一桌需要服务时,前台就记录哪一桌需要什么服务,比如1号桌要点菜,2号桌要结帐,服务员从前台取一条记录,根据记录提供服务,完了再来取下一条。这样服务的时间就被最有效的利用起来了。


Java 8 / Java 7 为我们提供了什么新功能


什么是竞态条件? 举个例子说明。


JRE、JDK、JVM 及 JIT 之间有什么不同


MVC的各个部分都有那些技术来实现?如何实现?


RPC 通信和 RMI 区别

一:RPC 远程过程调用

RPC(Remote Procedure Call Protocol)远程过程调用协议,通过网络从远程计算机上请求调用某种服务。

一次RPC调用的过程大概有10步:

1.执行客户端调用语句,传送参数

2.调用本地系统发送网络消息

3.消息传送到远程主机

4.服务器得到消息并取得参数

5.根据调用请求以及参数执行远程过程(服务)

6.执行过程完毕,将结果返回服务器句柄

7.服务器句柄返回结果,调用远程主机的系统网络服务发送结果

8.消息传回本地主机

9.客户端句柄由本地主机的网络服务接收消息

10.客户端接收到调用语句返回的结果数据

RMI远程调用步骤:

1,客户调用客户端辅助对象stub上的方法

2,客户端辅助对象stub打包调用信息(变量,方法名),通过网络发送给服务端辅助对象skeleton

3,服务端辅助对象skeleton将客户端辅助对象发送来的信息解包,找出真正被调用的方法以及该方法所在对象

4,调用真正服务对象上的真正方法,并将结果返回给服务端辅助对象skeleton

5,服务端辅助对象将结果打包,发送给客户端辅助对象stub

6,客户端辅助对象将返回值解包,返回给调用者

7,客户获得返回值

三:RPC与RMI的区别

1:方法调用方式不同:

RMI中是通过在客户端的Stub对象作为远程接口进行远程方法的调用。每个远程方法都具有方法签名。如果一个方法在服务器上执行,但是没有相匹配的签名被添加到这个远程接口(stub)上,那么这个新方法就不能被RMI客户方所调用。

RPC中是通过网络服务协议向远程主机发送请求,请求包含了一个参数集和一个文本值,通常形成“classname.methodname(参数集)”的形式。RPC远程主机就去搜索与之相匹配的类和方法,找到后就执行方法并把结果编码,通过网络协议发回。

2:适用语言范围不同:

RMI只用于Java;

RPC是网络服务协议,与操作系统和语言无关。

3:调用结果的返回形式不同:

Java是面向对象的,所以RMI的调用结果可以是对象类型或者基本数据类型;

RMI的结果统一由外部数据表示 (External Data Representation, XDR) 语言表示,这种语言抽象了字节序类和数据类型结构之间的差异。  

什么是 Web Service(Web服务)

WebService是一种跨编程语言和跨操作系统平台的远程调用技术。


JSWDL开发包的介绍。JAXP、JAXM的解释。SOAP、UDDI,WSDL解释。


WEB容器主要有哪些功能? 并请列出一些常见的WEB容器名字。

一个".java"源文件中是否可以包含多个类(不是内部类)?有什么限制


简单说说你了解的类加载器。是否实现过类加载器

那JVM是如何来让我们写的java文件运行的呢? 这个问题通常的问法好像是:类是如何被加载的。

记得第一次遇见这个问题的时候,同学给我的回答是:

1.虚拟机会加载JDK里类的核心包

2.虚拟机会加载JDK里类的扩展包

3.虚拟机会加载JDK里类的系统包

4.虚拟机再会加载我们写好的java类。

初学的时候,大家都这么说,好像也没发现什么错。 最近在浏览一些博客时看到一些更为详细的讲解,如java类加载全过程,该博文有一万多的点击,但感觉还是讲得不够详细,说了类的加载过程有哪些,但没有详细的展开,说了一些类初始化的细节。 在翻读《深入理解Java虚拟机》209-235页后,总结了其内容,谈谈自己对该部分的理解吧。

希望大家看了之后更能理解JVM的工作原理和java类的生产过程(类加载的过程);

类从被加载到虚拟机类存中开始,到被卸载出内存为止,它的整个生命周期包括

加载 → 验证 → 准备 → 解析 → 初始化 → 使用 → 卸载 7个部分、

下面我就来详细的说说每个部分的详细过程,再补充一下双亲委派模型。

再次之前我想补充一个名词解释,类加载器:虚拟机把 实现 类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流” 这个过程的代码称为类加载器

1. 加载

加载只是类加载过程的一个阶段而已,但往往被大家弄成了这就是类的加载过程,所以才有了博文开头时同学给我的那个回答;

希望大家不要混淆出这个很相似的名词,从而对类加载有所误读。

1.JDK在执行程序运行命令时会去JRE目录中找到jvm.dll , 并初始化JVM

这时会产生一个Bootstrap Loader(启动类加载器)

2.Bootstrap Loader 自动加载 Extended Loader(标准扩展类加载器)

3.Bootstrap Loader 自动加载 AppClass Loader(系统类加载器)

4.最后由 AppClass Loader 加载 我们指定(想要运行)的 java 类

这里可以提一下双亲委派模型加载类的方式:

实现双亲委派的代码都集中在java.lang.ClassLoader的 loadClass()方法中, 源码我就不贴出来了;

其源码大概意思如下:

1.先检查此类是否被加载过,若没有加载则调用父加载器的loadClass()方法,

2.若父加载器为空,则默认使用启动类加载器作为父加载器,

3.若父类加载失败,会抛出一个异常,然后再调用自己的findClass()方法来进行加载;

结合第一步加载可以这么理解,

1.首先要启动→ 启动类加载器,这时会调用启动类加载器的父加载器,但由于启动类加载器时所有类的父加载器,

所以其父加载器为空(相当于Object是所有类的父类,这种感脚~),然后它就会调用自己的findClass方法来自启动加载 ;

2.标准扩展类加载器启动时就会借助其父类 启动类加载器 作为父加载器 来启动了;

3.系统类加载器启动时就会借助其父类 标准扩展类加载器 作为父加载器 来启动了;

4.最后我们编写的普通类就会借助其父类 系统类加载器 作为父加载器 来启动了;

2.验证

验证主要分为以下几个步骤:文件格式验证->元数据验证->字节码验证->符号引用验证

1.文件格式验证:主要是检查字节码的字节流是否符合Class文件格式的规范,验证该文件是否能被当前的 jvm 所处理,

如果没问题,字节里就可以进入方法区进行保存了;

2.元数据验证:对字节码描述的信息进行语义分析,保证其描述的内容符合java语言的语法规范,能被java虚拟机识别;

3.字节码验证:该部分最为复杂,对方法体内的内容进行验证,保证代码在运行时不会做出什么危害虚拟机安全的事件;

4.符号引用验证:来验证一些引用的真实性与可行性,比如代码里面引了其他类(符号中通过字符串描述的全限定名是否能找到对应的类),这里就要去检测一下那些来究竟是否存在;或者说代码中访问了其他类的一些属性,这里就对那些属性的可以访问行进行了检验。(这一步将为后面的解析工作打下基础)

多说两句。。。 我觉得这个验证就是看class文件符不符合 JVM 的胃口 , 如果不符合 JVM 的胃口的话,无法完成加载,说明你写的代码 有毒.... 偷笑偷笑

3.准备

准备阶段会为类变量(指的是静态变量,这就是我们常说的,静态变量/方法 在类加载的时候就执行了,通过类名.静态**来调用)分配内存并设置类的初始值; 值得一提的是 如果有以下语句:

public static int i = 123 ;

在准备阶段的初始值是 0 ,而不是 123 , 是因为此时 只是分配内存空间而已, 并没有对 i 进行初始化, 真正的对 i 赋值是在 初始化 阶段;

4.解析

1.类或接口的解析;

2.字段解析;

3.类方法解析;

4.接口方法解析;

此部分内容涉及 invokedynamic指令,静态、动态语音调用 不做展开

如果解析到代码内容有问题,解析不通过将会抛出异常!

5.初始化

类初始化阶段是类加载过程中的最后一步,这才是执行类中定义的java程序代码(也可以说是字节码)。

在准备阶段,已经为变量赋过一次系统要求的初始值,到了初始化阶段会根据程序员的要求出初始化变量赋值。

Java虚拟机没有严格约束什么时候开始类加载过程的第一阶段,但严格规定了有且只有5钟情况必须立即马上光速对类进行 初始化

当然加载、验证、准备需要在次之前,(解析也可以在初始化以后再开始~)

1.遇到new,get static,put static,invoke static这4条字节码指令时,假如类还没进行初始化,则马上对其进行初始化工作。

也就是三种情况:用new实例化一个对象时、读取或设置一个雷的静态字段时、执行静态方法时;

2.使用java.lang.reflect.*的方法对类进行反射调用时,如果类还没有进行过初始化,立即马上光速对其进行初始化!!!

3.初始化一个类的时候,如果其父类还没有被初始化,那么会先去初始化其父类;

4.当 JVM 启动时,用户需要指定一个要执行的主类(包含static void main(String 【】args)的那个类),则JVM会先去初始化这个类;

5.当使用JDK1.7 的动态语言支持时,如果一个java.lang.invoke.MethodHandle实力最后的解析结果为 get static,put static,invoke static 的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先初始化;

小结:

介绍了类加载过程的 加载、验证、准备、解析、初始化、等5个阶段,以及虚拟机进行了哪些动作,简单叙述了类加载器的工作原理,如果有说得不妥当的地方,还以请大家批评指正,多多交流。


解释一下什么叫AOP(面向切面编程)

http://blog.csdn.net/liujiahan629629/article/details/18864211


请简述 Servlet 的生命周期及其相关的方法

https://www.cnblogs.com/xuekyo/archive/2013/02/24/2924072.html

Servlet的生命周期及每个阶段使用的方法

①实例化阶段:服务器对Servlet进行实例化,调用Servlet的构造方法

②初始化阶段:服务器调用Servlet的init方法进行初始化(只在第一次请求时调用)。

③请求处理阶段:服务器调用Servlet的service方法,然后根据请求方式调用相应的doXXX方法。

④服务终止阶段:服务器调用Servlet的destroy方法销毁Servlet实例


请简述一下 Ajax 的原理及实现步骤


简单描述Struts的主要功能

Struts2 是一个相当强大的Java Web开源框架,是一个基于POJO的Action的MVC Web框架。它基于当年的Webwork和XWork框架,继承其优点,同时做了相当的改进。Struts2现在在Java Web开发界的地位可以说是大红大紫,从开发人员的角度来分析,Struts2之所以能够如此的深入开发人员之心,与其优良的设计是分不开的。

下面我从使用Struts2一年之久的经验来分析一下Struts2的优点:

1.Struts2基于MVC架构,框架结构清晰,开发流程一目了然,开发人员可以很好的掌控开发的过程。 我在项目开发过程中,一个具体的功能的开发流程是:拿到一个具体的功能需求文档和设计好的前台界面(在开发中我不负责设计页面),分析需要从前台传递哪些参数,确定参数的变量名称,在Action中设置相应的变量,这些参数在前台如何显示,并将页面上的一些控件适当使用Struts2提供的服务器端控件来代替,编写Action对应的方法来完成业务逻辑,最后,做一些与配置文件相关的设置。当然实际的开发比这个过程要复杂,涉及到数据库,验证,异常等处理。但是使用Struts2进行开发,你的关注点绝大部分是在如何实现业务逻辑上,开发过程十分清晰明了。

2.使用OGNL进行参数传递。

OGNL提供了在Struts2里访问各种作用域中的数据的简单方式,你可以方便的获取Request,Attribute,Application,Session,Parameters中的数据。大大简化了开发人员在获取这些数据时的代码量。

3.强大的拦截器

Struts2 的拦截器是一个Action级别的AOP,Struts2中的许多特性都是通过拦截器来实现的,例如异常处理,文件上传,验证等。拦截器是可配置与重用的,可以将一些通用的功能如:登录验证,权限验证等置于拦截器中以完成一些Java Web项目中比较通用的功能。在我实现的的一Web项目中,就是使用Struts2的拦截器来完成了系统中的权限验证功能。

4.易于测试

Struts2的Action都是简单的POJO,这样可以方便的对Struts2的Action编写测试用例,大大方便了Java Web项目的测试。

5.易于扩展的插件机制

在Struts2添加扩展是一件愉快而轻松的事情,只需要将所需要的Jar包放到WEB-INF/lib文件夹中,在struts.xml中作一些简单的设置就可以实现扩展。常用的Struts2的扩展可以通过这个链接找到:

http://cwiki.apache.org/S2PLUGINS/home.html

6.模块化

Struts2已经把模块化作为了体系架构中的基本思想,可以通过三种方法来将应用程序模块化:

将配置信息拆分成多个文件

把自包含的应用模块创建为插件

创建新的框架特性,即将与特定应用无关的新功能组织成插件,以添加到多个应用中去。

7.全局结果与声明式异常

为应用程序添加全局的Result,和在配置文件中对异常进行处理,这样当处理过程中出现指定异常时,可以跳转到特定页面,这一功能十分实用。


什么是 N 层架构


什么是CORBA?用途是什么


什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”

一、什么是java虚拟机?

java虚拟机是执行字节码文件(.class)的虚拟机进程。

java源程序(.java)被编译器编译成字节码文件(.class)。然后字节码文件,将由java虚拟机,解释成机器码(不同平台的机器码不同)。利用机器码操作硬件和操作系统

二、为什么java被称为平台无关的编程语言?

因为不同的平台装有不同的JVM,它们能够将相同的.class文件,解释成不同平台所需要的机器码。正是因为有JVM的存在,java被称为平台无关的编程语言

三、详细解释(可以不看)

1)什么是平台?

CPU和操作系统的总称。

CPU进行计算和控制计算机系统,所以每种CPU都有自己的指令集

操作系统:控制程序执行的程序,充当程序和硬件之间的中介

2)什么是平台无关(跨平台)?

平台无关,不是说源程序(.java)和平台无关,能运行在各个不同的平台;而是说源程序编译后的.class文件,能在不同的平台上运行(只要不同的平台装有不同的JVM)

那来个平台相关的例子:VC编译出来的C语言可执行文件exe,能够在windows上运行,不能在linux上运行。

也就是说java源程序不是直接编译成机器码,而是二次编译的。第一次java源程序被javac编译成.class文件(这个文件和平台无关)。第二次,.class文件被JVM中的解释器编译,解释执行为不同平台所需要的机器码

(英语——》普通话——》各地的方言)

3)javac和JVM都包含在JDK(Java Developement ToolKit)中


什么是正则表达式?用途是什么?哪个包使用正则表达式来实现模式匹配


什么是懒加载(Lazy Loading)


什么是尾递归,为什么需要尾递归


什么是控制反转(Inversion of Control)与依赖注入(Dependency Injection)


ApplicationContext和beanfactory区别
BeanFacotry是spring中比较原始的Factory。如XMLBeanFactory就是一种典型的BeanFactory。原始的BeanFactory无法支持spring的许多插件,如AOP功能、Web应用等。
ApplicationContext接口,它由BeanFactory接口派生而来,因而提供BeanFactory所有的功能。ApplicationContext以一种更向面向框架的方式工作以及对上下文进行分层和实现继承,ApplicationContext包还提供了以下的功能:
• MessageSource, 提供国际化的消息访问
• 资源访问,如URL和文件
• 事件传播
• 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层
3.底层资源的访问
ApplicationContext扩展了ResourceLoader(资源加载器)接口,从而可以用来加载多个Resource,而BeanFactory是没有扩展ResourceLoader

4.对Web应用的支持
与BeanFactory通常以编程的方式被创建不同的是,ApplicationContext能以声明的方式创建,如使用ContextLoader。当然你也可以使用ApplicationContext的实现之一来以编程的方式创建ApplicationContext实例 。

ContextLoader有两个实现:ContextLoaderListener和ContextLoaderServlet。它们两个有着同样的功能,除了listener不能在Servlet 2.2兼容的容器中使用。自从Servelt 2.4规范,listener被要求在web应用启动后初始化。很多2.3兼容的容器已经实现了这个特性。使用哪一个取决于你自己,但是如果所有的条件都一样,你大概会更喜欢ContextLoaderListener;关于兼容方面的更多信息可以参照ContextLoaderServlet的JavaDoc。

这个listener需要检查contextConfigLocation参数。如果不存在的话,它将默认使用/WEB-INF/applicationContext.xml。如果它存在,它就会用预先定义的分隔符(逗号,分号和空格)分开分割字符串,并将这些值作为应用上下文将要搜索的位置。ContextLoaderServlet可以用来替换ContextLoaderListener。这个servlet像listener那样使用contextConfigLocation参数。

5.其它区别
1).BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化,这样,我们就不能发现一些存在的Spring的配置问题。而ApplicationContext则相反,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误。

2).BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册


你可能感兴趣的:(面试题(一))