赞
踩
从 JDBC 编程的角度讲,用 PreparedStatement 一般来说比 Statement 性能高,因为在使用时,SQL 语句被预编译并存储在 PreparedStatement 对象中.然后可以使用PreparedStatement 对象多次高效地执行该语句.
有外键约束会影响插入和删除性能,如果程序能够保证数据的完整性,那在设计数据库时就可以去掉外键.(比喻:就好比免检产品,就是为了提高效率,充分相信产品的制造商)
表中允许适当冗余,譬如,主题帖的回复数量和最后回复时间等,将姓名和密码单独从用户表中独立出来等,这些都是分析了业务的特点后,对数据库中表做了优化后的解决方案.也就是说在有些情况下,我们设计数据库可以适度的违反三个范式的设计原则,来获得更好的程序性能.
sql 语句全部大写,特别是列名和表名都大写.因为数据库中的语句执行机制是这样的:sql 语句发给 oracle 服务器语法检查和编译成为内部指令缓存和执行指令.而数据库在对语句进行检查和编译时,会自动将 sql 语句转换成大写,并且根据 SQL 缓存的特点,不要拼凑条件,而是用?(占位符参数)和 PreparedStatment 来执行参数化的 sql 语句.
还有索引对查询性能的改进.当经常查询索引列中的数据时,需要在表上创建索引.索引占用磁盘空间,并且降低添加、删除和更新行的速度.在多数情况下,索引用于数据检索的速度优势大大超过它的不足之处.但是,如果应用程序非常频繁地更新数据或磁盘空间有限, 则可能需要限制索引的数量
char 表示的是固定长度,所占存储空间为你定义时的大小,数据量较大的时候以char 字段为查询条件时查询得快一些.
Varchar2 表示的是实际长度的数据类型,所占存储空间为实际大小,前提是不超过你定义时的长度,如果存储的字段不是定长的,用varchar2 好一些.
1) 简单的通过查看执行 SQL 的运行时间.
2) 可以通过查看数据库提供的语句执行计划来分析相应的数据信息,例如查看执行计划中对应的COST(即成本耗费)值,反应了运行这段 SQL 的总体估计成本,通常COST 低的执行计划要好一些
1) 如果 oracle 中有事务未提交,又打开一个窗口去提交一个新的事务则可能会产生数据库锁.
2) 解锁的方式:
a) 结束当前的 session(会话,此处指和数据库的通讯)自动解锁例如,退出当前数据库连接所用的工具或应用程序.
b) 利用 dba 权限强行关闭掉了执行 Lock 操作的 Session.
示例:通过以上查询知道了 sid 和 SERIAL#(流水号)就可以开杀(就是去关掉会话,俗称杀掉会话)了
–查询出对应会话操作的 sid 和 serial
SELECT sid, serial#, username, osuser FROM v$session;
--根据指定的 sid 和 serial 来关闭对应的会话(杀会话的意思)
ALTER SYSTEM KILL SESSION 'sid,serial';
就数据库操作来说是通过加锁的概念来确保并发数据操作下事务的完整性和数据库的一致性
数据库主要有共享锁和排它锁.当你在修改数据的时候,别人也可以查看,但是他不能修改数据并提交,只能优先你的操作,等你向数据库提交了数据之后他才能执行增删改的操作, 这就是排它锁.共享锁就是你和另外一个用户可以同时共享数据(查看),但是你和他都不能修改.
UNION 在进行表连接后会筛选掉重复的记录,所以在表连接后会对所产生的结果集进行排序运算,删除重复的记录再返回结果.
而 UNION ALL 只是简单的将两个结果合并后就返回.这样,如果返回的两个结果集中有重复的数据,那么返回的结果集就会包含重复的数据了.
从效率上说,UNION ALL 要比 UNION 快很多,所以,如果可以确认合并的两个结果集中不包含重复的数据的话,那么就使用 UNION ALL,
答:最好的办法是利用 sql 语句进行分页,这样每次查询出的结果集中就只包含某页的数据内容.在 sql 语句无法实现分页的情况下,可以考虑对大的结果集通过游标定位方式来获取某页的数据.
不同的数据库下的分页方案各不一样,下面是主流的三种数据库的分页 sql:
sql server:
String sql =
"select top " + pageSize + " * from students where id not in" +
"(select top " + pageSize * (pageNumber-1) + " id from students order by id)" + "order by id";
mysql:
String sql =
"select * from students order by id limit " + pageSize*(pageNumber-1) + "," + pageSize;
oracle:
String sql = "select * from " +
(select *,rownum rid from (select * from students order by postime desc) where rid<=" + pagesize*pagenumber + ") as t" + "where t>" + pageSize*(pageNumber-1);
索引的作用:通过使用索引,大大提高数据库的检索速度,改善数据库性能. 索引分类:
存储过程(Stored Procedure)是一组为了完成特定功能的 SQL 语句集,是由流程控制和SQL 语句书写的命名语句块,经编译和优化后存储在数据库服务器中,应用程序使用时只要调用即可,可以通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来调用.
使用存储过程有以下的优点:
具有良好的安全性:可保证数据的安全性和完整性.通过存储过程可以使没有权限直接访问相应数据库对象的用户受控制的间接地执行数据库相关操作,从而保证数据的安全.
执行速度快,效率高:在运行存储过程前,数据库已对其进行了语法和句法分析,并 给出了优化执行方案.这种已经编译好的过程可极大地改善 SQL 语句的性能.由于执行 SQL 语句的大部分工作已经完成,所以存储过程能以极快的速度执行.
减少网络流量:可以降低网络的通信量.
模块化程序设计:存储过程可以封装业务逻辑,并存入数据服务器中,当业务逻辑发生变化时,无需修改调用存储过程的应用程序,只需修改存储过程即可.
使用存储过程的缺点是:
相对而言,调试比较麻烦
移植问题,数据库端代码当然是与具体数据库相关的.
代码可读性差
存储过程是一组预编译的 SQL 语句,经编译后存储在数据库中,可包含一个或多个 SQL 语句.能够完成一定功能的子程序,需要的时候用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它.存储过程可以包含数据操作语言,逻辑和调用函数等;它又可分为系统存储过程和用户自定义的存储过程.
SQL 指结构化查询语言,是一种 ANSI 的标准计算机语言.
语法:create view 视图名 AS <select 语句>
1、算术运算符
2、比较运算符
3、赋值运算符
4、逻辑运算符
运算符之间的优先级:
算术运算符》比较运算符》逻辑运算符》赋值运算符
int random=(int) (Math.random( )*(max-min)+min) int random=(int) (Math.random( )*(100-10)+10)
答:表达式的值可以是 byte、short、int 和 char、String、enum 类型的数据,但不能是 float 和 double 类型的数据.
while 结构在循环的开始判断下一个迭代是否应该继续.do/while 结构在循环的结尾来判断是否将继续下一轮迭代.do 结构至少会执行一次循环体.
1、break:跳出循环而执行循环后面的语句
2、continue:跳过循环体中剩余的语句而执行下一次循环
3、return 结束方法的调用.
int[] arr=new int[5]; int arr[]=new int[5];
int[] arr=new int[]{1,2,3,4,5};
int[] arr={1,2,3,4,5};
int[] num=new int[20]; num[0]=0;
num[1]=1;
for(int i=2;i<20;i++){
num[i]=num[i-1]+num[i-2];
}
for(int j=1;j<=20;j++){ if(j%5==0){
System.out.println( );
}
System.out.print(num[j-1]+" ");
}
作用域不同
局部变量的作用域仅限于定义它的方法
成员变量的作用域在整个类内部都是可见的
初始值不同
Java 会给成员变量一个初始值
Java 不会给局部变量赋予初始值
1、允许类组成较小的单元(类似文件夹),易于找到和使用相应的文件
2、更好的保护类、属性和方法
3、防止命名冲突
1、包名由小写字母组成,不能以圆点开头或结尾
2、包名之前最好加上唯一的前缀,通常使用组织倒置的网络域名
3、包名后续部分依不同机构内部的规范不同而不同
String 不是基本数据类型,
基本数据类型包括 byte、int、char、long、float、double、boolean 和 short.
Java.lang.String 类是 final 类型的,因此不可以继承这个类、不能修改这个类.
说明一下类变量和实例变量的区别. (重点)
类变量是所有对象共有的,所有的实例对象都共用一个类变量,内存中只有一处空间存放类变量的值.如果其中一个对象改变了类变量的值,其他对象得到的就是改变后的结果. 当类被加载到内存时,类变量就会分配相应的内存空间.
说明实例方法和类方法的区别? (重点)
方法前面有 static 关键字修饰的为类方法,否则为实例方法. 实例方法可以调用该类中的其他方法;类方法只能调用其他类方法,不能调用实例方法;当类文件加载到内存时, 实例方法不会被分配内存空间,只有在对象创建之后才会分配.而类方法在该类被加载到内 存时就分配了相应的内存空间.
数组没有 length( )方法,只有 length 的属性.String 有 length( )方法.
两个.其中包括一个字符对象和一个字符对象引用对象.
传引用是指传递的是地址而不是值本身,传值则是传递值的一份拷贝.
程序能正常编译.运行时会抛 NoSuchMethodError 异常.
Integer 类的 valueOf 方法可以将String 转成 Number.下面是代码示例:
String numString = “1000″;
int id=Integer.valueOf(numString).intValue( );
Java 虚拟机是能移植到不同硬件平台上的软件系统.
访问权限修饰符是表明类成员的访问权限类型的关键字.使用这些关键字来限定程序的方法或者变量的访问权限.它们包含:
public: 所有类都可以访问 protected: 同一个包内以及所有子类都可以访问 private: 只有归属的类才能访问默认: 归属类及相同包下的子类可以访问
当一个&表达式在求值的时候,两个操作数都会被求值,&&更像是一个操作符的快捷方式.当一个&&表达式求值的时候,先计算第一个操作数,如果它返回 true 才会计算第二个操作数.如果第一个操作数取值为 fale,第二个操作数就不会被求值.
声明变量我们只提供变量的类型和名字,并没有进行初始化.定义包括声明和初始化两个阶段 String s;只是变量声明,String s = new String(“bob”); 或者 String s = “bob”;是变量定义.
变量是一块命名的内存区域,以便程序进行访问.变量用来存储数据,随着程序的执行, 存储的数据也可能跟着改变.
输出 array.length 的值,如果是 0,说明数组为空.如果是 null 的话,会抛出空指针异常.
在 java 逻辑运算中使用逻辑运算符||与&&时将采取“短路”运算.举一个例子
上面一段代码的执行过程是当 a 满足大于 0 的时候将不会再执行后面的 a<1 代码,
这就是“短路”.
switch 语句能否作用在 byte 上,能否作用在 long 上,能否作用在 String 上? (重点)
switch支持的数据类型有: int,short,char,byte,String,enum,不支持long
short s = 1; s = s + 1;有什么错? short s = 1; s += 1;有什么错? (重点)
对于 short s = 1; s = s + 1; 由于 s1+1 运算时会自动提升表达式的类型,所以结果是 int 型,再赋值给 short 类型 s1 时,编译器将报告需要强制转换类型的错误.
对于 short s1 = 1; s1 += 1;由于 += 是 java 语言规定的运算符,java 编译器会对它进行特殊处理,因此可以正确编译.
char 型变量是用来存储 Unicode 编码的字符的,unicode 编码字符集中包含了汉字, 所以,char 型变量中当然可以存储汉字啦.不过,如果某个特殊的汉字没有被包含在
unicode 编码字符集中,那么,这个 char 型变量中就不能存储这个特殊汉字.补充说明:
unicode 编码占用两个字节,所以,char 类型的变量也是占用两个字节.
备注:后面一部分回答虽然不是在正面回答题目,但是,为了展现自己的学识和表现自己对问题理解的透彻深入,可以回答一些相关的知识,做到知无不言,言无不尽.
口诀:n 个数字来比较,外层循环 N-1,内层循环 N-1-i,两两相比小靠前.
代码示例:
操作符专门用来比较两个变量的值是否相等,也就是用于比较变量所对应的内存中所存储的数值是否相同,要比较两个基本类型的数据或两个引用变量是否相等,只能用操作符
如果一个变量指向的数据是对象类型的,那么,这时候涉及了两块内存,对象本身占用一块内存(堆内存),变量也占用一块内存,例如 Objet obj = new Object( );引用变量 obj 是一个内存,new Object( )是另一个内存,此时,变量 obj 所对应的内存中存储的数值就是对象占用的那块内存的首地址.对于指向对象类型的变量,如果要比较两个变量是否指向同一个对象,即要看这两个变量所对应的内存中的数值是否相等,这时候就需要用==操作符进行比较.
equals 方法是用于比较两个独立对象的内容是否相同,就好比去比较两个人的长相是否相同,它比较的两个对象是独立的.例如,对于下面的代码:
String a=new String(“foo”); String b=new String(“foo”);
两条 new 语句创建了两个对象,然后用 a,b 这两个变量分别指向了其中一个对象,这是两个不同的对象,它们的首地址是不同的,即 a 和 b 中存储的数值是不相同的,所以,表达式 ab 将返回 false,而这两个对象中的内容是相同的,所以,表达式 a.equals(b)将返回 true.
在实际开发中,我们经常要比较传递进行来的字符串内容是否等,例如,String input = new String(“quit”); input.equals(“quit”),许多人稍不注意就使用进行比较了, 这是错误的,随便从网上找几个项目实战的教学视频看看,里面就有大量这样的错误.记住,字 符串的比较基本上都是使用 equals 方法.
如果一个类没有自己定义 equals 方法,那么它将继承 Object 类的 equals 方法,Object 类的 equals 方法的实现代码如下:
boolean equals(Object o){
return this==o;
}
在语法定义上的区别:静态变量前要加 static 关键字,而实例变量前则不加.
在程序运行时的区别:实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量.静态变量不属于某个实例对象,而是属于类,所以也称为类变量,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用了.总之,实例变量必须创建对象后才可以通过这个对象来使用, 静态变量则可以直接使用类名来引用.
通常,在一个类中定义一个方法为 static,那就是说,无需本类的对象即可调用此方法,
明为 static 的方法有以下几条限制:
不可以.
因为非 static 方法是要与对象关联在一起的,必须创建一个对象后,才可以在该对象上进行方法调用,而static 方法调用时不需要创建对象,可以直接调用.也就是说,当一个 static 方法被调用时,可能还没有创建任何实例对象,如果从一个 static 方法中发出对非 static 方法的调用,那个非 static 方法是关联到哪个对象上的呢?这个逻辑无法成立,所以,一个
static 方法内部发出对非 static 方法的调用.
int 是 java 提供的 8 种原始数据类型之一.Java 为每个原始类型提供了封装类,Integer 是 java 为 int 提供的封装类.int 的默认值为 0,而 Integer 的默认值为 null,即 Integer 可以区分出未赋值和值为 0 的区别,int 则无法表达出未赋值的情况.
另外,Integer 提供了多个与整数相关的操作方法,例如, 将一个字符串转换成整数
Integer.parseInt(“数字字符串”);
Integer 中还定义了表示整数的最大值和最小值的常量.
Math 类中提供了三个与取整有关的方法:ceil、floor、round,这些方法的作用与它们的英文名称的含义相对应,例如,ceil 的英文意义是天花板,该方法就表示向上取整(就是往大值靠拢),所以,Math.ceil(11.3)的结果为 12,Math.ceil(-11.3)的结果是-11;floor 的英文意义是地板,该方法就表示向下取整,所以,Math.floor(11.6)的结果为 11,Math.floor(-11.6) 的结果是-12;最难掌握的是 round 方法,它表示“四舍五入”,算法为 Math.floor(x+0.5), 即 将 原 来 的 数 字 加 上 0.5 后 再 向 下 取 整 , 所 以 ,Math.round(11.5) 的 结 果 为12,Math.round(-11.5)的结果为-11.
请说出作用域 public,private,protected,以及不写时的区别(重点)
这四个作用域的可见范围如下表所示.
说明:如果在修饰的元素上面没有写任何访问修饰符,则表示 default (friendly).
作用域 | 当前类 | 同一包内 | 子孙类 | 其他包 |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
default(不写) | √ | √ | × | × |
private | √ | × | × | × |
备注:只要记住了有 4 种访问权限,4 个访问范围,然后将全选和范围在水平和垂直方向上分别按排从小到大或从大到小的顺序排列,就很容易画出上面的图了.
在 Java 中可以存在过程和函数.在Java 中,对于程序的各种功能的实现,都是通过方法实现的;而方法在运行的时候,就被称为过程.Java 中的函数有 main( ),时间函数等.
JAVA 平台提供了两个类:String 和 StringBuffer,它们可以储存和操作字符串,即包含
多个字符的字符数据.String 类表示内容不可改变的字符串.而 StringBuffer 类表示内容可以被修改的字符串.当你知道字符数据要改变的时候你就可以使用 StringBuffer.典型地,你可以使用 StringBuffers 来动态构造字符数据. 另外,String 实现了 equals 方法,new String(“abc”).equals(new String(“abc”)的结果为 true,而 StringBuffer 没有实现
equals 方法,所以,new StringBuffer(“abc”).equals(new StringBuffer(“abc”)的结果为 false.
接着要举一个具体的例子来说明,我们要把 1 到 100 的所有数字拼起来,组成一个串.
StringBuffer sbf = new StringBuffer( );
for(int i=0;i<100;i++){
sbf.append(i);
}
上面的代码效率很高,因为只创建了一个 StringBuffer 对象,而下面的代码效率很低,因为创建了 101 个对象.
String str = new String( );
for(int i=0;i<100;i++){
str = str + i;
}
在讲两者区别时,应把循环的次数搞成 10000,然后用 endTime-beginTime 来比较两者执行的时间差异,最后还要讲讲 StringBuilder 与 StringBuffer 的区别.
String 重写了 equals 方法和 hashCode 方法,而 StringBuffer 没有重写 equals 方法和
hashCode 方法,所以,将 StringBuffer 对象存储进 Java 集合类中时会出现问题.
StringBuffer 和 StringBuilder 类都表示内容可以被修改的字符串,StringBuilder 是线程不安全的,运行效率高,如果一个字符串变量是在方法里面定义,这种情况只可能有一
个线程访问它,不存在不安全的因素了,则用 StringBuilder.如果要在类里面定义成员变量,
并且这个类的实例对象会在多线程环境下使用,那么最好用 StringBuffer.
数组没有 length( )这个方法,有 length 的属性.String 有 length( )这个方法.
使用 final 关键字修饰一个变量时,是指引用变量不能变,引用变量所指向的对象中的内容还是可以改变的.例如,对于如下语句:
final StringBuffer a=new StringBuffer("immutable");
执行如下语句将报告编译期错误:
a=new StringBuffer("");
但是,执行如下语句则可以通过编译:
a.a ppend(" broken!");
有人在定义方法的参数时,可能想采用如下形式来阻止方法内部修改传进来的参数对象:
public void method(final StringBuffer param){
}
实际上,这是办不到的,在该方法内部仍然可以增加如下代码来修改参数对象:
param.append("a");
类和对象的关系就是抽象和具体的关系,类是多个对象进行综合抽象的结果,是实体对象的概念模型,而一个对象是一个类的实例.
封装是面向对象的三大特性之一,就是将类型的状态信息隐藏在类内部,不允许外部程序直接访问,而通过该类提供的方法来实现对隐藏信息的操作和访问.
封装的好处:隐藏类的实现细节;让使用者只能通过程序规定的方法来访问数据;可以加入存取控制语句,限制不合理操作.
this 关键字是对一个对象的默认引用.this 关键字用来表示以后调用当前方法的对象的引用.
this 使用举例
在定义和实现一个类的时候,可以在一个已经存在的类的基础之上来进行,把这个已经存在的类所定义的内容作为自己的内容,并可以加入若干新的内容,或修改原来的方法使之更适合特殊的需要,这就是继承.继承是子类自动共享父类数据和方法的机制,这是类之间的一种关系,提高了软件的可重用性和可扩展性.Java 中只支持单继承.无法继承父类的构造方法.
Overload 和 Override 的区别.Overload 的方法是否可以改变返回值的类型? (重点)
Overload 是重载的意思,Override 是覆盖的意思,也就是重写.
重载 Overload 表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同(即参数个数或类型不同),与方法的作用域和返回类型无关.
重写 Override 表示子类中的方法可以与父类中的某个方法的名称和参数完全相同,通过子类创建的实例对象调用这个方法时,将调用子类中的定义方法,这相当于把父类中定义的
那个完全相同的方法给覆盖了,这也是面向对象编程的多态性的一种表现.
子类重写父类的方法时,不能扩大方法的异常范围,即只能比父类抛出更少的异常,或者是抛出父类抛出的异常的子异常,因为子类可以解决父类的一些问题,不能比父类有更多的问题.
子类重写父类的方法时,不能缩小作用范围,子类方法的访问权限只能比父类的更大,不能更小.如果父类的方法是private 类型,那么,此时根本不属于重写方法的概念,相当于子类中增加了一个全新的方法.
扩展性回答:(请理解这些概念)
至于 Overloaded 的方法是否可以改变返回值的类型这个问题,要看你倒底想问什么呢?这个题目很模糊.如果几个Overloaded 的方法的参数列表不一样,它们的返回者类型当然也可以不一样.但我估计你想问的问题是:如果两个方法的参数列表完全一样,是否可以让它们的返回值不同来实现重载 Overload.这是不行的,我们可以用反证法来说明这个问题,因为我们有时候调用一个方法时也可以不定义返回结果变量,即不要关心其返回结果,例如,我们调用map.remove(key)方法时,虽然 remove 方法有返回值,但是我们通常都不会定义接收返回结果的变量,这时候假设该类中有两个名称和参数列表完全相同的方法,仅仅是返回类型不同,java 就无法确定编程者倒底是想调用哪个方法了,因为它无法通过返回结果类型来判断.
super 代表对当前对象的直接父类对象的默认引用.在子类中可以通过spuer 关键字来访问父类成员.
注意:
用 abstract 关键字修饰的类和方法. 抽象类和抽象方法的特点:
final 用于声明属性,方法和类,分别表示属性不可变,方法不可重写,类不可继承. 内部类要访问局部变量,局部变量必须定义成 final 类型,例如,一段代码…… finally 是异常处理语句结构的一部分,表示总是执行.
finalize 是 Object 类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以重写此方法提供垃圾收集时的其它资源回收,例如关闭文件等.JVM 不保证此方法总被调用
65. java 中实现多态的机制是什么? (重点)
多态是同一个实现接口,使用是不同的实例而执行不同的操作.靠的是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象,而程序调用的方法在运行期才动态绑定,就是引用变量所指向的具体实例对象的方法,也就是内存里正在运行的那个对象的方法,而不是引用变量的类型中定义的方法.
接口是一种规范和标准,它们可以约束类的行为,是一些方法特征的集合,但是没有方法的实现.接口可以看作是一种特殊的“抽象类”,但是采用完全不同的语法来表示,两者的设计理念是不同的,抽象类利于代码复用,接口利于代码扩展和维护.
含有 abstract 修饰符的 class 即为抽象类,abstract 类不能创建的实例对象.含有
abstract 方法的类必须定义为 abstract class,abstract class 类中的方法不必是抽象的.abstract class 类中定义抽象方法必须在具体(Concrete)子类中实现,所以,不能有抽象构造方法或抽象静态方法.如果的子类没有实现抽象父类中的所有抽象方法,那么子类也必须定义为 abstract 类型.
接口(interface)可以说成是抽象类的一种特例,接口中的所有方法都必须是抽象的. 接口中的方法定义默认为 public abstract 类型,接口中的成员变量类型默认为 public static final.
下面比较一下两者的语法区别:
抽象类可以有构造方法,接口中不能有构造方法.
抽象类中可以有普通成员变量,接口中没有普通成员变量
抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法.
抽象类中的抽象方法的访问类型可以是public,protected 和默认作用域(不建议使用, 因为默认作用域不能被其它包中的子类集成),但接口中的抽象方法只能是 public 类型的,并且默认即为 public abstract 类型.
抽象类中可以包含静态方法,接口中不能包含静态方法
抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final 类型,并且默认即为public static
final 类型.
一个类可以实现多个接口,但只能继承一个抽象类.
下面接着再说说两者在应用上的区别:
接口更多的是在系统架构设计方法发挥作用,主要用于定义模块之间的通信契约.而抽象类在代码实现方面发挥作用,可以实现代码的重用,例如,模板方法设计模式是抽象类的一个典型应用,假设某个项目的所有 Servlet 类都要用相同的方式进行权限判断、记录访问日志和处理异常,那么就可以定义一个抽象的基类,让所有的Servlet 都继承这个抽象基类,在抽象基类的 service 方法中完成权限判断、记录访问日志和处理异常的代码,在各个子类中只是完成各自的业务逻辑代码,伪代码如下:
public abstract class BaseServlet extends HttpServlet{
public final void service(HttpServletRequest request, HttpServletResponse response) throws IOExcetion,ServletException{
//记录访问日志
//进行权限判断if(具有权限){
try{
doService(request,response);
}
catch(Excetpion e){
//记录异常信息
}
}
}
protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws IOExcetion,ServletException;
//注意访问权限定义成 protected,显得既专业,又严谨,因为是专门给子类用的
}
}
public class MyServlet1 extends BaseServlet{
protected void doService(HttpServletRequest request, HttpServletResponse response) throws IOExcetion,ServletException{
//本 Servlet 处理具体业务逻辑代码
}
}
父类方法中间的某段代码不确定,留给子类去实现,就用模板方法设计模式.
备注:这道题的思路是先从总体解释抽象类和接口的基本概念,然后再比较两者的语法细节,最后再说两者的应用区别.比较两者语法细节区别的条理是:先从一个类中的构造方法、普通成员变量和方法(包括抽象方法),静态变量和方法,继承性等 6 个方面逐一去比较回答, 接着从第三者继承的角度的回答,特别是最后用了一个典型的例子来展现自己深厚的技术功底.
接口可以继承接口.抽象类可以实现(implements)接口,抽象类可继承实体类,但前提是实体类必须有明确的构造函数. 抽象类中可以有静态的 main 方法.
备注:只要明白了接口和抽象类的本质和作用,这些问题都很好回答,你想想,如果你是
java 语言的设计者,你是否会提供这样的支持,如果不提供的话,有什么理由吗?如果你没有道理不提供,那答案就是肯定的了.
只有记住抽象类与普通类的唯一区别就是不能创建实例对象和允许有 abstract 方法.
异常就是在程序的运行过程中所发生的不正常的事件,比如所需文件找不到,网络连接不通或中断,算术运算符出错,数组下标越界,类型转换异常等.
运行时异常(RuntimeException)与一般异常有何异同? (重点)
异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误,只要是 RuntimeException 类的子类都属于运行时异常,而不是 RuntimeException 类的子类的其它异常类都是一般异常或叫做受检异常.java 编译器要求方法必须声明抛出可能发生的非运行时异常,但是并不要求必须声明抛出未被捕获的运行时异常.
java 语言如何进行异常处理?关键字:throws、throw、try、catch、finally 分别代表什么意义?在 try 块中可以抛出异常吗? (重点)
Java 的异常处理是通过 5 个关键词来实现的:try、catch、throw、throws 和 finally.
throws 声明方法有可能抛出的异常
throw 手动抛出异常
try catch 是内部捕获异常并做自定义处理
finally 是无论是否有异常都会被处理的语句,除非在 finally 前,存在 System.exit(整数值)时除外
在 try 块中可以抛出异常,但是必须 catch 或者在方法中声明抛出的异常类型(运行时异常除外).
try-catch-finally 的执行顺序是先执行 try 块中的代码,如果发生异常则进入 catch 块处理,但是无论是否发生异常都会进入 finally 块,所以 finally 块中的代码会在try 块中的 return 语句执行前执行
error 表示恢复不是不可能但很困难的情况下的一种严重问题.比如说内存溢出.不可能指望程序能处理这样的情况. exception 表示一种设计或实现问题.也就是说,它表示如果程序运行正常,从不会发生的情况,可以使用异常处理机制处理.
当 JAVA 程序违反了 JAVA 的语义规则时,JAVA 虚拟机就会将发生的错误表示为一个异常. 违反语义规则包括 2 种情况.一种是 JAVA 类库内置的语义检查.例如数组下标越界,会引发IndexOutOfBoundsException;访问 null 的对象时会引发 NullPointerException.另一种情况就是
JAVA 允许程序员扩展这种语义检查,程序员可以创建自己的异常,并自由选择在何时用 throw
关键字引发异常.所有的异常都是 java.lang.Thowable 的子类.
异常是指 java 程序运行时(非编译)所发生的非正常情况或错误,与现实生活中的事件很相似,现实生活中的事件可以包含事件发生的时间、地点、人物、情节等信息,可以用一个对象来表示,Java 使用面向对象的方式来处理异常,它把程序中发生的每个异常也都分别封装到一个对象来表示的,该对象中包含有异常的信息.
Java 对异常进行了分类,不同类型的异常分别用不同的 Java 类表示,所有异常的根类为java.lang.Throwable,Throwable 下面又派生了两个子类:Error 和 Exception,Error 表示应用程序本身无法克服和恢复的一种严重问题,程序只有死的份了,例如,说内存溢出和线程死锁等系统问题.Exception 表示程序还能够克服和恢复的问题,其中又分为系统异常和普通异常,系统异常是软件本身缺陷所导致的问题,也就是软件开发人员考虑不周所导致的问题,软件使用者无法克服和恢复这种问题,但在这种问题下还可以让软件系统继续运行或者让软件死掉,例如,数组脚本越界(ArrayIndexOutOfBoundsException),空指针异常(NullPointerException)、类转换异常(ClassCastException);普通异常是运行环境的变化或异常所导致的问题,是用户能够克服的问题,例如,网络断线,硬盘空间不够,发生这样的异常后,程序不应该死掉.
java 为系统异常和普通异常提供了不同的解决方案,编译器强制普通异常必须 try…catch 处理或用throws 声明继续抛给上层调用方法处理,所以普通异常也称为checked 异常,而系统异常可以处理也可以不处理,所以,编译器不强制用 try…catch 处理或用 throws 声明,所以系统异常也称为 unchecked 异常.
提示答题者:就按照三个级别去思考:虚拟机必须宕机(就是挂掉的意思,相当于崩溃)的错误,程序可以死掉也可以不死掉的错误,程序不应该死掉的错误;
声明异常——throws:Java 语言中通过关键字 throws 声明某个方法可能抛出的各种异常.throws 可以同时声明多个异常,之间有逗号隔开.
抛出异常——throw:Java 语言中,可以通过使用throw 关键字来自行抛出异常. 同时它的区别还包括以下三方面:
(1) 作用不同:throw 用于程序员自行产生并抛出异常,throws 用于声明在该方法内抛出的异常.
(1) 使用的位置不同:throw 位于方法体内部,可以作为单独语句使用.throws 必须跟在方法参数列表后面,不能单独使用.
(2) 内容不同:throw 抛出一个异常对象,而且只能是一个.throws 后面跟异常类,而且可以跟多个异常类.
(1) Throwable 类:所有异常类型都是 Throwable 类的子类,它派生来个子类:Error 和
Exception
(2) Error 类:表示仅靠程序本身无法恢复的严重错误.
(3) Exception 类:由Java 应用程序抛出和处理的非严重错误.
(4) 运行时异常:包括 RuntimeException 及其所有子类.不需要程序必须对他们做出处理.
(5) Checked 异常(非运行时异常):除了运行时异常外的其他由 Exception 继承来的异常类.
Collection 是集合类的上级接口,继承与他的接口主要有 Set 和 List. Collections 是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作.
(1) 数组可以存储基本数据类型和对象,而集合中只能存储对象(可以以包装类形式存储基本数据类型).
(2) 数组的长度是固定的,集合长度是可以动态改变的
(3) 定义数组时必须指定数组元素类型,集合默认其中所有元素都是 Object
(4) 无法直接获取数组数组实际存储的元素个数,length 用来获取数组的长度,但可以通过
size( )直接获取集合实际存储的元素个数
(5) 集合有多种实现方式和不同的适用场合,而不像数组仅采用分配连续的空间方式
(6) 集合以接口和类的形式存在.具有封装,继承和多态等类的特性,通过简单的方法和属性调用即可实现各种复杂的操作,大大提高软件的开发效率.
Java 容器类 Collection、List、ArrayList、Vector 及 Map、HashTable、HashMap. Collection 是 List、Set 两个接口的父接口,
List 在 Collection 上增加了“有序”的特性,而 set 增加了“唯一”的特性;
Arraylist 是有序不唯一的;
Vector(向量类)可以实现类似动态数组的功能,但是又区别于C 语言中的概念,因为Java 中是不存在“指针”这一概念,相对于 Arraylist 而言,Vector 线程是安全的,也就是说是支持线程同步的,创建了一个 Vector 类的对象后,可以往其中随意地插入不同的类的对象,既不需顾及类型也不需预先选定向量的容量,并可方便的进行查找;
Map 不能包括两个相同的键,它是无序的,但是Map 的值是可以重复的;
Hashtable 是 Dictionary 类的子类,缺省是线程同步的,不允许关键字或值为 null;HashMap 是
Map 接口的实现类,缺省情况下是非同步的.
List,Set 是,Map 不是 .
ArrayList 集合:对数组进行了封装,实现了长度可变的数组,和数组采用相同存储方式,在内存中分配连续的空间.它的优点在于遍历元素和随机访问元素的效率比较高.
LinkedList 集合:采用链式存储方式,优点在于插入,删除元素时效率比较高.
Set 接口继承 Collection 接口.存储一组唯一(不允许重复),无序的对象
List 接口继承 Collection 接口,存储一组不唯一(允许重复),有序(以元素插入的次序来放置元素,不会重新排列)的对象.
Map 接口存储一组成对的键-值对象,提供 key(键)到 value(值)的映射.Map 中的 key 不要求有序,不允许重复.value 同样不要求有序,但允许重复.
equals( )? 它们有何区别? (重点)
Set 里的元素是不能重复的,元素重复与否是使用equals( )方法进行判断的.
equals( )和==方法决定引用值是否指向同一对象 equals( )在类中被覆盖,为的是当两个分离的对象的内容和类型相配的话,返回真值.
这样的题属于随意发挥题:这样的题比较考水平,两个方面的水平:一是要真正明白这些内容,二是要有较强的总结和表述能力.如果你明白,但表述不清楚,在别人那里则等同于不明白.
首先,List 与 Set 具有相似性,它们都是单列元素的集合,所以,它们有一个功共同的父接口,叫 Collection.
Set 里面不允许有重复的元素,所谓重复,即不能有两个相等(注意,不是仅仅是相同)的对象 ,
即假设 Set 集合中有了一个A 对象,现在我要向 Set 集合再存入一个 B 对象,但 B 对象与 A 对象 equals 相等,则 B 对象存储不进去,所以,Set 集合的 add 方法有一个 boolean 的返回值,当集合中没有某个元素,此时 add 方法可成功加入该元素时,则返回 true,当集合含有与某个元素equals 相等的元素时,此时 add 方法无法加入该元素,返回结果为false.Set 取元素时,没法说取第几个,只能以 Iterator 接口取得所有的元素,再逐一遍历各个元素.
List 表示有先后顺序的集合, 注意,不是那种按年龄、按大小、按价格之类的排序.当我们多次调用 add(Obj e)方法时,每次加入的对象就像火车站买票有排队顺序一样,按先来后到的顺序排序.有时候,也可以插队,即调用 add(int index,Obj e)方法,就可以指定当前对象在集合中的存放位置.一个对象可以被反复存储进 List 中,每调用一次 add 方法,这个对象就被插入进集合中一次,其实,并不是把这个对象本身存储进了集合中,而是在集合中用一个索引变量指向这个对象,当这个对象被 add 多次时,即相当于集合中有多个索引指向了这个对象 .List 除了可以以 Iterator 接口取得所有的元素,再逐一遍历各个元素之外,还可以调用get(index i)来明确说明取第几个.
Map与List 和Set不同,它是双列的集合,其中有put 方法,定义如下:put(obj key,obj value), 每次存储时,要存储一对key/value,不能存储重复的 key,这个重复的规则也是按equals 比较相等.取则可以根据 key 获得相应的value,即 get(Object key)返回值为 key 所对应的 value.另外, 也可以获得所有的key 的结合,还可以获得所有的value 的结合,还可以获得 key 和value 组合成的 Map.Entry 对象的集合.
List 以特定次序来持有元素,可有重复元素.Set 无法拥有重复元素,内部排序.Map 保存
key-value 值,value 可多值.
HashSet 按照 hashcode 值的某种运算方式进行存储,而不是直接按 hashCode 值的大小进行存储.
例如,“abc” —> 78,
“def” —> 62,
“xyz” —> # 65
在 hashSet 中的存储顺序不是 62,65,78.
LinkedHashSet 按插入的顺序存储,那被存储对象的 hashcode 方法还有什么作用呢?
hashset 集合比较两个对象是否相等,首先看 hashcode 方法是否相等,然后看 equals 方法是否相等.
new 两个 Student 插入到 HashSet 中,看 HashSet 的 size,实现
hashcode 和 equals 方法后再看 size.
同一个对象可以在Vector 中加入多次.往集合里面加元素,相当于集合里用一根绳子连接到了目标对象.往 HashSet 中却加不了多次的.
(条理上还需要整理,也是先说相同点,再说不同点)
HashMap 是 Hashtable 的轻量级实现(非线程安全的实现),他们都完成了Map 接口,主要区别在于 HashMap 允许空(null)键值(key),由于非线程安全,在只有一个线程访问的情况下, 效率要高于 Hashtable.
HashMap 允许将 null 作为一个entry 的 key 或者 value,而 Hashtable 不允许.
HashMap 把 Hashtable 的contains 方法去掉了,改成 containsvalue 和containsKey.因为 contains
方法容易让人引起误解.
Hashtable 继承自 Dictionary 类,而 HashMap 是 Java1.2 引进的Map interface 的一个实现.
最大的不同是,Hashtable 的方法是Synchronize 的,而HashMap 不是,在多个线程访问Hashtable
时,不需要自己为它的方法实现同步,而 HashMap 就必须为之提供外同步.
Hashtable 和 HashMap 采用的 hash/rehash 算法都大概一样,所以性能不会有很大的差异. 就 HashMap 与 HashTable 主要从三方面来说.
一.历史原因:Hashtable 是基于陈旧的 Dictionary 类的,HashMap 是Java 1.2 引进的 Map 接口的一个实现
二.同步性:Hashtable 是线程安全的,也就是说是同步的,而 HashMap 是线程序不安全的,不是同步的
三.值:只有 HashMap 可以让你将空值作为一个表的条目的 key 或value .
ArrayList 和Vector 都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector 由于使用了 synchronized 方法(线程安全),通常性能上较ArrayList 差,而LinkedList 使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快.
LinkedList 也是线程不安全的,LinkedList 提供了一些方法,使得 LinkedList 可以被当作堆栈和队列来使用.
这两个类都实现了 List 接口(List 接口继承了 Collection 接口),他们都是有序集合,即存储在这两个集合中的元素的位置都是有顺序的,相当于一种动态的数组,我们以后可以按位置索引号取出某个元素,并且其中的数据是允许重复的,这是 HashSet 之类的集合的最大不同处,HashSet 之类的集合不可以按索引号去检索其中的元素,也不允许有重复的元素(本来题目问的与 hashset 没有任何关系,但为了说清楚ArrayList 与 Vector 的功能,我们使用对比方式, 更有利于说明问题). 接着才说 ArrayList 与Vector 的区别,这主要包括两个方面:
(1) 同步性:
Vector 是线程安全的,也就是说是它的方法之间是线程同步的,而ArrayList 是线程序不安全的, 它的方法之间是线程不同步的.如果只有一个线程会访问到集合,那最好是使用 ArrayList,因为它不考虑线程安全,效率会高些;如果有多个线程会访问到集合,那最好是使用Vector,因为不需要我们自己再去考虑和编写线程安全的代码.
备注:对于 Vector&ArrayList、Hashtable&HashMap,要记住线程安全的问题,记住 Vector 与Hashtable 是旧的,是java 一诞生就提供了的,它们是线程安全的,ArrayList 与 HashMap 是java2 时才提供的,它们是线程不安全的.
(2) 数据增长:
ArrayList 与 Vector 都有一个初始的容量大小,当存储进它们里面的元素的个数超过了容量时, 就需要增加ArrayList 与Vector 的存储空间,每次要增加存储空间时,不是只增加一个存储单元, 而是增加多个存储单元,每次增加的存储单元的个数在内存空间利用与程序效率之间要取得一定的平衡.Vector 默认增长为原来两倍,而 ArrayList 的增长策略在文档中没有明确规定(从源代码看到的是增长为原来的 1.5 倍).ArrayList 与Vector 都可以设置初始的空间大小,Vector 还可以设置增长的空间大小,而ArrayList 没有提供设置增长空间的方法.
总结:即Vector 增长原来的一倍,ArrayList 增加原来的 0.5 倍.
Collections 和Arrays.Collections 提供了对一个Collection 容器进行诸如排序、复制、查找和填
充等一些非常有用的方法,Arrays 则是对一个数组进行类似的操作.
数组就像身上编了号站成一排的人,要找第 10 个人很容易,根据人身上的编号很快就能找到. 但插入、删除慢,要望某个位置插入或删除一个人时,后面的人身上的编号都要变.当然,加入或删除的人始终末尾的也快.
链表就像手牵着手站成一圈的人,要找第 10 个人不容易,必须从第一个人一个个数过去.但插入、删除快.插入时只要解开两个人的手,并重新牵上新加进来的人的手就可以.删除一样的道理.
JVM 中类的装载是由 ClassLoader 和它的子类来实现的,Java ClassLoader 是一个重要的
Java 运行时系统组件.它负责在运行时查找和装入类文件的类.
JVM(JAVA 虚拟机)里有多个类加载器,每个类加载器可以负责加载特定位置的类,例如,bootstrap 类加载器负责加载 jre/lib/rt.jar 中的类, 我们平时用的 jdk 中的类都位于 rt.jar 中.extclassloader 负责加载jar/lib/ext/*.jar 中的类,appclassloader 负责 classpath 指定的目录或
jar 中的类.除了 bootstrap 之外,其他的类加载器本身也都是java 类,它们的父类是 ClassLoader.
答:调用该访问返回一个以字符串指定类名的类的对象.
按参数中指定的字符串形式的类名去搜索并加载相应的类,如果该类字节码已经被加载过,则返回代表该字节码的 Class 实例对象,否则,按类加载器的委托机制去搜索和加载该类,如果所有的类加载器都无法加载到该类,则抛出 ClassNotFoundException.加载完这个 Class 字节码后, 接着就可以使用 Class 字节码的 newInstance 方法去创建该类的实例对象了.
有时候,我们程序中所有使用的具体类名在设计时(即开发时)无法确定,只有程序运行时才能确定,这时候就需要使用 Class.forName 去动态加载该类,这个类名通常是在配置文件中配置的,例如,spring 的 ioc 中每次依赖注入的具体类就是这样配置的,jdbc 的驱动类名通常也是通过配置文件来配置的,以便在产品交付使用后不用修改源程序就可以更换驱动类名.
orm是一种思想,就是把object 转变成数据库中的记录,或者把数据库中的记录转变成objecdt,
我们可以用jdbc 来实现这种思想,其实,如果我们的项目是严格按照oop 方式编写的话,我们的
jdbc 程序不管是有意还是无意,就已经在实现 orm 的工作了.
现在有许多 orm 工具,它们底层调用 jdbc 来实现了 orm 工作,我们直接使用这些工具,就省去了直接使用 jdbc 的繁琐细节,提高了开发效率,现在用的较多的 orm 工具是 hibernate.也听说一些其他 orm 工具,如 toplink,ojb 等
一个sql 命令发给服务器去执行的步骤为:语法检查,语义分析,编译成内部指令,缓存指令,执行指令等过程.
select * from student where id =3 缓存–xxxxx 二进制命令
select * from student where id =3 直接取-xxxxx 二进制命令
select * from student where id =4 会怎么干?
如果当初是select * from student where id =? 又会怎么干?
上面说的是性能提高,可以防止 sql 注入.(重点)
流,是指一连串流动的字符,是以先进先出的方式发送和接收数据的通道.
当先一个流写入数据时,这个流被称为输出流,输出流可以将信息送往程序的外部,如硬盘上的文件、打印机上的文件等.
输出流:只能从中读取数据,而不能向其中写入数据输出流:只能向其中写入数据,而不能从中读取数据
字节流的操作最小数据单元为 8 位的字节,而字符流操作的最小数据单元是 16 位的字符.
字节流和字符流的区分非常简单,字节流建议用于二进制数据(如图片),而字符流用于文本, 它们的用法几乎是完全一样的.
字节流,字符流.字节流继承于 InputStream OutputStream,字符流继承于 InputStreamReader OutputStreamWriter.在 java.io 包中还有许多其他的流,主要是为了提高性能和使用方便.
字节流与字符流的区别?(重点)
要把一片二进制数据数据逐一输出到某个设备中,或者从某个设备中逐一读取一片二进制数据,不管输入输出设备是什么,我们要用统一的方式来完成这些操作,用一种抽象的方式进行描述,这个抽象描述方式起名为IO 流,对应的抽象类为OutputStream 和InputStream ,不同的实现类就代表不同的输入和输出设备,它们都是针对字节进行操作的.
在应用中,经常要完全是字符的一段文本输出去或读进来,用字节流可以吗?计算机中的一切最终都是二进制的字节形式存在.对于“中国”这些字符,首先要得到其对应的字节,然后将字节写入到输出流.读取时,首先读到的是字节,可是我们要把它显示为字符,我们需要将
字节转换成字符.由于这样的需求很广泛,人家专门提供了字符流的包装类.
底层设备永远只接受字节数据,有时候要写字符串到底层设备,需要将字符串转成字节再进行写入.字符流是字节流的包装,字符流则是直接接受字符串,它内部将串转成字节,再写入底层设备,这为我们向 IO 设别写入或读取字符串提供了一点点方便.
字符向字节转换时,要注意编码的问题,因为字符串转成字节数组, 其实是转成该字符的某种编码的字节形式,读取也是反之的道理.
讲解字节流与字符流关系的代码案例:
import java.io.BufferedReader; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileReader;
import java.io.FileWriter;
import java.io.InputStreamReader; import java.io.PrintWriter;
public class IOTest {
public static void main(String[] args) throws Exception { String str = "中国人";
/*FileOutputStream fos = new FileOutputStream("1.txt"); fos.write(str.getBytes("UTF-8"));
fos.close( );*/
/*FileWriter fw = new FileWriter("1.txt"); fw.write(str);
fw.close( );*/
PrintWriter pw = new PrintWriter("1.txt","utf-8"); pw.write(str);
pw.close( );
/*FileReader fr = new FileReader("1.txt"); char[] buf = new char[1024];
int len = fr.read(buf);
String myStr = new String(buf,0,len); System.out.println(myStr);*/
/*FileInputStream fr = new FileInputStream("1.txt"); byte[] buf = new byte[1024];
int len = fr.read(buf);
String myStr = new String(buf,0,len,"UTF-8"); System.out.println(myStr);*/
BufferedReader br = new BufferedReader( new InputStreamReader(
new FileInputStream("1.txt"),"UTF-8"
)
);
String myStr = br.readLine( ); br.close( );
System.out.println(myStr);
}
}
什么是java 序列化,如何实现java 序列化?或者请解释Serializable 接口的作用. (重点)
我们有时候要将一个 java 对象变成字节流的形式传出去或者从一个字节流中恢复成一个 java 对象,例如,要将 java 对象存储到硬盘或者传送给网络上的其他计算机,这个过程我们可以自己写代码去把一个 java 对象变成某个格式的字节流再传输,但是,java 虚拟机本身就提供了这种支持,我们可以调用 OutputStream 的writeObject 方法来做,如果要让java 帮我们做,要被传输的对象必须实现serializable 接口,这样,javac 编译时就会进行特殊处理, 编译的类才可以被 writeObject 方法操作,这就是所谓的序列化.需要被序列化的类必须实现 Serializable 接口,该接口是一个 mini 接口,其中没有需要实现的方法,implements Serializable 只是为了标注该对象是可被序列化的.
例如,在 web 开发中,如果对象被保存在了 Session 中,Tomcat 在重启时要把 Session 对象序列化到硬盘,这个对象就必须实现 Serializable 接口.如果对象要经过分布式系统进行网络传输或通过 rmi 等远程调用,这就需要在网络上传输对象,被传输的对象就必须实现 Serializable 接口.
对象经常要通过 I/O(输入输出设备)进行传送,让你写程序传递对象,你会怎么做?把对象的状态数据用某种格式写入到硬盘,Person->“zxx,male,28,30000”Person,既然大家都要这么干,并且没有个统一的干法,于是,sun 公司就提出一种统一的解决方案,它会把对象变成某个格式进行输入和输出,这种格式对程序员来说是透明(transparent)的,但是,我们的某个类要想能被sun 的这种方案处理,必须实现 Serializable 接口.
ObjectOutputStream.writeObject(obj);//对象输出流保存对象,要使用序列化操作 Object obj = ObjectInputStream.readObject( );//对象输入流读取对象,使用反序列化操作
序列化 id 的作用是什么呢,用以下场景来解释它的作用:
假设两年前我保存了某个类的一个对象,这两年来,我修改该类(类名保持不变),删除了某个属性和增加了另外一个属性,两年后,我又去读取那个保存的对象,会有什么结果?Sun 的
jdk 无法获知你保存的对象到底是未修改前的类的对象还是修改后的类的对象.为此,一个解决办法就是在类中增加版本号信息,每一次类的属性修改,都应该把版本号升级一下,这样,在读取时,比较存储对象时的版本号与当前类的版本号,如果不一致,则直接报版本号不同的错!, 此处的版本号其实就是序列化 ID
JavaScript 是一种弱类型语言,在声明变量时不需要指变量的类型,变量的类型由赋给变量的值来决定.
常用的基本数据类型:
1 undefined(未定义类型)
2 null(空类型)
3 number(数值类型)
4 string(字符串类型)
5 boolean(布尔类型)
(警告窗口,确认窗口,信息输入窗口)的命令是什么? 弹出警告窗口“输入数据无效”
弹出确认窗口“确认保存吗?”
弹出信息输入窗口“请在此输入你的姓名”
“输入数据无效”:
alert(“输入数据无效”);//警告
“确认保存吗?”:
confirm(“你是菜鸟吗”);//确认
“请在此输入你的姓名”:
prompt(“请在此输入你的姓名”)//信息输入
BOM 是 Browser Object Model(浏览器对象模型)的简称,提供了独立于内容与浏览器窗口进行交互的对象.
DOM 即 Document Object Model(文档对象模型),如果需要对 HTML 文档中的元素进行访问,添加,删除,移动或重排,JavaScript 就是通过 DOM 来实现的,DOM 可被 JavaScript 用来读取, 改变 HTML,XHTML 及 XML 文档,所以 DOM 是由三部分组成的.
分别是:
定时执行有两种函数,setTimeout( )和setInterval( ),setTimeout( )是在一段时间后执行指定方法,而setInterval( )是每隔一段时间后执行指定的方法
document 对象的常用方法:
write( )
getElementById( )
getElementByName( )
getElementByTagName( )
location 对象提供了当前页面的 URL 信息.主要方法有:
reload( )是用于重新加载当前文档.
replace( )是用新的文档替换当前文档.
history 对象提供了用户最近浏览过的 URL 列表,主要方法有:
back( ):实现浏览器后退页面功能
forwarc( ):实现浏览器前进页面功能
go( ):在页面上实现前进、后退的功能.
主要用到两个属性,visibility 和display,区别在于:属性的值不同:visibility 属性的值有:visible(可见),hidden(不可见),Display 属性的值有:none(不可见),block
(可见).
visibility 属性设置元素不可见时,元素仍然占据页面上的空间,而display 属性设置元素不可见不会占据页面上的空间.
代码示例:
点和所有子节点,当 deep 为 false 时,只复制指定的节点和它的属性.
4) 删除和替换节点:分别用 removeChild(none),replaceChild(newNode,oldNode).
jQuery 也就是 JavaScript 和 Query(查询),即是辅助 JavaScript 开发的库.
jQuery 优势:
1). 轻量级
2). 强大的选择器
3). 出色的 DOM 操作封装
4). 可靠的事件处理机制
5). 出色的浏览器兼容性
6). 完善的 Ajax 支持
7). 出色的浏览器兼容性等
8).jQuery 理念:写的少,做的多
答:因为 jQuery 是轻量级的框架,大小不到 30kb,它有强大的选择器,出色的 DOM 操作的封装,有可靠的事件处理机制(jQuery 在处理事件绑定的时候相当的可靠),完善的 ajax(它的
ajax 封装的非常的好,不需要考虑复杂浏览器的兼容性和 XMLHttpRequest 对象的创建和使用的问题.) 出色的浏览器的兼容性. 而且支持链式操作,隐式迭代.行为层和结构层的分离,还支持丰富的插件,jQuery 的文档也非常的丰富.
答:在项目中是怎么用的是看看你有没有项目经验(根据自己的实际情况来回答) 你用过的选择器啊,复选框啊,表单啊,ajax 啊,事件等
配置 JQuery 环境的步骤
1) 下载 jQuery 类库,在 jsp 页面引用 jQuery 类库即可
<script type="text/javascript" src="jQuery/jQuery-1.7.2.min.js"/>
2) jQuery 的调用示例:
<script>
//创建一个页面默认启动调用的特效
$(document).ready(
function( ){
alert("页面启动时调用该方法!");
}
);
//以上代码也可以写成如下的简写方式
$(function( ){
alert("页面启动时调用该方法!");
});
</script>
答:1. 获取页面的元素
2. 修改页面的外观
3. 改变页面大的内容
4. 响应用户的页面操作
5. 为页面添加动态效果
6. 无需刷新页面,即可以从服务器获取信息 7. 简化常见的 javascript 任务
答: 两个方法有相似的功能,但是在执行时机方面是有区别的.
1) window.onload 方法是在网页中所有的元素(包括元素的所有关联文件)完全加载到浏览器后才执行的.
2) $(document).ready( ) 方法可以在 DOM 载入就绪时就对其进行操纵,并调用执行绑定的函数.
Jquery 对象才能调用jquery 中特有的方法.例如jqueryObj.html( )、jqueryObj.val( ) 等方法,而不能使用dom 对象特有的属性和方法,例如 domObj.value,domObj.innerHTML 等.
Jquery 对象可以和 dom 对象相互转化
基本选择器
基本选择器是 jQuery 中最常见的选择器,也是最简单的选择器,它通过元素 id,class 和标记名来查找DOM 元素
层次选择器
如果想通过 DOM 元素之间的层次关系来获取特定元素,就需要使用层次选择器
过滤器选择器
过滤选择器主要是通过特定的过滤规则来筛选出所需的 DOM 元素,该选择器都以“:” 开头,过滤选择器又分以下几种
表单选择器
表单选择器主要是通过所选择的表单元素进行过滤
属性选择器
属性过滤选择器的过滤规则是通过元素的属性来获取相应的元素
内容过滤器选择器
内容过滤选择器的过滤规则主要体现在它所包含的子元素和和文本内容上
可见性过滤选择器
可见性过滤选择器是根据元素的可见和不可见状态来选择相应的元素
答:jQuery 选择器支持 CSS 里的选择器,jQuery 选择器可用来添加样式和添加相应的为 CSS 中的选择器是只能添加相应的样式.
答: hover( )和 toggle( )都是 jQuery 中两个合成事件. hover( )方法用于模拟光标悬停事件.
toggle( )方法是连续点击事件.
答: 如果是一些常规的 ajax 程序的话,使用 . l o a d ( ) , .load( ), .load(),.get( ), . p o s t ( ) , 就可以搞定了 , 一般我会使用的是 .post( ),就可以搞定了, 一般我会使用的是 .post(),就可以搞定了,一般我会使用的是.get( ) 方法. 如果需要设定 beforeSend(提交前回调函数),error(失败后处理),success(成功后处理) 及 complete(请求完成后处理)回调函数等,这个时候我会使用$.ajax( )
1)
.
g
e
t
(
)
方法使用
G
E
T
方法来进行异步请求的
,
.get( ) 方法使用 GET 方法来进行异步请求的,
.get()方法使用GET方法来进行异步请求的,.post( ) 方法使用 POST 方法来进行异步请求的.
2) get 请求会将参数跟在 URL 后进行传递,而 POST 请求则是作为 HTTP 消息的实体正文
内容发送给 Web 服务器的,这种传递是对用户不可见的.
3) get 方式传输的数据大小不能超过 2KB 而 POST 要大的多
4) GET 方式请求的数据会被浏览器缓存起来,因此有安全问题.
答:load 方法一般在 载入远程 HTML 代码并插入到 DOM 中的时候用通常用来从 Web 服务器上获取静态的数据文件.
如果要传递参数的话,可以使用$.get( ) 或 $.post( ).
答:三种,html 拼接的,json 数组,form 表单经 serialize( )序列化的.
答: 知道,事件冒泡是从里面的往外面开始触发.
在 jQuery 中提供了 stopPropagation( )方法可以停止冒泡.
答: hide( ) 和 show( ) 同时修改多个样式属性.像高度,宽度,不透明度.
fadeIn( ) 和 fadeOut( ) fadeTo( ) 只改变不透明度
slideUp( ) 和 slideDown( ) slideToggle( ) 只改变高度
animate( ) 属于自定义动画的方法.、
jQuery 的 html( )可以给现有元素附加新的元素.
直接在元素还未生成前就绑定肯定是无效的,因为所绑定的元素目前根本不存在.
所以我们可以通过live 和 livequery 来动态绑定事件,这种绑定方式对现有和新生成的元素都有效.
URL,是英文 Uniform Resource Locator 的缩写,意思是统一资源定位符,是标准的互联网行资源(图片,Javacript 脚本文件等)的地址,是用于完整的描述 Internet 上网页和其他资源的地址的一种标识方法.简单的说,URL 就是我们常说的网址.
/:Web 应用的根目录,该目录下的所有文件对客户端都可以访问包括JSP、HTML
/WEB-INF:存放应用程序所使用的各种资源,该目录及其子目录对客户端都是不可以访问的,其中包括 web.xml(部署表述符)
/WEB-INF/classes:存放应用的所有 class 文件
/WEB-INF/lib:存放Web 应用使用的 JAR 文件
/bin:存放各种平台下用于启动和停止 Tomcat 的脚本文件
/conf:存放 Tomcat 服务器的各种配置文件,其中最重要的文件时 server.xml
/lib:存放 Tomcat 服务器所需的各种 JAR 文件
/logs:存放 Tomcat 的日志文件
/temp:Tomcat 运行时用于存放临时文件
/webapps:Web 应用的发布目录
/work:Tomcat 把由 JSP 生成的 Servler 放于此目录下
Tomcat 工作原理?(重点)
Tomcat 是 Servlet 运行环境(容器),每个 Servlet 执行 init( ),service( ),destory( ).
下面以分析 Tomcat Server 处理一个 http 请求的过程来解释 Tomcat 原理. 假设来自客户的请求为:http://localhost:8080/wsota/wsota_index.jsp
请求被发送到本机端口 8080,被在那里侦听的 Coyote HTTP/1.1 Connector 获得
Connector 把该请求交给它所在的Service 的Engine 来处理,并等待来自Engine 的回应
Engine 获得请求 localhost/wsota/wsota_index.jsp,匹配它所拥有的所有虚拟主机Host
Engine 匹配到名为 localhost 的 Host(即使匹配不到也把请求交给该 Host 处理,因为该 Host 被定义为该 Engine 的默认主机)
localhost Host 获得请求/wsota/wsota_index.jsp,匹配它所拥有的所有 Context
Host 匹配到路径为/wsota 的 Context(如果匹配不到就把该请求交给路径名为""的
Context 去处理)
path="/wsota"的Context 获得请求/wsota_index.jsp,在它的mapping table 中寻找对应的 servlet
Context 匹配到URL PATTERN 为*.jsp 的servlet,对应于 JspServlet 类
构造 HttpServletRequest 对象和 HttpServletResponse 对象, 作为参数调用
JspServlet 的 doGet 或 doPost 方法
Context 把执行完了之后的 HttpServletResponse 对象返回给 Host
Host 把 HttpServletResponse 对象返回给 Engine
Engine 把 HttpServletResponse 对象返回给 Connector
Connector 把 HttpServletResponse 对象返回给客户 browser
JSP 的执行过程? (重点)
1) 翻译阶段:当 Web 服务器接收到JSP 请求时,首先会对 JSP 文件进行翻译,将编写好的
JSP 文件通过 JSP 引擎转换成可识别的Java 源代码.
2) 编译阶段:经过翻译后的 JSP 文件相当于我们编写好的 Java 源文件,此时仅有Java 源文件是不够的,必须要将Java 源文件编译成可执行的字节码文件.所以 Web 容器处理JSP 请求的第二阶段就是执行编译.
3) 执行阶段:Web 容器接收了客户端的请求后,经过翻译和编译两个阶段,生成了可被执行的二进制字节码文件,此时就进入执行阶段.当执行结束后,会得到处理请求的结果,Web 容器又会再把生成的结果页面返回到客户端用户面前显示.
131. JSP 指令有几种?
JSP 指令主要有三种:page 指令,include 指令,taglib 指令
language:指定 JSP 页面使用的脚本语言,默认为“Java”
Import :通过该属性来引用脚本语言中使用到的类文件
contentType: 用来指定JSP 页面所采用的编码格式,
JSP 共有以下 9 个内置的对象:
request:表示 HttpServletRequest 对象,用户端请求.它包含了有关浏览器请求的信息,并且提供了几个用于获取cookie, header, 和 session 数据的有用的方法.
response:表示 HttpServletResponse 对象,并提供了几个用于设置送回 浏览器的响应的方法
(如 cookies,头信息等),网页传回用户端的回应
out:对象是javax.jsp.JspWriter 的一个实例,并提供了几个方法使你能用于向浏览器回送输出结果.
pageContext:表示一个javax.servlet.jsp.PageContext 对象.该对象提供了对JSP 页面内所有的对象及名字空间(就是四大作用域空间,如 page 空间、request 空间、session 空间、application 空间)的访问,也就是说他可以访问到当前请求对应 session 中保存的信息,也可以取当前应用所在的 application 的某一属性值,它相当于页面中所有功能的集大成者,包装了通用的 servlet 相关功能的方法.
session:表示一个请求的 javax.servlet.http.HttpSession 对象.Session 可以存贮用户的状态信息
applicaton: 表示一个 javax.servle.ServletContext 对象.类似于系统的全局变量,用于实现
Web 应用中的资源共享.
config:表示一个 javax.servlet.ServletConfig 对象.用于存放 JSP 编译后的初始数据.
page:表示从该页面产生的一个 servlet 实例,JSP 网页本身
exception: 针对错误网页,未捕捉的例外.表示 JSP 页面运行时产生的异常和错误信息,该对象只有在错误页面(page 指令中设定 isErrorPage 为 true 的页面)中才能够使用.
(1) getParameter(String name):获得客户端传送给服务器端的有name 指定的
参数值
(2) getParametervalues(String name):获得有 name 指定的参数的所有值
(3) void setCharacterEncoding(String charset):指定每个请求的编码,在request.getParameter( )方法之前进行设定,可以用于解决中问乱码问题
(4) getRequestDispatcher(String path):返回一个
Javax.servlet.RequestDispatcher 对象,该对象的forward 方法用于转发请求
(5) setAttribute(String name,Object):设置名字为 name 的request 的参数值
(6) getAttribute(String name):返回由 name 指定的属性值
(7) getAttributeNames( ):返回request 对象所有属性的名字集合,结果是一个枚举的实例
(8) getCookies( ):返回客户端的所有Cookie 对象,结果是一个 Cookie 数组
(9) getCharacterEncoding( ):返回请求中的字符编码方式
(10) getParameterNames( ):获得客户端传送给服务器端的所有参数的名字,结果是一个枚举的实例
(11) getProtocol( ):获取客户端向服务器端传送数据所依据的协议名称(12)getRequestURI( ):获取发出请求字符串的客户端地址(13)getRemoteAddr( ):获取客户端的 IP 地址
(14)getRemoteHost( ):获取客户端的名字(15)getSession([Boolean create]):返回和请求相关 Session (16)getServerName( ):获取服务器的名字(17)getServletPath( ):获取客户端所请求的脚本文件的路径(18)getServerPort( ):获取服务器的端口号(19)removeAttribute(String name):删除请求中的一个属性
1) void addCookie(Cookie cookie):在客户端添加 Cookie
2) void setContentType(Sring type):设置 HTTP 响应的 contentType 类型
3) void SetCharacterEncoding(String charset):设置响应所采用的字符编码类型
4) void sendRedirect(String location): 将请求重新定位到一个新的 URL 上
请求转发:请求转发是指将请求再转发到另一资源(一般为 JSP 或 Servlet).此过程依然在同一个请求范围内,转发后浏览器地址栏内容不变,请求转发使用RequestDispatcher 接口
中的 forward( )方法来实现,该方法可以把请求转发到另外一个资源,并让该资源对浏览器的
请 求 进 行 响 应 RequestDispatcher rd = request.getRequestDispatcher(path); rd.forward(request,response); 或
request.getRequestDispatcher(path) .forward(request,response);
重定向:重定向是指页面重新定位到某个新地址,之前的请求失效,进入一个新的请求, 且跳转后浏览器地址栏内容将变为新的指定地址.重定向是通过 HttpServletResponse 对象的
sendRedirect( )来实现,该方法相当于浏览器重新发送一个请求 response.sendRedirect(path);
• 请求转发和重定向区别如下:
forward( )只能将请求转发给同一个 Web 应用中的组件,而 sendRedirect( )方法不仅可以重定向到当前应用程序中的其他资源,还可以重定向到其他站点的资源.
请求转发和重定向是 Servlet 处理完数据后进行页面跳转的两种主要方式
psendRedirect( )方法重定向的访问过程结束后,浏览器地址栏中显示的 URL 会发生改变,由初始的 URL 地址变成重定向的目标 URL;而调用 forward( )方法的请求转发过程结束后,浏览
器地址栏保持初始的 URL 地址不变.forward( )方法的调用者与被调用者之间共享相同的request 对象和 response 对象;而 sendRedirect( )方法调用者和被调用者使用各自的request 对象和 response 对象,它们属于两个独立的请求和响应过程.
会话的跟踪,就是 session 机制,Session 机制是一种服务器端的机制,服务器使用一种类
似于散列表(HashTable)的结构来保存信息,主要用于在整个会话请求过程中共享数据.
Session 机制的实现原理:
void setAttribute(String key,Object value):以 key/value 的形式将对象保存到 session 中
Object getAttribute (String key):通过 key 获取 session 中保存的对象
void invalidate( ):设置 session 对象失效
String getId( ):获取 sessionId
void setMaxInactiveInterval( ):设置 session 的非活动时间
int getMaxInactiveInterval( ):获取 session 的有效非活动时间,以秒为单位
void removeAttribute(String key):从 session 中删除指定名称(key)所对应的对象
由于 HTTP 事务是无状态的,因此必须采取特殊措施是服务器在系列事务期间能继续确定和记住特定用户,这就是会话跟踪的概念.
实现此功能有四种实现技术:
1、用隐藏表单域() 非常适合不需要大量数据存储的会话应用.
2、URL 重写
URL 可以在后面附加参数,和服务器的请求一起发送,服务器根据相应的参数来判断是否为同一个客户端发送的请求,一般可以直接使用 HTTP 会话 API 执行 URL 重写,会自动附加相应的 sessionid,该技术主要使用场景是客户端的浏览器禁用 cookie 导致 session 无法使用的情况.
3、Cookie 技术
服务器通过 cookie 对象来保存 session 对象的 id,用来保证客户端每次发送请求过来都会带上 sessionid 以保证服务器可以识别不同客户端的请求,是 session 机制实现的前提,如果用户禁用 cookie 就只能使用 url 重写来保证 sessionid 的传递了.
4、Session 技术
根据session 对象的 sessionid(唯一标识)即可识别不同的客户端请求
page 是代表与一个页面相关的对象和属性.一个页面由一个编译好的 Java Servlet 类
(可以带有任何的 include 指令,但是没有 include 动作)表示.这既包括 servlet 又包括被编译成 servlet 的 JSP 页面
request 是代表与Web 客户机发出的一个请求相关的对象和属性.一个请求可能跨越多个页面,涉及多个Web 组件(由于 forward 指令和 include 动作的关系)
session 是代表与用于某个 Web 客户机的一个用户体验相关的对象和属性.一个
Web 会话可以也经常会跨越多个客户机请求
application 是代表与整个Web 应用程序相关的对象和属性.这实质上是跨越整个
Web 应用程序,包括多个页面、请求和会话的一个全局作用域
Cookie,有时也用其复数形式 cookies,指某些网站为了辨别用户身份、进行session 跟踪而储存在用户本地终端上的数据(通常经过加密).
Cookie 是由服务器端生成,发送给客户端浏览器的,浏览器会将其保存成某个目录下的文本文件.
Cookie cookie = new Cookie(“cookieName”,“cookieValue”);
其中 cookieName 为 Cookie 对象的名称,未来获取 Cookie 的时候需要使用.cookieValue 为
Cookie
对象的值也就是储存用户的信息如用户名、 密码等.
对特定对象的追踪,如访问者的访问次数,最后访问时间、路径等
统计网页浏览记录.
在Cookie 有效期内,记录用户登入信息.
实现各种个性化服务,如针对不同用户喜好以不同的风格展示不同的内容
提示:由于Cookie 保存在客户端,所以使用Cookie 存在一定的风险,所以不建议在Cookie
中保存比较重要或敏感的内容.
数据库连接是一种关键的有限的昂贵的资源,这一点企业级应用程序中体现得尤为突出. 对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标. 数据库连接池正是针对这个问题提出来的.
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而再不是重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏.这项技术能明显提高对数据库操作的性
能.
具体工作机制如下:
数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数来设定的.无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量.连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中.
数据库连接池的最小连接数和最大连接数的设置要考虑到下列几个因素:
MVC 模式是一种流行的软件的设计模式,MVC 模式将应用程序实现分为 3 个不同的基本部分.模型,视图和控制器.
模型(Model):表示数据和业务处理.在 MVC 的三个部件中,模型拥有最多的处理任务. 被模型返回的数据是中立的,就是说模型与数据格式无关,这样一个模型能为多个视图提供数据,由于应用于模型的代码只需写一次就可以被多个视图重用,所以减少了代码的重复性.对 应的组件是java Bean( java 类).
视图(view): 是用户看到并与之交互的界面.MVC 一个最大的好处就是它能为应用程序处理很多不同的视图,在视图中其实没有真正的处理发生,不管这些数据是什么,作为视图来 讲,它只是作为一种输出数据并允许用户操作的方式.对应的组件是 JSP 或 HTML 文件.视图提供可交互的界面,向客户显示模型数据.
控制器(Controller): 接收用户的输入并调用模型组件去完成用户的请求.当用户提交一个请求时,控制器本身不做执行业务处理.它只是接收请求并决定调用哪个模型组件去处理请求,然后确定用哪个视图来显示模型处理返回的数据.对应的组件可以是 Servlet. 控制器
响应客户的请求,根据客户的请求来操作模型,并把模型的响应结果经由视图展现给客户.
MVC 最重要的特点就是把显示和数据分离,提高代码的可重用性.
MVC 的处理过程:
首先控制器接收用户的请求,并决定应该调用哪个模型来处理,然后模型处理用户的请求并返回数据,最后控制器确定相应的视图将模型返回的数据呈现给用户.
Servlet 是一种服务器端的 Java 应用程序,具有独立于平台和协议的特性,可以生成动态的Web 页面. 它承担处理客户请求(Web 浏览器或其他 HTTP 客户程序)与服务器响应
(HTTP 服务器上的数据库或应用程序)的工作. Servlet 是位于Web 服务器内部的服务器端的Java 应用程序,与传统的从命令行启动的Java 应用程序不同,Servlet 由Web 服务器进行加载,该Web 服务器必须包含支持 Servlet 的Java 虚拟机.
servlet 有良好的生命周期的定义,包括加载和实例化、初始化、处理请求以及服务结束四个阶段.WEB 容器加载Servlet,生命周期开始.首先服务器调用 Servlet 的构造方法执行实例化操作,然后容器调用 Servlet 的 init 方法执行初始化操作,请求到达时运行 Servlet 的
service 方法,service 方法自动调用与请求类型对应的 doXXX 方法(doGet,doPost)等来处理请求,当服务器决定将 Servlet 实例销毁前调用其 destroy 方法(用于释放 Servlet 占用资源,例如流、数据库连接等).
HttpServlet 容器响应Web 客户请求流程如下:
1) Web 客户向 Servlet 容器发出 Http 请求;
2) Servlet 容器解析Web 客户的 Http 请求;
3) Servlet 容器创建一个 HttpRequest 对象,在这个对象中封装 Http 请求信息;
4) Servlet 容器创建一个 HttpResponse 对象;
5) Servlet 容器调用 HttpServlet 的service 方法,把 HttpRequest 和 HttpResponse 对象作为 service 方法的参数传给 HttpServlet 对象;
6) HttpServlet 调用 HttpRequest 的有关方法,获取 HTTP 请求信息;
7) HttpServlet 调用 HttpResponse 的有关方法,生成响应数据;
8) Servlet 容器把 HttpServlet 的响应结果传给Web 客户.
Jsp 页面中的form 表单标签里的method 属性为get 时调用doGet( ),为post 时调用
doPost( ).
JSP 是 Servlet 技术的扩展,本质上是 Servlet 的简易方式,更强调应用的外表表达.JSP 编译后是"类 servlet".Servlet 和JSP 最主要的不同点在于,Servlet 的应用逻辑是在Java 文件中,并且完全从表示层中的 HTML 里分离开来.而JSP 的情况是Java 和 HTML 可以组合成
一个扩展名为.jsp 的文件.JSP 侧重于展示内容,Servlet 主要用于控制逻辑.
通过 page 指令设置<%@ page isThreadSafe=“false”%>,默认 Servlet 支持多线程模式,即有多个客户端同时请求同一个 Servlet,服务器上的 Servlet 只会产生一个实例,但是会启动多个线程来响应客户请求,但是这样会导致线程安全问题,编程时建议不要在 Servlet 中定义成员属性来共享数据,以避免出现数据同步的问题.
当然可以,session 作用域中的内容,可以在多个请求中共享数据.例如可以在Servlet 中通过request.getSession( )来得到HttpSession 对象,然后将相应数据存储到该 session 对象中,在其它 jsp 页面上都可以通过内置对象 session 来获取存入的值,即使这些页面是通过重定向方式跳转过去的,也可以得到值.Session 本来就是用来保障在同一客户端和服务器之间多次请求之间的数据能够共享的一种机制.第八章
1).作用域访问对象
pageScope:与页面作用域(page)中属性相关联的 Map 类
requestScope:与请求作用域(request)中属性相关联的 Map 类
sessionScope:与会话作用域(session)中属性相关联的 Map 类
applicationScope:与应用程序作用域(application)中属性相关联的 Map 类
使用例子:request.setAttribute(“Student”,stu);当使用 EL 表达式访问某个属性值时候,应该 指 定 查 找 范 围 , 如 ${requestScope.Student} 如 果 不 指 定 查 找 范 围 , 会 按 照page->request->session->application 的顺序查找
注意:必须将对象保存在作用域中,才可以用 EL 表达式访问对象.
2).参数访问对象
param 按照参数名称访问单一请求值的 Map 对象
paramValues 按照参数名称访问数组请求值的 Map 对象
参数访问对象是和页面输入参数有关的隐式对象,通过它们可以得到用户的请求参
数:
比如登录页面表单有个表单项 提交用户
名,
在登录处理页面可以通过 ${param.userName} 访问它
3).JSP 隐式对象
pageContext 提供对页面信息的 JSP 内置对象的访问
1) JSP 页面显示乱码
<%@ page contentType=”text/html; charset=gb2312″%>
2) 表单提交中文时出现乱码
request.seCharacterEncoding(“gb2312″)对请求进行统一编码
3) 数据库连接出现乱码
要涉及中文的地方全部是乱码, 解决办法: 在数据库的数据库 URL 中加上
useUnicode=true&characterEncoding=GBK 就 OK 了.
4) 通过过滤器完成
5) 在 server.xml 中的设置编码格式
Hibernate 使用步骤(重点)
编写完了配置文件之后,具体的使用步骤如下:
读取并解析配置文件
读取并解析映射信息,创建 SessionFactory
打开 Session
创建事务 Transaction
持久化操作
提交事务
关闭 Session
关闭 SessionFactory
上面这些步骤是在单独使用 Hibernate 的步骤,如果通过spring 集成 Hibernate,则代码更加的简单.例如使用 Spring 的AOP 声明式事务,则不需要手工开启和关闭
Session 及提交或回滚事务.
Hibernate 中实体对象的三种状态. (重点)
http://www.cnblogs.com/jyh317/p/3666566.html
介绍一下 Hibernate 的二级缓存(重点)
按照以下思路来回答:
(1)首先说清楚什么是缓存,(2)再说有了 hibernate 的 Session 就是一级缓存, 即有了一级缓存,为什么还要有二级缓存,(3)最后再说如何配置 Hibernate 的二级缓存.
让多个线程和多个事务都可以共享这个缓存.我们希望的是一个人使用过,其他人也可以使用,session 没有这种效果.
3) 二级缓存是独立于 Hibernate 的软件部件,属于第三方的产品,多个厂商和组织都提供有缓存产品,例如,EHCache 和 OSCache 等等.在 Hibernate 中使用二级缓存,首先就要在hibernate.cfg.xml 配置文件中配置使用哪个厂家的缓存产品,接着需要配置该缓存产品自己的配置文件,最后要配置 Hibernate 中的哪些实体对象要纳入到二级缓存的管理中.
明白了二级缓存原理和有了这个思路后,很容易配置起 Hibernate 的二级缓存.扩展知识: 一个 SessionFactory 可以关联一个二级缓存,也即一个二级缓存只能负责缓存一个数据
库中的数据,当使用 Hibernate 的二级缓存后,注意不要有其他的应用或SessionFactory
来更改当前数据库中的数据,这样缓存的数据就会与数据库中的实际数据不一致.
Hibernate 如何延迟加载?(重点)
延迟加载机制是为了避免一些无谓的性能开销而提出来的,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作.在 Hibernate 中提供了对实体对象的延迟加载以及对集合的延迟加载,另外在 Hibernate3 中还提供了对属性的延迟加载.下面我们就分别介绍这些种类的延迟加载的细节.
类与类之间的关系主要体现在表与表之间的关系进行操作,它们都是对对象进行操作,
我们程序中把所有的表与类都映射在一起,它们通过配置文件中的 many-to-one、
one-to-many、many-to-many.
最常用的关系是双向一对多关系,也即主从表之间的关系: 双向一对多关系中,在这表这边配置:
Set 集合,one-to-many
在从表这边配置 many-to-one
如何优化 Hibernate?(重点)
内部缓存在 Hibernate 中又叫一级缓存,属于应用事务级缓存二级缓存:
从以下四个方面来说明工作原理:
配置文件 Hibernate.cfg.xml 文件中定义了和数据库进行连接的信息,包括数据库方
言.jdbc 驱动.用户名.密码和 URL 等.Configuration 类借助 dom4j 的 xml 解析器进行 xml 的解析设置环境,然后使用这些环境属性来生成 sessionfactory.这样 sessionfactory 生成的 session 就能够成功获得数据库的连接.
当保存一个 pojo 持久化对象时,触发 Hibernate 保存事件监听器进行处理.Hibernate 通过映射文件获得对象对应的数据库.表名以及属性对应的数据库列名,然后通过反射机制获得持久化对象的各个属性,最终组织向数据库插入新对象的 SQL 的insert 语句.调用
session.save()保存数据后这个对象会被标识为持久化状态放在 session,当事务进行提交时才真正执行insert 语句.
当需要读取读取文件时,Hibernate 先尝试从 session 缓存中读取,如果 session 缓存数据不存在或是脏数据并且配置了二级缓存,Hibernate 尝试从二级缓存中检索数据;否则
Hibernate 会根据对象类型·主键等信息组织select 语句到数据库中读取,再把select 结果组织成对象返回.
Hibernate 提供SQL、HQL、Criteria 查询方式.HQL 是其中运用最广泛的查询方式.用户使用 session.createQuery()函数以一条 HQL 语句为参数创建 Query 查询对象后,Hibernate 会使用 Anltr 库把 HQL 语句解析成 jdbc 可以识别的 sql 语句.如果设置了查询缓存,那么执行Query.list()时,Hibernate 会先对查询缓存进行查询,如果查询缓存不存在,再使用 select 语句查询数据库.
请注意如果没有匹配的数据库记录,load( ) 方法可能抛出无法恢复的异常(unrecoverable exception). 如果类的映射使用了代理(proxy),load( )方法会返回一个未初始化的代理,直到你调用该代理的某方法时才会去访问数据库. 若你希望在某对象中创建一个指向另一个对象的关联,又不想在从数据库中装载该对象时同时装载相关联的那个对象, 那么这种操作方式就用得上的了. 如果为相应类映射关系设置了 batch-size, 那么使用这种操作方式允许多个对象被一批装载(因为返回的是代理,无需从数据库中抓取所有对象的数据). 如果你不确定是否有匹配的行存在,应该使用 get( )方法,它会立刻访问数据库,如果没有对应的行,会返回 null.
HQL 是一种面向对象的查询语言,支持属性查询、参数查询、关联查询、分页查询等特性,还支持统计函数.还有如下优点:
使用 HQL 步骤以及 HQL 优化(重点)
步骤
Spring 是一个开源的控制反转(Inversion of Control,IoC)和面向切面(AOP)的容器框架.它的主要目的是使现有技术更加易用,推荐编码最佳实现,从而简化企业开发.
Spring 目前已经发展为一个功能丰富而易用的集成框架,其核心是一个完整的控制反转(IoC)的轻量级容器,用户可以使用他建立自己的应用程序.在容器上,Spring 提供了大量实用的服务.将很多高质量的开源项目集成到统一的框架上.
Spring 致力于 Java EE 应用的各种解决方案,而不是仅仅专注于某一层面的方案.可以说,Spring 是企业应用开发的“一站式”选择,Spring 贯穿表示层、业务层、持久层.然
而,Spring 并不想取代那些已有的框架,而以高度的开发性与他们无缝整合.
在我看来,在项目中引入Spring 可以带来以下的好处:
依赖:两个元素中一个定义发生改变则会引起另一个元素发生改变,则称这两个元素之间存在依赖关系.
控制反转(IoC):在传统的程序设计过程中,都是在应用内部创建及维护依赖的对象. 控制反转就是应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护是由外部容器负责的.这样控制权就由应用转移到外部容器,控制权的转移就是控制反转.
依赖注入:是指 在运行期,由外部容器动态地将依赖对象注入到组件中.
依赖注入让 Bean 与Bean 之间以配置文件组织在一起,而不是以硬编码的方式耦合在一起.
Spring 设计的核心是org.springframework.beans 包,它的设计目标是与JavaBean 组件一起使用.这个包通常不是由用户直接使用,而是由服务器将其作其他多数底层中介.下一个最高级抽象是 BeanFactory 接口,它是工厂设计模式的实现,允许通过名称创建和检索对
象.BeanFactory 也可以管理对象之间的关系.
BeanFactory 支持两种对象模型
1) 单态 模型提供了具有特定名的对象的共享实例,可以在查询时对其进行检索.Singleton 是默认的也是最常用的对象模型.对于无状态服务对象很理想.
2) 原型 模型确保每次检索都会创建单独的对象.在每个用户都需要自己的对象时,
原型模型最适合.
bean 工厂的概念是 Spring 作为 IOC 容器的基础.IOC 将处理事情的责任从应用程序转移到框架.Spring 框架使用 JavaBean 属性和配置数据来指定必须设置的依赖关系.
面向方面编程也叫面向切面编程,即 AOP,是一种编程技术,它允许程序员对横切关注点或横切典型的职责分界线的行为(例如日志和事务管理)进行模块化.AOP 的核心构造是方
面,它将那些影响多个类的行为封装到可重用的模块中.
AOP 和 IOC 是补充性的技术,它们都是运用模块化方式解决企业应用程序开发中的复杂问题.在典型的面向对象的开发方式中,可能要将日志记录语句放在所有方法和 Java 类中才能实现日志功能.在AOP 方式中,可以反过来将日志服务模块化,并以声明的方式将它们应用到需要的日志组件上.当然,优势就是 Java 类不需要知道日志服务的存在,也不需要知道日志服务的存在,也不需要考虑相关的代码.所以,用 Spring AOP 编写的应用程序代码是松散耦合的.
AOP 的功能完全集成到了 Spring 事务管理、日志和其他各种特性的上下文中.
Spring 采用 JDK 动态代理模式来实现 AOP 机制.
Spring AOP 采用动态代理的过程:
(1) 将切面使用动态代理的方式动态织入到目标对象(被代理类),形成一个代理对象;
(2) 目标对象如果没有实现代理接口,那么 Spring 会采用 CGLib 来生成代理对象, 该代理对象是目标对象的子类;
(3) 目标对象如果是 final 类,并且也没实现代理接口,就不能运用 AOP.
1、Aspect(切面)
切面,是对交叉业务逻辑的统称.
2、Joinpoint(连接点)
连接点,指切面可以织入到目标对象的位置(方法,属性等).
3、Advice(通知)
通知,指切面的具体实现.
4、Pointcut(切入点)
切入点,指通知应用到哪些类的哪些方法或属性之上的规则.
5、Introduction(引入)
引入,指动态地给一个对象增加方法或属性的一种特殊的通知.
6、Weaving(织入)
织入,指将通知插入到目标对象.
7、Target(目标对象)
目标对象,指需要织入切面的对象.
8、Proxy(代理对象)
代理对象,指切面织入目标对象之后形成的对象.
优点:
Spring 的依赖检查能够帮助检查在一个Bean 上的所有特定类型属性是否都已经设置依赖注入.只要在 dependency-check 的属性中指定依赖检查模式就可以了.
依赖检查的几种模式:
1) none:不执行任何依赖检查.任何属性都可以保持未设置状态,为默认;
2) simple:检查任意简单类型(原始(例如 int、double 等,还包括 String 类型)和集合类型)的属性是否设置,未设置将抛出UnsatisfiedDependencyexception 异常;
3) objects:检查任意对象类型属性是否设置,未设置将抛出
UnsatisfiedDependencyexception 异常;
4) all:检查任意类型的属性是否设置,未设置将抛出 UnsatisfiedDependencyexception 异常.
1) 、当项目规模大的时候,配置文件可读性、可维护性差,庞大的 Spring 配置文件难以阅读.
2) 、团队开发时,多人修改同一配置文件容易发生冲突,降低开发效率.
1) 、如每个开发人员负责一模块采用公用配置(包含数据源、事务等)+每个系统模 块一个单独配置文件(包含 dao、service 及 action)形式
2) 、如采用分层负责或者 DAO 配置代码由代码生成工具生成时,我们采用公用配置(包含数据源、事务等)+ DAO Bean 配置 + 业务逻辑 Bean 配置 + Action Bean 配置形式
① JDBC,② O/R Mapping(Hibernate,TopLink 等)
Spring 对持久层支持采用的策略:
1、Spring 对持久层“不发明重复的轮子”,即没有重新实现新的持久层方案,对现有持久层方案做封装,更利于使用.
2、采用DAO 模式
3、提供了大量的模板类来简化编程(HibernateDaoSupport,JdbcTemplate 等)
4、重新设计了一套完善的异常体系结构
① 类型丰富,细化异常类型
② 全都是运行时异常(RuntimeException)
Spring 支持声明式事务.声明式事务管理采用非侵入式的设计,可以分离业务逻辑和事务管理逻辑,具有良好的适应性.
Spring 使用事务服务代理和事务管理器(如 HibernateTransactionManager)来支持事务服务.
相对于 EJB 规范,Spring 对事务的边界多了一种嵌套事务(PROPAGATION_NESTED).
PROPAGATION_NESTED 含义:
如果客户端启动了事务 T1,那么 Bean 启动一个嵌套在 T1 中的子事务T2;
如果客户端没有启动事务,那么 Bean 会启动一个新的事务,类似于 REQUIRED_NEW
用在 Spring 配置文件中声明式的处理事务来代替硬代码式的处理事务.这样的好处是,事务管理不侵入开发的组件,具体来说,业务逻辑对象就不会意识到正在事务管理之中,事实上也应该如此,因为事务管理是属于系统层面的服务,而不是业务逻辑的一部分,如果想要改变事
务管理策略的话,也只需要在定义文件(即配置文件)中重新配置即可;在不需要事务管理的时候,只要在设定文件上修改一下,即可移去事务管理服务,无需改变代码重新编译,这样维护起来极其方便.
1) 配置 Hibernate 的事务管理器.
2) 定义事务通知,需要指定一个事务管理器
3) 使用 AOP,定义哪些方法应用这些规则
SpringMVC 工作机制?(重点)
spring mvc 请所有的请求都提交给DispatcherServlet,它会委托给应用系统的其他模块负责对请求进行真正的处理工作.
DispatcherServlet 查询一个或多个 HandleMapping,找到处理请求的Controller
DispatchServlet 请求提交到目标Controller
Controller 进行业务逻辑处理后,会返回一个 ModelAndView
Dispatcher 查询一个或多个 ViewResolver 视图解析器,找到 ModelAndView 对象指定的视图对象
视图对象负责渲染返回给客户端.
什么是 POJO 类(重点)
使用 POJO 名称是为了不和 EJB 混淆起来, 而且简称比较直接. 其中有一些属性及其
getter setter 方法的类,有时可以作为value object 或 dto(Data Transform Object)来使用.当然, 如果你有一个简单的运算属性也是可以的,但不允许有业务方法,也不能携带有connection 之类的方法.
错误的认识:(了解一下错误的说法,主要是需要记住正确的说法)
POJO 是这样的一种“纯粹的”JavaBean,在它里面除了 JavaBean 规范的方法和属性没有
别的东西,即 private 属性以及对这个属性方法的 public 的 get 和 set 方法.我们会发现这样的 JavaBean 很“单纯”,它只能装载数据,作为数据存储的载体,而不具有业务逻辑处理的能力.
真正的意思:(正确意义)
POJO = “Plain Old Java Object”,是 MartinFowler 等发明的一个术语,用来表示 普通的
Java 对象,不是JavaBean, EntityBean 或者 SessionBean.POJO 不但当任何特殊的角色,也不
实现任何特殊的 Java 框架的接口如,EJB, JDBC 等等.即 POJO 是一个简单的普通的 Java 对
象,它包含业务逻辑或持久逻辑等,但不是 JavaBean、EntityBean 等,不具有任何特殊角色和
不继承或不实现任何其它 Java 框架的类或接口.
BEA WebLogic Server,
IBM WebSphere Application Server, Oracle9i Application Server, jBoss,
Tomcat
Web 服务是一种可以用来解决跨网络应用集成问题的开发模式,是基于网络的、分布式的模块化组件,它执行特定的任务遵守具体的技术规范,这些规范使得Web Service 能与其他兼容的组件进行互操作.设计WEB 服务时应该把握的几个关键性原则:松散耦合、定义良好的接口、合适的粒度.web 服务体系结构的三种角色是:服务提供者、服务代理机构、服务使用者
Web 服务的优点有哪些?(重点)
封装性:
Web 服务是一种部署在Web 应用上的组件,具备良好的封装性.对使用者而言,仅看到服务描述,而该服务的具体实现、运行平台都是透明的,调用者无须关心,也无法关
心.Web 服务作为整体提供服务.
松散耦合:
当Web 服务的实现方式发生改变时,调用者是无法感受到这种改变的.对调用者而言,只要服务实现的接口不发生变化,具体实现的改变时完全透明的.
使用标准协议:
Web 服务所有的公共协议都是用标准协议描述、传输和交换.这些标准协议在各种平台上完全相同.
高度整合的能力:
由于Web 服务采用简单、易理解的标准Web 协议作为通信协议,完全屏蔽了不同平台的差异,无论是 CORBA、DOM 还是 EJB,都可以通过这种标准协议进行互操作,实现系统的最高可整合性.
高度的开放性:
Web 服务可以与其他的 Web 服务进行交互,具有语言和平台型,支持 CORBA,EJB,DCOM
等多种组件标准,支持各种通讯协议,如 HTTP、SMTP 和 RMI 等.
MVC 框架:Struts1,、Struts2、SpringMVC
ORM 框架:Hibernate、TopLink、ibatis、MyBatis
Ajax 框架:DWR、JQuery
其它框架:Spring、Xfire、SSH(spring,hibernate,struts 三个框架的集成)
Hibernate:
是一个优秀的持久化框架,负责简化将对象数据保存到数据库中,或从数据库中读取数据并封装到对象的工作.
Struts2:
以 webwork 优秀的设计思想为核心,吸收了Struts1 框架的部分优点,提供了一个更加整洁的 MVC 设计模式实现的 Web 应用程序框架;它引入了几个新的框架特性:从逻辑中分离出横切关注点的拦截器,减少或者消除配置文件,贯穿整个框架的强大表达式语言,支持可 变更和可重用的基于 MVC 模式的标签 API.
Spring:
Spring 的出现改变了 Java 世界,它的目标是使现有的 JavaEE 技术更容易使用和促进良好的编程习惯.它是一个轻量级的框架,渗透了 JavaEE 技术的方方面面,它主要作为依赖注入容器和 AOP 实现存在.还提供了声明式事务,对 DAO 层的支持等简化开发的功能.Spring 还可以很方便的与Struts2.hibernate 等框架集成.
权限管理怎么做的,用到了几张表? (重点)
权限管理这里我们用到了五张表,三张主表 user(用户表),role(角色表),privilege(权限表),两张
关系表,user-role,privilege-role, 关系表是为了解决 user 与 role、privilege 与 role 之间的多对多的关系,user 可以有若干个 role,role 可以给很多 user 使用,role 可以有若干个privilege,privilege 可以赋给很多role.
198. 电商项目里 redis 用到了哪些地方? (重点)
1,redis 生成商品 ID.
2,页面查询条件的缓存,(好处:页面初始化数据快速加载,提升用户体验度)
3,redis 集群(为了承载更大的数据量和redis 服务器的稳定,我们使用的redis-Cluster 方案,采用了 6 台节点(服务器)进行同时使用.三台主机,三天客机,如果主机发生宕机(挂掉)的情况,那么客机直接戴特使用,主机恢复后,主机机会继续使用)
4,session 共享,(因为我们的项目服务器架构方案是集群,并且是通过Nginx 进行转发,所以我们的用户 session 需要使用redis 来进行共享,缓存 session 信息)
5,购物车在登录的情况下,用的是 redis 来存储购物车.非登录的情况下,使用的 cookie 来缓存购物车(cookie 保存时间一个月).
因为商品详情页面访问量较大,为了节省网络资源,提高用户体验,因此我们使用 freeMarker
技术将商品详情页静态化
Freemarker 使用步骤:
内容都可能不同.网页设计者在写好服务器端的页面程序后,不需要手工控制,页面内容会按照页面程序的安排自动更改变换.
企业级购物车设计方案,面向对象的设计模型,购物车包含购物项,购物项包含最小销售单元
sku(sku=颜色*尺码),在非登录的情况下我们购物车是存储在 cookie 里,保存时间为一个月,在登录的情况下,使用redis 来存储购物车,使用redis 的 list 来存储购物车,hash 来存储购物项.
FastDFS 如何搭建的?怎么用的? (重点)
搭建:
1,配置基本环境2,安装Tracker 服务3,配置Tracker 服务4,安装storage 服务5,配置storage
服务,6 测试.
参考网址:详细的安装步骤以及 jar 网址: http://www.linuxidc.com/Linux/2016-04/130371.htm 使用:
1,先启动 fastDFS(代码 . fastDFS.sh)
2,查看 FastDFS 文件系统的服务
3, 查看 trackerd 进程是否启动成功,trackerd 进程是保存图片的存储路径( 代码 ps aux|grep trackerd)
4,查看 storaged 进程是否启动成功,storaged 进程是用来保存图,以及图片的副本.(代码
ps aux|grep storage)
ArrayList 初始化容量为:10,如果超出,扩容为原容量的 0.5 倍+1,第一次扩容为 16
HashMap 初始化容量为:16,如果超出,扩容为原容量的 1 倍,第一次扩容为 32
HashSet 初始化容量为: 16,如果超出,扩容为原容量的 1 倍,第一次扩容为 32
说说 redis 的五种数据类型(重点)
List(列表),Hash(哈希),String(字符串),Set(集合),Zset(sorted set,有序集合)在项目中,我们前三者用的相对较多.
Solr 在项目里用到了哪些地方? (重点)
1,因为电商网站的检索,我们不可能去用一个 like 模糊查询,我们使用的 solr 用来缓存商品上架数据.
2,使用了第三方的 IK 分词器进行分词,配置扩展字典和停词字典.
3,对查询的条件进行高亮显示.
4,为了维护项目的稳定性,以及提供更好的服务能力,我们这里 solr 使用的是集群方案,并且在solr 集群方案下我们使用的是大数据的zookeeper 框架来作为服务器中间件,对solr 服务器进行监听,给客户端提供服务.
追问:那你跟我说说zookeeper
因为 solr 集群依赖的是 zookeeper 服务中间件,所以就算宕机,也不会影响我们整个服务的使用,然后 zookeeper 也是使用的集群方案.三台机器进行的集群,根据 zookeeper 集群的特点,一台 leader,两台 follower.
在高并发处理这方面,我们使用的是 Nginx 服务器来做负载均衡和反向代理.并且 Nginx 我们也是搭建的两台服务器,并且使用 keepalived 来维护Nginx 的高可用(HA 机制)
使用过.
1.static Level DEBUG :
DEBUG Level 指出细粒度信息事件对调试应用程序是非常有帮助的. 2.static Level INFO
INFO level 表明 消息在粗粒度级别上突出强调应用程序的运行过程. 3.static Level WARN
WARN level 表明会出现潜在错误的情形. 4.static Level ERROR
ERROR level 指出虽然发生错误事件,但仍然不影响系统的继续运行. 5.static Level FATAL
FATAL level 指出每个严重的错误事件将会导致应用程序的退出.
另外,还有两个可用的特别的日志记录级别:
1.static Level ALL
ALL Level 是最低等级的,用于打开所有日志记录. 2.static Level OFF
OFF Level 是最高等级的,用于关闭所有日志记录.
日志记录器(Logger)的行为是分等级的:
分为 OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL 或者自定义的级别.
一般我们只使用这四种,优先级从高到低分别是 ERROR、WARN、INFO、DEBUG.
实现多线程的方法? (重点)
第一种是继承 Thread 类,实现 run( )方法,不可以抛异常,没有返回值第二种是实现Runnable 接口,实现 run( )方法,不可以抛异常,无返回值代码
(1) 继承 Thread 类,重写run 函数创建:
class xx extends Thread{ public void run( ){
Thread.sleep(1000) //线程休眠1000 毫秒,sleep 使线程进入Block 状态,并释放资
源
}}
开启线程:
对象.start( ) //启动线程,run 函数运行
(2) 实现 Runnable 接口,重写 run 函数开启线程:
Thread t = new Thread(对象) //创建线程对象t.start( )
(3) 实现 Callable 接口,重写 call 函数
Callable 是类似于 Runnable 的接口,实现 Callable 接口的类和实现 Runnable 的类都是可被其它线程执行的任务.
Callable 和Runnable 有几点不同:
①Callable 规定的方法是 call( ),而 Runnable 规定的方法是run( ).
②Callable 的任务执行后可返回值,而Runnable 的任务是不能返回值的
③call( )方法可抛出异常,而run( )方法是不能抛出异常的.
④运行 Callable 任务可拿到一个 Future 对象,Future 表示异步计算的结果,它提供了检查计算是否完成的方法,以等
待计算的完成,并检索计算的结果.通过 Future 对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果
MySQL:
常用分页:select * from content order by id desc limit 0,10;
Limit 是 MySQL 中特有的分页语法举例:
Select * from tableName limit 5; //返回前五行
Select * from tableName limit 0,5;//同样也是返回前五行Select * from tableName limit 5,10;//返回 6-15 行
Oracle:
--分页查询一
select * from (select a1.*,rownum rn from (select * from student) a1 where rownum <=5) where rn>=2;
--分页查询二
select a1.* from (select student.*,rownum rn from student where rownum <=5) a1 where rn >=3;
--分页查询三
select a1.* from (select student.*,rownum rn from student) a1 where rn between 3 and 5;
1,枚举写法
public enum Singleton { INSTANCE;
public void whateverMethod( ) {
}
}
2,懒汉(线程不安全)
public class Singleton {
private static Singleton instance; private Singleton ( ){}
public static Singleton getInstance( ) { if (instance == null) {
instance = new Singleton( );
}
return instance;
}
}
3,懒汉(线程安全)
public class Singleton {
private static Singleton instance; private Singleton ( ){}
public static synchronized Singleton getInstance( ) { if (instance == null) {
instance = new Singleton( );
}
return instance;
}
}
当然,还几种方式(共七种),谷歌搜索都可.
springmvc 只是 spring 其中的一部分.
spring 可以支持 hibernate ,mybatis ,JDBC
支持事务管理, 注解功能,表达式语言,测试
springmvc 就是一个简单的 web 框架,可以代替 SSH 框架. springmvc 比 struts2 性能优一些.
Map<String, String> map = new HashMap<String, String>( ); map.put("A", "ABC");
map.put("K", "KK");
map.put("L", "LV");
// 将 Map Key 转化为 List
List<String> mapKeyList = new ArrayList<String>(map.keySet( )); System.out.println("mapKeyList:"+mapKeyList);
// 将 Map Key 转化为 List
List<String> mapValuesList = new ArrayList<String>(map.values( )); System.out.println("mapValuesList:"+mapValuesList);
拦截器和过滤器的区别(重点)
1,拦截器是基于 java 的反射机制的,而过滤器是基于函数回调.
2,拦截器不依赖与servlet 容器,过滤器依赖与 servlet 容器.
3,拦截器只能对 action 请求起作用,而过滤器则可以对几乎所有的请求起作用.
4,拦截器可以访问 action 上下文、值栈里的对象,而过滤器不能访问.
5,在 action 的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次
请讲下堆与栈. (重点)
定义变量赋值赋值,以及随着方法的调用(参数为基本数据类型),会在栈中内存中开辟空间,栈中也会有相应的数据,但是方法调用结束后,会有弹栈的效果,若方法参数为引用数据类型,会在栈中有数据,随着方法调用结束,也会有弹栈的效果,同时在堆中也会有数据.
追问:那么堆中内存什么时候释放?
1,手动设置 null
2,变量的生命周期结束,有GC 回收(GC 指的就是垃圾回收)
变量的生命周期: 就是一个变量在程序执行过程中的“有效期”,比如说全局变量,那它在整个程序执行过程中都有效,及它的生命周期是整个程序执行过程,而对于一些在函数里定义的局部变量,它只是在调用函数是有效,函数调用结束,它的生命周期也完了
如何判断 List 集合是否为空? (重点)
if(null == list || list.size( ) ==0 ){
//为空的情况
}else{
//不为空的情况
}
没有区别,isEmpty( )判断有没有元素,而size( )返回有几个元素,如果判断一个集合有无元素,建议用 isEmpty( )方法比较符合.
这就相当与,你要要到商店买东西
list!=null 首先判断是否有商店
!list.isEmpty( ) 没有判断商店是否存在,而是判断商店是否有东西总结用法:如果连商店都没有,何来的的东西可卖
所以一般的判断是
if(list!=null && !list.isEmpty( )){
//不为空的情况
}else{
//为空的情况
}
在 bin/catalina.bat(windows 系统 tomcat)或者 bin/catalina.sh(Linux 系统 tomcat)中设置变量,示例如下:
JAVA_OPTS="-server -XX:PermSize=64M -XX:MaxPermSize=128m -Xms800m -Xmx800m
-XX:MaxNewSize=256m"
Xms 是最小内存,Xmx 是最大内存,
优点:
缺点:
通过获取到所有的返回的列名字,反射获取目标对象中这个名字对应的属性,调用 set 方法,进行赋值
内存溢出这块怎么解决? (重点)
原因分析
解决思路:
第一步,修改 JVM 启动参数,直接增加内存.(-Xms,-Xmx 参数一定不要忘记加.) 第二步,检查错误日志,查看“OutOfMemory”错误前是否有其它异常或错误. 第三步,对代码进行走查和分析,找出可能发生内存溢出的位置.
使用工具:
常用的有 BoundsCheaker、Deleaker、Visual Leak Detector 等
Mybatis 的缓存(重点)
映射语句文件中的所有 select 语句将会被缓存.
映射语句文件中的所有 insert,update 和 delete 语句会刷新缓存. 缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回.
根据时间表(比如 no Flush Interval,没有刷新间隔), 缓存不会以任何时间顺序 来刷新.
缓存会存储列表集合或对象(无论查询方法返回什么)的 1024 个引用.
缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而 且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改.
所有的这些属性都可以通过缓存元素的属性来修改.
线程池有哪些优点? (重点)
1 可以降低资源的消耗,通过重复使用已经创建的线程,可以降低创建线程和销毁线程带来的消耗;
2 提高响应速度,当任务到达时,不需要等待线程创建就能立即执行;
3 提高线程的可管理性,线程是稀缺资源,如果无限制的创建和销毁,不光会带来资源的消耗,还会降低系统的稳定性,通过线程池,可以对线程进行统一的分配,调优和监控.
项目里文件上传怎么做的? (重点)
我们文件上传使用的是异步上传,这里在前台用到 jQuery.form.js 这个框架来做图片的异步提交,在后台我们使用的是 springmvc,首先需要配置文件上传解析(支
持),MultParFile 必须跟文件上传项的 name 对应,然后获得图片名称,和扩展名调用封装
fastDFS 工具把图片上传到fastDFS.并且返回图片在服务器上面的路径,然后创建 json 对象添加图片路径,然后再把 json 回写到客户端,在前端的回调函数里面接受到后台传递过来的 json 数据,然后找到 img 标签,添加src 属性.(然后把文件上传到了本地,获取本地的服务器据对路径,然后再把图片上传到本地服务器)
项目里 zookeeper 是什么? (重点)
zookeeper 是在 Hadoop 大数据领域的服务中间件,它的第一个作用是存储数据(少量的),第二个作用是对客户端提供监听.
Ajax 的应用场景有哪些? (重点)
Ajax 的工作原理(重点)
Ajax 的工作原理相当于在用户和服务器之间加了—个中间层,使用户操作与服务器响应异步化.并不是所有的用户请求都提交给服务器,像—些数据验证和数据处理等都交给Ajax 引擎自己来做, 只有确定需要从服务器读取新数据时再由 Ajax 引擎代为向服务器提交请求.
Ajax 其核心只有JavaScript、XMLHTTPRequest 和 DOM,在旧的交互方式中,由用户触发一个HTTP 请求到服务器,服务器对其进行处理后再返回一个新的HTHL 页到客户端, 每当服务器处理客户端提交的请求时,客户都只能空闲等待,并且哪怕只是一次很小的交互、只需从服务器端得到很简单的一个数据,都要返回一个完整的 HTML 页,而用户每次都要浪费时间和带宽
去重新读取整个页面.而使用 Ajax 后用户从感觉上几乎所有的操作都会很快响应没有页面重载(白屏)的等待
简要阐述 Ajax 的实现步骤(重点)
get 请求
Redis 与 mysql 的区别? (重点)
mysql 是持久化存储,存放在磁盘里面,检索的话,会涉及到一定的 IO,为了解决这个瓶颈,于是出现了缓存
Mysql 是一个中小型的网络数据库,比oracle 和sqlserver 小, 但是并发能力远超过桌面数据库. redis 是一个支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库.
可以认为redis 比 mysql 简化很多.
mysql 支持集群.
现在大量的软件使用redis 作为mysql 在本地的数据库缓存,然后再适当的时候和mysql 同步.
为什么使用 redis 来生成商品 ID?(重点)
使得主键 id 字段能够持续自增、在高并发的场景下,id 值不重复, redis 本身单线程,不会出现并发问题.
Mybatis 中#与$的区别? (重点)
#{}表示一个占位符号,通过#{}可以实现 preparedStatement 向占位符中设置值,自动进行 java 类型和 jdbc 类型转换,#{}可以有效防止 sql 注入. #{}可以接收简单类型值或 pojo 属性值. 如果parameterType 传输单个简单类型值,#{}括号中可以是 value 或其它名称.
表示拼接 s q l 串 , 通过 {}表示拼接sql 串,通过 表示拼接sql串,通过{}可以将 parameterType 传入的内容拼接在sql 中且不进行 jdbc 类型转换, 可以接收简单类型值或 p o j o 属性值 , 如果 p a r a m e t e r T y p e 传输单个简单类型值 , {}可以接收简单类型值或 pojo 属性值,如果 parameterType 传输单个简单类型值, 可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,{}括号中只能是value
怎么防止表单重复提交(重点)
1>用 JavaScript 控制 Form 表单只能提交一次
var isCommitted = false;//表单是否已经提交标识,默认为 false function dosubmit( ){
if(isCommitted==false){
isCommitted = true;//提交表单后,将表单是否已经提交标识设置为 true return true;//返回 true 让表单正常提交
}else{
return false;//返回 false 那么表单将不提交 } 2>利用 Session 防止表单重复提交
具体的做法:在服务器端生成一个唯一的随机标识号,专业术语称为 Token(令牌),同时在当前用户的Session 域中保存这个Token.然后将Token 发送到客户端的Form 表单中,在Form 表单中使用隐藏域来存储这个 Token,表单提交的时候连同这个 Token 一起提交到服务器端,然后在服务器端判断客户端提交上来的 Token 与服务器端生成的 Token 是否一致,如果不一致,那就是重复提交了,此时服务器端就可以不处理重复提交的表单.如果相同则处理表单提交,处理完后清除当前用户的 Session 域中存储的标识号.
在下列情况下,服务器程序将拒绝处理用户提交的表单请求:
存储 Session 域中的Token(令牌)与表单提交的 Token(令牌)不同.
当前用户的 Session 中不存在 Token(令牌).
用户提交的表单数据中没有 Token(令牌)
Controller 是控制器,负责业务的调用
Service 是业务层逻辑层,负责具体业务的功能处理控制器上不能写@Service
Java 中交互方式同步与异步是什么?区别是什么? (重点)
同步交互:指发送一个请求,需要等待返回,然后才能够发送下一个请求,有个等待过程; 异步交互:指发送一个请求,不需要等待返回,随时可以再发送下一个请求,即不需要等待.
区别:一个需要等待,一个不需要等待,在部分情况下,我们的项目开发中都会优先选择不需要等待的异步交互方式.
Freemarker 生成商品静态页之后,如果商品的数据需要修改怎么办? (重点)
Freemarker 根本不支持Java 脚本代码,而是使用el 表达式来展示数据.FreeMarker 的设计初衷就是:模板 + 数据模型 = 输出,模板只负责在页面中展示,不涉及任何逻辑代码,而所有的逻辑都是由数据模型来处理
我们在每一次点击上架就会生成一次新的静态页面来替换原来的静态页面
结合回答方式总结:
(扣扣索索)企业软件一般不会在太新的 JRE 上运行,所以我没有很深入了解 JDK8.
(自信的说)不过我知道,JDK8 支持 Lambda 表达式.这说明 JDK8 开始支持函数式编程.
(看考官不满意)以后很多地方不用匿名内部类这种形式了,简单了很多(罗利啰嗦一大堆)
(考官提示,interface 的 default 你怎么看?)
(恍然大悟状)啊,对对对
(赶紧补充)接口可以有默认实现之后,我认为有几个好处.
聚合函数有哪些? (重点)
AVG( ) 返回某列的平均值COUNT( ) 返回某列的行数MAX( ) 返回某列的最大值MIN( ) 返回某列的最小值SUM( ) 返回某个列之和
JUnit4 中@AfterClass@BeforClass@after@before 的区别对比(重点)
@Before:初始化方法 对于每一个测试方法都要执行一次(注意与 BeforeClass 区别,后者是对于所有方法执行一次)
@After:释放资源 对于每一个测试方法都要执行一次(注意与 AfterClass 区别,后者是对于所有方法执行一次)
@Test:测试方法,在这里可以测试期望异常和超时时间
@Test(expected=ArithmeticException.class)检查被测方法是否抛出 ArithmeticException 异常
@Ignore:忽略的测试方法
@BeforeClass:针对所有测试,只执行一次,且必须为 static void @AfterClass:针对所有测试,只执行一次,且必须为 static void 一个 JUnit4 的单元测试用例执行顺序为:
@BeforeClass -> @Before -> @Test -> @After -> @AfterClass;
每一个测试方法的调用顺序为:
@Before -> @Test -> @After;
悲观锁乐观锁的区别? (重点)
悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会 block 直到它拿到锁.传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁.
乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制.乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition 机制的其实都是提供的乐观锁.
两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量.但如果经常产生冲突,上层应用会不断的进行retry,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适.
我们的项目对性能的要求很高,使用的是垂直应用架构,项目分为前台和后台,consolr 是我们的后台,partal 是我们的前台,前台后台共用我们的service 和 dao.(core 工程)
项目架构方案,在以前开发项目的时候是 all in one 传统的项目都是在 ORM 完成所有的业务逻辑,但是我们为了增加项目的性能和提高运行速度,这个时候我们只有去增加它的服务器节点,如果你的节点增加的越多,那么这个时候增加的节点所带来的项目加速是越小的,所 以呢,我们的项目是使用的垂直应用架构,这个就解决了传统架构方案的缺点,这样一来,节点越多,我们的效果越好.
用来简化spring应用的初始搭建以及开发过程 使用特定的方式来进行配置(properties或yml文件)
创建独立的spring引用程序 main方法运行
内嵌web服务器,如tomcat,jetty等。
简化maven配置,只需要继承工程
自动配置spring添加对应功能starter自动化配置
Spring Boot 的核心配置文件是 application 和 bootstrap 配置文件。
application 配置文件这个容易了解,主要用于 Spring Boot 项目的自动化配置。
bootstrap 配置文件有以下几个应用场景。
使用 Spring Cloud Config 配置中心时,这时需要在 bootstrap 配置文件中增加连接到配置中心的配置属性来加载外部配置中心的配置信息;
少量固定的不能被覆盖的属性;
少量加密/解密的场景。
properties 和 yml,它们的区别主要是书写格式不同,前者是键值对,后者是靠缩进的方式来决定层级关系的,另外yml 格式不支持@PropertySource
启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:
@SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。
@EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。
@ComponentScan:Spring组件扫描。
Starters可以了解为启动器,它包含了一系列可以集成到应用里面的依赖包,你可以一站式集成 Spring 及其余技术,而不需要四处找示例代码和依赖包。如你想使用 Spring JPA 访问数据库,只需加入 spring-boot-starter-data-jpa 启动器依赖就能使用了。
Starters包含了许多项目中需要用到的依赖,它们能快速持续的运行,都是一系列得到支持的管理传递性依赖,常用的starter有以下几个:
spring-boot-starter-web 嵌入tomcat和web开发需要servlet与jsp支持 spring-boot-starter-data-jpa 数据库支持
spring-boot-starter-data-redis redis数据库支持
spring-boot-starter-data-solr solr支持
mybatis-spring-boot-starter 第三方的mybatis集成starter
Spring Boot 可以通过 @PropertySource,@Value,@Environment, @ConfigurationProperties 来绑定变量。
以前的模式是,所有的代码在同一个工程中,部署在同一个服务器中,同一个项目的不同模块不同功能互相抢占资源。微服务将工程根据不同的业务规则拆分成微服务,微服务部署在不同的机器上,服务之间进行相互调用。Java微服务的框架有 dubbo(只能用来做微服务),spring cloud(提供了服务的发现,断路器等)
熔断机制是应对雪崩效应的一种微服务链路保护机制。当某个微服务不可用或者响应时间太长时,会进行服务降级,进而熔断该节点微服务的调用,快速返回“错误”的响应信息。当检测到该节点微服务调用响应正常后恢复调用链路。在SpringCloud框架里熔断机制通过Hystrix实现,Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内调用20次,如果失败,就会启动熔断机制。
服务降级,一般是从整体负荷考虑。就是当某个服务熔断之后,服务器将不再被调用,此时客户端可以自己准备一个本地的fallback回调,返回一个缺省值。这样做,虽然水平下降,但好歹可用,比直接挂掉强。
Hystrix相关注解
@EnableHystrix:开启熔断
@HystrixCommand(fallbackMethod=”XXX”):声明一个失败回滚处理函数XXX,当被注解的方法执行超时(默认是1000毫秒),就会执行fallback函数,返回错误提示。
Zookeeper保证了CP(C:一致性,P:分区容错性),Eureka保证了AP(A:高可用)
1) 当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的信息,但不能容忍直接down掉不可用。也就是说,服务注册功能对高可用性要求比较高,但zk会出现这样一种情况,当master节点因为网络故障与其他节点失去联系时,剩余节点会重新选leader。问题在于,选取leader时间过长,30 ~ 120s,且选取期间zk集群都不可用,这样就会导致选取期间注册服务瘫痪。在云部署的环境下,因网络问题使得zk集群失去master节点是较大概率会发生的事,虽然服务能够恢复,但是漫长的选取时间导致的注册长期不可用是不能容忍的。
2)Eureka保证了可用性,Eureka各个节点是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点仍然可以提供注册和查询服务。而Eureka的客户端向某个Eureka注册或发现时发生连接失败,则会自动切换到其他节点,只要有一台Eureka还在,就能保证注册服务可用,只是查到的信息可能不是最新的。除此之外,Eureka还有自我保护机制,如果在15分钟内超过85%的节点没有正常的心跳,那么Eureka就认为客户端与注册中心发生了网络故障,此时会出现以下几种情况:
①、Eureka不在从注册列表中移除因为长时间没有收到心跳而应该过期的服务。
②、Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其他节点上(即保证当前节点仍然可用)
③、当网络稳定时,当前实例新的注册信息会被同步到其他节点。 因此,Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像Zookeeper那样使整个微服务瘫痪。
在发布时,指定对应的服务名(服务名包括了IP地址和端口) 将服务注册到注册中心(eureka或者zookeeper),这一过程是springcloud自动实现 只需要在main方法添加@EnableDisscoveryClient 同一个服务修改端口就可以启动多个实例
调用方法:传递服务名称通过注册中心获取所有的可用实例,通过负载均衡策略调用(ribbon和feign)对应的服务。
Ribbon添加maven依赖 spring-starter-ribbon 使用@RibbonClient(value=“服务名称”) 使用RestTemplate调用远程服务对应的方法
feign添加maven依赖 spring-starter-feign 服务提供方提供对外接口 调用方使用 在接口上使用@FeignClient(“指定服务名”)
Ribbon和Feign都是用于调用其他服务的,不过方式不同。
启动类使用的注解不同,Ribbon用的是@RibbonClient,Feign用的是@EnableFeignClients。2.服务的指定位置不同,Ribbon是在@RibbonClient注解上声明,Feign则是在定义抽象方法的接口中使用@FeignClient声明。3.调用方式不同,Ribbon需要自己构建http请求,模拟http请求然后使用RestTemplate发送给其他服务,步骤相当繁琐。Feign则是在Ribbon的基础上进行了一次改进,采用接口的方式,将需要调用的其他服务的方法定义成抽象方法即可,不需要自己构建http请求。不过要注意的是抽象方法的注解、方法签名要和提供服务的方法完全一致。
当一个服务调用另一个服务由于网络原因或者自身原因出现问题时 ,调用者就会等待被调用者的响应 ,当更多的服务请求到这些资源时,导致更多的请求等待,这样就会发生连锁效应(雪崩效应),断路器就是解决这一问题,断路器有完全打开状态,一定时间内,达到一定的次数无法调用,并且多次检测没有恢复的迹象,断路器完全打开,那么下次请求就不会请求到该服务;半开状态,短时间内,有恢复迹象,断路器会将部分请求发给该服务,当能正常调用时,断路器关闭。关闭状态,当服务一直处于正常状态,能正常调用 ,断路器关闭。
分布式的、开源的分布式应用程序协调服务,原本是Hadoop、Hbase的一个重要组件,它为分布式应用提供一致性服务的软件,包括:配置维护、域名服务、分布式同步、组服务等。Zookeeper的功能很强大,应用场景很多,Zookeeper主要是做注册中心用。基于Dubbo框架开发的提供者、消费者都向Zookeeper注册自己的URL,消费者还能拿到并订阅提供者的注册URL,一边在后续程序的执行中去调用提供者。而提供者发生了变动, 也会通过Zookeeper向订阅的消费者发送通知。
常见的starter会包括下面四个方面的内容
自动配置文件,根据classpath是否存在指定的类来决定是否要执行该功能的自动配置。
spring.factories,非常重要,指导Spring Boot找到指定的自动配置文件。
endpoint:可以理解为一个admin,包含对服务的描述、界面、交互(业务信息的查询)。
health indicator:该starter提供的服务的健康指标。
1)Spring Boot 在启动时扫描项目所依赖的JAR包,寻找包含spring.factories文件的JAR
2)根据spring.factories配置加载AutoConfigure类
3)根据 @Conditional注解的条件,进行自动配置并将Bean注入Spring Context
1)添加mybatis的starter maven依赖
org.mybatis.spring.boot
mybatis-spring-boot-starter
2)在mybatis的接口中 添加@Mapper注解
3)在application.yml配置数据源信息
SpringCloud和Dubbo都是现在主流的微服务架构,SpringCloud是Apache旗下的Spring体系下的微服务解决方案,Dubbo是阿里系的分布式服务治理框架,从技术维度上,其实SpringCloud远远的超过Dubbo,Dubbo本身只是实现了服务治理,而SpringCloud现在以及有21个子项目以后还会更多,但是由于RPC以及注册中心元数据等原因,在技术选型的时候我们只能二者选其一。
服务的调用方式Dubbo使用的是RPC远程调用,而SpringCloud使用的是 Rest API,其实更符合微服务官方的定义,服务的注册中心来看,Dubbo使用了第三方的ZooKeeper作为其底层的注册中心,实现服务的注册和发现,SpringCloud使用Spring Cloud Netflix Eureka实现注册中心,当然SpringCloud也可以使用ZooKeeper实现,但一般我们不会这样做。
服务网关,Dubbo并没有本身的实现,只能通过其他第三方技术的整合,而SpringCloud有Zuul路由网关,作为路由服务器,进行消费者的请求分发,SpringCloud还支持断路器,与git完美集成分布式配置文件支持版本控制,事务总线实现配置文件的更新与服务自动装配等等一系列的微服务架构要素。
(1) 快照
将某个时间点的工作状态保存下来,恢复时可直接恢复指定时间点的工作状态Redis中这种方式称为RDB
(2) 日志
将对数据的所有操作过程记录下来,恢复数据时重新执行这些操作
Redis中这种方式称为AOF(主流)
hashmap是数组和链表组成的,数据结构中又叫“链表散列”
单线列表如果长度超过8的话会变成红黑树
Hash冲突
不同的对象算出来的数组下标是相同的这样就会产生hash冲突。
Hash冲突会产生单线链表。
当单线链表达到一定长度后效率会非常低,那么在jdk1.8以后的话,加入了红黑树,也就是说单线列表达到一定长度后就会变成一个红黑树
一下是需要理解的:
Hashmap的扩容并不是为单线链表准备的,单线链表只是为了解决hash冲 突准备的。也就是说当数组达到一定长度,比如说hashmap默认数组长度 是16,那么达到出发条件,数组存储比例达到了75% ,也就是16*0.75=12 的时候就会发生扩容
红黑树
一种二叉树,高效的检索效率
前面说了,当链表达到一定长度后,链表就会变成红黑树
触发条件
在链表长度大于8的时候,将后面的数据存在二叉树中
Hashmap的数据结构
数组,单线链表,红黑树(1.8
B+树:
B+ 树是一种树数据结构,是一个n叉树,每个节点通常有多个孩子,一颗B+树包含根节点、内部节点和叶子节点。根节点可能是一个叶子节点,也可能是一个包含两个或两个以上孩子节点的节点。
B+ 树通常用于数据库和操作系统的文件系统中。
NTFS, ReiserFS, NSS, XFS, JFS, ReFS 和BFS等文件系统都在使用B+树作为元数据索引。
B+ 树的特点是能够保持数据稳定有序,其插入与修改拥有较稳定的对数时间复杂度。
B+ 树元素自底向上插入。
红黑树
红黑树参考
红黑树(Red Black Tree) 是一种自平衡二叉查找树
红黑树和AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。
二叉平衡树的严格平衡策略以牺牲建立查找结构(插入,删除操作)的代价,换来了稳定的O(logN) 的查找时间复杂度
它虽然是复杂的,但它的最坏情况运行时间也是非常良好的,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目。
(1) 每个节点或者是黑色,或者是红色。
(2) 根节点是黑色。
(3) 每个叶子节点是黑色。 [注意:这里叶子节点,是指为空的叶子节点!]
(4) 如果一个节点是红色的,则它的子节点必须是黑色的。
(5) 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
委托动作从下到上,而查询动作则从上到下,当然这里面有一层优化,就是从下到上的时会先判断该类是否已经被加载过,如果加载过就直接返回,没必要继续向上委托,这就是经典的双亲委托模型。
双亲委托的模型的意义与破坏
首先双亲委托模型并不是强制约束,而是 Java设计者推荐给开发者的类加载器实现方式,在Java 的世界中大部分的类加载器都遵循这个模型。双亲委派模型对于保证Java程序的安全稳定运作很重要,其最大的意义就在于提升了Java平台运行的安全稳定性,为什么这么说?
因为双亲委托模型使得Java类随着它的类加载器一起具备了带有优先级的层次关系,在加载一个类的时候,如果准守这个模型,那么必定会先从处于模型最顶端的引导类加载器查询加载,因此就能保证对于一些基础类如Object,在不同的类加载器环境中使用的都是同一个类,但如果没有这个模型,比如黑客定义了一个Object类,或者说你自己定义了多个Object类,那么在使用时候会加载多份,那系统中将会出现多个不同的Object类,Java 类型体系中最基础的行为也就无法保证,应用程序也将会变得一片混乱和非常不安全。
如何打破双亲委托模型?
在具体实现的时候,是可以选择准守双亲模型或者不准守,如果选择准守就尽量不要动loadClass方法的逻辑,而只需要重写findClass方法即可,但如果继承了ClassLoader类,并重写了loadClass的委托逻辑,不再是像上委托查询,改为其他任何的查询加载模式,那么这种行为就能破坏双亲委托模型。
你说你想自定义一个java.lang.String类?那么能不能做到? 答案是可以的,但不推荐这么干,通过自定义一个类加载器,然后破坏双亲委托模型,最后在重写defineClass方法(在这个native方法里面也有检查限制),绕过Java语言的各种限制,是可以达到目标的,但其实这里面存在很大安全隐患的,对于java开头的包里面的基础数据类型是没有任何理由去破坏的,这种行为属于破坏双亲委托模型的最顶级行为。尽管他们的包名和类名都一样,但自定义类加载器的String类与JVM内置的String类仍然是不相等的,因为他们属于不同的类加载器加载的。
上面说的是顶级的破坏案例,当然还有一些是因为双亲委派模型自身的不足导致的。
另外一种破坏双亲委托模型的例子是热加载模式,为了解决不停机或者不停服务更新应用,典型的应用场景是在OSGI里面,默认情况下对于已经加载的类双亲委派模型是不会重新再加载的,但这样就意味着更新了不会被及时感知,如果需要做到动态更新,那么对于已经加载的类也必须再次进行加载,并且要处理好旧实例与新实例状态数据拷贝问题,这种模式也是破坏了双亲委派机制
数据库自增长序列或字段
UUID
3、批量生成ID
4、Redis生成ID
5、Twitter的snowflake算法(目前我们在使用的)
snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。雪花算法将生成不高于19位的有序Long型整数,多用于分布式环境的数据主键。
优点:
1)不依赖于数据库,灵活方便,且性能优于数据库。2)ID按照时间在单机上是递增的。
缺点:
在单机上是递增的,但是由于涉及到分布式环境,每台机器上的时钟不可能完全同步,也许有时候也会出现不是全局递增的情况。
脏读
脏读指事务A读取到了事务B更新了但是未提交的数据,然后事务B由于某种错误发生回滚,那么事务A读取到的就是脏数据。
具体的说,一个数据原本是干净的,但是事务B将它进行修改使得其不再干净,此时事务A读取到事务B修改后的数据,也就是脏数据,称为脏读,后来事务B由于良心发现又将数据回滚为最初的样子,而事务A不知道事务B进行了回滚操作,最终事务A读取到的是脏数据,称为脏读。
不同点:
一级缓存存在于一个SqlSession之内,二级缓存存在于不同的SqlSession之间
一级缓存不需要手动开启,属于默认开启状态;二级缓存需要手动开启
相同点:
在增删改SQL之后,缓存会自动清空
flushCache="true"的查询语句查询内容不存放进缓存
一级缓存
一级缓存是mybatis自带的缓存,mybatis每次在查询后,会将语句和参数相同的查询SQL的结果集存放进缓存,待下一次有相同的语句和参数时,mybatis直接将缓存内的结果集返回,而不再查询数据库。如果对于缓存的数据对应的表有增删改操作的话,缓存自动清空
对于基本类型和引用类型 == 的作用效果是不同的,如下所示:
• 基本类型:比较的是值是否相同;
• 引用类型:比较的是引用是否相同;
• 不对,两个对象的 hashCode()相同,equals()不一定 true。
• final 修饰的类叫最终类,该类不能被继承。
• final 修饰的方法不能被重写。
• final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。
•
操作字符串的类有:String、StringBuffer、StringBuilder。
String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。
StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。
抽象类能使用 final 修饰吗?
不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象类,如下图所示,编辑器也会提示错误信息:
实现:抽象类的子类使用 extends 来继承;接口必须使用 implements 来实现接口。
构造函数:抽象类可以有构造函数;接口不能有。
main 方法:抽象类可以有 main 方法,并且我们能运行它;接口不能有 main 方法。
实现数量:类可以实现很多个接口;但是只能继承一个抽象类。
访问修饰符:接口中的方法默认使用 public 修饰;抽象类中的方法可以是任意访问修饰符。
•
答:开发流程是:
1、项目启动
2、需求阶段
3、设计阶段
4、开发阶段
5、测试阶段
6、产品上线
一般每一周,周一的时候会开周会,项目经理会对这一周的工作,进行安排。周末的时候,会开周登例会,
对一周做总结,开发时,一般参照需求分析文档、数据库字典进行开发,每周编写周报表,用于监控项目进度
以及工作中遇到的问题
答:比较好的代码:
1、代码是低耦合的
2、代码是高内聚的
3、代码是简单的
4、代码是可读性高的
5、响应实体和请求实体分开,每个接口对应于一个实体不共用的
6、逻辑分层清晰的
答:1、选取最适用的字段属性。MySQL可以支持大数据量的存取,但是数据库中的表越小,在上面执行的查询就
越快。所以可以将表中的字段宽度设置的尽可能小
2、使用连接来代替子查询
3、使用联合(UNION)来代替手动创建的临时表
4、事务。不是所有的数据库操作都可以只用一条或少数几条SQL语句就可以完成的。更多的时候是需要用到
一系列的语句来完成某种工作。可以保持数据库中数据的完整性和一致性
5、锁定表。由于在事务执行的过程中,数据库将会被锁定,因此其他的用户请求只能暂时等待直到该事务结
束。有些情况下我们可以通过锁定表的放大来获得更好的性能
6、使用外键。锁定表的方法可以保护数据的完整性,但是却不能保证数据的关联性。此时我们可以使用外键
答:可通过给表加一个版本号或时间戳字段实现,当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断当前版本信息与第一次取出来的版本值大小,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据,拒绝更新,让用户重新操作
例:A调用B
定时删除:创建一个定时器,在键过期的时候启动定时器,定时删除;对内存比较友好,但浪费CPU资源
惰性删除:放任键不管,在下次读写的时候,检查是否过期,过期就删除;对CPU友好,但浪费内存资源
定期删除:每隔一段时间,程序对redis检查,对过期键进行删除。间隔小占CPU,间隔大则浪费内存
为什么HashMap可为空:调用hash()方法为空时不会调用hashcode方法
为什么Hashtable不可为空:为空时抛出空指针异常
数组 + 链表,默认16,默认扩容0.75,超过3/4时扩容到2倍
脏读 | 不可重复读 | 幻读 | |
---|---|---|---|
读取未提交内容 | | | |
读取已提交内容 | | | |
可重复度 | |||
可串行化 |
缓存雪崩我们可以简单的理解为:由于原有缓存失效,新缓存未到期间所有原本应该访问缓存的请 求都 去查询数据库了,⽽对数据库 CPU 和内存造成巨⼤压⼒,严重的会造成数据库宕机。从⽽形 成⼀系列 连锁反应,造成整个系统崩溃。⼀般有三种处理办法:
缓存穿透是什么?
缓存穿透是指⽤户查询数据,在数据库没有,⾃然在缓存中也不会有。这样就导致⽤户查询 的时候,在 缓存中找不到,每次都要去数据库再查询⼀遍,然后返回空(相当于进⾏了两次⽆ ⽤的查询)。这样请 求就绕过缓存直接查数据库,这也是经常提的缓存命中率问题。
怎么解决缓存穿透?
有很多种⽅法可以有效地解决缓存穿透问题,最常⻅的则是采⽤布隆过滤器,将所有可能存 在的数据哈 希到⼀个⾜够⼤的 bitmap 中,⼀个⼀定不存在的数据会被这个 bitmap 拦截
掉,从⽽避免了对底层存 储系统的查询压⼒。另外也有⼀个更为简单粗暴的⽅法,如果⼀个
查询返回的数据为空(不管是数据不 存在,还是系统故障),我们仍然把这个空结果进⾏缓
存,但它的过期时间会很短,最⻓不超过五分钟。 通过这个直接设置的默认值存放到缓存,
这样第⼆次到缓冲中获取就有值了,⽽不会继续访问数据库。
缓存预热
缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在⽤户 请求的时候, 先查询数据库,然后再将数据缓存的问题!⽤户直接查询事先被预热的缓存数
据!
缓存更新
缓存更新除了缓存服务器⾃带的缓存失效策略之外(Redis 默认的有 6 中策略可供选择),我们 还可以 根据具体的业务需求进⾏⾃定义的缓存淘汰,常⻅的策略有两种: (1)定时去清理过期的缓存;
(2)当有⽤户请求过来时,再判断这个请求所⽤到的缓存是否过期,过期的话就去底层系统得到新 数 据并更新缓存。 缓存降级 当访问量剧增、服务出现问题(如响应时间慢或不响应)或⾮核⼼服务影响到核⼼流程的性能 时,仍然 需要保证服务还是可⽤的,即使是有损服务。系统可以根据⼀些关键数据进⾏⾃动 降级,也可以配置开 关实现⼈⼯降级。降级的最终⽬的是保证核⼼服务可⽤,即使是有损 的。⽽且有些服务是⽆法降级的 (如加⼊购物⻋、结算)
wait只能在同步块(synchronize)环境中被调用,而sleep不需要。
wait方法在进入wait状态的时候会释放对象的锁,但是sleep方法不会。
进入wait状态的线程能够被notify和notifyAll线程唤醒,但是进入sleeping状态的线程不能被 notify方法唤醒,到期之后自动继续执行
synchronized的锁可重入、不可中断、非公平,每次使用会自动释放锁
Lock锁可重入、可中断、可公平,每次使用需要自己加锁以及释放锁,一般情况下需放在fifinally 里进行锁的释放,否则,可能因为未能正确释放锁而导致程序出问题。Lock可以实现有限时间内 的获取锁的等待,在指定的时间内如果获取不到锁,会返回false 此处,如果是熟悉ReentrantLock或者ReentrantReadWriteLock源码的同学,可以往底层原理延 伸,例如讲到Lock可以公平的时候,可以延伸到底层源码中是如何实现锁的公平性的
⾸先,为了确保分布式锁可⽤,我们⾄少要确保锁的实现同时满⾜以下三个条件:
互斥性。在任意时刻,只有⼀个客户端能持有锁。
不会发⽣死锁。即使有⼀个客户端在持有锁的期间崩溃⽽没有主动解锁,也能保证后续
其他客户端能加锁。
解铃还须系铃⼈。加锁和解锁必须是同⼀个客户端,客户端⾃⼰不能把别⼈加的锁给解
了 从 Redis 2.6.0 版本开始,通过内置的 Lua 解释器,可以使⽤ EVAL 命令对 Lua 脚本进⾏求 值。
Redis 使⽤单个 Lua 解释器去运⾏所有脚本,并且, Redis 也保证脚本会以原⼦性(atomic)
的⽅式执⾏:当某个脚本正在运⾏的时候,不会有其他脚本或 Redis 命令被执⾏。这和使⽤
MULTI / EXEC 包围的事务很类似。在其他别的客户端看来,脚本的效果(effffect)要么是不可⻅ 的(not visible),要么就是已完成的(already completed)。
实现分布式锁的⽅案⼤概有两种
采⽤lua脚本操作分布式锁
采⽤setnx、setex命令连⽤的⽅式实现分布式锁
解铃还须系铃⼈。加锁和解锁必须是同⼀个客户端,客户端⾃⼰不能把别⼈加的锁给解了
常见的实现分布式锁有两种方式
基于redis实现分布式锁:核心思想是获取锁的时候,使用setnx加锁,并使用expire命令为锁添
加一个超时时间,超过该时间则自动释放锁
基于zookeeper的实现方式,核心思想在zk中是为每个线程生成一个有序的临时节点,为确保有
序性,在排序一次全部节点,获取全部节点,每个线程判断自己是否最小,如果是的话,获得
锁,执行操作,操作完删除自身节点。如果不是第一个的节点则监听它的前一个节点,当它的前
一个节点被删除时,则它会获得锁,以此类推。特别注意,这里新建节点必须要是临时节点,确
保获取到锁的客户端宕机也不影响其他客户端获取锁
使用redis的优点是性能高,缺点是特定情况下master宕机,数据没完成同步,其他客户端可以继
续获取到锁 使用zk的优点是能保证一致性,缺点是频繁读写,性能较差
CAP 原则⼜称 CAP 定理,指的是在⼀个分布式系统中, Consistency(⼀致性)、 Availability (可⽤
性)、Partition tolerance(分区容错性),三者不可得兼。
⼀致性©:
在分布式系统中的所有数据备份,在同⼀时刻是否同样的值。(等同于所有节点访问同⼀份
最新的数据副本)
可⽤性(A):
在集群中⼀部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具
备 ⾼可⽤性)
分区容忍性§:
以实际效果⽽⾔,分区相当于对通信的时限要求。系统如果不能在时限内达成数据⼀致
性, 就意味着发⽣了分区的情况,必须就当前操作在 C 和 A 之间做出选择
原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性(Consistency)
事务前后数据的完整性必须保持一致。
隔离性(Isolation)
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库的改变就是永久性的,接下来即时数据库发生故障也不应该对其有任何影响。
线程的生命周期包含5个阶段,包括:新建、就绪、运行、阻塞、销毁。
新建:就是刚使用new方法,new出来的线程;
就绪:就是调用的线程的start()方法后,这时候线程处于等待CPU分配资源阶段,谁先抢的CPU资源,谁开始执行;
运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run方法定义了线程的操作和功能;
阻塞:在运行状态的时候,可能因为某些原因导致运行状态的线程变成了阻塞状态,比如sleep()、wait()之后线程就处于了阻塞状态,这个时候需要其他机制将处于阻塞状态的线程唤醒,比如调用notify或者notifyAll()方法。唤醒的线程不会立刻执行run方法,它们要再次等待CPU分配资源进入运行状态;
销毁:如果线程正常执行完毕后或线程被提前强制性的终止或出现异常导致结束,那么线程就要被销毁,释放资源;
sleep() 方法、suspend()方法、yield() 方法、wait() 方法、join()方法
suspend()使线程进入阻塞状态,只有对应的resume()被调用的时候,线程才会进入可执行状态。(不建议用,容易发生死锁)
yield()的效果等价于调度程序认为该线程已执行了足够的时间从而转到另一个线程。(暂停当前正在执行的线程,并执行其他线程,且让出的时间不可知)
join()也叫线程加入。是当前线程A调用另一个线程B的join()方法,当前线程转A入阻塞状态,直到线程B运行结束,线程A才由阻塞状态转为可执行状态。
1.newCachedThreadPool创建一个可缓存线程池程
2.newFixedThreadPool 创建一个定长线程池
3.newScheduledThreadPool 创建一个周期性执行任务的线程池
4.newSingleThreadExecutor 创建一个单线程化的线程池
newCachedThreadPool:一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool :创建一个指定工作线程数量的线程池,每当提交一个任务就创建一个工作线程,当线程 处于空闲状态时,它们并不会被回收,除非线程池被关闭了,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列(没有大小限制)中。
newScheduledThreadPool:它的核心线程数量是固定的,而非核心线程数是没有限制的,并且当非核心线程闲置时会被立即回收,它可安排给定延迟后运行命令或者定期地执行。这类线程池主要用于执行定时任务和具有固定周期的重复任务。
newSingleThreadExecutor :这类线程池内部只有一个核心线程,以无界队列方式来执行该线程,这使得这些任务之间不需要处理线程同步的问题,它确保所有的任务都在同一个线程中按顺序中执行,并且可以在任意给定的时间不会有多个线程是活动的。
ArrayList:内存地址连续,所以查询大于增删
LinkedLIst 内存地址不联系 插入时只需要将节点掰开即可,所以增删大于查询
#{}是预编译处理,${}是字符串替换。
Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用 PreparedStatement 的 set 方法来赋值;
Mybatis在处理时 , 就 是 把 {}时,就是把时,就是把{}替换成变量的值。
使用#{}可以有效的防止SQL注入,提高系统安全性。
优点:
1、使用Spring的IOC容器,将对象之间的依赖关系交由Spring管理,降低了组件之间的耦合性,实现了软件各层的解耦。
2、使用容器提供众多服务,如事务管理、消息队列等。
3、AOP技术,利用它可以实现一些拦截,如权限拦截。
4、对主流的框架提供了很好的支持,如Mybatis。
5、低浸入式设计
6、Spring的DI机制降低了业务对象替换的复杂性。
缺点:
Spring依赖反射机制,反射机制占用内存影响性能。
1、工厂模式
Spring通过BeanFactory或者ApplicationContext创建Bean对象用到了工厂模式。
2、单例模式
Spring中bean的作用域默认为单例模式(singleton)。
3、代理模式
Spring AOP(面向切面编程)基于动态代理,如果要代理的对象实现了某个接口,SpringAOP会使用JDK Proxy创建代理对象;如果对象没有实现接口,Spring AOP会使用CGLib生成一个被代理对象的子类作为代理。
4、模板方法模式
Spring中的jdbcTemplate等以Template结尾的对数据库操作的类就使用了模板模式。
5、适配器模式
Spring AOP的实现基于代理模式,但是SpringAOP的增强(Advice)使用了适配器模式。SpringMVC的HandlerAdapter也是适配器模式。
6、观察者模式
定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,Spring事件驱动模型就是观察者模式一个很经典的应用。
IOC:
IOC(Inverse of Control)即控制反转——某一接口具体实现类的选择控制权从调用类中移除,转交由第三方Spring容器借由bean配置来进行控制。SPring IoC负责创建对象、管理对象、装配对象、配置对象,并且管理对象的整个生命周期。
(注:依赖注入(DI)其实指的就是IoC,其解释为:让调用类对某一接口实现类的依赖关系由第三方(Spring 容器)注入,以移除调用类对某一接口实现类的依赖。)
AOP:
AOP(Aspect-Oriented Programming),一般称为面向切面编程,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理等。
启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:
@SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。
@EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。
@ComponentScan:Spring组件扫描。
隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了五个表示隔离级别的常量:
TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读,不可重复读和幻读,因此很少使用该隔离级别。比如PostgreSQL实际上并没有此级别。
TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。该级别可以防止脏读和不可重复读。
TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
优点:
1.耦合度比较低。不会影响其他模块的开发。
2.减轻团队的成本,可以并行开发,不用关注其他人怎么开发,先关注自己的开发。
3.配置比较简单,基本用注解就能实现,不用使用过多的配置文件。
4.微服务跨平台的,可以用任何一种语言开发。
5.每个微服务可以有自己的独立的数据库也有用公共的数据库。
6.直接写后端的代码,不用关注前端怎么开发,直接写自己的后端代码即可,然后暴露接口,通过组件进行服务通信。
缺点:
1.部署比较麻烦,给运维工程师带来一定的麻烦。
2.针对数据的管理比麻烦,因为微服务可以每个微服务使用一个数据库。
3.系统集成测试比较麻烦
4.性能的监控比较麻烦。【最好开发一个大屏监控系统】
集群吧,注册多台Eureka,然后把SpringCloud服务互相注册,客户端从Eureka获取信息时,按照Eureka的顺序来访问。
Nginx是反向代理同时可以实现负载均衡,nginx拦截客户端请求采用负载均衡策略根据upstream配置进行转发,相当于请求通过nginx服务器进行转发。Ribbon是客户端负载均衡,从注册中心读取目标服务器信息,然后客户端采用轮询策略对服务直接访问,全程在客户端操作。
当一个服务调用另一个服务由于网络原因或自身原因出现问题,调用者就会等待被调用者的响应 当更多的服务请求到这些资源导致更多的请求等待,发生连锁效应(雪崩效应)
断路器有三种状态
打开状态:一段时间内 达到一定的次数无法调用 并且多次监测没有恢复的迹象 断路器完全打开 那么下次请求就不会请求到该服务
半开状态:短时间内 有恢复迹象 断路器会将部分请求发给该服务,正常调用时 断路器关闭
关闭状态:当服务一直处于正常状态 能正常调用
服务降级:接口调用 失败就调用本地的方法返回一个空
服务熔断:接口调用失败就会进入调用接口提前定义好的一个熔断的方法,返回错误信息
服务隔离:隔离服务之间相互影响
服务监控:在服务发生调用时,会将每秒请求数、成功请求数等运行指标记录下来。
服务降级:当客户端请求服务器端的时候,防止客户端一直等待,不会处理业务逻辑代码,直接返回一个友好的提示给客户端。
服务熔断是在服务降级的基础上更直接的一种保护方式,当在一个统计时间范围内的请求失败数量达到设定值(requestVolumeThreshold)或当前的请求错误率达到设定的错误率阈值(errorThresholdPercentage)时开启断路,之后的请求直接走fallback方法,在设定时间(sleepWindowInMilliseconds)后尝试恢复。
服务隔离就是Hystrix为隔离的服务开启一个独立的线程池,这样在高并发的情况下不会影响其他服务。服务隔离有线程池和信号量两种实现方式,一般使用线程池方式
优点:
读写性能优异, Redis能读的速度是110000次/s,写的速度是81000次/s。
支持数据持久化,支持AOF和RDB两种持久化方式。
支持事务,Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。
数据结构丰富,除了支持string类型的value外还支持hash、set、zset、list等数据结构。
支持主从复制,主机会自动将数据同步到从机,可以进行读写分离。
缺点:
数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。
Redis 不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。
主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。
Redis 较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费
高性能:
假如用户第一次访问数据库中的某些数据。这个过程会比较慢,因为是从硬盘上读取的。将该用户访问的数据存在数缓存中,这样下一次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快。如果数据库中的对应数据改变的之后,同步改变缓存中相应的数据即可!
高并发:
直接操作缓存能够承受的请求是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。
完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于 HashMap,HashMap 的优势就是查找和操作的时间复杂度都是O(1);
数据结构简单,对数据操作也简单,Redis 中的数据结构是专门进行设计的;
采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
使用多路 I/O 复用模型,非阻塞 IO;
使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis 直接自己构建了 VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;
Redis 提供两种持久化机制 RDB(默认) 和 AOF 机制:
RDB
RDB:是Redis DataBase缩写快照
RDB是Redis默认的持久化方式。按照一定的时间将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件为dump.rdb。通过配置文件中的save参数来定义快照的周期。
优点:
1、只有一个文件 dump.rdb,方便持久化。
2、容灾性好,一个文件可以保存到安全的磁盘。
3、性能最大化,fork 子进程来完成写操作,让主进程继续处理命令,所以是 IO 最大化。使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis 的高性能
4.相对于数据集大时,比 AOF 的启动效率更高。
缺点:
1、数据安全性低。RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候)
2、AOF(Append-only file)持久化方式: 是指所有的命令行记录以 redis 命令请 求协议的格式完全持久化存储)保存为 aof 文件。
AOP
AOF:持久化
AOF持久化(即Append Only File持久化),则是将Redis执行的每次写命令记录到单独的日志文件中,当重启Redis会重新将持久化的日志中文件恢复数据。
当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复。
优点:
1、数据安全,aof 持久化可以配置 appendfsync 属性,有 always,每进行一次 命令操作就记录到 aof 文件中一次。
2、通过 append 模式写文件,即使中途服务器宕机,可以通过 redis-check-aof 工具解决数据一致性问题。
3、AOF 机制的 rewrite 模式。AOF 文件没被 rewrite 之前(文件过大时会对命令 进行合并重写),可以删除其中的某些命令(比如误操作的 flushall))
缺点:
1、AOF 文件比 RDB 文件大,且恢复速度慢。
2、数据集大的时候,比 rdb 启动效率低。
优缺点是什么?
AOF文件比RDB更新频率高,优先使用AOF还原数据。
AOF比RDB更安全也更大
RDB性能比AOF好
如果两个都配了优先加载AOF
描述:
缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。
解决方案:
接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。
这样可以防止攻击用户反复用同一个id暴力攻击
描述:
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力
解决方案:
设置热点数据永远不过期。加互斥锁,互斥锁
1、string类型 ------------------字符串类型
2、hash类型----------------------散列类型
3、list类型----------------------列表类型
4、set类型-----------------------无序集合类型
5、zset类型----------------------有序集合类型
常用的模式有下列几种:
1、工作队列模式:
特点:队列中的消息,只能被消费一次,如果有多个消费方,他们会采用一种轮循的机制,轮流消费
@@@在使用工作队列模式,不需要指定交换机(系统会使用默认的交换机),只需要指定队列名称即可
2、发布-订阅模式 (Publish/subscribe)
特点:生产方发送消息,是通过交换机将消息发送到队列中,一条消息,通过交换机,可以发送多个队
列中,多个消费方可以同时消费
@@@在使用工作队列模式中,需要指定交换机
例如:生产方产生一条消息,发送到交换机中,交换机将信息分别发送到:短信与邮件的队列中,
此时,监听短信队列和监听邮件队列的消费者,可以同时接收到消息
@@@:只要将交换机与队列绑定,交换机收到消息,就一定会转发对应的队列中
3、路由模式 (DIRECT)
特点:生产方发送消息,是通过交换机将消息发送到队列中,在发送消息时,生产方设置一个路由的key
只向指定的路由KEY的队列发送消息
@@@@@一个队列与交换机绑定,即使交换机收消息,但如果生产者发送消息时设置的路由key与队列绑
定的路由key不一致,该队列依然收不到消息
@@: 采用路由模式时,在发送消息时,不仅要指定交换机的名称,同时,要设置路由key
常见问题:路由模式与发布订阅模式的区别:
发布订阅模式:只要交换机收到消息,与它绑定的队列都可以收到消息
路由模式:交换机收到消息,即使队列与该交换机绑定,也必须要路由的key一致,才可以收到消息
4、通配符模式 (TOPIC)
特点:这种模式,交换机发送消息到队列,也需要指定路由key,在路由key中允许使用通配符
通配符有两种:
#: 通配一个或者多个名称,也可以通""
*: 通配一个名称,也可以通配“”
应用场景:同时将消息推送到满足指定格式的队列(一条消息,同时发送到多个满足key的队列)
死锁是指多个进程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。
互斥条件:一个资源每次只能被一个进程使用,即在一段时间内某 资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
请求与保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源 已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能 由获得该资源的进程自己来释放(只能是主动释放)。
循环等待条件: 若干进程间形成首尾相接循环等待资源的关系
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
死锁避免的基本思想:
系统对进程发出每一个系统能够满足的资源申请进行动态检查,并根据检查结果决定是否分配资源,如果分配后系统可能发生死锁,则不予分配,否则予以分配。这是一种保证系统不进入死锁状态的动态策略。
理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和解除死锁。所以,在系统设计、进程调度等方面注意如何让这四个必要条件不成立,如何确定资源的合理分配算法,避免进程永久占据系统资源。此外,也要防止进程在处于等待状态的情况下占用资源。因此,对资源的分配要给予合理的规划。
死锁避免和死锁预防的区别:
死锁预防是设法至少破坏产生死锁的四个必要条件之一,严格的防止死锁的出现,而死锁避免则不那么严格的限制产生死锁的必要条件的存在,因为即使死锁的必要条件存在,也不一定发生死锁。死锁避免是在系统运行过程中注意避免死锁的最终发生。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。