**
**
PX标识像素,%标识整个页面的宽度百分比
内联样式最高优先权,然后是内部样式,然后才是外部样式
DIV 和 SPAN 元素最大的特点是默认都没有对元素内的对象进行任何格式化渲染。主要用于应用样式表(共同点)。
两者最明显的区别在于DIV是块元素,而SPAN是行内元素(也译作内嵌元素)。
详解:1.所谓块元素,是以另起一行开始渲染的元素,行内元素则不需另起一行,测试一下下面的代码你会有更形象的理解:
测试紧跟前面的"测试"显示
1)类别选择器 用“.”来标识
2)标签选择器 用HTML标签来标识
3)ID选择器 用“#”号来标识
4)通配符选择器 用“*”号来标识
1)div大大缩减页面代码,提高页面浏览速度,table代码量繁多,页面浏览效率慢。
2)div结构清晰,可读性非常强,也容易被搜索引擎搜索到,优化了搜索引擎,Table结构复杂,可读性差。
3)div 缩短改版时间。只要简单的修改几个CSS文件就可以改变很多页面。Table要想改变的话,需要一个页面一个页面的去修改。
4)div表现和内容相分离,非常符合w3c标准。
5)table制作效率高于div
6)table浏览器兼容性高于div,我们常用的IE6.0,IE7.0火狐Firefox浏览器对div css设置上非常挑剔。
行级转块级:display:block
块级转行级:display:inline
byte、short、int、long、float、double、char、boolean
因为Java程序编译之后的代码不是能被硬件系统直接运行的代码,而是一种“中间码”——字节码。然后不同的硬件平台上安装有不同的Java虚拟机(JVM),由JVM来把字节码再“翻译”成所对应的硬件平台能够执行的代码。因此对于Java编程者来说,不需要考虑硬件平台是什么。所以Java可以跨平台。
不是,Strng是引用类型;String是final的类,是不可以被继承的。
&和&&都可以用作逻辑与的运算符,表示逻辑与(and),当运算符两边的表达式的结果都为true时,整个运算结果才为true,否则,只要有一方为false,则结果为false。
&&还具有短路的功能,即如果第一个表达式为false,则不再计算第二个表达式。
&还可以用作位运算符,当&操作符两边的表达式不是boolean类型时,&表示按位与操作.。
switch里面的条件必须是能隐式的转化成为Int的故long和String不行,byte可以;使用Switch时候还应注意它的穿透,即每个case后要跟break;
对于short s1 = 1; s1 = s1 + 1; 由于s1+1运算时会自动提升表达式的类型,所以结果是int型,再赋值给short类型s1时,编译器将报告需要强制转换类型的错误。
对于short s1 = 1; s1 += 1;由于 += 是java语言规定的运算符,java编译器会对它进行特殊处理,因此可以正确编译。
char型变量是用来存储Unicode编码的字符的,unicode编码字符集中包含了全世界所 有的字体。
2<<3 位移运算是最底层的运算,他直接操作的是二进制,故效率最快。
final用于修饰类、属性、方法
final修饰的类,不能被继承
final修饰的属性,是常量,值不可以改变
final修饰的方法,不可以被重写
静态变量也称为类变量,归全类共有,它不依赖于某个对象,可通过类名直接访问;而实例变量必须依存于某一实例,只能通过对象才能访问到它。
1)抽象:抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意 与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节。抽象包括两个方面,一是过程抽象,二是数据抽象。
2)继承:子类拥有父类一切非私有的属性和方法。
3)封装:封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面 向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。
4)多态性:同一种事物的不同种表现形式。
作用域 当前类 同包 子孙类 其他
public √ √ √ √
protected √ √ √ ×
default √ √ × ×
private √ × × ×
不写时默认为default。
(Overload)重载:发生在同一个类之中,方法名相同、参数列表不同,与返回值无关、与final无关、与修饰符无关、与异常无关。
(Override)重写:发生在子类和父类之间,方法名相同、参数列表相同、返回值相同、不能是final的方法、重写的方法不能有比父类方法更为严格的修饰符权限、重写的方法所抛出的异常不能比父类的更大。
如果父类私有的方法,子类拥有方法签名相同的方法,子类不属于重写父类的方法,该方法属于子类的新方法。
构造器不能被继承,故不能被重写、但可以被重载。
java中没有多继承,但是可以多实现,即一个类实现多个接口。
虽然没有多继承,但是java中接口可以近似的实现多继承,那就是接口;接口和接口之间可以进行多继承。
1)抽象类继承与object接口不继承object.
2)抽象类有构造器,接口中没有构造器。
3)抽象类中可以有普通成员变量和常量,接口中只能有常量,而且只能是public static final 不写默认。
4)抽象类中可以有抽象方法,也可以由普通的方法,接口中只能有抽象的方法而且修饰符只能是public abstract 不写默认。
5)抽象类中可以有final的方法,接口中不能有final的方法。
6)抽象类只能是单继承,多实现,接口是可以多继承其他接口,但是不能实现接口,和不能继承其他类。
7)抽象类中可以有静态的方法,接口中不可以。
重写、重载、父类的声明指向子类的对象。
int是java的基本数据类型,integer是1.4版本后提供的基本类型包装类,当两者作为成员变量时,初始值分别为;int是0;integer是null;其中integer提供了一些对整数操作的方法,还定义了integer型数值的最值,其他基本类型也有对应的包装类,基本类型包装类的出现,使得java完全面向对象.
String是不可变的,对String类的任何改变都会返回一个新的String 对象。
StringBuffer是可变的,对StringBuffer中的内容修改都是当前这个对象。
String重写了equals方法和hashCode方法,StringBuffer没有重写equals方法。String是final的类。StringBuffer不是。
String创建的字符串是在常量池中,创建的变量初始化一次,如果再对该字符串改变会产生新的字符串地址值,StringBuffer是在堆中创建对象,当对字符串改变时不会产生新的字符串地址值,如果对字符串进行频繁修改的话建议使用StringBuffer,以节省内存。
StringBuffer和StringBuilder,StringBuffer是线程安全的,StringBulider是线程不安全的。当不考虑并发问题时候,请使用StringBulider。
两个对象,一个是"xyx",一个是指向"xyx"的引用对象s。
数组中没有length()方法,但是有length属性,String中有length()方法
这道题很有争议,我是通过debug模式分为两种情况进行测试的.
1)finally中没有return时候:
会先执行try里面的,return会执行但是没有真正的return此时去执行了finally里面的,然后再返回来执行return.
2)finally中有return时候(其实这种情况不符合编程规范,会报黄线警告):
会先执行try里面的,return会执行但是没有真正的return此时去执行了finally里面的,然后执行finally里面的return,直接返回。
final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。
内部类要访问局部变量,局部变量必须定义成final类型。
finally是异常处理语句结构的一部分,表示总是执行。
finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。JVM不保证此方法总被调用
1、 基本数据类型:两个基本数据类型变量比较只能用==,比较的是值
2、 引用类型:== 比较的是指针,equals没有重写与==没有区别,重写了equals方法比较的是两个对象的内容,在javaBean规定当重写equals时候必须重写hashCode,两个对象的equals相等,hashCode也必须相等,如果不相等就会造成相同的对象在HashSet存储中存储两次
error表示恢复不是不可能但是很困难的情况下的一种严重问题,例如内存溢出,虚拟机错误等,exception是一种设计和实现问题,如果程序运行正常,从不会发生的情况。error是可以避免的,exception是不可避免的。
java的内存分为两类,一类是栈内存,一类是堆内存。栈内存是指程序进入一个方法时,会为这个方法单独分配一块私属存储空间,用于存储这个方法内部的局部变量,当这个方法结束时,分配给这个方法的栈会释放,这个栈中的变量也将随之释放。
堆是与栈作用不同的内存,一般用于存放不放在当前方法栈中的那些数据,例如,使用new创建的对象都放在堆里,所以,它不会随方法的结束而消失。方法中的局部变量使用final修饰后,放在堆中,而不是栈中。
GC是垃圾收集的意思(Gabage Collection),内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的显示操作方法。
内部类是指在一个外部类的内部再定义一个类。内部类作为外部类的一个成员,并且依附于外部类而存在的。内部类可为静态,可用protected和private修饰(而外部类只能使用public和缺省的包访问权限)。
内部类主要有以下几类:成员内部类、局部内部类、静态内部类、匿名内部类。
典型的情况是,内部类继承自某个类或实现某个接口,内部类的代码操作创建其的外围类的对象。所以你可以认为内部类提供了某种进入其外围类的窗口。
使用内部类最吸引人的原因是:每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。如果没有内部类提供的可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。从这个角度看,内部类使得多重继承的解决方案变得完整。接口解决了部分问题,而内部类有效地实现了“多重继承”。
完全可以。如果不是静态内部类,那没有什么限制!
如果你把静态嵌套类当作内部类的一种特例,那在这种情况下不可以访问外部类的普通成员变量,而只能访问外部类中的静态成员,例如,下面的代码:
class Outer{
static int x;
static class Inner {
void test() {
System.out.println(x);
}
}
}
答题时,也要能察言观色,揣摩提问者的心思,显然人家希望你说的是静态内部类不能访问外部类的成员,但你一上来就顶牛,这不好,要先顺着人家,让人家满意,然后再说特殊情况,让人家吃惊。
可以继承其他类或实现其他接口。不仅是可以,而是必须!
java –version
1)String.valueOf()
2)"" + 数字
3)Integer.toString()
1)new关键字
2)反射
3)克隆
4)反序列化
public static boolean checkEmail(String email) {
if(email == null || "".equals(email)) {
return false;
}
boolean temp = email.matches("\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*");
return temp;
}
空指针异常会抛出: java.lang.NullPointerException
解决方法:判断使用对象是否为null
if(obj != null) {
}
1、ASP是微软公司从自有技术发展出来的,一般仅能用于Windows平台,并总是作为微软Internet Information Server的强有力的基本特性出现,其开放性较差。
Java是Sun公司推出具有夸平台性的开发语言,能在Windows平台于Linux平台运行,开发简便、安全性极高,java所有源码都是开放的。
2、用java开发业务教复杂的程序,相对来说要轻松一些,java有丰富的开源框架和套现设计模式。
ASP只有.net框架,与java开源框架如:spring、Struts、hibernate、mybatis等比较会差一些。
3、java使用oracle数据库,都能夸平台,一般高端机都运行在Linux或Unix下,注定oracle是大型数据库。
ASP使用SQL Server数据库,运行在Windows平台下,而Windows能够安装的硬件是有限的,SQL Server数据库只能是中型数据库。
4、java运行需要JVM翻译,所以java执行效率没有.net高
综述,.net适合做网站,中型的!java适合做后台,企业系统,大型网站!
面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。
面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。
例如五子棋,面向过程的设计思路就是首先分析问题的步骤:1、开始游戏,2、黑子先走,3、绘制画面,4、判断输赢,5、轮到白子,6、绘制画面,7、判断输赢,8、返回步骤2,9、输出最后结果。把上面每个步骤用分别的函数来实现,问题就解决了。
而面向对象的设计则是从另外的思路来解决问题。整个五子棋可以分为 1、黑白双方,这两方的行为是一模一样的,2、棋盘系统,负责绘制画面,3、规则系统,负责判定诸如犯规、输赢等。第一类对象(玩家对象)负责接受用户输入,并告知第二类对象(棋盘对象)棋子布局的变化,棋盘对象接收到了棋子的i变化就要负责在屏幕上面显示出这种变化,同时利用第三类对象(规则系统)来对棋局进行判定。
可以明显地看出,面向对象是以功能来划分问题,而不是步骤。同样是绘制棋局,这样的行为在面向过程的设计中分散在了总多步骤中,很可能出现不同的绘制版本,因为通常设计人员会考虑到实际情况进行各种各样的简化。而面向对象的设计中,绘图只可能在棋盘对象中出现,从而保证了绘图的统一。
功能上的统一保证了面向对象设计的可扩展性。比如我要加入悔棋的功能,如果要改动面向过程的设计,那么从输入到判断到显示这一连串的步骤都要改动,甚至步骤之间的循序都要进行大规模调整。如果是面向对象的话,只用改动棋盘对象就行了,棋盘系统保存了黑白双方的棋谱,简单回溯就可以了,而显示和规则判断则不用顾及,同时整个对对象功能的调用顺序都没有变化,改动只是局部的。
java.lang.NullPointerException 空指针异常
java.lang.ClassNotFoundException 类找不到异常
java.lang.ArithmeticException 数学运算异常
java.lang.ArrayIndexOutOfBoundsException 数组下标越界
java.lang.ClassCastException 类型转换异常
java.lang.NumberFormatException 数字转换异常
java.sql.SQLException 操作数据库异常
java.io.IOException 输入输出流异常
java.io.FileNotFoundException 文件找不到异常
java.lang.InstantiationError 实例化异常
不能,因为this代表的是调用这个方法的对象的引用,super代表当前父类对象的引用,而静态方法是属于类的,不属于对象,静态优先于对象,静态方法成功加载后,对象还不一定存在。
Js验证表单,可以通过document对象获取表单中输入框的内容进行验证。
jQuery验证表单:可以通过jQuery选择器获取表单中输入框的内容进行验证,
现在基于jQuery第三方验证框架非常多,也可以通过jQuery第三方验证框架进行验证。
Java与JavaScript是目前比较流行的两种语言,单从表面上看,两者名称很相似,于是许多初学者容易将二者混淆,或者直接归为一类,其实不然,虽然两者有着紧密的联系,但确是两个完全不同的语言。接下来,笔者仅就她们的几个主要差别归纳起来。
一.开发厂商
众所周知,Java是SUN公司推出的程序设计语言,特别适合于Internet应用程序开发,其前身是Oak语言,而JavaScript则是NetScape公司的产品,是为了扩展NetScape Navigator功能而开发的一种可嵌入Web页面中的解释性语言,其前身是Live Script。由于Java的普及,NetScape和SUN两家公司签订合同后才将其命名为JavaScript。
二.面向对象与基于对象
Java是一种真正的纯面向对象编程语言,在Java中,一切都是对象;JavaScript是一种脚本语言,由于她本身提供了非常丰富的内部对象供程序员使用,因而它是基于对象的语言。
三.开发和运行环境的不同
若希望利用Java编写程序并使之运行,必须事先在系统内安装相应版本的JDK和JVM,保证代码能够得到编译和运行的环境;而编写JavaScript则相对简单,只需使用某种HTML文档编辑器甚至某种字符编辑器(如Notepad)即可,然后打开浏览器即可运行。
四.变量的区别
Java中使用变量在编译之前必须声明其数据类型,因而她采用的是强类型变量;JavaScript则不用在变量前声明类型,而是由解释器在运行时自动检查,所以她是弱类型变量。
五.标签的不同
利用Java写出的Applet小程序,在HTML中用……来标识;JavaScript程序在HTML中运行,其代码在标签内。
六.解释与编译
Java源代码在执行前被编译,因而在网络应用中,必须要求客户端安装有解释平台,也就意味着Java应用不与HTML文档集成(Applet小程序例外);JavaScript是一种解释性语言,其代码在发往客户端之前不需编译,而是将其嵌入到HTML文档中,一起发送给客户端,由浏览器解释执行。
另外,JavaScript仅是一种解释性语言,并没有复杂的语法和规则,更不支持如Java里的继承这样的性质,因此也比Java更加容易学习。
主要数据类型:string, boolean, number
复合数据类型:function, object
特殊数据类型: Null 空对象 Undefined 未定义
JavaScript是一种脚本语言,它采用小程序段的方式实现编程。像其它脚本语言一样,JavaScript同样已是一种解释性语言,它提供了一个易的开发过程。它的基本结构形式与C、C++、VB、Delphi十分类似。但它不像这些语言一样,需要先编译,而是在程序运行过程中被逐行地解释。它与HTML标识结合在一起,从而方便用户的使用操作。
1)基于对象的语言。
JavaScript是一种基于对象的语言,同时以可以看作一种面向对象的。这意味着它能运用自己已经创建的对象。因此,许多功能可以来自于脚本环境中对象的方法与脚本的相互作用。 2)简单性
JavaScript的简单性主要体现在:首先它是一种基于Java基本语句和控制流之上的简单而紧凑的设计, 从而对于学习Java是一种非常好的过渡。其次它的变量类型是采用弱类型,并未使用严格的数据类型。
3)安全性
JavaScript是一种安全性语言,它不允许访问本地的硬盘,并不能将数据存入到服务器上,不允许对网络文档进行修改和删除,只能通过浏览器实现信息浏览或动态交互。从而有效地防止数据的丢失。
4)动态性的
JavaScript是动态的,它可以直接对用户或客户输入做出响应,无须经过Web服务程序。它对用户的反映响应,是采用以事件驱动的方式进行的。所谓事件驱动,就是指在主页(Home Page)中执行了某种操作所产生的动作,就称为“事件”(Event)。比如按下鼠标、移动窗口、选择菜单等都可以视为事件。当事件发生后,可能会引起相应的事件响应。
5)跨平台性
JavaScript是依赖于浏览器本身,与操作环境无关,只要能运行浏览器的计算机,并支持JavaScript的浏览器就可正确执行。从而实现了“编写一次,走遍天下”的梦想。实际上JavaScript最杰出之处在于可以用很小的程序做大量的事。无须有高性能的电脑,软件仅需一个字处理软件及一浏览器,无须WEB服务器通道,通过自己的电脑即可完成所有的事情。
使用ajax的最大优点,就是能在不更新整个页面的前提下维护数据。这使得web应用程序更为迅捷地回应用户动作,并避免了在网络上发送那些没有改变过的信息。
这是js调试代码存在已久的问题,简单的我们可以使用浏览器提供的错误提示框,还有可以使用DW CS4提供的纠正错误,或者通过专业的插件,如firebug等
这个问题是大家遇到最常见的问题之一,因为修改了js内容调试的时候并不能显示新写代码的结果,是因为Js为了加速页面执行,当前页面会使用缓存保持当前调用的相同的连接,为了开发时调试方便可以在连接地址后面增加一个随机函数.
在传统的javascript中,如果想得到服务器端数据库或文件上的信息,或者发送客户端信息到服务器,需要建立一个HTML form然后Post或者get提交数据到服务端。用户需要点击submit 来发送或者接受数据信息,然后等待服务器响应请求,页面重写加载,因为服务器每次都要返回一个新的页面,所以传统的web应用有可能会很慢而且用户交互不友好。
使用ajax就可以使javascript通过XMLHttpRequest对象直接与服务器进行交互。通过HTTPRequest,一个web页面可以发送一个请求道web服务器并且接受web服务器返回的信息(不需要加载任何界面),展示给用户的还是同一个页面,用户感觉不到页面刷新,也看不到Javascript后台进行的发送请求和接受的响应。
表单验证、网页特效、网页游戏
1)jQuery是JavaScript的轻量级框架,对JavaScript进行了很好的封装,很多复杂的JavaScript代码不用写了,直接调用就可以,使开发简单、高效。
2)jQuery强大的选择器封装了DOM,操作网页元素更简单了。
3)在大型JavaScript框架中,jQuery对性能的理解最好,大小不超过30KB。
4)完善的ajax有着出色的浏览器兼容性,任何浏览器使用ajax都能兼容。
5)基于jQuery开发的插件目前已经有大约数千个。开发者可使用插件来进行表单确认、图表种类、字段提示、动画、进度条等任务。
基本:
$("#myELement") ID选择器
$(“div”) 标签选择器
$(".myClass") 类选择器
$("*") 通配符选择器
层级选择器
过滤选择器
子元素选择器
简单的写法(‘#id’)用来代替document.getElementById()。
支持css选择器。
完善的处理机制,就算写错了Id也不会报错。
如果是常规的ajax程序的话,使用load()、 . g e t ( ) 、 .get()、 .get()、.post(),一般我会使用的是 . p o s t ( ) 方 法 , 如 果 需 要 设 定 , b e f o r e S e n d ( 提 交 前 回 调 函 数 ) , e r r o r ( 失 败 后 处 理 ) , s u c c e s s ( 成 功 后 处 理 ) , 及 c o m p l e t e ( 请 求 完 成 后 处 理 ) 毁 掉 函 数 等 , 这 个 时 候 我 会 使 用 .post()方法,如果需要设定,beforeSend(提交前回调函数),error(失败后处理),success(成功后处理),及complete(请求完成后处理)毁掉函数等,这个时候我会使用 .post()方法,如果需要设定,beforeSend(提交前回调函数),error(失败后处理),success(成功后处理),及complete(请求完成后处理)毁掉函数等,这个时候我会使用.ajax()
1) . g e t 方 法 使 用 g e t 方 法 来 进 行 一 步 请 求 , .get方法使用get方法来进行一步请求, .get方法使用get方法来进行一步请求,.post是使用post方法来进行请求。
2)get请求会讲参数跟在url后进行传递,而post请求则是作为Http消息的实体.内容发送给web服务器的,这种传递是对用户不可见的。
3)get方式传输的数据大小不能超过2kb而post请求要大的多
4)get方式请求的数据会被浏览器缓存起来,因此有安全问题
addClass()来追加样式,removeClass()来删除样式,toggle()来切换样式。
Html()方法,类似于innerHTML属性,可以用来读取或者设置某个元素中的HTML内容,text()类似于innerText属性,可以用来读取或这是某个元素的文本内容,val()可以用来设置和获取元素的值。
1)获取页面元素
2)修改页面的外观
3)修改页面的内容
4)响应页面的操作
5)为页面添加动态效果
6)无需刷新页面,即可从服务器获取信息
7)简化常见的javascript的任务
html拼接、json数组、form表单经过serialize()序列化的
1)请求url
2)请求参数
3)请求类型,get或post
4)回调函数
5)传输类型,html或json等
Js获取输入框的value值:
var value = document.getElementById(“输入框ID属性”).value;
jQuery获取输入框的value值:
var value = $(“input”).val();
var value = $(“input”).attr(“value”);
去掉对web.xml的监视,把jsp提前编辑成Servlet。
有富余物理内存的情况,加大tomcat使用的jvm的内存
1)config 配置文件存放的路径
2)webapps 项目部署的目录
3)bin tomcat运行需要的脚本与jar包的目录
4)lib 运行项目时所需要的jar包的目录
5)work 部署项目的缓存目录
6)temp 临时文件存放的目录
7)logs 记录日志的目录
tomcat其实是一种web服务器,java编写的web项目可以部署在其上,用户在客户端请求时,都是先将请求发送到tomcat上,tomcat再将请求发送到对应的项目上。
启动tomcat
在Windows下:进入bin目录,双击startup.bat
在Linux下:cd进入bin目录,sh startup.sh
在开发工具eclipse中,右键选择Debug Server或者Run Server
停止tomcat
在Windows下:进入bin目录,双击shutdown.bat
在Linux下:cd进入bin目录,sh shutdown.sh
在开发工具eclipse中,选择服务器stop Server
配置文件在tomcat的config文件夹下
日志文件在logs文件夹下
在web容器中运行的服务器端java程序,主要用于响应HTTP请求。Servlet一般用于mvc中的控制器部分。
用来管理servlet生命周期的应用程序如(tomcat webloc等)
Servlet生命周期分为实例化、初始化、响应请求调用service()方法、消亡阶段调用destroy()方法。
执行过程如下:
1)当浏览器发送一个请求地址,tomcat会接收这个请求
2)tomcat会读取项目中的web.xml中的配置
3)当请求地址符合servlet-mapping标签映射的地址,会进入这个servlet
4)servlet首先会实例化(构造),然后初始化执行init()方法,init()方法至始至终执行一次,servlet对象是单实例
5)根据请求的方式是get或post,在service()方法中调用doget()或dopost()方法,完成此次请求
6)当服务器停止,会调用destroy()方法,销毁实例
Servlet实例化有两种,如下:
1) 第一次请求时,实例化servlet对象
2)在web.XML文件中的之间添加1,tomcat启动时就会实例化servlet对象
Form中的get和post方法,在数据传输过程中分别对应了HTTP协议中的GET和POST方法。二者主要区别如下:
1)Get是用来从服务器上获得数据,而Post是用来向服务器上传数据;
2)Get将表单中数据按照variable=value的形式,添加到action所指向的URL后面,并且两者使用“?”连接,而各个变量之间使用“&”连接;Post是将表单中的数据放在form的数据体中,按照变量和值相对应的方式,传递到action所指向URL;
3)Get是不安全的,因为在传输过程,数据被放在请求的URL中;Post的所有操作对用户来说都是不可见的;
4)Get传输的数据量小,这主要是因为受URL长度限制;而Post可以传输大量的数据,所以在上传文件只能使用Post;
5)Get限制Form表单的数据集必须为ASCII字符,而Post支持整个ISO10646字符集;
6)Get是Form的默认方法。
public class ServletName extends HttpServlet {
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
}
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
}
}
forward是容器中控制权的转向,是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器,浏览器根本不知道服务器发送的内容是从哪儿来的,所以它的地址栏中还是原来的地址。 redirect就是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址,一般来说浏览器会用刚才请求的所有参数重新请求,所以session,request参数都可以获取,并且从浏览器的地址栏中可以看到跳转后的链接地址。前者更加高效,在前者可以满足需要时,尽量使用forward()方法,并且,这样也有助于隐藏实际的链接;在有些情况下,比如,需要跳转到一个其它服务器上的资源,则必须使用sendRedirect()方法。
总结:forward是程序内部跳转,不会跳出tomcat服务器,redirect可以外部跳转,从一个服务器跳转到另一个服务器。
转发:request.getRequestDispatcher (“demo.jsp"). forward(request, response);
重定向:response.sendRedirect(“demo.jsp");
可以验证客户是否来自可信的网络,可以对客户提交的数据进行重新编码,可以从系统里获得配置的信息,可以过滤掉客户的某些不应该出现的词汇,可以验证用户是否登录,可以验证客户的浏览器是否支持当前的应用,可以记录系统的日志等等。
<%@page language=”java” contenType=”text/html;charset=gb2312” session=”true” buffer=”64kb” autoFlush=”true” isThreadSafe=”true” info=”text” errorPage=”error.jsp” isErrorPage=”true” isELIgnored=”true” pageEncoding=”gb2312” import=”java.sql.*”%>
isErrorPage:是否能使用Exception对象;isELIgnored:是否忽略EL表达式;
<%@include file=”filename”%>
<%@taglib prefix=”c”uri=”http://……”%>
JSP中的请求转发可利用forward动作实现:
Serlvet中实现请求转发的方式为:
getServletContext().getRequestDispatcher(path).forward(req,res)。
1)JSP页面乱码
<%@ page contentType=”text/html ; charset=utf-8”%>
2)表单提交中文时出现乱码
request.setCharacterEncoding(“utf-8”);
3)数据库连接出现乱码
是数据库连接中加入useUnicode=true&characterEncoding=utf-8;
1)两者的作用范围不同:
Session对象是用户级的,而Application是应用程序级别的
一个用户一个session对象,每个用户的session对象不同,在用户所访问的网站多个页面之间共享同一个session对象
一个Web应用程序一个application对象,每个Web应用程序的application对象不同,但一个Web应用程序的多个用户之间共享同一个application对象。
两者的生命周期不同:
session对象的生命周期:用户首次访问网站创建,用户离开该网站 (不一定要关闭浏览器) 消亡。
application对象的生命周期:启动Web服务器创建,关闭Web服务器销毁。
JSP共有以下9种基本内置组件
request:用户端请求,此请求会包含来自GET/POST请求的参数;
response:网页传回用户端的回应;
pageContext:网页的属性是在这里管理;
session:与请求有关的会话期;
application:servlet正在执行的内容;
out:用来传送回应的输出;
config:servlet的构架部件;
page:JSP网页本身;
exception:针对错误网页,未捕捉的例外
JSP共有以下6种基本动作
jsp:include:在页面被请求的时候引入一个文件。
jsp:useBean:寻找或者实例化一个JavaBean。
jsp:setProperty:设置JavaBean的属性。
jsp:getProperty:输出某个JavaBean的属性。
jsp:forward:把请求转到一个新的页面。
jsp:plugin:根据浏览器类型为Java插件生成OBJECT或EMBED标记
动态INCLUDE用jsp:include动作实现,
静态INCLUDE用include伪码实现,使用jsp指令引用<%@ include file=included.htm %>,不会检查所含文件的变化,适用于包含静态页面,先将内容先包含到主页面然后在一起编译,只编译一次。
JSP是Servlet技术的扩展,本质上是Servlet的简易方式,更强调应用的外表表达。JSP编译后是"类servlet"。Servlet和JSP最主要的不同点在于,Servlet的应用逻辑是在Java文件中,并且完全从表示层中的HTML里分离开来。而JSP的情况是Java和HTML可以组合成一个扩展名为.jsp的文件。JSP侧重于视图,Servlet主要用于控制逻辑。
request、session、application、cookie等
1)cookie数据存放在客户的浏览器上,session数据放在服务器上。
2)cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗考虑到安全应当使用session。
3)session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能考虑到减轻服务器性能方面,应当使用COOKIE。
4)单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。
生命周期如下:
1、执行构造方法,实例化
2、执行init方法,初始化
3、执行doFilter方法,过滤用户请求
4、当tomcat关闭时,执行destroy方法,进行销毁
构造方法与init方法在tomcat启动时就执行,至始至终只执行一次,
filter对象是单实例
执行过程如下:
1、浏览器发送一个请求,会到达tomcat
2、tomcat会根据项目中的web.xml中的Filter过滤路径配置,过滤请求
3、过滤到用户请求会进入Filter类中的doFilter方法
4、在doFilter方法中实现业务逻辑,最后调用doFilter(request, response)
传递用户请求
5、到达用户请求的页面
Windows
在tomcat的bin/catalina.bat最前面加入set JAVA_OPTS=-Xms128m -Xmx350m
Linux:
在tomcat 目录下的bin/catalina.sh添加:JAVA_OPTS=’-Xms512m -Xmx1024m’
-Xms:初始值-Xmx:最大值-Xmn:最小值
1、核心库(逻辑库) 用于页面上业务逻辑处理
2、函数库 利用jstl内置函数,获取结果
3、格式化库 格式化时间、格式化数字
4、SQL库(几乎不用) 操作数据库
触发器是一种特殊的存储过程,主要是通过事件来触发而被执行的。它可以强化约束,来维护数据的完整性和一致性,可以跟踪数据库内的操作从而不允许未经许可的更新和变化。可以联级运算。如,某表上的触发器上包含对另一个表的数据操作,而该操作又会导致该表触发器被触发。
存储过程是一个预编译的SQL语句,优点是允许模块化的设计,就是说只需创建一次,以后在该程序中就可以调用多次。如果某次操作需要执行多次SQL,使用存储过程比单纯SQL语句执行要快。
调用:
1)可以用一个命令对象来调用存储过程。
2)可以供外部程序调用,比如:java程序。
优点:
1)存储过程是预编译过的,执行效率高。
2)存储过程的代码直接存放于数据库中,通过存储过程名直接调用,减少网络通讯。
3)安全性高,执行存储过程需要有一定权限的用户。
4)存储过程可以重复使用,可减少数据库开发人员的工作量。
缺点:移植性差
存储过程 | 函数 |
---|---|
用于在数据库中完成特定的操作或者任务(如插入、删除等) | 用于特定的数据(如选择) |
程序头部声明用procedure | 程序头部声明用function |
程序头部声明时不需描述返回类型 | 程序头部声明时要描述返回类型,而且PL/SQL块中至少要包括一个有效的return语句 |
可以使用in/out/in out 三种模式的参数 | 可以使用in/out/in out 三种模式的参数 |
可作为一个独立的PL/SQL语句来执行 | 不能独立执行,必须作为表达式的一部分调用 |
可以通过out/in out 返回零个或多个值 | 通过return语句返回一个值,且改值要与声明部分一致,也可以是通过out类型的参数带出的变量 |
SQL语句(DML 或SELECT)中不可调用存储过程 | SQL语句(DML 或SELECT)中可以调用函数 |
索引就一种特殊的查询表,数据库的搜索可以利用它加速对数据的检索。它很类似与现实生活中书的目录,不需要查询整本书内容就可以找到想要的数据。索引可以是唯一的,创建索引允许指定单个列或者是多个列。缺点是它减慢了数据录入的速度,同时也增加了数据库的尺寸大小。
1、经常被查询的字段
2、不为空且字段值不重复
3、字段的值不经常被修改
逻辑上:
Single column 单行索引
Concatenated 多行索引
Unique 唯一索引
NonUnique 非唯一索引
Function-based函数索引
Domain 域索引
物理上:
Partitioned 分区索引
NonPartitioned 非分区索引
B-tree:
Normal 正常型B树
Rever Key 反转型B树
Bitmap 位图索引
事务就是被绑定在一起作为一个逻辑工作单元的SQL语句分组,如果任何一个语句操作失败那么整个操作就被失败,以后操作就会回滚到操作前状态,或者是上有个节点。为了确保要么执行,要么不执行,就可以使用事务。要将有组语句作为事务考虑,就需要通过ACID测试,即原子性,一致性,隔离性和持久性。
锁:在所以的DBMS中,锁是实现事务的关键,锁可以保证事务的完整性和并发性。与现实生活中锁一样,它可以使某些数据的拥有者,在某段时间内不能使用某些数据或数据结构。当然锁还分级别的。
视图:是一种虚拟的表,具有和物理表相同的功能。可以对视图进行增,改,查,操作,试图通常是有一个表或者多个表的行或列的子集。对视图的修改不影响基本表。它使得我们获取数据更容易,相比多表查询。
游标:是对查询出来的结果集作为一个单元来有效的处理。游标可以定在该单元中的特定行,从结果集的当前行检索一行或多行。可以对结果集当前行做修改。一般不使用游标,但是需要逐条处理数据的时候,游标显得十分重要。
优点:
1)对数据库的访问,因为视图可以有选择性的选取数据库里的一部分。
2)用户通过简单的查询可以从复杂查询中得到结果。
3)维护数据的独立性,试图可从多个表检索数据。
4)对于相同的数据可产生不同的视图。
缺点:
性能:查询视图时,必须把视图的查询转化成对基本表的查询,如果这个视图是由一个复杂的多表查询所定义,那么,即使是视图的一个简单查询,也把它变成一个复杂的结合体,需要花费一定的时间。
内连接、自连接、外连接(左、右、全)、交叉连接
内连接:只有两个元素表相匹配的才能在结果集中显示。
外连接:
左外连接:左边为驱动表,驱动表的数据全部显示,匹配表的不匹配的不会显示。
右外连接:右边为驱动表,驱动表的数据全部显示,匹配表的不匹配的不会显示。
全外连接:连接的表中不匹配的数据全部会显示出来。
交叉连接: 笛卡尔效应,显示的结果是链接表数的乘积。
主键在本表中是唯一的、不可唯空的,外键可以重复可以唯空;外键和另一张表的主键关联,不能创建对应表中不存在的外键。
1.建索引
2.减少表之间的关联
3.优化sql,尽量让sql很快定位数据,不要让sql做全表查询,应该走索引,把数据 量大的表排在前面
4.简化查询字段,没用的字段不要,已经对返回结果的控制,尽量返回少量数据
5.尽量用PreparedStatement来查询,不要用Statement
第一范式(1NF):字段具有原子性,不可再分。所有关系型数据库系统都满足第一范式。
数据库表中的字段都是单一属性的,不可再分。例如,姓名字段,其中的姓和名必须作为一个整体,无法区分哪部分是姓,哪部分是名,如果要区分出姓和名,必须设计成两个独立的字段。
第二范式(2NF):是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。
要求数据库表中的每个实例或行必须可以被惟一地区分。通常需要为表加上一个列,以存储各个实例的惟一标识。这个惟一属性列被称为主关键字或主键。
第二范式(2NF)要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性,如果存在,那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体,新实体与原实体之间是一对多的关系。为实现区分通常需要为表加上一个列,以存储各个实例的惟一标识。简而言之,第二范式就是非主属性非部分依赖于主关键字。
第三范式(3NF):必须先满足第二范式(2NF)。简而言之,第三范式(3NF)要求一个数据库表中不包含已在其它表中已包含的非主关键字信息。
所以第三范式具有如下特征:
1,每一列只有一个值
2,每一行都能区分。
3,每一个表都不包含其他表已经包含的非主关键字信息。
例如,帖子表中只能出现发帖人的id,而不能出现发帖人的id,还同时出现发帖人姓名,否则,只要出现同一发帖人id的所有记录,它们中的姓名部分都必须严格保持一致,这就是数据冗余。
UNION在进行表链接后会筛选掉重复的记录,所以在表链接后会对所产生的结果集进行排序运算,删除重复的记录再返回结果。实际大部分应用中是不会产生重复的记录,最常见的是过程表与历史表UNION。
UNION ALL只是简单的将两个结果合并后就返回。这样,如果返回的两个结果集中有重复的数据,那么返回的结果集就会包含重复的数据了。
从效率上说,UNION ALL 要比UNION快很多,所以,如果可以确认合并的两个结果集中不包含重复的数据的话,那么就使用UNION ALL。
Char的长度是固定的,而varchar2的长度是可以变化的,比如,存储字符串“abc”对于char(20),表示你存储的字符将占20个字节,包含17个空,而同样的varchar2(20)只占了3个字节,20只是最大值,当你存储的字符小于20时,按实际长度存储。
char的效率要被varchar2的效率高。
目前varchar是varchar2的同义词,工业标准的varchar类型可以存储空字符串,但是oracle不能这样做,尽管它保留以后这样做的权利。Oracle自己开发了一个数据类型varchar2,这个类型不是一个标准的varchar,他将在数据库中varchar列可以存储空字符串的特性改为存储null值,如果你想有向后兼容的能力,oracle建议使用varchar2而不是varchar
1)库函数不同。
2)Oracle是用表空间来管理的,Mysql不是。
3)显示当前所有的表、用户、改变连接用户、显示当前连接用户、执行外部脚本的语句的不同。
4)分页查询时候时候,mysql用limt oracle用rownum
5)sql的语法的不同。
Oracle语句分三类:DDL、DML、DQL、DCL。
DDL(Data Definition Language)数据定义语言,包括:
Create语句:可以创建数据库和数据库的一些对象。
Drop语句:可以删除数据表、索引、触发程序、条件约束以及数据表的权限等。
Alter语句:修改数据表定义及属性。
Truncate语句:删除表中的所有记录,包括所有空间分配的记录被删除。
DML(Data Manipulation Language)数据操控语言,包括:
Insert语句:向数据表张插入一条记录。
Delete语句:删除数据表中的一条或多条记录,也可以删除数据表中的所有记录,但是它的操作对象仍是记录。
Update语句:用于修改已存在表中的记录的内容。
DQL(Data Query Language)数据操控语言,包括:
Select语句:用于查询已存在表中的记录的内容。
DCL(Data Control Language)数据库控制语言,包括:
Grant语句:允许对象的创建者给某用户或某组或所有用户(PUBLIC)某些特定的权限。
Revoke语句:可以废除某用户或某组或所有用户访问权限
TCL(Transaction Control Language)事务控制语言
commit:提交事务
rollback:回滚事务
使用rownum,两种如下:
第一种:
select *
from (select t.*,rownum row_num from mytable t) b
where b.row_num
between 1 and 10
第二种:
select *
from ( select a.*, rownum rn from mytable a where rownum <= 10 )
where rn >= 1
使用rowid,如下:
select *
from scott.emp
where rowid
in
(select rd
from (select rowid as rd ,rownum as rn from scott.emp )
where rn<=6 and rn>3)
select * from (select * from t_example order by dbms_random.random)
where rownum <= 50
order by 排序查询、asc升序、desc降序
group by 分组查询、having 只能用于group by子句、作用于组内,having条件子句可以直接跟函数表达式。使用group by 子句的查询语句需要使用聚合函数。
oracle的commit就是DML语句提交数据(这里是释放锁不是锁表),在未提交前你前面的操作更新的都是内存,没有更新到物理文件中。
执行commit从用户角度讲就是更新到物理文件了,事实上commit时还没有写date file,而是记录了redo log file,要从内存写到data物理文件,需要触发检查点,由DBWR这个后台进程来写,这里内容有点多的,如果不深究的话你就理解成commit即为从内存更新到物理文件。
行转列
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RNeBeuiq-1595208364294)(C:\Users\Administrator\Desktop\范本\20200622面试题\新建文件夹\新建文件夹\clip_image003.jpg)]
1) 使用decode函数
select name,
sum(decode(course, '语文', score, 0)) as 语文,
sum(decode(course, '数学', score, 0)) as 数学,
sum(decode(course, '英语', score, 0)) as 英语
from GRADE group by name;
2) 使用case when语句
select name,
sum(case course when '语文' then score else 0 end) as 语文,
sum(case course when '数学' then score else 0 end) as 数学,
sum(case course when '英语' then score else 0 end) as 英语
from GRADE group by name;
select name,
sum(case when course='语文' then score else 0 end) as 语文,
sum(case when course='数学' then score else 0 end) as 数学,
sum(case when course='英语' then score else 0 end) as 英语
from GRADE group by name;
列转行
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W51QzASu-1595208364301)(C:\Users\Administrator\Desktop\范本\20200622面试题\新建文件夹\新建文件夹\clip_image005.jpg)]
select name, '语文' as course, cn_score as score from SC_GRADE
union all
select name, '数学' as course, math_score as score from SC_GRADE
union all
select name, '英语' as course, en_score as score from SC_GRADE
order by name;
PL/SQL是一种程序语言,叫做过程化SQL语言(Procedural Language/SQL)。PL/SQL是Oracle数据库对SQL语句的扩展。在普通SQL语句的使用上增加了编程语言的特点,所以PL/SQL把数据操作和查询语句组织在PL/SQL代码的过程性单元中,通过逻辑判断、循环等操作实现复杂的功能或者计算。PL/SQL 只有 Oracle 数据库有。 MySQL 目前不支持 PL/SQL 的。
Oracle使用序列来生成唯一编号,用来处理一个表中自增字段。 Oracle序列是原子对象,并且是一致的。也就是说,一旦您访问一个序列号,Oracle将在处理下一个请求之前自动递增下一个编号,从而确保不会出现重复值。
视图其实就是一条查询sql语句,用于显示一个或多个表或其他视图中的相关数据。
表就是关系数据库中实际存储数据用的。
1)字符串类型
char、nchar、varchar、varchar2、nvarchar2
2)数字类型
number、integer
3)浮点类型
binary_float、binary_double、float
4)日期类型
date、 timestamp
5)LOB类型
blob、clob、nclob、bfile
TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同:二者均删除表中的全部行。但 TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少。 DELETE 语句每次删除一行,并在事务日志中为所删除的每行记录一项。
TRUNCATE TABLE 通过释放存储表数据所用的数据页来删除数据,并且只在事务日志中记录页的释放。
TRUNCATE,DELETE,DROP放在一起比较:
TRUNCATE TABLE:删除内容、释放空间但不删除定义。
DELETE TABLE:删除内容不删除定义,不释放空间。
DROP TABLE:删除内容和定义,释放空间。
select to_char(sysdate, 'yyyy-MM-dd HH24:mi:ss') from dual;
使用distinct关键字
union(并集去重复)、
union all(并集不去重复)、
intersect(交集)、
minus(差集)
关键字:select、from、join、on、where、group by、having、order by、distinct执行顺序
1、from
2、join
3、on
4、where
5、group by 分组字段
6、having 表达式
7、select
8、distinct
9、order by
在我们不知道具体有什么数据的时候,也即未知,可以用NULL,我们称它为空,ORACLE中,含有空值的表列长度为零。
ORACLE允许任何一种数据类型的字段为空,除了以下两种情况:
1、主键字段(primary key),
2、定义时已经加了NOT NULL限制条件的字段
说明:
1、等价于没有任何值、是未知数。
2、NULL与0、空字符串、空格都不同。
3、对空值做加、减、乘、除等运算操作,结果仍为空。
4、NULL的处理使用NVL函数。
5、比较时使用关键字用“is null”和“is not null”。
6、空值不能被索引,所以查询时有些符合条件的数据可能查不出来,count(*)中,用nvl(列名,0)处理后再查。
7、排序时比其他数据都大(索引默认是降序排列,小→大),所以NULL值总是排在最后。
Select * from 表名 limit 起始位置, 查询数量
1、在MySQL、SqlServer、oracle中两个单引号代表字符
2、字符串转时间
Oracle:使用to_date(‘字符串时间’, ‘yyyy-MM-dd HH24:mi:ss’)函数
SqlServer:CONVERT(数据类型,日期值,日期样式ID)
MySQL:date_format(日期值,’%Y-%c-%d %h:%i:%s’);
在oracle中是可以的,在mysql中不可以。
如下语句:
update (select * from table1 t1 left join table2 t2 on t1.字段 = t2.字段 ) set 字段名 = 值 where 条件字段 = 条件值
实现线程有两种方式:1.继承Thread类,重写run方法,在调用start方法。
实现Runnable接口,重写run方法。在传给Thread构造器,调用时调用Thread的start方法。
用synchronized关键字修饰同步方法 。
不使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。
sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。 wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。
分几种情况:
1)其他方法前是否加了synchronized关键字,如果没加,则能。
2)如果这个方法内部调用了wait,则可以进入其他synchronized方法。
3)如果其他个方法都加了synchronized关键字,并且内部没有调用wait,则不能。
4)如果其他方法是static,它用的同步锁是当前类的字节码,与非静态的方法不能同步,因为非静态的方法用的是this。
一个程序中可以有多条执行线索同时执行,一个线程就是程序中的一条执行线索,每个线程上都关联有要执行的代码,即可以有多段程序代码同时运行,每个程序至少都有一个线程,即main方法执行的那个线程。如果只是一个cpu,它怎么能够同时执行多段程序呢?这是从宏观上来看的,cpu一会执行a线索,一会执行b线索,切换时间很快,给人的感觉是a,b在同时执行,好比大家在同一个办公室上网,只有一条链接到外部网线,其实,这条网线一会为a传数据,一会为b传数据,由于切换时间很短暂,所以,大家感觉都在同时上网。
线程是程序执行流的最小单元,相对独立、可调度的执行单元,是系统独立调度和分派CPU的基本单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。
程序是一段静态的代码,是应用软件执行的蓝本。
进程是程序一次动态执行的过程,它对应了从代码加载、执行完毕的一个完整过程,这也是进程开始到消亡的过程。
线程是进程中独立、可调度的执行单元,是执行中最小单位。
一个程序一般是一个进程,但可以一个程序中有多个进程。
一个进程中可以有多个线程,但只有一个主线程。
Java应用程序中默认的主线程是main方法,如果main方法中创建了其他线程,JVM就会执行其他的线程。
创建线程有三种方式:
1)是继承Thread类,创建格式如下:
Thread thread = new Thread();
2)是实现Runnable接口,创建格式如下:
Thread thread = new Thread(new Runnable());
其实Thread类实现了Runnable接口
3)通过线程池方式,获取线程
package com.myjava.thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPool {
private static int POOL_NUM = 10;
public static void main(String[] agrs){
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < POOL_NUM; i++) {
RunnableThread thread = new RunnableThread();
executorService.execute(thread);
}
}
}
class RunnableThread implements Runnable{
private int THREAD_NUM = 10;
public void run() {
for (int i = 0; i <THREAD_NUM; i++) {
System.out.println("线程"+Thread.currentThread()+i);
}
}
}
创建–运行–中断–死亡
创建:线程构造
运行:调用start()方法,进入run()方法
中断:sleep()、wait()
死亡:执行完run()方法或强制run()方法结束,线程死亡
currentThread()方法是获取当前线程
interrupt()唤醒休眠线程,休眠线程发生InterruptedException异常
1)新建状态(New):新创建了一个线程对象。
2)就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3)运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4)阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。
5)死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
通俗的说,就是可以将内存中Java对象可以写在硬盘上(序列化到硬盘上),反序列化就是讲硬盘的内容读取到内存中去;java是通过实现Serializable接口,实现的序列化,Serializable接口里面没有任何的方法,只是个标示接口。
答:listFiles方法接受一个FileFilter对象,这个FileFilter对象就是过虑的策略对象,不同的人提供不同的FileFilter实现,即提供了不同的过滤策略。
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class Jad2Java {
public static void main(String[] args) throws Exception {
File srcDir = new File("java");
if(!(srcDir.exists() && srcDir.isDirectory()))
throw new Exception("目录不存在");
File[] files = srcDir.listFiles(new FilenameFilter(){
public boolean accept(File dir, String name) {
return name.endsWith(".java");
}
}
);
System.out.println(files.length);
File destDir = new File("jad");
if(!destDir.exists()) destDir.mkdir();
for(File f :files){
FileInputStream fis = new FileInputStream(f);
String destFileName = f.getName().replaceAll("\\.java$", ".jad");
FileOutputStream fos = new FileOutputStream(new File(destDir,destFileName));
copy(fis,fos);
fis.close();
fos.close();
}
}
private static void copy(InputStream ips,OutputStream ops) throws Exception{
int len = 0;
byte[] buf = new byte[1024];
while((len = ips.read(buf)) != -1){
ops.write(buf,0,len);
}
}
}
由本题总结的思想及策略模式的解析:
class jad2java{
1. 得到某个目录下的所有的java文件集合
1.1 得到目录 File srcDir = new File(“d:\java”);
1.2 得到目录下的所有java文件:File[] files = srcDir.listFiles(new MyFileFilter());
1.3 只想得到.java的文件:
class MyFileFilter implememyts FileFilter{
public boolean accept(File pathname){
return pathname.getName().endsWith(".java")
}
}
}
2.将每个文件复制到另外一个目录,并改扩展名
2.1 得到目标目录,如果目标目录不存在,则创建之
2.2 根据源文件名得到目标文件名,注意要用正则表达式,注意.的转义。
2.3 根据表示目录的File和目标文件名的字符串,得到表示目标文件的File。
//要在硬盘中准确地创建出一个文件,需要知道文件名和文件的目录。
2.4 将源文件的流拷贝成目标文件流,拷贝方法独立成为一个方法,方法的参数采用抽象流的形式。
//方法接受的参数类型尽量面向父类,越抽象越好,这样适应面更宽广。
}
分析listFiles方法内部的策略模式实现原理
File[] listFiles(FileFilter filter){
File[] files = listFiles();
//Arraylist acceptedFilesList = new ArrayList();
File[] acceptedFiles = new File[files.length];
int pos = 0;
for(File file: files){
boolean accepted = filter.accept(file);
if(accepted){
//acceptedFilesList.add(file);
acceptedFiles[pos++] = file;
}
}
Arrays.copyOf(acceptedFiles,pos);
//return (File[])accpetedFilesList.toArray();
}
字节流,字符流。字节流继承于InputStream OutputStream,字符流继承于InputStreamReader OutputStreamWriter。在java.io包中还有许多其他的流,主要是为了提高性能和使用方便
把一片二进制数据数据逐一输出到某个设备中,或者从某个设备中逐一读取一片二进制数据,不管输入输出设备是什么,我们要用统一的方式来完成这些操作,用一种抽象的方式进行描述,这个抽象描述方式起名为IO流,对应的抽象类为OutputStream和InputStream ,不同的实现类就代表不同的输入和输出设备,它们都是针对字节进行操作的。
在应用中,经常要完全是字符的一段文本输出去或读进来,用字节流可以吗?计算机中的一切最终都是二进制的字节形式存在。对于“中国”这些字符,首先要得到其对应的字节,然后将字节写入到输出流。读取时,首先读到的是字节,可是我们要把它显示为字符,我们需要将字节转换成字符。由于这样的需求很广泛,人家专门提供了字符流的包装类。
底层设备永远只接受字节数据,有时候要写字符串到底层设备,需要将字符串转成字节再进行写入。字符流是字节流的包装,字符流则是直接接受字符串,它内部将串转成字节,再写入底层设备,这为我们向IO设别写入或读取字符串提供了一点点方便。
字符向字节转换时,要注意编码的问题,因为字符串转成字节数组,
其实是转成该字符的某种编码的字节形式,读取也是反之的道理。
讲解字节流与字符流关系的代码案例:
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.io.PrintWriter;
public class IOTest {
public static void main(String[] args) throws Exception {
String str = "中国人";
/*FileOutputStream fos = new FileOutputStream("1.txt");
fos.write(str.getBytes("UTF-8"));
fos.close();*/
/*FileWriter fw = new FileWriter("1.txt");
fw.write(str);
fw.close();*/
PrintWriter pw = new PrintWriter("1.txt","utf-8");
pw.write(str);
pw.close();
/*FileReader fr = new FileReader("1.txt");
char[] buf = new char[1024];
int len = fr.read(buf);
String myStr = new String(buf,0,len);
System.out.println(myStr);*/
/*FileInputStream fr = new FileInputStream("1.txt");
byte[] buf = new byte[1024];
int len = fr.read(buf);
String myStr = new String(buf,0,len,"UTF-8");
System.out.println(myStr);*/
BufferedReader br = new BufferedReader(
new InputStreamReader(
new FileInputStream("1.txt"),"UTF-8"
)
);
String myStr = br.readLine();
br.close();
System.out.println(myStr);
}
总结:很简单,字符流的底层就是字节流。而字符流主要是读取文本文件内容的,可以一个字符一个字符的读取,也可以一行一行的读取文本文件内容。而字节流读取单位为byte.byte作为计算机存储最基本单位,可以用字节流来读取很多其他格式的文件,比如图片视频等等。基于B/S和C/S的文件传输都可以采用字节流的形式。
File f = new File(fileName); //构造文件File类
f.isDirectory(); //判断是否为目录
File f = new File(filePath); //构造文件File类
String[] fileName = f.list(); //获取目录下的文件名
File[] files = f.listFiles(); //获取目录下的文件
读取文件:
public class FileRead {
/**
\* 1、找到指定的文件
\* 2、根据文件创建文件的输入流
\* 3、创建字节数组
\* 4、读取内容,放到字节数组里面
\* 5、关闭输入流
\* @param args
*/
public static void main(String[] args) {
File file = new File("E:" + File.separator + "hello.txt"); //构建指定文件
InputStream in = null;
try {
in = new FileInputStream(file); //根据文件创建文件的输入流
byte[] data = new byte[1024]; //创建字节数组
in.read(data); //读取内容,放到字节数组里面
System.out.println(new String(data));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
in.close(); //关闭输入流
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
写入文件:
public class FileWriter {
/**
\* 文件的输出流,用来写入文件内容
\* 1、找到指定的文件
\* 2、根据文件创建文件的输出流
\* 3、把内容转换成字节数组
\* 4、向文件写入内容
\* 5、关闭输出流
\* @param args
*/
public static void main(String[] args) {
File file = new File("E:" + File.separator + "hello.txt"); //构建指定文件
OutputStream out = null;
try {
out = new FileOutputStream(file); 根据文件创建文件的输出流
String message = "黄晓明与bady结婚了,扬子和黄圣依有女儿了。";
byte[] mesByte = message.getBytes(); //把内容转换成字节数组
out.write(mesByte); //向文件写入内容
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
out.close(); //关闭输出流
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public class FileCopy {
/**
\* 实现思路
\* 1、构建源文件与目标文件
\* 2、源文件创建输入流,目标文件创建输出流
\* 3、创建字节数组
\* 4、使用循环,源文件读取一部分内容,目标文件写入一部分内容,直到写完所有内容
\* 5、关闭源文件输入流,目标文件输出流
\* @param args
*/
public static void main(String[] args) {
//构建源文件
File file = new File("E:" + File.separator + "helloworld.txt");
//构建目标文件
File fileCopy = new File("D:" + File.separator + "helloworld.txt");
InputStream in = null;
OutputStream out = null;
try{
//目标文件不存在就创建
if(!(fileCopy.exists())) {
fileCopy.createNewFile();
}
//源文件创建输入流
in = new FileInputStream(file);
//目标文件创建输出流
out = new FileOutputStream(fileCopy, true);
//创建字节数组
byte[] temp = new byte[1024];
int length = 0;
//源文件读取一部分内容
while((length = in.read(temp)) != -1) {
//目标文件写入一部分内容
out.write(temp, 0, length);
}
}catch(IOException e) {
e.printStackTrace();
}finally {
try {
in.close(); //关闭源文件输入流
out.close(); //关闭目标文件输出流
}catch(IOException e) {
e.printStackTrace();
}
}
}
}
代码如下:
package com.huawei.interview.lym;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Types;
public class JdbcTest {
/**
\* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Connection cn = null;
CallableStatement cstmt = null;
try {
//这里最好不要这么干,因为驱动名写死在程序中了
Class.forName("com.mysql.jdbc.Driver");
//实际项目中,这里应用DataSource数据,如果用框架,
//这个数据源不需要我们编码创建,我们只需Datasource ds = context.lookup()
//cn = ds.getConnection();
cn = DriverManager.getConnection("jdbc:mysql:///test","root","root");
cstmt = cn.prepareCall("{call insert_Student(?,?,?)}");
cstmt.registerOutParameter(3,Types.INTEGER);
cstmt.setString(1, "wangwu");
cstmt.setInt(2, 25);
cstmt.execute();
//get第几个,不同的数据库不一样,建议不写
System.out.println(cstmt.getString(3));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally
{
/*try{cstmt.close();}catch(Exception e){}
try{cn.close();}catch(Exception e){}*/
try {
if(cstmt != null)
cstmt.close();
if(cn != null)
cn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
1)提高性能:在使用preparedStatement对象执行sql时候,命令被数据库编译和解析,然后被放到命令缓冲区,然后每当执行同一个preparedStatement时候,他就被再解析一次,但不会在编译,在缓冲区中可以发现预编译的命令,并且可以重新使用。
如果你要写Insert update delete 最好使用preparedStatement,在有大量用户的企业级应用软件中,经常会执行相同的sql,使用preparedStatement会增加整体的性能。
2)安全性:PreparedStatement可以防止sql注入。
package com.seecen.stream;
import java.sql.*;
public class TestJDBC {
/**
\* 1、实例话驱动类
\* 2、建立到数据库的连接
\* 3、将数据发送到数据库中
\* 4、执行语句(select语句)
\* 5、关闭
\* @param args
*/
public static void main(String[] args) {
ResultSet rs = null;
Statement stmt = null;
Connection conn = null;
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
conn = DriverManager.getConnection("jdbc:oracle:thin:@192.168.0.1:1521:yuewei", "scott", "tiger");
stmt = conn.createStatement();
rs = stmt.executeQuery("select * from dept");
while(rs.next()) {
System.out.println(rs.getString("deptno"));
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if(rs != null) {
rs.close();
rs = null;
}
if(stmt != null) {
stmt.close();
stmt = null;
}
if(conn != null) {
conn.close();
conn = null;
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
这两个类都实现了List接口(List接口继承了Collection接口),他们都是有序集合,即存储在这两个集合中的元素的位置都是有顺序的,相当于一种动态的数组,我们以后可以按位置索引号取出某个元素,,并且其中的数据是允许重复的,这是HashSet之类的集合的最大不同处,HashSet之类的集合不可以按索引号去检索其中的元素,也不允许有重复的元素(本来题目问的与hashset没有任何关系,但为了说清楚ArrayList与Vector的功能,我们使用对比方式,更有利于说明问题)。
接着才说ArrayList与Vector的区别,这主要包括两个方面:.
(1)同步性:
Vector是线程安全的,也就是说是它的方法之间是线程同步的,而ArrayList是线程不安全的,它的方法之间是线程不同步的。如果只有一个线程会访问到集合,那最好是使用ArrayList,因为它不考虑线程安全,效率会高些;如果有多个线程会访问到集合,那最好是使用Vector,因为不需要我们自己再去考虑和编写线程安全的代码。
备注:对于Vector&ArrayList、Hashtable&HashMap,要记住线程安全的问题,记住Vector与Hashtable是旧的,是java一诞生就提供了的,它们是线程安全的,ArrayList与HashMap是java2时才提供的,它们是线程不安全的。所以,我们讲课时先讲老的。
(2)数据增长:
ArrayList与Vector都有一个初始的容量大小,当存储进它们里面的元素的个数超过了容量时,就需要增加ArrayList与Vector的存储空间,每次要增加存储空间时,不是只增加一个存储单元,而是增加多个存储单元,每次增加的存储单元的个数在内存空间利用与程序效率之间要取得一定的平衡。Vector默认增长为原来两倍,而ArrayList的增长策略在文档中没有明确规定(从源代码看到的是增长为原来的1.5倍)。ArrayList与Vector都可以设置初始的空间大小,Vector还可以设置增长的空间大小,而ArrayList没有提供设置增长空间的方法。
总结:即Vector增长原来的一倍,ArrayList增加原来的0.5倍。
1)List和Set是Collection的子接口,map不是。
2)List的底层是数组的方式实现,Set是散列表的方式实现,map是键值对的方式。
3)list是有序可重复的,Set是无序不可重复的,map是有序,key不重复,value可重复
4)list和Set可直接使用itertator来进行遍历,map只能通过先遍历Key在遍历value.
Collection是集合类的上级接口,继承与他的接口主要有Set 和List.
Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。
Set里的元素是不能重复的,元素重复与否是使用equals()方法进行判断的。
equals()和==方法决定引用值是否指向同一对象equals()在类中被覆盖,为的是当两个分离的对象的内容和类型相配的话,返回真值。
1)继承不同
public class Hashtable extends Dictionary implements Map
public class HashMap extends AbstractMap implements Map
2)Hashtable 中的方法是同步的,而HashMap中的方法在缺省情况下是非同步的。在多线程并发的环境下,可以直接使用Hashtable,但是要使用HashMap的话就要自己增加同步处理了。
3)Hashtable中,key和value都不允许出现null值,在HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,即可以表示 HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。
4)两个遍历方式的内部实现上不同。
Hashtable、HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。
5)哈希值的使用不同,HashTable直接使用对象的hashCode。而HashMap重新计算hash值。
6)Hashtable和HashMap它们两个内部实现方式的数组的初始大小和扩容的方式。HashTable中hash数组默认大小是11,增加的方式是 old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数
List:是列表,有下标值,存储元素可以重复,遍历元素是有序的。
Set:是散列集,无下标值,存储元素不可重复,遍历元素时无序的。
Map:是以键值对存储,一个key一个value,key不可以重复,value可以重复。
数组:指定类型,固定长度,元素存储地址是连续的。
树:元素以树形结构存储,只有一个根节点。
栈:元素是先进后出,后进先出。
向量:动态数组,可以存储任何类型元素,动态长度,元素存储地址是连续的。
队列:元素存储是排列有序的,一定保证先进的先出,后进的后出。
相同点:
ArrayList和Linklist都是接口List的实现类,里面的数据都是有序可重复的。
区别:
ArrayList: 采用的是数组形式保存对象的,访问速度更快,而Linklist的插入和删除元素的速度更快
1) 下标遍历
2) Iterator遍历
3) Foreach遍历(最快)
先调用keySet()方法获取所有的key,在遍历key获取所有的元素
Map调用keySet()方法获取所有的key值,是一个Set集合
Map调用values()方法获取所有的value值,是一个Collection集合
Class> demo1=Class.forName(“Reflect.Demo”); //使用Class类
Class> demo2=new Demo().getClass(); //通过对象
Class> demo3=Demo.class; //通过类
获取所有方法:
Class<?> demo = Class.forName("Reflect.Demo");
Method[] methods = Demo.getDeclaredMethods();
获取所有属性:
Class<?> demo = Class.forName("Reflect.Demo");
Field[] fields =demo.getDeclaredFields();
1、Connection 用来与数据库建立连接
2、Statement 用来执行sql语句
3、ResultSet 用于接收结果集
4、PreparedStatement 是Statement子接口,执行sql语句,预编译,防止sql注入,安全性高
Statement 接口提供了三种执行 SQL 语句的方法:executeQuery、executeUpdate 和 execute。使用哪一个方法由 SQL 语句所产生的内容决定。
1.方法executeQuery
用于产生单个结果集的语句,例如 SELECT 语句。 被使用最多的执行 SQL 语句的方法是 executeQuery。这个方法被用来执行 SELECT 语句,它几乎是使用最多的 SQL 语句。
2.方法executeUpdate
用于执行 INSERT、UPDATE 或 DELETE 语句以及 SQL DDL(数据定义语言)语句,例如 CREATE TABLE 和 DROP TABLE。INSERT、UPDATE 或 DELETE 语句的效果是修改表中零行或多行中的一列或多列。executeUpdate 的返回值是一个整数,指示受影响的行数(即更新计数)。对于 CREATE TABLE 或 DROP TABLE 等不操作行的语句,executeUpdate 的返回值总为零。
使用executeUpdate方法是因为在 createTableCoffees 中的 SQL 语句是 DDL (数据定义语言)语句。创建表,改变表,删除表都是 DDL 语句的例子,要用 executeUpdate 方法来执行。你也可以从它的名字里看出,方法 executeUpdate 也被用于执行更新表 SQL 语句。实际上,相对于创建表来说,executeUpdate 用于更新表的时间更多,因为表只需要创建一次,但经常被更新。
3.方法execute:
用于执行返回多个结果集、多个更新计数或二者组合的语句。因为多数程序员不会需要该高级功能
execute方法应该仅在语句能返回多个ResultSet对象、多个更新计数或ResultSet对象与更新计数的组合时使用。当执行某个已存储过程或动态执行未知 SQL 字符串(即应用程序程序员在编译时未知)时,有可能出现多个结果的情况,尽管这种情况很少见。
因为方法 execute 处理非常规情况,所以获取其结果需要一些特殊处理并不足为怪。例如,假定已知某个过程返回两个结果集,则在使用方法 execute 执行该过程后,必须调用方法 getResultSet 获得第一个结果集,然后调用适当的 getXXX 方法获取其中的值。要获得第二个结果集,需要先调用 getMoreResults 方法,然后再调用 getResultSet 方法。如果已知某个过程返回两个更新计数,则首先调用方法 getUpdateCount,然后调用 getMoreResults,并再次调用 getUpdateCount。
对于不知道返回内容,则情况更为复杂。如果结果是 ResultSet 对象,则方法 execute 返回 true;如果结果是 Java int,则返回 false。如果返回 int,则意味着结果是更新计数或执行的语句是 DDL 命令。在调用方法 execute 之后要做的第一件事情是调用 getResultSet 或 getUpdateCount。调用方法 getResultSet 可以获得两个或多个 ResultSet 对象中第一个对象;或调用方法 getUpdateCount 可以获得两个或多个更新计数中第一个更新计数的内容。
当 SQL 语句的结果不是结果集时,则方法 getResultSet 将返回 null。这可能意味着结果是一个更新计数或没有其它结果。在这种情况下,判断 null 真正含义的唯一方法是调用方法 getUpdateCount,它将返回一个整数。这个整数为调用语句所影响的行数;如果为 -1 则表示结果是结果集或没有结果。如果方法 getResultSet 已返回 null(表示结果不是 ResultSet 对象),则返回值 -1 表示没有其它结果。也就是说,当下列条件为真时表示没有结果(或没有其它结果):
((stmt.getResultSet() == null) && (stmt.getUpdateCount() == -1))
如果已经调用方法 getResultSet 并处理了它返回的 ResultSet 对象,则有必要调用方法 getMoreResults 以确定是否有其它结果集或更新计数。如果 getMoreResults 返回 true,则需要再次调用 getResultSet 来检索下一个结果集。如上所述,如果 getResultSet 返回 null,则需要调用 getUpdateCount 来检查 null 是表示结果为更新计数还是表示没有其它结果。
当 getMoreResults 返回 false 时,它表示该 SQL 语句返回一个更新计数或没有其它结果。因此需要调用方法 getUpdateCount 来检查它是哪一种情况。在这种情况下,当下列条件为真时表示没有其它结果:
((stmt.getMoreResults() == false) && (stmt.getUpdateCount() == -1))
下面的代码演示了一种方法用来确认已访问调用方法 execute 所产生的全部结果集和更新计数:
stmt.execute(queryStringWithUnknownResults);
while (true) {
int rowCount = stmt.getUpdateCount();
if (rowCount > 0) { // 它是更新计数
System.out.println("Rows changed = " + count);
stmt.getMoreResults();
continue;
}
if (rowCount == 0) { // DDL 命令或 0 个更新
System.out.println(" No rows changed or statement was DDL
command");
stmt.getMoreResults();
continue;
}
// 执行到这里,证明有一个结果集
// 或没有其它结果
ResultSet rs = stmt.getResultSet;
if (rs != null) {
. . . // 使用元数据获得关于结果集列的信息
while (rs.next()) {
. . . // 处理结果
stmt.getMoreResults();
continue;
}
break; // 没有其它结果
1、使用Statement批量处理
public static void batchStatement() {
Connection conn = null;
Statement sta = null;
try {
conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
sta = conn.createStatement();
for(int i=1; i<=10000; i++) {
String sql = "insert into t_emp (f_id, f_no, f_name, f_sex, f_salary, f_birthday, f_departId) values (t_emp_seq.nextval, '" + i +"', '葫芦娃" + i + "', '男', " + i + ", to_date('2016-09-05','yyyy-MM-dd'), 1)";
//把sql语句加入批处理
sta.addBatch(sql);
}
//统一执行批处理的sql语句
int[] rows = sta.executeBatch();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
sta.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
2、使用PreparedStatement批量处理
public static void batchPreparedStatement() {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
ps = conn.prepareStatement("insert into t_emp (f_id, f_no, f_name, f_sex, f_salary, f_birthday, f_departId) values (t_emp_seq.nextval, ?,?,?,?,?,?)");
for(int i=1; i<=10000; i++) {
ps.setString(1, String.valueOf(10 + i));
ps.setString(2, "奥特曼" + i);
ps.setString(3, "女");
ps.setDouble(4, i);
ps.setTimestamp(5, new Timestamp(System.currentTimeMillis()));
ps.setInt(6, 2);
//加入批处理
ps.addBatch();
}
//统一执行批处理的sql语句
int[] rows = ps.executeBatch();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
ps.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Json是一种字符串数据格式,一般用于数据传输格式。
json字符串中[]对应JSONArray, {}对应JSONObject
(1).可读性方面。
JSON和XML的数据可读性基本相同,JSON和XML的可读性可谓不相上下,一边是建议的语法,一边是规范的标签形式,XML可读性较好些。
(2).可扩展性方面。
XML天生有很好的扩展性,JSON当然也有,没有什么是XML能扩展,JSON不能的。
(3).编码难度方面。
XML有丰富的编码工具,比如Dom4j、JDom等,JSON也有json.org提供的工具,但是JSON的编码明显比XML容易许多,即使不借助工具也能写出JSON的代码,可是要写好XML就不太容易了。
(4).解码难度方面。
XML的解析得考虑子节点父节点,让人头昏眼花,而JSON的解析难度几乎为0。这一点XML输的真是没话说。
(5).流行度方面。
XML已经被业界广泛的使用,而JSON才刚刚开始,但是在Ajax这个特定的领域,未来的发展一定是XML让位于JSON。到时Ajax应该变成Ajaj(Asynchronous Javascript and JSON)了。
(6).解析手段方面。
JSON和XML同样拥有丰富的解析手段。
(7).数据体积方面。
JSON相对于XML来讲,数据的体积小,传递的速度更快些。
(8).数据交互方面。
JSON与JavaScript的交互更加方便,更容易解析处理,更好的数据交互。
(9).数据描述方面。
JSON对数据的描述性比XML较差。
(10).传输速度方面。
JSON的速度要远远快于XML
1)设计上的区别:XML用来存储数据,重点在于数据本身,HTML用来定义数据,重在数据的显示模式。
2)XML可扩展性强,因为他本身就是可拓展性标记语言,课创建个性化的标记语言,提供更多数据操作。
3)XML语法比HTML严格。
4)起始标签和结束标签要匹配
5)嵌套标签不能相互嵌套
6)区分大小写
7)XML属性必须放在引号中,HTML可有可无。
8)XML必须有相应值,但HTML可以有不带属性的属性名。
1)两种形式 dtd schema。
2)本质区别:schema本身是xml的,可以被XML解析器解析(这也是从DTD上发展schema的根本目的),
1、当浏览器发送一个请求,会到达web服务器tomcat,tomcat会接收请求
2、tomcat会根据项目中的web.xml配置,判断请求是否符合Struts2核心过滤器(StrutsPrepareAndExecuteFilter)的过滤路径,如果符合请求会交给Struts2处理
3、struts核心过滤器会根据请求URL匹配struts.xml中package标签的namespace和action标签的name,请求URL=package(namespace) + action(name)
4、如果匹配上,在action标签的class属性找到相应的Action类,Action类是多例的,默认执行execute方法
5、execute方法执行完毕,会返回一个字符串,根据字符串内容匹配action标签下的result标签name属性
6、根据result标签配置的页面路径,进行页面跳转,页面内容呈现给用户
1、过滤器依赖于 Servlet 容器,而拦截器不依赖于 Servlet 容器。
2、Struts2 拦截器只能对 Action 请求起作用,而过滤器则可以对几乎所有请求起作用。
3、拦截器可以访问 Action 上下文(ActionContext)、值栈里的对象(ValueStack),而过滤器不能.
4、在 Action 的生命周期中,拦截器可以多次调用,而过滤器只能在容器初始化时被调用一次
1、 struts-default 包是 struts2 内置的,它定义了 struts2 内部的众多拦截器和 Result 类型,而 Struts2 很多核心的功能都是通过这些内置的拦截器实现,如:从请求中把请求参数封装到 action、文件上传和数据验证等等都是通过拦截器实现的。当包继承了 struts-default 包才能使用 struts2 为我们提供的这些功能。
2、struts-default 包 是 在 struts-default.xml 中 定 义 ,struts-default.xml 也是 Struts2
默认配置文件。 Struts2 每次都会自动加载 struts-default.xml 文件。
3、通常每个包都应该继承 struts-default 包
每个拦截器都是需要实现 Interceptor 接口
1、在web服务器启动时实例化Interceptor接口实现类与init方法
2、intercept(ActionInvocation invocation):每拦截一个动作请求,该方法就会被调用一次;
3、当Tomcat关闭时,调用destroy方法,在拦截器被销毁之前被调用;
4、Interceptor接口实现类构造方法与init方法,至始至终执行一次,Interceptor对象是单实例的,destroy方法也执行一次
dispatcher action转发到jsp 默认
redirect action重定向到jsp
chain action转发到action
redirectAction action重定向到action
json action传递json字符串到页面 ajax
stream 流的形式,上传下载
1、StrutsPrepareAndExecuteFilter,整个struts2的调度中心,它对请求进行过滤并决定struts2是否出来该请求。
2、Strtus.xml:struts2的应用配置文件,它负责配置系统中用到的action
3、Action:strtus2的动作执行单元实际处理用户的请求,封装业务所需的数据
4、Result:action运行后要转向下一个资源,可以是视图也可以说其他的action
5、interceptor:拦截器用于拦截action前后业务
6、Struts标签:用于页面上遍历后台传过来的数据
1、ActionContext是struts2框架中的,当前的Action的上下文环境,通过ActionContext可以获取到request、session、ServletContext等与Action有关的对象的引用;
2、ServletContext是域对象,一个web应用中只有一个ServletContext,生命周期伴随整个web应用,jsp九大内置对象中的application对象;
3、pageContext是JSP中的最重要的一个内置对象,可以通过pageContext获取其他域对象的应用,同时它是一个域对象,作用范围只针对当前页面,当前页面结束时,pageContext销毁, 生命周期是JSP四个域对象中最小的。
1、 name 包名称,在 struts2 的配置文件中,包名不能重复,name 并不是真正包名,只是为了管理 Action
2.、namespace 和 的 name 属性,决定 Action 的访问路径 (以/开始 )
3.、extends 继承哪个包,通常开发中继承 struts-default 包 (struts-default 包 在 struts-default.xml 中定义 )【可以使用包中默认的拦截器和结果集】
4、abstract:表示一个package是否为抽象package,设置为true表示为抽象package,不能配置Action,一般用于设置Struts2全局配置
1、属性接收
把input框的name属性作为Action类中属性,并且生成set与get方法,就可以接收表单参数
2、使用DynamicModel(动态模型)接收参数
a、把input框的name属性封装成一个类中的属性
b、在Action类中把封装好的类作为全局属性,并且生成set与get方法
c、在input框name属性中加上对象.属性名称
3、使用ModelDriven模型驱动
a、action类实现ModelDriven接口,重写getModel()方法,返回参数对象
b、在input框name属性中不需要加上对象.,参数类也不需要生成set与get方法
HttpServletRequest request =ServletActionContext.getRequest();
HttpServletResponse response =ServletActionContext.getResponse();
HttpSession session= request.getSession();
ServletContext servletContext=ServletActionContext.getServletContext();
MVC开发模式就是把视图代码与业务逻辑代码分离,使用控制层连接业务逻辑层与视图层
Model:项目中的业务逻辑,(业务领域),通常用JavaBean实现
View:项目中的展现,通常用jsp实现
Controller:项目中的控制层,通常用servlet实现
优点:
1、项目结构清晰,利于后期维护
2、低偶尔、高内聚
3、业务逻辑清晰,代码重用性强
缺点:
MVC项目层次结构比较多,增加开发工作量,所以开发效率慢,不适合小型项目
Struts在MVC中主要担任C角色,struts标签库担任V的角色
客户端初始化一个指向Servlet容器(例如Tomcat)的请求
这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin);
接着StrutsPrepareAndExecuteFilter被调用,StrutsPrepareAndExecuteFilter询问ActionMapper来决定这个请求是否需要调用某个Action;
如果ActionMapper决定需要调用某个Action,StrutsPrepareAndExecuteFilter把请求的处理交给ActionProxy;
ActionProxy通过ConfigurationManager询问框架的配置文件,找到需要调用的Action类;
ActionProxy创建一个ActionInvocation的实例。
ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。
一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。
返回结果通常是(但不总是,也可能是另外的一个Action链)一个需要被表示的JSP或者FreeMarker的模版。在表示的过程中可以使用Struts2框架中继承的标签。在这个过程中需要涉及到ActionMapper。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CbSk1xY4-1595208364305)(C:\Users\Administrator\Desktop\范本\20200622面试题\新建文件夹\新建文件夹\clip_image007.jpg)]
1、Action类中执行方法后返回字符串,会给拦截器类中的ActionInvocation,
2、在由拦截器类中intercept方法返回给了StrutsPrepareAndExecuteFilter核心过滤器
3、核心过滤器通过访问ActionMapper对象,使用字符串匹配到Result标签
1、使用感叹号调用其他方法
a、在struts.xml中开启动态方法调用常量
<constant name="struts.enable.DynamicMethodInvocation" value="true" />
b、在访问URL后面!方法名.后缀名,比如:web/dynamic!add.do
缺点:安全性低,暴露类中的方法
2、一个方法对应一个action标签,struts.xml配置如下:
<action name="" class="类的完整路径" method="方法名">action>
3、通配符
<package name="common" namespace="/" extends="struts-default">
<action name="*_*" class="com.tencent.action.{1}Action" method="{2}">
<result name="success">/{1}_{2}.jspresult>
action>
package>
注:通配符不推荐使用
ORM映射:Object Relational Mapping
* O:面向对象领域的Object(JavaBean对象)
* R:关系数据库领域的Relational(表的结构)
* M:映射Mapping(XML的配置文件)
Hibernate是一个开放源代码的对象关系映射(ORM)框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。
简单的来说 对象(java中的对象)和数据库(关系型数据库)中的表进行的对应类名-表名
属性-字段每个对象对应一条数据库里的记录 多个对象则返回一个集合
对JDBC访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代码。
Hibernate是一个基于JDBC的主流持久化框架,是一个优秀的ORM实现。他很大程度的简化DAO层的编码工作
Hibernate的原理是使用Java反射机制,自动完成ORM. Hibernate消耗的资源较多,包含的功能很全面,学习的复杂度较大,适合于开发大型企业应用,是一个重量级框架。
Hibernate映射的灵活性很出色。它支持各种关系数据库,从一对一到多对多的各种复杂关系。
Hibernate属于全自动,Mybatis属于半自动,Jdbc属于手动,
Hibernate的ORM是通过映射文件(注解)直接映射了对象和表的关系, 允许开发者直接操作“对象”达到操纵“表”的效果,故而不用关心表的结构。这些操作全部由Hibernate通过ORM来帮助我们自动的完成(如生成JDBC想要执行的SQL)。故而称为全自动配置
Mybatis的ORM是通过映射文件映射接口方法和SQL语句的对应关系。开发者需要编写动态SQL脚本来完成配置映射,在调用的时候 Mybatis会自动的将这种映射转换为对应的JDBC SQL语句。故而称为半自动。
从开发效率上讲hibernate较高,Mybatis居中,jdbc较低,
从执行效率上讲hibernate较低,Mybatis居中,jdbc较高,
因为jdbc是手工写sql语句,程序员对sql的控制能力更大,可以根据业务需要进行优化,
而Mybatis虽然也可以对sql进行优化,但是他里面将resultset封装为实体的过程中采用了反射机制所以一定程度上影响了性能,
而hibernate因为高度封装所以开发效率相对较高,但正因为这个原因,所以程序员在对sql语句的控制和优化方面相对比较弱,而且在将resultset封装成实体的过程中也采用了反射机制,所以在性能方面较低。
总而言之,并没有最好的框架 。hibernate功能强大 适合企业级开发 jdbc执行的效率高,Mybatis介于二者之间。需要根据项目的实际情况选择对应的持久层框架。
原理:
1)configuration类读取并解析hibernate.cfg.xml配置文件
2)读取解析映射信息,创建sessionFactory
3)打开session
4)创建事务
5)持久化操作
6)提交事务
8)关闭session
9)整个应用停止,关闭sessionFactory
1 .Configuration类的作用是对Hibernate进行配置,以及对它进行启动。(加载 hibernate.cfg.xml)并创建一个SessionFactory对象。
2 .SessionFactory接口
SessionFactory接口负责初始化Hibernate。它充当数据存储源的代理,并负责创建 Session对象。SessionFactory是线程安全的。
3 .Session接口
Session(会话)接口是Hibernate应用使用的主要接口。
Session接口负责执行被持久化对象的CRUD操作(增删改查)。Session对象是非线程安全的。
Session 相当于jdbc的connection
4 .Query与Criteria接口
总之Query和Criteria接口负责执行各种数据库查询。它可以使用HQL语句或SQL 语句两种表达方式。
5 .Transaction接口
Transaction(事务)接口是一个可选的API。负责操作相关的事务。
Hibernate框架中,使用session对象开启事务,所以需要来传递session对象,框架提供了ThreadLocal的方式来绑定当前线程下的session,配置如下
1、需要在hibernate.cfg.xml的配置文件中提供配置
<property name="hibernate.current_session_context_class">threadproperty>
2、通过SessionFactory对象获取当前线程下的session
public static Session getCurrentSession(){
return factory.getCurrentSession();//factory为SessionFactory对象
}
openSession和getCurrentSession的区别如下:
1、openSession 每一次获得的是一个全新的session对象,而getCurrentSession获得的是与当前线程绑定的session对象
2、openSession不需要配置,而getCurrentSession需要配置(配置方式如上)
3、openSession需要手动关闭,而getCurrentSession系统自动关闭
4、Session是线程不同步的,要保证线程安全就要使用getCurrentSession
自然主键:对象本身的一个属性.例如:创建一个人员表,每个人都有一个身份证号.(唯一的)使用身份证号作为表的主键.自然主键.(开发中不会使用这种方式)
代理主键:不是对象本身的一个属性.例如:创建一个人员表,为每个人员单独创建一个字段.用这个字段作为主键.代理主键.(开发中推荐使用这种方式)
\1. increment:适用于short,int,long作为主键.不是使用的数据库自动增长机制.
Hibernate中提供的一种增长机制.
先进行查询 :select max(id) from user;
再进行插入 :获得最大值+1作为新的记录的主键.
问题:不能在集群环境下或者有并发访问的情况下使用.
\2. identity:适用于short,int,long作为主键。但是这个必须使用在有自动增长数据库中.采用的是数据库底层的自动增长机制.
底层使用的是数据库的自动增长(auto_increment).像Oracle数据库没有自动增长.
\3. sequence:适用于short,int,long作为主键.底层使用的是序列的增长方式.
Oracle数据库底层没有自动增长,想自动增长需要使用序列.
\4. uuid:适用于char,varchar类型的作为主键.
利用jdk中的一个UUID.randomUUID()使用随机的字符串作为主键.
\5. native:本地策略.根据底层的数据库不同,自动选择适用于该种数据库的生成策略.(short,int,long)
如果底层使用的MySQL数据库:相当于identity.
如果底层使用Oracle数据库:相当于sequence.(使用hibernate_sequence.nextval)
\6. assigned:主键的生成不用Hibernate管理了.必须手动设置主键.
\1. 唯一标识OID的检索方式
session.get(对象.class,OID)
\2. 对象的导航的方式
\3. HQL的检索方式
Hibernate Query Language – Hibernate的查询语言
\4. QBC的检索方式
Query By Criteria – 条件查询
\5. SQL检索方式
本地的SQL检索
当Hibernate在查询数据的时候,数据并没有存在与内存中,当程序真正对数据的操作时,对象才存在与内存中,就实现了延迟加载,节省了服务器的内存开销,从而提高了服务器的性能。
原理是利用反射中的动态代理方式,返回的是代理对象 对象中只保存了查询的一个标识。当真正需要使用的时候 通过该标识去发送SQL的查询请求。
可以通过添加lazy=fasle属性来开启延迟加载.同时fetch属性不可以配置为join 并且查询方法(如 load 支持 get 不支持)必须支持lazy属性才可以。
Hibernate中怎样实现类之间的关系?(如:一对多、多对多的关系)
类与类之间的关系主要体现在表与表之间的关系进行操作,它们都是对对象进行操作,我们程序中把所有的表与类都映射在一起,它们通过配置文件中的many-to-one、one-to-many、many-to-many。
使用双向一对多关联,不使用单向一对多
灵活使用单向一对多
不使用一对一,用多对一取代
配置对象缓存,不适用集合缓存
一对多集合使用bag,多对多使用set
继承类使用显式多态
表字段要少,表关联不要怕多,有二级缓存。
加载方式:
load为延迟加载(返回的是一个只有id属性的代理对象,只有使用该对象属性时,才发出sql语句);
get为立即加载(返回真实对象,执行时,会立即向数据库发出sql语句)
返回结果:
load检索不到记录时,会抛ObjectNotFoundException异常
get检索不到记录时,会返回null
Transient(临时) 瞬时态
new 一个初始化对象后,并没有在数据库里保存数据,处于临时状态;
Persistent(持久化)
当执行save()方法,调用session.close()方法之前,内存中的对象与数据库有 对应关系处于持久化状态;
Detached(脱管/游离)
当执行session.close()之后,处于脱管状态;
状态的转换
处于托管状态下,调用update()方法后,转换为持久化状态;
在持久化状态下,执行delete()方法后,转换为临时状态;
在未初始化对象之前,调用get(),load(),find(),iterate()之后,直接进入持久化状态。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yWFvbotz-1595208364335)(C:\Users\Administrator\Desktop\范本\20200622面试题\新建文件夹\新建文件夹\clip_image009.jpg)]
注意:持久态对象有自动更新数据库的能力!!!
1、什么是缓存?
其实就是一块内存空间,将数据源(数据库或者文件)中的数据存放到缓存中.再次获取的时候 ,直接从缓存中获取而不需要再次从数据源中读取.可以提升程序的性能!
2、Hibernate框架提供了两种缓存
一级缓存 – 自带的不可卸载的(默认就启用了一级缓存).一级缓存的生命周期与session一致.一级缓存称为session级别的缓存.其中get,load,iterate都会使用一级缓存,一级缓存缓存的是对象
二级缓存 – 默认没有开启,需要手动配置才可以使用的.二级缓存可以在多个session中共享数据(可以跨session,被多个session共享),二级缓存称为是sessionFactory级别的缓存.
3、控制session的一级缓存
session.clear() – 清空缓存。
session.evict(Object entity) – 从一级缓存中清除指定的实体对象。
session.flush() – 刷新缓存
\1. 如果不考虑隔离性,也会产生写入数据的问题,这一类的问题叫丢失更新的问题。
\2. 例如:两个事务同时对某一条记录做修改,就会引发丢失更新的问题。
* A事务和B事务同时获取到一条数据,同时再做修改
* 如果A事务修改完成后,提交了事务
* B事务修改完成后,不管是提交还是回滚,如果不做处理,都会对数据产生影响
\3. 解决方案有两种
悲观锁
采用的是数据库提供的一种锁机制,如果采用做了这种机制,在SQL语句的后面添加 for update 子句
当A事务在操作该条记录时,会把该条记录锁起来,其他事务是不能操作这条记录的。
只有当A事务提交后,锁释放了,其他事务才能操作该条记录
乐观锁
采用版本号的机制来解决的。会给表结构添加一个字段version=0,默认值是0,当A事务在操作完该条记录,提交事务时,会先检查版本号,如果发生版本号的值相同时,才可以提交事务。同时会更新版本号version=1,当B事务操作完该条记录时,提交事务时,会先检查版本号,如果发现版本不同时,程序会出现错误。
\4. 使用Hibernate框架解决丢失更新的问题
* 悲观锁
* 使用session.get(Customer.class, 1,LockMode.UPGRADE); 方法
* 乐观锁
* 1.在对应的JavaBean中添加一个属性,名称可以是任意的。例如:private Integer version; 提供get和set方法
* 2.在映射的配置文件中,提供标签即可。
none – 不使用级联
save-update – 级联保存或更新
delete – 级联删除
all – 全部关联
cascade用来级联操作(保存、修改和删除)
inverse用来维护外键的 将控制权交给关系的另外一边来处理
解决双向关联的时候数据操作的时候N+1的问题
fetch的取值 – 控制SQL语句生成的格式
select – 默认值.发送查询语句
join – 连接查询. join.lazy就失效了
subselect – 子查询.发送一条子查询查询其关联对象.
lazy的取值 – 查找关联对象的时候是否采用延迟!
true – 默认.延迟
false – 不延迟
extra – 极其懒惰 在使用聚合查询的时候如果不配置就会
执行多条数据检索
IOC:Inverse of Control 控制反转的概念,就是将原本在程序中手动创建UserService对象 的控制权,交由Spring框架管理,简单说,就是创建UserService对象控制权被反转到了 Spring框架,IOC主要的目的就是用来解耦;
DI:Dependency Injection 依赖注入,在Spring框架负责创建Bean对象时,动态的将依 赖 对象注入到Bean组件;
IOC和DI的区别?
IOC 控制反转,指将对象的创建权,反转到Spring容器;
DI 依赖注入,指Spring创建对象的过程中,将对象依赖属性通过配置进行注入;
ApplicationContet接口继承BeanFactory接口,Spring核心工厂是BeanFactory,BeanFactory 采取延迟加载,第一次getBean时才会初始化Bean, ApplicationContext是会在加载配置文 件时初始化Bean。
1)使用类构造器实例化
<bean id="bean1" class="cn.ztkj.spring.Bean1">bean>
2)使用静态工厂方法实例化(简单工厂模式)
bean>
3)使用实例工厂方法实例化(工厂方法模式)
<bean id="bean3Factory" class="cn.ztkj.spring.Bean3Factory">bean>
<bean id="bean3" factory-bean="bean3Factory" factory-method="getBean3">bean>
当一个bean的作用域为singleton, IOC容器初始化后就会创建,那么Spring IOC容器
中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相
匹配,则只会返回bean的同一实例。
Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或 者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。根据经 验,对所有有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用
singleton作用域
在一次HTTP请求中,一个bean定义对应一个实例;即每次HTTP请求将会有各自的 bean实例,它们依据某个bean定义创建而成。该作用域仅在基于web的Spring 的 ApplicationContext情形下有效。
在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的
Spring ApplicationContext情形下有效。
在一个全局的HTTP Session中,一个bean定义对应一个实例。典型情况下,仅在使用 portlet context的时候有效。该作用域仅在基于web的Spring ApplicationContext情形 下有效。
1)构造器注入,通过元素完成注入
2)setter方法注入, 通过元素完成注入[开发中常用方式]
3)实例工厂注入
4)静态工厂注入
before:前置通知,在一个方法执行前被调用。
after:在方法执行之后调用的通知,无论方法执行是否成功。
after-returning:仅当方法成功完成后执行的通知。
after-throwing:在方法抛出异常退出时执行的通知。
around:在方法执行之前和之后调用的通知。
try{
前置通知:<aop:before>
环绕通知:<aop:around>前置部分
执行代码:调用目标方法
环绕通知:<aop:around>后置部分
后置通知:<aop:after-returning>
}catch(Exception) {
异常通知:<aop:after-throwing>
}finally {
最终通知:<aop:after>
}
背景介绍:
多个不具有继承关系的对象引入一个公共行为,例如日志,权限验证,事务等功能时,
只能在在每个对象里引用公共行为,这样做不便于维护,而且有大量重复代码。
AOP的出现弥补了OOP的这点不足。
换句话说,它有助于我们将不同的必要重复代码重构为不同的模块。
这给了我们的好处,我们可以在一个地方保持这些功能,而不是每次都写下来。
这种方法将产生更易于维护的代码,从而将业务逻辑从最混乱的因素中清除。
我们将这些功能分为不同的方面。
总结:AOP是一种编程思想(开发模式),在基于OOP基础之上更高级的一种编程思想,
把一系列通用业务提取出来,切入到任何一个面向对象的组件上
组成部分:
AOP主要由切面组件、切入点、通知类型组成。
作用:
一般用于做日志管理、事务管理、权限验证等功能。
1)Spring MVC:属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring框架提供了构建Web应用程序的全功能MVC模块。
2)Spring Task:是一个定时任务框架。应用于定时执行刷新Redis数据,定时执行删除过期数据
3)Spring Security:是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IOC,DI(控制反转Inversion of Control,DI:Dependency Injection依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能, 减少了为企业系统安全控制编写大量重复代码的工作。
4)Spring Data:是Spring的一个子项目,旨在统一和简化对各类型持久化存储,而不拘泥于是关系型数据库还是NoSQL数据存储。
5)SpringDataSolr:是一个搜索解决方案,基于Lucene的搜索服务器。应用于站内产品搜索
6)Spring Boot:是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。
7)Spring Cloud:是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。
8)Spring Data JPA:是一个可以作为一个独立的持久层技术框架
Spring定义了如下七种传播行为,这里以A业务和B业务之间如何传播事务为例说明:
1)PROPAGATION_REQUIRED:required,必须。默认值,A如果有事务,B将使用该事务;如果A没有事务,B将创建一个新的事务。
2)PROPAGATION_SUPPORTS:supports,支持。A如果有事务,B将使用该事务;如果A没有事务,B将以非事务执行。
3)PROPAGATION_MANDATORY:mandatory ,强制。A如果有事务,B将使用该事务;如果A没有事务,B将抛异常。
4)PROPAGATION_REQUIRES_NEW:requires_new,必须新的。如果A有事务,将A的事务挂起,B创建一个新的事务;如果A没有事务,B创建一个新的事务。
5)PROPAGATION_NOT_SUPPORTED:not_supported,不支持。如果A有事务,将A的事务挂起,B将以非事务执行;如果A没有事务,B将以非事务执行。
6)PROPAGATION_NEVER:never,从不。如果A有事务,B将抛异常;如果A没有事务,B将以非事务执行。
7)PROPAGATION_NESTED:nested,嵌套。A和B底层采用保存点机制,形成嵌套事务。
定义了一个事务可能受其他并发事务影响的程度,并发事务引起的问题:在典型的应用 程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务。并发虽然是必须 的,但可能会导致以下的问题。
的数据时。如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的。
查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个
并发事务在两次查询期间进行了更新。
了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一 个事务(T1)就会发现多了一些原本不存在的记录。
注意**:不可重复读重点是修改,**而幻读重点是新增或删除。
在Spring事务管理中,为我们定义了如下的隔离级别:
1)ISOLATION_DEFAULT:使用后端数据库默认的隔离级别;
2)ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取尚未提交的数据变 更,可能会导致脏读、幻读或不可重复读;
3)ISOLATION_READ_COMMITTED:允许读取并发事务已经提交的数据,可以阻止脏 读,但是幻读或不可重复读仍有可能发生;
4)ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据
是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生;
5)ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别,确保阻止
脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全
锁定事务相关的数据库表来实现的;
上面定义的隔离级别**,在 Spring 的 TransactionDefinition.class**中也分别用
常量 -1,0,1,2,4,8****表示。
IOC实现了工厂模式,ClassPathXmlApplicationContext通过读取applicationContext.xml 文件中的标签,把标签的类注入到IOC容器中,通过set方法或构造方法进 行注入对象与对象之间的关系,产生一个BeanFactory,BeanFactory通过getBean()方法获 取对象
Spring框架中使用到了大量的设计模式,下面列举了比较有代表性的:
1、代理模式—在AOP和remoting中被用的比较多。
2、单例模式—在spring配置文件中定义的bean默认为单例模式。
3、工厂模式—BeanFactory用来创建对象的实例。
4、原型模式。
MyBatis是一个可以自定义SQL、存储过程和高级映射的持久层框架。MyBatis是半自动ORM映射持久层框架,Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成。
MyBatis的缓存分为一级缓存和二级缓存,一级缓存放在session里面,默认就有,二级缓存放在它的命名空间里,默认是不打开的,使用二级缓存属性类需要实现Serializable序列化接口(可用来保存对象的状态),可在它的映射文件中配置
1)Mybatis动态sql可以让我们在Xml映射文件内,以标签的形式编写动态sql,完成逻辑判断和动态拼接sql的功能。
2)Mybatis提供了9种动态sql标签:trim|where|set|foreach|if|choose|when|otherwise|bind。
3)其执行原理为,使用OGNL从sql参数对象中计算表达式的值,根据表达式的值动态拼接sql,以此来完成动态sql的功能。
1)#{}是预编译处理,${}是字符串替换。
2)Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
3)Mybatis在处理 时 , 就 是 把 {}时,就是把 时,就是把{}替换成变量的值。
4)使用#{}可以有效的防止SQL注入,提高系统安全性。
Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以,称之为半自动ORM映射工具。
1)Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询,association标签与collection标签必须使用select属性。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false,aggressiveLazyLoading= true|false,
2)它的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。
1)Mybatis和hibernate不同,MyBatis不完全是一个ORM框架,因为MyBatis需要程序员自己编写Sql语句,不过mybatis可以通过XML或注解方式灵活配置要运行的sql语句,并将java对象和sql语句映射生成最终执行的sql,最后将sql执行的结果再映射生成java对象。
2)Mybatis学习门槛低,简单易学,程序员直接编写原生态sql,可严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,例如互联网软件、企业运营类软件等,因为这类软件需求变化频繁,一但需求变化要求成果输出迅速。但是灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数据库的软件则需要自定义多套sql映射文件,工作量大。
3)Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件(例如需求固定的定制化软件)如果用hibernate开发可以节省很多代码,提高效率。但是Hibernate的缺点是学习门槛高,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡,以及怎样用好Hibernate需要具有很强的经验和能力才行。
总之,按照用户的需求在有限的资源环境下只要能做出维护性、扩展性良好的软件架构都是好架构,所以框架只有适合才是最好。
1)MyBatis把sql语句从Java源程序中独立出来,放在单独的XML文件中编写,给程序的维护带来了很大便利。
2)MyBatis封装了底层JDBC API的调用细节,并能自动将结果集转换成Java Bean对象,大大简化了Java数据库编程的重复工作。
3)因为MyBatis需要程序员自己去编写sql语句,程序员可以结合数据库自身的特点灵活控制sql语句,因此能够实现比Hibernate等全自动orm框架更高的查询效率,能够完成复杂查询。
接口映射就是在MyBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定,我们直接调用接口方法就可以,这样比起原来了SqlSession提供的方法我们可以有更加灵活的选择和设置.
接口绑定有两种实现方式,一种是通过注解绑定,就是在接口的方法上面加上@Select@Update等注解里面包含Sql语句来绑定,另外一种就是通过xml里面写SQL来绑定,在这种情况下,要指定xml映射文件里面的namespace必须为接口的全路径名.
当Sql语句比较简单时候,用注解绑定;当SQL语句比较复杂时候,用xml绑定,一般用xml绑定的比较多
能,Mybatis不仅可以执行一对一、一对多的关联查询,还可以执行多对一,多对多的关联查询,多对一查询,其实就是一对一查询,只需要把selectOne()修改为selectList()即可;多对多查询,其实就是一对多查询,只需要把selectOne()修改为selectList()即可。
关联对象查询,有两种实现方式,一种是单独发送一个sql去查询关联对象,赋给主对象,然后返回主对象。另一种是使用嵌套查询,嵌套查询的含义为使用join查询,一部分列是A对象的属性值,另外一部分列是关联对象B的属性值,好处是只发一个sql查询,就可以把主对象和其关联对象查出来。
MyBatis里面的动态Sql一般是通过if节点来实现,通过OGNL语法来实现,但是如果要写的完整,必须配合where,trim节点,where节点是判断包含节点有内容就插入where,否则不插入,trim节点是用来判断如果动态语句是以and 或or开始,那么会自动把这个and或者or取掉。
第一种是使用标签,逐一定义列名和对象属性名之间的映射关系。
第二种是使用sql列的别名功能,将列别名书写为对象属性名,比如T_NAME AS NAME,对象属性名一般是name,小写,但是列名不区分大小写,Mybatis会忽略列名大小写,智能找到与之对应对象属性名,你甚至可以写成T_NAME AS NaMe,Mybatis一样可以正常工作。
有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。
1)通过在查询的sql语句中定义字段名的别名。
2)通过来映射字段名和实体类属性名的一一对应的关系。
1)在java中拼接通配符,通过#{}赋值
2)在Sql语句中拼接通配符 (不安全 会引起Sql注入)
3)使用动态SQL中的bind标签
<select id="selectUser" resultType="user" parameterType="user">
<bind name="pattern" value="'%' + username + '%'" />
select id,sex,age,username,password
from user
where username LIKE #{pattern}
select>
配置文件设置usegeneratedkeys 为true
1)直接在方法中传递参数,xml文件用#{0} #{1}来获取
2)使用 @param 注解:这样可以直接在xml文件中通过#{name}来获取
1)类的名字和数据库相同时,可以直接设置resultType参数为Pojo类
2)若不同,需要设置resultMap 将结果名字和Pojo名字进行转换
1)Mapper接口方法名和mapper.xml中定义的每个sql的id相同,所有接口方法不能重载
2)Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
3)Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
4)Mapper.xml文件中的namespace即是mapper接口的类路径。
1)客户端发送一个请求到服务器,tomcat会接收这个请求
2)tomcat会读取项目中的web.xml中的配置
3)判断请求是否符合SpringMVC 核心Servlet(DispatcherServlet)的请求路径
4)如果符合会把请求提交到SpringMVC的前端控制器DispatcherServlet
5)根据请求的URL由DispatcherServlet控制器查询一个或多个处理器映射器HandlerMapping,找到处理请求的处理器Controller
6)DispatcherServlet通过处理器适配器HandlerAdapter调用相应的Controller
7)Controller调用业务逻辑处理后,返回ModelAndView
8)DispatcherServlet查询一个或多个ViewResolver视图解析器,找到ModelAndView指定的视图
9)视图负责将结果显示到客户端
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y8y939nT-1595208364338)(C:\Users\Administrator\Desktop\范本\20200622面试题\新建文件夹\新建文件夹\clip_image011.gif)]
1)DispatcherServlet:SpringMVC前端控制器,是一个Servlet(不需要程序员编写)在web.xml中配置,org.springframework.web.servlet.DispatcherServlet
2)HandlerMapping:处理器映射器,是用来匹配请求对应的处理器(不需要程序员编写)
3)HandlerAdapter:处理器适配器,是用来执行处理器,并将处理器执行结果返回给DispatcherServlet(不需要程序员编写)
4)Controller:处理器处理用户请求,相当于Struts2中的Action(需要程序员编写)
5)ViewResolver:视图解析器,是将处理器返回逻辑视图(视图名)解析成一个真正的视图(不需要程序员编写),在springmvc的配置文件中配置
org.springframework.web.servlet.view.InternalResourceViewResolver
6)ModelAndView:用来存储返回页面数据和返回的视图路径的(不需要程序员编写)
7)Springmvc的配置文件:(需要程序员编写)
注解配置(扫描注解的路径,即哪些包里的类使用了注解),处理器映射器,处理解析器,视图解析器,Multipart类型数据解析器(上传文件), 全局异常处理类,拦截器
SpringMVC获取页面传递的数据也叫SpringMVC的参数绑定,方式如下:
1)绑定的是Java常用数据类型(基本数据类型,String)
a. 处理器Controller方法的参数名和页面input的name的值一致
b. 使用@RequestParam给处理器Controller方法的参数取别名,该别名需要和页
面input的name的值一致
c. @PathVariable,是用来对指定请求的URL路径里面的变量 , 通过
@PathVariable 可以将 URL 中占位符参数绑定到控制器处理方法的入参中:
@RequestMapping(value="/updateView/{id}")
public ModelAndView updateView(@PathVariable(value = "id") String id){
ModelAndView mav = new ModelAndView("demo/personUpdate");
Map<String,Object> map = new HashMap<String,Object>();
map.put("id", id);
Person person = personService.findPersonById(map);
mav.addObject("person", person);
return mav;
}
d. 使用HttpServletRequest,HttpServletSession,等
2**)绑定的是POJO类型数据**
页面上的input的name与自定义类的属性名一致即可,在处理器Controller的方法中直接使用该类的对象接收数据
3)特殊类型的数据,如java.util.Date类型
a. 在时间类型的属性上使用@DateTimeFormat(pattern=“yyyy-MM-dd”)
yyyy-MM-dd:是转换格式,必须跟页面传递过来的时间格式一致
b. 自定义一个转换类,然后在SpringMVC的配置文件中配置该转换类
c. 使用@InitBinder
在需要转换时间格式的处理器中,添加@InitBinder
4)绑定集合数组类型的参数
需要用到VO类,在VO类中定义集合属性,然后在页面上使用该集合属性即可,
如果集合为List,则页面上使用集合属性与下标
public class UserVo {
private List userList;
public List getUserList() {
return userList;
}
public void setUserList(List userList){
this.userList = userList;`
}
}
页面:
Controller:
public ModelAndView updateView(UserVo usrVo){
ModelAndView mav = new ModelAndView("demo/personUpdate");
Map<String,Object> map = new HashMap<String,Object>();
map.put("id", id);
Person person = personService.findPersonById(map);
mav.addObject("person", person);
return mav;
}
@Controller: 标注该类是一个处理器类
@RestController: 标注该类是一个处理器类,并且跟页面交互JSON数据时不用 @RequestBody和@RepsonseBody注解
@RequestMapping: 请求映射,将请求地址与处理器类及其方法映射起来
a.写在方法上:将请求地址与处理器类的方法映射起来
b.写在类上:窄化请求边缘
c.限定访问方法,可以限定请求形式get,post等的访问权限
在方法前面写@RequestMapping(value="/test1", method={RequestMethod.POST, RequestMethod.GET})
value: 指的是请求路径
method: 指的是允许访问的方式,是一个数组可以允许多个或者一个
@RequestParam:用在SpringMVC参数绑定中,给处理器Controller中的方法的参数取名
@ResponseBody: 将处理器返回的数据转换成JSON对象返回页面
@RequestBody: 将页面传递过来的JSON数据转换成JAVA对象,
页面传递的一定是JSON数据
要设置contentType为"application/json;charset=utf-8"
@PathVariable,是用来对指定请求的URL路径里面的变量 , 通过
@PathVariable 可以将 URL 中占位符参数绑定到控制器处理方法的入参中
@Service: 标注一个类为Service层的类
@Repository:标注一个类为DAO层的类
@Autowired:按照类型注入对象
@Transactional:用来标注执行该方法时如何添加事务
Spring Web MVC的处理器拦截器(如无特殊说明,下文所说的拦截器即处理器拦截器)
类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理
SpringMVC拦截器的实现:
1)需要自定义一个拦截器类,需要实现HandlerInterceptor
实现拦截器接口的方法
preHandle:预处理回调方法,实现处理器的预处理(如登录检查),第三个参 数为响应的处理器;
返回值:true表示继续流程(如调用下一个拦截器或处理器);
false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理 器,此时我们需要通过response来产生响应;
postHandle:后处理回调方法,实现处理器的后处理(但在渲染视图之前), 此时我们可以通过ModelAndView(模型和视图对象)对模型数据进 行处理或对视图进行处理,ModelAndView也可能为null。
afterCompletion:整个请求处理完毕回调方法,即在视图渲染完毕时回调,如 性能监控中我们可以在此记录结束时间并输出消耗时间,还可以进行一些资源 清理,类似于try-catch-finally中的finally,但仅调用处理器执行链中preHandle 返回true的拦截器的afterCompletion。
有时候我们可能只需要实现三个回调方法中的某一个,如果实现HandlerInterceptor 接口的话,三个方法必须实现,不管你需不需要,此时Spring提供了一个 HandlerInterceptorAdapter适配器(一种适配器设计模式的实现),允许我们只实 现 需要的回调方法。
2).在SpringMVC的配置文件配置需要拦截的路径
使用mvc:interceptor配置拦截器,
SpringMVC拦截器的应用场景:
1)日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等。
2)权限检查:如登录检测,进入处理器检测检测是否登录,如果没有直接返回到 登录页面;
3)性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理 器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间 (如果有反向代理,如apache可以自动记录);
4)通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续 流 程使用,还有如提取Locale、Theme信息等,只要是多个处理器都需要的 即可使 用拦截器实现。
5)OpenSessionInView:如Hibernate,在进入处理器打开Session,在完成后关闭Session。
…………本质也是AOP(面向切面编程),也就是说符合横切关注点的所有功能 都可以 放入拦截器实现。
1)转发到页面
默认方法
2)重定向到页面
视图名:redirect:/success.jsp
3)重定向到Handler
视图名:redirect:/sys/re.action
4)转发到Handler
视图名:forward:/sys/re.action
其中的第一个/表示系统的根目录
处理器的方法的返回值:
1)void
跳转全靠HttpServletRequest、HttpServletResponse,与Servlet无异
传值全靠HttpServletRequest,HttpServletSession
2)ModelAndView
传值:mav.addObject(key, value);
跳转:mav.setViewName(视图名);
3)String(代表视图名)
传值:model.addAttribute(key, value);
跳转:return 视图名;
SpringMVC默认是用Jackson包将JSON数据与对象进行转换
需要到如Jackson的JSON包
@RequestBody: 将视图页面传递过来的JSON串转换为Java对象,如果Controller上使 用的是@RestController注解则不需要使用@RequestBody
在请求时必须要指定传递的是JSON串,即设置contentType为application/json;charset=UTF-8
@ResponseBody: 将处理器中得到的Java对象转换为JSON串返回视图页面,如果 Controller上使用的是@RestController注解则不需要使用@ResponseBody
主要区别有以下3点:
区别1:
Struts2 的核心是基于一个Filter即StrutsPreparedAndExcuteFilter
SpringMvc的核心是基于一个Servlet即DispatcherServlet(前端控制器)
区别2:
Struts2是基于类开发的,传递的参数是通过类的属性传递(属性驱动和模型驱动), 所以只能设计成多例prototype
SpringMvc是基于类中的方法开发的,也就是一个url对应一个方法,传递参数是 传到方法的形参上面,所以既可以是单例模式也可以是多例模式singileton
区别3:
Struts2采用的是值栈存储请求以及响应数据,OGNL存取数据
SpringMvc采用request来解析请求内容,然后由其内部的getParameter给方法中 形参赋值,再把后台处理过的数据通过ModelAndView对象存储,Model存储数据, View存储返回的页面,再把对象通过request传输到页面去。
Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程,
它默认配置了很多框架的使用方式,就像maven整合了所有的jar包,spring boot整合了所有的框架
1)、快速创建独立运行的spring项目与主流框架集成
2)、使用嵌入式的servlet容器,应用无需打包成war包
3)、starters自动依赖与版本控制
4)、大量的自动配置,简化开发,也可修改默认值
5)、准生产环境的运行应用监控
6)、与云计算的天然集成
1)、核心文件:
Spring Boot 的核心配置文件是 application 和 bootstrap 配置文件
bootstrap (.yml 或者 .properties)
application (.yml 或者 .properties)。
2)、区别:
application 配置文件这个容易理解,主要用于 Spring Boot 项目的自动化配置。
bootstrap 配置文件有以下几个应用场景:
a、使用 Spring Cloud Config 配置中心时,这时需要在 bootstrap 配置文件中添加连接到配置中心的配置属性来加载外部配置中心的配置信息;
b、一些固定的不能被覆盖的属性;
c、一些加密/解密的场景;
bootstrap 由父ApplicationContext加载,比application优先加载
.properties 和 .yml,它们的区别主要是书写格式不同。
1).properties
app.user.name = javastack
2).yml
app:
user:
name: javastack
另外,.yml 格式不支持 @PropertySource 注解导入配置。
启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:
@SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。
@EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,
如关闭数据源自动配置功能:
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。
@ComponentScan:Spring组件扫描。
@SpringBootApplication = @Configuration + @EnableAutoConfiguration + @ComponentScan
1)继承spring-boot-starter-parent项目
2)导入spring-boot-dependencies项目依赖包
可以不需要,内置了 Tomcat/ Jetty 等容器。
1、打包用命令或者放到容器中运行
2、用 Maven/ Gradle 插件运行
3、直接执行 main 方法运行
1 main文件夹中目录结构
± javastack
± MyApplication.java
|
± customer
| ± Customer.java
| ± CustomerController.java
| ± CustomerService.java
| ± CustomerRepository.java
|
± order
± Order.java
± OrderController.java
± OrderService.java
± OrderRepository.java
这个目录结构是主流及推荐的做法,而在主入口类上加上 @SpringBootApplication 注解来开启 Spring Boot 的各项能力,如自动配置、组件扫描等。
2.resources文件夹中目录结构
1).static: 保存所有的静态资源,如JS,CSS,images;
2).templates: 保存所有的模板页面;(Spring Boot默认jar包使用嵌入式的Tomcat,默认不支持JSP页面),可以使用模板引擎(freemarker、thymeleaf)
3).application.properties:Spring Boot应用的配置文件;比如修改Spring Boot项目默认使用的端口号等等
Starters可以理解为启动器,它包含了一系列可以集成到应用里面的依赖包,你可以一站式集成 Spring 及其他技术,而不需要到处找示例代码和依赖包。如你想使用 Spring JPA 访问数据库,只要加入mybatis-spring-boot-starter 启动器依赖就能使用了。
Starters包含了许多项目中需要用到的依赖,它们能快速持续的运行,都是一系列得到支持的管理传递性依赖。
可以实现接口 ApplicationRunner 或者 CommandLineRunner,这两个接口实现方式一样,它们都只提供了一个 run 方法。
Spring Boot 支持 Java Util Logging, Log4j2, Lockback 作为日志框架,如果你使用 Starters 启动器,Spring Boot 将使用 Logback 作为默认日志框架
主要有两种方式:
Spring Loaded
Spring-boot-devtools
1)properties文件;
2)YAML文件;
3)系统环境变量;
4)命令行参数;
设计模式主要分三个类型:创建型、结构型和行为型。
其中创建型有:
一、Singleton,单例模式:保证一个类只有一个实例,并提供一个访问它的全局访问点
二、Abstract Factory,抽象工厂:提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们的具体类。
三、Factory Method,工厂方法:定义一个用于创建对象的接口,让子类决定实例化哪一个类,Factory Method使一个类的实例化延迟到了子类。
四、Builder,建造模式:将一个复杂对象的构建与他的表示相分离,使得同样的构建过程可以创建不同的表示。
五、Prototype,原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型来创建新的对象。
行为型有:
六、Iterator,迭代器模式:提供一个方法顺序访问一个聚合对象的各个元素,而又不需要暴露该对象的内部表示。
七、Observer,观察者模式:定义对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知自动更新。
八、Template Method,模板方法:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,TemplateMethod使得子类可以不改变一个算法的结构即可以重定义该算法得某些特定步骤。
九、Command,命令模式:将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化,对请求排队和记录请求日志,以及支持可撤销的操作。
十、State,状态模式:允许对象在其内部状态改变时改变他的行为。对象看起来似乎改变了他的类。
十一、Strategy,策略模式:定义一系列的算法,把他们一个个封装起来,并使他们可以互相替换,本模式使得算法可以独立于使用它们的客户。
十二、China of Responsibility,职责链模式:使多个对象都有机会处理请求,从而避免请求的送发者和接收者之间的耦合关系
十三、Mediator,中介者模式:用一个中介对象封装一些列的对象交互。
十四、Visitor,访问者模式:表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素类的前提下定义作用于这个元素的新操作。
十五、Interpreter,解释器模式:给定一个语言,定义他的文法的一个表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
十六、Memento,备忘录模式:在不破坏对象的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
结构型有:
十七、Composite,组合模式:将对象组合成树形结构以表示部分整体的关系,Composite使得用户对单个对象和组合对象的使用具有一致性。
十八、Facade,外观模式:为子系统中的一组接口提供一致的界面,fa?ade提供了一高层接口,这个接口使得子系统更容易使用。
十九、Proxy,代理模式:为其他对象提供一种代理以控制对这个对象的访问
二十、Adapter,适配器模式:将一类的接口转换成客户希望的另外一个接口,Adapter模式使得原本由于接口不兼容而不能一起工作那些类可以一起工作。
二十一、Decrator,装饰模式:动态地给一个对象增加一些额外的职责,就增加的功能来说,Decorator模式相比生成子类更加灵活。
二十二、Bridge,桥模式:将抽象部分与它的实现部分相分离,使他们可以独立的变化。
二十三、Flyweight,享元模式
这是组合设计模式。
我有很多个(假设10万个)数据要保存起来,以后还需要从保存的这些数据中检索是否存在某个数据,(我想说出二叉树的好处,该怎么说呢?那就是说别人的缺点),假如存在数组中,那么,碰巧要找的数字位于99999那个地方,那查找的速度将很慢,因为要从第1个依次往后取,取出来后进行比较。平衡二叉树(构建平衡二叉树需要先排序,我们这里就不作考虑了)可以很好地解决这个问题,但二叉树的遍历(前序,中序,后序)效率要比数组低很多,原理如下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VBE6qA5T-1595208364341)(C:\Users\Administrator\Desktop\范本\20200622面试题\新建文件夹\新建文件夹\clip_image013.jpg)]
代码如下:
package com.huawei.interview;
public class Node {
public int value;
public Node left;
public Node right;
public void store(int value)
{
if(value { if(left == null) { left = new Node(); left.value=value; } else { left.store(value); } } else if(value>this.value) { if(right == null) { right = new Node(); right.value=value; } else { right.store(value); } } } public boolean find(int value) { System.out.println("happen " + this.value); if(value == this.value) { return true; } else if(value>this.value) { if(right == null) return false; return right.find(value); }else { if(left == null) return false; return left.find(value); } } public void preList() { System.out.print(this.value + “,”); if(left!=null) left.preList(); if(right!=null) right.preList(); } public void middleList() { if(left!=null) left.preList(); System.out.print(this.value + “,”); if(right!=null) right.preList(); } public void afterList() { if(left!=null) left.preList(); if(right!=null) right.preList(); System.out.print(this.value + “,”); } public static void main(String [] args) { int [] data = new int[20]; for(int i=0;i { data[i] = (int)(Math.random()*100) + 1; System.out.print(data[i] + “,”); } System.out.println(); Node root = new Node(); root.value = data[0]; for(int i=1;i { root.store(data[i]); } root.find(data[19]); root.preList(); System.out.println(); root.middleList(); System.out.println(); root.afterList(); } } -----------------又一次临场写的代码--------------------------- import java.util.Arrays; import java.util.Iterator; public class Node { private Node left; private Node right; private int value; //private int num; public Node(int value){ this.value = value; } public void add(int value){ if(value > this.value) { if(right != null) right.add(value); else { Node node = new Node(value); right = node; } } else{ if(left != null) left.add(value); else { Node node = new Node(value); left = node; } } } public boolean find(int value){ if(value == this.value) return true; else if(value > this.value){ if(right == null) return false; else return right.find(value); }else{ if(left == null) return false; else return left.find(value); } } public void display(){ System.out.println(value); if(left != null) left.display(); if(right != null) right.display(); } /*public Iterator iterator(){ }*/ public static void main(String[] args){ int[] values = new int[8]; for(int i=0;i<8;i++){ int num = (int)(Math.random() * 15); //System.out.println(num); //if(Arrays.binarySearch(values, num)<0) if(!contains(values,num)) values[i] = num; else i–; } System.out.println(Arrays.toString(values)); Node root = new Node(values[0]); for(int i=1;i root.add(values[i]); } System.out.println(root.find(13)); root.display(); } public static boolean contains(int [] arr, int value){ int i = 0; for(;i if(arr[i] == value) return true; } return false; } package cn.itcast; import java.util.Date; public class A1 { public static void main(String [] args) { System.out.println(computeAge(8)); } public static int computeAge(int n) { if(n==1) return 10; return computeAge(n-1) + 2; } } public static void toBinary(int n,StringBuffer result) { if(n/2 != 0) toBinary(n/2,result); result.append(n%2); } public class QuickSort { /** * 快速排序 * @param strDate * @param left * @param right */ public void quickSort(String[] strDate,int left,int right){ String middle,tempDate; int i,j; i=left; j=right; middle=strDate[(i+j)/2]; do{ while(strDate[i].compareTo(middle)<0&& i i++; //找出左边比中间值大的数 while(strDate[j].compareTo(middle)>0&& j>left) j–; //找出右边比中间值小的数 if(i<=j){ //将左边大的数和右边小的数进行替换 tempDate=strDate[i]; strDate[i]=strDate[j]; strDate[j]=tempDate; i++; j–; } }while(i<=j); //当两者交错时停止 if(i quickSort(strDate,i,right);//从 } if(j>left){ quickSort(strDate,left,j); } } /** * @param args */ public static void main(String[] args){ String[] strVoid=new String[]{“11”,“66”,“22”,“0”,“55”,“22”,“0”,“32”}; QuickSort sort=new QuickSort(); sort.quickSort(strVoid,0,strVoid.length-1); for(int i=0;i System.out.println(strVoid[i]+" "); } } } 去零的代码: return sb.reverse().toString().replaceAll(“零[拾佰仟]”,“零”).replaceAll(“零+万”,“万”).replaceAll(“零+元”,“元”).replaceAll(“零+”,“零”); public class RenMingBi { /** * @param args add by zxx ,Nov 29, 2008 */ private static final char[] data = new char[]{ ‘零’,‘壹’,‘贰’,‘叁’,‘肆’,‘伍’,‘陆’,‘柒’,‘捌’,‘玖’ }; private static final char[] units = new char[]{ ‘元’,‘拾’,‘佰’,‘仟’,‘万’,‘拾’,‘佰’,‘仟’,‘亿’ }; public static void main(String[] args) { // TODO Auto-generated method stub System.out.println( convert(135689123)); } public static String convert(int money) { StringBuffer sbf = new StringBuffer(); int unit = 0; while(money!=0) { sbf.insert(0,units[unit++]); int number = money%10; sbf.insert(0, data[number]); money /= 10; } return sbf.toString(); } } 1,张三,28 2,李四,35 3,张三,28 4,王五,35 5,张三,28 6,李四,35 7,赵六,28 8,田七,35 程序代码如下(答题要博得用人单位的喜欢,包名用该公司,面试前就提前查好该公司的网址,如果查不到,现场问也是可以的。还要加上实现思路的注释): package com.huawei.interview; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.TreeSet; public class GetNameTest { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub //InputStream ips = GetNameTest.class.getResourceAsStream("/com/huawei/interview/info.txt"); //用上一行注释的代码和下一行的代码都可以,因为info.txt与GetNameTest类在同一包下面,所以,可以用下面的相对路径形式 Map results = new HashMap(); InputStream ips = GetNameTest.class.getResourceAsStream(“info.txt”); BufferedReader in = new BufferedReader(new InputStreamReader(ips)); String line = null; try { while((line=in.readLine())!=null) { dealLine(line,results); } sortResults(results); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } static class User { public String name; public Integer value; public User(String name,Integer value) { this.name = name; this.value = value; } @Override public boolean equals(Object obj) { // TODO Auto-generated method stub //下面的代码没有执行,说明往treeset中增加数据时,不会使用到equals方法。 boolean result = super.equals(obj); System.out.println(result); return result; } } private static void sortResults(Map results) { // TODO Auto-generated method stub TreeSet sortedResults = new TreeSet( new Comparator(){ public int compare(Object o1, Object o2) { // TODO Auto-generated method stub User user1 = (User)o1; User user2 = (User)o2; /*如果compareTo返回结果0,则认为两个对象相等,新的对象不会增加到集合中去 * 所以,不能直接用下面的代码,否则,那些个数相同的其他姓名就打印不出来。 * */ //return user1.value-user2.value; //return user1.value if(user1.value { return -1; }else if(user1.value>user2.value) { return 1; }else { return user1.name.compareTo(user2.name); } } } ); Iterator iterator = results.keySet().iterator(); while(iterator.hasNext()) { String name = (String)iterator.next(); Integer value = (Integer)results.get(name); if(value > 1) { sortedResults.add(new User(name,value)); } } printResults(sortedResults); } private static void printResults(TreeSet sortedResults) { Iterator iterator = sortedResults.iterator(); while(iterator.hasNext()) { User user = (User)iterator.next(); System.out.println(user.name + “:” + user.value); } } public static void dealLine(String line,Map map) { if(!"".equals(line.trim())) { String [] results = line.split(","); if(results.length == 3) { String name = results[1]; Integer value = (Integer)map.get(name); if(value == null) value = 0; map.put(name,value + 1); } } } } 第一种:饱汉模式 public class SingleTon { private SingleTon(){ } //实例化放在静态代码块里可提高程序的执行效率,但也可能更占用空间 private final static SingleTon instance = new SingleTon(); public static SingleTon getInstance(){ return instance; } } 第二种:饥汉模式 public class SingleTon { private SingleTon(){} private static instance = null;//new SingleTon(); public static synchronized SingleTon getInstance(){ if(instance == null) instance = new SingleTon(); return instance; } } 第三种:用枚举 public enum SingleTon{ ONE; } 第三:更实际的应用(在什么情况用单例) public class SequenceGenerator{ //下面是该类自身的业务功能代码 private int count = 0; public synchronized int getSequence(){ ++count; } //下面是把该类变成单例的代码 private SequenceGenerator(){} private final static instance = new SequenceGenerator(); public static SingleTon getInstance(){ return instance; } } 第四: public class MemoryDao { private HashMap map = new HashMap(); public void add(Student stu1){ map.put(SequenceGenerator.getInstance().getSequence(),stu1); } //把MemoryDao变成单例 } Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在。 一般Singleton模式通常有几种种形式: 第一种形式: 定义一个类,它的构造函数为private的,它有一个static的private的该类变量,在类初始化时实例话,通过一个public的getInstance方法获取对它的引用,继而调用其中的方法。 public class Singleton { private Singleton(){} //在自己内部定义自己一个实例,是不是很奇怪? //注意这是private 只供内部调用 private static Singleton instance = new Singleton(); //这里提供了一个供外部访问本class的静态方法,可以直接访问 public static Singleton getInstance() { return instance; } } 第二种形式: public class Singleton { private static Singleton instance = null; public static synchronized Singleton getInstance() { //这个方法比上面有所改进,不用每次都进行生成对象,只是第一次 //使用时生成实例,提高了效率! if (instance==null) instance=new Singleton(); return instance; } } 其他形式: 定义一个类,它的构造函数为private的,所有方法为static的。 一般认为第一种形式要更加安全些 这是一个菲波拉契数列问题 N阶乘: public int factorial(int m) 平衡二叉树是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。 在遍历二叉树时,能循环到起点指针称为有环。2、第1个人10,第2个比第1个人大2岁,依次递推,请用递归方式计算出第8个人多大?
3、排序都有哪几种方法?请列举。用JAVA实现一个快速排序。
4、金额转换,阿拉伯数字的金额转换成中国传统的形式如:(¥1011)->(一千零一拾一元整)输出。
5、从类似如下的文本文件中读取出所有的姓名,并打印出重复的姓名和重复的次数,并按重复次数排序:
6、写一个Singleton出来。
7、古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少?
public class lianxi01 {
public static void main(String[] args) {
System.out.println(“第1个月的兔子对数: 1”);
System.out.println(“第2个月的兔子对数: 1”);
int f1 = 1, f2 = 1, f, M=24;
for(int i=3; i<=M; i++) {
f = f2;
f2 = f1 + f2;
f1 = f;
System.out.println(“第” + i +"个月的兔子对数: "+f2);
}
}
}8、简单的说个递归
{
if (m < 0)
return 0;
else if ( m == 1)
reteurn 1;
else if (m > 1)
return m * factorial(m-1);
}9、什么是平衡二叉树
10、怎么判断二叉树是否有环