1.抽象类与接口:
抽象类与接口的关系:可以说接口是一个特殊的抽象类
抽象类:
- 抽象类中可以有抽象方法和普通方法;
- 抽象类中可以有抽象成员和普通成员;
- 抽象类中可以有静态成员和静态方法;
- 抽象类中有构造函数但是不能被实例化,因为有抽象方法没有被实现;
- 抽象类可以被普通类和抽象类继承;
- 如果一个类中有一个抽象方法,那么当前类一定是抽象类;抽象类中不一定有抽象方法;
- 抽象类中的抽象方法,需要由子类实现,如果子类不实现,则子类也需要定义为抽象的。
- 抽象类方法的访问权限默认都是default
- 抽象类中的抽象方法(其前有abstract修饰)不能用private、static、synchronized、native访问修饰符修饰。
接口:
- 接口中的所有成员默认都是public abstract final的且必须赋初值,所有方法都是public abstract;
- 在接口中只有方法的声明,没有方法体。
- 在接口中只有常量,因为定义的变量,在编译的时候都会默认加上public static final
- 在接口中的方法,永远都被public来修饰。
- 接口中没有构造方法,也不能实例化接口的对象。(所以接口不能继承类)
- 接口可以实现多继承
- 接口中定义的方法都需要有实现类来实现,如果实现类不能实现接口中的所有方法则实现类定义为抽象类。
- 接口可以继承接口,用extends
2.Java的语言性特点:
- Java致力于检查程序在编译和运行时的错误。
- Java虚拟机实现了跨平台接口。
- 类型检查帮助检查出许多开发早期出现的错误。
- Java自己操纵内存减少了内存出错的可能性。
- Java还实现了真数组,避免了覆盖数据的可能。
真数组: 数组元素在内存中是一个接着一个线性存放的,通过第一个元素就能访问随后的元素,避免了数据覆盖的可能性,和数据类型覆盖并没有关系。
3.Volatile
- Volatile:与锁相比,Volatile 变量是一种非常简单但同时又非常脆弱的同步机制,它在某些情况下将提供优于锁的性能和伸缩性。如果严格遵循 volatile 的使用条件 —— 即变量真正独立于其他变量和自己以前的值 —— 在某些情况下可以使用 volatile 代替 synchronized 来简化代码。然而,使用 volatile 的代码往往比使用锁的代码更加容易出错。
- 您只能在有限的一些情形下使用 volatile 变量替代锁。要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件:
- (1)对变量的写操作不依赖于当前值。
- (2)该变量没有包含在具有其他变量的不变式中。
- 实际上,这些条件表明,可以被写入 volatile 变量的这些有效值独立于任何程序的状态,包括变量的当前状态。
- 第一个条件的限制使 volatile 变量不能用作线程安全计数器。虽然增量操作(x++)看上去类似一个单独操作,实际上它是一个由读取-修改-写入操作序列组成的组合操作,必须以原子方式执行,而 volatile 不能提供必须的原子特性。实现正确的操作需要使 x 的值在操作期间保持不变,而 volatile 变量无法实现这点。
一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
2)禁止进行指令重排序。
volatile只提供了保证访问该变量时,每次都是从内存中读取最新值,并不会使用寄存器缓存该值——每次都会从内存中读取。
由于及时更新,很可能导致另一线程访问最新变量值,无法跳出循环的情况
多线程下计数器必须使用锁保护。
对volatile变量的操作不会造成阻塞。
4.内联函数
函数调用要有一定的时间和空间方面的开销。那么对于那些函数体代码不是很大,又频繁调用的函数来说,这个时间和空间的消耗会很大。
内联函数就是在程序编译时,编译器将程序中出现的内联函数的调用表达式用内联函数的函数体来直接进行替换。显然,这样就不会产生转去转回的问题,但是由于在编译时将函数体中的代码被替代到程序中,因此会增加目标程序代码量,进而增加空间开销,而在时间代销上不象函数调用时那么大,可见它是以目标代码的增加为代价来换取时间的节省。在写C代码时,我们都学到将一些简短的逻辑定义在宏里。这样做的好处是,在编译器编译的时候会将用到该宏的地方直接用宏的代码替换。这样就不再需要象调用方法那样的压栈、出栈,传参了。性能上提升了。内联函数的处理方式与宏类似,但与宏又有所不同,内联函数拥有函数的本身特性(类型、作用域等等)
在java中使用final关键字来指示一个函数为内联函数,这个指示并不是必需的。final关键字只是告诉编译器,在编译的时候考虑性能的提升,可以将final函数视为内联函数。但最后编译器会怎么处理,编译器会分析将final函数处理为内联和不处理为内联的性能比较了。
5.Servlet的生命周期
初始化阶段:Servlet启动,会读取配置文件中的信息,构造指定的Servlet对象,创建ServletConfig对象,将ServletConfig作为参数来调用init()方法。
6.面向对象的五大基本原则:
五个基本原则:
- 单一职责原则(Single-Resposibility Principle):一个类,最好只做一件事,只有一个引起它的变化。单一职责原则可以看做是低耦合、高内聚在面向对象原则上的引申,将职责定义为引起变化的原因,以提高内聚性来减少引起变化的原因。
- 开放封闭原则(Open-Closed principle):软件实体应该是可扩展的,而不可修改的。也就是,对扩展开放,对修改封闭的。
- Liskov(里氏)替换原则(Liskov-Substituion Principle):子类必须能够替换其基类。这一思想体现为对继承机制的约束规范,只有子类能够替换基类时,才能保证系统在运行期内识别子类,这是保证继承复用的基础。
- 依赖倒置原则(Dependecy-Inversion Principle):依赖于抽象。具体而言就是高层模块不依赖于底层模块,二者都同依赖于抽象;抽象不依赖于具体,具体依赖于抽象。
- 接口隔离原则(Interface-Segregation Principle):使用多个小的专门的接口,而不要使用一个大的总接口
补充:
常用ASCII码值:空格为32;数字0为48;“A”为65;“a”值为97。
*forward和redirect的区别:
- forward,服务器获取跳转页面内容传给用户,用户地址栏不变
- redirect,是服务器向用户发送转向的地址,redirect后地址栏变成新的地址