第一篇:面试题汇总

本题集由尚学堂学员整理,列举了众多IT公司面试真题,对应聘Java程序员职位的常见考点和知识体系都进行的分类和归纳整理。
本题集适合应聘Java和JavaEE职位的程序员作为面试复习、学习和强化的资料,也适合其他程序员作为拓展读物进行阅读。
本题集包含了常见的算法、面试题,也包含了新的高级技术,比如:微服务架构等技术的面试题目。本题集非常全面,对于工作1-5年左右的java程序员面试有非常好的指导作用。
大家也可以访问(直接在线观看最新版的面试题):www.bjsxt.com/javamianshiti.html
1.JAVA专业,1000课 | 3.大数据专业,500课 |
2.Python专业,500课 | 4.人工智能专业,500课 |
四个专业都要学,从零开始2000小时,成为高端人才,打下一生技术基础,不再是低端码农。

2.扫一扫,咨询详情:

访问官网 www.itbaizhan.cn
Java基础、语法:

C/C++语言都直接编译成针对特定平台机器码。如果要跨平台,需要使用相应的编译器重新编译。
Java源程序(.java)要先编译成与平台无关的字节码文件(.class),然后字节码文件再解释成机器码运行。解释是通过Java虚拟机来执行的。
字节码文件不面向任何具体平台,只面向虚拟机。
Java虚拟机是可运行Java字节码文件的虚拟计算机。不同平台的虚拟机是不同的,但它们都提供了相同的接口。
Java语言具有一次编译,到处运行的特点。就是说编译后的.class可以跨平台运行,前提是该平台具有相应的Java虚拟机。但是性能比C/C++要低。
Java的跨平台原理决定了其性能没有C/C++高

语言层次的安全性主要体现在:
Java取消了强大但又危险的指针,而代之以引用。由于指针可进行移动运算,指针可随便指向一个内存区域,而不管这个区域是否可用,这样做是危险的,因为原来这个内存地址可能存储着重要数据或者是其他程序运行所占用的,并且使用指针也容易数组越界。
垃圾回收机制:不需要程序员直接控制内存回收,由垃圾回收器在后台自动回收不再使用的内存。避免程序忘记及时回收,导致内存泄露。避免程序错误回收程序核心类库的内存,导致系统崩溃。
异常处理机制:Java异常机制主要依赖于try、catch、finally、throw、throws五个关键字。
强制类型转换:只有在满足强制转换规则的情况下才能强转成功。
底层的安全性可以从以下方面来说明
Java在字节码的传输过程中使用了公开密钥加密机制(PKC)。
在运行环境提供了四级安全性保障机制:
字节码校验器 -类装载器 -运行时内存布局 -文件访问限制
Java2平台包括标准版(J2SE)、企业版(J2EE)和微缩版(J2ME)三个版本:
Standard Edition(标准版) J2SE 包含那些构成Java语言核心的类。
比如:数据库连接、接口定义、输入/输出、网络编程
Enterprise Edition(企业版) J2EE 包含J2SE 中的类,并且还包含用于开发企业级应用的类。
比如servlet、JSP、XML、事务控制
Micro Edition(微缩版) J2ME 包含J2SE中一部分类,用于消费类电子产品的软件开发。
比如:呼机、智能卡、手机、PDA、机顶盒
他们的范围是:J2SE包含于J2EE中,J2ME包含了J2SE的核心类,但新添加了一些专有类
应用场合,API的覆盖范围各不相同。
JVM :JVM是Java Virtual Machine(Java虚拟机)的缩写,它是整个java实现跨平台的最核心的部分,所有的java程序会首先被编译为.class的类文件,这种类文件可以在虚拟机上执行,也就是说class并不直接与机器的操作系统相对应,而是经过虚拟机间接与操作系统交互,由虚拟机将程序解释给本地系统执行。JVM是Java平台的基础,和实际的机器一样,它也有自己的指令集,并且在运行时操作不同的内存区域。 JVM通过抽象操作系统和CPU结构,提供了一种与平台无关的代码执行方法,即与特殊的实现方法、主机硬件、主机操作系统无关。JVM的主要工作是解释自己的指令集(即字节码)到CPU的指令集或对应的系统调用,保护用户免被恶意程序骚扰。 JVM对上层的Java源文件是不关心的,它关注的只是由源文件生成的类文件(.class文件)。
JRE:JRE是java runtime environment(java运行环境)的缩写。光有JVM还不能让class文件执行,因为在解释class的时候JVM需要调用解释所需要的类库lib。在JDK的安装目录里你可以找到jre目录,里面有两个文件夹bin和lib,在这里可以认为bin里的就是jvm,lib中则是jvm工作所需要的类库,而jvm和lib和起来就称为jre。所以,在你写完java程序编译成.class之后,你可以把这个.class文件和jre一起打包发给朋友,这样你的朋友就可以运行你写程序了(jre里有运行.class的java.exe)。JRE是Sun公司发布的一个更大的系统,它里面就有一个JVM。JRE就与具体的CPU结构和操作系统有关,是运行Java程序必不可少的(除非用其他一些编译环境编译成.exe可执行文件……),JRE的地位就象一台PC机一样,我们写好的Win32应用程序需要操作系统帮我们运行,同样的,我们编写的Java程序也必须要JRE才能运行。
JDK:JDK是java development kit(java开发工具包)的缩写。每个学java的人都会先在机器上装一个JDK,那 让我们看一下JDK的安装目录。在目录下面有六个文件夹、一个src类库源码压缩包、和其他几个声明文件。其中,真正在运行java时起作用的是以下四个文件夹:bin、include、lib、jre。现在我们可以看出这样一个关系,JDK包含JRE,而JRE包含JVM。
bin:最主要的是编译器(javac.exe)
include:java和JVM交互用的头文件
lib:类库
jre:java运行环境
(注意:这里的bin、lib文件夹和jre里的bin、lib是不同的)总的来说JDK是用于java程序的开发,而jre则是只能运行class而没有编译的功能。eclipse、idea等其他IDE有自己的编译器而不是用JDK bin目录中自带的,所以在安装时你会发现他们只要求你选jre路径就ok了。
JDK,JRE,JVM三者关系概括如下:
jdk是JAVA程序开发时用的开发工具包,其内部也有JRE运行环境JRE。JRE是JAVA程序运行时需要的运行环境,就是说如果你光是运行JAVA程序而不是去搞开发的话,只安装JRE就能运行已经存在的JAVA程序了。JDk、JRE内部都包含JAVA虚拟机JVM,JAVA虚拟机内部包含许多应用程序的类的解释器和类加载器等等。
共有单行注释、多行注释、文档注释3种注释类型。使用如下:
单行注释,采用“//”方式.只能注释一行代码。如://类成员变量
多行注释,采用“/*...*/”方式,可注释多行代码,其中不允许出现嵌套。如:
/*System.out.println("a");
System.out.println("b");
System.out.println("c");*/
文档注释,采用“/**...*/”方式。如:
/**
* 子类 Dog
* @author Administrator
**/
public class Dog extends Animal{}
数据类型 | 关键字 | 字节数 | |
数值型 | 整数型 | byte | 1 |
short | 2 | ||
int | 4 | ||
long | 8 | ||
浮点型 | float | 4 | |
double | 8 | ||
布尔型 | boolean | 1(位) | |
字符型 | char | 2 |
共同点:
1、i++和++i都是变量自增1,都等价于i=i+1
2、如果i++,++i是一条单独的语句,两者没有任何区别
3、i++和++i的使用仅仅针对变量。 5++和++5会报错,因为5不是变量。
不同点:
如果i++,++i不是一条单独的语句,他们就有区别i++ :先运算后增1。如:
int x=5; int y=x++; System.out.println("x="+x+", y="+y); //以上代码运行后输出结果为:x=6, y=5
++i : 先增1后运算。如:
int x=5; int y=++x; System.out.println("x="+x+", y="+y); //以上代码运行后输出结果为:x=6, y=6
&和&&的联系(共同点):
&和&&都可以用作逻辑与运算符,但是要看使用时的具体条件来决定。
操作数1&操作数2,操作数1&&操作数2,
表达式1&表达式2,表达式1&&表达式2,
情况1:当上述的操作数是boolean类型变量时,&和&&都可以用作逻辑与运算符。
情况2:当上述的表达式结果是boolean类型变量时,&和&&都可以用作逻辑与运算符。
表示逻辑与(and),当运算符两边的表达式的结果或操作数都为true时,整个运算结果才为true,否则,只要有一方为false,结果都为false。
&和&&的区别(不同点):
(1)、&逻辑运算符称为逻辑与运算符,&&逻辑运算符称为短路与运算符,也可叫逻辑与运算符。
对于&:无论任何情况,&两边的操作数或表达式都会参与计算。
对于&&:当&&左边的操作数为false或左边表达式结果为false时,&&右边的操作数或表达式将不参与计算,此时最终结果都为false。
综上所述,如果逻辑与运算的第一个操作数是false或第一个表达式的结果为false时,对于第二个操作数或表达式是否进行运算,对最终的结果没有影响,结果肯定是false。推介平时多使用&&,因为它效率更高些。
、&还可以用作位运算符。当&两边操作数或两边表达式的结果不是boolean类型时,&用于按位与运算符的操作。
|和||的区别和联系与&和&&的区别和联系类似
使用位运算来实现效率最高。位运算符是对操作数以二进制比特位为单位进行操作和运算,操作数和结果都是整型数。对于位运算符“<<”, 是将一个数左移n位,就相当于乘以了2的n次方,那么,一个数乘以8只要将其左移3位即可,位运算cpu直接支持的,效率最高。所以,2乘以8等于几的最效率的方法是2 << 3
基本类型转换分为自动转换和强制转换。
自动转换规则:容量小的数据类型可以自动转换成容量大的数据类型,也可
以说低级自动向高级转换。这儿的容量指的不是字节数,而是指类型表述的范围。

强制转换规则:高级变为低级需要强制转换。
如何转换:
(1)赋值运算符“=”右边的转换,先自动转换成表达式中级别最高的数据类型,再进行运算。
(2)赋值运算符“=”两侧的转换,若左边级别>右边级别,会自动转换;若左边级别 == 右边级别,不用转换;若左边级别 < 右边级别,需强制转换。
(3)可以将整型常量直接赋值给byte, short, char等类型变量,而不需要进行强制类型转换,前提是不超出其表述范围,否则必须进行强制转换。
相同之处:都是分支语句,多超过一种的情况进行判断处理。
不同之处:
switch更适合用于多分支情况,就是有很多种情况需要判断处理,判断条件类型单一,只有一个入口,在分支执行完后(如果没有break跳出),不加判断地执行下去;而if—elseif---else多分枝主要适用于分支较少的分支结构,判断类型不是单一,只要一个分支被执行后,后边的分支不再执行。switch为等值判断(不允许比如>= <=),而if为等值和区间都可以,if的使用范围大。
while先判断后执行,第一次判断为false,循环体一次都不执行
do while先执行 后判断,最少执行1次。
如果while循环第一次判断为true, 则两种循环没有区别。
break: 结束当前循环并退出当前循环体。
break还可以退出switch语句
continue: 循环体中后续的语句不执行,但是循环没有结束,继续进行循环条件的判断(for循环还会i++)。continue只是结束本次循环。
package com.bjsxt; import java.io.File; public class $ { public static void main(String[] args) { String path = "D:/301SXT"; test(path); } private static void test(String path) { File f = new File(path); File[] fs = f.listFiles(); if (fs == null) { return; } for (File file : fs) { if (file.isFile()) { System.out.println(file.getPath()); } else { test(file.getPath()); } } }
递归算法是一种直接或者间接地调用自身算法的过程。在计算机编写程序中,递归算法对解决一大类问题是十分有效的,它往往使算法的描述简洁而且易于理解。
递归算法解决问题的特点:
(1) 递归就是在过程或函数里调用自身。
(2) 在使用递归策略时,必须有一个明确的递归结束条件,称为递归出口。
(3) 递归算法解题通常显得很简洁,但运行效率较低。所以一般不提倡用递归算法设计程序。
(4) 在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。所以一般不提倡用递归算法设计程序。
数组是(相同类型数据)的(有序)(集合)
数组会在内存中开辟一块连续的空间,每个空间相当于之前的一个变量,称为数组的元素element
元素的表示 数组名[下标或者索引] scores[7] scores[0] scores[9]
索引从0开始
每个数组元素有默认值 double 0.0 boolean false int 0
数组元素有序的,不是大小顺序,是索引 的顺序
数组中可以存储基本数据类型,可以存储引用数据类型;但是对于一个数组而言,数组的类型是固定的,只能是一个
length:数组的长度
数组的长度是固定的,一经定义,不能再发生变化(数组的扩容)
package com.bjsxt; public class TestBubbleSort { public static void sort(int[] a) { int temp = 0; // 外层循环,它决定一共走几趟 for (int i = 0; i <a.length-1; ++i) { //内层循环,它决定每趟走一次 for (int j = 0; j <a.length-i-1 ; ++j) { //如果后一个大于前一个 if (a[j + 1] < a[j]) { //换位 temp = a[j];a[j] = a[j + 1];a[j + 1] = temp; } } } public static void sort2(int[] a) { int temp = 0; for (int i = 0; i <a.length-1; ++i) { //通过符号位可以减少无谓的比较,如果已经有序了,就退出循环 int flag = 0; for (int j = 0; j <a.length-1-i ; ++j) { if (a[j + 1] < a[j]) { temp = a[j]; a[j] = a[j + 1]; a[j + 1] = temp; flag = 1; } } if(flag == 0){ break; } } } }
package com.bjsxt; public class TestSelectSort { public static void sort(int arr[]) { int temp = 0; for (int i = 0; i < arr.length - 1; i++) { // 认为目前的数就是最小的, 记录最小数的下标 int minIndex = i; for (int j = i + 1; j < arr.length; j++) { if (arr[minIndex] > arr[j]) { // 修改最小值的下标 minIndex = j; } } // 当退出for就找到这次的最小值 if (i != minIndex) { temp = arr[i]; arr[i] = arr[minIndex]; arr[minIndex] = temp; } } } }
package com.bjsxt; public class TestInsertSort { public static void sort(int arr[]) { int i, j; for (i = 1; i < arr.length; i++) { int temp = arr[i]; for (j = i; j > 0 && temp < arr[j - 1]; j--) { arr[j] = arr[j - 1]; } arr[j] = temp; } } }
总结1:可变参数
1.可变参数的形式 ...
2.可变参数只能是方法的形参
3.可变参数对应的实参可以0,1,2.....个,也可以是一个数组
4.在可变参数的方法中,将可变参数当做数组来处理
5.可变参数最多有一个,只能是最后一个
6.可变参数好处:方便 简单 减少重载方法的数量
7.如果定义了可变参数的方法,不允许同时定义相同类型数组参数的方法
总结2:数组做形参和可变参数做形参联系和区别
联系:
1.实参都可以是数组;2.方法体中,可变参数当做数组来处理
区别:
1.个数不同 可变参数只能有一个数组参数可以多个
2.位置不同 可变参数只能是最后一个 数组参数位置任意
3.实参不同 可变参数实参可以0,1,2.....个,也可以是一个数组,数组的实参只能是数组
类是对象的抽象,而对象是类的具体实例。类是抽象的,不占用内存,而对象是具体的,占用存储空间。类是用于创建对象的蓝图,它是一个定义包括在特定类型的对象中的方法和变量的软件模板。
类和对象好比图纸和实物的关系,模具和铸件的关系。
比如人类就是一个概念,人类具有身高,体重等属性。人类可以做吃饭、说话等方法。
小明就是一个具体的人,也就是实例,他的属性是具体的身高200cm,体重180kg,他做的方法是具体的吃了一碗白米饭,说了“12345”这样一句话。
两者都是软件开发思想,先有面向过程,后有面向对象。在大型项目中,针对面向过程的不足推出了面向对象开发思想。

比喻
蒋介石和毛泽东分别是面向过程和面向对象的杰出代表,这样充分说明,在解决复制问题时,面向对象有更大的优越性。
面向过程是蛋炒饭,面向对象是盖浇饭。盖浇饭的好处就是“菜”“饭”分离,从而提高了制作盖浇饭的灵活性。饭不满意就换饭,菜不满意换菜。用软件工程的专业术语就是“可维护性”比较好,“饭” 和“菜”的耦合度比较低。
区别
编程思路不同: 面向过程以实现功能的函数开发为主,而面向对象要首先抽象出类、属性及其方法,然后通过实例化类、执行方法来完成功能。
封装性:都具有封装性,但是面向过程是封装的是功能,而面向对象封装的是数据和功能。
面向对象具有继承性和多态性,而面向过程没有继承性和多态性,所以面向对象优势是明显。
方法重载和方法重写(覆盖)的区别
英文 | 位置不同 | 作用不同 | |
重载 | overload | 同一个类中 | 在一个类里面为一种行为提供多种实现方式并提高可读性 |
重写 | override | 子类和父类间 | 父类方法无法满足子类的要求,子类通过方法重写满足要求 |
修饰符 | 返回值 | 方法名 | 参数 | 抛出异常 | |
重载 | 无关 | 无关 | 相同 | 不同 | 无关 |
重写 | 大于等于 | 小于等于 | 相同 | 相同 | 小于等于 |
this是对象内部指代自身的引用,同时也是解决成员变量和局部变量同名问题;this可以调用成员变量,不能调用局部变量;this也可以调用成员方法,但是在普通方法中可以省略this,在构造方法中不允许省略,必须是构造方法的第一条语句。,而且在静态方法当中不允许出现this关键字。
super代表对当前对象的直接父类对象的引用,super可以调用直接父类的成员变量(注意权限修饰符的影响,比如不能访问private成员)
super可以调用直接父类的成员方法(注意权限修饰符的影响,比如不能访问private成员);super可以调用直接父类的构造方法,只限构造方法中使用,且必须是第一条语句。
static可以修饰变量、方法、代码块和内部类
static属性属于这个类所有,即由该类创建的所有对象共享同一个static属性。可以对象创建后通过对象名.属性名和类名.属性名两种方式来访问。也可以在没有创建任何对象之前通过类名.属性名的方式来访问。
static变量和非static变量的区别(都是成员变量,不是局部变量)
1.在内存中份数不同
不管有多少个对象,static变量只有1份。对于每个对象,实例变量都会有单独的一份
static变量是属于整个类的,也称为类变量。而非静态变量是属于对象的,也称为实例变量
2.在内存中存放的位置不同
2.在内存中存放的位置不同
3.访问的方式不同
实例变量: 对象名.变量名 stu1.name="小明明";
静态变量:对象名.变量名 stu1.schoolName="西二旗小学"; 不推荐如此使用
类名.变量名 Student.schoolName="东三旗小学"; 推荐使用
4.在内存中分配空间的时间不同
Student.schoolName="东三旗小学";或者Student stu1 = new Student("小明","男",20,98);
static方法也可以通过对象名.方法名和类名.方法名两种方式来访问
static代码块。当类被第一次使用时(可能是调用static属性和方法,或者创建其对象)执行静态代码块,且只被执行一次,主要作用是实现static属性的初始化。
static内部类:属于整个外部类,而不是属于外部类的每个对象。不能访问外部类的非静态成员(变量或者方法),.可以访问外部类的静态成员
final和abstract是功能相反的两个关键字,可以对比记忆
abstract可以用来修饰类和方法,不能用来修饰属性和构造方法;使用abstract修饰的类是抽象类,需要被继承,使用abstract修饰的方法是抽象方法,需要子类被重写。
final可以用来修饰类、方法和属性,不能修饰构造方法。使用final修饰的类不能被继承,使用final修饰的方法不能被重写,使用final修饰的变量的值不能被修改,所以就成了常量。
特别注意:final修饰基本类型变量,其值不能改变,由原来的变量变为常量;但是final修饰引用类型变量,栈内存中的引用不能改变,但是所指向的堆内存中的对象的属性值仍旧可以改变。例如
package com.bjsxt; class Test { public static void main(String[] args) { final Dog dog = new Dog("欧欧"); dog.name = "美美";//正确 dog = new Dog("亚亚");//错误 } }
final修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承例如:String类、Math类等。将变量或方法声明为final,可以保证它们在使用中不被改变。被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。被声明为final的方法也同样只能使用,不能重写,但是能够重载。 使用final修饰的对象,对象的引用地址不能变,但是对象的值可以变!
finally在异常处理时提供 finally 块来执行任何清除操作。如果有finally的话,则不管是否发生异常,finally语句都会被执行。一般情况下,都把关闭物理连接(IO流、数据库连接、Socket连接)等相关操作,放入到此代码块中。
finalize方法名。Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要清理工作。finalize() 方法是在垃圾收集器删除对象之前被调用的。它是在 Object 类中定义的,因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。 一般情况下,此方法由JVM调用,程序员不要去调用!
(1)public boolean equals(java.lang.Object)
比较对象的地址值是否相等,如果子类重写,则比较对象的内容是否相等;
(2)public native int hashCode() 获取哈希码
(3)public java.lang.String toString() 把数据转变成字符串
(4)public final native java.lang.Class getClass() 获取类结构信息
(5)protected void finalize() throws java.lang.Throwable
垃圾回收前执行的方法
(6)protected native Object clone() throws
java.lang.CloneNotSupportedException 克隆
(7)public final void wait() throws java.lang.InterruptedException
多线程中等待功能
(8)public final native void notify() 多线程中唤醒功能
(9)public final native void notifyAll() 多线程中唤醒所有等待线程的功能
同一个类 | 同一个包 | 子类 | 所有类 | |
private | * | |||
defailt | * | * | ||
protected | * | * | * | |
public | * | * | * | * |
类的访问权限只有两种
public公共的 可被同一项目中所有的类访问。 (必须与文件名同名)
default默认的 可被同一个包中的类访问。
成员(成员变量或成员方法)访问权限共有四种:
public 公共的 可以被项目中所有的类访问。(项目可见性)
protected 受保护的 可以被这个类本身访问;同一个包中的所有其他的类访问;被它的子类(同一个包以及不同包中的子类)访问。(子类可见性)
default 默认的被这个类本身访问;被同一个包中的类访问。(包可见性)
private 私有的 只能被这个类本身访问。(类可见性)
继承条件下构造方法的调用规则如下:
情况1:如果子类的构造方法中没有通过super显式调用父类的有参构造方法,也没有通过this显式调用自身的其他构造方法,则系统会默认先调用父类的无参构造方法。在这种情况下,写不写“super();”语句,效果是一样的。
情况2:如果子类的构造方法中通过super显式调用父类的有参构造方法,那将执行父类相应构造方法,而不执行父类无参构造方法。
情况3:如果子类的构造方法中通过this显式调用自身的其他构造方法,在相应构造方法中应用以上两条规则。
特别注意的是,如果存在多级继承关系,在创建一个子类对象时,以上规则会多次向更高一级父类应用,一直到执行顶级父类Object类的无参构造方法为止。
“==”是关系运算符,equals()是方法,同时他们的结果都返回布尔值;
“==”使用情况如下:
a) 基本类型,比较的是值
b) 引用类型,比较的是地址
c) 不能比较没有父子关系的两个对象
equals()方法使用如下:
a) 系统类一般已经覆盖了equals(),比较的是内容。
b) 用户自定义类如果没有覆盖equals(),将调用父类的equals (比如是Object),而Object的equals的比较是地址(return (this == obj);)
c) 用户自定义类需要覆盖父类的equals()
注意:Object的==和equals比较的都是地址,作用相同
实现多态的三个条件(前提条件,向上转型、向下转型)
1、继承的存在;(继承是多态的基础,没有继承就没有多态)
2、子类重写父类的方法。(多态下会调用子类重写后的方法)
3、父类引用变量指向子类对象。(涉及子类到父类的类型转换)
向上转型 Student person = new Student()
将一个父类的引用指向一个子类对象,成为向上转型,自动进行类型转换。此时通过父类引用变量调用的方法是子类覆盖或继承父类的方法,而不是父类的方法此时通过父类引用变量无法调用子类特有的方法。
向下转型 Student stu = (Student)person;
将一个指向子类对象的引用赋给一个子类的引用,成为向下转型,此时必须进行强制类型转换。向下转型必须转换为父类引用指向的真实子类类型,,否则将出现ClassCastException,不是任意的强制转换
向下转型时可以结合使用instanceof运算符进行强制类型转换,比如出现转换异常---ClassCastException
传统的C/C++语言,需要程序员负责回收已经分配内存。
显式回收垃圾回收的缺点:
1)程序忘记及时回收,从而导致内存泄露,降低系统性能。
2)程序错误回收程序核心类库的内存,导致系统崩溃。
Java语言不需要程序员直接控制内存回收,是由JRE在后台自动回收不再使用的内存,称为垃圾回收机制,简称GC;
1)可以提高编程效率。
2)保护程序的完整性。
3)其开销影响性能。Java虚拟机必须跟踪程序中有用的对象,确定哪些是无用的。
垃圾回收机制的 特点
1)垃圾回收机制回收JVM堆内存里的对象空间,不负责回收栈内存数据。
2)对其他物理连接,比如数据库连接、输入流输出流、Socket连接无能为力。
3)垃圾回收发生具有不可预知性,程序无法精确控制垃圾回收机制执行。
4)可以将对象的引用变量设置为null,暗示垃圾回收机制可以回收该对象。
现在的JVM有多种垃圾回收 实现算法,表现各异。
垃圾回收机制回收任何对象之前,总会先调用它的finalize方法(如果覆盖该方法,让一个新的引用变量重新引用该对象,则会重新激活对象)。
程序员可以通过System.gc()或者Runtime.getRuntime().gc()来通知系统进行垃圾回收,会有一些效果,但是系统是否进行垃圾回收依然不确定。
永远不要主动调用某个对象的finalize方法,应该交给垃圾回收机制调用。
1) 八个基本数据类型的包装类
基本数据类型 | 包装类 |
byte | Byte |
boolean | Boolean |
short | Short |
char | Character |
int | Integer |
long | Long |
float | Float |
double | Double |
2)为什么为基本类型引入包装类
2.1基本数据类型有方便之处,简单、高效。
2.2但是Java中的基本数据类型却是不面向对象的(没有属性、方法),这在实际使用时存在很多的不便(比如集合的元素只能是Object)。
为了解决这个不足,在设计类时为每个基本数据类型设计了一个对应的类进行包装,这样八个和基本数据类型对应的类统称为包装类(Wrapper Class)。
3) 包装类和基本数据类型之间的转换
3.1包装类------ wrapperInstance.xxxValue() ------>基本数据类型
3.2包装类-------new WrapperClass(primitive)
3.2包装类-------new WrapperClass(primitive)
4) 自动装箱和自动拆箱
JDK1.5提供了自动装箱(autoboxing)和自动拆箱(autounboxing)功能, 从而实现了包装类和基本数据类型之间的自动转换
5) 包装类还可以实现基本类型变量和字符串之间的转换
基本类型变量--->String.valueof()--->字符串 基本类型变量<---WrapperClass.parseXxx(string)---字符串
int是java提供的8种原始数据类型之一,Java为每个原始类型提供了封装类,Integer是java为int提供的封装类。
int是java提供的8种原始数据类型之一,Java为每个原始类型提供了封装类,Integer是java为int提供的封装类。
在Hibernate中,如果将OID定义为Integer类型,那么Hibernate就可以根据其值是否为null而判断一个对象是否是临时的,如果将OID定义为了int类型,还需要在hbm映射文件中设置其unsaved-value属性为0。
另外,Integer提供了多个与整数相关的操作方法,例如,将一个字符串转换成整数,Integer中还定义了表示整数的最大值和最小值的常量。
1) java.sql.Date是java.util.Date的子类,是一个包装了毫秒值的瘦包装器,允许 JDBC 将毫秒值标识为 SQL DATE 值。毫秒值表示自 1970 年 1 月 1 日 00:00:00 GMT 以来经过的毫秒数。 为了与 SQL DATE 的定义一致,由 java.sql.Date 实例包装的毫秒值必须通过将时间、分钟、秒和毫秒设置为与该实例相关的特定时区中的零来“规范化”。 说白了,java.sql.Date就是与数据库Date相对应的一个类型,而java.util.Date是纯java的Date。
2)JAVA里提供的日期和时间类,java.sql.Date和java.sql.Time,只会从数据库里读取某部分值,这有时会导致丢失数据。例如一个包含2002/05/22 5:00:57 PM的字段,读取日期时得到的是2002/05/22,而读取时间时得到的是5:00:57 PM. 你需要了解数据库里存储时间的精度。有些数据库,比如MySQL,精度为毫秒,然而另一些数据库,包括Oracle,存储SQL DATE类型数据时,毫秒部分的数据是不保存的。以下操作中容易出现不易被发现的BUG:获得一个JAVA里的日期对象。 从数据库里读取日期 试图比较两个日期对象是否相等。如果毫秒部分丢失,本来认为相等的两个日期对象用Equals方法可能返回false。.sql.Timestamp类比java.util.Date类精确度要高。这个类包了一个getTime()方法,但是它不会返回额外精度部分的数据,因此必须使用...
总之,java.util.Date 就是Java的日期对象,而java.sql.Date 是针对SQL语句使用的,只包含日期而没有时间部分。
package com.bjsxt; import java.io.File; public class $ { public static void main(String[] args) { String path = "D:/301SXT"; test(path); } private static void test(String path) { File f = new File(path); File[] fs = f.listFiles(); if (fs == null) { return; } for (File file : fs) { if (file.isFile()) { System.out.println(file.getPath()); } else { test(file.getPath()); } } }
A | Java程序经编译后产生machine code |
B. | Java程序经编译后会生产byte code |
C. | Java程序经编译后会产生DLL |
D. | 以上都不正确 |
答案:B
分析: Java是解释型语言,编译出来的是字节码; 因此A不正确,C是C/C++语言编译动态链接库的文件为.DLL; 正确答案为B
|
A | class中的construtor不可省略 |
B. | construtor与class同名,但方法不能与class同名 |
C. | construtor在一个对象被new时执行 |
D. | 一个class只能定义一个construtor |
答案:C
分析:A:如果class中的construtor省略不写,系统会默认提供一个无参构造
B:方法名可以与类名同名,只是不符合命名规范
D:一个class中可以定义N多个construtor,这些construtor构成构造方法的重载
|
A | private |
B. | protected |
C. | final |
D. | abstract |
答案:D
分析:接口中的访问权限修饰符只可以是public或default
接口中的所有的方法必须要实现类实现,所以不能使用final
接口中所有的方法默认都是abstract的,所以接口可以使用abstract修饰,但通常abstract可以省略不写
|
class A { public A(){ System.out.println("A"); } } class B extends A{ public B(){ System.out.println("B"); } public static void main(String[] args) { B b=new B(); } }
A | 不能通过编译 |
B. | 通过编译,输出AB |
C. | 通过编译,输出B |
D. | 通过编译,输出A |
答案:B
分析:在继承关系下,创建子类对象,先执行父类的构造方法,再执行子类的构造方法。
|
A | abstract不能与final并列修饰同一个类 |
B. | abstract类中可以有private的成员 |
C. | abstract方法必须在abstract类中 |
D. | static方法能处理非static的属性 |
答案:D
分析:因为static得方法在装载class得时候首先完成,比 构造方法早,此时非static得属性和方法还没有完成初始化所以不能调用。
|
A | 程序员必须创建一个线程来释放内存 |
B. | 内存回收程序负责释放无用内存 |
C. | 内存回收程序允许程序员直接释放内存 |
D. | 内存回收程序可以在指定的时间释放内存对象 |
答案:B
分析: A. 程序员不需要创建线程来释放内存.
C. 也不允许程序员直接释放内存.
D. 不一定在什么时刻执行垃圾回收.
|
A | _sysl_111 |
B. | 2 mail |
C. | $change |
D. | class |
答案:AC
分析: 标识符的命令规范,可以包含字母、数字、下划线、$,不能以数字开头,不能是Java关键字
|
A | java.lang.Cloneable是类 |
B. | java.langRunnable是接口 |
C. | Double对象在java.lang包中 |
D. | Double a=1.0是正确的java语句 |
Double a=1.0是正确的java语句
分析:java.lang.Cloneable是接口
|
A | 45.定义一个类名为”MyClass.java”的类,并且该类可被一个工程中的所有类访问,那么该类的正确声明为()(选择两项) |
B. | class MyClass extends Object |
C. | public class MyClass |
D. | public class MyClass extends Object |
答案:CD
分析: A 类的访问权限只能是public或default
B使用默认访问权限的类,只能在本包中访问
|
答: 面向对象的三大特征:封装、继承、多态。
举例:(比如设计一个游戏)我现在创建了一个对象,名叫战士。
战士的属性是—性别,年龄,职业,等级,战斗力,血量。
它的方法—战斗,逃跑,吃饭,睡觉,死。
后来,我又建了一个对象,叫人。
属性:性别,年龄,职业,等级,血量
方法:逃跑,吃饭,睡觉,死。
我让人,成为战士的父类,战士可以直接继承人的属性和方法。
战士修改成—
属性:战斗力。
方法:战斗。
看上去战士的资料变少了,实际上没有,我们仍然可以调用方法—战士.死。
而且我们还可以重载战士.死的方法,简称重载死法。
我还建了一个对象—法师,父类也是人。
属性:法力值
方法:施法,泡妞。
你看,用了继承,创建对象变得更方便了。
再后来,我又建立了一个对象,叫怪物。
属性:等级,战力,血量。
方法:战斗,死。
建了个对象,叫白兔怪,父类怪物,可继承怪物所有的属性和方法。
属性:毛色。
方法:卖萌,吃胡萝卜。
答:
内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。
内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。
memory leak会最终会导致out of memory!
答:Java中的序列化机制能够将一个实例对象(只序列化对象的属性值,而不会去序列化什么所谓的方法。)的状态信息写入到一个字节流中使其可以通过socket进行传输、或者持久化到存储数据库或文件系统中;然后在需要的时候通过字节流中的信息来重构一个相同的对象。一般而言,要使得一个类可以序列化,只需简单实现java.io.Serializable接口即可。
对象的序列化主要有两种用途:
1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
2) 在网络上传送对象的字节序列。
在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。
当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。
答:Java创建对象的几种方式(重要):
1、 用new语句创建对象,这是最常见的创建对象的方法。
2、 运用反射手段,调用java.lang.Class或者java.lang.reflect.Constructor类的newInstance()实例方法。
3、 调用对象的clone()方法。
4、运用反序列化手段,调用java.io.ObjectInputStream对象的 readObject()方法。
(1)和(2)都会明确的显式的调用构造函数 ;(3)是在内存上对已有对象的影印,所以不会调用构造函数 ;(4)是从文件中还原类的对象,也不会调用构造函数。
答:匿名内部类是没有名字的内部类,不能继承其它类,但一个内部类可以作为一个接口,由另一个内部类实现.
1、由于匿名内部类没有名字,所以它没有构造函数。因为没有构造函数,所以它必须完全借用父类的构造函数来实例化,换言之:匿名内部类完全把创建对象的任务交给了父类去完成。
2、在匿名内部类里创建新的方法没有太大意义,但它可以通过覆盖父类的方法达到神奇效果,如上例所示。这是多态性的体现。
3、因为匿名内部类没有名字,所以无法进行向下的强制类型转换,持有对一个匿名内部类对象引用的变量类型一定是它的直接或间接父类类型。
(1) 在Java中是使用泛型来约束HashMap中的key和value的类型的,即HashMap< K, V>;而泛型在Java的规定中必须是对象Object类型的,也就是说HashMap< K, V>可以理解为HashMap< Object, Object>,很显然基本数据类型不是Object类型的,因此不能作为键值,只能是引用类型。虽然我们在HashMap中可以这样添加数据:“map.put(1, “Java”);”,但实际上是将其中的key值1进行了自动装箱操作,变为了Integer类型。
(1) 在Java中是使用泛型来约束HashMap中的key和value的类型的,即HashMap< K, V>;而泛型在Java的规定中必须是对象Object类型的,也就是说HashMap< K, V>可以理解为HashMap< Object, Object>,很显然基本数据类型不是Object类型的,因此不能作为键值,只能是引用类型。虽然我们在HashMap中可以这样添加数据:“map.put(1, “Java”);”,但实际上是将其中的key值1进行了自动装箱操作,变为了Integer类型。
实现多态有三个前提条件:
1、 继承的存在;(继承是多态的基础,没有继承就没有多态)。
2、子类重写父类的方法。(多态下会调用子类重写后的方法)。
3、父类引用变量指向子类对象。(涉及子类到父类的类型转换)。
最后使用父类的引用变量调用子类重写的方法即可实现多态。
A | Java中的继承允许一个子类继承多个父类 |
B. | 父类更具有通用性,子类更具体 |
C. | Java中的继承存在着传递性 |
D. | 当实例化子类时会递归调用父类中的构造方法 |
答案:A
分析:Java是单继承的,一个类只能继承一个父类。
|
54.Java 中 Math.random()/Math.random()值为?
如果除数与被除数均为0.0的话,则运行结果为NaN(Not a Number的简写),计算错误。
不是,两者没有任何关联;
Pair是单独的类,只不过用不同类型的参数(泛型)进行了相应的实例化而已;所以,Pair< Manager>和Pair< Employee>不是子类的关系。
56.接口和抽象类的区别
抽象类和接口均包含抽象方法,类必须实现所有的抽象方法,否则是抽象类
抽象类和接口都不能实例化,他们位于继承树的顶端,用来被其他类继承和实现
两者的区别主要体现在两方面:语法方面和设计理念方面
语法方面的区别是比较低层次的,非本质的,主要表现在:
接口中只能定义全局静态常量,不能定义变量。抽象类中可以定义常量和变量。
接口中所有的方法都是全局抽象方法。抽象类中可以有0个、1个或多个,甚至全部都是抽象方法。
抽象类中可以有构造方法,但不能用来实例化,而在子类实例化是执行,完成属于抽象类的初始化操作。接口中不能定义构造方法。
一个类只能有一个直接父类(可以是抽象类),但可以充实实现多个接口。一个类使用extends来继承抽象类,使用implements来实现接口。
一个类只能有一个直接父类(可以是抽象类),但可以充实实现多个接口。一个类使用extends来继承抽象类,使用implements来实现接口。
抽象类体现了一种继承关系,目的是复用代码,抽象类中定义了各个子类的相同代码,可以认为父类是一个实现了部分功能的“中间产品”,而子类是“最终产品”。父类和子类之间必须存在“is-a”的关系,即父类和子类在概念本质上应该是相同的。
接口并不要求实现类和接口在概念本质上一致的,仅仅是实现了接口定义的约定或者能力而已。接口定义了“做什么”,而实现类负责完成“怎么做”,体现了功能(规范)和实现分离的原则。接口和实现之间可以认为是一种“has-a的关系”
相同点:
同步方法就是在方法前加关键字synchronized,然后被同步的方法一次只能有一个线程进入,其他线程等待。而同步代码块则是在方法内部使用大括号使得一个代码块得到同步。同步代码块会有一个同步的“目标”,使得同步块更加灵活一些(同步代码块可以通过“目标”决定需要锁定的对象)。
一般情况下,如果此“目标”为this,同步方法和代码块没有太大的区别。
区别:
同步方法直接在方法上加synchronized实现加锁,同步代码块则在方法内部加锁。很明显,同步方法锁的范围比较大,而同步代码块范围要小点。一般同步的范围越大,性能就越差。所以一般需要加锁进行同步的时候,范围越小越好,这样性能更好。
静态内部类不需要有指向外部类的引用。但非静态内部类需要持有对外部类的引用。
静态内部类可以有静态成员(方法,属性),而非静态内部类则不能有静态成员(方法,属性)。
非静态内部类能够访问外部类的静态和非静态成员。静态内部类不能访问外部类的非静态成员,只能访问外部类的静态成员。
实例化方式不同:
1) 静态内部类:不依赖于外部类的实例,直接实例化内部类对象
2) 非静态内部类:通过外部类的对象实例生成内部类对象
反射的概念:
反射,一种计算机处理方式。是程序可以访问、检测和修改它本身状态或行为的一种能力。
Java反射可以于运行时加载,探知和使用编译期间完全未知的类.
程序在运行状态中, 可以动态加载一个只有名称的类, 对于任意一个已经加载的类,都能够知道这个类的所有属性和方法; 对于任意一个对象,都能调用他的任意一个方法和属性;
加载完类之后, 在堆内存中会产生一个Class类型的对象(一个类只有一个Class对象), 这个对象包含了完整的类的结构信息,而且这个Class对象就像一面镜子,透过这个镜子看到类的结构,所以被称之为:反射.
java反射使得我们可以在程序运行时动态加载一个类,动态获取类的基本信息和定义的方法,构造函数,域等。
除了检阅类信息外,还可以动态创建类的实例,执行类实例的方法,获取类实例的域值。反射使java这种静态语言有了动态的特性。
反射的作用:
通过反射可以使程序代码访问装载到JVM 中的类的内部信息
1) 获取已装载类的属性信息
2) 获取已装载类的方法
3) 获取已装载类的构造方法信息
反射的优点:
增加程序的灵活性。
如struts中。请求的派发控制。
当请求来到时。struts通过查询配置文件。找到该请求对应的action。已经方法。
然后通过反射实例化action。并调用响应method。
如果不适用反射,那么你就只能写死到代码里了。
所以说,一个灵活,一个不灵活。
很少情况下是非用反射不可的。大多数情况下反射是为了提高程序的灵活性。因此一般框架中使用较多。因为框架要适用更多的情况。对灵活性要求较高。
A | java.sql |
B. | java.awt |
C. | java.lang |
D. | java.swing |
答案:A
分析:
java.awt和javax.swing两个包是图形用户界面编程所需要的包;
java.lang包则提供了Java编程中用到的基础类。
|
A | && |
B. | <> |
C. | if |
D. | = |
答案:AD
分析:
&&是逻辑运算符中的短路与;
<>表示不等于,但是Java中不能这么使用,应该是!=;
if不是运算符;
=是赋值运算符。
|
public class Test1 { public static void main(String[] args) { int a = 0; int c = 0; do{ --c; a = a - 1; } while (a > 0); System.out.println(c); } }
A | 0 |
B. | 1 |
C. | -1 |
D. | 死循环 |
答案:C
分析:
do-while循环的特点是先执行后判断,所以代码先执行--c操作,得到c为-1,之后执行a=a-1的操作,得到a为-1,然后判断a是否大于0,判断条件不成立,退出循环,输出c为-1。
|
A | abstract修饰符可修饰字段,方法和类 |
B. | 抽象方法的body部分必须用一对大括号{}包住 |
C. | 声明抽象方法,大括号可有可无 |
D. | 声明抽象方法不可写出大括号 |
答案:D
分析:
abstract只能修饰方法和类,不能修饰字段;
抽象方法不能有方法体,即没有{};
同B。
|
A | 形式参数可被视为local Variable |
B. | 形式参数可被视为local Variable |
C. | 形式参数可被所有的字段修饰符修饰 |
D. | 形式参数为方法被调用时,真正被传递的参数 |
答案:A
分析:
local Variable为局部变量,形参和局部变量一样都只有在方法内才会发生作用,也只能在方法中使用,不会在方法外可见;
对于形式参数只能用final修饰符,其它任何修饰符都会引起编译器错误;
真正被传递的参数是实参;
形式参数可是基本数据类型也可以是引用类型(对象)。
|
A | 实例方法可直接调用超类的实例方法 |
B. | 实例方法可直接调用超类的类方法 |
C. | 实例方法可直接调用其他类的实例方法 |
D. | 实例方法可直接调用本类的类方法 |
答案:D
分析:
实例方法不可直接调用超类的私有实例方法;
实例方法不可直接调用超类的私有的类方法;
要看访问权限。
|
A | 类 (Class) |
B. | Applet |
C. | Application |
D. | Servlet |
答案:BCD
分析:
是Java中的类,不是程序;
内嵌于Web文件中,由浏览器来观看的Applet;
可独立运行的 Application;
服务器端的 Servlet。
|
A | 环境变量可在编译source code时指定 |
B. | 在編译程序时,所指定的环境变置不包括class path |
C. | javac —次可同时编译数个Java 源文件 |
D. | javac.exe能指定编译结果要置于哪个目录(directory) |
答案:BCD
分析:
环境变量一般都是先配置好再编译源文件。
|
A | new |
B. | $Usdollars |
C. | 1234 |
D. | car.taxi |
答案:ACD
分析:
new是Java的关键字;
C. 数字不能开头;
D. 不能有“.”。
|
A | 数组是—种对象 |
B. | 数组属于一种原生类 |
C. | int number[]=(31,23,33,43,35,63) |
D. | 数组的大小可以任意改变 |
答案:BCD
分析:
B. Java中的原生类(即基本数据类型)有8种,但不包括数组;
C. 语法错误,应该“{···}”,而不是“(···)”;
D. 数组的长度一旦确定就不能修改。
|
A | private |
B. | public |
C. | protected |
D. | static |
答案:ACD
分析:
能够修饰interface的只有public、abstract以及默认的三种修饰符。
|
A | call by value不会改变实际参数的数值 |
B. | call by reference能改变实际参数的参考地址 |
C. | call by reference 不能改变实际参数的参考地址 |
D. | call by reference 能改变实际参数的内容 |
答案:ACD
分析:
Java中参数的传递有两种,一种是按值传递(call by value:传递的是具体的值,如基础数据类型),另一种是按引用传递(call by reference:传递的是对象的引用,即对象的存储地址)。前者不能改变实参的数值,后者虽然不能改变实参的参考地址,但可以通过该地址访问地址中的内容从而实现内容的改变。
|
A | 在类方法中可用this来调用本类的类办法 |
B. | 在类方法中调用本类的类方法时可以直接调用 |
C. | 在类方法中只能调用本类中的类方法 |
D. | 在类方法中绝对不能调用实例方法 |
答案:ACD
分析:
类方法是在类加载时被加载到方法区存储的,此时还没有创建对象,所以不能使用this或者super关键字;
C. 在类方法中还可以调用其他类的类方法;
D. 在类方法可以通过创建对象来调用实例方法。
|
A | Java面向对象语言容许单独的过栈与函数存在 |
B. | Java面向对象语言容许单独的方法存在 |
C. | Java语言中的方法属于类中的成员(member) |
D. | Java语言中的方法必定隶属于某一类(对象),调用方法与过程或函数相同 |
答案:ABC
分析:
B. Java不允许单独的方法,过程或函数存在,需要隶属于某一类中;
C. 静态方法属于类的成员,非静态方法属于对象的成员。
|
A | 能被java.exe成功运行的java class文件必须有main()方法 |
B. | J2SDK就是Java API |
C. | Appletviewer.exe可利用jar选项运行.jar文件 |
D. | 能被Appletviewer成功运行的java class文件必须有main()方法 |
答案:BCD
分析:
B. J2SDK是sun公司编程工具,API是指的应用程序编程接口;
C. Appletviewer.exe就是用来解释执行java applet应用程序的,一种执行HTML文件上的Java小程序类的Java浏览器;
D. 能被Appletviewer成功运行的java class文件可以没有main()方法。
|
A | float |
B. | double |
C. | Float |
D. | Double |
答案:B
分析:
小数默认是双精度浮点型即double类型的。
|
A | private |
B. | protected |
C. | final |
D. | abstract |
答案:D
分析:
能够修饰interface的只有public、abstract以及默认的三种修饰符。
|
A | 是 |
B. | 否 |
答案:A
分析:
Java创建对象的几种方式:
(1) 用new语句创建对象,这是最常见的创建对象的方法。
(2) 运用反射手段,调用java.lang.Class或者
java.lang.reflect.Constructor类的newInstance()实例方法。
(3) 调用对象的clone()方法。
(4) 运用反序列化手段,调用java.io.ObjectInputStream对象的readObject()方法。
(1)和(2)都会明确的显式的调用构造函数;(3)是在内存上对已有对象的影印,所以不会调用构造函数;(4)是从文件中还原类的对象,也不会调用构造函数。
|
78.存在使i+1< i的数么?
接口可以继承接口,抽象类可以实现接口,抽象类可以继承实体类。
int是java提供的8种原始数据类型之一。Java为每个原始类型提供了封装类,Integer是java为int提供的封装类。int的默认值为0,而Integer的默认值为null,即Integer可以区分出未赋值和值为0的区别,int则无法表达出未赋值的情况,例如,要想表达出没有参加考试和考试成绩为0的区别,则只能使用Integer。在JSP开发中,Integer的默认为null,所以用el表达式在文本框中显示时,值为空白字符串,而int默认的默认值为0,所以用el表达式在文本框中显示时,结果为0,所以,int不适合作为web层的表单数据的类型。
在Hibernate中,如果将OID定义为Integer类型,那么Hibernate就可以根据其值是否为null而判断一个对象是否是临时的,如果将OID定义为了int类型,还需要在hbm映射文件中设置其unsaved-value属性为0。另外,Integer提供了多个与整数相关的操作方法,例如,将一个字符串转换成整数,Integer中还定义了表示整数的最大值和最小值的常量。
SerialVersionUid,简言之,其目的是序列化对象版本控制,有关各版本反序列化时是否兼容。如果在新版本中这个值修改了,新版本就不兼容旧版本,反序列化时会抛出InvalidClassException异常。如果修改较小,比如仅仅是增加了一个属性,我们希望向下兼容,老版本的数据都能保留,那就不用修改;如果我们删除了一个属性,或者更改了类的继承关系,必然不兼容旧数据,这时就应该手动更新版本号,即SerialVersionUid。
< a\b[^>]+\bhref="([^"]*)"[^>]*>([\s\S]*?)< /a>
< a\b[^>]+\bhref="([^"]*)"[^>]*>([\s\S]*?)< /a>
答: 110
答:Java程序中创建新的类对象,使用关键字new是正确的; 回收无用的类对象使用关键字free是错误的.
答:getDeclaredFields(): 可以获取所有本类自己声明的方法, 不能获取继承的方法
getFields(): 只能获取所有public声明的方法, 包括继承的方法
答:不正确。
通常情况下,进行比较判断的处理,switch 和if-else可以互相转换来写;if-else作用的范围比switch-case作用范围要大,但是当switch-case和if-else都可以用的情况下,通常推荐使用switch-case。
比如:
switch (ch) { case 'a': System.out.println("A"); break; case 'b': System.out.println("B"); break; case 'c': System.out.println("C"); break; case 'd': System.out.println("D"); break; case 'e': System.out.println("E"); break; default: System.out.println("other"); break; }
换为if-else
if (ch == 'a') { System.out.println("A"); } else if (ch == 'b') { System.out.println('B'); } else if (ch == 'c') { System.out.println("C"); } else if (ch == 'd') { System.out.println("D"); } else if (ch == 'e') { System.out.println("E"); } else { System.out.println("Other"); }
&和&&的联系(共同点):
&和&&都可以用作逻辑与运算符,但是要看使用时的具体条件来决定。
操作数1&操作数2,操作数1&&操作数2,
操作数1&操作数2,操作数1&&操作数2,
情况1:当上述的操作数是boolean类型变量时,&和&&都可以用作逻辑与运算符。
情况2:当上述的表达式结果是boolean类型变量时,&和&&都可以用作逻辑与运算符。
表示逻辑与(and),当运算符两边的表达式的结果或操作数都为true时,整个运算结果才为true,否则,只要有一方为false,结果都为false。
表示逻辑与(and),当运算符两边的表达式的结果或操作数都为true时,整个运算结果才为true,否则,只要有一方为false,结果都为false。
(1)、&逻辑运算符称为逻辑与运算符,&&逻辑运算符称为短路与运算符,也可叫逻辑与运算符。
对于&:无论任何情况,&两边的操作数或表达式都会参与计算。
对于&&:当&&左边的操作数为false或左边表达式结果为false时,&&右边的操作数或表达式将不参与计算,此时最终结果都为false。
综上所述,如果逻辑与运算的第一个操作数是false或第一个表达式的结果为false时,对于第二个操作数或表达式是否进行运算,对最终的结果没有影响,结果肯定是false。推介平时多使用&&,因为它效率更高些。
(2)、&还可以用作位运算符。当&两边操作数或两边表达式的结果不是boolean类型时,&用于按位与运算符的操作。
final修饰基本类型变量,其值不能改变。
但是final修饰引用类型变量,栈内存中的引用不能改变,但是所指向的堆内存中的对象的属性值仍旧可以改变。
例如:
class Test { public static void main(String[] args) { final Dog dog = new Dog("欧欧"); dog.name = "美美";//正确 dog = new Dog("亚亚");//错误 } }
\d: 匹配一个数字字符。等价于[0-9]
\D: 匹配一个非数字字符。等价于[^0-9]
\s: 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]
. :匹配除换行符 \n 之外的任何单字符。要匹配 . ,请使用 \. 。
*:匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 \*。
+:匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 \+。
|:将两个匹配条件进行逻辑“或”(Or)运算
[0-9]{6}:匹配连续6个0-9之间的数字
\d+:匹配至少一个0-9之间的数字
A | m.length() |
B. | m.length |
C. | m.length()+1 |
D. | m.length+1 |
答案:B
分析:数组的长度是.length
|
A | long l = 4990 |
B. | int i = 4L |
C. | float f = 1.1 |
D. | double d = 34.4 |
答案:AD
分析:B int属于整数型应该是int=4 C应该是float f=1.1f
|
A | int k=new String(“aa”) |
B. | String str = String(“bb”) |
C. | char c=74; |
D. | long j=8888; |
答案:CD
分析:A需要强制类型转换 B String str =new String(“bb”)
|
System.out.println(""+("12"=="12"&&"12".equals("12"))); (“12”==”12”&&”12”.equals(“12”)) “12”==”12”&&”12”.equals(“12”)
true
false
A | & |
B. | && |
C. | | |
D. | || |
答案:BD
分析:A C是逻辑与计算
|
A | private void example(int m){...} |
B. | public int example(){...} |
C. | public void example2(){...} |
D. | public int example(int m.float f){...} |
答案:AD
分析:BC定义的是新函数
|
int i=1; Int j=i++; If((j>++j)&&(i++==j)){j+=i:} System.out.println(j);
A | 1 |
B. | 2 |
C. | 3 |
D. | 4 |
答案:B
分析: i++先引用后。++i 先增加后引用
|
A | for |
B. | do...while |
C. | while |
D. | while...do |
答案:B
分析: ACD都不一定进行循环
|
package com.bjsxt; public class smaillT{ public static void main(String args[]){ smaillT t=new smaillT(); int b = t.get(); System.out.println(b); } public int get() { try { return 1; }finally{ return 2; } } }
输出结果:2
int i=9; switch (i) { default: System.out.println("default"); case 0: System.out.println("zero"); break; case 1: System.out.println("one"); break; case 2: System.out.println("two"); break; }
打印结果:
打印结果:
zero
继承(英语:inheritance)是面向对象软件技术当中的一个概念。如果一个类别A“继承自”另一个类别B,就把这个A称为“B的子类别”,而把B称为“A的父类别”也可以称“B是A的超类”。继承可以使得子类别具有父类别的各种属性和方法,而不需要再次编写相同的代码。在令子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能。另外,为子类别追加新的属性和方法也是常见的做法。 一般静态的面向对象编程语言,继承属于静态的,意即在子类别的行为在编译期就已经决定,无法在执行期扩充。
那么如何使用继承呢?用extends关键字来继承父类。
如上面A类与B类,当写继承语句时, class A类 extends B类{ } 其中A类是子类,B类是父类。
英文 | 位置不同 | 作用不同 | |
重载 | overload | 同一个类中 |
在一个类里面为一种行为提供多种实现方式并提高可读性
现方式并提高可读性
|
重写 | override | 子类和父类间 |
父类方法无法满足子类的要求,子类通
过方法重写满足要求
|
计算机不能直接理解高级语言,只能理解和运行机器语言,所以必须要把高级语言翻译成机器语言,计算机才能运行高级语言所编写的程序。翻译的方式有两种,一个是编译,一个是解释。
用编译型语言写的程序执行之前,需要一个专门的编译过程,通过编译系统把高级语言翻译成机器语言,把源高级程序编译成为机器语言文件,比如windows下的exe文件。以后就可以直接运行而不需要编译了,因为翻译只做了一次,运行时不需要翻译,所以一般而言,编译型语言的程序执行效率高。
解释型语言在运行的时候才翻译,比如VB语言,在执行的时候,专门有一个解释器能够将VB语言翻译成机器语言,每个语句都是执行时才翻译。这样解释型语言每执行一次就要翻译一次,效率比较低。
编译型与解释型,两者各有利弊。前者由于程序执行速度快,同等条件下对系统要求较低,因此像开发操作系统、大型应用程序、数据库系统等时都采用它,像C/C++、Pascal/Object Pascal(Delphi)等都是编译语言,而一些网页脚本、服务器脚本及辅助开发接口这样的对速度要求不高、对不同系统平台间的兼容性有一定要求的程序则通常使用解释性语言,如JavaScript、VBScript、Perl、Python、Ruby、MATLAB 等等。
JAVA语言是一种编译型-解释型语言,同时具备编译特性和解释特性(其实,确切的说java就是解释型语言,其所谓的编译过程只是将.java文件编程成平台无关的字节码.class文件,并不是向C一样编译成可执行的机器语言,在此请读者注意Java中所谓的“编译”和传统的“编译”的区别)。作为编译型语言,JAVA程序要被统一编译成字节码文件——文件后缀是class。此种文件在java中又称为类文件。java类文件不能再计算机上直接执行,它需要被java虚拟机翻译成本地的机器码后才能执行,而java虚拟机的翻译过程则是解释性的。java字节码文件首先被加载到计算机内存中,然后读出一条指令,翻译一条指令,执行一条指令,该过程被称为java语言的解释执行,是由java虚拟机完成的。
&和&&都可以用作逻辑与运算符,但是要看使用时的具体条件来决定。
操作数1&操作数2 | 操作数1&&操作数2 |
表达式1&表达式2 | 表达式1&&表达式2 |
情况1:当上述的操作数是boolean类型变量时,&和&&都可以用作逻辑与运算符。
情况2:当上述的表达式结果是boolean类型变量时,&和&&都可以用作逻辑与运算符。
表示逻辑与(and),当运算符两边的表达式的结果或操作数都为true时,整个运算结果才为true,否则,只要有一方为false,结果都为false。
&和&&的区别(不同点):
(1)、&逻辑运算符称为逻辑与运算符,&&逻辑运算符称为短路与运算符,也可叫逻辑与运算符。
对于&:无论任何情况,&两边的操作数或表达式都会参与计算。
对于&&:当&&左边的操作数为false或左边表达式结果为false时,&&右边的操作数或表达式将不参与计算,此时最终结果都为false。
综上所述,如果逻辑与运算的第一个操作数是false或第一个表达式的结果为false时,对于第二个操作数或表达式是否进行运算,对最终的结果没有影响,结果肯定是false。推介平时多使用&&,因为它效率更高些。
(2)、&还可以用作位运算符。当&两边操作数或两边表达式的结果不是boolean类型时,&用于按位与运算符的操作。
|和||的区别和联系与&和&&的区别和联系类似
在异常处理时提供 finally 块来执行任何清除操作。
如果有finally的话,则不管是否发生异常,finally语句都会被执行,包括遇到return语句。
finally中语句不执行的唯一情况中执行了System.exit(0)语句。
A | al.java |
B. | al.class |
C. | al |
D. | 都对 |
答案:A
分析:.class是java的解析文件
|
A | 类型定义机制 |
B. | 数据封装机制 |
C. | 类型定义机制和数据封装机制 |
D. | 上述都不对 |
答案:C
|
A | 用基本数据类型作为参数 |
B. | 用对象作为参数 |
C. | A和B都对 |
D. | A和B都不对 |
答案:B
分析:基本数据类型不能改变实参的值
|
A | 安全性 |
B. | 多线性 |
C. | 跨平台 |
D. | 可移植 |
可移植
|
A | 构造函数的返回类型只能是void型 |
B. | 构造函数是类的一种特殊函数,它的方法名必须与类名相同 |
C. | 构造函数的主要作用是完成对类的对象的初始化工作 |
D. | 一般在创建新对象时,系统会自动调用构造函数 |
答案:A
分析:构造函数的名字与类的名字相同,并且不能指定返回类型。
|
A | static |
B. | package |
C. | private |
D. | public |
答案:A
|
package com.bjsxt; public class Test { public static void main(String[] args) { outer: for (int i = 0; i < 3; i++) inner: for (int j = 0; j < 2; j++) { if (j == 1) continue outer; System.out.println(j + " and " + i); } } }
A |
0 and 0
0 and 1
0 and 2
|
B. |
1 and 0
1 and 1
1 and 2
|
C. |
2 and 0
2 and 1
2 and 2
|
答案:A
|
package com.bjsxt; public class Test { private int m; public static void fun() { // some code… } }
A | 将private int m 改为 protected int m |
B. | 将private int m 改为 public int m |
C. | 将private int m 改为 static int m |
D. | 将private int m 改为int m |
答案:C
|
A | public void example(int m){…} |
B. | public int example(int m){…} |
C. | public void example2(){…} |
D. | public int example(int m,float f){…} |
答案:ABD
|
父类:
package com.bjsxt; public class FatherClass { public FatherClass() { System.out.println("FatherClassCreate"); } }
子类:
package com.bjsxt; import com.bjsxt.FatherClass; public class ChildClass extends FatherClass { public ChildClass() { System.out.println("ChildClass Create"); } public static void main(String[] args) { FatherClass fc = new FatherClass(); ChildClass cc = new ChildClass(); } }
执行:C:\>java com.bjsxt.ChildClass
输出结果:?
答:
FatherClassCreate
FatherClassCreate
ChildClass Create
答:因为类A、B不是接口,所以是不可以直接实现的,但可以将A、B类定义成父子类,那么C类就能实现A、B类的功能了。假如A为B的父类,B为C的父类,此时C就能使用A、B的功能。
答:构造方法可以被重载,但是构造方法不能被重写,子类也不能继承到父类的构造方法
答:范围是-128至127
public class TestDateFormat2 { public static void main(String[] args) throws Exception { //第一步:将字符串(2013-02-18 10:53:10)转换成日期Date DateFormat sdf=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); String sdate="2013-02-18 10:53:10"; Date date=sdf.parse(sdate); System.out.println(date); //第二步:将日期Date转换成字符串String DateFormat sdf2=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); String sdate2=sdf2.format(date); System.out.println(sdate2); } }
A. | 是 |
B. | 否 |
分析:答案:A
Java创建对象的几种方式(重要):
(1) 用new语句创建对象,这是最常见的创建对象的方法。
(2) 运用反射手段,调用java.lang.Class或者
java.lang.reflect.Constructor类的newInstance()实例方法。
(3) 调用对象的clone()方法
(4) 运用反序列化手段,调用java.io.ObjectInputStream对象的 readObject()方法。
(1)和(2)都会明确的显式的调用构造函数 ;(3)是在内存上对已有对象的影印,所以不会调用构造函数 ;(4)是从文件中还原类的对象,也不会调用构造函数。
(1)和(2)都会明确的显式的调用构造函数 ;(3)是在内存上对已有对象的影印,所以不会调用构造函数 ;(4)是从文件中还原类的对象,也不会调用构造函数。
|
A. | DES |
B. | MD5 |
C. | DSA |
D. | RSA |
分析:答案:A
分析:常用的对称加密算法有:DES、3DES、RC2、RC4、AES
常用的非对称加密算法有:RSA、DSA、ECC
使用单向散列函数的加密算法:MD5、SHA
|
publicstaticint get Value(int i){ int result=0; switch(i){ case 1: result=result +i case 2: result=result+i*2 case 3: result=result+i*3 } return result; }
A. | 0 |
B. | 2 |
C. | 4 |
D. | 10 |
答案:C
分析:result = 0 + 2 * 2;
|
publicvoid test(){ String a="a"; String b="b"; String c="c"; c=a+""+b+""+c; System.out.print(c); }
分析:答案: 一个对象,因为编译期进行了优化,3个字符串常量直接折叠为一个
答案: -11
分析:小数点后第一位=5
正数:Math.round(11.5)=12
负数:Math.round(-11.5)=-11
小数点后第一位<5
正数:Math.round(11.46)=11
负数:Math.round(-11.46)=-11
小数点后第一位>5
正数:Math.round(11.68)=12
负数:Math.round(-11.68)=-12
根据上面例子的运行结果,我们还可以按照如下方式总结,或许更加容易记忆:
参数的小数点后第一位<5,运算结果为参数整数部分。
参数的小数点后第一位>5,运算结果为参数整数部分绝对值+1,符号(即正负)不变。
参数的小数点后第一位=5,正数运算结果为整数部分+1,负数运算结果为整数部分。
终结:大于五全部加,等于五正数加,小于五全不加。
分析:十进制数278的对应十六进制数是116
分析:
1:“字节”是byte,“位”是bit ;
2: 1 byte = 8 bit ;
char 在Java中是2个字节。java采用unicode,2个字节(16位)来表示一个字符。
short 2个字节
int 4个字节
long 8个字节
分析:'a'是char型,1 是int行,int与char相加,char会被强转为int行,char的ASCII码对应的值是97,所以加一起打印98
A. | java程序经编译后会产生machine code |
B. | java程序经编译后会产生 byte code |
C. | java程序经编译后会产生DLL |
D. | 以上都不正确 |
答案:B
分析:java程序编译后会生成字节码文件,就是.class文件
|
A. | class中的constructor不可省略 |
B. | constructor必须与class同名,但方法不能与class同名 |
C. | constructor在一个对象被new时执行 |
D. | 一个class只能定义一个constructor |
答案:C
|
a=0;c=0; do{ ——c; a=a-1; }while(a>0); 后,c的值是()
A. | 0 |
B. | 1 |
C. | -1 |
D. | 死循环 |
答案:C
do{...}while(...);语句至少执行一次
|
A. | abstract修饰符可修饰字段、方法和类 |
B. | 抽象方法的body部分必须用一对大括号{}包住 |
C. | 声明抽象方法,大括号可有可无 |
D. | 声明抽象方法不可写出大括号 |
答案:D
分析: abstract不能修饰字段。既然是抽象方法,当然是没有实现的方法,根本就没有body部分。
|
A. | 形式参数可被视为local variable |
B. | 形式参数可被字段修饰符修饰 |
C. | 形式参数为方法被调用时,真正被传递的参数 |
D. | 形式参数不可以是对象 |
答案A:
分析:
A:形式参数可被视为local variable。形参和局部变量一样都不能离开方法。都只有在方法内才会发生作用,也只有在方法中使用,不会在方法外可见。
B:对于形式参数只能用final修饰符,其它任何修饰符都会引起编译器错误。但是用这个修饰符也有一定的限制,就是在方法中不能对参数做任何修改。 不过一般情况下,一个方法的形参不用final修饰。只有在特殊情况下,那就是:方法内部类。 一个方法内的内部类如果使用了这个方法的参数或者局部变量的话,这个参数或局部变量应该是final。
C:形参的值在调用时根据调用者更改,实参则用自身的值更改形参的值(指针、引用皆在此列),也就是说真正被传递的是实参。
D:方法的参数列表指定要传递给方法什么样的信息,采用的都是对象的形式。因此,在参数列表中必须指定每个所传递对象的类型及名字。想JAVA中任何传递对象的场合一样,这里传递的实际上也是引用,并且引用的类型必须正确。--《Thinking in JAVA》
|
1、两个变量的生命周期不同。
成员变量随着对象的创建而存在,随着对象的被回收而释放。
静态变量随着类的加载而存在,随着类的消失而消失。
2、调用方式不同。
成员变量只能被对象调用。
成员变量只能被对象调用。
成员变量只能被对象调用。
类名调用 :Person.country
3、别名不同。
成员变量也称为实例变量。
静态变量称为类变量。
4、数据存储位置不同。
成员变量数据存储在堆内存的对象中,所以也叫对象的特有数据.
静态变量数据存储在方法区(共享数据区)的静态区,所以也叫对象的共享数据.
1、用final修饰的类不能被扩展,也就是说不可能有子类;
2、用final修饰的方法不能被替换或隐藏:
① 使用final修饰的实例方法在其所属类的子类中不能被替换(overridden);
② 使用final修饰的静态方法在其所属类的子类中不能被重定义(redefined)而隐藏(hidden);
3、用final修饰的变量最多只能赋值一次,在赋值方式上不同类型的变量或稍有不同:
① 静态变量必须明确赋值一次(不能只使用类型缺省值);作为类成员的静态变量,赋值可以在其声明中通过初始化表达式完成,也可以在静态初始化块中进行;作为接口成员的静态变量,赋值只能在其声明中通过初始化表达式完成;
② 实例变量同样必须明确赋值一次(不能只使用类型缺省值);赋值可以在其声明中通过初始化表达式完成,也可以在实例初始化块或构造器中进行;
③ 方法参数变量在方法被调用时创建,同时被初始化为对应实参值,终止于方法体 (body)结束,在此期间其值不能改变;
④ 构造器参数变量在构造器被调用(通过实例创建表达式或显示的构造器调用)时创建,同时被初始化,为对应实参值,终止于构造器体结束,在此期间其值不能改变;
⑤ 异常处理器参数变量在有异常被try语句的catch子句捕捉到时创建,同时被初始化为实际的异常对象,终止于catch语句块结束,在此期间其值不能改变;
⑥ 局部变量在其值被访问之前必须被明确赋值;
A. | 除以10 |
B. | 除以2 |
C. | 乘以2 |
D. | 乘以10 |
乘以10
分析:可以看个例子
101.1 对应的十进制为 2^2*1 + 2^1*0 + 2^0*1 + 2^-1*1 = 5.5小数点右移一位
1011 对应的十进制为 2^3*1 + 2^2*0 + 2^1*1 + 2^0*1 = 11所以是扩大到原来的2倍
|
答:面向对象的特征主要有以下几个方面:
1、抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。
2、继承:继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)。继承让变化中的软件系统有了一定的延续性,同时继承也是封装程序中可变因素的重要手段(如果不能理解请阅读阎宏博士的《Java与模式》或《设计模式精解》中关于桥梁模式的部分)。
3、封装:通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。面向对象的本质就是将现实世界描绘成一系列完全自治、封闭的对象。我们在类中编写的方法就是对实现细节的一种封装;我们编写一个类就是对数据和数据操作的封装。可以说,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口(可以想想普通洗衣机和全自动洗衣机的差别,明显全自动洗衣机封装更好因此操作起来更简单;我们现在使用的智能手机也是封装得足够好的,因为几个按键就搞定了所有的事情)。
4、多态性:多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情。多态性分为编译时的多态性和运行时的多态性。如果将对象的方法视为对象向外界提供的服务,那么运行时的多态性可以解释为:当A系统访问B系统提供的服务时,B系统有多种提供服务的方式,但一切对A系统来说都是透明的(就像电动剃须刀是A系统,它的供电系统是B系统,B系统可以使用电池供电或者用交流电,甚至还有可能是太阳能,A系统只会通过B类对象调用供电的方法,但并不知道供电系统的底层实现是什么,究竟通过何种方式获得了动力)。方法重载(overload)实现的是编译时的多态性(也称为前绑定),而方法重写(override)实现的是运行时的多态性(也称为后绑定)。运行时的多态是面向对象最精髓的东西,要实现多态需要做两件事:1. 方法重写(子类继承父类并重写父类中已有的或抽象的方法);2. 对象造型(用父类型引用引用子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)。
答:不正确。3.4是双精度数,将双精度型(double)赋值给浮点型(float)属于下转型(down-casting,也称为窄化)会造成精度损失,因此需要强制类型转换float f =(float)3.4; 或者写成float f =3.4F;。
答:对于short s1 = 1; s1 = s1 + 1;由于1是int类型,因此s1+1运算结果也是int 型,需要强制转换类型才能赋值给short型。而short s1 = 1; s1 += 1;可以正确编译,因为s1+= 1;相当于s1 = (short)(s1 + 1);其中有隐含的强制类型转换。
答: goto 是Java中的保留字,在目前版本的Java中没有使用。(根据James Gosling(Java之父)编写的《The Java Programming Language》一书的附录中给出了一个Java关键字列表,其中有goto和const,但是这两个是目前无法使用的关键字,因此有些地方将其称之为保留字,其实保留字这个词应该有更广泛的意义,因为熟悉C语言的程序员都知道,在系统类库中使用过的有特殊意义的单词或单词的组合都被视为保留字)
答:Java是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入不是对象的基本数据类型,但是为了能够将这些基本数据类型当成对象操作,Java为每一个基本数据类型都引入了对应的包装类型(wrapper class),int的包装类就是Integer,从JDK 1.5开始引入了自动装箱/拆箱机制,使得二者可以相互转换。
Java 为每个原始类型提供了包装类型:
原始类型: boolean,char,byte,short,int,long,float,double
包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double
package com.bjsxt; public class AutoUnboxingTest { public static void main(String[] args) { Integer a = new Integer(3); Integer b = 3; // 将3自动装箱成Integer类型 int c = 3; System.out.println(a == b); // false 两个引用没有引用同一对象 System.out.println(a == c); // true a自动拆箱成int类型再和c比较 } }
补充:最近还遇到一个面试题,也是和自动装箱和拆箱相关的,代码如下所示:
public class Test03 { public static void main(String[] args) { Integer f1 = 100, f2 = 100, f3 = 150, f4 = 150; System.out.println(f1 == f2); System.out.println(f3 == f4); } }
如果不明就里很容易认为两个输出要么都是true要么都是false。首先需要注意的是f1、f2、f3、f4四个变量都是Integer对象,所以下面的==运算比较的不是值而是引用。装箱的本质是什么呢?当我们给一个Integer对象赋一个int值的时候,会调用Integer类的静态方法valueOf,如果看看valueOf的源代码就知道发生了什么。
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
IntegerCache是Integer的内部类,其代码如下所示:
/* Cache to support the object identity semantics of autoboxing for values between * -128 and 127 (inclusive) as required by JLS. * * The cache is initialized on first usage. The size of the cache * may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option. * During VM initialization, java.lang.Integer.IntegerCache.high property * may be set and saved in the private system properties in the * sun.misc.VM class. */ private static class IntegerCache { static final int low = -128; static final int high; static final Integer cache[]; static { // high value may be configured by property int h = 127; String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); if (integerCacheHighPropValue != null) { try { int i = parseInt(integerCacheHighPropValue); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - (-low) -1); } catch( NumberFormatException nfe) { // If the property cannot be parsed into an int, ignore it. } } high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); // range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127; } private IntegerCache() {} }
简单的说,如果字面量的值在-128到127之间,那么不会new新的Integer对象,而是直接引用常量池中的Integer对象,所以上面的面试题中f1==f2的结果是true,而f3==f4的结果是false。越是貌似简单的面试题其中的玄机就越多,需要面试者有相当深厚的功力。
答:&运算符有两种用法:(1)按位与;(2)逻辑与。&&运算符是短路与运算。逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是true整个表达式的值才是true。&&之所以称为短路运算是因为,如果&&左边的表达式的值是false,右边的表达式会被直接短路掉,不会进行运算。很多时候我们可能都需要用&&而不是&,例如在验证用户登录时判定用户名不是null而且不是空字符串,应当写为:username != null &&!username.equals(“”),二者的顺序不能交换,更不能用&运算符,因为第一个条件如果不成立,根本不能进行字符串的equals比较,否则会产生NullPointerException异常。注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此。
补充:如果你熟悉JavaScript,那你可能更能感受到短路运算的强大,想成为 JavaScript的高手就先从玩转短路运算开始吧。
答:Math.round(11.5)的返回值是12,Math.round(-11.5)的返回值是-11。四舍五入的原理是在参数上加0.5然后进行下取整。
答:早期的JDK中,switch(expr)中,expr可以是byte、short、char、int。从1.5版开始,Java中引入了枚举类型(enum),expr也可以是枚举,从JDK 1.7版开始,还可以是字符串(String)。长整型(long)是不可以的。
答: 2 << 3(左移3位相当于乘以2的3次方,右移3位相当于除以2的3次方)。
补充: 我们为编写的类重写hashCode方法时,可能会看到如下所示的代码,其实我们不太理解为什么要使用这样的乘法运算来产生哈希码(散列码),而且为什么这个数是个素数,为什么通常选择31这个数?前两个问题的答案你可以自己百度一下,选择31是因为可以用移位和减法运算来代替乘法,从而得到更好的性能。说到这里你可能已经想到了:31 * num <==> (num << 5) - num,左移5位相当于乘以2的5次方(32)再减去自身就相当于乘以31。现在的VM都能自动完成这个优化。
package com.bjsxt; public class PhoneNumber { private int areaCode; private String prefix; private String lineNumber; @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + areaCode; result = prime * result + ((lineNumber == null) ? 0 : lineNumber.hashCode()); result = prime * result + ((prefix == null) ? 0 : prefix.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; PhoneNumber other = (PhoneNumber) obj; if (areaCode != other.areaCode) return false; if (lineNumber == null) { if (other.lineNumber != null) return false; } else if (!lineNumber.equals(other.lineNumber)) return false; if (prefix == null) { if (other.prefix != null) return false; } else if (!prefix.equals(other.prefix)) return false; return true; } }
答:在最外层循环前加一个标记如A,然后用break A;可以跳出多重循环。(Java中支持带标签的break和continue语句,作用有点类似于C和C++中的goto语句,但是就像要避免使用goto一样,应该避免使用带标签的break和continue,因为它不会让你的程序变得更优雅,很多时候甚至有相反的作用,所以这种语法其实不知道更好)
答:构造器不能被继承,因此不能被重写,但可以被重载。
答:不对,如果两个对象x和y满足x.equals(y) == true,它们的哈希码(hash code)应当相同。Java对于eqauls方法和hashCode方法是这样规定的:(1)如果两个对象相同(equals方法返回true),那么它们的hashCode值一定要相同;(2)如果两个对象的hashCode相同,它们并不一定相同。当然,你未必要按照要求去做,但是如果你违背了上述原则就会发现在使用容器时,相同的对象可以出现在Set集合中,同时增加新元素的效率会大大下降(对于使用哈希存储的系统,如果哈希码频繁的冲突将会造成存取性能急剧下降)。
补充:关于equals和hashCode方法,很多Java程序都知道,但很多人也就是仅仅知道而已,在Joshua Bloch的大作《Effective Java》(很多软件公司,《Effective Java》、《Java编程思想》以及《重构:改善既有代码质量》是Java程序员必看书籍,如果你还没看过,那就赶紧去亚马逊买一本吧)中是这样介绍equals方法的:首先equals方法必须满足自反性(x.equals(x)必须返回true)、对称性(x.equals(y)返回true时,y.equals(x)也必须返回true)、传递性(x.equals(y)和y.equals(z)都返回true时,x.equals(z)也必须返回true)和一致性(当x和y引用的对象信息没有被修改时,多次调用x.equals(y)应该得到同样的返回值),而且对于任何非null值的引用x,x.equals(null)必须返回false。实现高质量的equals方法的诀窍包括:1. 使用==操作符检查“参数是否为这个对象的引用”;2. 使用instanceof操作符检查“参数是否为正确的类型”;3. 对于类中的关键属性,检查参数传入对象的属性是否与之相匹配;4. 编写完equals方法后,问自己它是否满足对称性、传递性、一致性;5. 重写equals时总是要重写hashCode;6. 不要将equals方法参数中的Object对象替换为其他的类型,在重写时不要忘掉@Override注解。
答:是值传递。Java 编程语言只有值传递参数。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性可以在被调用过程中被改变,但对象的引用是永远不会改变的。C++和C#中可以通过传引用或传输出参数来改变传入的参数的值。
补充:Java中没有传引用实在是非常的不方便,这一点在Java 8中仍然没有得到改进,正是如此在Java编写的代码中才会出现大量的Wrapper类(将需要通过方法调用修改的引用置于一个Wrapper类中,再将Wrapper对象传入方法),这样的做法只会让代码变得臃肿,尤其是让从C和C++转型为Java程序员的开发者无法容忍。
答:Java的三大特征之一,多态机制,包括方法的多态和对象的多态;方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。重载(overload)发生在同一个类中,相同的方法,如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;重写(override)发生在子类与父类之间也就是继承机制当中,当父类的方法不能满足子类的要求,此时子类重写父类的方法;要求:方法名、形参列表相同;返回值类型和异常类型,子类小于等于父类;访问权限,子类大于等于父类,切记父类的私有方法以及被final修饰的方法不能被子类重写;重载对返回类型没有特殊的要求。
答:方法的重载,即使返回值类型不同,也不能改变实现功能相同或类似这一既定事实;同时方法的重载只是要求两同三不同,即在同一个类中,相同的方法名称,参数列表当中的参数类型、个数、顺序不同;跟权限修饰符和返回值类无关
答:内部类就是在一个类的内部定义的类,内部类中不能定义静态成员(静态成员不是对象的特性,只是为了找一个容身之处,所以需要放到一个类中而已,这么一点小事,你还要把它放到类内部的一个类中,过分了啊!提供内部类,不是为让你干这种事情,无聊,不让你干。我想可能是既然静态成员类似c语言的全局变量,而内部类通常是用于创建内部对象用的,所以,把“全局变量”放在内部类中就是毫无意义的事情,既然是毫无意义的事情,就应该被禁止),内部类可以直接访问外部类中的成员变量,内部类可以定义在外部类的方法外面,也可以定义在外部类的方法体中,如下所示:
public class Outer { int out_x = 0; public void method() { Inner1 inner1 = new Inner1(); public class Inner2 //在方法体内部定义的内部类 { public method() { out_x = 3; } } Inner2 inner2 = new Inner2(); } public class Inner1 //在方法体外面定义的内部类 { } }
在方法体外面定义的内部类的访问类型可以是public,protecte,默认的,private等4种类型,这就好像类中定义的成员变量有4种访问类型一样,它们决定这个内部类的定义对其他类是否可见;对于这种情况,我们也可以在外面创建内部类的实例对象,创建内部类的实例对象时,一定要先创建外部类的实例对象,然后用这个外部类的实例对象去创建内部类的实例对象,代码如下:
Outer outer = new Outer();
Outer.Inner1 inner1 = outer.new Innner1();
在方法内部定义的内部类前面不能有访问类型修饰符,就好像方法中定义的局部变量一样,但这种内部类的前面可以使用final或abstract修饰符。这种内部类对其他类是不可见的其他类无法引用这种内部类,但是这种内部类创建的实例对象可以传递给其他类访问。这种内部类必须是先定义,后使用,即内部类的定义代码必须出现在使用该类之前,这与方法中的局部变量必须先定义后使用的道理也是一样的。这种内部类可以访问方法体中的局部变量,但是,该局部变量前必须加final修饰符。
对于这些细节,只要在eclipse写代码试试,根据开发工具提示的各类错误信息就可以马上了解到。
在方法体内部还可以采用如下语法来创建一种匿名内部类,即定义某一接口或类的子类的同时,还创建了该子类的实例对象,无需为该子类定义名称:
public class Outer { public void start() { new Thread( new Runable(){ public void run(){}; } ).start(); } }
最后,在方法外部定义的内部类前面可以加上static关键字,从而成为Static Nested Class,它不再具有内部类的特性,所有,从狭义上讲,它不是内部类。Static Nested Class与普通类在运行时的行为和功能上没有什么区别,只是在编程引用时的语法上有一些差别,它可以定义成public、protected、默认的、private等多种类型,而普通类只能定义成public和默认的这两种类型。在外面引用Static Nested Class类的名称为“外部类名.内部类名”。在外面不需要创建外部类的实例对象,就可以直接创建Static Nested Class,例如,假设Inner是定义在Outer类中的Static Nested Class,那么可以使用如下语句创建Inner类:
Outer.Inner inner = newOuter.Inner();
由于static Nested Class不依赖于外部类的实例对象,所以,static Nested Class能访问外部类的非static成员变量(不能直接访问,需要创建外部类实例才能访问非静态变量)。当在外部类中访问Static Nested Class时,可以直接使用Static Nested Class的名字,而不需要加上外部类的名字了,在Static Nested Class中也可以直接引用外部类的static的成员变量,不需要加上外部类的名字。
在静态方法中定义的内部类也是Static Nested Class,这时候不能在类前面加static关键字,静态方法中的Static Nested Class与普通方法中的内部类的应用方式很相似,它除了可以直接访问外部类中的static的成员变量,还可以访问静态方法中的局部变量,但是,该局部变量前必须加final修饰符。
备注:首先根据你的印象说出你对内部类的总体方面的特点:例如,在两个地方可以定义,可以访问外部类的成员变量,不能定义静态成员,这是大的特点。然后再说一些细节方面的知识,例如,几种定义方式的语法区别,静态内部类,以及匿名内部类。
Static Nested Class是被声明为静态(static)的内部类,它可以不依赖于外部类实例被实例化。而通常的内部类需要在外部类实例化后才能实例化,其语法看起来挺诡异的,如下所示。
package com.bjsxt; /** * 扑克类(一副扑克) * @author sxt * */ public class Poker { private static String[] suites = {"黑桃", "红桃", "草花", "方块"}; private static int[] faces = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; private Card[] cards; /** * 构造器 */ public Poker() { cards = new Card[52]; for(int i = 0; i < suites.length; i++) { for(int j = 0; j < faces.length; j++) { cards[i * 13 + j] = new Card(suites[i], faces[j]); } } } /** * 洗牌 (随机乱序) */ public void shuffle() { for(int i = 0, len = cards.length; i < len; i++) { int index = (int) (Math.random() * len); Card temp = cards[index]; cards[index] = cards[i]; cards[i] = temp; } } /** * 发牌 * @param index 发牌的位置 */ public Card deal(int index) { return cards[index]; } /** * 卡片类(一张扑克) * [内部类] * @author sxt */ public class Card { private String suite; // 花色 private int face; // 点数 public Card(String suite, int face) { this.suite = suite; this.face = face; } @Override public String toString() { String faceStr = ""; switch(face) { case 1: faceStr = "A"; break; case 11: faceStr = "J"; break; case 12: faceStr = "Q"; break; case 13: faceStr = "K"; break; default: faceStr = String.valueOf(face); } return suite + faceStr; } } }
测试类:
package com.bjsxt; class PokerTest { public static void main(String[] args) { Poker poker = new Poker(); poker.shuffle(); // 洗牌 Poker.Card c1 = poker.deal(0); // 发第一张牌 // 对于非静态内部类Card // 只有通过其外部类Poker对象才能创建Card对象 Poker.Card c2 = poker.new Card("红心", 1); // 自己创建一张牌 System.out.println(c1); // 洗牌后的第一张 System.out.println(c2); // 打印: 红心A } }
答:都不能。抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛盾的。本地方法是由本地代码(如C代码)实现的方法,而抽象方法是没有实现的,也是矛盾的。synchronized和方法的实现细节有关,抽象方法不涉及实现细节,因此也是相互矛盾的。
答:静态变量是被static修饰符修饰的变量,也称为类变量,它属于类,不属于类的任何一个对象,一个类不管创建多少个对象,静态变量在内存中有且仅有一个拷贝;实例变量必须依存于某一实例,需要先创建对象然后通过对象才能访问到它,静态变量可以实现让多个对象共享内存。两者的相同点:都有默认值而且在类的任何地方都可以调用。在Java开发中,上下文类和工具类中通常会有大量的静态成员。
答:不可以,静态方法只能访问静态成员,因为非静态方法的调用要先创建对象,因此在调用静态方法时可能对象并没有被初始化。
package com.bjsxt; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class MyUtil { private MyUtil() { throw new AssertionError(); } public static <T> T clone(T obj) throws Exception { ByteArrayOutputStream bout = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bout); oos.writeObject(obj); ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bin); return (T) ois.readObject(); // 说明:调用ByteArrayInputStream或ByteArrayOutputStream对象的close方法没有任何意义 // 这两个基于内存的流只要垃圾回收器清理对象就能够释放资源 } }
答:有两种方式:
1.实现Cloneable接口并重写Object类中的clone()方法;
2.实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆,代码如下。
下面是测试代码:
package com.bjsxt; import java.io.Serializable; /** * 人类 * @author sxt */ class Person implements Serializable { private static final long serialVersionUID = -9102017020286042305L; private String name; // 姓名 private int age; // 年龄 private Car car; // 座驾 public Person(String name, int age, Car car) { this.name = name; this.age = age; this.car = car; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + ", car=" + car + "]"; } } /** * 小汽车类 * @author sxt */ class Car implements Serializable { private static final long serialVersionUID = -5713945027627603702L; private String brand; // 品牌 private int maxSpeed; // 最高时速 public Car(String brand, int maxSpeed) { this.brand = brand; this.maxSpeed = maxSpeed; } public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } public int getMaxSpeed() { return maxSpeed; } public void setMaxSpeed(int maxSpeed) { this.maxSpeed = maxSpeed; } @Override public String toString() { return "Car [brand=" + brand + ", maxSpeed=" + maxSpeed + "]"; } } class CloneTest { public static void main(String[] args) { try { Person p1 = new Person("Hao LUO", 33, new Car("Benz", 300)); Person p2 = MyUtil.clone(p1); // 深度克隆 p2.getCar().setBrand("BYD"); // 修改克隆的Person对象p2关联的汽车对象的品牌属性 // 原来的Person对象p1关联的汽车不会受到任何影响 // 因为在克隆Person对象时其关联的汽车对象也被克隆了 System.out.println(p1); } catch (Exception e) { e.printStackTrace(); } } }
注意:基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对象是否支持序列化,这项检查是编译器完成的,不是在运行时抛出异常,这种是方案明显优于使用Object类的clone方法克隆对象。
答:接口可以继承接口。抽象类可以实现(implements)接口,抽象类可以继承具体类。抽象类中可以有静态的main方法。
备注:只要明白了接口和抽象类的本质和作用,这些问题都很好回答,你想想,如果你是java语言的设计者,你是否会提供这样的支持,如果不提供的话,有什么理由吗?如果你没有道理不提供,那答案就是肯定的了。
只有记住抽象类与普通类的唯一区别就是不能创建实例对象和允许有abstract方法。
答:可以,但一个源文件中最多只能有一个公开类(public class)而且文件名必须和公开类的类名完全保持一致。
答:可以继承其他类或实现其他接口,在Swing编程中常用此方式来实现事件监听和回调。 但是有一点需要注意,它只能继承一个类或一个接口。
答:一个内部类对象可以访问创建它的外部类对象的成员,包括私有成员。如果要访问外部类的局部变量,此时局部变量必须使用final修饰,否则无法访问。
(1) 修饰类:表示该类不能被继承;
(2) 修饰方法:表示方法不能被重写但是允许重载;
(3) 修饰变量:表示变量只能一次赋值以后值不能被修改(常量);
(4) 修饰对象:对象的引用地址不能变,但是对象的初始化值可以变。
package com.bjsxt; class A{ static{ System.out.print("1"); } public A(){ System.out.print("2"); } } class B extends A{ static{ System.out.print("a"); } public B(){ System.out.print("b"); } } public class Hello{ public static void main(String[] args){ A ab = new B(); ab = new B(); } }
答:执行结果:1a2b2b。创建对象时构造器的调用顺序是:先初始化静态成员,然后调用父类构造器,再初始化非静态成员,最后调用自身构造器。
考点:静态代码块优先级 > 构造方法的优先级如果再加一个普通代码块,优先顺序如下:静态代码块>普通代码块>构造方法
1 ) 如何将字符串转换为基本数据类型?
2 ) 如何将基本数据类型转换为字符串?
答:
1 ) 调用基本数据类型对应的包装类中的方法parseXXX(String)或valueOf(String)即可返回相应基本类型;
2 ) 一种方法是将基本数据类型与空字符串(””)连接(+)即可获得其所对应的字符串;另一种方法是调用String 类中的valueOf(…)方法返回相应字符串
答:方法很多,可以自己写实现也可以使用String或StringBuffer / StringBuilder中的方法。有一道很常见的面试题是用递归实现字符串反转,代码如下所示:
package com.bjsxt; public class A{ public static String reverse(String originStr) { if(originStr == null || originStr.length() <= 1) return originStr; return reverse(originStr.substring(1)) + originStr.charAt(0); } }
答:代码如下所示:
String s1 = "你好";
String s2 = newString(s1.getBytes("GB2312"), "ISO-8859-1");
在String类的构造方法当中,存在一个字符集设置的方法,具体如下:
1 ) 如何取得年月日、小时分钟秒?
2 ) 如何取得从1970年1月1日0时0分0秒到现在的毫秒数?
3 ) 如何取得某月的最后一天?
4 ) 如何格式化日期?
答:操作方法如下所示:
1 ) 创建java.util.Calendar 实例,调用其get()方法传入不同的参数即可获得参数所对应的值
2 ) 以下方法均可获得该毫秒数:
Calendar.getInstance().getTimeInMillis();
time.getActualMaximum(Calendar.DAY_OF_MONTH);
4 ) 利用java.text.DataFormat 的子类(如SimpleDateFormat类)中的format(Date)方法可将日期格式化。
package com.bjsxt; import java.util.Calendar; public class YesterdayCurrent { public static void main(String[] args){ Calendar cal = Calendar.getInstance(); cal.add(Calendar.DATE, -1); System.out.println(cal.getTime()); } }
在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中
1)Class类:代表一个类
2)Field 类:代表类的成员变量(属性)
3)Method类:代表类的成员方法
4)Constructor 类:代表类的构造方法
5)Array类:提供了动态创建数组,以及访问数组的元素的静态方法
Class类是Java 反射机制的起源和入口,用于获取与类相关的各种信息,提供了获取类信息的相关方法。Class类继承自Object类
Class类是所有类的共同的图纸。每个类有自己的对象,好比图纸和实物的关系;每个类也可看做是一个对象,有共同的图纸Class,存放类的 结构信息,能够通过相应方法取出相应信息:类的名字、属性、方法、构造方法、父类和接口
方 法 | 示 例 |
对象名 .getClass() |
String str="bdqn";
Class clazz = str.getClass();
|
对象名 .getSuperClass() |
Student stu = new Student();
Class c1 = stu.getClass();
Class c2 = stu.getSuperClass();
|
Class.forName() |
Class clazz = Class.forName("java.lang.Object");
Class.forName("oracle.jdbc.driver.OracleDriver");
|
类名.class |
类名.class
Class c2 = Student.class;
Class c2 = int.class
|
包装类.TYPE |
包装类.TYPE
Class c2 = Boolean.TYPE;
|
1)使用场合
在编译时根本无法知道该对象或类可能属于哪些类,程序只依靠运行时信息来发现该对象和类的真实信息。
2)主要作用
通过反射可以使程序代码访问装载到JVM 中的类的内部信息,获取已装载类的属性信息,获取已装载类的方法,获取已装载类的构造方法信息
3)反射的优点
反射提高了Java程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程序创建和控制任何类的对象,无需提前硬编码目标类;反射是其它一些常用语言,如C、C++、Fortran 或者Pascal等都不具备的
4) Java反射技术应用领域很广,如软件测试等;许多流行的开源框架例如Struts、Hibernate、Spring在实现过程中都采用了该技术
5)反射的缺点
性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此Java反射机制主要应用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用。
使用反射会模糊程序内部逻辑:程序人员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂。
面向对象设计原则是面向对象设计的基石,面向对象设计质量的依据和保障,设计模式是面向对象设计原则的经典应用
1)单一职责原则SRP
2)开闭原则OCP
3)里氏替代原则LSP
4)依赖注入原则DIP
5)接口分离原则ISP
6)迪米特原则LOD
7)组合/聚合复用原则CARP
8)开闭原则具有理想主义的色彩,它是面向对象设计的终极目标。其他设计原则都可以看作是开闭原则的实现手段或方法

String相关:
String str1="hello"; String str2=new String("hello"); System.out.println(str1==str2);
A. | true |
B. | false |
C. | hello |
D. | he |
答案:B
分析:str1没有使用new关键字,在堆中没有开辟空间,其值”hello”在常量池中,str2使用new关键字创建了一个对象,在堆中开辟了空间,”==”比较的是对象的引用,即内存地址,所以str1与str2两个对象的内存地址是不相同的
|
A. | int16 |
B. | int32 |
C. | int |
D. | long |
答案:C
|
public class Example { String str=new String("good"); char [] ch={'a','b','c'}; public static void main(String[] args) { Example ex=new Example(); ex.change(ex.str, ex.ch); System.out.print(ex.str+"and"); System.out.print(ex.ch); } public void change(String str,char ch[]){ str="test ok"; ch[0]='g'; } }
A. | goodandabc |
B. | goodandgbc |
C. | test okandabc |
D. | test okandgbc |
答案:B
分析:在方法调用时,在change方法中对str的值进行修改,是将str指向了常量江池中的”test ok”,而主方法中的ex.str仍然指向的是常量池中的”good”。字符型数组在方法调用时,将主方法中ex.ch的引用传递给change方法中的ch,指向是堆中的同一堆空间,所以修改ch[0]的时候,ex.ch可以看到相同的修改后的结果。
|
String[] s=new String[10];
A. | s[10]为”” |
B. | s[9]为null |
C. | s[0]为未定义 |
D. | s.length为10 |
答案:BD
分析: 引用数据类型的默认值均为null
s.length数组的长度 |
思路说明:replaceAll方法的本质是使用正则表达式进行匹配,最终调用的其实是Matcher对象的replaceAll方法。
import java.util.regex.Matcher; import java.util.regex.Pattern; public class TestStringReplaceAll { public static void main(String[] args) { String str = "a1s2d3f4h5j6k7"; // 将字符串中的数字全部替换为0 System.out.println(replaceAll(str, "\\d", "0")); } /** * @param str:源字符串 * @param regex:正则表达式 * @param newStr:替换后的子字符串 * @return 返回替换成功后的字符串 */ public static String replaceAll(String str, String regex, String newStr) { Pattern pattern = Pattern.compile(regex); Matcher mathcer = pattern.matcher(str); String reslut = mathcer.replaceAll(newStr); return reslut; } }
String []a=new String[10];
则:a[0]~a[9]=null;
a.length=10;
如果是int[]a=new int[10];
则:a[0]~a[9]= (0)
a.length= (10)
答:不可以,因为String类有final修饰符,而final修饰的类是不能被继承的,实现细节不允许改变。
public final class String implements java.io.Serializable,
Comparable< String>, CharSequence
public class Solution { public boolean isAnagram(String s, String t) { if(s.length()!=t.length()) return false; int bit[] = new int[26]; for(int i=0;i<s.length();i++){ bit[s.charAt(i)-'a']++; } for(int i=0;i<s.length();i++){ if(--bit[t.charAt(i)-'a']<0) return false; } return true; } }
两个或一个,”abc”对应一个对象,这个对象放在字符串常量缓冲区,常量”abc”不管出现多少遍,都是缓冲区中的那一个。New String每写一遍,就创建一个新的对象,它一句那个常量”abc”对象的内容来创建出一个新String对象。如果以前就用过’abc’,这句代表就不会创建”abc”自己了,直接从缓冲区拿。
String str1=“hello”; Sring str2=“he”+new String(“llo”); Sysem.out.println(str1==str2)); Sysem.out.println(str.equal(str2));
false
true
import java.util.*; public class Test 6{ public static void main(String[] args) { for (int i = 0; i < 10; i++) { Integer k=new Integer(i); System.out.println(k+" Hello world"); } } }
0 Hello world
1 Hello world
2 Hello world
3 Hello world
4 Hello world
5 Hello world
6 Hello world
7 Hello world
8 Hello world
9 Hello world
A. | String类是final类故不可继承 |
B. | String类final类故可以继承 |
C. | String类不是final类故不可继承 |
D. | String;类不是final类故可以继承 |
答案:A
|
A. | String temp[ ] = new String{“a”,”b”,”c”}; |
B. | String temp[ ] = {“a”,”b”,”c”}; |
C. | String temp= {“a”,”b”,”c”}; |
D. | String[ ] temp = {“a”,”b”,”c”}; |
答案:BD
|
package com.bjsxt; public class Test { public static void main(String[] args) { String s1 = new String("Hello"); String s2 = new String("Hello"); System.out.print(s1 == s2); String s3 = "Hello"; String s4 = "Hello"; System.out.print(s3 == s4); s1 = s3; s2 = s4; System.out.print(s1 == s2); } }
A. | false true true |
B. | true false true |
C. | true true false |
D. | true true false |
答案:A
|
public class Test { public static void main(String[] args) { //方式一 int num=Integer.parseInt("123"); //方式二 int num2=Integer.valueOf("123"); System.out.println(num+" "+num2); } }
public class Test { public static void main(String[] args) { String result=reverse("abc"); System.out.println(result); } public static String reverse(String str){ StringBuilder result=new StringBuilder(""); char[] chArra=str.toCharArray(); for(int i=chArra.length-1;i>=0;i--){ char ch=chArra[i]; result.append(ch); } return result.toString(); } }
public classDemo1 { publicstaticvoid main(String[] args) { String s="I follow Bill Gate.Tom Gate.John Gate"; System.out.println(s); s=s.replaceAll("Gate","Gates"); System.out.println(s); } }
答: 不是 。Java中的基本数据类型只有8个:byte、short、int、long、float、double、char、boolean;除了基本类型(primitive type)和枚举类型(enumeration type),剩下的都是引用类型(reference type)。
答: Java 平台提供了两种类型的字符串:String和StringBuffer / StringBuilder
相同点:
它们都可以储存和操作字符串,同时三者都使用final修饰,都属于终结类不能派生子类,操作的相关方法也类似例如获取字符串长度等;
不同点:
其中String是只读字符串,也就意味着String引用的字符串内容是不能被改变的,而StringBuffer和StringBuilder类表示的字符串对象可以直接进行修改,在修改的同时地址值不会发生改变。StringBuilder是JDK 1.5中引入的,它和StringBuffer的方法完全相同,区别在于它是在单线程环境下使用的,因为它的所有方面都没有被synchronized修饰,因此它的效率也比StringBuffer略高。在此重点说明一下,String、StringBuffer、StringBuilder三者类型不一样,无法使用equals()方法比较其字符串内容是否一样!
补充1:有一个面试题问:有没有哪种情况用+做字符串连接比调用StringBuffer / StringBuilder对象的append方法性能更好?如果连接后得到的字符串在静态存储区中是早已存在的,那么用+做字符串连接是优于StringBuffer / StringBuilder的append方法的。
补充2:下面也是一个面试题,问程序的输出,看看自己能不能说出正确答案。
package com.bjsxt; public class smallT { public static void main(String[] args) { String a = "Programming"; String b = new String("Programming"); String c = "Program" + "ming"; System.out.println(a == b); System.out.println(a == c); System.out.println(a.equals(b)); System.out.println(a.equals(c)); System.out.println(a.intern() == b.intern()); } }
解析:
String类存在intern()方法,含义如下:返回字符串对象的规范化表示形式。它遵循以下规则:对于任意两个字符串 s 和 t,当且仅当 s.equals(t) 为 true 时,s.intern() == t.intern() 才为 true。
字符串比较分为两种形式,一种使用比较运算符”==”比较,他们比较的是各自的字符串在内存当中的地址值是否相同;一种是使用equals()方法进行比较,比较的是两个字符串的内容是否相同!

结果如下:
a == b-->false
a == c-->true
a.equals(b)-->true
a.equals(c)-->true
a.intern() == b.intern()-->true
答:1) 为了效率。若允许被继承,则其高度的被使用率可能会降低程序的性能。
2)为了安全。JDK中提供的好多核心类比如String,这类的类的内部好多方法的实现都不是java编程语言本身编写的,好多方法都是调用的操作系统本地的API,这就是著名的“本地方法调用”,也只有这样才能做事,这种类是非常底层的,和操作系统交流频繁的,那么如果这种类可以被继承的话,如果我们再把它的方法重写了,往操作系统内部写入一段具有恶意攻击性质的代码什么的,这不就成了核心病毒了么?不希望别人改,这个类就像一个工具一样,类的提供者给我们提供了, 就希望我们直接用就完了,不想让我们随便能改,其实说白了还是安全性,如果随便能改了,那么java编写的程序肯定就很不稳定,你可以保证自己不乱改, 但是将来一个项目好多人来做,管不了别人,再说有时候万一疏忽了呢?他也不是估计的, 所以这个安全性是很重要的,java和C++相比,优点之一就包括这一点。
1) 基本数据类型包括byte、short/char、int、long、float、double、boolean
2 ) java.lang.String类是引用数据类型,并且是final类型的,因此不可以继承这个类、不能修改这个类。为了提高效率节省空间,我们应该用StringBuffer类
答:不是对前面s指向空间内容的直接修改。
因为String被设计成不可变(immutable)类,所以它的所有对象都是不可变对象。在这段代码中,s原先指向一个String对象,内容是 "Hello",然后我们对s进行了+操作,那么s所指向的那个对象是否发生了改变呢?答案是没有。这时,s不指向原来那个对象了,而指向了另一个 String对象,内容为"Hello world!",原来那个对象还存在于内存之中,只是s这个引用变量不再指向它了。
通过上面的说明,我们很容易导出另一个结论,如果经常对字符串进行各种各样的修改,或者说,不可预见的修改,那么使用String来代表字符串的话会引起很大的内存开销。因为 String对象建立之后不能再改变,所以对于每一个不同的字符串,都需要一个String对象来表示。这时,应该考虑使用StringBuffer类,它允许修改,而不是每个不同的字符串都要生成一个新的对象。并且,这两种类的对象转换十分容易。
同时,我们还可以知道,如果要使用内容相同的字符串,不必每次都new一个String。例如我们要在构造器中对一个名叫s的String引用变量进行初始化,把它设置为初始值,应当这样做:
public class Demo { private String s; ... public Demo { s = "Initial Value"; } ... }
而非
s = new String("Initial Value");
后者每次都会调用构造器,生成新对象,性能低下且内存开销大,并且没有意义,因为String对象不可改变,所以对于内容相同的字符串,只要一个String对象来表示就可以了。也就说,多次调用上面的构造器创建多个对象,他们的String类型属性s都指向同一个对象。
上面的结论还基于这样一个事实:对于字符串常量,如果内容相同,Java认为它们代表同一个String对象。而用关键字new调用构造器,总是会创建一个新的对象,无论内容是否相同。
至于为什么要把String类设计成不可变类,是它的用途决定的。其实不只String,很多Java标准类库中的类都是不可变的。在开发一个系统的时候,我们有时候也需要设计不可变类,来传递一组相关的值,这也是面向对象思想的体现。不可变类有一些优点,比如因为它的对象是只读的,所以多线程并发访问也不会有任何问题。当然也有一些缺点,比如每个不同的状态都要一个对象来代表,可能会造成性能上的问题。所以Java标准类库还提供了一个可变版本,即 StringBuffer。
答:两个或一个,”xyz”对应一个对象,这个对象放在字符串常量缓冲区,常量”xyz”不管出现多少遍,都是缓冲区中的那一个。New String每写一遍,就创建一个新的对象,它一句那个常量”xyz”对象的内容来创建出一个新String对象。如果以前就用过’xyz’,这句代表就不会创建”xyz”自己了,直接从缓冲区拿。
答:对于如下代码:
String s1 = "a";
String s2 = s1 + "b";
String s3 = "a" + "b";
System.out.println(s2 == "ab");
System.out.println(s3 == "ab");
第一条语句打印的结果为false,第二条语句打印的结果为true,这说明javac编译可以对字符串常量直接相加的表达式进行优化,不必要等到运行期去进行加法运算处理,而是在编译时去掉其中的加号,直接将其编译成一个这些常量相连的结果。
题目中的第一行代码被编译器在编译时优化后,相当于直接定义一个”abcd”的字符串,所以,上面的代码应该只创建了一个String对象。
写如下两行代码,
String s = "a" + "b" + "c" + "d";
System.out.println(s == "abcd");
最终打印的结果应该为true。
集合:


1、Collection 接口存储一组不唯一,无序的对象
2、List 接口存储一组不唯一,有序(插入顺序)的对象
3、Set 接口存储一组唯一,无序的对象
4、Map接口存储一组键值对象,提供key到value的映射。Key无序,唯一。value不要求有序,允许重复。(如果只使用key存储,而不使用value,那就是Set)
相同点:
1)实现原理相同---底层都使用数组
2)功能相同---实现增删改查等操作的方法相似
3)都是长度可变的数组结构,很多情况下可以互用
不同点:
1)Vector是早期JDK版本提供,ArrayList是新版本替代Vector的
2)Vector线程安全,ArrayList重速度轻安全,线程非安全长度需增长时,Vector默认增长一倍,ArrayList增长50%
相同点:
两者都实现了List接口,都具有List中元素有序、不唯一的特点。
不同点:
ArrayList实现了长度可变的数组,在内存中分配连续空间。遍历元素和随机访问元素的效率比较高;

LinkedList采用链表存储方式。插入、删除元素时效率比较高

相同点:
实现原理相同,功能相同,底层都是哈希表结构,查询速度快,在很多情况下可以互用
不同点:
1、Hashtable是早期提供的接口,HashMap是新版JDK提供的接口
2、Hashtable继承Dictionary类,HashMap实现Map接口
3、Hashtable线程安全,HashMap线程非安全
4、Hashtable不允许null值,HashMap允许null值
1)哈希表的查询速度特别快,时间复杂度为O(1)。
2)HashMap、Hashtable、HashSet这些集合采用的是哈希表结构,需要用到hashCode哈希码,hashCode是一个整数值。
3)系统类已经覆盖了hashCode方法 自定义类如果要放入hash类集合,必须重写hashcode。如果不重写,调用的是Object的hashcode,而Object的hashCode实际上是地址。
4)向哈希表中添加数据的原理:当向集合Set中增加对象时,首先集合计算要增加对象的hashCode码,根据该值来得到一个位置用来存放当前对象,如在该位置没有一个对象存在的话,那么集合Set认为该对象在集合中不存在,直接增加进去。如果在该位置有一个对象存在的话,接着将准备增加到集合中的对象与该位置上的对象进行equals方法比较,如果该equals方法返回false,那么集合认为集合中不存在该对象,在进行一次散列,将该对象放到散列后计算出的新地址里。如果equals方法返回true,那么集合认为集合中已经存在该对象了,不会再将该对象增加到集合中了。
5)在哈希表中判断两个元素是否重复要使用到hashCode()和equals()。hashCode决定数据在表中的存储位置,而equals判断是否存在相同数据。
6) Y=K(X) :K是函数,X是哈希码,Y是地址
1)TreeSet集合,元素不允许重复且有序(自然顺序)
2)TreeSet采用树结构存储数据,存入元素时需要和树中元素进行对比,需要指定比较策略。
3)可以通过Comparable(外部比较器)和Comparator(内部比较器)来指定比较策略,实现了Comparable的系统类可以顺利存入TreeSet。自定义类可以实现Comparable接口来指定比较策略。
4)可创建Comparator接口实现类来指定比较策略,并通过TreeSet构造方法参数传入。这种方式尤其对系统类非常适用。
数组不是面向对象的,存在明显的缺陷,集合完全弥补了数组的一些缺点,比数组更灵活更实用,可大大提高软件的开发效率而且不同的集合框架类可适用于不同场合。具体如下:
1)数组的效率高于集合类.
2)数组能存放基本数据类型和对象,而集合类中只能放对象。
3)数组容量固定且无法动态改变,集合类容量动态改变。
4)数组无法判断其中实际存有多少元素,length只告诉了array的容量。
5)集合有多种实现方式和不同的适用场合,而不像数组仅采用顺序表方式。
6)集合以类的形式存在,具有封装、继承、多态等类的特性,通过简单的方法和属性调用即可实现各种复杂操作,大大提高软件的开发效率。
1)Collection是Java提供的集合接口,存储一组不唯一,无序的对象。它有两个子接口List和Set。
2)Java中还有一个Collections类,专门用来操作集合类 ,它提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。
A. | LinkedList继承自List |
B. | AbstractSet继承自Set |
C. | HashSet继承自AbstractSet |
D. | TreeMap继承自HashMap |
答案: C
分析:A:LinkedList实现List接口
B:AbstractSet实现Set接口
D:TreeMap继承AbstractMap
|
答:HashMap与Hashtable实现原理相同,功能相同,底层都是哈希表结构,查询速度快,在很多情况下可以互用
两者的主要区别如下
1、Hashtable是早期JDK提供的接口,HashMap是新版JDK提供的接口
2、Hashtable继承Dictionary类,HashMap实现Map接口
3、Hashtable线程安全,HashMap线程非安全
4、Hashtable不允许null值,HashMap允许null值
HashSet与HashMap的区别
1、HashSet底层是采用HashMap实现的。HashSet 的实现比较简单,HashSet 的绝大部分方法都是通过调用 HashMap 的方法来实现的,因此 HashSet 和 HashMap 两个集合在实现本质上是相同的。
2、HashMap的key就是放进HashSet中对象,value是Object类型的。
3、当调用HashSet的add方法时,实际上是向HashMap中增加了一行(key-value对),该行的key就是向HashSet增加的那个对象,该行的value就是一个Object类型的常量
答:Java中集合主要分为两种:Collection和Map。Collection是List和Set接口的父接口;ArrayList和LinkedList是List的实现类;HashSet和TreeSet是Set的实现类;LinkedHashSet是HashSet的子类。HashMap和TreeMap是Map的实现类;LinkedHashMap是HashMap的子类。
图中:虚线框中为接口,实线框中为类。

答:List 接口存储一组不唯一,有序(插入顺序)的对象。
Set 接口存储一组唯一,无序的对象。
Map接口存储一组键值对象,提供key到value的映射。key无序,唯一。value不要求有序,允许重复。(如果只使用key存储,而不使用value,那就是Set)。
A. | 0 |
B. | 1 |
C. | 2 |
D. | 3 |
答案:A
分析:已经指定了长度, 所以不扩容
|
A. | List Map |
B. | Set Map |
C. | List Set |
D. | List Map Set |
答案:C
分析:Map接口继承了java.lang.Object类,但没有实现任何接口.
|
public class Solution { public ListNode mergeTwoLists(ListNode l1, ListNode l2) { if (l1 == null || l2 == null) { return l1 != null ? l1 : l2; } ListNode head = l1.val < l2.val ? l1 : l2; ListNode other = l1.val >= l2.val ? l1 : l2; ListNode prevHead = head; ListNode prevOther = other; while (prevHead != null) { ListNode next = prevHead.next; if (next != null && next.val > prevOther.val) { prevHead.next = prevOther; prevOther = next; } if(prevHead.next==null){ prevHead.next=prevOther; break; } prevHead=prevHead.next; } return head; } }
/** Definition for singly-linked list. public class ListNode { int val; ListNode next; ListNode(int x) { val = x; } * } */ public class Solution { public ListNode reverseList(ListNode head) { if(head==null||head.next ==null) return head; ListNode prev = reverseList(head.next); head.next.next = head; head.next = null; return prev; } }
public class Solution { public List<List<Integer>> subsets (int[] nums) { List<List<Integer>> res = new ArrayList<ArrayList<Integer>>(); List<Integer> item = new ArrayList<Integer>(); if(nums.length == 0 || nums == null) return res; Arrays.sort(nums); //排序 dfs(nums, 0, item, res); //递归调用 res.add(new ArrayList<Integer>()); //最后加上一个空集 return res; } public static void dfs(int[] nums, int start, List<Integer> item, List<List<Integer>> res){ for(int i = start; i < nums.length; i ++){ item.add(nums[i]); //item是以整数为元素的动态数组,而res是以数组为元素的数组,在这一步,当item增加完元素后,item所有元素构成一个完整的子串,再由res纳入 res.add(new ArrayList<Integer>(item)); dfs(nums, i + 1, item, res); item.remove(item.size() - 1); } } }
A. | HashMap |
B. | ConcurrentHashMap |
C. | WeakHashMap |
D. | TreeMap |
答案:B
分析:
A,C,D都线程不安全,B线程安全,具有同步功能
|
A. | ArrayList |
B. | Linkedlist |
C. | tor |
D. | Collection |
答案:B
分析:
数组插入、删除效率差,排除A
tor不是java里面的数据结构,是一种网络路由技术;因此排除C
Collection 是集合的接口,不是某种数据结构;因此排除D
|
A. | LinkedHashMap |
B. | LinkedHashSet |
C. | LinkedList |
LinkedList
分析:
Stack是先进后出的线性结构;所以链表比较合适;不需要散列表的数据结构
|
答:1. Map的实现类有HashMap,LinkedHashMap,TreeMap
2. HashMap是有无序的,LinkedHashMap和TreeMap都是有序的(LinkedHashMap记录了添加数据的顺序;TreeMap默认是自然升序)
3. LinkedHashMap底层存储结构是哈希表+链表,链表记录了添加数据的顺序
4. TreeMap底层存储结构是二叉树,二叉树的中序遍历保证了数据的有序性
5. LinkedHashMap有序性能比较高,因为底层数据存储结构采用的哈希表
package com.bjsxt; import java.util.LinkedList; public class Stack { LinkedList list = new LinkedList(); public synchronized void push(Object x) { synchronized (list) { list.addLast(x); notify(); } } public synchronized Object pop() throws Exception{ synchronized(list){ if(list.size()<=0){ wait(); } return list.removeLast( ); } } }
答:将if( list.size() <= 0 )改成:while( list.size() <= 0 )
答:TreeSet要求存放的对象所属的类必须实现Comparable接口,该接口提供了比较元素的compareTo()方法,当插入元素时会 回调该方法比较元素的大小。TreeMap要求存放的键值对映射的键必须实现Comparable接口从而根据键对元素进行排序。Collections 工具类的sort方法有两种重载的形式,第一种要求传入的待排序容器中存放的对象比较实现Comparable接口以实现元素的比较;第二种不强制性的要求容器中的元素必须可比较,但是要求传入第二个参数,参数是Comparator接口的子类型(需要重写compare方法实现元素的比较),相当于一个临时定义的排序规则,其实就是是通过接口注入比较元素大小的算法,也是对回调模式的应用。
public class Test { public static void main(String[] args) { List<String> li1 = new ArrayList<String>(); li1.add("8"); li1.add("8"); li1.add("9"); li1.add("9"); li1.add("0"); System.out.println(li1); //方法:将List中数据取出来来存到Set中 HashSet<String> set = new HashSet<String>(); for(int i=0;i<li1.size();i++){ set.add(li1.get(i)); } System.out.println(set); } }
分析:Java中的java.util.Map的实现类
1、HashMap
2、Hashtable
3、LinkedHashMap
4、TreeMap
A. | 循环队列有队头和队尾两个指针,因此,循环队列是非线性结构 |
B. | 在循环队列中,只需要队头指针就能反映队列中元素的动态变化情况 |
C. | 在循环队列中,只需要队尾指针就能反映队列中元素的动态变化情况 |
D. | 在循环队列中元素的个数是由队头指针和队尾指针共同决定的 |
答案:D
分析:循环队列中元素的个数是由队首指针和队尾指针共同决定的,元素的动态变化也是通过队首指针和队尾指针来反映的,当队首等于队尾时,队列为空。
|
答:List、Set 的父接口是Collection,Map 不是其子接口,而是与Collection接口是平行关系,互不包含。

Map是键值对映射容器,与List和Set有明显的区别,而Set存储的零散的元素且不允许有重复元素(数学中的集合也是如此),List是线性结构的容器,适用于按数值索引访问元素的情形。
答:ArrayList 和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector由于使用了synchronized 方法(线程安全),通常性能上较ArrayList 差,而LinkedList 使用双向链表实现存储(将内存中零散的内存单元通过附加的引用关联起来,形成一个可以按序号索引的线性结构,这种链式存储方式与数组的连续存储方式相比,其实对内存的利用率更高),按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。Vector属于遗留容器(早期的JDK中使用的容器,除此之外Hashtable、Dictionary、BitSet、Stack、Properties都是遗留容器),现在已经不推荐使用,但是由于ArrayList和LinkedListed都是非线程安全的,如果需要多个线程操作同一个容器,那么可以通过工具类Collections中的synchronizedList方法将其转换成线程安全的容器后再使用(这其实是装潢模式最好的例子,将已有对象传入另一个类的构造器中创建新的对象来增加新功能)。
补充:遗留容器中的Properties类和Stack类在设计上有严重的问题,Properties是一个键和值都是字符串的特殊的键值对映射,在设计上应该是关联一个Hashtable并将其两个泛型参数设置为String类型,但是Java API中的Properties直接继承了Hashtable,这很明显是对继承的滥用。这里复用代码的方式应该是HAS-A关系而不是IS-A关系,另一方面容器都属于工具类,继承工具类本身就是一个错误的做法,使用工具类最好的方式是HAS-A关系(关联)或USE-A关系(依赖) 。同理,Stack类继承Vector也是不正确的。
答:List以特定索引来存取元素,可有重复元素。
Set不能存放重复元素(用对象的equals()方法来区分元素是否重复) 。Map保存键值对(key-value pair)映射,映射关系可以是一对一或多对一。Set和Map容器都有基于哈希存储和排序树(红黑树)的两种实现版本,基于哈希存储的版本理论存取时间复杂度为O(1),而基于排序树版本的实现在插入或删除元素时会按照元素或元素的键(key)构成排序树从而达到排序和去重的效果。
答:TreeSet要求存放的对象所属的类必须实现Comparable接口,该接口提供了比较元素的compareTo()方法,当插入元素时会回调该方法比较元素的大小。
TreeMap要求存放的键值对映射的键必须实现Comparable接口从而根据键对元素进行排序。
Collections工具类的sort方法有两种重载的形式,第一种要求传入的待排序容器中存放的对象比较实现Comparable接口以实现元素的比较;第二种不强制性的要求容器中的元素必须可比较,但是要求传入第二个参数,参数是Comparator接口的子类型 (需要重写compare方法实现元素的比较),相当于一个临时定义的排序规则,其实就是是通过接口注入比较元素大小的算法,也是对回调模式的应用。
例子1:
Student.java
package com.bjsxt; public class Student implements Comparable<Student> { private String name; // 姓名 private int age; // 年龄 public Student(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Student [name=" + name + ", age=" + age + "]"; } @Override public int compareTo(Student o) { return this.age - o.age; // 比较年龄(年龄的升序) } }
Test01.java
package com.bjsxt; import java.util.Set; import java.util.TreeSet; class Test01 { public static void main(String[] args) { Set<Student> set = new TreeSet<>(); // Java 7的钻石语法(构造器后面的尖括号中不需要写类型) set.add(new Student("Hao LUO", 33)); set.add(new Student("XJ WANG", 32)); set.add(new Student("Bruce LEE", 60)); set.add(new Student("Bob YANG", 22)); for(Student stu : set) { System.out.println(stu); } // 输出结果: // Student [name=Bob YANG, age=22] // Student [name=XJ WANG, age=32] // Student [name=Hao LUO, age=33] // Student [name=Bruce LEE, age=60] } }
例子2:
Student.java
package com.bjsxt; public class Student { private String name; // 姓名 private int age; // 年龄 public Student(String name, int age) { this.name = name; this.age = age; } /** * 获取学生姓名 */ public String getName() { return name; } /** * 获取学生年龄 */ public int getAge() { return age; } @Override public String toString() { return "Student [name=" + name + ", age=" + age + "]"; } }
Test02.java
package com.bjsxt; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; class Test02 { public static void main(String[] args) { List<Student> list = new ArrayList<>(); // Java 7的钻石语法(构造器后面的尖括号中不需要写类型) list.add(new Student("Hao LUO", 33)); list.add(new Student("XJ WANG", 32)); list.add(new Student("Bruce LEE", 60)); list.add(new Student("Bob YANG", 22)); // 通过sort方法的第二个参数传入一个Comparator接口对象 // 相当于是传入一个比较对象大小的算法到sort方法中 // 由于Java中没有函数指针、仿函数、委托这样的概念 // 因此要将一个算法传入一个方法中唯一的选择就是通过接口回调 Collections.sort(list, new Comparator<Student> () { @Override public int compare(Student o1, Student o2) { return o1.getName().compareTo(o2.getName()); // 比较学生姓名 } }); for(Student stu : list) { System.out.println(stu); } // 输出结果: // Student [name=Bob YANG, age=22] // Student [name=Bruce LEE, age=60] // Student [name=Hao LUO, age=33] // Student [name=XJ WANG, age=32] } }
多线程:
public static void main(String[] args) { Thread t=new Thread(){ public void run(){ pong(); } }; t.run(); System.out.println("ping"); } static void pong(){ System.out.println("pong"); }
A. | pingpong |
B. | pongping |
C. | pingpong和pongping都有可能 |
D. | 都不输出 |
答案:B
分析:启动线程需要调用start()方法,而t.run()方法,则是使用对象名.分析:启动线程需要调用start()方法,而t.run()方法,则是使用对象名.
|
A. | public class X implements Runnable{public void run() {……}} |
B. | public class X extends Thread{public void run() {……}} |
C. | public class X extends Thread{public int run() {……}} |
D. | public class X implements Runnable{protected void run() {……}} |
答案:AB
分析: 继承Thread和实现Runable接口
|
作用:
要编写一个多线程安全(Thread-safe)的程序是困难的,为了让线程共享资源,必须小心地对共享资源进行同步,同步带来一定的效能延迟,而另一方面,在处理同步的时候,又要注意对象的锁定与释放,避免产生死结,种种因素都使得编写多线程程序变得困难。
尝试从另一个角度来思考多线程共享资源的问题,既然共享资源这么困难,那么就干脆不要共享,何不为每个线程创造一个资源的复本。将每一个线程存取数据的行为加以隔离,实现的方法就是给予每个线程一个特定空间来保管该线程所独享的资源。
比如:在Hibernate中的Session就有使用。
ThreadLocal的原理
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。
答:悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。
两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果经常产生冲突,上层应用会不断的进行retry,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。
答:当多个线程访问同一个数据时,容易出现线程安全问题,需要某种方式来确保资源在某一时刻只被一个线程使用。需要让线程同步,保证数据安全线程同步的实现方案: 同步代码块和同步方法,均需要使用synchronized关键字
同步代码块:public void makeWithdrawal(int amt) {
synchronized (acct) { }
}
同步方法:public synchronized void makeWithdrawal(int amt) { }
线程同步的好处:解决了线程安全问题
线程同步的缺点:性能下降,可能会带来死锁
方式1:继承Java.lang.Thread类,并覆盖run() 方法。优势:编写简单;劣势:无法继承其它父类
public class ThreadDemo1 { public static void main(String args[]) { MyThread1 t = new MyThread1(); t.start(); while (true) { System.out.println("兔子领先了,别骄傲"); } } } class MyThread1 extends Thread { public void run() { while (true) { System.out.println("乌龟领先了,加油"); } } }
方式2:实现Java.lang.Runnable接口,并实现run()方法。优势:可继承其它类,多线程可共享同一个Thread对象;劣势:编程方式稍微复杂,如需访问当前线程,需调用Thread.currentThread()方法
public class ThreadDemo2 { public static void main(String args[]) { MyThread2 mt = new MyThread2(); Thread t = new Thread(mt); t.start(); while (true) { System.out.println("兔子领先了,加油"); } } } class MyThread2 implements Runnable { public void run() { while (true) { System.out.println("乌龟超过了,再接再厉"); } } }
答:wait方法是线程通信的方法之一,必须用在 synchronized方法或者synchronized代码块中,否则会抛出异常,这就涉及到一个“锁”的概念,而wait方法必须使用上锁的对象来调用,从而持有该对象的锁进入线程等待状态,直到使用该上锁的对象调用notify或者notifyAll方法来唤醒之前进入等待的线程,以释放持有的锁。
答:线程是一个动态执行的过程,它有一个从产生到死亡的过程,共五种状态:
新建(new Thread)
当创建Thread类的一个实例(对象)时,此线程进入新建状态(未被启动)
例如:Thread t1=new Thread();
就绪(runnable)
线程已经被启动,正在等待被分配给CPU时间片,也就是说此时线程正在就绪队列中排队等候得到CPU资源。例如:t1.start();
运行(running)
线程获得CPU资源正在执行任务(run()方法),此时除非此线程自动放弃CPU资源或者有优先级更高的线程进入,线程将一直运行到结束。
死亡(dead)
当线程执行完毕或被其它线程杀死,线程就进入死亡状态,这时线程不可能再进入就绪状态等待执行。
自然终止:正常运行run()方法后终止
异常终止:调用stop()方法让一个线程终止运行
堵塞(blocked)
由于某种原因导致正在运行的线程让出CPU并暂停自己的执行,即进入堵塞状态。
正在睡眠:用sleep(long t) 方法可使线程进入睡眠方式。一个睡眠着的线程在指定的时间过去可进入就绪状态。
正在等待:调用wait()方法。(调用motify()方法回到就绪状态)
被另一个线程所阻塞:调用suspend()方法。(调用resume()方法恢复)
A. | sleep() |
B. | Suspend() |
C. | wait() |
D. | yield() |
答案:D
分析:yield会是线程进入就绪状态
|
答:不能。虽然volatile提供了同步的机制,但是知识一种弱的同步机制,如需要强线程安全,还需要使用synchronized。
Java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程。当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。
一、volatile的内存语义是:
当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值立即刷新到主内存中。
当读一个volatile变量时,JMM会把该线程对应的本地内存设置为无效,直接从主内存中读取共享变量。
二、volatile底层的实现机制
如果把加入volatile关键字的代码和未加入volatile关键字的代码都生成汇编代码,会发现加入volatile关键字的代码会多出一个lock前缀指令。
1 、重排序时不能把后面的指令重排序到内存屏障之前的位置
2、使得本CPU的Cache写入内存
3、写入动作也会引起别的CPU或者别的内核无效化其Cache,相当于让新写入的值对别的线程可见。
(1) 继承Thread类
public class java_thread extends Thread{ public static void main(String args[]) { new java_thread().run(); System.out.println("main thread run "); } public synchronized void run() { System.out.println("sub thread run "); } }
(2) 实现Runnable接口
public class java_thread implements Runnable{ public static void main(String args[]) { new Thread(new java_thread()).start(); System.out.println("main thread run "); } public void run() { System.out.println("sub thread run "); } }
在Executor框架下,利用Executors的静态方法可以创建三种类型的常用线程池:
1)FixedThreadPool这个线程池可以创建固定线程数的线程池。
2)SingleThreadExecutor是使用单个worker线程的Executor。
3)CachedThreadPool是一个”无限“容量的线程池,它会根据需要创建新线程。
A. | sleep是线程类(Thread)的方法,wait是Object类的方法 |
B. | Sleep不释放对象锁,wait放弃对象锁 |
C. | Sleep暂停线程、但监控状态任然保持,结束后会自动恢复 |
D. | Wait后进入等待锁定池,只针对此对象发出notify方法后获取对象锁进入运行状态。 |
答案:D
分析:针对此对象的notify方法后获取对象锁并进入就绪状态,而不是运行状态。另外针对此对象的notifyAll方法后也可能获取对象锁并进入就绪状态,而不是运行状态
|
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
区别 | 进程 | 线程 |
根本区别 | 作为资源分配的单位 | 调度和执行的单位 |
开销 | 每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销。 | 线程可以看成时轻量级的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开销小。 |
所处环境 | 系统在运行的时候会为每个进程分配不同的内存区域 | 除了CPU之外,不会为线程分配内存(线程所使用的资源是它所属的进程的资源),线程组只能共享资源 |
分配内存 | 系统在运行的时候会为每个进程分配不同的内存区域 | 除了CPU之外,不会为线程分配内存(线程所使用的资源是它所属的进程的资源),线程组只能共享资源 |
包含关系 | 没有线程的进程是可以被看作单线程的,如果一个进程内拥有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的。 | 线程是进程的一部分,所以线程有的时候被称为是轻权进程或者轻量级进程。 |
A. | Lock |
B. | Synchronized |
C. | Volatile |
答案:C
|
答:用一个for循环创建线程对象,同时调用wait()方法,让所有线程等待;直到最后一个线程也准备就绪后,调用notifyAll(), 同时启动所有线程。
比如:给你n个赛车,让他们都在起跑线上就绪后,同时出发,Java多线程如何写代码?
思路是,来一辆赛车就加上一把锁,并修改对应的操作数,如果没有全部就绪就等待,并释放锁,直到最后一辆赛车到场后唤醒所有的赛车线程。代码参考如下:
public class CarCompetion { // 参赛赛车的数量 protected final int totalCarNum = 10; // 当前在起跑线的赛车数量 protected int nowCarNum = 0; }
public class Car implements Runnable{ private int carNum; private CarCompetion competion = null; public Car(int carNum, CarCompetion competion) { this.carNum = carNum; this.competion = competion; } @Override public void run() { synchronized (competion) { competion.nowCarNum++; while (competion.nowCarNum < competion.totalCarNum) { try { competion.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } competion.notifyAll(); } startCar(); } private void startCar() { System.out.println("Car num " + this.carNum + " start to run."); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Car num " + this.carNum + " get to the finish line."); } }
public static void main(String[] args) { CarCompetion carCompetion = new CarCompetion(); final ExecutorService carPool = Executors.newFixedThreadPool(carCompetion.totalCarNum); for (int i = 0; i < carCompetion.totalCarNum; i++) { carPool.execute(new Car(i, carCompetion)); }
答:1.如果数据将在线程间共享。例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。
2.当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。
3.举个例子: 打电话是同步 发消息是异步
答:sleep是线程类(Thread)的方法;作用是导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复;调用sleep()不会释放对象锁。
wait是Object类的方法;对此对象调用wait方法导致本线程放弃对象锁,进入等 待此对象的等待锁定池。只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池,准备获得对象锁进行运行状态。
A. | 由调度程序为进程分配CPU |
B. | 建立一个进程控制块 |
C. | 为进程分配内存 |
D. | 为进程分配文件描述符 |
答案:BC
|
A. | 针对计数器,可以使用原子加 |
B. | 只有一个生产者和一个消费者,那么就可以做到免锁访问环形缓冲区(Ring Buffer) |
C. | RCU(Read-Copy-Update),新旧副本切换机制,对于旧副本可以采用延迟释放的做法 |
D. | CAS(Compare-and-Swap),如无锁栈,无锁队列等待 |
答案:D
分析:A 这方法虽然不太好,但是常见
B ProducerConsumerQueue就是这个,到处都是
C linux kernel里面大量使用
D 本质上其实就是乐观锁,操作起来很困难。。单生产者多消费者或者多生产者单消费者的情况下比较常见,也不容易遇到ABA问题。
|
答:① sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;
② 线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态;
③ sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常;
④ sleep()方法比yield()方法(跟操作系统相关)具有更好的可移植性。
答:不能。其它线程只能访问该对象的非同步方法,同步方法则不能进入。 只有等待当前线程执行完毕释放锁资源之后,其他线程才有可能进行执行该同步方法!
延伸 对象锁分为三种:共享资源、this、当前类的字节码文件对象
答:1. wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;
2. sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException 异常;
3. notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且与优先级无关;
4. notityAll():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争;
5. JDK 1.5通过Lock接口提供了显式(explicit)的锁机制,增强了灵活性以及对线程的协调。Lock接口中定义了加锁(lock())和解锁(unlock())的方法,同时还提供了newCondition()方法来产生用于线程之间通信的Condition对象;
6. JDK 1.5还提供了信号量(semaphore)机制,信号量可以用来限制对某个共享资源进行访问的线程的数量。在对资源进行访问之前,线程必须得到信号量的许可(调用Semaphore对象的acquire()方法);在完成对资源的访问后,线程必须向信号量归还许可(调用Semaphore对象的release()方法)。
下面的例子演示了100个线程同时向一个银行账户中存入1元钱,在没有使用同步机制和使用同步机制情况下的执行情况。
银行账户类:
package com.bjsxt; /** * 银行账户 * @author sxt * */ public class Account { private double balance; // 账户余额 /** * 存款 * @param money 存入金额 */ public void deposit(double money) { double newBalance = balance + money; try { Thread.sleep(10); // 模拟此业务需要一段处理时间 } catch(InterruptedException ex) { ex.printStackTrace(); } balance = newBalance; } /** * 获得账户余额 */ public double getBalance() { return balance; } }
存钱线程类:
package com.bjsxt; /** * 存钱线程 * @author sxt李端阳 * */ public class AddMoneyThread implements Runnable { private Account account; // 存入账户 private double money; // 存入金额 public AddMoneyThread(Account account, double money) { this.account = account; this.money = money; } @Override public void run() { account.deposit(money); } }
测试类:
package com.bjsxt; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Test01 { public static void main(String[] args) { Account account = new Account(); ExecutorService service = Executors.newFixedThreadPool(100); for(int i = 1; i <= 100; i++) { service.execute(new AddMoneyThread(account, 1)); } service.shutdown(); while(!service.isTerminated()) {} System.out.println("账户余额: " + account.getBalance()); } }
在没有同步的情况下,执行结果通常是显示账户余额在10元以下,出现这种状况的原因是,当一个线程A试图存入1元的时候,另外一个线程B也能够进入存款的方法中,线程B读取到的账户余额仍然是线程A存入1元钱之前的账户余额,因此也是在原来的余额0上面做了加1元的操作,同理线程C也会做类似的事情,所以最后100个线程执行结束时,本来期望账户余额为100元,但实际得到的通常在10元以下。解决这个问题的办法就是同步,当一个线程对银行账户存钱时,需要将此账户锁定,待其操作完成后才允许其他的线程进行操作,代码有如下几种调整方案:
1. 在银行账户的存款(deposit)方法上同步(synchronized)关键字
package com.bjsxt; /** * 银行账户 * @author SXT李端阳 */ public class Account { private double balance; // 账户余额 /** * 存款 * @param money 存入金额 */ public synchronized void deposit(double money) { double newBalance = balance + money; try { Thread.sleep(10); // 模拟此业务需要一段处理时间 } catch(InterruptedException ex) { ex.printStackTrace(); } balance = newBalance; } /** * 获得账户余额 */ public double getBalance() { return balance; } }
2. 在线程调用存款方法时对银行账户进行同步
package com.bjsxt; /** * 存钱线程 * @author SXT * */ public class AddMoneyThread implements Runnable { private Account account; // 存入账户 private double money; // 存入金额 public AddMoneyThread(Account account, double money) { this.account = account; this.money = money; } @Override public void run() { synchronized (account) { account.deposit(money); } } }
3. 通过JDK 1.5显示的锁机制,为每个银行账户创建一个锁对象,在存款操作进行加锁和解锁的操作
package com.bjsxt; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 银行账户 * * @author SXT李端阳 * */ public class Account { private Lock accountLock = new ReentrantLock(); private double balance; // 账户余额 /** * 存款 * * @param money * 存入金额 */ public void deposit(double money) { accountLock.lock(); try { double newBalance = balance + money; try { Thread.sleep(10); // 模拟此业务需要一段处理时间 } catch (InterruptedException ex) { ex.printStackTrace(); } balance = newBalance; } finally { accountLock.unlock(); } } /** * 获得账户余额 */ public double getBalance() { return balance; } }
按照上述三种方式对代码进行修改后,重写执行测试代码Test01,将看到最终的账户余额为100元。
答:Java 5以前实现多线程有两种实现方法:一种是继承Thread类;另一种是实现Runnable接口。两种方式都要通过重写run()方法来定义线程的行为,推荐使用后者,因为Java中的继承是单继承,一个类有一个父类,如果继承了Thread类就无法再继承其他类了,同时也可以实现资源共享,显然使用Runnable接口更为灵活。
补充:Java 5以后创建线程还有第三种方式:实现Callable接口,该接口中的call方法可以在线程执行结束时产生一个返回值,代码如下所示:
package com.bjsxt; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; class MyTask implements Callable<Integer> { private int upperBounds; public MyTask(int upperBounds) { this.upperBounds = upperBounds; } @Override public Integer call() throws Exception { int sum = 0; for(int i = 1; i <= upperBounds; i++) { sum += i; } return sum; } } public class Test { public static void main(String[] args) throws Exception { List<Future<Integer>> list = new ArrayList<>(); ExecutorService service = Executors.newFixedThreadPool(10); for(int i = 0; i < 10; i++) { list.add(service.submit(new MyTask((int) (Math.random() * 100)))); } int sum = 0; for(Future<Integer> future : list) { while(!future.isDone()) ; sum += future.get(); } System.out.println(sum); } }
答:synchronized关键字可以将对象或者方法标记为同步,以实现对对象和方法的互斥访问,可以用synchronized(对象) { … }定义同步代码块,或者在声明方法时将synchronized作为方法的修饰符。在第60题的例子中已经展示了synchronized关键字的用法。
答:启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM 调度并执行,这并不意味着线程就会立即运行。run()方法是线程启动后要进行回调(callback)的方法。
API解释如下:

答:在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁,这就是"池化资源"技术产生的原因。线程池顾名思义就是事先创建若干个可执行的线程放入一个池(容器)中,需要的时候从池中获取线程不用自行创建,使用完毕不需要销毁线程而是放回池中,从而减少创建和销毁线程对象的开销。
Java 5+中的Executor接口定义一个执行线程的工具。它的子类型即线程池接口是ExecutorService。要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,因此在工具类Executors面提供了一些静态工厂方法,生成一些常用的线程池,如下所示:
newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
newSingleThreadExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。
有通过Executors工具类创建线程池并使用线程池执行线程的代码。如果希望在服务器上使用线程池,强烈建议使用newFixedThreadPool方法来创建线程池,这样能获得更好的性能。

除去起始(new)状态和结束(finished)状态,线程有三种状态,分别是:就绪(ready)、运行(running)和阻塞(blocked)。其中就绪状态代表线程具备了运行的所有条件,只等待CPU调度(万事俱备,只欠东风);处于运行状态的线程可能因为CPU调度(时间片用完了)的原因回到就绪状态,也有可能因为调用了线程的yield方法回到就绪状态,此时线程不会释放它占有的资源的锁,坐等CPU以继续执行;运行状态的线程可能因为I/O中断、线程休眠、调用了对象的wait方法而进入阻塞状态(有的地方也称之为等待状态);而进入阻塞状态的线程会因为休眠结束、调用了对象的notify方法或notifyAll方法或其他线程执行结束而进入就绪状态。注意:调用wait方法会让线程进入等待池中等待被唤醒,notify方法或notifyAll方法会让等待锁中的线程从等待池进入等锁池,在没有得到对象的锁之前,线程仍然无法获得CPU的调度和执行。
答:Lock是Java 5以后引入的新的API,和关键字synchronized相比主要相同点:Lock 能完成synchronized所实现的所有功能;主要不同点:Lock 有比synchronized 更精确的线程语义和更好的性能。synchronized 会自动释放锁,而Lock 一定要求程序员手工释放,并且必须在finally 块中释放(这是释放外部资源的最好的地方)。
方式1:继承Java.lang.Thread类,并覆盖run() 方法。
优势:编写简单;
劣势:单继承的限制----无法继承其它父类,同时不能实现资源共享。
package com.bjsxt; public class ThreadDemo1 { public static void main(String args[]) { MyThread1 t = new MyThread1(); t.start(); while (true) { System.out.println("兔子领先了,别骄傲"); } } } class MyThread1 extends Thread { public void run() { while (true) { System.out.println("乌龟领先了,加油"); } } }
方式2:实现Java.lang.Runnable接口,并实现run()方法。
优势:可继承其它类,多线程可共享同一个Thread对象;
劣势:编程方式稍微复杂,如需访问当前线程,需调用Thread.currentThread()方法
package com.bjsxt; public class ThreadDemo2 { public static void main(String args[]) { MyThread2 mt = new MyThread2(); Thread t = new Thread(mt); t.start(); while (true) { System.out.println("兔子领先了,加油"); } } } class MyThread2 implements Runnable { public void run() { while (true) { System.out.println("乌龟超过了,再接再厉"); } } }
两种方法的区别
1) start方法:
用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里方法run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。
2) run():
run()方法只是类的一个普通方法而已,如果直接调用run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待,run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。
总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。这两个方法应该都比较熟悉,把需要并行处理的代码放在run()方法中,start()方法启动线程将自动调用 run()方法,这是由jvm的内存机制规定的。并且run()方法必须是public访问权限,返回值类型为void。
两种方式的比较 :
实际中往往采用实现Runable接口,一方面因为java只支持单继承,继承了Thread类就无法再继续继承其它类,而且Runable接口只有一个run方法;另一方面通过结果可以看出实现Runable接口才是真正的多线程。
线程是一个动态执行的过程,它也有一个从产生到死亡的过程。
生命周期的五种状态
新建(new Thread)
当创建Thread类的一个实例(对象)时,此线程进入新建状态(未被启动)
例如:Thread t1=new Thread();
就绪(runnable)
线程已经被启动,正在等待被分配给CPU时间片,也就是说此时线程正在就绪队列中排队等候得到CPU资源。例如:t1.start();
运行(running)
线程获得CPU资源正在执行任务(run()方法),此时除非此线程自动放弃CPU资源或者有优先级更高的线程进入,线程将一直运行到结束。
死亡(dead)
当线程执行完毕或被其它线程杀死,线程就进入死亡状态,这时线程不可能再进入就绪状态等待执行。
自然终止:正常运行run()方法后终止
异常终止:调用stop()方法让一个线程终止运行
堵塞(blocked)
由于某种原因导致正在运行的线程让出CPU并暂停自己的执行,即进入堵塞状态。
正在睡眠:用sleep(long t) 方法可使线程进入睡眠方式。一个睡眠着的线程在指定的时间过去可进入就绪状态。
正在等待:调用wait()方法。(调用motify()方法回到就绪状态)
被另一个线程所阻塞:调用suspend()方法。(调用resume()方法恢复)
当多个线程访问同一个数据时,容易出现线程安全问题,需要某种方式来确保资源在某一时刻只被一个线程使用。需要让线程同步,保证数据安全
线程同步的实现方案:
1)同步代码块,使用synchronized关键字
同步代码块:
synchronized (同步锁) {
授课代码;
}
同步方法:
public synchronized void makeWithdrawal(int amt) { }
线程同步的好处:解决了线程安全问题
线程同步的缺点:性能下降,可能会带来死锁
注意: 同步代码块,所使用的同步锁可以是三种,
1、this 2、 共享资源 3、 字节码文件对象
同步方法所使用的同步锁,默认的是this
答:Java中每个对象都有一个内置锁。
当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例(this实例)有关的锁。获得一个对象的锁也称为获取锁、锁定对象、在对象上锁定或在对象上同步。
当程序运行到synchronized同步方法或代码块时才该对象锁才起作用。
一个对象只有一个锁。所以,如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放(或返回)锁。这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放。
释放锁是指持锁线程退出了synchronized同步方法或代码块。
关于锁和同步,有一下几个要点:
1)只能同步方法,而不能同步变量和类;
2)每个对象只有一个锁;当提到同步时,应该清楚在什么上同步?也就是说,在哪个对象上同步?
3)不必同步类中所有的方法,类可以同时拥有同步和非同步方法。
4)如果两个线程要执行一个类中的synchronized方法,并且两个线程使用相同的实例来调用方法,那么一次只能有一个线程能够执行方法,另一个需要等待,直到锁被释放。也就是说:如果一个线程在对象上获得一个锁,就没有任何其他线程可以进入(该对象的)类中的任何一个同步方法。
5)如果线程拥有同步和非同步方法,则非同步方法可以被多个线程自由访问而不受锁的限制。
6)线程睡眠时,它所持的任何锁都不会释放。
7)线程可以获得多个锁。比如,在一个对象的同步方法里面调用另外一个对象的同步方法,则获取了两个对象的同步锁。
8)同步损害并发性,应该尽可能缩小同步范围。同步不但可以同步整个方法,还可以同步方法中一部分代码块。
9)在使用同步代码块时候,应该指定在哪个对象上同步,也就是说要获取哪个对象的锁。
Java提供了3个方法解决线程之间的通信问题,均是java.lang.Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常。
方法名 | 作 用 |
final void wait() | 表示线程一直等待,直到其它线程通知 |
void wait(long timeout) | 线程等待指定毫秒参数的时间 |
final void wait(long timeout,int nanos) | 线程等待指定毫秒、微妙的时间 |
final void notify() | 唤醒一个处于等待状态的线程。注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。 |
final void notifyAll() | 唤醒同一个对象上所有调用wait()方法的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争 |
A. | BufferedWriter |
B. | FileInputStream |
C. | ObjectInputStream |
D. | InputStreamReader |
答案:D
分析:A:字符输出的缓冲流
B:字节输入流
C:对象输入流
|
A. |
FileInputStream in=new FileInputStream("file.dat");
in.skip(9);
int c=in.read();
|
B. |
FileInputStream in=new FileInputStream("file.dat");
in.skip(10);
int c=in.read();
|
C. |
FileInputStream in=new FileInputStream("file.dat");
int c=in.read();
|
D. |
RandomAccessFile in=new RandomAccessFile("file.dat");
in.skip(7);
int c=in.readByte();
|
答案:A
分析: skip(long n)该方法中的n指的是要跳过的字节数
|
A. | new BufferedWriter(new FileWriter(“a.txt”)); |
B. | new BufferedReader (new FileInputStream(“a.dat”)); |
C. | new GZIPOutputStream(new FileOutputStream(“a.zip”)); |
D. | new ObjectInputStream(new FileInputStream(“a.dat”)); |
答案:B
分析:BufferedReader类的参数只能是Reader类型的,不能是InputStream类型。
|
A. | BufferedWriter |
B. | FileInputStream |
C. | ObjectInputStream |
D. | InputStreamReader |
答案:D
以InputStream(输入流)/OutputStream(输出流)为后缀的是字节流;
以Reader(输入流)/Writer(输出流)为后缀的是字符流。
|
A. | Java.io.FileOutputStream |
B. | java.ByteArrayOutputStream |
C. | java.io.BufferedOutputStream |
D. | java,.io.DataOutputStream |
答案:B
分析: ACD都是io到文件
|
public class test { public static void main(String[] args) { String str = "bjsxt"; writeFile(str); } public static void writeFile(String str) { File file = new File("c:/test.txt"); PrintStream ps = null; try { OutputStream fos = new FileOutputStream(file); ps = new PrintStream(fos); ps.print(str); } catch (FileNotFoundException e) { e.printStackTrace(); } finally { ps.close(); } } }
A. | BufferedWriter |
B. | FileInputStream |
C. | ObjectInputStream |
D. | InputStreamReader |
答案:D
|
答:序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决对象流读写操作时可能引发的问题(如果不进行序列化可能会存在数据乱序的问题)。
要实现序列化,需要让一个类实现Serializable接口,该接口是一个标识性接口,标注该类对象是可被序列化的,然后使用一个输出流来构造一个对象输出流并通过writeObject(Object obj)方法就可以将实现对象写出(即保存其状态);如果需要反序列化则可以用一个输入流建立对象输入流,然后通过readObject方法从流中读取对象。序列化除了能够实现对象的持久化之外,还能够用于对象的深度克隆(参见Java面试题集1-29题)
答:两种流分别是字节流,字符流。
字节流继承于InputStream、OutputStream,字符流继承于Reader、Writer。在java.io 包中还有许多其他的流,主要是为了提高性能和使用方便。
补充:关于Java的IO需要注意的有两点:一是两种对称性(输入和输出的对称性,字节和字符的对称性);二是两种设计模式(适配器模式和装潢模式)。另外Java中的流不同于C#的是它只有一个维度一个方向。
补充:下面用IO和NIO两种方式实现文件拷贝,这个题目在面试的时候是经常被问到的。
package com.bjsxt; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class MyUtil { private MyUtil() { throw new AssertionError(); } public static void fileCopy(String source, String target) throws IOException { try (InputStream in = new FileInputStream(source)) { try (OutputStream out = new FileOutputStream(target)) { byte[] buffer = new byte[4096]; int bytesToRead; while((bytesToRead = in.read(buffer)) != -1) { out.write(buffer, 0, bytesToRead); } } } } public static void fileCopyNIO(String source, String target) throws IOException { try (FileInputStream in = new FileInputStream(source)) { try (FileOutputStream out = new FileOutputStream(target)) { FileChannel inChannel = in.getChannel(); FileChannel outChannel = out.getChannel(); ByteBuffer buffer = ByteBuffer.allocate(4096); while(inChannel.read(buffer) != -1) { buffer.flip(); outChannel.write(buffer); buffer.clear(); } } } } }
注意:上面用到Java 7的TWR,使用TWR后可以不用在finally中释放外部资源 ,从而让代码更加优雅。
答:代码如下:
package com.bjsxt; import java.io.BufferedReader; import java.io.FileReader; public class Account { // 工具类中的方法都是静态方式访问的因此将构造器私有不允许创建对象(绝对好习惯) private Account() { throw new AssertionError(); } /** * 统计给定文件中给定字符串的出现次数 * @param filename 文件名 * @param word 字符串 * @return 字符串在文件中出现的次数 */ public static int countWordInFile(String filename, String word) { int counter = 0; try (FileReader fr = new FileReader(filename)) { try (BufferedReader br = new BufferedReader(fr)) { String line = null; while ((line = br.readLine()) != null) { int index = -1; while (line.length() >= word.length() && (index = line.indexOf(word)) >= 0) { counter++; line = line.substring(index + word.length()); } } } } catch (Exception ex) { ex.printStackTrace(); } return counter; } }
首先,你要明白什么是“流”。直观地讲,流就像管道一样,在程序和文件之间,输入输出的方向是针对程序而言,向程序中读入东西,就是输入流,从程序中向外读东西,就是输出流。
输入流是得到数据,输出流是输出数据,而节点流,处理流是流的另一种划分,按照功能不同进行的划分。节点流,可以从或向一个特定的地方(节点)读写数据。处理流是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如BufferedReader。处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。
字符流和字节流是流的一种划分,按处理照流的数据单位进行的划分。两类都分为输入和输出操作。在字节流中输出数据主要是使用OutputStream完成,输入使的是InputStream,在字符流中输出主要是使用Writer类完成,输入流主要使用Reader类完成。这四个都是抽象类。
字符流处理的单元为2个字节的Unicode字符,分别操作字符、字符数组或字符串,而字节流处理单元为1个字节,操作字节和字节数组。字节流是最基本的,所有的InputStrem和OutputStream的子类都是,主要用在处理二进制数据,它是按字节来处理的 但实际中很多的数据是文本,又提出了字符流的概念,它是按虚拟机的编码来处理,也就是要进行字符集的转化 这两个之间通过 InputStreamReader,OutputStreamWriter来关联,实际上是通过byte[]和String来关联的。
FileInputStream 从文件系统中的某个文件中获得输入字节。
FileOutputStream 从程序当中的数据,写入到指定文件。
ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。 ObjectOutputStream 和ObjectInputStream 分别与FileOutputStream 和 FileInputStream 一起使用时,可以为应用程序提供对对象图形的持久存储。ObjectInputStream 用于恢复那些以前序列化的对象。其他用途包括使用套接字流在主机之间传递对象,或者用于编组和解组远程通信系统中的实参和形参。
ByteArrayInputStream 包含一个内部缓冲区,该缓冲区包含从流中读取的字节。内部计数器跟踪 read 方法要提供的下一个字节。
FilterInputStream 包含其他一些输入流,它将这些流用作其基本数据源,它可以直接传输数据或提供一些额外的功能。FilterInputStream 类本身只是简单地重写那些将所有请求传递给所包含输入流的 InputStream 的所有方法。FilterInputStream 的子类可进一步重写这些方法中的一些方法,并且还可以提供一些额外的方法和字段。
StringBufferInputStream此类允许应用程序创建输入流,在该流中读取的字节由字符串内容提供。应用程序还可以使用ByteArrayInputStream 从 byte 数组中读取字节。 只有字符串中每个字符的低八位可以由此类使用。
ByteArrayOutputStream此类实现了一个输出流,其中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray() 和 toString() 获取数据。
FileOutputStream文件输出流是用于将数据写入 File 或FileDescriptor 的输出流。文件是否可用或能否可以被创建取决于基础平台。特别是某些平台一次只允许一个 FileOutputStream(或其他文件写入对象)打开文件进行写入。在这种情况下,如果所涉及的文件已经打开,则此类中的构造方法将失败。
FilterOutputStream类是过滤输出流的所有类的超类。这些流位于已存在的输出流(基础 输出流)之上,它们将已存在的输出流作为其基本数据接收器,但可能直接传输数据或提供一些额外的功能。 FilterOutputStream 类本身只是简单地重写那些将所有请求传递给所包含输出流的 OutputStream 的所有方法。FilterOutputStream 的子类可进一步地重写这些方法中的一些方法,并且还可以提供一些额外的方法和字段。
ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象。
PipedOutputStream可以将管道输出流连接到管道输入流来创建通信管道。管道输出流是管道的发送端。通常,数据由某个线程写入 PipedOutputStream 对象,并由其他线程从连接的 PipedInputStream 读取。不建议对这两个对象尝试使用单个线程,因为这样可能会造成该线程死锁。如果某个线程正从连接的管道输入流中读取数据字节,但该线程不再处于活动状态,则该管道被视为处于毁坏状态。
不带缓冲的流的工作原理:
它读取到一个字节/字符,就向用户指定的路径写出去,读一个写一个,所以就慢了。
带缓冲的流的工作原理:
读取到一个字节/字符,先不输出,等凑足了缓冲的最大容量后一次性写出去,从而提高了工作效率
优点:减少对硬盘的读取次数,降低对硬盘的损耗。
想把一个对象写在硬盘上或者网络上,对其进行序列化,把他序列化成为一个字节流。
实现和注意事项:
1)实现接口Serializable Serializable接口中没有任何的方法,实现该接口的类不需要实现额外的方法。
2)如果对象中的某个属性是对象类型,必须也实现Serializable接口才可以,序列化对静态变量无效
3)如果不希望某个属性参与序列化,不是将其static,而是transient串行化保存的只是变量的值,对于变量的任何修饰符,都不能保存序列化版本不兼容
(结合递归)
package com.bjsxt; import java.io.*; /** * CopyDocJob定义了实际执行的任务,即 * 从源目录拷贝文件到目标目录 */ public class CopyDir2 { public static void main(String[] args) { try { copyDirectiory("d:/301sxt","d:/301sxt2"); } catch (IOException e) { e.printStackTrace(); } } /** * 复制单个文件 * @param sourceFile 源文件 * @param targetFile 目标文件 * @throws IOException */ private static void copyFile(File sourceFile, File targetFile) throws IOException { BufferedInputStream inBuff = null; BufferedOutputStream outBuff = null; try { // 新建文件输入流 inBuff = new BufferedInputStream(new FileInputStream(sourceFile)); // 新建文件输出流 outBuff = new BufferedOutputStream(new FileOutputStream(targetFile)); // 缓冲数组 byte[] b = new byte[1024 * 5]; int len; while ((len = inBuff.read(b)) != -1) { outBuff.write(b, 0, len); } // 刷新此缓冲的输出流 outBuff.flush(); } finally { // 关闭流 if (inBuff != null) inBuff.close(); if (outBuff != null) outBuff.close(); } } /** * 复制目录 * @param sourceDir 源目录 * @param targetDir 目标目录 * @throws IOException */ private static void copyDirectiory(String sourceDir, String targetDir) throws IOException { // 检查源目录 File fSourceDir = new File(sourceDir); if(!fSourceDir.exists() || !fSourceDir.isDirectory()){ return; } //检查目标目录,如不存在则创建 File fTargetDir = new File(targetDir); if(!fTargetDir.exists()){ fTargetDir.mkdirs(); } // 遍历源目录下的文件或目录 File[] file = fSourceDir.listFiles(); for (int i = 0; i < file.length; i++) { if (file[i].isFile()) { // 源文件 File sourceFile = file[i]; // 目标文件 File targetFile = new File(fTargetDir, file[i].getName()); copyFile(sourceFile, targetFile); } //递归复制子目录 if (file[i].isDirectory()) { // 准备复制的源文件夹 String subSourceDir = sourceDir + File.separator + file[i].getName(); // 准备复制的目标文件夹 String subTargetDir = targetDir + File.separator + file[i].getName(); // 复制子目录 copyDirectiory(subSourceDir, subTargetDir); } } } }
Java BIO: 同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。
Java NIO: 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
Java AIO: 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。
NIO比BIO的改善之处是把一些无效的连接挡在了启动线程之前,减少了这部分资源的浪费(因为我们都知道每创建一个线程,就要为这个线程分配一定的内存空间)
AIO比NIO的进一步改善之处是将一些暂时可能无效的请求挡在了启动线程之前,比如在NIO的处理方式中,当一个请求来的话,开启线程进行处理,但这个请求所需要的资源还没有就绪,此时必须等待后端的应用资源,这时线程就被阻塞了。
适用场景分析:
BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解,如之前在Apache中使用。
NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持,如在 Nginx,Netty中使用。
AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持,在成长中,Netty曾经使用过,后来放弃。
网络编程:
1)IP地址
用来标志网络中的一个通信实体的地址。通信实体可以是计算机,路由器等。
2)IP地址分类
IPV4:32位地址,以点分十进制表示,如192.168.0.1
IPV6:128位(16个字节)写成8个16位的无符号整数,每个整数用四个十六进制位表示,数之间用冒号(:)分开,如:3ffe:3201:1401:1280:c8ff:fe4d:db39:1984
3)特殊的IP地址
127.0.0.1 本机地址
192.168.0.0--192.168.255.255私有地址,属于非注册地址,专门为组织机构内部使用。
4)端口:port
IP地址用来标志一台计算机,但是一台计算机上可能提供多种应用程序,使用端口来区分这些应用程序。 端口是虚拟的概念,并不是说在主机上真的有若干个端口。通过端口,可以在一个主机上运行多个网络应用程序。 端口范围0---65535,16位整数
5)端口分类
公认端口 0—1023 比如80端口分配给WWW,21端口分配给FTP,22端口分配给SSH,23端口分配给telnet,25端口分配给smtp
注册端口 1024—49151 分配给用户进程或应用程序
动态/私有端口 49152--65535
6)理解IP和端口的关系
IP地址好比每个人的地址(门牌号),端口好比是房间号。必须同时指定IP地址和端口号才能够正确的发送数据
IP地址好比为电话号码,而端口号就好比为分机号。

OSI(Open System Interconnection),开放式系统互联参考模型 。是一个逻辑上的定义,一个规范,它把网络协议从逻辑上分为了7层。每一层都有相关、相对应的物理设备,比如常规的路由器是三层交换设备,常规的交换机是二层交换设备。OSI七层模型是一种框架性的设计方法,建立七层模型的主要目的是为解决异种网络互连时所遇到的兼容性问题,其最主要的功能就是帮助不同类型的主机实现数据传输。它的最大优点是将服务、接口和协议这三个概念明确地区分开来,通过七个层次化的结构模型使不同的系统不同的网络之间实现可靠的通讯。
TCP/IP协议是Internet最基本的协议、Internet国际互联网络的基础,主要由网络层的IP协议和传输层的TCP协议组成。TCP/IP 定义了电子设备如何连入因特网,以及数据如何在它们之间传输的标准。协议采用了4层的层级结构,每一层都呼叫它的下一层所提供的协议来完成自己的需求。
ISO制定的OSI参考模型的过于庞大、复杂招致了许多批评。伴随着互联网的流行,其本身所采用的TCP/IP协议栈获得了更为广泛的应用和认可。在TCP/IP参考模型中,去掉了OSI参考模型中的会话层和表示层(这两层的功能被合并到应用层实现)。同时将OSI参考模型中的数据链路层和物理层合并为主机到网络层。
TCP和UDP是TCP/IP协议栈中传输层的两个协议,它们使用IP路由功能把数据包发送到目的地,从而为应用程序及应用层协议(包括:HTTP、SMTP、SNMP、FTP和Telnet)提供网络服务。
TCP的server和client之间通信就好比两个人打电话,需要互相知道对方的电话号码,然后开始对话。所以在两者的连接过程中间需要指定端口和地址。
UDP的server和client之间的通信就像两个人互相发信。我只需要知道对方的地址,然后就发信过去。对方是否收到我不知道,也不需要专门对口令似的来建立连接。具体区别如下:
1)TCP是面向连接的传输。UDP是无连接的传输
2)TCP有流量控制、拥塞控制,检验数据数据按序到达,而UDP则相反。
3)TCP的路由选择只发生在建立连接的时候,而UDP的每个报文都要进行路由选择
4)TCP是可靠性传输,他的可靠性是由超时重发机制实现的,而UDP则是不可靠传输
5)UDP因为少了很多控制信息,所以传输速度比TCP速度快
6)TCP适合用于传输大量数据,UDP适合用于传输小量数据
Socket编程的定义如下:
所谓socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。应用程序通常通过"套接字"向网络发出请求或者应答网络请求。
我们开发的网络应用程序位于应用层,TCP和UDP属于传输层协议,在应用层如何使用传输层的服务呢?在应用层和传输层之间,则是使用套接字来进行分离。
套接字就像是传输层为应用层开的一个小口,应用程序通过这个小口向远程发送数据,或者接收远程发来的数据;而这个小口以内,也就是数据进入这个口之后,或者数据从这个口出来之前,是不知道也不需要知道的,也不会关心它如何传输,这属于网络其它层次的工作。
Socket实际是传输层供给应用层的编程接口。传输层则在网络层的基础上提供进程到进程问的逻辑通道,而应用层的进程则利用传输层向另一台主机的某一进程通信。Socket就是应用层与传输层之间的桥梁
使用Socket编程可以开发客户机和服务器应用程序,可以在本地网络上进行通信,也可通过Internet在全球范围内通信。


生活案例1如果你想写封邮件发给远方的朋友,如何写信、将信打包,属于应用层。信怎么写,怎么打包完全由我们做主;而当我们将信投入邮筒时,邮筒的那个口就是套接字,在进入套接字之后,就是传输层、网络层等(邮局、公路交管或者航线等)其它层次的工作了。我们从来不会去关心信是如何从西安发往北京的,我们只知道写好了投入邮筒就OK了。
生活案例2:可以把Socket比作是一个港口码头,应用程序只要将数据交给Socket,就算完成了数据的发送,具体细节由Socket来完成,细节不必了解。同理,对于接收方,应用程序也要创建一个码头,等待数据的到达,并获取数据。
Java分别为TCP和UDP 两种通信协议提供了相应的Socket编程类,这些类存放在java.net包中。与TCP对应的是服务器的ServerSocket和客户端的Socket,与UDP对应的是DatagramSocket。
基于TCP创建的套接字可以叫做流套接字,服务器端相当于一个监听器,用来监听端口。 服务器与客服端之间的通讯都是输入输出流来实现的。基于UDP的套接字就是数据报套接字,• 两个都要先构造好相应的数据包。
基于TCP协议的Socket编程的主要步骤
服务器端(server):
1. 构建一个ServerSocket实例,指定本地的端口。这个socket就是用来监听指定端口的连接请求的。
2. 重复如下几个步骤:
a. 调用socket的accept()方法来获得下面客户端的连接请求。通过accept()方法返回的socket实例,建立了一个和客户端的新连接。
b. 通过这个返回的socket实例获取InputStream和OutputStream,可以通过这两个stream来分别读和写数据。
c. 结束的时候调用socket实例的close()方法关闭socket连接。
客户端(client):
1.构建Socket实例,通过指定的远程服务器地址和端口来建立连接。
2.通过Socket实例包含的InputStream和OutputStream来进行数据的读写。
3.操作结束后调用socket实例的close方法,关闭。

UDP
服务器端(server):
1. 构造DatagramSocket实例,指定本地端口。
2. 通过DatagramSocket实例的receive方法接收DatagramPacket.DatagramPacket中间就包含了通信的内容。
3. 通过DatagramSocket的send和receive方法来收和发DatagramPacket.
客户端(client):
1. 构造DatagramSocket实例。
2. 通过DatagramSocket实例的send和receive方法发送DatagramPacket报文。
3. 结束后,调用DatagramSocket的close方法关闭。
异常处理:
A. | NullPointerException |
B. | ClassCastException |
C. | FileNotFoundException |
D. | IndexOutOfBoundsException |
答案:C
分析:NullPointerException空指针异常
ClassCastException类型转换异常
IndexOutOfBoundsException索引超出边界的异常
以上这些异常都是程序在运行时发生的异常,所以不需要在编写程序时声明
|
答:OutOf MemoryError这种错误可以细分为多种不同的错误,每种错误都有自身的原因和解决办法,如下所示:
java.lang.OutOfMemoryError: Java heap space
错误原因:此OOM是由于JVM中heap的最大值不满足需要。
解决方法:1) 调高heap的最大值,即-Xmx的值调大。2) 如果你的程序存在内存泄漏,一味的增加heap空间也只是推迟该错误出现的时间而已,所以要检查程序是否存在内存泄漏。
java.lang.OutOfMemoryError: GC overhead limit exceeded
错误原因:此OOM是由于JVM在GC时,对象过多,导致内存溢出,建议调整GC的策略,在一定比例下开始GC而不要使用默认的策略,或者将新代和老代设置合适的大小,需要进行微调存活率。
解决方法:改变GC策略,在老代80%时就是开始GC,并且将-XX:SurvivorRatio(-XX:SurvivorRatio=8)和-XX:NewRatio(-XX:NewRatio=4)设置的更合理。
java.lang.OutOfMemoryError: Java perm space
错误原因:此OOM是由于JVM中perm的最大值不满足需要。
解决方法:调高heap的最大值,即-XX:MaxPermSize的值调大。
另外,注意一点,Perm一般是在JVM启动时加载类进来,如果是JVM运行较长一段时间而不是刚启动后溢出的话,很有可能是由于运行时有类被动态加载进来,此时建议用CMS策略中的类卸载配置。如:-XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled。
java.lang.OutOfMemoryError: unable to create new native thread
错误原因:当JVM向OS请求创建一个新线程时,而OS却由于内存不足无法创建新的native线程。
解决方法:如果JVM内存调的过大或者可利用率小于20%,可以建议将heap及perm的最大值下调,并将线程栈调小,即-Xss调小,如:-Xss128k。
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
错误原因:此类信息表明应用程序(或者被应用程序调用的APIs)试图分配一个大于堆大小的数组。例如,如果应用程序new一个数组对象,大小为512M,但是最大堆大小为256M,因此OutOfMemoryError会抛出,因为数组的大小超过虚拟机的限制。
解决方法:1) 首先检查heap的-Xmx是不是设置的过小。2) 如果heap的-Xmx已经足够大,那么请检查应用程序是不是存在bug,例如:应用程序可能在计算数组的大小时,存在算法错误,导致数组的size很大,从而导致巨大的数组被分配。
java.lang.OutOfMemoryError: request < size> bytes for < reason>. Out of swap space
错误原因:抛出这类错误,是由于从native堆中分配内存失败,并且堆内存可能接近耗尽。这类错误可能跟应用程序没有关系,例如下面两种原因也会导致错误的发生:1) 操作系统配置了较小的交换区。2)系统的另外一个进程正在消耗所有的内存。
解决办法:1) 检查os的swap是不是没有设置或者设置的过小。2) 检查是否有其他进程在消耗大量的内存,从而导致当前的JVM内存不够分配。
注意:虽然有时< reason>部分显示导致OOM的原因,但大多数情况下,< reason>显示的是提示分配失败的源模块的名称,所以有必要查看日志文件,如crash时的hs文件。
答:ClassCastException(类转换异常)
比如 Object obj=new Object(); String s=(String)obj;
IndexOutOfBoundsException(下标越界异常)
NullPointerException(空指针异常)
ArrayStoreException(数据存储异常,操作数组时类型不一致)
BufferOverflowException(IO操作时出现的缓冲区上溢异常)
InputMismatchException(输入类型不匹配异常)
ArithmeticException(算术异常)
注意:运行时异常都是RuntimeException子类异常。
A. | 继承自 Throwable |
B. | 不支持Serializable |
C. | 继承自 AbstractSet |
D. | 继承自FitelnputStream |
答案:A
分析:Throwable是Exception和Error的父类,Exception虽然没有实现Serializable接口,但是其父类Throwable已经实现了该接口,因此Exception也支持Serializable。
|
答:问题的根本原因是工程中某个jar包的版本(jar包编译时的所用的jdk版本)高于工程build path中jdk的版本,这个是不兼容的! 编程中遇到此异常Unsupported major.minor version 52.0(根据版本号,这里可以为其他数值,52是1.8jdk jar包与 1.8以下低版本jdk不匹配),在将build path中jdk的版本调整与jar包匹配后,解决异常。
答:会执行,在方法返回调用者前执行。Java允许在finally中改变返回值的做法是不好的,因为如果存在finally代码块,try中的return语句不会立马返回调用者,而是记录下返回值待finally代码块执行完毕之后再向调用者返回其值,然后如果在finally中修改了返回值,这会对程序造成很大的困扰,C#中就从语法上规定不能做这样的事。
(也许你的答案是在return之前,但往更细地说,我的答案是在return中间执行,请看下面程序代码的运行结果:
public classTest { /** * @paramargs add by zxx ,Dec 9, 2008 */ public static voidmain(String[] args) { // TODO Auto-generated method stub System.out.println(newTest().test());; } static int test() { int x = 1; try { returnx; } finally { ++x; } } }
执行结果
运行结果是1,为什么呢?主函数调用子函数并得到结果的过程,好比主函数准备一个空罐子,当子函数要返回结果时,先把结果放在罐子里,然后再将程序逻辑返回到主函数。所谓返回,就是子函数说,我不运行了,你主函数继续运行吧,这没什么结果可言,结果是在说这话之前放进罐子里的。
答:Java 通过面向对象的方法进行异常处理,把各种不同的异常进行分类,并提供了良好的接口。在Java 中,每个异常都是一个对象,它是Throwable 类或其子类的实例。当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个异常并进行处理。Java 的异常处理是通过5 个关键词来实现的:try、catch、throw、throws和finally。一般情况下是用try来执行一段程序,如果出现异常,系统会抛出(throw)一个异常,这时候你可以通过它的类型来捕捉(catch)它,或最后(finally)由缺省处理器来处理;try用来指定一块预防所有“异常”的程序;catch 子句紧跟在try块后面,用来指定你想要捕捉的“异常”的类型;throw 语句用来明确地抛出一个“异常”;throws用来标明一个成员函数可能抛出的各种“异常”;finally 为确保一段代码不管发生什么“异常”都被执行一段代码;可以在一个成员函数调用的外面写一个try语句,在这个成员函数内部写另一个try语句保护其他代码。每当遇到一个try 语句,“异常”的框架就放到栈上面,直到所有的try语句都完成。如果下一级的try语句没有对某种“异常”进行处理,栈就会展开,直到遇到有处理这种“异常”的try 语句。
答:异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误,只要程序设计得没有问题通常就不会发生。受检异常跟程序运行的上下文环境有关,即使程序设计无误,仍然可能因使用的问题而引发。Java编译器要求方法必须声明抛出可能发生的受检异常,但是并不要求必须声明抛出未被捕获的运行时异常。异常和继承一样,是面向对象程序设计中经常被滥用的东西,神作《Effective Java》中对异常的使用给出了以下指导原则:
不要将异常处理用于正常的控制流(设计良好的API不应该强迫它的调用者为了正常的控制流而使用异常)
对可以恢复的情况使用受检异常,对编程错误使用运行时异常
避免不必要的使用受检异常(可以通过一些状态检测手段来避免异常发生)
优先使用标准的异常
每个方法抛出的异常都要有文档
保持异常的原子性
不要在catch中忽略掉捕获到的异常
(异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误。java编译器要求方法必须声明抛出可能发生的非运行时异常,但是并不要求必须声明抛出未被捕获的运行时异常。)
有如下代码片断:
try{ throw new ExampleB("b") }catch(ExampleA e){ System.out.println("ExampleA"); }catch(Exception e){ System.out.println("Exception"); } }
请问执行此段代码的输出是什么?
答:输出:ExampleA。(根据里氏代换原则[能使用父类型的地方一定能使用子类型],抓取ExampleA类型异常的catch块能够抓住try块中抛出的ExampleB类型的异常)
补充: 比此题略复杂的一道面试题如下所示(此题的出处是《Java编程思想》),说出你的答案吧!
package com.bjsxt; class Annoyance extends Exception {} class Sneeze extends Annoyance {} class Human { public static void main(String[] args) throws Exception { try { try { throw new Sneeze(); } catch ( Annoyance a ) { System.out.println("Caught Annoyance"); throw a; } } catch ( Sneeze s ) { System.out.println("Caught Sneeze"); return ; } finally { System.out.println("Hello World!"); } } }
输出为:
Caught Annoyance
Caught Sneeze
Hello World!

Error类,表示仅靠程序本身无法恢复的严重错误,比如说内存溢出、动态链接异常、虚拟机错误。应用程序不应该抛出这种类型的对象。假如出现这种错误,除了尽力使程序安全退出外,在其他方面是无能为力的。所以在进行程序设计时,应该更关注Exception类。
Exception类,由Java应用程序抛出和处理的非严重错误,比如所需文件没有找到、零作除数,数组下标越界等。它的各种不同子类分别对应不同类型异常。可分为两类:Checked异常和Runtime异常
try-catch-finally程序块的执行流程以及执行结果比较复杂。
基本执行过程如下:
1)程序首先执行可能发生异常的try语句块。
2)如果try语句没有出现异常则执行完后跳至finally语句块执行;
3)如果try语句出现异常,则中断执行并根据发生的异常类型跳至相应的catch语句块执行处理。
4)catch语句块可以有多个,分别捕获不同类型的异常。
5)catch语句块执行完后程序会继续执行finally语句块。
finally语句是可选的,如果有的话,则不管是否发生异常,finally语句都会被执行。需要注意的是即使try和catch块中存在return语句,finally语句也会执行,是在执行完finally语句后再通过return退出。
1)作用不同:
throw用于程序员自行产生并抛出异常;
throws用于声明在该方法内抛出了异常
2) 使用的位置不同:
throw位于方法体内部,可以作为单独语句使用;
throws必须跟在方法参数列表的后面,不能单独使用。
3)内容不同:
throw抛出一个异常对象,且只能是一个;
throws后面跟异常类,而且可以有多个。
答:.class文件放在WEB-INF/classes文件下,.jar文件放在WEB-INF/lib文件夹下
答:document.getElementById(“username”).value;
区别:
JSP是在HTML代码里写JAVA代码,框架是HTML;而Servlet是在JAVA代码中写HTML代码,本身是个JAVA类。
JSP使人们把显示和逻辑分隔成为可能,这意味着两者的开发可并行进行;而Servlet并没有把两者分开。
Servlet独立地处理静态表示逻辑与动态业务逻辑.这样,任何文件的变动都需要对此服务程序重新编译;JSP允许用特殊标签直接嵌入到HTML页面, HTML内容与JAVA内容也可放在单独文件中,HTML内容的任何变动会自动编译装入到服务程序.
Servlet需要在web.xml中配置,而JSP无需配置。
目前JSP主要用在视图层,负责显示,而Servlet主要用在控制层,负责调度
联系:
都是Sun公司推出的动态网页技术。
先有Servlet,针对Servlet缺点推出JSP。JSP是Servlet的一种特殊形式,每个JSP页面就是一个Servlet实例——JSP页面由系统翻译成Servlet,Servlet再负责响应用户请求。
JavaScript 高级程序设计(特别是对浏览器差异的复杂处理),通常很困难也很耗时。为了应对这些调整,许多的 JavaScript库应运而生。这些 JavaScript 库常被称为 JavaScript 框架。
jQuery:
Ext JS - 可定制的 widget,用于构建富因特网应用程序(rich Internet applications)。
Prototype
MooTools。
YUI - Yahoo! User Interface Framework,涵盖大量函数的大型库,从简单的 JavaScript 功能到完整的 internet widget。
1、什么是HTML(超文本标记语言 Hyper Text Markup Language),HTML 是用来描述网页的一种语言。
2、CSS(层叠样式表 Cascading Style Sheets),样式定义如何显示 HTML 元素,语法为:selector {property:value} (选择符 {属性:值})
3、JavaScript是一种脚本语言,其源代码在发往客户端运行之前不需经过编译,而是将文本格式的字符代码发送给浏览器由浏览器解释运行
对于一个网页,HTML定义网页的结构,CSS描述网页的样子,JavaScript设置一个很经典的例子是说HTML就像 一个人的骨骼、器官,而CSS就是人的皮肤,有了这两样也就构成了一个植物人了,加上javascript这个植物人就可以对外界刺激做出反应,可以思 考、运动、可以给自己整容化妆(改变CSS)等等,成为一个活生生的人。
如果说HTML是肉身、CSS就是皮相、Javascript就是灵魂。没有Javascript,HTML+CSS是植物人,没有Javascript、CSS是个毁容的植物人。
如果说HTML是建筑师,CSS就是干装修的,Javascript是魔术师。
A. | JQuery(expression, [context]) |
B. | JQuery(html, [ownerDocument]) |
C. | JQuery(callback) |
答案:C
|
答:1. DOM(Document Object Model)
DOM是用与平台和语言无关的方式表示XML文档的官方W3C标准。DOM是以层次结构组织的节点或信息片断的集合。这个层次结构允许开发人员在树中寻找特定信息。分析该结构通常需要加载整个文档和构造层次结构,然后才能做任何工作。由于它是基于信息层次的,因而DOM被认为是基于树或基于对象的。
【优点】
①允许应用程序对数据和结构做出更改。
②访问是双向的,可以在任何时候在树中上下导航,获取和操作任意部分的数据。
【缺点】
①通常需要加载整个XML文档来构造层次结构,消耗资源大。
2. SAX(Simple API for XML)
SAX处理的优点非常类似于流媒体的优点。分析能够立即开始,而不是等待所有的数据被处理。而且,由于应用程序只是在读取数据时检查数据,因此不需要将数据存储在内存中。这对于大型文档来说是个巨大的优点。事实上,应用程序甚至不必解析整个文档;它可以在某个条件得到满足时停止解析。一般来说,SAX还比它的替代者DOM快许多。
选择DOM还是选择SAX? 对于需要自己编写代码来处理XML文档的开发人员来说, 选择DOM还是SAX解析模型是一个非常重要的设计决策。 DOM采用建立树形结构的方式访问XML文档,而SAX采用的是事件模型。
DOM解析器把XML文档转化为一个包含其内容的树,并可以对树进行遍历。用DOM解析模型的优点是编程容易,开发人员只需要调用建树的指令,然后利用navigation APIs访问所需的树节点来完成任务。可以很容易的添加和修改树中的元素。然而由于使用DOM解析器的时候需要处理整个XML文档,所以对性能和内存的要求比较高,尤其是遇到很大的XML文件的时候。由于它的遍历能力,DOM解析器常用于XML文档需要频繁的改变的服务中。
SAX解析器采用了基于事件的模型,它在解析XML文档的时候可以触发一系列的事件,当发现给定的tag的时候,它可以激活一个回调方法,告诉该方法制定的标签已经找到。SAX对内存的要求通常会比较低,因为它让开发人员自己来决定所要处理的tag.特别是当开发人员只需要处理文档中所包含的部分数据时,SAX这种扩展能力得到了更好的体现。但用SAX解析器的时候编码工作会比较困难,而且很难同时访问同一个文档中的多处不同数据。
【优势】
①不需要等待所有数据都被处理,分析就能立即开始。
②只在读取数据时检查数据,不需要保存在内存中。
③可以在某个条件得到满足时停止解析,不必解析整个文档。
④效率和性能较高,能解析大于系统内存的文档。
【缺点】
①需要应用程序自己负责TAG的处理逻辑(例如维护父/子关系等),文档越复杂程序就越复杂。
②单向导航,无法定位文档层次,很难同时访问同一文档的不同部分数据,不支持XPath。
3. JDOM(Java-based Document Object Model)
JDOM的目的是成为Java特定文档模型,它简化与XML的交互并且比使用DOM实现更快。由于是第一个Java特定模型,JDOM一直得到大力推广和促进。正在考虑通过“Java规范请求JSR-102”将它最终用作“Java标准扩展”。从2000年初就已经开始了JDOM开发。
JDOM与DOM主要有两方面不同。首先,JDOM仅使用具体类而不使用接口。这在某些方面简化了API,但是也限制了灵活性。第二,API大量使用了Collections类,简化了那些已经熟悉这些类的Java开发者的使用。
JDOM文档声明其目的是“使用20%(或更少)的精力解决80%(或更多)Java/XML问题”(根据学习曲线假定为20%)。JDOM对于大多数Java/XML应用程序来说当然是有用的,并且大多数开发者发现API比DOM容易理解得多。JDOM还包括对程序行为的相当广泛检查以防止用户做任何在XML中无意义的事。然而,它仍需要您充分理解XML以便做一些超出基本的工作(或者甚至理解某些情况下的错误)。这也许是比学习DOM或JDOM接口都更有意义的工作。
JDOM自身不包含解析器。它通常使用SAX2解析器来解析和验证输入XML文档(尽管它还可以将以前构造的DOM表示作为输入)。它包含一些转换器以将JDOM表示输出成SAX2事件流、DOM模型或XML文本文档。JDOM是在Apache许可证变体下发布的开放源码。
【优点】
①使用具体类而不是接口,简化了DOM的API。
②大量使用了Java集合类,方便了Java开发人员。
【缺点】
①没有较好的灵活性。
②性能较差。
4. DOM4J(Document Object Model for Java)
虽然DOM4J代表了完全独立的开发结果,但最初,它是JDOM的一种智能分支。它合并了许多超出基本XML文档表示的功能,包括集成的XPath支持、XML Schema支持以及用于大文档或流化文档的基于事件的处理。它还提供了构建文档表示的选项,它通过DOM4J API和标准DOM接口具有并行访问功能。从2000下半年开始,它就一直处于开发之中。
为支持所有这些功能,DOM4J使用接口和抽象基本类方法。DOM4J大量使用了API中的Collections类,但是在许多情况下,它还提供一些替代方法以允许更好的性能或更直接的编码方法。直接好处是,虽然DOM4J付出了更复杂的API的代价,但是它提供了比JDOM大得多的灵活性。
在添加灵活性、XPath集成和对大文档处理的目标时,DOM4J的目标与JDOM是一样的:针对Java开发者的易用性和直观操作。它还致力于成为比JDOM更完整的解决方案,实现在本质上处理所有Java/XML问题的目标。在完成该目标时,它比JDOM更少强调防止不正确的应用程序行为。
DOM4J是一个非常非常优秀的Java XML API,具有性能优异、功能强大和极端易用使用的特点,同时它也是一个开放源代码的软件。如今你可以看到越来越多的Java软件都在使用DOM4J来读写XML,特别值得一提的是连Sun的JAXM也在用DOM4J.
【优点】
①大量使用了Java集合类,方便Java开发人员,同时提供一些提高性能的替代方法。
②支持XPath。
③有很好的性能。
【缺点】
①大量使用了接口,API较为复杂。
二、比较
1. DOM4J性能最好,连Sun的JAXM也在用DOM4J。目前许多开源项目中大量采用DOM4J,例如大名鼎鼎的Hibernate也用DOM4J来读取XML配置文件。如果不考虑可移植性,那就采用DOM4J.
2. JDOM和DOM在性能测试时表现不佳,在测试10M文档时内存溢出,但可移植。在小文档情况下还值得考虑使用DOM和JDOM.虽然JDOM的开发者已经说明他们期望在正式发行版前专注性能问题,但是从性能观点来看,它确实没有值得推荐之处。另外,DOM仍是一个非常好的选择。DOM实现广泛应用于多种编程语言。它还是许多其它与XML相关的标准的基础,因为它正式获得W3C推荐(与基于非标准的Java模型相对),所以在某些类型的项目中可能也需要它(如在JavaScript中使用DOM)。
3. SAX表现较好,这要依赖于它特定的解析方式-事件驱动。一个SAX检测即将到来的XML流,但并没有载入到内存(当然当XML流被读入时,会有部分文档暂时隐藏在内存中)。
我的看法:如果XML文档较大且不考虑移植性问题建议采用DOM4J;如果XML文档较小则建议采用JDOM;如果需要及时处理而不需要保存数据则考虑SAX。但无论如何,还是那句话:适合自己的才是最好的,如果时间允许,建议大家讲这四种方法都尝试一遍然后选择一种适合自己的即可。
答:1.java采用unicode编码,2个字节(16位)来表示一个字符, 无论是汉字还是数字,字母,或其他语言都可以存储。
2.char 在java中是2个字节,所以可以存储中文
下面接着再说说两者在应用上的区别:
接口更多的是在系统架构设计方法发挥作用,主要用于定义模块之间的通信契约。而抽象类在代码实现方面发挥作用,可以实现代码的重用,例如,模板方法设计模式是抽象类的一个典型应用,假设某个项目的所有Servlet类都要用相同的方式进行权限判断、记录访问日志和处理异常,那么就可以定义一个抽象的基类,让所有的Servlet都继承这个抽象基类,在抽象基类的service方法中完成权限判断、记录访问日志和处理异常的代码,在各个子类中只是完成各自的业务逻辑代码,伪代码如下:
public abstract classBaseServlet 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 extendsBaseServlet { protected voiddoService(HttpServletRequest request, HttpServletResponse response) throwsIOExcetion,ServletException { 本Servlet只处理的具体业务逻辑代码 } }
父类方法中间的某段代码不确定,留给子类干,就用模板方法设计模式。
备注:这道题的思路是先从总体解释抽象类和接口的基本概念,然后再比较两者的语法细节,最后再说两者的应用区别。比较两者语法细节区别的条理是:先从一个类中的构造方法、普通成员变量和方法(包括抽象方法),静态变量和方法,继承性等6个方面逐一去比较回答,接着从第三者继承的角度的回答,特别是最后用了一个典型的例子来展现自己深厚的技术功底。
答:JavaScript 与Java是两个公司开发的不同的两个产品。Java 是原Sun 公司推出的面向对象的程序设计语言,特别适合于互联网应用程序开发;而JavaScript是Netscape公司的产品,为了扩展Netscape浏览器的功能而开发的一种可以嵌入Web页面中运行的基于对象和事件驱动的解释性语言,它的前身是LiveScript;而Java 的前身是Oak语言。
下面对两种语言间的异同作如下比较:
1)基于对象和面向对象:Java是一种真正的面向对象的语言,即使是开发简单的程序,必须设计对象;JavaScript是种脚本语言,它可以用来制作与网络无关的,与用户交互作用的复杂软件。它是一种基于对象(Object-Based)和事件驱动(Event-Driven)的编程语言。因而它本身提供了非常丰富的内部对象供设计人员使用;
2)解释和编译:Java 的源代码在执行之前,必须经过编译;JavaScript 是一种解释性编程语言,其源代码不需经过编译,由浏览器解释执行;
3)强类型变量和类型弱变量:Java采用强类型变量检查,即所有变量在编译之前必须作声明;JavaScript中变量声明,采用其弱类型。即变量在使用前不需作声明,而是解释器在运行时检查其数据类型;
4)代码格式不一样。
补充:上面列出的四点是原来所谓的标准答案中给出的。其实Java和JavaScript最重要的区别是一个是静态语言,一个是动态语言。目前的编程语言的发展趋势是函数式语言和动态语言。在Java中类(class)是一等公民,而JavaScript中函数(function)是一等公民。对于这种问题,在面试时还是用自己的语言回答会更加靠谱。
答:assertion(断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制。一般来说,assertion用于保证程序最基本、关键的正确性。assertion检查通常在开发和测试时开启。为了提高性能,在软件发布后, assertion检查通常是关闭的。在实现中,断言是一个包含布尔表达式的语句,在执行这个语句时假定该表达式为true;如果表达式计算为false,那么系统会报告一个AssertionError。
断言用于调试目的:
assert(a > 0); // throws an AssertionError if a <= 0
断言可以有两种形式:
assert Expression1;
assert Expression1 : Expression2 ;
Expression1 应该总是产生一个布尔值。
Expression2 可以是得出一个值的任意表达式;这个值用于生成显示更多调试信息的字符串消息。
断言在默认情况下是禁用的,要在编译时启用断言,需使用source 1.4 标记:
javac -source 1.4 Test.java
要在运行时启用断言,可使用-enableassertions 或者-ea 标记。
要在运行时选择禁用断言,可使用-da 或者-disableassertions 标记。
要在系统类中启用断言,可使用-esa 或者-dsa 标记。还可以在包的基础上启用或者禁用断言。可以在预计正常情况下不会到达的任何位置上放置断言。断言可以用于验证传递给私有方法的参数。不过,断言不应该用于验证传递给公有方法的参数,因为不管是否启用了断言,公有方法都必须检查其参数。不过,既可以在公有方法中,也可以在非公有方法中利用断言测试后置条件。另外,断言不应该以任何方式改变程序的状态。
答:UML是统一建模语言(Unified Modeling Language)的缩写,它发表于1997年,综合了当时已经存在的面向对象的建模语言、方法和过程,是一个支持模型化和软件系统开发的图形化语言,为软件开发的所有阶段提供模型化和可视化支持。使用UML可以帮助沟通与交流,辅助应用设计和文档的生成,还能够阐释系统的结构和行为。UML定义了多种图形化的符号来描述软件系统部分或全部的静态结构和动态结构,包括:用例图(use case diagram)、类图(class diagram)、时序图(sequence diagram)、协作图(collaboration diagram)、状态图(statechart diagram)、活动图(activity diagram)、构件图(component diagram)、部署图(deployment diagram)等。在这些图形化符号中,有三种图最为重要,分别是:用例图(用来捕获需求,描述系统的功能,通过该图可以迅速的了解系统的功能模块及其关系)、类图(描述类以及类与类之间的关系,通过该图可以快速了解系统)、时序图(描述执行特定任务时对象之间的交互关系以及执行顺序,通过该图可以了解对象能接收的消息也就是说对象能够向外界提供的服务)。
用例图:



答: XML文档定义分为DTD和Schema两种形式;其本质区别在于Schema本身也是一个XML文件,可以被XML解析器解析。对XML的解析主要有DOM(文档对象模型)、SAX、StAX(JDK 1.6中引入的新的解析XML的方式,Streaming API for XML) 等,其中DOM处理大型文件时其性能下降的非常厉害,这个问题是由DOM 的树结构所造成的,这种结构占用的内存较多,而且DOM 必须在解析文件之前把整个文档装入内存,适合对XML 的随机访问(典型的用空间换取时间的策略);SAX是事件驱动型的XML解析方式,它顺序读取XML文件,不需要一次全部装载整个文件。当遇到像文件开头,文档结束,或者标签开头与标签结束时,它会触发一个事件,用户通过在其回调事件中写入处理代码来处理XML文件,适合对XML 的顺序访问;如其名称所暗示的那样,StAX把重点放在流上。实际上,StAX与其他方法的区别就在于应用程序能够把XML作为一个事件流来处理。将XML作为一组事件来处理的想法并不新颖(事实上 SAX 已经提出来了),但不同之处在于StAX允许应用程序代码把这些事件逐个拉出来,而不用提供在解析器方便时从解析器中接收事件的处理程序。
答: XML的主要作用有两个方面:数据交换(曾经被称为业界数据交换的事实标准,现在此项功能在很多时候都被JSON取代)和信息配置。在做数据交换时,XML将数据用标签组装成起来,然后压缩打包加密后通过网络传送给接收者,接收解密与解压缩后再从XML文件中还原相关信息进行处理。目前很多软件都使用XML来存储配置信息,很多项目中我们通常也会将作为配置的硬代码(hard code)写在XML文件中,Java的很多框架也是这么做的。
Function testE(ss){ var reg=/^[1-9][0-9]{5}$/; if(req.test(ss)){ alert(“邮编OK”) }else{ alert(“邮编格式不正确”); } }
$(“*”).css(“border”,”2px dashed”)
答案:调用jQuery中的ajax函数,设置其async属性来表明是异步还是同步,如下:
$.ajax({
async:true//表示异步,false表示同步
})
答案:兼容firefox的 outerHTML,FF中没有outerHtml的方法
IE下,可以使用()或[]获取集合类对象;Firefox下,只能使用[]获取集合类对象.解决方法:统一使用[]获取集合类对象.
IE下,可以使用获取常规属性的方法来获取自定义属性,也可以使用getAttribute()获取自定义属性;Firefox下,只能使用getAttribute()获取自定义属性.解决方法:统一通过getAttribute()获取自定义属性
$.post(“show”,{uname=”张三”,pwd=”123”},function(data){ alert(data) })
答案:ready 事件的触发,表示文档结构已经加载完成(不包含图片等非文字媒体文件)
onload 事件的触发,表示页面包含图片等文件在内的所有元素都加载完成。
答案:
基本选择器
层次选择器
基本过滤选择器
内容过滤选择器
可见性过滤选择器
属性过滤选择器
子元素过滤选择器
表单选择器
表单过滤选择器
答案:使用reload()即可
Div居中:
margin:auto 0px;
内容居中:
text-align:center;
答案:
存储角度:
Session是服务器端的数据存储技术,cookie是客户端的数据存储技术
解决问题角度:
Session解决的是一个用户不同请求的数据共享问题,cookie解决的是不同请求的请求数据的共享问题
生命周期角度:
Session的id是依赖于cookie来进行存储的,浏览器关闭id就会失效
Cookie可以单独的设置其在浏览器的存储时间。
答案:
赋值角度说明:
null 表示此处没有值,undefined表示此处定义了但是没有赋值
从数据转换角度:
Null在做数值转换时会被转换为0,undefined会被转换为NaN
答案:
区别:doPost用来处理post请求,doGet用来处理get请求,获取参数:获取的参数是相同的都是HttpServletRequest \HttpServletResponse
答案:$(“a[class=view-link]”).attr(“target”,”_blank”)
var scope ="global scope"; function checkscope() { var scope ="local scope”; return function() { return scope} } console.log (checkscope()());
eq返回的是一个jquery对象 get返回的是一个html对象
在启动Weblogic的脚本中(位于所在Domian对应服务器目录下的startServerName),增加set MEM_ARGS=-Xms32m -Xmx200m,可以调整最小内存为32M,最大200M
三次握手是为了防止已失效的连接请求再次传送到服务器端。 二次握手不可行,因为:如果由于网络不稳定,虽然客户端以前发送的连接请求以到达服务方,但服务方的同意连接的应答未能到达客户端。则客户方要重新发送连接请求,若采用二次握手,服务方收到重传的请求连接后,会以为是新的请求,就会发送同意连接报文,并新开进程提供服务,这样会造成服务方资源的无谓浪费
A. | 200ok表示请求成功 |
B. | 400不良请求表示服务器未发现与请求URL匹配内容 |
C. | 404未发现表示由于语法错误儿导致服务器无法理解请求信息 |
D. | 500内部服务器错误,无法处理请求 |
答案:D
分析:
A 200ok 表示的意思是一切正常。一般用于相应GET和POST请求。这个状态码对servlet是缺省的;如果没有调用setStatus方法的话,就会得到200。
B 400 表示指出客户端请求中的语法错误
C 404 客户端所给的地址无法找到任何资源
|
A. | JSP命令 |
B. | JSP Action |
C. | JSP脚本 |
D. | JSP控件 |
答案:C
分析:JSP页面元素构成如下,因此ABD错误
A 200ok 表示的意思是一切正常。一般用于相应GET和POST请求。这个状态码对servlet是缺省的;如果没有调用setStatus方法的话,就会得到200。
B 400 表示指出客户端请求中的语法错误
C 404 客户端所给的地址无法找到任何资源
![]() |
A. | DOM |
B. | CSS |
C. | JavaScript |
D. | XmlHttpRequest |
答案:A
|
A. | HttpSession session=new HttpSession(); |
B. | String haha=session getParameler(:haha”); |
C. | session.removeAttribute(“haha”); |
D. | session.setAttribute(:haha:);XmlHttpRequest |
答案:A
|
答案:1、request对象
request 对象是 javax.servlet.httpServletRequest类型的对象。 该对象代表了客户端的请求信息,主要用于接受通过HTTP协议传送到服务器的数据。(包括头信息、系统信息、请求方式以及请求参数等)。request对象的作用域为一次请求。
2、response对象
response 代表的是对客户端的响应,主要是将JSP容器处理过的对象传回到客户端。response对象也具有作用域,它只在JSP页面内有效。
3、session对象
session 对象是由服务器自动创建的与用户请求相关的对象。服务器为每个用户都生成一个session对象,用于保存该用户的信息,跟踪用户的操作状态。session对象内部使用Map类来保存数据,因此保存数据的格式为 “Key/value”。 session对象的value可以使复杂的对象类型,而不仅仅局限于字符串类型。
4、application对象
application 对象可将信息保存在服务器中,直到服务器关闭,否则application对象中保存的信息会在整个应用中都有效。与session对象相比,application对象生命周期更长,类似于系统的“全局变量”。
5、out 对象
out 对象用于在Web浏览器内输出信息,并且管理应用服务器上的输出缓冲区。在使用 out 对象输出数据时,可以对数据缓冲区进行操作,及时清除缓冲区中的残余数据,为其他的输出让出缓冲空间。待数据输出完毕后,要及时关闭输出流。
6、pageContext 对象
pageContext 对象的作用是取得任何范围的参数,通过它可以获取 JSP页面的out、request、reponse、session、application 等对象。pageContext对象的创建和初始化都是由容器来完成的,在JSP页面中可以直接使用 pageContext对象。
7、config 对象
config 对象的主要作用是取得服务器的配置信息。通过 pageConext对象的 getServletConfig() 方法可以获取一个config对象。当一个Servlet 初始化时,容器把某些信息通过 config对象传递给这个 Servlet。 开发者可以在web.xml 文件中为应用程序环境中的Servlet程序和JSP页面提供初始化参数。
8、page 对象
page 对象代表JSP本身,只有在JSP页面内才是合法的。 page隐含对象本质上包含当前 Servlet接口引用的变量,类似于Java编程中的 this 指针。
9、exception 对象
exception 对象的作用是显示异常信息,只有在包含 isErrorPage="true" 的页面中才可以被使用,在一般的JSP页面中使用该对象将无法编译JSP文件。excepation对象和Java的所有对象一样,都具有系统提供的继承结构。exception 对象几乎定义了所有异常情况。在Java程序中,可以使用try/catch关键字来处理异常情况; 如果在JSP页面中出现没有捕获到的异常,就会生成 exception 对象,并把 exception 对象传送到在page指令中设定的错误页面中,然后在错误页面中处理相应的 exception 对象。
在web.xml中使用如下标签:
<servlet> <servlet-name></servlet-name> <servlet-class></servlet-class> </servlet> <servlet-mapping> <servlet-name></servlet-name> <url-pattern></url-pattern> </servlet-mapping> 或者使用注解方式: @WebServlet(name="servlet", urlPatterns={"/*"})
答: var arr=[1,2,3,4,5,6,7,8]
A. | var obj=( ); |
B. | var obj=[ ]; |
C. | var obj=//; |
D. | var obj=1; |
答案:AC
|
A. | <% %> |
B. | <% ! %> |
C. | <%@ %> |
D. | <%=%> |
答案:B
分析:B <% ! %> 可用作声明
A不正确
C为引用xxx,比如<% @page xxxxx%>
D为表达式
|
HTML含义:
Hyper Text Markup Language 超文本标记语言,是一种用来制作“网页”的简单标记语言;用HTML编写的超文本文档称为HTML文档,HTML文档的扩展名是html或者htm
版本变化:
HTML1.0——在1993年6月作为IETF工作草案发布(并非标准)
HTML 2.0——1995年11月作为RFC 1866发布
HTML 3.2——1997年1月14日,W3C推荐标准
HTML 4.0——1997年12月18日,W3C推荐标准
HTML 4.01(微小改进)——1999年12月24日,W3C推荐标准
HTML 5—2014年10月28日,W3C推荐标准HTML文档结构;
HTML 5.1 - 2016年
HTML 5.2 – 2018年最新版本
HTML 5.3 is coming…
锚链接是带有文本的超链接。可以跳转到页面的某个位置,适用于页面内容较多,超过一屏的场合 。分为页面内的锚链接和页面间的锚链接 。
说明:
1.在标记位置利用a标签的name属性设置标记。
2.在导航位置通过a标签的href属性用#开头加name属性值即可跳转锚点位置。
有些字符,比如说“<”字符,在HTML中有特殊的含义,因此不能在文本中使用。想要在HTML中显示一个小于号“<”,需要用到字符实体:<或者<
字符实体拥有三个部分:一个and符号(&),一个实体名或者一个实体号,最后是一个分号(;)
常用字符实体:
显示结果 | 描述 | 实体 | 实体号 |
空格 | |||
< | 小于 | < | < |
> | 大于 | > | |
& | and符号 | & | & |
' | 单引号 | ' (IE不支持) | ' |
" | 引号 | " | " |
£ | 英镑 | £ | £ |
¥ | 人民币元 | ¥ | ¥ |
§ | 章节 | ¥ | ¥ |
© | 版权 | © | © |
表单的作用:
利用表单可以收集客户端提交的有关信息。
常用表单项类型:
input标签 type属性 |
功能 | input标签 type属性 |
功能 |
text | 单行本框 | reset | 重置按钮 |
password | 密码框 | submit | 提交按钮 |
radio | 单选按钮 | textarea | 文本域 |
checkbox | 复选框 | select | 下拉框 |
button | 普通按钮 | hidden | 隐藏域 |
335.表格、框架、div三种HTML布局方式的特点
优点 | 缺点 | 应用场合 | |
表格 | 方便排列有规律、结构均匀的内容或数据 | 产生垃圾代码、影响页面下载时间、灵活性不大难于修改 | 内容或数据整齐的页面 |
框架 | 支持滚动条、方便导航 节省页面下载时间等 | 兼容性不好,保存时不方便、应用范围有限 | 小型商业网站、论坛后台管理 |
Div | 代码精简、提高页面下载速度、表现和内容分离 | 比较灵活、难于控制 | 复杂的不规则页面、业务种类较多的大型商业网站 |
336.form中input设置为readonly和disabled的区别
readonly | disabled | |
有效对象 | .只针对type为text/password有效 | 对所有表单元素有效 |
表单提交 | 当表单元素设置readonly后,表单提交能将该表单元素的值传递出去。 | 当表单元素设置disabled后,表单提交不能将该表单元素的值传递出去。 |
337.CSS的定义和作用
CSS的定义:CSS是Cascading Style Sheets(层叠样式表)的简称。
CSS是一系列格式规则,它们控制网页内容的外观。CSS简单来说就是用来美化网页用的。
CSS的具体作用包括:
使网页丰富多彩,易于控制。
页面的精确控制,实现精美、复杂页面 。
338.CSS2常用选择器类型及其含义
选择器名称 | 案例 | 语法格式 |
标签选择器 | h3{font-size:24px;font-family:"隶书“; }< h3>JSP< /h3> | 元素标签名{样式属性} |
类选择器 | .red {color:#F00;} < li class="red">Oracle< /li> | . 元素标签class属性值{样式属性} |
ID选择器 | #p1 {background-color:#0F0;} < p id="p1">content< /p> | #元素标签id属性值{样式属性} |
包含选择器 | div h3{color:red;} < div> < h3>CSS层叠样式表< /h3> < /div> | 父元素标签 子元素标签{ 样式属性 } |
子选择器 | div>ul{color:blue;} < div> < ul> < li>测试1 < ol> < li>嵌套元素< /li> < li>嵌套元素< /li> < li>嵌套元素< /li> < li>嵌套元素< /li> < /ol> < /li> < li>测试1< /li> < li>测试1< /li> < /ul> < /div> | 父元素标签名>子元素名{ 样式属性 } |
339.引入样式的三种方式及其优先级别
三种引用方式:
1. 外部样式表(存放.css文件中)
不需要style标签
< link rel=”stylesheet” href=”引用文件地址” />
2. 嵌入式样式表
< style type=“text/css”>
p{color:red;}
< /style>
3.内联样式
标签属性名为style
< p style=“color:red;”>< /p>
优先级级别:内联定义最高、内部CSS次之、外部CSS优先级最低。。
340.盒子模型
盒子模型类似于生活中的盒子,具有4个属性,外边距,内边距,边框,内容。
外边距:margin,用于设置元素和其他元素之间的距离。
内边距:padding,用于设置元素内容和边框之间的距离。
边框:border,用于设置元素边框粗细,颜色,线型。
内容:width,height,用于设置元素内容显示的大小。

例如:
<style> body{ margin: 0; /*取消body默认的外边距*/ } #img1{ width:200px; /*设置图片的宽度*/ border: 2px solid black; /*设置图片边框*/ margin: 5px; /*设置图片外边距(表示该图片与其他图片的距离为5px)*/ padding:10px; /*设置图片与边框之间的距离*/ } #img2{ height: 200px; /* 设置图片的高度*/ border: 2px solid black; /*设置图片的边框*/ margin: 5px; /*设置图片外边距*/ padding: 20px; /*设置图片与边框之间的距离*/ } </style> <img id="img1" src="img/2.jpg" /> <img id="img2" src="img/lss.jpg" />
341.JavaScript语言及其特点
Javascript一种基于对象(object-based)和事件驱动(Event Driven)的简单的并具有安全性能的脚本语言。特点:
解释性: JavaScript不同于一些编译性的程序语言,例如C、C++等,它是一种解释性的程序语言,它的源代码不需要经过编译,而直接在浏览器中运行时被解释。
基于对象: JavaScript是一种基于对象的语言。这意味着它能运用自己已经创建的对象。因此,许多功能可以来自于脚本环境中对象的方法与脚本的相互作用。
事件驱动: JavaScript可以直接对用户或客户输入做出响应,无须经过Web服务程序。它对用户的响应,是以事件驱动的方式进行的。所谓事件驱动,就是指在主页中执行了某种操作所产生的动作,此动作称为“事件”。比如按下鼠标、移动窗口、选择菜单等都可以视为事件。当事件发生后,可能会引起相应的事件响应。
跨平台:JavaScript依赖于浏览器本身,与操作环境无关,只要能运行浏览器的计算机,并支持JavaScript的浏览器就可正确执行。
342.JavaScript常用数据类型有哪些
1、数值型(Number):整数和浮点数统称为数值。例如85或3.1415926等。
2、字符串型(String):由0个,1个或多个字符组成的序列。在JavaScript中,用双引号或单引号括起来表示,如“您好”、‘学习JavaScript’等。 不区分单引号、双引号。
3、逻辑(布尔)型(Boolean):用true或false来表示。
4、空(null)值(Null):表示没有值,用于定义空的或不存在的引用。
要注意,空值不等同于空字符串""或0。
5、未定义(Undefined)值:它也是一个保留字。表示变量虽然已经声明,但却没有赋值。
除了以上五种基本的数据类型之外,JavaScript还支持复合数据类型,包括对象和数组两种。
343.html语法中哪条命令用于使一行文本折行,而不是插入一个新的段落? (B)
A. | < TD> |
B. | < BR> |
C. | < P> |
D. | < H1> |
分析:
A < td>定义标准表格
C < p>表示文本一个段落
D < h1>表示对文本标题进行强调的一种标签
|
344.Ajax的优点和缺点
优点:减轻服务器的负担,按需取数据,最大程度的减少冗余请求,局部刷新页面,减少用户心理和实际的等待时间,带来更好的用户体验,基于xml标准化,并被广泛支持,不需安装插件等,进一步促进页面和数据的分离
缺点:AJAX大量的使用了javascript和ajax引擎,这些取决于浏览器的支持.在编写的时候考虑对浏览器的兼容性.
345.怎样防止表单刷新重复提交问题?(说出思路即可)
JS脚本方式:
第一种:定义全局变量,在form提交前判断是否已有提交过
<script> var checkSubmitFlg = false; function checkSubmit(){ if(checkSubmitFlg == true){ return false; } checkSubmitFlg = true; return true; } </script> <form action="" onsubmit="return checkSubmit();"> </form>
第二种:单击提交按钮后,立刻禁用改按钮
第三种:单击提交按钮后,弹出屏蔽层,防止用户第二次点击
346.JQuery.get()和JQuery.ajax()方法之间的区别是什么?
JQuery.ajax()是对原生的javaScript的ajax的封装,简化了ajax的步骤,用户可用JQuery.ajax()发送get或者post方式请求,Jquery.get()是对ajax的get方式的封装,只能发送get方式的请求。
347.Jquery里的缓存问题如何解决?例如($.ajax()以及$.get())
$.ajax()请求时候加上cache:false的参数,如:
$.ajax({ type : "get", url : "XX", dataType : "json", cache:false, success : function(json) { } });
$.get()请求时候加上时间,如:
$.get("url","data"+new Date(),function(data){});
348.Javascript是面向对象的,怎么体现Javascript的继承关系?
Javascript里面没有像java那样的继承,javascript中的继承机制仅仅是靠模拟的,可以使用prototype原型来实现
349.Javascript的有几种种变量。变量范围有什么不同?
可以分为三种
1、原生类型(string,number,boolean)
2、对象(Date,Array)
3、特殊类型(var vara;(只什么没有定义),var varb = null;(定义一个变量并赋值为null))
350.Js如何获取页面的dom对象
1、直接获取
//1.1 -- id方式获取
var varid = document.getElementById("unameid");
//1.2 -- name获取(获取的是数组对象)
var varname = document.getElementsByName("sex");
//1.3 -- 元素获取(获取的是数组对象)
var varinput = document.getElementsByTagName("input");
2、间接方式获取
//2.1 父子关系 --childNodes
var varchilds = document.getElementById("div01").childNodes;
//2.2 子父关系--parentNode
var varfather2 = document.getElementById("unameid").parentNode;
//2.3 兄弟之间相互获取 nextSibling:下一个节点 previousSibling:上一个节点
351.Servlet API中forward() 与redirect()的区别?
答:为实现程序的模块化,就需要保证在不同的Servlet之间可以相互跳转,而Servlet中主要有两种实现跳转的方式:FORWARD方式与redirect方式。
Forward() : 是服务器内部的重定向,服务器直接访问目标地址的URL,把那个URL的响应内容读取出来,而客户端并不知道,因此在客户端浏览器的地址栏里不会显示跳转后的地址,还是原来的地址。由于在整个定向的过程中用的是同一个Request,因此FORWARD会将Request的信息带到被定向的JSP或Servlet中使用。
Redirect():则是客户端的重定向,是完全的跳转,即客户端浏览器会获取跳转后的地址,然后重新发送请求,因此浏览器中会显示跳转后的地址。同时,由于这种方式比FORWARD方式多了一次网络请求,因此其效率低于FORWARD方式,需要注意到的是,客户端的重定向可以通过设置特定的HTTP 头或写JavaScript脚本来实现。
鉴于以上的区别,一般当FORWARD方式可以满足需求时,尽可能的使用FORWARD方式。但在有些情况下,例如,需要跳转到一个其他服务器上的资源时,则必须使用redirect 方式。
352.Session域和request域什么区别?
作用域:存放数据,获取数据(传递数据)
有效的作用域:生命周期,作用范围
httpServeltRequest:
生命周期:一次请求之间
作用范围:所有被请求转发过的servlet都能获取到
httpSession:
生命周期:一次会话
作用范围:所有的servlet都可以获取到
servletContex:
生命周期:从项目开始运行到服务器关闭
作用范围:所有的servlet都可以获取到
作用域如何选用?
httpServeltRequest:和当前请求有关的信息
httpSession:和当前用户有关的信息
servletContex:访问量比较大,不易更改
353.页面中有一个命名为bankNo的下拉列表,写js脚本获取当前选项的索引值,如果用jquery如何获取
var a = document.getElementsByName("bankNo")[0].value;
var b = $("select[name=bankNo]").val();
354.写出要求11位数字的正则表达式
^[1-9]\d{10}$
355.分别获取指定name、Id的javascript对象,如果用jquey如何获取
js: id--document.getElementById("id"); name--document.getElementsByName("name"); jquery id--$("#id"); name--$("元素名称[name="name值"]");
356.一个页面有两个form,如何获取第一个form
用id方式获取;document.getElementById("id");
357.如何设置一个层的可见/隐藏
可见 : document.getElementById("divid").style.display = "block";
隐藏 : document.getElementById("divid").style.display = "none";
358.描述JSP中动态INCLUDE与静态INCLUDE的区别?
动态导入
1、会将多个jsp页面分别再编写成java文件,编译成class文件
2、jsp文件中允许有相同的变量名,每个页面互不影响
3、当java代码比较多优先选用动态导入
4、效率相对较低,耦合性低
静态导入
1、会将多个jsp页面合成一个jsp页面,再编写成java文件,编译成class文件
2、jsp文件中不允许有相同的变量名
3、当java代码比较少或者没有java代码是优先选用静态导入
4、效率相对较高,耦合性高
359.列举JSP的内置对象及方法
request表示HttpServletRequest对象。它包含了有关浏览器请求的信息,并且提供了几个用于获取cookie, header, 和session数据的有用的方法。
response表示HttpServletResponse对象,并提供了几个用于设置送回 浏览器的响应的方法(如cookies,头信息等)
out对象是javax.jsp.JspWriter的一个实例,提供了几个方法使你能用于向浏览器回送输出结果
pageContext表示一个javax.servlejt.sp.PageContext对象。它是用于方便存取各种范围的名字空间、servlet相关的对象的API,并且包装了通用的servlet相关功能的方法。
session表示一个请求的javax.servlet.http.HttpSession对象。Session可以存贮用户的状态信息
applicaton 表示一个javax.servle.ServletContext对象。这有助于查找有关servlet引擎和servlet环境的信息
config表示一个javax.servlet.ServletConfig对象。该对象用于存取servlet实例的初始化参数。
page表示从该页面产生的一个servlet实例
Exception异常
360.列举jsp的四大作用域
page、request、session、application
361.html和xhtml的区别是什么?
HTML与XHTML之间的差别,粗略可以分为两大类比较:一个是功能上的差别,另外是书写习惯的差别。关于功能上的差别,主要是XHTML可兼容各大浏览器、手机以及PDA,并且浏览器也能快速正确地编译网页。
因为XHTML的语法较为严谨, 所以如果你是习惯松散结构的HTML编写者,那需要特别注意XHTML的规则。但也不必太过担心,因为XHTML的规则并不太难。下面列出了几条容易犯的错误,供大家引用。
1:所有标签都必须小写
在XHTML中,所有的标签都必须小写,不能大小写穿插其中,也不能全部都是大写。看一个例子。
错误:< Head>< /Head>< Body>< /Body>
正确:< head>< /head>< body>< /body>
2:标签必须成双成对
像是< p>...< /p>、< a>...< /a>、< div>...< /div>标签等,当出现一个标签时,必须要有对应的结束标签,缺一不可,就像在任何程序语言中的括号一样。
错误:大家好< p>我是muki
正确:< p>大家好< /p>< p>我是muki< /p>
3:标签顺序必须正确
标签由外到内,一层层包覆着,所以假设你先写div后写h1,结尾就要先写h1后写div。只要记住一个原则“先进后出”,先弹出的标签要后结尾。
错误:< div>< h1>大家好< /div>< /h1>
正确:< div>< h1>大家好< /h1>< /div>
4:所有属性都必须使用双引号
在XHTML 1.0中规定连单引号也不能使用,所以全程都得用双引号。
错误:< div style=font-size:11px>hello< /div>
正确:< div style="font-size:11px">hello< /div
5:不允许使用target="_blank"
从XHTML 1.1开始全面禁止target属性,如果想要有开新窗口的功能,就必须改写为rel="external",并搭配JavaScript实现此效果。
错误:< a href="http://blog.mukispace.com" target="_blank">MUKI space< /a>
正确:< a href="http://blog.mukispace.com" rel="external">MUKI space< /a>
362.你做的页面用哪些浏览器测试过?这些测试的内核分别是什么?
1、Trident内核代表产品Internet Explorer,又称其为IE内核。 Trident(又称为MSHTML),是微软开发的一种排版引擎。使用Trident渲染引擎的浏览器包括:IE、傲游、世界之窗浏览器、Avant、腾讯TT、Netscape 8、NetCaptor、Sleipnir、GOSURF、GreenBrowser和KKman等。
2、Gecko内核代表作品Mozilla ,FirefoxGecko是一套开放源代码的、以C++编写的网页排版引擎。Gecko是最流行的排版引擎之一,仅次于Trident。使用它的最著名浏览器有Firefox、Netscape6至9
3、WebKit内核代表作品Safari、Chromewebkit , 是一个开源项目,包含了来自KDE项目和苹果公司的一些组件,主要用于Mac OS系统,它的特点在于源码结构清晰、渲染速度极快。缺点是对网页代码的兼容性不高,导致一些编写不标准的网页无法正常显示。主要代表作品有Safari和Google的浏览器Chrome。
4、Presto内核代表作品OperaPresto, 是由Opera Software开发的浏览器排版引擎,供Opera 7.0及以上使用。它取代了旧版Opera 4至6版本使用的Elektra排版引擎,包括加入动态功能,例如网页或其部分可随着DOM及Script语法的事件而重新排版。
363.你遇到了哪些浏览器的兼容性问题?怎么解决的?
答:因为不同的浏览器对同一段代码有不同的解析,造成页面显示效果不统一的情况;这是我们常见的兼容性问题。
解决方法:
1、针对不同的浏览器写不同的代码
2、使用jquery屏蔽浏览器差异
遇到不同的兼容问题,需要针对前端进行兼容适配;
364.你知道的常用的js库有哪些?
1.moment.js
举个例子:
用js转换时间戳为日期
let date = new Date(1437925575663); let year = date.getFullYear() + '-'; let month = ( date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1 ) + '-'; let day = date.getDate(); ... return year + month + day; 用moment.js return moment(1437925575663).format('YYYY-MM-DD HH:mm:ss')
2.chart.js
绘制简单的柱状图,曲线图,蛛网图,环形图,饼图等完全够用,用法比较简单。
3.D3.js
功能太强大了,看首页就知道了,感觉没有什么图d3绘不出来的。
4.Rx.js
很好的解决了异步和事件组合的问题。
5.lodash.js
365.Js中的三种弹出式消息提醒(警告窗口、确认窗口、信息输入窗口)的命令是什么?
alter(),confirm(),prompt()
366.谈谈js的闭包
答:闭包无处不在,比如:jQuery、zepto的核心代码都包含在一个大的闭包中,所以下面我先写一个最简单最原始的闭包,以便让你在大脑里产生闭包的画面:
function A(){ function B(){ console.log("Hello Closure!"); } return B; } var C = A(); C();//Hello Closure!
这是最简单的闭包。
有了初步认识后,我们简单分析一下它和普通函数有什么不同,上面代码翻译成自然语言如下:
(1)定义普通函数 A
(2)在 A 中定义普通函数 B
(3)在 A 中返回 B
(4)执行 A, 并把 A 的返回结果赋值给变量 C
(5)执行 C
把这5步操作总结成一句话就是:
函数A的内部函数B被函数A外的一个变量 c 引用。
把这句话再加工一下就变成了闭包的定义:
当一个内部函数被其外部函数之外的变量引用时,就形成了一个闭包。
因此,当你执行上述5步操作时,就已经定义了一个闭包!
这就是闭包。
367.写一段js,遍历所有的li,将每个li的内容逐个alert出来
<body> <ul> <li>张三:123</li> <li>李四:456</li> <li>王五:789</li> <li>赵六:147</li> <ul> <body>
function test(){ var varli = document.getElementsByTagName("li"); for (var i=0;i<varli.length;i++) { alert(varli[i].innerText); } }
368.页面上如何用JavaScript对多个checkbox全选
//全选 function checkAll(){ //获取复选框对象--数组对象 var varcheck = document.getElementsByName("name"); //alert(varcheck.length); //遍历for for(var i=0;i<varcheck.length;i++){ varcheck[i].checked = true; } }
369.写一个简单的JQuery的ajax
<script type="text/javascript" src="js/jquery-1.9.1.js" charset="utf-8"></script> <script type="text/javascript"> function testJqAjax(){ //url :请求地址 //type :请求的方式 get/post //data :请求的参数(json/String) //cache:true(走缓存 ) false(不走缓存) //result:当ajax发送成功后会调用success后面的函数,result:相当于形参,返回的数据 //async:是否为异步请求 默认true异步 , false同步 $.ajax({ url:"TestJqAjax", type:"get", /* data:"uname=zhangsan&realname=张三丰", */ data:{uname:"zhangsan",realname:"张三丰"}, cache:false, async:false, success:function(result){ alert(result); } }); } //ajax的get方式的请求 function jqAjaxGet(){ //url,[data],[callback](当ajax发送成功后调用的函数) $.get("TestJqAjax",{uname:"zhangsan",realname:"张三丰"},function(result){ alert(result); }); } function jqAjaxPost() { //url,[data],[callback](当ajax发送成功后调用的函数) $.post("TestJqAjax",{uname:"zhangsan",realname:"张三丰"},function(result){ alert(result); }); } </script>
370.Js截取字符串abcdefg的efg
function test2(){ var str = "abcdefg"; var substr = str.substring(4); alert(substr); }
371.http的请求头信息包含了什么?
请求行(请求方式,资源路径,协议和协议版本号)
若干请求头
请求实体内容
372.http的响应码200,404,302,500表示的含义分别是?
200 - 确定。客户端请求已成功
302 - 临时移动转移,请求的内容已临时移动新的位置
404 - 未找到文件或目录
500 - 服务器内部错误
373.Servlet中request对象的方法有?
/获取网络信息 private void getNet(HttpServletRequest req, HttpServletResponse resp) { System.out.println("TestHttpRequest.getNet(获取客户端的ip):"+req.getRemoteAddr()); System.out.println("TestHttpRequest.getNet(获取客户端的端口):"+req.getRemotePort()); System.out.println("TestHttpRequest.getNet(获取服务器的ip):"+req.getLocalAddr()); System.out.println("TestHttpRequest.getNet(获取服务器的端口):"+req.getLocalPort()); } //获取实体内容 private void getContent(HttpServletRequest req, HttpServletResponse resp) { //获取单条信息 String uname = req.getParameter("uname"); //获取多条信息,数组格式 String[] favs = req.getParameterValues("fav"); //遍历数组 //判断 if(favs!=null&&favs.length>0){ for (int i = 0; i < favs.length; i++) { System.out.println("TestHttpRequest.getContent(fav):"+favs[i]); } } String un = req.getParameter("un"); System.out.println("TestHttpRequest.getContent():"+uname+"--"+favs+"--"+un); } //获取请求头信息 private void getHeads(HttpServletRequest req, HttpServletResponse resp) { //获取单条头信息 //System.out.println("TestHttpRequest.getHeads(获取请求头信息-浏览器头信息):"+req.getHeader("User-Agent")); //获取所有头信息--返回枚举类型 Enumeration strHeads = req.getHeaderNames(); //遍历枚举类型 while (strHeads.hasMoreElements()) { String strhead = (String) strHeads.nextElement(); System.out.println("TestHttpRequest.getHeads(获取头信息):"+req.getHeader(strhead)); } } //获取请求行的信息 private void getLines(HttpServletRequest req, HttpServletResponse resp) { System.out.println("TestHttpRequest.getLines(请求方式***):"+req.getMethod()); System.out.println("TestHttpRequest.getLines(资源路径):"+req.getRequestURI()); System.out.println("TestHttpRequest.getLines(地址):"+req.getRequestURL()); System.out.println("TestHttpRequest.getLines(协议):"+req.getScheme()); System.out.println("TestHttpRequest.getLines(协议的版本号):"+req.getProtocol()); System.out.println("TestHttpRequest.getLines(获取参数信息):"+req.getQueryString()); System.out.println("TestHttpRequest.getLines(项目名称***):"+req.getContextPath()); }
374.Javascript的常用对象有哪些
常用对象包括日期对象Date,字符串对象String,数组对象Array
//获取并显示系统当前时间
function testDate(){ var date = new Date(); var fmtDate = date.getFullYear()+"-"+(date.getMonth()+1)+ "-"+date.getDate()+"-"+date.getHours() +":"+date.getMinutes()+":"+date.getSeconds(); alert(fmtDate); }
//获取出’sxt’的下标位置
function testString(){ var str = 'welcome to beijingsxt'; alert(str.indexOf('sxt')); }
//遍历数组信息
function testArray(){ var arr = new Array('a',123,'c',true,'e'); for(var item in arr){ document.write(arr[item]+" "); } }
375.DOM和BOM及其关系
BOM浏览器对象模型,由一系列对象组成,是访问、控制、修改浏览器的属性的方法。
DOM文档对象模型,由一系列对象组成,是访问、检索、修改XHTML文档内容与结构的标准方法。

关系:
–BOM描述了与浏览器进行交互的方法和接口
–DOM描述了处理网页内容的方法和接口
–DOM属于BOM的一个属性
376.JavaScript中获取某个元素的三种方式JavaScript中的三种弹出式消息提醒命令是什么?
window.alert() 显示一个提示信息
window.confirm() 显示一个带有提示信息、确定和取消按钮的对话框
window.prompt() 显示可提示用户输入的对话框
setTimeout与setInterval 的区别
setTimeout和setInterval的语法相同。它们都有两个参数,一个是将要执行的代码字符串,还有一个是以毫秒为单位的时间间隔,当过了那个时间段之后就将执行那段代码。
不过这两个函数还是有区别的,setInterval在执行完一次代码之后,经过了那个固定的时间间隔,它还会自动重复执行代码,而setTimeout只执行一次那段代码。
window.setTimeout("function",time);//设置一个超时对象,只执行一次,无周期
window.setInterval("function",time);//设置一个超时对象,周期='交互时间'
377.JavaScript操作CSS的两种方式
第一种方式:操作元素的属性(对象.style.样式名=样式值;)
//改变直接样式
var child2 = document.createElement("div"); child2.innerHTML = "child2"; child2.style.fontWeight = "bold"; parent.appendChild(child2);
第二种方式:操作元素的类(对象.className=类;)
例如:
var parent = document.getElementById("parent"); //改变className var child0 = document.createElement("div"); child0.innerHTML = "child0"; child0.className = "newDiv"; parent.appendChild(child0);
378.静态网页和动态网页的联系和区别
联系:
1)静态网页是网站建设的基础,静态网页和动态网页都要使用到HTMl语言。
2)静态网页是相对于动态网页而言,指没有后台数据库、不含程序和不可交互的网页、是标准的HTML文件,它的文件扩展名是.htm或.html。你编的是什么它显示的就是什么、不会有任何改变。
3)静态网页和动态网页之间并不矛盾,为了网站适应搜索引擎检索的需要,动态网站可以采用静动结合的原则,适合采用动态网页的地方用动态网页,如果必要使用静态网页,则可以考虑用静态网页的方法来实现,在同一个网站上,动态网页内容和静态网页内容同时存在也是很常见的事情。
区别:
1)程序是否在服务器端运行,是重要标志。在服务器端运行的程序、网页、组件,属于动态网页,它们会随不同客户、不同时间,返回不同的网页,例如ASP、PHP、JSP、ASP.net、CGI等。运行于客户端的程序、网页、插件、组件,属于静态网页,例如html页、Flash、javascript、VBscript等等,它们是永远不变的。
2)编程技术不同。静态网页和动态网页主要根据网页制作的语言来区分。静态网页使用语言:HTML。 动态网页使用语言:HTML+ASP 或 HTML+PHP 或 HTML+JSP 等其它网站动态语言。
3)被搜索引擎收录情况不同。由于编程技术不容,静态网页是纯粹HTML格式的网页,页面内容稳定,不论是网页是否被访问,页面都被保存在网站服务器上,很容易被搜索引擎收录。而动态网页的内容是当用户点击请求时才从数据库中调出返回给用户一个网页的内容,并不是存放在服务器上的独立文件,相比较于静态网页而言,动态网页很难被搜索引擎收录。
4)用户访问速度不同。用户访问动态网页时,网页在获得搜索指令后经过数据库的调查匹配,再将与指令相符的内容传递给服务器,通过服务器的编译将网页编译成标准的HTML代码,从而传递给用户浏览器,多个读取过程大大降低了用户的访问速度。而静态网页不同,由于网页内容直接存取在服务器上,省去了服务器的编译过程,用户访问网页速度很快。
5)制作和后期维护工作量不同。动态网页的设计以数据库技术为基础,可以实现多种功能,降低了网站维护的工作量。而静态网页由于没有数据库的支持,网页内容更改时需要直接修改代码,在网站内容制作和维护中,所需的工作量更大。动态网页与静态网页各有特点,网站设计师在网页设计时,主要根据网站的功能需求和网站内容多少选择不同网页。如,网站包含信息量太大时,就需要选择动态网页,反之,则选择静态网页。
379.JSP/ASP/PHP的比较
ASP(Active Server Pages),JSP(JavaServer Pages),PHP(Hypertext Preprocessor)是目前主流的三种动态网页语言。
ASP是微软(Microsoft)所开发的一种后台脚本语言,它的语法和Visual BASIC类似,可以像SSI(Server Side Include)那样把后台脚本代码内嵌到HTML页面中。虽然ASP简单易用,但是它自身存在着许多缺陷,最重要的就是安全性问题。
PHP是一种跨平台的服务器端的嵌入式脚本语言。它大量地借用C,Java和Perl语言的语法, 并耦合PHP自己的特性,使WEB开发者能够快速地写出动态产生页面。它支持目前绝大多数数据库。
JSP是一个简化的Servlet,它是由Sun公司倡导、许多公司参与一起建立的一种动态网页技术标准。JSP技术有点类似ASP技术,它是在传统的网页HTML中插入Java程序段和JSP标记(tag),从而形成JSP文件,后缀名为(*.jsp)。 用JSP开发的Web应用是跨平台的,既能在Linux下运行,也能在其他操作系统上运行。
ASP优点: 无需编译、易于生成、独立于浏览器、面向对象、与任何ActiveX scripting 语言兼容、源程序码不会外漏。
缺点:
1)Windows本身的所有问题都会一成不变的也累加到了它的身上。安全性、稳定性、跨平台性都会因为与NT的捆绑而显现出来。
2)ASP由于使用了COM组件所以它会变的十分强大,但是这样的强大由于Windows NT系统最初的设计问题而会引发大量的安全问题。只要在这样的组件或是操作中一不注意,那么外部攻击就可以取得相当高的权限而导致网站瘫痪或者数据丢失。
3)还无法完全实现一些企业级的功能:完全的集群、负载均横。
PHP优点:
1)一种能快速学习、跨平台、有良好数据库交互能力的开发语言。
2)简单轻便,易学易用。
3 ) 与Apache及其它扩展库结合紧密。
缺点:
1 ) 数据库支持的极大变化。
2 ) 不适合应用于大型电子商务站点。
JSP优点:
1 ) 一处编写随处运行。
2 ) 系统的多台平支持。
3 ) 强大的的可伸缩性。
4 ) 多样化和功能强大的开发工具支持。
缺点:
1) 与ASP一样,Java的一些优势正是它致命的问题所在。
2 ) 开发速度慢
380.CGI/Servlet/JSP的比较
CGI(Common Gateway Interface),通用网关接口,是一种根据请求信息动态产生回应内容的技术。
通过CGI,Web 服务器可以将根据请求不同启动不同的外部程序,并将请求内容转发给该程序,在程序执行结束后,将执行结果作为回应返回给客户端。也就是说,对于每个请求,都要产生一个新的进程进行处理。
Servlet 是在服务器上运行的小程序。在实际运行的时候Java Servlet与Web服务器会融为一体。与CGI不同的是,Servlet对每个请求都是单独启动一个线程,而不是进程。这种处理方式大幅度地降低了系统里的进程数量,提高了系统的并发处理能力。
比较:
1) JSP从本质上说就是Servlet。JSP技术产生于Servlet之后,两者分工协作,Servlet侧重于解决运算和业务逻辑问题,JSP则侧重于解决展示问题。
2 ) 与CGI相比,Servlet效率更高。Servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁。而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于Servlet 。
3)与CGI相比,Servlet更容易使用,功能更强大,具有更好的可移植性,更节省投资。在未来的技术发展过程中,Servlet有可能彻底取代CGI。
381.HTTP协议工作原理及其特点
超文本传输协议(HTTP:Hypertext Transport Protocol)是万维网应用层的协议,它通过两个程序实现:一个是客户端程序(各种浏览器),另一个是服务器 (常称Web服务器) 。这两个通常运行在不同的主机上,通过交换报文来完成网页请求和响应,报文可简单分为请求报文和响应报文。
工作原理(流程):
客户机与服务器建立连接后,浏览器可以向web服务器发送请求并显示收到的网页,当用户在浏览器地址栏中输入一个URL或点击一个超连接时,浏览器就向服务器发出了HTTP请求,请求方式的格式为:统一资源标识符、协议版本号,后边是MIME(Multipurpose Internet Mail Extensions)信息包括请求修饰符、客户机信息和可能的内容。该请求被送往由URL指定的WEB服务器,WEB服务器接收到请求后,进行相应反映,其格式为:一个状态行包括信息的协议版本号、一个成功或错误的代码,后边服务器信息、实体信息和可能的内容。即以HTTP规定的格式送回所要求的文件或其他相关信息,再由用户计算机上的浏览器负责解释和显示。

特点:
1)支持客户/服务器模式。
2)简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
3)灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
4)无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
5)无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
382.get和post的区别
1. Get是不安全的,因为在传输过程,数据被放在请求的URL中;Post的所有操作对用户来说都是不可见的。
2. Get传送的数据量较小,这主要是因为受URL长度限制;Post传送的数据量较大,一般被默认为不受限制。
3. Get限制Form表单的数据集的值必须为ASCII字符;而Post支持整个ISO10646字符集。
4. Get执行效率却比Post方法好。Get是form提交的默认方法。
383.如何解决表单提交的中文乱码问题
1)设置页面编码,若是jsp页面,需编写代码
<%@page language="java" pageEncoding="UTF-8" contentType="text/html;charset=UTF-8" %>
若是html页面,在网页头部(< head>< /head>)中添加下面这段代码
< meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
2)将form表单提交方式变为post方式,即添加method="post";)在Servlet类中编写代码request.setCharacterEncoding("UTF-8"),而且必须写在第一行。
3)如果是get请求,在Servlet类中编写代码
byte [] bytes = str.getBytes("iso-8859-1");
String cstr = new String(bytes,"utf-8");
或者直接修改Tomcat服务器配置文件server.xml增加内容:
URIEncoding="utf-8"
384.绝对路径、根路径、相对路径的含义及其区别
绝对路径指对站点的根目录而言某文件的位置,相对路径指以当前文件所处目录而言某文件的位置,相对路径-以引用文件之网页所在位置为参考基础,而建立出的目录路径。绝对路径-以Web站点根目录为参考基础的目录路径。
先给出一个网站结构图做实例加深理解,A网站(域名为http://www.a.com):/include/a-test.html,/img/a-next.jpg;B网站(域名为http://www.b.com):/include/b-test.html,/img/b-next.jpg。
相对路径是从引用的网页文件本身开始构建的,如果在A网站中的a-test.html中要插入图片a-next.jpg,可以这样做:< img src="../img/a-next.jpg" />,重点是img前面的../,表示从html处于的include开始起步,输入一个../表示回到上面一级父文件夹下,然后再接着img/表示又从父级文件夹下的img文件开始了,最后定位img下面的next.jpg。
根路径是从网站的最底层开始起,一般的网站的根目录就是域名下对应的文件夹,就如D盘是一个网站,双击D盘进入到D盘看到的就是网站的根目录,这种路径的链接样式是这样的:如果在A网站中的a-test.html中要插入图片a-next.jpg,可以这样做:< img src="/img/a-next.jpg" >,以/开头表示从网站根目录算起,找到根目录下面的img文件夹下的next.jpg。
绝对路径就很好理解了,这种路径一般带有网站的域名,如果在A网站中的a-test.html中要插入图片a-next.jpg,需要这样这样写:< img src="http://www.a.com/img/a-next.jpg" >,将图片路径上带有了域名信息,再打个比方:如果在A网站中的a-test.html中要插入B网站的图片b-next.jpg,就需要这样写:< img src="http://www.b.com/img/b-next.jpg" >,这种方法适用与在不同网站之间插入外部网站的图片。
385.如实现servlet的单线程模式
实现servlet的单线程的jsp命令是: <%@ page isThreadSafe=”false”%>。默认isThreadSafe值为true。
属性isThreadSafe=false模式表示它是以Singleton模式运行,该模式implements了接口SingleThreadMode, 该模式同一时刻只有一个实例,不会出现信息同步与否的概念。若多个用户同时访问一个这种模式的页面,那么先访问者完全执行完该页面后,后访问者才开始执行。
属性isThreadSafe=true模式表示它以多线程方式运行。该模式的信息同步,需访问同步方法(用synchronized标记的)来实现。 一般格式如下:
public synchronized void syncmethod(...){ while(...) { this.wait(); } this.notifyAll(); }
386.Servlet的生命周期
1、加载:在下列时刻加载 Servlet:(1)如果已配置自动加载选项,则在启动服务器时自动
2、加载 (web.xml中设置< load-on-start>);(2)在服务器启动后,客户机首次向 Servlet 发出请求时;(3)重新加载 Servlet 时(只执行一次)
3、实例化:加载 Servlet 后,服务器创建一个 Servlet 实例。(只执行一次)
4、初始化:调用 Servlet 的 init() 方法。在初始化阶段,Servlet 初始化参数被传递给 Servlet 配置对象ServletConfig。 (只执行一次)
5、请求处理:对于到达服务器的客户机请求,服务器创建针对此次请求的一个“请求”对象和一个“响应”对象。服务器调用 Servlet 的 service() 方法,该方法用于传递“请求”和“响应”对象。service() 方法从“请求”对象获得请求信息、处理该请求并用“响应”对象的方法以将响应传回客户机。service() 方法可以调用其它方法来处理请求,例如 doGet()、doPost() 或其它的方法。(每次请求都执行该步骤)
6、销毁:当服务器不再需要 Servlet, 或重新装入 Servlet 的新实例时,服务器会调用 Servlet 的 destroy() 方法。(只执行一次)
387.转发和重定向的区别
转发是在服务端直接做的事情,是对客户端的同一个request进行传递,浏览器并不知道。重定向是由浏览器来做的事情。重定向时,服务端返回一个response,里面包含了跳转的地址,由浏览器获得后,自动发送一个新request。转发像呼叫转移或者110报警中心,重定向似114查号台。

a) 区别1:跳转效率的不同
转发效率相对高;重定向效率相对低
b) 区别2:实现语句不同
转发 request.getRequestDispatcher("xxxx").forward(request,response) ;
重定向 response.sendRedirect("xxxx")
c) 区别3:是否共有同一个request的数据
转发源组件与目标组件共有同一个request数据
重定向源组件与目标组件不共有同一个request数据(可使用session共有数据)
d) 区别4:浏览器URL地址的不同
转发后浏览器URL地址保持不变(源组件地址)
重定向后浏览器URL地址改变为重定向后的地址(目标组件地址)
e) 区别5:"/"路径的含义不同
转发时"/"代表当前项目的根路径 ;重定向时"/"代表当前服务器的根路径
f) 区别6:跳转范围的不同
只能转发到同一应用中的URL(默认) ;可以重定向任何服务器、任何应用的URL
g) 区别7:刷新是否导致重复提交
转发会导致重复提交(可以通过同步令牌解决);重定向不会导致重复提交
h) 区别8:是否经过过滤器
转发不经过过滤器(默认情况);重定向经过过滤器
388.JSP的执行过程
在JSP运行过程中,首先由客户端发出请求,Web服务器接收到请求后,如果是第一次访问某个jsp页面,Web服务器对它进行以下3个操作。
1)翻译:由.jsp变为.java,由JSP引擎实现。
2)编译:由.java变为.class,由 Java编译器实现。
3)执行:由.class变为.html,用Java虚拟机执行编译文件,然后将执行结果返回给Web服务器,并最终返回给客户端
如果不是第一次访问某个JSP页面,则只执行第三步。所以第一次访问JSP较慢。
389.JSP动作有哪些,简述作用?
jsp:include:在页面被请求的时候引入一个文件。
jsp:useBean:寻找或者实例化一个JavaBean。 jsp:setProperty:设置JavaBean的属性。
jsp:getProperty:输出某个JavaBean的属性。
jsp:forward:把请求转到一个新的页面。 jsp:plugin:根据浏览器类型为Java插件生成OBJECT或EMBED标记。
390.page/request/session/application作用域区别
page:当前页面范围
request:当前页面范围+转发页面(forward)+包含页面(include)
session:当前会话:session在以下几种情况下失效
1)销毁session:Session.invalidate();
2)超过最大非活动间隔时间
3)手动关闭浏览器(session并没有立刻失效,因为服务器端session仍旧存在,超过最大非活动间隔时间后真正失效)
application:当前应用;服务器重新启动前一直有效
391.JSP和Servlet的区别和联系
区别:
1)JSP是在HTML代码里写JAVA代码,框架是HTML;而Servlet是在JAVA代码中写HTML代码,本身是个JAVA类。
2)JSP使人们把显示和逻辑分隔成为可能,这意味着两者的开发可并行进行;而Servlet并没有把两者分开。
3)Servlet独立地处理静态表示逻辑与动态业务逻辑.这样,任何文件的变动都需要对此服务程序重新编译;JSP允许用特殊标签直接嵌入到HTML页面, HTML内容与JAVA内容也可放在单独文件中,HTML内容的任何变动会自动编译装入到服务程序.
4)Servlet需要在web.xml中配置,而JSP无需配置。
5)目前JSP主要用在视图层,负责显示,而Servlet主要用在控制层,负责调度
联系:
1)都是Sun公司推出的动态网页技术。
2)先有Servlet,针对Servlet缺点推出JSP。JSP是Servlet的一种特殊形式,每个JSP页面就是一个Servlet实例——JSP页面由系统翻译成Servlet,Servlet再负责响应用户请求。
392.谈谈过滤器原理及其作用?
原理:
过滤器是运行在服务器端的一个拦截作用的web组件,一个请求来到时,web容器会判断是否有过滤器与该信息资源相关联,如果有则交给过滤器处理,然后再交给目标资源,响应的时候则以相反的顺序交给过滤器处理,最后再返回给用户浏览器
一般用于日志记录、性能、安全、权限管理等公共模块。
过滤器开发:
过滤器是一个实现了javax.servlet.Filter接口的java类
主要业务代码放在doFilter方法中
业务代码完成后要将请求向后传递,即调用FilterChain对象的doFilter方法
配置:
在web.xml中增加如下代码 <filter> <filter-name>MyFilter</filter-name> <filter-class>Filter完整类名</filter-class> </filter> <filter-mapping> <filter-name>MyFilter</filter-name> <url-pattern>/*(要过虑的url,此处*表示过虑所有的url)</url-pattern> </filter-mapping>
谈谈监听器作用及其分类?
监听器也叫Listener,是一个实现特定接口的java类,使用时需要在web.xml中配置,它是web服务器端的一个组件,它们用于监听的事件源分别为SerlvetConext,HttpSession和ServletRequest这三个域对象
主要有以下三种操作:
–监听三个域对象创建和销毁的事件监听器
–监听域对象中属性的增加和删除的事件监听器
–监听绑定到HttpSession域中的某个对象的状态的时间监听器
接口分类:
–ServletContextListener
HttpSessionListener
–ServletRequestListener
–ServletContextAttributeListener
–HttpSessionAttributeListener
ServletRequestAttributeListener
–HttpSessionBindingListener(不需要配置)
–HttpSessionActivationListener(不需要配置)
配置:
< listener>< listener-class>实现以上任意接口的java类全名< /listener-class>< /listener>
393.jQuery相比JavaScript的优势在哪里
jQuery的语法更加简单。
jQuery消除了JavaScript跨平台兼容问题。
相比其他JavaScript和JavaScript库,jQuery更容易使用。
jQuery有一个庞大的库/函数。
jQuery有良好的文档和帮助手册。
jQuery支持AJAX
394.DOM对象和jQuery对象的区别及其转换
DOM对象,是我们用传统的方法(javascript)获得的对象,jQuery对象即是用jQuery类库的选择器获得的对象,它是对DOM对象的一种封装,jQuery对象不能使用DOM对象的方法,只能使用jQuery对象自己的方法。
普通的dom对象一般可以通过$()转换成jquery对象
如:var cr=document.getElementById("cr"); //dom对象
var $cr = $(cr); //转换成jquery对象
由于jquery对象本身是一个集合。所以如果jquery对象要转换为dom对象则必须取出其中的某一项,一般可通过索引取出
如:$("#msg")[0],$("div").eq(1)[0],$("div").get()[1],$("td")[5]这几种语法在jQuery中都是合法的
395.jQuery中$的作用主要有哪些
1)$用作选择器
例如:根据id获得页面元素$("#元素ID")
2)$相当于window.onload 和 $(document).ready(...)
例如:$(function(){...}); function(){...}会在DOM树加载完毕之后执行。
3)$用作JQuery的工具函数的前缀
例如: var str = ' Welcome to shanghai.com ';
str = $.trim(str);去掉空格
4)$(element):把DOM节点转化成jQuery节点
例如:var cr=document.getElementById("cr"); //dom对象
var $cr = $(cr); //转换成jquery对象
5)$(html):使用HTML字符串创建jQuery节点
例如:var obj = $("< div>尚学堂,实战化教学第一品牌< /div>")
396.Ajax含义及其主要技术
Ajax (Asynchronous JavaScript and XML 阿贾克斯)不是一个新的技术,事实上,它是一些旧有的成熟的技术以一种全新的更加强大的方式整合在一起。
Ajax的关键技术:
1)使用CSS构建用户界面样式,负责页面排版和美工
2)使用DOM进行动态显示和交互,对页面进行局部修改
3)使用XMLHttpRequest异步获取数据
4)使用JavaScript将所有元素绑定在一起
397.Ajax的工作原理
Ajax的原理简单来说通过XmlHttpRequest对象来向服务器发异步请求,从服务器获得数据,然后用javascript来操作DOM而更新页面。这其中最关键的一步就是从服务器获得请求数据。要清楚这个过程和原理,我们必须对 XMLHttpRequest有所了解。
XMLHttpRequest是ajax的核心机制,它是在IE5中首先引入的,是一种支持异步请求的技术。简单的说,也就是javascript可以及时向服务器提出请求和处理响应,而不阻塞用户。达到无刷新的效果。
398.JSON及其作用
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,采用完全独立于语言的文本格式,是理想的数据交换格式。同时,JSON是 JavaScript 原生格式,这意味着在 JavaScript 中处理 JSON数据不须要任何特殊的 API 或工具包。
在JSON中,有两种结构:对象和数组。
{} 对象
[] 数组
, 分隔属性
: 左边为属性名,右边为属性值
属性名可用可不用引号括起,属性值为字符串一定要用引号括起
举例:
varo={ "xlid": "cxh", "xldigitid": 123456, "topscore": 2000, "topplaytime": "2009-08-20" }; jsonranklist=[ { "xlid": "cxh", "xldigitid": 123456, "topscore": 2000, "topplaytime": "2009-08-20" }, { "xlid": "zd", "xldigitid": 123456, "topscore": 1500, "topplaytime": "2009-11-20" } ];
399.文件上传组件Common-fileUpload的常用类及其作用?
DiskFileItemFactory:磁盘文件工厂类,设置上传文件保存的磁盘目录,缓冲区大小。
ServletFileUpload:上传处理类,此类真正读取客户上传的文件,同时可以设置最大接收大小。
FileItem:上传的文件对象,可以是多个文件,每个上传的文件都是一个单独的FileItem对象。
400.说出Servlet的生命周期,并说出Servlet和CGI的区别?
答:Web容器加载Servlet并将其实例化后,Servlet生命周期开始,容器运行其init()方法进行Servlet的初始化;请求到达时调用Servlet的service方法,service方法会调用与请求对应的doGet或doPost等方法;当服务器关闭会项目被卸载时服务器会将Servlet实例销毁,此时会调用Servlet的destroy方法。Servlet与CGI的区别在于Servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI 对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于Servlet。
【补充1】SUN公司在1996年发布Servlet技术就是为了和CGI进行竞争,Servlet是一个特殊的Java程序,一个基于Java的Web应用通常包含一个或多个Servlet类。 Servlet不能够自行创建并执行,它是在Servlet容器中运行的,容器将用户的请求传递给Servlet程序,此外将Servlet的响应回传给用户。通常一个Servlet会关联一个或多个JSP页面。以前CGI经常因为性能开销上的问题被诟病,然而Fast CGI早就已经解决了CGI效率上的问题,所以面试的时候大可不必诟病CGI,腾讯的网站就使用了CGI技术,相信你也没感觉它哪里不好。
【补充2】Servlet接口定义了5个方法,其中前三个方法与Servlet生命周期相关:
void init(ServletConfig config) throws ServletException
void service(ServletRequest req, ServletResponse resp) throws ServletException, java.io.IOException
void destory()
java.lang.String getServletInfo()
ServletConfig getServletConfig()
401.JSP 和Servlet 有有什么关系?
答:其实这个问题在上面已经阐述过了,Servlet是一个特殊的Java程序,它运行于服务器的JVM中,能够依靠服务器的支持向浏览器提供显示内容。JSP本质上是Servlet的一种简易形式, JSP会被服务器处理成一个类似于Servlet的Java程序,可以简化页面内容的生成。Servlet和JSP最主要的不同点在于,Servlet 的应用逻辑是在Java 文件中,并且完全从表示层中的HTML分离开来。而JSP的情况是Java和HTML可以组合成一个扩展名为.jsp 的文件(有人说,Servlet就是在Java中写HTML,而JSP就是在HTML中写Java代码,当然,这个说法还是很片面的)。JSP侧重于视图,Servlet更侧重于控制逻辑,在MVC架构模式中,JSP适合充当视图(view)而Servlet适合充当控制器(controller)。
402.JSP中的四种作用域?
答:page、request、session和application,具体如下:
①page 代表与一个页面相关的对象和属性。
②request 代表与Web客户机发出的一个请求相关的对象和属性。一个请求可能跨越多个页面,涉及多个Web 组件;需要在页面显示的临时数据可以置于此作用域
③session代表与某个用户与服务器建立的一次会话相关的对象和属性。跟某个用户相关的数据应该放在用户自己的session中
④application代表与整个Web应用程序相关的对象和属性,它实质上是跨越整个Web应用程序,包括多个页面、请求和会话的一个全局作用域。
403.如何实现JSP或Servlet的单线程模式?
<%@page isThreadSafe=”false”%>
【补充】Servlet默认的工作模式是单实例多线程,如果Servlet实现了标识接口SingleThreadModel又或是JSP页面通过page指令设置isThreadSafe属性为false,那么它们生成的Java代码会以单线程多实例方式工作。显然,这样做会导致每个请求创建一个Servlet实例,这种实践将导致严重的性能问题。
404.实现会话跟踪的技术有哪些?
答:由于HTTP协议本身是无状态的,服务器为了区分不同的用户,就需要对用户会话进行跟踪,简单的说就是为用户进行登记,为用户分配唯一的ID,下一次用户在请求中包含此ID,服务器据此判断到底是哪一个用户。
①URL 重写:在URL中添加用户会话的信息作为请求的参数,或者将唯一的会话ID添加到URL结尾以标识一个会话。
②设置表单隐藏域:将和会话跟踪相关的字段添加到隐式表单域中,这些信息不会在浏览器中显示但是提交表单时会提交给服务器。
这两种方式很难处理跨越多个页面的信息传递,因为如果每次都要修改URL或在页面中添加隐式表单域来存储用户会话相关信息,事情将变得非常麻烦。
③cookie:cookie有两种,一种是基于窗口的,浏览器窗口关闭后,cookie就没有了;另一种是将信息存储在一个临时文件中,并设置存在的时间。当用户通过浏览器和服务器建立一次会话后,会话ID就会随响应信息返回存储在基于窗口的cookie中,那就意味着只要浏览器没有关闭,会话没有超时,下一次请求时这个会话ID又会提交给服务器让服务器识别用户身份。会话中可以为用户保存信息。会话对象是在服务器内存中的,而基于窗口的cookie是在客户端内存中的。如果浏览器禁用了cookie,那么就需要通过下面两种方式进行会话跟踪。当然,在使用cookie时要注意几点:首先不要在cookie中存放敏感信息;其次cookie存储的数据量有限(4k),不能将过多的内容存储cookie中;再者浏览器通常只允许一个站点最多存放20个cookie。当然,和用户会话相关的其他信息(除了会话ID)也可以存在cookie方便进行会话跟踪。
④HttpSession:在所有会话跟踪技术中,HttpSession对象是最强大也是功能最多的。当一个用户第一次访问某个网站时会自动创建HttpSession,每个用户可以访问他自己的HttpSession。可以通过HttpServletRequest对象的getSession方法获得HttpSession,通过HttpSession的setAttribute方法可以将一个值放在HttpSession中,通过调用HttpSession对象的getAttribute方法,同时传入属性名就可以获取保存在HttpSession中的对象。与上面三种方式不同的是,HttpSession放在服务器的内存中,因此不要将过大的对象放在里面,即使目前的Servlet容器可以在内存将满时将HttpSession中的对象移到其他存储设备中,但是这样势必影响性能。添加到HttpSession中的值可以是任意Java对象,这个对象最好实现了Serializable接口,这样Servlet容器在必要的时候可以将其序列化到文件中,否则在序列化时就会出现异常。
405.过滤器有哪些作用和用法?
答: Java Web开发中的过滤器(filter)是从Servlet 2.3规范开始增加的功能,并在Servlet 2.4规范中得到增强。对Web应用来说,过滤器是一个驻留在服务器端的Web组件,它可以截取客户端和服务器之间的请求与响应信息,并对这些信息进行过滤。当Web容器接受到一个对资源的请求时,它将判断是否有过滤器与这个资源相关联。如果有,那么容器将把请求交给过滤器进行处理。在过滤器中,你可以改变请求的内容,或者重新设置请求的报头信息,然后再将请求发送给目标资源。当目标资源对请求作出响应时候,容器同样会将响应先转发给过滤器,再过滤器中,你可以对响应的内容进行转换,然后再将响应发送到客户端。
常见的过滤器用途主要包括:对用户请求进行统一认证、对用户的访问请求进行记录和审核、对用户发送的数据进行过滤或替换、转换图象格式、对响应内容进行压缩以减少传输量、对请求或响应进行加解密处理、触发资源访问事件、对XML的输出应用XSLT等。
和过滤器相关的接口主要有:Filter、FilterConfig、FilterChain
406.监听器有哪些作用和用法?
答:Java Web开发中的监听器(listener)就是application、session、request三个对象创建、销毁或者往其中添加修改删除属性时自动执行代码的功能组件,如下所示:
①ServletContextListener:对Servlet上下文的创建和销毁进行监听。
②ServletContextAttributeListener:监听Servlet上下文属性的添加、删除和替换。
③HttpSessionListener:对Session的创建和销毁进行监听。
补充:session的销毁有两种情况:1session超时(可以在web.xml中通过< session-config>/< session-timeout>标签配置超时时间);2通过调用session对象的invalidate()方法使session失效。
④HttpSessionAttributeListener:对Session对象中属性的添加、删除和替换进行监听。
⑤ServletRequestListener:对请求对象的初始化和销毁进行监听。
⑥ServletRequestAttributeListener:对请求对象属性的添加、删除和替换进行监听。
407.你的项目中使用过哪些JSTL标签?
答:项目中主要使用了JSTL的核心标签库,包括< c:if>、< c:choose>、< c: when>、< c: otherwise>、< c:forEach>等,主要用于构造循环和分支结构以控制显示逻辑。
【说明】虽然JSTL标签库提供了core、sql、fmt、xml等标签库,但是实际开发中建议只使用核心标签库(core),而且最好只使用分支和循环标签并辅以表达式语言(EL),这样才能真正做到数据显示和业务逻辑的分离,这才是最佳实践。
408.使用标签库有什么好处?如何自定义JSP标签?
答:使用标签库的好处包括以下几个方面:
分离JSP页面的内容和逻辑,简化了Web开发;
开发者可以创建自定义标签来封装业务逻辑和显示逻辑;
标签具有很好的可移植性、可维护性和可重用性;
避免了对Scriptlet(小脚本)的使用(很多公司的项目开发都不允许在JSP中书写小脚本)
自定义JSP标签包括以下几个步骤:
编写一个Java类实现实现Tag/BodyTag/IterationTag接口(通常不直接实现这些接口而是继承TagSupport/BodyTagSupport/SimpleTagSupport类,这是对适配器模式中缺省适配模式的应用)
重写doStartTag()、doEndTag()等方法,定义标签要完成的功能
编写扩展名为tld的标签描述文件对自定义标签进行部署,tld文件通常放在WEB-INF文件夹或其子目录
在JSP页面中使用taglib指令引用该标签库
下面是一个例子:
package com.bjsxt; package com.lovo.tags; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.tagext.TagSupport; public class TimeTag extends TagSupport { private static final long serialVersionUID = 1L; private String format = "yyyy-MM-dd hh:mm:ss"; private String foreColor = "black"; private String backColor = "white"; public int doStartTag() throws JspException { SimpleDateFormat sdf = new SimpleDateFormat(format); JspWriter writer = pageContext.getOut(); StringBuilder sb = new StringBuilder(); sb.append(String.format("<span style='color:%s;background-color:%s'>%s</span>", foreColor, backColor, sdf.format(new Date()))); try { writer.print(sb.toString()); } catch(IOException e) { e.printStackTrace(); } return SKIP_BODY; } public void setFormat(String format) { this.format = format; } public void setForeColor(String foreColor) { this.foreColor = foreColor; } public void setBackColor(String backColor) { this.backColor = backColor; } }
标签库描述文件(该文件通常放在WEB-INF目录或其子目录下)
<?xml version="1.0" encoding="UTF-8" ?> <taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0"> <description>定义标签库</description> <tlib-version>1.0</tlib-version> <short-name>MyTag</short-name> <tag> <name>time</name> <tag-class>com.lovo.tags.TimeTag</tag-class> <body-content>empty</body-content> <attribute> <name>format</name> <required>false</required> </attribute> <attribute> <name>foreColor</name> </attribute> <attribute> <name>backColor</name> </attribute> </tag> </taglib>
JSP页面
<%@ page pageEncoding="UTF-8"%> <%@ taglib prefix="my" uri="/WEB-INF/tld/my.tld" %> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE html> <html> <head> <base href="<%=basePath%>"> <title>首页</title> <style type="text/css"> * { font-family: "Arial"; font-size:72px; } </style> </head> <body> <my:time format="yyyy-MM-dd" backColor="blue" foreColor="yellow"/> </body> </html>
运行结果

【注意】如果要将自定义的标签库发布成JAR文件,需要将标签库描述文件(tld文件)放在JAR文件的META-INF目录下,可以JDK自带的jar工具完成JAR文件的生成。
409.表达式语言(EL)的隐式对象及其作用?
答: pageContext、initParam(访问上下文参数)、param(访问请求参数)、paramValues、header(访问请求头)、headerValues、cookie(访问cookie)、applicationScope(访问application作用域)、sessionScope(访问session作用域)、requestScope(访问request作用域)、pageScope(访问page作用域)。用法如下所示:
${pageContext.request.method}
${pageContext["request"]["method"]}
${pageContext.request["method"]}
${pageContext["request"].method}
${initParam.defaultEncoding}
${header["accept-language"]}
${headerValues["accept-language"][0]}
${cookie.jsessionid.value}
${sessionScope.loginUser.username}
【补充】表达式语言的.和[]运算作用是一致的,唯一的差别在于如果访问的属性名不符合Java标识符命名规则,例如上面的accept-language就不是一个有效的Java标识符,那么这时候就只能用[]运算符而不能使用.获取它的值
410.表达式语言(EL)支持哪些运算符?
答:除了.和[]运算符,EL还提供了:
算术运算符:+、-、*、/或div、%或mod
关系运算符:==或eq、!=或ne、>或gt、>=或ge、< 或lt、< =或le
逻辑运算符:&&或and、||或or、!或not
条件运算符:${statement? A : B}(跟Java的条件运算符类似)
empty运算符:检查一个值是否为null或者空(数组长度为0或集合中没有元素也返回true)
411.Servlet 3中的异步处理指的是什么?
答:在Servlet 3中引入了一项新的技术可以让Servlet异步处理请求。有人可能会质疑,既然都有多线程了,还需要异步处理请求吗?答案是肯定的,因为如果一个任务处理时间相当长,那么Servlet或Filter会一直占用着请求处理线程直到任务结束,随着并发用户的增加,容器将会遭遇线程超出的风险,这这种情况下很多的请求将会被堆积起来而后续的请求可能会遭遇拒绝服务,直到有资源可以处理请求为止。异步特性可以帮助应用节省容器中的线程,特别适合执行时间长而且用户需要得到结果的任务,如果用户不需要得到结果则直接将一个Runnable对象交给Executor(如果不清楚请查看前文关于多线程和线程池的部分)并立即返回即可。
【补充】多线程在Java诞生初期无疑是一个亮点,而Servlet单实例多线程的工作方式也曾为其赢得美名,然而技术的发展往往会颠覆我们很多的认知,就如同当年爱因斯坦的相对论颠覆了牛顿的经典力学一般。事实上,异步处理绝不是Serlvet 3首创,如果你了解Node.js的话,对Servlet 3的这个重要改进就不以为奇了。
412.如何在基于Java的Web项目中实现文件上传和下载?
答:(稍后呈现,我准备用HTML5写一个带进度条的客户端,然后再用Servlet 3提供的文件上传支持来做一个多文件上传的例子)
413.简述值栈(Value-Stack)的原理和生命周期
答: Value-Stack贯穿整个 Action 的生命周期,保存在request作用域中,所以它和request的生命周期一样。当Struts 2接受一个请求时,会创建ActionContext、Value-Stack和Action对象,然后把Action存放进Value-Stack,所以Action的实例变量可以通过OGNL访问。由于Action是多实例的,和使用单例的Servlet不同, 每个Action都有一个对应的Value-Stack,Value-Stack存放的数据类型是该Action的实例,以及该Action中的实例变量,Action对象默认保存在栈顶。
414.阐述Session加载实体对象的过程。
答:Session加载实体对象的步骤是:
① Session在调用数据库查询功能之前, 首先会在缓存中进行查询, 在一级缓存中, 通过实体类型和主键进行查找, 如果一级缓存查找命中且数据状态合法, 则直接返回
② 如果一级缓存没有命中, 接下来Session会在当前NonExists记录(相当于一个查询黑名单, 如果出现重复的无效查询可以迅速判断, 从而提升性能)中进行查找, 如果NonExists中存在同样的查询条件,则返回null
③ 对于load方法, 如果一级缓存查询失败则查询二级缓存, 如果二级缓存命中则直接返回
④ 如果之前的查询都未命中, 则发出SQL语句, 如果查询未发现对应记录则将此次查询添加到Session的NonExists中加以记录, 并返回null
⑤ 根据映射配置和SQL语句得到ResultSet,并创建对应的实体对象
⑥ 将对象纳入Session(一级缓存)管理
⑦ 执行拦截器的onLoad方法(如果有对应的拦截器)
⑧ 将数据对象纳入二级缓存
⑨ 返回数据对象
415.怎么防止重复提交
1.禁掉提交按钮。表单提交后使用Javascript使提交按钮disable。这种方法防止心急的用户多次点击按钮。但有个问题,如果客户端把Javascript给禁止掉,这种方法就无效了。
2.Post/Redirect/Get模式。在提交后执行页面重定向,这就是所谓的Post-Redirect-Get (PRG)模式。简言之,当用户提交了表单后,你去执行一个客户端的重定向,转到提交成功信息页面。
这能避免用户按F5导致的重复提交,而其也不会出现浏览器表单重复提交的警告,也能消除按浏览器前进和后退按导致的同样问题。
3.在session中存放一个特殊标志。当表单页面被请求时,生成一个特殊的字符标志串,存在session中,同时放在表单的隐藏域里。接受处理表单数据时,检查标识字串是否存在,并立即从session中删除它,然后正常处理数据。
如果发现表单提交里没有有效的标志串,这说明表单已经被提交过了,忽略这次提交。
4.在数据库里添加约束。在数据库里添加唯一约束或创建唯一索引,防止出现重复数据。这是最有效的防止重复提交数据的方法。
416.$(document).ready(function(){}) jQuery(document).ready(function(){}); 有什么区别?
window.jQuery = window.$ = jQuery;
这两者可以互换使用。一般建议优先使用$
417.写出输出结果
<script> function Foo() { getName = function (){alert(1);}; return this; } Foo.getName = function() {alert (2);}; Foo.prototype.getName = function (){ alert (3);}; var getName = function (){alert (4);}; function getName(){alert (5);} </script>
//请写出以下输出结果:
Foo.getName(); // 2
getName(); // 4
Foo().getName(); // 1
getName(); // 1
new Foo.getName(); // 2
new Foo().getName(); // 3
new new Foo().getName(); // 3
418.web项目从浏览器发起交易响应缓慢,请简述从哪些方面如数分析
从前端后端分别取考虑,后台是不是数据库死锁等。
前台看看是不是js 错误,或者图片过大,dom 渲染dom树,画面优化。cmd amd 规范等
答:工厂设计模式:程序在接口和子类之间加入了一个过渡端,通过此过渡端可以动态取得实现了共同接口的子类实例化对象。
代理设计模式:指由一个代理主题来操作真实主题,真实主题执行具体的业务操作,而代理主题负责其他相关业务的处理。比如生活中的通过代理访问网络,客户通过网络代理连接网络(具体业务),由代理服务器完成用户权限和访问限制等与上网相关的其他操作(相关业务)
适配器模式:如果一个类要实现一个具有很多抽象方法的接口,但是本身只需要实现接口中的部分方法便可以达成目的,所以此时就需要一个中间的过渡类,但此过渡类又不希望直接使用,所以将此类定义为抽象类最为合适,再让以后的子类直接继承该抽象类便可选择性的覆写所需要的方法,而此抽象类便是适配器类。
工厂设计模式:
思路说明:由一个工厂类根据传入的参数(一般是字符串参数),动态决定应该创建哪一个产品子类(这些产品子类继承自同一个父类或接口)的实例,并以父类形式返回
优点:客户端不负责对象的创建,而是由专门的工厂类完成;客户端只负责对象的调用,实现了创建和调用的分离,降低了客户端代码的难度;
缺点:如果增加和减少产品子类,需要修改简单工厂类,违背了开闭原则;如果产品子类过多,会导致工厂类非常的庞大,违反了高内聚原则,不利于后期维护。
public class SimpleFactory { public static Product createProduct(String pname){ Product product=null; if("p1".equals(pname)){ product = new Product1(); }else if("p2".equals(pname)){ product = new Product2(); }else if("pn".equals(pname)){ product = new ProductN(); } return product; } }
单例模式
/** * 饿汉式的单例模式 * 在类加载的时候创建单例实例,而不是等到第一次请求实例的时候的时候创建 * 1、私有 的无参数构造方法Singleton(),避免外部创建实例 * 2、私有静态属性instance * 3、公有静态方法getInstance() */ public class Singleton { private static Singleton instance = new Singleton(); private Singleton(){ } public static Singleton getInstance(){ return instance; } } /** * 懒汉式的单例模式 *在类加载的时候不创建单例实例,只有在第一次请求实例的时候的时候创建 */ public class Singleton { private static Singleton instance; private Singleton(){ } /** * 多线程情况的单例模式,避免创建多个对象 */ public static Singleton getInstance(){ if(instance ==null){//避免每次加锁,只有第一次没有创建对象时才加锁 synchronized(Singleton.class){//加锁,只允许一个线程进入 if(instance==null){ //只创建一次对象 instance = new Singleton(); } } } return instance; }}
答:工厂模式:工厂模式是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
适配器模式:适配器模式是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。
模板模式:在模板模式中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
答:struts2中action是多例的,即一个session产生一个action
背景:
1) Struts2会对每一个请求,产生一个Action的实例来处理.
2) Spring的Ioc容器管理的bean默认是单实例的.
首先从数据安全性的问题上考虑,我们的Action应该保证是多例的,这样才不会出现数据问题。但是如果有的action比如只有admin才能操作,或者某些action,全站公用一个来提高性能,这样的话,就可以使用单例模式。
不过幸好,Spring的bean可以针对每一个设置它的scope,所以,上面的问题就不是问题了。如果用单例,就在spring的action bean配置的时候设置scope=”prototype”
如果是单例的话,若出现两个用户都修改一个对象的属性值,则会因为用户修改时间不同,两个用户访问得到的属性不一样,操作得出的结果不一样.
举个例子:有一块布长度300cm,能做一件上衣(用掉100cm)和一件裤子(用掉200cm);甲和乙同时访问得到的长度都是300cm,
甲想做上衣和裤子,他先截取100cm去做上衣,等上衣做完再去做裤子,而乙这时正好也拿100cm去做上衣,那好,等甲做完上衣再做裤子的时候发现剩下的布(100cm)已经不够做裤子了…..这就是影响系统的性能,解决的办法就是给甲和乙一人一块300cm的布,就不会出现布被别人偷用的事情,也是就单实例和多实例的区别
如果设置成单例,那么多个线程会共享一个ActionContext和ValueStack,这样并发访问的时候就会出现问题了
struts 2的Action是多实例的并非单例,也就是每次请求产生一个Action的对象。原因是:struts 2的Action中包含数据,例如你在页面填写的数据就会包含在Action的成员变量里面。如果Action是单实例的话,这些数据在多线程的环境下就会相互影响,例如造成别人填写的数据被你看到了。所以Struts2的Action是多例模式的。
问题出现了,可以让Struts2的action变成单例模式么?
Struts2中,可以使用注解开发,在Action上@Scope(“prototype”) 指定为多例 , 默认为singleton()单例)
基本上action的scope需要是prototype,就是每次请求都建立新的线程
不写的话,默认是singleton了
答:单例模式主要作用是保证在Java应用程序中,一个类只有一个实例存在。下面给出两种不同形式的单例:
第一种形式:饿汉式单例
package com.bjsxt; public class Singleton { private Singleton(){} private static Singleton instance = new Singleton(); public static Singleton getInstance(){ return instance; } }
第二种形式:懒汉式单例
package com.bjsxt; public class Singleton { private static Singleton instance = null; private Singleton() {} public static synchronized Singleton getInstance(){ if (instance==null) instance=newSingleton(); return instance; } }
单例的特点:外界无法通过构造器来创建对象,该类必须提供一个静态方法向外界提供该类的唯一实例。
【补充】用Java进行服务器端编程时,使用单例模式的机会还是很多的,服务器上的资源都是很宝贵的,对于那些无状态的对象其实都可以单例化或者静态化(在内存中仅有唯一拷贝),如果使用了spring这样的框架来进行对象托管,Spring的IoC容器在默认情况下对所有托管对象都是进行了单例化处理的。
答:在GoF的《Design Patterns: Elements of Reusable Object-Oriented Software》中给出了三类(创建型[对类的实例化过程的抽象化]、结构型[描述如何将类或对象结合在一起形成更大的结构]、行为型[对在不同的对象之间划分责任和算法的抽象化])共23种设计模式,包括:Abstract Factory(抽象工厂模式),Builder(建造者模式),Factory Method(工厂方法模式),Prototype(原始模型模式),Singleton(单例模式);Facade(门面模式),Adapter(适配器模式),Bridge(桥梁模式),Composite(合成模式),Decorator(装饰模式),Flyweight(享元模式),Proxy(代理模式);Command(命令模式),Interpreter(解释器模式),Visitor(访问者模式),Iterator(迭代子模式),Mediator(调停者模式),Memento(备忘录模式),Observer(观察者模式),State(状态模式),Strategy(策略模式),Template Method(模板方法模式), Chain Of Responsibility(责任链模式)。
所谓设计模式,就是一套被反复使用的代码设计经验的总结(情境中一个问题经过证实的一个解决方案)。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。设计模式使人们可以更加简单方便的复用成功的设计和体系结构。将已证实的技术表述成设计模式也会使新系统开发者更加容易理解其设计思路。
【补充】设计模式并不是像某些地方吹嘘的那样是遥不可及的编程理念,说白了设计模式就是对面向对象的编程原则的实践,面向对象的编程原则包括:
单一职责原则:一个类只做它该做的事情。(单一职责原则想表达的就是“高内聚”,写代码最终极的原则只有六个字“高内聚、低耦合”,就如同葵花宝典或辟邪剑谱的中心思想就八个字“欲练此功必先自宫”,所谓的高内聚就是一个代码模块只完成一项功能,在面向对象中,如果只让一个类完成它该做的事,而不涉及与它无关的领域就是践行了高内聚的原则,这个类就只有单一职责。我们都知道一句话叫“因为专注,所以专业”,一个对象如果承担太多的职责,那么注定它什么都做不好。这个世界上任何好的东西都有两个特征,一个是功能单一,好的相机绝对不是电视购物里面卖的那种一个机器有一百多种功能的,它基本上只能照相;另一个是模块化,好的自行车是组装车,从减震叉、刹车到变速器,所有的部件都是可以拆卸和重新组装的,好的乒乓球拍也不是成品拍,一定是底板和胶皮可以拆分和自行组装的,一个好的软件系统,它里面的每个功能模块也应该是可以轻易的拿到其他系统中使用的,这样才能实现软件复用的目标。)
开闭原则:软件实体应当对扩展开放,对修改关闭。(在理想的状态下,当我们需要为一个软件系统增加新功能时,只需要从原来的系统派生出一些新类就可以,不需要修改原来的任何一行代码。要做到开闭有两个要点:①抽象是关键,一个系统中如果没有抽象类或接口系统就没有扩展点;②封装可变性,将系统中的各种可变因素封装到一个继承结构中,如果多个可变因素混杂在一起,系统将变得复杂而换乱,如果不清楚如何封装可变性,可以参考《设计模式精解》一书中对桥梁模式的讲解的章节。)
依赖倒转原则:面向接口编程。(该原则说得直白和具体一些就是声明方法的参数类型、方法的返回类型、变量的引用类型时,尽可能使用抽象类型而不用具体类型,因为抽象类型可以被它的任何一个子类型所替代,请参考下面的里氏替换原则。)
里氏替换原则:任何时候都可以用子类型替换掉父类型。(关于里氏替换原则的描述,Barbara Liskov女士的描述比这个要复杂得多,但简单的说就是能用父类型的地方就一定能使用子类型。里氏替换原则可以检查继承关系是否合理,如果一个继承关系违背了里氏替换原则,那么这个继承关系一定是错误的,需要对代码进行重构。例如让猫继承狗,或者狗继承猫,又或者让正方形继承长方形都是错误的继承关系,因为你很容易找到违反里氏替换原则的场景。需要注意的是:子类一定是增加父类的能力而不是减少父类的能力,因为子类比父类的能力更多,把能力多的对象当成能力少的对象来用当然没有任何问题。)
接口隔离原则:接口要小而专,绝不能大而全。(臃肿的接口是对接口的污染,既然接口表示能力,那么一个接口只应该描述一种能力,接口也应该是高度内聚的。例如,琴棋书画就应该分别设计为四个接口,而不应设计成一个接口中的四个方法,因为如果设计成一个接口中的四个方法,那么这个接口很难用,毕竟琴棋书画四样都精通的人还是少数,而如果设计成四个接口,会几项就实现几个接口,这样的话每个接口被复用的可能性是很高的。Java中的接口代表能力、代表约定、代表角色,能否正确的使用接口一定是编程水平高低的重要标识。)
合成聚合复用原则:优先使用聚合或合成关系复用代码。(通过继承来复用代码是面向对象程序设计中被滥用得最多的东西,因为所有的教科书都无一例外的对继承进行了鼓吹从而误导了初学者,类与类之间简单的说有三种关系,IS-A关系、HAS-A关系、USE-A关系,分别代表继承、关联和依赖。其中,关联关系根据其关联的强度又可以进一步划分为关联、聚合和合成,但说白了都是HAS-A关系,合成聚合复用原则想表达的是优先考虑HAS-A关系而不是IS-A关系复用代码,原因嘛可以自己从百度上找到一万个理由,需要说明的是,即使在Java的API中也有不少滥用继承的例子,例如Properties类继承了Hashtable类,Stack类继承了Vector类,这些继承明显就是错误的,更好的做法是在Properties类中放置一个Hashtable类型的成员并且将其键和值都设置为字符串来存储数据,而Stack类的设计也应该是在Stack类中放一个Vector对象来存储数据。记住:任何时候都不要继承工具类,工具是可以拥有并可以使用的(HAS/USE),而不是拿来继承的。)
迪米特法则:迪米特法则又叫最少知识原则,一个对象应当对其他对象有尽可能少的了解。(迪米特法则简单的说就是如何做到“低耦合”,门面模式和调停者模式就是对迪米特法则的践行。对于门面模式可以举一个简单的例子,你去一家公司洽谈业务,你不需要了解这个公司内部是如何运作的,你甚至可以对这个公司一无所知,去的时候只需要找到公司入口处的前台美女,告诉她们你要做什么,她们会找到合适的人跟你接洽,前台的美女就是公司这个系统的门面。再复杂的系统都可以为用户提供一个简单的门面,Java Web开发中作为前端控制器的Servlet或Filter不就是一个门面吗,浏览器对服务器的运作方式一无所知,但是通过前端控制器就能够根据你的请求得到相应的服务。调停者模式也可以举一个简单的例子来说明,例如一台计算机,CPU、内存、硬盘、显卡、声卡各种设备需要相互配合才能很好的工作,但是如果这些东西都直接连接到一起,计算机的布线将异常复杂,在这种情况下,主板作为一个调停者的身份出现,它将各个设备连接在一起而不需要每个设备之间直接交换数据,这样就减小了系统的耦合度和复杂度。迪米特法则用通俗的话来将就是不要和陌生人打交道,如果真的需要,找一个自己的朋友,让他替你和陌生人打交道。)


答: 按照分层开发的观点,可以将应用划分为:表示层、业务逻辑层和持久层,每一层都有属于自己类别的设计模式。
表示层设计模式:
1) Interceptor Filter:拦截过滤器,提供请求预处理和后处理的方案,可以对请求和响应进行过滤。/p>
2) Front Controller:通过中央控制器提供请求管理和处理,管理内容读取、安全性、视图管理和导航等功能。Struts 2中的StrutsPrepareAndExecuteFilter、Spring MVC中的DispatcherServlet都是前端控制器,后者如下图所示:

3) View Helper:视图帮助器,负责将显示逻辑和业务逻辑分开。显示的部分放在视图组件中,业务逻辑代码放在帮助器中,典型的功能是内容读取、验证与适配。
4) Composite View:复合视图。
业务逻辑层设计模式:
1) Business Delegate:业务委托,减少表示层和业务逻辑层之间的耦合。
2) Value Object:值对象,解决层之间交换数据的开销问题。
3) Session Façade:会话门面,隐藏业务逻辑组件的细节,集中工作流程。
4) Value Object Assembler:灵活的组装不同的值对象
5) Value List Handler:提供执行查询和处理结果的解决方案,还可以缓存查询结果,从而达到提升性能的目的。
6) Service Locator:服务定位器,可以查找、创建和定位服务工厂,封装其实现细节,减少复杂性,提供单个控制点,通过缓存提高性能。
持久层设计模式:
Data Access Object:数据访问对象,以面向对象的方式完成对数据的增删改查。
【补充】如果想深入的了解Java企业级应用的设计模式和架构模式,可以参考这些书籍: 《Pro Java EE Spring Patterns》、《POJO in Action》、《Patterns of Enterprise Application Architecture》。
答:面试被问到关于设计模式的知识时,可以拣最常用的作答,例如:
1) 工厂模式:工厂类可以根据条件生成不同的子类实例,这些子类有一个公共的抽象父类并且实现了相同的方法,但是这些方法针对不同的数据进行了不同的操作(多态方法)。当得到子类的实例后,开发人员可以调用基类中的方法而不必考虑到底返回的是哪一个子类的实例。
2) 代理模式:给一个对象提供一个代理对象,并由代理对象控制原对象的引用。实际开发中,按照使用目的的不同,代理可以分为:远程代理、虚拟代理、保护代理、Cache代理、防火墙代理、同步化代理、智能引用代理。
3) 适配器模式:把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起使用的类能够一起工作。
4) 模板方法模式:提供一个抽象类,将部分逻辑以具体方法或构造器的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法(多态实现),从而实现不同的业务逻辑。
除此之外,还可以讲讲上面提到的门面模式、桥梁模式、单例模式、装潢模式(Collections工具类里面的synchronizedXXX方法把一个线程不安全的容器变成线程安全容器就是对装潢模式的应用,而Java IO里面的过滤流(有的翻译成处理流)也是应用装潢模式的经典例子)等,反正原则就是拣自己最熟悉的用得最多的作答,以免言多必失。
设计模式是一套被反复使用的、多数人知晓、经过分类编目的优秀代码设计经验的总结。特定环境下特定问题的处理方法。
1)重用设计和代码 重用设计比重用代码更有意义,自动带来代码重用
2)提高扩展性 大量使用面向接口编程,预留扩展插槽,新的功能或特性很容易加入到系统中来
3)提高灵活性 通过组合提高灵活性,可允许代码修改平稳发生,对一处修改不会波及到其他模块
4) 提高开发效率 正确使用设计模式,可以节省大量的时间


package com.bjsxt; public class SimpleFactory { public static Product createProduct(String pname){ Product product=null; if("p1".equals(pname)){ product = new Product(); }else if("p2".equals(pname)){ product = new Product(); }else if("pn".equals(pname)){ product = new Product(); } return product; } }
基本原理:由一个工厂类根据传入的参数(一般是字符串参数),动态决定应该创建哪一个产品子类(这些产品子类继承自同一个父类或接口)的实例,并以父类形式返回
优点:客户端不负责对象的创建,而是由专门的工厂类完成;客户端只负责对象的调用,实现了创建和调用的分离,降低了客户端代码的难度;
缺点:如果增加和减少产品子类,需要修改简单工厂类,违背了开闭原则;如果产品子类过多,会导致工厂类非常的庞大,违反了高内聚原则,不利于后期维护
分析:建议挑选有一定技术难度,并且在实际开发中应用较多的设计模式。可以挑选装饰模式和动态代理模式。此处挑选动态代理设计模式。
讲解思路:生活案例引入、技术讲解、优缺点分析、典型应用。
1、生活案例引入:送生日蛋糕:
MM们要过生日了,怎么也得表示下吧。最起码先送个蛋糕。蛋糕多种多样了。巧克力,冰淇淋,奶油等等。这都是基本的了,再加点额外的装饰,如蛋糕里放点花、放贺卡、放点干果吃着更香等等。
分析:
方案1:如果采用继承会造成大量的蛋糕子类
方案2:蛋糕作为主体,花,贺卡,果仁等是装饰者,需要时加到蛋糕上。要啥我就加啥。
技术讲解
装饰模式(别名Wrapper)是在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能。它通过创建一个包装对象,也就是装饰来包裹真实对象,提供了比继承更具弹性的代替方案。
装饰模式一般涉及到的角色
抽象构建角色(Component):给出一个抽象的接口,以规范准备接受附加责任的对象。
具体的构建角色(ConcreteComponent):定义一个将要接受附加责任的类。
抽象的装饰角色 (Decorator):持有一个抽象构建(Component)角色的引用,并定义一个与抽象构件一致的接口。
具体的装饰角色(ConcreteDecorator):负责给构建对象“贴上”附加的责任。

3、优缺点分析
优点
1)Decorator模式与继承关系的目的都是要扩展对象的功能,但是Decorato更多的灵活性。
2)把类中的装饰功能从类中搬移出去,这样可以简化原有的类。有效地把类的核心功能和装饰功能区分开了。
3)通过使用不同的具体装饰类以及这些装饰类的排列组合,可创造出很多不同行为的组合。
缺点
这种比继承更加灵活机动的特性,也同时意味着更加多的复杂性。
装饰模式会导致设计中出现许多小类,如果过度使用,会使程序变得很复杂。
符合的设计原则:
多用组合,少用继承。利用继承设计子类的行为是在编译时静态决定的,且所有的子类都会继承到相同的行为。如能够利用组合扩展对象的行为,就可在运行时动态进行扩展。
类应设计的对扩展开放,对修改关闭。
4、典型应用
java IO中需要完成对不同输入输出源的操作,如果单纯的使用继承这一方式,无疑需要很多的类。比如说,我们操作文件需要一个类,实现文件的字节读取需要一个类,实现文件的字符读取又需要一个类....一次类推每个特定的操作都需要一个特定的类。这无疑会导致大量的IO继承类的出现。显然对于编程是很不利的。而是用装饰模式则可以很好的解决这一问题,在装饰模式中:节点流(如FileInputStream)直接与输入源交互,之后通过过滤流(FilterInputStream)进行装饰,这样获得的io对象便具有某几个的功能,很好的拓展了IO的功能。

高级框架
Maven使用项目对象模型(POM)的概念,可以通过一小段描述信息来管理项目的构建,报告和文档的软件项目管理工具。
Maven 除了以程序构建能力为特色之外,还提供高级项目管理工具。由于 Maven 的缺省构建规则有较高的可重用性,所以常常用两三行 Maven 构建脚本就可以构建简单的项目。由于 Maven 的面向项目的方法,许多 Apache Jakarta 项目发布时使用 Maven,而且公司项目采用 Maven 的比例在持续增长。
Maven的出现,解决了开发过程中的jar包升级及依赖的难题。它可以对项目依赖的jar包进行管理,可以让你的项目保持基本的依赖,排除冗余jar包,并且可以让你非常轻松的对依赖的jar包进行版本升级。而这些仅仅是Maven最基本的功能,它可以在这基础上对项目进行清理、编译、测试、打包、发布等等构建项目的工作。
可以说,Maven是现在Java社区中最强大的项目管理和项目构建工具,而更加值得庆幸的是,这样一个强大的工具,它的使用也是非常简单的。
现在,JavaEE项目使用的开源软件都可以通过Maven来获取,并且,越来越多的公司也开始使用Maven来管理构建项目了。
1.maven&ant同属apach是流行的构建工具。
都是为了简化软件开发而存在的。但是maven因为自身管理一个项目对象模型(project object model),这个模型其实就是抽象了一个项目的开发流程,它包含了一个项目的生命周期的各个阶段,并将这个周期固定下来,这也就是约定大于配置。约定大于配置的意思就是,我maven将项目开发的各个阶段固定起来了,每个文件的存放位置,每个阶段要生成什么文件、保存为什么格式并且要把它放在什么位置,我都固定好了。我知道一个软件是怎么开发出来,如果一个项目要使用maven,可以,但你要遵循我的规则,文件目录不要乱建乱放,只有这样maven才会将源码用起来。这就是约定大于配置,因为maven已经将流程固定下来了,只要遵守约定,就不需要自己手动去配置了,这将大大地提高开发效率。就像是开车一样,只要知道点火、油门、方向、刹车,就可以将车子开东起来(当然出于安全和法律考虑,还是要考驾照的。),关于车子内部的传动原理,电气原理,工程原理,普通人并不需要了解多少,日常够用就好了。这也是约定大于配置的一个例子。配置就是自己造一辆车去开,有必要,有能力,有时间吗?
2.maven的中央仓库和pom.xml文件。中央仓库统一存放了开发用到的各种jar包,要用时只需要添加依赖到pom文件中,maven就会自动下载,当然为了方便一般会在本地建一个仓库,减少下载时间。pom文件是maven的配置文件,maven就是通过管理pom文件和一些核心插件来管理项目。当然我前面将maven拟人化了,其实maven是没有智力的,一切都是封装好的流程,只是maven将很多操作隐藏起来了。
3.ant的build.xml文件。build文件是ant的配置文件,ant依靠它来执行操作,与maven不同的是ant没有固定一条程序链。你想要执行什么操作以及操作之间的顺序和依赖关系,都需要手动添加到build文件中,一点一滴都要写清楚,否则ant就不会执行。
4.maven和ant区别
Maven 拥有约定,只要遵守约定,它就知道你的源代码在哪里。Maven 是声明式的。你需要做的只是创建一个 pom.xml 文件然后将源代码放到默认的目录。Maven 会帮你处理其它的事情。Maven 有一个生命周期,当你运行 mvn install 的时候被调用。这条命令告诉 Maven 执行一系列的有序的步骤,直到到达你指定的生命周期。缺点是运行许多默认目标。
而ant没有约定,项目生命周期,它是命令式的。所有操作都要手动去创建、布置。甚至连build.xml文件都需要手动创建。
Maven仓库是基于简单文件系统存储的,集中化管理Java API资源(构件)的一个服务。仓库中的任何一个构件都有其唯一的坐标,根据这个坐标可以定义其在仓库中的唯一存储路径。得益于 Maven 的坐标机制,任何 Maven项目使用任何一个构件的方式都是完全相同的,Maven 可以在某个位置统一存储所有的 Maven 项目共享的构件,这个统一的位置就是仓库,项目构建完毕后生成的构件也可以安装或者部署到仓库中,供其它项目使用。
对于Maven来说,仓库分为两类:本地仓库和远程仓库。
POM工程
POM工程是逻辑工程。用在父级工程或聚合工程中。用来做jar包的版本控制。
JAR工程
将会打包成jar用作jar包使用。即常见的本地工程 - Java Project。
WAR工程
将会打包成war,发布在服务器上的工程。如网站或服务。即常见的网络工程 - Dynamic Web Project。war工程默认没有WEB-INF目录及web.xml配置文件,IDE通常会显示工程错误,提供完整工程结构可以解决。
install
本地安装, 包含编译,打包,安装到本地仓库
编译 - javac
打包 - jar, 将java代码打包为jar文件
安装到本地仓库 - 将打包的jar文件,保存到本地仓库目录中。
clean
清除已编译信息。
删除工程中的target目录。
compile
只编译。 javac命令
deploy
部署。 常见于结合私服使用的命令。
相当于是install+上传jar到私服。
包含编译,打包,安装到本地仓库,上传到私服仓库。
package
打包。 包含编译,打包两个功能。
配置管理
在我们的应用中除了代码外,还有一些就是各种配置。比如数据库连接等。一般我们都是使用配置文件的方式,在代码中引入这些配置文件。当我们只有一种配置,只有一台服务器,并且不经常修改的时候,使用配置文件是一个很好的做法,但是如果我们配置非常多,有很多服务器都需要这个配置,这时使用配置文件就不是个好主意了。这个时候往往需要寻找一种集中管理配置的方法,我们在这个集中的地方修改了配置,所有对这个配置感兴趣的都可以获得变更。Zookeeper就是这种服务,它使用Zab这种一致性协议来提供一致性。现在有很多开源项目使用Zookeeper来维护配置,比如在HBase中,客户端就是连接一个Zookeeper,获得必要的HBase集群的配置信息,然后才可以进一步操作。还有在开源的消息队列Kafka中,也使用Zookeeper来维护broker的信息。在Alibaba开源的SOA框架Dubbo中也广泛的使用Zookeeper管理一些配置来实现服务治理。
名字服务
名字服务这个就很好理解了。比如为了通过网络访问一个系统,我们得知道对方的IP地址,但是IP地址对人非常不友好,这个时候我们就需要使用域名来访问。但是计算机是不能是域名的。怎么办呢?如果我们每台机器里都备有一份域名到IP地址的映射,这个倒是能解决一部分问题,但是如果域名对应的IP发生变化了又该怎么办呢?于是我们有了DNS这个东西。我们只需要访问一个大家熟知的(known)的点,它就会告诉你这个域名对应的IP是什么。在我们的应用中也会存在很多这类问题,特别是在我们的服务特别多的时候,如果我们在本地保存服务的地址的时候将非常不方便,但是如果我们只需要访问一个大家都熟知的访问点,这里提供统一的入口,那么维护起来将方便得多了。
分布式锁
其实在第一篇文章中已经介绍了Zookeeper是一个分布式协调服务。这样我们就可以利用Zookeeper来协调多个分布式进程之间的活动。比如在一个分布式环境中,为了提高可靠性,我们的集群的每台服务器上都部署着同样的服务。但是,一件事情如果集群中的每个服务器都进行的话,那相互之间就要协调,编程起来将非常复杂。而如果我们只让一个服务进行操作,那又存在单点。通常还有一种做法就是使用分布式锁,在某个时刻只让一个服务去干活,当这台服务出问题的时候锁释放,立即fail over到另外的服务。这在很多分布式系统中都是这么做,这种设计有一个更好听的名字叫Leader Election(leader选举)。比如HBase的Master就是采用这种机制。但要注意的是分布式锁跟同一个进程的锁还是有区别的,所以使用的时候要比同一个进程里的锁更谨慎的使用。
集群管理
在分布式的集群中,经常会由于各种原因,比如硬件故障,软件故障,网络问题,有些节点会进进出出。有新的节点加入进来,也有老的节点退出集群。这个时候,集群中其他机器需要感知到这种变化,然后根据这种变化做出对应的决策。比如我们是一个分布式存储系统,有一个中央控制节点负责存储的分配,当有新的存储进来的时候我们要根据现在集群目前的状态来分配存储节点。这个时候我们就需要动态感知到集群目前的状态。还有,比如一个分布式的SOA架构中,服务是一个集群提供的,当消费者访问某个服务时,就需要采用某种机制发现现在有哪些节点可以提供该服务(这也称之为服务发现,比如Alibaba开源的SOA框架Dubbo就采用了Zookeeper作为服务发现的底层机制)。还有开源的Kafka队列就采用了Zookeeper作为Cosnumer的上下线管理。
在Zookeeper中,znode是一个跟Unix文件系统路径相似的节点,可以往这个节点存储或获取数据。
Zookeeper底层是一套数据结构。这个存储结构是一个树形结构,其上的每一个节点,我们称之为“znode”
zookeeper中的数据是按照“树”结构进行存储的。而且znode节点还分为4中不同的类型。
每一个znode默认能够存储1MB的数据(对于记录状态性质的数据来说,够了)
可以使用zkCli命令,登录到zookeeper上,并通过ls、create、delete、get、set等命令操作这些znode节点
答:(1)PERSISTENT 持久化节点: 所谓持久节点,是指在节点创建后,就一直存在,直到有删除操作来主动清除这个节点。否则不会因为创建该节点的客户端会话失效而消失。
(2)PERSISTENT_SEQUENTIAL 持久顺序节点:这类节点的基本特性和上面的节点类型是一致的。额外的特性是,在ZK中,每个父节点会为他的第一级子节点维护一份时序,会记录每个子节点创建的先后顺序。基于这个特性,在创建子节点的时候,可以设置这个属性,那么在创建节点过程中,ZK会自动为给定节点名加上一个数字后缀,作为新的节点名。这个数字后缀的范围是整型的最大值。 在创建节点的时候只需要传入节点 “/test_”,这样之后,zookeeper自动会给”test_”后面补充数字。
(3)EPHEMERAL 临时节点:和持久节点不同的是,临时节点的生命周期和客户端会 话绑定。也就是说,如果客户端会话失效,那么这个节点就会自动被清除掉。注意,这里提到的是会话失效,而非连接断开。另外,在临时节点下面不能创建子节点。
这里还要注意一件事,就是当你客户端会话失效后,所产生的节点也不是一下子就消失了,也要过一段时间,大概是10秒以内,可以试一下,本机操作生成节点,在服务器端用命令来查看当前的节点数目,你会发现客户端已经stop,但是产生的节点还在。
EPHEMERAL_SEQUENTIAL 临时自动编号节点:此节点是属于临时节点,不过带有顺序,客户端会话结束节点就消失。
Dubbo是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和Spring框架无缝集成。Dubbo框架,是基于容器运行的.。容器是Spring。
其核心部分包含:
1. 远程通讯: 提供对多种基于长连接的NIO框架抽象封装,包括多种线程模型,序列化,以及“请求-响应”模式的信息交换方式。
2. 集群容错: 提供基于接口方法的透明远程过程调用,包括多协议支持,以及软负载均衡,失败容错,地址路由,动态配置等集群支持。
3. 自动发现: 基于注册中心目录服务,使服务消费方能动态的查找服务提供方,使地址透明,使服务提供方可以平滑增加或减少机器。
Dubbo能做什么?
1.透明化的远程方法调用,就像调用本地方法一样调用远程方法,只需简单配置,没有任何API侵入。
2.软负载均衡及容错机制,可在内网替代F5等硬件负载均衡器,降低成本,减少单点。
3. 服务自动注册与发现,不再需要写死服务提供方地址,注册中心基于接口名查询服务提供者的IP地址,并且能够平滑添加或删除服务提供者。
Dubbo的存在简单来说就是要减小service层的压力。
远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。
registry
注册中心. 是用于发布和订阅服务的一个平台.用于替代SOA结构体系框架中的ESB服务总线的。
发布
开发服务端代码完毕后, 将服务信息发布出去. 实现一个服务的公开.
订阅
客户端程序,从注册中心下载服务内容 这个过程是订阅.
订阅服务的时候, 会将发布的服务所有信息,一次性下载到客户端.
客户端也可以自定义, 修改部分服务配置信息. 如: 超时的时长, 调用的重试次数等.
Consumer
服务的消费者, 就是服务的客户端.
消费者必须使用Dubbo技术开发部分代码. 基本上都是配置文件定义.
provider
服务的提供者, 就是服务端.
服务端必须使用Dubbo技术开发部分代码. 以配置文件为主.
container
容器. Dubbo技术的服务端(Provider), 在启动执行的时候, 必须依赖容器才能正常启动.
默认依赖的就是spring容器. 且Dubbo技术不能脱离spring框架.
在2.5.3版本的dubbo中, 默认依赖的是spring2.5版本技术. 可以选用spring4.5以下版本.
在2.5.7版本的dubbo中, 默认依赖的是spring4.3.10版本技术. 可以选择任意的spring版本.
monitor
监控中心. 是Dubbo提供的一个jar工程.
主要功能是监控服务端(Provider)和消费端(Consumer)的使用数据的. 如: 服务端是什么,有多少接口,多少方法, 调用次数, 压力信息等. 客户端有多少, 调用过哪些服务端, 调用了多少次等.

0 start: 启动Spring容器时,自动启动Dubbo的Provider
1、register: Dubbo的Provider在启动后自动会去注册中心注册内容.注册的内容包括:
1.1 Provider的 IP
1.2 Provider 的端口.
1.3 Provider 对外提供的接口列表.哪些方法.哪些接口类
1.4 Dubbo 的版本.
1.5 访问Provider的协议.
2、subscribe: 订阅.当Consumer启动时,自动去Registry获取到所已注册的服务的信息.
3、notify: 通知.当Provider的信息发生变化时, 自动由Registry向Consumer推送通知.
4、invoke: 调用. Consumer 调用Provider中方法
4.1 同步请求.消耗一定性能.但是必须是同步请求,因为需要接收调用方法后的结果.
5、count:次数. 每隔2分钟,provoider和consumer自动向Monitor发送访问次数.Monitor进行统计.
1、Dubbo协议(官方推荐协议)
优点:
采用NIO复用单一长连接,并使用线程池并发处理请求,减少握手和加大并发效率,性能较好(推荐使用)
缺点:
大文件上传时,可能出现问题(不使用Dubbo文件上传)
2、RMI(Remote Method Invocation)协议
优点:
JDK自带的能力。可与原生RMI互操作,基于TCP协议
缺点:
偶尔连接失败.
3、Hessian协议
优点:
可与原生Hessian互操作,基于HTTP协议
缺点:
需hessian.jar支持,http短连接的开销大
1、Zookeeper(官方推荐)
优点:支持分布式.很多周边产品.
缺点: 受限于Zookeeper软件的稳定性.Zookeeper专门分布式辅助软件,稳定较优
2、Multicast
优点:去中心化,不需要单独安装软件.
缺点:Provider和Consumer和Registry不能跨机房(路由)
3、Redis
优点:支持集群,性能高
缺点:要求服务器时间同步.否则可能出现集群失败问题.
4、Simple
优点: 标准RPC服务.没有兼容问题
缺点: 不支持集群.
答:SessionFactory对应Hibernate的一个数据存储的概念,它是线程安全的,可以被多个线程并发访问。SessionFactory一般只会在启动的时候构建。对于应用程序,最好将SessionFactory通过单例的模式进行封装以便于访问。Session是一个轻量级非线程安全的对象(线程间不能共享session),它表示与数据库进行交互的一个工作单元。Session是由SessionFactory创建的,在任务完成之后它会被关闭。Session是持久层服务对外提供的主要接口。Session会延迟获取数据库连接(也就是在需要的时候才会获取)。为了避免创建太多的session,可以使用ThreadLocal来取得当前的session,无论你调用多少次getCurrentSession()方法,返回的都是同一个session。
答:主要有以下三项区别:
1)如果没有找到符合条件的记录, get方法返回null,load方法抛出异常
2)get方法直接返回实体类对象, load方法返回实体类对象的代理
3)在Hibernate 3之前,get方法只在一级缓存(内部缓存)中进行数据查找, 如果没有找到对应的数据则越过二级缓存, 直接发出SQL语句完成数据读取; load方法则可以充分利用二级缓存中的现有数据;当然从Hibernate 3开始,get方法不再是对二级缓存只写不读,它也是可以访问二级缓存的
简单的说,对于load()方法Hibernate认为该数据在数据库中一定存在可以放心的使用代理来实现延迟加载,如果没有数据就抛出异常,而通过get()方法去取的数据可以不存在。
答:Hibernate的对象有三种状态:瞬态、持久态和游离态。游离状态的实例可以通过调用save()、persist()或者saveOrUpdate()方法进行持久化;脱管状态的实例可以通过调用 update()、0saveOrUpdate()、lock()或者replicate()进行持久化。save()和persist()将会引发SQL的INSERT语句,而update()或merge()会引发UPDATE语句。save()和update()的区别在于一个是将瞬态对象变成持久态,一个是将游离态对象变为持久态。merge方法可以完成save()和update()方法的功能,它的意图是将新的状态合并到已有的持久化对象上或创建新的持久化对象。按照官方文档的说明:(1)persist()方法把一个瞬态的实例持久化,但是并"不保证"标识符被立刻填入到持久化实例中,标识符的填入可能被推迟到flush的时间;(2) persist"保证",当它在一个事务外部被调用的时候并不触发一个Insert语句,当需要封装一个长会话流程的时候,一个persist这样的函数是需要的。(3)save"不保证"第2条,它要返回标识符,所以它会立即执行Insert语句,不管是不是在事务内部还是外部。update()方法是把一个已经更改过的脱管状态的对象变成持久状态;lock()方法是把一个没有更改过的脱管状态的对象变成持久状态。
vsftpd 是“very secure FTP daemon”的缩写,安全性是它的一个最大的特点。vsftpd 是一个 UNIX 类操作系统上运行的服务器的名字,它可以运行在诸如 Linux、BSD、Solaris、 HP-UNIX等系统上面,是一个完全免费的、开放源代码的ftp服务器软件,支持很多其他的 FTP 服务器所不支持的特征。
Nginx (engine x) 是一个高性能的HTTP和反向代理服务。Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Rambler.ru站点(俄文:Рамблер)开发的,第一个公开版本0.1.0发布于2004年10月4日。
Nginx 是一个很强大的高性能Web和反向代理服务,它具有很多非常优越的特性:在连接高并发的情况下,Nginx是Apache服务不错的替代品:Nginx在美国是做虚拟主机生意的老板们经常选择的软件平台之一。
答:http协议代理
搭建虚拟主机
服务的反向代理
在反向代理中配置集群的负载均衡
正向代理,意思是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端才能使用正向代理。
反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。
答:Remote Dictionary Server(Redis)是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Map), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。
1. 支持多种数据结构,如 string(字符串)、 list(双向链表)、dict(hash表)、set(集合)、zset(排序set)、hyperloglog(基数估算)
2. 支持持久化操作,可以进行aof及rdb数据持久化到磁盘,从而进行数据备份或数据恢复等操作,较好的防止数据丢失的手段。
3. 支持通过Replication进行数据复制,通过master-slave机制,可以实时进行数据的同步复制,支持多级复制和增量复制,master-slave机制是Redis进行HA的重要手段。
单进程请求,所有命令串行执行,并发情况下不需要考虑数据一致性问题。
答:String(字符串)
Hash(hash表)
List(链表)
Set(集合)
SortedSet(有序集合zset)
incr 让当前键值以1的数量递增,并返回递增后的值
incrby 可以指定参数一次增加的数值,并返回递增后的值
incrby 可以指定参数一次增加的数值,并返回递增后的值
decrby 可以指定参数一次递减的数值,并返回递减后的值
incrbyfloat 可以递增一个双精度浮点数
append 作用是向键值的末尾追加value。如果键不存在则将该键的值设置为value。返回值是追加后字符串的总长度。
mget/mset 作用与get/set相似,不过mget/mset可以同时获得/设置多个键的键值
del 根据key来删除value
flushdb 清除当前库的所有数据
hset 存储一个哈希键值对的集合
hget获取一个哈希键的值
hmset 存储一个或多个哈希是键值对的集合
hmget 获取多个指定的键的值
hexists 判断哈希表中的字段名是否存在 如果存在返回1 否则返回0
hdel 删除一个或多个字段
hgetall 获取一个哈希是键值对的集合
hvals 只返回字段值
hkeys 只返回字段名
hlen 返回key的hash的元素个数
lpush key value向链表左侧添加
rpush key value向链表右侧添加
lpop key 从左边移出一个元素
rpop key 从右边移出一个元素
llen key 返回链表中元素的个数 相当于关系型数据库中 select count(*)
lrange key start end lrange命令将返回索引从start到stop之间的所有元素。Redis的列表起始索引为0。
lrange也支持负索引 lrange nn -2 -1 如 -1表示最右边第一个元素 -2表示最右边第二个元素,依次类推。
lindex key indexnumber 如果要将列表类型当做数组来用,lindex命令是必不可少的。lindex命令用来返回指定索引的元素,索引从0开始
如果是负数表示从右边开始计算的索引,最右边元素的索引是-1。
Lset key indexnumber value 是另一个通过索引操作列表的命令,它会将索引为index的元素赋值为value。
sadd key value 添加一个string元素到,key对应的set集合中,成功返回1,如果元素已经在集合中返回0
scard key 返回set的元素个数,如果set是空或者key不存在返回0
smembers key 返回key对应set的所有元素,结果是无序的
sismember key value 判断value 是否在set中,存在返回1,0表示不存在或者key不存在
srem key value 从key对应set中移除给定元素,成功返回1,如果value 在集合中不存在或者key不存在返回0
zadd key score value 将一个或多个value及其socre加入到set中
zrange key start end 0和-1表示从索引为0的元素到最后一个元素(同LRANGE命令相似)
zrange key 0 -1 withscores 也可以连同score一块输出,使用WITHSCORES参数
zremrangebyscore key start end 可用于范围删除操作
ping 测试redis是否链接 如果已链接返回 PONG
echo value测试redis是否链接 如果已链接返回 echo命令后给定的值
keys * 返回所有的key 可以加*通配
exists key判断string类型一个key是否存在 如果存在返回1 否则返回0
expire key time(s) 设置一个key的过期时间 单位秒。时间到达后会删除key及value
ttl key 查询已设置过期时间的key的剩余时间 如果返回-2表示该键值对已经被删除
persist 移除给定key的过期时间
select dbindex 选择数据库(0-15)
move key dbIndex 将当前数据库中的key转移到其他数据库中
dbsize 返回当前数据库中的key的数目
info 获取服务器的信息和统计
flushdb 删除当前选择的数据库中的key
flushall 删除所有数据库中的所有key
quit 退出连接
答:以下两种
RDB方式
AOF方式
答:是RDB是对内存中数据库状态进行快照
RDB方式:将Redis在内存中的数据库状态保存到磁盘里面,RDB文件是一个经过压缩的二进制文件,通过该文件可以还原生成RDB文件时的数据库状态(默认下,持久化到dump.rdb文件,并且在redis重启后,自动读取其中文件,据悉,通常情况下一千万的字符串类型键,1GB的快照文件,同步到内存中的 时间是20-30秒)
RDB的生成方式:
1、执行命令手动生成
有两个Redis命令可以用于生成RDB文件,一个是SAVE,另一个是BGSAVE SAVE命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止,在服务器进程阻塞期间,服务器不能处理任何命令请求,BGSAVE命令会派生出一个子进程,然后由子进程负责创建RDB文件,服务器进程(父进程)继续处理命令请求,创建RDB文件结束之前,客户端发送的BGSAVE和SAVE命令会被服务器拒绝
2、通过配置自动生成
可以设置服务器配置的save选项,让服务器每隔一段时间自动执行一次BGSAVE命令,可以通过save选项设置多个保存条件,但只要其中任意一个条件被满足,服务器就会执行BGSAVE命令
例如:
save 900 1
save 300 10
save 60 10000
那么只要满足以下三个条件中的任意一个,BGSAVE命令就会被执行
服务器在900秒之内,对数据库进行了至少1次修改
服务器在300秒之内,对数据库进行了至少10次修改
服务器在60秒之内,对数据库进行了至少10000次修改
AOF持久化方式在redis中默认是关闭的,需要修改配置文件开启该方式。
AOF:把每条命令都写入文件,类似mysql的binlog日志
AOF方式:是通过保存Redis服务器所执行的写命令来记录数据库状态的文件。
AOF文件刷新的方式,有三种:
appendfsync always - 每提交一个修改命令都调用fsync刷新到AOF文件,非常非常慢,但也非常安全
appendfsync everysec - 每秒钟都调用fsync刷新到AOF文件,很快,但可能会丢失一秒以内的数据
appendfsync no - 依靠OS进行刷新,redis不主动刷新AOF,这样最快,但安全性就差
默认并推荐每秒刷新,这样在速度和安全上都做到了兼顾
AOF数据恢复方式
服务器在启动时,通过载入和执行AOF文件中保存的命令来还原服务器关闭之前的数据库状态,具体过程:
载入AOF文件
创建模拟客户端
从AOF文件中读取一条命令
使用模拟客户端执行命令
循环读取并执行命令,直到全部完成
如果同时启用了RDB和AOF方式,AOF优先,启动时只加载AOF文件恢复数据
答:什么叫做全文检索呢?这要从我们生活中的数据说起。
我们生活中的数据总体分为两种:结构化数据和非结构化数据。
1)结构化数据:指具有固定格式或有限长度的数据,如数据库,元数据等。
2)非结构化数据:指不定长或无固定格式的数据,如邮件,word文档等。
非结构化数据又一种叫法叫全文数据。
按照数据的分类,搜索也分为两种:
1)对结构化数据的搜索:如对数据库的搜索,用SQL语句。
2)对非结构化数据的搜索:如利用windows的搜索也可以搜索文件内容,
全文检索: 就是一种将文件中所有文本与检索项匹配的文字资料检索方法。全文检索首先将要查询的目标文档中的词提取出来,组成索引,通过查询索引达到搜索目标文档的目的。这种先建立索引,再对索引进行搜索的过程就叫全文检索(Full-text Search)。
Lucene是一个高效的,基于Java的全文检索库。
Lucene是apache软件基金会4 jakarta项目组的一个子项目,是一个开放源代码的全文检索引擎工具包,但它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的功能,或者是以此为基础建立起完整的全文检索引擎。Lucene是一套用于全文检索和搜寻的开源程序库,由Apache软件基金会支持和提供。Lucene提供了一个简单却强大的应用程序接口,能够做全文索引和搜寻。在Java开发环境里Lucene是一个成熟的免费开源工具。就其本身而言,Lucene是当前以及最近几年最受欢迎的免费Java信息检索程序库。
答:Solr是一个独立的企业级搜索应用服务器,它对外提供类似于Web-service的API接口。
Solr是一个高性能,采用Java开发,基于Lucene的全文搜索服务器。同时对其进行了扩展,提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展并对查询性能进行了优化,并且提供了一个完善的功能管理界面,是一款非常优秀的全文检索引擎。
文档通过Http利用XML 加到一个搜索集合中。查询该集合也是通过http收到一个XML/JSON响应来实现。它的主要特性包括:高效、灵活的缓存功能,垂直搜索功能,高亮显示搜索结果,通过索引复制来提高可用性,提供一套强大Data Schema来定义字段,类型和设置文本分析,提供基于Web的管理界面等。
答:如下两个部分
Solr的web服务
Solr的索引库
正排索引是以文档的ID为关键字,索引文档中每个字的位置信息,查找时扫描索引中每个文档中字的信息直到找出所有包含查询关键字的文档。
但是在查询的时候需对所有的文档进行扫描以确保没有遗漏,这样就使得检索时间大大延长,检索效率低下。
尽管正排索引的工作原理非常的简单,但是由于其检索效率太低,除非在特定情况下,否则实用性价值不大。
对数据进行分析,抽取出数据中的词条,以词条作为key,对应数据的存储位置作为value,实现索引的存储。这种索引称为倒排索引。
当solr存储文档时,solr会首先对文档数据进行分词,创建索引库和文档数据库。所谓的分词是指:将一段字符文本按照一定的规则分成若干个单词。
ActiveMQ是一种开源的,实现了JMS1.1规范的,面向消息(MOM)的中间件,为应用程序提供高效的、可扩展的、稳定的和安全的企业级消息通信。ActiveMQ使用Apache提供的授权,任何人都可以对其实现代码进行修改。
ActiveMQ的设计目标是提供标准的,面向消息的,能够跨越多语言和多系统的应用集成消息通信中间件。
ActiveMQ实现了JMS标准并提供了很多附加的特性。这些附加的特性包括,JMX管理(java Management Extensions,即java管理扩展),主从管理(master/salve,这是集群模式的一种,主要体现在可靠性方面,当主中介(代理)出现故障,那么从代理会替代主代理的位置,不至于使消息系统瘫痪)、消息组通信(同一组的消息,仅会提交给一个客户进行处理)、有序消息管理(确保消息能够按照发送的次序被接受者接收)。消息优先级(优先级高的消息先被投递和处理)、订阅消息的延迟接收(订阅消息在发布时,如果订阅者没有开启连接,那么当订阅者开启连接时,消息中介将会向其提交之前的,其未处理的消息)、接收者处理过慢(可以使用动态负载平衡,将多数消息提交到处理快的接收者,这主要是对PTP消息所说)、虚拟接收者(降低与中介的连接数目)、成熟的消息持久化技术(部分消息需要持久化到数据库或文件系统中,当中介崩溃时,信息不会丢失)、支持游标操作(可以处理大消息)、支持消息的转换、通过使用Apache的Camel可以支持EIP、使用镜像队列的形式轻松的对消息队列进行监控等。
答:如下3个场景都可以使用消息服务
1、异步处理
2、应用的解耦
3、流量的削峰
JMS(Java Messaging Service)是Java平台上有关面向消息中间件的技术规范,它便于消息系统中的Java应用程序进行消息交换,并且通过提供标准的产生、发送、接收消息的接口,简化企业应用的开发。
答:JMS消息机制主要分为两种模型:PTP模型和Pub/Sub模型。
1、PTP模型:(Point to Point 对点模型) 每一个消息传递给一个消息消费者,保证消息传递给消息消费者,且消息不会同时被多个消费者接收。如果消息消费者暂时不在连接范围内,JMS会自动保证消息不会丢失,直到消息消费者进入连接,消息将自动送达。因此,JMS需要将消息保存到永久性介质上,例如数据库或者文件。
2、Pub-Sub模型:(publish-subscription 发布者订阅者模型)每个主题可以拥有多个订阅者。JMS系统负责将消息的副本传给该主题的每个订阅者。
如果希望每一条消息都能够被处理,那么应该使用PTP消息模型。如果并不要求消息都必须被消息消费者接收到的情况下,可使用pub-sub消息模型。Pub-Sub模型可以在一对多的消息广播时使用。
Jsonp(JSON with Padding) 是 json 的一种"使用模式",可以让网页从别的域名(网站)那获取资料,即跨域读取数据。
跨域是指一个域(网站)下的文档或脚本试图去请求另一个域(网站)下的资源。
同源策略/SOP(Same origin policy)是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,现在所有支持JavaScript 的浏览器都会使用这个策略。如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源
MyCat是目前最流行的基于java语言编写的数据库中间件,是一个实现了MySQL协议的服务器,前端用户可以把它看作是一个数据库代理,用MySQL客户端工具和命令行访问,而其后端可以用MySQL原生协议与多个MySQL服务器通信,也可以用JDBC协议与大多数主流数据库服务器通信,其核心功能是分库分表。配合数据库的主从模式还可实现读写分离。
MyCat是基于阿里开源的Cobar产品而研发,Cobar的稳定性、可靠性、优秀的架构和性能以及众多成熟的使用案例使得MyCat变得非常的强大。
MyCat发展到目前的版本,已经不是一个单纯的MySQL代理了,它的后端可以支持MySQL、SQL Server、Oracle、DB2、PostgreSQL等主流数据库,也支持MongoDB这种新型NoSQL方式的存储,未来还会支持更多类型的存储。而在最终用户看来,无论是那种存储方式,在MyCat里,都是一个传统的数据库表,支持标准的SQL语句进行数据的操作,这样一来,对前端业务系统来说,可以大幅降低开发难度,提升开发速度。
就是把原本存储于一个库的数据存储到多个库上。
由于对数据库的读写都是对同一个库进行操作,所以单库并不能解决大规模并发写入的问题。
例如,我们会建立定义数据库workDB、商品数据库payDB、用户数据库userDB、日志数据库logDB等,分别用于存储项目数据定义表、商品定义表、用户数据表、日志数据表等。
优点
1)减少增量数据写入时的锁对查询的影响。
2)由于单表数量下降,常见的查询操作由于减少了需要扫描的记录,使得单表单次查询所需的检索行数变少,减少了磁盘IO,时延变短。
缺点:
无法解决单表数据量太大的问题。
横向切分/水平切分
把原本存储于一个表的数据分块存储到多个表上。当一个表中的数据量过大时,我们可以把该表的数据按照某种规则,进行划分,然后存储到多个结构相同的表,和不同的库上。
例如,我们userDB中的userTable中数据量很大,那么可以把userDB切分为结构相同的多个userDB:part0DB、part1DB等,再将userDB上的userTable,切分为很多userTable:userTable0、userTable1等,然后将这些表按照一定的规则存储到多个userDB上。
优点:
1)单表的并发能力提高了,磁盘I/O性能也提高了。
2)如果出现高并发的话,总表可以根据不同的查询,将并发压力分到不同的小表里面。
缺点:无法实现表连接查询。
答:Apache:全球应用最广泛的http服务器,免费,出自apache基金组织
Tomcat:应用也算非常广泛的web 服务器,支持部分j2ee,免费,出自 apache基金组织
JBoss:开源的应用服务器,比较受人喜爱,免费(文档要收费)
weblogic:应该说算是业界第一的app server,全部支持j2ee1.4(收费)
JBoss也支持j2ee
JBoss和WebLogic都含有Jsp和Servlet容器,也就可以做web容器,
JBoss和WebLogic也包含EJB容器,是完整的J2EE应用服务器
tomcat 只能做jsp和servlet的container
A. | nagios |
B. | Jenkins |
C. | nginx |
D. | docker |
分析:答案: C Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like 协议下发行。其特点是占有内存少,并发能力强,事实上nginx的并发能力确实在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等
|
作用:
Tomcat:目前应用非常广泛的免费web服务器,支持部分j2ee。
WebSphere:是IBM集成软件平台。可做web服务器,WebSphere提供了可靠、灵活和健壮的集成软件。
Weblogic:是美国bea公司出品的一个基于j2ee架构的中间件。BEA WebLogic是用于开发、集成、部署和管理大型分布式Web应用、网络应用和数据库应用的Java应用服务器。
特点(区别):
1)价位不同:Tomcat的是免费的;WebLogic与WebSphere是收费的,而且价格不菲。
2) 开源性不同:Tomcat的是完全开源的,而其他两个不是。WebLogic与WebSphere都是对业内多种标准的全面支持,包括JSB、JMS、JDBC、XML和WML,使Web应用系统实施更简单,且保护投资,同时也使基于标准的解决方案的开发更加简便。
3) 扩展性的不同:WebLogic和WebSphere都是以其高扩展的架构体系闻名于业内,包括客户机连接的共享、资源 pooling以及动态网页。
4)应用范围的区别:Tomcat 是一个小型的轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。WebLogic和WebSphere是商业软件,功能齐全强大,主要应用于大型企业的大型项目。
5)安全性问题区别:因为Tomcat是开源的,所以它们的安全性相对来说比较低,万一应用服务器本身有什么漏洞,你是没办法向Apache索赔的。而WebLogic和WebSphere其容错、系统管理和安全性能已经在全球数以千记的关键任务环境中得以验证。
C/S结构,即Client/Server(客户机/服务器)结构,通过将任务合理分配到Client端和Server端,降低了系统的通讯开销,可充分利用两端硬件环境优势。早期软件系统多以此作为首选设计标准。
B/S结构,即Browser/Server(浏览器/服务器)结构,是随着Internet技术的兴起,对C/S结构的一种变化或者改进的结构。在这种结构下,用户界面完全通过WWW浏览器实现,一部分事务逻辑在前端实现,但是主要事务逻辑在服务器端实现,节约了开发成本,便于软件维护。
区别
1、C/S是建立在局域网的基础上的。B/S是建立在广域网的基础上的,但并不是说B/S结构不能在局域网上使用。
2、B/S业务扩展简单方便,通过增加页面即可增加服务器功能。C/S的客户端还需要安装专用的客户端软件,不利于扩展。
3、B/S维护简单方便。开发、维护等几乎所有工作也都集中在服务器端,当企业对网络应用进行升级时,只需更新服务器端的软件就可以,这减轻了异地用户系统维护与升级的成本
4、B/S响应速度不及C/S;
5、B/S用户体验效果不是很理想
容器也是 java 程序,它的主要作用是为应用程序提供运行环境。容器用来接管安全性、并发性、事务处理、交换到辅助存储器和其它服务的责任
以tomcat为例:Tomcat是一个后台服务进程,其它的servlet(相当于DLL)是在Tomcat容器内运行,Broswer只与Tomcat通迅; Tomcat接受browser的请求,经过一系列动作(如果是静态网页,那么装载,按http协议形成响应流;如果是动态的如JSP,那就要调用JDK 中的servlet.jsp接口,解释形成静态网页,按http协议生成响应流发送回browser)后,形成静态网页,返回响应。
•传统的数据库连接方式
一个连接对象对应一个物理连接
每次操作都打开一个物理连接,
使用完都关闭连接,造成系统性能低下。
•连接池技术
客户程序得到的连接对象是连接池中物理连接的一个句柄,调用连接对象的close()方法,物理连接并没有关闭,数据源的实现只是删除了客户程序中的连接对象和池中的连接对象之间的联系.
•数据库连接的建立及关闭是耗费系统资源的操作,在大型应用中对系统的性能影响尤为明显。为了能重复利用数据库连接对象,缩短请求的响应时间和提高服务器的性能,支持更多的客户,应采用连接池技术.

数据库连接池的原理
传统连接方式:
首先调用Class.forName()方法加载数据库驱动,
然后调用DriverManager.getConnection()方法建立连接.
连接池技术:
连接池解决方案是在应用程序启动时就预先建立多个数据库连接对象,然后将连接对象保存到连接池中。
当客户请求到来时,从池中取出一个连接对象为客户服务。
当请求完成时,客户程序调用close()方法,将连接对象放回池中.
对于多于连接池中连接数的请求,排队等待。
应用程序还可根据连接池中连接的使用率,动态增加或减少池中的连接数。


一、MVC原理
MVC是一种程序开发设计模式,它实现了显示模块与功能模块的分离。提高了程序的可维护性、可移植性、可扩展性与可重用性,降低了程序的开发难度。它主要分模型、视图、控制器三层。
1、模型(model)它是应用程序的主体部分,主要包括业务逻辑模块和数据模块。模型与数据格式无关,这样一个模型能为多个视图提供数据。由于应用于模型的代码只需写一次就可以被多个视图重用,所以减少了代码的重复性
2、视图(view) 用户与之交互的界面、在web中视图一般由jsp,html组成
3、控制器(controller)接收来自界面的请求 并交给模型进行处理 在这个过程中控制器不做任何处理只是起到了一个连接的作用
二、MVC的优点
1、降低代码耦合性。在MVC模式中,三个层各施其职,所以如果一旦哪一层的需求发生了变化,就只需要更改相应的层中的代码而不会影响到其他层中的代码。
2、有利于分工合作。在MVC模式中,由于按层把系统分开,那么就能更好的实现开发中的分工。网页设计人员可进行开发视图层中的JSP,而对业务熟悉的人员可开发业务层,而其他开发人员可开发控制层。
3、有利于组件的重用。如控制层可独立成一个能用的组件,表示层也可做成通用的操作界面。可以为一个模型在运行时同时建立和使用多个视图。
三、MVC的不足之处
1、增加了系统结构和实现的复杂性。对于简单的界面,严格遵循MVC,使模型、视图与控制器分离,会增加结构的复杂性,并可能产生过多的更新操作,降低运行效率。
2、视图与控制器间的过于紧密的连接。视图与控制器是相互分离,但确实联系紧密的部件,视图没有控制器的存在,其应用是很有限的,反之亦然,这样就妨碍了他们的独立重用。
3、视图对模型数据的低效率访问。依据模型操作接口的不同,视图可能需要多次调用才能获得足够的显示数据。对未变化数据的不必要的频繁访问,也将损害操作性能。
4、目前,一般高级的界面工具或构造器不支持模式。改造这些工具以适应MVC需要和建立分离的部件的代价是很高的,从而造成MVC使用的困难。
1)页面提交页码(第几页)到Servlet中
2)Servlet接收到页码后,将页码传递给分页工具类(PageBean)
3)Servlet中调用Service层传入PageBean对象
4)Service层调用DAO层传入PageBean对象
5)Servlet中得到查询出来的数据,并setAttrivute保存
6)在页面中得到(getAttribute)数据,遍历输出
答: Unix和Linux平台下使用最广泛的免费HTTP服务器是Apache服务器,而Windows平台的服务器通常使用IIS作为Web服务器。选择Web服务器应考虑的因素有:性能、安全性、日志和统计、虚拟主机、代理服务器、缓冲服务和集成应用程序等。下面是对常用服务器的简介:
IIS:Microsoft的Web服务器产品为Internet Information Services。IIS 是允许在公共Intranet或Internet上发布信息的Web服务器。IIS是目前最流行的Web服务器产品之一,很多著名的网站都是建立在IIS的平台上。IIS提供了一个图形界面的管理工具,称为Internet服务管理器,可用于监视配置和控制Internet服务。IIS是一种Web服务组件,其中包括Web服务器、FTP服务器、NNTP服务器和SMTP服务器,分别用于网页浏览、文件传输、新闻服务和邮件发送等方面,它使得在网络(包括互联网和局域网)上发布信息成了一件很容易的事。它提供ISAPI(Intranet Server API)作为扩展Web服务器功能的编程接口;同时,它还提供一个Internet数据库连接器,可以实现对数据库的查询和更新。
Kangle:Kangle Web服务器是一款跨平台、功能强大、安全稳定、易操作的高性能Web服务器和反向代理服务器软件。此外,Kangle也是一款专为做虚拟主机研发的Web服务器。实现虚拟主机独立进程、独立身份运行。用户之间安全隔离,一个用户出问题不影响其他用户。支持PHP、ASP、ASP.NET、Java、Ruby等多种动态开发语言。
WebSphere:WebSphere Application Server是功能完善、开放的Web应用程序服务器,是IBM电子商务计划的核心部分,它是基于Java的应用环境,用于建立、部署和管理Internet和Intranet Web应用程序,适应各种Web应用程序服务器的需要,范围从简单到高级直到企业级。
WebLogic:BEA WebLogic Server是一种多功能、基于标准的Web应用服务器,为企业构建自己的应用提供了坚实的基础。各种应用开发、部署所有关键性的任务,无论是集成各种系统和数据库,还是提交服务、跨Internet协作,Weblogic都提供了相应的支持。由于它具有全面的功能、对开放标准的遵从性、多层架构、支持基于组件的开发,基于Internet的企业都选择它来开发、部署最佳的应用。BEA WebLogic Server在使应用服务器成为企业应用架构的基础方面一直处于领先地位,为构建集成化的企业级应用提供了稳固的基础,它们以 Internet的容量和速度,在连网的企业之间共享信息、提交服务,实现协作自动化。
Apache:目前Apache仍然是世界上用得最多的Web服务器,市场占有率约为60%左右。世界上很多著名的网站都是Apache的产物,它的成功之处主要在于它的源代码开放、有一支强大的开发团队、支持跨平台的应用(可以运行在几乎所有的Unix、Windows、Linux系统平台上)以及它的可移植性等方面。
Tomcat:Tomcat是一个开放源代码、运行Servlet和JSP的容器。TomcatServer实现了Servlet和JSP规范。此外,Tomcat还实现了Apache-Jakarta规范而且比绝大多数商业应用软件服务器要好,因此目前也有不少的Web服务器都选择了Tomcat。
Nginx:读作"engine x",是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP代理服务器。 Nginx是由Igor Sysoev为俄罗斯访问量第二的 Rambler.ru站点开发的,第一个公开版本0.1.0发布于2004年10月4日。其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名。
答:Model 1是以页面为中心的Java Web开发,只适合非常小型的应用程序,Model 2是基于MVC架构模式的应用,这一点在前文的面试题中已经详细讲解过了。

框架(framework)是一个框子--》指约束性,也是一个架子--》指支撑性IT语境中的框架,特指为解决一个开放性问题而设计的具有一定约束性的支撑结构,在此结构上可以根据具体问题扩展、按插更多的组成部分,从而更迅速和方便地架构完整的解决问题的方案。
1)框架本身一般不完整到可以解决特定问题,但是可以帮助您快速解决特定问题:
没有框架所有的工作都从零开始做,有了框架,为我们提供了一定的功能。我们就可以在框架的基础上开发,极大的解决了生产力。
不同的框架,是为了解决不同领域的问题,一定要为了解决问题才去学习框架。
2)框架天生就是为了扩展而设计的
3)框架里面可以为后续的组件提供很多辅助性、支撑性的方便易用的实用工具(utilities),也就是框架时常配套一些帮组解决某类问题的库(libraries)或工具(tools).
在java中就是一系列的jar包,其本质就是对jdk功能的扩展。
是为了解决传统MVC模式(jsp+servlet+javabean)一些问题而出现的框架
传统MVC模式模式问题:
1) 所有的Servlet和Servlet映射都要配置在web.xml中,如果项目太大,web.xml就太庞大并且不能实现模块化管理。
2)Servlet的主要功能就是接受参数、调用逻辑、跳转页面,比如像其他字符编码、文件上传等功能也要写在Servlet中,不能让Servlet主要功能而需要做处理一些特例。
3)接受参数比较麻烦
(String name = request.getParameter(“name”)),不能通过model接受,只能单个接收,接收完成后转换封装model。
4)跳转页面方式比较单一(forward,redirect),并且当我们的页面名称发生改变时需要改变Servlet源代码。
现在比较常用的MVC框架:
webwork
Struts
Struts2
SpringMVC
一个请求在struts2框架中处理大概分为一下几个步骤:
1)客户浏览器发送一个指向Servlet容器(例如Tomcat)的请求
2)这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin)
3)接着FilterDispatcher(StrutsPrepareAndExecuteFilter)被调用,FilterDispatcher询问ActionMapper来决定这个请是否需要调用某个Action
4)如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy
5)ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类
6)ActionProxy创建一个ActionInvocation的实例。
7)ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。
8)一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是(但不总是,也 可 能是另外的一个Action链)一个需要被表示的JSP或者FreeMarker的模版。在表示的过程中可以使用Struts2 框架中继承的标签。在这个过程中需要涉及到ActionMapper
java里的拦截器是动态拦截Action调用的对象,它提供了一种机制可以使开发者定义一个action执行的前后执行的代码,也可以在一个action执行前阻止其执行,同时也提供了一种可以提取action中可重用部分的方式。
在AOP(Aspect Oriented Programming)中拦截器用于在某个方法或字段被访问之前,进行拦截后在之前或之后加入某些操作
1)struts2中的功能(参数处理、文件上传、字符编码等)都是通过系统拦截器实现的
2)当然我们也可以自定义拦截器,进行可插拔配置,可以执行Action的方法前后,加入相关逻辑完成业务。
使用场景:
1)用户登录判断,在执行action的前面判断是否已经登录,如果没有登录的就跳转登录页面。
2)用户权限判断,在执行action的前面判断是否具有,如果没有权限就给出提示信息。
3)操作日志...
1)用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获;
2)DispatcherServlet 对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象 (包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;
3)DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(...)方法)
4) 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
5)Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象;
6)根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet ;
7)ViewResolver 结合Model和View,来渲染视图
8)将渲染结果返回给客户端。
快速记忆:
核心控制器捕获请求,查找Hander,执行Handler,选择ViewResolver,通过ViewResoler渲染视图并返回
目前企业中使用SpringMvc的比例已经远远超过Struts2,那么两者到底有什么区别,是很多初学者比较关注的问题,下面我们就来对SpringMvc和Struts2进行各方面的比较:
1)核心控制器(前端控制器、预处理控制器):对于使用过mvc框架的人来说这个词应该不会陌生,核心控制器的主要用途是处理所有的请求,然后对那些特殊的请求 (控制器)统一的进行处理(字符编码、文件上传、参数接受、异常处理等等),spring mvc核心控制器是Servlet,而Struts2是Filter。
2)控制器实例:Spring Mvc会比Struts快一些(理论上)。Spring Mvc是基于方法设计,而Sturts是基于对象,每次发一次请求都会实例一个action,每个action都会被注入 属性,而Spring更像Servlet一样,只有一个实例,每次请求执行对应的方法即可(注意:由于是单例实例,所以应当避免全局变量的修改,这样会产生线程安全问题 )
3)管理方式:大部分的公司的核心架构中,就会使用到spring,而spring mvc又是spring中的一个模块,所以spring对于spring mvc的控制器管理更加简单方便,而且提供了全 注解方式进行管理,各种功能的注解都比较全面,使用简单,而struts2需要采用XML很多的配置参数来管理(虽然也可以采用注解,但是几乎没有公司那 样使用)
4)参数传递:Struts2中自身提供多种参数接受,其实都是通过(ValueStack)进行传递和赋值,而SpringMvc是通过方法的参数进行接收。
5)学习难度:Struts更加很多新的技术点,比如拦截器、值栈及OGNL表达式,学习成本较高,springmvc 比较简单,很较少的时间都能上手。
6)intercepter 的实现机制:struts有以自己的interceptor机制,spring mvc用的是独立的AOP方式。这样导致struts的配置文件量还是比spring mvc大,虽然struts的配置能继承,所以我觉得论使用上来讲,spring mvc使用更加简洁,开发效率Spring MVC确实比struts2高。spring mvc是方法级别的拦截,一个方法对应一个request上下文,而方法同时又跟一个url对应,所以说从架构本身上spring3 mvc就容易实现restful url。struts2是类级别的拦截,一个类对应一个request上下文;实现restful url要费劲,因为struts2 action的一个方法可以对应一个url;而其类属性却被所有方法共享,这也就无法用注解或其他方式标识其所属方法了。spring3 mvc的方法之间基本上独立的,独享request response数据,请求数据通过参数获取,处理结果通过ModelMap交回给框架方法之间不共享变量,而struts2搞的就比较乱,虽然方法之间 也是独立的,但其所有Action变量是共享的,这不会影响程序运行,却给我们编码,读程序时带来麻烦。
7)spring mvc处理ajax请求,直接通过返回数据,方法中使用注解@ResponseBody,spring mvc自动帮我们对象转换为JSON数据。而struts2是通过插件的方式来处理。
在springMVC流行起来之前,struts2在MVC框架中占核心地位,随着SpringMVC的出现,SpringMVC慢慢的取代了struts2,但是很多的企业原来搭建的框架都是使用struts2。
Spring是什么?
Spring是J2EE应用程序框架,是轻量级的IOC和AOP的容器框架,主要针对JavaBean的生命周期进行管理的轻量级容器,可以单独使用,也可以和struts框架,ibatis框架等组合使用。
1)IOC(Inversion of Control)
ioc控制反转,又称为“依赖注入”;
IOC的基本概念是:不创建对象,但是描述创建它们的方式。在代码中不直接与对象和服务连接,但在配置文件中描述哪一个组件需要哪一项服务。容器负责将这些联系在一起。
其原理是基于OO设计原则的The Hollywood Principle:Don't call us, we'll call you(别找我,我会来找你的)。也就是说,所有的组件都是被动的(Passive),所有的组件初始化和调用都由容器负责。组件处在一个容器当中,由容 器负责管理。
简单的来讲,就是由容器控制程序之间的关系,而非传统实现中,由程序代码直接操控。这也就是所谓“控制反转”的概念所在:控制权由应用代码中转到了外部容器,控制权的转移,是所谓反转。
2)AOP 面向切面编程
核心原理:使用动态代理的设计模式在执行方法前后或出现异常常做加入相关逻辑
我们使用AOP来做:
1)事务处理:执行方法前开启事务,执行完成后关闭事务,出现异常后回滚事务
2)权限判断:在执行方法前,判断是否具有权限
3)日志:在执行前进行日志处理
多个事物存在是怎么处理的策略
1)PROPAGATION_REQUIRED:如果存在一个事务,则支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
2)PROPAGATION_SUPPORTS:如果存在一个事务,支持当前事务,如果当前没有事务,就以非事务方式执行。
3)PROPAGATION_MANDATORY:如果存在一个事务,支持当前事务,如果当前没有事务,就抛出异常。
4)PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
5)PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
6)PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
7)PROPAGATION_NESTED:支持当前事务,新增Savepoint点,与当前事务同步提交或回滚。
对象关系映射(Object Relation Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术,简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中,那么到底如何实现持久化呢?一种简单的方案时采用硬编码方式(jdbc操作sql方式),为每一种可能的数据库访问操作提供单独的方法。
这种方案存在以下不足:
1. 持久化层缺乏弹性,一旦出现业务需求变更,就必须修改持久化层的接口
2. 持久化层同时与域模型与关系数据库模型绑定,不管域模型还是关系数据库模型发生变化,都要修改持久化层的相关程序代码,增加软件的维护难度。
ORM提供了实现持久化层的另一种模式,它采用映射元数据来描述对象关系的映射,使得ORM中间件能在任何一个应用的业务逻辑层和数据库层之间充当桥梁,Java典型的ORM框架有:Hibernate,ibatis(mybtis),speedframework。
ORM框架的方法论基于三个核心原则:
简单:以最基本的形式建模数据
传达性:数据库结构被任何人都能理解的语言文档化
精确性:基于数据模型创建正确标准化了结构
对象关系映射(Object Relation Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术,可以简单的方案采用硬编码方式(jdbc操作sql方式),为每一种可能的数据库访问操作提供单独的方法,这种方法存在很多缺陷,使用ORM框架(为了解决面向对象与关系数据库存在互不匹配的现象的框架)来解决。
临时状态/瞬时状态(transient):刚刚用new语句创建,没有被持久化,无id
不处于session中(没有使用session的方法去操作临时对象),该对象成为临时对象
持久化状态,托管状态(persistent):已经被持久化,加入session的缓存中,session是没有关闭
该状态的对象为持久化对象。
游离状态,脱管状态(detached):已经被持久化,但不处于session中,该状态的对象为游离对象。
删除状态(removed):对象有关联的id,并且在session管理下,但是已经被计划(事务提交的时候,commit)删除,如果没有事务就不能删除
相互转换
答:一、why(为什么要用Hibernate缓存?)
Hibernate是一种持久化层框架,经常访问物理数据库。
为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能
缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据。
为了提高访问速度,把磁盘或者数据库访问变成内存访问
二、what(Hibernate缓存原理是怎样的?)Hibernate缓存包括两大类:Hibernate 一级缓存和Hibernate二级缓存
1. Hibernate一级缓存又称为”session的缓存”。
session缓存内置不能被卸载,session的缓存是事务范围的缓存(session对象的生命周期通常对应一个数据库事务或者一个应用事务)。
一级缓存中,持久化类的每个实例都具有唯一的OID
2. Hibernate的二级缓存又称为”sessionFactory的缓存”。
由于sessionFactory对象的生命周期和应用程序的整个过程对应,因此Hibernate二级缓存是进程范围或者集群范围的缓存,有可能出现并发问题,因此需要采用适当的并发访问策略,该策略为被缓存的数据提供了事务隔离级别。
第二级缓存是可选的,是一个可配置的插件,默认下sessionFactory不会启用这个插件。
什么样的数据适合存放到二级缓存中?
1) 很少被修改的数据 (帖子的最后回复时间)
2) 经常被查询的数据 (电商的地点)
3) 不是很重要的数据,允许出现偶尔并发的数据
4) 不会被并发访问的数据
5) 常量数据
扩展:Hibernate的二级缓存默认是不支持分布式缓存的,使用memcache,redis等中央缓存来代替二级缓存。
webservice是一个SOA(面向服务的编程)的架构,它是不依赖于语言,不依赖于平台,可以实现不同的语言间的相互调用,通过Internet进行基于http协议的网络应用间的交互。
1、异构系统(不同的开发语言)的整合
2、不同客户端的整合 (浏览器、手机端(android\ios)、微信)
3、实实在在的例子:
天气预报:可以通过实现webservice客户端调用远程天气服务实现的
4、单点登录:一个服务实现所有系统的登录
Activity是一个业务流程管理(BPM)和工作流系统,适用于开发人员和系统管理员,其核心是超快速,稳定的BPMN2的流程引擎,它易于与Spring集成使用。
主要用在OA中,把线下流程放在线上,把现实生活中一些流程固话定义到系统中,然后通过输入表单数据完成业务。
他可以用在OA系统的流程管理中
请假流程(小于三天,一级主管审批,大于三天二级主管审批)
报销流程(价格区间)
答:MyBatis是一个可以自定义SQL、存储过程和高级映射的持久层框架。
答:1)Mybatis使用RowBounds对象进行分页,也可以直接编写sql实现分页,也可以使用Mybatis的分页插件。
2)分页插件的原理:实现Mybatis提供的接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql。
举例:select * from student,拦截sql后重写为:select t.* from (select * from student)t limit 0,10
答:1)Mybatis和hibernate不同,它不完全是一个ORM框架,因为MyBatis需要程序员自己编写Sql语句,不过mybatis可以通过XML或注解方式灵活配置要运行的sql语句,并将java对象和sql语句映射生成最终执行的sql,最后将sql执行的结果再映射生成java对象。
2)Mybatis学习门槛低,简单易学,程序员直接编写原生态sql,可严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,例如互联网软件、企业运营类软件等,因为这类软件需求变化频繁,一但需求变化要求成果输出迅速。但是灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数据库的软件则需要自定义多套sql映射文件,工作量大。
3)Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件(例如需求固定的定制化软件)如果用hibernate开发可以节省很多代码,提高效率。但是Hibernate的缺点是学习门槛高,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡,以及怎样用好Hibernate需要具有很强的经验和能力才行。
总之,按照用户的需求在有限的资源环境下只要能做出维护性、扩展性良好的软件架构都是好架构,所以框架只有适合才是最好。
答: Mybatis将所有Xml配置信息都封装到All-In-One重量级对象Configuration内部。在Xml映射文件中,< parameterMap>标签会被解析为ParameterMap对象,其每个子元素会被解析为ParameterMapping对象。< resultMap>标签会被解析为ResultMap对象,其每个子元素会被解析为ResultMapping对象。每一个< select>、< insert>、< update>、< delete>标签均会被解析为MappedStatement对象,标签内的sql会被解析为BoundSql对象。
答:接口映射就是在MyBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定,我们直接调用接口方法就可以,这样比起原来了SqlSession提供的方法我们可以有更加灵活的选择和设置.
答:能,Mybatis不仅可以执行一对一、一对多的关联查询,还可以执行多对一,多对多的关联查询,多对一查询,其实就是一对一查询,只需要把selectOne()修改为selectList()即可;多对多查询,其实就是一对多查询,只需要把selectOne()修改为selectList()即可。
关联对象查询,有两种实现方式,一种是单独发送一个sql去查询关联对象,赋给主对象,然后返回主对象。另一种是使用嵌套查询,嵌套查询的含义为使用join查询,一部分列是A对象的属性值,另外一部分列是关联对象B的属性值,好处是只发一个sql查询,就可以把主对象和其关联对象查出来。
答:MyBatis里面的动态Sql一般是通过if节点来实现,通过OGNL语法来实现,但是如果要写的完整,必须配合where,trim节点,where节点是判断包含节点有内容就插入where,否则不插入,trim节点是用来判断如果动态语句是以and 或or开始,那么会自动把这个and或者or取掉。
答:1)Mapper接口方法名和mapper.xml中定义的每个sql的id相同
2)Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
3)Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
4)Mapper.xml文件中的namespace即是mapper接口的类路径。
答:第一种是使用< resultMap>标签,逐一定义列名和对象属性名之间的映射关系。
第二种是使用sql列的别名功能,将列别名书写为对象属性名,比如T_NAME AS NAME,对象属性名一般是name,小写,但是列名不区分大小写,Mybatis会忽略列名大小写,智能找到与之对应对象属性名,你甚至可以写成T_NAME AS NaMe,Mybatis一样可以正常工作。
有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。
答:接口绑定有两种实现方式,一种是通过注解绑定,就是在接口的方法上面加上@Select@Update等注解里面包含Sql语句来绑定,另外一种就是通过xml里面写SQL来绑定,在这种情况下,要指定xml映射文件里面的namespace必须为接口的全路径名.
答:有联合查询和嵌套查询,联合查询是几个表联合查询,只查询一次,通过在resultMap里面配置association节点配置一对一的类就可以完成;嵌套查询是先查一个表,根据这个表里面的结果的外键id,去再另外一个表里面查询数据,也是通过association配置,但另外一个表的查询通过select属性配置。
答:当Sql语句比较简单时候,用注解绑定;当SQL语句比较复杂时候,用xml绑定,一般用xml绑定的比较多
答:1) MyBatis把sql语句从Java源程序中独立出来, 放在单独的XML文件中编写,给程序的维护带来了很大便利。
2) MyBatis封装了底层JDBC API的调用细节,并能自动将结果集转换成Java Bean对象,大大简化了Java数据库编程的重复工作。
3) 因为MyBatis需要程序员自己去编写sql语句,程序员可以结合数据库自身的特点灵活控制sql语句, 因此能够实现比Hibernate等全自动orm框架更高的查询效率,能够完成复杂查询。
微服务框架
答:Spring Boot的优点有:
减少开发,测试时间和努力。
使用JavaConfig有助于避免使用XML。
避免大量的Maven导入和各种版本冲突。
提供意见发展方法。
通过提供默认值快速开始开发。
没有单独的Web服务器需要。这意味着你不再需要启动Tomcat,Glassfish或其他任何东西。
需要更少的配置 因为没有web.xml文件。只需添加用@ Configuration注释的类,然后添加用@Bean注释的方法,Spring将自动加载对象并像以前一样对其进行管理。您甚至可以将@Autowired添加到bean方法中,以使Spring自动装入需要的依赖关系中。
基于环境的配置 使用这些属性,您可以将您正在使用的环境传递到应用程序:-Dspring.profiles.active = {enviornment}。在加载主应用程序属性文件后,Spring将在(application{environment} .properties)中加载后续的应用程序属性文件。
答:这可以使用DEV工具来实现。通过这种依赖关系,您可以节省任何更改,嵌入式tomcat将重新启动。Spring Boot有一个开发工具(DevTools)模块,它有助于提高开发人员的生产力。Java开发人员面临的一个主要挑战是将文件更改自动部署到服务器并自动重启服务器。开发人员可以重新加载Spring Boot上的更改,而无需重新启动服务器。这将消除每次手动部署更改的需要。Spring Boot在发布它的第一个版本时没有这个功能。这是开发人员最需要的功能。DevTools模块完全满足开发人员的需求。该模块将在生产环境中被禁用。它还提供H2数据库控制台以更好地测试应用程序。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>
1、单体架构
单体架构也称之为单体系统或者是单体应用。就是一种把系统中所有的功能、模块耦合在一个应用中的架构方式。
单体架构特点:打包成一个独立的单元(导成一个唯一的jar包或者是war包),会一个进程的方式来运行。
单体架构的优点、缺点
优点:
项目易于管理
部署简单
缺点:
测试成本高
可伸缩性差
可靠性差
迭代困难
跨语言程度差
团队协作难
2、MVC架构
MVC架构特点:
MVC是模型(Model)、视图(View)、控制器(Controller)3个单词的缩写。 下面我们从这3个方面来讲解MVC中的三个要素。
Model是指数据模型,是对客观事物的抽象。 如一篇博客文章,我们可能会以一个Post类来表示,那么,这个Post类就是数据对象。 同时,博客文章还有一些业务逻辑,如发布、回收、评论等,这一般表现为类的方法,这也是model的内容和范畴。 对于Model,主要是数据、业务逻辑和业务规则。相对而言,这是MVC中比较稳定的部分,一般成品后不会改变。 开发初期的最重要任务,主要也是实现Model的部分。这一部分写得好,后面就可以改得少,开发起来就快。
View是指视图,也就是呈现给用户的一个界面,是model的具体表现形式,也是收集用户输入的地方。 如你在某个博客上看到的某一篇文章,就是某个Post类的表现形式。 View的目的在于提供与用户交互的界面。换句话说,对于用户而言,只有View是可见的、可操作的。 事实上也是如此,你不会让用户看到Model,更不会让他直接操作Model。 你只会让用户看到你想让他看的内容。 这就是View要做的事,他往往是MVC中变化频繁的部分,也是客户经常要求改来改去的地方。 今天你可能会以一种形式来展示你的博文,明天可能就变成别的表现形式了。
Contorller指的是控制器,主要负责与model和view打交道。 换句话说,model和view之间一般不直接打交道,他们老死不相往来。view中不会对model作任何操作, model不会输出任何用于表现的东西,如HTML代码等。这俩甩手不干了,那总得有人来干吧,只能Controller上了。 Contorller用于决定使用哪些Model,对Model执行什么操作,为视图准备哪些数据,是MVC中沟通的桥梁。
MVC架构优缺点
优点:
各施其职,互不干涉。
在MVC模式中,三个层各施其职,所以如果一旦哪一层的需求发生了变化,就只需要更改相应的层中的代码而不会影响到其它层中的代码。
有利于开发中的分工。
在MVC模式中,由于按层把系统分开,那么就能更好的实现开发中的分工。网页设计人员可以进行开发视图层中的JSP,对业务熟悉的开发人员可开发业务层,而其它开发人员可开发控制层。
有利于组件的重用。
分层后更有利于组件的重用。如控制层可独立成一个能用的组件,视图层也可做成通用的操作界面。
缺点:
增加了系统结构和实现的复杂性。
视图与控制器间的过于紧密的连接。
视图对模型数据的低效率访问。
3、面向服务架构(SOA)
面向服务的架构(SOA)是一个组件模型,它将应用程序拆分成不同功能单元(称为服务)通过这些服务之间定义良好的接口和契约联系起来。接口是采用中立的方式进行定义的,它应该独立于实现服务的硬件平台、操作系统和编程语言。这使得构建在各种各样的系统中的服务可以以一种统一和通用的方式进行交互。
面向服务架构特点:
系统是由多个服务构成
每个服务可以单独独立部署
每个服务之间是松耦合的。服务内部是高内聚的,外部是低耦合的。高内聚就是每个服务只关注完成一个功能。
服务的优点、缺点
优点:
测试容易
可伸缩性强
可靠性强
跨语言程度会更加灵活
团队协作容易
系统迭代容易
缺点:
运维成本过高,部署数量较多
接口兼容多版本
分布式系统的复杂性
分布式事务
业界对于可扩展的系统架构设计有一个朴素的理念,就是:通过加机器就可以解决容量和可用性问题。(如果一台不行那就两台)。
我是个段子:(世界上没有什么事是一顿烧烤不能解决的。如果有,那就两顿。)
这一理念在“云计算”概念疯狂流行的今天,得到了广泛的认可!对于一个规模迅速增长的系统而言,容量和性能问题当然是首当其冲的。但是随着时间的向前,系统规模的增长,除了面对性能与容量的问题外,还需要面对功能与模块数量上的增长带来的系统复杂性问题以及业务的变化带来的提供差异化服务问题。而许多系统,在架构设计时并未充分考虑到这些问题,导致系统的重构成为常态,从而影响业务交付能力,还浪费人力财力!对此,《可扩展的艺术》一书提出了一个更加系统的可扩展模型—— AKF可扩展立方 (Scalability Cube) 。这个立方体中沿着三个坐标轴设置分别为:X、Y、Z。
Y轴扩展会将庞大的整体应用拆分为多个服务。每个服务实现一组相关的功能,如订单管理、客户管理等。在工程上常见的方案是 服务化架构(SOA) 。比如对于一个电子商务平台,我们可以拆分成不同的服务
X轴扩展与我们前面朴素理念是一致的,通过绝对平等地复制服务与数据,以解决容量和可用性的问题。其实就是将微服务运行多个实例,做集群加负载均衡的模式。
Z轴扩展通常是指基于请求者或用户独特的需求,进行系统划分,并使得划分出来的子系统是相互隔离但又是完整的。以生产汽车的工厂来举例:福特公司为了发展在中国的业务,或者利用中国的廉价劳动力,在中国建立一个完整的子工厂,与美国工厂一样,负责完整的汽车生产。这就是一种Z轴扩展。
Spring Cloud是一个微服务框架,相比Dubbo等RPC框架, Spring Cloud提供的全套的分布式系统解决方案。
Spring Cloud对微服务基础框架Netflix的多个开源组件进行了封装,同时又实现了和云端平台以及和Spring Boot开发框架的集成。
Spring Cloud为微服务架构开发涉及的配置管理,服务治理,熔断机制,智能路由,微代理,控制总线,一次性token,全局一致性锁,leader选举,分布式session,集群状态管理等操作提供了一种简单的开发方式。
Spring Cloud 为开发者提供了快速构建分布式系统的工具,开发者可以快速的启动服务或构建应用、同时能够快速和云平台资源进行对接

Eureka是Netflix开发的服务发现组件,本身是一个基于REST的服务。Spring Cloud将它集成在其子项目spring-cloud-netflix中,以实现Spring Cloud的服务注册于发现,同时还提供了负载均衡、故障转移等能力。
1、Eureka Server
通过Register、Get、Renew等接口提供服务的注册和发现。
2、Application Service (Service Provider)
服务提供方
把自身的服务实例注册到Eureka Server中
3、Application Client (Service Consumer)
服务调用方
通过Eureka Server 获取服务列表,消费服务。
1.Ribbon 是一个基于Http和TCP的客服端负载均衡工具,它是基于Netflix Ribbon实现的。
2.它不像spring cloud服务注册中心、配置中心、API网关那样独立部署,但是它几乎存在于每个spring cloud 微服务中。包括feign提供的声明式服务调用也是基于该Ribbon实现的。
3.ribbon默认提供很多种负载均衡算法,例如 轮询、随机 等等。甚至包含自定义的负载均衡算法。
目前业界主流的负载均衡方案可分成两类:
第一类:集中式负载均衡, 即在consumer和provider之间使用独立的负载均衡设施(可以是硬件,如F5, 也可以是软件,如nginx), 由该设施负责把 访问请求 通过某种策略转发至provider;
第二类:进程内负载均衡,将负载均衡逻辑集成到consumer,consumer从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的provider。
Ribbon就属于后者,它只是一个类库,集成于consumer进程,consumer通过它来获取到provider的地址。
id | 策略名称 | 策略对应的类名 | 实现原理 |
1 | 轮询策略(默认) | RoundRobinRule | 轮询策略表示每次都顺序取下一个provider,比如一共有5个provider,第1次取第1个,第2次取第2个,第3次取第3个,以此类推 |
2 | 权重轮询策略 | WeightedResponseTimeRule | 1.根据每个provider的响应时间分配一个权重,响应时间越长,权重越小,被选中的可能性越低。 2.原理:一开始为轮询策略,并开启一个计时器,每30秒收集一次每个provider的平均响应时间,当信息足够时,给每个provider附上一个权重,并按权重随机选择provider,高权越重的provider会被高概率选中。 |
3 | 随机策略 | RandomRule | 从provider列表中随机选择一个provider |
4 | 最少并发数策略 | BestAvailableRule | 选择正在请求中的并发数最小的provider,除非这个provider在熔断中。 |
5 | 在“选定的负载均衡策略”基础上进行重试机制 | RetryRule | 1.“选定的负载均衡策略”这个策略是轮询策略RoundRobinRule 2.该重试策略先设定一个阈值时间段,如果在这个阈值时间段内当选择provider不成功,则一直尝试采用“选定的负载均衡策略:轮询策略”最后选择一个可用的provider |
6 | 可用性敏感策略 | AvailabilityFilteringRule | 过滤性能差的provider,有2种: 第一种:过滤掉在eureka中处于一直连接失败provider 第二种:过滤掉高并发的provider |
7 | 区域敏感性策略 | ZoneAvoidanceRule | 1.以一个区域为单位考察可用性,对于不可用的区域整个丢弃,从剩下区域中选可用的provider 2.如果这个ip区域内有一个或多个实例不可达或响应变慢,都会降低该ip区域内其他ip被选中的权重。 |
Feign是一种声明式、模板化的HTTP客户端技术(仅在consumer中使用)。
声明式调用就像调用本地方法一样调用远程方法;无感知远程http请求。
1、Spring Cloud的声明式调用, 可以做到使用 HTTP请求远程服务时能就像调用本地方法一样的体验,开发者完全感知不到这是远程方法,更感知不到这是个HTTP请求。
2、它像Dubbo一样,consumer直接调用接口方法调用provider,而不需要通过常规的Http Client构造请求再解析返回数据。
3、它解决了让开发者调用远程接口就跟调用本地方法一样,无需关注与远程的交互细节,更无需关注分布式环境开发。
在微服务架构中,一个请求需要调用多个服务是非常常见的。如客户端访问A服务,而A服务需要调用B服务,B服务需要调用C服务,由于网络原因或者自身的原因,如果B服务或者C服务不能及时响应,A服务将处于阻塞状态,直到B服务C服务响应。此时若有大量的请求涌入,容器的线程资源会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,造成连锁反应,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的“雪崩”效应
降级
超时降级、资源不足时(线程或信号量)降级,降级后可以配合降级接口返回托底数据。实现一个fallback方法, 当请求后端服务出现异常的时候, 可以使用fallback方法返回的值.
隔离(线程池隔离和信号量隔离)
限制调用分布式服务的资源使用,某一个调用的服务出现问题不会影响其他服务调用。
熔断
当失败率(如因网络故障/超时造成的失败率高)达到阀值自动触发降级,熔断器触发的快速失败会进行快速恢复。
缓存
提供了请求缓存。
请求合并
提供请求合并。

答:如下这六种
代理设计模式
聚合设计模式
链条设计模式
聚合链条设计模式
数据共享设计模式
异步消息设计模式
答:网关服务,通常是外部访问服务的唯一接口,访问内部的所有服务都必须先经过网关服务。网关服务的主要功能是消息解析过滤,路由,转发等。
答:采用URL指定路由方式
采用服务名称指定路由方式
路由的排除方法
路由的添加前缀方法





ELK是三个工具的集合,Elasticsearch + Logstash + Kibana,这三个工具组合形成了一套实用、易用的监控架构,很多公司利用它来搭建可视化的海量日志分析平台。
1. ElasticSearch
ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。
2. Logstash
Logstash是一个用于管理日志和事件的工具,你可以用它去收集日志、转换日志、解析日志并将他们作为数据提供给其它模块调用,例如搜索、存储等。
3. Kibana
Kibana是一个优秀的前端日志展示框架,它可以非常详细的将日志转化为各种图表,为用户提供强大的数据可视化支持。


数据库
A. | Oracle |
B. | MySql |
C. | IMS |
D. | MongoDB |
答案:AB分析: IMS 是IP Mulitimedia Subsystem的缩写,是IP多媒体系统
MongoDB分布式文档存储数据库
|
答:在Java中开源的常用的数据库连接池有以下几种 :
(1)DBCP
DBCP是一个依赖Jakarta commons-pool对象池机制的数据库连接池.DBCP可以直接的在应用程序中使用,Tomcat的数据源使用的就是DBCP。
(2)c3p0
c3p0是一个开放源代码的JDBC连接池,它在lib目录中与Hibernate一起发布,包括了实现jdbc3和jdbc2扩展规范说明的Connection 和Statement 池的DataSources 对象。
(3)Druid
阿里出品,淘宝和支付宝专用数据库连接池,但它不仅仅是一个数据库连接池,它还包含一个ProxyDriver,一系列内置的JDBC组件库,一个SQL Parser。支持所有JDBC兼容的数据库,包括Oracle、MySql、Derby、Postgresql、SQL Server、H2等等。
A. | 一对一的联系 |
B. | 多对一的联系 |
C. | 一对多的联系 |
D. | 多对多的联系 |
答案:D
|
A. | 基本表或视图 |
B. | 视图 |
C. | 数据字典 |
D. | 基本表 |
答案:A
|
A. | 第一范式的 |
B. | 第二范式的 |
C. | 第三范式的 |
D. | BCNF范式的 |
答案:A
分析:
根据数据库三大范式的依赖性要求,从B,BC函数确定A和D这一点上,明显看出B,BC都有可能是主码.
若B是主码的话,仔细看会发现,F中竟然没有谁去函数确定C,这显然是说不通的,(因为C至少会被B这个主码函数确定);
若BC是主码,那么F中存在非主属性对候选码的部分依赖,不满足第二范式的要求,故为第一范式.
|
答:DAO(DataAccess Object)顾名思义是一个为数据库或其他持久化机制提供了抽象接口的对象,在不暴露数据库实现细节的前提下提供了各种数据操作。为了建立一个健壮的Java EE应用,应该将所有对数据源的访问操作进行抽象化后封装在一个公共API中。用程序设计语言来说,就是建立一个接口,接口中定义了此应用程序中将会用到的所有事务方法。在这个应用程序中,当需要和数据源进行交互的时候则使用这个接口,并且编写一个单独的类来实现这个接口,在逻辑上该类对应一个特定的数据存储。DAO模式实际上包含了两个模式,一是Data Accessor(数据访问器),二是Data Object(数据对象),前者要解决如何访问数据的问题,而后者要解决的是如何用对象封装数据。
Mysql:使用limit关键字
Select * from 表名 where 条件 limit 开始位置,结束位置。通过动态的改变开始和结束位置的值来实现分页。
Oracle:通过rownum来实现
select * from ( select rownum rn,t.* from addressbook where rownum<= 20 ) where rownum > 10
Sqlserver:
select top 20 * from addressbook where id not in (select top 10 id from addressbook)
如:select * from (select t.*,rownum r from (select * from A) t where rownum < 10) where r >5
select * from A:要查询的数据
select t.*,rownum r from (select * from A) t where rownum < 10:取前10行
select * from (select t.*,rownum r from (select * from A) t where rownum < 10) where r >5:取5-10行
答: 1. SQL优化的原则是:将一次操作需要读取的BLOCK数减到最低,即在最短的时间达到最大的数据吞吐量。
调整不良SQL通常可以从以下几点切入:
1) 检查不良的SQL,考虑其写法是否还有可优化内容
2) 检查子查询 考虑SQL子查询是否可以用简单连接的方式进行重新书写
3) 检查优化索引的使用
4) 考虑数据库的优化器
2. 避免出现SELECT * FROM table 语句,要明确查出的字段。
3. 在一个SQL语句中,如果一个where条件过滤的数据库记录越多,定位越准确,则该where条件越应该前移。
4. 查询时尽可能使用索引覆盖。即对SELECT的字段建立复合索引,这样查询时只进行索引扫描,不读取数据块。
5. 在判断有无符合条件的记录时建议不要用SELECT COUNT (*)和select top 1 语句。
6. 使用内层限定原则,在拼写SQL语句时,将查询条件分解、分类,并尽量在SQL语句的最里层进行限定,以减少数据的处理量。
7. 应绝对避免在order by子句中使用表达式。
9. 小心使用 IN 和 OR,需要注意In集合中的数据量。建议集合中的数据不超过200个。
10. <> 用 < 、 > 代替,>用>=代替, < 用< =代替,这样可以有效的利用索引。
11. 在查询时尽量减少对多余数据的读取包括多余的列与多余的行。
12. 对于复合索引要注意,例如在建立复合索引时列的顺序是F1,F2,F3,则在where或order by子句中这些字段出现的顺序要与建立索引时的字段顺序一致,且必须包含第一列。只能是F1或F1,F2或F1,F2,F3。否则不会用到该索引。
一、表中字符串带空格的原因
1、空格就是空格。
2、控制符 显示为 空格。
二、解决方法
第一种情况,去空格的处理的比较简单,Replace(column,' ','') 就可以解决。
第二种情况,解决方法就比较麻烦点:需要先查出相应的ASCII码,再用Replace(column,char(ascii码),'')解决,以下举个栗子:
CREATE TABLE #temp
(NAME NVARCHAR(50))
INSERT INTO #temp SELECT '明天就是国庆了'+CHAR(10) --换行符
SELECT * FROM #temp --末尾显示为空格
SELECT REPLACE(NAME,' ','') FROM #temp --去不掉这个空格
SELECT REPLACE(NAME,CHAR(10),'') FROM #temp --去掉空格
SELECT REPLACE(NAME,CHAR(ASCII(RIGHT(NAME,1))),'') FROM #temp --在不知道是最后一位是什么字符导致空格的情况下,先转ASCII码,在替换
DROP TABLE #temp
----下面是查询结果:
--'明天就是国庆了 '
--'明天就是国庆了 '
--'明天就是国庆了'
--'明天就是国庆了'
答:
1.数据库的设计
尽量把数据库设计的更小的占磁盘空间.
1).尽可能使用更小的整数类型.(mediumint就比int更合适).
2).尽可能的定义字段为not null,除非这个字段需要null.
3).如果没有用到变长字段的话比如varchar,那就采用固定大小的纪录格式比如char.
4).表的主索引应该尽可能的短.这样的话每条纪录都有名字标志且更高效.
5).只创建确实需要的索引。索引有利于检索记录,但是不利于快速保存记录。如果总是要在表的组合字段上做搜索,那么就在这些字段上创建索引。索引的第一部分必须是最常使用的字段.如果总是需要用到很多字段,首先就应该多复制这些字段,使索引更好的压缩。
6).所有数据都得在保存到数据库前进行处理。
7).所有字段都得有默认值。
8).在某些情况下,把一个频繁扫描的表分成两个速度会快好多。在对动态格式表扫描以取得相关记录时,它可能使用更小的静态格式表的情况下更是如此。
2.系统的用途
1).尽量使用长连接.
2).explain 复杂的SQL语句。
3).如果两个关联表要做比较话,做比较的字段必须类型和长度都一致.
4).LIMIT语句尽量要跟order by或者 distinct.这样可以避免做一次full table scan.
5).如果想要清空表的所有记录,建议用truncate table tablename而不是delete from tablename.
6).能使用STORE PROCEDURE 或者 USER FUNCTION的时候.
7).在一条insert语句中采用多重纪录插入格式.而且使用load data infile来导入大量数据,这比单纯的indert快好多.
8).经常OPTIMIZE TABLE 来整理碎片.
9).还有就是date 类型的数据如果频繁要做比较的话尽量保存在unsigned int 类型比较快。
3.系统的瓶颈
1).磁盘搜索.
并行搜索,把数据分开存放到多个磁盘中,这样能加快搜索时间.
2).磁盘读写(IO)
可以从多个媒介中并行的读取数据。
3).CPU周期
数据存放在主内存中.这样就得增加CPU的个数来处理这些数据。
4).内存带宽
当CPU要将更多的数据存放到CPU的缓存中来的话,内存的带宽就成了瓶颈.
命令行工具
–crsctl管理集群相关的操作:
-启动和关闭Oracle集群
-启用和禁用Oracle集群后台进程
-注册集群资源
-srvctl 管理Oracle 资源相关操作
-启动和关闭数据库实例和服务
在Oracle Grid安装的home路径下的命令行工具crsctl和srvctl用来管理Oracle集群。使用crsctl可以监控和管理任何集群节点的集群组件和资源。srvctl工具提供了类似的功能,来监控和管理Oracle相关的资源,例如数据库实例和数据库服务。crsctl命令只能是集群管理者来运行,srvctl命令可以是其他用户,例如数据库管理员来使用。
一、存储过程与函数的区别:
1.一般来说,存储过程实现的功能要复杂一点,而函数的实现的功能针对性比较强。
2.对于存储过程来说可以返回参数(output),而函数只能返回值或者表对象。
3.存储过程一般是作为一个独立的部分来执行,而函数可以作为查询语句的一个部分来调用,由于函数可以返回一个表对象,因此它可以在查询语句中位于FROM关键字的后面。
二、存储过程的优点:
1.执行速度更快 – 在数据库中保存的存储过程语句都是编译过的
2.允许模块化程序设计 – 类似方法的复用
3.提高系统安全性 – 防止SQL注入
4.减少网络流通量 – 只要传输存储过程的名称
系统存储过程一般以sp开头,用户自定义的存储过程一般以usp开头
缺点:
第一,创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。
第二,索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。
第三,当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。
客户信息表(c CUSTOM)有以下字段:
id、name、mobile
客户订单表(C_ORDER)有以下字段:
id、custom_id、commodity、count、order _date
Mysql:
Select * from c_order order by order_date desc limit 0,5;
Oracle:
Select o.*,rownum n
from c_order order by order_date desc where n<6;
A. | HQL与SQL没什么差别 |
B. | HQL面向对象,而SQL操纵关系数据库 |
C. | 在 HQL 与 SQL 中,都包含 select,insert,update,delete 语句 |
D. | HQL仅用于査询数据,不支持insert,update和delete语句 |
答案:BC
|
字段名称 | 字段解释 | 字段类型 | 字段长度 | 约束 |
s_id | 学号 | 字符 | 10 | PK |
s_name | 学生姓名 | 字符 | 50 | Not null |
s_age | 学生年龄 | 数值 | 3 | Not null |
s-sex | 学生性别 | 字符(男:1女:0) | 1 | Not null |
下面是教师表(Teacher )的结构说明
字段名称 | 字段解释 | 字段类型 | 字段长度 | 约束 |
t_id | 教师编号 | 字符 | 10 | PK |
t_name | 教师名字 | 字符 | 50 | Not null |
下面是课程表(Course)的结构说明
字段名称 | 字段解释 | 字段类型 | 字段长度 | 约束 |
c_id | 课程编号 | 字符 | 10 | PK |
c_name | 课程名字 | 字符 | 50 | Not null |
t_id | 教师编号 | 字符 | 10 | Not null |
下面是成绩表(SC)的结构说明
字段名称 | 字段解释 | 字段类型 | 字段长度 | 约束 |
s_id | 学号 | 字符 | 10 | PK |
c_id | 课程编号 | 字符 | 10 | Not null |
score | 成绩 | 数值 | 3 | Not null |
1、查询“001”课程比“002”课程成绩高的所有学生的学号;
select a.s_id from (select s_id,score from SC where C_ID='001') a,(select s_id,score
from SC where C_ID='002') b
where a.score>b.score and a.s_id=b.s_id;
2、查询平均成绩大于60分的同学的学号和平均成绩;
select S_ID,avg(score)
from sc
group by S_ID having avg(score) >60;
3、查询所有同学的学号、姓名、选课数、总成绩;
select Student.S_ID,Student.Sname,count(SC.C_ID),sum(score)
from Student left Outer join SC on Student.S_ID=SC.S_ID
group by Student.S_ID,Sname
4、查询姓“李”的老师的个数;
select count(distinct(Tname))
from Teacher
where Tname like '李%';
5、查询没学过“叶平”老师课的同学的学号、姓名;
select Student.S_ID,Student.Sname
from Student
where S_ID not in (select distinct( SC.S_ID) from SC,Course,Teacher where SC.C_ID=Course.C_ID and Teacher.T#=Course.T# and Teacher.Tname='叶平');
6、查询学过“001”并且也学过编号“002”课程的同学的学号、姓名;
elect Student.S_ID,Student.Sname from Student,SC where Student.S_ID=SC.S_ID and SC.C_ID='001'and exists( Select * from SC as SC_2 where SC_2.S_ID=SC.S_ID and SC_2.C_ID='002');
7、查询学过“叶平”老师所教的所有课的同学的学号、姓名;
select S_ID,Sname
from Student
where S_ID in (select S_ID from SC ,Course ,Teacher where SC.C_ID=Course.C_ID and Teacher.T#=Course.T# and Teacher.Tname='叶平' group by S_ID having count(SC.C_ID)=(select count(C_ID) from Course,Teacher where Teacher.T#=Course.T# and Tname='叶平'));
8、查询课程编号“002”的成绩比课程编号“001”课程低的所有同学的学号、姓名;
Select S_ID,Sname from (select Student.S_ID,Student.Sname,score ,(select score from SC SC_2 where SC_2.S_ID=Student.S_ID and SC_2.C_ID='002') score2
from Student,SC where Student.S_ID=SC.S_ID and C_ID='001') S_2 where score2 < score;
9、查询所有课程成绩小于60分的同学的学号、姓名;
select S_ID,Sname
from Student
where S_ID not in (select S.S_ID from Student AS S,SC where S.S_ID=SC.S_ID and score>60);
10、查询没有学全所有课的同学的学号、姓名;
select Student.S_ID,Student.Sname
from Student,SC
where Student.S_ID=SC.S_ID group by Student.S_ID,Student.Sname having count(C_ID) <(select count(C_ID) from Course);
11、查询至少有一门课与学号为“1001”的同学所学相同的同学的学号和姓名;
select distinct S_ID,Sname from Student,SC where Student.S_ID=SC.S_ID and SC.C_ID in (select C_ID from SC where S_ID='1001');
12、查询至少学过学号为“001”同学所有一门课的其他同学学号和姓名;
select distinct SC.S_ID,Sname
from Student,SC
where Student.S_ID=SC.S_ID and C_ID in (select C_ID from SC where S_ID='001');
S(S#,SN,SD,SA),其中S#,SN,SD,SA分别代表学号、学员姓名、所属单位、学员年龄。
C (C#,CN ),其中C#,CN分别代表课程编号、课程名称
SC(S#,C#,G),其中S#,C#,G分别代表学号、所选修的课程编号、学习成绩
请使用2种标准SQL语句査洵选修课程名称为“税收基础”的学员学号和姓名,并说明其优缺点 。
SQL92标准:
SELECT SN,SD FROM S
WHERE [S#] IN(
SELECT [S#] FROM C,SC
WHERE C.[C#]=SC.[C#]
AND CN=N'税收基础')
SQL99标准:
elect s.s#,s.sn from s
join sc on s.s#=sc.s#
join c on sc.c#=c.c#
where c.cn='税收基础'
优点:
SQL99将连接条件和过滤条件分开,显得代码清晰。
SQL92书写简单易于理解。
缺点:
SQL92连接条件和过滤条件都写在一起,不利于查看。
SQL99书写相对麻烦不易于理解。
1.这么大数据量首先建议 使用大数据的DB,可以用spring batch 来做类似这样的处理。定量向DB存储数据。如果需要定时,可以考虑 quartz。
Mysql数据库设计:
1.读写分离;
2.纵向横向拆分库、表。
MySQL的基本功能中包括replication(复制)功能。所谓replication,就是确定master以及与之同步的slave服务器,再加上slave将master中写入的内容polling过来更新自身内容的功能。这样slave就是master的replica(复制品)。这样就可以准备多台内容相同的服务器。
通过master和salve的replication,准备好多台服务器之后,让应用程序服务器通过负载均衡器去处理查询slave。这样就能将查询分散到多台服务器上。
应用程序实现上应该只把select等读取之类的查询发送给负载均衡器,而更新应当直接发送给master。要是在slave上执行更新操作,slave和master的内容就无法同步。MySQL会检测到master和slave之间内容差异,并停止replication,这回导致系统故障。Slave可以采用LVS(linux系统自带的负载均衡器)实现查询的负载均衡。
使用MySQL的replication是利用的冗余化,实现冗余化需要实现的最小服务器数量是4台,三台slave和一台master,slave为什么是需要三台呢,比如一台slave死机了,现在需要修复再次上线,那么意味着你必须停止一台slave来复制MySQL的数据,如果只有两台slave,一台坏了,你就必须停止服务,如果有三台,坏了一台,你复制数据时停止一台,还有一台可以运维。
对于数据的处理是能放入到内存中就尽量放入到内存中如果不能放入到内存中,可以利用MySQL的Partitioning。
Partitioning就是表分割也就是讲A表和B表放在不同的服务器上。简单来说,Partitioning就是充分利用局部性进行分割,提高缓存利用效率,从而实现Partitioning的效果。其中最重要的一点就是以Partitioning为前提设计的系统将表分割开,用RDBMS的方式的话,对于一对多的关系经常使用JOIN查询将两张表连接起来。但是如果将表分割开了之后,也就是两张表不在同一个数据库,不在同一个服务器上怎样使用JOIN操作,这里需要注意的是如果是用where in操作不是省了一些麻烦了嘛。
MySQL有多种存储引擎,每种存储引擎有各自的优缺点,可以择优选择使用:
MyISAM、InnoDB、MERGE、MEMORY(HEAP)、BDB(BerkeleyDB)、EXAMPLE、FEDERATED、ARCHIVE、CSV、BLACKHOLE。
MySQL支持数个存储引擎作为对不同表的类型的处理器。MySQL存储引擎包括处理事务安全表的引擎和处理非事务安全表的引擎。
· MyISAM管理非事务表。它提供高速存储和检索,以及全文搜索能力。MyISAM在所有MySQL配置里被支持,它是默认的存储引擎,除非你配置MySQL默认使用另外一个引擎。
· MEMORY存储引擎提供“内存中”表。MERGE存储引擎允许集合将被处理同样的MyISAM表作为一个单独的表。就像MyISAM一样,MEMORY和MERGE存储引擎处理非事务表,这两个引擎也都被默认包含在MySQL中。
注释:MEMORY存储引擎正式地被确定为HEAP引擎。
· InnoDB和BDB存储引擎提供事务安全表。BDB被包含在为支持它的操作系统发布的MySQL-Max二进制分发版里。InnoDB也默认被包括在所 有MySQL 5.1二进制分发版里,你可以按照喜好通过配置MySQL来允许或禁止任一引擎。
· EXAMPLE存储引擎是一个“存根”引擎,它不做什么。你可以用这个引擎创建表,但没有数据被存储于其中或从其中检索。这个引擎的目的是服务,在 MySQL源代码中的一个例子,它演示说明如何开始编写新存储引擎。同样,它的主要兴趣是对开发者。
· NDB Cluster是被MySQL Cluster用来实现分割到多台计算机上的表的存储引擎。它在MySQL-Max 5.1二进制分发版里提供。这个存储引擎当前只被Linux, Solaris, 和Mac OS X 支持。在未来的MySQL分发版中,我们想要添加其它平台对这个引擎的支持,包括Windows。
· ARCHIVE存储引擎被用来无索引地,非常小地覆盖存储的大量数据。
· CSV存储引擎把数据以逗号分隔的格式存储在文本文件中。
· BLACKHOLE存储引擎接受但不存储数据,并且检索总是返回一个空集。
· FEDERATED存储引擎把数据存在远程数据库中。在MySQL 5.1中,它只和MySQL一起工作,使用MySQL C Client API。在未来的分发版中,我们想要让它使用其它驱动器或客户端连接方法连接到另外的数据源。
fname | kecheng | fenshu |
张三 | 语文 | 81 |
张三 | 数学 | 65 |
李四 | 语文 | 76 |
李四 | 数学 | 90 |
王五 | 语文 | 61 |
王五 | 数学 | 100 |
王五 | 英语 | 90 |
1.请用一条sql语句从t_result表中查询出每门课都大于75分的学生姓名;
select b.fname from
(select fname,count(kecheng) c from t_result group by fname)a,
(Select fname,kecheng,count(fname) c from t_result where fenshu >75 group by fname)b
where a.fname = b.fname and a.c = b.c
2.请用一条sql写出总分排名前三的学生姓名,总分,平均分
select fname,sum(fenshu),avg(fenshu) from t_result GROUP By fname order by SUM(fenshu) desc;
org_employee;表中字段:雇员编号(emp_id),雇员姓名(em_name),雇员年龄(emp_age),雇员部门(depart_name);请写出执行以下操作的sql语句:
1)向表中增加一条数据:雇员编号(1001),雇员姓名(张三),雇员年龄(24),雇员部门(研发部);
INSERT INTO org_employee
VALUES(‘1001’,’张三’,’24’,’研发部’);
2)查询雇员年龄在55(包含)至60(不包含)岁之间的雇员数据
SELECT * FROM org_employee
WHERE emp_age>=55 and emp_age <60;
3)分部门查询各个部门的雇员数量
SELECT COUNT(*),depart_name group by depart_name;
4)删除姓名为张三的雇员数据
Delete from org_employee where em_name =’张三’;
5)在表中增加一个日期类型的字段雇员出生日期,字段为emp_brithday
Alter table org_employee add(emp_brithday date);
6)将表org_employee删除
drop org_employee;
请用sql语句实现表2的查询结果
表1
ID | matchdate | result |
1 | 2015-02-04 | 胜 |
2 | 2015-02-04 | 负 |
3 | 2015-02-04 | 胜 |
4 | 2015-04-07 | 胜 |
5 | 2015-04-07 | 胜 |
6 | 2015-04-07 | 胜 |
表2
比赛日期 | 胜 | 负 |
2015-02-04 | 2 | 1 |
2015-04-07 | 1 | 2 |
SQL语句:
create table t_second( matchdate date, win varchar(3), lose varchar(3) ); insert into t_second (matchdate,win) select matchdate,count(result) from t_test where result ='胜' GROUP BY matchdate; update t_second,(select matchdate,count(result) as lose from t_test where result ='负' GROUP BY matchdate)s set t_second.lose = s.lose where t_second.matchdate = s.matchdate;
SELECT… FROM EMP WHERE DEPT_NO NOT IN (SELECT DEPT_NO FROM DEPT WHERE DEPT_CAT=’A’);
SELECT… FROM EMP WHERE DEPT_NO NOT EXISTS(SELECT DEPT_NO FROM DEPT WHERE DEPT_CAT=’A’);
优化的理由:not in 和not exists
如果查询语句使用了not in 那么内外表都进行全表扫描,没有用到索引;
而not extsts 的子查询依然能用到表上的索引。所以无论那个表大,用not exists 都比not in 要快。
使用ETL工具,如infomatic,datastage,kettle等,可以完成异构数据库的迁移
以kettle为例
表输入选择 oracle库
表输出选择DB库
循环执行可以进行全库迁移
姓名:name | 课程:subject | 分数:score | 学号:stuid |
张三 | 数学 | 89 | 1 |
张三 | 语文 | 80 | 1 |
张三 | 英语 | 70 | 1 |
李四 | 数学 | 90 | 2 |
李四 | 语文 | 70 | 2 |
李四 | 英语 | 80 | 2 |
1.计算每个人的总成绩并排名(要求显示字段:姓名,总成绩)
select name,sum(score) s from t_stu GROUP BY name;
2.列出各门课程成绩最好的学生(要求显示字段:学号,姓名,科目,成绩)
select t1.stuid,t1.name,t1.subject,t1.score from t_stu t1,
(select subject,MAX(score) as maxscore from t_stu group by subject)t2
where t1.subject = t2.subject and t1.score = t2.maxscore;
3.列出各个课程的平均成绩(要求显示字段;课程,平均成绩)
select subject,AVG(score)平均成绩 from t_stu
group by subject;
Stu表
sid(学生ID) | sname(姓名) | sage(年龄) |
1 | 张三 | 23 |
2 | 李四 | 25 |
3 | 王五 | 24 |
Grade表
gid(分数主键) | cid(课程ID) | sid(学生主键) | grade(分数) |
1 | 2 | 3 | 86 |
2 | 2 | 2 | 79 |
3 | 1 | 2 | 80 |
4 | 1 | 1 | 81 |
5 | 1 | 3 | 70 |
6 | 2 | 1 | 78 |
请写sql统计出有两门以上的课的分数在80分以上的学生的姓名和年龄?
Select sname,sage from Stu where Stu.sid in (
Select sid from Grade where grade >80
)
字段名称 | 字段解释 | 字段类型 | 字段长度 | 约束 |
s_id | 学号 | 字符 | 10 | PK |
s_name | 学生姓名 | 字符 | 50 | Not null |
s_age | 学生年龄 | 数值 | 3 | Not null |
s-sex | 学生性别 | 字符(男:1女:0) | 1 | Not null |
下面是教师表(Teacher )的结构说明
字段名称 | 字段解释 | 字段类型 | 字段长度 | 约束 |
t_id | 教师编号 | 字符 | 10 | PK |
t_name | 教师名字 | 字符 | 50 | Not null |
下面是课程表(Course)的结构说明
字段名称 | 字段解释 | 字段类型 | 字段长度 | 约束 |
c_id | 课程编号 | 字符 | 10 | PK |
c_name | 课程名字 | 字符 | 50 | Not null |
t_id | 教师编号 | 字符 | 10 | Not null |
下面是成绩表(SC)的结构说明
字段名称 | 字段解释 | 字段类型 | 字段长度 | 约束 |
s_id | 学号 | 字符 | 10 | PK |
c_id | 课程编号 | 字符 | 10 | Not null |
score | 成绩 | 数值 | 3 | Not null |
查询同名同姓学生名单,并统计同名人数
select 姓名,count(学号) as num
from 学生表
group by 姓名
having count(学号)>1 --保证查找到的都是存在2个以上(包括2)的同名同姓的姓名及人数。
查询平均成绩大于60分的学生的学号和平均成绩;
Select s_id,avg(score) from sc groupby s_id having avg(score)>60
查询姓“李”的老师的个数;
Select count(*),teacher.t_name from teacher where teacher.t_name like '李%'
Sql server方案:
select top 10 * from t where id not in
(select top 30 id from t order by id ) orde by id
Mysql方案:select * from t order by id limit 30,10
Oracle方案:
select rownum num,tid from (select rownum num,tid from t_test) where num>=30 and num<=41;
A表
ID | NAME |
B表
ID | NAME | OTHER |
1 | Aaa | Ddd |
2 | Bbb | Eee |
insert into a select id,name from b;
它是从一个或几个基本表中导出的 表,是从现有基本表中抽取若干子集组成用户的“专用表”。
基本表:基本表的定义指建立基本关系模式,
而变更则是指对数据库中已存在的基本表进行删除与修改。
区别:
1、视图是已经编译好的sql语句。而表不是
2、视图没有实际的物理记录。而表有。
3、表是内容,视图是窗口
4、表只用物理空间而视图不占用物理空间,
视图只是逻辑概念的存在,表可以及时对它进行修改,
但视图只能有创建的语句来修改
5、表是内模式,试图是外模式
6、视图是查看数据表的一种方法,
可以查询数据表中某些字段构成的数据,
只是一些SQL语句的集合。从安全的角度说,
视图可以不给用户接触数据表,从而不知道表结构。
7、表属于全局模式中的表,是实表;视图属于局部模式的表,
是虚表。
8、视图的建立和删除只影响视图本身,不影响对应的基本表。
联系:视图(view)是在基本表之上建立的表,它的结构(
即所定义的列)和内容(即所有数据行)都来自基本表,
它依据基本表存在而存在。一个视图可以对应一个基本表,
也可以对应多个基本表。
视图是基本表的抽象和在逻辑意义上建立的新关系
事务与锁是不同的。事务具有ACID(原子性、一致性、隔离性和持久性),锁是用于解决隔离性的一种机制。事务的隔离级别通过锁的机制来实现。另外锁有不同的粒度,同时事务也是有不同的隔离级别的(一般有四种:读未提交Read uncommitted,
读已提交Read committed,
可重复读Repeatable read,
可串行化Serializable)。
在具体的程序设计中,开启事务其实是要数据库支持才行的,如果数据库本身不支持事务,那么仍然无法确保你在程序中使用的事务是有效的。
锁可以分为乐观锁和悲观锁:
悲观锁:认为在修改数据库数据的这段时间里存在着也想修改此数据的事务;
乐观锁:认为在短暂的时间里不会有事务来修改此数据库的数据;
我们一般意义上讲的锁其实是指悲观锁,在数据处理过程中,将数据置于锁定状态(由数据库实现)
如果开启了事务,在事务没提交之前,别人是无法修改该数据的;如果rollback,你在本次事务中的修改将撤消(不是别人修改的会没有,因为别人此时无法修改)。当然,前提是你使用的数据库支持事务。还有一个要注意的是,部分数据库支持自定义SQL锁覆盖事务隔离级别默认的锁机制,如果使用了自定义的锁,那就另当别论。
重点:一般事务使用的是悲观锁(具有排他性)
写一个SQL语句,查询选修了计算机原理的学生学号和姓名
select 学号,姓名 from Student where 学号 in
(select 学号 from Sc where 课程编号 in
(Select 课程编号 from Course where 课程名称 = ‘计算机原理’))
写一个SQL语句,查询“周星驰”同学选修了的课程名字
select 课程名称 from Course where 编号 in (
select Sc.课程编号 from Student,Sc where Student.姓名=’周星驰’ and Student.学号 = Sc.学号)
写一个SQL语句,查询选修了5门课程的学生学号和姓名
Select 学号,姓名 from Student where 学号 in (
Select 学号,count(课程编号) from Sc group by 学号 having count(课程编号)>=5)
Student(S#,Sname,Sage,Ssex)学生表
S#:学号
Sname:学生姓名
Sage:学生年龄
Ssex: 学生性别
Course(C#,Cname,T#)课程表
C#,课程编号;
Cname:课程名字;
T#:教师编号;
SC(S#,C#,score)成绩表
S#:学号;
C#,课程编号;
Score:成绩;
Teacher(T#,Tname)教师表
T#:教师编号;
Tname:教师名字
查询“001”课程比“002”课程成绩高的所有学生学号
select SC1.S#
from SC SC1 JOIN SC SC2 ON SC1.S#=SC2.S#
WHERE SC1.C#='001' AND SC2.C#='002' AND SC1.score>SC2.score
查询平均成绩大于60分的同学的学号和平均成绩
select S#,AVG(score) 平均成绩
from SC
group by S#
having AVG(score)>60
查询所有同学的学号、姓名、选课数、总成绩
select Student.S#,Sname,COUNT(*) 选课数,SUM(score) 总成绩
from Student JOIN SC on Student.S#=SC.S#
group by Student.S#,Sname
查询姓“李”的老师的个数
Select count(*) from Teacher where Tname like ‘李%’;
查询没学过“叶平”老师课的同学的学号、姓名
SELECT stu2.s#,stu2.stuname FROM Student stu2 WHERE stu2.s# NOT IN
(SELECT DISTINCT stu.s# FROM student stu, course c,teacher tea,score score
WHERE stu.s#= score.s# AND course.c#= score.c#
AND tea.t#= course.t#AND tea.tname= '叶平' )
JVM
内存管理的职责为分配内存,回收内存。 没有自动内存管理的语言/平台容易发生错误。
典型的问题包括悬挂指针问题,一个指针引用了一个已经被回收的内存地址,导致程序的运行完全不可知。
另一个典型问题为内存泄露,内存已经分配,但是已经没有了指向该内存的指针,导致内存泄露。 程序员要花费大量时间在调试该类问题上。
JVM中类的装载是由类加载器(ClassLoader)和它的子类来实现的,Java中的类加载器是一个重要的Java运行时系统组件,它负责在运行时查找和装入类文件中的类。
由于Java的跨平台性,经过编译的Java源程序并不是一个可执行程序,而是一个或多个类文件。当Java程序需要使用某个类时,JVM会确保这个类已经被加载、连接(验证、准备和解析)和初始化。类的加载是指把类的.class文件中的数据读入到内存中,通常是创建一个字节数组读入.class文件,然后产生与所加载类对应的Class对象。加载完成后,Class对象还不完整,所以此时的类还不可用。当类被加载后就进入连接阶段,这一阶段包括验证、准备(为静态变量分配内存并设置默认的初始值)和解析(将符号引用替换为直接引用)三个步骤。最后JVM对类进行初始化,包括:1)如果类存在直接的父类并且这个类还没有被初始化,那么就先初始化父类;2)如果类中存在初始化语句,就依次执行这些初始化语句。
类的加载是由类加载器完成的,类加载器包括:根加载器(BootStrap)、扩展加载器(Extension)、系统加载器(System)和用户自定义类加载器(java.lang.ClassLoader的子类)。从Java 2(JDK 1.2)开始,类加载过程采取了父亲委托机制(PDM)。PDM更好的保证了Java平台的安全性,在该机制中,JVM自带的Bootstrap是根加载器,其他的加载器都有且仅有一个父类加载器。类的加载首先请求父类加载器加载,父类加载器无能为力时才由其子类加载器自行加载。JVM不会向Java程序提供对Bootstrap的引用。下面是关于几个类加载器的说明:
Bootstrap:一般用本地代码实现,负责加载JVM基础核心类库(rt.jar);
Extension:从java.ext.dirs系统属性所指定的目录中加载类库,它的父加载器是Bootstrap;
System:又叫应用类加载器,其父类是Extension。它是应用最广泛的类加载器。它从环境变量classpath或者系统属性java.class.path所指定的目录中记载类,是用户自定义加载器的默认父加载器。
答:JVM原理:
JVM是Java Virtual Machine(Java虚拟机)的缩写,它是整个java实现跨平台的最核心的部分,所有的Java程序会首先被编译为.class的类文件,这种类文件可以在虚拟机上执行,也就是说class并不直接与机器的操作系统相对应,而是经过虚拟机间接与操作系统交互,由虚拟机将程序解释给本地系统执行。JVM是Java平台的基础,和实际的机器一样,它也有自己的指令集,并且在运行时操作不同的内存区域。JVM通过抽象操作系统和CPU结构,提供了一种与平台无关的代码执行方法,即与特殊的实现方法、主机硬件、主机操作系统无关。JVM的主要工作是解释自己的指令集(即字节码)到CPU的指令集或对应的系统调用,保护用户免被恶意程序骚扰。JVM对上层的Java源文件是不关心的,它关注的只是由源文件生成的类文件(.class文件)。
内存泄漏与溢出的区别:
1) 内存泄漏是指分配出去的内存无法回收了。
2) 内存溢出是指程序要求的内存,超出了系统所能分配的范围,从而发生溢出。比如用byte类型的变量存储10000这个数据,就属于内存溢出。
3) 内存溢出是提供的内存不够;内存泄漏是无法再提供内存资源。
何时产生内存泄漏:
1) 静态集合类:在使用Set、Vector、HashMap等集合类的时候需要特别注意,有可能会发生内存泄漏。当这些集合被定义成静态的时候,由于它们的生命周期跟应用程序一样长,这时候,就有可能会发生内存泄漏。
2) 监听器:在Java中,我们经常会使用到监听器,如对某个控件添加单击监听器addOnClickListener(),但往往释放对象的时候会忘记删除监听器,这就有可能造成内存泄漏。好的方法就是,在释放对象的时候,应该记住释放所有监听器,这就能避免了因为监听器而导致的内存泄漏。
3) 各种连接:Java中的连接包括数据库连接、网络连接和io连接,如果没有显式调用其close()方法,是不会自动关闭的,这些连接就不能被GC回收而导致内存泄漏。一般情况下,在try代码块里创建连接,在finally里释放连接,就能够避免此类内存泄漏。
4) 外部模块的引用:调用外部模块的时候,也应该注意防止内存泄漏。如模块A调用了外部模块B的一个方法,如:public void register(Object o)。这个方法有可能就使得A模块持有传入对象的引用,这时候需要查看B模块是否提供了去除引用的方法,如unregister()。这种情况容易忽略,而且发生了内存泄漏的话,比较难察觉,应该在编写代码过程中就应该注意此类问题。
5) 单例模式:使用单例模式的时候也有可能导致内存泄漏。因为单例对象初始化后将在JVM的整个生命周期内存在,如果它持有一个外部对象(生命周期比较短)的引用,那么这个外部对象就不能被回收,而导致内存泄漏。如果这个外部对象还持有其它对象的引用,那么内存泄漏会更严重,因此需要特别注意此类情况。这种情况就需要考虑下单例模式的设计会不会有问题,应该怎样保证不会产生内存泄漏问题。
GC线程是守护线程。线程分为守护线程和非守护线程(即用户线程)。只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。
类加载器按照层次,从顶层到底层,分为以下三种:
(1)启动类加载器(Bootstrap ClassLoader)
这个类加载器负责将存放在JAVA_HOME/lib下的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的类库加载到虚拟机内存中。启动类加载器无法被Java程序直接引用。
(2)扩展类加载器(Extension ClassLoader)
这个加载器负责加载JAVA_HOME/lib/ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器
(3)应用程序类加载器(Application ClassLoader)
这个加载器是ClassLoader中getSystemClassLoader()方法的返回值,所以一般也称它为系统类加载器。它负责加载用户类路径(Classpath)上所指定的类库,可直接使用这个加载器,如果应用程序没有自定义自己的类加载器,一般情况下这个就是程序中默认的类加载器
双亲委派模型:
双亲委派模型要求除了顶层的启动类加载器外,其他的类加载器都应当有自己的父类加载器。这里类加载器之间的父子关系一般不会以继承关系来实现,而是都使用组合关系来复用父加载器的代码
工作过程:
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传递到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。
好处:
Java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类Object,它放在rt.jar中,无论哪一个类加载器要加载这个类,最终都是委派给启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类,判断两个类是否相同是通过classloader.class这种方式进行的,所以哪怕是同一个class文件如果被两个classloader加载,那么他们也是不同的类。
1、对于GC来说,当程序员创建对象时,GC就开始监控这个对象的地址、大小以及使用情况。通常,GC采用有向图的方式记录和管理堆(heap)中的所有对象。通过这种方式确定哪些对象是"可达的",哪些对象是"不可达的"。当GC确定一些对象为"不可达”时,GC就有责任回收这些内存空间。
2、可以。程序员可以手动执行System.gc(),通知GC运行,但是Java语言规范并不保证GC一定会执行。
3. System.gc();或者Runtime.getRuntime().gc();
答:理论上Java因为有垃圾回收机制(GC)不会存在内存泄露问题(这也是Java被广泛使用于服务器端编程的一个重要原因);然而在实际开发中,可能会存在无用但可达的对象,这些对象不能被GC回收也会发生内存泄露。一个例子就是hibernate的Session(一级缓存)中的对象属于持久态,垃圾回收器是不会回收这些对象的,然而这些对象中可能存在无用的垃圾对象。下面的例子也展示了Java中发生内存泄露的情况:
package com.bjsxt; import java.util.Arrays; import java.util.EmptyStackException; public class MyStack<T> { private T[] elements; private int size = 0; private static final int INIT_CAPACITY = 16; public MyStack() { elements = (T[]) new Object[INIT_CAPACITY]; } public void push(T elem) { ensureCapacity(); elements[size++] = elem; } public T pop() { if(size == 0) throw new EmptyStackException(); return elements[--size]; } private void ensureCapacity() { if(elements.length == size) { elements = Arrays.copyOf(elements, 2 * size + 1); } } }
上面的代码实现了一个栈(先进后出(FILO))结构,乍看之下似乎没有什么明显的问题,它甚至可以通过你编写的各种单元测试。然而其中的pop方法却存在内存泄露的问题,当我们用pop方法弹出栈中的对象时,该对象不会被当作垃圾回收,即使使用栈的程序不再引用这些对象,因为栈内部维护着对这些对象的过期引用(obsolete reference)。在支持垃圾回收的语言中,内存泄露是很隐蔽的,这种内存泄露其实就是无意识的对象保持。如果一个对象引用被无意识的保留起来了,那么垃圾回收器不会处理这个对象,也不会处理该对象引用的其他对象,即使这样的对象只有少数几个,也可能会导致很多的对象被排除在垃圾回收之外,从而对性能造成重大影响,极端情况下会引发Disk Paging(物理内存与硬盘的虚拟内存交换数据),甚至造成OutOfMemoryError。
答:GC是垃圾收集的意思,内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的显示操作方法。Java程序员不用担心内存管理,因为垃圾收集器会自动进行管理。要请求垃圾收集,可以调用下面的方法之一:System.gc() 或Runtime.getRuntime().gc() ,但JVM可以屏蔽掉显示的垃圾回收调用。
垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。垃圾回收器通常是作为一个单独的低优先级的线程运行,不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收,程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。在Java诞生初期,垃圾回收是Java最大的亮点之一,因为服务器端的编程需要有效的防止内存泄露问题,然而时过境迁,如今Java的垃圾回收机制已经成为被诟病的东西。移动智能终端用户通常觉得iOS的系统比Android系统有更好的用户体验,其中一个深层次的原因就在于Android系统中垃圾回收的不可预知性。
补充:垃圾回收机制有很多种,包括:分代复制垃圾回收、标记垃圾回收、增量垃圾回收等方式。标准的Java进程既有栈又有堆。栈保存了原始型局部变量,堆保存了要创建的对象。Java平台对堆内存回收和再利用的基本算法被称为标记和清除,但是Java对其进行了改进,采用“分代式垃圾收集”。这种方法会跟Java对象的生命周期将堆内存划分为不同的区域,在垃圾收集过程中,可能会将对象移动到不同区域:
伊甸园(Eden):这是对象最初诞生的区域,并且对大多数对象来说,这里是它们唯一存在过的区域。
幸存者乐园(Survivor):从伊甸园幸存下来的对象会被挪到这里。
终身颐养园(Tenured):这是足够老的幸存对象的归宿。年轻代收集(Minor-GC)过程是不会触及这个地方的。当年轻代收集不能把对象放进终身颐养园时,就会触发一次完全收集(Major-GC),这里可能还会牵扯到压缩,以便为大对象腾出足够的空间。
与垃圾回收相关的JVM参数:
-Xms / -Xmx --- 堆的初始大小 / 堆的最大大小
-Xmn --- 堆中年轻代的大小
-XX:-DisableExplicitGC --- 让System.gc()不产生任何作用
-XX:+PrintGCDetail --- 打印GC的细节
-XX:+PrintGCDateStamps --- 打印GC操作的时间戳
Linux操作
答:linux指令
arch 显示机器的处理器架构(1)
uname -m 显示机器的处理器架构(2)
shutdown -h now 关闭系统(1)
shutdown -r now 重启(1)
cd /home 进入 '/ home' 目录'
cd .. 返回上一级目录
cd ../.. 返回上两级目录
mkdir dir1 创建一个叫做 'dir1' 的目录'
mkdir dir1 dir2 同时创建两个目录
find / -name file1 从 '/' 开始进入根文件系统搜索文件和目录
find / -user user1 搜索属于用户 'user1' 的文件和目录
linuxtomcat启动
进入tomcat下的bin目录执行 ./catalina.sh start直接启动即可,然后使用tail -f /usr/local/tomcat6/logs/catalina.out查看tomcat启动日志。
A. | Mic |
B. | mid |
C. | mitegistry |
D. | policytool |
答案:A
|
A. | kill |
B. | tar |
C. | rsyne |
D. | policytool |
答案:c
分析:
A:kill命令,常用于杀死进程;
B:tar命令,tar命令是Unix/Linux系统中备份文件的可靠方法,几乎可以工作于任何环境中,它的使用权限是所有用户
C:类unix系统下的数据镜像备份工具
D:在终端下输入lsof即可显示系统打开的文件,因为 lsof 需要访问核心内存和各种文件,所以必须以 root 用户的身份运行它才能够充分地发挥其功能
|
A. | 传输控制协议 |
B. | 用户数据报协议 |
C. | 网际协议 |
D. | 网际控制报文协议 |
答案:A
|
A. | defaults |
B. | sw |
C. | rw 和 ro |
D. | noauto |
答案:D
|
A. | 文件类型 |
B. | 文件所有者的权限 |
C. | 文件所有者所在组的权限 |
D. | 其他用户的权限 |
答案:C
|
A. | kill |
B. | < CTRL >;+C |
C. | shut down |
D. | halt |
答案:B
|
A. | -m |
B. | -d |
C. | -d |
D. | -p |
答案:D
|
A. | i 节点和文件是一一对应的(每个文件都有唯一一个索引结点号与之对应,而对于一个索引结点号,却可以有多个文件名与之对应) |
B. | i 节点能描述文件占用的块数 |
C. | i 节点描述了文件大小和指向数据块的指针 |
D. | 通过i 节点实现文件的逻辑结构和物理结构的转换 |
答案:A
|
A. | tar |
B. | gzip |
C. | compress |
D. | uncompress |
答案:D
|
A. | csh |
B. | tcsh |
C. | awk |
D. | sed |
答案:C
|
A. | 配置域名服务器 |
B. | 定义一条本机指向所在网络的路由 |
C. | 定义一条本机指向所在网络网关的路由 |
D. | 定义一条本机指向目标网络网关的路由 |
答案:C
|
A. | /etc/hosts |
B. | /etc/HOSTNAME |
C. | /etc/resolv.conf |
D. | /etc/gateways |
答案:D
|
A. | route add –net 192.168.1.0 gw 192.168.1.1 netmask 255.255.255.0 metric 1 |
B. | route add –net 172.16.1.0 gw 192.168.1.1 netmask 255.255.255.0 metric 1 |
C. | route add –net 172.16.1.0 gw 172.16.1.1 netmask 255.255.255.0 metric 1 |
D. | route add default 192.168.1.0 netmask 172.168.1.1 metric 1 |
答案:B
|
A. | 配置本地回环地址 |
B. | 配置网卡的IP地址 |
C. | 激活网络适配器 |
D. | 加载网卡到内核中 |
答案:D
|
A. | 硬链接就是让链接文件的i 节点号指向被链接文件的i 节点 |
B. | 硬链接和符号连接都是产生一个新的i 节点 |
C | 链接分为硬链接和符号链接 |
D. | 硬连接不能链接目录文件 |
答案:B
|
A. | 主机IP设置有误 |
B. | 没有设置连接局域网的网关 |
C. | 局域网的网关或主机的网关设置有误 |
D. | 局域网DNS服务器设置有误 |
答案:C
|
A. | /etc/HOSTNAME |
B. | /etc/hosts |
C. | /etc/resolv.conf |
D. | /etc/networks |
答案:B
|
A. | 删除系统不用的设备驱动程序时 |
B. | 升级内核时 |
C. | 添加新硬件时 |
D. | 将网卡激活 |
答案:D
|
A. | 直接赋值 |
B. | 使用read命令 |
C. | 使用命令行参数 |
D. | 使用命令的输出 |
答案:A
|
A. | cp |
B. | dd |
C. | fmt |
D. | cut |
答案:D
|
A. | 交互进程 |
B. | 批处理进程 |
C. | 守护进程 |
D. | 就绪进程 |
答案:D
|
A. | CPU 已满负荷地运转 |
B. | CPU 的运行效率为30% |
C. | CPU 的运行效率为50% |
D. | CPU 的运行效率为80% |
答案:A
|
A. | 64MB |
B. | 128MB |
C. | 256MB |
D. | 512MB |
答案:C
|
A. | full |
B. | expert |
C. | newbie |
D. | menu |
答案:D
|
A. | cat |
B. | more |
C. | less |
D. | menu |
答案:C
|
A. | root 1 4.0 0.0 344 204? S 17:09 0:00 init |
B. | root 2 0.0 0.1 2916 1520? S 17:09 0:00 /sbin/getty |
C. | root 3 0.0 0.2 1364 632? S 17:09 0:00 /usr/sbin/syslogd |
D. | root 4 0.0 1344 1204? S 17:09 0:10 /usr/sbin/inetd |
答案:D
|
A. | telnet |
B. | FTP |
C. | SNMP |
D. | NFS |
答案:B
|
A. | ping |
B. | ifconfig |
C. | traceroute |
D. | netstat |
答案:C
|
A. | -rwxr-xr-x |
B. | -rwxr--r-- |
C. | -r--r--r-- |
D. | -r-xr-x—x |
答案:C
|
A. | -rw-rw-rw- 2 hel-s users 56 Sep 09 11:05 hello |
B. | -rwxrwxrwx 2 hel-s users 56 Sep 09 11:05 goodbey |
C. | drwxr--r-- 1 hel users 1024 Sep 10 08:10 zhang |
D. | lrwxr--r-- 1 hel users 2024 Sep 12 08:12 cheng |
答案:D
|
A. | IP地址 |
B. | MAC地址 |
C. | 网络地址 |
D. | 主机别名 |
答案:A
|
A. | 服务器/工作站 |
B. | B/S |
C. | 集中式 |
D. | 分布式 |
答案:B
|
A. | less |
B. | mesg |
C. | write |
D. | echo to |
答案:C
|
A. | 文件 |
B. | 磁盘 |
C. | 网络文件 |
D. | 操作 |
答案:C
|
A. | cp |
B. | tr |
C. | dir |
D. | cpio |
答案:D
|
A. | /bin |
B. | /etc |
C. | /dev |
D. | /lib |
答案:C
|
A. | # reboot |
B. | # halt |
C. | # reboot |
D. | # shutdown –r now |
答案:D
|
A. | 故障管理 |
B. | 日常备份管理 |
C. | 升级管理 |
D. | 发送 |
答案:A
|
A. | Ctrl+Alt+Del |
B. | halt |
C. | shutdown -r now |
D. | reboot |
答案:B
|
A. | ping |
B. | ifconfig |
C. | arp |
D. | traceroute |
答案:C
|
A. | < a>; |
B. | < o>; |
C. | < I>; |
D. | A |
答案:B
|
A. | < x>; |
B. | < d>;< w>; |
C. | < D>; |
D. | < d>;< d>; |
答案:A
|
A. | 上箭头 |
B. | 下箭头 |
C. | < .>; |
D. | < *>; |
答案:C
|
A. | mkdir |
B. | rmdir |
C. | mv |
D. | traceroute |
答案:D
|
A. | < Esc>; |
B. | ^q |
C. | exit |